@pierre/diffs 1.3.0-beta.1 → 1.3.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CodeView.d.ts +4 -0
- package/dist/components/CodeView.d.ts.map +1 -1
- package/dist/components/CodeView.js +38 -0
- package/dist/components/CodeView.js.map +1 -1
- package/dist/components/File.d.ts.map +1 -1
- package/dist/components/File.js +9 -9
- package/dist/components/File.js.map +1 -1
- package/dist/components/FileDiff.d.ts.map +1 -1
- package/dist/components/FileDiff.js +3 -2
- package/dist/components/FileDiff.js.map +1 -1
- package/dist/components/VirtualizedFile.js +6 -1
- package/dist/components/VirtualizedFile.js.map +1 -1
- package/dist/components/VirtualizedFileDiff.js +22 -42
- package/dist/components/VirtualizedFileDiff.js.map +1 -1
- package/dist/components/Virtualizer.js +5 -3
- package/dist/components/Virtualizer.js.map +1 -1
- package/dist/editor/editor.d.ts +7 -1
- package/dist/editor/editor.d.ts.map +1 -1
- package/dist/editor/editor.js +550 -405
- package/dist/editor/editor.js.map +1 -1
- package/dist/editor/editor2.js +6 -0
- package/dist/editor/editor2.js.map +1 -0
- package/dist/editor/pieceTable.d.ts +1 -1
- package/dist/editor/pieceTable.d.ts.map +1 -1
- package/dist/editor/pieceTable.js +2 -22
- package/dist/editor/pieceTable.js.map +1 -1
- package/dist/editor/quickEdit.js +2 -4
- package/dist/editor/quickEdit.js.map +1 -1
- package/dist/editor/searchPanel.d.ts +6 -7
- package/dist/editor/searchPanel.d.ts.map +1 -1
- package/dist/editor/searchPanel.js +102 -137
- package/dist/editor/searchPanel.js.map +1 -1
- package/dist/editor/selection.js +8 -2
- package/dist/editor/selection.js.map +1 -1
- package/dist/editor/sprite.d.ts +7 -0
- package/dist/editor/sprite.d.ts.map +1 -0
- package/dist/editor/sprite.js +38 -0
- package/dist/editor/sprite.js.map +1 -0
- package/dist/editor/textDocument.d.ts +1 -1
- package/dist/editor/textDocument.d.ts.map +1 -1
- package/dist/editor/textDocument.js +2 -2
- package/dist/editor/textDocument.js.map +1 -1
- package/dist/editor/textMeasure.js +3 -3
- package/dist/editor/textMeasure.js.map +1 -1
- package/dist/editor/tokenzier.d.ts +6 -2
- package/dist/editor/tokenzier.d.ts.map +1 -1
- package/dist/editor/tokenzier.js +127 -85
- package/dist/editor/tokenzier.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/jsx.d.ts +1 -0
- package/dist/react/jsx.d.ts.map +1 -1
- package/dist/react/types.js +1 -0
- package/dist/renderers/DiffHunksRenderer.js +5 -9
- package/dist/renderers/DiffHunksRenderer.js.map +1 -1
- package/dist/ssr/index.d.ts +2 -2
- package/dist/style.js +1 -1
- package/dist/style.js.map +1 -1
- package/dist/types.d.ts +13 -12
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/computeEstimatedDiffHeights.js +9 -20
- package/dist/utils/computeEstimatedDiffHeights.js.map +1 -1
- package/dist/utils/iterateOverDiff.js +147 -182
- package/dist/utils/iterateOverDiff.js.map +1 -1
- package/dist/utils/virtualDiffLayout.d.ts +23 -2
- package/dist/utils/virtualDiffLayout.d.ts.map +1 -1
- package/dist/utils/virtualDiffLayout.js +41 -1
- package/dist/utils/virtualDiffLayout.js.map +1 -1
- package/dist/worker/WorkerPoolManager.js +1 -1
- package/dist/worker/WorkerPoolManager.js.map +1 -1
- package/dist/worker/{wasm-D4DU5jgR.js → wasm-BaDzIkIn.js} +2 -2
- package/dist/worker/wasm-BaDzIkIn.js.map +1 -0
- package/dist/worker/worker-portable.js +294 -292
- package/dist/worker/worker-portable.js.map +1 -1
- package/dist/worker/worker.js +179 -181
- package/dist/worker/worker.js.map +1 -1
- package/package.json +22 -21
- package/dist/editor/css.d.ts +0 -6
- package/dist/editor/css.d.ts.map +0 -1
- package/dist/editor/css.js +0 -218
- package/dist/editor/css.js.map +0 -1
- package/dist/worker/wasm-D4DU5jgR.js.map +0 -1
|
@@ -18,15 +18,15 @@ var Metrics = class {
|
|
|
18
18
|
this.#canvasCtx ??= document.createElement("canvas").getContext("2d") ?? void 0;
|
|
19
19
|
if (this.#canvasCtx === void 0) throw new Error("Could not get canvas context");
|
|
20
20
|
const { fontSize, fontFamily, tabSize, lineHeight } = getComputedStyle(root);
|
|
21
|
-
if (lineHeight.endsWith("px")) this.lineHeight =
|
|
22
|
-
else if (fontSize.endsWith("px")) this.lineHeight = round(
|
|
21
|
+
if (lineHeight.endsWith("px")) this.lineHeight = parseFloat(lineHeight.slice(0, -2));
|
|
22
|
+
else if (fontSize.endsWith("px")) this.lineHeight = round(parseFloat(fontSize.slice(0, -2)) * parseFloat(lineHeight));
|
|
23
23
|
const font = fontSize + " " + fontFamily;
|
|
24
24
|
if (this.#font !== font || this.ch === -1) {
|
|
25
25
|
this.#font = font;
|
|
26
26
|
this.#canvasCtx.font = font;
|
|
27
27
|
this.ch = this.canvasMeasureTextWidth("0");
|
|
28
28
|
}
|
|
29
|
-
this.tabSize =
|
|
29
|
+
this.tabSize = parseInt(tabSize, 10);
|
|
30
30
|
}
|
|
31
31
|
/** measure the width of the text */
|
|
32
32
|
measureTextWidth(text) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textMeasure.js","names":["#root","#canvasCtx","#font"],"sources":["../../src/editor/textMeasure.ts"],"sourcesContent":["import { h, round } from './utils';\n\nexport class Metrics {\n #root?: HTMLElement;\n #canvasCtx?: CanvasRenderingContext2D;\n #font?: string;\n\n /** Width of the '0' character. */\n ch: number = -1;\n /** Size of a tab(\\t) character. */\n tabSize: number = 2;\n /** Height of the code line. */\n lineHeight: number = 20;\n\n /** initialize the metrics */\n init(root: HTMLElement): void {\n if (\n this.#root === root &&\n this.#canvasCtx !== undefined &&\n this.ch !== -1\n ) {\n // already initialized\n return;\n }\n\n this.#root = root;\n this.#canvasCtx ??=\n document.createElement('canvas').getContext('2d') ?? undefined;\n if (this.#canvasCtx === undefined) {\n throw new Error('Could not get canvas context');\n }\n\n const { fontSize, fontFamily, tabSize, lineHeight } =\n getComputedStyle(root);\n if (lineHeight.endsWith('px')) {\n this.lineHeight =
|
|
1
|
+
{"version":3,"file":"textMeasure.js","names":["#root","#canvasCtx","#font"],"sources":["../../src/editor/textMeasure.ts"],"sourcesContent":["import { h, round } from './utils';\n\nexport class Metrics {\n #root?: HTMLElement;\n #canvasCtx?: CanvasRenderingContext2D;\n #font?: string;\n\n /** Width of the '0' character. */\n ch: number = -1;\n /** Size of a tab(\\t) character. */\n tabSize: number = 2;\n /** Height of the code line. */\n lineHeight: number = 20;\n\n /** initialize the metrics */\n init(root: HTMLElement): void {\n if (\n this.#root === root &&\n this.#canvasCtx !== undefined &&\n this.ch !== -1\n ) {\n // already initialized\n return;\n }\n\n this.#root = root;\n this.#canvasCtx ??=\n document.createElement('canvas').getContext('2d') ?? undefined;\n if (this.#canvasCtx === undefined) {\n throw new Error('Could not get canvas context');\n }\n\n const { fontSize, fontFamily, tabSize, lineHeight } =\n getComputedStyle(root);\n if (lineHeight.endsWith('px')) {\n this.lineHeight = parseFloat(lineHeight.slice(0, -2));\n } else if (fontSize.endsWith('px')) {\n this.lineHeight = round(\n parseFloat(fontSize.slice(0, -2)) * parseFloat(lineHeight)\n );\n }\n const font = fontSize + ' ' + fontFamily;\n if (this.#font !== font || this.ch === -1) {\n this.#font = font;\n this.#canvasCtx.font = font;\n this.ch = this.canvasMeasureTextWidth('0');\n }\n this.tabSize = parseInt(tabSize, 10);\n }\n\n /** measure the width of the text */\n measureTextWidth(text: string): number {\n const textWithExpandedTabs = text.replaceAll(\n '\\t',\n ' '.repeat(this.tabSize)\n );\n if (needsDomTextMeasurement(textWithExpandedTabs)) {\n return this.domMeasureTextWidth(textWithExpandedTabs);\n }\n return this.canvasMeasureTextWidth(textWithExpandedTabs);\n }\n\n /** measure the width of the text using the canvas measureText API */\n canvasMeasureTextWidth(text: string): number {\n if (this.#canvasCtx === undefined) {\n throw new Error('Metrics not initialized');\n }\n return round(this.#canvasCtx.measureText(text).width);\n }\n\n /**\n * measure the width of the text using the DOM\n * this is slow because it cause a reflow, use it for non-ascii text\n */\n domMeasureTextWidth(text: string): number {\n if (this.#root === undefined) {\n throw new Error('Metrics not initialized');\n }\n const measureEl = h(\n 'span',\n {\n style: {\n position: 'absolute',\n top: '0',\n left: '0',\n visibility: 'hidden',\n pointerEvents: 'none',\n whiteSpace: 'pre',\n font: 'inherit',\n },\n textContent: text,\n },\n this.#root\n );\n try {\n return measureEl.getBoundingClientRect().width;\n } finally {\n measureEl.remove();\n }\n }\n}\n\n/** Check if the text needs DOM text measurement. */\nexport function needsDomTextMeasurement(text: string): boolean {\n for (let i = 0; i < text.length; i++) {\n const code = text.charCodeAt(i);\n if (\n (code >= 0xd800 && code <= 0xdfff) ||\n code === 0x200d ||\n code === 0xfe0e ||\n code === 0xfe0f\n ) {\n return true;\n }\n }\n return false;\n}\n\n/** snap the text offset to the Unicode boundary */\nexport function snapTextOffsetToUnicodeBoundary(\n text: string,\n offset: number\n): number {\n const boundedOffset = Math.max(0, Math.min(offset, text.length));\n if (\n boundedOffset === 0 ||\n boundedOffset === text.length ||\n !needsDomTextMeasurement(text)\n ) {\n return boundedOffset;\n }\n // Avoid measuring a caret position inside one visual emoji/grapheme.\n // Browser caret movement can report offsets around UTF-16 surrogate\n // pairs and emoji joiners; measuring a partial sequence gives a\n // replacement-glyph width.\n const segmenter = new Intl.Segmenter(undefined, {\n granularity: 'grapheme',\n });\n for (const segment of segmenter.segment(text)) {\n const segmentStart = segment.index;\n const segmentEnd = segmentStart + segment.segment.length;\n if (boundedOffset > segmentStart && boundedOffset < segmentEnd) {\n return segmentEnd;\n }\n if (boundedOffset <= segmentStart) {\n break;\n }\n }\n return boundedOffset;\n}\n\n/** get the offsets of the Unicode grapheme clusters in the text */\nexport function getUnicodeMeasurementOffsets(\n text: string\n): number[] | undefined {\n if (!needsDomTextMeasurement(text)) {\n return undefined;\n }\n const offsets = [0];\n const segmenter = new Intl.Segmenter(undefined, {\n granularity: 'grapheme',\n });\n for (const segment of segmenter.segment(text)) {\n offsets.push(segment.index + segment.segment.length);\n }\n return offsets;\n}\n\n/** get the number of columns of the ASCII text */\nexport function getExpandedAsciiTextColumns(\n text: string,\n tabSize: number\n): number {\n let columns = 0;\n for (let i = 0; i < text.length; i++) {\n if (text.charCodeAt(i) > 127) {\n return -1;\n }\n columns += text.charCodeAt(i) === /* '\\t' */ 9 ? tabSize : 1;\n }\n return columns;\n}\n"],"mappings":"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"}
|
|
@@ -9,6 +9,7 @@ interface EditorTokenizerProps {
|
|
|
9
9
|
codeOptions: BaseCodeOptions;
|
|
10
10
|
setStyle: (style: string) => void;
|
|
11
11
|
onDeferTokenize: (lines: Map<number, Array<HighlightedToken>>, themeType: 'dark' | 'light') => void;
|
|
12
|
+
__debug?: boolean;
|
|
12
13
|
}
|
|
13
14
|
/** Stoppable code tokenizer for the editor */
|
|
14
15
|
declare class EditorTokenizer {
|
|
@@ -20,12 +21,15 @@ declare class EditorTokenizer {
|
|
|
20
21
|
highlighter,
|
|
21
22
|
textDocument,
|
|
22
23
|
setStyle,
|
|
23
|
-
onDeferTokenize
|
|
24
|
+
onDeferTokenize,
|
|
25
|
+
__debug
|
|
24
26
|
}: EditorTokenizerProps);
|
|
25
27
|
cleanUp(): void;
|
|
26
28
|
tokenize(change: TextDocumentChange, renderRange?: RenderRange): Map<number, Array<HighlightedToken>>;
|
|
27
|
-
|
|
29
|
+
prebuildStateStack(renderRange?: RenderRange): void;
|
|
28
30
|
stopBackgroundTokenize(): void;
|
|
31
|
+
pauseBackgroundTokenize(): void;
|
|
32
|
+
resumeBackgroundTokenize(): void;
|
|
29
33
|
}
|
|
30
34
|
declare function tokenizeLine(grammar: IGrammar, colorMap: string[], lineText: string, stateStack: StateStack, timeLimit?: number): {
|
|
31
35
|
ruleStack: StateStack;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenzier.d.ts","names":["IGrammar","StateStack","BaseCodeOptions","DiffsHighlighter","HighlightedToken","RenderRange","TextDocument","TextDocumentChange","EditorTokenizerProps","Array","Map","EditorTokenizer","codeOptions","highlighter","textDocument","setStyle","onDeferTokenize","tokenizeLine","renderLineTokens","HTMLElement"],"sources":["../../src/editor/tokenzier.d.ts"],"sourcesContent":["import { type IGrammar, type StateStack } from 'shiki/textmate';\nimport type { BaseCodeOptions, DiffsHighlighter, HighlightedToken, RenderRange } from '../types';\nimport type { TextDocument, TextDocumentChange } from './textDocument';\nexport interface EditorTokenizerProps {\n highlighter: DiffsHighlighter;\n textDocument: TextDocument<unknown>;\n codeOptions: BaseCodeOptions;\n setStyle: (style: string) => void;\n onDeferTokenize: (lines: Map<number, Array<HighlightedToken>>, themeType: 'dark' | 'light') => void;\n}\n/** Stoppable code tokenizer for the editor */\nexport declare class EditorTokenizer {\n #private;\n static TOKENIZE_TIME_LIMIT: number;\n get themeType(): 'light' | 'dark';\n constructor({ codeOptions, highlighter, textDocument, setStyle, onDeferTokenize }: EditorTokenizerProps);\n cleanUp(): void;\n tokenize(change: TextDocumentChange, renderRange?: RenderRange): Map<number, Array<HighlightedToken>>;\n
|
|
1
|
+
{"version":3,"file":"tokenzier.d.ts","names":["IGrammar","StateStack","BaseCodeOptions","DiffsHighlighter","HighlightedToken","RenderRange","TextDocument","TextDocumentChange","EditorTokenizerProps","Array","Map","EditorTokenizer","codeOptions","highlighter","textDocument","setStyle","onDeferTokenize","__debug","tokenizeLine","renderLineTokens","HTMLElement"],"sources":["../../src/editor/tokenzier.d.ts"],"sourcesContent":["import { type IGrammar, type StateStack } from 'shiki/textmate';\nimport type { BaseCodeOptions, DiffsHighlighter, HighlightedToken, RenderRange } from '../types';\nimport type { TextDocument, TextDocumentChange } from './textDocument';\nexport interface EditorTokenizerProps {\n highlighter: DiffsHighlighter;\n textDocument: TextDocument<unknown>;\n codeOptions: BaseCodeOptions;\n setStyle: (style: string) => void;\n onDeferTokenize: (lines: Map<number, Array<HighlightedToken>>, themeType: 'dark' | 'light') => void;\n __debug?: boolean;\n}\n/** Stoppable code tokenizer for the editor */\nexport declare class EditorTokenizer {\n #private;\n static TOKENIZE_TIME_LIMIT: number;\n get themeType(): 'light' | 'dark';\n constructor({ codeOptions, highlighter, textDocument, setStyle, onDeferTokenize, __debug }: EditorTokenizerProps);\n cleanUp(): void;\n tokenize(change: TextDocumentChange, renderRange?: RenderRange): Map<number, Array<HighlightedToken>>;\n prebuildStateStack(renderRange?: RenderRange): void;\n stopBackgroundTokenize(): void;\n pauseBackgroundTokenize(): void;\n resumeBackgroundTokenize(): void;\n}\nexport declare function tokenizeLine(grammar: IGrammar, colorMap: string[], lineText: string, stateStack: StateStack, timeLimit?: number): {\n ruleStack: StateStack;\n resolvedTokens: Array<HighlightedToken>;\n};\nexport declare function renderLineTokens(tokens: Array<HighlightedToken>, themeType: 'light' | 'dark'): (HTMLElement | string)[];\n//# sourceMappingURL=tokenzier.d.ts.map"],"mappings":";;;;;UAGiBQ,oBAAAA;eACAL;EADAK,YAAAA,EAECF,YAFmB,CAAA,OAAA,CAAA;EACpBH,WAAAA,EAEAD,eAFAC;EACCG,QAAAA,EAAAA,CAAAA,KAAAA,EAAAA,MAAAA,EAAAA,GAAAA,IAAAA;EACDJ,eAAAA,EAAAA,CAAAA,KAAAA,EAEYQ,GAFZR,CAAAA,MAAAA,EAEwBO,KAFxBP,CAE8BE,gBAF9BF,CAAAA,CAAAA,EAAAA,SAAAA,EAAAA,MAAAA,GAAAA,OAAAA,EAAAA,GAAAA,IAAAA;EAE8BE,OAAAA,CAAAA,EAAAA,OAAAA;;;AAAf,cAIXO,eAAAA,CAJW;EAIXA,CAAAA,OAAAA;EAIHC,OAAAA,mBAAAA,EAAAA,MAAAA;EAAaC,IAAAA,SAAAA,CAAAA,CAAAA,EAAAA,OAAAA,GAAAA,MAAAA;EAAaC,WAAAA,CAAAA;IAAAA,WAAAA;IAAAA,WAAAA;IAAAA,YAAAA;IAAAA,QAAAA;IAAAA,eAAAA;IAAAA;EAAAA,CAAAA,EAAoDN,oBAApDM;EAAcC,OAAAA,CAAAA,CAAAA,EAAAA,IAAAA;EAAUC,QAAAA,CAAAA,MAAAA,EAE/CT,kBAF+CS,EAAAA,WAAAA,CAAAA,EAEbX,WAFaW,CAAAA,EAECN,GAFDM,CAAAA,MAAAA,EAEaP,KAFbO,CAEmBZ,gBAFnBY,CAAAA,CAAAA;EAAiBC,kBAAAA,CAAAA,WAAAA,CAAAA,EAGhDZ,WAHgDY,CAAAA,EAAAA,IAAAA;EAAWT,sBAAAA,CAAAA,CAAAA,EAAAA,IAAAA;EAE3ED,uBAAAA,CAAAA,CAAAA,EAAAA,IAAAA;EAAkCF,wBAAAA,CAAAA,CAAAA,EAAAA,IAAAA;;AAA0BI,iBAMzDS,YAAAA,CANyDT,OAAAA,EAMnCT,QANmCS,EAAAA,QAAAA,EAAAA,MAAAA,EAAAA,EAAAA,QAAAA,EAAAA,MAAAA,EAAAA,UAAAA,EAMyBR,UANzBQ,EAAAA,SAAAA,CAAAA,EAAAA,MAAAA,CAAAA,EAAAA;EAAZC,SAAAA,EAOtDT,UAPsDS;EAChCL,cAAAA,EAOjBI,KAPiBJ,CAOXD,gBAPWC,CAAAA;CAAW;AAKxBa,iBAIAC,gBAAAA,CAJY,MAAA,EAIaV,KAJb,CAImBL,gBAJnB,CAAA,EAAA,SAAA,EAAA,OAAA,GAAA,MAAA,CAAA,EAAA,CAIqEgB,WAJrE,GAAA,MAAA,CAAA,EAAA"}
|
package/dist/editor/tokenzier.js
CHANGED
|
@@ -15,84 +15,96 @@ var EditorTokenizer = class EditorTokenizer {
|
|
|
15
15
|
#tokenizeMaxLineLength;
|
|
16
16
|
#setStyle;
|
|
17
17
|
#onDeferTokenize;
|
|
18
|
-
#
|
|
19
|
-
#
|
|
18
|
+
#debug;
|
|
19
|
+
#disposes;
|
|
20
|
+
#stateStack = [INITIAL];
|
|
20
21
|
#lastLine = -1;
|
|
21
22
|
#isStopped = true;
|
|
23
|
+
#isPaused = false;
|
|
22
24
|
#backgroundJobId = 0;
|
|
23
25
|
#backgroundChangedLineRanges;
|
|
24
26
|
#backgroundChangedRangeIndex = 0;
|
|
25
|
-
#
|
|
27
|
+
#isMessageListenerAttached = false;
|
|
28
|
+
#prebuildStateStack = debounce(async (renderRange) => {
|
|
26
29
|
const { startingLine = 0, totalLines = Infinity } = renderRange ?? {};
|
|
27
30
|
const endLine = Math.min(totalLines === Infinity ? Infinity : startingLine + totalLines, this.#textDocument.lineCount);
|
|
28
31
|
if (this.#grammar === void 0) {
|
|
29
32
|
await this.#highlighter.loadLanguage(this.#textDocument.languageId);
|
|
30
33
|
this.#grammar = this.#highlighter.getLanguage(this.#textDocument.languageId);
|
|
31
34
|
}
|
|
32
|
-
this.#
|
|
35
|
+
this.#buildStateStack(endLine);
|
|
33
36
|
}, 500);
|
|
34
37
|
#onMessage = ({ data }) => {
|
|
35
|
-
if (data
|
|
38
|
+
if (typeof data !== "object" || data === null) return;
|
|
39
|
+
const { type, jobId } = data;
|
|
40
|
+
if (type === "tokenize" && typeof jobId === "number" && jobId === this.#backgroundJobId) this.#backgroundTokenize(jobId);
|
|
36
41
|
};
|
|
37
|
-
|
|
42
|
+
get themeType() {
|
|
43
|
+
return this.#themeType;
|
|
44
|
+
}
|
|
45
|
+
constructor({ codeOptions, highlighter, textDocument, setStyle, onDeferTokenize, __debug }) {
|
|
46
|
+
const { themeType = "system", theme = DEFAULT_THEMES, tokenizeMaxLineLength = 1e3 } = codeOptions;
|
|
47
|
+
this.#mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)");
|
|
48
|
+
if (themeType === "system") this.#themeType = this.#mediaQueryList.matches ? "dark" : "light";
|
|
49
|
+
else this.#themeType = themeType;
|
|
50
|
+
if (typeof theme !== "string") {
|
|
51
|
+
const observer = new MutationObserver((mutations) => {
|
|
52
|
+
for (const { type, attributeName } of mutations) if (type === "attributes" && attributeName !== null && (attributeName === "class" || attributeName.startsWith("data-"))) {
|
|
53
|
+
const themeType$1 = getComputedStyle(document.body).colorScheme === "dark" ? "dark" : "light";
|
|
54
|
+
this.#emitThemeChange(theme[themeType$1], themeType$1);
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
observer.observe(document.documentElement, { attributes: true });
|
|
59
|
+
observer.observe(document.body, { attributes: true });
|
|
60
|
+
this.#disposes = [addEventListener(this.#mediaQueryList, "change", (e) => {
|
|
61
|
+
const themeType$1 = e.matches ? "dark" : "light";
|
|
62
|
+
this.#emitThemeChange(theme[themeType$1], themeType$1);
|
|
63
|
+
}), () => observer.disconnect()];
|
|
64
|
+
}
|
|
65
|
+
this.#highlighter = highlighter;
|
|
66
|
+
this.#textDocument = textDocument;
|
|
67
|
+
this.#tokenizeMaxLineLength = tokenizeMaxLineLength;
|
|
68
|
+
this.#setStyle = setStyle;
|
|
69
|
+
this.#onDeferTokenize = onDeferTokenize;
|
|
70
|
+
this.#debug = __debug ?? false;
|
|
71
|
+
if (highlighter.getLoadedLanguages().includes(textDocument.languageId)) this.#grammar = highlighter.getLanguage(textDocument.languageId);
|
|
72
|
+
this.#colorMap = [];
|
|
73
|
+
this.#setTheme(typeof theme === "string" ? theme : theme[this.#themeType]);
|
|
74
|
+
}
|
|
75
|
+
#emitThemeChange(themeName, themeType) {
|
|
38
76
|
this.#themeType = themeType;
|
|
39
77
|
this.#setTheme(themeName);
|
|
40
78
|
this.stopBackgroundTokenize();
|
|
41
|
-
this.#
|
|
79
|
+
this.#stateStack = [INITIAL];
|
|
42
80
|
if (this.#grammar !== void 0 && this.#textDocument.lineCount > 0) this.#scheduleBackgroundTokenize(0);
|
|
43
|
-
}
|
|
44
|
-
#setTheme
|
|
81
|
+
}
|
|
82
|
+
#setTheme(themeName) {
|
|
45
83
|
this.#colorMap = this.#highlighter.setTheme(themeName).colorMap;
|
|
46
84
|
const { colors = {} } = this.#highlighter.getTheme(themeName);
|
|
47
85
|
const selectionBackground = colors["editor.selectionBackground"];
|
|
48
86
|
const lineHighlightBackground = colors["editor.lineHighlightBackground"];
|
|
49
87
|
const gutterForeground = colors["editorLineNumber.foreground"];
|
|
50
88
|
const gutterActiveForeground = colors["editorLineNumber.activeForeground"];
|
|
89
|
+
const cursorForeground = colors["editorCursor.foreground"];
|
|
90
|
+
const findMatchBackground = colors["editor.findMatchBackground"];
|
|
91
|
+
const findMatchHighlightBackground = colors["editor.findMatchHighlightBackground"];
|
|
51
92
|
this.#setStyle(`:host {
|
|
52
93
|
--diffs-editor-selection-bg: ${selectionBackground ?? "var(--diffs-line-bg)"};
|
|
53
94
|
--diffs-editor-line-highlight-bg: ${lineHighlightBackground ?? "var(--diffs-line-bg)"};
|
|
54
95
|
--diffs-editor-line-number-fg: ${gutterForeground ?? "var(--diffs-fg-number)"};
|
|
55
96
|
--diffs-editor-line-number-active-bg: ${lineHighlightBackground ?? "var(--diffs-line-bg, var(--diffs-bg))"};
|
|
56
97
|
--diffs-editor-line-number-active-fg: ${gutterActiveForeground ?? "var(--diffs-selection-number-fg)"};
|
|
98
|
+
${cursorForeground !== void 0 ? `--diffs-editor-cursor-fg: ${cursorForeground};` : ""}
|
|
99
|
+
${findMatchBackground !== void 0 ? `--diffs-editor-match-bg: ${findMatchBackground};` : ""}
|
|
100
|
+
${findMatchHighlightBackground !== void 0 ? `--diffs-editor-match-highlight-bg: ${findMatchHighlightBackground};` : ""}
|
|
57
101
|
}`);
|
|
58
|
-
};
|
|
59
|
-
#watchColorScheme = (theme) => {
|
|
60
|
-
const observer = new MutationObserver((mutations) => {
|
|
61
|
-
for (const { type, attributeName } of mutations) if (type === "attributes" && attributeName !== null && (attributeName === "class" || attributeName.startsWith("data-"))) {
|
|
62
|
-
const themeType = getComputedStyle(document.body).colorScheme === "dark" ? "dark" : "light";
|
|
63
|
-
this.#onThemeChange(theme[themeType], themeType);
|
|
64
|
-
break;
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
observer.observe(document.documentElement, { attributes: true });
|
|
68
|
-
observer.observe(document.body, { attributes: true });
|
|
69
|
-
this.#editorEventDisposes = [addEventListener(this.#mediaQueryList, "change", (e) => {
|
|
70
|
-
const themeType = e.matches ? "dark" : "light";
|
|
71
|
-
this.#onThemeChange(theme[themeType], themeType);
|
|
72
|
-
}), () => observer.disconnect()];
|
|
73
|
-
};
|
|
74
|
-
get themeType() {
|
|
75
|
-
return this.#themeType;
|
|
76
|
-
}
|
|
77
|
-
constructor({ codeOptions, highlighter, textDocument, setStyle, onDeferTokenize }) {
|
|
78
|
-
const { themeType = "system", theme = DEFAULT_THEMES, tokenizeMaxLineLength = 1e3 } = codeOptions;
|
|
79
|
-
this.#mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)");
|
|
80
|
-
if (themeType === "system") this.#themeType = this.#mediaQueryList.matches ? "dark" : "light";
|
|
81
|
-
else this.#themeType = themeType;
|
|
82
|
-
if (typeof theme !== "string") this.#watchColorScheme(theme);
|
|
83
|
-
this.#highlighter = highlighter;
|
|
84
|
-
this.#textDocument = textDocument;
|
|
85
|
-
this.#tokenizeMaxLineLength = tokenizeMaxLineLength;
|
|
86
|
-
this.#setStyle = setStyle;
|
|
87
|
-
this.#onDeferTokenize = onDeferTokenize;
|
|
88
|
-
if (highlighter.getLoadedLanguages().includes(textDocument.languageId)) this.#grammar = highlighter.getLanguage(textDocument.languageId);
|
|
89
|
-
this.#colorMap = [];
|
|
90
|
-
this.#setTheme(typeof theme === "string" ? theme : theme[this.#themeType]);
|
|
91
102
|
}
|
|
92
103
|
cleanUp() {
|
|
104
|
+
this.#detachMessageListener();
|
|
93
105
|
this.stopBackgroundTokenize();
|
|
94
|
-
this.#
|
|
95
|
-
this.#
|
|
106
|
+
this.#disposes?.forEach((dispose) => dispose());
|
|
107
|
+
this.#disposes = void 0;
|
|
96
108
|
}
|
|
97
109
|
tokenize(change, renderRange) {
|
|
98
110
|
if (this.#grammar === void 0) throw new Error("Grammar not loaded");
|
|
@@ -110,42 +122,42 @@ var EditorTokenizer = class EditorTokenizer {
|
|
|
110
122
|
for (const [rangeStart, rangeEnd] of changedLineRanges) if (rangeStart < viewStart) offscreenSyncEnd = Math.max(offscreenSyncEnd, Math.min(rangeEnd, viewStart - 1));
|
|
111
123
|
}
|
|
112
124
|
const shouldFlushOffscreenLines = offscreenSyncEnd >= dirtyStart && (canReuseCachedStates || change.lineDelta < 0);
|
|
113
|
-
if (canReuseCachedStates) this.#
|
|
125
|
+
if (canReuseCachedStates) this.#buildStateStack(dirtyStart);
|
|
114
126
|
else {
|
|
115
|
-
this.#
|
|
116
|
-
if (renderRange === void 0 || dirtyStart >= viewStart) this.#
|
|
127
|
+
this.#stateStack.length = Math.min(this.#stateStack.length, dirtyStart + 1);
|
|
128
|
+
if (renderRange === void 0 || dirtyStart >= viewStart) this.#buildStateStack(viewStart);
|
|
117
129
|
}
|
|
118
130
|
let changedRangeIndex = 0;
|
|
119
131
|
let currentChangedRangeEnd = changedLineRanges[changedRangeIndex][1];
|
|
120
132
|
let backgroundStartLine;
|
|
121
133
|
let backgroundChangedRangeIndex = 0;
|
|
122
134
|
let line = canReuseCachedStates ? changedLineRanges[changedRangeIndex][0] : viewStart;
|
|
123
|
-
let state = this.#
|
|
135
|
+
let state = this.#stateStack[line] ?? INITIAL;
|
|
124
136
|
let settled = false;
|
|
125
137
|
const dirtyLines = /* @__PURE__ */ new Map();
|
|
126
138
|
const offscreenDirtyLines = shouldFlushOffscreenLines ? /* @__PURE__ */ new Map() : void 0;
|
|
127
139
|
if (offscreenDirtyLines !== void 0 && !canReuseCachedStates) {
|
|
128
140
|
const offscreenEnd = Math.min(offscreenSyncEnd + 1, viewStart, renderRangeEndLine);
|
|
129
141
|
if (offscreenEnd > dirtyStart) {
|
|
130
|
-
this.#
|
|
142
|
+
this.#buildStateStack(offscreenEnd);
|
|
131
143
|
let offscreenLine = dirtyStart;
|
|
132
|
-
let offscreenState = this.#
|
|
144
|
+
let offscreenState = this.#stateStack[offscreenLine] ?? INITIAL;
|
|
133
145
|
for (; offscreenLine < offscreenEnd; offscreenLine++) {
|
|
134
146
|
const resolved = this.#tokenizeLineAt(offscreenLine, offscreenState);
|
|
135
147
|
offscreenState = resolved.state;
|
|
136
148
|
offscreenDirtyLines.set(offscreenLine, resolved.resolvedTokens);
|
|
137
149
|
}
|
|
138
|
-
if (canCacheTokenizedStates) this.#
|
|
150
|
+
if (canCacheTokenizedStates) this.#stateStack[offscreenEnd] = offscreenState;
|
|
139
151
|
}
|
|
140
152
|
}
|
|
141
153
|
for (; line < renderRangeEndLine;) {
|
|
142
|
-
const previousNextState = canReuseCachedStates ? this.#
|
|
143
|
-
if (canCacheTokenizedStates) this.#
|
|
154
|
+
const previousNextState = canReuseCachedStates ? this.#stateStack[line + 1] : void 0;
|
|
155
|
+
if (canCacheTokenizedStates) this.#stateStack[line] = state;
|
|
144
156
|
const { resolvedTokens, state: nextState } = this.#tokenizeLineAt(line, state);
|
|
145
157
|
state = nextState;
|
|
146
158
|
if (line >= viewStart) dirtyLines.set(line, resolvedTokens);
|
|
147
159
|
else offscreenDirtyLines?.set(line, resolvedTokens);
|
|
148
|
-
if (canCacheTokenizedStates) this.#
|
|
160
|
+
if (canCacheTokenizedStates) this.#stateStack[line + 1] = state;
|
|
149
161
|
settled = line >= currentChangedRangeEnd && canReuseCachedStates && previousNextState !== void 0 && state.equals(previousNextState);
|
|
150
162
|
if (settled) {
|
|
151
163
|
changedRangeIndex++;
|
|
@@ -156,12 +168,12 @@ var EditorTokenizer = class EditorTokenizer {
|
|
|
156
168
|
backgroundChangedRangeIndex = changedRangeIndex;
|
|
157
169
|
break;
|
|
158
170
|
}
|
|
159
|
-
if (this.#
|
|
171
|
+
if (this.#stateStack[nextRange[0]] === void 0) {
|
|
160
172
|
currentChangedRangeEnd = nextRange[1];
|
|
161
173
|
line++;
|
|
162
174
|
} else {
|
|
163
175
|
line = nextRange[0];
|
|
164
|
-
state = this.#
|
|
176
|
+
state = this.#stateStack[line] ?? state;
|
|
165
177
|
currentChangedRangeEnd = nextRange[1];
|
|
166
178
|
}
|
|
167
179
|
settled = false;
|
|
@@ -169,8 +181,8 @@ var EditorTokenizer = class EditorTokenizer {
|
|
|
169
181
|
}
|
|
170
182
|
line++;
|
|
171
183
|
}
|
|
172
|
-
if (canCacheTokenizedStates) if (line < renderRangeEndLine) this.#
|
|
173
|
-
else this.#
|
|
184
|
+
if (canCacheTokenizedStates) if (line < renderRangeEndLine) this.#stateStack[line + 1] = state;
|
|
185
|
+
else this.#stateStack[line] = state;
|
|
174
186
|
if (offscreenDirtyLines !== void 0 && offscreenDirtyLines.size > 0) this.#onDeferTokenize(offscreenDirtyLines, this.#themeType);
|
|
175
187
|
if (backgroundStartLine !== void 0) this.#scheduleBackgroundTokenize(backgroundStartLine, changedLineRanges, backgroundChangedRangeIndex);
|
|
176
188
|
else if (!settled && line < lineCount) {
|
|
@@ -179,31 +191,61 @@ var EditorTokenizer = class EditorTokenizer {
|
|
|
179
191
|
}
|
|
180
192
|
return dirtyLines;
|
|
181
193
|
}
|
|
182
|
-
|
|
183
|
-
this.#
|
|
194
|
+
prebuildStateStack(renderRange) {
|
|
195
|
+
this.#prebuildStateStack(renderRange);
|
|
184
196
|
}
|
|
185
197
|
stopBackgroundTokenize() {
|
|
186
|
-
|
|
198
|
+
if (this.#isStopped) return;
|
|
187
199
|
this.#isStopped = true;
|
|
200
|
+
this.#isPaused = false;
|
|
188
201
|
this.#lastLine = -1;
|
|
189
202
|
this.#backgroundChangedLineRanges = void 0;
|
|
190
203
|
this.#backgroundChangedRangeIndex = 0;
|
|
204
|
+
this.#detachMessageListener();
|
|
191
205
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
this.#
|
|
195
|
-
this.#
|
|
196
|
-
|
|
197
|
-
|
|
206
|
+
pauseBackgroundTokenize() {
|
|
207
|
+
if (this.#isStopped || this.#isPaused) return;
|
|
208
|
+
if (this.#debug) console.log("[diffs/editor] background tokenization paused", { jobId: this.#backgroundJobId });
|
|
209
|
+
this.#isPaused = true;
|
|
210
|
+
}
|
|
211
|
+
resumeBackgroundTokenize() {
|
|
212
|
+
if (this.#isStopped || !this.#isPaused || this.#grammar === void 0 || this.#lastLine < 0) return;
|
|
213
|
+
if (this.#debug) console.log("[diffs/editor] background tokenization resumed", { jobId: this.#backgroundJobId });
|
|
214
|
+
this.#isPaused = false;
|
|
215
|
+
this.#postTokenizeMessage(this.#backgroundJobId);
|
|
216
|
+
}
|
|
217
|
+
#attachMessageListener() {
|
|
218
|
+
if (this.#isMessageListenerAttached) return;
|
|
198
219
|
globalThis.addEventListener("message", this.#onMessage);
|
|
199
|
-
this.#
|
|
220
|
+
this.#isMessageListenerAttached = true;
|
|
221
|
+
}
|
|
222
|
+
#detachMessageListener() {
|
|
223
|
+
if (!this.#isMessageListenerAttached) return;
|
|
224
|
+
globalThis.removeEventListener("message", this.#onMessage);
|
|
225
|
+
this.#isMessageListenerAttached = false;
|
|
200
226
|
}
|
|
201
|
-
#
|
|
227
|
+
#postTokenizeMessage(jobId) {
|
|
202
228
|
globalThis.postMessage({
|
|
203
229
|
type: "tokenize",
|
|
204
230
|
jobId
|
|
205
231
|
});
|
|
206
232
|
}
|
|
233
|
+
#scheduleBackgroundTokenize(startLine, changedLineRanges, changedRangeIndex = 0) {
|
|
234
|
+
const jobId = ++this.#backgroundJobId;
|
|
235
|
+
if (this.#debug) console.log("[diffs/editor] background tokenization scheduled", {
|
|
236
|
+
jobId,
|
|
237
|
+
startLine,
|
|
238
|
+
changedLineRanges,
|
|
239
|
+
changedRangeIndex
|
|
240
|
+
});
|
|
241
|
+
this.#isStopped = false;
|
|
242
|
+
this.#isPaused = false;
|
|
243
|
+
this.#lastLine = startLine;
|
|
244
|
+
this.#backgroundChangedLineRanges = changedLineRanges;
|
|
245
|
+
this.#backgroundChangedRangeIndex = changedRangeIndex;
|
|
246
|
+
this.#attachMessageListener();
|
|
247
|
+
this.#postTokenizeMessage(jobId);
|
|
248
|
+
}
|
|
207
249
|
#tokenizeLineAt(line, state) {
|
|
208
250
|
if (this.#grammar === void 0) throw new Error("Grammar not loaded");
|
|
209
251
|
const lineText = this.#textDocument.getLineText(line);
|
|
@@ -232,32 +274,32 @@ var EditorTokenizer = class EditorTokenizer {
|
|
|
232
274
|
state: result.ruleStack
|
|
233
275
|
};
|
|
234
276
|
}
|
|
235
|
-
#
|
|
277
|
+
#buildStateStack(endAt) {
|
|
236
278
|
const boundedEndAt = Math.min(Math.max(0, endAt), this.#textDocument.lineCount);
|
|
237
|
-
if (this.#
|
|
238
|
-
let line = this.#
|
|
239
|
-
let state = this.#
|
|
279
|
+
if (this.#stateStack.length > boundedEndAt || this.#grammar === void 0) return;
|
|
280
|
+
let line = this.#stateStack.length - 1;
|
|
281
|
+
let state = this.#stateStack[line] ?? INITIAL;
|
|
240
282
|
for (; line < boundedEndAt; line++) {
|
|
241
|
-
this.#
|
|
283
|
+
this.#stateStack[line] = state;
|
|
242
284
|
const lineText = this.#textDocument.getLineText(line);
|
|
243
285
|
if (lineText.length <= this.#tokenizeMaxLineLength && lineText !== "" && lineText.trim() !== "") state = this.#grammar.tokenizeLine2(lineText, state, EditorTokenizer.TOKENIZE_TIME_LIMIT).ruleStack;
|
|
244
286
|
}
|
|
245
|
-
this.#
|
|
287
|
+
this.#stateStack[line] = state;
|
|
246
288
|
}
|
|
247
289
|
#backgroundTokenize(jobId) {
|
|
248
|
-
if (this.#isStopped || this.#grammar === void 0 || jobId !== this.#backgroundJobId) return;
|
|
290
|
+
if (this.#isStopped || this.#isPaused || this.#grammar === void 0 || jobId !== this.#backgroundJobId) return;
|
|
249
291
|
const t = performance.now();
|
|
250
292
|
const lines = /* @__PURE__ */ new Map();
|
|
251
293
|
const totalLines = this.#textDocument.lineCount;
|
|
252
294
|
const changedLineRanges = this.#backgroundChangedLineRanges;
|
|
253
295
|
let line = this.#lastLine;
|
|
254
|
-
let state = this.#
|
|
296
|
+
let state = this.#stateStack[line] ?? INITIAL;
|
|
255
297
|
let settled = false;
|
|
256
298
|
let changedRangeIndex = this.#backgroundChangedRangeIndex;
|
|
257
299
|
let currentChangedRangeEnd = changedLineRanges?.[changedRangeIndex]?.[1];
|
|
258
300
|
for (; line < totalLines;) {
|
|
259
|
-
this.#
|
|
260
|
-
const previousNextState = currentChangedRangeEnd !== void 0 ? this.#
|
|
301
|
+
this.#stateStack[line] = state;
|
|
302
|
+
const previousNextState = currentChangedRangeEnd !== void 0 ? this.#stateStack[line + 1] : void 0;
|
|
261
303
|
const lineText = this.#textDocument.getLineText(line);
|
|
262
304
|
if (lineText.length > this.#tokenizeMaxLineLength) {
|
|
263
305
|
console.warn(`[diffs] Line(${line}) too long to tokenize: ${lineText.length}`);
|
|
@@ -276,7 +318,7 @@ var EditorTokenizer = class EditorTokenizer {
|
|
|
276
318
|
lines.set(line, ret.resolvedTokens);
|
|
277
319
|
state = ret.ruleStack;
|
|
278
320
|
}
|
|
279
|
-
this.#
|
|
321
|
+
this.#stateStack[line + 1] = state;
|
|
280
322
|
settled = currentChangedRangeEnd !== void 0 && line >= currentChangedRangeEnd && previousNextState !== void 0 && state.equals(previousNextState);
|
|
281
323
|
line++;
|
|
282
324
|
if (settled) {
|
|
@@ -284,25 +326,25 @@ var EditorTokenizer = class EditorTokenizer {
|
|
|
284
326
|
const nextRange = changedLineRanges?.[changedRangeIndex];
|
|
285
327
|
if (nextRange === void 0) break;
|
|
286
328
|
currentChangedRangeEnd = nextRange[1];
|
|
287
|
-
if (this.#
|
|
329
|
+
if (this.#stateStack[nextRange[0]] === void 0) settled = false;
|
|
288
330
|
else {
|
|
289
331
|
line = nextRange[0];
|
|
290
|
-
state = this.#
|
|
332
|
+
state = this.#stateStack[line] ?? state;
|
|
291
333
|
settled = false;
|
|
292
334
|
continue;
|
|
293
335
|
}
|
|
294
336
|
}
|
|
295
|
-
if (performance.now() - t >
|
|
337
|
+
if (performance.now() - t > 1) break;
|
|
296
338
|
}
|
|
297
339
|
this.#onDeferTokenize(lines, this.#themeType);
|
|
298
|
-
if (this.#isStopped || jobId !== this.#backgroundJobId) return;
|
|
340
|
+
if (this.#isStopped || this.#isPaused || jobId !== this.#backgroundJobId) return;
|
|
299
341
|
if (settled || line >= totalLines) {
|
|
300
342
|
this.stopBackgroundTokenize();
|
|
301
343
|
return;
|
|
302
344
|
}
|
|
303
345
|
this.#lastLine = line;
|
|
304
346
|
this.#backgroundChangedRangeIndex = changedRangeIndex;
|
|
305
|
-
this.#
|
|
347
|
+
this.#postTokenizeMessage(jobId);
|
|
306
348
|
}
|
|
307
349
|
};
|
|
308
350
|
function tokenizeLine(grammar, colorMap, lineText, stateStack, timeLimit) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenzier.js","names":["#textDocument","#grammar","#highlighter","#buildStateStackMap","#backgroundJobId","#backgroundTokenize","#themeType","#setTheme","#stateStackCache","#scheduleBackgroundTokenize","#colorMap","#setStyle","#onThemeChange","#editorEventDisposes","#mediaQueryList","#watchColorScheme","#tokenizeMaxLineLength","#onDeferTokenize","changedLineRanges: readonly [number, number][]","backgroundStartLine: number | undefined","dirtyLines: Map<number, Array<HighlightedToken>>","offscreenDirtyLines:\n | Map<number, Array<HighlightedToken>>\n | undefined","#tokenizeLineAt","#prebuildStateStackMap","#onMessage","#isStopped","#lastLine","#backgroundChangedLineRanges","#backgroundChangedRangeIndex","#postBackgroundTokenizeMessage","resolvedTokens: Array<HighlightedToken>"],"sources":["../../src/editor/tokenzier.ts"],"sourcesContent":["import {\n EncodedTokenMetadata,\n type IGrammar,\n INITIAL,\n type StateStack,\n} from 'shiki/textmate';\n\nimport { DEFAULT_THEMES } from '../constants';\nimport type {\n BaseCodeOptions,\n DiffsHighlighter,\n HighlightedToken,\n RenderRange,\n ThemesType,\n} from '../types';\nimport type { TextDocument, TextDocumentChange } from './textDocument';\nimport { addEventListener, debounce, h } from './utils';\n\nexport interface EditorTokenizerProps {\n highlighter: DiffsHighlighter;\n textDocument: TextDocument<unknown>;\n codeOptions: BaseCodeOptions;\n setStyle: (style: string) => void;\n onDeferTokenize: (\n lines: Map<number, Array<HighlightedToken>>,\n themeType: 'dark' | 'light'\n ) => void;\n}\n\n/** Stoppable code tokenizer for the editor */\nexport class EditorTokenizer {\n static TOKENIZE_TIME_LIMIT = 500;\n\n #highlighter: DiffsHighlighter;\n #grammar: IGrammar | undefined;\n #mediaQueryList: MediaQueryList;\n #themeType: 'light' | 'dark';\n #colorMap: string[];\n #textDocument: TextDocument<unknown>;\n #tokenizeMaxLineLength: number;\n #setStyle: EditorTokenizerProps['setStyle'];\n #onDeferTokenize: EditorTokenizerProps['onDeferTokenize'];\n #editorEventDisposes?: (() => void)[];\n\n // state\n #stateStackCache: StateStack[] = [INITIAL];\n #lastLine: number = -1;\n #isStopped: boolean = true;\n #backgroundJobId: number = 0;\n #backgroundChangedLineRanges: readonly [number, number][] | undefined;\n #backgroundChangedRangeIndex: number = 0;\n\n #prebuildStateStackMap = debounce(async (renderRange?: RenderRange) => {\n const { startingLine = 0, totalLines = Infinity } = renderRange ?? {};\n const endLine = Math.min(\n totalLines === Infinity ? Infinity : startingLine + totalLines,\n this.#textDocument.lineCount\n );\n if (this.#grammar === undefined) {\n await this.#highlighter.loadLanguage(this.#textDocument.languageId);\n this.#grammar = this.#highlighter.getLanguage(\n this.#textDocument.languageId\n );\n }\n this.#buildStateStackMap(endLine);\n }, 500);\n\n #onMessage = ({\n data,\n }: MessageEvent<{ type: 'tokenize'; jobId: number }>) => {\n if (data.type === 'tokenize' && data.jobId === this.#backgroundJobId) {\n this.#backgroundTokenize(data.jobId);\n }\n };\n\n // By default, diffs components support dual themes, but the tokenizer only renders\n // the preferred theme. When the theme type is changed, the tokenizer will re-tokenize the document.\n #onThemeChange = (themeName: string, themeType: 'light' | 'dark') => {\n this.#themeType = themeType;\n this.#setTheme(themeName);\n this.stopBackgroundTokenize();\n this.#stateStackCache = [INITIAL];\n if (this.#grammar !== undefined && this.#textDocument.lineCount > 0) {\n this.#scheduleBackgroundTokenize(0);\n }\n };\n\n #setTheme = (themeName: string): void => {\n this.#colorMap = this.#highlighter.setTheme(themeName).colorMap;\n const { colors = {} } = this.#highlighter.getTheme(themeName);\n const selectionBackground = colors['editor.selectionBackground'];\n const lineHighlightBackground = colors['editor.lineHighlightBackground'];\n const gutterForeground = colors['editorLineNumber.foreground'];\n const gutterActiveForeground = colors['editorLineNumber.activeForeground'];\n this.#setStyle(`:host {\n --diffs-editor-selection-bg: ${selectionBackground ?? 'var(--diffs-line-bg)'};\n --diffs-editor-line-highlight-bg: ${lineHighlightBackground ?? 'var(--diffs-line-bg)'};\n --diffs-editor-line-number-fg: ${gutterForeground ?? 'var(--diffs-fg-number)'};\n --diffs-editor-line-number-active-bg: ${lineHighlightBackground ?? 'var(--diffs-line-bg, var(--diffs-bg))'};\n --diffs-editor-line-number-active-fg: ${gutterActiveForeground ?? 'var(--diffs-selection-number-fg)'};\n }`);\n };\n\n #watchColorScheme = (theme: ThemesType) => {\n const observer = new MutationObserver((mutations) => {\n for (const { type, attributeName } of mutations) {\n if (\n type === 'attributes' &&\n attributeName !== null &&\n (attributeName === 'class' || attributeName.startsWith('data-'))\n ) {\n const themeType =\n getComputedStyle(document.body).colorScheme === 'dark'\n ? 'dark'\n : 'light';\n this.#onThemeChange(theme[themeType], themeType);\n break;\n }\n }\n });\n observer.observe(document.documentElement, { attributes: true });\n observer.observe(document.body, { attributes: true });\n this.#editorEventDisposes = [\n addEventListener(this.#mediaQueryList, 'change', (e) => {\n const themeType = e.matches ? 'dark' : 'light';\n this.#onThemeChange(theme[themeType], themeType);\n }),\n () => observer.disconnect(),\n ];\n };\n\n get themeType(): 'light' | 'dark' {\n return this.#themeType;\n }\n\n constructor({\n codeOptions,\n highlighter,\n textDocument,\n setStyle,\n onDeferTokenize,\n }: EditorTokenizerProps) {\n const {\n themeType = 'system',\n theme = DEFAULT_THEMES,\n tokenizeMaxLineLength = 1000,\n } = codeOptions;\n this.#mediaQueryList = window.matchMedia('(prefers-color-scheme: dark)');\n if (themeType === 'system') {\n this.#themeType = this.#mediaQueryList.matches ? 'dark' : 'light';\n } else {\n this.#themeType = themeType;\n }\n if (typeof theme !== 'string') {\n this.#watchColorScheme(theme);\n }\n this.#highlighter = highlighter;\n this.#textDocument = textDocument;\n this.#tokenizeMaxLineLength = tokenizeMaxLineLength;\n this.#setStyle = setStyle;\n this.#onDeferTokenize = onDeferTokenize;\n if (highlighter.getLoadedLanguages().includes(textDocument.languageId)) {\n this.#grammar = highlighter.getLanguage(textDocument.languageId);\n }\n this.#colorMap = [];\n this.#setTheme(typeof theme === 'string' ? theme : theme[this.#themeType]);\n }\n\n cleanUp(): void {\n this.stopBackgroundTokenize();\n this.#editorEventDisposes?.forEach((dispose) => dispose());\n this.#editorEventDisposes = undefined;\n }\n\n // to use `tokenize`, call `prebuildStateStackMap` first to prebuild\n // the state stack map for the given render range.\n tokenize(\n change: TextDocumentChange,\n renderRange?: RenderRange\n ): Map<number, Array<HighlightedToken>> {\n if (this.#grammar === undefined) {\n throw new Error('Grammar not loaded');\n }\n\n const { lineCount } = this.#textDocument;\n const { startingLine = 0, totalLines = Infinity } = renderRange ?? {};\n const renderRangeEndLine =\n totalLines === Infinity\n ? lineCount\n : Math.min(startingLine + totalLines, lineCount);\n\n const dirtyStart = change.startLine;\n const viewStart = Math.max(startingLine, dirtyStart);\n const crossesRenderRangeEnd =\n renderRange !== undefined &&\n totalLines !== Infinity &&\n change.lineDelta > 0 &&\n dirtyStart < renderRangeEndLine &&\n change.endLine >= renderRangeEndLine;\n const canReuseCachedStates = change.lineDelta === 0;\n const canCacheTokenizedStates =\n canReuseCachedStates ||\n renderRange === undefined ||\n dirtyStart >= viewStart;\n const changedLineRanges: readonly [number, number][] = canReuseCachedStates\n ? (change.changedLineRanges ?? [[dirtyStart, change.endLine]])\n : [[dirtyStart, change.endLine]];\n let offscreenSyncEnd = -1;\n if (dirtyStart < viewStart) {\n for (const [rangeStart, rangeEnd] of changedLineRanges) {\n if (rangeStart < viewStart) {\n offscreenSyncEnd = Math.max(\n offscreenSyncEnd,\n Math.min(rangeEnd, viewStart - 1)\n );\n }\n }\n }\n const shouldFlushOffscreenLines =\n offscreenSyncEnd >= dirtyStart &&\n (canReuseCachedStates || change.lineDelta < 0);\n if (canReuseCachedStates) {\n this.#buildStateStackMap(dirtyStart);\n } else {\n this.#stateStackCache.length = Math.min(\n this.#stateStackCache.length,\n dirtyStart + 1\n );\n if (renderRange === undefined || dirtyStart >= viewStart) {\n this.#buildStateStackMap(viewStart);\n }\n }\n\n let changedRangeIndex = 0;\n let currentChangedRangeEnd = changedLineRanges[changedRangeIndex][1];\n let backgroundStartLine: number | undefined;\n let backgroundChangedRangeIndex = 0;\n let line = canReuseCachedStates\n ? changedLineRanges[changedRangeIndex][0]\n : viewStart;\n let state = this.#stateStackCache[line] ?? INITIAL;\n let settled = false;\n const dirtyLines: Map<number, Array<HighlightedToken>> = new Map();\n const offscreenDirtyLines:\n | Map<number, Array<HighlightedToken>>\n | undefined = shouldFlushOffscreenLines ? new Map() : undefined;\n if (offscreenDirtyLines !== undefined && !canReuseCachedStates) {\n const offscreenEnd = Math.min(\n offscreenSyncEnd + 1,\n viewStart,\n renderRangeEndLine\n );\n if (offscreenEnd > dirtyStart) {\n this.#buildStateStackMap(offscreenEnd);\n let offscreenLine = dirtyStart;\n let offscreenState = this.#stateStackCache[offscreenLine] ?? INITIAL;\n for (; offscreenLine < offscreenEnd; offscreenLine++) {\n const resolved = this.#tokenizeLineAt(offscreenLine, offscreenState);\n offscreenState = resolved.state;\n offscreenDirtyLines.set(offscreenLine, resolved.resolvedTokens);\n }\n if (canCacheTokenizedStates) {\n this.#stateStackCache[offscreenEnd] = offscreenState;\n }\n }\n }\n for (; line < renderRangeEndLine; ) {\n const previousNextState = canReuseCachedStates\n ? this.#stateStackCache[line + 1]\n : undefined;\n if (canCacheTokenizedStates) {\n this.#stateStackCache[line] = state;\n }\n\n const { resolvedTokens, state: nextState } = this.#tokenizeLineAt(\n line,\n state\n );\n state = nextState;\n\n if (line >= viewStart) {\n dirtyLines.set(line, resolvedTokens);\n } else {\n offscreenDirtyLines?.set(line, resolvedTokens);\n }\n\n if (canCacheTokenizedStates) {\n this.#stateStackCache[line + 1] = state;\n }\n settled =\n line >= currentChangedRangeEnd &&\n canReuseCachedStates &&\n previousNextState !== undefined &&\n state.equals(previousNextState);\n if (settled) {\n changedRangeIndex++;\n const nextRange = changedLineRanges[changedRangeIndex];\n if (nextRange === undefined) {\n break;\n }\n if (nextRange[0] >= renderRangeEndLine) {\n backgroundStartLine = nextRange[0];\n backgroundChangedRangeIndex = changedRangeIndex;\n break;\n }\n if (this.#stateStackCache[nextRange[0]] === undefined) {\n currentChangedRangeEnd = nextRange[1];\n line++;\n } else {\n line = nextRange[0];\n state = this.#stateStackCache[line] ?? state;\n currentChangedRangeEnd = nextRange[1];\n }\n settled = false;\n continue;\n }\n line++;\n }\n\n if (canCacheTokenizedStates) {\n if (line < renderRangeEndLine) {\n this.#stateStackCache[line + 1] = state;\n } else {\n this.#stateStackCache[line] = state;\n }\n }\n\n if (offscreenDirtyLines !== undefined && offscreenDirtyLines.size > 0) {\n this.#onDeferTokenize(offscreenDirtyLines, this.#themeType);\n }\n\n if (backgroundStartLine !== undefined) {\n this.#scheduleBackgroundTokenize(\n backgroundStartLine,\n changedLineRanges,\n backgroundChangedRangeIndex\n );\n } else if (!settled && line < lineCount) {\n const backgroundLine =\n crossesRenderRangeEnd && dirtyStart >= viewStart\n ? renderRangeEndLine\n : dirtyStart < viewStart && !canReuseCachedStates\n ? dirtyStart\n : line;\n this.#scheduleBackgroundTokenize(\n backgroundLine,\n canReuseCachedStates ? changedLineRanges : undefined,\n changedRangeIndex\n );\n }\n\n return dirtyLines;\n }\n\n prebuildStateStackMap(renderRange?: RenderRange): void {\n this.#prebuildStateStackMap(renderRange);\n }\n\n stopBackgroundTokenize(): void {\n removeEventListener('message', this.#onMessage);\n this.#isStopped = true;\n this.#lastLine = -1;\n this.#backgroundChangedLineRanges = undefined;\n this.#backgroundChangedRangeIndex = 0;\n }\n\n #scheduleBackgroundTokenize(\n startLine: number,\n changedLineRanges?: readonly [number, number][],\n changedRangeIndex = 0\n ): void {\n const jobId = ++this.#backgroundJobId;\n\n this.#isStopped = false;\n this.#lastLine = startLine;\n this.#backgroundChangedLineRanges = changedLineRanges;\n this.#backgroundChangedRangeIndex = changedRangeIndex;\n\n globalThis.addEventListener('message', this.#onMessage);\n this.#postBackgroundTokenizeMessage(jobId);\n }\n\n #postBackgroundTokenizeMessage(jobId: number): void {\n // use `postMessage` instead of `setTimeout(fn, 0)` to avoid 4ms delay\n globalThis.postMessage({ type: 'tokenize', jobId });\n }\n\n #tokenizeLineAt(\n line: number,\n state: StateStack\n ): { resolvedTokens: Array<HighlightedToken>; state: StateStack } {\n if (this.#grammar === undefined) {\n throw new Error('Grammar not loaded');\n }\n const lineText = this.#textDocument.getLineText(line);\n if (lineText.length > this.#tokenizeMaxLineLength) {\n console.warn(\n `[diffs] Line(${line}) too long to tokenize: ${lineText.length}`\n );\n return { resolvedTokens: [[0, '', lineText]], state };\n }\n if (lineText === '' || lineText.trim() === '') {\n return { resolvedTokens: [[0, '', lineText]], state };\n }\n const result = tokenizeLine(\n this.#grammar,\n this.#colorMap,\n lineText,\n state,\n EditorTokenizer.TOKENIZE_TIME_LIMIT\n );\n return {\n resolvedTokens: result.resolvedTokens,\n state: result.ruleStack,\n };\n }\n\n #buildStateStackMap(endAt: number) {\n const boundedEndAt = Math.min(\n Math.max(0, endAt),\n this.#textDocument.lineCount\n );\n if (\n this.#stateStackCache.length > boundedEndAt ||\n this.#grammar === undefined\n ) {\n return;\n }\n let line = this.#stateStackCache.length - 1;\n let state = this.#stateStackCache[line] ?? INITIAL;\n for (; line < boundedEndAt; line++) {\n this.#stateStackCache[line] = state;\n const lineText = this.#textDocument.getLineText(line);\n if (\n lineText.length <= this.#tokenizeMaxLineLength &&\n lineText !== '' &&\n lineText.trim() !== ''\n ) {\n state = this.#grammar.tokenizeLine2(\n lineText,\n state,\n EditorTokenizer.TOKENIZE_TIME_LIMIT\n ).ruleStack;\n }\n }\n this.#stateStackCache[line] = state;\n }\n\n #backgroundTokenize(jobId: number) {\n if (\n this.#isStopped ||\n this.#grammar === undefined ||\n jobId !== this.#backgroundJobId\n ) {\n return;\n }\n\n const t = performance.now();\n const lines = new Map<number, Array<HighlightedToken>>();\n const totalLines = this.#textDocument.lineCount;\n const changedLineRanges = this.#backgroundChangedLineRanges;\n\n let line = this.#lastLine;\n let state = this.#stateStackCache[line] ?? INITIAL;\n let settled = false;\n let changedRangeIndex = this.#backgroundChangedRangeIndex;\n let currentChangedRangeEnd = changedLineRanges?.[changedRangeIndex]?.[1];\n for (; line < totalLines; ) {\n this.#stateStackCache[line] = state;\n\n const previousNextState =\n currentChangedRangeEnd !== undefined\n ? this.#stateStackCache[line + 1]\n : undefined;\n const lineText = this.#textDocument.getLineText(line);\n if (lineText.length > this.#tokenizeMaxLineLength) {\n console.warn(\n `[diffs] Line(${line}) too long to tokenize: ${lineText.length}`\n );\n lines.set(line, [[0, '', lineText]]);\n } else if (lineText === '' || lineText.trim() === '') {\n lines.set(line, [[0, '', lineText]]);\n } else {\n const ret = tokenizeLine(\n this.#grammar,\n this.#colorMap,\n lineText,\n state,\n EditorTokenizer.TOKENIZE_TIME_LIMIT\n );\n lines.set(line, ret.resolvedTokens);\n state = ret.ruleStack;\n }\n\n this.#stateStackCache[line + 1] = state;\n settled =\n currentChangedRangeEnd !== undefined &&\n line >= currentChangedRangeEnd &&\n previousNextState !== undefined &&\n state.equals(previousNextState);\n line++;\n if (settled) {\n changedRangeIndex++;\n const nextRange = changedLineRanges?.[changedRangeIndex];\n if (nextRange === undefined) {\n break;\n }\n currentChangedRangeEnd = nextRange[1];\n if (this.#stateStackCache[nextRange[0]] === undefined) {\n settled = false;\n } else {\n line = nextRange[0];\n state = this.#stateStackCache[line] ?? state;\n settled = false;\n continue;\n }\n }\n\n // limit the time of partial tokenize to 2ms\n if (performance.now() - t > 2) {\n break;\n }\n }\n\n this.#onDeferTokenize(lines, this.#themeType);\n if (this.#isStopped || jobId !== this.#backgroundJobId) {\n return;\n }\n\n if (settled || line >= totalLines) {\n this.stopBackgroundTokenize();\n return;\n }\n\n this.#lastLine = line;\n this.#backgroundChangedRangeIndex = changedRangeIndex;\n this.#postBackgroundTokenizeMessage(jobId);\n }\n}\n\nexport function tokenizeLine(\n grammar: IGrammar,\n colorMap: string[],\n lineText: string,\n stateStack: StateStack,\n timeLimit?: number\n): {\n ruleStack: StateStack;\n resolvedTokens: Array<HighlightedToken>;\n} {\n const result = grammar.tokenizeLine2(lineText, stateStack, timeLimit);\n if (result.stoppedEarly) {\n console.warn(\n `[diffs] Time limit reached when tokenizing line: ${lineText.substring(0, 100)}`\n );\n }\n const rawTokens = result.tokens;\n const tokensLength = rawTokens.length / 2;\n const resolvedTokens: Array<HighlightedToken> = [];\n for (let j = 0; j < tokensLength; j++) {\n const offset = rawTokens[2 * j];\n const nextOffset =\n j + 1 < tokensLength ? rawTokens[2 * j + 2] : lineText.length;\n if (offset === nextOffset) {\n // should never reach here, skip if happens anyway\n continue;\n }\n const metadata = rawTokens[2 * j + 1];\n const bg = EncodedTokenMetadata.getForeground(metadata);\n const fg = colorMap[bg];\n const tokenText = lineText.slice(offset, nextOffset);\n resolvedTokens.push([offset, fg, tokenText]);\n }\n return {\n ruleStack: result.ruleStack,\n resolvedTokens,\n };\n}\n\nexport function renderLineTokens(\n tokens: Array<HighlightedToken>,\n themeType: 'light' | 'dark'\n): (HTMLElement | string)[] {\n return tokens.map(([char, fg, textContent]) => {\n if (char === 0 && fg === '') {\n if (textContent === '') {\n return h('br');\n }\n return textContent;\n }\n return h('span', {\n dataset: {\n char: char.toString(),\n },\n style: `--diffs-token-${themeType}:${fg};`,\n textContent: textContent,\n });\n });\n}\n"],"mappings":";;;;;;AA8BA,IAAa,kBAAb,MAAa,gBAAgB;CAC3B,OAAO,sBAAsB;CAE7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA,mBAAiC,CAAC,QAAQ;CAC1C,YAAoB;CACpB,aAAsB;CACtB,mBAA2B;CAC3B;CACA,+BAAuC;CAEvC,yBAAyB,SAAS,OAAO,gBAA8B;EACrE,MAAM,EAAE,eAAe,GAAG,aAAa,aAAa,eAAe,EAAE;EACrE,MAAM,UAAU,KAAK,IACnB,eAAe,WAAW,WAAW,eAAe,YACpD,MAAKA,aAAc,UACpB;AACD,MAAI,MAAKC,YAAa,QAAW;AAC/B,SAAM,MAAKC,YAAa,aAAa,MAAKF,aAAc,WAAW;AACnE,SAAKC,UAAW,MAAKC,YAAa,YAChC,MAAKF,aAAc,WACpB;;AAEH,QAAKG,mBAAoB,QAAQ;IAChC,IAAI;CAEP,cAAc,EACZ,WACuD;AACvD,MAAI,KAAK,SAAS,cAAc,KAAK,UAAU,MAAKC,gBAClD,OAAKC,mBAAoB,KAAK,MAAM;;CAMxC,kBAAkB,WAAmB,cAAgC;AACnE,QAAKC,YAAa;AAClB,QAAKC,SAAU,UAAU;AACzB,OAAK,wBAAwB;AAC7B,QAAKC,kBAAmB,CAAC,QAAQ;AACjC,MAAI,MAAKP,YAAa,UAAa,MAAKD,aAAc,YAAY,EAChE,OAAKS,2BAA4B,EAAE;;CAIvC,aAAa,cAA4B;AACvC,QAAKC,WAAY,MAAKR,YAAa,SAAS,UAAU,CAAC;EACvD,MAAM,EAAE,SAAS,EAAE,KAAK,MAAKA,YAAa,SAAS,UAAU;EAC7D,MAAM,sBAAsB,OAAO;EACnC,MAAM,0BAA0B,OAAO;EACvC,MAAM,mBAAmB,OAAO;EAChC,MAAM,yBAAyB,OAAO;AACtC,QAAKS,SAAU;qCACkB,uBAAuB,uBAAuB;0CACzC,2BAA2B,uBAAuB;uCACrD,oBAAoB,yBAAyB;8CACtC,2BAA2B,wCAAwC;8CACnE,0BAA0B,mCAAmC;OACpG;;CAGL,qBAAqB,UAAsB;EACzC,MAAM,WAAW,IAAI,kBAAkB,cAAc;AACnD,QAAK,MAAM,EAAE,MAAM,mBAAmB,UACpC,KACE,SAAS,gBACT,kBAAkB,SACjB,kBAAkB,WAAW,cAAc,WAAW,QAAQ,GAC/D;IACA,MAAM,YACJ,iBAAiB,SAAS,KAAK,CAAC,gBAAgB,SAC5C,SACA;AACN,UAAKC,cAAe,MAAM,YAAY,UAAU;AAChD;;IAGJ;AACF,WAAS,QAAQ,SAAS,iBAAiB,EAAE,YAAY,MAAM,CAAC;AAChE,WAAS,QAAQ,SAAS,MAAM,EAAE,YAAY,MAAM,CAAC;AACrD,QAAKC,sBAAuB,CAC1B,iBAAiB,MAAKC,gBAAiB,WAAW,MAAM;GACtD,MAAM,YAAY,EAAE,UAAU,SAAS;AACvC,SAAKF,cAAe,MAAM,YAAY,UAAU;IAChD,QACI,SAAS,YAAY,CAC5B;;CAGH,IAAI,YAA8B;AAChC,SAAO,MAAKN;;CAGd,YAAY,EACV,aACA,aACA,cACA,UACA,mBACuB;EACvB,MAAM,EACJ,YAAY,UACZ,QAAQ,gBACR,wBAAwB,QACtB;AACJ,QAAKQ,iBAAkB,OAAO,WAAW,+BAA+B;AACxE,MAAI,cAAc,SAChB,OAAKR,YAAa,MAAKQ,eAAgB,UAAU,SAAS;MAE1D,OAAKR,YAAa;AAEpB,MAAI,OAAO,UAAU,SACnB,OAAKS,iBAAkB,MAAM;AAE/B,QAAKb,cAAe;AACpB,QAAKF,eAAgB;AACrB,QAAKgB,wBAAyB;AAC9B,QAAKL,WAAY;AACjB,QAAKM,kBAAmB;AACxB,MAAI,YAAY,oBAAoB,CAAC,SAAS,aAAa,WAAW,CACpE,OAAKhB,UAAW,YAAY,YAAY,aAAa,WAAW;AAElE,QAAKS,WAAY,EAAE;AACnB,QAAKH,SAAU,OAAO,UAAU,WAAW,QAAQ,MAAM,MAAKD,WAAY;;CAG5E,UAAgB;AACd,OAAK,wBAAwB;AAC7B,QAAKO,qBAAsB,SAAS,YAAY,SAAS,CAAC;AAC1D,QAAKA,sBAAuB;;CAK9B,SACE,QACA,aACsC;AACtC,MAAI,MAAKZ,YAAa,OACpB,OAAM,IAAI,MAAM,qBAAqB;EAGvC,MAAM,EAAE,cAAc,MAAKD;EAC3B,MAAM,EAAE,eAAe,GAAG,aAAa,aAAa,eAAe,EAAE;EACrE,MAAM,qBACJ,eAAe,WACX,YACA,KAAK,IAAI,eAAe,YAAY,UAAU;EAEpD,MAAM,aAAa,OAAO;EAC1B,MAAM,YAAY,KAAK,IAAI,cAAc,WAAW;EACpD,MAAM,wBACJ,gBAAgB,UAChB,eAAe,YACf,OAAO,YAAY,KACnB,aAAa,sBACb,OAAO,WAAW;EACpB,MAAM,uBAAuB,OAAO,cAAc;EAClD,MAAM,0BACJ,wBACA,gBAAgB,UAChB,cAAc;EAChB,MAAMkB,oBAAiD,uBAClD,OAAO,qBAAqB,CAAC,CAAC,YAAY,OAAO,QAAQ,CAAC,GAC3D,CAAC,CAAC,YAAY,OAAO,QAAQ,CAAC;EAClC,IAAI,mBAAmB;AACvB,MAAI,aAAa,WACf;QAAK,MAAM,CAAC,YAAY,aAAa,kBACnC,KAAI,aAAa,UACf,oBAAmB,KAAK,IACtB,kBACA,KAAK,IAAI,UAAU,YAAY,EAAE,CAClC;;EAIP,MAAM,4BACJ,oBAAoB,eACnB,wBAAwB,OAAO,YAAY;AAC9C,MAAI,qBACF,OAAKf,mBAAoB,WAAW;OAC/B;AACL,SAAKK,gBAAiB,SAAS,KAAK,IAClC,MAAKA,gBAAiB,QACtB,aAAa,EACd;AACD,OAAI,gBAAgB,UAAa,cAAc,UAC7C,OAAKL,mBAAoB,UAAU;;EAIvC,IAAI,oBAAoB;EACxB,IAAI,yBAAyB,kBAAkB,mBAAmB;EAClE,IAAIgB;EACJ,IAAI,8BAA8B;EAClC,IAAI,OAAO,uBACP,kBAAkB,mBAAmB,KACrC;EACJ,IAAI,QAAQ,MAAKX,gBAAiB,SAAS;EAC3C,IAAI,UAAU;EACd,MAAMY,6BAAmD,IAAI,KAAK;EAClE,MAAMC,sBAEU,4CAA4B,IAAI,KAAK,GAAG;AACxD,MAAI,wBAAwB,UAAa,CAAC,sBAAsB;GAC9D,MAAM,eAAe,KAAK,IACxB,mBAAmB,GACnB,WACA,mBACD;AACD,OAAI,eAAe,YAAY;AAC7B,UAAKlB,mBAAoB,aAAa;IACtC,IAAI,gBAAgB;IACpB,IAAI,iBAAiB,MAAKK,gBAAiB,kBAAkB;AAC7D,WAAO,gBAAgB,cAAc,iBAAiB;KACpD,MAAM,WAAW,MAAKc,eAAgB,eAAe,eAAe;AACpE,sBAAiB,SAAS;AAC1B,yBAAoB,IAAI,eAAe,SAAS,eAAe;;AAEjE,QAAI,wBACF,OAAKd,gBAAiB,gBAAgB;;;AAI5C,SAAO,OAAO,qBAAsB;GAClC,MAAM,oBAAoB,uBACtB,MAAKA,gBAAiB,OAAO,KAC7B;AACJ,OAAI,wBACF,OAAKA,gBAAiB,QAAQ;GAGhC,MAAM,EAAE,gBAAgB,OAAO,cAAc,MAAKc,eAChD,MACA,MACD;AACD,WAAQ;AAER,OAAI,QAAQ,UACV,YAAW,IAAI,MAAM,eAAe;OAEpC,sBAAqB,IAAI,MAAM,eAAe;AAGhD,OAAI,wBACF,OAAKd,gBAAiB,OAAO,KAAK;AAEpC,aACE,QAAQ,0BACR,wBACA,sBAAsB,UACtB,MAAM,OAAO,kBAAkB;AACjC,OAAI,SAAS;AACX;IACA,MAAM,YAAY,kBAAkB;AACpC,QAAI,cAAc,OAChB;AAEF,QAAI,UAAU,MAAM,oBAAoB;AACtC,2BAAsB,UAAU;AAChC,mCAA8B;AAC9B;;AAEF,QAAI,MAAKA,gBAAiB,UAAU,QAAQ,QAAW;AACrD,8BAAyB,UAAU;AACnC;WACK;AACL,YAAO,UAAU;AACjB,aAAQ,MAAKA,gBAAiB,SAAS;AACvC,8BAAyB,UAAU;;AAErC,cAAU;AACV;;AAEF;;AAGF,MAAI,wBACF,KAAI,OAAO,mBACT,OAAKA,gBAAiB,OAAO,KAAK;MAElC,OAAKA,gBAAiB,QAAQ;AAIlC,MAAI,wBAAwB,UAAa,oBAAoB,OAAO,EAClE,OAAKS,gBAAiB,qBAAqB,MAAKX,UAAW;AAG7D,MAAI,wBAAwB,OAC1B,OAAKG,2BACH,qBACA,mBACA,4BACD;WACQ,CAAC,WAAW,OAAO,WAAW;GACvC,MAAM,iBACJ,yBAAyB,cAAc,YACnC,qBACA,aAAa,aAAa,CAAC,uBACzB,aACA;AACR,SAAKA,2BACH,gBACA,uBAAuB,oBAAoB,QAC3C,kBACD;;AAGH,SAAO;;CAGT,sBAAsB,aAAiC;AACrD,QAAKc,sBAAuB,YAAY;;CAG1C,yBAA+B;AAC7B,sBAAoB,WAAW,MAAKC,UAAW;AAC/C,QAAKC,YAAa;AAClB,QAAKC,WAAY;AACjB,QAAKC,8BAA+B;AACpC,QAAKC,8BAA+B;;CAGtC,4BACE,WACA,mBACA,oBAAoB,GACd;EACN,MAAM,QAAQ,EAAE,MAAKxB;AAErB,QAAKqB,YAAa;AAClB,QAAKC,WAAY;AACjB,QAAKC,8BAA+B;AACpC,QAAKC,8BAA+B;AAEpC,aAAW,iBAAiB,WAAW,MAAKJ,UAAW;AACvD,QAAKK,8BAA+B,MAAM;;CAG5C,+BAA+B,OAAqB;AAElD,aAAW,YAAY;GAAE,MAAM;GAAY;GAAO,CAAC;;CAGrD,gBACE,MACA,OACgE;AAChE,MAAI,MAAK5B,YAAa,OACpB,OAAM,IAAI,MAAM,qBAAqB;EAEvC,MAAM,WAAW,MAAKD,aAAc,YAAY,KAAK;AACrD,MAAI,SAAS,SAAS,MAAKgB,uBAAwB;AACjD,WAAQ,KACN,gBAAgB,KAAK,0BAA0B,SAAS,SACzD;AACD,UAAO;IAAE,gBAAgB,CAAC;KAAC;KAAG;KAAI;KAAS,CAAC;IAAE;IAAO;;AAEvD,MAAI,aAAa,MAAM,SAAS,MAAM,KAAK,GACzC,QAAO;GAAE,gBAAgB,CAAC;IAAC;IAAG;IAAI;IAAS,CAAC;GAAE;GAAO;EAEvD,MAAM,SAAS,aACb,MAAKf,SACL,MAAKS,UACL,UACA,OACA,gBAAgB,oBACjB;AACD,SAAO;GACL,gBAAgB,OAAO;GACvB,OAAO,OAAO;GACf;;CAGH,oBAAoB,OAAe;EACjC,MAAM,eAAe,KAAK,IACxB,KAAK,IAAI,GAAG,MAAM,EAClB,MAAKV,aAAc,UACpB;AACD,MACE,MAAKQ,gBAAiB,SAAS,gBAC/B,MAAKP,YAAa,OAElB;EAEF,IAAI,OAAO,MAAKO,gBAAiB,SAAS;EAC1C,IAAI,QAAQ,MAAKA,gBAAiB,SAAS;AAC3C,SAAO,OAAO,cAAc,QAAQ;AAClC,SAAKA,gBAAiB,QAAQ;GAC9B,MAAM,WAAW,MAAKR,aAAc,YAAY,KAAK;AACrD,OACE,SAAS,UAAU,MAAKgB,yBACxB,aAAa,MACb,SAAS,MAAM,KAAK,GAEpB,SAAQ,MAAKf,QAAS,cACpB,UACA,OACA,gBAAgB,oBACjB,CAAC;;AAGN,QAAKO,gBAAiB,QAAQ;;CAGhC,oBAAoB,OAAe;AACjC,MACE,MAAKiB,aACL,MAAKxB,YAAa,UAClB,UAAU,MAAKG,gBAEf;EAGF,MAAM,IAAI,YAAY,KAAK;EAC3B,MAAM,wBAAQ,IAAI,KAAsC;EACxD,MAAM,aAAa,MAAKJ,aAAc;EACtC,MAAM,oBAAoB,MAAK2B;EAE/B,IAAI,OAAO,MAAKD;EAChB,IAAI,QAAQ,MAAKlB,gBAAiB,SAAS;EAC3C,IAAI,UAAU;EACd,IAAI,oBAAoB,MAAKoB;EAC7B,IAAI,yBAAyB,oBAAoB,qBAAqB;AACtE,SAAO,OAAO,aAAc;AAC1B,SAAKpB,gBAAiB,QAAQ;GAE9B,MAAM,oBACJ,2BAA2B,SACvB,MAAKA,gBAAiB,OAAO,KAC7B;GACN,MAAM,WAAW,MAAKR,aAAc,YAAY,KAAK;AACrD,OAAI,SAAS,SAAS,MAAKgB,uBAAwB;AACjD,YAAQ,KACN,gBAAgB,KAAK,0BAA0B,SAAS,SACzD;AACD,UAAM,IAAI,MAAM,CAAC;KAAC;KAAG;KAAI;KAAS,CAAC,CAAC;cAC3B,aAAa,MAAM,SAAS,MAAM,KAAK,GAChD,OAAM,IAAI,MAAM,CAAC;IAAC;IAAG;IAAI;IAAS,CAAC,CAAC;QAC/B;IACL,MAAM,MAAM,aACV,MAAKf,SACL,MAAKS,UACL,UACA,OACA,gBAAgB,oBACjB;AACD,UAAM,IAAI,MAAM,IAAI,eAAe;AACnC,YAAQ,IAAI;;AAGd,SAAKF,gBAAiB,OAAO,KAAK;AAClC,aACE,2BAA2B,UAC3B,QAAQ,0BACR,sBAAsB,UACtB,MAAM,OAAO,kBAAkB;AACjC;AACA,OAAI,SAAS;AACX;IACA,MAAM,YAAY,oBAAoB;AACtC,QAAI,cAAc,OAChB;AAEF,6BAAyB,UAAU;AACnC,QAAI,MAAKA,gBAAiB,UAAU,QAAQ,OAC1C,WAAU;SACL;AACL,YAAO,UAAU;AACjB,aAAQ,MAAKA,gBAAiB,SAAS;AACvC,eAAU;AACV;;;AAKJ,OAAI,YAAY,KAAK,GAAG,IAAI,EAC1B;;AAIJ,QAAKS,gBAAiB,OAAO,MAAKX,UAAW;AAC7C,MAAI,MAAKmB,aAAc,UAAU,MAAKrB,gBACpC;AAGF,MAAI,WAAW,QAAQ,YAAY;AACjC,QAAK,wBAAwB;AAC7B;;AAGF,QAAKsB,WAAY;AACjB,QAAKE,8BAA+B;AACpC,QAAKC,8BAA+B,MAAM;;;AAI9C,SAAgB,aACd,SACA,UACA,UACA,YACA,WAIA;CACA,MAAM,SAAS,QAAQ,cAAc,UAAU,YAAY,UAAU;AACrE,KAAI,OAAO,aACT,SAAQ,KACN,oDAAoD,SAAS,UAAU,GAAG,IAAI,GAC/E;CAEH,MAAM,YAAY,OAAO;CACzB,MAAM,eAAe,UAAU,SAAS;CACxC,MAAMC,iBAA0C,EAAE;AAClD,MAAK,IAAI,IAAI,GAAG,IAAI,cAAc,KAAK;EACrC,MAAM,SAAS,UAAU,IAAI;EAC7B,MAAM,aACJ,IAAI,IAAI,eAAe,UAAU,IAAI,IAAI,KAAK,SAAS;AACzD,MAAI,WAAW,WAEb;EAEF,MAAM,WAAW,UAAU,IAAI,IAAI;EAEnC,MAAM,KAAK,SADA,qBAAqB,cAAc,SAAS;EAEvD,MAAM,YAAY,SAAS,MAAM,QAAQ,WAAW;AACpD,iBAAe,KAAK;GAAC;GAAQ;GAAI;GAAU,CAAC;;AAE9C,QAAO;EACL,WAAW,OAAO;EAClB;EACD;;AAGH,SAAgB,iBACd,QACA,WAC0B;AAC1B,QAAO,OAAO,KAAK,CAAC,MAAM,IAAI,iBAAiB;AAC7C,MAAI,SAAS,KAAK,OAAO,IAAI;AAC3B,OAAI,gBAAgB,GAClB,QAAO,EAAE,KAAK;AAEhB,UAAO;;AAET,SAAO,EAAE,QAAQ;GACf,SAAS,EACP,MAAM,KAAK,UAAU,EACtB;GACD,OAAO,iBAAiB,UAAU,GAAG,GAAG;GAC3B;GACd,CAAC;GACF"}
|
|
1
|
+
{"version":3,"file":"tokenzier.js","names":["#textDocument","#grammar","#highlighter","#buildStateStack","#backgroundJobId","#backgroundTokenize","#themeType","#mediaQueryList","themeType","#emitThemeChange","#disposes","#tokenizeMaxLineLength","#setStyle","#onDeferTokenize","#debug","#colorMap","#setTheme","#stateStack","#scheduleBackgroundTokenize","#detachMessageListener","changedLineRanges: readonly [number, number][]","backgroundStartLine: number | undefined","dirtyLines: Map<number, Array<HighlightedToken>>","offscreenDirtyLines:\n | Map<number, Array<HighlightedToken>>\n | undefined","#tokenizeLineAt","#prebuildStateStack","#isStopped","#isPaused","#lastLine","#backgroundChangedLineRanges","#backgroundChangedRangeIndex","#postTokenizeMessage","#isMessageListenerAttached","#onMessage","#attachMessageListener","resolvedTokens: Array<HighlightedToken>"],"sources":["../../src/editor/tokenzier.ts"],"sourcesContent":["import {\n EncodedTokenMetadata,\n type IGrammar,\n INITIAL,\n type StateStack,\n} from 'shiki/textmate';\n\nimport { DEFAULT_THEMES } from '../constants';\nimport type {\n BaseCodeOptions,\n DiffsHighlighter,\n HighlightedToken,\n RenderRange,\n} from '../types';\nimport type { TextDocument, TextDocumentChange } from './textDocument';\nimport { addEventListener, debounce, h } from './utils';\n\nexport interface EditorTokenizerProps {\n highlighter: DiffsHighlighter;\n textDocument: TextDocument<unknown>;\n codeOptions: BaseCodeOptions;\n setStyle: (style: string) => void;\n onDeferTokenize: (\n lines: Map<number, Array<HighlightedToken>>,\n themeType: 'dark' | 'light'\n ) => void;\n __debug?: boolean;\n}\n\n/** Stoppable code tokenizer for the editor */\nexport class EditorTokenizer {\n static TOKENIZE_TIME_LIMIT = 500;\n\n #highlighter: DiffsHighlighter;\n #grammar: IGrammar | undefined;\n #mediaQueryList: MediaQueryList;\n #themeType: 'light' | 'dark';\n #colorMap: string[];\n #textDocument: TextDocument<unknown>;\n #tokenizeMaxLineLength: number;\n #setStyle: EditorTokenizerProps['setStyle'];\n #onDeferTokenize: EditorTokenizerProps['onDeferTokenize'];\n #debug: boolean;\n #disposes?: (() => void)[];\n\n // state\n #stateStack: StateStack[] = [INITIAL]; // cached state stack by line index\n #lastLine: number = -1;\n #isStopped: boolean = true;\n #isPaused: boolean = false;\n #backgroundJobId: number = 0;\n #backgroundChangedLineRanges: readonly [number, number][] | undefined;\n #backgroundChangedRangeIndex: number = 0;\n #isMessageListenerAttached: boolean = false;\n\n #prebuildStateStack = debounce(async (renderRange?: RenderRange) => {\n const { startingLine = 0, totalLines = Infinity } = renderRange ?? {};\n const endLine = Math.min(\n totalLines === Infinity ? Infinity : startingLine + totalLines,\n this.#textDocument.lineCount\n );\n if (this.#grammar === undefined) {\n await this.#highlighter.loadLanguage(this.#textDocument.languageId);\n this.#grammar = this.#highlighter.getLanguage(\n this.#textDocument.languageId\n );\n }\n this.#buildStateStack(endLine);\n }, 500);\n\n #onMessage = ({ data }: MessageEvent<unknown>) => {\n if (typeof data !== 'object' || data === null) {\n return;\n }\n const { type, jobId } = data as {\n type?: unknown;\n jobId?: unknown;\n };\n if (\n type === 'tokenize' &&\n typeof jobId === 'number' &&\n jobId === this.#backgroundJobId\n ) {\n this.#backgroundTokenize(jobId);\n }\n };\n\n get themeType(): 'light' | 'dark' {\n return this.#themeType;\n }\n\n constructor({\n codeOptions,\n highlighter,\n textDocument,\n setStyle,\n onDeferTokenize,\n __debug,\n }: EditorTokenizerProps) {\n const {\n themeType = 'system',\n theme = DEFAULT_THEMES,\n tokenizeMaxLineLength = 1000,\n } = codeOptions;\n this.#mediaQueryList = window.matchMedia('(prefers-color-scheme: dark)');\n if (themeType === 'system') {\n this.#themeType = this.#mediaQueryList.matches ? 'dark' : 'light';\n } else {\n this.#themeType = themeType;\n }\n if (typeof theme !== 'string') {\n const observer = new MutationObserver((mutations) => {\n for (const { type, attributeName } of mutations) {\n if (\n type === 'attributes' &&\n attributeName !== null &&\n (attributeName === 'class' || attributeName.startsWith('data-'))\n ) {\n const themeType =\n getComputedStyle(document.body).colorScheme === 'dark'\n ? 'dark'\n : 'light';\n this.#emitThemeChange(theme[themeType], themeType);\n break;\n }\n }\n });\n observer.observe(document.documentElement, { attributes: true });\n observer.observe(document.body, { attributes: true });\n this.#disposes = [\n addEventListener(this.#mediaQueryList, 'change', (e) => {\n const themeType = e.matches ? 'dark' : 'light';\n this.#emitThemeChange(theme[themeType], themeType);\n }),\n () => observer.disconnect(),\n ];\n }\n this.#highlighter = highlighter;\n this.#textDocument = textDocument;\n this.#tokenizeMaxLineLength = tokenizeMaxLineLength;\n this.#setStyle = setStyle;\n this.#onDeferTokenize = onDeferTokenize;\n this.#debug = __debug ?? false;\n if (highlighter.getLoadedLanguages().includes(textDocument.languageId)) {\n this.#grammar = highlighter.getLanguage(textDocument.languageId);\n }\n this.#colorMap = [];\n this.#setTheme(typeof theme === 'string' ? theme : theme[this.#themeType]);\n }\n\n // By default, diffs components support dual themes, but the tokenizer only renders\n // the preferred theme. When the theme type is changed, the tokenizer will re-tokenize the document.\n #emitThemeChange(themeName: string, themeType: 'light' | 'dark') {\n this.#themeType = themeType;\n this.#setTheme(themeName);\n this.stopBackgroundTokenize();\n this.#stateStack = [INITIAL];\n if (this.#grammar !== undefined && this.#textDocument.lineCount > 0) {\n this.#scheduleBackgroundTokenize(0);\n }\n }\n\n #setTheme(themeName: string) {\n this.#colorMap = this.#highlighter.setTheme(themeName).colorMap;\n const { colors = {} } = this.#highlighter.getTheme(themeName);\n const selectionBackground = colors['editor.selectionBackground'];\n const lineHighlightBackground = colors['editor.lineHighlightBackground'];\n const gutterForeground = colors['editorLineNumber.foreground'];\n const gutterActiveForeground = colors['editorLineNumber.activeForeground'];\n const cursorForeground = colors['editorCursor.foreground'];\n const findMatchBackground = colors['editor.findMatchBackground'];\n const findMatchHighlightBackground =\n colors['editor.findMatchHighlightBackground'];\n this.#setStyle(`:host {\n --diffs-editor-selection-bg: ${selectionBackground ?? 'var(--diffs-line-bg)'};\n --diffs-editor-line-highlight-bg: ${lineHighlightBackground ?? 'var(--diffs-line-bg)'};\n --diffs-editor-line-number-fg: ${gutterForeground ?? 'var(--diffs-fg-number)'};\n --diffs-editor-line-number-active-bg: ${lineHighlightBackground ?? 'var(--diffs-line-bg, var(--diffs-bg))'};\n --diffs-editor-line-number-active-fg: ${gutterActiveForeground ?? 'var(--diffs-selection-number-fg)'};\n ${cursorForeground !== undefined ? `--diffs-editor-cursor-fg: ${cursorForeground};` : ''}\n ${findMatchBackground !== undefined ? `--diffs-editor-match-bg: ${findMatchBackground};` : ''}\n ${findMatchHighlightBackground !== undefined ? `--diffs-editor-match-highlight-bg: ${findMatchHighlightBackground};` : ''}\n }`);\n }\n\n cleanUp(): void {\n this.#detachMessageListener();\n this.stopBackgroundTokenize();\n this.#disposes?.forEach((dispose) => dispose());\n this.#disposes = undefined;\n }\n\n // to use `tokenize`, call `prebuildStateStackMap` first to prebuild\n // the state stack map for the given render range.\n tokenize(\n change: TextDocumentChange,\n renderRange?: RenderRange\n ): Map<number, Array<HighlightedToken>> {\n if (this.#grammar === undefined) {\n throw new Error('Grammar not loaded');\n }\n\n const { lineCount } = this.#textDocument;\n const { startingLine = 0, totalLines = Infinity } = renderRange ?? {};\n const renderRangeEndLine =\n totalLines === Infinity\n ? lineCount\n : Math.min(startingLine + totalLines, lineCount);\n\n const dirtyStart = change.startLine;\n const viewStart = Math.max(startingLine, dirtyStart);\n const crossesRenderRangeEnd =\n renderRange !== undefined &&\n totalLines !== Infinity &&\n change.lineDelta > 0 &&\n dirtyStart < renderRangeEndLine &&\n change.endLine >= renderRangeEndLine;\n const canReuseCachedStates = change.lineDelta === 0;\n const canCacheTokenizedStates =\n canReuseCachedStates ||\n renderRange === undefined ||\n dirtyStart >= viewStart;\n const changedLineRanges: readonly [number, number][] = canReuseCachedStates\n ? (change.changedLineRanges ?? [[dirtyStart, change.endLine]])\n : [[dirtyStart, change.endLine]];\n let offscreenSyncEnd = -1;\n if (dirtyStart < viewStart) {\n for (const [rangeStart, rangeEnd] of changedLineRanges) {\n if (rangeStart < viewStart) {\n offscreenSyncEnd = Math.max(\n offscreenSyncEnd,\n Math.min(rangeEnd, viewStart - 1)\n );\n }\n }\n }\n const shouldFlushOffscreenLines =\n offscreenSyncEnd >= dirtyStart &&\n (canReuseCachedStates || change.lineDelta < 0);\n if (canReuseCachedStates) {\n this.#buildStateStack(dirtyStart);\n } else {\n this.#stateStack.length = Math.min(\n this.#stateStack.length,\n dirtyStart + 1\n );\n if (renderRange === undefined || dirtyStart >= viewStart) {\n this.#buildStateStack(viewStart);\n }\n }\n\n let changedRangeIndex = 0;\n let currentChangedRangeEnd = changedLineRanges[changedRangeIndex][1];\n let backgroundStartLine: number | undefined;\n let backgroundChangedRangeIndex = 0;\n let line = canReuseCachedStates\n ? changedLineRanges[changedRangeIndex][0]\n : viewStart;\n let state = this.#stateStack[line] ?? INITIAL;\n let settled = false;\n const dirtyLines: Map<number, Array<HighlightedToken>> = new Map();\n const offscreenDirtyLines:\n | Map<number, Array<HighlightedToken>>\n | undefined = shouldFlushOffscreenLines ? new Map() : undefined;\n if (offscreenDirtyLines !== undefined && !canReuseCachedStates) {\n const offscreenEnd = Math.min(\n offscreenSyncEnd + 1,\n viewStart,\n renderRangeEndLine\n );\n if (offscreenEnd > dirtyStart) {\n this.#buildStateStack(offscreenEnd);\n let offscreenLine = dirtyStart;\n let offscreenState = this.#stateStack[offscreenLine] ?? INITIAL;\n for (; offscreenLine < offscreenEnd; offscreenLine++) {\n const resolved = this.#tokenizeLineAt(offscreenLine, offscreenState);\n offscreenState = resolved.state;\n offscreenDirtyLines.set(offscreenLine, resolved.resolvedTokens);\n }\n if (canCacheTokenizedStates) {\n this.#stateStack[offscreenEnd] = offscreenState;\n }\n }\n }\n for (; line < renderRangeEndLine; ) {\n const previousNextState = canReuseCachedStates\n ? this.#stateStack[line + 1]\n : undefined;\n if (canCacheTokenizedStates) {\n this.#stateStack[line] = state;\n }\n\n const { resolvedTokens, state: nextState } = this.#tokenizeLineAt(\n line,\n state\n );\n state = nextState;\n\n if (line >= viewStart) {\n dirtyLines.set(line, resolvedTokens);\n } else {\n offscreenDirtyLines?.set(line, resolvedTokens);\n }\n\n if (canCacheTokenizedStates) {\n this.#stateStack[line + 1] = state;\n }\n settled =\n line >= currentChangedRangeEnd &&\n canReuseCachedStates &&\n previousNextState !== undefined &&\n state.equals(previousNextState);\n if (settled) {\n changedRangeIndex++;\n const nextRange = changedLineRanges[changedRangeIndex];\n if (nextRange === undefined) {\n break;\n }\n if (nextRange[0] >= renderRangeEndLine) {\n backgroundStartLine = nextRange[0];\n backgroundChangedRangeIndex = changedRangeIndex;\n break;\n }\n if (this.#stateStack[nextRange[0]] === undefined) {\n currentChangedRangeEnd = nextRange[1];\n line++;\n } else {\n line = nextRange[0];\n state = this.#stateStack[line] ?? state;\n currentChangedRangeEnd = nextRange[1];\n }\n settled = false;\n continue;\n }\n line++;\n }\n\n if (canCacheTokenizedStates) {\n if (line < renderRangeEndLine) {\n this.#stateStack[line + 1] = state;\n } else {\n this.#stateStack[line] = state;\n }\n }\n\n if (offscreenDirtyLines !== undefined && offscreenDirtyLines.size > 0) {\n this.#onDeferTokenize(offscreenDirtyLines, this.#themeType);\n }\n\n if (backgroundStartLine !== undefined) {\n this.#scheduleBackgroundTokenize(\n backgroundStartLine,\n changedLineRanges,\n backgroundChangedRangeIndex\n );\n } else if (!settled && line < lineCount) {\n const backgroundLine =\n crossesRenderRangeEnd && dirtyStart >= viewStart\n ? renderRangeEndLine\n : dirtyStart < viewStart && !canReuseCachedStates\n ? dirtyStart\n : line;\n this.#scheduleBackgroundTokenize(\n backgroundLine,\n canReuseCachedStates ? changedLineRanges : undefined,\n changedRangeIndex\n );\n }\n\n return dirtyLines;\n }\n\n prebuildStateStack(renderRange?: RenderRange): void {\n this.#prebuildStateStack(renderRange);\n }\n\n stopBackgroundTokenize(): void {\n if (this.#isStopped) {\n return;\n }\n this.#isStopped = true;\n this.#isPaused = false;\n this.#lastLine = -1;\n this.#backgroundChangedLineRanges = undefined;\n this.#backgroundChangedRangeIndex = 0;\n this.#detachMessageListener();\n }\n\n pauseBackgroundTokenize(): void {\n if (this.#isStopped || this.#isPaused) {\n return;\n }\n if (this.#debug) {\n console.log('[diffs/editor] background tokenization paused', {\n jobId: this.#backgroundJobId,\n });\n }\n this.#isPaused = true;\n }\n\n resumeBackgroundTokenize(): void {\n if (\n this.#isStopped ||\n !this.#isPaused ||\n this.#grammar === undefined ||\n this.#lastLine < 0\n ) {\n return;\n }\n if (this.#debug) {\n console.log('[diffs/editor] background tokenization resumed', {\n jobId: this.#backgroundJobId,\n });\n }\n this.#isPaused = false;\n this.#postTokenizeMessage(this.#backgroundJobId);\n }\n\n #attachMessageListener(): void {\n if (this.#isMessageListenerAttached) {\n return;\n }\n globalThis.addEventListener('message', this.#onMessage);\n this.#isMessageListenerAttached = true;\n }\n\n #detachMessageListener(): void {\n if (!this.#isMessageListenerAttached) {\n return;\n }\n globalThis.removeEventListener('message', this.#onMessage);\n this.#isMessageListenerAttached = false;\n }\n\n #postTokenizeMessage(jobId: number): void {\n // use `postMessage` instead of `setTimeout(fn, 0)` to avoid 4ms delay\n globalThis.postMessage({ type: 'tokenize', jobId });\n }\n\n #scheduleBackgroundTokenize(\n startLine: number,\n changedLineRanges?: readonly [number, number][],\n changedRangeIndex = 0\n ): void {\n const jobId = ++this.#backgroundJobId;\n\n if (this.#debug) {\n console.log('[diffs/editor] background tokenization scheduled', {\n jobId,\n startLine,\n changedLineRanges,\n changedRangeIndex,\n });\n }\n\n this.#isStopped = false;\n this.#isPaused = false;\n this.#lastLine = startLine;\n this.#backgroundChangedLineRanges = changedLineRanges;\n this.#backgroundChangedRangeIndex = changedRangeIndex;\n this.#attachMessageListener();\n this.#postTokenizeMessage(jobId);\n }\n\n #tokenizeLineAt(\n line: number,\n state: StateStack\n ): { resolvedTokens: Array<HighlightedToken>; state: StateStack } {\n if (this.#grammar === undefined) {\n throw new Error('Grammar not loaded');\n }\n const lineText = this.#textDocument.getLineText(line);\n if (lineText.length > this.#tokenizeMaxLineLength) {\n console.warn(\n `[diffs] Line(${line}) too long to tokenize: ${lineText.length}`\n );\n return { resolvedTokens: [[0, '', lineText]], state };\n }\n if (lineText === '' || lineText.trim() === '') {\n return { resolvedTokens: [[0, '', lineText]], state };\n }\n const result = tokenizeLine(\n this.#grammar,\n this.#colorMap,\n lineText,\n state,\n EditorTokenizer.TOKENIZE_TIME_LIMIT\n );\n return {\n resolvedTokens: result.resolvedTokens,\n state: result.ruleStack,\n };\n }\n\n #buildStateStack(endAt: number) {\n const boundedEndAt = Math.min(\n Math.max(0, endAt),\n this.#textDocument.lineCount\n );\n if (this.#stateStack.length > boundedEndAt || this.#grammar === undefined) {\n return;\n }\n let line = this.#stateStack.length - 1;\n let state = this.#stateStack[line] ?? INITIAL;\n for (; line < boundedEndAt; line++) {\n this.#stateStack[line] = state;\n const lineText = this.#textDocument.getLineText(line);\n if (\n lineText.length <= this.#tokenizeMaxLineLength &&\n lineText !== '' &&\n lineText.trim() !== ''\n ) {\n state = this.#grammar.tokenizeLine2(\n lineText,\n state,\n EditorTokenizer.TOKENIZE_TIME_LIMIT\n ).ruleStack;\n }\n }\n this.#stateStack[line] = state;\n }\n\n #backgroundTokenize(jobId: number) {\n if (\n this.#isStopped ||\n this.#isPaused ||\n this.#grammar === undefined ||\n jobId !== this.#backgroundJobId\n ) {\n return;\n }\n\n const t = performance.now();\n const lines = new Map<number, Array<HighlightedToken>>();\n const totalLines = this.#textDocument.lineCount;\n const changedLineRanges = this.#backgroundChangedLineRanges;\n\n let line = this.#lastLine;\n let state = this.#stateStack[line] ?? INITIAL;\n let settled = false;\n let changedRangeIndex = this.#backgroundChangedRangeIndex;\n let currentChangedRangeEnd = changedLineRanges?.[changedRangeIndex]?.[1];\n for (; line < totalLines; ) {\n this.#stateStack[line] = state;\n\n const previousNextState =\n currentChangedRangeEnd !== undefined\n ? this.#stateStack[line + 1]\n : undefined;\n const lineText = this.#textDocument.getLineText(line);\n if (lineText.length > this.#tokenizeMaxLineLength) {\n console.warn(\n `[diffs] Line(${line}) too long to tokenize: ${lineText.length}`\n );\n lines.set(line, [[0, '', lineText]]);\n } else if (lineText === '' || lineText.trim() === '') {\n lines.set(line, [[0, '', lineText]]);\n } else {\n const ret = tokenizeLine(\n this.#grammar,\n this.#colorMap,\n lineText,\n state,\n EditorTokenizer.TOKENIZE_TIME_LIMIT\n );\n lines.set(line, ret.resolvedTokens);\n state = ret.ruleStack;\n }\n\n this.#stateStack[line + 1] = state;\n settled =\n currentChangedRangeEnd !== undefined &&\n line >= currentChangedRangeEnd &&\n previousNextState !== undefined &&\n state.equals(previousNextState);\n line++;\n if (settled) {\n changedRangeIndex++;\n const nextRange = changedLineRanges?.[changedRangeIndex];\n if (nextRange === undefined) {\n break;\n }\n currentChangedRangeEnd = nextRange[1];\n if (this.#stateStack[nextRange[0]] === undefined) {\n settled = false;\n } else {\n line = nextRange[0];\n state = this.#stateStack[line] ?? state;\n settled = false;\n continue;\n }\n }\n\n // limit the time of partial tokenize to 1ms\n if (performance.now() - t > 1) {\n break;\n }\n }\n\n this.#onDeferTokenize(lines, this.#themeType);\n if (this.#isStopped || this.#isPaused || jobId !== this.#backgroundJobId) {\n return;\n }\n\n if (settled || line >= totalLines) {\n this.stopBackgroundTokenize();\n return;\n }\n\n this.#lastLine = line;\n this.#backgroundChangedRangeIndex = changedRangeIndex;\n this.#postTokenizeMessage(jobId);\n }\n}\n\nexport function tokenizeLine(\n grammar: IGrammar,\n colorMap: string[],\n lineText: string,\n stateStack: StateStack,\n timeLimit?: number\n): {\n ruleStack: StateStack;\n resolvedTokens: Array<HighlightedToken>;\n} {\n const result = grammar.tokenizeLine2(lineText, stateStack, timeLimit);\n if (result.stoppedEarly) {\n console.warn(\n `[diffs] Time limit reached when tokenizing line: ${lineText.substring(0, 100)}`\n );\n }\n const rawTokens = result.tokens;\n const tokensLength = rawTokens.length / 2;\n const resolvedTokens: Array<HighlightedToken> = [];\n for (let j = 0; j < tokensLength; j++) {\n const offset = rawTokens[2 * j];\n const nextOffset =\n j + 1 < tokensLength ? rawTokens[2 * j + 2] : lineText.length;\n if (offset === nextOffset) {\n // should never reach here, skip if happens anyway\n continue;\n }\n const metadata = rawTokens[2 * j + 1];\n const bg = EncodedTokenMetadata.getForeground(metadata);\n const fg = colorMap[bg];\n const tokenText = lineText.slice(offset, nextOffset);\n resolvedTokens.push([offset, fg, tokenText]);\n }\n return {\n ruleStack: result.ruleStack,\n resolvedTokens,\n };\n}\n\nexport function renderLineTokens(\n tokens: Array<HighlightedToken>,\n themeType: 'light' | 'dark'\n): (HTMLElement | string)[] {\n return tokens.map(([char, fg, textContent]) => {\n if (char === 0 && fg === '') {\n if (textContent === '') {\n return h('br');\n }\n return textContent;\n }\n return h('span', {\n dataset: {\n char: char.toString(),\n },\n style: `--diffs-token-${themeType}:${fg};`,\n textContent: textContent,\n });\n });\n}\n"],"mappings":"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"}
|