@pierre/diffs 1.3.0-beta.2 → 1.3.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/components/CodeView.d.ts +4 -0
  2. package/dist/components/CodeView.d.ts.map +1 -1
  3. package/dist/components/CodeView.js +38 -0
  4. package/dist/components/CodeView.js.map +1 -1
  5. package/dist/components/File.d.ts.map +1 -1
  6. package/dist/components/File.js +9 -9
  7. package/dist/components/File.js.map +1 -1
  8. package/dist/components/FileDiff.d.ts.map +1 -1
  9. package/dist/components/FileDiff.js +3 -2
  10. package/dist/components/FileDiff.js.map +1 -1
  11. package/dist/components/VirtualizedFile.js +6 -1
  12. package/dist/components/VirtualizedFile.js.map +1 -1
  13. package/dist/components/VirtualizedFileDiff.js +22 -42
  14. package/dist/components/VirtualizedFileDiff.js.map +1 -1
  15. package/dist/components/Virtualizer.js +5 -3
  16. package/dist/components/Virtualizer.js.map +1 -1
  17. package/dist/editor/editor.d.ts +7 -1
  18. package/dist/editor/editor.d.ts.map +1 -1
  19. package/dist/editor/editor.js +550 -405
  20. package/dist/editor/editor.js.map +1 -1
  21. package/dist/editor/editor2.js +6 -0
  22. package/dist/editor/editor2.js.map +1 -0
  23. package/dist/editor/pieceTable.d.ts +1 -1
  24. package/dist/editor/pieceTable.d.ts.map +1 -1
  25. package/dist/editor/pieceTable.js +2 -22
  26. package/dist/editor/pieceTable.js.map +1 -1
  27. package/dist/editor/quickEdit.js +2 -4
  28. package/dist/editor/quickEdit.js.map +1 -1
  29. package/dist/editor/searchPanel.d.ts +6 -7
  30. package/dist/editor/searchPanel.d.ts.map +1 -1
  31. package/dist/editor/searchPanel.js +102 -137
  32. package/dist/editor/searchPanel.js.map +1 -1
  33. package/dist/editor/selection.js +8 -2
  34. package/dist/editor/selection.js.map +1 -1
  35. package/dist/editor/sprite.d.ts +7 -0
  36. package/dist/editor/sprite.d.ts.map +1 -0
  37. package/dist/editor/sprite.js +38 -0
  38. package/dist/editor/sprite.js.map +1 -0
  39. package/dist/editor/textDocument.d.ts +1 -1
  40. package/dist/editor/textDocument.d.ts.map +1 -1
  41. package/dist/editor/textDocument.js +2 -2
  42. package/dist/editor/textDocument.js.map +1 -1
  43. package/dist/editor/textMeasure.js +3 -3
  44. package/dist/editor/textMeasure.js.map +1 -1
  45. package/dist/editor/tokenzier.d.ts +6 -2
  46. package/dist/editor/tokenzier.d.ts.map +1 -1
  47. package/dist/editor/tokenzier.js +127 -85
  48. package/dist/editor/tokenzier.js.map +1 -1
  49. package/dist/index.d.ts +2 -2
  50. package/dist/react/index.d.ts +2 -2
  51. package/dist/renderers/DiffHunksRenderer.js +5 -9
  52. package/dist/renderers/DiffHunksRenderer.js.map +1 -1
  53. package/dist/ssr/index.d.ts +2 -2
  54. package/dist/style.js +1 -1
  55. package/dist/style.js.map +1 -1
  56. package/dist/types.d.ts +13 -12
  57. package/dist/types.d.ts.map +1 -1
  58. package/dist/utils/computeEstimatedDiffHeights.js +9 -20
  59. package/dist/utils/computeEstimatedDiffHeights.js.map +1 -1
  60. package/dist/utils/iterateOverDiff.js +147 -182
  61. package/dist/utils/iterateOverDiff.js.map +1 -1
  62. package/dist/utils/virtualDiffLayout.d.ts +23 -2
  63. package/dist/utils/virtualDiffLayout.d.ts.map +1 -1
  64. package/dist/utils/virtualDiffLayout.js +41 -1
  65. package/dist/utils/virtualDiffLayout.js.map +1 -1
  66. package/dist/worker/WorkerPoolManager.js +1 -1
  67. package/dist/worker/WorkerPoolManager.js.map +1 -1
  68. package/dist/worker/{wasm-D4DU5jgR.js → wasm-BaDzIkIn.js} +2 -2
  69. package/dist/worker/wasm-BaDzIkIn.js.map +1 -0
  70. package/dist/worker/worker-portable.js +294 -292
  71. package/dist/worker/worker-portable.js.map +1 -1
  72. package/dist/worker/worker.js +179 -181
  73. package/dist/worker/worker.js.map +1 -1
  74. package/package.json +3 -2
  75. package/dist/editor/css.d.ts +0 -6
  76. package/dist/editor/css.d.ts.map +0 -1
  77. package/dist/editor/css.js +0 -218
  78. package/dist/editor/css.js.map +0 -1
  79. package/dist/worker/wasm-D4DU5jgR.js.map +0 -1
@@ -1,13 +1,14 @@
1
1
  import { getFiletypeFromFileName } from "../utils/getFiletypeFromFileName.js";
2
2
  import { isMoveCursorShortcut, isPrimaryModifier, isSafari } from "./platform.js";
3
3
  import { resolveEditorCommandFromKeyboardEvent } from "./command.js";
4
- import { editorCSS, editorGlobalCSS } from "./css.js";
4
+ import editor_default from "./editor2.js";
5
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";
6
+ import { SVGSpriteSheet } from "./sprite.js";
8
7
  import { addEventListener, debounce, extend, h, round } from "./utils.js";
9
8
  import { QuickEditWidget } from "./quickEdit.js";
10
9
  import { SearchPanelWidget } from "./searchPanel.js";
10
+ 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";
11
+ import { TextDocument } from "./textDocument.js";
11
12
  import { Metrics, getExpandedAsciiTextColumns, getUnicodeMeasurementOffsets, snapTextOffsetToUnicodeBoundary } from "./textMeasure.js";
12
13
  import { EditorTokenizer, renderLineTokens } from "./tokenzier.js";
13
14
 
@@ -22,7 +23,6 @@ function clampDomOffset(node, offset) {
22
23
  }
