mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-31 10:51:44 +02:00
Perhaps faster normalization: Never visit a descendant twice in normalizaton (#1661)
* Perhaps faster normalization: Never visit a descendant twice in normalizaton * Change getFirstInvalidaDescendantKey to getFirstInvalidaDescendant * Add from-JSON-big in benchmark * Better annotation * Remove un-used test change
This commit is contained in:
committed by
Ian Storm Taylor
parent
6ad3aada5b
commit
c569569729
66
packages/slate/benchmark/models/from-json-big.js
Normal file
66
packages/slate/benchmark/models/from-json-big.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/* eslint-disable react/jsx-key */
|
||||||
|
|
||||||
|
import { Value } from '../..'
|
||||||
|
|
||||||
|
export default function(json) {
|
||||||
|
Value.fromJSON(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const input = {
|
||||||
|
document: {
|
||||||
|
nodes: Array.from(Array(100)).map(() => ({
|
||||||
|
type: 'list',
|
||||||
|
object: 'block',
|
||||||
|
isVoid: false,
|
||||||
|
data: {},
|
||||||
|
nodes: Array.from(Array(10)).map(() => ({
|
||||||
|
type: 'list-item',
|
||||||
|
object: 'block',
|
||||||
|
isVoid: false,
|
||||||
|
data: {},
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
leaves: [
|
||||||
|
{
|
||||||
|
object: 'leaf',
|
||||||
|
marks: [],
|
||||||
|
text: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
object: 'text',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'link',
|
||||||
|
object: 'inline',
|
||||||
|
isVoid: false,
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
},
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
leaves: [
|
||||||
|
{
|
||||||
|
object: 'leaf',
|
||||||
|
marks: [],
|
||||||
|
text: 'Some text for a link',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
object: 'text',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
leaves: [
|
||||||
|
{
|
||||||
|
object: 'leaf',
|
||||||
|
marks: [],
|
||||||
|
text: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
object: 'text',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})),
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
}
|
@@ -65,32 +65,17 @@ function normalizeNodeAndChildren(change, node, schema) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizedKeys = []
|
let child = node.getFirstInvalidDescendant(schema)
|
||||||
let child = node.nodes.first()
|
|
||||||
let path = change.value.document.getPath(node.key)
|
let path = change.value.document.getPath(node.key)
|
||||||
|
|
||||||
// We can't just loop the children and normalize them, because in the process
|
|
||||||
// of normalizing one child, we might end up creating another. Instead, we
|
|
||||||
// have to normalize one at a time, and check for new children along the way.
|
|
||||||
while (node && child) {
|
while (node && child) {
|
||||||
const lastSize = change.operations.size
|
|
||||||
normalizeNodeAndChildren(change, child, schema)
|
normalizeNodeAndChildren(change, child, schema)
|
||||||
normalizedKeys.push(child.key)
|
|
||||||
|
|
||||||
// PERF: if size is unchanged, then no operation happens
|
|
||||||
// Therefore we can simply normalize the next child
|
|
||||||
if (lastSize === change.operations.size) {
|
|
||||||
const nextIndex = node.nodes.indexOf(child) + 1
|
|
||||||
child = node.nodes.get(nextIndex)
|
|
||||||
} else {
|
|
||||||
node = change.value.document.refindNode(path, node.key)
|
node = change.value.document.refindNode(path, node.key)
|
||||||
if (!node) {
|
if (!node) {
|
||||||
path = []
|
path = []
|
||||||
child = null
|
child = null
|
||||||
} else {
|
} else {
|
||||||
path = change.value.document.refindPath(path, node.key)
|
path = change.value.document.refindPath(path, node.key)
|
||||||
child = node.nodes.find(c => !normalizedKeys.includes(c.key))
|
child = node.getFirstInvalidDescendant(schema)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1987,6 +1987,22 @@ class Node {
|
|||||||
validate(schema) {
|
validate(schema) {
|
||||||
return schema.validateNode(this)
|
return schema.validateNode(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the first invalid descendant
|
||||||
|
*
|
||||||
|
* @param {Schema} schema
|
||||||
|
* @return {Node|Text|Null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
getFirstInvalidDescendant(schema) {
|
||||||
|
let result = null
|
||||||
|
this.nodes.find(n => {
|
||||||
|
result = n.validate(schema) ? n : n.getFirstInvalidDescendant(schema)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2072,6 +2088,7 @@ memoize(
|
|||||||
'getTextAtOffset',
|
'getTextAtOffset',
|
||||||
'getTextsAtRangeAsArray',
|
'getTextsAtRangeAsArray',
|
||||||
'validate',
|
'validate',
|
||||||
|
'getFirstInvalidDescendant',
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
takesArguments: true,
|
takesArguments: true,
|
||||||
|
@@ -495,6 +495,18 @@ class Text extends Record(DEFAULTS) {
|
|||||||
validate(schema) {
|
validate(schema) {
|
||||||
return schema.validateNode(this)
|
return schema.validateNode(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the first invalid descendant
|
||||||
|
* PREF: Do not cache this method; because it can cause cycle reference
|
||||||
|
*
|
||||||
|
* @param {Schema} schema
|
||||||
|
* @returns {Text|Null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
getFirstInvalidDescendant(schema) {
|
||||||
|
return this.validate(schema) ? this : null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user