@pierre/diffs 1.3.5 → 1.4.0

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 (257) hide show
  1. package/dist/components/CodeView.d.ts +113 -68
  2. package/dist/components/CodeView.d.ts.map +1 -1
  3. package/dist/components/CodeView.js +153 -58
  4. package/dist/components/CodeView.js.map +1 -1
  5. package/dist/components/File.d.ts +75 -15
  6. package/dist/components/File.d.ts.map +1 -1
  7. package/dist/components/File.js +223 -62
  8. package/dist/components/File.js.map +1 -1
  9. package/dist/components/FileDiff.d.ts +97 -21
  10. package/dist/components/FileDiff.d.ts.map +1 -1
  11. package/dist/components/FileDiff.js +400 -100
  12. package/dist/components/FileDiff.js.map +1 -1
  13. package/dist/components/UnresolvedFile.d.ts +2 -2
  14. package/dist/components/UnresolvedFile.d.ts.map +1 -1
  15. package/dist/components/UnresolvedFile.js +2 -2
  16. package/dist/components/UnresolvedFile.js.map +1 -1
  17. package/dist/components/VirtualizedFile.d.ts +16 -7
  18. package/dist/components/VirtualizedFile.d.ts.map +1 -1
  19. package/dist/components/VirtualizedFile.js +140 -51
  20. package/dist/components/VirtualizedFile.js.map +1 -1
  21. package/dist/components/VirtualizedFileDiff.d.ts +14 -8
  22. package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
  23. package/dist/components/VirtualizedFileDiff.js +183 -93
  24. package/dist/components/VirtualizedFileDiff.js.map +1 -1
  25. package/dist/components/VirtulizerDevelopment.d.ts +1 -1
  26. package/dist/components/VirtulizerDevelopment.d.ts.map +1 -1
  27. package/dist/edit/index.d.ts +7 -4
  28. package/dist/edit/index.js +5 -1
  29. package/dist/editor/EditStateManager.d.ts +56 -0
  30. package/dist/editor/EditStateManager.d.ts.map +1 -0
  31. package/dist/editor/EditStateManager.js +135 -0
  32. package/dist/editor/EditStateManager.js.map +1 -0
  33. package/dist/editor/cloneRetainedDiffSessionSnapshot.d.ts +7 -0
  34. package/dist/editor/cloneRetainedDiffSessionSnapshot.d.ts.map +1 -0
  35. package/dist/editor/cloneRetainedDiffSessionSnapshot.js +16 -0
  36. package/dist/editor/cloneRetainedDiffSessionSnapshot.js.map +1 -0
  37. package/dist/editor/editPrediction.d.ts +76 -0
  38. package/dist/editor/editPrediction.d.ts.map +1 -0
  39. package/dist/editor/editPrediction.js +365 -0
  40. package/dist/editor/editPrediction.js.map +1 -0
  41. package/dist/editor/editStack.d.ts +19 -40
  42. package/dist/editor/editStack.d.ts.map +1 -1
  43. package/dist/editor/editStack.js +67 -4
  44. package/dist/editor/editStack.js.map +1 -1
  45. package/dist/editor/editor.d.ts +118 -29
  46. package/dist/editor/editor.d.ts.map +1 -1
  47. package/dist/editor/editor.js +1196 -403
  48. package/dist/editor/editor.js.map +1 -1
  49. package/dist/editor/editor2.js +1 -1
  50. package/dist/editor/languages.d.ts +3 -3
  51. package/dist/editor/languages.d.ts.map +1 -1
  52. package/dist/editor/languages.js.map +1 -1
  53. package/dist/editor/lineAnnotations.d.ts +3 -3
  54. package/dist/editor/lineAnnotations.d.ts.map +1 -1
  55. package/dist/editor/lineAnnotations.js +16 -7
  56. package/dist/editor/lineAnnotations.js.map +1 -1
  57. package/dist/editor/marker.d.ts +2 -2
  58. package/dist/editor/marker.d.ts.map +1 -1
  59. package/dist/editor/marker.js.map +1 -1
  60. package/dist/editor/matchBrackets.d.ts +2 -2
  61. package/dist/editor/matchBrackets.d.ts.map +1 -1
  62. package/dist/editor/matchBrackets.js.map +1 -1
  63. package/dist/editor/pieceTable.d.ts +1 -1
  64. package/dist/editor/pieceTable.js.map +1 -1
  65. package/dist/editor/searchPanel.d.ts +2 -2
  66. package/dist/editor/searchPanel.d.ts.map +1 -1
  67. package/dist/editor/searchPanel.js +3 -2
  68. package/dist/editor/searchPanel.js.map +1 -1
  69. package/dist/editor/selection.d.ts +33 -23
  70. package/dist/editor/selection.d.ts.map +1 -1
  71. package/dist/editor/selection.js +3 -3
  72. package/dist/editor/selection.js.map +1 -1
  73. package/dist/editor/selectionAction.d.ts +3 -3
  74. package/dist/editor/selectionAction.d.ts.map +1 -1
  75. package/dist/editor/selectionAction.js.map +1 -1
  76. package/dist/editor/textDocument.d.ts +11 -8
  77. package/dist/editor/textDocument.d.ts.map +1 -1
  78. package/dist/editor/textDocument.js +28 -5
  79. package/dist/editor/textDocument.js.map +1 -1
  80. package/dist/editor/textDocumentChangeTransaction.d.ts +15 -0
  81. package/dist/editor/textDocumentChangeTransaction.d.ts.map +1 -0
  82. package/dist/editor/textDocumentChangeTransaction.js +12 -0
  83. package/dist/editor/textDocumentChangeTransaction.js.map +1 -0
  84. package/dist/editor/textMeasure.d.ts +5 -0
  85. package/dist/editor/textMeasure.d.ts.map +1 -1
  86. package/dist/editor/textMeasure.js +10 -0
  87. package/dist/editor/textMeasure.js.map +1 -1
  88. package/dist/editor/tokenizer.d.ts +1 -1
  89. package/dist/editor/tokenizer.d.ts.map +1 -1
  90. package/dist/editor/tokenizer.js +6 -3
  91. package/dist/editor/tokenizer.js.map +1 -1
  92. package/dist/editor/types.d.ts +267 -0
  93. package/dist/editor/types.d.ts.map +1 -0
  94. package/dist/editor/types.js +0 -0
  95. package/dist/editor/utils.d.ts +2 -1
  96. package/dist/editor/utils.d.ts.map +1 -1
  97. package/dist/editor/utils.js +4 -1
  98. package/dist/editor/utils.js.map +1 -1
  99. package/dist/index.d.ts +8 -6
  100. package/dist/index.js +9 -8
  101. package/dist/managers/InteractionManager.d.ts.map +1 -1
  102. package/dist/managers/InteractionManager.js +7 -0
  103. package/dist/managers/InteractionManager.js.map +1 -1
  104. package/dist/react/CodeView.d.ts +36 -25
  105. package/dist/react/CodeView.d.ts.map +1 -1
  106. package/dist/react/CodeView.js +15 -14
  107. package/dist/react/CodeView.js.map +1 -1
  108. package/dist/react/EditContext.d.ts +8 -11
  109. package/dist/react/EditContext.d.ts.map +1 -1
  110. package/dist/react/EditContext.js +2 -9
  111. package/dist/react/EditContext.js.map +1 -1
  112. package/dist/react/File.d.ts +10 -6
  113. package/dist/react/File.d.ts.map +1 -1
  114. package/dist/react/File.js +8 -4
  115. package/dist/react/File.js.map +1 -1
  116. package/dist/react/FileDiff.d.ts +10 -5
  117. package/dist/react/FileDiff.d.ts.map +1 -1
  118. package/dist/react/FileDiff.js +8 -4
  119. package/dist/react/FileDiff.js.map +1 -1
  120. package/dist/react/MultiFileDiff.d.ts +8 -5
  121. package/dist/react/MultiFileDiff.d.ts.map +1 -1
  122. package/dist/react/MultiFileDiff.js +8 -12
  123. package/dist/react/MultiFileDiff.js.map +1 -1
  124. package/dist/react/PatchDiff.d.ts +7 -4
  125. package/dist/react/PatchDiff.d.ts.map +1 -1
  126. package/dist/react/PatchDiff.js +8 -4
  127. package/dist/react/PatchDiff.js.map +1 -1
  128. package/dist/react/UnresolvedFile.d.ts +3 -3
  129. package/dist/react/UnresolvedFile.d.ts.map +1 -1
  130. package/dist/react/UnresolvedFile.js.map +1 -1
  131. package/dist/react/index.d.ts +8 -5
  132. package/dist/react/types.d.ts +46 -9
  133. package/dist/react/types.d.ts.map +1 -1
  134. package/dist/react/utils/renderDiffChildren.d.ts +14 -12
  135. package/dist/react/utils/renderDiffChildren.d.ts.map +1 -1
  136. package/dist/react/utils/renderDiffChildren.js +2 -2
  137. package/dist/react/utils/renderDiffChildren.js.map +1 -1
  138. package/dist/react/utils/renderFileChildren.d.ts +14 -12
  139. package/dist/react/utils/renderFileChildren.d.ts.map +1 -1
  140. package/dist/react/utils/renderFileChildren.js +2 -2
  141. package/dist/react/utils/renderFileChildren.js.map +1 -1
  142. package/dist/react/utils/useFileDiffInstance.d.ts +22 -10
  143. package/dist/react/utils/useFileDiffInstance.d.ts.map +1 -1
  144. package/dist/react/utils/useFileDiffInstance.js +112 -22
  145. package/dist/react/utils/useFileDiffInstance.js.map +1 -1
  146. package/dist/react/utils/useFileInstance.d.ts +15 -9
  147. package/dist/react/utils/useFileInstance.d.ts.map +1 -1
  148. package/dist/react/utils/useFileInstance.js +84 -25
  149. package/dist/react/utils/useFileInstance.js.map +1 -1
  150. package/dist/renderers/DiffHunksRenderer.d.ts +30 -15
  151. package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
  152. package/dist/renderers/DiffHunksRenderer.js +172 -108
  153. package/dist/renderers/DiffHunksRenderer.js.map +1 -1
  154. package/dist/renderers/FileRenderer.d.ts +27 -11
  155. package/dist/renderers/FileRenderer.d.ts.map +1 -1
  156. package/dist/renderers/FileRenderer.js +138 -64
  157. package/dist/renderers/FileRenderer.js.map +1 -1
  158. package/dist/renderers/UnresolvedFileHunksRenderer.d.ts +2 -2
  159. package/dist/renderers/UnresolvedFileHunksRenderer.d.ts.map +1 -1
  160. package/dist/renderers/UnresolvedFileHunksRenderer.js +2 -2
  161. package/dist/renderers/UnresolvedFileHunksRenderer.js.map +1 -1
  162. package/dist/shiki-stream/tokenizer.d.ts.map +1 -1
  163. package/dist/shiki-stream/tokenizer.js +2 -1
  164. package/dist/shiki-stream/tokenizer.js.map +1 -1
  165. package/dist/ssr/index.d.ts +2 -2
  166. package/dist/ssr/preloadDiffs.d.ts +21 -21
  167. package/dist/ssr/preloadDiffs.d.ts.map +1 -1
  168. package/dist/ssr/preloadDiffs.js +1 -1
  169. package/dist/ssr/preloadDiffs.js.map +1 -1
  170. package/dist/ssr/preloadFile.d.ts +6 -6
  171. package/dist/ssr/preloadFile.d.ts.map +1 -1
  172. package/dist/ssr/preloadFile.js.map +1 -1
  173. package/dist/ssr/preloadPatchFile.d.ts +4 -4
  174. package/dist/ssr/preloadPatchFile.d.ts.map +1 -1
  175. package/dist/ssr/preloadPatchFile.js.map +1 -1
  176. package/dist/style.js +1 -1
  177. package/dist/types.d.ts +13 -263
  178. package/dist/types.d.ts.map +1 -1
  179. package/dist/utils/appendItems.d.ts +5 -0
  180. package/dist/utils/appendItems.d.ts.map +1 -0
  181. package/dist/utils/appendItems.js +8 -0
  182. package/dist/utils/appendItems.js.map +1 -0
  183. package/dist/utils/areDiffTargetsEqual.js +1 -1
  184. package/dist/utils/areDiffTargetsEqual.js.map +1 -1
  185. package/dist/utils/areFileTargetsEqual.d.ts +7 -0
  186. package/dist/utils/areFileTargetsEqual.d.ts.map +1 -0
  187. package/dist/utils/areFileTargetsEqual.js +8 -0
  188. package/dist/utils/areFileTargetsEqual.js.map +1 -0
  189. package/dist/utils/areManagedSnapshotsEqual.d.ts +1 -1
  190. package/dist/utils/areManagedSnapshotsEqual.d.ts.map +1 -1
  191. package/dist/utils/areManagedSnapshotsEqual.js.map +1 -1
  192. package/dist/utils/areOptionsEqual.d.ts +3 -3
  193. package/dist/utils/areOptionsEqual.d.ts.map +1 -1
  194. package/dist/utils/areOptionsEqual.js.map +1 -1
  195. package/dist/utils/cloneFileDiffMetadata.d.ts +3 -2
  196. package/dist/utils/cloneFileDiffMetadata.d.ts.map +1 -1
  197. package/dist/utils/cloneFileDiffMetadata.js +8 -5
  198. package/dist/utils/cloneFileDiffMetadata.js.map +1 -1
  199. package/dist/utils/composeCacheKey.d.ts +6 -0
  200. package/dist/utils/composeCacheKey.d.ts.map +1 -0
  201. package/dist/utils/composeCacheKey.js +10 -0
  202. package/dist/utils/composeCacheKey.js.map +1 -0
  203. package/dist/utils/editSessionAnnotations.d.ts +69 -0
  204. package/dist/utils/editSessionAnnotations.d.ts.map +1 -0
  205. package/dist/utils/editSessionAnnotations.js +64 -0
  206. package/dist/utils/editSessionAnnotations.js.map +1 -0
  207. package/dist/utils/editSessionHunks.d.ts +5 -3
  208. package/dist/utils/editSessionHunks.d.ts.map +1 -1
  209. package/dist/utils/editSessionHunks.js +7 -5
  210. package/dist/utils/editSessionHunks.js.map +1 -1
  211. package/dist/utils/getDiffHunksRendererOptions.d.ts +1 -1
  212. package/dist/utils/getDiffHunksRendererOptions.d.ts.map +1 -1
  213. package/dist/utils/getDiffHunksRendererOptions.js.map +1 -1
  214. package/dist/utils/getFileRendererOptions.d.ts +1 -1
  215. package/dist/utils/getFileRendererOptions.d.ts.map +1 -1
  216. package/dist/utils/getFileRendererOptions.js.map +1 -1
  217. package/dist/utils/hydratePartialDiff.d.ts.map +1 -1
  218. package/dist/utils/hydratePartialDiff.js +2 -1
  219. package/dist/utils/hydratePartialDiff.js.map +1 -1
  220. package/dist/utils/lineAnnotationIdentity.d.ts +16 -0
  221. package/dist/utils/lineAnnotationIdentity.d.ts.map +1 -0
  222. package/dist/utils/lineAnnotationIdentity.js +17 -0
  223. package/dist/utils/lineAnnotationIdentity.js.map +1 -0
  224. package/dist/utils/parseDiffFromFile.d.ts +2 -2
  225. package/dist/utils/parseDiffFromFile.d.ts.map +1 -1
  226. package/dist/utils/parseDiffFromFile.js +6 -6
  227. package/dist/utils/parseDiffFromFile.js.map +1 -1
  228. package/dist/utils/parsePatchFiles.d.ts +6 -3
  229. package/dist/utils/parsePatchFiles.d.ts.map +1 -1
  230. package/dist/utils/parsePatchFiles.js +34 -9
  231. package/dist/utils/parsePatchFiles.js.map +1 -1
  232. package/dist/utils/renderDiffWithHighlighter.d.ts.map +1 -1
  233. package/dist/utils/renderDiffWithHighlighter.js +6 -5
  234. package/dist/utils/renderDiffWithHighlighter.js.map +1 -1
  235. package/dist/utils/renderFileWithHighlighter.d.ts.map +1 -1
  236. package/dist/utils/renderFileWithHighlighter.js +7 -4
  237. package/dist/utils/renderFileWithHighlighter.js.map +1 -1
  238. package/dist/utils/trimPatchContext.d.ts.map +1 -1
  239. package/dist/utils/trimPatchContext.js +3 -2
  240. package/dist/utils/trimPatchContext.js.map +1 -1
  241. package/dist/utils/updateDiffHunks.js +1 -1
  242. package/dist/utils/virtualDiffLayout.js +1 -1
  243. package/dist/worker/WorkerPoolManager.d.ts +4 -3
  244. package/dist/worker/WorkerPoolManager.d.ts.map +1 -1
  245. package/dist/worker/WorkerPoolManager.js +33 -4
  246. package/dist/worker/WorkerPoolManager.js.map +1 -1
  247. package/dist/worker/types.d.ts +5 -0
  248. package/dist/worker/types.d.ts.map +1 -1
  249. package/dist/worker/worker-portable.js +14 -6
  250. package/dist/worker/worker-portable.js.map +1 -1
  251. package/dist/worker/worker.js +14 -6
  252. package/dist/worker/worker.js.map +1 -1
  253. package/package.json +9 -1
  254. package/dist/editor/stateStorage.d.ts +0 -15
  255. package/dist/editor/stateStorage.d.ts.map +0 -1
  256. package/dist/editor/stateStorage.js +0 -65
  257. package/dist/editor/stateStorage.js.map +0 -1
@@ -1,23 +1,27 @@
1
1
  import { dequeueRender, queueRender } from "../managers/UniversalRenderingManager.js";
2
2
  import { isGutterUtilityPath } from "../utils/isGutterUtilityPath.js";
3
+ import { computeLineOffsets } from "../utils/computeFileOffsets.js";
3
4
  import { getFiletypeFromFileName } from "../utils/getFiletypeFromFileName.js";
4
5
  import { isSafari } from "../utils/platform.js";
6
+ import { EditStack } from "./editStack.js";
7
+ import { getTextDocumentChangeTransaction } from "./textDocumentChangeTransaction.js";
8
+ import { TextDocument } from "./textDocument.js";
9
+ import { EditStateManager, cloneEditorViewState, toManagedEditState } from "./EditStateManager.js";
5
10
  import { isMoveCursorShortcut, isPrimaryModifier } from "./platform.js";
6
11
  import { resolveEditorCommandFromKeyboardEvent, resolveFindAgainShortcut } from "./command.js";
7
- import { EditStack } from "./editStack.js";
12
+ import { cloneRetainedDiffSessionSnapshot } from "./cloneRetainedDiffSessionSnapshot.js";
8
13
  import editor_default from "./editor2.js";
14
+ import { buildEditPredictionRequest, matchesEditPredictionPattern, recordEditPrediction } from "./editPrediction.js";
9
15
  import { resolveBlockCommentEdits, resolveCommentConfig, resolveLineCommentEdits } from "./languages.js";
10
16
  import { addEventListener, clampDomOffset, extend, getLineNumberAttr, h, lookupScrollContainer, round } from "./utils.js";
11
17
  import { applyDocumentChangeToLineAnnotations, renderLineAnnotations } from "./lineAnnotations.js";
12
18
  import { PopoverManager } from "./popover.js";
