diff --git a/core/editor.js b/core/editor.js index 8c4a42ee99..a25dcfcaa8 100644 --- a/core/editor.js +++ b/core/editor.js @@ -4,6 +4,7 @@ import extend from 'extend'; import Delta from 'quill-delta'; import DeltaOp from 'quill-delta/lib/op'; import { LeafBlot } from 'parchment'; +import { Range } from './selection'; import CursorBlot from '../blots/cursor'; import Block, { bubbleFormats } from '../blots/block'; import Break from '../blots/break'; @@ -204,14 +205,8 @@ class Editor { const oldText = new Delta().insert(oldValue); const newText = new Delta().insert(textBlot.value()); const relativeSelectionInfo = selectionInfo && [ - { - index: selectionInfo[0].index - index, - length: selectionInfo[0].length, - }, - { - index: selectionInfo[1].index - index, - length: selectionInfo[1].length, - }, + new Range(selectionInfo[0].index - index, selectionInfo[0].length), + new Range(selectionInfo[1].index - index, selectionInfo[1].length), ]; const diffDelta = new Delta() .retain(index) @@ -343,74 +338,74 @@ function splitDelta(delta, index) { } function diffDeltas(oldDelta, newDelta, selectionInfo = undefined) { - if (selectionInfo) { - // generate better diffs than Delta#diff by taking into account the - // old and new selection. for example, a text change from "xxx" to "xx" - // could be a delete or forwards-delete of any one of the x's, or the - // result of selecting two of the x's and typing "x". - const [oldSelection, newSelection] = selectionInfo; - const oldDeltaLength = oldDelta.length(); - const newDeltaLength = newDelta.length(); - if (oldSelection.length === 0 && newSelection.length === 0) { - const oldCursor = oldSelection.index; - const newCursor = newSelection.index; - const [oldBefore, oldAfter] = splitDelta(oldDelta, oldCursor); - const [newBefore, newAfter] = splitDelta(newDelta, newCursor); - if (equal(oldAfter, newAfter)) { - const prefixLength = Math.min(oldCursor, newCursor); - const [oldPrefix, oldMiddle] = splitDelta(oldBefore, prefixLength); - const [newPrefix, newMiddle] = splitDelta(newBefore, prefixLength); - if (equal(oldPrefix, newPrefix)) { - // insert or delete right before cursor - return new Delta() - .retain(prefixLength) - .concat(oldMiddle.diff(newMiddle)); - } - } else if (equal(oldBefore, newBefore)) { - const suffixLength = Math.min( - oldDeltaLength - oldCursor, - newDeltaLength - newCursor, - ); - const [oldMiddle, oldSuffix] = splitDelta( - oldAfter, - oldDeltaLength - oldCursor - suffixLength, - ); - const [newMiddle, newSuffix] = splitDelta( - newAfter, - newDeltaLength - newCursor - suffixLength, - ); - if (equal(oldSuffix, newSuffix)) { - // insert or delete right after cursor - return new Delta() - .retain(oldCursor) - .concat(oldMiddle.diff(newMiddle)); - } + if (selectionInfo == null) { + return oldDelta.diff(newDelta); + } + + // generate better diffs than Delta#diff by taking into account the + // old and new selection. for example, a text change from "xxx" to "xx" + // could be a delete or forwards-delete of any one of the x's, or the + // result of selecting two of the x's and typing "x". + const [oldSelection, newSelection] = selectionInfo; + const oldDeltaLength = oldDelta.length(); + const newDeltaLength = newDelta.length(); + if (oldSelection.length === 0 && newSelection.length === 0) { + // see if we have an insert or delete before or after cursor + const oldCursor = oldSelection.index; + const newCursor = newSelection.index; + const [oldBefore, oldAfter] = splitDelta(oldDelta, oldCursor); + const [newBefore, newAfter] = splitDelta(newDelta, newCursor); + if (equal(oldAfter, newAfter)) { + const prefixLength = Math.min(oldCursor, newCursor); + const [oldPrefix, oldMiddle] = splitDelta(oldBefore, prefixLength); + const [newPrefix, newMiddle] = splitDelta(newBefore, prefixLength); + if (equal(oldPrefix, newPrefix)) { + // insert or delete right before cursor + return new Delta() + .retain(prefixLength) + .concat(oldMiddle.diff(newMiddle)); } - } else if (oldSelection.length > 0 && newSelection.length === 0) { - // see if diff could be a splice of the old selection range - const oldPrefix = oldDelta.slice(0, oldSelection.index); - const oldSuffix = oldDelta.slice( - oldSelection.index + oldSelection.length, + } else if (equal(oldBefore, newBefore)) { + const suffixLength = Math.min( + oldDeltaLength - oldCursor, + newDeltaLength - newCursor, ); - const prefixLength = oldPrefix.length(); - const suffixLength = oldSuffix.length(); - if (newDeltaLength >= prefixLength + suffixLength) { - const newPrefix = newDelta.slice(0, prefixLength); - const newSuffix = newDelta.slice(newDeltaLength - suffixLength); - if (equal(oldPrefix, newPrefix) && equal(oldSuffix, newSuffix)) { - const oldMiddle = oldDelta.slice( - prefixLength, - oldDeltaLength - suffixLength, - ); - const newMiddle = newDelta.slice( - prefixLength, - newDeltaLength - suffixLength, - ); - return new Delta() - .retain(prefixLength) - .concat(newMiddle) - .delete(oldMiddle.length()); - } + const [oldMiddle, oldSuffix] = splitDelta( + oldAfter, + oldDeltaLength - oldCursor - suffixLength, + ); + const [newMiddle, newSuffix] = splitDelta( + newAfter, + newDeltaLength - newCursor - suffixLength, + ); + if (equal(oldSuffix, newSuffix)) { + // insert or delete right after cursor + return new Delta().retain(oldCursor).concat(oldMiddle.diff(newMiddle)); + } + } + } + if (oldSelection.length > 0 && newSelection.length === 0) { + // see if diff could be a splice of the old selection range + const oldPrefix = oldDelta.slice(0, oldSelection.index); + const oldSuffix = oldDelta.slice(oldSelection.index + oldSelection.length); + const prefixLength = oldPrefix.length(); + const suffixLength = oldSuffix.length(); + if (newDeltaLength >= prefixLength + suffixLength) { + const newPrefix = newDelta.slice(0, prefixLength); + const newSuffix = newDelta.slice(newDeltaLength - suffixLength); + if (equal(oldPrefix, newPrefix) && equal(oldSuffix, newSuffix)) { + const oldMiddle = oldDelta.slice( + prefixLength, + oldDeltaLength - suffixLength, + ); + const newMiddle = newDelta.slice( + prefixLength, + newDeltaLength - suffixLength, + ); + return new Delta() + .retain(prefixLength) + .concat(newMiddle) + .delete(oldMiddle.length()); } } } diff --git a/test/unit/core/quill.js b/test/unit/core/quill.js index 7530c0547f..c508599824 100644 --- a/test/unit/core/quill.js +++ b/test/unit/core/quill.js @@ -243,7 +243,7 @@ describe('Quill', function() { ) { return function(done) { this.quill.setText(`${oldText}\n`); - this.quill.setSelection(oldSelection); // number or {index, length} + this.quill.setSelection(oldSelection); // number or Range this.quill.update(); const oldContents = this.quill.getContents(); const textNode = this.container.firstChild.firstChild.firstChild; @@ -345,7 +345,7 @@ describe('Quill', function() { 'replace yay with y', editTest( 'yay', - { index: 0, length: 3 }, + new Range(0, 3), 'y', 1, new Delta().insert('y').delete(3),