diff --git a/core/editor.ts b/core/editor.ts index 3a01bb55ab..209dedaf3c 100644 --- a/core/editor.ts +++ b/core/editor.ts @@ -6,6 +6,7 @@ import Delta, { AttributeMap, Op } from 'quill-delta'; import Block, { BlockEmbed, bubbleFormats } from '../blots/block'; import Break from '../blots/break'; import CursorBlot from '../blots/cursor'; +import Embed from '../blots/embed'; import Scroll from '../blots/scroll'; import TextBlot, { escapeText } from '../blots/text'; import { Range } from './selection'; @@ -30,17 +31,17 @@ class Editor { normalizedDelta.reduce((index, op) => { const length = Op.length(op); let attributes = op.attributes || {}; - let addedNewline = false; + let isImplicitNewlinePrepended = false; + let isImplicitNewlineAppended = false; if (op.insert != null) { deleteDelta.retain(length); if (typeof op.insert === 'string') { const text = op.insert; - // @ts-expect-error TODO: Fix this the next time the file is edited. - addedNewline = + isImplicitNewlineAppended = !text.endsWith('\n') && (scrollLength <= index || // @ts-expect-error - this.scroll.descendant(BlockEmbed, index)[0]); + !!this.scroll.descendant(BlockEmbed, index)[0]); this.scroll.insertAt(index, text); const [line, offset] = this.scroll.line(index); let formats = merge({}, bubbleFormats(line)); @@ -53,12 +54,30 @@ class Editor { } else if (typeof op.insert === 'object') { const key = Object.keys(op.insert)[0]; // There should only be one key if (key == null) return index; - // @ts-expect-error TODO: Fix this the next time the file is edited. - addedNewline = - this.scroll.query(key, Scope.INLINE) != null && - (scrollLength <= index || + const isInlineEmbed = this.scroll.query(key, Scope.INLINE) != null; + if (isInlineEmbed) { + if ( + scrollLength <= index || // @ts-expect-error - this.scroll.descendant(BlockEmbed, index)[0]); + !!this.scroll.descendant(BlockEmbed, index)[0] + ) { + isImplicitNewlineAppended = true; + } + } else if (index > 0) { + // @ts-expect-error + const [leaf, offset] = this.scroll.descendant(LeafBlot, index - 1); + if (leaf instanceof TextBlot) { + const text = leaf.value(); + if (text[offset] !== '\n') { + isImplicitNewlinePrepended = true; + } + } else if ( + leaf instanceof Embed && + leaf.statics.scope === Scope.INLINE_BLOT + ) { + isImplicitNewlinePrepended = true; + } + } this.scroll.insertAt(index, key, op.insert[key]); } scrollLength += length; @@ -74,10 +93,13 @@ class Editor { Object.keys(attributes).forEach(name => { this.scroll.formatAt(index, length, name, attributes[name]); }); - const addedLength = addedNewline ? 1 : 0; - scrollLength += addedLength; + const prependedLength = isImplicitNewlinePrepended ? 1 : 0; + const addedLength = isImplicitNewlineAppended ? 1 : 0; + scrollLength += addedLength + prependedLength; + console.log(JSON.stringify(deleteDelta.ops)); deleteDelta.delete(addedLength); - return index + length + addedLength; + deleteDelta.retain(prependedLength); + return index + length + addedLength + prependedLength; }, 0); deleteDelta.reduce((index, op) => { if (typeof op.delete === 'number') { diff --git a/test/unit/core/editor.js b/test/unit/core/editor.js index 9c23be16b1..84cb839ac0 100644 --- a/test/unit/core/editor.js +++ b/test/unit/core/editor.js @@ -611,6 +611,32 @@ describe('Editor', function() { ); }); + it('insert block embed to the middle of text and delete', function() { + const editor = this.initialize(Editor, `
abc
`); + editor.applyDelta( + new Delta() + .retain(2) + .insert({ video: '#' }) + .delete(2), + ); + expect(this.container).toEqualHTML( + 'a
abc
def
`); + editor.applyDelta( + new Delta() + .retain(5) + .insert({ video: '#' }) + .delete(2), + ); + expect(this.container).toEqualHTML( + 'abc
f
', + ); + }); + it('append formatted block embed', function() { const editor = this.initialize(Editor, '0123