23
24
  var Editor = class {
24
25
  #options;
25
- #editMode = "simple";
26
26
  #wrap = false;
27
27
  #metrics = new Metrics();
28
28
  #tokenizer;
@@ -30,7 +30,8 @@ var Editor = class {
30
30
  #globalEventDisposes;
31
31
  #mouseUpDisposes;
32
32
  #detach;
33
- #component;
33
+ #fileInstance;
34
+ #fileInstanceType;
34
35
  #fileContents;
35
36
  #lineAnnotations;
36
37
  #textDocument;
@@ -44,7 +45,8 @@ var Editor = class {
44
45
  #globalStyleElement;
45
46
  #editorStyleElement;
46
47
  #themeStyleElement;
47
- #componentContainer;
48
+ #spriteElement;
49
+ #fileContainer;
48
50
  #contentElement;
49
51
  #overlayElement;
50
52
  #primaryCaretElement;
@@ -60,14 +62,16 @@ var Editor = class {
60
62
  #reservedSelections;
61
63
  #selections;
62
64
  #initSelections;
65
+ #matches;
63
66
  #scrollingToLine;
64
67
  #scrollingToLineChar;
68
+ #scrollingToLineNoFocus = false;
65
69
  #retainSearchPanelFocus = false;
66
70
  #emitChange = debounce((fileContents, lineAnnotations) => {
67
71
  this.#options.onChange?.(fileContents, lineAnnotations);
68
72
  }, 500);
69
73
  #onDeferTokenize = (lines, themeType) => {
70
- this.#component?.applyLineChange?.(lines, themeType);
74
+ this.#fileInstance?.applyLineChange?.(lines, themeType);
71
75
  if (this.#renderRange !== void 0 && this.#renderRange.totalLines !== Infinity) {
72
76
  const { startingLine, totalLines } = this.#renderRange;
73
77
  const endLine = Math.min(startingLine + totalLines, this.#textDocument?.lineCount ?? 0);
@@ -93,83 +97,12 @@ var Editor = class {
93
97
  });
94
98
  component.rerender();
95
99
  }
96
- this.#component = component;
100
+ this.#fileInstance = component;
97
101
  this.#initialize();
98
102
  this.#detach = component.attachEditor(this);
99
103
  return () => this.cleanUp();
100
104
  }
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) {
105
+ syncToRenderedView(highlighter, fileInstanceType, fileContainer, fileContents, lineAnnotations, renderRange) {
173
106
  const shadowRoot = fileContainer.shadowRoot;
174
107
  if (shadowRoot == null) {
175
108
  console.error("[editor] Could not find the shadow root.");
@@ -182,10 +115,10 @@ var Editor = class {
182
115
  }
183
116
  if (codeElement === void 0) return;
184
117
  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) {
118
+ if (contentEl === void 0 || contentEl.dataset.content === void 0) return;
119
+ this.#fileInstanceType = fileInstanceType;
120
+ this.#wrap = this.#fileInstance?.options.overflow === "wrap";
121
+ if (fileInstanceType === "diff" || lineAnnotations !== void 0 && lineAnnotations.length > 0) {
189
122
  let startingLine;
190
123
  let endLine;
191
124
  for (const child of contentEl.children) {
@@ -193,9 +126,12 @@ var Editor = class {
193
126
  const line = el.dataset.line;
194
127
  const lineType = el.dataset.lineType;
195
128
  if (line !== void 0) {
196
- const lineIndex = Number(line) - 1;
197
- startingLine ??= lineIndex;
198
- endLine = lineIndex;
129
+ const lineNumber = parseInt(line, 10);
130
+ if (!Number.isNaN(lineNumber)) {
131
+ const lineIndex = lineNumber - 1;
132
+ startingLine ??= lineIndex;
133
+ endLine = lineIndex;
134
+ }
199
135
  }
200
136
  if (lineType === void 0 || !isLineEditable(lineType)) el.contentEditable = "false";
201
137
  }
@@ -210,14 +146,15 @@ var Editor = class {
210
146
  bufferAfter: 0
211
147
  };
212
148
  }
213
- if (this.#componentContainer !== fileContainer) {
214
- this.#componentContainer = fileContainer;
215
- this.#codePaddingTop = Number(getComputedStyle(codeElement).paddingTop.slice(0, -2));
149
+ if (this.#fileContainer !== fileContainer) {
150
+ this.#fileContainer = fileContainer;
151
+ const codePaddingTop = parseInt(getComputedStyle(codeElement).paddingTop.slice(0, -2), 10);
152
+ this.#codePaddingTop = Number.isNaN(codePaddingTop) ? 0 : codePaddingTop;
216
153
  if (this.#globalStyleElement !== void 0) fileContainer.appendChild(this.#globalStyleElement);
217
154
  if (this.#editorStyleElement !== void 0) shadowRoot.appendChild(this.#editorStyleElement);
218
155
  if (this.#themeStyleElement !== void 0) shadowRoot.appendChild(this.#themeStyleElement);
219
156
  }
220
- if (this.#textDocument === void 0 || this.#fileContents === void 0 || this.#fileContents.name !== fileContents.name) {
157
+ if (this.#textDocument === void 0 || this.#fileContents === void 0 || this.#fileContents.name !== fileContents.name || this.#fileContents.lang !== fileContents.lang) {
221
158
  const textDocument = new TextDocument(fileContents.name, fileContents.contents, fileContents.lang ?? getFiletypeFromFileName(fileContents.name));
222
159
  this.#fileContents = fileContents;
223
160
  this.#textDocument = textDocument;
@@ -225,16 +162,17 @@ var Editor = class {
225
162
  this.#tokenizer = new EditorTokenizer({
226
163
  highlighter,
227
164
  textDocument,
228
- codeOptions: this.#component?.options ?? {},
165
+ codeOptions: this.#fileInstance?.options ?? {},
229
166
  onDeferTokenize: this.#onDeferTokenize,
230
167
  setStyle: (css) => {
231
168
  this.#themeStyleElement.textContent = css;
232
- }
169
+ },
170
+ __debug: this.#options.__debug
233
171
  });
234
172
  this.#shouldIgnoreSelectionChange = false;
235
173
  this.#selectionElements?.forEach((el) => el.remove());
236
174
  this.#selectionElements?.clear();
237
- this.#component?.setSelectedLines(null);
175
+ this.#fileInstance?.setSelectedLines(null);
238
176
  this.#selectionElements = void 0;
239
177
  this.#selections = void 0;
240
178
  this.#scrollingToLine = void 0;
@@ -245,11 +183,6 @@ var Editor = class {
245
183
  this.#quickEdit = void 0;
246
184
  }
247
185
  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
186
  this.#metrics.init(contentEl);
254
187
  this.#contentElement = extend(contentEl, {
255
188
  contentEditable: "true",
@@ -262,167 +195,14 @@ var Editor = class {
262
195
  translate: false
263
196
  });
264
197
  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);
198
+ this.#listenContentElement(contentEl);
419
199
  }
420
200
  this.#lineYCache.clear();
421
201
  this.#wrapLineOffsetsCache.clear();
422
202
  this.#lastCharX = void 0;
423
203
  this.#lineAnnotations = lineAnnotations;
424
204
  this.#renderRange = renderRange;
425
- this.#tokenizer?.prebuildStateStackMap(renderRange);
205
+ this.#tokenizer?.prebuildStateStack(renderRange);
426
206
  if (this.#initSelections !== void 0) {
427
207
  this.setSelections(this.#initSelections);
428
208
  this.#scrollToPrimaryCaret();
@@ -433,33 +213,121 @@ var Editor = class {
433
213
  console.log("[diffs/editor] render file:", fileContents.name, "RenderRange:", startingLine + "-" + (startingLine + totalLines), "of", this.#textDocument.lineCount, "lines");
434
214
  }
435
215
  if (this.#scrollingToLine !== void 0) {
436
- this.#scrollToLine(this.#scrollingToLine, this.#scrollingToLineChar);
216
+ this.#scrollToLine(this.#scrollingToLine, this.#scrollingToLineChar, this.#scrollingToLineNoFocus);
437
217
  this.#scrollingToLine = void 0;
438
218
  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;
219
+ this.#scrollingToLineNoFocus = false;
220
+ } else if (this.#selections !== void 0 && this.#selections.length > 0 && !this.#retainSearchPanelFocus) this.focus({ preventScroll: true });
221
+ if (this.#retainSearchPanelFocus) this.#searchPanel?.focus();
222
+ if (this.#quickEdit !== void 0 && this.#isLineVisible(this.#quickEdit.line) && this.#contentElement !== void 0) this.#quickEdit.render(this.#contentElement);
223
+ }
224
+ postponeBackgroundTokenizeToNextFrame() {
225
+ const tokenizer = this.#tokenizer;
226
+ if (tokenizer !== void 0) {
227
+ tokenizer.pauseBackgroundTokenize();
442
228
  requestAnimationFrame(() => {
443
- this.#searchPanel?.focus();
229
+ tokenizer.resumeBackgroundTokenize();
444
230
  });
445
231
  }
446
- if (this.#quickEdit !== void 0 && this.#isLineVisible(this.#quickEdit.line) && this.#contentElement !== void 0) this.#quickEdit.render(this.#contentElement);
232
+ }
233
+ setSelections(selections) {
234
+ const textDocument = this.#textDocument;
235
+ if (textDocument !== void 0) {
236
+ const resolvedSelections = selections.map((selection) => {
237
+ const start = textDocument.normalizePosition(selection.start);
238
+ const end = textDocument.normalizePosition(selection.end);
239
+ return {
240
+ direction: selection.direction === "none" ? DirectionNone : selection.direction === "backward" ? DirectionBackward : DirectionForward,
241
+ start,
242
+ end
243
+ };
244
+ });
245
+ this.#updateSelections(resolvedSelections);
246
+ this.#scrollToPrimaryCaret();
247
+ } else this.#initSelections = selections;
248
+ }
249
+ focus(options) {
250
+ const preventScroll = options?.preventScroll ?? false;
251
+ const primarySelection = this.#selections?.at(-1);
252
+ if (primarySelection !== void 0) {
253
+ const pos = primarySelection.direction === DirectionBackward ? primarySelection.end : primarySelection.start;
254
+ this.#focus(pos, preventScroll);
255
+ } else this.#focus(void 0, preventScroll);
256
+ }
257
+ cleanUp() {
258
+ this.#tokenizer?.cleanUp();
259
+ this.#tokenizer = void 0;
260
+ this.#globalEventDisposes?.forEach((dispose) => dispose());
261
+ this.#globalEventDisposes = void 0;
262
+ this.#editorEventDisposes?.forEach((dispose) => dispose());
263
+ this.#editorEventDisposes = void 0;
264
+ this.#detach?.();
265
+ this.#detach = void 0;
266
+ this.#fileInstance?.setSelectedLines(null);
267
+ this.#fileInstance = void 0;
268
+ this.#fileContents = void 0;
269
+ this.#lineAnnotations = void 0;
270
+ this.#textDocument = void 0;
271
+ this.#renderRange = void 0;
272
+ this.#gutterWidthCache = void 0;
273
+ this.#contentWidthCache = void 0;
274
+ this.#lineYCache.clear();
275
+ this.#wrapLineOffsetsCache.clear();
276
+ this.#lastCharX = void 0;
277
+ this.#globalStyleElement?.remove();
278
+ this.#globalStyleElement = void 0;
279
+ this.#editorStyleElement?.remove();
280
+ this.#editorStyleElement = void 0;
281
+ this.#themeStyleElement?.remove();
282
+ this.#themeStyleElement = void 0;
283
+ this.#spriteElement?.remove();
284
+ this.#spriteElement = void 0;
285
+ this.#fileContainer = void 0;
286
+ this.#contentElement?.removeAttribute("contentEditable");
287
+ this.#contentElement = void 0;
288
+ this.#overlayElement?.remove();
289
+ this.#overlayElement = void 0;
290
+ this.#primaryCaretElement?.remove();
291
+ this.#primaryCaretElement = void 0;
292
+ this.#selectionElements?.forEach((el) => el.remove());
293
+ this.#selectionElements?.clear();
294
+ this.#selectionElements = void 0;
295
+ this.#searchPanel?.cleanup();
296
+ this.#searchPanel = void 0;
297
+ this.#quickEdit?.cleanup();
298
+ this.#quickEdit = void 0;
299
+ this.#resizeObserver?.disconnect();
300
+ this.#resizeObserver = void 0;
301
+ this.#shouldIgnoreSelectionChange = false;
302
+ this.#selectionStart = void 0;
303
+ this.#selections = void 0;
304
+ this.#reservedSelections = void 0;
447
305
  }
448
306
  #initialize() {
307
+ this.#globalStyleElement = h("style", {
308
+ dataset: "editorGlobalCss",
309
+ textContent: `
310
+ [data-annotation-slot] {
311
+ user-select: none;
312
+ -webkit-user-select: none;
313
+ }
314
+ `
315
+ });
449
316
  this.#editorStyleElement = h("style", {
450
317
  dataset: "editorCss",
451
- textContent: editorCSS
318
+ textContent: editor_default
452
319
  });
453
320
  this.#themeStyleElement = h("style", { dataset: "editorThemeCss" });
454
- this.#globalStyleElement = h("style", {
455
- dataset: "editorGlobalCss",
456
- textContent: editorGlobalCSS
457
- });
321
+ const fragment = document.createElement("div");
322
+ fragment.innerHTML = SVGSpriteSheet;
323
+ const sprite = fragment.firstElementChild;
324
+ this.#spriteElement = sprite instanceof SVGSVGElement ? sprite : void 0;
458
325
  this.#overlayElement = h("div", { dataset: "editorOverlay" });
459
326
  this.#globalEventDisposes = [
460
327
  addEventListener(document, "selectionchange", () => {
461
- const shadowRoot = this.#componentContainer?.shadowRoot;
328
+ const shadowRoot = this.#fileContainer?.shadowRoot;
462
329
  if (this.#shouldIgnoreSelectionChange || shadowRoot == null) return;
330
+ if (this.#selections !== void 0 && this.#selections.length > 1 && !this.#isContentMouseDown) return;
463
331
  const composedRange = document.getSelection()?.getComposedRanges({ shadowRoots: [shadowRoot] })?.[0];
464
332
  if (composedRange === void 0 || !this.#rangeBelongsToEditor(composedRange)) return;
465
333
  let selection = convertSelection(composedRange, DirectionNone);
@@ -500,6 +368,169 @@ var Editor = class {
500
368
  }, { passive: true })
501
369
  ];
502
370
  }
371
+ #listenContentElement(contentEl) {
372
+ const gutterEl = contentEl.previousElementSibling;
373
+ const targetIsContentElement = (e) => {
374
+ const target = e.composedPath()[0];
375
+ return target === contentEl || contentEl.contains(target);
376
+ };
377
+ this.#editorEventDisposes?.forEach((dispose) => dispose());
378
+ this.#editorEventDisposes = [
379
+ addEventListener(contentEl, "pointerdown", (e) => {
380
+ if (e.pointerType !== "mouse") return;
381
+ if (isSafari() && this.#lineAnnotations !== void 0 && this.#lineAnnotations.length > 0) this.#mouseUpDisposes = [...contentEl.querySelectorAll("[data-line-annotation]")].map((el) => [addEventListener(el, "mouseenter", () => {
382
+ this.#shouldIgnoreSelectionChange = true;
383
+ }), addEventListener(el, "mouseleave", () => {
384
+ this.#shouldIgnoreSelectionChange = false;
385
+ })]).flat();
386
+ this.#isContentMouseDown = true;
387
+ this.#selectionStart = void 0;
388
+ if (e.button === 0 && isPrimaryModifier(e)) this.#reservedSelections = this.#selections?.map((selection) => ({ ...selection }));
389
+ if (e.shiftKey) {
390
+ const primarySelection = this.#selections?.at(-1);
391
+ if (primarySelection !== void 0) {
392
+ const pos = primarySelection.direction === DirectionBackward ? primarySelection.end : primarySelection.start;
393
+ this.#setWindowSelection({
394
+ start: pos,
395
+ end: pos,
396
+ direction: DirectionNone
397
+ });
398
+ }
399
+ this.#shiftKeyPressed = true;
400
+ }
401
+ }, { passive: true }),
402
+ addEventListener(contentEl, "keydown", (e) => {
403
+ if (e.key === "Escape") {
404
+ e.preventDefault();
405
+ this.#searchPanel?.cleanup();
406
+ this.#searchPanel = void 0;
407
+ this.#retainSearchPanelFocus = false;
408
+ this.#quickEdit?.cleanup();
409
+ this.#quickEdit = void 0;
410
+ if (this.#selections !== void 0 && this.#selections.length > 0) {
411
+ const primarySelection = this.#selections.at(-1);
412
+ if (!isCollapsedSelection(primarySelection) || this.#selections.length > 1) {
413
+ const pos = getCaretPosition(primarySelection);
414
+ this.#updateSelections([{
415
+ start: pos,
416
+ end: pos,
417
+ direction: DirectionNone
418
+ }]);
419
+ this.#focus(pos);
420
+ }
421
+ }
422
+ return;
423
+ }
424
+ if (!targetIsContentElement(e)) return;
425
+ const mvShortcut = isMoveCursorShortcut(e);
426
+ const textDocument = this.#textDocument;
427
+ if (this.#selections !== void 0 && this.#selections.length > 0 && mvShortcut !== void 0 && textDocument !== void 0) {
428
+ if (e.shiftKey) this.#updateSelections(mapSelectionShift(textDocument, this.#selections, mvShortcut));
429
+ else this.#updateSelections(mapCursorMove(textDocument, this.#selections, mvShortcut));
430
+ this.#scrollToPrimaryCaret();
431
+ e.preventDefault();
432
+ return;
433
+ }
434
+ const command = resolveEditorCommandFromKeyboardEvent(e);
435
+ if (command !== void 0) {
436
+ e.preventDefault();
437
+ this.#runCommand(command);
438
+ }
439
+ }),
440
+ addEventListener(contentEl, "copy", (e) => {
441
+ if (!targetIsContentElement(e)) return;
442
+ e.preventDefault();
443
+ e.clipboardData?.setData("text", this.#getSelectionText());
444
+ }),
445
+ addEventListener(contentEl, "cut", (e) => {
446
+ if (!targetIsContentElement(e)) return;
447
+ e.preventDefault();
448
+ e.clipboardData?.setData("text", this.#getSelectionText());
449
+ this.#replaceSelectionText("");
450
+ }),
451
+ addEventListener(contentEl, "paste", (e) => {
452
+ if (!targetIsContentElement(e)) return;
453
+ e.preventDefault();
454
+ const text = e.clipboardData?.getData("text");
455
+ if (text !== void 0) this.#replaceSelectionText(text);
456
+ }),
457
+ addEventListener(contentEl, "beforeinput", (e) => {
458
+ if (!targetIsContentElement(e)) return;
459
+ e.preventDefault();
460
+ this.#handleInput(e.inputType, e.data);
461
+ }),
462
+ addEventListener(contentEl, "compositionstart", (e) => {
463
+ if (!targetIsContentElement(e)) return;
464
+ this.#shouldIgnoreSelectionChange = true;
465
+ }, { passive: true }),
466
+ addEventListener(contentEl, "compositionend", (e) => {
467
+ if (!targetIsContentElement(e)) return;
468
+ this.#shouldIgnoreSelectionChange = false;
469
+ this.#handleInput("insertText", e.data);
470
+ }, { passive: true })
471
+ ];
472
+ if (gutterEl !== null && gutterEl.dataset.gutter !== void 0) this.#editorEventDisposes.push(addEventListener(gutterEl, "pointerdown", (e) => {
473
+ let target = e.composedPath()[0];
474
+ if (target?.dataset.lineNumberContent !== void 0) target = target.parentElement ?? void 0;
475
+ const textDocument = this.#textDocument;
476
+ if (target === void 0 || textDocument === void 0) return;
477
+ const columnNumber = target.dataset.columnNumber;
478
+ const lineType = target.dataset.lineType;
479
+ if (columnNumber === void 0 || lineType === void 0 || !isLineEditable(lineType)) return;
480
+ const lineNumber = parseInt(columnNumber, 10);
481
+ if (Number.isNaN(lineNumber)) return;
482
+ const line = lineNumber - 1;
483
+ const selection = {
484
+ start: {
485
+ line,
486
+ character: 0
487
+ },
488
+ end: {
489
+ line,
490
+ character: textDocument.getLineText(line).length
491
+ },
492
+ direction: DirectionForward
493
+ };
494
+ this.#isGutterMouseDown = true;
495
+ this.#selectionStart = selection;
496
+ this.#updateSelections([selection]);
497
+ this.#focus(selection.end);
498
+ this.#mouseUpDisposes = [addEventListener(document, "mousemove", (e$1) => {
499
+ let target$1 = e$1.composedPath()[0];
500
+ if (target$1?.dataset.lineNumberContent !== void 0) target$1 = target$1?.parentElement ?? void 0;
501
+ else if (target$1?.tagName === "SPAN") target$1 = target$1?.closest("[data-line]");
502
+ if (target$1 === void 0) return;
503
+ const line$1 = target$1.dataset.columnNumber ?? target$1.dataset.line;
504
+ const lineType$1 = target$1.dataset.lineType;
505
+ if (this.#isGutterMouseDown && this.#textDocument !== void 0 && line$1 !== void 0 && lineType$1 !== void 0 && isLineEditable(lineType$1)) {
506
+ const lineNumber$1 = parseInt(line$1, 10);
507
+ if (Number.isNaN(lineNumber$1)) return;
508
+ const lineIndex = lineNumber$1 - 1;
509
+ let selection$1 = {
510
+ start: {
511
+ line: lineIndex,
512
+ character: 0
513
+ },
514
+ end: {
515
+ line: lineIndex,
516
+ character: this.#textDocument.getLineText(lineIndex).length
517
+ },
518
+ direction: DirectionForward
519
+ };
520
+ if (this.#selectionStart !== void 0) selection$1 = createSelectionFrom(this.#selectionStart, selection$1);
521
+ else this.#selectionStart = selection$1;
522
+ this.#updateSelections([selection$1]);
523
+ this.#focus(selection$1.end);
524
+ }
525
+ }, { passive: true })];
526
+ }, { passive: true }));
527
+ this.#resizeObserver?.disconnect();
528
+ this.#resizeObserver = new ResizeObserver(() => {
529
+ this.#handleLayoutResize();
530
+ });
531
+ this.#resizeObserver.observe(contentEl);
532
+ this.#resizeObserver.observe(contentEl.parentElement);
533
+ }
503
534
  #runCommand(command) {
