From 164b38f0cd10118d1f3ff8ae11daff03e657ec9e Mon Sep 17 00:00:00 2001 From: Jeremy Nicholl Date: Mon, 31 Jan 2022 14:49:16 -0500 Subject: [PATCH 1/3] Avoid copying attribute map when deleting Calling cleanupFormattingGap should not make a copy of the attributes because it needs to be able to update them. --- src/types/YText.js | 2 +- tests/y-text.tests.js | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/types/YText.js b/src/types/YText.js index 328795e0..8e505f5a 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -465,7 +465,7 @@ const deleteText = (transaction, currPos, length) => { currPos.forward() } if (start) { - cleanupFormattingGap(transaction, start, currPos.right, startAttrs, map.copy(currPos.currentAttributes)) + cleanupFormattingGap(transaction, start, currPos.right, startAttrs, currPos.currentAttributes) } const parent = /** @type {AbstractType} */ (/** @type {Item} */ (currPos.left || currPos.right).parent) if (parent._searchMarker) { diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 2a555237..0ca9a879 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -138,6 +138,28 @@ export const testNotMergeEmptyLinesFormat = tc => { ]) } +/** + * @param {t.TestCase} tc + */ +export const testPreserveAttributesThroughDelete = tc => { + const ydoc = new Y.Doc() + const testText = ydoc.getText('test'); + testText.applyDelta([ + { insert: 'Text' }, + { insert: '\n', attributes: { title: true } }, + { insert: '\n' } + ]) + testText.applyDelta([ + { retain: 4 }, + { delete: 1 }, + { retain: 1, attributes: { title: true } }, + ]) + t.compare(testText.toDelta(), [ + { insert: 'Text' }, + { insert: '\n', attributes: { title: true } }, + ]) +} + /** * @param {t.TestCase} tc */ From 84e95f11cb9bdae13f3c4af3625efdc85990bfff Mon Sep 17 00:00:00 2001 From: Jeremy Nicholl Date: Thu, 3 Feb 2022 15:19:57 -0500 Subject: [PATCH 2/3] Fix formatting --- tests/y-text.tests.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 0ca9a879..7a8c448a 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -143,7 +143,7 @@ export const testNotMergeEmptyLinesFormat = tc => { */ export const testPreserveAttributesThroughDelete = tc => { const ydoc = new Y.Doc() - const testText = ydoc.getText('test'); + const testText = ydoc.getText('test') testText.applyDelta([ { insert: 'Text' }, { insert: '\n', attributes: { title: true } }, @@ -152,11 +152,11 @@ export const testPreserveAttributesThroughDelete = tc => { testText.applyDelta([ { retain: 4 }, { delete: 1 }, - { retain: 1, attributes: { title: true } }, + { retain: 1, attributes: { title: true } } ]) t.compare(testText.toDelta(), [ { insert: 'Text' }, - { insert: '\n', attributes: { title: true } }, + { insert: '\n', attributes: { title: true } } ]) } From 2d1e3fde43965940d019a0d3279348a2fded7c42 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 4 Feb 2022 11:26:32 +0100 Subject: [PATCH 3/3] fixed edge formatting case --- src/types/YText.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 8e505f5a..c0339003 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -338,14 +338,16 @@ const formatText = (transaction, parent, currPos, length, attributes) => { * * @param {Transaction} transaction * @param {Item} start - * @param {Item|null} end exclusive end, automatically iterates to the next Content Item + * @param {Item|null} curr exclusive end, automatically iterates to the next Content Item * @param {Map} startAttributes - * @param {Map} endAttributes This attribute is modified! + * @param {Map} currAttributes * @return {number} The amount of formatting Items deleted. * * @function */ -const cleanupFormattingGap = (transaction, start, end, startAttributes, endAttributes) => { +const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAttributes) => { + let end = curr + const endAttributes = map.copy(currAttributes) while (end && (!end.countable || end.deleted)) { if (!end.deleted && end.content.constructor === ContentFormat) { updateCurrentAttributes(endAttributes, /** @type {ContentFormat} */ (end.content)) @@ -353,7 +355,11 @@ const cleanupFormattingGap = (transaction, start, end, startAttributes, endAttri end = end.right } let cleanups = 0 + let reachedEndOfCurr = false while (start !== end) { + if (curr === start) { + reachedEndOfCurr = true + } if (!start.deleted) { const content = start.content switch (content.constructor) { @@ -363,6 +369,9 @@ const cleanupFormattingGap = (transaction, start, end, startAttributes, endAttri // Either this format is overwritten or it is not necessary because the attribute already existed. start.delete(transaction) cleanups++ + if (!reachedEndOfCurr && (currAttributes.get(key) || null) === value) { + currAttributes.delete(key) + } } break }