13
- import { applyDeleteCharacterToSelections, applyDeleteHardLineForwardToSelections, applyDeleteSoftLineBackwardToSelections, applyDeleteWordBackwardToSelections, applyTextChangeToSelections, applyTextReplaceToSelections, applyTransposeToSelections, comparePosition, convertSelection, createSelectionFrom, createSelectionFromAnchorAndFocusOffsets, expandCollapsedSelectionToWord, extendSelection, extendSelections, findNextMatch, getAutoSurroundReplacementTexts, getCaretPosition, getDocumentBoundarySelection, getDocumentFullSelection, getSelectedLineBlocks, getSelectionAnchor, getSelectionClipboardTexts, getSelectionText, isCollapsedSelection, isLineEditable, mapCursorMove, mapSelectionShift, mergeOverlappingSelections, remapSelectionsAfterEdits, resolveIndentEdits, resolveSelectionCut, selectionIntersects, shiftSelectionLines } from "./selection.js";
19
+ import { applyDeleteCharacterToSelections, applyDeleteHardLineForwardToSelections, applyDeleteSoftLineBackwardToSelections, applyDeleteWordBackwardToSelections, applyTextChangeToSelections, applyTextReplaceToSelections, applyTransposeToSelections, comparePosition, convertSelection, createSelectionFrom, createSelectionFromAnchorAndFocusOffsets, expandCollapsedSelectionToWord, extendSelection, extendSelections, findNextMatch, getAutoSurroundReplacementTexts, getCaretPosition, getDocumentBoundarySelection, getDocumentFullSelection, getSelectedLineBlocks, getSelectionAnchor, getSelectionClipboardTexts, getSelectionText, isCollapsedSelection, isLineEditable, mapCursorMove, mapSelectionShift, mergeOverlappingSelections, remapOffsetThroughEdits, remapSelectionsAfterEdits, resolveIndentEdits, resolveSelectionCut, selectionIntersects, shiftSelectionLines, snapCharacterToGraphemeBoundary } from "./selection.js";
14
20
  import { MarkerRenderer, markerSeverityDatasetKey } from "./marker.js";
15
21
  import { findBracketMatchRanges } from "./matchBrackets.js";
16
22
  import { createSpriteElement } from "./sprite.js";
17
23
  import { SearchPanelWidget } from "./searchPanel.js";
18
24
  import { SelectionActionWidget } from "./selectionAction.js";
19
- import { cloneEditorState, createStateStorage } from "./stateStorage.js";
20
- import { TextDocument } from "./textDocument.js";
21
25
  import { Metrics, getExpandedAsciiTextColumns, getUnicodeMeasurementOffsets, snapTextOffsetToUnicodeBoundary } from "./textMeasure.js";
22
26
  import { EditorTokenizer, renderLineTokens } from "./tokenizer.js";
23
27
  //#region src/editor/editor.ts
@@ -33,29 +37,45 @@ function getShadowRootRange(shadowRoot) {
33
37
  endOffset: range.endOffset
34
38
  };
35
39
  }
