From 1c86b2fcf7fb57d6161e9042f86685c86b73db8a Mon Sep 17 00:00:00 2001 From: Ian Storm Taylor Date: Wed, 27 Jul 2016 22:30:37 -0700 Subject: [PATCH] fix deleting sibling inline node --- examples/links/index.js | 2 + lib/models/node.js | 12 ++++ lib/models/state.js | 68 ++++++++++++++----- .../inside-inline-sibling/index.js | 17 +++++ .../inside-inline-sibling/input.yaml | 14 ++++ .../inside-inline-sibling/output.yaml | 7 ++ .../inside-inline-sibling/index.js | 17 +++++ .../inside-inline-sibling/input.yaml | 14 ++++ .../inside-inline-sibling/output.yaml | 7 ++ .../inside-inline-sibling/index.js | 30 ++++++++ .../inside-inline-sibling/input.yaml | 14 ++++ .../inside-inline-sibling/output.yaml | 7 ++ .../inside-inline-sibling/index.js | 17 +++++ .../inside-inline-sibling/input.yaml | 14 ++++ .../inside-inline-sibling/output.yaml | 7 ++ .../inside-inline-sibling/index.js | 30 ++++++++ .../inside-inline-sibling/input.yaml | 14 ++++ .../inside-inline-sibling/output.yaml | 7 ++ .../delete/inside-inline-sibling/index.js | 35 ++++++++++ .../delete/inside-inline-sibling/input.yaml | 14 ++++ .../delete/inside-inline-sibling/output.yaml | 7 ++ 21 files changed, 338 insertions(+), 16 deletions(-) create mode 100644 test/transforms/fixtures/delete-at-range/inside-inline-sibling/index.js create mode 100644 test/transforms/fixtures/delete-at-range/inside-inline-sibling/input.yaml create mode 100644 test/transforms/fixtures/delete-at-range/inside-inline-sibling/output.yaml create mode 100644 test/transforms/fixtures/delete-backward-at-range/inside-inline-sibling/index.js create mode 100644 test/transforms/fixtures/delete-backward-at-range/inside-inline-sibling/input.yaml create mode 100644 test/transforms/fixtures/delete-backward-at-range/inside-inline-sibling/output.yaml create mode 100644 test/transforms/fixtures/delete-backward/inside-inline-sibling/index.js create mode 100644 test/transforms/fixtures/delete-backward/inside-inline-sibling/input.yaml create mode 100644 test/transforms/fixtures/delete-backward/inside-inline-sibling/output.yaml create mode 100644 test/transforms/fixtures/delete-forward-at-range/inside-inline-sibling/index.js create mode 100644 test/transforms/fixtures/delete-forward-at-range/inside-inline-sibling/input.yaml create mode 100644 test/transforms/fixtures/delete-forward-at-range/inside-inline-sibling/output.yaml create mode 100644 test/transforms/fixtures/delete-forward/inside-inline-sibling/index.js create mode 100644 test/transforms/fixtures/delete-forward/inside-inline-sibling/input.yaml create mode 100644 test/transforms/fixtures/delete-forward/inside-inline-sibling/output.yaml create mode 100644 test/transforms/fixtures/delete/inside-inline-sibling/index.js create mode 100644 test/transforms/fixtures/delete/inside-inline-sibling/input.yaml create mode 100644 test/transforms/fixtures/delete/inside-inline-sibling/output.yaml diff --git a/examples/links/index.js b/examples/links/index.js index 55ed701a1..cebdca0c2 100644 --- a/examples/links/index.js +++ b/examples/links/index.js @@ -58,6 +58,8 @@ class Links extends React.Component { onChange = (state) => { this.setState({ state }) + console.log(state.selection.toJS()) + console.log(state.document.toJS()) } /** diff --git a/lib/models/node.js b/lib/models/node.js index 3f6daf769..e7126ddba 100644 --- a/lib/models/node.js +++ b/lib/models/node.js @@ -909,6 +909,18 @@ const Node = { removals = removals.add(desc.key) } + return desc + }) + + // Remove any nodes marked for removal. + removals.forEach((key) => { + node = node.removeDescendant(key) + }) + + removals = removals.clear() + + // And, ensuring... + node = node.mapDescendants((desc) => { if (desc.kind == 'text') { let next = node.getNextSibling(desc) diff --git a/lib/models/state.js b/lib/models/state.js index fc5661470..28e7e4cc0 100644 --- a/lib/models/state.js +++ b/lib/models/state.js @@ -566,7 +566,10 @@ class State extends new Record(DEFAULTS) { // Determine what the selection will be after deleting. const { startText } = this const { startKey, startOffset, endKey, endOffset } = selection - const previous = document.getPreviousSibling(startText) + const block = document.getClosestBlock(startText) + const highest = block.getHighestChild(startText) + const previous = block.getPreviousSibling(highest) + const next = block.getNextSibling(highest) if ( previous && @@ -574,7 +577,16 @@ class State extends new Record(DEFAULTS) { (endKey != startKey || endOffset == startText.length) ) { if (previous.kind == 'text') { - after = selection.collapseToEndOf(previous) + if (next && next.kind == 'text') { + after = selection.merge({ + anchorKey: previous.key, + anchorOffset: previous.length, + focusKey: previous.key, + focusOffset: previous.length + }) + } else { + after = selection.collapseToEndOf(previous) + } } else { const last = previous.getTexts().last() after = selection.collapseToEndOf(last) @@ -631,13 +643,27 @@ class State extends new Record(DEFAULTS) { } else if (selection.isAtEndOf(startNode) && startNode.length == 1) { - const previous = document.getPreviousSibling(startNode) + const block = document.getClosestBlock(startKey) + const highest = block.getHighestChild(startKey) + const previous = block.getPreviousSibling(highest) + const next = block.getNextSibling(highest) - if (previous && previous.kind == 'text') { - after = selection.collapseToEndOf(previous) - } else if (previous) { - const last = previous.getTexts().last() - after = selection.collapseToEndOf(last) + if (previous) { + if (previous.kind == 'text') { + if (next && next.kind == 'text') { + after = selection.merge({ + anchorKey: previous.key, + anchorOffset: previous.length, + focusKey: previous.key, + focusOffset: previous.length + }) + } else { + after = selection.collapseToEndOf(previous) + } + } else { + const last = previous.getTexts().last() + after = selection.collapseToEndOf(last) + } } else { after = selection.moveBackward(n) } @@ -663,31 +689,41 @@ class State extends new Record(DEFAULTS) { deleteForward(n = 1) { let state = this - let { document, selection } = state + let { document, selection, startText } = state let { startKey, startOffset } = selection let after = selection // Determine what the selection should be after deleting. const block = document.getClosestBlock(startKey) const inline = document.getClosestInline(startKey) - const previous = document.getPreviousSibling(startKey) - const startText = document.getDescendant(startKey) + const highest = block.getHighestChild(startKey) + const previous = block.getPreviousSibling(highest) + const next = block.getNextSibling(highest) if (selection.isExpanded) { after = selection.collapseToStart() } else if ((block && block.isVoid) || (inline && inline.isVoid)) { - const next = document.getNextText(startKey) - const prev = document.getPreviousText(startKey) + const nextText = document.getNextText(startKey) + const prevText = document.getPreviousText(startKey) after = next - ? selection.collapseToStartOf(next) - : selection.collapseToEndOf(prev) + ? selection.collapseToStartOf(nextText) + : selection.collapseToEndOf(prevText) } else if (previous && startOffset == 0 && startText.length == 1) { if (previous.kind == 'text') { - after = selection.collapseToEndOf(previous) + if (next && next.kind == 'text') { + after = selection.merge({ + anchorKey: previous.key, + anchorOffset: previous.length, + focusKey: previous.key, + focusOffset: previous.length + }) + } else { + after = selection.collapseToEndOf(previous) + } } else { const last = previous.getTexts().last() after = selection.collapseToEndOf(last) diff --git a/test/transforms/fixtures/delete-at-range/inside-inline-sibling/index.js b/test/transforms/fixtures/delete-at-range/inside-inline-sibling/index.js new file mode 100644 index 000000000..9c06b19a3 --- /dev/null +++ b/test/transforms/fixtures/delete-at-range/inside-inline-sibling/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTexts() + const second = texts.get(1) + const range = selection.merge({ + anchorKey: second.key, + anchorOffset: second.length - 1, + focusKey: second.key, + focusOffset: second.length + }) + + return state + .transform() + .deleteAtRange(range) + .apply() +} diff --git a/test/transforms/fixtures/delete-at-range/inside-inline-sibling/input.yaml b/test/transforms/fixtures/delete-at-range/inside-inline-sibling/input.yaml new file mode 100644 index 000000000..d7a661508 --- /dev/null +++ b/test/transforms/fixtures/delete-at-range/inside-inline-sibling/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: a + - kind: text + text: two diff --git a/test/transforms/fixtures/delete-at-range/inside-inline-sibling/output.yaml b/test/transforms/fixtures/delete-at-range/inside-inline-sibling/output.yaml new file mode 100644 index 000000000..9ec6b7237 --- /dev/null +++ b/test/transforms/fixtures/delete-at-range/inside-inline-sibling/output.yaml @@ -0,0 +1,7 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: onetwo diff --git a/test/transforms/fixtures/delete-backward-at-range/inside-inline-sibling/index.js b/test/transforms/fixtures/delete-backward-at-range/inside-inline-sibling/index.js new file mode 100644 index 000000000..c96089f56 --- /dev/null +++ b/test/transforms/fixtures/delete-backward-at-range/inside-inline-sibling/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTexts() + const second = texts.get(1) + const range = selection.merge({ + anchorKey: second.key, + anchorOffset: second.length, + focusKey: second.key, + focusOffset: second.length + }) + + return state + .transform() + .deleteBackwardAtRange(range) + .apply() +} diff --git a/test/transforms/fixtures/delete-backward-at-range/inside-inline-sibling/input.yaml b/test/transforms/fixtures/delete-backward-at-range/inside-inline-sibling/input.yaml new file mode 100644 index 000000000..d7a661508 --- /dev/null +++ b/test/transforms/fixtures/delete-backward-at-range/inside-inline-sibling/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: a + - kind: text + text: two diff --git a/test/transforms/fixtures/delete-backward-at-range/inside-inline-sibling/output.yaml b/test/transforms/fixtures/delete-backward-at-range/inside-inline-sibling/output.yaml new file mode 100644 index 000000000..9ec6b7237 --- /dev/null +++ b/test/transforms/fixtures/delete-backward-at-range/inside-inline-sibling/output.yaml @@ -0,0 +1,7 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: onetwo diff --git a/test/transforms/fixtures/delete-backward/inside-inline-sibling/index.js b/test/transforms/fixtures/delete-backward/inside-inline-sibling/index.js new file mode 100644 index 000000000..197c0343c --- /dev/null +++ b/test/transforms/fixtures/delete-backward/inside-inline-sibling/index.js @@ -0,0 +1,30 @@ + +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 range = selection.merge({ + anchorKey: second.key, + anchorOffset: second.length, + focusKey: second.key, + focusOffset: second.length + }) + + const next = state + .transform() + .moveTo(range) + .deleteBackward() + .apply() + + const updated = next.document.getTexts().first() + + assert.deepEqual( + next.selection.toJS(), + range.collapseToStartOf(updated).moveForward(first.length).toJS() + ) + + return next +} diff --git a/test/transforms/fixtures/delete-backward/inside-inline-sibling/input.yaml b/test/transforms/fixtures/delete-backward/inside-inline-sibling/input.yaml new file mode 100644 index 000000000..d7a661508 --- /dev/null +++ b/test/transforms/fixtures/delete-backward/inside-inline-sibling/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: a + - kind: text + text: two diff --git a/test/transforms/fixtures/delete-backward/inside-inline-sibling/output.yaml b/test/transforms/fixtures/delete-backward/inside-inline-sibling/output.yaml new file mode 100644 index 000000000..9ec6b7237 --- /dev/null +++ b/test/transforms/fixtures/delete-backward/inside-inline-sibling/output.yaml @@ -0,0 +1,7 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: onetwo diff --git a/test/transforms/fixtures/delete-forward-at-range/inside-inline-sibling/index.js b/test/transforms/fixtures/delete-forward-at-range/inside-inline-sibling/index.js new file mode 100644 index 000000000..337f6b589 --- /dev/null +++ b/test/transforms/fixtures/delete-forward-at-range/inside-inline-sibling/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTexts() + const second = texts.get(1) + const range = selection.merge({ + anchorKey: second.key, + anchorOffset: 0, + focusKey: second.key, + focusOffset: 0 + }) + + return state + .transform() + .deleteForwardAtRange(range) + .apply() +} diff --git a/test/transforms/fixtures/delete-forward-at-range/inside-inline-sibling/input.yaml b/test/transforms/fixtures/delete-forward-at-range/inside-inline-sibling/input.yaml new file mode 100644 index 000000000..d7a661508 --- /dev/null +++ b/test/transforms/fixtures/delete-forward-at-range/inside-inline-sibling/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: a + - kind: text + text: two diff --git a/test/transforms/fixtures/delete-forward-at-range/inside-inline-sibling/output.yaml b/test/transforms/fixtures/delete-forward-at-range/inside-inline-sibling/output.yaml new file mode 100644 index 000000000..9ec6b7237 --- /dev/null +++ b/test/transforms/fixtures/delete-forward-at-range/inside-inline-sibling/output.yaml @@ -0,0 +1,7 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: onetwo diff --git a/test/transforms/fixtures/delete-forward/inside-inline-sibling/index.js b/test/transforms/fixtures/delete-forward/inside-inline-sibling/index.js new file mode 100644 index 000000000..ab3b91504 --- /dev/null +++ b/test/transforms/fixtures/delete-forward/inside-inline-sibling/index.js @@ -0,0 +1,30 @@ + +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 range = selection.merge({ + anchorKey: second.key, + anchorOffset: 0, + focusKey: second.key, + focusOffset: 0 + }) + + const next = state + .transform() + .moveTo(range) + .deleteForward() + .apply() + + const updated = next.document.getTexts().first() + + assert.deepEqual( + next.selection.toJS(), + range.collapseToStartOf(updated).moveForward(first.length).toJS() + ) + + return next +} diff --git a/test/transforms/fixtures/delete-forward/inside-inline-sibling/input.yaml b/test/transforms/fixtures/delete-forward/inside-inline-sibling/input.yaml new file mode 100644 index 000000000..d7a661508 --- /dev/null +++ b/test/transforms/fixtures/delete-forward/inside-inline-sibling/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: a + - kind: text + text: two diff --git a/test/transforms/fixtures/delete-forward/inside-inline-sibling/output.yaml b/test/transforms/fixtures/delete-forward/inside-inline-sibling/output.yaml new file mode 100644 index 000000000..9ec6b7237 --- /dev/null +++ b/test/transforms/fixtures/delete-forward/inside-inline-sibling/output.yaml @@ -0,0 +1,7 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: onetwo diff --git a/test/transforms/fixtures/delete/inside-inline-sibling/index.js b/test/transforms/fixtures/delete/inside-inline-sibling/index.js new file mode 100644 index 000000000..6ceb701b0 --- /dev/null +++ b/test/transforms/fixtures/delete/inside-inline-sibling/index.js @@ -0,0 +1,35 @@ + +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 range = selection.merge({ + anchorKey: second.key, + anchorOffset: second.length - 1, + focusKey: second.key, + focusOffset: second.length + }) + + const next = state + .transform() + .moveTo(range) + .delete() + .apply() + + const updated = next.document.getTexts().first() + + assert.deepEqual( + next.selection.toJS(), + range.merge({ + anchorKey: updated.key, + anchorOffset: first.length, + focusKey: updated.key, + focusOffset: first.length + }).toJS() + ) + + return next +} diff --git a/test/transforms/fixtures/delete/inside-inline-sibling/input.yaml b/test/transforms/fixtures/delete/inside-inline-sibling/input.yaml new file mode 100644 index 000000000..d7a661508 --- /dev/null +++ b/test/transforms/fixtures/delete/inside-inline-sibling/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: a + - kind: text + text: two diff --git a/test/transforms/fixtures/delete/inside-inline-sibling/output.yaml b/test/transforms/fixtures/delete/inside-inline-sibling/output.yaml new file mode 100644 index 000000000..9ec6b7237 --- /dev/null +++ b/test/transforms/fixtures/delete/inside-inline-sibling/output.yaml @@ -0,0 +1,7 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: onetwo