overleaf/libraries/overleaf-editor-core/lib/range.js
Domagoj Kriskovic 2440f89be5 [overleaf-editor-core] AddCommentOperation and DeleteCommentOperation (#16871)
* [overleaf-editor-core] AddCommentOperation and DeleteCommentOperation

* added add comment op test

* delete comment op test

* import core to escape circle deps

* desctructure in tests

* require directly in builder

* invert of add comment is always delete comment

* no merging on compose

* NoOp if comment is not found

* use comment.clone()

* update test

* change CommentRawData type

* jsdoc assert type

* fix formating

* EditNoOperation

* return other in compose

* use ReturnType

* Revert "use ReturnType"

This reverts commit 2c7e04f1541310e9fc08963170a783a437ed1992.

* transorm add comment operation

* transform delete comment operation

* moved comment.js

* format fix

* fix transform addComment and textoperation

* fix merge

* test more complex test operations

* change to else if

* move range.js

* fix types

* fix AddComment and TextOperation transform

* fixed AddComment-TextOperation trasform, added test

* deletecommentoperation should win

* should not delete comment

* remove unused function, fix type

* fix format

* add resolved for existing comment

* transform EditNoOperation

* fix test description

* change the order of EditNoOperation

* fix DeleteCommentOperation-DeleteCommentOperation transform

* fix types after merging main

* refactor operation types

GitOrigin-RevId: 6f127763a6dc50d4fe3524d9b25dc7526b6b0028
2024-02-19 09:04:15 +00:00

202 lines
4.2 KiB
JavaScript

// @ts-check
class Range {
/**
* @param {number} pos
* @param {number} length
*/
constructor(pos, length) {
/** @readonly */
this.pos = pos
/** @readonly */
this.length = length
}
get start() {
return this.pos
}
get end() {
return this.pos + this.length
}
/**
* @param {Range} range
* @returns {boolean}
*/
startsAfter(range) {
return this.start >= range.end
}
/**
* @param {number} pos
* @returns {boolean}
*/
startIsAfter(pos) {
return this.start > pos
}
/**
*
* @returns {boolean}
*/
isEmpty() {
return this.length === 0
}
/**
* checks if the range contains a given range
* @param {Range} range
*/
contains(range) {
return this.start <= range.start && this.end >= range.end
}
/**
* checks if the range contains a cursor (i.e. is not at the ends of the range)
* @param {number} cursor
*/
containsCursor(cursor) {
return this.start <= cursor && this.end >= cursor
}
/**
* @param {Range} range
*/
overlaps(range) {
return this.start < range.end && this.end > range.start
}
/**
* checks if the range touches a given range
* @param {Range} range
*/
touches(range) {
return this.end === range.start || this.start === range.end
}
/**
* @param {Range} range
* @returns {Range}
*/
subtract(range) {
if (this.contains(range)) {
return this.shrinkBy(range.length)
}
if (range.contains(this)) {
return new Range(this.pos, 0)
}
if (range.overlaps(this)) {
if (range.start < this.start) {
const intersectedLength = range.end - this.start
return new Range(range.pos, this.length - intersectedLength)
} else {
const intersectedLength = this.end - range.start
return new Range(this.pos, this.length - intersectedLength)
}
}
return new Range(this.pos, this.length)
}
/**
* @param {Range} range
* @returns {boolean}
*/
canMerge(range) {
return this.overlaps(range) || this.touches(range)
}
/**
* @param {Range} range
*/
merge(range) {
if (!this.canMerge(range)) {
throw new Error('Ranges cannot be merged')
}
const newPos = Math.min(this.pos, range.pos)
const newEnd = Math.max(this.end, range.end)
return new Range(newPos, newEnd - newPos)
}
/**
* Moves the range by a given number
* @param {number} length
*/
moveBy(length) {
return new Range(this.pos + length, this.length)
}
/**
* Extends the range by a given number
* @param {number} extensionLength
*/
extendBy(extensionLength) {
return new Range(this.pos, this.length + extensionLength)
}
/**
* Shrinks the range by a given number
* @param {number} shrinkLength
*/
shrinkBy(shrinkLength) {
const newLength = this.length - shrinkLength
if (newLength < 0) {
throw new Error('Cannot shrink range by more than its length')
}
return new Range(this.pos, newLength)
}
/**
* Splits a range on the cursor and insert a range with the length provided
* @param {number} cursor
* @param {number} length
* @returns {[Range, Range, Range]}
*/
insertAt(cursor, length) {
if (!this.containsCursor(cursor)) {
throw new Error('The cursor must be contained in the range')
}
const rangeUpToCursor = new Range(this.pos, cursor - this.pos)
const insertedRange = new Range(cursor, length)
const rangeAfterCursor = new Range(
cursor + length,
this.length - rangeUpToCursor.length
)
return [rangeUpToCursor, insertedRange, rangeAfterCursor]
}
toRaw() {
return {
pos: this.pos,
length: this.length,
}
}
static fromRaw(raw) {
return new Range(raw.pos, raw.length)
}
/**
* Splits a range into two ranges, at a given cursor
* @param {number} cursor
* @returns {[Range, Range]}
*/
splitAt(cursor) {
if (!this.containsCursor(cursor)) {
throw new Error('The cursor must be contained in the range')
}
const rangeUpToCursor = new Range(this.pos, cursor - this.pos)
const rangeAfterCursor = new Range(
cursor,
this.length - rangeUpToCursor.length
)
return [rangeUpToCursor, rangeAfterCursor]
}
}
module.exports = Range