From b4e5c5cc1f3b61e81eb5bc848d374a125be942e8 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 13 Jun 2019 10:30:39 +0200 Subject: [PATCH] Correctly insert embed when using YText.applyDelta --- src/types/YText.js | 23 +++++++++++++++++++++-- tests/testHelper.js | 2 +- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 6c9c2657..8c46503f 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -226,7 +226,7 @@ const insertAttributes = (transaction, parent, left, right, currentAttributes, a * @param {Item|null} left * @param {Item|null} right * @param {Map} currentAttributes - * @param {string} text + * @param {string|object} text * @param {Object} attributes * @return {ItemListPosition} * @@ -299,6 +299,17 @@ const formatText = (transaction, parent, left, right, currentAttributes, length, left = right right = right.right } + // Quill just assumes that the editor starts with a newline and that it always + // ends with a newline. We only insert that newline when a new newline is + // inserted - i.e when length is bigger than type.length + if (length > 0) { + let newlines = '' + for (; length > 0; length--) { + newlines += '\n' + } + left = new Item(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, new ContentString(newlines)) + left.integrate(transaction) + } return insertNegatedAttributes(transaction, parent, left, right, negatedAttributes) } @@ -664,7 +675,15 @@ export class YText extends AbstractType { for (let i = 0; i < delta.length; i++) { const op = delta[i] if (op.insert !== undefined) { - pos = insertText(transaction, this, pos.left, pos.right, currentAttributes, op.insert, op.attributes || {}) + // Quill assumes that the content starts with an empty paragraph. + // Yjs/Y.Text assumes that it starts empty. We always hide that + // there is a newline at the end of the content. + // If we omit this step, clients will see a different number of + // paragraphs, but nothing bad will happen. + const ins = (typeof op.insert === 'string' && i === delta.length - 1 && pos.right === null && op.insert.slice(-1) === '\n') ? op.insert.slice(0, -1) : op.insert + if (typeof ins !== 'string' || ins.length > 0) { + pos = insertText(transaction, this, pos.left, pos.right, currentAttributes, ins, op.attributes || {}) + } } else if (op.retain !== undefined) { pos = formatText(transaction, this, pos.left, pos.right, currentAttributes, op.retain, op.attributes || {}) } else if (op.delete !== undefined) { diff --git a/tests/testHelper.js b/tests/testHelper.js index 2a8d4d40..56639a39 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -282,7 +282,7 @@ export const compare = users => { t.compare(userArrayValues[i], userArrayValues[i + 1]) t.compare(userMapValues[i], userMapValues[i + 1]) t.compare(userXmlValues[i], userXmlValues[i + 1]) - t.compare(userTextValues[i].map(/** @param {any} a */ a => a.insert).join('').length, users[i].getText('text').length) + t.compare(userTextValues[i].map(/** @param {any} a */ a => typeof a.insert === 'string' ? a.insert : ' ').join('').length, users[i].getText('text').length) t.compare(userTextValues[i], userTextValues[i + 1]) t.compare(getStateVector(users[i].store), getStateVector(users[i + 1].store)) compareDS(createDeleteSetFromStructStore(users[i].store), createDeleteSetFromStructStore(users[i + 1].store))