504
535
  const textDocument = this.#textDocument;
505
536
  if (textDocument === void 0) return;
@@ -609,7 +640,7 @@ var Editor = class {
609
640
  }
610
641
  #rerender(change, newLineAnnotations, renderRange = this.#renderRange, shouldUpdateBuffer) {
611
642
  const tokenizer = this.#tokenizer;
612
- const component = this.#component;
643
+ const component = this.#fileInstance;
613
644
  const fileContents = this.#fileContents;
614
645
  const textDocument = this.#textDocument;
615
646
  const contentEl = this.#contentElement;
@@ -617,21 +648,25 @@ var Editor = class {
617
648
  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
649
  tokenizer.stopBackgroundTokenize();
619
650
  const t = performance.now();
620
- const isAdvancedMode = this.#editMode === "advanced";
651
+ const isFileDiff = this.#fileInstanceType === "diff";
621
652
  const dirtyLines = tokenizer.tokenize(change, renderRange);
622
653
  const t2 = performance.now();
623
654
  if (dirtyLines.size > 0) {
624
655
  const children = contentEl.children;
625
656
  const dirtyLineIndexes = new Set(dirtyLines.keys());
626
- if (isAdvancedMode) for (const child of children) {
657
+ if (isFileDiff) for (const child of children) {
627
658
  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;
659
+ const line = el.dataset.line;
660
+ if (line !== void 0) {
661
+ const lineNumber = parseInt(line, 10);
662
+ if (!Number.isNaN(lineNumber)) {
663
+ const lineIndex = lineNumber - 1;
664
+ const tokens = dirtyLines.get(lineIndex);
665
+ if (tokens !== void 0) {
666
+ el.replaceChildren(...renderLineTokens(tokens, tokenizer.themeType));
667
+ dirtyLineIndexes.delete(lineIndex);
668
+ if (dirtyLineIndexes.size === 0) break;
669
+ }
635
670
  }
636
671
  }
637
672
  }
@@ -639,13 +674,16 @@ var Editor = class {
639
674
  const startingLine = renderRange?.startingLine ?? 0;
640
675
  for (let i = change.startLine - startingLine; i < children.length; i++) {
641
676
  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;
677
+ if (child !== void 0 && child.dataset.line !== void 0) {
678
+ const lineNumber = parseInt(child.dataset.line, 10);
679
+ if (!Number.isNaN(lineNumber)) {
680
+ const lineIndex = lineNumber - 1;
681
+ if (dirtyLines.has(lineIndex)) {
682
+ const tokens = dirtyLines.get(lineIndex);
683
+ child.replaceChildren(...renderLineTokens(tokens, tokenizer.themeType));
684
+ dirtyLineIndexes.delete(lineIndex);
685
+ if (dirtyLineIndexes.size === 0) break;
686
+ }
649
687
  }
650
688
  }
651
689
  }
@@ -678,11 +716,12 @@ var Editor = class {
678
716
  const children = parent.children;
679
717
  for (let i = children.length - 1; i >= 0; i--) {
680
718
  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
- }
719
+ const { line, columnNumber, lineAnnotation } = child.dataset;
720
+ if (line === void 0 && columnNumber === void 0 && lineAnnotation === void 0) continue;
721
+ const lineIndex = lineAnnotation !== void 0 ? parseInt(lineAnnotation.split(",")[1], 10) : parseInt(line ?? columnNumber, 10) - 1;
722
+ if (Number.isNaN(lineIndex)) continue;
723
+ if (lineIndex < change.lineCount) break;
724
+ child.remove();
686
725
  }
687
726
  }
688
727
  if (change.lineDelta !== 0) {
@@ -690,7 +729,8 @@ var Editor = class {
690
729
  contentEl.style.gridRow = "span " + contentEl.children.length;
691
730
  }
692
731
  component.applyLineChange?.(dirtyLines, tokenizer.themeType);
693
- if (change.lineDelta !== 0 || isAdvancedMode) component.applyLayoutChange(textDocument, newLineAnnotations, shouldUpdateBuffer);
732
+ if (change.lineDelta !== 0 || isFileDiff) component.applyLayoutChange(textDocument, newLineAnnotations, shouldUpdateBuffer);
733
+ if (isFileDiff) this.#tokenizer?.stopBackgroundTokenize();
694
734
  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
735
  }
696
736
  #handleInput(inputType, data) {
@@ -719,40 +759,64 @@ var Editor = class {
719
759
  }
720
760
  }
721
761
  #updateSelections(selections) {
722
- if (selections.length === 0) return;
762
+ this.postponeBackgroundTokenizeToNextFrame();
723
763
  const gutterBuffer = this.#contentElement?.previousElementSibling;
724
- const normalizedSelections = mergeOverlappingSelections(selections);
725
- const primarySelection = normalizedSelections.at(-1);
726
- this.#selections = normalizedSelections;
727
764
  this.#primaryCaretElement = void 0;
728
- this.#component?.setSelectedLines(null);
765
+ this.#fileInstance?.setSelectedLines(null);
729
766
  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", "");
767
+ if (selections.length === 0 && this.#matches === void 0) {
768
+ this.#selections = void 0;
769
+ this.#matches = void 0;
770
+ this.#selectionElements?.forEach((el) => el.remove());
771
+ this.#selectionElements?.clear();
772
+ return;
739
773
  }
740
774
  const fragment = document.createDocumentFragment();
741
775
  const renderCtx = {
742
776
  fragment,
743
777
  elements: /* @__PURE__ */ new Map()
744
778
  };
745
- for (const selection of normalizedSelections) {
746
- if (!isCollapsedSelection(selection)) this.#renderSelection(renderCtx, selection);
747
- this.#renderCaret(renderCtx, selection, selection === primarySelection);
779
+ if (selections.length > 0) {
780
+ const normalizedSelections = mergeOverlappingSelections(selections);
781
+ const primarySelection = normalizedSelections.at(-1);
782
+ this.#selections = normalizedSelections;
783
+ if (isCollapsedSelection(primarySelection)) {
784
+ const line = primarySelection.start.line + 1;
785
+ this.#fileInstance?.setSelectedLines({
786
+ start: line,
787
+ end: line
788
+ });
789
+ } else if (gutterBuffer !== void 0 && gutterBuffer instanceof HTMLElement) {
790
+ const pos = getCaretPosition(primarySelection);
791
+ gutterBuffer.querySelector(`[data-column-number="${pos.line + 1}"]`)?.setAttribute("data-active", "");
792
+ }
793
+ for (const selection of normalizedSelections) {
794
+ if (!isCollapsedSelection(selection)) this.#renderSelection(renderCtx, selection, "selection");
795
+ this.#renderCaret(renderCtx, selection, selection === primarySelection);
796
+ }
797
+ if (this.#options.enabledQuickEdit === true && !isCollapsedSelection(primarySelection)) this.#renderQuickEditIcon(renderCtx, primarySelection);
798
+ }
799
+ const textDocument = this.#textDocument;
800
+ if (this.#matches !== void 0 && textDocument !== void 0) {
801
+ const primarySelection = this.#selections?.at(-1);
802
+ const primaryStartOffset = primarySelection !== void 0 ? textDocument.offsetAt(primarySelection.start) : -1;
803
+ const primaryEndOffset = primarySelection !== void 0 ? textDocument.offsetAt(primarySelection.end) : -1;
804
+ for (const [startOffset, endOffset] of this.#matches) {
805
+ const selection = {
806
+ start: textDocument.positionAt(startOffset),
807
+ end: textDocument.positionAt(endOffset),
808
+ direction: DirectionNone
809
+ };
810
+ const isFocused = primaryStartOffset === startOffset && primaryEndOffset === endOffset;
811
+ this.#renderSelection(renderCtx, selection, "match", isFocused);
812
+ }
748
813
  }
749
- if (this.#options.enabledQuickEdit === true && !isCollapsedSelection(primarySelection)) this.#renderQuickEditIcon(renderCtx, primarySelection);
750
814
  this.#overlayElement?.appendChild(fragment);
751
815
  this.#selectionElements?.forEach((el) => el.remove());
752
816
  this.#selectionElements?.clear();
753
817
  this.#selectionElements = renderCtx.elements;
754
818
  }
755
- #updateWindowSelection(selection) {
819
+ #setWindowSelection(selection) {
756
820
  const winSelection = window.getSelection();
757
821
  if (winSelection === null) return;
758
822
  let { start, end, direction } = selection;
@@ -777,7 +841,7 @@ var Editor = class {
777
841
  #focus(position, preventScroll = true) {
778
842
  if (position !== void 0) {
779
843
  this.#shouldIgnoreSelectionChange = true;
780
- this.#updateWindowSelection({
844
+ this.#setWindowSelection({
781
845
  start: position,
782
846
  end: position,
783
847
  direction: DirectionNone
@@ -788,11 +852,9 @@ var Editor = class {
788
852
  this.#shouldIgnoreSelectionChange = false;
789
853
  });
790
854
  });
791
- } else requestAnimationFrame(() => {
792
- this.#contentElement?.focus({ preventScroll });
793
- });
855
+ } else this.#contentElement?.focus({ preventScroll });
794
856
  }
