From ea62ad9e4419be2370426009c511fbea9f7daa19 Mon Sep 17 00:00:00 2001 From: Ian Storm Taylor Date: Mon, 2 Dec 2019 18:54:22 -0500 Subject: [PATCH] fix inline matching, add matching arrays, closes #3174 #3191 --- packages/slate-schema/src/checkers.ts | 5 +-- packages/slate-schema/src/rules.ts | 14 ++++---- .../test/validations/marks/invalid-or.js | 27 +++++++++++++++ packages/slate/src/interfaces/editor/index.ts | 2 ++ .../src/interfaces/editor/queries/location.ts | 9 +---- .../src/interfaces/editor/queries/mark.ts | 17 ++++++++++ .../src/interfaces/editor/queries/node.ts | 20 +++-------- .../src/interfaces/editor/transforms/node.ts | 4 +-- .../src/interfaces/editor/transforms/text.ts | 18 +++++++--- packages/slate/src/interfaces/mark.ts | 5 ++- packages/slate/src/interfaces/node.ts | 2 +- .../test/queries/nodes/match-inline/block.js | 2 +- .../nodes/match-inline/inline-multiple.js | 3 -- .../nodes/match-inline/inline-nested.js | 2 -- .../nodes/match-inline/inline-reverse.js | 3 -- .../queries/nodes/match-inline/inline-void.js | 6 +--- .../test/queries/nodes/match-inline/inline.js | 6 +--- .../nodes/match-multiple/inline-multiple.js | 26 +++++++++++++++ .../nodes/match-multiple/inline-nested.js | 33 +++++++++++++++++++ .../queries/nodes/match-multiple/inline.js | 24 ++++++++++++++ .../setNodes/inline/inline-across.js | 4 +-- .../setNodes/inline/inline-block-hanging.js | 2 +- 22 files changed, 170 insertions(+), 64 deletions(-) create mode 100644 packages/slate-schema/test/validations/marks/invalid-or.js create mode 100644 packages/slate/src/interfaces/editor/queries/mark.ts create mode 100644 packages/slate/test/queries/nodes/match-multiple/inline-multiple.js create mode 100644 packages/slate/test/queries/nodes/match-multiple/inline-nested.js create mode 100644 packages/slate/test/queries/nodes/match-multiple/inline.js diff --git a/packages/slate-schema/src/checkers.ts b/packages/slate-schema/src/checkers.ts index 9a9d2572f..948f09f48 100644 --- a/packages/slate-schema/src/checkers.ts +++ b/packages/slate-schema/src/checkers.ts @@ -70,8 +70,9 @@ export const checkNode = ( } if ('marks' in v && v.marks != null) { - for (const [mark, index, n, p] of Node.marks(node)) { - if (!v.marks.some(m => Mark.matches(mark, m))) { + for (const entry of Node.marks(node)) { + if (!Editor.isMarkMatch(editor, entry, v.marks)) { + const [mark, index, n, p] = entry return { code: 'mark_invalid', node: n, path: p, mark, index } } } diff --git a/packages/slate-schema/src/rules.ts b/packages/slate-schema/src/rules.ts index c5aa8e86d..d0a4b799d 100644 --- a/packages/slate-schema/src/rules.ts +++ b/packages/slate-schema/src/rules.ts @@ -13,19 +13,19 @@ export interface MarkRule { } export interface ChildValidation { - match?: NodeMatch | NodeMatch[] + match?: NodeMatch min?: number max?: number } export interface NodeValidation { children?: ChildValidation[] - first?: NodeMatch[] - last?: NodeMatch[] - marks?: MarkMatch[] - next?: NodeMatch[] - parent?: NodeMatch[] - previous?: NodeMatch[] + first?: NodeMatch + last?: NodeMatch + marks?: MarkMatch + next?: NodeMatch + parent?: NodeMatch + previous?: NodeMatch properties?: Record text?: (text: string) => boolean } diff --git a/packages/slate-schema/test/validations/marks/invalid-or.js b/packages/slate-schema/test/validations/marks/invalid-or.js new file mode 100644 index 000000000..b558905f1 --- /dev/null +++ b/packages/slate-schema/test/validations/marks/invalid-or.js @@ -0,0 +1,27 @@ +/** @jsx jsx */ + +import { jsx } from 'slate-hyperscript' + +export const schema = [ + { + for: 'node', + match: 'element', + validate: { + marks: [{ a: true }, { b: true }], + }, + }, +] + +export const input = ( + + + text + + +) + +export const output = ( + + text + +) diff --git a/packages/slate/src/interfaces/editor/index.ts b/packages/slate/src/interfaces/editor/index.ts index deb7ffea7..56cebb4c5 100755 --- a/packages/slate/src/interfaces/editor/index.ts +++ b/packages/slate/src/interfaces/editor/index.ts @@ -4,6 +4,7 @@ import { ElementQueries } from './queries/element' import { GeneralTransforms } from './transforms/general' import { GeneralQueries } from './queries/general' import { LocationQueries } from './queries/location' +import { MarkQueries } from './queries/mark' import { MarkTransforms } from './transforms/mark' import { NodeTransforms } from './transforms/node' import { NodeQueries } from './queries/node' @@ -34,6 +35,7 @@ export const Editor = { ...GeneralQueries, ...GeneralTransforms, ...LocationQueries, + ...MarkQueries, ...MarkTransforms, ...NodeQueries, ...NodeTransforms, diff --git a/packages/slate/src/interfaces/editor/queries/location.ts b/packages/slate/src/interfaces/editor/queries/location.ts index 5f7d27ce3..c5e6fd354 100644 --- a/packages/slate/src/interfaces/editor/queries/location.ts +++ b/packages/slate/src/interfaces/editor/queries/location.ts @@ -350,13 +350,6 @@ export const LocationQueries = { for (const entry of Editor.texts(editor, { reverse, at })) { const [node, path] = entry - const isMatch = (m: MarkMatch, entry: MarkEntry) => { - if (typeof m === 'function') { - return m(entry) - } else { - return Mark.matches(entry[0], m) - } - } if (mode === 'universal') { if (first) { @@ -384,7 +377,7 @@ export const LocationQueries = { const mark = node.marks[index] const markEntry: MarkEntry = [mark, index, node, path] - if (match != null && !isMatch(match, markEntry)) { + if (match != null && !Editor.isMarkMatch(editor, markEntry, match)) { continue } diff --git a/packages/slate/src/interfaces/editor/queries/mark.ts b/packages/slate/src/interfaces/editor/queries/mark.ts new file mode 100644 index 000000000..cb9f55b2d --- /dev/null +++ b/packages/slate/src/interfaces/editor/queries/mark.ts @@ -0,0 +1,17 @@ +import { Editor, Mark, MarkEntry, MarkMatch } from '../../..' + +export const MarkQueries = { + /** + * Check if a mark entry is a match. + */ + + isMarkMatch(editor: Editor, entry: MarkEntry, match: MarkMatch): boolean { + if (Array.isArray(match)) { + return match.some(m => Editor.isMarkMatch(editor, entry, m)) + } else if (typeof match === 'function') { + return match(entry) + } else { + return Mark.matches(entry[0], match) + } + }, +} diff --git a/packages/slate/src/interfaces/editor/queries/node.ts b/packages/slate/src/interfaces/editor/queries/node.ts index c0bfba6c3..e1f622b84 100644 --- a/packages/slate/src/interfaces/editor/queries/node.ts +++ b/packages/slate/src/interfaces/editor/queries/node.ts @@ -5,20 +5,13 @@ export const NodeQueries = { * Check if a node entry is a match. */ - isMatch(editor: Editor, entry: NodeEntry, match: NodeMatch | NodeMatch[]) { - const [node] = entry - - // If match is an array, treat it as an OR condition. + isMatch(editor: Editor, entry: NodeEntry, match: NodeMatch): boolean { if (Array.isArray(match)) { - for (const m of match) { - if (Editor.isMatch(editor, entry, m)) { - return true - } - } - - return false + return match.some(m => Editor.isMatch(editor, entry, m)) } + const [node] = entry + switch (match) { case 'text': return Text.isText(node) @@ -27,11 +20,6 @@ export const NodeQueries = { case 'element': return Element.isElement(node) case 'inline': - return ( - (Element.isElement(node) && editor.isInline(node)) || - Text.isText(node) - ) - case 'inline-element': return Element.isElement(node) && editor.isInline(node) case 'block': return ( diff --git a/packages/slate/src/interfaces/editor/transforms/node.ts b/packages/slate/src/interfaces/editor/transforms/node.ts index be73534c6..f2a1ae835 100644 --- a/packages/slate/src/interfaces/editor/transforms/node.ts +++ b/packages/slate/src/interfaces/editor/transforms/node.ts @@ -48,7 +48,7 @@ export const NodeTransforms = { } else if (Text.isText(node)) { match = 'text' } else if (editor.isInline(node)) { - match = 'inline' + match = ['inline', 'text'] } else { match = 'block' } @@ -682,7 +682,7 @@ export const NodeTransforms = { const path = at match = ([, p]) => Path.equals(p, path) } else if (editor.isInline(element)) { - match = 'inline' + match = ['inline', 'text'] } else { match = 'block' } diff --git a/packages/slate/src/interfaces/editor/transforms/text.ts b/packages/slate/src/interfaces/editor/transforms/text.ts index 05d149291..8c2297438 100644 --- a/packages/slate/src/interfaces/editor/transforms/text.ts +++ b/packages/slate/src/interfaces/editor/transforms/text.ts @@ -193,7 +193,7 @@ export const TextTransforms = { // If the insert point is at the edge of an inline node, move it outside // instead since it will need to be split otherwise. - const inlineElementMatch = Editor.match(editor, at, 'inline-element') + const inlineElementMatch = Editor.match(editor, at, 'inline') if (inlineElementMatch) { const [, inlinePath] = inlineElementMatch @@ -270,7 +270,7 @@ export const TextTransforms = { } } - const inlineMatch = Editor.match(editor, at, 'inline')! + const inlineMatch = Editor.match(editor, at, ['inline', 'text'])! const [, inlinePath] = inlineMatch const isInlineStart = Editor.isStart(editor, at, inlinePath) const isInlineEnd = Editor.isEnd(editor, at, inlinePath) @@ -285,7 +285,10 @@ export const TextTransforms = { isInlineEnd ? Path.next(inlinePath) : inlinePath ) - Editor.splitNodes(editor, { at, match: hasBlocks ? 'block' : 'inline' }) + Editor.splitNodes(editor, { + at, + match: hasBlocks ? 'block' : ['inline', 'text'], + }) const startRef = Editor.pathRef( editor, @@ -296,13 +299,18 @@ export const TextTransforms = { Editor.insertNodes(editor, starts, { at: startRef.current!, - match: 'inline', + match: ['inline', 'text'], }) + Editor.insertNodes(editor, middles, { at: middleRef.current!, match: 'block', }) - Editor.insertNodes(editor, ends, { at: endRef.current!, match: 'inline' }) + + Editor.insertNodes(editor, ends, { + at: endRef.current!, + match: ['inline', 'text'], + }) if (!options.at) { let path diff --git a/packages/slate/src/interfaces/mark.ts b/packages/slate/src/interfaces/mark.ts index 0797a7352..778ac83c6 100755 --- a/packages/slate/src/interfaces/mark.ts +++ b/packages/slate/src/interfaces/mark.ts @@ -62,4 +62,7 @@ export type MarkEntry = [Mark, number, Text, Path] * `MarkMatch` values are used as shorthands for matching mark objects. */ -export type MarkMatch = Partial | ((entry: MarkEntry) => boolean) +export type MarkMatch = + | Partial + | ((entry: MarkEntry) => boolean) + | MarkMatch[] diff --git a/packages/slate/src/interfaces/node.ts b/packages/slate/src/interfaces/node.ts index ed6e69805..a137c7eb0 100755 --- a/packages/slate/src/interfaces/node.ts +++ b/packages/slate/src/interfaces/node.ts @@ -574,9 +574,9 @@ export type NodeMatch = | 'block' | 'element' | 'inline' - | 'inline-element' | 'text' | 'editor' | 'void' | Partial | ((entry: NodeEntry) => boolean) + | NodeMatch[] diff --git a/packages/slate/test/queries/nodes/match-inline/block.js b/packages/slate/test/queries/nodes/match-inline/block.js index da6a1867e..c2f82362c 100644 --- a/packages/slate/test/queries/nodes/match-inline/block.js +++ b/packages/slate/test/queries/nodes/match-inline/block.js @@ -15,4 +15,4 @@ export const run = editor => { ) } -export const output = [[one, [0, 0]]] +export const output = [] diff --git a/packages/slate/test/queries/nodes/match-inline/inline-multiple.js b/packages/slate/test/queries/nodes/match-inline/inline-multiple.js index c717ff4b5..35f2631c2 100644 --- a/packages/slate/test/queries/nodes/match-inline/inline-multiple.js +++ b/packages/slate/test/queries/nodes/match-inline/inline-multiple.js @@ -18,9 +18,6 @@ export const run = editor => { } export const output = [ - [one, [0, 0]], [two, [0, 1]], - [three, [0, 2]], [four, [0, 3]], - [five, [0, 4]], ] diff --git a/packages/slate/test/queries/nodes/match-inline/inline-nested.js b/packages/slate/test/queries/nodes/match-inline/inline-nested.js index b5bb412e4..09f15188e 100644 --- a/packages/slate/test/queries/nodes/match-inline/inline-nested.js +++ b/packages/slate/test/queries/nodes/match-inline/inline-nested.js @@ -22,12 +22,10 @@ export const run = editor => { } export const output = [ - [one, [0, 0]], [ twothreefour , [0, 1], ], - [five, [0, 2]], ] diff --git a/packages/slate/test/queries/nodes/match-inline/inline-reverse.js b/packages/slate/test/queries/nodes/match-inline/inline-reverse.js index b4d4da92b..4d8e5320f 100644 --- a/packages/slate/test/queries/nodes/match-inline/inline-reverse.js +++ b/packages/slate/test/queries/nodes/match-inline/inline-reverse.js @@ -23,9 +23,6 @@ export const run = editor => { } export const output = [ - [five, [0, 4]], [four, [0, 3]], - [three, [0, 2]], [two, [0, 1]], - [one, [0, 0]], ] diff --git a/packages/slate/test/queries/nodes/match-inline/inline-void.js b/packages/slate/test/queries/nodes/match-inline/inline-void.js index c3fb5ab29..8b201ab22 100644 --- a/packages/slate/test/queries/nodes/match-inline/inline-void.js +++ b/packages/slate/test/queries/nodes/match-inline/inline-void.js @@ -17,8 +17,4 @@ export const run = editor => { ) } -export const output = [ - [one, [0, 0]], - [two, [0, 1]], - [three, [0, 2]], -] +export const output = [[two, [0, 1]]] diff --git a/packages/slate/test/queries/nodes/match-inline/inline.js b/packages/slate/test/queries/nodes/match-inline/inline.js index ad227813a..bd355821b 100644 --- a/packages/slate/test/queries/nodes/match-inline/inline.js +++ b/packages/slate/test/queries/nodes/match-inline/inline.js @@ -17,8 +17,4 @@ export const run = editor => { ) } -export const output = [ - [one, [0, 0]], - [two, [0, 1]], - [three, [0, 2]], -] +export const output = [[two, [0, 1]]] diff --git a/packages/slate/test/queries/nodes/match-multiple/inline-multiple.js b/packages/slate/test/queries/nodes/match-multiple/inline-multiple.js new file mode 100644 index 000000000..9ef27b8f1 --- /dev/null +++ b/packages/slate/test/queries/nodes/match-multiple/inline-multiple.js @@ -0,0 +1,26 @@ +/** @jsx jsx */ + +import { Editor } from 'slate' +import { jsx } from '../../..' + +export const input = ( + + + onetwothreefourfive + + +) + +export const run = editor => { + return Array.from( + Editor.nodes(editor, { at: [], match: ['inline', 'text'], mode: 'highest' }) + ) +} + +export const output = [ + [one, [0, 0]], + [two, [0, 1]], + [three, [0, 2]], + [four, [0, 3]], + [five, [0, 4]], +] diff --git a/packages/slate/test/queries/nodes/match-multiple/inline-nested.js b/packages/slate/test/queries/nodes/match-multiple/inline-nested.js new file mode 100644 index 000000000..8ebf61efe --- /dev/null +++ b/packages/slate/test/queries/nodes/match-multiple/inline-nested.js @@ -0,0 +1,33 @@ +/** @jsx jsx */ + +import { Editor } from 'slate' +import { jsx } from '../../..' + +export const input = ( + + + one + + twothreefour + + five + + +) + +export const run = editor => { + return Array.from( + Editor.nodes(editor, { at: [], match: ['inline', 'text'], mode: 'highest' }) + ) +} + +export const output = [ + [one, [0, 0]], + [ + + twothreefour + , + [0, 1], + ], + [five, [0, 2]], +] diff --git a/packages/slate/test/queries/nodes/match-multiple/inline.js b/packages/slate/test/queries/nodes/match-multiple/inline.js new file mode 100644 index 000000000..f812b0e48 --- /dev/null +++ b/packages/slate/test/queries/nodes/match-multiple/inline.js @@ -0,0 +1,24 @@ +/** @jsx jsx */ + +import { Editor } from 'slate' +import { jsx } from '../../..' + +export const input = ( + + + onetwothree + + +) + +export const run = editor => { + return Array.from( + Editor.nodes(editor, { at: [], match: ['inline', 'text'], mode: 'highest' }) + ) +} + +export const output = [ + [one, [0, 0]], + [two, [0, 1]], + [three, [0, 2]], +] diff --git a/packages/slate/test/transforms/setNodes/inline/inline-across.js b/packages/slate/test/transforms/setNodes/inline/inline-across.js index c8265d653..7dda21842 100644 --- a/packages/slate/test/transforms/setNodes/inline/inline-across.js +++ b/packages/slate/test/transforms/setNodes/inline/inline-across.js @@ -36,10 +36,10 @@ export const output = ( word - + - + another diff --git a/packages/slate/test/transforms/setNodes/inline/inline-block-hanging.js b/packages/slate/test/transforms/setNodes/inline/inline-block-hanging.js index 9b4350756..28ac2b9d9 100644 --- a/packages/slate/test/transforms/setNodes/inline/inline-block-hanging.js +++ b/packages/slate/test/transforms/setNodes/inline/inline-block-hanging.js @@ -36,7 +36,7 @@ export const output = ( word - +