From a50ec42d674f37c74c8afa5ed38844fbf0da3088 Mon Sep 17 00:00:00 2001 From: Ian Storm Taylor Date: Mon, 18 Jul 2016 16:52:11 -0700 Subject: [PATCH] fix to not create snapshot history for selection changes, closes #94 --- lib/models/transform.js | 133 ++++++++++++++++++++++++++-------------- 1 file changed, 87 insertions(+), 46 deletions(-) diff --git a/lib/models/transform.js b/lib/models/transform.js index bb63c1d03..8bc8f0913 100644 --- a/lib/models/transform.js +++ b/lib/models/transform.js @@ -68,25 +68,16 @@ const SELECTION_TRANSFORMS = [ ] /** - * State transforms, that act on both the document and selection. + * State-level document transforms. */ -const STATE_TRANSFORMS = [ - 'collapseToEndOfNextBlock', - 'collapseToEndOfNextText', - 'collapseToEndOfPreviousBlock', - 'collapseToEndOfPreviousText', - 'collapseToStartOfNextBlock', - 'collapseToStartOfNextText', - 'collapseToStartOfPreviousBlock', - 'collapseToStartOfPreviousText', +const STATE_DOCUMENT_TRANSFORMS = [ 'delete', 'deleteBackward', 'deleteForward', 'insertFragment', 'insertText', 'addMark', - 'moveTo', 'setBlock', 'setInline', 'splitBlock', @@ -98,6 +89,30 @@ const STATE_TRANSFORMS = [ 'wrapInline' ] +/** + * State selection transforms. + */ + +const STATE_SELECTION_TRANSFORMS = [ + 'collapseToEndOfNextBlock', + 'collapseToEndOfNextText', + 'collapseToEndOfPreviousBlock', + 'collapseToEndOfPreviousText', + 'collapseToStartOfNextBlock', + 'collapseToStartOfNextText', + 'collapseToStartOfPreviousBlock', + 'collapseToStartOfPreviousText', + 'moveTo', +] + +/** + * All state-level transforms. + */ + +const STATE_TRANSFORMS = [] + .concat(STATE_DOCUMENT_TRANSFORMS) + .concat(STATE_SELECTION_TRANSFORMS) + /** * All transforms. */ @@ -122,23 +137,12 @@ const DEFAULT_PROPERTIES = { class Transform extends new Record(DEFAULT_PROPERTIES) { - /** - * Create a history-ready snapshot of the current state. - * - * @return {Snapshot} snapshot - */ - - snapshot() { - let { state, steps } = this - let { document, selection } = state - return new Snapshot({ document, selection, steps }) - } - /** * Apply the transform and return the new state. * * @param {Object} options * @property {Boolean} isNative + * @property {Boolean} snapshot * @return {State} state */ @@ -149,29 +153,9 @@ class Transform extends new Record(DEFAULT_PROPERTIES) { let { undos, redos } = history // Determine whether we need to create a new snapshot. - let shouldSnapshot = false - const previous = undos.peek() - - // If there isn't a previous state, snapshot. - if (!previous) shouldSnapshot = true - - // If there is a previous state but the steps are different, snapshot. - if (!shouldSnapshot && previous) { - const types = steps.map(step => step.type) - const prevTypes = previous.steps.map(step => step.type) - const diff = xor(types.toArray(), prevTypes.toArray()) - if (diff.length) shouldSnapshot = true - } - - // If the current steps aren't one of the "combinale" types, snapshot. - if (!shouldSnapshot) { - const allCombinable = ( - steps.every(step => step.type == 'insertText') || - steps.every(step => step.type == 'deleteForward') || - steps.every(step => step.type == 'deleteBackward') - ) - if (!allCombinable) shouldSnapshot = true - } + const shouldSnapshot = options.snapshot == null + ? this.shouldSnapshot() + : options.snapshot // If we should, save a snapshot into the history before transforming. if (shouldSnapshot) { @@ -237,6 +221,63 @@ class Transform extends new Record(DEFAULT_PROPERTIES) { } } + /** + * Check whether the current transform steps should create a snapshot. + * + * @return {Boolean} + */ + + shouldSnapshot() { + const transform = this + const { state, steps } = transform + const { cursorMarks, history, selection } = state + const { undos, redos } = history + const previous = undos.peek() + + // If the only steps applied are selection transforms, don't snapshot. + const onlySelections = steps.every((step) => { + return ( + includes(SELECTION_TRANSFORMS, step.type) || + includes(STATE_SELECTION_TRANSFORMS, step.type) + ) + }) + + if (onlySelections) return false + + // If there isn't a previous state, snapshot. + if (!previous) return true + + // If there is a previous state but the steps are different, snapshot. + const types = steps.map(step => step.type) + const prevTypes = previous.steps.map(step => step.type) + const diff = xor(types.toArray(), prevTypes.toArray()) + if (diff.length) return true + + // If the current steps aren't one of the "combinable" types, snapshot. + const allCombinable = ( + steps.every(step => step.type == 'insertText') || + steps.every(step => step.type == 'deleteForward') || + steps.every(step => step.type == 'deleteBackward') + ) + + if (!allCombinable) return true + + // Otherwise, don't snapshot. + return false + } + + /** + * Create a history-ready snapshot of the current state. + * + * @return {Snapshot} snapshot + */ + + snapshot() { + let { state, steps } = this + let { document, selection } = state + return new Snapshot({ document, selection, steps }) + } + /** * Undo to the previous state in the history. *