1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-28 09:29:49 +02:00

Experimental chunking optimisation and other performance improvements (#5871)

* Chunking optimization

* Fix comments

* Remove redundant `insertionsMinusRemovals` variable

* Fix typo

* Unblock Netlify builds

* Add placeholder

* Upgrade Playwright (fixes crash when debugging)

* Fix `autoFocus` not working

* Fix huge document test

* Fix the previous issue without changing `useSlateSelector`

* Retry `test:integration`

* Re-implement `useSlateWithV`

* Retry `test:integration`

* Update docs

* Update JS examples to match TS examples

* Upload Playwright's `test-results` directory in CI to access traces

* Change trace mode to `retain-on-first-failure`

* Fix: `Locator.fill(text)` is flaky on Editable

* Add changesets

* Increase minimum `slate-dom` version

* Update changeset

* Update 09-performance.md

* Deprecate the `useSlateWithV` hook

* Fix errors and improve clarity in 09-performance.md

* Minimum `slate-dom` version is now 0.116

* Update `yarn.lock`
This commit is contained in:
Joe Anderson
2025-06-07 00:42:11 +01:00
committed by GitHub
parent 583d28fe13
commit fb87646e86
65 changed files with 5234 additions and 876 deletions

View File

@@ -16,9 +16,9 @@ import {
Element,
Node,
NodeEntry,
Range,
Transforms,
createEditor,
DecoratedRange,
} from 'slate'
import { withHistory } from 'slate-history'
import {
@@ -27,7 +27,6 @@ import {
RenderElementProps,
RenderLeafProps,
Slate,
useSlate,
useSlateStatic,
withReact,
} from 'slate-react'
@@ -48,13 +47,12 @@ const CodeLineType = 'code-line'
const CodeHighlightingExample = () => {
const [editor] = useState(() => withHistory(withReact(createEditor())))
const decorate = useDecorate(editor)
const decorate = useDecorate()
const onKeyDown = useOnKeydown(editor)
return (
<Slate editor={editor} initialValue={initialValue}>
<ExampleToolbar />
<SetNodeToDecorations />
<Editable
decorate={decorate}
renderElement={ElementWrapper}
@@ -166,48 +164,27 @@ const renderLeaf = (props: RenderLeafProps) => {
)
}
const useDecorate = (editor: CustomEditor) => {
return useCallback(
([node, path]: NodeEntry) => {
if (Element.isElement(node) && node.type === CodeLineType) {
const ranges = editor.nodeToDecorations?.get(node) || []
return ranges
}
const useDecorate = () => {
return useCallback(([node, path]: NodeEntry) => {
if (Element.isElement(node) && node.type === CodeBlockType) {
return decorateCodeBlock([node, path])
}
return []
},
[editor.nodeToDecorations]
)
return []
}, [])
}
interface TokenRange extends Range {
token: boolean
[key: string]: unknown
}
type EditorWithDecorations = CustomEditor & {
nodeToDecorations: Map<Element, TokenRange[]>
}
const getChildNodeToDecorations = ([
const decorateCodeBlock = ([
block,
blockPath,
]: NodeEntry<CodeBlockElement>): Map<Element, TokenRange[]> => {
const nodeToDecorations = new Map<Element, TokenRange[]>()
]: NodeEntry<CodeBlockElement>): DecoratedRange[] => {
const text = block.children.map(line => Node.string(line)).join('\n')
const language = block.language
const tokens = Prism.tokenize(text, Prism.languages[language])
const tokens = Prism.tokenize(text, Prism.languages[block.language])
const normalizedTokens = normalizeTokens(tokens) // make tokens flat and grouped by line
const blockChildren = block.children as Element[]
const decorations: DecoratedRange[] = []
for (let index = 0; index < normalizedTokens.length; index++) {
const tokens = normalizedTokens[index]
const element = blockChildren[index]
if (!nodeToDecorations.has(element)) {
nodeToDecorations.set(element, [])
}
let start = 0
for (const token of tokens) {
@@ -219,41 +196,19 @@ const getChildNodeToDecorations = ([
const end = start + length
const path = [...blockPath, index, 0]
const range = {
decorations.push({
anchor: { path, offset: start },
focus: { path, offset: end },
token: true,
...Object.fromEntries(token.types.map(type => [type, true])),
}
nodeToDecorations.get(element)!.push(range)
})
start = end
}
}
return nodeToDecorations
}
// precalculate editor.nodeToDecorations map to use it inside decorate function then
const SetNodeToDecorations = () => {
const editor = useSlate() as EditorWithDecorations
const blockEntries = Array.from(
Editor.nodes<CodeBlockElement>(editor, {
at: [],
mode: 'highest',
match: n => Element.isElement(n) && n.type === CodeBlockType,
})
)
const nodeToDecorations = mergeMaps(
...blockEntries.map(getChildNodeToDecorations)
)
editor.nodeToDecorations = nodeToDecorations
return null
return decorations
}
const useOnKeydown = (editor: CustomEditor) => {
@@ -306,18 +261,6 @@ const LanguageSelect = (props: LanguageSelectProps) => {
)
}
const mergeMaps = <K, V>(...maps: Map<K, V>[]) => {
const map = new Map<K, V>()
for (const m of maps) {
for (const item of m) {
map.set(...item)
}
}
return map
}
const toChildren = (content: string): CustomText[] => [{ text: content }]
const toCodeLines = (content: string): CodeLineElement[] =>
content