795
- #scrollToPrimaryCaret() {
857
+ #scrollToPrimaryCaret(noFocus = false) {
796
858
  const primaryCaretElement = this.#primaryCaretElement;
797
859
  const primarySelection = this.#selections?.at(-1);
798
860
  if (primarySelection === void 0) return;
@@ -801,20 +863,21 @@ var Editor = class {
801
863
  block: "nearest",
802
864
  inline: "nearest"
803
865
  });
804
- this.#focus(primarySelection.direction === DirectionBackward ? primarySelection.end : primarySelection.start);
866
+ if (!noFocus) this.#focus(primarySelection.direction === DirectionBackward ? primarySelection.end : primarySelection.start);
805
867
  } else {
806
868
  const pos = getCaretPosition(primarySelection);
807
- this.#scrollToLine(pos.line, pos.character);
869
+ this.#scrollToLine(pos.line, pos.character, noFocus);
808
870
  }
809
871
  }
810
872
  #getScrollMargin() {
811
- const componentTop = this.#component?.top ?? 0;
873
+ const componentTop = this.#fileInstance?.top ?? 0;
812
874
  const top = this.#searchPanel !== void 0 ? 48 : 0;
813
875
  const start = this.#getGutterWidth() + this.#metrics.ch;
814
876
  const end = this.#metrics.ch;
