@pierre/diffs 1.2.6 → 1.3.0-beta.1
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 +0 -1
- package/dist/components/CodeView.d.ts.map +1 -1
- package/dist/components/CodeView.js +0 -23
- package/dist/components/CodeView.js.map +1 -1
- package/dist/components/File.d.ts +7 -2
- package/dist/components/File.d.ts.map +1 -1
- package/dist/components/File.js +36 -2
- package/dist/components/File.js.map +1 -1
- package/dist/components/FileDiff.d.ts +8 -2
- package/dist/components/FileDiff.d.ts.map +1 -1
- package/dist/components/FileDiff.js +73 -1
- package/dist/components/FileDiff.js.map +1 -1
- package/dist/components/UnresolvedFile.d.ts.map +1 -1
- package/dist/components/UnresolvedFile.js +2 -2
- package/dist/components/VirtualizedFile.d.ts +2 -1
- package/dist/components/VirtualizedFile.d.ts.map +1 -1
- package/dist/components/VirtualizedFile.js +48 -48
- package/dist/components/VirtualizedFile.js.map +1 -1
- package/dist/components/VirtualizedFileDiff.js +42 -22
- package/dist/components/VirtualizedFileDiff.js.map +1 -1
- package/dist/components/Virtualizer.d.ts +1 -1
- package/dist/components/Virtualizer.d.ts.map +1 -1
- package/dist/components/Virtualizer.js +3 -5
- package/dist/components/Virtualizer.js.map +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/editor/command.d.ts +6 -0
- package/dist/editor/command.d.ts.map +1 -0
- package/dist/editor/command.js +31 -0
- package/dist/editor/command.js.map +1 -0
- package/dist/editor/css.d.ts +6 -0
- package/dist/editor/css.d.ts.map +1 -0
- package/dist/editor/css.js +218 -0
- package/dist/editor/css.js.map +1 -0
- package/dist/editor/editStack.d.ts +66 -0
- package/dist/editor/editStack.d.ts.map +1 -0
- package/dist/editor/editStack.js +218 -0
- package/dist/editor/editStack.js.map +1 -0
- package/dist/editor/editor.d.ts +22 -0
- package/dist/editor/editor.d.ts.map +1 -0
- package/dist/editor/editor.js +1323 -0
- package/dist/editor/editor.js.map +1 -0
- package/dist/editor/index.d.ts +3 -0
- package/dist/editor/index.js +4 -0
- package/dist/editor/lineAnnotations.d.ts +8 -0
- package/dist/editor/lineAnnotations.d.ts.map +1 -0
- package/dist/editor/lineAnnotations.js +32 -0
- package/dist/editor/lineAnnotations.js.map +1 -0
- package/dist/editor/pieceTable.d.ts +33 -0
- package/dist/editor/pieceTable.d.ts.map +1 -0
- package/dist/editor/pieceTable.js +590 -0
- package/dist/editor/pieceTable.js.map +1 -0
- package/dist/editor/platform.d.ts +12 -0
- package/dist/editor/platform.d.ts.map +1 -0
- package/dist/editor/platform.js +44 -0
- package/dist/editor/platform.js.map +1 -0
- package/dist/editor/quickEdit.d.ts +29 -0
- package/dist/editor/quickEdit.d.ts.map +1 -0
- package/dist/editor/quickEdit.js +81 -0
- package/dist/editor/quickEdit.js.map +1 -0
- package/dist/editor/searchPanel.d.ts +30 -0
- package/dist/editor/searchPanel.d.ts.map +1 -0
- package/dist/editor/searchPanel.js +219 -0
- package/dist/editor/searchPanel.js.map +1 -0
- package/dist/editor/selection.d.ts +126 -0
- package/dist/editor/selection.d.ts.map +1 -0
- package/dist/editor/selection.js +900 -0
- package/dist/editor/selection.js.map +1 -0
- package/dist/editor/textDocument.d.ts +139 -0
- package/dist/editor/textDocument.d.ts.map +1 -0
- package/dist/editor/textDocument.js +202 -0
- package/dist/editor/textDocument.js.map +1 -0
- package/dist/editor/textMeasure.d.ts +32 -0
- package/dist/editor/textMeasure.d.ts.map +1 -0
- package/dist/editor/textMeasure.js +108 -0
- package/dist/editor/textMeasure.js.map +1 -0
- package/dist/editor/tokenzier.d.ts +37 -0
- package/dist/editor/tokenzier.d.ts.map +1 -0
- package/dist/editor/tokenzier.js +348 -0
- package/dist/editor/tokenzier.js.map +1 -0
- package/dist/editor/utils.d.ts +16 -0
- package/dist/editor/utils.d.ts.map +1 -0
- package/dist/editor/utils.js +37 -0
- package/dist/editor/utils.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/react/EditorContext.d.ts +16 -0
- package/dist/react/EditorContext.d.ts.map +1 -0
- package/dist/react/EditorContext.js +26 -0
- package/dist/react/EditorContext.js.map +1 -0
- package/dist/react/File.d.ts +2 -1
- package/dist/react/File.d.ts.map +1 -1
- package/dist/react/File.js +3 -2
- package/dist/react/File.js.map +1 -1
- package/dist/react/FileDiff.d.ts +3 -1
- package/dist/react/FileDiff.d.ts.map +1 -1
- package/dist/react/FileDiff.js +3 -2
- package/dist/react/FileDiff.js.map +1 -1
- package/dist/react/MultiFileDiff.d.ts +3 -1
- package/dist/react/MultiFileDiff.d.ts.map +1 -1
- package/dist/react/MultiFileDiff.js +3 -2
- package/dist/react/MultiFileDiff.js.map +1 -1
- package/dist/react/PatchDiff.d.ts +3 -1
- package/dist/react/PatchDiff.d.ts.map +1 -1
- package/dist/react/PatchDiff.js +3 -2
- package/dist/react/PatchDiff.js.map +1 -1
- package/dist/react/index.d.ts +3 -2
- package/dist/react/index.js +2 -1
- package/dist/react/jsx.d.ts +0 -1
- package/dist/react/jsx.d.ts.map +1 -1
- package/dist/react/types.d.ts +1 -0
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/types.js +0 -1
- package/dist/react/utils/useFileDiffInstance.d.ts +3 -1
- package/dist/react/utils/useFileDiffInstance.d.ts.map +1 -1
- package/dist/react/utils/useFileDiffInstance.js +31 -5
- package/dist/react/utils/useFileDiffInstance.js.map +1 -1
- package/dist/react/utils/useFileInstance.d.ts +4 -1
- package/dist/react/utils/useFileInstance.d.ts.map +1 -1
- package/dist/react/utils/useFileInstance.js +30 -5
- package/dist/react/utils/useFileInstance.js.map +1 -1
- package/dist/renderers/DiffHunksRenderer.d.ts +2 -2
- package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
- package/dist/renderers/DiffHunksRenderer.js +9 -5
- package/dist/renderers/DiffHunksRenderer.js.map +1 -1
- package/dist/renderers/FileRenderer.d.ts +5 -1
- package/dist/renderers/FileRenderer.d.ts.map +1 -1
- package/dist/renderers/FileRenderer.js +108 -41
- package/dist/renderers/FileRenderer.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 +45 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/cleanLastNewline.js +6 -1
- package/dist/utils/cleanLastNewline.js.map +1 -1
- package/dist/utils/computeEstimatedDiffHeights.js +20 -9
- package/dist/utils/computeEstimatedDiffHeights.js.map +1 -1
- package/dist/utils/computeFileOffsets.d.ts +13 -0
- package/dist/utils/computeFileOffsets.d.ts.map +1 -0
- package/dist/utils/computeFileOffsets.js +33 -0
- package/dist/utils/computeFileOffsets.js.map +1 -0
- package/dist/utils/createTransformerWithState.js +9 -0
- package/dist/utils/createTransformerWithState.js.map +1 -1
- package/dist/utils/iterateOverDiff.js +182 -147
- package/dist/utils/iterateOverDiff.js.map +1 -1
- package/dist/utils/renderDiffWithHighlighter.js +1 -1
- package/dist/utils/renderFileWithHighlighter.js +5 -14
- package/dist/utils/renderFileWithHighlighter.js.map +1 -1
- package/dist/utils/virtualDiffLayout.d.ts +2 -23
- package/dist/utils/virtualDiffLayout.d.ts.map +1 -1
- package/dist/utils/virtualDiffLayout.js +1 -41
- package/dist/utils/virtualDiffLayout.js.map +1 -1
- package/dist/worker/WorkerPoolManager.js +1 -1
- package/dist/worker/{wasm-BaDzIkIn.js → wasm-D4DU5jgR.js} +2 -2
- package/dist/worker/wasm-D4DU5jgR.js.map +1 -0
- package/dist/worker/worker-portable.js +349 -363
- package/dist/worker/worker-portable.js.map +1 -1
- package/dist/worker/worker.js +222 -243
- package/dist/worker/worker.js.map +1 -1
- package/package.json +9 -1
- package/dist/utils/iterateOverFile.d.ts +0 -50
- package/dist/utils/iterateOverFile.d.ts.map +0 -1
- package/dist/utils/iterateOverFile.js +0 -49
- package/dist/utils/iterateOverFile.js.map +0 -1
- package/dist/worker/wasm-BaDzIkIn.js.map +0 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { h, round } from "./utils.js";
|
|
2
|
+
|
|
3
|
+
//#region src/editor/textMeasure.ts
|
|
4
|
+
var Metrics = class {
|
|
5
|
+
#root;
|
|
6
|
+
#canvasCtx;
|
|
7
|
+
#font;
|
|
8
|
+
/** Width of the '0' character. */
|
|
9
|
+
ch = -1;
|
|
10
|
+
/** Size of a tab(\t) character. */
|
|
11
|
+
tabSize = 2;
|
|
12
|
+
/** Height of the code line. */
|
|
13
|
+
lineHeight = 20;
|
|
14
|
+
/** initialize the metrics */
|
|
15
|
+
init(root) {
|
|
16
|
+
if (this.#root === root && this.#canvasCtx !== void 0 && this.ch !== -1) return;
|
|
17
|
+
this.#root = root;
|
|
18
|
+
this.#canvasCtx ??= document.createElement("canvas").getContext("2d") ?? void 0;
|
|
19
|
+
if (this.#canvasCtx === void 0) throw new Error("Could not get canvas context");
|
|
20
|
+
const { fontSize, fontFamily, tabSize, lineHeight } = getComputedStyle(root);
|
|
21
|
+
if (lineHeight.endsWith("px")) this.lineHeight = Number(lineHeight.slice(0, -2));
|
|
22
|
+
else if (fontSize.endsWith("px")) this.lineHeight = round(Number(fontSize.slice(0, -2)) * Number(lineHeight));
|
|
23
|
+
const font = fontSize + " " + fontFamily;
|
|
24
|
+
if (this.#font !== font || this.ch === -1) {
|
|
25
|
+
this.#font = font;
|
|
26
|
+
this.#canvasCtx.font = font;
|
|
27
|
+
this.ch = this.canvasMeasureTextWidth("0");
|
|
28
|
+
}
|
|
29
|
+
this.tabSize = Number(tabSize);
|
|
30
|
+
}
|
|
31
|
+
/** measure the width of the text */
|
|
32
|
+
measureTextWidth(text) {
|
|
33
|
+
const textWithExpandedTabs = text.replaceAll(" ", " ".repeat(this.tabSize));
|
|
34
|
+
if (needsDomTextMeasurement(textWithExpandedTabs)) return this.domMeasureTextWidth(textWithExpandedTabs);
|
|
35
|
+
return this.canvasMeasureTextWidth(textWithExpandedTabs);
|
|
36
|
+
}
|
|
37
|
+
/** measure the width of the text using the canvas measureText API */
|
|
38
|
+
canvasMeasureTextWidth(text) {
|
|
39
|
+
if (this.#canvasCtx === void 0) throw new Error("Metrics not initialized");
|
|
40
|
+
return round(this.#canvasCtx.measureText(text).width);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* measure the width of the text using the DOM
|
|
44
|
+
* this is slow because it cause a reflow, use it for non-ascii text
|
|
45
|
+
*/
|
|
46
|
+
domMeasureTextWidth(text) {
|
|
47
|
+
if (this.#root === void 0) throw new Error("Metrics not initialized");
|
|
48
|
+
const measureEl = h("span", {
|
|
49
|
+
style: {
|
|
50
|
+
position: "absolute",
|
|
51
|
+
top: "0",
|
|
52
|
+
left: "0",
|
|
53
|
+
visibility: "hidden",
|
|
54
|
+
pointerEvents: "none",
|
|
55
|
+
whiteSpace: "pre",
|
|
56
|
+
font: "inherit"
|
|
57
|
+
},
|
|
58
|
+
textContent: text
|
|
59
|
+
}, this.#root);
|
|
60
|
+
try {
|
|
61
|
+
return measureEl.getBoundingClientRect().width;
|
|
62
|
+
} finally {
|
|
63
|
+
measureEl.remove();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
/** Check if the text needs DOM text measurement. */
|
|
68
|
+
function needsDomTextMeasurement(text) {
|
|
69
|
+
for (let i = 0; i < text.length; i++) {
|
|
70
|
+
const code = text.charCodeAt(i);
|
|
71
|
+
if (code >= 55296 && code <= 57343 || code === 8205 || code === 65038 || code === 65039) return true;
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
/** snap the text offset to the Unicode boundary */
|
|
76
|
+
function snapTextOffsetToUnicodeBoundary(text, offset) {
|
|
77
|
+
const boundedOffset = Math.max(0, Math.min(offset, text.length));
|
|
78
|
+
if (boundedOffset === 0 || boundedOffset === text.length || !needsDomTextMeasurement(text)) return boundedOffset;
|
|
79
|
+
const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
80
|
+
for (const segment of segmenter.segment(text)) {
|
|
81
|
+
const segmentStart = segment.index;
|
|
82
|
+
const segmentEnd = segmentStart + segment.segment.length;
|
|
83
|
+
if (boundedOffset > segmentStart && boundedOffset < segmentEnd) return segmentEnd;
|
|
84
|
+
if (boundedOffset <= segmentStart) break;
|
|
85
|
+
}
|
|
86
|
+
return boundedOffset;
|
|
87
|
+
}
|
|
88
|
+
/** get the offsets of the Unicode grapheme clusters in the text */
|
|
89
|
+
function getUnicodeMeasurementOffsets(text) {
|
|
90
|
+
if (!needsDomTextMeasurement(text)) return;
|
|
91
|
+
const offsets = [0];
|
|
92
|
+
const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
93
|
+
for (const segment of segmenter.segment(text)) offsets.push(segment.index + segment.segment.length);
|
|
94
|
+
return offsets;
|
|
95
|
+
}
|
|
96
|
+
/** get the number of columns of the ASCII text */
|
|
97
|
+
function getExpandedAsciiTextColumns(text, tabSize) {
|
|
98
|
+
let columns = 0;
|
|
99
|
+
for (let i = 0; i < text.length; i++) {
|
|
100
|
+
if (text.charCodeAt(i) > 127) return -1;
|
|
101
|
+
columns += text.charCodeAt(i) === 9 ? tabSize : 1;
|
|
102
|
+
}
|
|
103
|
+
return columns;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
//#endregion
|
|
107
|
+
export { Metrics, getExpandedAsciiTextColumns, getUnicodeMeasurementOffsets, needsDomTextMeasurement, snapTextOffsetToUnicodeBoundary };
|
|
108
|
+
//# sourceMappingURL=textMeasure.js.map
|
|
@@ -0,0 +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 = Number(lineHeight.slice(0, -2));\n } else if (fontSize.endsWith('px')) {\n this.lineHeight = round(\n Number(fontSize.slice(0, -2)) * Number(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 = Number(tabSize);\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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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { BaseCodeOptions, DiffsHighlighter, HighlightedToken, RenderRange } from "../types.js";
|
|
2
|
+
import { TextDocument, TextDocumentChange } from "./textDocument.js";
|
|
3
|
+
import { IGrammar, StateStack } from "shiki/textmate";
|
|
4
|
+
|
|
5
|
+
//#region src/editor/tokenzier.d.ts
|
|
6
|
+
interface EditorTokenizerProps {
|
|
7
|
+
highlighter: DiffsHighlighter;
|
|
8
|
+
textDocument: TextDocument<unknown>;
|
|
9
|
+
codeOptions: BaseCodeOptions;
|
|
10
|
+
setStyle: (style: string) => void;
|
|
11
|
+
onDeferTokenize: (lines: Map<number, Array<HighlightedToken>>, themeType: 'dark' | 'light') => void;
|
|
12
|
+
}
|
|
13
|
+
/** Stoppable code tokenizer for the editor */
|
|
14
|
+
declare class EditorTokenizer {
|
|
15
|
+
#private;
|
|
16
|
+
static TOKENIZE_TIME_LIMIT: number;
|
|
17
|
+
get themeType(): 'light' | 'dark';
|
|
18
|
+
constructor({
|
|
19
|
+
codeOptions,
|
|
20
|
+
highlighter,
|
|
21
|
+
textDocument,
|
|
22
|
+
setStyle,
|
|
23
|
+
onDeferTokenize
|
|
24
|
+
}: EditorTokenizerProps);
|
|
25
|
+
cleanUp(): void;
|
|
26
|
+
tokenize(change: TextDocumentChange, renderRange?: RenderRange): Map<number, Array<HighlightedToken>>;
|
|
27
|
+
prebuildStateStackMap(renderRange?: RenderRange): void;
|
|
28
|
+
stopBackgroundTokenize(): void;
|
|
29
|
+
}
|
|
30
|
+
declare function tokenizeLine(grammar: IGrammar, colorMap: string[], lineText: string, stateStack: StateStack, timeLimit?: number): {
|
|
31
|
+
ruleStack: StateStack;
|
|
32
|
+
resolvedTokens: Array<HighlightedToken>;
|
|
33
|
+
};
|
|
34
|
+
declare function renderLineTokens(tokens: Array<HighlightedToken>, themeType: 'light' | 'dark'): (HTMLElement | string)[];
|
|
35
|
+
//#endregion
|
|
36
|
+
export { EditorTokenizer, EditorTokenizerProps, renderLineTokens, tokenizeLine };
|
|
37
|
+
//# sourceMappingURL=tokenzier.d.ts.map
|
|
@@ -0,0 +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 prebuildStateStackMap(renderRange?: RenderRange): void;\n stopBackgroundTokenize(): 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;;;AAEYQ,cAGRC,eAAAA,CAHQD;EAAG,CAAA,OAAA;EAGXC,OAAAA,mBAAe,EAAA,MAAA;EAIlBC,IAAAA,SAAAA,CAAAA,CAAAA,EAAAA,OAAAA,GAAAA,MAAAA;EAAaC,WAAAA,CAAAA;IAAAA,WAAAA;IAAAA,WAAAA;IAAAA,YAAAA;IAAAA,QAAAA;IAAAA;EAAAA,CAAAA,EAAwDL,oBAAxDK;EAAaC,OAAAA,CAAAA,CAAAA,EAAAA,IAAAA;EAAcC,QAAAA,CAAAA,MAAAA,EAErCR,kBAFqCQ,EAAAA,WAAAA,CAAAA,EAEHV,WAFGU,CAAAA,EAEWL,GAFXK,CAAAA,MAAAA,EAEuBN,KAFvBM,CAE6BX,gBAF7BW,CAAAA,CAAAA;EAAUC,qBAAAA,CAAAA,WAAAA,CAAAA,EAG5BX,WAH4BW,CAAAA,EAAAA,IAAAA;EAAmBR,sBAAAA,CAAAA,CAAAA,EAAAA,IAAAA;;AAEhCH,iBAI/BY,YAAAA,CAJ+BZ,OAAAA,EAITL,QAJSK,EAAAA,QAAAA,EAAAA,MAAAA,EAAAA,EAAAA,QAAAA,EAAAA,MAAAA,EAAAA,UAAAA,EAImDJ,UAJnDI,EAAAA,SAAAA,CAAAA,EAAAA,MAAAA,CAAAA,EAAAA;EAAgCD,SAAAA,EAKxEH,UALwEG;EAANK,cAAAA,EAM7DA,KAN6DA,CAMvDL,gBANuDK,CAAAA;CAAZC;AAC7BL,iBAOhBa,gBAAAA,CAPgBb,MAAAA,EAOSI,KAPTJ,CAOeD,gBAPfC,CAAAA,EAAAA,SAAAA,EAAAA,OAAAA,GAAAA,MAAAA,CAAAA,EAAAA,CAOiEc,WAPjEd,GAAAA,MAAAA,CAAAA,EAAAA"}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { DEFAULT_THEMES } from "../constants.js";
|
|
2
|
+
import { addEventListener, debounce, h } from "./utils.js";
|
|
3
|
+
import { EncodedTokenMetadata, INITIAL } from "shiki/textmate";
|
|
4
|
+
|
|
5
|
+
//#region src/editor/tokenzier.ts
|
|
6
|
+
/** Stoppable code tokenizer for the editor */
|
|
7
|
+
var EditorTokenizer = class EditorTokenizer {
|
|
8
|
+
static TOKENIZE_TIME_LIMIT = 500;
|
|
9
|
+
#highlighter;
|
|
10
|
+
#grammar;
|
|
11
|
+
#mediaQueryList;
|
|
12
|
+
#themeType;
|
|
13
|
+
#colorMap;
|
|
14
|
+
#textDocument;
|
|
15
|
+
#tokenizeMaxLineLength;
|
|
16
|
+
#setStyle;
|
|
17
|
+
#onDeferTokenize;
|
|
18
|
+
#editorEventDisposes;
|
|
19
|
+
#stateStackCache = [INITIAL];
|
|
20
|
+
#lastLine = -1;
|
|
21
|
+
#isStopped = true;
|
|
22
|
+
#backgroundJobId = 0;
|
|
23
|
+
#backgroundChangedLineRanges;
|
|
24
|
+
#backgroundChangedRangeIndex = 0;
|
|
25
|
+
#prebuildStateStackMap = debounce(async (renderRange) => {
|
|
26
|
+
const { startingLine = 0, totalLines = Infinity } = renderRange ?? {};
|
|
27
|
+
const endLine = Math.min(totalLines === Infinity ? Infinity : startingLine + totalLines, this.#textDocument.lineCount);
|
|
28
|
+
if (this.#grammar === void 0) {
|
|
29
|
+
await this.#highlighter.loadLanguage(this.#textDocument.languageId);
|
|
30
|
+
this.#grammar = this.#highlighter.getLanguage(this.#textDocument.languageId);
|
|
31
|
+
}
|
|
32
|
+
this.#buildStateStackMap(endLine);
|
|
33
|
+
}, 500);
|
|
34
|
+
#onMessage = ({ data }) => {
|
|
35
|
+
if (data.type === "tokenize" && data.jobId === this.#backgroundJobId) this.#backgroundTokenize(data.jobId);
|
|
36
|
+
};
|
|
37
|
+
#onThemeChange = (themeName, themeType) => {
|
|
38
|
+
this.#themeType = themeType;
|
|
39
|
+
this.#setTheme(themeName);
|
|
40
|
+
this.stopBackgroundTokenize();
|
|
41
|
+
this.#stateStackCache = [INITIAL];
|
|
42
|
+
if (this.#grammar !== void 0 && this.#textDocument.lineCount > 0) this.#scheduleBackgroundTokenize(0);
|
|
43
|
+
};
|
|
44
|
+
#setTheme = (themeName) => {
|
|
45
|
+
this.#colorMap = this.#highlighter.setTheme(themeName).colorMap;
|
|
46
|
+
const { colors = {} } = this.#highlighter.getTheme(themeName);
|
|
47
|
+
const selectionBackground = colors["editor.selectionBackground"];
|
|
48
|
+
const lineHighlightBackground = colors["editor.lineHighlightBackground"];
|
|
49
|
+
const gutterForeground = colors["editorLineNumber.foreground"];
|
|
50
|
+
const gutterActiveForeground = colors["editorLineNumber.activeForeground"];
|
|
51
|
+
this.#setStyle(`:host {
|
|
52
|
+
--diffs-editor-selection-bg: ${selectionBackground ?? "var(--diffs-line-bg)"};
|
|
53
|
+
--diffs-editor-line-highlight-bg: ${lineHighlightBackground ?? "var(--diffs-line-bg)"};
|
|
54
|
+
--diffs-editor-line-number-fg: ${gutterForeground ?? "var(--diffs-fg-number)"};
|
|
55
|
+
--diffs-editor-line-number-active-bg: ${lineHighlightBackground ?? "var(--diffs-line-bg, var(--diffs-bg))"};
|
|
56
|
+
--diffs-editor-line-number-active-fg: ${gutterActiveForeground ?? "var(--diffs-selection-number-fg)"};
|
|
57
|
+
}`);
|
|
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
|
+
}
|
|
92
|
+
cleanUp() {
|
|
93
|
+
this.stopBackgroundTokenize();
|
|
94
|
+
this.#editorEventDisposes?.forEach((dispose) => dispose());
|
|
95
|
+
this.#editorEventDisposes = void 0;
|
|
96
|
+
}
|
|
97
|
+
tokenize(change, renderRange) {
|
|
98
|
+
if (this.#grammar === void 0) throw new Error("Grammar not loaded");
|
|
99
|
+
const { lineCount } = this.#textDocument;
|
|
100
|
+
const { startingLine = 0, totalLines = Infinity } = renderRange ?? {};
|
|
101
|
+
const renderRangeEndLine = totalLines === Infinity ? lineCount : Math.min(startingLine + totalLines, lineCount);
|
|
102
|
+
const dirtyStart = change.startLine;
|
|
103
|
+
const viewStart = Math.max(startingLine, dirtyStart);
|
|
104
|
+
const crossesRenderRangeEnd = renderRange !== void 0 && totalLines !== Infinity && change.lineDelta > 0 && dirtyStart < renderRangeEndLine && change.endLine >= renderRangeEndLine;
|
|
105
|
+
const canReuseCachedStates = change.lineDelta === 0;
|
|
106
|
+
const canCacheTokenizedStates = canReuseCachedStates || renderRange === void 0 || dirtyStart >= viewStart;
|
|
107
|
+
const changedLineRanges = canReuseCachedStates ? change.changedLineRanges ?? [[dirtyStart, change.endLine]] : [[dirtyStart, change.endLine]];
|
|
108
|
+
let offscreenSyncEnd = -1;
|
|
109
|
+
if (dirtyStart < viewStart) {
|
|
110
|
+
for (const [rangeStart, rangeEnd] of changedLineRanges) if (rangeStart < viewStart) offscreenSyncEnd = Math.max(offscreenSyncEnd, Math.min(rangeEnd, viewStart - 1));
|
|
111
|
+
}
|
|
112
|
+
const shouldFlushOffscreenLines = offscreenSyncEnd >= dirtyStart && (canReuseCachedStates || change.lineDelta < 0);
|
|
113
|
+
if (canReuseCachedStates) this.#buildStateStackMap(dirtyStart);
|
|
114
|
+
else {
|
|
115
|
+
this.#stateStackCache.length = Math.min(this.#stateStackCache.length, dirtyStart + 1);
|
|
116
|
+
if (renderRange === void 0 || dirtyStart >= viewStart) this.#buildStateStackMap(viewStart);
|
|
117
|
+
}
|
|
118
|
+
let changedRangeIndex = 0;
|
|
119
|
+
let currentChangedRangeEnd = changedLineRanges[changedRangeIndex][1];
|
|
120
|
+
let backgroundStartLine;
|
|
121
|
+
let backgroundChangedRangeIndex = 0;
|
|
122
|
+
let line = canReuseCachedStates ? changedLineRanges[changedRangeIndex][0] : viewStart;
|
|
123
|
+
let state = this.#stateStackCache[line] ?? INITIAL;
|
|
124
|
+
let settled = false;
|
|
125
|
+
const dirtyLines = /* @__PURE__ */ new Map();
|
|
126
|
+
const offscreenDirtyLines = shouldFlushOffscreenLines ? /* @__PURE__ */ new Map() : void 0;
|
|
127
|
+
if (offscreenDirtyLines !== void 0 && !canReuseCachedStates) {
|
|
128
|
+
const offscreenEnd = Math.min(offscreenSyncEnd + 1, viewStart, renderRangeEndLine);
|
|
129
|
+
if (offscreenEnd > dirtyStart) {
|
|
130
|
+
this.#buildStateStackMap(offscreenEnd);
|
|
131
|
+
let offscreenLine = dirtyStart;
|
|
132
|
+
let offscreenState = this.#stateStackCache[offscreenLine] ?? INITIAL;
|
|
133
|
+
for (; offscreenLine < offscreenEnd; offscreenLine++) {
|
|
134
|
+
const resolved = this.#tokenizeLineAt(offscreenLine, offscreenState);
|
|
135
|
+
offscreenState = resolved.state;
|
|
136
|
+
offscreenDirtyLines.set(offscreenLine, resolved.resolvedTokens);
|
|
137
|
+
}
|
|
138
|
+
if (canCacheTokenizedStates) this.#stateStackCache[offscreenEnd] = offscreenState;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
for (; line < renderRangeEndLine;) {
|
|
142
|
+
const previousNextState = canReuseCachedStates ? this.#stateStackCache[line + 1] : void 0;
|
|
143
|
+
if (canCacheTokenizedStates) this.#stateStackCache[line] = state;
|
|
144
|
+
const { resolvedTokens, state: nextState } = this.#tokenizeLineAt(line, state);
|
|
145
|
+
state = nextState;
|
|
146
|
+
if (line >= viewStart) dirtyLines.set(line, resolvedTokens);
|
|
147
|
+
else offscreenDirtyLines?.set(line, resolvedTokens);
|
|
148
|
+
if (canCacheTokenizedStates) this.#stateStackCache[line + 1] = state;
|
|
149
|
+
settled = line >= currentChangedRangeEnd && canReuseCachedStates && previousNextState !== void 0 && state.equals(previousNextState);
|
|
150
|
+
if (settled) {
|
|
151
|
+
changedRangeIndex++;
|
|
152
|
+
const nextRange = changedLineRanges[changedRangeIndex];
|
|
153
|
+
if (nextRange === void 0) break;
|
|
154
|
+
if (nextRange[0] >= renderRangeEndLine) {
|
|
155
|
+
backgroundStartLine = nextRange[0];
|
|
156
|
+
backgroundChangedRangeIndex = changedRangeIndex;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
if (this.#stateStackCache[nextRange[0]] === void 0) {
|
|
160
|
+
currentChangedRangeEnd = nextRange[1];
|
|
161
|
+
line++;
|
|
162
|
+
} else {
|
|
163
|
+
line = nextRange[0];
|
|
164
|
+
state = this.#stateStackCache[line] ?? state;
|
|
165
|
+
currentChangedRangeEnd = nextRange[1];
|
|
166
|
+
}
|
|
167
|
+
settled = false;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
line++;
|
|
171
|
+
}
|
|
172
|
+
if (canCacheTokenizedStates) if (line < renderRangeEndLine) this.#stateStackCache[line + 1] = state;
|
|
173
|
+
else this.#stateStackCache[line] = state;
|
|
174
|
+
if (offscreenDirtyLines !== void 0 && offscreenDirtyLines.size > 0) this.#onDeferTokenize(offscreenDirtyLines, this.#themeType);
|
|
175
|
+
if (backgroundStartLine !== void 0) this.#scheduleBackgroundTokenize(backgroundStartLine, changedLineRanges, backgroundChangedRangeIndex);
|
|
176
|
+
else if (!settled && line < lineCount) {
|
|
177
|
+
const backgroundLine = crossesRenderRangeEnd && dirtyStart >= viewStart ? renderRangeEndLine : dirtyStart < viewStart && !canReuseCachedStates ? dirtyStart : line;
|
|
178
|
+
this.#scheduleBackgroundTokenize(backgroundLine, canReuseCachedStates ? changedLineRanges : void 0, changedRangeIndex);
|
|
179
|
+
}
|
|
180
|
+
return dirtyLines;
|
|
181
|
+
}
|
|
182
|
+
prebuildStateStackMap(renderRange) {
|
|
183
|
+
this.#prebuildStateStackMap(renderRange);
|
|
184
|
+
}
|
|
185
|
+
stopBackgroundTokenize() {
|
|
186
|
+
removeEventListener("message", this.#onMessage);
|
|
187
|
+
this.#isStopped = true;
|
|
188
|
+
this.#lastLine = -1;
|
|
189
|
+
this.#backgroundChangedLineRanges = void 0;
|
|
190
|
+
this.#backgroundChangedRangeIndex = 0;
|
|
191
|
+
}
|
|
192
|
+
#scheduleBackgroundTokenize(startLine, changedLineRanges, changedRangeIndex = 0) {
|
|
193
|
+
const jobId = ++this.#backgroundJobId;
|
|
194
|
+
this.#isStopped = false;
|
|
195
|
+
this.#lastLine = startLine;
|
|
196
|
+
this.#backgroundChangedLineRanges = changedLineRanges;
|
|
197
|
+
this.#backgroundChangedRangeIndex = changedRangeIndex;
|
|
198
|
+
globalThis.addEventListener("message", this.#onMessage);
|
|
199
|
+
this.#postBackgroundTokenizeMessage(jobId);
|
|
200
|
+
}
|
|
201
|
+
#postBackgroundTokenizeMessage(jobId) {
|
|
202
|
+
globalThis.postMessage({
|
|
203
|
+
type: "tokenize",
|
|
204
|
+
jobId
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
#tokenizeLineAt(line, state) {
|
|
208
|
+
if (this.#grammar === void 0) throw new Error("Grammar not loaded");
|
|
209
|
+
const lineText = this.#textDocument.getLineText(line);
|
|
210
|
+
if (lineText.length > this.#tokenizeMaxLineLength) {
|
|
211
|
+
console.warn(`[diffs] Line(${line}) too long to tokenize: ${lineText.length}`);
|
|
212
|
+
return {
|
|
213
|
+
resolvedTokens: [[
|
|
214
|
+
0,
|
|
215
|
+
"",
|
|
216
|
+
lineText
|
|
217
|
+
]],
|
|
218
|
+
state
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
if (lineText === "" || lineText.trim() === "") return {
|
|
222
|
+
resolvedTokens: [[
|
|
223
|
+
0,
|
|
224
|
+
"",
|
|
225
|
+
lineText
|
|
226
|
+
]],
|
|
227
|
+
state
|
|
228
|
+
};
|
|
229
|
+
const result = tokenizeLine(this.#grammar, this.#colorMap, lineText, state, EditorTokenizer.TOKENIZE_TIME_LIMIT);
|
|
230
|
+
return {
|
|
231
|
+
resolvedTokens: result.resolvedTokens,
|
|
232
|
+
state: result.ruleStack
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
#buildStateStackMap(endAt) {
|
|
236
|
+
const boundedEndAt = Math.min(Math.max(0, endAt), this.#textDocument.lineCount);
|
|
237
|
+
if (this.#stateStackCache.length > boundedEndAt || this.#grammar === void 0) return;
|
|
238
|
+
let line = this.#stateStackCache.length - 1;
|
|
239
|
+
let state = this.#stateStackCache[line] ?? INITIAL;
|
|
240
|
+
for (; line < boundedEndAt; line++) {
|
|
241
|
+
this.#stateStackCache[line] = state;
|
|
242
|
+
const lineText = this.#textDocument.getLineText(line);
|
|
243
|
+
if (lineText.length <= this.#tokenizeMaxLineLength && lineText !== "" && lineText.trim() !== "") state = this.#grammar.tokenizeLine2(lineText, state, EditorTokenizer.TOKENIZE_TIME_LIMIT).ruleStack;
|
|
244
|
+
}
|
|
245
|
+
this.#stateStackCache[line] = state;
|
|
246
|
+
}
|
|
247
|
+
#backgroundTokenize(jobId) {
|
|
248
|
+
if (this.#isStopped || this.#grammar === void 0 || jobId !== this.#backgroundJobId) return;
|
|
249
|
+
const t = performance.now();
|
|
250
|
+
const lines = /* @__PURE__ */ new Map();
|
|
251
|
+
const totalLines = this.#textDocument.lineCount;
|
|
252
|
+
const changedLineRanges = this.#backgroundChangedLineRanges;
|
|
253
|
+
let line = this.#lastLine;
|
|
254
|
+
let state = this.#stateStackCache[line] ?? INITIAL;
|
|
255
|
+
let settled = false;
|
|
256
|
+
let changedRangeIndex = this.#backgroundChangedRangeIndex;
|
|
257
|
+
let currentChangedRangeEnd = changedLineRanges?.[changedRangeIndex]?.[1];
|
|
258
|
+
for (; line < totalLines;) {
|
|
259
|
+
this.#stateStackCache[line] = state;
|
|
260
|
+
const previousNextState = currentChangedRangeEnd !== void 0 ? this.#stateStackCache[line + 1] : void 0;
|
|
261
|
+
const lineText = this.#textDocument.getLineText(line);
|
|
262
|
+
if (lineText.length > this.#tokenizeMaxLineLength) {
|
|
263
|
+
console.warn(`[diffs] Line(${line}) too long to tokenize: ${lineText.length}`);
|
|
264
|
+
lines.set(line, [[
|
|
265
|
+
0,
|
|
266
|
+
"",
|
|
267
|
+
lineText
|
|
268
|
+
]]);
|
|
269
|
+
} else if (lineText === "" || lineText.trim() === "") lines.set(line, [[
|
|
270
|
+
0,
|
|
271
|
+
"",
|
|
272
|
+
lineText
|
|
273
|
+
]]);
|
|
274
|
+
else {
|
|
275
|
+
const ret = tokenizeLine(this.#grammar, this.#colorMap, lineText, state, EditorTokenizer.TOKENIZE_TIME_LIMIT);
|
|
276
|
+
lines.set(line, ret.resolvedTokens);
|
|
277
|
+
state = ret.ruleStack;
|
|
278
|
+
}
|
|
279
|
+
this.#stateStackCache[line + 1] = state;
|
|
280
|
+
settled = currentChangedRangeEnd !== void 0 && line >= currentChangedRangeEnd && previousNextState !== void 0 && state.equals(previousNextState);
|
|
281
|
+
line++;
|
|
282
|
+
if (settled) {
|
|
283
|
+
changedRangeIndex++;
|
|
284
|
+
const nextRange = changedLineRanges?.[changedRangeIndex];
|
|
285
|
+
if (nextRange === void 0) break;
|
|
286
|
+
currentChangedRangeEnd = nextRange[1];
|
|
287
|
+
if (this.#stateStackCache[nextRange[0]] === void 0) settled = false;
|
|
288
|
+
else {
|
|
289
|
+
line = nextRange[0];
|
|
290
|
+
state = this.#stateStackCache[line] ?? state;
|
|
291
|
+
settled = false;
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (performance.now() - t > 2) break;
|
|
296
|
+
}
|
|
297
|
+
this.#onDeferTokenize(lines, this.#themeType);
|
|
298
|
+
if (this.#isStopped || jobId !== this.#backgroundJobId) return;
|
|
299
|
+
if (settled || line >= totalLines) {
|
|
300
|
+
this.stopBackgroundTokenize();
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
this.#lastLine = line;
|
|
304
|
+
this.#backgroundChangedRangeIndex = changedRangeIndex;
|
|
305
|
+
this.#postBackgroundTokenizeMessage(jobId);
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
function tokenizeLine(grammar, colorMap, lineText, stateStack, timeLimit) {
|
|
309
|
+
const result = grammar.tokenizeLine2(lineText, stateStack, timeLimit);
|
|
310
|
+
if (result.stoppedEarly) console.warn(`[diffs] Time limit reached when tokenizing line: ${lineText.substring(0, 100)}`);
|
|
311
|
+
const rawTokens = result.tokens;
|
|
312
|
+
const tokensLength = rawTokens.length / 2;
|
|
313
|
+
const resolvedTokens = [];
|
|
314
|
+
for (let j = 0; j < tokensLength; j++) {
|
|
315
|
+
const offset = rawTokens[2 * j];
|
|
316
|
+
const nextOffset = j + 1 < tokensLength ? rawTokens[2 * j + 2] : lineText.length;
|
|
317
|
+
if (offset === nextOffset) continue;
|
|
318
|
+
const metadata = rawTokens[2 * j + 1];
|
|
319
|
+
const fg = colorMap[EncodedTokenMetadata.getForeground(metadata)];
|
|
320
|
+
const tokenText = lineText.slice(offset, nextOffset);
|
|
321
|
+
resolvedTokens.push([
|
|
322
|
+
offset,
|
|
323
|
+
fg,
|
|
324
|
+
tokenText
|
|
325
|
+
]);
|
|
326
|
+
}
|
|
327
|
+
return {
|
|
328
|
+
ruleStack: result.ruleStack,
|
|
329
|
+
resolvedTokens
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
function renderLineTokens(tokens, themeType) {
|
|
333
|
+
return tokens.map(([char, fg, textContent]) => {
|
|
334
|
+
if (char === 0 && fg === "") {
|
|
335
|
+
if (textContent === "") return h("br");
|
|
336
|
+
return textContent;
|
|
337
|
+
}
|
|
338
|
+
return h("span", {
|
|
339
|
+
dataset: { char: char.toString() },
|
|
340
|
+
style: `--diffs-token-${themeType}:${fg};`,
|
|
341
|
+
textContent
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
//#endregion
|
|
347
|
+
export { EditorTokenizer, renderLineTokens, tokenizeLine };
|
|
348
|
+
//# sourceMappingURL=tokenzier.js.map
|
|
@@ -0,0 +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":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/editor/utils.d.ts
|
|
2
|
+
declare function h<K extends keyof HTMLElementTagNameMap>(tagName: K, props?: {
|
|
3
|
+
style?: string | Partial<CSSStyleDeclaration>;
|
|
4
|
+
dataset?: DOMStringMap | string[] | string;
|
|
5
|
+
children?: (Node | string)[];
|
|
6
|
+
} & Partial<Omit<HTMLElementTagNameMap[K], 'style' | 'dataset' | 'children'>>, parent?: Element | ShadowRoot | DocumentFragment): HTMLElementTagNameMap[K];
|
|
7
|
+
declare function addEventListener<K extends keyof HTMLElementEventMap>(el: HTMLElement, event: K, listener: (this: HTMLElement, evt: HTMLElementEventMap[K]) => void, options?: AddEventListenerOptions): () => void;
|
|
8
|
+
declare function addEventListener<K extends keyof DocumentEventMap>(el: Document, event: K, listener: (this: Document, evt: DocumentEventMap[K]) => void, options?: AddEventListenerOptions): () => void;
|
|
9
|
+
declare function addEventListener<K extends keyof WindowEventMap>(el: Window, event: K, listener: (this: Window, evt: WindowEventMap[K]) => void, options?: AddEventListenerOptions): () => void;
|
|
10
|
+
declare function addEventListener<K extends keyof MediaQueryListEventMap>(el: MediaQueryList, event: K, listener: (this: MediaQueryList, evt: MediaQueryListEventMap[K]) => void, options?: AddEventListenerOptions): () => void;
|
|
11
|
+
declare function extend<T extends object>(obj: T, attrs: Partial<T>): T;
|
|
12
|
+
declare function debounce<T extends (...args: any[]) => void>(func: T, wait: number): (...args: Parameters<T>) => void;
|
|
13
|
+
declare function round(value: number, precision?: number): number;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { addEventListener, debounce, extend, h, round };
|
|
16
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","names":["h","K","HTMLElementTagNameMap","CSSStyleDeclaration","Partial","DOMStringMap","Node","Omit","Element","ShadowRoot","DocumentFragment","addEventListener","HTMLElementEventMap","HTMLElement","AddEventListenerOptions","DocumentEventMap","Document","WindowEventMap","Window","MediaQueryListEventMap","MediaQueryList","extend","T","debounce","Parameters","round"],"sources":["../../src/editor/utils.d.ts"],"sourcesContent":["export declare function h<K extends keyof HTMLElementTagNameMap>(tagName: K, props?: {\n style?: string | Partial<CSSStyleDeclaration>;\n dataset?: DOMStringMap | string[] | string;\n children?: (Node | string)[];\n} & Partial<Omit<HTMLElementTagNameMap[K], 'style' | 'dataset' | 'children'>>, parent?: Element | ShadowRoot | DocumentFragment): HTMLElementTagNameMap[K];\nexport declare function addEventListener<K extends keyof HTMLElementEventMap>(el: HTMLElement, event: K, listener: (this: HTMLElement, evt: HTMLElementEventMap[K]) => void, options?: AddEventListenerOptions): () => void;\nexport declare function addEventListener<K extends keyof DocumentEventMap>(el: Document, event: K, listener: (this: Document, evt: DocumentEventMap[K]) => void, options?: AddEventListenerOptions): () => void;\nexport declare function addEventListener<K extends keyof WindowEventMap>(el: Window, event: K, listener: (this: Window, evt: WindowEventMap[K]) => void, options?: AddEventListenerOptions): () => void;\nexport declare function addEventListener<K extends keyof MediaQueryListEventMap>(el: MediaQueryList, event: K, listener: (this: MediaQueryList, evt: MediaQueryListEventMap[K]) => void, options?: AddEventListenerOptions): () => void;\nexport declare function extend<T extends object>(obj: T, attrs: Partial<T>): T;\nexport declare function debounce<T extends (...args: any[]) => void>(func: T, wait: number): (...args: Parameters<T>) => void;\nexport declare function round(value: number, precision?: number): number;\n//# sourceMappingURL=utils.d.ts.map"],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/editor/utils.ts
|
|
2
|
+
function h(tagName, props, parent) {
|
|
3
|
+
const { style, dataset, children,...attrs } = props ?? {};
|
|
4
|
+
const el = document.createElement(tagName);
|
|
5
|
+
Object.assign(el, attrs);
|
|
6
|
+
if (style !== void 0) if (typeof style === "string") el.style.cssText = style;
|
|
7
|
+
else Object.assign(el.style, style);
|
|
8
|
+
if (dataset !== void 0) if (typeof dataset === "string") el.dataset[dataset] = "";
|
|
9
|
+
else if (Array.isArray(dataset)) dataset.forEach((key) => {
|
|
10
|
+
el.dataset[key] = "";
|
|
11
|
+
});
|
|
12
|
+
else Object.assign(el.dataset, dataset);
|
|
13
|
+
if (children !== void 0) el.replaceChildren(...children);
|
|
14
|
+
if (parent !== void 0) parent.appendChild(el);
|
|
15
|
+
return el;
|
|
16
|
+
}
|
|
17
|
+
function addEventListener(el, event, listener, options) {
|
|
18
|
+
el.addEventListener(event, listener, options);
|
|
19
|
+
return () => el.removeEventListener(event, listener);
|
|
20
|
+
}
|
|
21
|
+
function extend(obj, attrs) {
|
|
22
|
+
return Object.assign(obj, attrs);
|
|
23
|
+
}
|
|
24
|
+
function debounce(func, wait) {
|
|
25
|
+
let timeout;
|
|
26
|
+
return function(...args) {
|
|
27
|
+
clearTimeout(timeout);
|
|
28
|
+
timeout = setTimeout(() => func.apply(this, args), wait);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function round(value, precision = 1e3) {
|
|
32
|
+
return Math.round(value * precision) / precision;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
export { addEventListener, debounce, extend, h, round };
|
|
37
|
+
//# sourceMappingURL=utils.js.map
|