mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-30 10:29:48 +02:00
transform.delete and transform.insertFragment performance optimize (#5137)
* feat: transform.delete and transform.insertFragment performance optimize * feat: add changeset * feat: optimize code Co-authored-by: mainhanu <chijun89@gmail.com>
This commit is contained in:
5
.changeset/new-needles-itch.md
Normal file
5
.changeset/new-needles-itch.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'slate': minor
|
||||
---
|
||||
|
||||
transform.delete and transform.insertFragment performance optimize
|
@@ -1,4 +1,3 @@
|
||||
import { produce } from 'immer'
|
||||
import { Operation } from '..'
|
||||
import { TextDirection } from './types'
|
||||
|
||||
@@ -373,125 +372,125 @@ export const Path: PathInterface = {
|
||||
operation: Operation,
|
||||
options: PathTransformOptions = {}
|
||||
): Path | null {
|
||||
return produce(path, p => {
|
||||
const { affinity = 'forward' } = options
|
||||
if (!path) return null
|
||||
|
||||
// PERF: Exit early if the operation is guaranteed not to have an effect.
|
||||
if (!path || path?.length === 0) {
|
||||
return
|
||||
}
|
||||
// PERF: use destructing instead of immer
|
||||
const p = [...path]
|
||||
const { affinity = 'forward' } = options
|
||||
|
||||
if (p === null) {
|
||||
return null
|
||||
}
|
||||
// PERF: Exit early if the operation is guaranteed not to have an effect.
|
||||
if (path.length === 0) {
|
||||
return p
|
||||
}
|
||||
|
||||
switch (operation.type) {
|
||||
case 'insert_node': {
|
||||
const { path: op } = operation
|
||||
switch (operation.type) {
|
||||
case 'insert_node': {
|
||||
const { path: op } = operation
|
||||
|
||||
if (
|
||||
Path.equals(op, p) ||
|
||||
Path.endsBefore(op, p) ||
|
||||
Path.isAncestor(op, p)
|
||||
) {
|
||||
p[op.length - 1] += 1
|
||||
}
|
||||
|
||||
break
|
||||
if (
|
||||
Path.equals(op, p) ||
|
||||
Path.endsBefore(op, p) ||
|
||||
Path.isAncestor(op, p)
|
||||
) {
|
||||
p[op.length - 1] += 1
|
||||
}
|
||||
|
||||
case 'remove_node': {
|
||||
const { path: op } = operation
|
||||
break
|
||||
}
|
||||
|
||||
if (Path.equals(op, p) || Path.isAncestor(op, p)) {
|
||||
case 'remove_node': {
|
||||
const { path: op } = operation
|
||||
|
||||
if (Path.equals(op, p) || Path.isAncestor(op, p)) {
|
||||
return null
|
||||
} else if (Path.endsBefore(op, p)) {
|
||||
p[op.length - 1] -= 1
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'merge_node': {
|
||||
const { path: op, position } = operation
|
||||
|
||||
if (Path.equals(op, p) || Path.endsBefore(op, p)) {
|
||||
p[op.length - 1] -= 1
|
||||
} else if (Path.isAncestor(op, p)) {
|
||||
p[op.length - 1] -= 1
|
||||
p[op.length] += position
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'split_node': {
|
||||
const { path: op, position } = operation
|
||||
|
||||
if (Path.equals(op, p)) {
|
||||
if (affinity === 'forward') {
|
||||
p[p.length - 1] += 1
|
||||
} else if (affinity === 'backward') {
|
||||
// Nothing, because it still refers to the right path.
|
||||
} else {
|
||||
return null
|
||||
} else if (Path.endsBefore(op, p)) {
|
||||
p[op.length - 1] -= 1
|
||||
}
|
||||
|
||||
break
|
||||
} else if (Path.endsBefore(op, p)) {
|
||||
p[op.length - 1] += 1
|
||||
} else if (Path.isAncestor(op, p) && path[op.length] >= position) {
|
||||
p[op.length - 1] += 1
|
||||
p[op.length] -= position
|
||||
}
|
||||
|
||||
case 'merge_node': {
|
||||
const { path: op, position } = operation
|
||||
|
||||
if (Path.equals(op, p) || Path.endsBefore(op, p)) {
|
||||
p[op.length - 1] -= 1
|
||||
} else if (Path.isAncestor(op, p)) {
|
||||
p[op.length - 1] -= 1
|
||||
p[op.length] += position
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'split_node': {
|
||||
const { path: op, position } = operation
|
||||
|
||||
if (Path.equals(op, p)) {
|
||||
if (affinity === 'forward') {
|
||||
p[p.length - 1] += 1
|
||||
} else if (affinity === 'backward') {
|
||||
// Nothing, because it still refers to the right path.
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
} else if (Path.endsBefore(op, p)) {
|
||||
p[op.length - 1] += 1
|
||||
} else if (Path.isAncestor(op, p) && path[op.length] >= position) {
|
||||
p[op.length - 1] += 1
|
||||
p[op.length] -= position
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'move_node': {
|
||||
const { path: op, newPath: onp } = operation
|
||||
|
||||
// If the old and new path are the same, it's a no-op.
|
||||
if (Path.equals(op, onp)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (Path.isAncestor(op, p) || Path.equals(op, p)) {
|
||||
const copy = onp.slice()
|
||||
|
||||
if (Path.endsBefore(op, onp) && op.length < onp.length) {
|
||||
copy[op.length - 1] -= 1
|
||||
}
|
||||
|
||||
return copy.concat(p.slice(op.length))
|
||||
} else if (
|
||||
Path.isSibling(op, onp) &&
|
||||
(Path.isAncestor(onp, p) || Path.equals(onp, p))
|
||||
) {
|
||||
if (Path.endsBefore(op, p)) {
|
||||
p[op.length - 1] -= 1
|
||||
} else {
|
||||
p[op.length - 1] += 1
|
||||
}
|
||||
} else if (
|
||||
Path.endsBefore(onp, p) ||
|
||||
Path.equals(onp, p) ||
|
||||
Path.isAncestor(onp, p)
|
||||
) {
|
||||
if (Path.endsBefore(op, p)) {
|
||||
p[op.length - 1] -= 1
|
||||
}
|
||||
|
||||
p[onp.length - 1] += 1
|
||||
} else if (Path.endsBefore(op, p)) {
|
||||
if (Path.equals(onp, p)) {
|
||||
p[onp.length - 1] += 1
|
||||
}
|
||||
|
||||
p[op.length - 1] -= 1
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
case 'move_node': {
|
||||
const { path: op, newPath: onp } = operation
|
||||
|
||||
// If the old and new path are the same, it's a no-op.
|
||||
if (Path.equals(op, onp)) {
|
||||
return p
|
||||
}
|
||||
|
||||
if (Path.isAncestor(op, p) || Path.equals(op, p)) {
|
||||
const copy = onp.slice()
|
||||
|
||||
if (Path.endsBefore(op, onp) && op.length < onp.length) {
|
||||
copy[op.length - 1] -= 1
|
||||
}
|
||||
|
||||
return copy.concat(p.slice(op.length))
|
||||
} else if (
|
||||
Path.isSibling(op, onp) &&
|
||||
(Path.isAncestor(onp, p) || Path.equals(onp, p))
|
||||
) {
|
||||
if (Path.endsBefore(op, p)) {
|
||||
p[op.length - 1] -= 1
|
||||
} else {
|
||||
p[op.length - 1] += 1
|
||||
}
|
||||
} else if (
|
||||
Path.endsBefore(onp, p) ||
|
||||
Path.equals(onp, p) ||
|
||||
Path.isAncestor(onp, p)
|
||||
) {
|
||||
if (Path.endsBefore(op, p)) {
|
||||
p[op.length - 1] -= 1
|
||||
}
|
||||
|
||||
p[onp.length - 1] += 1
|
||||
} else if (Path.endsBefore(op, p)) {
|
||||
if (Path.equals(onp, p)) {
|
||||
p[onp.length - 1] += 1
|
||||
}
|
||||
|
||||
p[op.length - 1] -= 1
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return p
|
||||
},
|
||||
}
|
||||
|
@@ -187,10 +187,11 @@ export const TextTransforms: TextTransforms = {
|
||||
}
|
||||
}
|
||||
|
||||
for (const pathRef of pathRefs) {
|
||||
const path = pathRef.unref()!
|
||||
Transforms.removeNodes(editor, { at: path, voids })
|
||||
}
|
||||
pathRefs
|
||||
.reverse()
|
||||
.map(r => r.unref())
|
||||
.filter((r): r is Path => r !== null)
|
||||
.forEach(p => Transforms.removeNodes(editor, { at: p, voids }))
|
||||
|
||||
if (!endVoid) {
|
||||
const point = endRef.current!
|
||||
|
Reference in New Issue
Block a user