From c7f0c655ab10fbbf09ff5dbfe3d6f5883cd9f0d5 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Mon, 24 Oct 2016 09:33:58 +0200 Subject: [PATCH] Add normalize nodes to transforms bykey --- .eslintrc | 1 + src/plugins/schema.js | 25 +++-- src/serializers/raw.js | 3 + src/transforms/by-key.js | 182 +++++++++++++++++++++++++++++------- src/transforms/normalize.js | 8 +- test.js | 66 +++++++++++++ 6 files changed, 231 insertions(+), 54 deletions(-) create mode 100644 test.js diff --git a/.eslintrc b/.eslintrc index 0a1911d9d..0a596efac 100644 --- a/.eslintrc +++ b/.eslintrc @@ -76,6 +76,7 @@ "no-unreachable": "error", "no-unsafe-finally": "error", "no-unused-expressions": "error", + "no-unused-vars": "warn", "no-useless-call": "error", "no-useless-computed-key": "error", "no-useless-constructor": "error", diff --git a/src/plugins/schema.js b/src/plugins/schema.js index e70cdb480..cfdc08204 100644 --- a/src/plugins/schema.js +++ b/src/plugins/schema.js @@ -25,7 +25,7 @@ const DOCUMENT_CHILDREN_RULE = { return invalids.size ? invalids : null }, normalize: (transform, document, invalids) => { - return invalids.reduce((t, n) => t.removeNodeByKey(n.key), transform) + return invalids.reduce((t, n) => t.removeNodeByKey(n.key, { normalize: false }), transform) } } @@ -45,7 +45,7 @@ const BLOCK_CHILDREN_RULE = { return invalids.size ? invalids : null }, normalize: (transform, block, invalids) => { - return invalids.reduce((t, n) => t.removeNodeByKey(n.key), transform) + return invalids.reduce((t, n) => t.removeNodeByKey(n.key, { normalize: false }), transform) } } @@ -64,7 +64,7 @@ const MIN_TEXT_RULE = { return nodes.size === 0 ? true : null }, normalize: (transform, node) => { - return transform.insertNodeByKey(node.key, 0, Text.create()) + return transform.insertNodeByKey(node.key, 0, Text.create(), { normalize: false }) } } @@ -84,7 +84,7 @@ const INLINE_CHILDREN_RULE = { return invalids.size ? invalids : null }, normalize: (transform, inline, invalids) => { - return invalids.reduce((t, n) => t.removeNodeByKey(n.key), transform) + return invalids.reduce((t, n) => t.removeNodeByKey(n.key, { normalize: false }), transform) } } @@ -102,7 +102,7 @@ const INLINE_NO_EMPTY = { return inline.text == '' }, normalize: (transform, node) => { - return transform.removeNodeByKey(node.key) + return transform.removeNodeByKey(node.key, { normalize: false }) } } @@ -119,12 +119,12 @@ const INLINE_VOID_TEXT_RULE = { validate: (node) => { return node.text !== ' ' || node.nodes.size !== 1 }, - normalize: (transform, node) => { + normalize: (transform, node, result) => { transform = node.nodes.reduce((t, child) => { - return t.removeNodeByKey(child.key) + return t.removeNodeByKey(child.key, { normalize: false }) }, transform) - return transform.insertNodeByKey(node.key, 0, Text.createFromString(' ')) + return transform.insertNodeByKey(node.key, 0, Text.createFromString(' '), { normalize: false }) } } @@ -136,7 +136,6 @@ const INLINE_VOID_TEXT_RULE = { const INLINE_VOID_TEXTS_AROUND_RULE = { match: (object) => { - return object.kind == 'block' return object.kind == 'block' || object.kind == 'inline' }, validate: (block) => { @@ -162,8 +161,8 @@ const INLINE_VOID_TEXTS_AROUND_RULE = { }, normalize: (transform, block, invalids) => { return invalids.reduce((t, { index, next, prev }) => { - if (prev) t = transform.insertNodeByKey(block.key, index, Text.create()) - if (next) t = transform.insertNodeByKey(block.key, index + 1, Text.create()) + if (prev) t = transform.insertNodeByKey(block.key, index, Text.create(), { normalize: false }) + if (next) t = transform.insertNodeByKey(block.key, index + 1, Text.create(), { normalize: false }) return t }, transform) } @@ -200,7 +199,7 @@ const NO_ADJACENT_TEXT_RULE = { .reverse() .reduce((t, pair) => { const [ first, second ] = pair - return t.joinNodeByKey(second.key, first.key) + return t.joinNodeByKey(second.key, first.key, { normalize: false }) }, transform) } } @@ -256,7 +255,7 @@ const NO_EMPTY_TEXT_RULE = { }, normalize: (transform, node, invalids) => { return invalids.reduce((t, text) => { - return t.removeNodeByKey(text.key) + return t.removeNodeByKey(text.key, { normalize: false }) }, transform) } } diff --git a/src/serializers/raw.js b/src/serializers/raw.js index d0ec3214e..1b53861d7 100644 --- a/src/serializers/raw.js +++ b/src/serializers/raw.js @@ -482,6 +482,7 @@ const Raw = { untersifyBlock(object) { if (object.isVoid || !object.nodes || !object.nodes.length) { return { + key: object.key, data: object.data, kind: object.kind, type: object.type, @@ -508,6 +509,7 @@ const Raw = { untersifyInline(object) { if (object.isVoid || !object.nodes || !object.nodes.length) { return { + key: object.key, data: object.data, kind: object.kind, type: object.type, @@ -567,6 +569,7 @@ const Raw = { if (object.ranges) return object return { + key: object.key, kind: object.kind, ranges: [{ text: object.text, diff --git a/src/transforms/by-key.js b/src/transforms/by-key.js index db8ced66d..6f339f1ef 100644 --- a/src/transforms/by-key.js +++ b/src/transforms/by-key.js @@ -1,7 +1,4 @@ - -import Text from '../models/text' import Normalize from '../utils/normalize' -import uid from '../utils/uid' /** * Add mark to text at `offset` and `length` in node by `key`. @@ -11,15 +8,25 @@ import uid from '../utils/uid' * @param {Number} offset * @param {Number} length * @param {Mixed} mark + * @param {Object} options + * @param {Boolean} normalize * @return {Transform} */ -export function addMarkByKey(transform, key, offset, length, mark) { +export function addMarkByKey(transform, key, offset, length, mark, options = {}) { + const { normalize = true } = options mark = Normalize.mark(mark) const { state } = transform const { document } = state const path = document.getPath(key) - return transform.addMarkOperation(path, offset, length, mark) + const parent = document.getParent(key) + + transform = transform.addMarkOperation(path, offset, length, mark) + if (normalize) { + transform = transform.normalizeNodeByKey(parent.key) + } + + return transform } /** @@ -29,16 +36,23 @@ export function addMarkByKey(transform, key, offset, length, mark) { * @param {String} key * @param {Number} index * @param {Node} node + * @param {Object} options + * @param {Boolean} normalize * @return {Transform} */ -export function insertNodeByKey(transform, key, index, node) { +export function insertNodeByKey(transform, key, index, node, options = {}) { + const { normalize = true } = options const { state } = transform const { document } = state const path = document.getPath(key) - const newPath = path.slice().push(index) - return transform.insertNodeOperation(path, index, node) + transform = transform.insertNodeOperation(path, index, node) + if (normalize) { + transform = transform.normalizeNodeByKey(key) + } + + return transform } /** @@ -49,14 +63,24 @@ export function insertNodeByKey(transform, key, index, node) { * @param {Number} offset * @param {String} text * @param {Set} marks (optional) + * @param {Object} options + * @param {Boolean} normalize * @return {Transform} */ -export function insertTextByKey(transform, key, offset, text, marks) { +export function insertTextByKey(transform, key, offset, text, marks, options = {}) { + const { normalize = true } = options const { state } = transform const { document } = state const path = document.getPath(key) - return transform.insertTextOperation(path, offset, text, marks) + const parent = document.getParent(key) + + transform = transform.insertTextOperation(path, offset, text, marks) + if (normalize) { + transform = transform.normalizeNodeByKey(parent.key) + } + + return transform } /** @@ -65,15 +89,30 @@ export function insertTextByKey(transform, key, offset, text, marks) { * @param {Transform} transform * @param {String} key * @param {String} withKey + * @param {Object} options + * @param {Boolean} normalize * @return {Transform} */ -export function joinNodeByKey(transform, key, withKey) { +export function joinNodeByKey(transform, key, withKey, options = {}) { + const { normalize = true } = options const { state } = transform const { document } = state const path = document.getPath(key) const withPath = document.getPath(withKey) - return transform.joinNodeOperation(path, withPath) + const parent = document.getCommonAncestor(key, withKey) + + transform = transform.joinNodeOperation(path, withPath) + + if (normalize) { + if (parent) { + transform = transform.normalizeNodeByKey(parent.key) + } else { + transform = transform.normalizeDocument() + } + } + + return transform } /** @@ -83,20 +122,28 @@ export function joinNodeByKey(transform, key, withKey) { * @param {String} key * @param {String} newKey * @param {Number} index + * @param {Object} options + * @param {Boolean} normalize * @return {Transform} */ -export function moveNodeByKey(transform, key, newKey, newIndex) { +export function moveNodeByKey(transform, key, newKey, newIndex, options = {}) { + const { normalize = true } = options 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) - 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) + + transform = transform.moveNodeOperation(path, newPath, newIndex) + const parent = document.getCommonAncestor(key, newKey) + + if (normalize) { + if (parent) { + transform = transform.normalizeNodeByKey(parent.key) + } else { + transform = transform.normalizeDocument() + } + } return transform } @@ -109,15 +156,25 @@ export function moveNodeByKey(transform, key, newKey, newIndex) { * @param {Number} offset * @param {Number} length * @param {Mark} mark + * @param {Object} options + * @param {Boolean} normalize * @return {Transform} */ -export function removeMarkByKey(transform, key, offset, length, mark) { +export function removeMarkByKey(transform, key, offset, length, mark, options = {}) { + const { normalize = true } = options mark = Normalize.mark(mark) const { state } = transform const { document } = state const path = document.getPath(key) - return transform.removeMarkOperation(path, offset, length, mark) + const parent = document.getParent(key) + + transform = transform.removeMarkOperation(path, offset, length, mark) + if (normalize) { + transform = transform.normalizeNodeByKey(parent.key) + } + + return transform } /** @@ -125,19 +182,29 @@ export function removeMarkByKey(transform, key, offset, length, mark) { * * @param {Transform} transform * @param {String} key + * @param {Object} options + * @param {Boolean} normalize * @return {Transform} */ -export function removeNodeByKey(transform, key) { +export function removeNodeByKey(transform, key, options = {}) { + const { normalize = true } = options const { state } = transform let { document } = state - const node = document.assertDescendant(key) const path = document.getPath(key) const parent = document.getParent(key) - const previous = document.getPreviousSibling(key) - const next = document.getNextSibling(key) - return transform.removeNodeOperation(path) + transform = transform.removeNodeOperation(path) + + if (normalize) { + if (parent) { + transform = transform.normalizeNodeByKey(parent.key) + } else { + transform = transform.normalizeDocument() + } + } + + return transform } /** @@ -147,16 +214,24 @@ export function removeNodeByKey(transform, key) { * @param {String} key * @param {Number} offset * @param {Number} length + * @param {Object} options + * @param {Boolean} normalize * @return {Transform} */ -export function removeTextByKey(transform, key, offset, length) { +export function removeTextByKey(transform, key, offset, length, options = {}) { + const { normalize = true } = options const { state } = transform let { document } = state const path = document.getPath(key) const parent = document.getParent(key) - return transform.removeTextOperation(path, offset, length) - .normalizeNodeByKey(parent) + + transform = transform.removeTextOperation(path, offset, length) + if (normalize) { + transform = transform.normalizeNodeByKey(parent.key) + } + + return transform } /** @@ -167,16 +242,26 @@ export function removeTextByKey(transform, key, offset, length) { * @param {Number} offset * @param {Number} length * @param {Mark} mark + * @param {Object} options + * @param {Boolean} normalize * @return {Transform} */ -export function setMarkByKey(transform, key, offset, length, mark, properties) { +export function setMarkByKey(transform, key, offset, length, mark, properties, options = {}) { + const { normalize = true } = options mark = Normalize.mark(mark) properties = Normalize.markProperties(properties) const { state } = transform const { document } = state const path = document.getPath(key) - return transform.setMarkOperation(path, offset, length, mark, properties) + const parent = document.getParent(key) + + transform = transform.setMarkOperation(path, offset, length, mark, properties) + if (normalize) { + transform = transform.normalizeNodeByKey(parent.key) + } + + return transform } /** @@ -185,15 +270,28 @@ export function setMarkByKey(transform, key, offset, length, mark, properties) { * @param {Transform} transform * @param {String} key * @param {Object || String} properties + * @param {Object} options + * @param {Boolean} normalize * @return {Transform} */ -export function setNodeByKey(transform, key, properties) { +export function setNodeByKey(transform, key, properties, options = {}) { + const { normalize = true } = options properties = Normalize.nodeProperties(properties) const { state } = transform const { document } = state const path = document.getPath(key) - transform.setNodeOperation(path, properties) + const parent = document.getParent(key) + + transform = transform.setNodeOperation(path, properties) + + if (normalize) { + if (parent) { + transform = transform.normalizeNodeByKey(parent.key) + } else { + transform = transform.normalizeDocument() + } + } return transform } @@ -204,13 +302,27 @@ export function setNodeByKey(transform, key, properties) { * @param {Transform} transform * @param {String} key * @param {Number} offset + * @param {Object} options + * @param {Boolean} normalize * @return {Transform} */ -export function splitNodeByKey(transform, key, offset) { +export function splitNodeByKey(transform, key, offset, options = {}) { + const { normalize = true } = options let { state } = transform let { document } = state const path = document.getPath(key) + const parent = document.getParent(key) - return transform.splitNodeOperation(path, offset) + transform = transform.splitNodeOperation(path, offset) + + if (normalize) { + if (parent) { + transform = transform.normalizeNodeByKey(parent.key) + } else { + transform = transform.normalizeDocument() + } + } + + return transform } diff --git a/src/transforms/normalize.js b/src/transforms/normalize.js index 65259dc75..24a87e625 100644 --- a/src/transforms/normalize.js +++ b/src/transforms/normalize.js @@ -1,5 +1,3 @@ -import Schema from '../models/schema' -import Raw from '../serializers/raw' import warning from '../utils/warning' import { default as defaultSchema } from '../plugins/schema' @@ -30,8 +28,6 @@ function _refreshNode(transform, node) { */ function _normalizeChildrenWith(transform, schema, node) { - let { state } = transform - if (!node.nodes) { return transform } @@ -53,7 +49,6 @@ function _normalizeChildrenWith(transform, schema, node) { */ function _normalizeNodeWith(transform, schema, node) { - let { state } = transform const failure = schema.__validate(node) // Node is valid? @@ -87,6 +82,7 @@ function _normalizeNodeWith(transform, schema, node) { */ export function normalizeNodeWith(transform, schema, node) { + // console.log(`normalize node key=${node.key}`) // Iterate over its children transform = _normalizeChildrenWith(transform, schema, node) @@ -149,7 +145,7 @@ export function normalizeDocument(transform) { export function normalizeNodeByKey(transform, key) { const { state } = transform const { document } = state - const node = document.assertDescendant(key) + const node = document.key == key ? document : document.assertDescendant(key) return transform.normalizeNodeWith(defaultSchema, node) } diff --git a/test.js b/test.js new file mode 100644 index 000000000..ef1faf612 --- /dev/null +++ b/test.js @@ -0,0 +1,66 @@ +import { Raw, Inline } from './src' + +function printNode(node, depth = 0) { + const indent = Array(depth * 2).join(' ') + + if (node.kind === 'text') { + console.log(`${indent}#text key=${node.key}`) + const ranges = node.getRanges() + ranges.forEach(range => { + console.log(`${indent} ${JSON.stringify(range.text)}`) + }) + } + else { + console.log(`${indent}#${node.kind + (node.isVoid ? '(void)' : '')} type=${node.type} key=${node.key} ${node.data ? JSON.stringify(node.data.toJS()) : ''}`) + node.nodes.forEach(child => printNode(child, depth + 1)) + } +} + +function print(st) { + printNode(st.document) + console.log('') +} + +const state = Raw.deserialize({ + nodes: [ + { + kind: 'block', + type: 'paragraph', + key: 'container', + nodes: [ + { + key: 'in1', + kind: 'inline', + type: 'link', + nodes: [ + { + key: 'sometext', + kind: 'text', + text: 'Hello' + }, + { + key: 'in2', + kind: 'inline', + type: 'image', + isVoid: true, + nodes: [ + { + kind: 'text', + text: ' ' + } + ] + } + ] + } + ] + } + ] +}, { terse: true }) + +print(state) + +const newState = state.transform() + .moveNodeByKey('in2', 'container') + .apply() + +print(newState)