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:
@@ -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
|
||||
|
Reference in New Issue
Block a user