diff --git a/src/models/block.js b/src/models/block.js index f86cfcbfb..e22fc5aa9 100644 --- a/src/models/block.js +++ b/src/models/block.js @@ -53,6 +53,10 @@ class Block extends new Record(DEFAULTS) { properties.isVoid = !!properties.isVoid properties.nodes = Block.createList(properties.nodes) + if (properties.nodes.size == 0) { + properties.nodes = properties.nodes.push(Text.create()) + } + return new Block(properties) } diff --git a/src/models/inline.js b/src/models/inline.js index cc703781a..4ffa22ed1 100644 --- a/src/models/inline.js +++ b/src/models/inline.js @@ -53,6 +53,10 @@ class Inline extends new Record(DEFAULTS) { properties.isVoid = !!properties.isVoid properties.nodes = Inline.createList(properties.nodes) + if (properties.nodes.size == 0) { + properties.nodes = properties.nodes.push(Text.create()) + } + return new Inline(properties) } diff --git a/src/models/node.js b/src/models/node.js index f7ca75c3f..496d7093a 100644 --- a/src/models/node.js +++ b/src/models/node.js @@ -942,6 +942,24 @@ const Node = { */ insertNode(index, node) { + let keys = new Set([ this.key ]) + + this.findDescendant((desc) => { + keys = keys.add(desc.key) + }) + + if (keys.contains(node.key)) { + node = node.regenerateKey() + } + + if (node.kind != 'text') { + node = node.mapDescendants((desc) => { + return keys.contains(desc.key) + ? desc.regenerateKey() + : desc + }) + } + const nodes = this.nodes.splice(index, 0, node) return this.merge({ nodes }) }, @@ -1040,6 +1058,16 @@ const Node = { return this.merge({ nodes }) }, + /** + * Regenerate the node's key. + * + * @return {Node} node + */ + + regenerateKey() { + return this.merge({ key: uid() }) + }, + /** * Remove a `node` from the children node map. * diff --git a/src/models/text.js b/src/models/text.js index 738ecd35f..ae9592a15 100644 --- a/src/models/text.js +++ b/src/models/text.js @@ -34,7 +34,7 @@ class Text extends new Record(DEFAULTS) { * Create a new `Text` with `properties`. * * @param {Object} properties - * @return {Text} text + * @return {Text} */ static create(properties = {}) { @@ -223,7 +223,7 @@ class Text extends new Record(DEFAULTS) { * @param {Numbder} index * @param {String} text * @param {String} marks (optional) - * @return {Text} text + * @return {Text} */ insertText(index, text, marks) { @@ -238,6 +238,16 @@ class Text extends new Record(DEFAULTS) { return this.merge({ characters }) } + /** + * Regenerate the node's key. + * + * @return {Text} + */ + + regenerateKey() { + return this.merge({ key: uid() }) + } + /** * Remove a `mark` at `index` and `length`. * @@ -265,7 +275,7 @@ class Text extends new Record(DEFAULTS) { * * @param {Number} index * @param {Number} length - * @return {Text} text + * @return {Text} */ removeText(index, length) { diff --git a/src/transforms/apply-operation.js b/src/transforms/apply-operation.js index 9a9fd1f86..5cc8dd70d 100644 --- a/src/transforms/apply-operation.js +++ b/src/transforms/apply-operation.js @@ -90,8 +90,7 @@ function insertNode(state, operation) { let { document } = state let parent = document.assertPath(path) const isParent = document == parent - const nodes = parent.nodes.splice(index, 0, node) - parent = parent.merge({ nodes }) + parent = parent.insertNode(index, node) document = isParent ? parent : document.updateDescendant(parent) state = state.merge({ document }) return state @@ -212,7 +211,6 @@ function removeText(state, operation) { let node = document.assertPath(path) node = node.removeText(offset, length) document = document.updateDescendant(node) - document = document.normalize() state = state.merge({ document }) return state } @@ -249,7 +247,6 @@ function setNode(state, operation) { let node = document.assertPath(path) node = node.merge(properties) document = document.updateDescendant(node) - document = document.normalize() state = state.merge({ document }) return state } diff --git a/src/transforms/at-current-range.js b/src/transforms/at-current-range.js index 767247091..e7b78b43a 100644 --- a/src/transforms/at-current-range.js +++ b/src/transforms/at-current-range.js @@ -436,18 +436,38 @@ export function splitBlock(transform, depth = 1) { export function splitInline(transform, depth = Infinity) { let { state } = transform let { document, selection } = state + + // If the selection is expanded, remove it first. + if (selection.isExpanded) { + transform.delete() + state = transform.state + document = state.document + selection = state.selection + } + let after = selection + const { startKey, startOffset } = selection + let startNode = document.assertDescendant(startKey) + const furthestInline = document.getFurthestInline(startKey) + const offset = furthestInline.getOffset(startNode) + + // If the selection is at the start of end of the furthest inline, there isn't + // anything to split, so abort. + if ( + (offset + startOffset == 0) || + (offset + startNode.length == startOffset) + ) { + return transform + } transform.unsetSelection() transform.splitInlineAtRange(selection, depth) state = transform.state document = state.document + const closestInline = document.getClosestInline(startKey) - const { startKey } = selection - const inlineParent = document.getClosestInline(startKey) - - if (inlineParent) { - const startNode = document.getDescendant(startKey) + if (closestInline) { + startNode = document.getDescendant(startKey) const nextNode = document.getNextText(startNode) after = selection.collapseToStartOf(nextNode) } diff --git a/src/transforms/at-range.js b/src/transforms/at-range.js index 2816fe9ae..22860d61e 100644 --- a/src/transforms/at-range.js +++ b/src/transforms/at-range.js @@ -95,6 +95,7 @@ export function deleteAtRange(transform, range) { const lonely = document.getFurthest(endBlock, p => p.nodes.size == 1) || endBlock transform.removeNodeByKey(lonely.key) transform.normalize() + return transform } @@ -270,6 +271,7 @@ export function insertBlockAtRange(transform, range, block) { } transform.normalize() + return transform } @@ -320,7 +322,9 @@ export function insertFragmentAtRange(transform, range, fragment) { }) } - transform.splitNodeByKey(startChild.key, offset) + if (startOffset != 0) { + transform.splitNodeByKey(startChild.key, offset) + } state = transform.state document = state.document @@ -347,12 +351,14 @@ export function insertFragmentAtRange(transform, range, fragment) { const inlineIndex = startBlock.nodes.indexOf(inlineChild) firstBlock.nodes.forEach((inline, i) => { - const newIndex = inlineIndex + i + 1 + const o = startOffset == 0 ? 0 : 1 + const newIndex = inlineIndex + i + o transform.insertNodeByKey(startBlock.key, newIndex, inline) }) } transform.normalize() + return transform } @@ -387,6 +393,7 @@ export function insertInlineAtRange(transform, range, inline) { transform.splitNodeByKey(startKey, startOffset) transform.insertNodeByKey(parent.key, index + 1, inline) transform.normalize() + return transform } @@ -524,6 +531,7 @@ export function splitBlockAtRange(transform, range, height = 1) { transform.splitNodeByKey(node.key, offset) transform.normalize() + return transform } @@ -673,6 +681,7 @@ export function unwrapBlockAtRange(transform, range, properties) { }) transform.normalize() + return transform } @@ -715,6 +724,7 @@ export function unwrapInlineAtRange(transform, range, properties) { }) transform.normalize() + return transform } @@ -729,6 +739,7 @@ export function unwrapInlineAtRange(transform, range, properties) { export function wrapBlockAtRange(transform, range, block) { block = Normalize.block(block) + block = block.merge({ nodes: block.nodes.clear() }) const { state } = transform const { document } = state @@ -799,6 +810,7 @@ export function wrapInlineAtRange(transform, range, inline) { if (range.isCollapsed) return transform inline = Normalize.inline(inline) + inline = inline.merge({ nodes: inline.nodes.clear() }) const { startKey, startOffset, endKey, endOffset } = range let { state } = transform @@ -820,15 +832,25 @@ export function wrapInlineAtRange(transform, range, inline) { : endChild.getOffset(endKey) + endOffset if (startBlock == endBlock) { - transform.splitNodeByKey(endChild.key, endOff) - transform.splitNodeByKey(startChild.key, startOff) + if (endOff != endChild.length) { + transform.splitNodeByKey(endChild.key, endOff) + } + + if (startOff != 0) { + transform.splitNodeByKey(startChild.key, startOff) + } state = transform.state document = state.document startBlock = document.getClosestBlock(startKey) startChild = startBlock.getHighestChild(startKey) - const startInner = document.getNextSibling(startChild) + + const startInner = startOff == 0 + ? startChild + : document.getNextSibling(startChild) + const startInnerIndex = startBlock.nodes.indexOf(startInner) + const endInner = startKey == endKey ? startInner : startBlock.getHighestChild(endKey) const inlines = startBlock.nodes .skipUntil(n => n == startInner) @@ -880,6 +902,7 @@ export function wrapInlineAtRange(transform, range, inline) { } transform.normalize() + return transform } diff --git a/src/transforms/by-key.js b/src/transforms/by-key.js index 1ac192e6d..366563cc9 100644 --- a/src/transforms/by-key.js +++ b/src/transforms/by-key.js @@ -37,7 +37,42 @@ export function insertNodeByKey(transform, key, index, node) { const { document } = state const path = document.getPath(key) const newPath = path.slice().push(index) - return transform.insertNodeOperation(path, index, node) + + transform.insertNodeOperation(path, index, node) + + // If the node is an inline void, the parent is a block, and the node will be + // inserted at the block's edge, we need to add surrounding text nodes. + if (node.kind == 'inline' && node.isVoid) { + const parent = document.assertDescendant(key) + + if (index == 0) { + const text = Text.create() + transform.insertNodeByKey(key, index, text) + } + + if (index == parent.nodes.size) { + const text = Text.create() + transform.insertNodeByKey(key, index + 1, text) + } + } + + // If the node is a text node, and it is insert next to a text node, it should + // be joined with it. + if (node.kind == 'text') { + const parent = document.assertDescendant(key) + const previous = index == 0 ? null : parent.nodes.get(index - 1) + const next = parent.nodes.get(index) + + if (next && next.kind == 'text') { + transform.joinNodeByKey(next.key, node.key) + } + + if (previous && previous.kind == 'text') { + transform.joinNodeByKey(node.key, previous.key) + } + } + + return transform } /** @@ -58,6 +93,23 @@ export function insertTextByKey(transform, key, offset, text, marks) { return transform.insertTextOperation(path, offset, text, marks) } +/** + * Join a node by `key` with a node `withKey`. + * + * @param {Transform} transform + * @param {String} key + * @param {String} withKey + * @return {Transform} + */ + +export function joinNodeByKey(transform, key, withKey) { + const { state } = transform + const { document } = state + const path = document.getPath(key) + const withPath = document.getPath(withKey) + return transform.joinNodeOperation(path, withPath) +} + /** * Move a node by `key` to a new parent by `key` and `index`. * @@ -71,9 +123,39 @@ export function insertTextByKey(transform, key, offset, text, marks) { export function moveNodeByKey(transform, key, newKey, newIndex) { const { state } = transform const { document } = state + const node = document.assertDescendant(key) + const prevParent = document.getParent(key) const path = document.getPath(key) const newPath = document.getPath(newKey) - return transform.moveNodeOperation(path, newPath, newIndex) + const parent = document.key == newKey ? document : document.assertDescendant(newKey) + const previous = newIndex == 0 ? null : parent.nodes.get(newIndex - 1) + const next = parent.nodes.get(newIndex) + transform.moveNodeOperation(path, newPath, newIndex) + + // If the node to move is a text node, and it will be moved adjacent to + // another text node, join them together. + if (node.kind == 'text') { + if (next && next.kind == 'text') { + transform.joinNodeByKey(next.key, node.key) + } + + if (previous && previous.kind == 'text') { + transform.joinNodeByKey(node.key, previous.key) + } + } + + // If the node to be moved is the last child of its parent, then create a new + // empty text node in its place. + if (prevParent.nodes.size == 1) { + if (prevParent.kind == 'block') { + const text = Text.create() + transform.insertNodeByKey(prevParent.key, 0, text) + } else { + transform.removeNodeByKey(prevParent.key) + } + } + + return transform } /** @@ -105,21 +187,29 @@ export function removeMarkByKey(transform, key, offset, length, mark) { export function removeNodeByKey(transform, key) { const { state } = transform - const { document } = state + let { document } = state const node = document.assertDescendant(key) - const parent = document.getParent(key) - const index = parent.nodes.indexOf(node) const path = document.getPath(key) + const parent = document.getParent(key) + const previous = document.getPreviousSibling(key) + const next = document.getNextSibling(key) transform.removeNodeOperation(path) - // If the node isn't a text node, or it isn't the last node in its parent, - // then we have nothing else to do. - if (node.kind != 'text' || parent.nodes.size > 1) return transform + // If there are no more remaining nodes in the parent, re-add an empty text + // node so that we guarantee to always have text nodes as the tree's leaves. + if (parent.nodes.size == 1) { + const text = Text.create() + transform.insertNodeByKey(parent.key, 0, text) + } + + // If the previous and next siblings are both text nodes, join them. + if ( + (previous && previous.kind == 'text') && + (next && next.kind == 'text') + ) { + transform.joinNodeByKey(next.key, previous.key) + } - // Otherwise, re-add an empty text node into the parent so that we guarantee - // to always have text nodes as the leaves of the node tree. - const text = Text.create() - transform.insertNodeByKey(parent.key, index, text) return transform } @@ -135,9 +225,41 @@ export function removeNodeByKey(transform, key) { export function removeTextByKey(transform, key, offset, length) { const { state } = transform - const { document } = state + let { document } = state const path = document.getPath(key) - return transform.removeTextOperation(path, offset, length) + transform.removeTextOperation(path, offset, length) + + // If the text node is now empty, we might need to remove more nodes. + document = transform.state.document + const node = document.getDescendant(key) + const parent = document.getParent(key) + const previous = document.getPreviousSibling(key) + const next = document.getNextSibling(key) + + // If the text node isn't empty, don't do anything more. + if (node.text != '') { + return transform + } + + // If the empty text node is the only node remaining in a non-void inline, + // remove the inline completely. + if ( + parent.kind == 'inline' && + parent.isVoid == false && + parent.nodes.size == 1 + ) { + transform.removeNodeByKey(parent.key) + } + + // Otherwise, if the text node is not needed in the tree any more, remove it. + else if ( + (previous && previous.isVoid == false) || + (next && next.isVoid == false) + ) { + transform.removeNodeByKey(key) + } + + return transform } /** @@ -174,8 +296,58 @@ export function setNodeByKey(transform, key, properties) { const { state } = transform const { document } = state const node = document.assertDescendant(key) + const parent = document.getParent(key) + const index = parent.nodes.indexOf(node) const path = document.getPath(key) - return transform.setNodeOperation(path, properties) + const previous = document.getPreviousSibling(key) + const next = document.getNextSibling(key) + transform.setNodeOperation(path, properties) + + // If the `isVoid` property is being changed to true, remove all of the node's + // children, and add additional text nodes around it if necessary. + if (properties.isVoid == true && node.isVoid == false) { + node.nodes.forEach((child) => { + transform.removeNodeByKey(child.key) + }) + + if (node.kind == 'inline') { + if (!next) { + const text = Text.create() + transform.insertNodeByKey(parent.key, index + 1, text) + } + + if (!previous) { + const text = Text.create() + transform.insertNodeByKey(parent.key, index, text) + } + } + } + + // If the `isVoid` property is being changed to `false` and the node is an + // inline node, remove any additional unnecessary text around it. + if ( + properties.isVoid == false && + node.isVoid == true && + node.kind == 'inline' + ) { + if ( + previous && + previous.kind == 'text' && + previous.text == '' + ) { + transform.removeNodeByKey(previous.key) + } + + if ( + next && + next.kind == 'text' && + next.text == '' + ) { + transform.removeNodeByKey(next.key) + } + } + + return transform } /** @@ -188,10 +360,92 @@ export function setNodeByKey(transform, key, properties) { */ export function splitNodeByKey(transform, key, offset) { - const { state } = transform - const { document } = state + let { state } = transform + let { document } = state const path = document.getPath(key) - return transform.splitNodeOperation(path, offset) + transform.splitNodeOperation(path, offset) + + // Traverse the nodes on both sides of the split, ensuring that there are no + // empty inline nodes, or empty text nodes that should be removed. + state = transform.state + document = state.document + const parent = document.getParent(key) + + // Define an iterator that will apply normalization transforms. + parent.filterDescendants((d) => { + + // We don't need to do any normalization for block nodes. + if (d.kind == 'block') { + return + } + + // If an inline void node has no text, add a space character. + if ( + d.kind == 'inline' && + d.text == '' && + d.isVoid == true + ) { + transform.insertTextByKey(d.key, 0, ' ') + } + + // If an non-void inline node has no text now, remove it. + if ( + d.kind == 'inline' && + d.text == '' && + d.isVoid == false + ) { + transform.removeNodeByKey(d.key) + } + + // Check to ensure that extra empty text nodes are preserved around inline + // void nodes. + if ( + d.kind == 'inline' && + d.isVoid == true + ) { + const previous = document.getPreviousSibling(d) + const next = document.getNextSibling(d) + + if ( + (!previous) || + (previous.kind == 'block' || previous.kind == 'inline' && previous.isVoid) + ) { + const p = document.getParent(d) + const index = p.nodes.indexOf(d) + const text = Text.create() + transform.insertNodeByKey(p, index, text) + } + + if ( + (!next) || + (next.kind == 'block' || next.kind == 'inline' && next.isVoid) + ) { + const p = document.getParent(d) + const index = p.nodes.indexOf(d) + const text = Text.create() + transform.insertNodeByKey(p, index + 1, text) + } + } + + // If an empty text node is adjacent to an non-void inline node, remove it. + if ( + d.kind == 'text' && + d.text == '' + ) { + const previous = document.getPreviousSibling(d) + const next = document.getNextSibling(d) + + if ( + (previous && previous.kind == 'inline' && previous.isVoid == false) || + (next && next.kind == 'inline' && next.isVoid == false) + ) { + transform.removeNodeByKey(d.key) + } + } + }) + + // Return the transform. + return transform } /** diff --git a/src/transforms/index.js b/src/transforms/index.js index 7fc192338..38df2acc4 100644 --- a/src/transforms/index.js +++ b/src/transforms/index.js @@ -15,6 +15,7 @@ import { addMarkOperation, insertNodeOperation, insertTextOperation, + joinNodeOperation, moveNodeOperation, removeMarkOperation, removeNodeOperation, @@ -85,6 +86,7 @@ import { addMarkByKey, insertNodeByKey, insertTextByKey, + joinNodeByKey, moveNodeByKey, removeMarkByKey, removeNodeByKey, @@ -169,6 +171,7 @@ export default { addMarkOperation, insertNodeOperation, insertTextOperation, + joinNodeOperation, moveNodeOperation, removeMarkOperation, removeNodeOperation, @@ -233,6 +236,7 @@ export default { addMarkByKey, insertNodeByKey, insertTextByKey, + joinNodeByKey, moveNodeByKey, removeMarkByKey, removeNodeByKey, diff --git a/src/transforms/normalize.js b/src/transforms/normalize.js index 12169ea52..2bb6f786c 100644 --- a/src/transforms/normalize.js +++ b/src/transforms/normalize.js @@ -1,4 +1,3 @@ - import Schema from '../models/schema' import { default as defaultSchema } from '../plugins/schema' diff --git a/test/transforms/fixtures/at-current-range/split-inline/block-end/index.js b/test/transforms/fixtures/at-current-range/split-inline/block-end/index.js index 572bc022d..13594b6a4 100644 --- a/test/transforms/fixtures/at-current-range/split-inline/block-end/index.js +++ b/test/transforms/fixtures/at-current-range/split-inline/block-end/index.js @@ -22,7 +22,7 @@ export default function (state) { assert.deepEqual( next.selection.toJS(), - range.collapseToStartOf(updated).toJS() + range.collapseToEndOf(updated).toJS() ) return next diff --git a/test/transforms/fixtures/at-current-range/split-inline/block-end/output.yaml b/test/transforms/fixtures/at-current-range/split-inline/block-end/output.yaml index a236cf96f..f752cee89 100644 --- a/test/transforms/fixtures/at-current-range/split-inline/block-end/output.yaml +++ b/test/transforms/fixtures/at-current-range/split-inline/block-end/output.yaml @@ -8,8 +8,3 @@ nodes: nodes: - kind: text text: word - - kind: inline - type: link - nodes: - - kind: text - text: "" diff --git a/test/transforms/fixtures/at-current-range/split-inline/block-start/output.yaml b/test/transforms/fixtures/at-current-range/split-inline/block-start/output.yaml index e67e780fb..f752cee89 100644 --- a/test/transforms/fixtures/at-current-range/split-inline/block-start/output.yaml +++ b/test/transforms/fixtures/at-current-range/split-inline/block-start/output.yaml @@ -3,11 +3,6 @@ nodes: - kind: block type: paragraph nodes: - - kind: inline - type: link - nodes: - - kind: text - text: "" - kind: inline type: link nodes: diff --git a/test/transforms/fixtures/at-range/split-inline-at-range/block-end/output.yaml b/test/transforms/fixtures/at-range/split-inline-at-range/block-end/output.yaml index a236cf96f..f752cee89 100644 --- a/test/transforms/fixtures/at-range/split-inline-at-range/block-end/output.yaml +++ b/test/transforms/fixtures/at-range/split-inline-at-range/block-end/output.yaml @@ -8,8 +8,3 @@ nodes: nodes: - kind: text text: word - - kind: inline - type: link - nodes: - - kind: text - text: "" diff --git a/test/transforms/fixtures/at-range/split-inline-at-range/block-start/output.yaml b/test/transforms/fixtures/at-range/split-inline-at-range/block-start/output.yaml index e67e780fb..f752cee89 100644 --- a/test/transforms/fixtures/at-range/split-inline-at-range/block-start/output.yaml +++ b/test/transforms/fixtures/at-range/split-inline-at-range/block-start/output.yaml @@ -3,11 +3,6 @@ nodes: - kind: block type: paragraph nodes: - - kind: inline - type: link - nodes: - - kind: text - text: "" - kind: inline type: link nodes: diff --git a/test/transforms/fixtures/by-key/insert-node-by-key/duplicate/index.js b/test/transforms/fixtures/by-key/insert-node-by-key/duplicate/index.js new file mode 100644 index 000000000..1abd5e32b --- /dev/null +++ b/test/transforms/fixtures/by-key/insert-node-by-key/duplicate/index.js @@ -0,0 +1,22 @@ + +import assert from 'assert' +import { Block } from '../../../../../..' + +export default function (state) { + const { document, selection } = state + const first = document.getBlocks().first() + + debugger + const next = state + .transform() + .insertNodeByKey(document.key, 0, first) + .apply() + + const one = next.document.getBlocks().first() + const two = next.document.getBlocks().last() + + assert.equal(one.type, two.type) + assert.notEqual(one.key, two.key) + + return next +} diff --git a/test/transforms/fixtures/by-key/insert-node-by-key/duplicate/input.yaml b/test/transforms/fixtures/by-key/insert-node-by-key/duplicate/input.yaml new file mode 100644 index 000000000..3c2724d28 --- /dev/null +++ b/test/transforms/fixtures/by-key/insert-node-by-key/duplicate/input.yaml @@ -0,0 +1,7 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one diff --git a/test/transforms/fixtures/by-key/insert-node-by-key/duplicate/output.yaml b/test/transforms/fixtures/by-key/insert-node-by-key/duplicate/output.yaml new file mode 100644 index 000000000..65b99a54a --- /dev/null +++ b/test/transforms/fixtures/by-key/insert-node-by-key/duplicate/output.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: block + type: paragraph + nodes: + - kind: text + text: one diff --git a/test/transforms/fixtures/by-key/remove-node-by-key/block/index.js b/test/transforms/fixtures/by-key/move-node-by-key/block/index.js similarity index 78% rename from test/transforms/fixtures/by-key/remove-node-by-key/block/index.js rename to test/transforms/fixtures/by-key/move-node-by-key/block/index.js index 92ee63209..59bfe59e5 100644 --- a/test/transforms/fixtures/by-key/remove-node-by-key/block/index.js +++ b/test/transforms/fixtures/by-key/move-node-by-key/block/index.js @@ -5,6 +5,6 @@ export default function (state) { return state .transform() - .removeNodeByKey(first.key) + .moveNodeByKey(first.key, document.key, 1) .apply() } diff --git a/test/transforms/fixtures/by-key/remove-node-by-key/block/input.yaml b/test/transforms/fixtures/by-key/move-node-by-key/block/input.yaml similarity index 77% rename from test/transforms/fixtures/by-key/remove-node-by-key/block/input.yaml rename to test/transforms/fixtures/by-key/move-node-by-key/block/input.yaml index 881deb1d9..ee05966e8 100644 --- a/test/transforms/fixtures/by-key/remove-node-by-key/block/input.yaml +++ b/test/transforms/fixtures/by-key/move-node-by-key/block/input.yaml @@ -4,9 +4,9 @@ nodes: type: paragraph nodes: - kind: text - text: word + text: one - kind: block type: paragraph nodes: - kind: text - text: another + text: two diff --git a/test/transforms/fixtures/by-key/move-node-by-key/block/output.yaml b/test/transforms/fixtures/by-key/move-node-by-key/block/output.yaml new file mode 100644 index 000000000..0be0b6c01 --- /dev/null +++ b/test/transforms/fixtures/by-key/move-node-by-key/block/output.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: two + - kind: block + type: paragraph + nodes: + - kind: text + text: one diff --git a/test/transforms/fixtures/by-key/remove-node-by-key/inline/index.js b/test/transforms/fixtures/by-key/move-node-by-key/inline/index.js similarity index 65% rename from test/transforms/fixtures/by-key/remove-node-by-key/inline/index.js rename to test/transforms/fixtures/by-key/move-node-by-key/inline/index.js index 1a88ca23a..5227152ee 100644 --- a/test/transforms/fixtures/by-key/remove-node-by-key/inline/index.js +++ b/test/transforms/fixtures/by-key/move-node-by-key/inline/index.js @@ -1,10 +1,11 @@ export default function (state) { const { document, selection } = state + const block = document.getBlocks().first() const first = document.getInlines().first() return state .transform() - .removeNodeByKey(first.key) + .moveNodeByKey(first.key, block.key, 1) .apply() } diff --git a/test/transforms/fixtures/by-key/remove-node-by-key/inline/input.yaml b/test/transforms/fixtures/by-key/move-node-by-key/inline/input.yaml similarity index 81% rename from test/transforms/fixtures/by-key/remove-node-by-key/inline/input.yaml rename to test/transforms/fixtures/by-key/move-node-by-key/inline/input.yaml index c17ccbcf2..496aeb02a 100644 --- a/test/transforms/fixtures/by-key/remove-node-by-key/inline/input.yaml +++ b/test/transforms/fixtures/by-key/move-node-by-key/inline/input.yaml @@ -7,9 +7,9 @@ nodes: type: link nodes: - kind: text - text: word + text: one - kind: inline type: link nodes: - kind: text - text: another + text: two diff --git a/test/transforms/fixtures/by-key/move-node-by-key/inline/output.yaml b/test/transforms/fixtures/by-key/move-node-by-key/inline/output.yaml new file mode 100644 index 000000000..6f46511d3 --- /dev/null +++ b/test/transforms/fixtures/by-key/move-node-by-key/inline/output.yaml @@ -0,0 +1,15 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + text: two + - kind: inline + type: link + nodes: + - kind: text + text: one diff --git a/test/transforms/fixtures/by-key/move-node-by-key/text/index.js b/test/transforms/fixtures/by-key/move-node-by-key/text/index.js new file mode 100644 index 000000000..c08e67853 --- /dev/null +++ b/test/transforms/fixtures/by-key/move-node-by-key/text/index.js @@ -0,0 +1,11 @@ + +export default function (state) { + const { document, selection } = state + const text = document.getTexts().last() + const block = document.getBlocks().first() + + return state + .transform() + .moveNodeByKey(text.key, block.key, 1) + .apply() +} diff --git a/test/transforms/fixtures/by-key/move-node-by-key/text/input.yaml b/test/transforms/fixtures/by-key/move-node-by-key/text/input.yaml new file mode 100644 index 000000000..ee05966e8 --- /dev/null +++ b/test/transforms/fixtures/by-key/move-node-by-key/text/input.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: block + type: paragraph + nodes: + - kind: text + text: two diff --git a/test/transforms/fixtures/by-key/move-node-by-key/text/output.yaml b/test/transforms/fixtures/by-key/move-node-by-key/text/output.yaml new file mode 100644 index 000000000..a4cad435b --- /dev/null +++ b/test/transforms/fixtures/by-key/move-node-by-key/text/output.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: onetwo + - kind: block + type: paragraph + nodes: + - kind: text + text: "" diff --git a/test/transforms/fixtures/by-key/remove-text-by-key/adjacent-non-void-inlines/index.js b/test/transforms/fixtures/by-key/remove-text-by-key/adjacent-non-void-inlines/index.js new file mode 100644 index 000000000..c5deeb445 --- /dev/null +++ b/test/transforms/fixtures/by-key/remove-text-by-key/adjacent-non-void-inlines/index.js @@ -0,0 +1,10 @@ + +export default function (state) { + const { document, selection } = state + const second = document.getTexts().get(1) + + return state + .transform() + .removeTextByKey(second.key, 0, 1) + .apply() +} diff --git a/test/transforms/fixtures/by-key/remove-text-by-key/adjacent-non-void-inlines/input.yaml b/test/transforms/fixtures/by-key/remove-text-by-key/adjacent-non-void-inlines/input.yaml new file mode 100644 index 000000000..a78b31f17 --- /dev/null +++ b/test/transforms/fixtures/by-key/remove-text-by-key/adjacent-non-void-inlines/input.yaml @@ -0,0 +1,17 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + text: one + - kind: text + text: a + - kind: inline + type: link + nodes: + - kind: text + text: two diff --git a/test/transforms/fixtures/by-key/remove-text-by-key/adjacent-non-void-inlines/output.yaml b/test/transforms/fixtures/by-key/remove-text-by-key/adjacent-non-void-inlines/output.yaml new file mode 100644 index 000000000..496aeb02a --- /dev/null +++ b/test/transforms/fixtures/by-key/remove-text-by-key/adjacent-non-void-inlines/output.yaml @@ -0,0 +1,15 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: two diff --git a/test/transforms/fixtures/by-key/remove-node-by-key/text/index.js b/test/transforms/fixtures/by-key/remove-text-by-key/inline-last-character/index.js similarity index 81% rename from test/transforms/fixtures/by-key/remove-node-by-key/text/index.js rename to test/transforms/fixtures/by-key/remove-text-by-key/inline-last-character/index.js index 809d05121..955d83d1b 100644 --- a/test/transforms/fixtures/by-key/remove-node-by-key/text/index.js +++ b/test/transforms/fixtures/by-key/remove-text-by-key/inline-last-character/index.js @@ -5,6 +5,6 @@ export default function (state) { return state .transform() - .removeNodeByKey(first.key) + .removeTextByKey(first.key, 0, 1) .apply() } diff --git a/test/transforms/fixtures/by-key/remove-text-by-key/inline-last-character/input.yaml b/test/transforms/fixtures/by-key/remove-text-by-key/inline-last-character/input.yaml new file mode 100644 index 000000000..8001e3ad1 --- /dev/null +++ b/test/transforms/fixtures/by-key/remove-text-by-key/inline-last-character/input.yaml @@ -0,0 +1,10 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + text: a diff --git a/test/transforms/fixtures/by-key/remove-node-by-key/text/output.yaml b/test/transforms/fixtures/by-key/remove-text-by-key/inline-last-character/output.yaml similarity index 100% rename from test/transforms/fixtures/by-key/remove-node-by-key/text/output.yaml rename to test/transforms/fixtures/by-key/remove-text-by-key/inline-last-character/output.yaml diff --git a/test/transforms/fixtures/by-key/remove-text-by-key/inline-void/index.js b/test/transforms/fixtures/by-key/remove-text-by-key/inline-void/index.js new file mode 100644 index 000000000..c38e1667f --- /dev/null +++ b/test/transforms/fixtures/by-key/remove-text-by-key/inline-void/index.js @@ -0,0 +1,10 @@ + +export default function (state) { + const { document, selection } = state + const last = document.getTexts().last() + + return state + .transform() + .removeTextByKey(last.key, 0, 4) + .apply() +} diff --git a/test/transforms/fixtures/by-key/remove-text-by-key/inline-void/input.yaml b/test/transforms/fixtures/by-key/remove-text-by-key/inline-void/input.yaml new file mode 100644 index 000000000..fbfc98018 --- /dev/null +++ b/test/transforms/fixtures/by-key/remove-text-by-key/inline-void/input.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: "" + - kind: inline + type: image + isVoid: true + - kind: text + text: word diff --git a/test/transforms/fixtures/by-key/remove-text-by-key/inline-void/output.yaml b/test/transforms/fixtures/by-key/remove-text-by-key/inline-void/output.yaml new file mode 100644 index 000000000..e19de93d6 --- /dev/null +++ b/test/transforms/fixtures/by-key/remove-text-by-key/inline-void/output.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: "" + - kind: inline + type: image + isVoid: true + - kind: text + text: "" diff --git a/test/transforms/fixtures/by-key/remove-text-by-key/inline/index.js b/test/transforms/fixtures/by-key/remove-text-by-key/inline/index.js new file mode 100644 index 000000000..b99ee839d --- /dev/null +++ b/test/transforms/fixtures/by-key/remove-text-by-key/inline/index.js @@ -0,0 +1,10 @@ + +export default function (state) { + const { document, selection } = state + const first = document.getTexts().first() + + return state + .transform() + .removeTextByKey(first.key, 3, 1) + .apply() +} diff --git a/test/transforms/fixtures/by-key/remove-text-by-key/inline/input.yaml b/test/transforms/fixtures/by-key/remove-text-by-key/inline/input.yaml new file mode 100644 index 000000000..f752cee89 --- /dev/null +++ b/test/transforms/fixtures/by-key/remove-text-by-key/inline/input.yaml @@ -0,0 +1,10 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + text: word diff --git a/test/transforms/fixtures/by-key/remove-node-by-key/inline/output.yaml b/test/transforms/fixtures/by-key/remove-text-by-key/inline/output.yaml similarity index 83% rename from test/transforms/fixtures/by-key/remove-node-by-key/inline/output.yaml rename to test/transforms/fixtures/by-key/remove-text-by-key/inline/output.yaml index df7991b4d..e0e015d73 100644 --- a/test/transforms/fixtures/by-key/remove-node-by-key/inline/output.yaml +++ b/test/transforms/fixtures/by-key/remove-text-by-key/inline/output.yaml @@ -7,4 +7,4 @@ nodes: type: link nodes: - kind: text - text: another + text: wor diff --git a/test/transforms/fixtures/by-key/remove-text-by-key/next-void-inline/index.js b/test/transforms/fixtures/by-key/remove-text-by-key/next-void-inline/index.js new file mode 100644 index 000000000..c5deeb445 --- /dev/null +++ b/test/transforms/fixtures/by-key/remove-text-by-key/next-void-inline/index.js @@ -0,0 +1,10 @@ + +export default function (state) { + const { document, selection } = state + const second = document.getTexts().get(1) + + return state + .transform() + .removeTextByKey(second.key, 0, 1) + .apply() +} diff --git a/test/transforms/fixtures/by-key/remove-text-by-key/next-void-inline/input.yaml b/test/transforms/fixtures/by-key/remove-text-by-key/next-void-inline/input.yaml new file mode 100644 index 000000000..739e5c59f --- /dev/null +++ b/test/transforms/fixtures/by-key/remove-text-by-key/next-void-inline/input.yaml @@ -0,0 +1,17 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + text: one + - kind: text + text: a + - kind: inline + type: image + isVoid: true + - kind: text + text: "" diff --git a/test/transforms/fixtures/by-key/remove-text-by-key/next-void-inline/output.yaml b/test/transforms/fixtures/by-key/remove-text-by-key/next-void-inline/output.yaml new file mode 100644 index 000000000..ebb1fd4d6 --- /dev/null +++ b/test/transforms/fixtures/by-key/remove-text-by-key/next-void-inline/output.yaml @@ -0,0 +1,15 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + text: one + - kind: inline + type: image + isVoid: true + - kind: text + text: "" diff --git a/test/transforms/fixtures/by-key/remove-text-by-key/previous-void-inline/index.js b/test/transforms/fixtures/by-key/remove-text-by-key/previous-void-inline/index.js new file mode 100644 index 000000000..af62d3422 --- /dev/null +++ b/test/transforms/fixtures/by-key/remove-text-by-key/previous-void-inline/index.js @@ -0,0 +1,10 @@ + +export default function (state) { + const { document, selection } = state + const third = document.getTexts().get(2) + + return state + .transform() + .removeTextByKey(third.key, 0, 1) + .apply() +} diff --git a/test/transforms/fixtures/by-key/remove-text-by-key/previous-void-inline/input.yaml b/test/transforms/fixtures/by-key/remove-text-by-key/previous-void-inline/input.yaml new file mode 100644 index 000000000..d468b8878 --- /dev/null +++ b/test/transforms/fixtures/by-key/remove-text-by-key/previous-void-inline/input.yaml @@ -0,0 +1,17 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: "" + - kind: inline + type: image + isVoid: true + - kind: text + text: a + - kind: inline + type: link + nodes: + - kind: text + text: two diff --git a/test/transforms/fixtures/by-key/remove-text-by-key/previous-void-inline/output.yaml b/test/transforms/fixtures/by-key/remove-text-by-key/previous-void-inline/output.yaml new file mode 100644 index 000000000..2a4137d88 --- /dev/null +++ b/test/transforms/fixtures/by-key/remove-text-by-key/previous-void-inline/output.yaml @@ -0,0 +1,15 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: "" + - kind: inline + type: image + isVoid: true + - kind: inline + type: link + nodes: + - kind: text + text: two diff --git a/test/transforms/fixtures/by-key/remove-text-by-key/text/index.js b/test/transforms/fixtures/by-key/remove-text-by-key/text/index.js new file mode 100644 index 000000000..b99ee839d --- /dev/null +++ b/test/transforms/fixtures/by-key/remove-text-by-key/text/index.js @@ -0,0 +1,10 @@ + +export default function (state) { + const { document, selection } = state + const first = document.getTexts().first() + + return state + .transform() + .removeTextByKey(first.key, 3, 1) + .apply() +} diff --git a/test/transforms/fixtures/by-key/remove-node-by-key/text/input.yaml b/test/transforms/fixtures/by-key/remove-text-by-key/text/input.yaml similarity index 100% rename from test/transforms/fixtures/by-key/remove-node-by-key/text/input.yaml rename to test/transforms/fixtures/by-key/remove-text-by-key/text/input.yaml diff --git a/test/transforms/fixtures/by-key/remove-node-by-key/block/output.yaml b/test/transforms/fixtures/by-key/remove-text-by-key/text/output.yaml similarity index 77% rename from test/transforms/fixtures/by-key/remove-node-by-key/block/output.yaml rename to test/transforms/fixtures/by-key/remove-text-by-key/text/output.yaml index 6c07a315f..2ee7c9598 100644 --- a/test/transforms/fixtures/by-key/remove-node-by-key/block/output.yaml +++ b/test/transforms/fixtures/by-key/remove-text-by-key/text/output.yaml @@ -4,4 +4,4 @@ nodes: type: paragraph nodes: - kind: text - text: another + text: wor diff --git a/test/transforms/index.js b/test/transforms/index.js index a6dfcbc80..3a65e4092 100644 --- a/test/transforms/index.js +++ b/test/transforms/index.js @@ -18,7 +18,6 @@ describe('transforms', () => { for (const transform of transforms) { if (transform[0] == '.') continue - if (transform == 'insert-node-by-key') continue describe(`${toCamel(transform)}()`, () => { const transformDir = resolve(__dirname, './fixtures/by-key', transform)