@pierre/diffs 1.3.0-beta.4 → 1.3.0-beta.5

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 (89) hide show
  1. package/README.md +6 -6
  2. package/dist/components/CodeView.d.ts.map +1 -1
  3. package/dist/components/CodeView.js +6 -6
  4. package/dist/components/CodeView.js.map +1 -1
  5. package/dist/components/File.d.ts +1 -0
  6. package/dist/components/File.d.ts.map +1 -1
  7. package/dist/components/File.js +26 -12
  8. package/dist/components/File.js.map +1 -1
  9. package/dist/components/FileDiff.d.ts +1 -0
  10. package/dist/components/FileDiff.d.ts.map +1 -1
  11. package/dist/components/FileDiff.js +12 -11
  12. package/dist/components/FileDiff.js.map +1 -1
  13. package/dist/components/FileStream.js +4 -2
  14. package/dist/components/FileStream.js.map +1 -1
  15. package/dist/components/UnresolvedFile.d.ts.map +1 -1
  16. package/dist/components/VirtualizedFile.d.ts +5 -1
  17. package/dist/components/VirtualizedFile.d.ts.map +1 -1
  18. package/dist/components/VirtualizedFile.js +82 -21
  19. package/dist/components/VirtualizedFile.js.map +1 -1
  20. package/dist/components/VirtualizedFileDiff.d.ts +6 -1
  21. package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
  22. package/dist/components/VirtualizedFileDiff.js +77 -15
  23. package/dist/components/VirtualizedFileDiff.js.map +1 -1
  24. package/dist/components/VirtulizerDevelopment.d.ts.map +1 -1
  25. package/dist/editor/command.d.ts +1 -1
  26. package/dist/editor/command.d.ts.map +1 -1
  27. package/dist/editor/command.js +3 -3
  28. package/dist/editor/command.js.map +1 -1
  29. package/dist/editor/editor.d.ts +31 -5
  30. package/dist/editor/editor.d.ts.map +1 -1
  31. package/dist/editor/editor.js +240 -178
  32. package/dist/editor/editor.js.map +1 -1
  33. package/dist/editor/editor2.js +1 -1
  34. package/dist/editor/editor2.js.map +1 -1
  35. package/dist/editor/index.d.ts +2 -2
  36. package/dist/editor/marker.d.ts +2 -2
  37. package/dist/editor/marker.d.ts.map +1 -1
  38. package/dist/editor/marker.js +2 -2
  39. package/dist/editor/marker.js.map +1 -1
  40. package/dist/editor/pieceTable.d.ts +6 -1
  41. package/dist/editor/pieceTable.d.ts.map +1 -1
  42. package/dist/editor/pieceTable.js +32 -1
  43. package/dist/editor/pieceTable.js.map +1 -1
  44. package/dist/editor/searchPanel.d.ts +12 -3
  45. package/dist/editor/searchPanel.d.ts.map +1 -1
  46. package/dist/editor/searchPanel.js +168 -54
  47. package/dist/editor/searchPanel.js.map +1 -1
  48. package/dist/editor/selection.d.ts.map +1 -1
  49. package/dist/editor/sprite.d.ts +2 -2
  50. package/dist/editor/sprite.d.ts.map +1 -1
  51. package/dist/editor/sprite.js +10 -3
  52. package/dist/editor/sprite.js.map +1 -1
  53. package/dist/editor/textMeasure.d.ts +1 -0
  54. package/dist/editor/textMeasure.d.ts.map +1 -1
  55. package/dist/editor/textMeasure.js +6 -0
  56. package/dist/editor/textMeasure.js.map +1 -1
  57. package/dist/editor/tokenzier.js +9 -6
  58. package/dist/editor/tokenzier.js.map +1 -1
  59. package/dist/managers/InteractionManager.js +1 -1
  60. package/dist/managers/InteractionManager.js.map +1 -1
  61. package/dist/managers/ResizeManager.js +1 -1
  62. package/dist/managers/ResizeManager.js.map +1 -1
  63. package/dist/react/CodeView.js +1 -1
  64. package/dist/react/jsx.d.ts.map +1 -1
  65. package/dist/renderers/DiffHunksRenderer.d.ts +3 -2
  66. package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
  67. package/dist/renderers/DiffHunksRenderer.js +49 -2
  68. package/dist/renderers/DiffHunksRenderer.js.map +1 -1
  69. package/dist/renderers/FileRenderer.js +12 -0
  70. package/dist/renderers/FileRenderer.js.map +1 -1
  71. package/dist/ssr/FileDiffReact.js +1 -1
  72. package/dist/types.d.ts +17 -2
  73. package/dist/types.d.ts.map +1 -1
  74. package/dist/utils/includesFileAnnotations.d.ts +17 -0
  75. package/dist/utils/includesFileAnnotations.d.ts.map +1 -0
  76. package/dist/utils/includesFileAnnotations.js +19 -0
  77. package/dist/utils/includesFileAnnotations.js.map +1 -0
  78. package/dist/utils/parseMergeConflictDiffFromFile.js.map +1 -1
  79. package/dist/utils/renderDiffWithHighlighter.js +4 -2
  80. package/dist/utils/renderDiffWithHighlighter.js.map +1 -1
  81. package/dist/utils/renderFileWithHighlighter.js +4 -2
  82. package/dist/utils/renderFileWithHighlighter.js.map +1 -1
  83. package/dist/worker/{wasm-BaDzIkIn.js → wasm-qE0LgnY3.js} +2 -2
  84. package/dist/worker/{wasm-BaDzIkIn.js.map → wasm-qE0LgnY3.js.map} +1 -1
  85. package/dist/worker/worker-portable.js +289 -253
  86. package/dist/worker/worker-portable.js.map +1 -1
  87. package/dist/worker/worker.js +8 -4
  88. package/dist/worker/worker.js.map +1 -1
  89. package/package.json +4 -10
