@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.
Files changed (165) hide show
  1. package/dist/components/CodeView.d.ts +0 -1
  2. package/dist/components/CodeView.d.ts.map +1 -1
  3. package/dist/components/CodeView.js +0 -23
  4. package/dist/components/CodeView.js.map +1 -1
  5. package/dist/components/File.d.ts +7 -2
  6. package/dist/components/File.d.ts.map +1 -1
  7. package/dist/components/File.js +36 -2
  8. package/dist/components/File.js.map +1 -1
  9. package/dist/components/FileDiff.d.ts +8 -2
  10. package/dist/components/FileDiff.d.ts.map +1 -1
  11. package/dist/components/FileDiff.js +73 -1
  12. package/dist/components/FileDiff.js.map +1 -1
  13. package/dist/components/UnresolvedFile.d.ts.map +1 -1
  14. package/dist/components/UnresolvedFile.js +2 -2
  15. package/dist/components/VirtualizedFile.d.ts +2 -1
  16. package/dist/components/VirtualizedFile.d.ts.map +1 -1
  17. package/dist/components/VirtualizedFile.js +48 -48
  18. package/dist/components/VirtualizedFile.js.map +1 -1
  19. package/dist/components/VirtualizedFileDiff.js +42 -22
  20. package/dist/components/VirtualizedFileDiff.js.map +1 -1
  21. package/dist/components/Virtualizer.d.ts +1 -1
  22. package/dist/components/Virtualizer.d.ts.map +1 -1
  23. package/dist/components/Virtualizer.js +3 -5
  24. package/dist/components/Virtualizer.js.map +1 -1
  25. package/dist/constants.d.ts.map +1 -1
  26. package/dist/editor/command.d.ts +6 -0
  27. package/dist/editor/command.d.ts.map +1 -0
  28. package/dist/editor/command.js +31 -0
  29. package/dist/editor/command.js.map +1 -0
  30. package/dist/editor/css.d.ts +6 -0
  31. package/dist/editor/css.d.ts.map +1 -0
  32. package/dist/editor/css.js +218 -0
  33. package/dist/editor/css.js.map +1 -0
  34. package/dist/editor/editStack.d.ts +66 -0
  35. package/dist/editor/editStack.d.ts.map +1 -0
  36. package/dist/editor/editStack.js +218 -0
  37. package/dist/editor/editStack.js.map +1 -0
  38. package/dist/editor/editor.d.ts +22 -0
  39. package/dist/editor/editor.d.ts.map +1 -0
  40. package/dist/editor/editor.js +1323 -0
  41. package/dist/editor/editor.js.map +1 -0
  42. package/dist/editor/index.d.ts +3 -0
  43. package/dist/editor/index.js +4 -0
  44. package/dist/editor/lineAnnotations.d.ts +8 -0
  45. package/dist/editor/lineAnnotations.d.ts.map +1 -0
  46. package/dist/editor/lineAnnotations.js +32 -0
  47. package/dist/editor/lineAnnotations.js.map +1 -0
  48. package/dist/editor/pieceTable.d.ts +33 -0
  49. package/dist/editor/pieceTable.d.ts.map +1 -0
  50. package/dist/editor/pieceTable.js +590 -0
  51. package/dist/editor/pieceTable.js.map +1 -0
  52. package/dist/editor/platform.d.ts +12 -0
  53. package/dist/editor/platform.d.ts.map +1 -0
  54. package/dist/editor/platform.js +44 -0
  55. package/dist/editor/platform.js.map +1 -0
  56. package/dist/editor/quickEdit.d.ts +29 -0
  57. package/dist/editor/quickEdit.d.ts.map +1 -0
  58. package/dist/editor/quickEdit.js +81 -0
  59. package/dist/editor/quickEdit.js.map +1 -0
  60. package/dist/editor/searchPanel.d.ts +30 -0
  61. package/dist/editor/searchPanel.d.ts.map +1 -0
  62. package/dist/editor/searchPanel.js +219 -0
  63. package/dist/editor/searchPanel.js.map +1 -0
  64. package/dist/editor/selection.d.ts +126 -0
  65. package/dist/editor/selection.d.ts.map +1 -0
  66. package/dist/editor/selection.js +900 -0
  67. package/dist/editor/selection.js.map +1 -0
  68. package/dist/editor/textDocument.d.ts +139 -0
  69. package/dist/editor/textDocument.d.ts.map +1 -0
  70. package/dist/editor/textDocument.js +202 -0
  71. package/dist/editor/textDocument.js.map +1 -0
  72. package/dist/editor/textMeasure.d.ts +32 -0
  73. package/dist/editor/textMeasure.d.ts.map +1 -0
  74. package/dist/editor/textMeasure.js +108 -0
  75. package/dist/editor/textMeasure.js.map +1 -0
  76. package/dist/editor/tokenzier.d.ts +37 -0
  77. package/dist/editor/tokenzier.d.ts.map +1 -0
  78. package/dist/editor/tokenzier.js +348 -0
  79. package/dist/editor/tokenzier.js.map +1 -0
  80. package/dist/editor/utils.d.ts +16 -0
  81. package/dist/editor/utils.d.ts.map +1 -0
  82. package/dist/editor/utils.js +37 -0
  83. package/dist/editor/utils.js.map +1 -0
  84. package/dist/index.d.ts +2 -2
  85. package/dist/index.js +2 -2
  86. package/dist/react/EditorContext.d.ts +16 -0
  87. package/dist/react/EditorContext.d.ts.map +1 -0
  88. package/dist/react/EditorContext.js +26 -0
  89. package/dist/react/EditorContext.js.map +1 -0
  90. package/dist/react/File.d.ts +2 -1
  91. package/dist/react/File.d.ts.map +1 -1
  92. package/dist/react/File.js +3 -2
  93. package/dist/react/File.js.map +1 -1
  94. package/dist/react/FileDiff.d.ts +3 -1
  95. package/dist/react/FileDiff.d.ts.map +1 -1
  96. package/dist/react/FileDiff.js +3 -2
  97. package/dist/react/FileDiff.js.map +1 -1
  98. package/dist/react/MultiFileDiff.d.ts +3 -1
  99. package/dist/react/MultiFileDiff.d.ts.map +1 -1
  100. package/dist/react/MultiFileDiff.js +3 -2
  101. package/dist/react/MultiFileDiff.js.map +1 -1
  102. package/dist/react/PatchDiff.d.ts +3 -1
  103. package/dist/react/PatchDiff.d.ts.map +1 -1
  104. package/dist/react/PatchDiff.js +3 -2
  105. package/dist/react/PatchDiff.js.map +1 -1
  106. package/dist/react/index.d.ts +3 -2
  107. package/dist/react/index.js +2 -1
  108. package/dist/react/jsx.d.ts +0 -1
  109. package/dist/react/jsx.d.ts.map +1 -1
  110. package/dist/react/types.d.ts +1 -0
  111. package/dist/react/types.d.ts.map +1 -1
  112. package/dist/react/types.js +0 -1
  113. package/dist/react/utils/useFileDiffInstance.d.ts +3 -1
  114. package/dist/react/utils/useFileDiffInstance.d.ts.map +1 -1
  115. package/dist/react/utils/useFileDiffInstance.js +31 -5
  116. package/dist/react/utils/useFileDiffInstance.js.map +1 -1
  117. package/dist/react/utils/useFileInstance.d.ts +4 -1
  118. package/dist/react/utils/useFileInstance.d.ts.map +1 -1
  119. package/dist/react/utils/useFileInstance.js +30 -5
  120. package/dist/react/utils/useFileInstance.js.map +1 -1
  121. package/dist/renderers/DiffHunksRenderer.d.ts +2 -2
  122. package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
  123. package/dist/renderers/DiffHunksRenderer.js +9 -5
  124. package/dist/renderers/DiffHunksRenderer.js.map +1 -1
  125. package/dist/renderers/FileRenderer.d.ts +5 -1
  126. package/dist/renderers/FileRenderer.d.ts.map +1 -1
  127. package/dist/renderers/FileRenderer.js +108 -41
  128. package/dist/renderers/FileRenderer.js.map +1 -1
  129. package/dist/ssr/index.d.ts +2 -2
  130. package/dist/style.js +1 -1
  131. package/dist/style.js.map +1 -1
  132. package/dist/types.d.ts +45 -1
  133. package/dist/types.d.ts.map +1 -1
  134. package/dist/utils/cleanLastNewline.js +6 -1
  135. package/dist/utils/cleanLastNewline.js.map +1 -1
  136. package/dist/utils/computeEstimatedDiffHeights.js +20 -9
  137. package/dist/utils/computeEstimatedDiffHeights.js.map +1 -1
  138. package/dist/utils/computeFileOffsets.d.ts +13 -0
  139. package/dist/utils/computeFileOffsets.d.ts.map +1 -0
  140. package/dist/utils/computeFileOffsets.js +33 -0
  141. package/dist/utils/computeFileOffsets.js.map +1 -0
  142. package/dist/utils/createTransformerWithState.js +9 -0
  143. package/dist/utils/createTransformerWithState.js.map +1 -1
  144. package/dist/utils/iterateOverDiff.js +182 -147
  145. package/dist/utils/iterateOverDiff.js.map +1 -1
  146. package/dist/utils/renderDiffWithHighlighter.js +1 -1
  147. package/dist/utils/renderFileWithHighlighter.js +5 -14
  148. package/dist/utils/renderFileWithHighlighter.js.map +1 -1
  149. package/dist/utils/virtualDiffLayout.d.ts +2 -23
  150. package/dist/utils/virtualDiffLayout.d.ts.map +1 -1
  151. package/dist/utils/virtualDiffLayout.js +1 -41
  152. package/dist/utils/virtualDiffLayout.js.map +1 -1
  153. package/dist/worker/WorkerPoolManager.js +1 -1
  154. package/dist/worker/{wasm-BaDzIkIn.js → wasm-D4DU5jgR.js} +2 -2
  155. package/dist/worker/wasm-D4DU5jgR.js.map +1 -0
  156. package/dist/worker/worker-portable.js +349 -363
  157. package/dist/worker/worker-portable.js.map +1 -1
  158. package/dist/worker/worker.js +222 -243
  159. package/dist/worker/worker.js.map +1 -1
  160. package/package.json +9 -1
  161. package/dist/utils/iterateOverFile.d.ts +0 -50
  162. package/dist/utils/iterateOverFile.d.ts.map +0 -1
  163. package/dist/utils/iterateOverFile.js +0 -49
  164. package/dist/utils/iterateOverFile.js.map +0 -1
  165. package/dist/worker/wasm-BaDzIkIn.js.map +0 -1
