From 083df2dd931cbd2248dfb908a2f96a71e3df090b Mon Sep 17 00:00:00 2001 From: Ian Storm Taylor Date: Tue, 29 Nov 2016 16:19:40 -0800 Subject: [PATCH] fix to always opt for the furthest text node at an offset --- src/models/node.js | 8 +++++++- src/transforms/at-range.js | 16 +++++++++------- .../join-blocks-with-inline-void/index.js | 17 +++++++++++++++++ .../join-blocks-with-inline-void/input.yaml | 17 +++++++++++++++++ .../join-blocks-with-inline-void/output.yaml | 12 ++++++++++++ 5 files changed, 62 insertions(+), 8 deletions(-) create mode 100644 test/transforms/fixtures/at-range/delete-backward-at-range/join-blocks-with-inline-void/index.js create mode 100644 test/transforms/fixtures/at-range/delete-backward-at-range/join-blocks-with-inline-void/input.yaml create mode 100644 test/transforms/fixtures/at-range/delete-backward-at-range/join-blocks-with-inline-void/output.yaml diff --git a/src/models/node.js b/src/models/node.js index addd35f7f..5928ef26c 100644 --- a/src/models/node.js +++ b/src/models/node.js @@ -990,8 +990,14 @@ const Node = { let length = 0 return this .getTexts() - .find((text) => { + .find((text, i, texts) => { + const next = texts.get(i + 1) length += text.length + + // If the next text is an empty string, return false, because we want + // the furthest text node at the offset, and it will also match. + if (next && next.length == 0) return false + return length >= offset }) }, diff --git a/src/transforms/at-range.js b/src/transforms/at-range.js index 0b126220b..004994a76 100644 --- a/src/transforms/at-range.js +++ b/src/transforms/at-range.js @@ -61,6 +61,7 @@ export function deleteAtRange(transform, range, options = {}) { const { normalize = true } = options const { startKey, startOffset, endKey, endOffset } = range + // If the start and end key are the same, we can just remove text. if (startKey == endKey) { const index = startOffset const length = endOffset - startOffset @@ -68,10 +69,9 @@ export function deleteAtRange(transform, range, options = {}) { return } + // Split at the range edges within a common ancestor, without normalizing. let { state } = transform let { document } = state - - // split the nodes at range, within the common ancestor let ancestor = document.getCommonAncestor(startKey, endKey) let startChild = ancestor.getHighestChild(startKey) let endChild = ancestor.getHighestChild(endKey) @@ -81,12 +81,9 @@ export function deleteAtRange(transform, range, options = {}) { transform.splitNodeByKey(startChild.key, startOff, OPTS) transform.splitNodeByKey(endChild.key, endOff, OPTS) + // Refresh variables. state = transform.state document = state.document - const startBlock = document.getClosestBlock(startKey) - const endBlock = document.getClosestBlock(document.getNextText(endKey).key) - - // remove all of the nodes between range ancestor = document.getCommonAncestor(startKey, endKey) startChild = ancestor.getHighestChild(startKey) endChild = ancestor.getHighestChild(endKey) @@ -94,13 +91,18 @@ export function deleteAtRange(transform, range, options = {}) { const endIndex = ancestor.nodes.indexOf(endChild) const middles = ancestor.nodes.slice(startIndex + 1, endIndex + 1) + // Remove all of the middle nodes, between the splits. if (middles.size) { - // remove first nodes directly so the document is not normalized middles.forEach(child => { transform.removeNodeByKey(child.key, OPTS) }) } + // If the start and end block are different, move all of the nodes from the + // end block into the start block. + const startBlock = document.getClosestBlock(startKey) + const endBlock = document.getClosestBlock(document.getNextText(endKey).key) + if (startBlock.key !== endBlock.key) { endBlock.nodes.forEach((child, i) => { const newKey = startBlock.key diff --git a/test/transforms/fixtures/at-range/delete-backward-at-range/join-blocks-with-inline-void/index.js b/test/transforms/fixtures/at-range/delete-backward-at-range/join-blocks-with-inline-void/index.js new file mode 100644 index 000000000..4aeb185b2 --- /dev/null +++ b/test/transforms/fixtures/at-range/delete-backward-at-range/join-blocks-with-inline-void/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTexts() + const last = texts.last() + const range = selection.merge({ + anchorKey: last.key, + anchorOffset: 0, + focusKey: last.key, + focusOffset: 0 + }) + + return state + .transform() + .deleteBackwardAtRange(range) + .apply() +} diff --git a/test/transforms/fixtures/at-range/delete-backward-at-range/join-blocks-with-inline-void/input.yaml b/test/transforms/fixtures/at-range/delete-backward-at-range/join-blocks-with-inline-void/input.yaml new file mode 100644 index 000000000..c3571ce29 --- /dev/null +++ b/test/transforms/fixtures/at-range/delete-backward-at-range/join-blocks-with-inline-void/input.yaml @@ -0,0 +1,17 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: "" + - kind: inline + type: emoji + isVoid: true + - kind: text + text: "" + - kind: block + type: paragraph + nodes: + - kind: text + text: "" diff --git a/test/transforms/fixtures/at-range/delete-backward-at-range/join-blocks-with-inline-void/output.yaml b/test/transforms/fixtures/at-range/delete-backward-at-range/join-blocks-with-inline-void/output.yaml new file mode 100644 index 000000000..057979d81 --- /dev/null +++ b/test/transforms/fixtures/at-range/delete-backward-at-range/join-blocks-with-inline-void/output.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: "" + - kind: inline + type: emoji + isVoid: true + - kind: text + text: ""