@pierre/diffs 1.3.0-beta.9 → 1.3.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CodeView.d.ts +12 -12
- package/dist/components/CodeView.d.ts.map +1 -1
- package/dist/components/CodeView.js +49 -42
- package/dist/components/CodeView.js.map +1 -1
- package/dist/components/File.d.ts +7 -2
- package/dist/components/File.d.ts.map +1 -1
- package/dist/components/File.js +36 -8
- package/dist/components/File.js.map +1 -1
- package/dist/components/FileDiff.d.ts +34 -6
- package/dist/components/FileDiff.d.ts.map +1 -1
- package/dist/components/FileDiff.js +198 -17
- package/dist/components/FileDiff.js.map +1 -1
- package/dist/components/VirtualizedFile.d.ts +2 -1
- package/dist/components/VirtualizedFile.d.ts.map +1 -1
- package/dist/components/VirtualizedFile.js +9 -5
- package/dist/components/VirtualizedFile.js.map +1 -1
- package/dist/components/VirtualizedFileDiff.d.ts +14 -0
- package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
- package/dist/components/VirtualizedFileDiff.js +61 -3
- package/dist/components/VirtualizedFileDiff.js.map +1 -1
- package/dist/components/Virtualizer.d.ts +14 -2
- package/dist/components/Virtualizer.d.ts.map +1 -1
- package/dist/components/Virtualizer.js +27 -2
- package/dist/components/Virtualizer.js.map +1 -1
- package/dist/edit/index.d.ts +5 -0
- package/dist/edit/index.js +3 -0
- package/dist/editor/command.d.ts +1 -1
- package/dist/editor/command.d.ts.map +1 -1
- package/dist/editor/command.js +11 -0
- package/dist/editor/command.js.map +1 -1
- package/dist/editor/editStack.d.ts +6 -2
- package/dist/editor/editStack.d.ts.map +1 -1
- package/dist/editor/editStack.js +53 -12
- package/dist/editor/editStack.js.map +1 -1
- package/dist/editor/editor.d.ts +51 -23
- package/dist/editor/editor.d.ts.map +1 -1
- package/dist/editor/editor.js +978 -238
- package/dist/editor/editor.js.map +1 -1
- package/dist/editor/editor2.js +1 -1
- package/dist/editor/languages.d.ts +26 -0
- package/dist/editor/languages.d.ts.map +1 -0
- package/dist/editor/languages.js +363 -0
- package/dist/editor/languages.js.map +1 -0
- package/dist/editor/lineAnnotations.d.ts.map +1 -1
- package/dist/editor/lineAnnotations.js +25 -26
- package/dist/editor/lineAnnotations.js.map +1 -1
- package/dist/editor/marker.d.ts +5 -3
- package/dist/editor/marker.d.ts.map +1 -1
- package/dist/editor/marker.js +111 -91
- package/dist/editor/marker.js.map +1 -1
- package/dist/editor/matchBrackets.d.ts +3 -2
- package/dist/editor/matchBrackets.d.ts.map +1 -1
- package/dist/editor/matchBrackets.js +2 -10
- package/dist/editor/matchBrackets.js.map +1 -1
- package/dist/editor/pieceTable.d.ts +2 -1
- package/dist/editor/pieceTable.d.ts.map +1 -1
- package/dist/editor/pieceTable.js +103 -39
- package/dist/editor/pieceTable.js.map +1 -1
- package/dist/editor/platform.js +2 -0
- package/dist/editor/platform.js.map +1 -1
- package/dist/editor/popover.d.ts +25 -5
- package/dist/editor/popover.d.ts.map +1 -1
- package/dist/editor/popover.js +60 -14
- package/dist/editor/popover.js.map +1 -1
- package/dist/editor/searchPanel.d.ts.map +1 -1
- package/dist/editor/searchPanel.js +9 -2
- package/dist/editor/searchPanel.js.map +1 -1
- package/dist/editor/selection.d.ts +31 -11
- package/dist/editor/selection.d.ts.map +1 -1
- package/dist/editor/selection.js +198 -70
- package/dist/editor/selection.js.map +1 -1
- package/dist/editor/selectionAction.d.ts +4 -3
- package/dist/editor/selectionAction.d.ts.map +1 -1
- package/dist/editor/selectionAction.js +9 -5
- package/dist/editor/selectionAction.js.map +1 -1
- package/dist/editor/stateStorage.d.ts +15 -0
- package/dist/editor/stateStorage.d.ts.map +1 -0
- package/dist/editor/stateStorage.js +65 -0
- package/dist/editor/stateStorage.js.map +1 -0
- package/dist/editor/textDocument.d.ts +12 -77
- package/dist/editor/textDocument.d.ts.map +1 -1
- package/dist/editor/textDocument.js +76 -34
- package/dist/editor/textDocument.js.map +1 -1
- package/dist/editor/textMeasure.d.ts +7 -12
- package/dist/editor/textMeasure.d.ts.map +1 -1
- package/dist/editor/textMeasure.js +40 -32
- package/dist/editor/textMeasure.js.map +1 -1
- package/dist/editor/{tokenzier.d.ts → tokenizer.d.ts} +3 -3
- package/dist/editor/tokenizer.d.ts.map +1 -0
- package/dist/editor/{tokenzier.js → tokenizer.js} +91 -26
- package/dist/editor/tokenizer.js.map +1 -0
- package/dist/editor/utils.js +1 -1
- package/dist/editor/utils.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +6 -5
- package/dist/managers/InteractionManager.d.ts +18 -7
- package/dist/managers/InteractionManager.d.ts.map +1 -1
- package/dist/managers/InteractionManager.js +154 -99
- package/dist/managers/InteractionManager.js.map +1 -1
- package/dist/managers/UniversalRenderingManager.d.ts.map +1 -1
- package/dist/managers/UniversalRenderingManager.js +1 -2
- package/dist/managers/UniversalRenderingManager.js.map +1 -1
- package/dist/react/CodeView.d.ts +13 -13
- package/dist/react/CodeView.d.ts.map +1 -1
- package/dist/react/CodeView.js +38 -29
- package/dist/react/CodeView.js.map +1 -1
- package/dist/react/EditContext.d.ts +20 -0
- package/dist/react/EditContext.d.ts.map +1 -0
- package/dist/react/EditContext.js +20 -0
- package/dist/react/EditContext.js.map +1 -0
- package/dist/react/File.d.ts +2 -1
- package/dist/react/File.d.ts.map +1 -1
- package/dist/react/File.js +3 -2
- package/dist/react/File.js.map +1 -1
- package/dist/react/FileDiff.d.ts +2 -2
- package/dist/react/FileDiff.d.ts.map +1 -1
- package/dist/react/FileDiff.js +3 -2
- package/dist/react/FileDiff.js.map +1 -1
- package/dist/react/MultiFileDiff.d.ts +2 -2
- package/dist/react/MultiFileDiff.d.ts.map +1 -1
- package/dist/react/MultiFileDiff.js +3 -2
- package/dist/react/MultiFileDiff.js.map +1 -1
- package/dist/react/PatchDiff.d.ts +2 -2
- package/dist/react/PatchDiff.d.ts.map +1 -1
- package/dist/react/PatchDiff.js +3 -2
- package/dist/react/PatchDiff.js.map +1 -1
- package/dist/react/UnresolvedFile.d.ts +1 -1
- package/dist/react/UnresolvedFile.js.map +1 -1
- package/dist/react/index.d.ts +4 -4
- package/dist/react/index.js +3 -3
- package/dist/react/types.d.ts +9 -1
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/utils/useFileDiffInstance.d.ts +5 -3
- package/dist/react/utils/useFileDiffInstance.d.ts.map +1 -1
- package/dist/react/utils/useFileDiffInstance.js +28 -27
- package/dist/react/utils/useFileDiffInstance.js.map +1 -1
- package/dist/react/utils/useFileInstance.d.ts +5 -3
- package/dist/react/utils/useFileInstance.d.ts.map +1 -1
- package/dist/react/utils/useFileInstance.js +29 -25
- package/dist/react/utils/useFileInstance.js.map +1 -1
- package/dist/react/utils/useUnresolvedFileInstance.js +3 -3
- package/dist/react/utils/useUnresolvedFileInstance.js.map +1 -1
- package/dist/renderers/DiffHunksRenderer.d.ts +33 -1
- package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
- package/dist/renderers/DiffHunksRenderer.js +134 -21
- package/dist/renderers/DiffHunksRenderer.js.map +1 -1
- package/dist/renderers/FileRenderer.d.ts +2 -0
- package/dist/renderers/FileRenderer.d.ts.map +1 -1
- package/dist/renderers/FileRenderer.js +33 -11
- package/dist/renderers/FileRenderer.js.map +1 -1
- package/dist/ssr/FileDiffReact.js +1 -1
- package/dist/ssr/index.d.ts +2 -2
- package/dist/ssr/preloadDiffs.js +1 -1
- package/dist/style.js +1 -1
- package/dist/types.d.ts +185 -42
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/annotationHelpers.d.ts +20 -0
- package/dist/utils/annotationHelpers.d.ts.map +1 -0
- package/dist/utils/annotationHelpers.js +29 -0
- package/dist/utils/annotationHelpers.js.map +1 -0
- package/dist/utils/computeFileOffsets.d.ts.map +1 -1
- package/dist/utils/computeFileOffsets.js +34 -5
- package/dist/utils/computeFileOffsets.js.map +1 -1
- package/dist/utils/editSessionHunks.d.ts +66 -0
- package/dist/utils/editSessionHunks.d.ts.map +1 -0
- package/dist/utils/editSessionHunks.js +437 -0
- package/dist/utils/editSessionHunks.js.map +1 -0
- package/dist/utils/getHunkSideBoundaries.d.ts +7 -0
- package/dist/utils/getHunkSideBoundaries.d.ts.map +1 -0
- package/dist/utils/getHunkSideBoundaries.js +12 -0
- package/dist/utils/getHunkSideBoundaries.js.map +1 -0
- package/dist/utils/getTotalLineCountFromHunks.d.ts.map +1 -1
- package/dist/utils/getTotalLineCountFromHunks.js +2 -1
- package/dist/utils/getTotalLineCountFromHunks.js.map +1 -1
- package/dist/utils/hydratePartialDiff.d.ts.map +1 -1
- package/dist/utils/hydratePartialDiff.js +4 -3
- package/dist/utils/hydratePartialDiff.js.map +1 -1
- package/dist/utils/isGutterUtilityPath.d.ts +5 -0
- package/dist/utils/isGutterUtilityPath.d.ts.map +1 -0
- package/dist/utils/isGutterUtilityPath.js +12 -0
- package/dist/utils/isGutterUtilityPath.js.map +1 -0
- package/dist/utils/iterateOverDiff.d.ts.map +1 -1
- package/dist/utils/iterateOverDiff.js +17 -12
- package/dist/utils/iterateOverDiff.js.map +1 -1
- package/dist/utils/parseMergeConflictDiffFromFile.d.ts.map +1 -1
- package/dist/utils/parseMergeConflictDiffFromFile.js +9 -6
- package/dist/utils/parseMergeConflictDiffFromFile.js.map +1 -1
- package/dist/utils/parsePatchFiles.d.ts.map +1 -1
- package/dist/utils/parsePatchFiles.js +6 -3
- package/dist/utils/parsePatchFiles.js.map +1 -1
- package/dist/utils/realignChangeContent.d.ts +33 -0
- package/dist/utils/realignChangeContent.d.ts.map +1 -0
- package/dist/utils/realignChangeContent.js +148 -0
- package/dist/utils/realignChangeContent.js.map +1 -0
- package/dist/utils/renderDiffWithHighlighter.js +1 -1
- package/dist/utils/resolveRegion.d.ts.map +1 -1
- package/dist/utils/resolveRegion.js +18 -2
- package/dist/utils/resolveRegion.js.map +1 -1
- package/dist/utils/updateDiffHunks.d.ts +8 -2
- package/dist/utils/updateDiffHunks.d.ts.map +1 -1
- package/dist/utils/updateDiffHunks.js +9 -7
- package/dist/utils/updateDiffHunks.js.map +1 -1
- package/dist/utils/virtualDiffLayout.d.ts +42 -2
- package/dist/utils/virtualDiffLayout.d.ts.map +1 -1
- package/dist/utils/virtualDiffLayout.js +101 -7
- package/dist/utils/virtualDiffLayout.js.map +1 -1
- package/dist/worker/worker-portable.js +60 -20
- package/dist/worker/worker-portable.js.map +1 -1
- package/dist/worker/worker.js +61 -19
- package/dist/worker/worker.js.map +1 -1
- package/package.json +10 -7
- package/dist/editor/index.d.ts +0 -3
- package/dist/editor/index.js +0 -3
- package/dist/editor/tokenzier.d.ts.map +0 -1
- package/dist/editor/tokenzier.js.map +0 -1
- package/dist/react/EditorContext.d.ts +0 -15
- package/dist/react/EditorContext.d.ts.map +0 -1
- package/dist/react/EditorContext.js +0 -23
- package/dist/react/EditorContext.js.map +0 -1
package/dist/editor/editor.js
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import { dequeueRender, queueRender } from "../managers/UniversalRenderingManager.js";
|
|
2
|
+
import { isGutterUtilityPath } from "../utils/isGutterUtilityPath.js";
|
|
2
3
|
import { getFiletypeFromFileName } from "../utils/getFiletypeFromFileName.js";
|
|
3
4
|
import { isMoveCursorShortcut, isPrimaryModifier, isSafari } from "./platform.js";
|
|
4
5
|
import { resolveEditorCommandFromKeyboardEvent, resolveFindAgainShortcut } from "./command.js";
|
|
5
|
-
import { EditStack } from "./editStack.js";
|
|
6
6
|
import editor_default from "./editor2.js";
|
|
7
|
+
import { EditStack } from "./editStack.js";
|
|
8
|
+
import { resolveBlockCommentEdits, resolveCommentConfig, resolveLineCommentEdits } from "./languages.js";
|
|
7
9
|
import { addEventListener, clampDomOffset, extend, getLineNumberAttr, h, round } from "./utils.js";
|
|
8
10
|
import { applyDocumentChangeToLineAnnotations, renderLineAnnotations } from "./lineAnnotations.js";
|
|
9
11
|
import { PopoverManager } from "./popover.js";
|
|
10
|
-
import { applyDeleteCharacterToSelections, applyDeleteHardLineForwardToSelections, applyDeleteSoftLineBackwardToSelections, applyDeleteWordBackwardToSelections, applyTextChangeToSelections, applyTextReplaceToSelections, applyTransposeToSelections, comparePosition, convertSelection, createSelectionFrom, createSelectionFromAnchorAndFocusOffsets, expandCollapsedSelectionToWord, extendSelection, extendSelections,
|
|
12
|
+
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";
|
|
11
13
|
import { MarkerRenderer, markerSeverityDatasetKey } from "./marker.js";
|
|
12
14
|
import { findBracketMatchRanges } from "./matchBrackets.js";
|
|
13
15
|
import { createSpriteElement } from "./sprite.js";
|
|
14
16
|
import { SearchPanelWidget } from "./searchPanel.js";
|
|
15
17
|
import { SelectionActionWidget } from "./selectionAction.js";
|
|
18
|
+
import { cloneEditorState, createStateStorage } from "./stateStorage.js";
|
|
16
19
|
import { TextDocument } from "./textDocument.js";
|
|
17
20
|
import { Metrics, getExpandedAsciiTextColumns, getUnicodeMeasurementOffsets, snapTextOffsetToUnicodeBoundary } from "./textMeasure.js";
|
|
18
|
-
import { EditorTokenizer, renderLineTokens } from "./
|
|
21
|
+
import { EditorTokenizer, renderLineTokens } from "./tokenizer.js";
|
|
19
22
|
//#region src/editor/editor.ts
|
|
20
23
|
function getShadowRootRange(shadowRoot) {
|
|
21
24
|
const selection = shadowRoot.getSelection?.();
|
|
@@ -29,16 +32,38 @@ function getShadowRootRange(shadowRoot) {
|
|
|
29
32
|
endOffset: range.endOffset
|
|
30
33
|
};
|
|
31
34
|
}
|
|
35
|
+
function requirePersistedCacheKey(file) {
|
|
36
|
+
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.`);
|
|
37
|
+
return file.cacheKey;
|
|
38
|
+
}
|
|
39
|
+
function isPromise(value) {
|
|
40
|
+
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
41
|
+
}
|
|
32
42
|
const MAX_EDIT_WIDEN_WINDOW_MULTIPLE = 2;
|
|
43
|
+
const MAX_WRAP_OFFSETS_CACHE_LINES = 1e4;
|
|
44
|
+
const SELECTION_ACTION_POPOVER_PLACEMENT_KEY = "selection-action";
|
|
45
|
+
const MULTI_SELECTION_CLIPBOARD_TYPE = "application/vnd.pierre.diffs-selections+json";
|
|
33
46
|
var Editor = class {
|
|
34
47
|
#options;
|
|
35
48
|
#metrics = new Metrics();
|
|
36
49
|
#tokenizer;
|
|
37
50
|
#popoverManager;
|
|
51
|
+
#textDocumentCache = /* @__PURE__ */ new Map();
|
|
52
|
+
#stateStorage;
|
|
53
|
+
#stateStorageOption;
|
|
54
|
+
#pendingStateWrites = /* @__PURE__ */ new Map();
|
|
55
|
+
#pendingStateRestore;
|
|
56
|
+
#stateRestoreGeneration = 0;
|
|
57
|
+
#restoreStateOnNextSync = false;
|
|
38
58
|
#editorEventDisposes;
|
|
39
59
|
#globalEventDisposes;
|
|
40
60
|
#selectEventDisposes;
|
|
41
61
|
#detach;
|
|
62
|
+
#attachState = {
|
|
63
|
+
generation: 0,
|
|
64
|
+
callback: void 0,
|
|
65
|
+
delivered: false
|
|
66
|
+
};
|
|
42
67
|
#contentOffset;
|
|
43
68
|
#gutterWidthCache;
|
|
44
69
|
#contentWidthCache;
|
|
@@ -66,9 +91,11 @@ var Editor = class {
|
|
|
66
91
|
#markerRenderer;
|
|
67
92
|
#searchPanel;
|
|
68
93
|
#selectionAction;
|
|
94
|
+
#canMountSelectionAction = true;
|
|
69
95
|
#shouldIgnoreSelectionChange = false;
|
|
70
96
|
#suppressNativeSelectionSync = false;
|
|
71
97
|
#contentHasFocus = false;
|
|
98
|
+
#replacementFocusRequest;
|
|
72
99
|
#isComposing = false;
|
|
73
100
|
#isGutterMouseDown = false;
|
|
74
101
|
#isContentMouseDown = false;
|
|
@@ -87,13 +114,13 @@ var Editor = class {
|
|
|
87
114
|
#fontRemeasureScheduled = false;
|
|
88
115
|
#themeSelectionRefreshFrame;
|
|
89
116
|
#onDeferTokenize = (lines, themeType) => {
|
|
90
|
-
this.#fileInstance?.updateRenderCache(lines, themeType, false);
|
|
117
|
+
this.#fileInstance?.updateRenderCache(lines, themeType, false, false);
|
|
91
118
|
if (this.#renderRange !== void 0 && this.#renderRange.totalLines !== Infinity) {
|
|
92
119
|
const { startingLine, totalLines } = this.#renderRange;
|
|
93
120
|
const endLine = Math.min(startingLine + totalLines, this.#textDocument?.lineCount ?? 0);
|
|
94
121
|
for (const [line, tokens] of lines) if (line >= startingLine && line < endLine) {
|
|
95
122
|
const lineElement = this.#getLineElement(line);
|
|
96
|
-
if (lineElement !== void 0) lineElement.replaceChildren(...renderLineTokens(tokens
|
|
123
|
+
if (lineElement !== void 0) lineElement.replaceChildren(...renderLineTokens(tokens));
|
|
97
124
|
}
|
|
98
125
|
}
|
|
99
126
|
};
|
|
@@ -101,21 +128,38 @@ var Editor = class {
|
|
|
101
128
|
this.#options = options;
|
|
102
129
|
}
|
|
103
130
|
setOptions(options) {
|
|
104
|
-
this.#options
|
|
131
|
+
const previousStorageOption = this.#options.persistStateStorage ?? "inMemory";
|
|
132
|
+
const nextOptions = {
|
|
105
133
|
...this.#options,
|
|
106
134
|
...options
|
|
107
135
|
};
|
|
136
|
+
if (nextOptions.persistState === true && this.#fileInstance?.type === "file") {
|
|
137
|
+
const file = this.#fileInstance.__getCurrentFile?.() ?? this.#fileInfo;
|
|
138
|
+
if (file !== void 0) requirePersistedCacheKey(file);
|
|
139
|
+
}
|
|
140
|
+
this.#options = nextOptions;
|
|
141
|
+
if (this.#options.persistState !== true) {
|
|
142
|
+
this.#textDocumentCache.clear();
|
|
143
|
+
this.#stateRestoreGeneration++;
|
|
144
|
+
this.#restoreStateOnNextSync = false;
|
|
145
|
+
}
|
|
146
|
+
if ((this.#options.persistStateStorage ?? "inMemory") !== previousStorageOption) {
|
|
147
|
+
this.#stateRestoreGeneration++;
|
|
148
|
+
this.#stateStorage = void 0;
|
|
149
|
+
this.#stateStorageOption = void 0;
|
|
150
|
+
this.#pendingStateWrites.clear();
|
|
151
|
+
}
|
|
108
152
|
}
|
|
109
153
|
edit(fileInstance) {
|
|
110
|
-
|
|
111
|
-
|
|
154
|
+
if (this.#options.persistState === true && fileInstance.type === "file") {
|
|
155
|
+
const file = fileInstance.__getCurrentFile?.();
|
|
156
|
+
if (file !== void 0) requirePersistedCacheKey(file);
|
|
157
|
+
}
|
|
158
|
+
this.#invalidateOnAttach();
|
|
159
|
+
if (fileInstance.options.useTokenTransformer !== true) {
|
|
112
160
|
fileInstance.setOptions({
|
|
113
|
-
...
|
|
114
|
-
useTokenTransformer: true
|
|
115
|
-
enableGutterUtility: false,
|
|
116
|
-
enableLineSelection: false,
|
|
117
|
-
expandUnchanged: true,
|
|
118
|
-
lineHoverHighlight: "disabled"
|
|
161
|
+
...fileInstance.options,
|
|
162
|
+
useTokenTransformer: true
|
|
119
163
|
});
|
|
120
164
|
fileInstance.rerender();
|
|
121
165
|
}
|
|
@@ -125,23 +169,22 @@ var Editor = class {
|
|
|
125
169
|
return () => this.cleanUp();
|
|
126
170
|
}
|
|
127
171
|
/**
|
|
128
|
-
* Apply edits to current attached file.
|
|
172
|
+
* Apply edits to current attached file. Every edit joins the undo timeline:
|
|
173
|
+
* a programmatic edit must leave the document and its history exactly as
|
|
174
|
+
* the same edit typed by the user would (history equivalence — see
|
|
175
|
+
* TextDocument.applyResolvedEdits), so it is undoable like any other edit.
|
|
176
|
+
*
|
|
177
|
+
* @param updateHistory Whether to record caller selection snapshots for
|
|
178
|
+
* exact undo/redo restoration. Defaults to true. When false, live selections
|
|
179
|
+
* are remapped during replay and the text edit still joins the undo timeline.
|
|
129
180
|
*/
|
|
130
|
-
applyEdits(edits, updateHistory =
|
|
181
|
+
applyEdits(edits, updateHistory = true) {
|
|
131
182
|
const textDocument = this.#textDocument;
|
|
132
183
|
if (textDocument == null) throw new Error("Editor is not attached");
|
|
133
184
|
const wasFocused = this.#contentHasFocus;
|
|
134
185
|
const selectionsBefore = this.#selections;
|
|
135
186
|
const selectionOffsetsBefore = selectionsBefore?.map((selection) => [textDocument.offsetAt(selection.start), textDocument.offsetAt(selection.end)]);
|
|
136
|
-
const resolvedEditOffsets = selectionsBefore === void 0 ? void 0 : edits.
|
|
137
|
-
const a = textDocument.offsetAt(edit.range.start);
|
|
138
|
-
const b = textDocument.offsetAt(edit.range.end);
|
|
139
|
-
return {
|
|
140
|
-
start: Math.min(a, b),
|
|
141
|
-
end: Math.max(a, b),
|
|
142
|
-
text: edit.newText
|
|
143
|
-
};
|
|
144
|
-
}).sort((a, b) => a.start - b.start);
|
|
187
|
+
const resolvedEditOffsets = selectionsBefore === void 0 ? void 0 : textDocument.resolveEdits(edits).sort((a, b) => a.start - b.start);
|
|
145
188
|
const change = textDocument.applyEdits(edits, updateHistory, selectionsBefore);
|
|
146
189
|
if (change === void 0) return;
|
|
147
190
|
let nextSelections;
|
|
@@ -167,45 +210,58 @@ var Editor = class {
|
|
|
167
210
|
redo() {
|
|
168
211
|
this.#runCommand("redo");
|
|
169
212
|
}
|
|
213
|
+
getFile() {
|
|
214
|
+
const fileInfo = this.#fileInfo;
|
|
215
|
+
const textDocument = this.#textDocument;
|
|
216
|
+
if (fileInfo === void 0 || textDocument === void 0) return;
|
|
217
|
+
const file = { ...fileInfo };
|
|
218
|
+
Object.defineProperty(file, "contents", {
|
|
219
|
+
enumerable: true,
|
|
220
|
+
get: () => textDocument.getText()
|
|
221
|
+
});
|
|
222
|
+
return file;
|
|
223
|
+
}
|
|
224
|
+
getText() {
|
|
225
|
+
return this.#textDocument?.getText() ?? "";
|
|
226
|
+
}
|
|
170
227
|
getState() {
|
|
171
|
-
const
|
|
172
|
-
if (fileRef === void 0) throw new Error("Editor is not attached");
|
|
228
|
+
const fileInstance = this.#fileInstance;
|
|
173
229
|
return {
|
|
174
|
-
file: {
|
|
175
|
-
...fileRef,
|
|
176
|
-
cacheKey: "edited-at-" + Date.now()
|
|
177
|
-
},
|
|
178
230
|
selections: this.#selections,
|
|
179
|
-
|
|
180
|
-
renderRange: this.#renderRange
|
|
231
|
+
view: fileInstance != null ? { scrollLeft: fileInstance.getCodeScrollLeft() } : void 0
|
|
181
232
|
};
|
|
182
233
|
}
|
|
183
|
-
setState({
|
|
184
|
-
this.#
|
|
185
|
-
this.#
|
|
186
|
-
this.#
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
lineAnnotations,
|
|
193
|
-
renderRange
|
|
194
|
-
});
|
|
234
|
+
setState({ selections, view }) {
|
|
235
|
+
if (this.#fileInstance === void 0 || this.#textDocument === void 0) throw new Error("Editor is not attached");
|
|
236
|
+
this.#canMountSelectionAction = false;
|
|
237
|
+
this.#updateSelections(selections ?? []);
|
|
238
|
+
if (view != null) {
|
|
239
|
+
this.#fileInstance.setCodeScrollLeft(view.scrollLeft);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
this.#scrollToPrimaryCaret();
|
|
195
243
|
}
|
|
196
244
|
setSelections(selections) {
|
|
197
245
|
const textDocument = this.#textDocument;
|
|
198
246
|
if (textDocument === void 0) throw new Error("Text document is not initialized");
|
|
199
247
|
const resolvedSelections = selections.map((selection) => {
|
|
200
|
-
|
|
201
|
-
|
|
248
|
+
let start = textDocument.normalizePosition(selection.start);
|
|
249
|
+
let end = textDocument.normalizePosition(selection.end);
|
|
250
|
+
let direction = selection.direction === "none" ? 0 : selection.direction === "backward" ? -1 : 1;
|
|
251
|
+
if (comparePosition(start, end) > 0) {
|
|
252
|
+
[start, end] = [end, start];
|
|
253
|
+
if (direction !== 0) direction = direction === 1 ? -1 : 1;
|
|
254
|
+
}
|
|
202
255
|
return {
|
|
203
|
-
direction
|
|
256
|
+
direction,
|
|
204
257
|
start,
|
|
205
258
|
end
|
|
206
259
|
};
|
|
207
260
|
});
|
|
261
|
+
this.#canMountSelectionAction = false;
|
|
208
262
|
this.#updateSelections(resolvedSelections);
|
|
263
|
+
const primarySelection = resolvedSelections.at(-1);
|
|
264
|
+
if (primarySelection !== void 0) this.#revealLineIfCollapsed(getCaretPosition(primarySelection).line);
|
|
209
265
|
this.#scrollToPrimaryCaret();
|
|
210
266
|
}
|
|
211
267
|
setMarkers(markers) {
|
|
@@ -232,6 +288,19 @@ var Editor = class {
|
|
|
232
288
|
}
|
|
233
289
|
focus(options) {
|
|
234
290
|
const preventScroll = options?.preventScroll ?? false;
|
|
291
|
+
const lineNumber = options?.lineNumber;
|
|
292
|
+
if (lineNumber === "first-visible" || typeof lineNumber === "number") {
|
|
293
|
+
const textDocument = this.#textDocument;
|
|
294
|
+
if (textDocument == null || this.#fileInstance == null) return;
|
|
295
|
+
const targetLineNumber = lineNumber === "first-visible" ? this.#getFirstVisibleLineNumber(options?.offset) : lineNumber;
|
|
296
|
+
if (targetLineNumber == null) return;
|
|
297
|
+
const position = textDocument.normalizePosition({
|
|
298
|
+
line: targetLineNumber - 1,
|
|
299
|
+
character: lineNumber === "first-visible" ? 0 : options?.character ?? 0
|
|
300
|
+
});
|
|
301
|
+
this.#focusAtPosition(position, preventScroll);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
235
304
|
const primarySelection = this.#selections?.at(-1);
|
|
236
305
|
if (primarySelection !== void 0) {
|
|
237
306
|
const pos = primarySelection.direction === -1 ? primarySelection.end : primarySelection.start;
|
|
@@ -239,9 +308,19 @@ var Editor = class {
|
|
|
239
308
|
} else this.#focus(void 0, preventScroll);
|
|
240
309
|
}
|
|
241
310
|
blur() {
|
|
311
|
+
this.#replacementFocusRequest = void 0;
|
|
312
|
+
this.#contentHasFocus = false;
|
|
313
|
+
this.#shouldIgnoreSelectionChange = false;
|
|
242
314
|
this.#contentElement?.blur();
|
|
243
315
|
}
|
|
244
316
|
cleanUp(recycle = false) {
|
|
317
|
+
this.#invalidateOnAttach();
|
|
318
|
+
if (!recycle) this.#attachState.delivered = false;
|
|
319
|
+
const hadFileInstance = this.#fileInstance != null;
|
|
320
|
+
const shouldRestoreState = this.#isStatePersistenceEnabled;
|
|
321
|
+
this.#stateRestoreGeneration++;
|
|
322
|
+
this.#persistCurrentState();
|
|
323
|
+
if (hadFileInstance) this.#restoreStateOnNextSync = shouldRestoreState;
|
|
245
324
|
dequeueRender(this.#handleCustomPasteEvent);
|
|
246
325
|
this.#tokenizer?.cleanUp();
|
|
247
326
|
this.#tokenizer = void 0;
|
|
@@ -255,7 +334,7 @@ var Editor = class {
|
|
|
255
334
|
this.#editorEventDisposes = void 0;
|
|
256
335
|
this.#selectEventDisposes?.forEach((dispose) => dispose());
|
|
257
336
|
this.#selectEventDisposes = void 0;
|
|
258
|
-
this.#detach?.();
|
|
337
|
+
this.#detach?.(recycle);
|
|
259
338
|
this.#detach = void 0;
|
|
260
339
|
this.#gutterWidthCache = void 0;
|
|
261
340
|
this.#contentWidthCache = void 0;
|
|
@@ -277,6 +356,7 @@ var Editor = class {
|
|
|
277
356
|
this.#gutterElement = void 0;
|
|
278
357
|
this.#contentElement?.removeAttribute("contentEditable");
|
|
279
358
|
this.#contentElement = void 0;
|
|
359
|
+
this.#replacementFocusRequest = void 0;
|
|
280
360
|
this.#contentHasFocus = false;
|
|
281
361
|
this.#overlayElement?.remove();
|
|
282
362
|
this.#overlayElement = void 0;
|
|
@@ -288,6 +368,24 @@ var Editor = class {
|
|
|
288
368
|
this.#themeSelectionRefreshFrame = void 0;
|
|
289
369
|
}
|
|
290
370
|
this.#resetState();
|
|
371
|
+
this.#fileInstance = void 0;
|
|
372
|
+
}
|
|
373
|
+
/** @internal Capture outgoing state and substitute cached text before render. */
|
|
374
|
+
__prepareFile(file) {
|
|
375
|
+
if (this.#options.persistState !== true) return file;
|
|
376
|
+
const cacheKey = requirePersistedCacheKey(file);
|
|
377
|
+
const fileInfo = this.#fileInfo;
|
|
378
|
+
const languageId = file.lang ?? getFiletypeFromFileName(file.name);
|
|
379
|
+
if (fileInfo !== void 0 && (requirePersistedCacheKey(fileInfo) !== cacheKey || fileInfo.name !== file.name || this.#textDocument?.languageId !== languageId)) {
|
|
380
|
+
this.#stateRestoreGeneration++;
|
|
381
|
+
this.#persistCurrentState();
|
|
382
|
+
}
|
|
383
|
+
const textDocument = this.#getCachedTextDocument(file, cacheKey);
|
|
384
|
+
if (textDocument === void 0 || textDocument.getText() === file.contents) return file;
|
|
385
|
+
return {
|
|
386
|
+
...file,
|
|
387
|
+
contents: textDocument.getText()
|
|
388
|
+
};
|
|
291
389
|
}
|
|
292
390
|
/** @internal */
|
|
293
391
|
__postponeBgTokenizeToNextFrame() {
|
|
@@ -300,7 +398,15 @@ var Editor = class {
|
|
|
300
398
|
}
|
|
301
399
|
}
|
|
302
400
|
/** @internal */
|
|
401
|
+
__captureFocusForDOMReplacement() {
|
|
402
|
+
const contentElement = this.#contentElement;
|
|
403
|
+
const shadowActiveElement = this.#fileContainer?.shadowRoot?.activeElement;
|
|
404
|
+
if (contentElement != null && (this.#contentHasFocus || shadowActiveElement === contentElement || shadowActiveElement != null && contentElement.contains(shadowActiveElement))) this.#replacementFocusRequest = {};
|
|
405
|
+
}
|
|
406
|
+
/** @internal */
|
|
303
407
|
__syncRenderView = (highlighter, fileContainer, fileOrDiff, lineAnnotations, renderRange) => {
|
|
408
|
+
const fileInstance = this.#fileInstance;
|
|
409
|
+
if (fileInstance == null) return;
|
|
304
410
|
const shadowRoot = fileContainer.shadowRoot;
|
|
305
411
|
if (shadowRoot == null) {
|
|
306
412
|
console.error("[editor] Could not find the shadow root.");
|
|
@@ -319,7 +425,10 @@ var Editor = class {
|
|
|
319
425
|
}
|
|
320
426
|
break;
|
|
321
427
|
}
|
|
322
|
-
if (codeElement === void 0 || contentEl === void 0)
|
|
428
|
+
if (codeElement === void 0 || contentEl === void 0) {
|
|
429
|
+
this.#replacementFocusRequest = void 0;
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
323
432
|
this.#getPopoverManager().setViewportElements(fileContainer, codeElement);
|
|
324
433
|
if (this.#fileContainer !== fileContainer) {
|
|
325
434
|
this.#fileContainer = fileContainer;
|
|
@@ -328,28 +437,42 @@ var Editor = class {
|
|
|
328
437
|
if (this.#themeStyleElement !== void 0) shadowRoot.appendChild(this.#themeStyleElement);
|
|
329
438
|
if (this.#spriteElement !== void 0) shadowRoot.prepend(this.#spriteElement);
|
|
330
439
|
}
|
|
331
|
-
|
|
440
|
+
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;
|
|
441
|
+
const persistedCacheKey = this.#isStatePersistenceEnabled ? requirePersistedCacheKey(fileOrDiff) : void 0;
|
|
442
|
+
let persistedStateTarget;
|
|
443
|
+
if (shouldRebuildDocument) {
|
|
444
|
+
this.#invalidateOnAttach();
|
|
332
445
|
let contents = "";
|
|
333
446
|
if ("contents" in fileOrDiff) contents = fileOrDiff.contents;
|
|
334
447
|
else contents = fileOrDiff.additionLines.join("");
|
|
335
448
|
const editStack = new EditStack({ maxEntries: this.#options.historyMaxEntries });
|
|
336
|
-
const textDocument = new TextDocument(fileOrDiff.name, contents, fileOrDiff.lang ?? getFiletypeFromFileName(fileOrDiff.name), 0, editStack);
|
|
337
449
|
const { name, lang, cacheKey } = fileOrDiff;
|
|
450
|
+
const languageId = lang ?? getFiletypeFromFileName(fileOrDiff.name);
|
|
451
|
+
const textDocument = (persistedCacheKey !== void 0 ? this.#getCachedTextDocument(fileOrDiff, persistedCacheKey) : void 0) ?? new TextDocument(fileOrDiff.name, contents, languageId, 0, editStack);
|
|
338
452
|
this.#fileInfo = {
|
|
339
453
|
name,
|
|
340
454
|
lang,
|
|
341
455
|
cacheKey
|
|
342
456
|
};
|
|
343
457
|
this.#textDocument = textDocument;
|
|
458
|
+
if (persistedCacheKey !== void 0) {
|
|
459
|
+
this.#textDocumentCache.set(persistedCacheKey, textDocument);
|
|
460
|
+
persistedStateTarget = {
|
|
461
|
+
cacheKey: persistedCacheKey,
|
|
462
|
+
textDocument
|
|
463
|
+
};
|
|
464
|
+
}
|
|
344
465
|
this.#tokenizer?.cleanUp();
|
|
345
466
|
this.#tokenizer = void 0;
|
|
346
467
|
this.#resetState();
|
|
468
|
+
this.#wrapLineOffsetsCache.clear();
|
|
347
469
|
this.#selections = this.#initSelections;
|
|
348
|
-
requestAnimationFrame(() => {
|
|
349
|
-
this.#options.onAttach?.(this, this.#fileInstance);
|
|
350
|
-
});
|
|
351
470
|
if (this.#textDocument !== void 0 && this.#options.__debug === true) console.log("[diffs/editor] text document rebuilt from", fileOrDiff.name);
|
|
352
471
|
}
|
|
472
|
+
if (persistedStateTarget === void 0 && this.#restoreStateOnNextSync && persistedCacheKey !== void 0 && this.#textDocument !== void 0) persistedStateTarget = {
|
|
473
|
+
cacheKey: persistedCacheKey,
|
|
474
|
+
textDocument: this.#textDocument
|
|
475
|
+
};
|
|
353
476
|
const textDocument = this.#textDocument;
|
|
354
477
|
if (this.#tokenizer == null && textDocument != null) this.#tokenizer = new EditorTokenizer({
|
|
355
478
|
highlighter,
|
|
@@ -381,11 +504,12 @@ var Editor = class {
|
|
|
381
504
|
this.#listenContentElement(contentEl, gutterEl);
|
|
382
505
|
if (this.#contentElement !== void 0 && this.#options.__debug === true) console.log("[diffs/editor] full re-render triggered !!!");
|
|
383
506
|
}
|
|
507
|
+
if (this.#replacementFocusRequest != null) this.#replacementFocusRequest.target = contentEl;
|
|
384
508
|
if (contentEl.ariaLabel !== fileOrDiff.name) contentEl.ariaLabel = fileOrDiff.name;
|
|
385
|
-
if (lineAnnotations !== void 0 && lineAnnotations.length > 0 || this.#isDiff
|
|
509
|
+
if (lineAnnotations !== void 0 && lineAnnotations.length > 0 || this.#isDiff) for (const child of this.#contentElement.children) {
|
|
386
510
|
const el = child;
|
|
387
|
-
const { lineAnnotation, lineType } = el.dataset;
|
|
388
|
-
if (lineAnnotation !== void 0 || lineType === "change-deletion") el.setAttribute("contenteditable", "false");
|
|
511
|
+
const { lineAnnotation, lineType, separator } = el.dataset;
|
|
512
|
+
if (lineAnnotation !== void 0 || separator !== void 0 || lineType === "change-deletion") el.setAttribute("contenteditable", "false");
|
|
389
513
|
}
|
|
390
514
|
this.#resetCache();
|
|
391
515
|
this.#tokenizer?.syncTheme(this.#fileInstance?.options ?? {});
|
|
@@ -393,19 +517,140 @@ var Editor = class {
|
|
|
393
517
|
this.#renderRange = renderRange;
|
|
394
518
|
this.#viewportWindowLines = renderRange?.totalLines;
|
|
395
519
|
this.#tokenizer?.prebuildStateStack(renderRange);
|
|
396
|
-
this.#markerRenderer?.
|
|
520
|
+
this.#markerRenderer?.removePopover();
|
|
397
521
|
if (this.#selections !== void 0 || this.#matches !== void 0 || this.#markerRenderer !== void 0) this.#updateSelections(this.#selections ?? []);
|
|
398
522
|
if (this.#initSelections !== void 0 && this.#primaryCaretElement !== void 0) {
|
|
399
523
|
this.#initSelections = void 0;
|
|
400
524
|
this.#scrollToPrimaryCaret(false, "center");
|
|
401
525
|
} else if (this.#scrollingToLine !== void 0) this.#scrollToLine(this.#scrollingToLine, this.#scrollingToLineChar, this.#scrollingToLineNoFocus);
|
|
402
|
-
else if (this.#
|
|
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 });
|
|
403
528
|
if (this.#retainSearchPanelFocus) this.#searchPanel?.focus();
|
|
404
529
|
if (this.#options.__debug === true && renderRange !== void 0) {
|
|
405
530
|
const { startingLine, totalLines } = renderRange;
|
|
406
531
|
console.log("[diffs/editor] render file:", fileOrDiff.name, "RenderRange:", startingLine + "-" + (startingLine + totalLines), "of", this.#textDocument?.lineCount, "lines");
|
|
407
532
|
}
|
|
533
|
+
if (persistedStateTarget !== void 0) {
|
|
534
|
+
this.#restoreStateOnNextSync = false;
|
|
535
|
+
this.#restorePersistedState(persistedStateTarget.cacheKey, persistedStateTarget.textDocument);
|
|
536
|
+
}
|
|
537
|
+
this.#scheduleOnAttach(fileInstance);
|
|
408
538
|
};
|
|
539
|
+
get #isStatePersistenceEnabled() {
|
|
540
|
+
return this.#options.persistState === true && this.#fileInstance?.type === "file";
|
|
541
|
+
}
|
|
542
|
+
#getCachedTextDocument(file, cacheKey) {
|
|
543
|
+
const textDocument = this.#textDocumentCache.get(cacheKey);
|
|
544
|
+
const languageId = file.lang ?? getFiletypeFromFileName(file.name);
|
|
545
|
+
return textDocument?.languageId === languageId ? textDocument : void 0;
|
|
546
|
+
}
|
|
547
|
+
#getStateStorage() {
|
|
548
|
+
const option = this.#options.persistStateStorage ?? "inMemory";
|
|
549
|
+
if (this.#stateStorage === void 0 || this.#stateStorageOption !== option) {
|
|
550
|
+
this.#stateStorage = createStateStorage(option);
|
|
551
|
+
this.#stateStorageOption = option;
|
|
552
|
+
}
|
|
553
|
+
return this.#stateStorage;
|
|
554
|
+
}
|
|
555
|
+
#persistCurrentState() {
|
|
556
|
+
const fileInfo = this.#fileInfo;
|
|
557
|
+
const textDocument = this.#textDocument;
|
|
558
|
+
if (!this.#isStatePersistenceEnabled || fileInfo === void 0 || textDocument === void 0) return;
|
|
559
|
+
const cacheKey = requirePersistedCacheKey(fileInfo);
|
|
560
|
+
this.#textDocumentCache.set(cacheKey, textDocument);
|
|
561
|
+
let storage;
|
|
562
|
+
try {
|
|
563
|
+
storage = this.#getStateStorage();
|
|
564
|
+
} catch {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
const state = cloneEditorState(this.getState());
|
|
568
|
+
const pendingRestore = this.#pendingStateRestore;
|
|
569
|
+
if (pendingRestore?.cacheKey === cacheKey && pendingRestore.textDocument === textDocument) {
|
|
570
|
+
this.#pendingStateRestore = void 0;
|
|
571
|
+
if (textDocument.version === pendingRestore.documentVersion && this.#selections === pendingRestore.selections && state.view?.scrollLeft === pendingRestore.view?.scrollLeft) return;
|
|
572
|
+
this.#writeState(storage, cacheKey, state, pendingRestore.completion);
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
this.#writeState(storage, cacheKey, state);
|
|
576
|
+
}
|
|
577
|
+
#writeState(storage, cacheKey, state, waitFor) {
|
|
578
|
+
const previousWrite = this.#pendingStateWrites.get(cacheKey);
|
|
579
|
+
if (waitFor !== void 0 || previousWrite !== void 0) {
|
|
580
|
+
let pending = waitFor ?? Promise.resolve();
|
|
581
|
+
if (previousWrite !== void 0) pending = pending.then(() => previousWrite);
|
|
582
|
+
this.#trackStateWrite(cacheKey, pending.then(() => storage.set(cacheKey, state)));
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
let result;
|
|
586
|
+
try {
|
|
587
|
+
result = storage.set(cacheKey, state);
|
|
588
|
+
} catch {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
if (!isPromise(result)) return;
|
|
592
|
+
this.#trackStateWrite(cacheKey, result);
|
|
593
|
+
}
|
|
594
|
+
#trackStateWrite(cacheKey, result) {
|
|
595
|
+
const pending = result.catch(() => {});
|
|
596
|
+
this.#pendingStateWrites.set(cacheKey, pending);
|
|
597
|
+
pending.finally(() => {
|
|
598
|
+
if (this.#pendingStateWrites.get(cacheKey) === pending) this.#pendingStateWrites.delete(cacheKey);
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
#restorePersistedState(cacheKey, textDocument) {
|
|
602
|
+
const generation = ++this.#stateRestoreGeneration;
|
|
603
|
+
const documentVersion = textDocument.version;
|
|
604
|
+
const selections = this.#selections;
|
|
605
|
+
const view = this.getState().view;
|
|
606
|
+
const applyState = (state) => {
|
|
607
|
+
const currentView = this.getState().view;
|
|
608
|
+
if (state === void 0 || 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;
|
|
609
|
+
this.setState(cloneEditorState(state));
|
|
610
|
+
};
|
|
611
|
+
const readState = () => {
|
|
612
|
+
let result;
|
|
613
|
+
try {
|
|
614
|
+
result = this.#getStateStorage().get(cacheKey);
|
|
615
|
+
} catch {
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
if (isPromise(result)) return result.then(applyState).catch(() => {});
|
|
619
|
+
else try {
|
|
620
|
+
applyState(result);
|
|
621
|
+
} catch {}
|
|
622
|
+
};
|
|
623
|
+
const pendingWrite = this.#pendingStateWrites.get(cacheKey);
|
|
624
|
+
const result = pendingWrite === void 0 ? readState() : pendingWrite.then(readState);
|
|
625
|
+
if (isPromise(result)) {
|
|
626
|
+
const pendingRestore = {
|
|
627
|
+
cacheKey,
|
|
628
|
+
textDocument,
|
|
629
|
+
documentVersion,
|
|
630
|
+
selections,
|
|
631
|
+
view,
|
|
632
|
+
completion: result.catch(() => {})
|
|
633
|
+
};
|
|
634
|
+
this.#pendingStateRestore = pendingRestore;
|
|
635
|
+
pendingRestore.completion.finally(() => {
|
|
636
|
+
if (this.#pendingStateRestore === pendingRestore) this.#pendingStateRestore = void 0;
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
#isLineRenderable(line) {
|
|
641
|
+
return this.#fileInstance?.isLineRenderable?.(line + 1) ?? true;
|
|
642
|
+
}
|
|
643
|
+
get #resolveRenderableLine() {
|
|
644
|
+
const fileInstance = this.#fileInstance;
|
|
645
|
+
if (fileInstance?.getNearestRenderableLine == null) return;
|
|
646
|
+
return (line, direction) => {
|
|
647
|
+
const nearest = fileInstance.getNearestRenderableLine(line + 1, direction);
|
|
648
|
+
return nearest == null ? void 0 : nearest - 1;
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
#revealLineIfCollapsed(line) {
|
|
652
|
+
if (!this.#isLineRenderable(line)) this.#fileInstance?.revealLine?.(line + 1);
|
|
653
|
+
}
|
|
409
654
|
get #diffSyle() {
|
|
410
655
|
return this.#fileInstance?.options.diffStyle ?? "split";
|
|
411
656
|
}
|
|
@@ -417,21 +662,24 @@ var Editor = class {
|
|
|
417
662
|
}
|
|
418
663
|
#getPopoverManager() {
|
|
419
664
|
return this.#popoverManager ??= new PopoverManager({
|
|
420
|
-
hasActivePopover: () => this.#selectionAction !== void 0,
|
|
421
|
-
updateActivePopover: () =>
|
|
665
|
+
hasActivePopover: () => this.#selectionAction !== void 0 || this.#markerRenderer?.isPopoverVisible() === true,
|
|
666
|
+
updateActivePopover: () => {
|
|
667
|
+
this.#updateSelectionActionPopover();
|
|
668
|
+
this.#markerRenderer?.updatePopoverPosition();
|
|
669
|
+
}
|
|
422
670
|
});
|
|
423
671
|
}
|
|
424
672
|
#resetCache() {
|
|
425
673
|
this.#lineYCache.clear();
|
|
426
|
-
this.#wrapLineOffsetsCache.clear();
|
|
427
674
|
this.#lineElementsCache.clear();
|
|
428
675
|
this.#lastAccessedCharX = void 0;
|
|
429
676
|
}
|
|
430
677
|
#resetState() {
|
|
431
|
-
this.#
|
|
678
|
+
this.#setEditorActiveLineSafe(null);
|
|
432
679
|
this.#gutterWidthCache = void 0;
|
|
433
680
|
this.#contentWidthCache = void 0;
|
|
434
681
|
this.#shouldIgnoreSelectionChange = false;
|
|
682
|
+
this.#suppressNativeSelectionSync = false;
|
|
435
683
|
this.#overlayElements?.forEach((el) => el.remove());
|
|
436
684
|
this.#overlayElements = void 0;
|
|
437
685
|
this.#selections = void 0;
|
|
@@ -443,6 +691,33 @@ var Editor = class {
|
|
|
443
691
|
this.#searchPanel = void 0;
|
|
444
692
|
this.#selectionAction?.cleanup();
|
|
445
693
|
this.#selectionAction = void 0;
|
|
694
|
+
this.#canMountSelectionAction = true;
|
|
695
|
+
}
|
|
696
|
+
#invalidateOnAttach() {
|
|
697
|
+
const attachState = this.#attachState;
|
|
698
|
+
attachState.generation++;
|
|
699
|
+
if (attachState.callback != null) {
|
|
700
|
+
dequeueRender(attachState.callback);
|
|
701
|
+
attachState.callback = void 0;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
#scheduleOnAttach(fileInstance) {
|
|
705
|
+
const attachState = this.#attachState;
|
|
706
|
+
const textDocument = this.#textDocument;
|
|
707
|
+
if (attachState.delivered || attachState.callback != null || textDocument == null) return;
|
|
708
|
+
const { generation } = attachState;
|
|
709
|
+
const callback = () => {
|
|
710
|
+
if (attachState.callback !== callback) return;
|
|
711
|
+
attachState.callback = void 0;
|
|
712
|
+
if (generation !== attachState.generation || this.#fileInstance !== fileInstance || this.#textDocument !== textDocument) return;
|
|
713
|
+
const contentElement = this.#contentElement;
|
|
714
|
+
const shadowRoot = this.#fileContainer?.shadowRoot;
|
|
715
|
+
if (contentElement == null || shadowRoot == null || !shadowRoot.contains(contentElement)) return;
|
|
716
|
+
attachState.delivered = true;
|
|
717
|
+
this.#options.onAttach?.(this, fileInstance);
|
|
718
|
+
};
|
|
719
|
+
attachState.callback = callback;
|
|
720
|
+
queueRender(callback);
|
|
446
721
|
}
|
|
447
722
|
#initialize() {
|
|
448
723
|
this.#globalStyleElement = h("style", {
|
|
@@ -461,7 +736,26 @@ var Editor = class {
|
|
|
461
736
|
this.#themeStyleElement = h("style", { dataset: "editorThemeCss" });
|
|
462
737
|
this.#spriteElement = createSpriteElement();
|
|
463
738
|
this.#overlayElement = h("div", { dataset: "editorOverlay" });
|
|
739
|
+
const replacementFocusTargetIsContent = (event) => {
|
|
740
|
+
const target = event.composedPath()[0];
|
|
741
|
+
return target instanceof Node && this.#contentElement?.contains(target) === true;
|
|
742
|
+
};
|
|
743
|
+
const cancelReplacementFocus = () => {
|
|
744
|
+
if (this.#replacementFocusRequest == null) return;
|
|
745
|
+
this.#replacementFocusRequest = void 0;
|
|
746
|
+
this.#contentHasFocus = false;
|
|
747
|
+
this.#shouldIgnoreSelectionChange = false;
|
|
748
|
+
};
|
|
464
749
|
this.#globalEventDisposes = [
|
|
750
|
+
addEventListener(document, "focusin", (event) => {
|
|
751
|
+
if (!replacementFocusTargetIsContent(event)) cancelReplacementFocus();
|
|
752
|
+
}, { passive: true }),
|
|
753
|
+
addEventListener(document, "pointerdown", (event) => {
|
|
754
|
+
if (!replacementFocusTargetIsContent(event)) queueMicrotask(cancelReplacementFocus);
|
|
755
|
+
}, {
|
|
756
|
+
capture: true,
|
|
757
|
+
passive: true
|
|
758
|
+
}),
|
|
465
759
|
addEventListener(document, "selectionchange", () => {
|
|
466
760
|
const shadowRoot = this.#fileContainer?.shadowRoot;
|
|
467
761
|
if (this.#shouldIgnoreSelectionChange || this.#suppressNativeSelectionSync || shadowRoot == null || !this.#contentHasFocus) return;
|
|
@@ -514,6 +808,12 @@ var Editor = class {
|
|
|
514
808
|
this.#selectEventDisposes?.forEach((dispose) => dispose());
|
|
515
809
|
this.#selectEventDisposes = disposes;
|
|
516
810
|
}
|
|
811
|
+
#isLineSelectionEnabled() {
|
|
812
|
+
return this.#fileInstance?.options.enableLineSelection === true;
|
|
813
|
+
}
|
|
814
|
+
#preserveEditorSelectionsForGutterGesture() {
|
|
815
|
+
this.#suppressNativeSelectionSync = this.#selections !== void 0;
|
|
816
|
+
}
|
|
517
817
|
#listenContentElement(contentEl, gutterEl) {
|
|
518
818
|
const { onFocus, onBlur } = this.#options;
|
|
519
819
|
const targetIsContentElement = (e) => {
|
|
@@ -528,10 +828,15 @@ var Editor = class {
|
|
|
528
828
|
if (!this.#isContentMouseDown && !this.#shouldIgnoreSelectionChange && this.#selections !== void 0 && this.#selections.length > 0) this.#setWindowSelection(this.#selections.at(-1));
|
|
529
829
|
}, { passive: true }),
|
|
530
830
|
addEventListener(contentEl, "blur", () => {
|
|
831
|
+
if (this.#replacementFocusRequest?.target === contentEl) {
|
|
832
|
+
this.#replacementFocusRequest = void 0;
|
|
833
|
+
this.#shouldIgnoreSelectionChange = false;
|
|
834
|
+
}
|
|
531
835
|
this.#contentHasFocus = false;
|
|
532
836
|
onBlur?.();
|
|
533
837
|
}, { passive: true }),
|
|
534
838
|
addEventListener(contentEl, "pointerdown", (e) => {
|
|
839
|
+
this.#canMountSelectionAction = true;
|
|
535
840
|
this.#suppressNativeSelectionSync = false;
|
|
536
841
|
if (e.pointerType !== "mouse") return;
|
|
537
842
|
if (this.#isDeletedLineTarget(e)) {
|
|
@@ -540,7 +845,7 @@ var Editor = class {
|
|
|
540
845
|
return;
|
|
541
846
|
}
|
|
542
847
|
this.#setDeletedTextSelectionActive(false);
|
|
543
|
-
this.#markerRenderer?.
|
|
848
|
+
this.#markerRenderer?.removePopover();
|
|
544
849
|
if (isSafari() && this.#lineAnnotations !== void 0 && this.#lineAnnotations.length > 0) {
|
|
545
850
|
const annotationDisposes = [...contentEl.querySelectorAll("[data-line-annotation]")].map((el) => [addEventListener(el, "mouseenter", () => {
|
|
546
851
|
this.#shouldIgnoreSelectionChange = true;
|
|
@@ -573,27 +878,21 @@ var Editor = class {
|
|
|
573
878
|
this.#retainSearchPanelFocus = false;
|
|
574
879
|
this.#selectionAction?.cleanup();
|
|
575
880
|
this.#selectionAction = void 0;
|
|
576
|
-
|
|
577
|
-
const primarySelection = this.#selections.at(-1);
|
|
578
|
-
if (!isCollapsedSelection(primarySelection) || this.#selections.length > 1) {
|
|
579
|
-
const pos = getCaretPosition(primarySelection);
|
|
580
|
-
this.#updateSelections([{
|
|
581
|
-
start: pos,
|
|
582
|
-
end: pos,
|
|
583
|
-
direction: 0
|
|
584
|
-
}]);
|
|
585
|
-
this.#focus(pos);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
881
|
+
this.#runCommand("simplifySelection");
|
|
588
882
|
return;
|
|
589
883
|
}
|
|
590
884
|
if (!targetIsContentElement(e)) return;
|
|
885
|
+
this.#canMountSelectionAction = true;
|
|
591
886
|
this.#suppressNativeSelectionSync = false;
|
|
592
887
|
const mvShortcut = isMoveCursorShortcut(e);
|
|
593
888
|
const textDocument = this.#textDocument;
|
|
594
889
|
if (this.#selections !== void 0 && this.#selections.length > 0 && mvShortcut !== void 0 && textDocument !== void 0) {
|
|
595
|
-
|
|
596
|
-
|
|
890
|
+
const cursorMoveOptions = {
|
|
891
|
+
getSoftLineOffsets: this.#isWrap ? (line) => this.#wrapLineText(line) : void 0,
|
|
892
|
+
resolveRenderableLine: this.#resolveRenderableLine
|
|
893
|
+
};
|
|
894
|
+
if (e.shiftKey) this.#updateSelections(mapSelectionShift(textDocument, this.#selections, mvShortcut, cursorMoveOptions));
|
|
895
|
+
else this.#updateSelections(mapCursorMove(textDocument, this.#selections, mvShortcut, cursorMoveOptions));
|
|
597
896
|
this.#scrollToPrimaryCaret();
|
|
598
897
|
e.preventDefault();
|
|
599
898
|
return;
|
|
@@ -626,19 +925,34 @@ var Editor = class {
|
|
|
626
925
|
addEventListener(contentEl, "copy", (e) => {
|
|
627
926
|
if (!targetIsContentElement(e)) return;
|
|
628
927
|
e.preventDefault();
|
|
629
|
-
e.clipboardData?.setData("text", this.#
|
|
928
|
+
if (this.#isDeletedTextSelectionActive()) e.clipboardData?.setData("text", this.#deletedTextForClipboard());
|
|
929
|
+
else this.#writeSelectionClipboardData(e.clipboardData, this.#getSelectionText(), this.#getSelectionClipboardTexts());
|
|
630
930
|
}),
|
|
631
931
|
addEventListener(contentEl, "cut", (e) => {
|
|
632
932
|
if (!targetIsContentElement(e)) return;
|
|
633
933
|
e.preventDefault();
|
|
634
|
-
e.clipboardData?.setData("text", this.#
|
|
934
|
+
if (this.#isDeletedTextSelectionActive()) e.clipboardData?.setData("text", this.#deletedTextForClipboard());
|
|
935
|
+
else {
|
|
936
|
+
const selectionTexts = this.#getSelectionClipboardTexts();
|
|
937
|
+
this.#writeSelectionClipboardData(e.clipboardData, this.#cutSelectionText(), selectionTexts);
|
|
938
|
+
}
|
|
635
939
|
}),
|
|
636
940
|
addEventListener(contentEl, "paste", (e) => {
|
|
637
941
|
if (!targetIsContentElement(e)) return;
|
|
638
942
|
e.preventDefault();
|
|
639
|
-
const
|
|
943
|
+
const clipboardData = e.clipboardData;
|
|
640
944
|
const textDocument = this.#textDocument;
|
|
641
|
-
if (
|
|
945
|
+
if (clipboardData === null || textDocument === void 0) return;
|
|
946
|
+
let text = clipboardData.getData("text");
|
|
947
|
+
const selectionCount = this.#selections?.length ?? 0;
|
|
948
|
+
if (selectionCount > 1) {
|
|
949
|
+
const multiSelectionText = clipboardData.getData(MULTI_SELECTION_CLIPBOARD_TYPE);
|
|
950
|
+
if (multiSelectionText !== void 0) try {
|
|
951
|
+
const selectionTexts = JSON.parse(multiSelectionText);
|
|
952
|
+
if (Array.isArray(selectionTexts) && selectionTexts.length === selectionCount) text = selectionTexts;
|
|
953
|
+
} catch {}
|
|
954
|
+
}
|
|
955
|
+
this.#replaceSelectionText(Array.isArray(text) ? text.map((t) => textDocument.normalizeEol(t)) : textDocument.normalizeEol(text), void 0, true, "document");
|
|
642
956
|
}),
|
|
643
957
|
addEventListener(contentEl, "beforeinput", (e) => {
|
|
644
958
|
if (!targetIsContentElement(e)) return;
|
|
@@ -665,9 +979,20 @@ var Editor = class {
|
|
|
665
979
|
];
|
|
666
980
|
const deletionsCode = this.#fileContainer?.shadowRoot?.querySelector("[data-deletions]");
|
|
667
981
|
if (deletionsCode != null) this.#editorEventDisposes.push(addEventListener(deletionsCode, "pointerdown", (e) => {
|
|
668
|
-
const
|
|
982
|
+
const path = e.composedPath();
|
|
983
|
+
if (isGutterUtilityPath(path)) {
|
|
984
|
+
this.#preserveEditorSelectionsForGutterGesture();
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
const target = path[0];
|
|
669
988
|
const gutterRow = target instanceof HTMLElement ? target.closest("[data-column-number]") : null;
|
|
670
|
-
if (gutterRow instanceof HTMLElement
|
|
989
|
+
if (gutterRow instanceof HTMLElement) {
|
|
990
|
+
if (this.#isLineSelectionEnabled()) {
|
|
991
|
+
this.#preserveEditorSelectionsForGutterGesture();
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
if (this.#beginDeletionGutterSelection(gutterRow, deletionsCode, e.pointerType === "mouse")) return;
|
|
995
|
+
}
|
|
671
996
|
this.#setDeletedTextSelectionActive(true);
|
|
672
997
|
if (this.#selections !== void 0) this.#updateSelections([]);
|
|
673
998
|
}, { passive: true }));
|
|
@@ -686,7 +1011,16 @@ var Editor = class {
|
|
|
686
1011
|
return lineNumber - 1;
|
|
687
1012
|
};
|
|
688
1013
|
this.#editorEventDisposes.push(addEventListener(gutterEl, "pointerdown", (e) => {
|
|
689
|
-
const
|
|
1014
|
+
const path = e.composedPath();
|
|
1015
|
+
if (isGutterUtilityPath(path)) {
|
|
1016
|
+
this.#preserveEditorSelectionsForGutterGesture();
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
const gutterRow = resolveGutterTarget(path[0]);
|
|
1020
|
+
if (gutterRow !== void 0 && this.#isLineSelectionEnabled()) {
|
|
1021
|
+
this.#preserveEditorSelectionsForGutterGesture();
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
690
1024
|
if (gutterRow?.dataset.lineType === "change-deletion") {
|
|
691
1025
|
const code = gutterRow.closest("[data-code]");
|
|
692
1026
|
if (code != null) this.#beginDeletionGutterSelection(gutterRow, code, e.pointerType === "mouse");
|
|
@@ -696,7 +1030,8 @@ var Editor = class {
|
|
|
696
1030
|
const textDocument = this.#textDocument;
|
|
697
1031
|
const lineIndex = resolveEditableLine(gutterRow);
|
|
698
1032
|
if (lineIndex === void 0 || textDocument === void 0) return;
|
|
699
|
-
this.#
|
|
1033
|
+
this.#canMountSelectionAction = true;
|
|
1034
|
+
this.#markerRenderer?.removePopover();
|
|
700
1035
|
const selection = this.#spanLineSelection(lineIndex, lineIndex, textDocument);
|
|
701
1036
|
this.#isGutterMouseDown = true;
|
|
702
1037
|
this.#selectionStart = selection;
|
|
@@ -726,8 +1061,17 @@ var Editor = class {
|
|
|
726
1061
|
#handleCustomPasteEvent = async () => {
|
|
727
1062
|
const clipboard = this.#options.clipboard;
|
|
728
1063
|
if (clipboard !== void 0) {
|
|
729
|
-
|
|
730
|
-
this.#
|
|
1064
|
+
let text = await clipboard.readText();
|
|
1065
|
+
const selectionCount = this.#selections?.length ?? 0;
|
|
1066
|
+
if (selectionCount > 1) {
|
|
1067
|
+
const multiSelectionText = await clipboard.readText(MULTI_SELECTION_CLIPBOARD_TYPE);
|
|
1068
|
+
try {
|
|
1069
|
+
const selectionTexts = JSON.parse(multiSelectionText);
|
|
1070
|
+
if (Array.isArray(selectionTexts) && selectionTexts.length === selectionCount) text = selectionTexts;
|
|
1071
|
+
} catch {}
|
|
1072
|
+
}
|
|
1073
|
+
const textDocument = this.#textDocument;
|
|
1074
|
+
if (textDocument !== void 0) this.#replaceSelectionText(Array.isArray(text) ? text.map((t) => textDocument.normalizeEol(t)) : textDocument.normalizeEol(text), void 0, true, "document");
|
|
731
1075
|
}
|
|
732
1076
|
};
|
|
733
1077
|
#computeContentOffset(contentEl) {
|
|
@@ -763,9 +1107,11 @@ var Editor = class {
|
|
|
763
1107
|
this.#updateSelections(expanded);
|
|
764
1108
|
this.focus();
|
|
765
1109
|
} else {
|
|
766
|
-
const nextMatch =
|
|
1110
|
+
const nextMatch = findNextMatch(textDocument, selections);
|
|
767
1111
|
if (nextMatch !== void 0) {
|
|
768
1112
|
this.#updateSelections(nextMatch);
|
|
1113
|
+
const primaryMatch = nextMatch.at(-1);
|
|
1114
|
+
if (primaryMatch !== void 0) this.#revealLineIfCollapsed(getCaretPosition(primaryMatch).line);
|
|
769
1115
|
this.#scrollToPrimaryCaret();
|
|
770
1116
|
}
|
|
771
1117
|
}
|
|
@@ -775,18 +1121,74 @@ var Editor = class {
|
|
|
775
1121
|
case "moveLineDown":
|
|
776
1122
|
this.#moveSelectedLines(command === "moveLineUp" ? -1 : 1);
|
|
777
1123
|
break;
|
|
1124
|
+
case "copyLineUp":
|
|
1125
|
+
case "copyLineDown":
|
|
1126
|
+
this.#copySelectedLines(command === "copyLineUp" ? -1 : 1);
|
|
1127
|
+
break;
|
|
1128
|
+
case "simplifySelection": {
|
|
1129
|
+
const selections = this.#selections;
|
|
1130
|
+
const primarySelection = selections?.at(-1);
|
|
1131
|
+
if (selections === void 0 || primarySelection === void 0) break;
|
|
1132
|
+
if (selections.length > 1) {
|
|
1133
|
+
this.#updateSelections([primarySelection]);
|
|
1134
|
+
this.#focus(getCaretPosition(primarySelection));
|
|
1135
|
+
} else if (!isCollapsedSelection(primarySelection)) {
|
|
1136
|
+
const caret = getCaretPosition(primarySelection);
|
|
1137
|
+
this.#updateSelections([{
|
|
1138
|
+
start: caret,
|
|
1139
|
+
end: caret,
|
|
1140
|
+
direction: 0
|
|
1141
|
+
}]);
|
|
1142
|
+
this.#focus(caret);
|
|
1143
|
+
}
|
|
1144
|
+
break;
|
|
1145
|
+
}
|
|
1146
|
+
case "insertBlankLine":
|
|
1147
|
+
this.#insertBlankLine();
|
|
1148
|
+
break;
|
|
1149
|
+
case "deleteHardLineForward":
|
|
1150
|
+
this.#deleteHardLineForward();
|
|
1151
|
+
break;
|
|
1152
|
+
case "toggleComment":
|
|
1153
|
+
case "toggleBlockComment": {
|
|
1154
|
+
const selections = this.#selections;
|
|
1155
|
+
if (selections === void 0) break;
|
|
1156
|
+
const { lineComment, blockComment } = resolveCommentConfig(textDocument.languageId, this.#options.languageCommentConfig);
|
|
1157
|
+
if (command === "toggleComment" && lineComment !== null) {
|
|
1158
|
+
this.#applyCommandEdits(resolveLineCommentEdits(textDocument, selections, lineComment));
|
|
1159
|
+
break;
|
|
1160
|
+
}
|
|
1161
|
+
const linewise = command === "toggleComment";
|
|
1162
|
+
const result = resolveBlockCommentEdits(textDocument, selections, blockComment, linewise);
|
|
1163
|
+
if (result !== void 0) this.#applyCommandEdits(result.edits, linewise ? void 0 : (document) => result.nextSelectionOffsets.map(([start, end, direction]) => ({
|
|
1164
|
+
start: document.positionAt(start),
|
|
1165
|
+
end: document.positionAt(end),
|
|
1166
|
+
direction
|
|
1167
|
+
})));
|
|
1168
|
+
break;
|
|
1169
|
+
}
|
|
778
1170
|
case "indent":
|
|
779
1171
|
case "outdent":
|
|
1172
|
+
case "indentLess":
|
|
1173
|
+
case "indentMore":
|
|
780
1174
|
if (this.#selections !== void 0) {
|
|
781
1175
|
const edits = [];
|
|
782
1176
|
const nextSelections = [];
|
|
1177
|
+
const editedLines = /* @__PURE__ */ new Set();
|
|
783
1178
|
const sameLineIndents = [];
|
|
784
1179
|
for (const selection of this.#selections) {
|
|
785
1180
|
const startLine = selection.start.line;
|
|
786
|
-
const outdent = command === "outdent";
|
|
787
|
-
|
|
1181
|
+
const outdent = command === "outdent" || command === "indentLess";
|
|
1182
|
+
const lineBased = command === "indentLess" || command === "indentMore";
|
|
1183
|
+
if (startLine !== selection.end.line || outdent || lineBased) {
|
|
788
1184
|
const ret = resolveIndentEdits(textDocument, selection, this.#metrics.tabSize, outdent);
|
|
789
|
-
|
|
1185
|
+
for (const edit of ret[0]) {
|
|
1186
|
+
const line = edit.range.start.line;
|
|
1187
|
+
if (!editedLines.has(line)) {
|
|
1188
|
+
editedLines.add(line);
|
|
1189
|
+
edits.push(edit);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
790
1192
|
nextSelections.push(ret[1]);
|
|
791
1193
|
} else {
|
|
792
1194
|
const text = textDocument.charAt({
|
|
@@ -838,17 +1240,19 @@ var Editor = class {
|
|
|
838
1240
|
const fullSelection = getDocumentFullSelection(textDocument);
|
|
839
1241
|
this.#updateSelections([fullSelection]);
|
|
840
1242
|
this.focus();
|
|
841
|
-
const
|
|
842
|
-
|
|
1243
|
+
const renderedLineRanges = this.#getRenderedEditableLineRanges();
|
|
1244
|
+
const firstRenderedLine = renderedLineRanges[0]?.[0];
|
|
1245
|
+
const lastRenderedLine = renderedLineRanges.at(-1)?.[1];
|
|
1246
|
+
if (firstRenderedLine !== void 0 && lastRenderedLine !== void 0) {
|
|
843
1247
|
this.#suppressNativeSelectionSync = true;
|
|
844
1248
|
this.#setWindowSelection({
|
|
845
1249
|
start: {
|
|
846
|
-
line:
|
|
1250
|
+
line: firstRenderedLine,
|
|
847
1251
|
character: 0
|
|
848
1252
|
},
|
|
849
1253
|
end: {
|
|
850
|
-
line:
|
|
851
|
-
character: textDocument.getLineLength(
|
|
1254
|
+
line: lastRenderedLine,
|
|
1255
|
+
character: textDocument.getLineLength(lastRenderedLine)
|
|
852
1256
|
},
|
|
853
1257
|
direction: 1
|
|
854
1258
|
});
|
|
@@ -858,8 +1262,9 @@ var Editor = class {
|
|
|
858
1262
|
case "moveCursorToDocStart":
|
|
859
1263
|
case "moveCursorToDocEnd":
|
|
860
1264
|
{
|
|
861
|
-
const
|
|
862
|
-
this.#updateSelections([
|
|
1265
|
+
const boundarySelection = getDocumentBoundarySelection(textDocument, command === "moveCursorToDocEnd", this.#isDiff);
|
|
1266
|
+
this.#updateSelections([boundarySelection]);
|
|
1267
|
+
this.#revealLineIfCollapsed(getCaretPosition(boundarySelection).line);
|
|
863
1268
|
this.#scrollToPrimaryCaret();
|
|
864
1269
|
}
|
|
865
1270
|
break;
|
|
@@ -869,25 +1274,191 @@ var Editor = class {
|
|
|
869
1274
|
const atEnd = command === "expandSelectionDocEnd";
|
|
870
1275
|
const selections = this.#selections;
|
|
871
1276
|
if (selections !== void 0) {
|
|
872
|
-
|
|
1277
|
+
const boundarySelection = getDocumentBoundarySelection(textDocument, atEnd, this.#isDiff);
|
|
1278
|
+
this.#updateSelections(extendSelections(selections, boundarySelection));
|
|
1279
|
+
this.#revealLineIfCollapsed(getCaretPosition(boundarySelection).line);
|
|
873
1280
|
this.#scrollToPrimaryCaret();
|
|
874
1281
|
}
|
|
875
1282
|
}
|
|
876
1283
|
break;
|
|
877
1284
|
case "undo":
|
|
878
|
-
if (this.#textDocument?.canUndo === true) {
|
|
879
|
-
const undoResult = this.#textDocument.undo();
|
|
880
|
-
if (undoResult !== void 0) this.#applyChange(...undoResult);
|
|
881
|
-
}
|
|
882
|
-
break;
|
|
883
1285
|
case "redo":
|
|
884
|
-
|
|
885
|
-
const redoResult = this.#textDocument.redo();
|
|
886
|
-
if (redoResult !== void 0) this.#applyChange(...redoResult);
|
|
887
|
-
}
|
|
1286
|
+
this.#applyHistoryChange(command);
|
|
888
1287
|
break;
|
|
889
1288
|
}
|
|
890
1289
|
}
|
|
1290
|
+
#applyHistoryChange(command) {
|
|
1291
|
+
const textDocument = this.#textDocument;
|
|
1292
|
+
if (textDocument === void 0) return;
|
|
1293
|
+
if (command === "undo" && !textDocument.canUndo || command === "redo" && !textDocument.canRedo) return;
|
|
1294
|
+
const selections = this.#selections;
|
|
1295
|
+
const selectionOffsets = selections?.map((selection) => [textDocument.offsetAt(selection.start), textDocument.offsetAt(selection.end)]);
|
|
1296
|
+
const result = command === "undo" ? textDocument.undo() : textDocument.redo();
|
|
1297
|
+
if (result === void 0) return;
|
|
1298
|
+
const [change, recordedSelections, lineAnnotations, selectionEdits] = result;
|
|
1299
|
+
const nextSelections = recordedSelections ?? (selections !== void 0 && selectionOffsets !== void 0 && selectionEdits !== void 0 ? remapSelectionsAfterEdits(textDocument, selections, selectionOffsets, selectionEdits) : void 0);
|
|
1300
|
+
this.#applyChange(change, nextSelections, lineAnnotations);
|
|
1301
|
+
}
|
|
1302
|
+
/** Applies one undoable command batch and records its resulting selections. */
|
|
1303
|
+
#applyCommandEdits(edits, resolveNextSelections) {
|
|
1304
|
+
const textDocument = this.#textDocument;
|
|
1305
|
+
const selections = this.#selections;
|
|
1306
|
+
if (textDocument === void 0 || selections === void 0) return;
|
|
1307
|
+
const remapSelections = resolveNextSelections === void 0;
|
|
1308
|
+
const selectionOffsets = remapSelections ? selections.map((selection) => [textDocument.offsetAt(selection.start), textDocument.offsetAt(selection.end)]) : void 0;
|
|
1309
|
+
const resolvedEdits = remapSelections ? edits.map((edit) => {
|
|
1310
|
+
const start = textDocument.offsetAt(edit.range.start);
|
|
1311
|
+
const end = textDocument.offsetAt(edit.range.end);
|
|
1312
|
+
return {
|
|
1313
|
+
start: Math.min(start, end),
|
|
1314
|
+
end: Math.max(start, end),
|
|
1315
|
+
text: edit.newText
|
|
1316
|
+
};
|
|
1317
|
+
}).sort((a, b) => {
|
|
1318
|
+
const startOrder = a.start - b.start;
|
|
1319
|
+
return startOrder !== 0 ? startOrder : a.end - b.end;
|
|
1320
|
+
}) : void 0;
|
|
1321
|
+
const change = textDocument.applyEdits(edits, true, selections, void 0, true);
|
|
1322
|
+
if (change === void 0) return;
|
|
1323
|
+
const nextSelections = remapSelections ? remapSelectionsAfterEdits(textDocument, selections, selectionOffsets, resolvedEdits) : resolveNextSelections(textDocument);
|
|
1324
|
+
textDocument.setLastUndoSelectionsAfter(nextSelections);
|
|
1325
|
+
this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change));
|
|
1326
|
+
}
|
|
1327
|
+
/** Copies selected line blocks and keeps the selection in the requested copy. */
|
|
1328
|
+
#copySelectedLines(direction) {
|
|
1329
|
+
const textDocument = this.#textDocument;
|
|
1330
|
+
const selections = this.#selections;
|
|
1331
|
+
if (textDocument === void 0 || selections === void 0) return;
|
|
1332
|
+
const blocks = getSelectedLineBlocks(selections);
|
|
1333
|
+
if (blocks.length === 0) return;
|
|
1334
|
+
const copiedLinesBefore = [];
|
|
1335
|
+
let copiedLineCount = 0;
|
|
1336
|
+
const edits = [];
|
|
1337
|
+
for (const block of blocks) {
|
|
1338
|
+
copiedLinesBefore.push(copiedLineCount);
|
|
1339
|
+
const blockLineCount = block.endLine - block.startLine + 1;
|
|
1340
|
+
copiedLineCount += blockLineCount;
|
|
1341
|
+
const text = textDocument.getText({
|
|
1342
|
+
start: {
|
|
1343
|
+
line: block.startLine,
|
|
1344
|
+
character: 0
|
|
1345
|
+
},
|
|
1346
|
+
end: {
|
|
1347
|
+
line: block.endLine,
|
|
1348
|
+
character: textDocument.getLineLength(block.endLine)
|
|
1349
|
+
}
|
|
1350
|
+
});
|
|
1351
|
+
if (direction > 0) {
|
|
1352
|
+
const position = {
|
|
1353
|
+
line: block.startLine,
|
|
1354
|
+
character: 0
|
|
1355
|
+
};
|
|
1356
|
+
edits.push({
|
|
1357
|
+
range: {
|
|
1358
|
+
start: position,
|
|
1359
|
+
end: position
|
|
1360
|
+
},
|
|
1361
|
+
newText: text + textDocument.eol
|
|
1362
|
+
});
|
|
1363
|
+
} else if (block.endLine < textDocument.lineCount - 1) {
|
|
1364
|
+
const position = {
|
|
1365
|
+
line: block.endLine + 1,
|
|
1366
|
+
character: 0
|
|
1367
|
+
};
|
|
1368
|
+
edits.push({
|
|
1369
|
+
range: {
|
|
1370
|
+
start: position,
|
|
1371
|
+
end: position
|
|
1372
|
+
},
|
|
1373
|
+
newText: text + textDocument.eol
|
|
1374
|
+
});
|
|
1375
|
+
} else {
|
|
1376
|
+
const position = {
|
|
1377
|
+
line: block.endLine,
|
|
1378
|
+
character: textDocument.getLineLength(block.endLine)
|
|
1379
|
+
};
|
|
1380
|
+
edits.push({
|
|
1381
|
+
range: {
|
|
1382
|
+
start: position,
|
|
1383
|
+
end: position
|
|
1384
|
+
},
|
|
1385
|
+
newText: textDocument.eol + text
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
const nextSelections = selections.map((selection) => {
|
|
1390
|
+
const line = selection.start.line;
|
|
1391
|
+
let low = 0;
|
|
1392
|
+
let high = blocks.length - 1;
|
|
1393
|
+
while (low <= high) {
|
|
1394
|
+
const middle = low + high >>> 1;
|
|
1395
|
+
const block = blocks[middle];
|
|
1396
|
+
if (line < block.startLine) high = middle - 1;
|
|
1397
|
+
else if (line > block.endLine) low = middle + 1;
|
|
1398
|
+
else {
|
|
1399
|
+
low = middle;
|
|
1400
|
+
break;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
const blockIndex = Math.max(0, low <= high ? low : high);
|
|
1404
|
+
const block = blocks[blockIndex];
|
|
1405
|
+
const shift = copiedLinesBefore[blockIndex] + (direction > 0 ? block.endLine - block.startLine + 1 : 0);
|
|
1406
|
+
return {
|
|
1407
|
+
start: {
|
|
1408
|
+
...selection.start,
|
|
1409
|
+
line: selection.start.line + shift
|
|
1410
|
+
},
|
|
1411
|
+
end: {
|
|
1412
|
+
...selection.end,
|
|
1413
|
+
line: selection.end.line + shift
|
|
1414
|
+
},
|
|
1415
|
+
direction: selection.direction
|
|
1416
|
+
};
|
|
1417
|
+
});
|
|
1418
|
+
this.#applyCommandEdits(edits, () => nextSelections);
|
|
1419
|
+
}
|
|
1420
|
+
/** Inserts an indented blank line after each selection's final line. */
|
|
1421
|
+
#insertBlankLine() {
|
|
1422
|
+
const textDocument = this.#textDocument;
|
|
1423
|
+
const selections = this.#selections;
|
|
1424
|
+
if (textDocument === void 0 || selections === void 0) return;
|
|
1425
|
+
const selectionLines = selections.map((selection) => getCaretPosition(selection).line);
|
|
1426
|
+
const targetLines = Array.from(new Set(selectionLines)).sort((a, b) => a - b);
|
|
1427
|
+
const targetIndex = /* @__PURE__ */ new Map();
|
|
1428
|
+
const indents = /* @__PURE__ */ new Map();
|
|
1429
|
+
const edits = [];
|
|
1430
|
+
for (let index = 0; index < targetLines.length; index++) {
|
|
1431
|
+
const line = targetLines[index];
|
|
1432
|
+
const lineText = textDocument.getLineText(line);
|
|
1433
|
+
const indent = /^\s*/.exec(lineText)?.[0] ?? "";
|
|
1434
|
+
targetIndex.set(line, index);
|
|
1435
|
+
indents.set(line, indent);
|
|
1436
|
+
const position = {
|
|
1437
|
+
line,
|
|
1438
|
+
character: textDocument.getLineLength(line)
|
|
1439
|
+
};
|
|
1440
|
+
edits.push({
|
|
1441
|
+
range: {
|
|
1442
|
+
start: position,
|
|
1443
|
+
end: position
|
|
1444
|
+
},
|
|
1445
|
+
newText: textDocument.eol + indent
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
const nextSelections = selections.map((_, index) => {
|
|
1449
|
+
const line = selectionLines[index];
|
|
1450
|
+
const position = {
|
|
1451
|
+
line: line + 1 + targetIndex.get(line),
|
|
1452
|
+
character: indents.get(line).length
|
|
1453
|
+
};
|
|
1454
|
+
return {
|
|
1455
|
+
start: position,
|
|
1456
|
+
end: position,
|
|
1457
|
+
direction: 0
|
|
1458
|
+
};
|
|
1459
|
+
});
|
|
1460
|
+
this.#applyCommandEdits(edits, () => nextSelections);
|
|
1461
|
+
}
|
|
891
1462
|
#moveSelectedLines(direction) {
|
|
892
1463
|
const textDocument = this.#textDocument;
|
|
893
1464
|
const selections = this.#selections;
|
|
@@ -956,15 +1527,15 @@ var Editor = class {
|
|
|
956
1527
|
this.#lineElementsCache.clear();
|
|
957
1528
|
this.#lastAccessedCharX = void 0;
|
|
958
1529
|
this.#metrics.clearTextWidthCache();
|
|
959
|
-
if (contentWidthChanged
|
|
960
|
-
this.#lineYCache.clear();
|
|
1530
|
+
if (contentWidthChanged) {
|
|
961
1531
|
this.#wrapLineOffsetsCache.clear();
|
|
1532
|
+
if (this.#isWrap || lineAnnotations > 0) this.#lineYCache.clear();
|
|
962
1533
|
}
|
|
963
1534
|
if (this.#selections !== void 0 || this.#matches !== void 0 || this.#markerRenderer !== void 0) {
|
|
964
1535
|
this.#updateSelections(this.#selections ?? []);
|
|
965
|
-
if (this.#selections !== void 0) this.focus();
|
|
1536
|
+
if (this.#selections !== void 0 && this.#contentHasFocus) this.focus();
|
|
966
1537
|
}
|
|
967
|
-
this.#markerRenderer?.
|
|
1538
|
+
this.#markerRenderer?.removePopover();
|
|
968
1539
|
this.#computeContentOffset(this.#contentElement);
|
|
969
1540
|
};
|
|
970
1541
|
#remeasureMetricsOnFontLoad() {
|
|
@@ -977,8 +1548,9 @@ var Editor = class {
|
|
|
977
1548
|
this.#gutterWidthCache = void 0;
|
|
978
1549
|
this.#contentWidthCache = void 0;
|
|
979
1550
|
this.#resetCache();
|
|
1551
|
+
this.#wrapLineOffsetsCache.clear();
|
|
980
1552
|
if (this.#selections !== void 0 || this.#matches !== void 0 || this.#markerRenderer !== void 0) this.#updateSelections(this.#selections ?? []);
|
|
981
|
-
this.#markerRenderer?.
|
|
1553
|
+
this.#markerRenderer?.removePopover();
|
|
982
1554
|
});
|
|
983
1555
|
}
|
|
984
1556
|
#rerender(change, newLineAnnotations, renderRange = this.#renderRange, shouldUpdateBuffer) {
|
|
@@ -994,45 +1566,57 @@ var Editor = class {
|
|
|
994
1566
|
const t2 = performance.now();
|
|
995
1567
|
if (dirtyLines.size > 0) {
|
|
996
1568
|
const children = contentEl.children;
|
|
997
|
-
const dirtyLineIndexes = new Set(
|
|
998
|
-
const
|
|
999
|
-
for (let i =
|
|
1569
|
+
const dirtyLineIndexes = /* @__PURE__ */ new Set();
|
|
1570
|
+
for (const lineIndex of dirtyLines.keys()) if (this.#isLineRenderable(lineIndex)) dirtyLineIndexes.add(lineIndex);
|
|
1571
|
+
for (let i = 0; i < children.length; i++) {
|
|
1000
1572
|
const child = children[i];
|
|
1001
1573
|
if (child !== void 0) {
|
|
1002
1574
|
const lineNumber = getLineNumberAttr(child);
|
|
1003
1575
|
const lineType = child.dataset.lineType;
|
|
1004
1576
|
if (lineNumber === void 0 || lineType === "change-deletion") continue;
|
|
1005
1577
|
const lineIndex = lineNumber - 1;
|
|
1006
|
-
if (
|
|
1578
|
+
if (dirtyLineIndexes.has(lineIndex)) {
|
|
1007
1579
|
const tokens = dirtyLines.get(lineIndex);
|
|
1008
|
-
child.replaceChildren(...renderLineTokens(tokens
|
|
1580
|
+
child.replaceChildren(...renderLineTokens(tokens));
|
|
1009
1581
|
dirtyLineIndexes.delete(lineIndex);
|
|
1010
1582
|
if (dirtyLineIndexes.size === 0) break;
|
|
1011
1583
|
}
|
|
1012
1584
|
}
|
|
1013
1585
|
}
|
|
1014
|
-
if (dirtyLineIndexes.size > 0)
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1586
|
+
if (dirtyLineIndexes.size > 0) {
|
|
1587
|
+
let lastRenderedLineNumber = 0;
|
|
1588
|
+
for (let i = children.length - 1; i >= 0; i--) {
|
|
1589
|
+
const child = children[i];
|
|
1590
|
+
const lineNumber = getLineNumberAttr(child);
|
|
1591
|
+
if (lineNumber !== void 0 && child.dataset.lineType !== "change-deletion") {
|
|
1592
|
+
lastRenderedLineNumber = lineNumber;
|
|
1593
|
+
break;
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
for (const lineIndex of dirtyLineIndexes) {
|
|
1597
|
+
if (lineIndex < lastRenderedLineNumber) continue;
|
|
1598
|
+
const tokens = dirtyLines.get(lineIndex);
|
|
1599
|
+
const lineNumber = String(lineIndex + 1);
|
|
1600
|
+
h("div", {
|
|
1601
|
+
dataset: {
|
|
1602
|
+
line: lineNumber,
|
|
1603
|
+
lineType: "context",
|
|
1604
|
+
lineIndex: lineIndex.toString()
|
|
1605
|
+
},
|
|
1606
|
+
children: renderLineTokens(tokens)
|
|
1607
|
+
}, contentEl);
|
|
1608
|
+
if (gutterEl !== void 0) h("div", {
|
|
1609
|
+
dataset: {
|
|
1610
|
+
lineType: "context",
|
|
1611
|
+
columnNumber: lineNumber,
|
|
1612
|
+
lineIndex: lineIndex.toString()
|
|
1613
|
+
},
|
|
1614
|
+
children: [h("span", {
|
|
1615
|
+
dataset: { lineNumberContent: "" },
|
|
1616
|
+
textContent: lineNumber
|
|
1617
|
+
})]
|
|
1618
|
+
}, gutterEl);
|
|
1619
|
+
}
|
|
1036
1620
|
}
|
|
1037
1621
|
}
|
|
1038
1622
|
if (change.lineDelta < 0) for (const children of [contentEl.children, gutterEl?.children ?? []]) for (let i = children.length - 1; i >= 0; i--) {
|
|
@@ -1053,9 +1637,9 @@ var Editor = class {
|
|
|
1053
1637
|
contentEl.style.gridRow = "span " + gridRow;
|
|
1054
1638
|
if (gutterEl !== void 0) gutterEl.style.gridRow = "span " + gridRow;
|
|
1055
1639
|
}
|
|
1056
|
-
fileInstance.updateRenderCache(dirtyLines, tokenizer.themeType, !didLineCountChange);
|
|
1640
|
+
fileInstance.updateRenderCache(dirtyLines, tokenizer.themeType, !didLineCountChange, didLineCountChange);
|
|
1057
1641
|
if (didLineCountChange) fileInstance.applyDocumentChange(textDocument, newLineAnnotations, shouldUpdateBuffer);
|
|
1058
|
-
if (this.#isDiff &&
|
|
1642
|
+
if (didLineCountChange || this.#isDiff && this.#diffSyle === "unified") this.#resetCache();
|
|
1059
1643
|
if (newLineAnnotations !== void 0) {
|
|
1060
1644
|
this.#lineAnnotations = newLineAnnotations;
|
|
1061
1645
|
renderLineAnnotations(newLineAnnotations, contentEl, gutterEl);
|
|
@@ -1075,7 +1659,7 @@ var Editor = class {
|
|
|
1075
1659
|
case "insertCompositionText": break;
|
|
1076
1660
|
case "insertLineBreak":
|
|
1077
1661
|
case "insertParagraph":
|
|
1078
|
-
this.#replaceSelectionText("\n");
|
|
1662
|
+
this.#replaceSelectionText(this.#textDocument?.eol ?? "\n");
|
|
1079
1663
|
break;
|
|
1080
1664
|
case "deleteContentBackward":
|
|
1081
1665
|
this.#deleteSelectionText();
|
|
@@ -1087,9 +1671,6 @@ var Editor = class {
|
|
|
1087
1671
|
case "deleteHardLineBackward":
|
|
1088
1672
|
this.#deleteSoftLineBackward();
|
|
1089
1673
|
break;
|
|
1090
|
-
case "deleteHardLineForward":
|
|
1091
|
-
this.#deleteHardLineForward();
|
|
1092
|
-
break;
|
|
1093
1674
|
case "deleteWordBackward":
|
|
1094
1675
|
this.#deleteWordBackward();
|
|
1095
1676
|
break;
|
|
@@ -1101,7 +1682,7 @@ var Editor = class {
|
|
|
1101
1682
|
break;
|
|
1102
1683
|
}
|
|
1103
1684
|
}
|
|
1104
|
-
#focus(position, preventScroll = true) {
|
|
1685
|
+
#focus(position, preventScroll = true, shouldFocus) {
|
|
1105
1686
|
this.#contentHasFocus = true;
|
|
1106
1687
|
if (position !== void 0) {
|
|
1107
1688
|
this.#shouldIgnoreSelectionChange = true;
|
|
@@ -1110,13 +1691,115 @@ var Editor = class {
|
|
|
1110
1691
|
end: position,
|
|
1111
1692
|
direction: 0
|
|
1112
1693
|
});
|
|
1113
|
-
|
|
1694
|
+
queueRender(() => {
|
|
1695
|
+
if (shouldFocus?.() === false) {
|
|
1696
|
+
this.#shouldIgnoreSelectionChange = false;
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1114
1699
|
this.#contentElement?.focus({ preventScroll });
|
|
1115
|
-
|
|
1700
|
+
queueRender(() => {
|
|
1116
1701
|
this.#shouldIgnoreSelectionChange = false;
|
|
1117
1702
|
});
|
|
1118
1703
|
});
|
|
1119
|
-
} else this.#contentElement?.focus({ preventScroll });
|
|
1704
|
+
} else if (shouldFocus?.() !== false) this.#contentElement?.focus({ preventScroll });
|
|
1705
|
+
else this.#contentHasFocus = false;
|
|
1706
|
+
}
|
|
1707
|
+
#restoreReplacementFocus(position) {
|
|
1708
|
+
const request = this.#replacementFocusRequest;
|
|
1709
|
+
if (request == null) return;
|
|
1710
|
+
if (this.#retainSearchPanelFocus) {
|
|
1711
|
+
this.#replacementFocusRequest = void 0;
|
|
1712
|
+
this.#contentHasFocus = false;
|
|
1713
|
+
this.#shouldIgnoreSelectionChange = false;
|
|
1714
|
+
return;
|
|
1715
|
+
}
|
|
1716
|
+
const contentElement = this.#contentElement;
|
|
1717
|
+
if (contentElement == null || !this.#replacementFocusIsAvailable(contentElement)) {
|
|
1718
|
+
this.#replacementFocusRequest = void 0;
|
|
1719
|
+
this.#contentHasFocus = false;
|
|
1720
|
+
return;
|
|
1721
|
+
}
|
|
1722
|
+
const shouldFocus = () => {
|
|
1723
|
+
if (this.#replacementFocusRequest !== request) return false;
|
|
1724
|
+
const shouldRestore = this.#replacementFocusIsAvailable(contentElement);
|
|
1725
|
+
this.#replacementFocusRequest = void 0;
|
|
1726
|
+
return shouldRestore;
|
|
1727
|
+
};
|
|
1728
|
+
if (position != null) {
|
|
1729
|
+
this.#focus(position, true, shouldFocus);
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
const primarySelection = this.#selections?.at(-1);
|
|
1733
|
+
if (primarySelection == null) {
|
|
1734
|
+
this.#focus(void 0, true, shouldFocus);
|
|
1735
|
+
return;
|
|
1736
|
+
}
|
|
1737
|
+
const primaryPosition = primarySelection.direction === -1 ? primarySelection.end : primarySelection.start;
|
|
1738
|
+
this.#focus(primaryPosition, true, shouldFocus);
|
|
1739
|
+
}
|
|
1740
|
+
#replacementFocusIsAvailable(contentElement) {
|
|
1741
|
+
const fileContainer = this.#fileContainer;
|
|
1742
|
+
const shadowRoot = fileContainer?.shadowRoot;
|
|
1743
|
+
if (fileContainer == null || shadowRoot == null || this.#contentElement !== contentElement || !shadowRoot.contains(contentElement)) return false;
|
|
1744
|
+
const { activeElement: shadowActiveElement } = shadowRoot;
|
|
1745
|
+
if (shadowActiveElement === contentElement || shadowActiveElement != null && contentElement.contains(shadowActiveElement)) return true;
|
|
1746
|
+
if (shadowActiveElement != null) return false;
|
|
1747
|
+
const { ownerDocument } = fileContainer;
|
|
1748
|
+
const { activeElement } = ownerDocument;
|
|
1749
|
+
return activeElement == null || activeElement === ownerDocument.body || activeElement === ownerDocument.documentElement || activeElement === fileContainer;
|
|
1750
|
+
}
|
|
1751
|
+
#getFirstVisibleLineNumber(offset = 0) {
|
|
1752
|
+
const contentElement = this.#contentElement;
|
|
1753
|
+
const fileContainer = this.#fileContainer;
|
|
1754
|
+
if (this.#textDocument == null || contentElement == null || fileContainer == null) return;
|
|
1755
|
+
const viewport = this.#fileInstance?.getEditorViewport?.() ?? this.#getDefaultEditorViewport(fileContainer);
|
|
1756
|
+
let viewportTop;
|
|
1757
|
+
let viewportBottom;
|
|
1758
|
+
if (viewport instanceof HTMLElement) {
|
|
1759
|
+
const viewportRect = viewport.getBoundingClientRect();
|
|
1760
|
+
viewportTop = viewportRect.top;
|
|
1761
|
+
viewportBottom = viewportRect.bottom;
|
|
1762
|
+
} else {
|
|
1763
|
+
const viewportWindow = viewport.defaultView;
|
|
1764
|
+
if (viewportWindow == null) return;
|
|
1765
|
+
viewportTop = 0;
|
|
1766
|
+
viewportBottom = viewportWindow.innerHeight;
|
|
1767
|
+
}
|
|
1768
|
+
const stickyHeader = fileContainer.shadowRoot?.querySelector("[data-diffs-header][data-sticky]");
|
|
1769
|
+
if (stickyHeader != null) viewportTop = Math.max(viewportTop, stickyHeader.getBoundingClientRect().bottom);
|
|
1770
|
+
if (Number.isFinite(offset)) viewportTop += Math.max(0, offset);
|
|
1771
|
+
if (viewportBottom <= viewportTop) return;
|
|
1772
|
+
for (const child of contentElement.children) {
|
|
1773
|
+
const row = child;
|
|
1774
|
+
const lineNumber = getLineNumberAttr(row);
|
|
1775
|
+
const lineType = row.dataset.lineType;
|
|
1776
|
+
if (lineNumber == null || lineType == null || !isLineEditable(lineType)) continue;
|
|
1777
|
+
const rowRect = row.getBoundingClientRect();
|
|
1778
|
+
if (rowRect.bottom <= rowRect.top) continue;
|
|
1779
|
+
if (rowRect.top >= viewportTop && rowRect.top < viewportBottom) return lineNumber;
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
#getDefaultEditorViewport(fileContainer) {
|
|
1783
|
+
const ownerDocument = fileContainer.ownerDocument;
|
|
1784
|
+
let element = fileContainer.parentElement;
|
|
1785
|
+
while (element != null && element !== ownerDocument.body && element !== ownerDocument.documentElement) {
|
|
1786
|
+
const overflowY = element.ownerDocument.defaultView?.getComputedStyle(element).overflowY;
|
|
1787
|
+
if (overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") return element;
|
|
1788
|
+
element = element.parentElement;
|
|
1789
|
+
}
|
|
1790
|
+
return ownerDocument;
|
|
1791
|
+
}
|
|
1792
|
+
#focusAtPosition(position, preventScroll) {
|
|
1793
|
+
const selection = {
|
|
1794
|
+
start: position,
|
|
1795
|
+
end: position,
|
|
1796
|
+
direction: 0
|
|
1797
|
+
};
|
|
1798
|
+
this.#canMountSelectionAction = false;
|
|
1799
|
+
this.#updateSelections([selection]);
|
|
1800
|
+
this.#revealLineIfCollapsed(position.line);
|
|
1801
|
+
if (preventScroll) this.#focus(position, true);
|
|
1802
|
+
else this.#scrollToPrimaryCaret();
|
|
1120
1803
|
}
|
|
1121
1804
|
#setWindowSelection(selection) {
|
|
1122
1805
|
const winSelection = window.getSelection();
|
|
@@ -1149,7 +1832,11 @@ var Editor = class {
|
|
|
1149
1832
|
block: scrollPosition,
|
|
1150
1833
|
inline: "nearest"
|
|
1151
1834
|
});
|
|
1152
|
-
if (!noFocus)
|
|
1835
|
+
if (!noFocus) {
|
|
1836
|
+
const position = primarySelection.direction === -1 ? primarySelection.end : primarySelection.start;
|
|
1837
|
+
if (this.#replacementFocusRequest !== void 0) this.#restoreReplacementFocus(position);
|
|
1838
|
+
else this.#focus(position);
|
|
1839
|
+
}
|
|
1153
1840
|
} else {
|
|
1154
1841
|
const pos = getCaretPosition(primarySelection);
|
|
1155
1842
|
this.#scrollToLine(pos.line, pos.character, noFocus);
|
|
@@ -1179,10 +1866,14 @@ var Editor = class {
|
|
|
1179
1866
|
block: "center",
|
|
1180
1867
|
inline: "nearest"
|
|
1181
1868
|
});
|
|
1182
|
-
if (!noFocus)
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1869
|
+
if (!noFocus) {
|
|
1870
|
+
const position = {
|
|
1871
|
+
line,
|
|
1872
|
+
character: char
|
|
1873
|
+
};
|
|
1874
|
+
if (this.#replacementFocusRequest !== void 0) this.#restoreReplacementFocus(position);
|
|
1875
|
+
else this.#focus(position);
|
|
1876
|
+
}
|
|
1186
1877
|
this.#scrollingToLine = void 0;
|
|
1187
1878
|
this.#scrollingToLineChar = void 0;
|
|
1188
1879
|
this.#scrollingToLineFixed = false;
|
|
@@ -1196,7 +1887,7 @@ var Editor = class {
|
|
|
1196
1887
|
block: "center",
|
|
1197
1888
|
inline: "nearest"
|
|
1198
1889
|
});
|
|
1199
|
-
if (modelLinePosition.height > 0) {
|
|
1890
|
+
if (modelLinePosition.height > 0 && this.#isLineRenderable(line)) {
|
|
1200
1891
|
this.#scrollingToLine = line;
|
|
1201
1892
|
this.#scrollingToLineChar = char;
|
|
1202
1893
|
this.#scrollingToLineNoFocus = noFocus;
|
|
@@ -1223,7 +1914,7 @@ var Editor = class {
|
|
|
1223
1914
|
block: "center",
|
|
1224
1915
|
inline: "nearest"
|
|
1225
1916
|
});
|
|
1226
|
-
if (this.#scrollingToLine === line && (yFix === 0 || this.#scrollingToLineFixed)) {
|
|
1917
|
+
if (this.#scrollingToLine === line && (yFix === 0 || this.#scrollingToLineFixed) || !this.#isLineRenderable(line)) {
|
|
1227
1918
|
this.#scrollingToLine = void 0;
|
|
1228
1919
|
this.#scrollingToLineChar = void 0;
|
|
1229
1920
|
this.#scrollingToLineFixed = false;
|
|
@@ -1277,7 +1968,7 @@ var Editor = class {
|
|
|
1277
1968
|
}
|
|
1278
1969
|
this.#setDeletedTextSelectionActive(true);
|
|
1279
1970
|
this.#updateSelections([]);
|
|
1280
|
-
|
|
1971
|
+
this.#setEditorActiveLineSafe(getLineNumberAttr(focusContent) ?? null, true, "deletions");
|
|
1281
1972
|
const lo = Math.min(anchorIndex, focusIndex);
|
|
1282
1973
|
const hi = Math.max(anchorIndex, focusIndex);
|
|
1283
1974
|
this.#deletedSelectionText = lo < 0 ? "" : rows.slice(lo, hi + 1).filter((row) => row.getAttribute("data-line-type") === "change-deletion").map((row) => row.textContent ?? "").join("\n");
|
|
@@ -1315,19 +2006,18 @@ var Editor = class {
|
|
|
1315
2006
|
if (pre == null) return;
|
|
1316
2007
|
if (active) {
|
|
1317
2008
|
pre.setAttribute("data-deleted-text-selection", "");
|
|
1318
|
-
|
|
2009
|
+
this.#setEditorActiveLineSafe(null);
|
|
1319
2010
|
this.#deletedSelectionText = "";
|
|
1320
2011
|
} else {
|
|
1321
2012
|
pre.removeAttribute("data-deleted-text-selection");
|
|
1322
2013
|
this.#deletedSelectionText = "";
|
|
1323
2014
|
}
|
|
1324
2015
|
}
|
|
1325
|
-
#
|
|
2016
|
+
#setEditorActiveLineSafe(lineNumber, lineNumberOnly = false, side = "additions") {
|
|
1326
2017
|
try {
|
|
1327
|
-
this.#fileInstance?.
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
lineNumberOnly
|
|
2018
|
+
this.#fileInstance?.setEditorActiveLine(lineNumber, {
|
|
2019
|
+
lineNumberOnly,
|
|
2020
|
+
side
|
|
1331
2021
|
});
|
|
1332
2022
|
} catch {}
|
|
1333
2023
|
}
|
|
@@ -1341,7 +2031,7 @@ var Editor = class {
|
|
|
1341
2031
|
#updateSelections(selections) {
|
|
1342
2032
|
this.__postponeBgTokenizeToNextFrame();
|
|
1343
2033
|
this.#primaryCaretElement = void 0;
|
|
1344
|
-
this.#
|
|
2034
|
+
this.#setEditorActiveLineSafe(null);
|
|
1345
2035
|
if (selections.length === 0 && this.#matches === void 0 && this.#markerRenderer === void 0) {
|
|
1346
2036
|
this.#selections = void 0;
|
|
1347
2037
|
this.#overlayElements?.forEach((el) => el.remove());
|
|
@@ -1361,10 +2051,7 @@ var Editor = class {
|
|
|
1361
2051
|
this.#selections = normalizedSelections;
|
|
1362
2052
|
const hasNonEmptySelection = normalizedSelections.some((selection) => !isCollapsedSelection(selection));
|
|
1363
2053
|
const caretLine = getCaretPosition(primarySelection).line + 1;
|
|
1364
|
-
this.#
|
|
1365
|
-
start: caretLine,
|
|
1366
|
-
end: caretLine
|
|
1367
|
-
}, hasNonEmptySelection);
|
|
2054
|
+
this.#setEditorActiveLineSafe(caretLine, hasNonEmptySelection);
|
|
1368
2055
|
for (const selection of normalizedSelections) {
|
|
1369
2056
|
if (!isCollapsedSelection(selection)) this.#renderSelection(renderCtx, "selection", selection);
|
|
1370
2057
|
this.#renderCaret(renderCtx, selection, selection === primarySelection);
|
|
@@ -1374,16 +2061,50 @@ var Editor = class {
|
|
|
1374
2061
|
}
|
|
1375
2062
|
const textDocument = this.#textDocument;
|
|
1376
2063
|
if (this.#matches !== void 0 && textDocument !== void 0) {
|
|
2064
|
+
const matches = this.#matches;
|
|
2065
|
+
const renderRange = this.#renderRange;
|
|
2066
|
+
const renderedLineRanges = renderRange !== void 0 && Number.isFinite(renderRange.totalLines) || this.#isDiff && this.#fileInstance?.options.expandUnchanged !== true ? this.#getRenderedEditableLineRanges() : void 0;
|
|
1377
2067
|
const primarySelection = this.#selections?.at(-1);
|
|
1378
2068
|
const primaryStartOffset = primarySelection !== void 0 ? textDocument.offsetAt(primarySelection.start) : -1;
|
|
1379
2069
|
const primaryEndOffset = primarySelection !== void 0 ? textDocument.offsetAt(primarySelection.end) : -1;
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
2070
|
+
let firstMatchIndex = 0;
|
|
2071
|
+
const rangeCount = renderedLineRanges?.length ?? 1;
|
|
2072
|
+
for (let rangeIndex = 0; rangeIndex < rangeCount; rangeIndex++) {
|
|
2073
|
+
let visibleEndOffset = Infinity;
|
|
2074
|
+
const lineRange = renderedLineRanges?.[rangeIndex];
|
|
2075
|
+
if (lineRange !== void 0) {
|
|
2076
|
+
const [firstLine, lastLine] = lineRange;
|
|
2077
|
+
const visibleStartOffset = textDocument.offsetAt({
|
|
2078
|
+
line: firstLine,
|
|
2079
|
+
character: 0
|
|
2080
|
+
});
|
|
2081
|
+
const endLine = lastLine + 1;
|
|
2082
|
+
visibleEndOffset = endLine < textDocument.lineCount ? textDocument.offsetAt({
|
|
2083
|
+
line: endLine,
|
|
2084
|
+
character: 0
|
|
2085
|
+
}) : textDocument.offsetAt({
|
|
2086
|
+
line: endLine - 1,
|
|
2087
|
+
character: textDocument.getLineLength(endLine - 1)
|
|
2088
|
+
});
|
|
2089
|
+
let low = firstMatchIndex;
|
|
2090
|
+
let high = matches.length;
|
|
2091
|
+
while (low < high) {
|
|
2092
|
+
const middle = low + Math.floor((high - low) / 2);
|
|
2093
|
+
if (matches[middle][1] <= visibleStartOffset) low = middle + 1;
|
|
2094
|
+
else high = middle;
|
|
2095
|
+
}
|
|
2096
|
+
firstMatchIndex = low;
|
|
2097
|
+
}
|
|
2098
|
+
for (; firstMatchIndex < matches.length; firstMatchIndex++) {
|
|
2099
|
+
const [startOffset, endOffset] = matches[firstMatchIndex];
|
|
2100
|
+
if (startOffset >= visibleEndOffset) break;
|
|
2101
|
+
const range = {
|
|
2102
|
+
start: textDocument.positionAt(startOffset),
|
|
2103
|
+
end: textDocument.positionAt(endOffset)
|
|
2104
|
+
};
|
|
2105
|
+
const isFocused = primaryStartOffset === startOffset && primaryEndOffset === endOffset;
|
|
2106
|
+
this.#renderSelection(renderCtx, "match", range, isFocused ? "focus" : void 0);
|
|
2107
|
+
}
|
|
1387
2108
|
}
|
|
1388
2109
|
}
|
|
1389
2110
|
if (this.#markerRenderer !== void 0 && textDocument !== void 0) for (const marker of this.#markerRenderer.markers) this.#renderSelection(renderCtx, "marker", marker, markerSeverityDatasetKey(marker.severity));
|
|
@@ -1396,7 +2117,14 @@ var Editor = class {
|
|
|
1396
2117
|
#renderSelection(renderCtx, type, range, extraDataset) {
|
|
1397
2118
|
if (this.#textDocument === void 0) return;
|
|
1398
2119
|
const { start, end } = range;
|
|
1399
|
-
|
|
2120
|
+
let firstLine = start.line;
|
|
2121
|
+
let endLine = end.line + 1;
|
|
2122
|
+
if (this.#renderRange !== void 0) {
|
|
2123
|
+
const { startingLine, totalLines } = this.#renderRange;
|
|
2124
|
+
firstLine = Math.max(firstLine, startingLine);
|
|
2125
|
+
endLine = Math.min(endLine, startingLine + totalLines);
|
|
2126
|
+
}
|
|
2127
|
+
for (let line = firstLine; line < endLine; line++) {
|
|
1400
2128
|
if (!this.#isLineVisible(line)) continue;
|
|
1401
2129
|
const isLastLine = line === end.line;
|
|
1402
2130
|
const startChar = line === start.line ? start.character : 0;
|
|
@@ -1453,8 +2181,10 @@ var Editor = class {
|
|
|
1453
2181
|
const rounded = (this.#options.roundedSelection ?? true) && type === "selection";
|
|
1454
2182
|
const addRoundedCorner = (line, wrapLine, left, radius) => {
|
|
1455
2183
|
const top = this.#getLineY(line) + wrapLine * lineHeight;
|
|
1456
|
-
const
|
|
1457
|
-
|
|
2184
|
+
const lineElement = this.#getLineElement(line);
|
|
2185
|
+
let cornerBg = "initial";
|
|
2186
|
+
if (lineElement !== void 0 && (lineElement.dataset.lineType === "change-addition" || lineElement.dataset.selectedLine !== void 0)) cornerBg = getComputedStyle(lineElement).getPropertyValue("--diffs-line-bg");
|
|
2187
|
+
const css = `width:${ch}px;transform:translateX(${left}px) translateY(${top}px);--diffs-selection-corner-bg:${cornerBg}`;
|
|
1458
2188
|
const dataset = {
|
|
1459
2189
|
selectionCorner: "",
|
|
1460
2190
|
[radius]: ""
|
|
@@ -1573,13 +2303,14 @@ var Editor = class {
|
|
|
1573
2303
|
return;
|
|
1574
2304
|
}
|
|
1575
2305
|
if (this.#selectionAction === void 0) {
|
|
2306
|
+
if (!this.#canMountSelectionAction) return;
|
|
1576
2307
|
const getActiveSelection = () => this.#selections?.at(-1) ?? primarySelection;
|
|
1577
2308
|
const selectionActionElement = renderSelectionAction({
|
|
1578
2309
|
textDocument,
|
|
1579
2310
|
get selection() {
|
|
1580
2311
|
return getActiveSelection();
|
|
1581
2312
|
},
|
|
1582
|
-
applyEdits: (edits) => this.applyEdits(edits
|
|
2313
|
+
applyEdits: (edits) => this.applyEdits(edits),
|
|
1583
2314
|
getSelectionText: () => this.#textDocument?.getText(getActiveSelection()) ?? "",
|
|
1584
2315
|
replaceSelectionText: (text) => {
|
|
1585
2316
|
this.#replaceSelectionText(text, [getActiveSelection()]);
|
|
@@ -1590,7 +2321,7 @@ var Editor = class {
|
|
|
1590
2321
|
}
|
|
1591
2322
|
});
|
|
1592
2323
|
this.#selectionAction = new SelectionActionWidget(selectionActionElement, overlayElement, () => this.#updateSelectionActionPopover());
|
|
1593
|
-
this.#getPopoverManager().resetPlacement();
|
|
2324
|
+
this.#getPopoverManager().resetPlacement(SELECTION_ACTION_POPOVER_PLACEMENT_KEY);
|
|
1594
2325
|
}
|
|
1595
2326
|
const lineHeight = this.#metrics.lineHeight;
|
|
1596
2327
|
const isBackward = primarySelection.direction === -1;
|
|
@@ -1624,17 +2355,19 @@ var Editor = class {
|
|
|
1624
2355
|
const atDocumentEdge = isBackward ? head.line < 3 : head.line >= lineCount - 3;
|
|
1625
2356
|
const canUseFallback = this.#isLineVisible(fallback.anchor.line);
|
|
1626
2357
|
const popoverManager = this.#getPopoverManager();
|
|
2358
|
+
const viewport = popoverManager.getPlacementBounds();
|
|
1627
2359
|
const useFallback = canUseFallback && popoverManager.choosePlacement({
|
|
1628
2360
|
preferred: preferredGeometry,
|
|
1629
2361
|
fallback: fallbackGeometry,
|
|
1630
|
-
viewport
|
|
2362
|
+
viewport,
|
|
1631
2363
|
popoverHeight,
|
|
1632
|
-
atDocumentEdge
|
|
2364
|
+
atDocumentEdge,
|
|
2365
|
+
placementKey: SELECTION_ACTION_POPOVER_PLACEMENT_KEY
|
|
1633
2366
|
}) === "fallback";
|
|
1634
|
-
if (!canUseFallback) popoverManager.setPlacement("preferred");
|
|
2367
|
+
if (!canUseFallback) popoverManager.setPlacement("preferred", SELECTION_ACTION_POPOVER_PLACEMENT_KEY);
|
|
1635
2368
|
const { placeAbove } = useFallback ? fallback : preferred;
|
|
1636
2369
|
const { left, anchorTop } = useFallback ? fallbackGeometry : preferredGeometry;
|
|
1637
|
-
this.#selectionAction.reposition(left, anchorTop, this.#getGutterWidth(), placeAbove);
|
|
2370
|
+
this.#selectionAction.reposition(left, anchorTop, this.#getGutterWidth(), placeAbove, viewport);
|
|
1638
2371
|
}
|
|
1639
2372
|
#openSearchPanel(mode) {
|
|
1640
2373
|
if (this.#searchPanel !== void 0) {
|
|
@@ -1666,6 +2399,7 @@ var Editor = class {
|
|
|
1666
2399
|
const scrollToMatch = ([startOffset, endOffset], retainFocus) => {
|
|
1667
2400
|
const nextSelection = createSelectionFromAnchorAndFocusOffsets(textDocument, startOffset, endOffset);
|
|
1668
2401
|
this.#updateSelections([nextSelection]);
|
|
2402
|
+
this.#revealLineIfCollapsed(getCaretPosition(nextSelection).line);
|
|
1669
2403
|
this.#scrollToPrimaryCaret(true);
|
|
1670
2404
|
this.#retainSearchPanelFocus = retainFocus;
|
|
1671
2405
|
};
|
|
@@ -1732,6 +2466,18 @@ var Editor = class {
|
|
|
1732
2466
|
if (textDocument === void 0 || selections === void 0) return "";
|
|
1733
2467
|
return getSelectionText(textDocument, selections);
|
|
1734
2468
|
}
|
|
2469
|
+
#getSelectionClipboardTexts() {
|
|
2470
|
+
const textDocument = this.#textDocument;
|
|
2471
|
+
const selections = this.#selections;
|
|
2472
|
+
if (textDocument === void 0 || selections === void 0) return [];
|
|
2473
|
+
return getSelectionClipboardTexts(textDocument, selections);
|
|
2474
|
+
}
|
|
2475
|
+
/** Writes both the portable text and the selection pairing metadata. */
|
|
2476
|
+
#writeSelectionClipboardData(clipboardData, text, selectionTexts) {
|
|
2477
|
+
if (clipboardData === null) return;
|
|
2478
|
+
clipboardData.setData("text", text);
|
|
2479
|
+
if (selectionTexts.length > 1) clipboardData.setData(MULTI_SELECTION_CLIPBOARD_TYPE, JSON.stringify(selectionTexts));
|
|
2480
|
+
}
|
|
1735
2481
|
#cutSelectionText() {
|
|
1736
2482
|
const textDocument = this.#textDocument;
|
|
1737
2483
|
const selections = this.#selections;
|
|
@@ -1762,15 +2508,15 @@ var Editor = class {
|
|
|
1762
2508
|
textDocument.setLastUndoSelectionsAfter(nextSelections);
|
|
1763
2509
|
this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change));
|
|
1764
2510
|
}
|
|
1765
|
-
#replaceSelectionText(text, selections = this.#selections, undoBoundary = false) {
|
|
2511
|
+
#replaceSelectionText(text, selections = this.#selections, undoBoundary = false, textOrder = "selection") {
|
|
1766
2512
|
if (selections === void 0) return;
|
|
1767
2513
|
const textDocument = this.#textDocument;
|
|
1768
2514
|
const primarySelection = selections.at(-1);
|
|
1769
2515
|
if (textDocument === void 0 || primarySelection === void 0) return;
|
|
1770
|
-
const { nextSelections, change } = Array.isArray(text) && text.length === selections.length ? applyTextReplaceToSelections(textDocument, selections, text, this.#lineAnnotations, undoBoundary) : applyTextChangeToSelections(textDocument, selections, {
|
|
2516
|
+
const { nextSelections, change } = Array.isArray(text) && text.length === selections.length ? applyTextReplaceToSelections(textDocument, selections, text, this.#lineAnnotations, undoBoundary, textOrder) : applyTextChangeToSelections(textDocument, selections, {
|
|
1771
2517
|
start: textDocument.offsetAt(primarySelection.start),
|
|
1772
2518
|
end: textDocument.offsetAt(primarySelection.end),
|
|
1773
|
-
text: Array.isArray(text) ? text.join(
|
|
2519
|
+
text: Array.isArray(text) ? text.join(textDocument.eol) : text
|
|
1774
2520
|
}, this.#lineAnnotations, void 0, undoBoundary);
|
|
1775
2521
|
if (change !== void 0) this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change));
|
|
1776
2522
|
}
|
|
@@ -1817,26 +2563,16 @@ var Editor = class {
|
|
|
1817
2563
|
const { nextSelections, change } = applyTransposeToSelections(textDocument, selections, this.#lineAnnotations);
|
|
1818
2564
|
if (change !== void 0) this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change));
|
|
1819
2565
|
}
|
|
1820
|
-
#getFileRef() {
|
|
1821
|
-
const fileInfo = this.#fileInfo;
|
|
1822
|
-
const textDocument = this.#textDocument;
|
|
1823
|
-
if (fileInfo === void 0 || textDocument === void 0) return;
|
|
1824
|
-
const file = { ...fileInfo };
|
|
1825
|
-
Object.defineProperty(file, "contents", {
|
|
1826
|
-
enumerable: true,
|
|
1827
|
-
get: () => textDocument.getText()
|
|
1828
|
-
});
|
|
1829
|
-
return file;
|
|
1830
|
-
}
|
|
1831
2566
|
#applyChange(change, newSelections, newLineAnnotations, options) {
|
|
1832
|
-
const fileRef = this
|
|
2567
|
+
const fileRef = this.getFile();
|
|
1833
2568
|
const onChange = this.#options.onChange;
|
|
1834
2569
|
if (fileRef !== void 0 && onChange !== void 0) onChange(fileRef, newLineAnnotations ?? this.#lineAnnotations);
|
|
1835
2570
|
if (change.lineDelta !== 0 || this.#isWrap) {
|
|
1836
2571
|
for (const line of this.#lineYCache.keys()) if (line >= change.startLine) this.#lineYCache.delete(line);
|
|
1837
2572
|
}
|
|
1838
|
-
if (this.#
|
|
1839
|
-
|
|
2573
|
+
if (this.#wrapLineOffsetsCache.size > 0) {
|
|
2574
|
+
if (change.lineDelta === 0 && (change.changedLineRanges.length === 1 || change.changedLineChanges?.every(([, , editLineDelta]) => editLineDelta === 0) === true)) for (const [rangeStart, rangeEnd] of change.changedLineRanges) for (let line = rangeStart; line <= rangeEnd; line++) this.#wrapLineOffsetsCache.delete(line);
|
|
2575
|
+
else for (const line of this.#wrapLineOffsetsCache.keys()) if (line >= change.startLine) this.#wrapLineOffsetsCache.delete(line);
|
|
1840
2576
|
}
|
|
1841
2577
|
this.#lastAccessedCharX = void 0;
|
|
1842
2578
|
let renderRange = this.#renderRange;
|
|
@@ -1847,7 +2583,7 @@ var Editor = class {
|
|
|
1847
2583
|
if (primarySelection.end.line >= renderRangeEndLine) {
|
|
1848
2584
|
const widenedTotalLines = primarySelection.end.line - renderRange.startingLine + 1;
|
|
1849
2585
|
const maxWidenLines = this.#viewportWindowLines === void 0 || this.#viewportWindowLines === Infinity ? Infinity : this.#viewportWindowLines * MAX_EDIT_WIDEN_WINDOW_MULTIPLE;
|
|
1850
|
-
if (change.startLine <= renderRangeEndLine && widenedTotalLines <= maxWidenLines) {
|
|
2586
|
+
if (change.startLine <= renderRangeEndLine && widenedTotalLines <= maxWidenLines && this.#isLineRenderable(primarySelection.end.line)) {
|
|
1851
2587
|
if (primarySelection.end.line > renderRangeEndLine) shouldUpdateBuffer = true;
|
|
1852
2588
|
renderRange = {
|
|
1853
2589
|
...renderRange,
|
|
@@ -1862,6 +2598,8 @@ var Editor = class {
|
|
|
1862
2598
|
if (newSelections !== void 0) {
|
|
1863
2599
|
this.#updateSelections(newSelections);
|
|
1864
2600
|
if (options?.skipFocus !== true) {
|
|
2601
|
+
const revealTarget = newSelections.at(-1);
|
|
2602
|
+
if (revealTarget !== void 0) this.#revealLineIfCollapsed(getCaretPosition(revealTarget).line);
|
|
1865
2603
|
if (this.#primaryCaretElement !== void 0) requestAnimationFrame(() => {
|
|
1866
2604
|
this.#primaryCaretElement?.scrollIntoView({
|
|
1867
2605
|
block: "nearest",
|
|
@@ -1885,39 +2623,31 @@ var Editor = class {
|
|
|
1885
2623
|
}
|
|
1886
2624
|
}
|
|
1887
2625
|
}
|
|
1888
|
-
#
|
|
1889
|
-
const lineElement = this.#getLineElement(line);
|
|
1890
|
-
if (lineElement === void 0) return;
|
|
1891
|
-
if (navigator.userAgent.includes("jsdom")) return;
|
|
1892
|
-
const backgroundColor = getComputedStyle(lineElement, "::after").backgroundColor;
|
|
1893
|
-
return backgroundColor === "" || backgroundColor === "transparent" || backgroundColor === "rgba(0, 0, 0, 0)" ? void 0 : backgroundColor;
|
|
1894
|
-
}
|
|
1895
|
-
#getRenderedEditableLineRange() {
|
|
2626
|
+
#getRenderedEditableLineRanges() {
|
|
1896
2627
|
const contentElement = this.#contentElement;
|
|
1897
|
-
if (contentElement === void 0) return;
|
|
1898
|
-
|
|
1899
|
-
let last;
|
|
2628
|
+
if (contentElement === void 0) return [];
|
|
2629
|
+
const ranges = [];
|
|
1900
2630
|
for (const child of contentElement.children) {
|
|
1901
2631
|
const el = child;
|
|
1902
2632
|
const lineType = el.dataset.lineType;
|
|
1903
2633
|
const lineNumber = getLineNumberAttr(el);
|
|
1904
2634
|
if (lineNumber === void 0 || lineType === void 0 || !isLineEditable(lineType)) continue;
|
|
1905
2635
|
const line = lineNumber - 1;
|
|
1906
|
-
|
|
1907
|
-
if (
|
|
2636
|
+
const current = ranges.at(-1);
|
|
2637
|
+
if (current !== void 0 && line <= current[1] + 1) current[1] = Math.max(current[1], line);
|
|
2638
|
+
else ranges.push([line, line]);
|
|
1908
2639
|
}
|
|
1909
|
-
return
|
|
1910
|
-
first,
|
|
1911
|
-
last
|
|
1912
|
-
};
|
|
2640
|
+
return ranges;
|
|
1913
2641
|
}
|
|
1914
2642
|
#getLineElement(line) {
|
|
1915
2643
|
let lineElement = this.#lineElementsCache.get(line);
|
|
1916
2644
|
if (lineElement !== void 0) return lineElement ?? void 0;
|
|
2645
|
+
const renderRange = this.#renderRange;
|
|
2646
|
+
if (renderRange !== void 0 && (line < renderRange.startingLine || line >= renderRange.startingLine + renderRange.totalLines)) return;
|
|
1917
2647
|
const contentElement = this.#contentElement;
|
|
1918
2648
|
if (contentElement === void 0) return;
|
|
1919
|
-
if (
|
|
1920
|
-
const { startingLine } =
|
|
2649
|
+
if (renderRange !== void 0) {
|
|
2650
|
+
const { startingLine } = renderRange;
|
|
1921
2651
|
const { children } = contentElement;
|
|
1922
2652
|
for (let i = line - startingLine; i <= children.length; i++) {
|
|
1923
2653
|
const child = children[i];
|
|
@@ -2008,6 +2738,7 @@ var Editor = class {
|
|
|
2008
2738
|
#wrapLineText(line) {
|
|
2009
2739
|
const cachedOffsets = this.#wrapLineOffsetsCache.get(line);
|
|
2010
2740
|
if (cachedOffsets !== void 0) return cachedOffsets;
|
|
2741
|
+
if (this.#wrapLineOffsetsCache.size >= MAX_WRAP_OFFSETS_CACHE_LINES) this.#wrapLineOffsetsCache.clear();
|
|
2011
2742
|
const lineText = this.#textDocument?.getLineText(line);
|
|
2012
2743
|
if (lineText === void 0 || lineText.length === 0) {
|
|
2013
2744
|
const offsets = new Uint32Array([0]);
|
|
@@ -2037,21 +2768,30 @@ var Editor = class {
|
|
|
2037
2768
|
try {
|
|
2038
2769
|
const unicodeOffsets = getUnicodeMeasurementOffsets(lineText);
|
|
2039
2770
|
const wrapLineStartLeft = div.getBoundingClientRect().left + this.#metrics.ch;
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
range.
|
|
2045
|
-
range.
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2771
|
+
const graphemeCount = unicodeOffsets === void 0 ? lineText.length : unicodeOffsets.length - 1;
|
|
2772
|
+
const graphemeStart = (index) => unicodeOffsets === void 0 ? index : unicodeOffsets[index];
|
|
2773
|
+
const measureGrapheme = (index) => {
|
|
2774
|
+
range.setStart(textNode, graphemeStart(index));
|
|
2775
|
+
range.setEnd(textNode, graphemeStart(index + 1));
|
|
2776
|
+
return range.getBoundingClientRect();
|
|
2777
|
+
};
|
|
2778
|
+
starts.push(0);
|
|
2779
|
+
let lastTop = measureGrapheme(0).top;
|
|
2780
|
+
let searchStart = 1;
|
|
2781
|
+
while (searchStart < graphemeCount) {
|
|
2782
|
+
let low = searchStart;
|
|
2783
|
+
let high = graphemeCount;
|
|
2784
|
+
while (low < high) {
|
|
2785
|
+
const mid = low + high >> 1;
|
|
2786
|
+
if (measureGrapheme(mid).top > lastTop) high = mid;
|
|
2787
|
+
else low = mid + 1;
|
|
2051
2788
|
}
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2789
|
+
if (low >= graphemeCount) break;
|
|
2790
|
+
const { left, top } = measureGrapheme(low);
|
|
2791
|
+
const startsPastLineStart = isSafari() && left - wrapLineStartLeft > this.#metrics.ch / 2;
|
|
2792
|
+
starts.push(graphemeStart(startsPastLineStart ? low - 1 : low));
|
|
2793
|
+
lastTop = top;
|
|
2794
|
+
searchStart = low + 1;
|
|
2055
2795
|
}
|
|
2056
2796
|
const offsets = new Uint32Array(starts.length + 1);
|
|
2057
2797
|
for (let i = 0; i < starts.length; i++) offsets[i] = starts[i];
|