@@ -1,11 +1,12 @@
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 { EditStack } from "./editStack.js";
4
5
  import editor_default from "./editor2.js";
5
6
  import { addEventListener, clampDomOffset, extend, getLineNumberAttr, h, round } from "./utils.js";
6
7
  import { applyDocumentChangeToLineAnnotations, renderLineAnnotations } from "./lineAnnotations.js";
7
8
  import { DirectionBackward, DirectionForward, DirectionNone, applyDeleteHardLineForwardToSelections, applyDeleteSoftLineBackwardToSelections, applyDeleteWordBackwardToSelections, 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";
8
- import { MarkerManager, markerSeverityDatasetKey } from "./marker.js";
9
+ import { MarkerRenderer, markerSeverityDatasetKey } from "./marker.js";
9
10
  import { createSpriteElement } from "./sprite.js";
10
11
  import { SearchPanelWidget } from "./searchPanel.js";
11
12
  import { SelectionActionWidget } from "./selectionAction.js";
@@ -19,17 +20,10 @@ var Editor = class {
19
20
  #wrap = false;
20
21
  #metrics = new Metrics();
21
22
  #tokenizer;
22
- #markerManager;
23
23
  #editorEventDisposes;
24
24
  #globalEventDisposes;
25
25
  #selectEventDisposes;
26
26
  #detach;
27
- #fileInstance;
28
- #fileContents;
29
- #lineAnnotations;
30
- #textDocument;
31
- #renderRange;
32
- #codePaddingTop = 0;
33
27
  #gutterWidthCache;
34
28
  #contentWidthCache;
35
29
  #lineYCache = /* @__PURE__ */ new Map();
@@ -44,19 +38,25 @@ var Editor = class {
44
38
  #gutterElement;
45
39
  #contentElement;
46
40
  #overlayElement;
47
- #primaryCaretElement;
48
41
  #overlayElements;
49
- #selectionAction;
50
- #searchPanel;
42
+ #primaryCaretElement;
51
43
  #resizeObserver;
44
+ #fileInstance;
45
+ #fileContents;
46
+ #lineAnnotations;
47
+ #textDocument;
48
+ #renderRange;
49
+ #markerRenderer;
50
+ #searchPanel;
51
+ #selectionAction;
52
52
  #shouldIgnoreSelectionChange = false;
53
53
  #isGutterMouseDown = false;
54
54
  #isContentMouseDown = false;
55
55
  #shiftKeyPressed = false;
56
56
  #selectionStart;
57
57
  #reservedSelections;
58
- #selections;
59
58
  #initSelections;
59
+ #selections;
60
60
  #matches;
61
61
  #scrollingToLine;
62
62
  #scrollingToLineChar;
@@ -78,15 +78,16 @@ var Editor = class {
78
78
  }
79
79
  edit(component) {
80
80
  const { useTokenTransformer, enableGutterUtility, enableLineSelection, expandUnchanged, diffStyle, lineHoverHighlight,...rest } = component.options;
81
- if (useTokenTransformer !== true || enableGutterUtility === true || enableLineSelection === true || expandUnchanged !== true && Object.hasOwn(component, "fileDiff") || diffStyle === "unified" || lineHoverHighlight !== "disabled") {
81
+ const isDiff = component.type === "file-diff";
82
+ if (useTokenTransformer !== true || enableGutterUtility === true || enableLineSelection === true || lineHoverHighlight !== "disabled" || expandUnchanged !== true && isDiff || diffStyle === "unified" && isDiff) {
82
83
  component.setOptions({
83
84
  ...rest,
84
85
  useTokenTransformer: true,
85
86
  enableGutterUtility: false,
86
87
  enableLineSelection: false,
88
+ lineHoverHighlight: "disabled",
87
89
  expandUnchanged: true,
88
- diffStyle: "split",
89
- lineHoverHighlight: "disabled"
90
+ diffStyle: "split"
90
91
  });
91
92
  component.rerender();
92
93
  }
@@ -95,7 +96,134 @@ var Editor = class {
95
96
  this.#detach = component.attachEditor(this);
96
97
  return () => this.cleanUp();
97
98
  }
98
- syncToRenderedView(highlighter, fileContainer, fileContents, didFileChange, lineAnnotations, renderRange) {
99
+ /**
100
+ * Apply edits to current attached file.
101
+ */
102
+ applyEdits(edits, updateHistory = false) {
103
+ const textDocument = this.#textDocument;
104
+ if (textDocument == null) throw new Error("Editor is not attached");
105
+ const change = textDocument.applyEdits(edits, updateHistory, this.#selections);
106
+ if (change !== void 0) this.#applyChange(change, void 0, this.#applyChangeToLineAnnotations(change));
107
+ }
108
+ getState() {
109
+ const fileRef = this.#getFileRef();
110
+ if (fileRef === void 0) throw new Error("Editor is not attached");
111
+ return {
112
+ file: {
113
+ ...fileRef,
114
+ cacheKey: "edited-at-" + Date.now()
115
+ },
116
+ selections: this.#selections,
117
+ lineAnnotations: this.#lineAnnotations,
118
+ renderRange: this.#renderRange
119
+ };
120
+ }
121
+ setState({ file, lineAnnotations, renderRange, selections }) {
122
+ this.#resetCache();
123
+ this.#resetState();
124
+ this.#initSelections = selections;
125
+ this.#fileInstance?.render({
126
+ file: {
127
+ ...file,
128
+ cacheKey: "edited-at-" + Date.now()
129
+ },
130
+ lineAnnotations,
131
+ renderRange
132
+ });
133
+ }
134
+ setSelections(selections) {
135
+ const textDocument = this.#textDocument;
136
+ if (textDocument === void 0) throw new Error("Text document is not initialized");
137
+ const resolvedSelections = selections.map((selection) => {
138
+ const start = textDocument.normalizePosition(selection.start);
139
+ const end = textDocument.normalizePosition(selection.end);
140
+ return {
141
+ direction: selection.direction === "none" ? DirectionNone : selection.direction === "backward" ? DirectionBackward : DirectionForward,
142
+ start,
143
+ end
144
+ };
145
+ });
146
+ this.#updateSelections(resolvedSelections);
147
+ this.#scrollToPrimaryCaret(false, "center");
148
+ }
149
+ setMarkers(markers) {
150
+ const textDocument = this.#textDocument;
151
+ if (textDocument === void 0) throw new Error("Text document is not initialized");
152
+ if (markers.length === 0) {
153
+ this.#markerRenderer?.cleanup();
154
+ this.#markerRenderer = void 0;
155
+ this.#updateSelections(this.#selections ?? []);
156
+ return;
157
+ }
158
+ this.#markerRenderer ??= new MarkerRenderer({
159
+ getLineHeight: () => this.#metrics.lineHeight,
160
+ getFileContainer: () => this.#fileContainer,
161
+ getCharX: (line, character) => this.#getCharX(line, character),
162
+ getLineY: (line) => this.#getLineY(line),
163
+ isMouseDown: () => this.#isContentMouseDown || this.#isGutterMouseDown
164
+ });
165
+ this.#markerRenderer.setMarkers(markers, textDocument);
166
+ if (this.#contentElement !== void 0) this.#markerRenderer.listenHover(this.#contentElement);
167
+ this.#updateSelections(this.#selections ?? []);
168
+ }
169
+ focus(options) {
170
+ const preventScroll = options?.preventScroll ?? false;
171
+ const primarySelection = this.#selections?.at(-1);
172
+ if (primarySelection !== void 0) {
173
+ const pos = primarySelection.direction === DirectionBackward ? primarySelection.end : primarySelection.start;
174
+ this.#focus(pos, preventScroll);
175
+ } else this.#focus(void 0, preventScroll);
176
+ }
177
+ blur() {
178
+ this.#contentElement?.blur();
179
+ }
180
+ cleanUp() {
181
+ this.#tokenizer?.cleanUp();
182
+ this.#tokenizer = void 0;
183
+ this.#globalEventDisposes?.forEach((dispose) => dispose());
184
+ this.#globalEventDisposes = void 0;
185
+ this.#editorEventDisposes?.forEach((dispose) => dispose());
186
+ this.#editorEventDisposes = void 0;
187
+ this.#selectEventDisposes?.forEach((dispose) => dispose());
188
+ this.#selectEventDisposes = void 0;
189
+ this.#detach?.();
190
+ this.#detach = void 0;
191
+ this.#gutterWidthCache = void 0;
192
+ this.#contentWidthCache = void 0;
193
+ this.#lineYCache.clear();
194
+ this.#wrapLineOffsetsCache.clear();
195
+ this.#lastAccessedLineElement = void 0;
196
+ this.#lastAccessedCharX = void 0;
197
+ this.#globalStyleElement?.remove();
198
+ this.#globalStyleElement = void 0;
199
+ this.#editorStyleElement?.remove();
200
+ this.#editorStyleElement = void 0;
201
+ this.#themeStyleElement?.remove();
202
+ this.#themeStyleElement = void 0;
203
+ this.#spriteElement?.remove();
204
+ this.#spriteElement = void 0;
205
+ this.#fileContainer = void 0;
206
+ this.#gutterElement = void 0;
207
+ this.#contentElement?.removeAttribute("contentEditable");
208
+ this.#contentElement = void 0;
209
+ this.#overlayElement?.remove();
210
+ this.#overlayElement = void 0;
211
+ this.#resizeObserver?.disconnect();
212
+ this.#resizeObserver = void 0;
213
+ this.#resetState();
214
+ }
215
+ /** @internal */
216
+ __postponeBackgroundTokenizeToNextFrame() {
217
+ const tokenizer = this.#tokenizer;
218
+ if (tokenizer !== void 0) {
219
+ tokenizer.pauseBackgroundTokenize();
220
+ requestAnimationFrame(() => {
221
+ tokenizer.resumeBackgroundTokenize();
222
+ });
223
+ }
224
+ }
225
+ /** @internal */
226
+ __syncRenderView = (highlighter, fileContainer, fileContents, lineAnnotations, renderRange) => {
99
227
  const shadowRoot = fileContainer.shadowRoot;
100
228
  if (shadowRoot == null) {
101
229
  console.error("[editor] Could not find the shadow root.");
@@ -122,8 +250,9 @@ var Editor = class {
122
250
  if (this.#themeStyleElement !== void 0) shadowRoot.appendChild(this.#themeStyleElement);
123
251
  if (this.#spriteElement !== void 0) shadowRoot.prepend(this.#spriteElement);
124
252
  }
125
- if (this.#textDocument === void 0 || this.#fileContents === void 0 || didFileChange) {
126
- const textDocument = new TextDocument(fileContents.name, fileContents.contents, fileContents.lang ?? getFiletypeFromFileName(fileContents.name));
253
+ if (this.#textDocument === void 0 || this.#fileContents === void 0 || this.#fileContents.name !== fileContents.name || this.#fileContents.contents !== fileContents.contents || this.#fileContents.lang !== fileContents.lang || this.#fileContents.cacheKey !== fileContents.cacheKey) {
254
+ const editStack = new EditStack({ maxEntries: this.#options.historyMaxEntries });
255
+ const textDocument = new TextDocument(fileContents.name, fileContents.contents, fileContents.lang ?? getFiletypeFromFileName(fileContents.name), 0, editStack);
127
256
  this.#fileContents = fileContents;
128
257
  this.#textDocument = textDocument;
129
258
  this.#tokenizer?.cleanUp();
@@ -137,25 +266,12 @@ var Editor = class {
137
266
  },
138
267
  __debug: this.#options.__debug
139
268
  });
140
- this.#fileInstance?.setSelectedLines(null);
141
- this.#shouldIgnoreSelectionChange = false;
142
- this.#overlayElements?.forEach((el) => el.remove());
143
- this.#overlayElements?.clear();
144
- this.#overlayElements = void 0;
145
- this.#selections = void 0;
146
- this.#scrollingToLine = void 0;
147
- this.#reservedSelections = void 0;
148
- this.#searchPanel?.cleanup();
149
- this.#searchPanel = void 0;
150
- this.#selectionAction?.cleanup();
151
- this.#selectionAction = void 0;
269
+ this.#resetState();
270
+ this.#selections = this.#initSelections;
271
+ this.#options.onAttach?.(this, this.#fileInstance);
272
+ if (this.#textDocument !== void 0 && this.#options.__debug === true) console.log("[diffs/editor] text document changed !!!");
152
273
  }
153
274
  if (this.#contentElement !== contentEl) {
154
- if (this.#contentElement !== void 0 && this.#options.__debug === true) console.log("[diffs/editor] full re-render triggered !!!");
155
- const codePaddingTop = parseInt(getComputedStyle(codeElement).paddingTop.slice(0, -2), 10);
156
- this.#codePaddingTop = Number.isNaN(codePaddingTop) ? 0 : codePaddingTop;
157
- this.#gutterWidthCache = void 0;
158
- this.#contentWidthCache = void 0;
159
275
  this.#gutterElement = gutterEl;
160
276
  this.#contentElement = extend(contentEl, {
161
277
  contentEditable: "true",
@@ -170,134 +286,48 @@ var Editor = class {
170
286
  if (this.#overlayElement !== void 0) contentEl.after(this.#overlayElement);
171
287
  this.#metrics.init(contentEl);
172
288
  this.#listenContentElement(contentEl, gutterEl);
289
+ if (this.#contentElement !== void 0 && this.#options.__debug === true) console.log("[diffs/editor] full re-render triggered !!!");
173
290
  }
174
- this.#lineYCache.clear();
175
- this.#wrapLineOffsetsCache.clear();
176
- this.#lastAccessedLineElement = void 0;
177
- this.#lastAccessedCharX = void 0;
291
+ this.#resetCache();
178
292
  this.#wrap = this.#fileInstance?.options.overflow === "wrap";
179
293
  this.#lineAnnotations = lineAnnotations;
180
294
  this.#renderRange = renderRange;
181
295
  this.#tokenizer?.prebuildStateStack(renderRange);
182
- if (this.#initSelections !== void 0) {
183
- this.setSelections(this.#initSelections);
184
- this.#scrollToPrimaryCaret();
296
+ if (this.#selections !== void 0 || this.#matches !== void 0 || this.#markerRenderer !== void 0) this.#updateSelections(this.#selections ?? []);
297
+ if (this.#initSelections !== void 0 && this.#primaryCaretElement !== void 0) {
185
298
  this.#initSelections = void 0;
186
- } else if (this.#selections !== void 0 || this.#matches !== void 0 || this.#markerManager !== void 0) this.#updateSelections(this.#selections ?? []);
187
- if (this.#options.__debug === true && renderRange !== void 0) {
188
- const { startingLine, totalLines } = renderRange;
189
- console.log("[diffs/editor] render file:", fileContents.name, "RenderRange:", startingLine + "-" + (startingLine + totalLines), "of", this.#textDocument.lineCount, "lines");
190
- }
191
- if (this.#scrollingToLine !== void 0) this.#scrollToLine(this.#scrollingToLine, this.#scrollingToLineChar, this.#scrollingToLineNoFocus);
299
+ this.#scrollToPrimaryCaret(false, "center");
300
+ } else if (this.#scrollingToLine !== void 0) this.#scrollToLine(this.#scrollingToLine, this.#scrollingToLineChar, this.#scrollingToLineNoFocus);
192
301
  else if (this.#selections !== void 0 && this.#selections.length > 0 && !this.#retainSearchPanelFocus) this.focus({ preventScroll: true });
193
302
  if (this.#retainSearchPanelFocus) this.#searchPanel?.focus();
194
303
  if (this.#selectionAction !== void 0 && this.#isLineVisible(this.#selectionAction.line) && this.#contentElement !== void 0) this.#selectionAction.render(this.#contentElement);
195
- }
196
- postponeBackgroundTokenizeToNextFrame() {
197
- const tokenizer = this.#tokenizer;
198
- if (tokenizer !== void 0) {
199
- tokenizer.pauseBackgroundTokenize();
200
- requestAnimationFrame(() => {
201
- tokenizer.resumeBackgroundTokenize();
202
- });
203
- }
204
- }
205
- setSelections(selections) {
206
- const textDocument = this.#textDocument;
207
- if (textDocument !== void 0) {
208
- const resolvedSelections = selections.map((selection) => {
209
- const start = textDocument.normalizePosition(selection.start);
210
- const end = textDocument.normalizePosition(selection.end);
211
- return {
212
- direction: selection.direction === "none" ? DirectionNone : selection.direction === "backward" ? DirectionBackward : DirectionForward,
213
- start,
214
- end
215
- };
216
- });
217
- this.#updateSelections(resolvedSelections);
218
- this.#scrollToPrimaryCaret();
219
- } else this.#initSelections = selections;
220
- }
221
- setMarkers(markers) {
222
- const textDocument = this.#textDocument;
223
- if (textDocument === void 0) throw new Error("Text document is not initialized");
224
- if (markers.length === 0) {
225
- this.#markerManager?.cleanup();
226
- this.#markerManager = void 0;
227
- this.#updateSelections(this.#selections ?? []);
228
- return;
304
+ if (this.#options.__debug === true && renderRange !== void 0) {
305
+ const { startingLine, totalLines } = renderRange;
306
+ console.log("[diffs/editor] render file:", fileContents.name, "RenderRange:", startingLine + "-" + (startingLine + totalLines), "of", this.#textDocument.lineCount, "lines");
229
307
  }
230
- this.#markerManager ??= new MarkerManager({
231
- getLineHeight: () => this.#metrics.lineHeight,
232
- getFileContainer: () => this.#fileContainer,
233
- getCharX: (line, character) => this.#getCharX(line, character),
234
- getLineY: (line) => this.#getLineY(line),
235
- isMouseDown: () => this.#isContentMouseDown || this.#isGutterMouseDown
236
- });
237
- this.#markerManager.setMarkers(markers, textDocument);
238
- if (this.#contentElement !== void 0) this.#markerManager.listenHover(this.#contentElement);
239
- this.#updateSelections(this.#selections ?? []);
240
- }
241
- focus(options) {
242
- const preventScroll = options?.preventScroll ?? false;
243
- const primarySelection = this.#selections?.at(-1);
244
- if (primarySelection !== void 0) {
245
- const pos = primarySelection.direction === DirectionBackward ? primarySelection.end : primarySelection.start;
246
- this.#focus(pos, preventScroll);
247
- } else this.#focus(void 0, preventScroll);
248
- }
249
- cleanUp() {
250
- this.#tokenizer?.cleanUp();
251
- this.#tokenizer = void 0;
252
- this.#globalEventDisposes?.forEach((dispose) => dispose());
253
- this.#globalEventDisposes = void 0;
254
- this.#editorEventDisposes?.forEach((dispose) => dispose());
255
- this.#editorEventDisposes = void 0;
256
- this.#selectEventDisposes?.forEach((dispose) => dispose());
257
- this.#selectEventDisposes = void 0;
258
- this.#markerManager?.cleanup();
259
- this.#markerManager = void 0;
260
- this.#detach?.();
261
- this.#detach = void 0;
262
- this.#fileInstance?.setSelectedLines(null);
263
- this.#fileInstance = void 0;
264
- this.#fileContents = void 0;
265
- this.#lineAnnotations = void 0;
266
- this.#textDocument = void 0;
267
- this.#renderRange = void 0;
268
- this.#gutterWidthCache = void 0;
269
- this.#contentWidthCache = void 0;
308
+ };
309
+ #resetCache() {
270
310
  this.#lineYCache.clear();
271
311
  this.#wrapLineOffsetsCache.clear();
272
312
  this.#lastAccessedLineElement = void 0;
273
313
  this.#lastAccessedCharX = void 0;
274
- this.#globalStyleElement?.remove();
275
- this.#globalStyleElement = void 0;
276
- this.#editorStyleElement?.remove();
277
- this.#editorStyleElement = void 0;
278
- this.#themeStyleElement?.remove();
279
- this.#themeStyleElement = void 0;
280
- this.#spriteElement?.remove();
281
- this.#spriteElement = void 0;
282
- this.#fileContainer = void 0;
283
- this.#gutterElement = void 0;
284
- this.#contentElement?.removeAttribute("contentEditable");
285
- this.#contentElement = void 0;
286
- this.#overlayElement?.remove();
287
- this.#overlayElement = void 0;
314
+ }
315
+ #resetState() {
316
+ this.#gutterWidthCache = void 0;
317
+ this.#contentWidthCache = void 0;
318
+ this.#fileInstance?.setSelectedLines(null);
319
+ this.#shouldIgnoreSelectionChange = false;
288
320
  this.#overlayElements?.forEach((el) => el.remove());
289
321
  this.#overlayElements = void 0;
290
- this.#primaryCaretElement = void 0;
322
+ this.#selections = void 0;
323
+ this.#reservedSelections = void 0;
324
+ this.#scrollingToLine = void 0;
325
+ this.#markerRenderer?.cleanup();
326
+ this.#markerRenderer = void 0;
291
327
  this.#searchPanel?.cleanup();
292
328
  this.#searchPanel = void 0;
293
329
  this.#selectionAction?.cleanup();
294
330
  this.#selectionAction = void 0;
295
- this.#resizeObserver?.disconnect();
296
- this.#resizeObserver = void 0;
297
- this.#shouldIgnoreSelectionChange = false;
298
- this.#selectionStart = void 0;
299
- this.#selections = void 0;
300
- this.#reservedSelections = void 0;
301
331
  }
302
332
  #initialize() {
303
333
  this.#globalStyleElement = h("style", {
@@ -370,7 +400,7 @@ var Editor = class {
370
400
  this.#editorEventDisposes = [
371
401
  addEventListener(contentEl, "pointerdown", (e) => {
372
402
  if (e.pointerType !== "mouse") return;
373
- this.#markerManager?.removePopup();
403
+ this.#markerRenderer?.removePopup();
374
404
  if (isSafari() && this.#lineAnnotations !== void 0 && this.#lineAnnotations.length > 0) this.#selectEventDisposes = [...contentEl.querySelectorAll("[data-line-annotation]")].map((el) => [addEventListener(el, "mouseenter", () => {
375
405
  this.#shouldIgnoreSelectionChange = true;
376
406
  }), addEventListener(el, "mouseleave", () => {
@@ -484,7 +514,7 @@ var Editor = class {
484
514
  const textDocument = this.#textDocument;
485
515
  const lineIndex = resolveEditableLine(resolveGutterTarget(e.composedPath()[0]));
486
516
  if (lineIndex === void 0 || textDocument === void 0) return;
487
- this.#markerManager?.removePopup();
517
+ this.#markerRenderer?.removePopup();
488
518
  const selection = {
489
519
  start: {
490
520
  line: lineIndex,
@@ -523,7 +553,7 @@ var Editor = class {
523
553
  }, { passive: true })];
524
554
  }, { passive: true }));
525
555
  }
526
- this.#markerManager?.listenHover(contentEl);
556
+ this.#markerRenderer?.listenHover(contentEl);
527
557
  this.#resizeObserver?.disconnect();
528
558
  this.#resizeObserver = new ResizeObserver(() => {
529
559
  this.#handleLayoutResize();
@@ -536,7 +566,10 @@ var Editor = class {
536
566
  if (textDocument === void 0) return;
537
567
  switch (command) {
538
568
  case "openSearchPanel":
539
- this.#renderSearchPanel();
569
+ this.#openSearchPanel("find");
570
+ break;
571
+ case "openSearchReplacePanel":
572
+ this.#openSearchPanel("replace");
540
573
  break;
541
574
  case "findNextMatch": {
542
575
  const selections = this.#selections;
@@ -633,11 +666,11 @@ var Editor = class {
633
666
  this.#lineYCache.clear();
634
667
  this.#wrapLineOffsetsCache.clear();
635
668
  }
636
- if (this.#selections !== void 0 || this.#matches !== void 0 || this.#markerManager !== void 0) {
669
+ if (this.#selections !== void 0 || this.#matches !== void 0 || this.#markerRenderer !== void 0) {
637
670
  this.#updateSelections(this.#selections ?? []);
638
671
  if (this.#selections !== void 0) this.focus();
639
672
  }
640
- this.#markerManager?.removePopup();
673
+ this.#markerRenderer?.removePopup();
641
674
  }
642
675
  #rerender(change, newLineAnnotations, renderRange = this.#renderRange, shouldUpdateBuffer) {
643
676
  const tokenizer = this.#tokenizer;
@@ -789,13 +822,13 @@ var Editor = class {
789
822
  console.error("[diffs/editor] failed to update window selection:", err);
790
823
  }
791
824
  }
792
- #scrollToPrimaryCaret(noFocus = false) {
793
- const primaryCaretElement = this.#primaryCaretElement;
825
+ #scrollToPrimaryCaret(noFocus = false, scrollPosition = "nearest") {
794
826
  const primarySelection = this.#selections?.at(-1);
795
827
  if (primarySelection === void 0) return;
828
+ const primaryCaretElement = this.#primaryCaretElement;
796
829
  if (primaryCaretElement !== void 0) {
797
830
  primaryCaretElement.scrollIntoView({
798
- block: "nearest",
831
+ block: scrollPosition,
799
832
  inline: "nearest"
800
833
  });
801
834
  if (!noFocus) this.#focus(primarySelection.direction === DirectionBackward ? primarySelection.end : primarySelection.start);
@@ -812,7 +845,7 @@ var Editor = class {
812
845
  return `${componentTop + top}px ${end}px 0 ${start}px`;
813
846
  }
814
847
  #scrollToLine(line, char = 0, noFocus = false) {
815
- this.postponeBackgroundTokenizeToNextFrame();
848
+ this.__postponeBackgroundTokenizeToNextFrame();
816
849
  const virtualCaret = h("div", { style: {
817
850
  position: "absolute",
818
851
  left: "0",
@@ -868,11 +901,11 @@ var Editor = class {
868
901
  virtualCaret.remove();
869
902
  }
870
903
  #updateSelections(selections) {
871
- this.postponeBackgroundTokenizeToNextFrame();
904
+ this.__postponeBackgroundTokenizeToNextFrame();
872
905
  this.#primaryCaretElement = void 0;
873
906
  this.#fileInstance?.setSelectedLines(null);
874
907
  this.#gutterElement?.querySelectorAll("[data-active]").forEach((el) => el.removeAttribute("data-active"));
875
- if (selections.length === 0 && this.#matches === void 0 && this.#markerManager === void 0) {
908
+ if (selections.length === 0 && this.#matches === void 0 && this.#markerRenderer === void 0) {
876
909
  this.#selections = void 0;
877
910
  this.#overlayElements?.forEach((el) => el.remove());
878
911
  this.#overlayElements?.clear();
@@ -917,7 +950,7 @@ var Editor = class {
917
950
  this.#renderSelection(renderCtx, "match", range, isFocused ? "focus" : void 0);
918
951
  }
919
952
  }
920
- if (this.#markerManager !== void 0 && textDocument !== void 0) for (const marker of this.#markerManager.markers) this.#renderSelection(renderCtx, "marker", marker, markerSeverityDatasetKey(marker.severity));
953
+ if (this.#markerRenderer !== void 0 && textDocument !== void 0) for (const marker of this.#markerRenderer.markers) this.#renderSelection(renderCtx, "marker", marker, markerSeverityDatasetKey(marker.severity));
921
954
  this.#overlayElement?.appendChild(fragment);
922
955
  this.#overlayElements?.forEach((el) => el.remove());
923
956
  this.#overlayElements?.clear();
@@ -1097,7 +1130,7 @@ var Editor = class {
1097
1130
  const cacheKey = "selectionActionIcon-" + line + "(" + wrapLine + ")";
1098
1131
  if (renderCtx.elements.has(cacheKey)) return;
1099
1132
  const selectionActionIcon = SelectionActionWidget.renderIcon(left, this.#getLineY(line) + wrapLine * this.#metrics.lineHeight, renderCtx.fragment, () => {
1100
- const cleanUpSelectionAction = () => {
1133
+ const cleanUp = () => {
1101
1134
  this.#selectionAction?.cleanup();
1102
1135
  this.#selectionAction = void 0;
1103
1136
  };
@@ -1105,7 +1138,7 @@ var Editor = class {
1105
1138
  this.#lineYCache.clear();
1106
1139
  if (this.#selections !== void 0) this.#updateSelections(this.#selections);
1107
1140
  };
1108
- cleanUpSelectionAction();
1141
+ cleanUp();
1109
1142
  const textDocument = this.#textDocument;
1110
1143
  const renderSelectionAction = this.#options.renderSelectionAction;
1111
1144
  const fileContainer = this.#fileContainer;
@@ -1115,18 +1148,15 @@ var Editor = class {
1115
1148
  const selectionActionElement = renderSelectionAction({
1116
1149
  textDocument,
1117
1150
  selection,
1118
- applyEdits: (edits) => {
1119
- const change = textDocument.applyEdits(edits, true, this.#selections);
1120
- if (change !== void 0) this.#applyChange(change);
1121
- },
1151
+ applyEdits: (edits) => this.applyEdits(edits, true),
1122
1152
  getSelectionText: () => {
1123
1153
  return this.#textDocument?.getText(selection) ?? "";
1124
1154
  },
1125
1155
  replaceSelectionText: (text) => {
1126
- this.#replaceSelectionText(text);
1156
+ this.#replaceSelectionText(text, [selection]);
1127
1157
  },
1128
1158
  close: () => {
1129
- cleanUpSelectionAction();
1159
+ cleanUp();
1130
1160
  handleWidgetDomResize();
1131
1161
  this.#scrollToPrimaryCaret();
1132
1162
  }
@@ -1144,7 +1174,14 @@ var Editor = class {
1144
1174
  });
1145
1175
  renderCtx.elements.set(cacheKey, selectionActionIcon);
1146
1176
  }
1147
- #renderSearchPanel() {
1177
+ #openSearchPanel(mode) {
1178
+ if (this.#searchPanel !== void 0) {
1179
+ this.#searchPanel.setMode(mode);
1180
+ return;
1181
+ }
1182
+ this.#renderSearchPanel(mode);
1183
+ }
1184
+ #renderSearchPanel(mode) {
1148
1185
  this.#searchPanel?.cleanup();
1149
1186
  const textDocument = this.#textDocument;
1150
1187
  const preElement = this.#fileContainer?.shadowRoot?.querySelector("pre");
@@ -1174,15 +1211,37 @@ var Editor = class {
1174
1211
  textDocument,
1175
1212
  containerElement: preElement,
1176
1213
  defaultQuery,
1214
+ mode,
1177
1215
  initialMatch,
1178
1216
  scrollToMatch,
1179
- onUpdate: (allMatches) => {
1217
+ applyReplace: (edits) => {
1218
+ if (edits.length === 0) return;
1219
+ const change = textDocument.applyEdits(edits.map((edit) => ({
1220
+ range: {
1221
+ start: textDocument.positionAt(edit.start),
1222
+ end: textDocument.positionAt(edit.end)
1223
+ },
1224
+ newText: edit.text
1225
+ })), true, this.#selections);
1226
+ if (change !== void 0) this.#applyChange(change, void 0, this.#applyChangeToLineAnnotations(change), { skipSearchRefresh: true });
1227
+ },
1228
+ onUpdate: (allMatches, options) => {
1180
1229
  if (allMatches.length === 0) {
1181
1230
  this.#matches = void 0;
1182
1231
  this.#updateSelections(this.#selections ?? []);
1183
1232
  return;
1184
1233
  }
1185
1234
  this.#matches = allMatches;
1235
+ if (options?.syncSelection === false) {
1236
+ this.#updateSelections(this.#selections ?? []);
1237
+ const primarySelection$1 = this.#selections?.at(-1);
1238
+ if (primarySelection$1 !== void 0) {
1239
+ const startOffset = textDocument.offsetAt(primarySelection$1.start);
1240
+ const endOffset = textDocument.offsetAt(primarySelection$1.end);
1241
+ for (const match of allMatches) if (match[0] === startOffset && match[1] === endOffset) return match;
1242
+ }
1243
+ return;
1244
+ }
1186
1245
  const primarySelection = this.#selections?.at(-1);
1187
1246
  let searchOffset = 0;
1188
1247
  let nextMatch;
@@ -1210,8 +1269,7 @@ var Editor = class {
1210
1269
  if (textDocument === void 0 || selections === void 0) return "";
1211
1270
  return getSelectionText(textDocument, selections);
1212
1271
  }
1213
- #replaceSelectionText(text) {
1214
- const selections = this.#selections;
1272
+ #replaceSelectionText(text, selections = this.#selections) {
1215
1273
  if (selections === void 0) return;
1216
1274
  const textDocument = this.#textDocument;
1217
1275
  const primarySelection = selections.at(-1);
@@ -1286,18 +1344,21 @@ var Editor = class {
1286
1344
  const { nextSelections, change } = applyTextChangeToSelections(this.#textDocument, this.#selections, edit, this.#lineAnnotations, this.#metrics.tabSize);
1287
1345
  if (change !== void 0) this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change));
1288
1346
  }
1289
- #applyChange(change, selections, newLineAnnotations) {
1347
+ #getFileRef() {
1290
1348
  const fileContents = this.#fileContents;
1291
1349
  const textDocument = this.#textDocument;
1350
+ if (fileContents === void 0 || textDocument === void 0) return;
1351
+ const { contents: _,...file } = fileContents;
1352
+ Object.defineProperty(file, "contents", {
1353
+ enumerable: true,
1354
+ get: () => textDocument.getText()
1355
+ });
1356
+ return file;
1357
+ }
1358
+ #applyChange(change, selections, newLineAnnotations, options) {
1359
+ const fileRef = this.#getFileRef();
1292
1360
  const onChange = this.#options.onChange;
1293
- if (fileContents !== void 0 && textDocument !== void 0 && onChange !== void 0) {
1294
- const { contents: _,...file } = fileContents;
1295
- Object.defineProperty(file, "contents", {
1296
- enumerable: true,
1297
- get: () => textDocument.getText()
1298
- });
1299
- onChange(file, newLineAnnotations ?? this.#lineAnnotations);
1300
- }
1361
+ if (fileRef !== void 0 && onChange !== void 0) onChange(fileRef, newLineAnnotations ?? this.#lineAnnotations);
1301
1362
  if (change.lineDelta !== 0) {
1302
1363
  for (const line of this.#lineYCache.keys()) if (line >= change.startLine) this.#lineYCache.delete(line);
1303
1364
  }
@@ -1317,6 +1378,7 @@ var Editor = class {
1317
1378
  else if (primarySelection.end.line > renderRangeEndLine) shouldUpdateBuffer = true;
1318
1379
  }
1319
1380
  this.#rerender(change, newLineAnnotations, renderRange, shouldUpdateBuffer);
1381
+ if (options?.skipSearchRefresh !== true && this.#searchPanel !== void 0 && this.#matches !== void 0) this.#searchPanel.updateMatches({ syncSelection: false });
1320
1382
  if (selections !== void 0) {
1321
1383
  this.#updateSelections(selections);
1322
1384
  if (this.#primaryCaretElement !== void 0) this.#primaryCaretElement.scrollIntoView({
@@ -1391,7 +1453,7 @@ var Editor = class {
1391
1453
  if (cachedY !== void 0) return cachedY;
1392
1454
  const lineElement = this.#getLineElement(line);
1393
1455
  if (lineElement === void 0) return -1;
1394
- const y = lineElement.offsetTop + this.#codePaddingTop;
1456
+ const y = lineElement.offsetTop + this.#metrics.paddingTop;
1395
1457
  this.#lineYCache.set(line, y);
1396
1458
  return y;
1397
1459
  }