815
877
  return `${componentTop + top}px ${end}px 0 ${start}px`;
816
878
  }
817
- #scrollToLine(line, char = 0) {
879
+ #scrollToLine(line, char = 0, noFocus = false) {
880
+ this.postponeBackgroundTokenizeToNextFrame();
818
881
  const virtualCaret = h("div", { style: {
819
882
  position: "absolute",
820
883
  left: "0",
@@ -832,111 +895,183 @@ var Editor = class {
832
895
  block: "center",
833
896
  inline: "nearest"
834
897
  });
835
- this.#focus({
898
+ if (!noFocus) this.#focus({
836
899
  line,
837
900
  character: char
838
901
  });
839
- requestAnimationFrame(() => virtualCaret.remove());
840
902
  } else {
841
903
  const approximateLineY = ((this.#lineAnnotations ?? []).filter((annotation) => annotation.lineNumber < line).length + line) * this.#metrics.lineHeight;
842
904
  virtualCaret.style.top = approximateLineY + "px";
843
- this.#componentContainer?.shadowRoot?.appendChild(virtualCaret);
905
+ this.#fileContainer?.shadowRoot?.appendChild(virtualCaret);
844
906
  this.#scrollingToLine = line;
845
907
  this.#scrollingToLineChar = char;
908
+ this.#scrollingToLineNoFocus = noFocus;
846
909
  virtualCaret.scrollIntoView({
847
910
  block: "center",
848
911
  inline: "nearest"
849
912
  });
850
- requestAnimationFrame(() => virtualCaret.remove());
851
913
  }
914
+ virtualCaret.remove();
852
915
  }
853
- #renderSelection(renderCtx, selection) {
916
+ #renderSelection(renderCtx, selection, type, isFocused) {
854
917
  if (this.#textDocument === void 0) return;
855
918
  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;
919
+ for (let line = start.line; line <= end.line; line++) {
920
+ if (!this.#isLineVisible(line)) continue;
921
+ const isLastLine = line === end.line;
922
+ const lineText = this.#textDocument.getLineText(line);
923
+ const startChar = line === start.line ? start.character : 0;
924
+ const endChar = isLastLine ? end.character : lineText.length;
861
925
  if (this.#wrap) {
862
- const paddingInline = this.#metrics.ch;
863
926
  const contentWidth = this.#getContentWidth();
864
- if (2 * paddingInline + this.#metrics.measureTextWidth(lineText) > contentWidth) {
865
- this.#renderWrappedSelection(renderCtx, selection, ln, lineText, startChar, endChar, paddingInline);
927
+ if (2 * this.#metrics.ch + this.#metrics.measureTextWidth(lineText) > contentWidth) {
928
+ this.#renderWrappedSelection(renderCtx, line, lineText, startChar, endChar, isLastLine, type, isFocused);
866
929
  continue;
867
930
  }
868
931
  }
869
932
  let left = 0;
870
933
  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);
934
+ if (startChar === 0) left = this.#getGutterWidth() + this.#metrics.ch;
935
+ else left = this.#getCharX(line, startChar)[0];
936
+ if (startChar === endChar) width = isLastLine ? 0 : this.#metrics.ch;
937
+ else width = this.#getCharX(line, endChar)[0] - left + (isLastLine ? 0 : this.#metrics.ch);
938
+ this.#renderSelectionBlock(renderCtx, line, 0, left, width, type, isFocused);
879
939
  }
880
940
  }
881
- #renderWrappedSelection(renderCtx, selection, line, lineText, startChar, endChar, paddingInline) {
941
+ #renderWrappedSelection(renderCtx, line, lineText, startChar, endChar, isLastLine, type, isFocused = false) {
882
942
  const wrapOffsets = this.#wrapLineText(line);
883
943
  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];
944
+ const offsetLeft = this.#getGutterWidth() + this.#metrics.ch;
945
+ for (let wrapLine = 0; wrapLine < segmentCount; wrapLine++) {
946
+ const segmentStart = wrapOffsets[wrapLine];
947
+ const segmentEnd = wrapOffsets[wrapLine + 1];
889
948
  const wrapStartChar = Math.max(startChar, segmentStart);
890
949
  const wrapEndChar = Math.min(endChar, segmentEnd);
891
950
  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
951
  let segmentLeft;
898
952
  let segmentWidth;
899
- if (wrapStartChar === 0 && wrapEndChar === 0) {
900
- segmentLeft = offsetLeft;
901
- segmentWidth = line === selection.end.line ? 0 : paddingInline;
902
- } else {
953
+ if (wrapStartChar === 0) segmentLeft = offsetLeft;
954
+ else {
903
955
  const prefixInSegment = lineText.slice(segmentStart, wrapStartChar);
904
956
  const prefixAsciiColumns = getExpandedAsciiTextColumns(prefixInSegment, this.#metrics.tabSize);
905
957
  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
958
  }
913
- this.#renderSelectionRange(renderCtx, selection, line, w, wrapStartChar, wrapEndChar, segmentWidth, segmentLeft, w === lastSegmentIndex);
959
+ if (wrapStartChar === wrapEndChar) segmentWidth = wrapLine === segmentCount - 1 ? 0 : this.#metrics.ch;
960
+ else {
961
+ const selectionInSegment = lineText.slice(wrapStartChar, wrapEndChar);
962
+ const selectionAsciiWidth = getExpandedAsciiTextColumns(selectionInSegment, this.#metrics.tabSize);
963
+ segmentWidth = selectionAsciiWidth !== -1 ? selectionAsciiWidth * this.#metrics.ch : this.#metrics.measureTextWidth(selectionInSegment);
964
+ if (!isLastLine && wrapLine === segmentCount - 1) segmentWidth += this.#metrics.ch;
965
+ }
966
+ this.#renderSelectionBlock(renderCtx, line, wrapLine, segmentLeft, segmentWidth, type, isFocused);
914
967
  }
915
968
  }
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;
969
+ #renderSelectionBlock(renderCtx, line, wrapLine, left, width, type, isFocused = false) {
970
+ if (width === 0) return;
971
+ const { ch, lineHeight } = this.#metrics;
972
+ const y = this.#getLineY(line) + wrapLine * lineHeight;
973
+ const css = `width:${width}px;transform:translateX(${left}px) translateY(${y}px);`;
974
+ const cacheKey = `${type}-block-${left}-${y}-${width}-${isFocused ? "f" : ""}`;
919
975
  const selectionEls = this.#selectionElements;
920
- if (renderCtx.elements.has(cacheKey)) return;
921
- let rangeEl;
976
+ const rounded = (this.#options.roundedSelection ?? true) && type === "selection";
977
+ const addRoundedCorner = (line$1, wrapLine$1, left$1, radius) => {
978
+ const top = this.#getLineY(line$1) + wrapLine$1 * lineHeight;
979
+ const css$1 = `width:${ch}px;transform:translateX(${left$1}px) translateY(${top}px);`;
980
+ const dataset = {
981
+ selectionCorner: "",
982
+ [radius]: ""
983
+ };
984
+ const cacheKeyPrefix = `${type}-block-${left$1}-${top}-1ch`;
985
+ let cacheKey$1 = cacheKeyPrefix + "-" + radius;
986
+ if (radius === "rbl") {
987
+ const prevCornerKey = cacheKeyPrefix + "-rtl";
988
+ const prevCorner = renderCtx.elements.get(prevCornerKey);
989
+ if (prevCorner !== void 0) {
990
+ prevCorner.remove();
991
+ renderCtx.elements.delete(prevCornerKey);
992
+ cacheKey$1 += "-rtl";
993
+ dataset.rtl = "";
994
+ }
995
+ }
996
+ let cornerEl = renderCtx.elements.get(cacheKey$1);
997
+ if (cornerEl !== void 0) return;
998
+ if (selectionEls?.has(cacheKey$1) === true) {
999
+ cornerEl = selectionEls.get(cacheKey$1);
1000
+ selectionEls.delete(cacheKey$1);
1001
+ } else cornerEl = h("div", {
1002
+ dataset: "selectionRange",
1003
+ style: { cssText: css$1 },
1004
+ children: [h("div", { dataset })]
1005
+ }, renderCtx.fragment);
1006
+ renderCtx.elements.set(cacheKey$1, cornerEl);
1007
+ };
1008
+ const addRadiusStyle = (element) => {
1009
+ const end = left + width;
1010
+ const dataset = element.dataset;
1011
+ const previousSelectionRange = renderCtx.previousSelectionRange;
1012
+ if (previousSelectionRange === void 0 || previousSelectionRange.line !== line || previousSelectionRange.wrapLine !== wrapLine) renderCtx.previousSelectionRange = {
1013
+ element,
1014
+ line,
1015
+ wrapLine,
1016
+ left,
1017
+ width
1018
+ };
1019
+ if (previousSelectionRange === void 0 || end <= previousSelectionRange.left) [
1020
+ "rtl",
1021
+ "rtr",
1022
+ "rbl",
1023
+ "rbr"
1024
+ ].forEach((key) => {
1025
+ dataset[key] = "";
1026
+ });
1027
+ else {
1028
+ const prevLine = previousSelectionRange.line;
1029
+ const prevWrapLine = previousSelectionRange.wrapLine;
1030
+ const prevLeft = previousSelectionRange.left;
1031
+ const prevDataset = previousSelectionRange.element.dataset;
1032
+ const prevEnd = prevLeft + previousSelectionRange.width;
1033
+ if (prevLeft > left) addRoundedCorner(prevLine, prevWrapLine, prevLeft - ch, "rbr");
1034
+ delete prevDataset.rbl;
1035
+ delete dataset.rtl;
1036
+ delete dataset.rtr;
1037
+ if (end >= prevEnd) delete prevDataset.rbr;
1038
+ if (end > prevEnd) {
1039
+ addRoundedCorner(prevLine, prevWrapLine, prevEnd, "rbl");
1040
+ dataset.rtr = "";
1041
+ }
1042
+ if (end < prevEnd) addRoundedCorner(line, wrapLine, end, "rtl");
1043
+ if (left < prevLeft) dataset.rtl = "";
1044
+ dataset.rbl = "";
1045
+ dataset.rbr = "";
1046
+ }
1047
+ };
1048
+ let rangeEl = renderCtx.elements.get(cacheKey);
1049
+ if (rangeEl !== void 0) {
1050
+ if (rounded) addRadiusStyle(rangeEl);
1051
+ return;
1052
+ }
922
1053
  if (selectionEls?.has(cacheKey) === true) {
923
1054
  rangeEl = selectionEls.get(cacheKey);
924
1055
  selectionEls.delete(cacheKey);
925
- } else rangeEl = h("div", {
926
- dataset: "selectionRange",
927
- style: { cssText: css }
928
- }, renderCtx.fragment);
1056
+ } else {
1057
+ rangeEl = h("div", {
1058
+ dataset: type + "Range",
1059
+ style: { cssText: css }
1060
+ }, renderCtx.fragment);
1061
+ if (type === "match" && isFocused === true) rangeEl.dataset.focus = "";
1062
+ }
1063
+ if (rounded) addRadiusStyle(rangeEl);
929
1064
  renderCtx.elements.set(cacheKey, rangeEl);
930
1065
  }
931
1066
  #renderCaret(renderCtx, selection, isPrimary) {
932
1067
  const { line, character } = getCaretPosition(selection);
933
1068
  if (!this.#isLineVisible(line)) return;
934
1069
  const [left, wrapLine] = this.#getCharX(line, character);
935
- const cacheKey = "caret-" + line + "(" + wrapLine + ")-" + character;
1070
+ const cacheKey = "caret-" + line + "/" + wrapLine + ":" + character;
936
1071
  if (renderCtx.elements.has(cacheKey)) return;
937
1072
  const caretEl = h("div", {
938
1073
  dataset: "caret",
939
- style: { transform: `translateY(${this.#getLineY(line) + wrapLine * this.#metrics.lineHeight}px) translateX(${left - 1}px)` }
1074
+ style: { transform: `translateX(${left - 1}px) translateY(${this.#getLineY(line) + wrapLine * this.#metrics.lineHeight}px)` }
940
1075
  }, renderCtx.fragment);
941
1076
  renderCtx.elements.set(cacheKey, caretEl);
942
1077
  if (isPrimary) {
@@ -962,7 +1097,7 @@ var Editor = class {
962
1097
  cleanUpQuickEdit();
963
1098
  const textDocument = this.#textDocument;
964
1099
  const renderQuickEdit = this.#options.renderQuickEdit;
965
- const fileContainer = this.#componentContainer;
1100
+ const fileContainer = this.#fileContainer;
966
1101
  if (textDocument === void 0 || renderQuickEdit === void 0 || fileContainer == null) return;
967
1102
  const line$1 = selection.end.line;
968
1103
  const lineText = textDocument.getLineText(line$1);
@@ -1001,48 +1136,60 @@ var Editor = class {
1001
1136
  #renderSearchPanel() {
1002
1137
  this.#searchPanel?.cleanup();
1003
1138
  const textDocument = this.#textDocument;
1004
- const preElement = this.#componentContainer?.shadowRoot?.querySelector("pre");
1139
+ const preElement = this.#fileContainer?.shadowRoot?.querySelector("pre");
1140
+ const selections = this.#selections;
1005
1141
  if (textDocument === void 0 || preElement == null) return;
1006
1142
  let defaultQuery = "";
1007
1143
  let initialMatch = void 0;
1008
- const selections = this.#selections;
1009
1144
  if (selections !== void 0 && selections.length > 0) {
1010
1145
  let primarySelection = selections.at(-1);
1011
1146
  if (isCollapsedSelection(primarySelection)) {
1012
1147
  primarySelection = expandCollapsedSelectionToWord(textDocument, primarySelection);
1013
1148
  this.#updateSelections([...selections.slice(0, -1), primarySelection]);
1014
1149
  const selectionText = textDocument.getText(primarySelection);
1015
- if (!selectionText.includes("\n")) {
1150
+ if (selectionText !== "" && !selectionText.includes("\n")) {
1016
1151
  defaultQuery = selectionText;
1017
1152
  initialMatch = [textDocument.offsetAt(primarySelection.start), textDocument.offsetAt(primarySelection.end)];
1018
1153
  }
1019
1154
  }
1020
1155
  }
1156
+ const scrollToMatch = ([startOffset, endOffset], retainFocus) => {
1157
+ const nextSelection = createSelectionFromAnchorAndFocusOffsets(textDocument, startOffset, endOffset);
1158
+ this.#updateSelections([nextSelection]);
1159
+ this.#scrollToPrimaryCaret(true);
1160
+ this.#retainSearchPanelFocus = retainFocus;
1161
+ };
1021
1162
  this.#searchPanel = new SearchPanelWidget({
1022
1163
  textDocument,
1023
1164
  containerElement: preElement,
1024
1165
  defaultQuery,
1025
1166
  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);
1167
+ scrollToMatch,
1168
+ onUpdate: (allMatches) => {
1169
+ if (allMatches.length === 0) {
1170
+ this.#matches = void 0;
1171
+ this.#updateSelections(this.#selections ?? []);
1172
+ return;
1173
+ }
1174
+ this.#matches = allMatches;
1175
+ const primarySelection = this.#selections?.at(-1);
1176
+ let searchOffset = 0;
1177
+ let nextMatch;
1178
+ if (primarySelection !== void 0) searchOffset = textDocument.offsetAt(primarySelection.start);
1179
+ for (const m of allMatches) if (m[0] >= searchOffset) {
1180
+ nextMatch = m;
1181
+ break;
1041
1182
  }
1183
+ if (nextMatch !== void 0) scrollToMatch(nextMatch, true);
1184
+ this.#matches = allMatches;
1185
+ this.#updateSelections(this.#selections ?? []);
1186
+ return nextMatch;
1042
1187
  },
1043
1188
  onClose: () => {
1044
1189
  this.#searchPanel = void 0;
1045
1190
  this.#retainSearchPanelFocus = false;
1191
+ this.#matches = void 0;
1192
+ this.#updateSelections(this.#selections ?? []);
1046
1193
  }
1047
1194
  });
1048
1195
  this.#retainSearchPanelFocus = false;
@@ -1137,17 +1284,15 @@ var Editor = class {
1137
1284
  this.#rerender(change, newLineAnnotations, renderRange, shouldUpdateBuffer);
1138
1285
  if (selections !== void 0) {
1139
1286
  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
- }
1287
+ if (this.#primaryCaretElement !== void 0) this.#primaryCaretElement.scrollIntoView({
1288
+ block: "nearest",
1289
+ inline: "nearest"
1150
1290
  });
1291
+ else if (selections.length > 0) {
1292
+ const pos = getCaretPosition(selections.at(-1));
1293
+ this.#scrollToLine(pos.line, pos.character);
1294
+ }
1295
+ this.focus({ preventScroll: true });
1151
1296
  }
1152
1297
  }
1153
1298
  #applyChangeToLineAnnotations(change) {
@@ -1162,17 +1307,17 @@ var Editor = class {
1162
1307
  #getLineElement(line) {
1163
1308
  const contentElement = this.#contentElement;
1164
1309
  if (contentElement === void 0) return;
1165
- if (this.#renderRange !== void 0 && this.#editMode === "simple") {
1310
+ if (this.#renderRange !== void 0 && this.#fileInstanceType === "file") {
1166
1311
  const { startingLine } = this.#renderRange;
1167
1312
  const { children } = contentElement;
1168
1313
  for (let i = line - startingLine; i <= children.length; i++) {
1169
1314
  const child = children[i];
1170
1315
  const lineNumber = child?.dataset.line;
1171
1316
  const lineType = child?.dataset.lineType;
1172
- if (lineNumber !== void 0 && lineType !== void 0 && isLineEditable(lineType) && Number(lineNumber) === line + 1) return child;
1317
+ if (lineNumber !== void 0 && lineType !== void 0 && isLineEditable(lineType) && parseInt(lineNumber, 10) === line + 1) return child;
1173
1318
  }
1174
1319
  }
1175
- if (this.#editMode === "advanced") return contentElement.querySelector(`[data-line="${line + 1}"]:not([data-line-type="change-deletion"])`) ?? void 0;
1320
+ if (this.#fileInstanceType === "diff") return contentElement.querySelector(`[data-line="${line + 1}"]:not([data-line-type="change-deletion"])`) ?? void 0;
1176
1321
  return contentElement.querySelector(`[data-line="${line + 1}"]`) ?? void 0;
1177
1322
  }
1178
1323
  #getGutterWidth() {
@@ -1180,7 +1325,7 @@ var Editor = class {
1180
1325
  if (gutterElement == null || !(gutterElement instanceof HTMLElement) || !gutterElement.hasAttribute("data-gutter")) return 0;
1181
1326
  if (this.#gutterWidthCache === void 0) {
1182
1327
  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));
1328
+ if (diffsColumnNumberWidth !== void 0 && diffsColumnNumberWidth.length > 2 && diffsColumnNumberWidth.endsWith("px")) this.#gutterWidthCache = parseInt(diffsColumnNumberWidth.slice(0, -2), 10);
1184
1329
  else this.#gutterWidthCache = gutterElement.offsetWidth;
1185
1330
  }
1186
1331
  return this.#gutterWidthCache;
@@ -1189,7 +1334,7 @@ var Editor = class {
1189
1334
  if (this.#contentElement === void 0) return 0;
1190
1335
  if (this.#contentWidthCache === void 0) {
1191
1336
  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));
1337
+ if (diffsColumnContentWidth !== void 0 && diffsColumnContentWidth.length > 2 && diffsColumnContentWidth.endsWith("px")) this.#contentWidthCache = parseFloat(diffsColumnContentWidth.slice(0, -2));
1193
1338
  else this.#contentWidthCache = this.#contentElement.offsetWidth;
1194
1339
  }
1195
1340
  return this.#contentWidthCache;