36
- function requirePersistedCacheKey(file) {
37
- if (typeof file.cacheKey !== "string" || file.cacheKey.length === 0) throw new Error(`Editor persistState requires a non-empty file.cacheKey for "${file.name}". Provide a unique, stable cacheKey for every editable file.`);
38
- return file.cacheKey;
40
+ /** Describe replacing the complete document from its previous text. */
41
+ function createFullDocumentChange(previousContents, contents) {
42
+ const lineOffsets = computeLineOffsets(previousContents);
43
+ const lastLineOffset = lineOffsets[lineOffsets.length - 1] ?? 0;
44
+ return {
45
+ start: 0,
46
+ end: previousContents.length,
47
+ text: contents,
48
+ range: {
49
+ start: {
50
+ line: 0,
51
+ character: 0
52
+ },
53
+ end: {
54
+ line: lineOffsets.length - 1,
55
+ character: previousContents.length - lastLineOffset
56
+ }
57
+ }
58
+ };
39
59
  }
40
- function isPromise(value) {
41
- return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
60
+ function isVirtualizedEditorComponent(instance) {
61
+ return instance != null && "renderType" in instance && instance.renderType === "virtualized";
42
62
  }
43
63
  const MAX_EDIT_WIDEN_WINDOW_MULTIPLE = 2;
44
64
  const MAX_WRAP_OFFSETS_CACHE_LINES = 1e4;
65
+ const EDIT_PREDICTION_DEBOUNCE_MS = 300;
66
+ const MAX_EDIT_PREDICTION_RESPONSE_EDITS = 256;
67
+ const MAX_EDIT_PREDICTION_RESPONSE_BYTES = 128 * 1024;
68
+ const editPredictionTextEncoder = new TextEncoder();
45
69
  const SELECTION_ACTION_POPOVER_PLACEMENT_KEY = "selection-action";
46
70
  const MULTI_SELECTION_CLIPBOARD_TYPE = "application/vnd.pierre.diffs-selections+json";
47
71
  var Editor = class {
72
+ type;
48
73
  #options;
74
+ #initialState;
75
+ #editSession;
49
76
  #metrics = new Metrics();
50
77
  #tokenizer;
51
78
  #popoverManager;
52
- #textDocumentCache = /* @__PURE__ */ new Map();
53
- #stateStorage;
54
- #stateStorageOption;
55
- #pendingStateWrites = /* @__PURE__ */ new Map();
56
- #pendingStateRestore;
57
- #stateRestoreGeneration = 0;
58
- #restoreStateOnNextSync = false;
59
79
  #editorEventDisposes;
60
80
  #globalEventDisposes;
61
81
  #selectEventDisposes;
@@ -79,17 +99,25 @@ var Editor = class {
79
99
  #fileContainer;
80
100
  #gutterElement;
81
101
  #contentElement;
102
+ #deletionsColumn;
82
103
  #overlayElement;
83
104
  #overlayElements;
105
+ #caretElements;
106
+ #caretHighlightElements;
84
107
  #primaryCaretElement;
85
108
  #resizeObserver;
86
109
  #fileInstance;
87
- #fileInfo;
110
+ #applyDocumentChange;
111
+ #publishChange;
112
+ #isRendering = false;
113
+ #restoreEditorStateOnSync = false;
88
114
  #lineAnnotations;
89
- #textDocument;
115
+ #editStateKey;
116
+ #ownsVerticalViewport;
90
117
  #renderRange;
91
118
  #viewportWindowLines;
92
119
  #markerRenderer;
120
+ #carets;
93
121
  #searchPanel;
94
122
  #selectionAction;
95
123
  #canMountSelectionAction = true;
@@ -100,6 +128,7 @@ var Editor = class {
100
128
  #isComposing = false;
101
129
  #isGutterMouseDown = false;
102
130
  #isContentMouseDown = false;
131
+ #altColumnDrag;
103
132
  #shiftKeyPressed = false;
104
133
  #selectionStart;
105
134
  #deletedSelectionText = "";
@@ -114,53 +143,166 @@ var Editor = class {
114
143
  #retainSearchPanelFocus = false;
115
144
  #fontRemeasureScheduled = false;
116
145
  #themeSelectionRefreshFrame;
146
+ #editPredictionTimer;
147
+ #editPredictionAbortController;
148
+ #editPredictionGeneration = 0;
149
+ #editPredictionRevealed = false;
150
+ #retryEditPredictionOnRender = false;
151
+ #editPrediction;
152
+ #editPredictionHistory = [];
153
+ #editPredictionSpacers = /* @__PURE__ */ new Map();
154
+ #ghostTextRows = /* @__PURE__ */ new Map();
117
155
  #onDeferTokenize = (lines, themeType) => {
156
+ if (!this.#isRendering) return;
118
157
  this.#fileInstance?.updateRenderCache(lines, themeType);
119
158
  if (this.#renderRange !== void 0 && this.#renderRange.totalLines !== Infinity) {
159
+ const predictionLines = this.#editPrediction === void 0 ? void 0 : new Set(this.#editPrediction.response.edits.map((edit) => edit.range.start.line));
160
+ let refreshPrediction = false;
120
161
  const { startingLine, totalLines } = this.#renderRange;
121
- const endLine = Math.min(startingLine + totalLines, this.#textDocument?.lineCount ?? 0);
162
+ const endLine = Math.min(startingLine + totalLines, (this.#editSession ?? this.#initialState)?.document?.lineCount ?? 0);
122
163
  for (const [line, tokens] of lines) if (line >= startingLine && line < endLine) {
123
164
  const lineElement = this.#getLineElement(line);
124
- if (lineElement !== void 0) lineElement.replaceChildren(...renderLineTokens(tokens));
165
+ if (lineElement !== void 0) {
166
+ lineElement.replaceChildren(...renderLineTokens(tokens));
167
+ refreshPrediction ||= predictionLines?.has(line) === true;
168
+ }
125
169
  }
170
+ if (refreshPrediction && this.#selections !== void 0) this.#updateSelections(this.#selections);
126
171
  }
127
172
  };
128
- constructor(options = {}) {
173
+ /**
174
+ * @param type The component surface this editor can attach to.
175
+ * @param options Configure editor behavior and lifecycle callbacks.
176
+ * @param editStateKey Retain this editable draft and its undo/redo history
177
+ * in memory so a later editor using the same type and key can resume them.
178
+ */
179
+ constructor(type, options = {}, editStateKey) {
180
+ this.type = type;
129
181
  this.#options = options;
182
+ this.#ownsVerticalViewport = options.ownsVerticalViewport === true;
183
+ this.#initialState = options.initialState;
184
+ this.#editStateKey = editStateKey;
185
+ }
186
+ get #getTypedEditor() {
187
+ return this.type === "file" ? this : this;
130
188
  }
131
189
  setOptions(options) {
132
- const previousStorageOption = this.#options.persistStateStorage ?? "inMemory";
133
- const nextOptions = {
190
+ const previousRenderCaret = this.#options.renderCaret;
191
+ const previousEditPrediction = this.#options.editPrediction;
192
+ this.#options = {
134
193
  ...this.#options,
135
194
  ...options
136
195
  };
137
- if (nextOptions.persistState === true && this.#fileInstance?.type === "file") {
138
- const file = this.#fileInstance.__getCurrentFile?.() ?? this.#fileInfo;
139
- if (file !== void 0) requirePersistedCacheKey(file);
196
+ if (previousRenderCaret !== this.#options.renderCaret) {
197
+ this.#caretElements?.forEach((element) => element.remove());
198
+ this.#caretElements = void 0;
199
+ this.#renderCarets();
140
200
  }
141
- this.#options = nextOptions;
142
- if (this.#options.persistState !== true) {
143
- this.#textDocumentCache.clear();
144
- this.#stateRestoreGeneration++;
145
- this.#restoreStateOnNextSync = false;
146
- }
147
- if ((this.#options.persistStateStorage ?? "inMemory") !== previousStorageOption) {
148
- this.#stateRestoreGeneration++;
149
- this.#stateStorage = void 0;
150
- this.#stateStorageOption = void 0;
151
- this.#pendingStateWrites.clear();
201
+ if (previousEditPrediction !== this.#options.editPrediction) {
202
+ this.#cancelEditPrediction(true);
203
+ this.#editPredictionHistory = [];
204
+ this.#scheduleEditPrediction();
152
205
  }
153
206
  }
207
+ __emitEditComplete(event) {
208
+ this.#options.onComplete?.(event);
209
+ }
210
+ /** @internal */
211
+ __getGhostTextRows() {
212
+ return this.#ghostTextRows;
213
+ }
214
+ setCarets(carets) {
215
+ const textDocument = this.#editSession?.document;
216
+ if (textDocument === void 0) return;
217
+ this.#caretElements?.forEach((element) => element.remove());
218
+ this.#caretElements = void 0;
219
+ this.#caretHighlightElements?.forEach((element) => element.remove());
220
+ this.#caretHighlightElements = void 0;
221
+ this.#carets = carets.length === 0 ? void 0 : carets.map((caret) => {
222
+ const anchor = textDocument.normalizePosition(caret.anchor);
223
+ const focus = textDocument.normalizePosition(caret.focus);
224
+ return {
225
+ caret: {
226
+ ...caret,
227
+ anchor,
228
+ focus
229
+ },
230
+ anchorOffset: textDocument.offsetAt(anchor),
231
+ focusOffset: textDocument.offsetAt(focus)
232
+ };
233
+ });
234
+ this.#renderCarets();
235
+ }
154
236
  edit(fileInstance) {
155
- if (this.#options.persistState === true && fileInstance.type === "file") {
156
- const file = fileInstance.__getCurrentFile?.();
157
- if (file !== void 0) requirePersistedCacheKey(file);
158
- }
159
- this.#invalidateOnAttach();
237
+ const editor = this.#getTypedEditor;
238
+ const previousSession = this.#editSession;
239
+ if (this.#isRendering) throw new Error("Editor.edit: the editor is already rendering its component");
240
+ if (this.#fileInstance != null && this.#fileInstance !== fileInstance) throw new Error("Editor.edit: a recycled edit session cannot attach to a different component");
241
+ const initialState = this.#initialState;
242
+ const initialEditorState = initialState?.editor;
243
+ const editStateKey = this.#editStateKey;
244
+ const editSession = getEditSession({
245
+ type: this.type,
246
+ editStateKey,
247
+ owner: this,
248
+ initialState,
249
+ previousSession
250
+ });
251
+ const previousDiffSession = editSession.diffSession;
252
+ this.#initialState = void 0;
253
+ this.#editSession = editSession;
160
254
  this.#fileInstance = fileInstance;
161
- this.#initialize();
162
- this.#detach = fileInstance.attachEditor(this);
163
- return () => this.cleanUp();
255
+ this.#isRendering = true;
256
+ this.#restoreEditorStateOnSync = editSession.editor != null;
257
+ try {
258
+ this.#initialize();
259
+ if (fileInstance.type === "file" && editor.type === "file") {
260
+ editor.#applyDocumentChange = fileInstance.applyDocumentChange.bind(fileInstance);
261
+ editor.#publishChange = (changes, file, lineAnnotations) => {
262
+ const event = {
263
+ changes,
264
+ file,
265
+ editor,
266
+ lineAnnotations
267
+ };
268
+ editor.#options.onChange?.(event);
269
+ fileInstance.emitEditChange(event);
270
+ };
271
+ if (this.#detach == null) this.#detach = fileInstance.__attachEditor(editor);
272
+ else fileInstance.__resumeEditor(editor);
273
+ } else if (fileInstance.type === "file-diff" && editor.type === "file-diff") {
274
+ editor.#applyDocumentChange = fileInstance.applyDocumentChange.bind(fileInstance);
275
+ editor.#publishChange = (changes, file, lineAnnotations) => {
276
+ const event = {
277
+ changes,
278
+ file,
279
+ editor,
280
+ lineAnnotations
281
+ };
282
+ editor.#options.onChange?.(event);
283
+ fileInstance.emitEditChange(event);
284
+ };
285
+ if (this.#detach == null) this.#detach = fileInstance.__attachEditor(editor);
286
+ else fileInstance.__resumeEditor(editor);
287
+ } else throw new Error("Editor.edit: Impossible edit state");
288
+ } catch (error) {
289
+ this.cleanUp("recycle");
290
+ if (editSession.type === "file-diff" && editSession.diffSession == null) editSession.diffSession = previousDiffSession;
291
+ if (this.#detach == null && editStateKey != null) if (editor.type === "file") EditStateManager.releaseFile(editStateKey, editor);
292
+ else EditStateManager.releaseFileDiff(editStateKey, editor);
293
+ this.#editSession = previousSession;
294
+ if (this.#detach == null) {
295
+ this.#fileInstance = void 0;
296
+ this.#applyDocumentChange = void 0;
297
+ this.#publishChange = void 0;
298
+ }
299
+ if (initialState != null && initialEditorState != null) initialState.editor = initialEditorState;
300
+ this.#initialState = initialState;
301
+ throw error;
302
+ }
303
+ return () => {
304
+ this.cleanUp("complete");
305
+ };
164
306
  }
165
307
  /**
166
308
  * Apply edits to current attached file. Every edit joins the undo timeline:
@@ -173,8 +315,8 @@ var Editor = class {
173
315
  * are remapped during replay and the text edit still joins the undo timeline.
174
316
  */
175
317
  applyEdits(edits, updateHistory = true) {
176
- const textDocument = this.#textDocument;
177
- if (textDocument == null) throw new Error("Editor is not attached");
318
+ const textDocument = this.#editSession?.document;
319
+ if (textDocument == null) throw new Error("Editor.applyEdits: Editor is not attached");
178
320
  const wasFocused = this.#contentHasFocus;
179
321
  const selectionsBefore = this.#selections;
180
322
  const selectionOffsetsBefore = selectionsBefore?.map((selection) => [textDocument.offsetAt(selection.start), textDocument.offsetAt(selection.end)]);
@@ -190,11 +332,11 @@ var Editor = class {
190
332
  }
191
333
  /** Whether there is an edit to undo. */
192
334
  get canUndo() {
193
- return this.#textDocument?.canUndo ?? false;
335
+ return (this.#editSession ?? this.#initialState)?.document?.canUndo ?? false;
194
336
  }
195
337
  /** Whether there is an undone edit to redo. */
196
338
  get canRedo() {
197
- return this.#textDocument?.canRedo ?? false;
339
+ return (this.#editSession ?? this.#initialState)?.document?.canRedo ?? false;
198
340
  }
199
341
  /** Undo the last edit. Does nothing when there is nothing to undo. */
200
342
  undo() {
@@ -205,8 +347,9 @@ var Editor = class {
205
347
  this.#runCommand("redo");
206
348
  }
207
349
  getFile() {
208
- const fileInfo = this.#fileInfo;
209
- const textDocument = this.#textDocument;
350
+ const editSession = this.#editSession ?? this.#initialState;
351
+ const fileInfo = editSession?.fileInfo;
352
+ const textDocument = editSession?.document;
210
353
  if (fileInfo === void 0 || textDocument === void 0) return;
211
354
  const file = { ...fileInfo };
212
355
  Object.defineProperty(file, "contents", {
@@ -216,32 +359,56 @@ var Editor = class {
216
359
  return file;
217
360
  }
218
361
  getText() {
219
- return this.#textDocument?.getText() ?? "";
220
- }
221
- getState() {
222
- const fileInstance = this.#fileInstance;
362
+ return (this.#editSession ?? this.#initialState)?.document?.getText() ?? "";
363
+ }
364
+ /** Return an isolated copy of selections and restorable view state. */
365
+ getViewState() {
366
+ const fileInstance = this.#isRendering ? this.#fileInstance : void 0;
367
+ if (!this.#isRendering && this.#editSession?.editor != null) return cloneEditorViewState(this.#editSession.editor);
368
+ const selections = this.#selections?.map((selection) => ({
369
+ ...selection,
370
+ start: { ...selection.start },
371
+ end: { ...selection.end }
372
+ }));
373
+ if (fileInstance == null) return { selections };
374
+ const viewport = this.#getOwnedVerticalViewport();
375
+ const scrollLeft = fileInstance.getCodeScrollLeft();
376
+ if (viewport == null) return {
377
+ selections,
378
+ view: { scrollLeft }
379
+ };
223
380
  return {
224
- selections: this.#selections,
225
- view: fileInstance != null ? {
226
- scrollLeft: fileInstance.getCodeScrollLeft(),
227
- scrollTop: this.#getViewportScrollTop()
228
- } : void 0
381
+ selections,
382
+ view: {
383
+ scrollLeft,
384
+ scrollTop: viewport.scrollTop
385
+ }
229
386
  };
230
387
  }
231
- setState({ selections, view }) {
232
- if (this.#fileInstance === void 0 || this.#textDocument === void 0) throw new Error("Editor is not attached");
388
+ /**
389
+ * Return the objects that make up the active edit session, or undefined when
390
+ * no complete session exists.
391
+ */
392
+ getEditState() {
393
+ const editSession = this.#editSession;
394
+ if (editSession == null) return;
395
+ if (this.#fileInstance == null) throw new Error(`Editor.getEditState: active ${this.type} session has no component`);
396
+ return toManagedEditState(editSession);
397
+ }
398
+ setViewState({ selections, view }) {
399
+ if (!this.#isRendering || this.#fileInstance === void 0 || this.#editSession?.document === void 0) throw new Error("Editor.setViewState: Editor is not attached");
233
400
  this.#canMountSelectionAction = false;
234
401
  this.#updateSelections(selections ?? []);
235
402
  if (view != null) {
236
403
  this.#fileInstance.setCodeScrollLeft(view.scrollLeft);
237
- if (view.scrollTop !== void 0) this.#setViewportScrollTop(view.scrollTop);
238
- return;
239
- }
240
- this.#scrollToPrimaryCaret();
404
+ const viewport = this.#getOwnedVerticalViewport();
405
+ if (view.scrollTop !== void 0 && viewport != null) viewport.scrollTop = view.scrollTop;
406
+ } else if (this.#ownsVerticalViewport) this.#scrollToPrimaryCaret();
407
+ this.#checkpointEditSessionState();
241
408
  }
242
409
  setSelections(selections) {
243
- const textDocument = this.#textDocument;
244
- if (textDocument === void 0) throw new Error("Text document is not initialized");
410
+ const textDocument = this.#editSession?.document;
411
+ if (textDocument === void 0) throw new Error("Editor.setSelections: Text document is not initialized");
245
412
  const resolvedSelections = selections.map((selection) => {
246
413
  let start = textDocument.normalizePosition(selection.start);
247
414
  let end = textDocument.normalizePosition(selection.end);
@@ -263,8 +430,8 @@ var Editor = class {
263
430
  this.#scrollToPrimaryCaret();
264
431
  }
265
432
  setMarkers(markers) {
266
- const textDocument = this.#textDocument;
267
- if (textDocument === void 0) throw new Error("Text document is not initialized");
433
+ const textDocument = this.#editSession?.document;
434
+ if (textDocument === void 0) throw new Error("Editor.setMarkers: Text document is not initialized");
268
435
  if (markers.length === 0) {
269
436
  this.#markerRenderer?.cleanup();
270
437
  this.#markerRenderer = void 0;
@@ -283,13 +450,14 @@ var Editor = class {
283
450
  this.#markerRenderer.setMarkers(markers, textDocument);
284
451
  if (this.#contentElement !== void 0) this.#markerRenderer.listenHover(this.#contentElement);
285
452
  this.#updateSelections(this.#selections ?? []);
453
+ this.#renderCarets();
286
454
  }
287
455
  focus(options) {
288
456
  const preventScroll = options?.preventScroll ?? false;
289
457
  const lineNumber = options?.lineNumber;
290
458
  if (lineNumber === "first-visible" || typeof lineNumber === "number") {
291
- const textDocument = this.#textDocument;
292
- if (textDocument == null || this.#fileInstance == null) return;
459
+ const textDocument = this.#editSession?.document;
460
+ if (textDocument == null || !this.#isRendering || this.#fileInstance == null) return;
293
461
  const targetLineNumber = lineNumber === "first-visible" ? this.#getFirstVisibleLineNumber(options?.offset) : lineNumber;
294
462
  if (targetLineNumber == null) return;
295
463
  const position = textDocument.normalizePosition({
@@ -311,29 +479,30 @@ var Editor = class {
311
479
  this.#shouldIgnoreSelectionChange = false;
312
480
  this.#contentElement?.blur();
313
481
  }
314
- cleanUp(recycle = false) {
482
+ cleanUp(reason = "discard") {
483
+ const fileInstance = this.#fileInstance;
484
+ const recycle = reason === "recycle";
485
+ this.#cancelEditPrediction(true, false);
486
+ if (!recycle) this.#editPredictionHistory = [];
487
+ const editSession = this.#editSession;
488
+ if (fileInstance != null && editSession != null) {
489
+ const discardDiffState = !this.#checkpointEditSessionState();
490
+ if (!recycle) this.#releaseEditSession(discardDiffState);
491
+ }
315
492
  this.#invalidateOnAttach();
316
493
  if (!recycle) this.#attachState.delivered = false;
317
- const hadFileInstance = this.#fileInstance != null;
318
- const shouldRestoreState = this.#options.persistState === true;
319
- this.#stateRestoreGeneration++;
320
- this.#persistCurrentState();
321
- if (hadFileInstance) this.#restoreStateOnNextSync = shouldRestoreState;
494
+ this.#isRendering = false;
322
495
  dequeueRender(this.#handleCustomPasteEvent);
323
496
  this.#tokenizer?.cleanUp();
324
497
  this.#tokenizer = void 0;
325
- if (!recycle) {
326
- this.#textDocument = void 0;
327
- this.#fileInfo = void 0;
328
- }
329
498
  this.#globalEventDisposes?.forEach((dispose) => dispose());
330
499
  this.#globalEventDisposes = void 0;
331
500
  this.#editorEventDisposes?.forEach((dispose) => dispose());
332
501
  this.#editorEventDisposes = void 0;
333
- this.#selectEventDisposes?.forEach((dispose) => dispose());
334
- this.#selectEventDisposes = void 0;
335
- this.#detach?.(recycle);
336
- this.#detach = void 0;
502
+ if (!recycle) {
503
+ this.#detach?.();
504
+ this.#detach = void 0;
505
+ }
337
506
  this.#gutterWidthCache = void 0;
338
507
  this.#contentWidthCache = void 0;
339
508
  this.#lineYCache.clear();
@@ -352,6 +521,7 @@ var Editor = class {
352
521
  this.#popoverManager?.cleanUp();
353
522
  this.#popoverManager = void 0;
354
523
  this.#gutterElement = void 0;
524
+ this.#deletionsColumn = void 0;
355
525
  this.#contentElement?.removeAttribute("contentEditable");
356
526
  this.#contentElement = void 0;
357
527
  this.#replacementFocusRequest = void 0;
@@ -365,25 +535,44 @@ var Editor = class {
365
535
  cancelAnimationFrame(this.#themeSelectionRefreshFrame);
366
536
  this.#themeSelectionRefreshFrame = void 0;
367
537
  }
368
- this.#resetState();
369
- this.#fileInstance = void 0;
370
- }
371
- /** @internal Capture outgoing state and substitute cached text before render. */
372
- __prepareFile(file) {
373
- if (this.#options.persistState !== true) return file;
374
- const cacheKey = requirePersistedCacheKey(file);
375
- const fileInfo = this.#fileInfo;
376
- const languageId = file.lang ?? getFiletypeFromFileName(file.name);
377
- if (fileInfo !== void 0 && (requirePersistedCacheKey(fileInfo) !== cacheKey || fileInfo.name !== file.name || this.#textDocument?.languageId !== languageId)) {
378
- this.#stateRestoreGeneration++;
379
- this.#persistCurrentState();
380
- }
381
- const textDocument = this.#getCachedTextDocument(file, cacheKey);
382
- if (textDocument === void 0 || textDocument.getText() === file.contents) return file;
383
- return {
384
- ...file,
385
- contents: textDocument.getText()
386
- };
538
+ this.#resetState(recycle);
539
+ if (!recycle) try {
540
+ if (fileInstance != null && editSession != null) {
541
+ const editor = this.#getTypedEditor;
542
+ const mode = reason === "complete" ? "install" : "discard";
543
+ if (fileInstance.type === "file" && editor.type === "file") fileInstance.__completeEditSession(editor, mode);
544
+ else if (fileInstance.type === "file-diff" && editor.type === "file-diff") fileInstance.__completeEditSession(editor, mode);
545
+ else throw new Error("Editor.cleanUp: Impossible edit state");
546
+ }
547
+ } finally {
548
+ this.#editSession = void 0;
549
+ this.#fileInstance = void 0;
550
+ this.#applyDocumentChange = void 0;
551
+ this.#publishChange = void 0;
552
+ }
553
+ }
554
+ #checkpointEditSessionState() {
555
+ const fileInstance = this.#fileInstance;
556
+ const editSession = this.#editSession;
557
+ if (fileInstance == null || editSession == null) return false;
558
+ const editorState = this.#isRendering ? this.getViewState() : editSession.editor ?? {};
559
+ const { view } = editorState;
560
+ const hasNonDefaultView = view != null && (view.scrollLeft !== 0 || (view.scrollTop ?? 0) !== 0);
561
+ const hasEditorState = editorState.selections != null || hasNonDefaultView;
562
+ editSession.editor = cloneEditorViewState(editorState);
563
+ if (editSession.type === "file") return false;
564
+ const capturedDiffSession = fileInstance.__captureDocumentSessionState();
565
+ editSession.diffSession = capturedDiffSession?.diffSession;
566
+ if (capturedDiffSession == null) return false;
567
+ const document = editSession.document;
568
+ return capturedDiffSession.hasChanges || hasEditorState || document?.canUndo === true || document?.canRedo === true;
569
+ }
570
+ #releaseEditSession(discardDiffState = false) {
571
+ const editStateKey = this.#editStateKey;
572
+ if (editStateKey == null) return;
573
+ const editor = this.#getTypedEditor;
574
+ if (editor.type === "file") EditStateManager.releaseFile(editStateKey, editor);
575
+ else EditStateManager.releaseFileDiff(editStateKey, editor, discardDiffState);
387
576
  }
388
577
  /** @internal */
389
578
  __postponeBgTokenizeToNextFrame() {
@@ -402,9 +591,40 @@ var Editor = class {
402
591
  if (contentElement != null && (this.#contentHasFocus || shadowActiveElement === contentElement || shadowActiveElement != null && contentElement.contains(shadowActiveElement))) this.#replacementFocusRequest = {};
403
592
  }
404
593
  /** @internal */
405
- __syncRenderView = (highlighter, fileContainer, fileOrDiff, lineAnnotations, renderRange) => {
594
+ __getDocumentContents(fallbackFile) {
595
+ const fileInfo = this.#editSession?.fileInfo;
596
+ const textDocument = this.#editSession?.document;
597
+ if (textDocument == null) return fileInfo == null || fallbackFile == null ? void 0 : {
598
+ ...fileInfo,
599
+ contents: fallbackFile.contents
600
+ };
601
+ const resolvedFileInfo = fileInfo ?? fallbackFile;
602
+ if (resolvedFileInfo == null) return;
603
+ return {
604
+ name: resolvedFileInfo.name,
605
+ lang: resolvedFileInfo.lang,
606
+ contents: textDocument.getText()
607
+ };
608
+ }
609
+ /** @internal */
610
+ __getDocumentSessionState() {
611
+ const snapshot = this.#editSession?.diffSession;
612
+ return snapshot != null ? cloneRetainedDiffSessionSnapshot(snapshot) : void 0;
613
+ }
614
+ /** @internal */
615
+ __syncRenderView = (props) => {
616
+ const { highlighter, fileContainer, renderRange, resetHistory = false } = props;
617
+ const renderView = props;
618
+ let fileOrDiff;
619
+ if ("file" in renderView && this.type === "file") fileOrDiff = renderView.file;
620
+ else if ("fileDiff" in renderView && this.type === "file-diff") fileOrDiff = renderView.fileDiff;
621
+ else throw new Error("Editor.__syncRenderView: Impossible render state");
622
+ const lineAnnotations = renderView.lineAnnotations;
623
+ const editStateKey = this.#editStateKey;
624
+ const externalDocument = renderView.externalDocument === true;
406
625
  const fileInstance = this.#fileInstance;
407
- if (fileInstance == null) return;
626
+ const editSession = this.#editSession;
627
+ if (!this.#isRendering || fileInstance == null || editSession == null) return;
408
628
  const shadowRoot = fileContainer.shadowRoot;
409
629
  if (shadowRoot == null) {
410
630
  console.error("[editor] Could not find the shadow root.");
@@ -413,16 +633,26 @@ var Editor = class {
413
633
  let codeElement;
414
634
  let gutterEl;
415
635
  let contentEl;
416
- for (const el of shadowRoot.querySelectorAll("[data-code]")) if (el.dataset.deletions === void 0) {
417
- codeElement = el;
636
+ let deletionsGutterEl;
637
+ let deletionsContentEl;
638
+ for (const el of shadowRoot.querySelectorAll("[data-code]")) {
639
+ const isDeletions = el.dataset.deletions != null;
640
+ if (!isDeletions) codeElement = el;
418
641
  for (const child of el.children) {
419
- const el = child;
420
- const { gutter, content } = el.dataset;
421
- if (gutter !== void 0) gutterEl = el;
422
- else if (content !== void 0) contentEl = el;
642
+ if (!(child instanceof HTMLElement)) continue;
643
+ const { gutter, content } = child.dataset;
644
+ if (gutter != null) if (isDeletions) deletionsGutterEl = child;
645
+ else gutterEl = child;
646
+ else if (content != null) if (isDeletions) deletionsContentEl = child;
647
+ else contentEl = child;
423
648
  }
424
- break;
425
649
  }
650
+ if (deletionsContentEl == null) this.#deletionsColumn = void 0;
651
+ else if (deletionsGutterEl == null) throw new Error("Editor.__syncRenderView: the deletions column has no gutter");
652
+ else this.#deletionsColumn = {
653
+ content: deletionsContentEl,
654
+ gutter: deletionsGutterEl
655
+ };
426
656
  if (codeElement === void 0 || contentEl === void 0) {
427
657
  this.#replacementFocusRequest = void 0;
428
658
  return;
@@ -435,45 +665,44 @@ var Editor = class {
435
665
  if (this.#themeStyleElement !== void 0) shadowRoot.appendChild(this.#themeStyleElement);
436
666
  if (this.#spriteElement !== void 0) shadowRoot.prepend(this.#spriteElement);
437
667
  }
438
- const shouldRebuildDocument = this.#textDocument === void 0 || this.#fileInfo === void 0 || this.#fileInfo.name !== fileOrDiff.name || this.#fileInfo.lang !== fileOrDiff.lang || this.#fileInfo.cacheKey !== fileOrDiff.cacheKey;
439
- const persistedCacheKey = this.#options.persistState === true ? requirePersistedCacheKey(fileOrDiff) : void 0;
440
- let persistedStateTarget;
441
- if (shouldRebuildDocument) {
668
+ const languageId = fileOrDiff.lang ?? getFiletypeFromFileName(fileOrDiff.name);
669
+ const contents = "contents" in fileOrDiff ? fileOrDiff.contents : fileOrDiff.additionLines.join("");
670
+ const previousTextDocument = editSession.document;
671
+ const documentChanges = externalDocument && previousTextDocument !== void 0 && previousTextDocument.getText() !== contents ? [createFullDocumentChange(previousTextDocument.getText(), contents)] : void 0;
672
+ const resetForExternalDocument = externalDocument && resetHistory;
673
+ if (editSession.document == null || editSession.fileInfo == null || editStateKey == null && (editSession.fileInfo.name !== fileOrDiff.name || editSession.document.languageId !== languageId) || resetForExternalDocument) {
674
+ this.#cancelEditPrediction(true);
675
+ this.#editPredictionHistory = [];
442
676
  this.#invalidateOnAttach();
443
- let contents = "";
444
- if ("contents" in fileOrDiff) contents = fileOrDiff.contents;
445
- else contents = fileOrDiff.additionLines.join("");
446
- const editStack = new EditStack({ maxEntries: this.#options.historyMaxEntries });
447
- const { name, lang, cacheKey } = fileOrDiff;
448
- const languageId = lang ?? getFiletypeFromFileName(fileOrDiff.name);
449
- const cachedTextDocument = persistedCacheKey !== void 0 ? this.#getCachedTextDocument(fileOrDiff, persistedCacheKey) : void 0;
450
- const reusableTextDocument = fileInstance.type === "file" || cachedTextDocument?.getText() === contents ? cachedTextDocument : void 0;
451
- const textDocument = reusableTextDocument ?? new TextDocument(fileOrDiff.name, contents, languageId, 0, editStack);
452
- this.#fileInfo = {
677
+ const { name, lang } = fileOrDiff;
678
+ let textDocument = resetForExternalDocument || editStateKey == null && editSession.fileInfo != null ? void 0 : editSession.document;
679
+ if (previousTextDocument != null && textDocument == null) editSession.editor = void 0;
680
+ if (textDocument == null) {
681
+ const editStack = new EditStack({ maxEntries: this.#options.historyMaxEntries });
682
+ textDocument = new TextDocument(fileOrDiff.name, contents, languageId, 0, editStack);
683
+ }
684
+ editSession.fileInfo = {
453
685
  name,
454
- lang,
455
- cacheKey
686
+ lang
456
687
  };
457
- this.#textDocument = textDocument;
458
- if (persistedCacheKey !== void 0) {
459
- this.#textDocumentCache.set(persistedCacheKey, textDocument);
460
- persistedStateTarget = {
461
- cacheKey: persistedCacheKey,
462
- textDocument
463
- };
464
- }
688
+ editSession.document = textDocument;
689
+ if (resetForExternalDocument) editSession.diffSession = void 0;
465
690
  this.#tokenizer?.cleanUp();
466
691
  this.#tokenizer = void 0;
467
692
  this.#resetState();
468
693
  this.#wrapLineOffsetsCache.clear();
469
694
  this.#selections = this.#initSelections;
470
- if (this.#options.__debug === true) console.log(reusableTextDocument !== void 0 ? "[diffs/editor] cached text document reused for" : "[diffs/editor] text document rebuilt from", fileOrDiff.name);
695
+ if (this.#options.__debug === true) console.log("[diffs/editor] text document initialized from", fileOrDiff.name);
471
696
  }
472
- if (persistedStateTarget === void 0 && this.#restoreStateOnNextSync && persistedCacheKey !== void 0 && this.#textDocument !== void 0) persistedStateTarget = {
473
- cacheKey: persistedCacheKey,
474
- textDocument: this.#textDocument
475
- };
476
- const textDocument = this.#textDocument;
697
+ if (externalDocument && !resetForExternalDocument) {
698
+ this.#applyExternalDocumentReplacement(editSession, contents, lineAnnotations);
699
+ const { name, lang } = fileOrDiff;
700
+ editSession.fileInfo = {
701
+ name,
702
+ lang
703
+ };
704
+ }
705
+ const textDocument = editSession.document;
477
706
  if (this.#tokenizer == null && textDocument != null) this.#tokenizer = new EditorTokenizer({
478
707
  highlighter,
479
708
  textDocument,
@@ -517,178 +746,86 @@ var Editor = class {
517
746
  this.#renderRange = renderRange;
518
747
  this.#viewportWindowLines = renderRange?.totalLines;
519
748
  this.#tokenizer?.prebuildStateStack(renderRange);
520
- this.#markerRenderer?.removePopover();
521
- if (this.#selections !== void 0 || this.#matches !== void 0 || this.#markerRenderer !== void 0) this.#updateSelections(this.#selections ?? []);
522
- if (this.#initSelections !== void 0 && this.#primaryCaretElement !== void 0) {
523
- this.#initSelections = void 0;
524
- this.#scrollToPrimaryCaret(false, "center");
525
- } else if (this.#scrollingToLine !== void 0) this.#scrollToLine(this.#scrollingToLine, this.#scrollingToLineChar, this.#scrollingToLineNoFocus);
526
- else if (this.#replacementFocusRequest !== void 0) this.#restoreReplacementFocus();
527
- else if (this.#selections !== void 0 && this.#selections.length > 0 && this.#contentHasFocus && !this.#retainSearchPanelFocus) this.focus({ preventScroll: true });
528
- if (this.#retainSearchPanelFocus) this.#searchPanel?.focus();
529
- if (this.#options.__debug === true && renderRange !== void 0) {
530
- const { startingLine, totalLines } = renderRange;
531
- console.log("[diffs/editor] render file:", fileOrDiff.name, "RenderRange:", startingLine + "-" + (startingLine + totalLines), "of", this.#textDocument?.lineCount, "lines");
532
- }
533
- if (persistedStateTarget !== void 0) {
534
- this.#restoreStateOnNextSync = false;
535
- this.#restorePersistedState(persistedStateTarget.cacheKey, persistedStateTarget.textDocument);
749
+ const retainedEditorState = editSession.editor;
750
+ const restoreEditorStateOnSync = this.#restoreEditorStateOnSync;
751
+ try {
752
+ this.#markerRenderer?.removePopover();
753
+ if (this.#selections !== void 0 || this.#matches !== void 0 || this.#markerRenderer !== void 0) this.#updateSelections(this.#selections ?? []);
754
+ this.#renderCarets();
755
+ if (this.#retryEditPredictionOnRender) this.#scheduleEditPrediction();
756
+ if (this.#initSelections !== void 0 && this.#primaryCaretElement !== void 0) {
757
+ this.#initSelections = void 0;
758
+ this.#scrollToPrimaryCaret(false, "center");
759
+ } else if (this.#scrollingToLine !== void 0) this.#scrollToLine(this.#scrollingToLine, this.#scrollingToLineChar, this.#scrollingToLineNoFocus);
760
+ else if (this.#replacementFocusRequest !== void 0) this.#restoreReplacementFocus();
761
+ else if (this.#selections !== void 0 && this.#selections.length > 0 && this.#contentHasFocus && !this.#retainSearchPanelFocus) this.focus({ preventScroll: true });
762
+ if (this.#retainSearchPanelFocus) this.#searchPanel?.focus();
763
+ if (restoreEditorStateOnSync) {
764
+ this.#restoreEditorStateOnSync = false;
765
+ if (retainedEditorState !== void 0) this.setViewState(retainedEditorState);
766
+ }
767
+ if (this.#options.__debug === true && renderRange !== void 0) {
768
+ const { startingLine, totalLines } = renderRange;
769
+ console.log("[diffs/editor] render file:", fileOrDiff.name, "RenderRange:", startingLine + "-" + (startingLine + totalLines), "of", editSession.document?.lineCount, "lines");
770
+ }
771
+ this.#checkpointEditSessionState();
772
+ } catch (error) {
773
+ this.#restoreEditorStateOnSync = restoreEditorStateOnSync;
774
+ editSession.editor = retainedEditorState;
775
+ throw error;
536
776
  }
777
+ if (documentChanges != null) this.#emitChange(documentChanges, lineAnnotations);
537
778
  this.#scheduleOnAttach(fileInstance);
538
779
  };
539
- #getCachedTextDocument(file, cacheKey) {
540
- const textDocument = this.#textDocumentCache.get(cacheKey);
541
- const languageId = file.lang ?? getFiletypeFromFileName(file.name);
542
- return textDocument?.languageId === languageId ? textDocument : void 0;
543
- }
544
- #getStateStorage() {
545
- const option = this.#options.persistStateStorage ?? "inMemory";
546
- if (this.#stateStorage === void 0 || this.#stateStorageOption !== option) {
547
- this.#stateStorage = createStateStorage(option);
548
- this.#stateStorageOption = option;
549
- }
550
- return this.#stateStorage;
551
- }
552
- #persistCurrentState() {
553
- const fileInfo = this.#fileInfo;
554
- const textDocument = this.#textDocument;
555
- if (this.#options.persistState !== true || this.#fileInstance === void 0 || fileInfo === void 0 || textDocument === void 0) return;
556
- const cacheKey = requirePersistedCacheKey(fileInfo);
557
- this.#textDocumentCache.set(cacheKey, textDocument);
558
- let storage;
559
- try {
560
- storage = this.#getStateStorage();
561
- } catch {
562
- return;
563
- }
564
- const state = cloneEditorState(this.getState());
565
- const pendingRestore = this.#pendingStateRestore;
566
- if (pendingRestore?.cacheKey === cacheKey && pendingRestore.textDocument === textDocument) {
567
- this.#pendingStateRestore = void 0;
568
- if (textDocument.version === pendingRestore.documentVersion && this.#selections === pendingRestore.selections && state.view?.scrollLeft === pendingRestore.view?.scrollLeft && state.view?.scrollTop === pendingRestore.view?.scrollTop) return;
569
- this.#writeState(storage, cacheKey, state, pendingRestore.completion);
570
- return;
571
- }
572
- this.#writeState(storage, cacheKey, state);
573
- }
574
- #writeState(storage, cacheKey, state, waitFor) {
575
- const previousWrite = this.#pendingStateWrites.get(cacheKey);
576
- if (waitFor !== void 0 || previousWrite !== void 0) {
577
- let pending = waitFor ?? Promise.resolve();
578
- if (previousWrite !== void 0) pending = pending.then(() => previousWrite);
579
- this.#trackStateWrite(cacheKey, pending.then(() => storage.set(cacheKey, state)));
580
- return;
581
- }
582
- let result;
583
- try {
584
- result = storage.set(cacheKey, state);
585
- } catch {
586
- return;
587
- }
588
- if (!isPromise(result)) return;
589
- this.#trackStateWrite(cacheKey, result);
590
- }
591
- #trackStateWrite(cacheKey, result) {
592
- const pending = result.catch(() => {});
593
- this.#pendingStateWrites.set(cacheKey, pending);
594
- pending.finally(() => {
595
- if (this.#pendingStateWrites.get(cacheKey) === pending) this.#pendingStateWrites.delete(cacheKey);
596
- });
597
- }
598
- #restorePersistedState(cacheKey, textDocument) {
599
- const generation = ++this.#stateRestoreGeneration;
600
- const documentVersion = textDocument.version;
780
+ #applyExternalDocumentReplacement(editSession, contents, lineAnnotations) {
781
+ const textDocument = editSession.document;
782
+ if (textDocument == null || textDocument.getText() === contents) return;
601
783
  const selections = this.#selections;
602
- const view = this.getState().view;
603
- let inputWatch;
604
- const applyState = (state) => {
605
- const currentView = this.getState().view;
606
- if (generation !== this.#stateRestoreGeneration || this.#textDocument !== textDocument || textDocument.version !== documentVersion || this.#selections !== selections || currentView?.scrollLeft !== view?.scrollLeft || this.#fileInfo === void 0 || requirePersistedCacheKey(this.#fileInfo) !== cacheKey) return;
607
- const userScrolled = inputWatch?.userScrolled() === true;
608
- if (state === void 0) {
609
- this.#fileInstance?.setCodeScrollLeft(0);
610
- if (!userScrolled) this.#setViewportScrollTop(0);
611
- return;
612
- }
613
- const restored = cloneEditorState(state);
614
- if (userScrolled && restored.view !== void 0) restored.view = { scrollLeft: restored.view.scrollLeft };
615
- this.setState(restored);
616
- };
617
- const readState = () => {
618
- let result;
619
- try {
620
- result = this.#getStateStorage().get(cacheKey);
621
- } catch {
622
- return;
623
- }
624
- if (isPromise(result)) return result.then(applyState).catch(() => {});
625
- else try {
626
- applyState(result);
627
- } catch {}
784
+ const selectionOffsets = selections?.map((selection) => [textDocument.offsetAt(selection.start), textDocument.offsetAt(selection.end)]);
785
+ const replacement = {
786
+ start: 0,
787
+ end: textDocument.getText().length,
788
+ text: contents
628
789
  };
629
- const pendingWrite = this.#pendingStateWrites.get(cacheKey);
630
- const result = pendingWrite === void 0 ? readState() : pendingWrite.then(readState);
631
- if (isPromise(result)) {
632
- inputWatch = this.#watchViewportUserInput();
633
- const pendingRestore = {
634
- cacheKey,
635
- textDocument,
636
- documentVersion,
637
- selections,
638
- view,
639
- completion: result.catch(() => {})
640
- };
641
- this.#pendingStateRestore = pendingRestore;
642
- pendingRestore.completion.finally(() => {
643
- inputWatch?.dispose();
644
- if (this.#pendingStateRestore === pendingRestore) this.#pendingStateRestore = void 0;
645
- });
790
+ if (textDocument.applyResolvedEdits([replacement], true, selections, void 0, true) == null) return;
791
+ if (selections != null && selectionOffsets != null) {
792
+ const nextSelections = remapSelectionsAfterEdits(textDocument, selections, selectionOffsets, [replacement]);
793
+ textDocument.setLastUndoSelectionsAfter(nextSelections);
794
+ this.#selections = nextSelections;
646
795
  }
796
+ if (this.#lineAnnotations != null || lineAnnotations != null) textDocument.setLastUndoLineAnnotations(this.#lineAnnotations ?? [], lineAnnotations ?? []);
797
+ this.#tokenizer?.cleanUp();
798
+ this.#tokenizer = void 0;
799
+ this.#resetCache();
800
+ this.#wrapLineOffsetsCache.clear();
801
+ this.#markerRenderer?.removePopover();
802
+ if (this.#searchPanel !== void 0 && this.#matches !== void 0) this.#searchPanel.updateMatches({ syncSelection: false });
647
803
  }
648
- #watchViewportUserInput() {
649
- const viewport = this.#getScrollViewport();
650
- if (!(viewport instanceof HTMLElement)) return;
651
- let scrolled = false;
652
- const eventTypes = [
653
- "wheel",
654
- "touchstart",
655
- "mousedown"
656
- ];
657
- const dispose = () => {
658
- for (const type of eventTypes) viewport.removeEventListener(type, onInput, { capture: true });
659
- };
660
- const onInput = () => {
661
- scrolled = true;
662
- dispose();
663
- };
664
- for (const type of eventTypes) viewport.addEventListener(type, onInput, {
665
- capture: true,
666
- passive: true
667
- });
668
- return {
669
- userScrolled: () => scrolled,
670
- dispose
671
- };
804
+ get #fileDiffInstance() {
805
+ return this.#fileInstance?.type === "file-diff" ? this.#fileInstance : void 0;
806
+ }
807
+ get #virtualizedInstance() {
808
+ return isVirtualizedEditorComponent(this.#fileInstance) ? this.#fileInstance : void 0;
672
809
  }
673
810
  #isLineRenderable(line) {
674
- return this.#fileInstance?.isLineRenderable?.(line + 1) ?? true;
811
+ return this.#fileDiffInstance?.isLineRenderable?.(line + 1) ?? true;
675
812
  }
676
813
  get #resolveRenderableLine() {
677
- const fileInstance = this.#fileInstance;
678
- if (fileInstance?.getNearestRenderableLine == null) return;
814
+ const fileInstance = this.#fileDiffInstance;
815
+ if (fileInstance == null) return;
679
816
  return (line, direction) => {
680
817
  const nearest = fileInstance.getNearestRenderableLine(line + 1, direction);
681
818
  return nearest == null ? void 0 : nearest - 1;
682
819
  };
683
820
  }
684
821
  #revealLineIfCollapsed(line) {
685
- if (!this.#isLineRenderable(line)) this.#fileInstance?.revealLine?.(line + 1);
822
+ if (!this.#isLineRenderable(line)) this.#fileDiffInstance?.revealLine?.(line + 1);
686
823
  }
687
824
  get #diffSyle() {
688
- return this.#fileInstance?.options.diffStyle ?? "split";
825
+ return this.#fileDiffInstance?.options.diffStyle ?? "split";
689
826
  }
690
827
  get #isDiff() {
691
- return this.#fileInstance?.type === "file-diff";
828
+ return this.type === "file-diff";
692
829
  }
693
830
  get #isWrap() {
694
831
  return this.#fileInstance?.options.overflow === "wrap";
@@ -707,16 +844,20 @@ var Editor = class {
707
844
  this.#lineElementsCache.clear();
708
845
  this.#lastAccessedCharX = void 0;
709
846
  }
710
- #resetState() {
847
+ #resetState(preserveCarets = false) {
711
848
  this.#setEditorActiveLineSafe(null);
712
849
  this.#gutterWidthCache = void 0;
713
850
  this.#contentWidthCache = void 0;
714
- this.#shouldIgnoreSelectionChange = false;
851
+ this.#resetSelectionGesture();
715
852
  this.#suppressNativeSelectionSync = false;
716
853
  this.#overlayElements?.forEach((el) => el.remove());
717
854
  this.#overlayElements = void 0;
855
+ this.#caretElements?.forEach((element) => element.remove());
856
+ this.#caretElements = void 0;
857
+ this.#caretHighlightElements?.forEach((element) => element.remove());
858
+ this.#caretHighlightElements = void 0;
859
+ if (!preserveCarets) this.#carets = void 0;
718
860
  this.#selections = void 0;
719
- this.#reservedSelections = void 0;
720
861
  this.#scrollingToLine = void 0;
721
862
  this.#markerRenderer?.cleanup();
722
863
  this.#markerRenderer = void 0;
@@ -736,13 +877,13 @@ var Editor = class {
736
877
  }
737
878
  #scheduleOnAttach(fileInstance) {
738
879
  const attachState = this.#attachState;
739
- const textDocument = this.#textDocument;
880
+ const textDocument = this.#editSession?.document;
740
881
  if (attachState.delivered || attachState.callback != null || textDocument == null) return;
741
882
  const { generation } = attachState;
742
883
  const callback = () => {
743
884
  if (attachState.callback !== callback) return;
744
885
  attachState.callback = void 0;
745
- if (generation !== attachState.generation || this.#fileInstance !== fileInstance || this.#textDocument !== textDocument) return;
886
+ if (generation !== attachState.generation || this.#fileInstance !== fileInstance || this.#editSession?.document !== textDocument) return;
746
887
  const contentElement = this.#contentElement;
747
888
  const shadowRoot = this.#fileContainer?.shadowRoot;
748
889
  if (contentElement == null || shadowRoot == null || !shadowRoot.contains(contentElement)) return;
@@ -753,21 +894,16 @@ var Editor = class {
753
894
  queueRender(callback);
754
895
  }
755
896
  #getScrollViewport() {
756
- const viewport = this.#fileInstance?.getEditorViewport?.();
757
- if (viewport !== void 0) return viewport;
897
+ const viewport = this.#virtualizedInstance?.getEditorViewport?.();
898
+ if (viewport != null) return viewport;
758
899
  const fileContainer = this.#fileContainer;
759
900
  if (this.#fileInstance == null || fileContainer == null) return;
760
901
  return lookupScrollContainer(fileContainer);
761
902
  }
762
- #getViewportScrollTop() {
763
- const viewport = this.#getScrollViewport();
764
- if (viewport instanceof HTMLElement) return viewport.scrollTop;
765
- return viewport?.defaultView?.scrollY ?? 0;
766
- }
767
- #setViewportScrollTop(scrollTop) {
768
- const viewport = this.#getScrollViewport();
769
- if (viewport instanceof HTMLElement) viewport.scrollTop = scrollTop;
770
- else if (viewport instanceof Document) viewport.defaultView?.scrollTo({ top: scrollTop });
903
+ #getOwnedVerticalViewport() {
904
+ if (!this.#ownsVerticalViewport) return;
905
+ const viewport = this.#virtualizedInstance?.getEditorViewport?.();
906
+ if (viewport instanceof HTMLElement) return viewport;
771
907
  }
772
908
  #initialize() {
773
909
  this.#globalStyleElement = h("style", {
@@ -796,6 +932,13 @@ var Editor = class {
796
932
  this.#contentHasFocus = false;
797
933
  this.#shouldIgnoreSelectionChange = false;
798
934
  };
935
+ const finishMouseSelection = (event) => {
936
+ if (event.pointerType !== "mouse") return;
937
+ const refocusEditor = this.#isGutterMouseDown;
938
+ this.#resetSelectionGesture();
939
+ if (refocusEditor) this.#focus();
940
+ if (this.#options.enabledSelectionAction === true && this.#selections !== void 0 && this.#selections.length > 0 && !isCollapsedSelection(this.#selections.at(-1))) this.#updateSelections(this.#selections);
941
+ };
799
942
  this.#globalEventDisposes = [
800
943
  addEventListener(document, "focusin", (event) => {
801
944
  if (!replacementFocusTargetIsContent(event)) cancelReplacementFocus();
@@ -812,7 +955,9 @@ var Editor = class {
812
955
  if (this.#selections !== void 0 && this.#selections.length > 1 && !this.#isContentMouseDown) return;
813
956
  const selectionRaw = document.getSelection();
814
957
  if (selectionRaw == null) return;
815
- const composedRange = typeof selectionRaw.getComposedRanges === "function" ? selectionRaw.getComposedRanges({ shadowRoots: [shadowRoot] })?.[0] : getShadowRootRange(shadowRoot);
958
+ let composedRange;
959
+ if (typeof selectionRaw.getComposedRanges === "function") composedRange = selectionRaw.getComposedRanges({ shadowRoots: [shadowRoot] })?.[0];
960
+ else composedRange = getShadowRootRange(shadowRoot);
816
961
  if (composedRange === void 0 || !this.#rangeBelongsToEditor(composedRange)) return;
817
962
  let selection = convertSelection(composedRange, 0);
818
963
  if (selection === void 0) return;
@@ -828,32 +973,38 @@ var Editor = class {
828
973
  const previous = this.#selections[0];
829
974
  if (comparePosition(previous.start, selection.start) === 0 && comparePosition(previous.end, selection.end) === 0) selection.direction = previous.direction;
830
975
  }
976
+ if (this.#altColumnDrag !== void 0) {
977
+ this.#altColumnDrag.focusLine = getCaretPosition(selection).line;
978
+ if (this.#updateAltColumnSelections()) return;
979
+ }
831
980
  if (this.#reservedSelections !== void 0) this.#updateSelections([...this.#reservedSelections.filter((reservedSelection) => !selectionIntersects(reservedSelection, selection)), selection]);
832
981
  else this.#updateSelections([selection]);
833
982
  }, { passive: true }),
834
- addEventListener(document, "pointerup", (e) => {
835
- if (e.pointerType !== "mouse") return;
836
- this.#selectEventDisposes?.forEach((dispose) => dispose());
837
- this.#selectEventDisposes = void 0;
838
- if (this.#isGutterMouseDown) {
839
- this.#isGutterMouseDown = false;
840
- this.#focus();
841
- }
842
- this.#shouldIgnoreSelectionChange = false;
843
- this.#isContentMouseDown = false;
844
- this.#shiftKeyPressed = false;
845
- this.#selectionStart = void 0;
846
- this.#reservedSelections = void 0;
847
- if (this.#options.enabledSelectionAction === true && this.#selections !== void 0 && this.#selections.length > 0 && !isCollapsedSelection(this.#selections.at(-1))) this.#updateSelections(this.#selections);
848
- }, { passive: true }),
983
+ addEventListener(document, "pointerup", finishMouseSelection, { passive: true }),
984
+ addEventListener(document, "pointercancel", finishMouseSelection, { passive: true }),
849
985
  addEventListener(document, "keydown", (e) => {
850
986
  if (e.key === "Shift") this.#selectionStart = this.#selections?.at(-1);
987
+ else if (e.key === "Alt" && !e.repeat && this.#contentHasFocus && this.#options.editPrediction?.mode === "subtle") {
988
+ this.#editPredictionRevealed = !this.#editPredictionRevealed;
989
+ this.#updateSelections(this.#selections ?? []);
990
+ }
851
991
  }, { passive: true }),
852
992
  addEventListener(document, "keyup", (e) => {
853
993
  if (e.key === "Shift") this.#selectionStart = void 0;
854
994
  }, { passive: true })
855
995
  ];
856
996
  }
997
+ #resetSelectionGesture() {
998
+ this.#selectEventDisposes?.forEach((dispose) => dispose());
999
+ this.#selectEventDisposes = void 0;
1000
+ this.#shouldIgnoreSelectionChange = false;
1001
+ this.#isGutterMouseDown = false;
1002
+ this.#isContentMouseDown = false;
1003
+ this.#altColumnDrag = void 0;
1004
+ this.#shiftKeyPressed = false;
1005
+ this.#selectionStart = void 0;
1006
+ this.#reservedSelections = void 0;
1007
+ }
857
1008
  #replaceSelectEventListeners(disposes) {
858
1009
  this.#selectEventDisposes?.forEach((dispose) => dispose());
859
1010
  this.#selectEventDisposes = disposes;
@@ -889,6 +1040,7 @@ var Editor = class {
889
1040
  this.#canMountSelectionAction = true;
890
1041
  this.#suppressNativeSelectionSync = false;
891
1042
  if (e.pointerType !== "mouse") return;
1043
+ this.#altColumnDrag = void 0;
892
1044
  if (this.#isDeletedLineTarget(e)) {
893
1045
  this.#setDeletedTextSelectionActive(true);
894
1046
  if (this.#selections !== void 0) this.#updateSelections([]);
@@ -896,17 +1048,39 @@ var Editor = class {
896
1048
  }
897
1049
  this.#setDeletedTextSelectionActive(false);
898
1050
  this.#markerRenderer?.removePopover();
1051
+ const selectEventDisposes = [];
899
1052
  if (isSafari() && this.#lineAnnotations !== void 0 && this.#lineAnnotations.length > 0) {
900
1053
  const annotationDisposes = [...contentEl.querySelectorAll("[data-line-annotation]")].map((el) => [addEventListener(el, "mouseenter", () => {
901
1054
  this.#shouldIgnoreSelectionChange = true;
902
1055
  }), addEventListener(el, "mouseleave", () => {
903
1056
  this.#shouldIgnoreSelectionChange = false;
904
1057
  })]).flat();
905
- this.#replaceSelectEventListeners(annotationDisposes);
1058
+ selectEventDisposes.push(...annotationDisposes);
906
1059
  }
907
1060
  this.#isContentMouseDown = true;
1061
+ const isAltColumnDrag = e.button === 0 && e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey;
908
1062
  this.#selectionStart = void 0;
909
- if (e.button === 0 && isPrimaryModifier(e)) this.#reservedSelections = this.#selections?.map((selection) => ({ ...selection }));
1063
+ if (isAltColumnDrag) {
1064
+ this.#altColumnDrag = {
1065
+ pointerId: e.pointerId,
1066
+ startClientX: e.clientX,
1067
+ clientX: e.clientX,
1068
+ startScrollLeft: contentEl.parentElement?.scrollLeft ?? 0
1069
+ };
1070
+ this.#reservedSelections = void 0;
1071
+ this.#selections = void 0;
1072
+ this.#updateSelections([]);
1073
+ selectEventDisposes.push(addEventListener(document, "pointermove", (moveEvent) => {
1074
+ const drag = this.#altColumnDrag;
1075
+ if (drag === void 0 || moveEvent.pointerType !== "mouse" || moveEvent.pointerId !== drag.pointerId) return;
1076
+ drag.clientX = moveEvent.clientX;
1077
+ this.#updateAltColumnSelections();
1078
+ }, {
1079
+ capture: true,
1080
+ passive: true
1081
+ }));
1082
+ } else if (e.button === 0 && isPrimaryModifier(e)) this.#reservedSelections = this.#selections?.map((selection) => ({ ...selection }));
1083
+ this.#replaceSelectEventListeners(selectEventDisposes);
910
1084
  if (e.shiftKey) {
911
1085
  const primarySelection = this.#selections?.at(-1);
912
1086
  if (primarySelection !== void 0) {
@@ -924,6 +1098,17 @@ var Editor = class {
924
1098
  if (!targetIsContentElement(e)) return;
925
1099
  this.#canMountSelectionAction = true;
926
1100
  this.#suppressNativeSelectionSync = false;
1101
+ if (e.isComposing || e.keyCode === 229) return;
1102
+ if (e.key === "Tab" && !e.shiftKey && !e.ctrlKey && !e.metaKey && !e.isComposing && !this.#isComposing && (!e.altKey || this.#options.editPrediction?.mode === "subtle") && this.#editPrediction?.rendered === true) {
1103
+ this.#acceptEditPrediction();
1104
+ e.preventDefault();
1105
+ return;
1106
+ }
1107
+ if (e.key === "Escape" && this.#editPrediction !== void 0) {
1108
+ this.#cancelEditPrediction(true);
1109
+ e.preventDefault();
1110
+ return;
1111
+ }
927
1112
  const command = resolveEditorCommandFromKeyboardEvent(e, this.#options.keymap);
928
1113
  if (command !== void 0) {
929
1114
  e.preventDefault();
@@ -938,7 +1123,7 @@ var Editor = class {
938
1123
  return;
939
1124
  }
940
1125
  const mvShortcut = isMoveCursorShortcut(e);
941
- const textDocument = this.#textDocument;
1126
+ const textDocument = this.#editSession?.document;
942
1127
  if (this.#selections !== void 0 && this.#selections.length > 0 && mvShortcut !== void 0 && textDocument !== void 0) {
943
1128
  const cursorMoveOptions = {
944
1129
  getSoftLineOffsets: this.#isWrap ? (line) => this.#wrapLineTextOrWholeLine(line) : void 0,
@@ -989,7 +1174,7 @@ var Editor = class {
989
1174
  if (!targetIsContentElement(e)) return;
990
1175
  e.preventDefault();
991
1176
  const clipboardData = e.clipboardData;
992
- const textDocument = this.#textDocument;
1177
+ const textDocument = this.#editSession?.document;
993
1178
  if (clipboardData === null || textDocument === void 0) return;
994
1179
  let text = clipboardData.getData("text");
995
1180
  const selectionCount = this.#selections?.length ?? 0;
@@ -1004,7 +1189,10 @@ var Editor = class {
1004
1189
  }),
1005
1190
  addEventListener(contentEl, "beforeinput", (e) => {
1006
1191
  if (!targetIsContentElement(e)) return;
1007
- if (e.inputType === "insertCompositionText") return;
1192
+ if (e.inputType === "insertCompositionText") {
1193
+ this.#cancelEditPrediction(true);
1194
+ return;
1195
+ }
1008
1196
  e.preventDefault();
1009
1197
  this.#handleInput(e.inputType, e.data);
1010
1198
  }),
@@ -1014,6 +1202,7 @@ var Editor = class {
1014
1202
  }),
1015
1203
  addEventListener(contentEl, "compositionstart", (e) => {
1016
1204
  if (!targetIsContentElement(e)) return;
1205
+ this.#cancelEditPrediction(true);
1017
1206
  this.#isComposing = true;
1018
1207
  this.#shouldIgnoreSelectionChange = true;
1019
1208
  }, { passive: true }),
@@ -1075,7 +1264,7 @@ var Editor = class {
1075
1264
  return;
1076
1265
  }
1077
1266
  if (e.pointerType !== "mouse") return;
1078
- const textDocument = this.#textDocument;
1267
+ const textDocument = this.#editSession?.document;
1079
1268
  const lineIndex = resolveEditableLine(gutterRow);
1080
1269
  if (lineIndex === void 0 || textDocument === void 0) return;
1081
1270
  this.#canMountSelectionAction = true;
@@ -1088,7 +1277,7 @@ var Editor = class {
1088
1277
  this.#focus();
1089
1278
  this.#replaceSelectEventListeners([addEventListener(document, "mousemove", (e) => {
1090
1279
  if (!this.#isGutterMouseDown) return;
1091
- const textDocument = this.#textDocument;
1280
+ const textDocument = this.#editSession?.document;
1092
1281
  const lineIndex = resolveEditableLine(resolveGutterTarget(e.composedPath()[0], true));
1093
1282
  if (lineIndex === void 0 || textDocument === void 0) return;
1094
1283
  const anchorLine = this.#selectionStart?.start.line ?? lineIndex;
@@ -1118,14 +1307,14 @@ var Editor = class {
1118
1307
  if (Array.isArray(selectionTexts) && selectionTexts.length === selectionCount) text = selectionTexts;
1119
1308
  } catch {}
1120
1309
  }
1121
- const textDocument = this.#textDocument;
1122
- if (textDocument !== void 0) this.#replaceSelectionText(Array.isArray(text) ? text.map((t) => textDocument.normalizeEol(t)) : textDocument.normalizeEol(text), void 0, true, "document");
1310
+ const textDocument = this.#editSession?.document;
1311
+ if (textDocument != null) this.#replaceSelectionText(Array.isArray(text) ? text.map((t) => textDocument.normalizeEol(t)) : textDocument.normalizeEol(text), void 0, true, "document");
1123
1312
  }
1124
1313
  };
1125
1314
  #computeContentOffset(contentEl) {
1126
1315
  if (this.#isDiff && this.#diffSyle === "split" && this.#isWrap) {
1127
1316
  this.#contentOffset = {
1128
- top: contentEl.offsetTop,
1317
+ top: contentEl.offsetTop - this.#metrics.paddingTop,
1129
1318
  left: contentEl.offsetLeft - this.#getGutterWidth()
1130
1319
  };
1131
1320
  if (this.#options.__debug === true) console.log("[diffs/editor] content offset:", this.#contentOffset);
@@ -1135,8 +1324,8 @@ var Editor = class {
1135
1324
  if (this.#isDiff && this.#diffSyle === "split" && this.#isWrap) return this.#contentOffset;
1136
1325
  }
1137
1326
  #runCommand(command) {
1138
- const textDocument = this.#textDocument;
1139
- if (textDocument === void 0) return;
1327
+ const textDocument = this.#editSession?.document;
1328
+ if (textDocument == null) return;
1140
1329
  switch (command) {
1141
1330
  case "openSearchPanel":
1142
1331
  this.#openSearchPanel("find");
@@ -1336,8 +1525,8 @@ var Editor = class {
1336
1525
  }
1337
1526
  }
1338
1527
  #applyHistoryChange(command) {
1339
- const textDocument = this.#textDocument;
1340
- if (textDocument === void 0) return;
1528
+ const textDocument = this.#editSession?.document;
1529
+ if (textDocument == null) return;
1341
1530
  if (command === "undo" && !textDocument.canUndo || command === "redo" && !textDocument.canRedo) return;
1342
1531
  const selections = this.#selections;
1343
1532
  const selectionOffsets = selections?.map((selection) => [textDocument.offsetAt(selection.start), textDocument.offsetAt(selection.end)]);
@@ -1345,11 +1534,12 @@ var Editor = class {
1345
1534
  if (result === void 0) return;
1346
1535
  const [change, recordedSelections, lineAnnotations, selectionEdits] = result;
1347
1536
  const nextSelections = recordedSelections ?? (selections !== void 0 && selectionOffsets !== void 0 && selectionEdits !== void 0 ? remapSelectionsAfterEdits(textDocument, selections, selectionOffsets, selectionEdits) : void 0);
1348
- this.#applyChange(change, nextSelections, lineAnnotations);
1537
+ const replayedLineAnnotations = lineAnnotations ?? (this.#lineAnnotations != null ? applyDocumentChangeToLineAnnotations(change, this.#lineAnnotations) : void 0);
1538
+ this.#applyChange(change, nextSelections, replayedLineAnnotations);
1349
1539
  }
1350
1540
  /** Applies one undoable command batch and records its resulting selections. */
1351
1541
  #applyCommandEdits(edits, resolveNextSelections) {
1352
- const textDocument = this.#textDocument;
1542
+ const textDocument = this.#editSession?.document;
1353
1543
  const selections = this.#selections;
1354
1544
  if (textDocument === void 0 || selections === void 0) return;
1355
1545
  const remapSelections = resolveNextSelections === void 0;
@@ -1374,7 +1564,7 @@ var Editor = class {
1374
1564
  }
1375
1565
  /** Copies selected line blocks and keeps the selection in the requested copy. */
1376
1566
  #copySelectedLines(direction) {
1377
- const textDocument = this.#textDocument;
1567
+ const textDocument = this.#editSession?.document;
1378
1568
  const selections = this.#selections;
1379
1569
  if (textDocument === void 0 || selections === void 0) return;
1380
1570
  const blocks = getSelectedLineBlocks(selections);
@@ -1467,7 +1657,7 @@ var Editor = class {
1467
1657
  }
1468
1658
  /** Inserts an indented blank line after each selection's final line. */
1469
1659
  #insertBlankLine() {
1470
- const textDocument = this.#textDocument;
1660
+ const textDocument = this.#editSession?.document;
1471
1661
  const selections = this.#selections;
1472
1662
  if (textDocument === void 0 || selections === void 0) return;
1473
1663
  const selectionLines = selections.map((selection) => getCaretPosition(selection).line);
@@ -1508,7 +1698,7 @@ var Editor = class {
1508
1698
  this.#applyCommandEdits(edits, () => nextSelections);
1509
1699
  }
1510
1700
  #moveSelectedLines(direction) {
1511
- const textDocument = this.#textDocument;
1701
+ const textDocument = this.#editSession?.document;
1512
1702
  const selections = this.#selections;
1513
1703
  if (textDocument === void 0 || selections === void 0) return;
1514
1704
  const blocks = getSelectedLineBlocks(selections);
@@ -1585,6 +1775,7 @@ var Editor = class {
1585
1775
  }
1586
1776
  this.#markerRenderer?.removePopover();
1587
1777
  this.#computeContentOffset(this.#contentElement);
1778
+ this.#renderCarets();
1588
1779
  };
1589
1780
  #remeasureMetricsOnFontLoad() {
1590
1781
  if (this.#fontRemeasureScheduled) return;
@@ -1598,19 +1789,21 @@ var Editor = class {
1598
1789
  this.#resetCache();
1599
1790
  this.#wrapLineOffsetsCache.clear();
1600
1791
  if (this.#selections !== void 0 || this.#matches !== void 0 || this.#markerRenderer !== void 0) this.#updateSelections(this.#selections ?? []);
1792
+ this.#renderCarets();
1601
1793
  this.#markerRenderer?.removePopover();
1602
1794
  });
1603
1795
  }
1604
1796
  #rerender(change, newLineAnnotations, renderRange = this.#renderRange, shouldUpdateBuffer) {
1605
1797
  const tokenizer = this.#tokenizer;
1606
1798
  const fileInstance = this.#fileInstance;
1607
- const textDocument = this.#textDocument;
1799
+ const applyDocumentChange = this.#applyDocumentChange;
1800
+ const textDocument = this.#editSession?.document;
1608
1801
  const gutterEl = this.#gutterElement;
1609
1802
  const contentEl = this.#contentElement;
1610
- if (tokenizer === void 0 || fileInstance === void 0 || textDocument === void 0 || contentEl === void 0) return;
1803
+ if (tokenizer == null || fileInstance == null || applyDocumentChange == null || textDocument == null || contentEl == null) return;
1611
1804
  tokenizer.stopBackgroundTokenize();
1612
1805
  const t = performance.now();
1613
- const dirtyLines = tokenizer.tokenize(change, renderRange, !this.#isDiff);
1806
+ const dirtyLines = tokenizer.tokenize(change, renderRange, !this.#isDiff && this.#virtualizedInstance != null);
1614
1807
  const t2 = performance.now();
1615
1808
  if (dirtyLines.size > 0) {
1616
1809
  const children = contentEl.children;
@@ -1689,19 +1882,20 @@ var Editor = class {
1689
1882
  shouldRefreshDiffsView: this.#isDiff && !didLineCountChange,
1690
1883
  lineCountChangeInFlight: didLineCountChange
1691
1884
  });
1692
- if (didLineCountChange) fileInstance.applyDocumentChange(textDocument, newLineAnnotations, shouldUpdateBuffer);
1885
+ if (didLineCountChange) applyDocumentChange(textDocument, newLineAnnotations, shouldUpdateBuffer);
1693
1886
  if (didLineCountChange || this.#isDiff && this.#diffSyle === "unified") this.#resetCache();
1694
- if (newLineAnnotations !== void 0) {
1887
+ if (newLineAnnotations != null) {
1695
1888
  this.#lineAnnotations = newLineAnnotations;
1696
- renderLineAnnotations(newLineAnnotations, contentEl, gutterEl);
1889
+ if (!this.#isDiff || !didLineCountChange) renderLineAnnotations(newLineAnnotations, contentEl, gutterEl, fileInstance.getAnnotationSlotName);
1697
1890
  }
1891
+ this.#renderCarets();
1698
1892
  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)`);
1699
1893
  }
1700
1894
  #handleInput(inputType, data) {
1701
1895
  switch (inputType) {
1702
1896
  case "insertText": {
1703
1897
  const text = data ?? "";
1704
- const textDocument = this.#textDocument;
1898
+ const textDocument = this.#editSession?.document;
1705
1899
  const selections = this.#selections;
1706
1900
  const autoSurroundTexts = textDocument !== void 0 && selections !== void 0 ? getAutoSurroundReplacementTexts(textDocument, selections, text, this.#options.autoSurround) : void 0;
1707
1901
  this.#replaceSelectionText(autoSurroundTexts ?? text);
@@ -1710,7 +1904,7 @@ var Editor = class {
1710
1904
  case "insertCompositionText": break;
1711
1905
  case "insertLineBreak":
1712
1906
  case "insertParagraph":
1713
- this.#replaceSelectionText(this.#textDocument?.eol ?? "\n");
1907
+ this.#replaceSelectionText(this.#editSession?.document?.eol ?? "\n");
1714
1908
  break;
1715
1909
  case "deleteContentBackward":
1716
1910
  this.#deleteSelectionText();
@@ -1802,7 +1996,7 @@ var Editor = class {
1802
1996
  #getFirstVisibleLineNumber(offset = 0) {
1803
1997
  const contentElement = this.#contentElement;
1804
1998
  const fileContainer = this.#fileContainer;
1805
- if (this.#textDocument == null || contentElement == null || fileContainer == null) return;
1999
+ if (this.#editSession?.document == null || contentElement == null || fileContainer == null) return;
1806
2000
  const viewport = this.#getScrollViewport();
1807
2001
  if (viewport === void 0) return;
1808
2002
  let viewportTop;
@@ -1919,7 +2113,7 @@ var Editor = class {
1919
2113
  this.#scrollingToLineFixed = false;
1920
2114
  this.#scrollingToLineNoFocus = false;
1921
2115
  } else {
1922
- const modelLinePosition = this.#fileInstance?.getLinePosition?.(line + 1);
2116
+ const modelLinePosition = this.#virtualizedInstance?.getLinePosition?.(line + 1);
1923
2117
  if (modelLinePosition !== void 0) {
1924
2118
  virtualCaret.style.top = modelLinePosition.top + "px";
1925
2119
  this.#fileContainer?.shadowRoot?.appendChild(virtualCaret);
@@ -2068,8 +2262,493 @@ var Editor = class {
2068
2262
  if (this.#selections !== void 0 || this.#matches !== void 0 || this.#markerRenderer !== void 0) this.#updateSelections(this.#selections ?? []);
2069
2263
  });
2070
2264
  }
2265
+ #updateAltColumnSelections() {
2266
+ const drag = this.#altColumnDrag;
2267
+ const selectionStart = this.#selectionStart;
2268
+ const textDocument = this.#editSession?.document;
2269
+ if (drag === void 0 || drag.focusLine === void 0 || selectionStart === void 0 || textDocument === void 0) return false;
2270
+ const anchor = selectionStart.direction === -1 ? selectionStart.end : selectionStart.start;
2271
+ const scrollLeft = this.#contentElement?.parentElement?.scrollLeft ?? 0;
2272
+ const characterDeltaRaw = (drag.clientX - drag.startClientX + scrollLeft - drag.startScrollLeft) / this.#metrics.ch;
2273
+ const characterDelta = characterDeltaRaw < 0 ? -Math.round(-characterDeltaRaw) : Math.round(characterDeltaRaw);
2274
+ const focusCharacter = Math.max(0, anchor.character + characterDelta);
2275
+ const goal = {
2276
+ line: drag.focusLine,
2277
+ character: focusCharacter
2278
+ };
2279
+ if (drag.renderedGoal !== void 0 && comparePosition(drag.renderedGoal, goal) === 0) return true;
2280
+ drag.renderedGoal = goal;
2281
+ const selections = [];
2282
+ const step = goal.line < anchor.line ? -1 : 1;
2283
+ for (let line = anchor.line;; line += step) {
2284
+ if (this.#isLineRenderable(line)) {
2285
+ const lineText = textDocument.getLineText(line);
2286
+ const anchorOffset = Math.min(anchor.character, lineText.length);
2287
+ const focusOffset = Math.min(focusCharacter, lineText.length);
2288
+ const anchorCharacter = snapCharacterToGraphemeBoundary(lineText, anchorOffset);
2289
+ const lineFocusCharacter = focusOffset === anchorOffset ? anchorCharacter : snapCharacterToGraphemeBoundary(lineText, focusOffset);
2290
+ selections.push({
2291
+ start: {
2292
+ line,
2293
+ character: Math.min(anchorCharacter, lineFocusCharacter)
2294
+ },
2295
+ end: {
2296
+ line,
2297
+ character: Math.max(anchorCharacter, lineFocusCharacter)
2298
+ },
2299
+ direction: anchorCharacter === lineFocusCharacter ? 0 : anchorCharacter < lineFocusCharacter ? 1 : -1
2300
+ });
2301
+ }
2302
+ if (line === goal.line) break;
2303
+ }
2304
+ this.#updateSelections(selections);
2305
+ return true;
2306
+ }
2307
+ #removeRenderedEditPrediction() {
2308
+ for (const [key, element] of this.#overlayElements ?? []) if (key.startsWith("editPrediction")) {
2309
+ element.remove();
2310
+ this.#overlayElements?.delete(key);
2311
+ }
2312
+ }
2313
+ #composeEditPredictionGroup(textDocument, edits, startIndex) {
2314
+ const firstEdit = edits[startIndex];
2315
+ let endIndex = startIndex;
2316
+ let endLine = firstEdit.range.end.line;
2317
+ while (endIndex + 1 < edits.length && edits[endIndex + 1].range.start.line <= endLine) {
2318
+ endIndex++;
2319
+ endLine = Math.max(endLine, edits[endIndex].range.end.line);
2320
+ }
2321
+ if (endIndex === startIndex && (firstEdit.newText.length === 0 || comparePosition(firstEdit.range.start, firstEdit.range.end) === 0)) return {
2322
+ edit: firstEdit,
2323
+ endIndex
2324
+ };
2325
+ const start = firstEdit.range.start;
2326
+ const end = {
2327
+ line: endLine,
2328
+ character: textDocument.getLineLength(endLine)
2329
+ };
2330
+ const parts = [];
2331
+ let consumed = textDocument.offsetAt(start);
2332
+ for (let index = startIndex; index <= endIndex; index++) {
2333
+ const edit = edits[index];
2334
+ const editStart = textDocument.offsetAt(edit.range.start);
2335
+ const editEnd = textDocument.offsetAt(edit.range.end);
2336
+ parts.push(textDocument.getTextSlice(consumed, editStart), edit.newText);
2337
+ consumed = editEnd;
2338
+ }
2339
+ parts.push(textDocument.getTextSlice(consumed, textDocument.offsetAt(end)));
2340
+ return {
2341
+ edit: {
2342
+ range: {
2343
+ start,
2344
+ end
2345
+ },
2346
+ newText: parts.join("")
2347
+ },
2348
+ endIndex
2349
+ };
2350
+ }
2351
+ #getEditPredictionGroups() {
2352
+ const prediction = this.#editPrediction;
2353
+ const textDocument = this.#editSession?.document;
2354
+ if (prediction == null || textDocument == null || prediction.document !== textDocument || prediction.version !== textDocument.version || this.#options.editPrediction?.mode === "subtle" && !this.#editPredictionRevealed) return;
2355
+ const { edits } = prediction.response;
2356
+ const groups = [];
2357
+ let allVisible = edits.length > 0;
2358
+ for (let startIndex = 0; startIndex < edits.length;) {
2359
+ const { edit, endIndex } = this.#composeEditPredictionGroup(textDocument, edits, startIndex);
2360
+ for (let line = edit.range.start.line; allVisible && line <= edit.range.end.line; line++) allVisible = this.#isLineVisible(line);
2361
+ groups.push({
2362
+ edit,
2363
+ startIndex,
2364
+ endIndex
2365
+ });
2366
+ startIndex = endIndex + 1;
2367
+ }
2368
+ return {
2369
+ groups,
2370
+ allVisible
2371
+ };
2372
+ }
2373
+ #fillGhostTextElement(element, newText, anchorLeft, lineLeft, insertionSuffix) {
2374
+ if (this.#isWrap) {
2375
+ element.dataset.wrap = "";
2376
+ element.style.width = `calc(100cqw - ${lineLeft}px)`;
2377
+ } else {
2378
+ delete element.dataset.wrap;
2379
+ element.style.width = "max-content";
2380
+ }
2381
+ const lines = newText.split(/\r\n|\r|\n/);
2382
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
2383
+ const lineText = lines[lineIndex];
2384
+ const suffix = lineIndex === lines.length - 1 ? insertionSuffix : void 0;
2385
+ const isEmpty = lineText.length === 0 && suffix === void 0;
2386
+ const line = h("span", {
2387
+ dataset: isEmpty ? ["editPredictionLine", "empty"] : "editPredictionLine",
2388
+ textContent: isEmpty ? "​" : lineText
2389
+ }, element);
2390
+ if (suffix !== void 0) h("span", { dataset: "editPredictionSuffix" }, line).append(suffix);
2391
+ if (lineIndex === 0 && anchorLeft !== lineLeft) line.style.textIndent = `${anchorLeft - lineLeft}px`;
2392
+ }
2393
+ }
2394
+ #cloneInsertionSuffix(group) {
2395
+ const prediction = this.#editPrediction;
2396
+ const textDocument = this.#editSession?.document;
2397
+ if (prediction == null || textDocument == null) return;
2398
+ const { edit, startIndex, endIndex } = group;
2399
+ const { start, end } = edit.range;
2400
+ if (!(comparePosition(start, end) === 0 && start.character < textDocument.getLineLength(start.line)) || prediction.response.edits[startIndex - 1]?.range.end.line === start.line || prediction.response.edits[endIndex + 1]?.range.start.line === start.line) return;
2401
+ const sourceLine = this.#getLineElement(start.line);
2402
+ if (sourceLine === void 0) return document.createTextNode(textDocument.getLineText(start.line).slice(start.character));
2403
+ const [suffixNode, suffixOffset] = getSelectionAnchor(sourceLine, start.character);
2404
+ const suffixRange = document.createRange();
2405
+ suffixRange.selectNodeContents(sourceLine);
2406
+ suffixRange.setStart(suffixNode, clampDomOffset(suffixNode, suffixOffset));
2407
+ const suffix = suffixRange.cloneContents();
2408
+ if (suffix.firstChild?.textContent === "") suffix.firstChild.remove();
2409
+ return suffix;
2410
+ }
2411
+ #measureGhostTextRows(group) {
2412
+ const { edit } = group;
2413
+ const { start } = edit.range;
2414
+ const [anchorLeft, anchorWrapLine] = this.#getCharX(start.line, start.character);
2415
+ let anchorVisualLines = 1;
2416
+ let ghostVisualLines = edit.newText.split(/\r\n|\r|\n/).length;
2417
+ const overlayElement = this.#overlayElement;
2418
+ if (this.#isWrap && overlayElement != null) {
2419
+ anchorVisualLines = Math.max(1, this.#wrapLineTextOrWholeLine(start.line).length - 1);
2420
+ const probe = h("span", {
2421
+ dataset: "editPrediction",
2422
+ style: { visibility: "hidden" }
2423
+ }, overlayElement);
2424
+ this.#fillGhostTextElement(probe, edit.newText, anchorLeft, this.#getCharX(start.line, 0)[0], this.#cloneInsertionSuffix(group));
2425
+ const { width, height } = probe.getBoundingClientRect();
2426
+ probe.remove();
2427
+ const { lineHeight } = this.#metrics;
2428
+ if (width > 0 && height > 0 && lineHeight > 0) ghostVisualLines = Math.round(height / lineHeight);
2429
+ }
2430
+ return Math.max(0, ghostVisualLines - (anchorVisualLines - anchorWrapLine));
2431
+ }
2432
+ #syncEditPredictionSpacers(notifyComponent = true) {
2433
+ const nextSpacers = /* @__PURE__ */ new Map();
2434
+ const ghostTextRows = /* @__PURE__ */ new Map();
2435
+ const previousRows = this.#ghostTextRows;
2436
+ let rowsChanged = false;
2437
+ const contentElement = this.#contentElement;
2438
+ const composed = this.#getEditPredictionGroups();
2439
+ if (contentElement != null && composed != null && composed.allVisible) {
2440
+ for (const group of composed.groups) {
2441
+ const { edit } = group;
2442
+ if (edit.newText.length === 0) continue;
2443
+ const count = this.#measureGhostTextRows(group);
2444
+ if (count > (ghostTextRows.get(edit.range.start.line) ?? 0)) ghostTextRows.set(edit.range.start.line, count);
2445
+ }
2446
+ if (ghostTextRows.size > 0) {
2447
+ let rowIndexes;
2448
+ const startingLine = this.#renderRange?.startingLine ?? 0;
2449
+ for (const [line, count] of ghostTextRows) {
2450
+ rowsChanged ||= previousRows.get(line) !== count;
2451
+ const lineElement = this.#getLineElement(line);
2452
+ if (lineElement === void 0) continue;
2453
+ let rowIndex = line - startingLine;
2454
+ if (contentElement.children[rowIndex] !== lineElement) {
2455
+ if (rowIndexes === void 0) {
2456
+ rowIndexes = /* @__PURE__ */ new Map();
2457
+ for (let index = 0; index < contentElement.children.length; index++) rowIndexes.set(contentElement.children[index], index);
2458
+ }
2459
+ rowIndex = rowIndexes.get(lineElement) ?? -1;
2460
+ }
2461
+ if (rowIndex < 0) continue;
2462
+ nextSpacers.set(lineElement, count);
2463
+ const gutterRow = this.#gutterElement?.children[rowIndex];
2464
+ if (gutterRow instanceof HTMLElement) nextSpacers.set(gutterRow, count);
2465
+ const partner = this.#deletionsColumnPartner(rowIndex);
2466
+ if (partner != null) {
2467
+ nextSpacers.set(partner.content, (nextSpacers.get(partner.content) ?? 0) + count);
2468
+ nextSpacers.set(partner.gutter, (nextSpacers.get(partner.gutter) ?? 0) + count);
2469
+ }
2470
+ }
2471
+ }
2472
+ }
2473
+ let changed = false;
2474
+ for (const [element, count] of this.#editPredictionSpacers) {
2475
+ if (nextSpacers.get(element) === count) continue;
2476
+ delete element.dataset.editPredictionSpacer;
2477
+ element.style.removeProperty("--diffs-edit-prediction-spacer-height");
2478
+ changed = true;
2479
+ }
2480
+ for (const [element, count] of nextSpacers) {
2481
+ if (this.#editPredictionSpacers.get(element) === count) continue;
2482
+ element.dataset.editPredictionSpacer = "";
2483
+ element.style.setProperty("--diffs-edit-prediction-spacer-height", `${count}lh`);
2484
+ changed = true;
2485
+ }
2486
+ this.#editPredictionSpacers = nextSpacers;
2487
+ if (rowsChanged || ghostTextRows.size !== previousRows.size) {
2488
+ this.#ghostTextRows = ghostTextRows;
2489
+ if (notifyComponent) this.#virtualizedInstance?.syncGhostTextRows();
2490
+ }
2491
+ if (changed) this.#resetCache();
2492
+ }
2493
+ #deletionsColumnPartner(rowIndex) {
2494
+ const deletionsColumn = this.#deletionsColumn;
2495
+ const contentElement = this.#contentElement;
2496
+ if (deletionsColumn == null || contentElement == null || !this.#isDiff || this.#diffSyle !== "split" || this.#isWrap) return;
2497
+ let track = 0;
2498
+ for (let index = 0; index < rowIndex; index++) track += gridTrackSpan(contentElement.children[index]);
2499
+ const { content, gutter } = deletionsColumn;
2500
+ let covered = 0;
2501
+ for (let index = 0; index < content.children.length; index++) {
2502
+ const child = content.children[index];
2503
+ covered += gridTrackSpan(child);
2504
+ if (covered > track) {
2505
+ const gutterChild = gutter.children[index];
2506
+ if (!(child instanceof HTMLElement) || !(gutterChild instanceof HTMLElement)) throw new Error("Editor: deletions column rows and gutter cells are out of step");
2507
+ return {
2508
+ content: child,
2509
+ gutter: gutterChild
2510
+ };
2511
+ }
2512
+ }
2513
+ }
2514
+ #cancelEditPrediction(removeRendered, notifyComponent = true) {
2515
+ if (this.#editPredictionTimer !== void 0) {
2516
+ clearTimeout(this.#editPredictionTimer);
2517
+ this.#editPredictionTimer = void 0;
2518
+ }
2519
+ this.#editPredictionAbortController?.abort();
2520
+ this.#editPredictionAbortController = void 0;
2521
+ this.#editPredictionGeneration++;
2522
+ this.#editPredictionRevealed = false;
2523
+ this.#retryEditPredictionOnRender = false;
2524
+ this.#editPrediction = void 0;
2525
+ this.#syncEditPredictionSpacers(notifyComponent);
2526
+ if (removeRendered) this.#removeRenderedEditPrediction();
2527
+ }
2528
+ #includesEditPredictionPath(path) {
2529
+ const options = this.#options.editPrediction;
2530
+ if (options === void 0) return false;
2531
+ const normalizedPath = path.replaceAll("\\", "/");
2532
+ return (options.include === void 0 || options.include.some((pattern) => matchesEditPredictionPattern(normalizedPath, pattern))) && options.exclude?.some((pattern) => matchesEditPredictionPattern(normalizedPath, pattern)) !== true;
2533
+ }
2534
+ #scheduleEditPrediction() {
2535
+ this.#cancelEditPrediction(true);
2536
+ const selection = this.#selections?.[0];
2537
+ if (this.#options.editPrediction === void 0 || this.#editSession?.document === void 0 || this.#editSession?.fileInfo === void 0 || this.#selections?.length !== 1 || selection === void 0 || !isCollapsedSelection(selection)) return;
2538
+ const document = this.#editSession?.document;
2539
+ const cursorOffset = document.offsetAt(getCaretPosition(selection));
2540
+ this.#editPredictionTimer = setTimeout(() => {
2541
+ this.#editPredictionTimer = void 0;
2542
+ const options = this.#options.editPrediction;
2543
+ const currentSelection = this.#selections?.[0];
2544
+ const path = this.#editSession?.fileInfo?.name;
2545
+ if (options === void 0 || path === void 0 || this.#editSession?.document !== document || this.#selections?.length !== 1 || currentSelection === void 0 || !isCollapsedSelection(currentSelection) || document.offsetAt(getCaretPosition(currentSelection)) !== cursorOffset) return;
2546
+ if (!this.#includesEditPredictionPath(path)) return;
2547
+ const isLineEditable = (line) => this.#isLineInRenderRange(line) && this.#isLineRenderable(line);
2548
+ if (!isLineEditable(getCaretPosition(currentSelection).line)) {
2549
+ this.#retryEditPredictionOnRender = true;
2550
+ return;
2551
+ }
2552
+ const request = buildEditPredictionRequest(path, document, cursorOffset, this.#editPredictionHistory, isLineEditable);
2553
+ if (request === void 0) return;
2554
+ const excerptStartOffset = document.offsetAt({
2555
+ line: request.excerptStartLine,
2556
+ character: 0
2557
+ });
2558
+ const editableStart = excerptStartOffset + request.editableRange.start;
2559
+ const editableEnd = excerptStartOffset + request.editableRange.end;
2560
+ const controller = new AbortController();
2561
+ const generation = ++this.#editPredictionGeneration;
2562
+ this.#editPredictionAbortController = controller;
2563
+ let prediction;
2564
+ try {
2565
+ prediction = options.provider.predict(request, { signal: controller.signal });
2566
+ } catch {
2567
+ this.#editPredictionAbortController = void 0;
2568
+ return;
2569
+ }
2570
+ Promise.resolve(prediction).then((response) => {
2571
+ const selection = this.#selections?.[0];
2572
+ if (controller.signal.aborted || generation !== this.#editPredictionGeneration || this.#editPredictionAbortController !== controller || this.#editSession?.document !== document || document.version !== request.version || this.#selections?.length !== 1 || selection === void 0 || !isCollapsedSelection(selection) || document.offsetAt(getCaretPosition(selection)) !== cursorOffset) return;
2573
+ if (response == null || !Array.isArray(response.edits) || response.edits.length === 0 || response.edits.length > MAX_EDIT_PREDICTION_RESPONSE_EDITS || response.newCursor == null) return;
2574
+ const resolvedEdits = [];
2575
+ let responseBytes = 0;
2576
+ for (const edit of response.edits) {
2577
+ if (edit == null || typeof edit.newText !== "string" || !isValidEditPredictionPosition(document, edit.range?.start) || !isValidEditPredictionPosition(document, edit.range?.end) || comparePosition(edit.range.start, edit.range.end) > 0) return;
2578
+ responseBytes += editPredictionTextEncoder.encode(edit.newText).byteLength;
2579
+ if (responseBytes > MAX_EDIT_PREDICTION_RESPONSE_BYTES) return;
2580
+ const start = document.offsetAt(edit.range.start);
2581
+ const end = document.offsetAt(edit.range.end);
2582
+ const resolvedEdit = document.resolveEdits([edit])[0];
2583
+ if (resolvedEdit.start !== start || resolvedEdit.end !== end) return;
2584
+ resolvedEdits.push(resolvedEdit);
2585
+ }
2586
+ resolvedEdits.sort((left, right) => {
2587
+ const startDelta = left.start - right.start;
2588
+ return startDelta === 0 ? left.end - right.end : startDelta;
2589
+ });
2590
+ for (let index = 0; index < resolvedEdits.length; index++) {
2591
+ const edit = resolvedEdits[index];
2592
+ if (edit.start < editableStart || edit.end > editableEnd || index > 0 && resolvedEdits[index - 1].end > edit.start) return;
2593
+ }
2594
+ const edits = resolvedEdits.filter((edit) => edit.text !== document.getTextSlice(edit.start, edit.end));
2595
+ if (edits.length === 0) return;
2596
+ const firstEditPosition = document.positionAt(edits[0].start);
2597
+ const lastEditPosition = document.positionAt(edits.at(-1).end);
2598
+ const affectedStart = document.offsetAt({
2599
+ line: firstEditPosition.line,
2600
+ character: 0
2601
+ });
2602
+ const affectedEnd = document.offsetAt({
2603
+ line: lastEditPosition.line,
2604
+ character: document.getLineLength(lastEditPosition.line)
2605
+ });
2606
+ const predictedParts = [];
2607
+ let consumed = affectedStart;
2608
+ for (const edit of edits) {
2609
+ predictedParts.push(document.getTextSlice(consumed, edit.start), edit.text);
2610
+ consumed = edit.end;
2611
+ }
2612
+ predictedParts.push(document.getTextSlice(consumed, affectedEnd));
2613
+ const predictedLines = predictedParts.join("").split(/\r\n|\r|\n/);
2614
+ const affectedEndLine = firstEditPosition.line + predictedLines.length - 1;
2615
+ const lineDelta = predictedLines.length - (lastEditPosition.line - firstEditPosition.line + 1);
2616
+ const newCursor = response.newCursor;
2617
+ if (!Number.isInteger(newCursor.line) || !Number.isInteger(newCursor.character) || newCursor.line < 0 || newCursor.character < 0) return;
2618
+ if (newCursor.line >= firstEditPosition.line && newCursor.line <= affectedEndLine) {
2619
+ const line = predictedLines[newCursor.line - firstEditPosition.line];
2620
+ if (newCursor.character > line.length || splitsSurrogatePair(line, newCursor.character)) return;
2621
+ } else {
2622
+ const originalLine = newCursor.line < firstEditPosition.line ? newCursor.line : newCursor.line - lineDelta;
2623
+ if (originalLine < 0 || originalLine >= document.lineCount || newCursor.character > document.getLineLength(originalLine)) return;
2624
+ const originalOffset = document.offsetAt({
2625
+ line: originalLine,
2626
+ character: newCursor.character
2627
+ });
2628
+ if (splitsSurrogatePair(document.charAt(originalOffset - 1) + document.charAt(originalOffset), 1)) return;
2629
+ }
2630
+ this.#editPrediction = {
2631
+ document,
2632
+ version: request.version,
2633
+ cursorOffset,
2634
+ rendered: false,
2635
+ response: {
2636
+ edits: edits.map((edit) => ({
2637
+ range: {
2638
+ start: document.positionAt(edit.start),
2639
+ end: document.positionAt(edit.end)
2640
+ },
2641
+ newText: edit.text
2642
+ })),
2643
+ newCursor: { ...newCursor }
2644
+ }
2645
+ };
2646
+ this.#updateSelections(this.#selections);
2647
+ }).catch(() => {}).finally(() => {
2648
+ if (this.#editPredictionAbortController === controller) this.#editPredictionAbortController = void 0;
2649
+ });
2650
+ }, EDIT_PREDICTION_DEBOUNCE_MS);
2651
+ }
2652
+ #recordEditPredictionHistory(change, source) {
2653
+ const textDocument = this.#editSession?.document;
2654
+ const path = this.#editSession?.fileInfo?.name;
2655
+ const transaction = getTextDocumentChangeTransaction(change);
2656
+ if (textDocument === void 0 || path === void 0 || transaction === void 0 || !this.#includesEditPredictionPath(path)) return;
2657
+ this.#editPredictionHistory = recordEditPrediction(this.#editPredictionHistory, path, textDocument, transaction, source);
2658
+ }
2659
+ #acceptEditPrediction() {
2660
+ const prediction = this.#editPrediction;
2661
+ const textDocument = this.#editSession?.document;
2662
+ const selection = this.#selections?.[0];
2663
+ if (prediction === void 0 || !prediction.rendered || textDocument === void 0 || prediction.document !== textDocument || prediction.version !== textDocument.version || this.#selections?.length !== 1 || selection === void 0 || !isCollapsedSelection(selection) || textDocument.offsetAt(getCaretPosition(selection)) !== prediction.cursorOffset) return;
2664
+ const { edits, newCursor } = prediction.response;
2665
+ this.#cancelEditPrediction(true);
2666
+ const change = textDocument.applyEdits(edits.map((edit) => ({
2667
+ range: {
2668
+ start: { ...edit.range.start },
2669
+ end: { ...edit.range.end }
2670
+ },
2671
+ newText: edit.newText
2672
+ })), true, this.#selections, void 0, true);
2673
+ if (change === void 0) {
2674
+ this.#scheduleEditPrediction();
2675
+ return;
2676
+ }
2677
+ const cursor = textDocument.normalizePosition(newCursor);
2678
+ const nextSelections = [{
2679
+ start: cursor,
2680
+ end: cursor,
2681
+ direction: 0
2682
+ }];
2683
+ textDocument.setLastUndoSelectionsAfter(nextSelections);
2684
+ this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change), { editSource: "prediction" });
2685
+ }
2686
+ #renderEditPrediction(renderCtx) {
2687
+ const prediction = this.#editPrediction;
2688
+ const textDocument = this.#editSession?.document;
2689
+ if (prediction !== void 0) prediction.rendered = false;
2690
+ const composed = this.#getEditPredictionGroups();
2691
+ if (prediction == null || textDocument == null || composed == null || !composed.allVisible) return;
2692
+ for (const group of composed.groups) {
2693
+ const { edit, endIndex } = group;
2694
+ const { start, end } = edit.range;
2695
+ const isDeletion = edit.newText.length === 0;
2696
+ const isReplacement = comparePosition(start, end) !== 0;
2697
+ const lineLength = textDocument.getLineLength(start.line);
2698
+ const isMidLineInsertion = !isReplacement && start.character < lineLength;
2699
+ if (isReplacement) this.#renderSelection(renderCtx, isDeletion ? "editPredictionDeletion" : "editPredictionReplacement", {
2700
+ start,
2701
+ end
2702
+ });
2703
+ else if (isMidLineInsertion) this.#renderSelection(renderCtx, "editPredictionInsertion", {
2704
+ start,
2705
+ end: {
2706
+ line: start.line,
2707
+ character: lineLength
2708
+ }
2709
+ });
2710
+ if (isDeletion) continue;
2711
+ const [anchorLeft, anchorWrapLine] = this.#getCharX(start.line, start.character);
2712
+ const lineLeft = this.#getCharX(start.line, 0)[0];
2713
+ const anchorTop = this.#getLineY(start.line) + anchorWrapLine * this.#metrics.lineHeight;
2714
+ const key = `editPrediction-${endIndex}`;
2715
+ let element = this.#overlayElements?.get(key);
2716
+ if (element !== void 0) {
2717
+ this.#overlayElements?.delete(key);
2718
+ element.replaceChildren();
2719
+ } else element = h("span", {
2720
+ ariaHidden: "true",
2721
+ contentEditable: "false",
2722
+ dataset: "editPrediction"
2723
+ }, renderCtx.fragment);
2724
+ if (isReplacement) {
2725
+ element.dataset.replacement = "";
2726
+ const lineElement = this.#getLineElement(start.line);
2727
+ if (lineElement !== void 0) element.style.setProperty("--diffs-edit-prediction-bg", getComputedStyle(lineElement).getPropertyValue("--diffs-line-bg"));
2728
+ } else {
2729
+ delete element.dataset.replacement;
2730
+ element.style.removeProperty("--diffs-edit-prediction-bg");
2731
+ }
2732
+ this.#fillGhostTextElement(element, edit.newText, anchorLeft, lineLeft, this.#cloneInsertionSuffix(group));
2733
+ element.style.transform = `translateX(${lineLeft}px) translateY(${anchorTop}px)`;
2734
+ renderCtx.elements.set(key, element);
2735
+ }
2736
+ prediction.rendered = true;
2737
+ }
2071
2738
  #updateSelections(selections) {
2072
2739
  this.__postponeBgTokenizeToNextFrame();
2740
+ const previousSelections = this.#selections;
2741
+ let selectionsChanged = previousSelections?.length !== selections.length;
2742
+ if (!selectionsChanged && previousSelections !== void 0) for (let i = 0; i < selections.length; i++) {
2743
+ const previous = previousSelections[i];
2744
+ const next = selections[i];
2745
+ if (previous.direction !== next.direction || comparePosition(previous.start, next.start) !== 0 || comparePosition(previous.end, next.end) !== 0) {
2746
+ selectionsChanged = true;
2747
+ break;
2748
+ }
2749
+ }
2750
+ if (selectionsChanged) this.#cancelEditPrediction(true);
2751
+ this.#syncEditPredictionSpacers();
2073
2752
  this.#primaryCaretElement = void 0;
2074
2753
  this.#setEditorActiveLineSafe(null);
2075
2754
  if (selections.length === 0 && this.#matches === void 0 && this.#markerRenderer === void 0) {
@@ -2078,6 +2757,7 @@ var Editor = class {
2078
2757
  this.#overlayElements?.clear();
2079
2758
  this.#selectionAction?.cleanup();
2080
2759
  this.#selectionAction = void 0;
2760
+ if (selectionsChanged) this.#scheduleEditPrediction();
2081
2761
  return;
2082
2762
  }
2083
2763
  const fragment = document.createDocumentFragment();
@@ -2096,14 +2776,14 @@ var Editor = class {
2096
2776
  if (!isCollapsedSelection(selection)) this.#renderSelection(renderCtx, "selection", selection);
2097
2777
  this.#renderCaret(renderCtx, selection, selection === primarySelection);
2098
2778
  }
2099
- const bracketMatchRanges = this.#options.matchBrackets !== false && this.#textDocument !== void 0 && this.#tokenizer !== void 0 && isCollapsedSelection(primarySelection) ? findBracketMatchRanges(this.#textDocument, this.#tokenizer, primarySelection.start) : void 0;
2779
+ const bracketMatchRanges = this.#options.matchBrackets !== false && this.#editSession?.document != null && this.#tokenizer !== void 0 && isCollapsedSelection(primarySelection) ? findBracketMatchRanges(this.#editSession?.document, this.#tokenizer, primarySelection.start) : void 0;
2100
2780
  if (bracketMatchRanges !== void 0) for (const range of bracketMatchRanges) this.#renderSelection(renderCtx, "bracketMatch", range);
2101
2781
  }
2102
- const textDocument = this.#textDocument;
2782
+ const textDocument = this.#editSession?.document;
2103
2783
  if (this.#matches !== void 0 && textDocument !== void 0) {
2104
2784
  const matches = this.#matches;
2105
2785
  const renderRange = this.#renderRange;
2106
- const renderedLineRanges = renderRange !== void 0 && Number.isFinite(renderRange.totalLines) || this.#isDiff && this.#fileInstance?.options.expandUnchanged !== true ? this.#getRenderedEditableLineRanges() : void 0;
2786
+ const renderedLineRanges = renderRange != null && Number.isFinite(renderRange.totalLines) || this.#isDiff && this.#fileDiffInstance?.options.expandUnchanged !== true ? this.#getRenderedEditableLineRanges() : void 0;
2107
2787
  const primarySelection = this.#selections?.at(-1);
2108
2788
  const primaryStartOffset = primarySelection !== void 0 ? textDocument.offsetAt(primarySelection.start) : -1;
2109
2789
  const primaryEndOffset = primarySelection !== void 0 ? textDocument.offsetAt(primarySelection.end) : -1;
@@ -2148,14 +2828,70 @@ var Editor = class {
2148
2828
  }
2149
2829
  }
2150
2830
  if (this.#markerRenderer !== void 0 && textDocument !== void 0) for (const marker of this.#markerRenderer.markers) this.#renderSelection(renderCtx, "marker", marker, markerSeverityDatasetKey(marker.severity));
2831
+ this.#renderEditPrediction(renderCtx);
2151
2832
  this.#overlayElement?.appendChild(fragment);
2152
2833
  this.#overlayElements?.forEach((el) => el.remove());
2153
2834
  this.#overlayElements?.clear();
2154
2835
  this.#overlayElements = renderCtx.elements;
2155
2836
  this.#updateSelectionActionPopover();
2837
+ if (selectionsChanged) this.#scheduleEditPrediction();
2838
+ }
2839
+ #renderCarets() {
2840
+ const carets = this.#carets;
2841
+ const renderCaret = this.#options.renderCaret;
2842
+ const overlayElement = this.#overlayElement;
2843
+ this.#caretHighlightElements?.forEach((element) => element.remove());
2844
+ this.#caretHighlightElements = void 0;
2845
+ if (carets === void 0 || renderCaret === void 0 || overlayElement === void 0) {
2846
+ this.#caretElements?.forEach((element) => element.remove());
2847
+ this.#caretElements = void 0;
2848
+ return;
2849
+ }
2850
+ const elements = this.#caretElements ??= /* @__PURE__ */ new Map();
2851
+ for (const [trackedCaret, element] of elements) if (!this.#isLineVisible(trackedCaret.caret.focus.line)) {
2852
+ element.remove();
2853
+ elements.delete(trackedCaret);
2854
+ }
2855
+ const fragment = document.createDocumentFragment();
2856
+ const highlightElements = [];
2857
+ for (const trackedCaret of carets) {
2858
+ const { caret } = trackedCaret;
2859
+ const { line, character } = caret.focus;
2860
+ if (trackedCaret.anchorOffset !== trackedCaret.focusOffset) {
2861
+ const highlightContext = {
2862
+ fragment,
2863
+ elements: /* @__PURE__ */ new Map()
2864
+ };
2865
+ this.#renderSelection(highlightContext, "caretHighlight", trackedCaret.anchorOffset < trackedCaret.focusOffset ? {
2866
+ start: caret.anchor,
2867
+ end: caret.focus
2868
+ } : {
2869
+ start: caret.focus,
2870
+ end: caret.anchor
2871
+ }, void 0, caret.focus);
2872
+ for (const highlightElement of highlightContext.elements.values()) {
2873
+ highlightElement.style.setProperty("--diffs-caret-highlight-bg", `color-mix(in srgb, ${caret.metadata.color} 32%, transparent)`);
2874
+ highlightElements.push(highlightElement);
2875
+ }
2876
+ }
2877
+ if (!this.#isLineVisible(line)) continue;
2878
+ let element = elements.get(trackedCaret);
2879
+ if (element === void 0) {
2880
+ element = h("div", {
2881
+ dataset: "remoteCaret",
2882
+ children: [renderCaret(caret)]
2883
+ }, fragment);
2884
+ elements.set(trackedCaret, element);
2885
+ }
2886
+ const [left, wrapLine] = this.#getCharX(line, character);
2887
+ const top = this.#getLineY(line) + wrapLine * this.#metrics.lineHeight;
2888
+ element.style.transform = `translateX(${left}px) translateY(${top}px)`;
2889
+ }
2890
+ this.#caretHighlightElements = highlightElements;
2891
+ overlayElement.appendChild(fragment);
2156
2892
  }
2157
- #renderSelection(renderCtx, type, range, extraDataset) {
2158
- if (this.#textDocument === void 0) return;
2893
+ #renderSelection(renderCtx, type, range, extraDataset, connectedCaret) {
2894
+ if (this.#editSession?.document == null) return;
2159
2895
  const { start, end } = range;
2160
2896
  let firstLine = start.line;
2161
2897
  let endLine = end.line + 1;
@@ -2168,12 +2904,18 @@ var Editor = class {
2168
2904
  if (!this.#isLineVisible(line)) continue;
2169
2905
  const isLastLine = line === end.line;
2170
2906
  const startChar = line === start.line ? start.character : 0;
2171
- const endChar = isLastLine ? end.character : this.#textDocument.getLineLength(line);
2907
+ const endChar = isLastLine ? end.character : this.#editSession?.document.getLineLength(line);
2908
+ if (startChar === endChar && (type === "editPredictionDeletion" || type === "editPredictionReplacement")) {
2909
+ if (isLastLine && line !== start.line) continue;
2910
+ const [left, wrapLine] = this.#getCharX(line, startChar);
2911
+ this.#renderSelectionBlock(renderCtx, type, line, wrapLine, left, this.#metrics.ch, extraDataset);
2912
+ continue;
2913
+ }
2172
2914
  if (this.#isWrap) {
2173
2915
  const contentWidth = this.#getContentWidth();
2174
- const lineText = this.#textDocument.getLineText(line);
2916
+ const lineText = this.#editSession?.document.getLineText(line);
2175
2917
  if (2 * this.#metrics.ch + this.#metrics.measureTextWidth(lineText) > contentWidth) {
2176
- this.#renderWrappedSelection(renderCtx, line, lineText, startChar, endChar, isLastLine, type, extraDataset);
2918
+ this.#renderWrappedSelection(renderCtx, line, lineText, startChar, endChar, isLastLine, type, extraDataset, connectedCaret);
2177
2919
  continue;
2178
2920
  }
2179
2921
  }
@@ -2185,10 +2927,13 @@ var Editor = class {
2185
2927
  if (!isLastLine && type === "selection") paddingEnd = this.#metrics.ch;
2186
2928
  if (startChar === endChar) width = paddingEnd;
2187
2929
  else width = this.#getCharX(line, endChar)[0] - left + paddingEnd;
2188
- this.#renderSelectionBlock(renderCtx, type, line, 0, left, width, extraDataset);
2930
+ this.#renderSelectionBlock(renderCtx, type, line, 0, left, width, extraDataset, {
2931
+ start: connectedCaret?.line === start.line && connectedCaret.character === start.character && line === start.line,
2932
+ end: connectedCaret?.line === end.line && connectedCaret.character === end.character && line === end.line
2933
+ });
2189
2934
  }
2190
2935
  }
2191
- #renderWrappedSelection(renderCtx, line, lineText, startChar, endChar, isLastLine, type, extraDataset) {
2936
+ #renderWrappedSelection(renderCtx, line, lineText, startChar, endChar, isLastLine, type, extraDataset, connectedCaret) {
2192
2937
  const wrapOffsets = this.#wrapLineTextOrWholeLine(line);
2193
2938
  const segmentCount = wrapOffsets.length - 1;
2194
2939
  const offsetLeft = this.#getGutterWidth() + this.#metrics.ch + (this.#activeContentOffset?.left ?? 0);
@@ -2203,7 +2948,10 @@ var Editor = class {
2203
2948
  let paddingEnd = 0;
2204
2949
  if (!isLastLine && wrapLine === segmentCount - 1 && type === "selection") paddingEnd = this.#metrics.ch;
2205
2950
  const segmentWidth = wrapStartChar === wrapEndChar ? paddingEnd : this.#segmentTextWidth(lineText, segmentStart, wrapEndChar) - segmentStartWidth + paddingEnd;
2206
- this.#renderSelectionBlock(renderCtx, type, line, wrapLine, segmentLeft, segmentWidth, extraDataset);
2951
+ this.#renderSelectionBlock(renderCtx, type, line, wrapLine, segmentLeft, segmentWidth, extraDataset, {
2952
+ start: connectedCaret?.line === line && connectedCaret.character === startChar && wrapStartChar === startChar,
2953
+ end: connectedCaret?.line === line && connectedCaret.character === endChar && wrapEndChar === endChar
2954
+ });
2207
2955
  }
2208
2956
  }
2209
2957
  #segmentTextWidth(lineText, segmentStart, character) {
@@ -2212,13 +2960,13 @@ var Editor = class {
2212
2960
  const asciiColumns = getExpandedAsciiTextColumns(segmentText, this.#metrics.tabSize);
2213
2961
  return asciiColumns !== -1 ? asciiColumns * this.#metrics.ch : this.#metrics.measureTextWidth(segmentText);
2214
2962
  }
2215
- #renderSelectionBlock(renderCtx, type, line, wrapLine, left, width, extraDataset) {
2963
+ #renderSelectionBlock(renderCtx, type, line, wrapLine, left, width, extraDataset, connectedCaretEdge) {
2216
2964
  if (width === 0) return;
2217
2965
  const { ch, lineHeight } = this.#metrics;
2218
2966
  const y = this.#getLineY(line) + wrapLine * lineHeight;
2219
2967
  const cacheKey = `${type}-${line}/${wrapLine}-${left}-${width} ${extraDataset ?? ""}`;
2220
2968
  const overlayEls = this.#overlayElements;
2221
- const rounded = (this.#options.roundedSelection ?? true) && type === "selection";
2969
+ const rounded = (this.#options.roundedSelection ?? true) && (type === "selection" || type === "caretHighlight");
2222
2970
  const addRoundedCorner = (line, wrapLine, left, radius) => {
2223
2971
  const top = this.#getLineY(line) + wrapLine * lineHeight;
2224
2972
  const lineElement = this.#getLineElement(line);
@@ -2248,7 +2996,7 @@ var Editor = class {
2248
2996
  cornerEl.style.cssText = css;
2249
2997
  overlayEls.delete(cacheKey);
2250
2998
  } else cornerEl = h("div", {
2251
- dataset: "selectionRange",
2999
+ dataset: type === "caretHighlight" ? "caretHighlightRange" : "selectionRange",
2252
3000
  style: { cssText: css },
2253
3001
  children: [h("div", { dataset })]
2254
3002
  }, renderCtx.fragment);
@@ -2293,6 +3041,14 @@ var Editor = class {
2293
3041
  dataset.rbl = "";
2294
3042
  dataset.rbr = "";
2295
3043
  }
3044
+ if (connectedCaretEdge?.start === true) {
3045
+ delete dataset.rtl;
3046
+ delete dataset.rbl;
3047
+ }
3048
+ if (connectedCaretEdge?.end === true) {
3049
+ delete dataset.rtr;
3050
+ delete dataset.rbr;
3051
+ }
2296
3052
  };
2297
3053
  let rangeEl = renderCtx.elements.get(cacheKey);
2298
3054
  if (rangeEl !== void 0) {
@@ -2305,6 +3061,10 @@ var Editor = class {
2305
3061
  } else rangeEl = h("div", { dataset: extraDataset ? [type + "Range", extraDataset] : type + "Range" }, renderCtx.fragment);
2306
3062
  rangeEl.style.width = `${width}px`;
2307
3063
  rangeEl.style.transform = `translateX(${left}px) translateY(${y}px)`;
3064
+ if (type === "editPredictionInsertion" || type === "editPredictionReplacement") {
3065
+ const lineElement = this.#getLineElement(line);
3066
+ if (lineElement !== void 0) rangeEl.style.setProperty("--diffs-edit-prediction-bg", getComputedStyle(lineElement).getPropertyValue("--diffs-line-bg"));
3067
+ } else rangeEl.style.removeProperty("--diffs-edit-prediction-bg");
2308
3068
  if (rounded) addRadiusStyle(rangeEl);
2309
3069
  renderCtx.elements.set(cacheKey, rangeEl);
2310
3070
  }
@@ -2327,7 +3087,7 @@ var Editor = class {
2327
3087
  #updateSelectionActionPopover() {
2328
3088
  const primarySelection = this.#selections?.at(-1);
2329
3089
  const overlayElement = this.#overlayElement;
2330
- const textDocument = this.#textDocument;
3090
+ const textDocument = this.#editSession?.document;
2331
3091
  const renderSelectionAction = this.#options.renderSelectionAction;
2332
3092
  const cleanup = () => {
2333
3093
  this.#selectionAction?.cleanup();
@@ -2351,7 +3111,7 @@ var Editor = class {
2351
3111
  return getActiveSelection();
2352
3112
  },
2353
3113
  applyEdits: (edits) => this.applyEdits(edits),
2354
- getSelectionText: () => this.#textDocument?.getText(getActiveSelection()) ?? "",
3114
+ getSelectionText: () => this.#editSession?.document?.getText(getActiveSelection()) ?? "",
2355
3115
  replaceSelectionText: (text) => {
2356
3116
  this.#replaceSelectionText(text, [getActiveSelection()]);
2357
3117
  },
@@ -2421,7 +3181,7 @@ var Editor = class {
2421
3181
  }
2422
3182
  #renderSearchPanel(mode) {
2423
3183
  this.#searchPanel?.cleanup();
2424
- const textDocument = this.#textDocument;
3184
+ const textDocument = this.#editSession?.document;
2425
3185
  const preElement = this.#fileContainer?.shadowRoot?.querySelector("pre");
2426
3186
  const selections = this.#selections;
2427
3187
  if (textDocument === void 0 || preElement == null) return;
@@ -2504,13 +3264,13 @@ var Editor = class {
2504
3264
  this.#retainSearchPanelFocus = false;
2505
3265
  }
2506
3266
  #getSelectionText() {
2507
- const textDocument = this.#textDocument;
3267
+ const textDocument = this.#editSession?.document;
2508
3268
  const selections = this.#selections;
2509
3269
  if (textDocument === void 0 || selections === void 0) return "";
2510
3270
  return getSelectionText(textDocument, selections);
2511
3271
  }
2512
3272
  #getSelectionClipboardTexts() {
2513
- const textDocument = this.#textDocument;
3273
+ const textDocument = this.#editSession?.document;
2514
3274
  const selections = this.#selections;
2515
3275
  if (textDocument === void 0 || selections === void 0) return [];
2516
3276
  return getSelectionClipboardTexts(textDocument, selections);
@@ -2522,7 +3282,7 @@ var Editor = class {
2522
3282
  if (selectionTexts.length > 1) clipboardData.setData(MULTI_SELECTION_CLIPBOARD_TYPE, JSON.stringify(selectionTexts));
2523
3283
  }
2524
3284
  #cutSelectionText() {
2525
- const textDocument = this.#textDocument;
3285
+ const textDocument = this.#editSession?.document;
2526
3286
  const selections = this.#selections;
2527
3287
  if (textDocument === void 0 || selections === void 0 || selections.length === 0) return "";
2528
3288
  if (selections.some((selection) => isCollapsedSelection(selection))) {
@@ -2535,7 +3295,7 @@ var Editor = class {
2535
3295
  return text;
2536
3296
  }
2537
3297
  #applySelectionCutEdits(edits, nextSelectionOffsets) {
2538
- const textDocument = this.#textDocument;
3298
+ const textDocument = this.#editSession?.document;
2539
3299
  const selections = this.#selections;
2540
3300
  if (textDocument === void 0 || selections === void 0 || edits.length === 0) return;
2541
3301
  const change = textDocument.applyResolvedEdits(edits, true, selections, void 0, true);
@@ -2553,7 +3313,7 @@ var Editor = class {
2553
3313
  }
2554
3314
  #replaceSelectionText(text, selections = this.#selections, undoBoundary = false, textOrder = "selection") {
2555
3315
  if (selections === void 0) return;
2556
- const textDocument = this.#textDocument;
3316
+ const textDocument = this.#editSession?.document;
2557
3317
  const primarySelection = selections.at(-1);
2558
3318
  if (textDocument === void 0 || primarySelection === void 0) return;
2559
3319
  const { nextSelections, change } = Array.isArray(text) && text.length === selections.length ? applyTextReplaceToSelections(textDocument, selections, text, this.#lineAnnotations, undoBoundary, textOrder) : applyTextChangeToSelections(textDocument, selections, {
@@ -2565,14 +3325,14 @@ var Editor = class {
2565
3325
  }
2566
3326
  #deleteSelectionText(forward = false) {
2567
3327
  const selections = this.#selections;
2568
- const textDocument = this.#textDocument;
3328
+ const textDocument = this.#editSession?.document;
2569
3329
  if (selections === void 0 || textDocument === void 0) return;
2570
3330
  const { nextSelections, change } = applyDeleteCharacterToSelections(textDocument, selections, forward, this.#lineAnnotations, this.#metrics.tabSize);
2571
3331
  if (change !== void 0) this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change));
2572
3332
  }
2573
3333
  #deleteSoftLineBackward() {
2574
3334
  const selections = this.#selections;
2575
- const textDocument = this.#textDocument;
3335
+ const textDocument = this.#editSession?.document;
2576
3336
  if (selections === void 0 || textDocument === void 0) return;
2577
3337
  const { nextSelections, change } = applyDeleteSoftLineBackwardToSelections(textDocument, selections, this.#isWrap ? (line, character) => {
2578
3338
  const wrapOffsets = this.#wrapLineTextOrWholeLine(line);
@@ -2587,35 +3347,33 @@ var Editor = class {
2587
3347
  }
2588
3348
  #deleteWordBackward() {
2589
3349
  const selections = this.#selections;
2590
- const textDocument = this.#textDocument;
3350
+ const textDocument = this.#editSession?.document;
2591
3351
  if (selections === void 0 || textDocument === void 0) return;
2592
3352
  const { nextSelections, change } = applyDeleteWordBackwardToSelections(textDocument, selections, this.#lineAnnotations);
2593
3353
  if (change !== void 0) this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change));
2594
3354
  }
2595
3355
  #deleteHardLineForward() {
2596
3356
  const selections = this.#selections;
2597
- const textDocument = this.#textDocument;
3357
+ const textDocument = this.#editSession?.document;
2598
3358
  if (selections === void 0 || textDocument === void 0) return;
2599
3359
  const { nextSelections, change } = applyDeleteHardLineForwardToSelections(textDocument, selections, this.#lineAnnotations);
2600
3360
  if (change !== void 0) this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change));
2601
3361
  }
2602
3362
  #insertTranspose() {
2603
3363
  const selections = this.#selections;
2604
- const textDocument = this.#textDocument;
3364
+ const textDocument = this.#editSession?.document;
2605
3365
  if (selections === void 0 || textDocument === void 0) return;
2606
3366
  const { nextSelections, change } = applyTransposeToSelections(textDocument, selections, this.#lineAnnotations);
2607
3367
  if (change !== void 0) this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change));
2608
3368
  }
2609
3369
  #applyChange(change, newSelections, newLineAnnotations, options) {
2610
- const fileRef = this.getFile();
2611
- const onChange = this.#options.onChange;
2612
- if (fileRef !== void 0 && onChange !== void 0) {
2613
- const lineAnnotations = newLineAnnotations ?? this.#lineAnnotations;
2614
- onChange(fileRef, lineAnnotations, {
2615
- changes: change.changes,
2616
- file: fileRef,
2617
- lineAnnotations
2618
- });
3370
+ if (this.#editPrediction != null) this.#cancelEditPrediction(true);
3371
+ const textDocument = this.#editSession?.document;
3372
+ if (textDocument !== void 0 && this.#carets !== void 0) for (const trackedCaret of this.#carets) {
3373
+ trackedCaret.anchorOffset = remapOffsetThroughEdits(trackedCaret.anchorOffset, change.changes);
3374
+ trackedCaret.focusOffset = remapOffsetThroughEdits(trackedCaret.focusOffset, change.changes);
3375
+ trackedCaret.caret.anchor = textDocument.positionAt(trackedCaret.anchorOffset);
3376
+ trackedCaret.caret.focus = textDocument.positionAt(trackedCaret.focusOffset);
2619
3377
  }
2620
3378
  if (change.lineDelta !== 0 || this.#isWrap) {
2621
3379
  for (const line of this.#lineYCache.keys()) if (line >= change.startLine) this.#lineYCache.delete(line);
@@ -2644,9 +3402,13 @@ var Editor = class {
2644
3402
  }
2645
3403
  }
2646
3404
  this.#rerender(change, newLineAnnotations, renderRange, shouldUpdateBuffer);
3405
+ if (newSelections != null) this.#updateSelections(newSelections);
3406
+ this.#checkpointEditSessionState();
3407
+ this.#recordEditPredictionHistory(change, options?.editSource ?? "user");
3408
+ this.#scheduleEditPrediction();
3409
+ this.#emitChange(change.changes, newLineAnnotations ?? this.#lineAnnotations);
2647
3410
  if (options?.skipSearchRefresh !== true && this.#searchPanel !== void 0 && this.#matches !== void 0) this.#searchPanel.updateMatches({ syncSelection: false });
2648
- if (newSelections !== void 0) {
2649
- this.#updateSelections(newSelections);
3411
+ if (newSelections != null) {
2650
3412
  if (options?.skipFocus !== true) {
2651
3413
  const revealTarget = newSelections.at(-1);
2652
3414
  if (revealTarget !== void 0) this.#revealLineIfCollapsed(getCaretPosition(revealTarget).line);
@@ -2664,11 +3426,17 @@ var Editor = class {
2664
3426
  }
2665
3427
  }
2666
3428
  }
3429
+ #emitChange(changes, lineAnnotations) {
3430
+ const file = this.getFile();
3431
+ const publishChange = this.#publishChange;
3432
+ if (file == null || publishChange == null) return;
3433
+ publishChange(changes, file, lineAnnotations);
3434
+ }
2667
3435
  #applyChangeToLineAnnotations(change) {
2668
- if (this.#lineAnnotations !== void 0) {
3436
+ if (this.#lineAnnotations != null) {
2669
3437
  const nextLineAnnotations = applyDocumentChangeToLineAnnotations(change, this.#lineAnnotations);
2670
- if (nextLineAnnotations !== void 0) {
2671
- this.#textDocument?.setLastUndoLineAnnotations(this.#lineAnnotations, nextLineAnnotations);
3438
+ if (nextLineAnnotations != null) {
3439
+ this.#editSession?.document?.setLastUndoLineAnnotations(this.#lineAnnotations, nextLineAnnotations);
2672
3440
  return nextLineAnnotations;
2673
3441
  }
2674
3442
  }
@@ -2689,11 +3457,15 @@ var Editor = class {
2689
3457
  }
2690
3458
  return ranges;
2691
3459
  }
3460
+ #isLineInRenderRange(line) {
3461
+ const renderRange = this.#renderRange;
3462
+ return renderRange == null || line >= renderRange.startingLine && line < renderRange.startingLine + renderRange.totalLines;
3463
+ }
2692
3464
  #getLineElement(line) {
2693
3465
  let lineElement = this.#lineElementsCache.get(line);
2694
3466
  if (lineElement !== void 0) return lineElement ?? void 0;
2695
3467
  const renderRange = this.#renderRange;
2696
- if (renderRange !== void 0 && (line < renderRange.startingLine || line >= renderRange.startingLine + renderRange.totalLines)) return;
3468
+ if (!this.#isLineInRenderRange(line)) return;
2697
3469
  const contentElement = this.#contentElement;
2698
3470
  if (contentElement === void 0) return;
2699
3471
  if (renderRange !== void 0) {
@@ -2744,7 +3516,7 @@ var Editor = class {
2744
3516
  }
2745
3517
  #getCharX(line, char) {
2746
3518
  if (this.#lastAccessedCharX !== void 0 && this.#lastAccessedCharX[0] === line && this.#lastAccessedCharX[1] === char) return [this.#lastAccessedCharX[2], this.#lastAccessedCharX[3]];
2747
- const lineText = this.#textDocument?.getLineText(line);
3519
+ const lineText = this.#editSession?.document?.getLineText(line);
2748
3520
  const offsetLeft = this.#getGutterWidth() + this.#metrics.ch;
2749
3521
  if (lineText === void 0 || lineText.length === 0 || char <= 0) return [offsetLeft + (this.#activeContentOffset?.left ?? 0), 0];
2750
3522
  const boundedCharacter = snapTextOffsetToUnicodeBoundary(lineText, Math.min(char, lineText.length));
@@ -2790,7 +3562,7 @@ var Editor = class {
2790
3562
  const cachedOffsets = this.#wrapLineOffsetsCache.get(line);
2791
3563
  if (cachedOffsets !== void 0) return cachedOffsets;
2792
3564
  if (this.#wrapLineOffsetsCache.size >= MAX_WRAP_OFFSETS_CACHE_LINES) this.#wrapLineOffsetsCache.clear();
2793
- const lineText = this.#textDocument?.getLineText(line);
3565
+ const lineText = this.#editSession?.document?.getLineText(line);
2794
3566
  if (lineText === void 0 || lineText.length === 0) {
2795
3567
  const offsets = new Uint32Array([0]);
2796
3568
  this.#wrapLineOffsetsCache.set(line, offsets);
@@ -2828,7 +3600,8 @@ var Editor = class {
2828
3600
  const measureGrapheme = (index) => {
2829
3601
  range.setStart(textNode, graphemeStart(index));
2830
3602
  range.setEnd(textNode, graphemeStart(index + 1));
2831
- return range.getBoundingClientRect();
3603
+ const clientRects = range.getClientRects();
3604
+ return clientRects.item(clientRects.length - 1) ?? range.getBoundingClientRect();
2832
3605
  };
2833
3606
  starts.push(0);
2834
3607
  let lastTop = measureGrapheme(0).top;
@@ -2860,7 +3633,7 @@ var Editor = class {
2860
3633
  #wrapLineTextOrWholeLine(line) {
2861
3634
  const offsets = this.#wrapLineText(line);
2862
3635
  if (offsets !== void 0) return offsets;
2863
- const length = this.#textDocument?.getLineText(line)?.length ?? 0;
3636
+ const length = this.#editSession?.document?.getLineText(line)?.length ?? 0;
2864
3637
  return Uint32Array.of(0, length);
2865
3638
  }
2866
3639
  #rangeBelongsToEditor({ startContainer, endContainer }) {
@@ -2869,11 +3642,31 @@ var Editor = class {
2869
3642
  return contentEl.contains(startContainer) && contentEl.contains(endContainer);
2870
3643
  }
2871
3644
  #isLineVisible(line) {
2872
- const lineCount = this.#textDocument?.lineCount ?? 0;
3645
+ const lineCount = this.#editSession?.document?.lineCount ?? 0;
2873
3646
  if (line < 0 || line >= lineCount) return false;
2874
3647
  return this.#getLineElement(line) !== void 0;
2875
3648
  }
2876
3649
  };
3650
+ function getEditSession({ type, editStateKey, owner, initialState, previousSession }) {
3651
+ if (initialState != null && initialState.type !== type) throw new TypeError(`Editor: initialState: a ${initialState.type} state cannot initialize a ${type} editor`);
3652
+ const initialSession = initialState;
3653
+ if (editStateKey != null) return EditStateManager.activate(type, editStateKey, owner, initialSession);
3654
+ if (initialSession != null) return initialSession;
3655
+ if (previousSession != null) return previousSession;
3656
+ return type === "file" ? { type: "file" } : { type: "file-diff" };
3657
+ }
3658
+ function gridTrackSpan(child) {
3659
+ if (!(child instanceof HTMLElement)) return 1;
3660
+ return Number(child.dataset.bufferSize ?? 1);
3661
+ }
3662
+ function isValidEditPredictionPosition(document, position) {
3663
+ return position !== void 0 && Number.isInteger(position.line) && Number.isInteger(position.character) && position.line >= 0 && position.line < document.lineCount && position.character >= 0 && position.character <= document.getLineLength(position.line);
3664
+ }
3665
+ function splitsSurrogatePair(text, offset) {
3666
+ const previous = text.charCodeAt(offset - 1);
3667
+ const next = text.charCodeAt(offset);
3668
+ return offset > 0 && offset < text.length && previous >= 55296 && previous <= 56319 && next >= 56320 && next <= 57343;
3669
+ }
2877
3670
  //#endregion
2878
3671
  export { Editor };
2879
3672