1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-22 06:53:25 +02:00

fix inline matching, add matching arrays, closes #3174 #3191

This commit is contained in:
Ian Storm Taylor
2019-12-02 18:54:22 -05:00
parent e39f1e77ea
commit ea62ad9e44
22 changed files with 170 additions and 64 deletions

View File

@@ -70,8 +70,9 @@ export const checkNode = (
} }
if ('marks' in v && v.marks != null) { if ('marks' in v && v.marks != null) {
for (const [mark, index, n, p] of Node.marks(node)) { for (const entry of Node.marks(node)) {
if (!v.marks.some(m => Mark.matches(mark, m))) { if (!Editor.isMarkMatch(editor, entry, v.marks)) {
const [mark, index, n, p] = entry
return { code: 'mark_invalid', node: n, path: p, mark, index } return { code: 'mark_invalid', node: n, path: p, mark, index }
} }
} }

View File

@@ -13,19 +13,19 @@ export interface MarkRule {
} }
export interface ChildValidation { export interface ChildValidation {
match?: NodeMatch | NodeMatch[] match?: NodeMatch
min?: number min?: number
max?: number max?: number
} }
export interface NodeValidation { export interface NodeValidation {
children?: ChildValidation[] children?: ChildValidation[]
first?: NodeMatch[] first?: NodeMatch
last?: NodeMatch[] last?: NodeMatch
marks?: MarkMatch[] marks?: MarkMatch
next?: NodeMatch[] next?: NodeMatch
parent?: NodeMatch[] parent?: NodeMatch
previous?: NodeMatch[] previous?: NodeMatch
properties?: Record<string, any> properties?: Record<string, any>
text?: (text: string) => boolean text?: (text: string) => boolean
} }

View File

@@ -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 = (
<editor>
<element>
<mark c>text</mark>
</element>
</editor>
)
export const output = (
<editor>
<element>text</element>
</editor>
)

View File

@@ -4,6 +4,7 @@ import { ElementQueries } from './queries/element'
import { GeneralTransforms } from './transforms/general' import { GeneralTransforms } from './transforms/general'
import { GeneralQueries } from './queries/general' import { GeneralQueries } from './queries/general'
import { LocationQueries } from './queries/location' import { LocationQueries } from './queries/location'
import { MarkQueries } from './queries/mark'
import { MarkTransforms } from './transforms/mark' import { MarkTransforms } from './transforms/mark'
import { NodeTransforms } from './transforms/node' import { NodeTransforms } from './transforms/node'
import { NodeQueries } from './queries/node' import { NodeQueries } from './queries/node'
@@ -34,6 +35,7 @@ export const Editor = {
...GeneralQueries, ...GeneralQueries,
...GeneralTransforms, ...GeneralTransforms,
...LocationQueries, ...LocationQueries,
...MarkQueries,
...MarkTransforms, ...MarkTransforms,
...NodeQueries, ...NodeQueries,
...NodeTransforms, ...NodeTransforms,

View File

@@ -350,13 +350,6 @@ export const LocationQueries = {
for (const entry of Editor.texts(editor, { reverse, at })) { for (const entry of Editor.texts(editor, { reverse, at })) {
const [node, path] = entry 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 (mode === 'universal') {
if (first) { if (first) {
@@ -384,7 +377,7 @@ export const LocationQueries = {
const mark = node.marks[index] const mark = node.marks[index]
const markEntry: MarkEntry = [mark, index, node, path] const markEntry: MarkEntry = [mark, index, node, path]
if (match != null && !isMatch(match, markEntry)) { if (match != null && !Editor.isMarkMatch(editor, markEntry, match)) {
continue continue
} }

View File

@@ -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)
}
},
}

View File

@@ -5,19 +5,12 @@ export const NodeQueries = {
* Check if a node entry is a match. * Check if a node entry is a match.
*/ */
isMatch(editor: Editor, entry: NodeEntry, match: NodeMatch | NodeMatch[]) { isMatch(editor: Editor, entry: NodeEntry, match: NodeMatch): boolean {
const [node] = entry
// If match is an array, treat it as an OR condition.
if (Array.isArray(match)) { if (Array.isArray(match)) {
for (const m of match) { return match.some(m => Editor.isMatch(editor, entry, m))
if (Editor.isMatch(editor, entry, m)) {
return true
}
} }
return false const [node] = entry
}
switch (match) { switch (match) {
case 'text': case 'text':
@@ -27,11 +20,6 @@ export const NodeQueries = {
case 'element': case 'element':
return Element.isElement(node) return Element.isElement(node)
case 'inline': case 'inline':
return (
(Element.isElement(node) && editor.isInline(node)) ||
Text.isText(node)
)
case 'inline-element':
return Element.isElement(node) && editor.isInline(node) return Element.isElement(node) && editor.isInline(node)
case 'block': case 'block':
return ( return (

View File

@@ -48,7 +48,7 @@ export const NodeTransforms = {
} else if (Text.isText(node)) { } else if (Text.isText(node)) {
match = 'text' match = 'text'
} else if (editor.isInline(node)) { } else if (editor.isInline(node)) {
match = 'inline' match = ['inline', 'text']
} else { } else {
match = 'block' match = 'block'
} }
@@ -682,7 +682,7 @@ export const NodeTransforms = {
const path = at const path = at
match = ([, p]) => Path.equals(p, path) match = ([, p]) => Path.equals(p, path)
} else if (editor.isInline(element)) { } else if (editor.isInline(element)) {
match = 'inline' match = ['inline', 'text']
} else { } else {
match = 'block' match = 'block'
} }

View File

@@ -193,7 +193,7 @@ export const TextTransforms = {
// If the insert point is at the edge of an inline node, move it outside // If the insert point is at the edge of an inline node, move it outside
// instead since it will need to be split otherwise. // 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) { if (inlineElementMatch) {
const [, inlinePath] = 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 [, inlinePath] = inlineMatch
const isInlineStart = Editor.isStart(editor, at, inlinePath) const isInlineStart = Editor.isStart(editor, at, inlinePath)
const isInlineEnd = Editor.isEnd(editor, at, inlinePath) const isInlineEnd = Editor.isEnd(editor, at, inlinePath)
@@ -285,7 +285,10 @@ export const TextTransforms = {
isInlineEnd ? Path.next(inlinePath) : inlinePath 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( const startRef = Editor.pathRef(
editor, editor,
@@ -296,13 +299,18 @@ export const TextTransforms = {
Editor.insertNodes(editor, starts, { Editor.insertNodes(editor, starts, {
at: startRef.current!, at: startRef.current!,
match: 'inline', match: ['inline', 'text'],
}) })
Editor.insertNodes(editor, middles, { Editor.insertNodes(editor, middles, {
at: middleRef.current!, at: middleRef.current!,
match: 'block', match: 'block',
}) })
Editor.insertNodes(editor, ends, { at: endRef.current!, match: 'inline' })
Editor.insertNodes(editor, ends, {
at: endRef.current!,
match: ['inline', 'text'],
})
if (!options.at) { if (!options.at) {
let path let path

View File

@@ -62,4 +62,7 @@ export type MarkEntry = [Mark, number, Text, Path]
* `MarkMatch` values are used as shorthands for matching mark objects. * `MarkMatch` values are used as shorthands for matching mark objects.
*/ */
export type MarkMatch = Partial<Mark> | ((entry: MarkEntry) => boolean) export type MarkMatch =
| Partial<Mark>
| ((entry: MarkEntry) => boolean)
| MarkMatch[]

View File

@@ -574,9 +574,9 @@ export type NodeMatch =
| 'block' | 'block'
| 'element' | 'element'
| 'inline' | 'inline'
| 'inline-element'
| 'text' | 'text'
| 'editor' | 'editor'
| 'void' | 'void'
| Partial<Node> | Partial<Node>
| ((entry: NodeEntry) => boolean) | ((entry: NodeEntry) => boolean)
| NodeMatch[]

View File

@@ -15,4 +15,4 @@ export const run = editor => {
) )
} }
export const output = [[<text>one</text>, [0, 0]]] export const output = []

View File

@@ -18,9 +18,6 @@ export const run = editor => {
} }
export const output = [ export const output = [
[<text>one</text>, [0, 0]],
[<inline>two</inline>, [0, 1]], [<inline>two</inline>, [0, 1]],
[<text>three</text>, [0, 2]],
[<inline>four</inline>, [0, 3]], [<inline>four</inline>, [0, 3]],
[<text>five</text>, [0, 4]],
] ]

View File

@@ -22,12 +22,10 @@ export const run = editor => {
} }
export const output = [ export const output = [
[<text>one</text>, [0, 0]],
[ [
<inline> <inline>
two<inline>three</inline>four two<inline>three</inline>four
</inline>, </inline>,
[0, 1], [0, 1],
], ],
[<text>five</text>, [0, 2]],
] ]

View File

@@ -23,9 +23,6 @@ export const run = editor => {
} }
export const output = [ export const output = [
[<text>five</text>, [0, 4]],
[<inline>four</inline>, [0, 3]], [<inline>four</inline>, [0, 3]],
[<text>three</text>, [0, 2]],
[<inline>two</inline>, [0, 1]], [<inline>two</inline>, [0, 1]],
[<text>one</text>, [0, 0]],
] ]

View File

@@ -17,8 +17,4 @@ export const run = editor => {
) )
} }
export const output = [ export const output = [[<inline void>two</inline>, [0, 1]]]
[<text>one</text>, [0, 0]],
[<inline void>two</inline>, [0, 1]],
[<text>three</text>, [0, 2]],
]

View File

@@ -17,8 +17,4 @@ export const run = editor => {
) )
} }
export const output = [ export const output = [[<inline>two</inline>, [0, 1]]]
[<text>one</text>, [0, 0]],
[<inline>two</inline>, [0, 1]],
[<text>three</text>, [0, 2]],
]

View File

@@ -0,0 +1,26 @@
/** @jsx jsx */
import { Editor } from 'slate'
import { jsx } from '../../..'
export const input = (
<editor>
<block>
one<inline>two</inline>three<inline>four</inline>five
</block>
</editor>
)
export const run = editor => {
return Array.from(
Editor.nodes(editor, { at: [], match: ['inline', 'text'], mode: 'highest' })
)
}
export const output = [
[<text>one</text>, [0, 0]],
[<inline>two</inline>, [0, 1]],
[<text>three</text>, [0, 2]],
[<inline>four</inline>, [0, 3]],
[<text>five</text>, [0, 4]],
]

View File

@@ -0,0 +1,33 @@
/** @jsx jsx */
import { Editor } from 'slate'
import { jsx } from '../../..'
export const input = (
<editor>
<block>
one
<inline>
two<inline>three</inline>four
</inline>
five
</block>
</editor>
)
export const run = editor => {
return Array.from(
Editor.nodes(editor, { at: [], match: ['inline', 'text'], mode: 'highest' })
)
}
export const output = [
[<text>one</text>, [0, 0]],
[
<inline>
two<inline>three</inline>four
</inline>,
[0, 1],
],
[<text>five</text>, [0, 2]],
]

View File

@@ -0,0 +1,24 @@
/** @jsx jsx */
import { Editor } from 'slate'
import { jsx } from '../../..'
export const input = (
<editor>
<block>
one<inline>two</inline>three
</block>
</editor>
)
export const run = editor => {
return Array.from(
Editor.nodes(editor, { at: [], match: ['inline', 'text'], mode: 'highest' })
)
}
export const output = [
[<text>one</text>, [0, 0]],
[<inline>two</inline>, [0, 1]],
[<text>three</text>, [0, 2]],
]

View File

@@ -36,10 +36,10 @@ export const output = (
<anchor /> <anchor />
word word
</inline> </inline>
<text key /> <text />
</block> </block>
<block> <block>
<text key /> <text />
<inline key> <inline key>
another another
<focus /> <focus />

View File

@@ -36,7 +36,7 @@ export const output = (
<anchor /> <anchor />
word word
</inline> </inline>
<text key /> <text />
</block> </block>
<block> <block>
<text /> <text />