mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #18838 from overleaf/mj-history-detached-ranges
[project-history] Handle detached ranges GitOrigin-RevId: cdbe9b46a03d55fd7b865fdd87092aaad1920c62
This commit is contained in:
parent
49dc94192a
commit
801a17af27
6 changed files with 88 additions and 7 deletions
|
@ -91,10 +91,6 @@ export function createRangeBlobDataFromUpdate(update) {
|
|||
/** @type {Map<string, {ranges: Range[], resolved: boolean}>} */
|
||||
const commentMap = new Map()
|
||||
for (const comment of comments) {
|
||||
const range = new Range(
|
||||
comment.op.hpos ?? comment.op.p,
|
||||
comment.op.hlen ?? comment.op.c.length
|
||||
)
|
||||
const id = comment.op.t
|
||||
if (!commentMap.has(id)) {
|
||||
commentMap.set(id, {
|
||||
|
@ -109,7 +105,13 @@ export function createRangeBlobDataFromUpdate(update) {
|
|||
if (entry.resolved !== (comment.op.resolved ?? false)) {
|
||||
throw new Error('Mismatching resolved status for comment')
|
||||
}
|
||||
entry.ranges.push(range)
|
||||
|
||||
const commentLength = comment.op.c.length
|
||||
if (commentLength > 0) {
|
||||
// Empty comments in operations are translated to detached comments
|
||||
const range = new Range(comment.op.hpos ?? comment.op.p, commentLength)
|
||||
entry.ranges.push(range)
|
||||
}
|
||||
}
|
||||
const commentList = new CommentList(
|
||||
[...commentMap.entries()].map(
|
||||
|
|
|
@ -126,6 +126,18 @@ async function getRangesSnapshot(projectId, version, pathname) {
|
|||
for (const comment of comments) {
|
||||
trackedDeletionOffset = 0
|
||||
let trackedDeletionIndex = 0
|
||||
if (comment.ranges.length === 0) {
|
||||
// Translate detached comments into zero length comments at position 0
|
||||
docUpdaterCompatibleComments.push({
|
||||
op: {
|
||||
p: 0,
|
||||
c: '',
|
||||
t: comment.id,
|
||||
resolved: comment.resolved,
|
||||
},
|
||||
})
|
||||
continue
|
||||
}
|
||||
for (const commentRange of comment.ranges) {
|
||||
let commentRangeContent = ''
|
||||
let offsetFromOverlappingRangeAtStart = 0
|
||||
|
|
|
@ -906,13 +906,18 @@ class SyncUpdateExpander {
|
|||
* @param {Comment} expectedComment
|
||||
*/
|
||||
function commentRangesAreInSync(persistedComment, expectedComment) {
|
||||
const expectedPos = expectedComment.op.hpos ?? expectedComment.op.p
|
||||
const expectedLength = expectedComment.op.hlen ?? expectedComment.op.c.length
|
||||
if (expectedLength === 0) {
|
||||
// A zero length comment from RangesManager is a detached comment in history
|
||||
return persistedComment.ranges.length === 0
|
||||
}
|
||||
|
||||
if (persistedComment.ranges.length !== 1) {
|
||||
// The editor only supports single range comments
|
||||
return false
|
||||
}
|
||||
const persistedRange = persistedComment.ranges[0]
|
||||
const expectedPos = expectedComment.op.hpos ?? expectedComment.op.p
|
||||
const expectedLength = expectedComment.op.hlen ?? expectedComment.op.c.length
|
||||
return (
|
||||
persistedRange.pos === expectedPos &&
|
||||
persistedRange.length === expectedLength
|
||||
|
|
|
@ -66,6 +66,25 @@ describe('HistoryBlobTranslator', function () {
|
|||
})
|
||||
})
|
||||
|
||||
describe('for update with zero length comments', function () {
|
||||
beforeEach(function () {
|
||||
this.result = createRangeBlobDataFromUpdate(
|
||||
update('pathname', this.text, {
|
||||
changes: [],
|
||||
comments: [
|
||||
{ op: { c: '', p: 4, t: 'comment-1', resolved: false } },
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
it('should treat them as detached comments', function () {
|
||||
expect(this.result).to.deep.equal({
|
||||
comments: [{ id: 'comment-1', ranges: [] }],
|
||||
trackedChanges: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('for update with ranges object with only comments', function () {
|
||||
it('should return unmoved ranges', function () {
|
||||
const result = createRangeBlobDataFromUpdate(
|
||||
|
|
|
@ -757,6 +757,7 @@ Four five six\
|
|||
],
|
||||
resolved: false,
|
||||
},
|
||||
{ id: 'comment-2', ranges: [], resolved: true },
|
||||
],
|
||||
})
|
||||
this.data = await this.SnapshotManager.promises.getRangesSnapshot(
|
||||
|
@ -785,6 +786,19 @@ Four five six\
|
|||
expect(this.data.comments[0].op.resolved).to.be.false
|
||||
expect(this.data.comments[1].op.resolved).to.be.false
|
||||
expect(this.data.comments[2].op.resolved).to.be.false
|
||||
expect(this.data.comments[3].op.resolved).to.be.true
|
||||
})
|
||||
|
||||
it('should include thread id', function () {
|
||||
expect(this.data.comments[0].op.t).to.eq('comment-1')
|
||||
expect(this.data.comments[1].op.t).to.eq('comment-1')
|
||||
expect(this.data.comments[2].op.t).to.eq('comment-1')
|
||||
expect(this.data.comments[3].op.t).to.eq('comment-2')
|
||||
})
|
||||
|
||||
it('should translated detached comment to zero length op', function () {
|
||||
expect(this.data.comments[3].op.p).to.eq(0)
|
||||
expect(this.data.comments[3].op.c).to.eq('')
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1286,6 +1286,35 @@ describe('SyncManager', function () {
|
|||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('treats zero length comments as detached comments', async function () {
|
||||
this.loadedSnapshotDoc.getComments.returns({
|
||||
toArray: sinon.stub().returns([new Comment('comment1', [])]),
|
||||
})
|
||||
this.comments = [makeComment('comment1', 16, '')]
|
||||
this.resolvedCommentIds = []
|
||||
|
||||
const updates = [
|
||||
docContentSyncUpdate(
|
||||
this.persistedDoc,
|
||||
this.persistedDocContent,
|
||||
{
|
||||
comments: this.comments,
|
||||
},
|
||||
this.resolvedCommentIds
|
||||
),
|
||||
]
|
||||
|
||||
const expandedUpdates =
|
||||
await this.SyncManager.promises.expandSyncUpdates(
|
||||
this.projectId,
|
||||
this.historyId,
|
||||
updates,
|
||||
this.extendLock
|
||||
)
|
||||
|
||||
expect(expandedUpdates).to.deep.equal([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('syncing tracked changes', function () {
|
||||
|
|
Loading…
Reference in a new issue