Skip to content

Commit 10dcd94

Browse files
committed
test(files): cover the code-highlight incremental re-tokenization gate
Export and unit-test changeTouchesCodeBlock: prose-only edits map decorations (false), edits inside a code block or a setNodeMarkup language change re-tokenize (true) — the perf-correctness path that keeps highlighting off the keystroke path.
1 parent be81caf commit 10dcd94

2 files changed

Lines changed: 40 additions & 2 deletions

File tree

apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/code-highlight.test.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,22 @@
33
*/
44
import { Editor } from '@tiptap/core'
55
import { afterEach, describe, expect, it } from 'vitest'
6-
import { buildDecorations } from './code-highlight'
6+
import { buildDecorations, changeTouchesCodeBlock } from './code-highlight'
77
import { createMarkdownContentExtensions } from './extensions'
88

99
let editor: Editor | null = null
1010

11+
/** Position just inside the first code block in the current editor doc. */
12+
function codeBlockPos(ed: Editor): number {
13+
let pos = -1
14+
ed.state.doc.descendants((node, p) => {
15+
if (pos === -1 && node.type.name === 'codeBlock') pos = p
16+
return pos === -1
17+
})
18+
if (pos === -1) throw new Error('no code block')
19+
return pos
20+
}
21+
1122
function decorationClassesFor(markdown: string): string[] {
1223
editor = new Editor({ extensions: createMarkdownContentExtensions() })
1324
editor.commands.setContent(markdown, { contentType: 'markdown' })
@@ -41,3 +52,30 @@ describe('code block syntax highlighting', () => {
4152
expect(decorationClassesFor('```unregistered-lang\n+++ foo\n```')).toHaveLength(0)
4253
})
4354
})
55+
56+
describe('changeTouchesCodeBlock (incremental re-tokenization gate)', () => {
57+
function mount(markdown: string): Editor {
58+
editor = new Editor({ extensions: createMarkdownContentExtensions() })
59+
editor.commands.setContent(markdown, { contentType: 'markdown' })
60+
return editor
61+
}
62+
63+
it('is false when an edit lands only in prose (decorations are mapped, not rebuilt)', () => {
64+
const ed = mount('intro text\n\n```js\nconst x = 1\n```')
65+
const tr = ed.state.tr.insertText('Z', 1) // inside the leading paragraph
66+
expect(changeTouchesCodeBlock(tr, tr.doc)).toBe(false)
67+
})
68+
69+
it('is true when an edit lands inside a code block (forces a re-tokenize)', () => {
70+
const ed = mount('intro\n\n```js\nconst x = 1\n```')
71+
const tr = ed.state.tr.insertText('y', codeBlockPos(ed) + 1)
72+
expect(changeTouchesCodeBlock(tr, tr.doc)).toBe(true)
73+
})
74+
75+
it('is true when the code block language changes via setNodeMarkup', () => {
76+
const ed = mount('```js\nconst x = 1\n```')
77+
const pos = codeBlockPos(ed)
78+
const tr = ed.state.tr.setNodeMarkup(pos, undefined, { language: 'python' })
79+
expect(changeTouchesCodeBlock(tr, tr.doc)).toBe(true)
80+
})
81+
})

apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/code-highlight.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export function buildDecorations(doc: ProseMirrorNode): DecorationSet {
8585
* a `setNodeMarkup` language change (whose step range covers the node). When false, the cheap
8686
* path just maps existing decorations instead of re-tokenizing.
8787
*/
88-
function changeTouchesCodeBlock(tr: Transaction, doc: ProseMirrorNode): boolean {
88+
export function changeTouchesCodeBlock(tr: Transaction, doc: ProseMirrorNode): boolean {
8989
let touches = false
9090
for (const map of tr.mapping.maps) {
9191
map.forEach((_oldStart, _oldEnd, newStart, newEnd) => {

0 commit comments

Comments
 (0)