From dfcd791b3b5fa57b47f297142f0637f8609694e4 Mon Sep 17 00:00:00 2001 From: Ian Storm Taylor Date: Thu, 28 Jul 2016 17:17:18 -0700 Subject: [PATCH] fix deleteAtRange to split only to common ancestor --- lib/models/node.js | 26 ++++++++++++ lib/models/transforms.js | 42 +++++++++++-------- .../across-nested-blocks/index.js | 19 +++++++++ .../across-nested-blocks/input.yaml | 20 +++++++++ .../across-nested-blocks/output.yaml | 15 +++++++ .../delete/across-nested-blocks/index.js | 29 +++++++++++++ .../delete/across-nested-blocks/input.yaml | 20 +++++++++ .../delete/across-nested-blocks/output.yaml | 15 +++++++ 8 files changed, 169 insertions(+), 17 deletions(-) create mode 100644 test/transforms/fixtures/delete-at-range/across-nested-blocks/index.js create mode 100644 test/transforms/fixtures/delete-at-range/across-nested-blocks/input.yaml create mode 100644 test/transforms/fixtures/delete-at-range/across-nested-blocks/output.yaml create mode 100644 test/transforms/fixtures/delete/across-nested-blocks/index.js create mode 100644 test/transforms/fixtures/delete/across-nested-blocks/input.yaml create mode 100644 test/transforms/fixtures/delete/across-nested-blocks/output.yaml diff --git a/lib/models/node.js b/lib/models/node.js index e7126ddba..9f0f1751a 100644 --- a/lib/models/node.js +++ b/lib/models/node.js @@ -296,6 +296,32 @@ const Node = { return this.nodes.find(node => node.key == key) }, + /** + * Get the common ancestor of nodes `one` and `two` by keys. + * + * @param {String or Node} one + * @param {String or Node} two + * @return {Node} + */ + + getCommonAncestor(one, two) { + this.assertDescendant(one) + this.assertDescendant(two) + let ancestors = new List() + let oneParent = this.getParent(one) + let twoParent = this.getParent(two) + + while (oneParent) { + ancestors = ancestors.push(oneParent) + oneParent = this.getParent(oneParent) + } + + while (twoParent) { + if (ancestors.includes(twoParent)) return twoParent + twoParent = this.getParent(twoParent) + } + }, + /** * Get a descendant node by `key`. * diff --git a/lib/models/transforms.js b/lib/models/transforms.js index 4488b9858..8984602d3 100644 --- a/lib/models/transforms.js +++ b/lib/models/transforms.js @@ -86,42 +86,50 @@ const Transforms = { // Split the blocks and determine the edge boundaries. const start = range.collapseToStart() const end = range.collapseToEnd() - node = node.splitBlockAtRange(start, Infinity) - node = node.splitBlockAtRange(end, Infinity) + let ancestor = node.getCommonAncestor(startKey, endKey) + const isAncestor = ancestor == node - const startText = node.getDescendant(startKey) - const startEdgeText = node.getNextText(startKey) + ancestor = ancestor.splitBlockAtRange(start, Infinity) + ancestor = ancestor.splitBlockAtRange(end, Infinity) - const endText = node.getNextText(endKey) - const endEdgeText = node.getDescendant(endKey) + const startText = ancestor.getDescendant(startKey) + const startEdgeText = ancestor.getNextText(startKey) + + const endText = ancestor.getNextText(endKey) + const endEdgeText = ancestor.getDescendant(endKey) // Remove the new blocks inside the edges. - const startEdgeBlock = node.getFurthestBlock(startEdgeText) - const endEdgeBlock = node.getFurthestBlock(endEdgeText) + const startEdgeBlock = ancestor.getFurthestBlock(startEdgeText) + const endEdgeBlock = ancestor.getFurthestBlock(endEdgeText) - const nodes = node.nodes + const nodes = ancestor.nodes .takeUntil(n => n == startEdgeBlock) - .concat(node.nodes.skipUntil(n => n == endEdgeBlock).rest()) + .concat(ancestor.nodes.skipUntil(n => n == endEdgeBlock).rest()) - node = node.merge({ nodes }) + ancestor = ancestor.merge({ nodes }) // Take the end edge's split text and move it to the start edge. - let startBlock = node.getClosestBlock(startText) - let endChild = node.getFurthestInline(endText) || endText + let startBlock = ancestor.getClosestBlock(startText) + let endChild = ancestor.getFurthestInline(endText) || endText const startNodes = startBlock.nodes.push(endChild) startBlock = startBlock.merge({ nodes: startNodes }) - node = node.updateDescendant(startBlock) + ancestor = ancestor.updateDescendant(startBlock) // While the end child is an only child, remove the block it's in. - let endParent = node.getClosestBlock(endChild) + let endParent = ancestor.getClosestBlock(endChild) while (endParent && endParent.nodes.size == 1) { endChild = endParent - endParent = node.getClosestBlock(endParent) + endParent = ancestor.getClosestBlock(endParent) } - node = node.removeDescendant(endChild) + ancestor = ancestor.removeDescendant(endChild) + + // Update the node. + node = isAncestor + ? ancestor + : node.updateDescendant(ancestor) // Normalize the adjacent text nodes. return node.normalize() diff --git a/test/transforms/fixtures/delete-at-range/across-nested-blocks/index.js b/test/transforms/fixtures/delete-at-range/across-nested-blocks/index.js new file mode 100644 index 000000000..221492bbb --- /dev/null +++ b/test/transforms/fixtures/delete-at-range/across-nested-blocks/index.js @@ -0,0 +1,19 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTexts() + const first = texts.get(0) + const second = texts.get(1) + const third = texts.get(2) + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: first.length, + focusKey: second.key, + focusOffset: 0 + }) + + return state + .transform() + .deleteAtRange(range) + .apply() +} diff --git a/test/transforms/fixtures/delete-at-range/across-nested-blocks/input.yaml b/test/transforms/fixtures/delete-at-range/across-nested-blocks/input.yaml new file mode 100644 index 000000000..adac95c13 --- /dev/null +++ b/test/transforms/fixtures/delete-at-range/across-nested-blocks/input.yaml @@ -0,0 +1,20 @@ + +nodes: + - kind: block + type: list + nodes: + - kind: block + type: item + nodes: + - kind: text + text: one + - kind: block + type: item + nodes: + - kind: text + text: two + - kind: block + type: item + nodes: + - kind: text + text: three diff --git a/test/transforms/fixtures/delete-at-range/across-nested-blocks/output.yaml b/test/transforms/fixtures/delete-at-range/across-nested-blocks/output.yaml new file mode 100644 index 000000000..44d7a8f17 --- /dev/null +++ b/test/transforms/fixtures/delete-at-range/across-nested-blocks/output.yaml @@ -0,0 +1,15 @@ + +nodes: + - kind: block + type: list + nodes: + - kind: block + type: item + nodes: + - kind: text + text: onetwo + - kind: block + type: item + nodes: + - kind: text + text: three diff --git a/test/transforms/fixtures/delete/across-nested-blocks/index.js b/test/transforms/fixtures/delete/across-nested-blocks/index.js new file mode 100644 index 000000000..364c3f637 --- /dev/null +++ b/test/transforms/fixtures/delete/across-nested-blocks/index.js @@ -0,0 +1,29 @@ + +import assert from 'assert' + +export default function (state) { + const { document, selection } = state + const texts = document.getTexts() + const first = texts.get(0) + const second = texts.get(1) + const third = texts.get(2) + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: first.length, + focusKey: second.key, + focusOffset: 0 + }) + + const next = state + .transform() + .moveTo(range) + .delete() + .apply() + + assert.deepEqual( + next.selection.toJS(), + range.collapseToStart().toJS() + ) + + return next +} diff --git a/test/transforms/fixtures/delete/across-nested-blocks/input.yaml b/test/transforms/fixtures/delete/across-nested-blocks/input.yaml new file mode 100644 index 000000000..adac95c13 --- /dev/null +++ b/test/transforms/fixtures/delete/across-nested-blocks/input.yaml @@ -0,0 +1,20 @@ + +nodes: + - kind: block + type: list + nodes: + - kind: block + type: item + nodes: + - kind: text + text: one + - kind: block + type: item + nodes: + - kind: text + text: two + - kind: block + type: item + nodes: + - kind: text + text: three diff --git a/test/transforms/fixtures/delete/across-nested-blocks/output.yaml b/test/transforms/fixtures/delete/across-nested-blocks/output.yaml new file mode 100644 index 000000000..44d7a8f17 --- /dev/null +++ b/test/transforms/fixtures/delete/across-nested-blocks/output.yaml @@ -0,0 +1,15 @@ + +nodes: + - kind: block + type: list + nodes: + - kind: block + type: item + nodes: + - kind: text + text: onetwo + - kind: block + type: item + nodes: + - kind: text + text: three