From 10cf4b44f8bd1a3c452bfc41273405c3fc57f8fa Mon Sep 17 00:00:00 2001 From: Bartosz Sypytkowski <b.sypytkowski@gmail.com> Date: Thu, 17 Aug 2023 20:00:48 +0200 Subject: [PATCH] fixed issues with unbounded XmlText.toString --- src/structs/Item.js | 2 +- src/types/YText.js | 23 +++++++++++++---------- tests/y-weak-links.tests.js | 25 ++++++++++++++++++++++++- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/structs/Item.js b/src/structs/Item.js index ed657d3d..8cad00cf 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -314,7 +314,7 @@ export class Item extends AbstractStruct { * bit2: countable * bit3: deleted * bit4: mark - mark node as fast-search-marker - * bit5: linked - this item is linked by Weak Link references + * bit9: linked - this item is linked by Weak Link references * @type {number} byte */ this.info = this.content.isCountable() ? binary.BIT2 : 0 diff --git a/src/types/YText.js b/src/types/YText.js index 40c861cc..00a43ffb 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1260,13 +1260,13 @@ export const rangeDelta = (parent, start, end, snapshot, prevSnapshot, computeYC } } const computeDelta = () => { - // scope represents offset at current block from which we're intersted in picking string + // startOffset represents offset at current block from which we're intersted in picking string // if it's -1 it means, we're out of scope and we should break at this point - let scope = start === null ? 0 : -1 + let startOffset = start === null ? 0 : -1 loop: while (n !== null) { - if (scope < 0 && start !== null) { + if (startOffset < 0 && start !== null) { if (start.client === n.id.client && start.clock >= n.id.clock && start.clock < n.id.clock + n.length) { - scope = n.id.clock + n.length - start.clock - 1 + startOffset = start.clock - n.id.clock } } if (isVisible(n, snapshot) || (prevSnapshot !== undefined && isVisible(n, prevSnapshot))) { @@ -1288,16 +1288,16 @@ export const rangeDelta = (parent, start, end, snapshot, prevSnapshot, computeYC currentAttributes.delete('ychange') } let s = /** @type {ContentString} */ (n.content).str - if (scope > 0) { - str += s.slice(scope) - scope = 0 + if (startOffset > 0) { + str += s.slice(startOffset) + startOffset = 0 } else if (end !== null && end.client === n.id.client && end.clock >= n.id.clock && end.clock < n.id.clock + n.length) { // we reached the end or range - const offset = n.id.clock + n.length - end.clock - 1 - str += s.slice(0, s.length + offset) // scope is negative + const endOffset = n.id.clock + n.length - end.clock - 1 + str += s.slice(0, s.length + endOffset) // scope is negative packStr() break loop - } else if (scope == 0) { + } else if (startOffset == 0) { str += s } break @@ -1328,6 +1328,9 @@ export const rangeDelta = (parent, start, end, snapshot, prevSnapshot, computeYC } break } + } else if (end !== null && end.client === n.id.client && end.clock >= n.id.clock && end.clock < n.id.clock + n.length) { + // block may not passed visibility check, but we still need to verify boundaries + break; } n = n.right } diff --git a/tests/y-weak-links.tests.js b/tests/y-weak-links.tests.js index b86369d4..2f0750c1 100644 --- a/tests/y-weak-links.tests.js +++ b/tests/y-weak-links.tests.js @@ -682,7 +682,7 @@ export const testRemoteMapUpdate = tc => { /** * @param {t.TestCase} tc */ -const testTextBasic = tc => { +export const testTextBasic = tc => { const { testConnector, text0, text1 } = init(tc, { users: 2 }) text0.insert(0, 'abcd') // 'abcd' @@ -701,6 +701,29 @@ const testTextBasic = tc => { t.compare(insert.toString(), 'be') } +/** + * @param {t.TestCase} tc + */ +export const testXmlTextBasic = tc => { + const { testConnector, xml0, xml1 } = init(tc, { users: 2 }) + const text0 = new Y.XmlText() + xml0.insert(0, [text0]) + + text0.insert(0, 'abcd') // 'abcd' + const link0 = text0.quote(1, 2) // quote: [bc] + t.compare(link0.toString(), 'bc') + text0.insert(2, 'ef') // 'abefcd', quote: [befc] + t.compare(link0.toString(), 'befc') + text0.delete(3, 3) // 'abe', quote: [be] + t.compare(link0.toString(), 'be') + text0.insertEmbed(3, link0) // 'abe[be]' + + testConnector.flushAllMessages() + const text1 = /** @type {Y.XmlText} */ (xml1.get(0)) + const delta = text1.toDelta() + const { insert } = delta[1] // YWeakLink + t.compare(insert.toString(), 'be') +} /** * @param {t.TestCase} tc */