@@ -0,0 +1,1323 @@
1
+ import { getFiletypeFromFileName } from "../utils/getFiletypeFromFileName.js";
2
+ import { isMoveCursorShortcut, isPrimaryModifier, isSafari } from "./platform.js";
3
+ import { resolveEditorCommandFromKeyboardEvent } from "./command.js";
4
+ import { editorCSS, editorGlobalCSS } from "./css.js";
5
+ import { applyDocumentChangeToLineAnnotations } from "./lineAnnotations.js";
6
+ import { DirectionBackward, DirectionForward, DirectionNone, applyDeleteHardLineForwardToSelections, applyTextChangeToSelections, applyTextReplaceToSelections, applyTransposeToSelections, comparePosition, convertSelection, createSelectionFrom, createSelectionFromAnchorAndFocusOffsets, expandCollapsedSelectionToWord, extendSelection, extendSelections, findNexMatch, getCaretPosition, getDocumentBoundarySelection, getDocumentFullSelection, getSelectionAnchor, getSelectionText, isCollapsedSelection, isLineEditable, mapCursorMove, mapSelectionShift, mergeOverlappingSelections, resolveIndentEdits, selectionIntersects } from "./selection.js";
7
+ import { TextDocument } from "./textDocument.js";
8
+ import { addEventListener, debounce, extend, h, round } from "./utils.js";
9
+ import { QuickEditWidget } from "./quickEdit.js";
10
+ import { SearchPanelWidget } from "./searchPanel.js";
11
+ import { Metrics, getExpandedAsciiTextColumns, getUnicodeMeasurementOffsets, snapTextOffsetToUnicodeBoundary } from "./textMeasure.js";
12
+ import { EditorTokenizer, renderLineTokens } from "./tokenzier.js";
13
+
14
+ //#region src/editor/editor.ts
15
+ function clampDomOffset(node, offset) {
16
+ if (node.nodeType === 3) {
17
+ const length = node.textContent?.length ?? 0;
18
+ return Math.max(0, Math.min(offset, length));
19
+ }
20
+ if (node.nodeType === 1) return Math.max(0, Math.min(offset, node.childNodes.length));
21
+ return 0;
22
+ }
23
+ var Editor = class {
24
+ #options;
25
+ #editMode = "simple";
26
+ #wrap = false;
27
+ #metrics = new Metrics();
28
+ #tokenizer;
29
+ #editorEventDisposes;
30
+ #globalEventDisposes;
31
+ #mouseUpDisposes;
32
+ #detach;
33
+ #component;
34
+ #fileContents;
35
+ #lineAnnotations;
36
+ #textDocument;
37
+ #renderRange;
38
+ #codePaddingTop = 0;
39
+ #gutterWidthCache;
40
+ #contentWidthCache;
41
+ #lineYCache = /* @__PURE__ */ new Map();
42
+ #wrapLineOffsetsCache = /* @__PURE__ */ new Map();
43
+ #lastCharX;
44
+ #globalStyleElement;
45
+ #editorStyleElement;
46
+ #themeStyleElement;
47
+ #componentContainer;
48
+ #contentElement;
49
+ #overlayElement;
50
+ #primaryCaretElement;
51
+ #selectionElements;
52
+ #quickEdit;
53
+ #searchPanel;
54
+ #resizeObserver;
55
+ #shouldIgnoreSelectionChange = false;
56
+ #isGutterMouseDown = false;
57
+ #isContentMouseDown = false;
58
+ #shiftKeyPressed = false;
59
+ #selectionStart;
60
+ #reservedSelections;
61
+ #selections;
62
+ #initSelections;
63
+ #scrollingToLine;
64
+ #scrollingToLineChar;
65
+ #retainSearchPanelFocus = false;
66
+ #emitChange = debounce((fileContents, lineAnnotations) => {
67
+ this.#options.onChange?.(fileContents, lineAnnotations);
68
+ }, 500);
69
+ #onDeferTokenize = (lines, themeType) => {
70
+ this.#component?.applyLineChange?.(lines, themeType);
71
+ if (this.#renderRange !== void 0 && this.#renderRange.totalLines !== Infinity) {
72
+ const { startingLine, totalLines } = this.#renderRange;
73
+ const endLine = Math.min(startingLine + totalLines, this.#textDocument?.lineCount ?? 0);
74
+ for (const [line, tokens] of lines) if (line >= startingLine && line < endLine) {
75
+ const lineElement = this.#getLineElement(line);
76
+ if (lineElement !== void 0) lineElement.replaceChildren(...renderLineTokens(tokens, themeType));
77
+ }
78
+ }
79
+ };
80
+ constructor(options = {}) {
81
+ this.#options = options;
82
+ }
83
+ edit(component) {
84
+ const { useTokenTransformer, enableGutterUtility, enableLineSelection, expandUnchanged, lineHoverHighlight,...rest } = component.options;
85
+ if (useTokenTransformer !== true || enableGutterUtility === true || enableLineSelection === true || expandUnchanged !== true && Object.hasOwn(component, "fileDiff") || lineHoverHighlight !== "disabled") {
86
+ component.setOptions({
87
+ ...rest,
88
+ useTokenTransformer: true,
89
+ enableGutterUtility: false,
90
+ enableLineSelection: false,
91
+ expandUnchanged: true,
92
+ lineHoverHighlight: "disabled"
93
+ });
94
+ component.rerender();
95
+ }
96
+ this.#component = component;
97
+ this.#initialize();
98
+ this.#detach = component.attachEditor(this);
99
+ return () => this.cleanUp();
100
+ }
101
+ setSelections(selections) {
102
+ const textDocument = this.#textDocument;
103
+ if (textDocument !== void 0) {
104
+ const resolvedSelections = selections.map((selection) => {
105
+ const start = textDocument.normalizePosition(selection.start);
106
+ const end = textDocument.normalizePosition(selection.end);
107
+ return {
108
+ direction: selection.direction === "none" ? DirectionNone : selection.direction === "backward" ? DirectionBackward : DirectionForward,
109
+ start,
110
+ end
111
+ };
112
+ });
113
+ this.#updateSelections(resolvedSelections);
114
+ this.#scrollToPrimaryCaret();
115
+ } else this.#initSelections = selections;
116
+ }
117
+ focus(options) {
118
+ const preventScroll = options?.preventScroll ?? false;
119
+ const primarySelection = this.#selections?.at(-1);
120
+ if (primarySelection !== void 0) {
121
+ const pos = primarySelection.direction === DirectionBackward ? primarySelection.end : primarySelection.start;
122
+ this.#focus(pos, preventScroll);
123
+ } else this.#focus(void 0, preventScroll);
124
+ }
125
+ cleanUp() {
126
+ this.#tokenizer?.cleanUp();
127
+ this.#tokenizer = void 0;
128
+ this.#globalEventDisposes?.forEach((dispose) => dispose());
129
+ this.#globalEventDisposes = void 0;
130
+ this.#editorEventDisposes?.forEach((dispose) => dispose());
131
+ this.#editorEventDisposes = void 0;
132
+ this.#detach?.();
133
+ this.#detach = void 0;
134
+ this.#component?.setSelectedLines(null);
135
+ this.#component = void 0;
136
+ this.#fileContents = void 0;
137
+ this.#lineAnnotations = void 0;
138
+ this.#textDocument = void 0;
139
+ this.#renderRange = void 0;
140
+ this.#gutterWidthCache = void 0;
141
+ this.#contentWidthCache = void 0;
142
+ this.#lineYCache.clear();
143
+ this.#wrapLineOffsetsCache.clear();
144
+ this.#lastCharX = void 0;
145
+ this.#globalStyleElement?.remove();
146
+ this.#globalStyleElement = void 0;
147
+ this.#editorStyleElement?.remove();
148
+ this.#editorStyleElement = void 0;
149
+ this.#themeStyleElement?.remove();
150
+ this.#themeStyleElement = void 0;
151
+ this.#componentContainer = void 0;
152
+ this.#contentElement?.removeAttribute("contentEditable");
153
+ this.#contentElement = void 0;
154
+ this.#overlayElement?.remove();
155
+ this.#overlayElement = void 0;
156
+ this.#primaryCaretElement?.remove();
157
+ this.#primaryCaretElement = void 0;
158
+ this.#selectionElements?.forEach((el) => el.remove());
159
+ this.#selectionElements?.clear();
160
+ this.#selectionElements = void 0;
161
+ this.#searchPanel?.cleanup();
162
+ this.#searchPanel = void 0;
163
+ this.#quickEdit?.cleanup();
164
+ this.#quickEdit = void 0;
165
+ this.#resizeObserver?.disconnect();
166
+ this.#resizeObserver = void 0;
167
+ this.#shouldIgnoreSelectionChange = false;
168
+ this.#selectionStart = void 0;
169
+ this.#selections = void 0;
170
+ this.#reservedSelections = void 0;
171
+ }
172
+ syncWithRender(highlighter, fileContainer, fileContents, lineAnnotations, renderRange, editMode) {
173
+ const shadowRoot = fileContainer.shadowRoot;
174
+ if (shadowRoot == null) {
175
+ console.error("[editor] Could not find the shadow root.");
176
+ return;
177
+ }
178
+ let codeElement;
179
+ for (const el of shadowRoot.querySelectorAll("[data-code]")) if (el.dataset.deletions === void 0) {
180
+ codeElement = el;
181
+ break;
182
+ }
183
+ if (codeElement === void 0) return;
184
+ const contentEl = codeElement.children[1];
185
+ if (contentEl === void 0) return;
186
+ this.#editMode = editMode ?? "simple";
187
+ this.#wrap = this.#component?.options.overflow === "wrap";
188
+ if (editMode === "advanced" || (lineAnnotations?.length ?? 0) > 0) {
189
+ let startingLine;
190
+ let endLine;
191
+ for (const child of contentEl.children) {
192
+ const el = child;
193
+ const line = el.dataset.line;
194
+ const lineType = el.dataset.lineType;
195
+ if (line !== void 0) {
196
+ const lineIndex = Number(line) - 1;
197
+ startingLine ??= lineIndex;
198
+ endLine = lineIndex;
199
+ }
200
+ if (lineType === void 0 || !isLineEditable(lineType)) el.contentEditable = "false";
201
+ }
202
+ if (endLine !== void 0 && renderRange !== void 0) {
203
+ const { startingLine: startingLine$1, totalLines } = renderRange;
204
+ endLine = Math.max(endLine, startingLine$1 + totalLines);
205
+ }
206
+ if (startingLine !== void 0 && endLine !== void 0) renderRange = {
207
+ startingLine,
208
+ totalLines: endLine - startingLine,
209
+ bufferBefore: 0,
210
+ bufferAfter: 0
211
+ };
212
+ }
213
+ if (this.#componentContainer !== fileContainer) {
214
+ this.#componentContainer = fileContainer;
215
+ this.#codePaddingTop = Number(getComputedStyle(codeElement).paddingTop.slice(0, -2));
216
+ if (this.#globalStyleElement !== void 0) fileContainer.appendChild(this.#globalStyleElement);
217
+ if (this.#editorStyleElement !== void 0) shadowRoot.appendChild(this.#editorStyleElement);
218
+ if (this.#themeStyleElement !== void 0) shadowRoot.appendChild(this.#themeStyleElement);
219
+ }
220
+ if (this.#textDocument === void 0 || this.#fileContents === void 0 || this.#fileContents.name !== fileContents.name) {
221
+ const textDocument = new TextDocument(fileContents.name, fileContents.contents, fileContents.lang ?? getFiletypeFromFileName(fileContents.name));
222
+ this.#fileContents = fileContents;
223
+ this.#textDocument = textDocument;
224
+ this.#tokenizer?.cleanUp();
225
+ this.#tokenizer = new EditorTokenizer({
226
+ highlighter,
227
+ textDocument,
228
+ codeOptions: this.#component?.options ?? {},
229
+ onDeferTokenize: this.#onDeferTokenize,
230
+ setStyle: (css) => {
231
+ this.#themeStyleElement.textContent = css;
232
+ }
233
+ });
234
+ this.#shouldIgnoreSelectionChange = false;
235
+ this.#selectionElements?.forEach((el) => el.remove());
236
+ this.#selectionElements?.clear();
237
+ this.#component?.setSelectedLines(null);
238
+ this.#selectionElements = void 0;
239
+ this.#selections = void 0;
240
+ this.#scrollingToLine = void 0;
241
+ this.#reservedSelections = void 0;
242
+ this.#searchPanel?.cleanup();
243
+ this.#searchPanel = void 0;
244
+ this.#quickEdit?.cleanup();
245
+ this.#quickEdit = void 0;
246
+ }
247
+ if (this.#contentElement !== contentEl) {
248
+ const guttterEl = contentEl.previousElementSibling;
249
+ const targetIsContentElement = (e) => {
250
+ const target = e.composedPath()[0];
251
+ return target === contentEl || contentEl.contains(target);
252
+ };
253
+ this.#metrics.init(contentEl);
254
+ this.#contentElement = extend(contentEl, {
255
+ contentEditable: "true",
256
+ role: "textbox",
257
+ ariaMultiLine: "true",
258
+ autocapitalize: "off",
259
+ writingSuggestions: "off",
260
+ autocorrect: false,
261
+ spellcheck: false,
262
+ translate: false
263
+ });
264
+ if (this.#overlayElement !== void 0) contentEl.after(this.#overlayElement);
265
+ this.#editorEventDisposes?.forEach((dispose) => dispose());
266
+ this.#editorEventDisposes = [
267
+ addEventListener(contentEl, "pointerdown", (e) => {
268
+ if (e.pointerType !== "mouse") return;
269
+ if (isSafari() && this.#lineAnnotations !== void 0 && this.#lineAnnotations.length > 0) this.#mouseUpDisposes = [...contentEl.querySelectorAll("[data-line-annotation]")].map((el) => [addEventListener(el, "mouseenter", () => {
270
+ this.#shouldIgnoreSelectionChange = true;
271
+ }), addEventListener(el, "mouseleave", () => {
272
+ this.#shouldIgnoreSelectionChange = false;
273
+ })]).flat();
274
+ this.#isContentMouseDown = true;
275
+ this.#selectionStart = void 0;
276
+ if (e.button === 0 && isPrimaryModifier(e)) this.#reservedSelections = this.#selections?.map((selection) => ({ ...selection }));
277
+ if (e.shiftKey) {
278
+ const primarySelection = this.#selections?.at(-1);
279
+ if (primarySelection !== void 0) {
280
+ const pos = primarySelection.direction === DirectionBackward ? primarySelection.end : primarySelection.start;
281
+ this.#updateWindowSelection({
282
+ start: pos,
283
+ end: pos,
284
+ direction: DirectionNone
285
+ });
286
+ }
287
+ this.#shiftKeyPressed = true;
288
+ }
289
+ }, { passive: true }),
290
+ addEventListener(contentEl, "keydown", (e) => {
291
+ if (e.key === "Escape") {
292
+ e.preventDefault();
293
+ this.#searchPanel?.cleanup();
294
+ this.#searchPanel = void 0;
295
+ this.#retainSearchPanelFocus = false;
296
+ this.#quickEdit?.cleanup();
297
+ this.#quickEdit = void 0;
298
+ if (this.#selections !== void 0 && this.#selections.length > 0) {
299
+ const primarySelection = this.#selections.at(-1);
300
+ if (!isCollapsedSelection(primarySelection) || this.#selections.length > 1) {
301
+ const pos = getCaretPosition(primarySelection);
302
+ this.#updateSelections([{
303
+ start: pos,
304
+ end: pos,
305
+ direction: DirectionNone
306
+ }]);
307
+ this.#focus(pos);
308
+ }
309
+ }
310
+ return;
311
+ }
312
+ if (!targetIsContentElement(e)) return;
313
+ const mvShortcut = isMoveCursorShortcut(e);
314
+ const textDocument = this.#textDocument;
315
+ if (this.#selections !== void 0 && this.#selections.length > 0 && mvShortcut !== void 0 && textDocument !== void 0) {
316
+ if (e.shiftKey) this.#updateSelections(mapSelectionShift(textDocument, this.#selections, mvShortcut));
317
+ else this.#updateSelections(mapCursorMove(textDocument, this.#selections, mvShortcut));
318
+ this.#scrollToPrimaryCaret();
319
+ e.preventDefault();
320
+ return;
321
+ }
322
+ const command = resolveEditorCommandFromKeyboardEvent(e);
323
+ if (command !== void 0) {
324
+ e.preventDefault();
325
+ this.#runCommand(command);
326
+ }
327
+ }),
328
+ addEventListener(contentEl, "copy", (e) => {
329
+ if (!targetIsContentElement(e)) return;
330
+ e.preventDefault();
331
+ e.clipboardData?.setData("text", this.#getSelectionText());
332
+ }),
333
+ addEventListener(contentEl, "cut", (e) => {
334
+ if (!targetIsContentElement(e)) return;
335
+ e.preventDefault();
336
+ e.clipboardData?.setData("text", this.#getSelectionText());
337
+ this.#replaceSelectionText("");
338
+ }),
339
+ addEventListener(contentEl, "paste", (e) => {
340
+ if (!targetIsContentElement(e)) return;
341
+ e.preventDefault();
342
+ const text = e.clipboardData?.getData("text");
343
+ if (text !== void 0) this.#replaceSelectionText(text);
344
+ }),
345
+ addEventListener(contentEl, "beforeinput", (e) => {
346
+ if (!targetIsContentElement(e)) return;
347
+ e.preventDefault();
348
+ this.#handleInput(e.inputType, e.data);
349
+ }),
350
+ addEventListener(contentEl, "compositionstart", (e) => {
351
+ if (!targetIsContentElement(e)) return;
352
+ this.#shouldIgnoreSelectionChange = true;
353
+ }, { passive: true }),
354
+ addEventListener(contentEl, "compositionend", (e) => {
355
+ if (!targetIsContentElement(e)) return;
356
+ this.#shouldIgnoreSelectionChange = false;
357
+ this.#handleInput("insertText", e.data);
358
+ }, { passive: true })
359
+ ];
360
+ if (guttterEl !== null && guttterEl.dataset.gutter !== void 0) this.#editorEventDisposes.push(addEventListener(guttterEl, "pointerdown", (e) => {
361
+ let target = e.composedPath()[0];
362
+ if (target?.dataset.lineNumberContent !== void 0) target = target.parentElement ?? void 0;
363
+ const textDocument = this.#textDocument;
364
+ if (target === void 0 || textDocument === void 0) return;
365
+ const lineNumber = target.dataset.columnNumber;
366
+ const lineType = target.dataset.lineType;
367
+ if (lineNumber === void 0 || lineType === void 0 || !isLineEditable(lineType)) return;
368
+ const lineIndex = Number(lineNumber) - 1;
369
+ const selection = {
370
+ start: {
371
+ line: lineIndex,
372
+ character: 0
373
+ },
374
+ end: {
375
+ line: lineIndex,
376
+ character: textDocument.getLineText(lineIndex).length
377
+ },
378
+ direction: DirectionForward
379
+ };
380
+ this.#isGutterMouseDown = true;
381
+ this.#selectionStart = selection;
382
+ this.#updateSelections([selection]);
383
+ this.#focus(selection.end);
384
+ this.#mouseUpDisposes = [addEventListener(document, "mousemove", (e$1) => {
385
+ let target$1 = e$1.composedPath()[0];
386
+ if (target$1?.dataset.lineNumberContent !== void 0) target$1 = target$1?.parentElement ?? void 0;
387
+ else if (target$1?.tagName === "SPAN") target$1 = target$1?.closest("[data-line]");
388
+ if (target$1 === void 0) return;
389
+ const lineNumber$1 = target$1.dataset.columnNumber ?? target$1.dataset.line;
390
+ const lineType$1 = target$1.dataset.lineType;
391
+ if (this.#isGutterMouseDown && this.#textDocument !== void 0 && lineNumber$1 !== void 0 && lineType$1 !== void 0 && isLineEditable(lineType$1)) {
392
+ const lineIndex$1 = Number(lineNumber$1) - 1;
393
+ let selection$1 = {
394
+ start: {
395
+ line: lineIndex$1,
396
+ character: 0
397
+ },
398
+ end: {
399
+ line: lineIndex$1,
400
+ character: this.#textDocument.getLineText(lineIndex$1).length
401
+ },
402
+ direction: DirectionForward
403
+ };
404
+ if (this.#selectionStart !== void 0) selection$1 = createSelectionFrom(this.#selectionStart, selection$1);
405
+ else this.#selectionStart = selection$1;
406
+ this.#updateSelections([selection$1]);
407
+ this.#focus(selection$1.end);
408
+ }
409
+ }, { passive: true })];
410
+ }, { passive: true }));
411
+ this.#resizeObserver?.disconnect();
412
+ this.#resizeObserver = new ResizeObserver(() => {
413
+ requestAnimationFrame(() => {
414
+ this.#handleLayoutResize();
415
+ });
416
+ });
417
+ this.#resizeObserver.observe(contentEl);
418
+ this.#resizeObserver.observe(contentEl.parentElement);
419
+ }
420
+ this.#lineYCache.clear();
421
+ this.#wrapLineOffsetsCache.clear();
422
+ this.#lastCharX = void 0;
423
+ this.#lineAnnotations = lineAnnotations;
424
+ this.#renderRange = renderRange;
425
+ this.#tokenizer?.prebuildStateStackMap(renderRange);
426
+ if (this.#initSelections !== void 0) {
427
+ this.setSelections(this.#initSelections);
428
+ this.#scrollToPrimaryCaret();
429
+ this.#initSelections = void 0;
430
+ } else if (this.#selections !== void 0 && this.#selections.length > 0) this.#updateSelections(this.#selections);
431
+ if (this.#options.__debug === true && renderRange !== void 0) {
432
+ const { startingLine, totalLines } = renderRange;
433
+ console.log("[diffs/editor] render file:", fileContents.name, "RenderRange:", startingLine + "-" + (startingLine + totalLines), "of", this.#textDocument.lineCount, "lines");
434
+ }
435
+ if (this.#scrollingToLine !== void 0) {
436
+ this.#scrollToLine(this.#scrollingToLine, this.#scrollingToLineChar);
437
+ this.#scrollingToLine = void 0;
438
+ this.#scrollingToLineChar = void 0;
439
+ } else if (this.#selections !== void 0 && this.#selections.length > 0) this.focus({ preventScroll: true });
440
+ if (this.#retainSearchPanelFocus) {
441
+ this.#retainSearchPanelFocus = false;
442
+ requestAnimationFrame(() => {
443
+ this.#searchPanel?.focus();
444
+ });
445
+ }
446
+ if (this.#quickEdit !== void 0 && this.#isLineVisible(this.#quickEdit.line) && this.#contentElement !== void 0) this.#quickEdit.render(this.#contentElement);
447
+ }
448
+ #initialize() {
449
+ this.#editorStyleElement = h("style", {
450
+ dataset: "editorCss",
451
+ textContent: editorCSS
452
+ });
453
+ this.#themeStyleElement = h("style", { dataset: "editorThemeCss" });
454
+ this.#globalStyleElement = h("style", {
455
+ dataset: "editorGlobalCss",
456
+ textContent: editorGlobalCSS
457
+ });
458
+ this.#overlayElement = h("div", { dataset: "editorOverlay" });
459
+ this.#globalEventDisposes = [
460
+ addEventListener(document, "selectionchange", () => {
461
+ const shadowRoot = this.#componentContainer?.shadowRoot;
462
+ if (this.#shouldIgnoreSelectionChange || shadowRoot == null) return;
463
+ const composedRange = document.getSelection()?.getComposedRanges({ shadowRoots: [shadowRoot] })?.[0];
464
+ if (composedRange === void 0 || !this.#rangeBelongsToEditor(composedRange)) return;
465
+ let selection = convertSelection(composedRange, DirectionNone);
466
+ if (selection === void 0) return;
467
+ if (this.#isContentMouseDown && this.#shiftKeyPressed && this.#selections !== void 0 && this.#selections.length > 0) {
468
+ const primarySelection = this.#selections.at(-1);
469
+ this.#updateSelections([extendSelection(primarySelection, selection)]);
470
+ return;
471
+ }
472
+ if (this.#isContentMouseDown) if (this.#selectionStart !== void 0) selection = createSelectionFrom(this.#selectionStart, selection);
473
+ else this.#selectionStart = selection;
474
+ else if (this.#selectionStart !== void 0) selection.direction = createSelectionFrom(this.#selectionStart, selection).direction;
475
+ if (this.#reservedSelections !== void 0) this.#updateSelections([...this.#reservedSelections.filter((reservedSelection) => !selectionIntersects(reservedSelection, selection)), selection]);
476
+ else this.#updateSelections([selection]);
477
+ }, { passive: true }),
478
+ addEventListener(document, "pointerup", (e) => {
479
+ if (e.pointerType !== "mouse") return;
480
+ this.#mouseUpDisposes?.forEach((dispose) => dispose());
481
+ this.#mouseUpDisposes = void 0;
482
+ if (this.#isGutterMouseDown) {
483
+ this.#isGutterMouseDown = false;
484
+ this.#focus();
485
+ }
486
+ this.#shouldIgnoreSelectionChange = false;
487
+ this.#isContentMouseDown = false;
488
+ this.#shiftKeyPressed = false;
489
+ this.#selectionStart = void 0;
490
+ this.#reservedSelections = void 0;
491
+ this.#selectionElements?.forEach((el, key) => {
492
+ if (key.startsWith("quickEditIcon-")) el.dataset.visible = "true";
493
+ });
494
+ }, { passive: true }),
495
+ addEventListener(document, "keydown", (e) => {
496
+ if (e.key === "Shift") this.#selectionStart = this.#selections?.at(-1);
497
+ }, { passive: true }),
498
+ addEventListener(document, "keyup", (e) => {
499
+ if (e.key === "Shift") this.#selectionStart = void 0;
500
+ }, { passive: true })
501
+ ];
502
+ }
503
+ #runCommand(command) {
504
+ const textDocument = this.#textDocument;
505
+ if (textDocument === void 0) return;
506
+ switch (command) {
507
+ case "openSearchPanel":
508
+ this.#renderSearchPanel();
509
+ break;
510
+ case "findNextMatch": {
511
+ const selections = this.#selections;
512
+ const textDocument$1 = this.#textDocument;
513
+ if (selections === void 0 || textDocument$1 === void 0) break;
514
+ if (selections.some(isCollapsedSelection)) {
515
+ const expanded = selections.map((sel) => {
516
+ if (isCollapsedSelection(sel)) return expandCollapsedSelectionToWord(textDocument$1, sel);
517
+ return sel;
518
+ });
519
+ this.#updateSelections(expanded);
520
+ this.focus();
521
+ } else {
522
+ const nextMatch = findNexMatch(textDocument$1, selections);
523
+ if (nextMatch !== void 0) {
524
+ this.#updateSelections(nextMatch);
525
+ this.#scrollToPrimaryCaret();
526
+ }
527
+ }
528
+ break;
529
+ }
530
+ case "indent":
531
+ case "outdent":
532
+ if (this.#selections !== void 0) {
533
+ const edits = [];
534
+ const nextSelections = [];
535
+ for (const selection of this.#selections) {
536
+ const startLine = selection.start.line;
537
+ const outdent = command === "outdent";
538
+ if (startLine !== selection.end.line || outdent) {
539
+ const ret = resolveIndentEdits(textDocument, selection, this.#metrics.tabSize, outdent);
540
+ edits.push(...ret[0]);
541
+ nextSelections.push(ret[1]);
542
+ } else {
543
+ const lineChar0 = textDocument.charAt({
544
+ line: startLine,
545
+ character: 0
546
+ });
547
+ this.#replaceSelectionText(lineChar0 === " " ? " " : " ".repeat(this.#metrics.tabSize));
548
+ }
549
+ }
550
+ const change = textDocument.applyEdits(edits, true, this.#selections, nextSelections);
551
+ if (change !== void 0) this.#applyChange(change, nextSelections);
552
+ }
553
+ break;
554
+ case "selectAll":
555
+ this.#updateSelections([getDocumentFullSelection(textDocument)]);
556
+ this.focus();
557
+ break;
558
+ case "moveCursorToDocStart":
559
+ case "moveCursorToDocEnd":
560
+ {
561
+ const atEnd = command === "moveCursorToDocEnd";
562
+ this.#updateSelections([getDocumentBoundarySelection(textDocument, atEnd)]);
563
+ this.#scrollToPrimaryCaret();
564
+ }
565
+ break;
566
+ case "expandSelectionDocStart":
567
+ case "expandSelectionDocEnd":
568
+ {
569
+ const atEnd = command === "expandSelectionDocEnd";
570
+ const selections = this.#selections;
571
+ if (selections !== void 0) {
572
+ this.#updateSelections(extendSelections(selections, getDocumentBoundarySelection(textDocument, atEnd)));
573
+ this.#scrollToPrimaryCaret();
574
+ }
575
+ }
576
+ break;
577
+ case "undo":
578
+ if (this.#textDocument?.canUndo === true) {
579
+ const undoResult = this.#textDocument.undo();
580
+ if (undoResult !== void 0) this.#applyChange(...undoResult);
581
+ }
582
+ break;
583
+ case "redo":
584
+ if (this.#textDocument?.canRedo === true) {
585
+ const redoResult = this.#textDocument.redo();
586
+ if (redoResult !== void 0) this.#applyChange(...redoResult);
587
+ }
588
+ break;
589
+ }
590
+ }
591
+ #handleLayoutResize() {
592
+ const lineAnnotations = this.#lineAnnotations?.length ?? 0;
593
+ const prevGutterWidth = this.#gutterWidthCache;
594
+ const prevContentWidth = this.#contentWidthCache;
595
+ this.#gutterWidthCache = void 0;
596
+ this.#contentWidthCache = void 0;
597
+ const gutterWidthChanged = this.#getGutterWidth() !== prevGutterWidth;
598
+ const contentWidthChanged = this.#getContentWidth() !== prevContentWidth;
599
+ if (!gutterWidthChanged && !contentWidthChanged) return;
600
+ this.#lastCharX = void 0;
601
+ if (contentWidthChanged && (this.#wrap || lineAnnotations > 0)) {
602
+ this.#lineYCache.clear();
603
+ this.#wrapLineOffsetsCache.clear();
604
+ }
605
+ if (this.#selections !== void 0) {
606
+ this.#updateSelections(this.#selections);
607
+ this.focus();
608
+ }
609
+ }
610
+ #rerender(change, newLineAnnotations, renderRange = this.#renderRange, shouldUpdateBuffer) {
611
+ const tokenizer = this.#tokenizer;
612
+ const component = this.#component;
613
+ const fileContents = this.#fileContents;
614
+ const textDocument = this.#textDocument;
615
+ const contentEl = this.#contentElement;
616
+ const gutterEl = this.#contentElement?.previousElementSibling ?? void 0;
617
+ if (tokenizer === void 0 || component === void 0 || fileContents === void 0 || textDocument === void 0 || contentEl === void 0 || gutterEl === void 0 || !(gutterEl instanceof HTMLElement) || gutterEl.dataset.gutter === void 0) return;
618
+ tokenizer.stopBackgroundTokenize();
619
+ const t = performance.now();
620
+ const isAdvancedMode = this.#editMode === "advanced";
621
+ const dirtyLines = tokenizer.tokenize(change, renderRange);
622
+ const t2 = performance.now();
623
+ if (dirtyLines.size > 0) {
624
+ const children = contentEl.children;
625
+ const dirtyLineIndexes = new Set(dirtyLines.keys());
626
+ if (isAdvancedMode) for (const child of children) {
627
+ const el = child;
628
+ if (el.dataset.line !== void 0) {
629
+ const lineIndex = Number(el.dataset.line) - 1;
630
+ const tokens = dirtyLines.get(lineIndex);
631
+ if (tokens !== void 0) {
632
+ el.replaceChildren(...renderLineTokens(tokens, tokenizer.themeType));
633
+ dirtyLineIndexes.delete(lineIndex);
634
+ if (dirtyLineIndexes.size === 0) break;
635
+ }
636
+ }
637
+ }
638
+ else {
639
+ const startingLine = renderRange?.startingLine ?? 0;
640
+ for (let i = change.startLine - startingLine; i < children.length; i++) {
641
+ const child = children[i];
642
+ if (child?.dataset.line !== void 0) {
643
+ const lineIndex = Number(child.dataset.line) - 1;
644
+ if (dirtyLines.has(lineIndex)) {
645
+ const tokens = dirtyLines.get(lineIndex);
646
+ child.replaceChildren(...renderLineTokens(tokens, tokenizer.themeType));
647
+ dirtyLineIndexes.delete(lineIndex);
648
+ if (dirtyLineIndexes.size === 0) break;
649
+ }
650
+ }
651
+ }
652
+ }
653
+ if (dirtyLineIndexes.size > 0) for (const lineIndex of dirtyLineIndexes) {
654
+ const tokens = dirtyLines.get(lineIndex);
655
+ const lineNumber = String(lineIndex + 1);
656
+ h("div", {
657
+ dataset: {
658
+ line: lineNumber,
659
+ lineType: "context",
660
+ lineIndex: lineIndex.toString()
661
+ },
662
+ children: renderLineTokens(tokens, tokenizer.themeType)
663
+ }, contentEl);
664
+ h("div", {
665
+ dataset: {
666
+ lineType: "context",
667
+ columnNumber: lineNumber,
668
+ lineIndex: lineIndex.toString()
669
+ },
670
+ children: [h("span", {
671
+ dataset: { lineNumberContent: "" },
672
+ textContent: lineNumber
673
+ })]
674
+ }, gutterEl);
675
+ }
676
+ }
677
+ if (change.lineDelta < 0) for (const parent of [contentEl, gutterEl]) {
678
+ const children = parent.children;
679
+ for (let i = children.length - 1; i >= 0; i--) {
680
+ const child = children[i];
681
+ const { lineIndex, lineAnnotation } = child.dataset;
682
+ if (lineIndex !== void 0 || lineAnnotation !== void 0) {
683
+ if (Number(lineAnnotation !== void 0 ? lineAnnotation.split(",")[1] : lineIndex) < change.lineCount) break;
684
+ child.remove();
685
+ }
686
+ }
687
+ }
688
+ if (change.lineDelta !== 0) {
689
+ gutterEl.style.gridRow = "span " + gutterEl.children.length;
690
+ contentEl.style.gridRow = "span " + contentEl.children.length;
691
+ }
692
+ component.applyLineChange?.(dirtyLines, tokenizer.themeType);
693
+ if (change.lineDelta !== 0 || isAdvancedMode) component.applyLayoutChange(textDocument, newLineAnnotations, shouldUpdateBuffer);
694
+ if (this.#options.__debug === true) console.log(`[diffs/editor] re-render in: ${round(performance.now() - t2)}ms,`, `tokenize in: ${round(t2 - t)}ms (${dirtyLines.size} dirty lines)`);
695
+ }
696
+ #handleInput(inputType, data) {
697
+ switch (inputType) {
698
+ case "insertText":
699
+ this.#replaceSelectionText(data ?? "");
700
+ break;
701
+ case "insertParagraph":
702
+ this.#replaceSelectionText("\n");
703
+ break;
704
+ case "deleteContentBackward":
705
+ this.#deleteSelectionText();
706
+ break;
707
+ case "deleteContentForward":
708
+ this.#deleteSelectionText(true);
709
+ break;
710
+ case "deleteHardLineForward":
711
+ this.#deleteHardLineForward();
712
+ break;
713
+ case "insertTranspose":
714
+ this.#insertTranspose();
715
+ break;
716
+ default:
717
+ console.warn(`[diffs] Unknown input type: ${inputType}`);
718
+ break;
719
+ }
720
+ }
721
+ #updateSelections(selections) {
722
+ if (selections.length === 0) return;
723
+ const gutterBuffer = this.#contentElement?.previousElementSibling;
724
+ const normalizedSelections = mergeOverlappingSelections(selections);
725
+ const primarySelection = normalizedSelections.at(-1);
726
+ this.#selections = normalizedSelections;
727
+ this.#primaryCaretElement = void 0;
728
+ this.#component?.setSelectedLines(null);
729
+ gutterBuffer?.querySelectorAll("[data-active]").forEach((el) => el.removeAttribute("data-active"));
730
+ if (isCollapsedSelection(primarySelection)) {
731
+ const line = primarySelection.start.line + 1;
732
+ this.#component?.setSelectedLines({
733
+ start: line,
734
+ end: line
735
+ });
736
+ } else if (gutterBuffer !== void 0 && gutterBuffer instanceof HTMLElement) {
737
+ const pos = getCaretPosition(primarySelection);
738
+ gutterBuffer.querySelector(`[data-column-number="${pos.line + 1}"]`)?.setAttribute("data-active", "");
739
+ }
740
+ const fragment = document.createDocumentFragment();
741
+ const renderCtx = {
742
+ fragment,
743
+ elements: /* @__PURE__ */ new Map()
744
+ };
745
+ for (const selection of normalizedSelections) {
746
+ if (!isCollapsedSelection(selection)) this.#renderSelection(renderCtx, selection);
747
+ this.#renderCaret(renderCtx, selection, selection === primarySelection);
748
+ }
749
+ if (this.#options.enabledQuickEdit === true && !isCollapsedSelection(primarySelection)) this.#renderQuickEditIcon(renderCtx, primarySelection);
750
+ this.#overlayElement?.appendChild(fragment);
751
+ this.#selectionElements?.forEach((el) => el.remove());
752
+ this.#selectionElements?.clear();
753
+ this.#selectionElements = renderCtx.elements;
754
+ }
755
+ #updateWindowSelection(selection) {
756
+ const winSelection = window.getSelection();
757
+ if (winSelection === null) return;
758
+ let { start, end, direction } = selection;
759
+ if (comparePosition(start, end) > 0) [start, end] = [end, start];
760
+ const startLineElement = this.#getLineElement(start.line);
761
+ const endLineElement = this.#getLineElement(end.line);
762
+ if (startLineElement === void 0 || endLineElement === void 0) return;
763
+ let [anchorNode, anchorOffset] = getSelectionAnchor(startLineElement, start.character);
764
+ let [focusNode, focusOffset] = getSelectionAnchor(endLineElement, end.character);
765
+ if (direction === DirectionBackward) [anchorNode, anchorOffset, focusNode, focusOffset] = [
766
+ focusNode,
767
+ focusOffset,
768
+ anchorNode,
769
+ anchorOffset
770
+ ];
771
+ try {
772
+ winSelection.setBaseAndExtent(anchorNode, clampDomOffset(anchorNode, anchorOffset), focusNode, clampDomOffset(focusNode, focusOffset));
773
+ } catch (err) {
774
+ console.error("[diffs/editor] failed to update window selection:", err);
775
+ }
776
+ }
777
+ #focus(position, preventScroll = true) {
778
+ if (position !== void 0) {
779
+ this.#shouldIgnoreSelectionChange = true;
780
+ this.#updateWindowSelection({
781
+ start: position,
782
+ end: position,
783
+ direction: DirectionNone
784
+ });
785
+ requestAnimationFrame(() => {
786
+ this.#contentElement?.focus({ preventScroll });
787
+ requestAnimationFrame(() => {
788
+ this.#shouldIgnoreSelectionChange = false;
789
+ });
790
+ });
791
+ } else requestAnimationFrame(() => {
792
+ this.#contentElement?.focus({ preventScroll });
793
+ });
794
+ }
795
+ #scrollToPrimaryCaret() {
796
+ const primaryCaretElement = this.#primaryCaretElement;
797
+ const primarySelection = this.#selections?.at(-1);
798
+ if (primarySelection === void 0) return;
799
+ if (primaryCaretElement !== void 0) {
800
+ primaryCaretElement.scrollIntoView({
801
+ block: "nearest",
802
+ inline: "nearest"
803
+ });
804
+ this.#focus(primarySelection.direction === DirectionBackward ? primarySelection.end : primarySelection.start);
805
+ } else {
806
+ const pos = getCaretPosition(primarySelection);
807
+ this.#scrollToLine(pos.line, pos.character);
808
+ }
809
+ }
810
+ #getScrollMargin() {
811
+ const componentTop = this.#component?.top ?? 0;
812
+ const top = this.#searchPanel !== void 0 ? 48 : 0;
813
+ const start = this.#getGutterWidth() + this.#metrics.ch;
814
+ const end = this.#metrics.ch;
815
+ return `${componentTop + top}px ${end}px 0 ${start}px`;
816
+ }
817
+ #scrollToLine(line, char = 0) {
818
+ const virtualCaret = h("div", { style: {
819
+ position: "absolute",
820
+ left: "0",
821
+ width: "2px",
822
+ height: this.#metrics.lineHeight + "px",
823
+ scrollMargin: this.#getScrollMargin()
824
+ } });
825
+ if (this.#getLineElement(line) !== void 0) {
826
+ const [left, wrapLine] = this.#getCharX(line, char);
827
+ const lineY = this.#getLineY(line) + wrapLine * this.#metrics.lineHeight;
828
+ virtualCaret.style.top = lineY + "px";
829
+ virtualCaret.style.left = left + "px";
830
+ this.#overlayElement?.appendChild(virtualCaret);
831
+ virtualCaret.scrollIntoView({
832
+ block: "center",
833
+ inline: "nearest"
834
+ });
835
+ this.#focus({
836
+ line,
837
+ character: char
838
+ });
839
+ requestAnimationFrame(() => virtualCaret.remove());
840
+ } else {
841
+ const approximateLineY = ((this.#lineAnnotations ?? []).filter((annotation) => annotation.lineNumber < line).length + line) * this.#metrics.lineHeight;
842
+ virtualCaret.style.top = approximateLineY + "px";
843
+ this.#componentContainer?.shadowRoot?.appendChild(virtualCaret);
844
+ this.#scrollingToLine = line;
845
+ this.#scrollingToLineChar = char;
846
+ virtualCaret.scrollIntoView({
847
+ block: "center",
848
+ inline: "nearest"
849
+ });
850
+ requestAnimationFrame(() => virtualCaret.remove());
851
+ }
852
+ }
853
+ #renderSelection(renderCtx, selection) {
854
+ if (this.#textDocument === void 0) return;
855
+ const { start, end } = selection;
856
+ for (let ln = start.line; ln <= end.line; ln++) {
857
+ if (!this.#isLineVisible(ln)) continue;
858
+ const lineText = this.#textDocument.getLineText(ln);
859
+ const startChar = ln === start.line ? start.character : 0;
860
+ const endChar = ln === end.line ? end.character : lineText.length;
861
+ if (this.#wrap) {
862
+ const paddingInline = this.#metrics.ch;
863
+ const contentWidth = this.#getContentWidth();
864
+ if (2 * paddingInline + this.#metrics.measureTextWidth(lineText) > contentWidth) {
865
+ this.#renderWrappedSelection(renderCtx, selection, ln, lineText, startChar, endChar, paddingInline);
866
+ continue;
867
+ }
868
+ }
869
+ let left = 0;
870
+ let width = 0;
871
+ if (startChar === endChar && startChar === 0) {
872
+ left = this.#getGutterWidth() + this.#metrics.ch;
873
+ width = ln === end.line ? 0 : this.#metrics.ch;
874
+ } else {
875
+ left = this.#getCharX(ln, startChar)[0];
876
+ width = endChar === startChar ? 0 : this.#getCharX(ln, endChar)[0] - left;
877
+ }
878
+ this.#renderSelectionRange(renderCtx, selection, ln, 0, startChar, endChar, width, left);
879
+ }
880
+ }
881
+ #renderWrappedSelection(renderCtx, selection, line, lineText, startChar, endChar, paddingInline) {
882
+ const wrapOffsets = this.#wrapLineText(line);
883
+ const segmentCount = wrapOffsets.length - 1;
884
+ const lastSegmentIndex = segmentCount - 1;
885
+ const offsetLeft = this.#getGutterWidth() + paddingInline;
886
+ for (let w = 0; w < segmentCount; w++) {
887
+ const segmentStart = wrapOffsets[w];
888
+ const segmentEnd = wrapOffsets[w + 1];
889
+ const wrapStartChar = Math.max(startChar, segmentStart);
890
+ const wrapEndChar = Math.min(endChar, segmentEnd);
891
+ if (wrapStartChar > wrapEndChar) continue;
892
+ if (wrapStartChar === wrapEndChar) {
893
+ const isAtLineStart = wrapStartChar === 0 && w === 0;
894
+ const isAtLineEnd = wrapEndChar === lineText.length && w === lastSegmentIndex;
895
+ if (!isAtLineStart && !isAtLineEnd) continue;
896
+ }
897
+ let segmentLeft;
898
+ let segmentWidth;
899
+ if (wrapStartChar === 0 && wrapEndChar === 0) {
900
+ segmentLeft = offsetLeft;
901
+ segmentWidth = line === selection.end.line ? 0 : paddingInline;
902
+ } else {
903
+ const prefixInSegment = lineText.slice(segmentStart, wrapStartChar);
904
+ const prefixAsciiColumns = getExpandedAsciiTextColumns(prefixInSegment, this.#metrics.tabSize);
905
+ segmentLeft = offsetLeft + (prefixAsciiColumns !== -1 ? prefixAsciiColumns * this.#metrics.ch : this.#metrics.measureTextWidth(prefixInSegment));
906
+ if (wrapStartChar === wrapEndChar) segmentWidth = 0;
907
+ else {
908
+ const selectionInSegment = lineText.slice(wrapStartChar, wrapEndChar);
909
+ const selectionAsciiWidth = getExpandedAsciiTextColumns(selectionInSegment, this.#metrics.tabSize);
910
+ segmentWidth = selectionAsciiWidth !== -1 ? selectionAsciiWidth * this.#metrics.ch : this.#metrics.measureTextWidth(selectionInSegment);
911
+ }
912
+ }
913
+ this.#renderSelectionRange(renderCtx, selection, line, w, wrapStartChar, wrapEndChar, segmentWidth, segmentLeft, w === lastSegmentIndex);
914
+ }
915
+ }
916
+ #renderSelectionRange(renderCtx, selection, ln, wrapLine, startChar, endChar, width, left, applyEolSpacing = true) {
917
+ const css = `width:${width + (!applyEolSpacing || selection.end.line === ln || startChar === endChar && ln !== selection.start.line ? 0 : this.#metrics.ch)}px;transform:translateY(${this.#getLineY(ln) + wrapLine * this.#metrics.lineHeight}px) translateX(${left}px);`;
918
+ const cacheKey = "selection-range-" + css;
919
+ const selectionEls = this.#selectionElements;
920
+ if (renderCtx.elements.has(cacheKey)) return;
921
+ let rangeEl;
922
+ if (selectionEls?.has(cacheKey) === true) {
923
+ rangeEl = selectionEls.get(cacheKey);
924
+ selectionEls.delete(cacheKey);
925
+ } else rangeEl = h("div", {
926
+ dataset: "selectionRange",
927
+ style: { cssText: css }
928
+ }, renderCtx.fragment);
929
+ renderCtx.elements.set(cacheKey, rangeEl);
930
+ }
931
+ #renderCaret(renderCtx, selection, isPrimary) {
932
+ const { line, character } = getCaretPosition(selection);
933
+ if (!this.#isLineVisible(line)) return;
934
+ const [left, wrapLine] = this.#getCharX(line, character);
935
+ const cacheKey = "caret-" + line + "(" + wrapLine + ")-" + character;
936
+ if (renderCtx.elements.has(cacheKey)) return;
937
+ const caretEl = h("div", {
938
+ dataset: "caret",
939
+ style: { transform: `translateY(${this.#getLineY(line) + wrapLine * this.#metrics.lineHeight}px) translateX(${left - 1}px)` }
940
+ }, renderCtx.fragment);
941
+ renderCtx.elements.set(cacheKey, caretEl);
942
+ if (isPrimary) {
943
+ caretEl.style.scrollMargin = this.#getScrollMargin();
944
+ this.#primaryCaretElement = caretEl;
945
+ }
946
+ }
947
+ #renderQuickEditIcon(renderCtx, selection) {
948
+ const line = getCaretPosition(selection).line;
949
+ if (!this.#isLineVisible(line)) return;
950
+ const [left, wrapLine] = this.#getCharX(line, 0);
951
+ const cacheKey = "quickEditIcon-" + line + "(" + wrapLine + ")";
952
+ if (renderCtx.elements.has(cacheKey)) return;
953
+ const quickEditIcon = QuickEditWidget.renderIcon(left, this.#getLineY(line) + wrapLine * this.#metrics.lineHeight, renderCtx.fragment, () => {
954
+ const cleanUpQuickEdit = () => {
955
+ this.#quickEdit?.cleanup();
956
+ this.#quickEdit = void 0;
957
+ };
958
+ const handleWidgetDomResize = () => {
959
+ this.#lineYCache.clear();
960
+ if (this.#selections !== void 0) this.#updateSelections(this.#selections);
961
+ };
962
+ cleanUpQuickEdit();
963
+ const textDocument = this.#textDocument;
964
+ const renderQuickEdit = this.#options.renderQuickEdit;
965
+ const fileContainer = this.#componentContainer;
966
+ if (textDocument === void 0 || renderQuickEdit === void 0 || fileContainer == null) return;
967
+ const line$1 = selection.end.line;
968
+ const lineText = textDocument.getLineText(line$1);
969
+ const quickEditElement = renderQuickEdit({
970
+ textDocument,
971
+ selection,
972
+ applyEdits: (edits) => {
973
+ const change = textDocument.applyEdits(edits, true, this.#selections);
974
+ if (change !== void 0) this.#applyChange(change);
975
+ },
976
+ getSelectionText: () => {
977
+ return this.#textDocument?.getText(selection) ?? "";
978
+ },
979
+ replaceSelectionText: (text) => {
980
+ this.#replaceSelectionText(text);
981
+ },
982
+ close: () => {
983
+ cleanUpQuickEdit();
984
+ handleWidgetDomResize();
985
+ this.#scrollToPrimaryCaret();
986
+ }
987
+ });
988
+ let leadingWhitespaces = 0;
989
+ for (let i = 0; i < lineText.length; i++) {
990
+ const charCode = lineText.charCodeAt(i);
991
+ if (charCode === 32) leadingWhitespaces++;
992
+ else if (charCode === 9) leadingWhitespaces += this.#metrics.tabSize;
993
+ else break;
994
+ }
995
+ this.#quickEdit = new QuickEditWidget(line$1, quickEditElement, fileContainer, leadingWhitespaces, handleWidgetDomResize);
996
+ this.#updateSelections([selection]);
997
+ if (this.#isLineVisible(line$1) && this.#contentElement !== void 0) this.#quickEdit.render(this.#contentElement);
998
+ });
999
+ renderCtx.elements.set(cacheKey, quickEditIcon);
1000
+ }
1001
+ #renderSearchPanel() {
1002
+ this.#searchPanel?.cleanup();
1003
+ const textDocument = this.#textDocument;
1004
+ const preElement = this.#componentContainer?.shadowRoot?.querySelector("pre");
1005
+ if (textDocument === void 0 || preElement == null) return;
1006
+ let defaultQuery = "";
1007
+ let initialMatch = void 0;
1008
+ const selections = this.#selections;
1009
+ if (selections !== void 0 && selections.length > 0) {
1010
+ let primarySelection = selections.at(-1);
1011
+ if (isCollapsedSelection(primarySelection)) {
1012
+ primarySelection = expandCollapsedSelectionToWord(textDocument, primarySelection);
1013
+ this.#updateSelections([...selections.slice(0, -1), primarySelection]);
1014
+ const selectionText = textDocument.getText(primarySelection);
1015
+ if (!selectionText.includes("\n")) {
1016
+ defaultQuery = selectionText;
1017
+ initialMatch = [textDocument.offsetAt(primarySelection.start), textDocument.offsetAt(primarySelection.end)];
1018
+ }
1019
+ }
1020
+ }
1021
+ this.#searchPanel = new SearchPanelWidget({
1022
+ textDocument,
1023
+ containerElement: preElement,
1024
+ defaultQuery,
1025
+ initialMatch,
1026
+ getCurrentSearchRange: () => this.#selections?.at(-1),
1027
+ postSearch: (kind, [startOffset, endOffset], retainFocus) => {
1028
+ if (kind === "findNext" || kind === "findPrevious" || kind === "replace") {
1029
+ const nextSelection = createSelectionFromAnchorAndFocusOffsets(textDocument, startOffset, endOffset);
1030
+ this.#updateSelections([nextSelection]);
1031
+ this.#scrollToPrimaryCaret();
1032
+ if (retainFocus === true) {
1033
+ this.#retainSearchPanelFocus = true;
1034
+ requestAnimationFrame(() => {
1035
+ this.#searchPanel?.focus();
1036
+ });
1037
+ }
1038
+ } else if (kind === "findAll" || kind === "replaceAll") {
1039
+ const { line, character } = textDocument.positionAt(startOffset);
1040
+ this.#scrollToLine(line, character);
1041
+ }
1042
+ },
1043
+ onClose: () => {
1044
+ this.#searchPanel = void 0;
1045
+ this.#retainSearchPanelFocus = false;
1046
+ }
1047
+ });
1048
+ this.#retainSearchPanelFocus = false;
1049
+ }
1050
+ #getSelectionText() {
1051
+ const textDocument = this.#textDocument;
1052
+ const selections = this.#selections;
1053
+ if (textDocument === void 0 || selections === void 0) return "";
1054
+ return getSelectionText(textDocument, selections);
1055
+ }
1056
+ #replaceSelectionText(text) {
1057
+ const selections = this.#selections;
1058
+ if (selections === void 0) return;
1059
+ const textDocument = this.#textDocument;
1060
+ const primarySelection = selections.at(-1);
1061
+ if (textDocument === void 0 || primarySelection === void 0) return;
1062
+ const { nextSelections, change } = Array.isArray(text) && text.length === selections.length ? applyTextReplaceToSelections(textDocument, selections, text, this.#lineAnnotations) : applyTextChangeToSelections(textDocument, selections, {
1063
+ start: textDocument.offsetAt(primarySelection.start),
1064
+ end: textDocument.offsetAt(primarySelection.end),
1065
+ text: Array.isArray(text) ? text.join("\n") : text
1066
+ }, this.#lineAnnotations);
1067
+ if (change !== void 0) this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change));
1068
+ }
1069
+ #deleteSelectionText(forward = false) {
1070
+ const selections = this.#selections;
1071
+ const textDocument = this.#textDocument;
1072
+ if (selections === void 0 || textDocument === void 0) return;
1073
+ const primarySelection = selections.at(-1);
1074
+ if (primarySelection === void 0) return;
1075
+ const edit = isCollapsedSelection(primarySelection) ? (() => {
1076
+ const offset = textDocument.offsetAt(primarySelection.start);
1077
+ const nextOffset = forward ? Math.min(textDocument.getText().length, offset + 1) : Math.max(0, offset - 1);
1078
+ return {
1079
+ start: Math.min(offset, nextOffset),
1080
+ end: Math.max(offset, nextOffset),
1081
+ text: ""
1082
+ };
1083
+ })() : {
1084
+ start: textDocument.offsetAt(primarySelection.start),
1085
+ end: textDocument.offsetAt(primarySelection.end),
1086
+ text: ""
1087
+ };
1088
+ this.#applyResolvedTextEdit(edit);
1089
+ }
1090
+ #deleteHardLineForward() {
1091
+ const selections = this.#selections;
1092
+ const textDocument = this.#textDocument;
1093
+ if (selections === void 0 || textDocument === void 0) return;
1094
+ const { nextSelections, change } = applyDeleteHardLineForwardToSelections(textDocument, selections, this.#lineAnnotations);
1095
+ if (change !== void 0) this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change));
1096
+ }
1097
+ #insertTranspose() {
1098
+ const selections = this.#selections;
1099
+ const textDocument = this.#textDocument;
1100
+ if (selections === void 0 || textDocument === void 0) return;
1101
+ const { nextSelections, change } = applyTransposeToSelections(textDocument, selections, this.#lineAnnotations);
1102
+ if (change !== void 0) this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change));
1103
+ }
1104
+ #applyResolvedTextEdit(edit) {
1105
+ if (this.#selections === void 0 || this.#textDocument === void 0) return;
1106
+ const { nextSelections, change } = applyTextChangeToSelections(this.#textDocument, this.#selections, edit, this.#lineAnnotations, this.#metrics.tabSize);
1107
+ if (change !== void 0) this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change));
1108
+ }
1109
+ #applyChange(change, selections, newLineAnnotations) {
1110
+ const fileContents = this.#fileContents;
1111
+ const textDocument = this.#textDocument;
1112
+ const onChange = this.#options.onChange;
1113
+ if (fileContents !== void 0 && textDocument !== void 0 && onChange !== void 0) {
1114
+ const { contents: _,...file } = fileContents;
1115
+ let contents;
1116
+ Object.defineProperty(file, "contents", { get: () => contents ??= textDocument.getText() });
1117
+ this.#emitChange(file, newLineAnnotations ?? this.#lineAnnotations);
1118
+ }
1119
+ if (change.lineDelta !== 0) {
1120
+ for (const line of this.#lineYCache.keys()) if (line >= change.startLine) this.#lineYCache.delete(line);
1121
+ }
1122
+ if (this.#wrap) {
1123
+ for (const line of this.#wrapLineOffsetsCache.keys()) if (line >= change.startLine) this.#wrapLineOffsetsCache.delete(line);
1124
+ }
1125
+ this.#lastCharX = void 0;
1126
+ let renderRange = this.#renderRange;
1127
+ let shouldUpdateBuffer;
1128
+ if (renderRange !== void 0 && selections !== void 0 && selections.length > 0) {
1129
+ const primarySelection = selections.at(-1);
1130
+ const renderRangeEndLine = renderRange.startingLine + renderRange.totalLines;
1131
+ if (primarySelection.end.line === renderRangeEndLine) renderRange = {
1132
+ ...renderRange,
1133
+ totalLines: renderRange.totalLines + 1
1134
+ };
1135
+ else if (primarySelection.end.line > renderRangeEndLine) shouldUpdateBuffer = true;
1136
+ }
1137
+ this.#rerender(change, newLineAnnotations, renderRange, shouldUpdateBuffer);
1138
+ if (selections !== void 0) {
1139
+ this.#updateSelections(selections);
1140
+ this.focus({ preventScroll: true });
1141
+ requestAnimationFrame(() => {
1142
+ if (this.#primaryCaretElement !== void 0) this.#primaryCaretElement.scrollIntoView({
1143
+ block: "nearest",
1144
+ inline: "nearest"
1145
+ });
1146
+ else if (selections.length > 0) {
1147
+ const pos = getCaretPosition(selections.at(-1));
1148
+ this.#scrollToLine(pos.line, pos.character);
1149
+ }
1150
+ });
1151
+ }
1152
+ }
1153
+ #applyChangeToLineAnnotations(change) {
1154
+ if (this.#lineAnnotations !== void 0) {
1155
+ const nextLineAnnotations = applyDocumentChangeToLineAnnotations(change, this.#lineAnnotations);
1156
+ if (nextLineAnnotations !== void 0) {
1157
+ this.#textDocument?.setLastUndoLineAnnotations(this.#lineAnnotations, nextLineAnnotations);
1158
+ return nextLineAnnotations;
1159
+ }
1160
+ }
1161
+ }
1162
+ #getLineElement(line) {
1163
+ const contentElement = this.#contentElement;
1164
+ if (contentElement === void 0) return;
1165
+ if (this.#renderRange !== void 0 && this.#editMode === "simple") {
1166
+ const { startingLine } = this.#renderRange;
1167
+ const { children } = contentElement;
1168
+ for (let i = line - startingLine; i <= children.length; i++) {
1169
+ const child = children[i];
1170
+ const lineNumber = child?.dataset.line;
1171
+ const lineType = child?.dataset.lineType;
1172
+ if (lineNumber !== void 0 && lineType !== void 0 && isLineEditable(lineType) && Number(lineNumber) === line + 1) return child;
1173
+ }
1174
+ }
1175
+ if (this.#editMode === "advanced") return contentElement.querySelector(`[data-line="${line + 1}"]:not([data-line-type="change-deletion"])`) ?? void 0;
1176
+ return contentElement.querySelector(`[data-line="${line + 1}"]`) ?? void 0;
1177
+ }
1178
+ #getGutterWidth() {
1179
+ const gutterElement = this.#contentElement?.previousElementSibling;
1180
+ if (gutterElement == null || !(gutterElement instanceof HTMLElement) || !gutterElement.hasAttribute("data-gutter")) return 0;
1181
+ if (this.#gutterWidthCache === void 0) {
1182
+ const diffsColumnNumberWidth = this.#contentElement?.parentElement?.style.getPropertyValue("--diffs-column-number-width");
1183
+ if (diffsColumnNumberWidth !== void 0 && diffsColumnNumberWidth.length > 2 && diffsColumnNumberWidth.endsWith("px")) this.#gutterWidthCache = Number(diffsColumnNumberWidth.slice(0, -2));
1184
+ else this.#gutterWidthCache = gutterElement.offsetWidth;
1185
+ }
1186
+ return this.#gutterWidthCache;
1187
+ }
1188
+ #getContentWidth() {
1189
+ if (this.#contentElement === void 0) return 0;
1190
+ if (this.#contentWidthCache === void 0) {
1191
+ const diffsColumnContentWidth = this.#contentElement.parentElement?.style.getPropertyValue("--diffs-column-content-width");
1192
+ if (diffsColumnContentWidth !== void 0 && diffsColumnContentWidth.length > 2 && diffsColumnContentWidth.endsWith("px")) this.#contentWidthCache = Number(diffsColumnContentWidth.slice(0, -2));
1193
+ else this.#contentWidthCache = this.#contentElement.offsetWidth;
1194
+ }
1195
+ return this.#contentWidthCache;
1196
+ }
1197
+ #getLineY(line) {
1198
+ const cachedY = this.#lineYCache.get(line);
1199
+ if (cachedY !== void 0) return cachedY;
1200
+ const lineElement = this.#getLineElement(line);
1201
+ if (lineElement === void 0) return -1;
1202
+ const y = lineElement.offsetTop + this.#codePaddingTop;
1203
+ this.#lineYCache.set(line, y);
1204
+ return y;
1205
+ }
1206
+ #getCharX(line, char) {
1207
+ if (this.#lastCharX !== void 0 && this.#lastCharX[0] === line && this.#lastCharX[1] === char) return [this.#lastCharX[2], this.#lastCharX[3]];
1208
+ const lineText = this.#textDocument?.getLineText(line);
1209
+ const offsetLeft = this.#getGutterWidth() + this.#metrics.ch;
1210
+ if (lineText === void 0 || lineText.length === 0 || char <= 0) return [offsetLeft, 0];
1211
+ const boundedCharacter = snapTextOffsetToUnicodeBoundary(lineText, Math.min(char, lineText.length));
1212
+ const textBeforeCharacter = lineText.slice(0, boundedCharacter);
1213
+ const asciiColumns = getExpandedAsciiTextColumns(textBeforeCharacter, this.#metrics.tabSize);
1214
+ let left = 0;
1215
+ let wrapLine = 0;
1216
+ if (asciiColumns !== -1) left = offsetLeft + asciiColumns * this.#metrics.ch;
1217
+ else left = offsetLeft + this.#metrics.measureTextWidth(textBeforeCharacter);
1218
+ if (this.#wrap) {
1219
+ const contentWidth = this.#getContentWidth();
1220
+ if (2 * this.#metrics.ch + this.#metrics.measureTextWidth(lineText) > contentWidth) {
1221
+ const wrapOffsets = this.#wrapLineText(line);
1222
+ for (let w = 0; w + 1 < wrapOffsets.length; w++) {
1223
+ const segmentStart = wrapOffsets[w];
1224
+ if (boundedCharacter <= wrapOffsets[w + 1]) {
1225
+ wrapLine = w;
1226
+ const prefixInSegment = lineText.slice(segmentStart, boundedCharacter);
1227
+ const segmentAsciiColumns = getExpandedAsciiTextColumns(prefixInSegment, this.#metrics.tabSize);
1228
+ if (segmentAsciiColumns !== -1) left = offsetLeft + segmentAsciiColumns * this.#metrics.ch;
1229
+ else left = offsetLeft + this.#metrics.measureTextWidth(prefixInSegment);
1230
+ break;
1231
+ }
1232
+ }
1233
+ }
1234
+ }
1235
+ if (this.#lastCharX !== void 0) {
1236
+ this.#lastCharX[0] = line;
1237
+ this.#lastCharX[1] = char;
1238
+ this.#lastCharX[2] = left;
1239
+ this.#lastCharX[3] = wrapLine;
1240
+ } else this.#lastCharX = [
1241
+ line,
1242
+ char,
1243
+ left,
1244
+ wrapLine
1245
+ ];
1246
+ return [left, wrapLine];
1247
+ }
1248
+ #wrapLineText(line) {
1249
+ const cachedOffsets = this.#wrapLineOffsetsCache.get(line);
1250
+ if (cachedOffsets !== void 0) return cachedOffsets;
1251
+ const lineText = this.#textDocument?.getLineText(line);
1252
+ if (lineText === void 0 || lineText.length === 0) {
1253
+ const offsets = new Uint32Array([0]);
1254
+ this.#wrapLineOffsetsCache.set(line, offsets);
1255
+ return offsets;
1256
+ }
1257
+ const div = h("div", {
1258
+ style: {
1259
+ position: "absolute",
1260
+ top: "0",
1261
+ left: "0",
1262
+ width: "100%",
1263
+ boxSizing: "border-box",
1264
+ visibility: "hidden",
1265
+ pointerEvents: "none",
1266
+ whiteSpace: "pre-wrap",
1267
+ wordBreak: "break-word",
1268
+ font: "inherit",
1269
+ paddingInline: "1ch",
1270
+ tabSize: this.#metrics.tabSize.toString()
1271
+ },
1272
+ textContent: lineText
1273
+ }, this.#contentElement);
1274
+ const textNode = div.firstChild;
1275
+ const range = document.createRange();
1276
+ const starts = [];
1277
+ try {
1278
+ const unicodeOffsets = getUnicodeMeasurementOffsets(lineText);
1279
+ const wrapLineStartLeft = div.getBoundingClientRect().left + this.#metrics.ch;
1280
+ let previousOffset = 0;
1281
+ let lastTop = Number.NEGATIVE_INFINITY;
1282
+ for (let i = 0, offsetIndex = 0; i < lineText.length;) {
1283
+ const nextOffset = unicodeOffsets === void 0 ? i + 1 : unicodeOffsets[offsetIndex + 1];
1284
+ range.setStart(textNode, i);
1285
+ range.setEnd(textNode, nextOffset);
1286
+ const { left, top } = range.getBoundingClientRect();
1287
+ if (top > lastTop) {
1288
+ const startsPastLineStart = isSafari() && starts.length > 0 && left - wrapLineStartLeft > this.#metrics.ch / 2;
1289
+ starts.push(startsPastLineStart ? previousOffset : i);
1290
+ lastTop = top;
1291
+ }
1292
+ previousOffset = i;
1293
+ i = nextOffset;
1294
+ offsetIndex++;
1295
+ }
1296
+ const offsets = new Uint32Array(starts.length + 1);
1297
+ for (let i = 0; i < starts.length; i++) offsets[i] = starts[i];
1298
+ offsets[starts.length] = lineText.length;
1299
+ this.#wrapLineOffsetsCache.set(line, offsets);
1300
+ return offsets;
1301
+ } finally {
1302
+ div.remove();
1303
+ }
1304
+ }
1305
+ #rangeBelongsToEditor({ startContainer, endContainer }) {
1306
+ const contentEl = this.#contentElement;
1307
+ if (contentEl === void 0) return false;
1308
+ return contentEl.contains(startContainer) && contentEl.contains(endContainer);
1309
+ }
1310
+ #isLineVisible(line) {
1311
+ const lineCount = this.#textDocument?.lineCount ?? 0;
1312
+ if (line < 0 || line >= lineCount) return false;
1313
+ if (this.#renderRange === void 0) return true;
1314
+ const { startingLine, totalLines } = this.#renderRange;
1315
+ if (line < startingLine) return false;
1316
+ if (totalLines === Infinity) return true;
1317
+ return line < startingLine + totalLines;
1318
+ }
1319
+ };
1320
+
1321
+ //#endregion
1322
+ export { Editor };
1323
+ //# sourceMappingURL=editor.js.map