diff --git a/blots/scroll.ts b/blots/scroll.ts index 69df3a66fd..5adeb95b0f 100644 --- a/blots/scroll.ts +++ b/blots/scroll.ts @@ -169,18 +169,19 @@ class Scroll extends ScrollBlot { ? first.delta : new Delta().insert({ [first.key]: first.value }); insertInlineContents(this, index, delta); - const newlineCharIndex = index + delta.length(); + const newlineCharLength = first.type === 'block' ? 1 : 0; + const lineEndIndex = index + delta.length() + newlineCharLength; if (shouldInsertNewlineChar) { - this.insertAt(newlineCharIndex, '\n'); + this.insertAt(lineEndIndex - 1, '\n'); } const formats = bubbleFormats(this.line(index)[0]); const attributes = AttributeMap.diff(formats, first.attributes) || {}; Object.keys(attributes).forEach(name => { - this.formatAt(newlineCharIndex, 1, name, attributes[name]); + this.formatAt(lineEndIndex - 1, 1, name, attributes[name]); }); - index = newlineCharIndex + 1; + index = lineEndIndex; } let [refBlot, refBlotOffset] = this.children.find(index); diff --git a/test/fuzz/editor.spec.ts b/test/fuzz/editor.spec.ts index 961817095e..8ea674b959 100644 --- a/test/fuzz/editor.spec.ts +++ b/test/fuzz/editor.spec.ts @@ -122,9 +122,13 @@ const generateSingleInsertDelta = (): Delta['ops'][number] & { return op; }; -const safePushInsert = (delta: Delta) => { +const safePushInsert = (delta: Delta, isDoc: boolean) => { const op = generateSingleInsertDelta(); - if (typeof op.insert === 'object' && op.insert[BLOCK_EMBED_NAME]) { + if ( + typeof op.insert === 'object' && + op.insert[BLOCK_EMBED_NAME] && + (!isDoc || (delta.ops.length && !isLineFinished(delta))) + ) { delta.insert('\n'); } delta.push(op); @@ -134,7 +138,7 @@ const generateDocument = () => { const delta = new Delta(); const operationCount = 2 + randomInt(20); for (let i = 0; i < operationCount; i += 1) { - safePushInsert(delta); + safePushInsert(delta, true); } if (!isLineFinished(delta)) { delta.insert('\n'); @@ -161,7 +165,7 @@ const generateChange = ( const delta = new Delta(); const operationCount = randomInt(5) + 1; for (let i = 0; i < operationCount; i += 1) { - safePushInsert(delta); + safePushInsert(delta, false); } if ( needNewline || @@ -244,7 +248,14 @@ describe('editor', () => { const doc = quill.getContents(); const change = generateChange(doc, randomInt(4) + 1); const diff = quill.updateContents(change); - expect(change).toEqual(diff); + try { + expect(change).toEqual(diff); + } catch (err) { + console.log(JSON.stringify(doc.ops)); + console.log(JSON.stringify(change.ops)); + console.log(JSON.stringify(diff.ops)); + throw err; + } } }); }); diff --git a/test/unit/core/editor.spec.ts b/test/unit/core/editor.spec.ts index 4ce254d21f..df428957ff 100644 --- a/test/unit/core/editor.spec.ts +++ b/test/unit/core/editor.spec.ts @@ -975,6 +975,33 @@ describe('Editor', () => { }); }); + test('prepends a formatted block embed', () => { + const editor = createEditor(`

`); + editor.insertContents( + 0, + new Delta().insert({ video: '#' }, { width: '300' }), + ); + expect(editor.getDelta().ops).toEqual([ + { insert: { video: '#' }, attributes: { width: '300' } }, + { insert: '\n' }, + ]); + }); + + test('prepends two formatted block embeds', () => { + const editor = createEditor(`

`); + editor.insertContents( + 0, + new Delta() + .insert({ video: '#' }, { width: '300' }) + .insert({ video: '#' }, { width: '600' }), + ); + expect(editor.getDelta().ops).toEqual([ + { insert: { video: '#' }, attributes: { width: '300' } }, + { insert: { video: '#' }, attributes: { width: '600' } }, + { insert: '\n' }, + ]); + }); + test('inserts formatted block embeds (styles)', () => { const editor = createEditor(`

`); editor.insertContents(