@pierre/diffs 1.3.0-beta.8 → 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 +51 -43
- 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/FileStream.d.ts.map +1 -1
- package/dist/components/FileStream.js +2 -1
- package/dist/components/FileStream.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 +29 -3
- 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 +54 -13
- 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 +995 -242
- 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 +1 -0
- package/dist/editor/searchPanel.d.ts.map +1 -1
- package/dist/editor/searchPanel.js +15 -8
- 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 +5 -4
- package/dist/index.js +7 -6
- 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 +2 -1
- package/dist/managers/UniversalRenderingManager.d.ts.map +1 -1
- package/dist/managers/UniversalRenderingManager.js +7 -3
- 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/WorkerPoolManager.d.ts.map +1 -1
- package/dist/worker/WorkerPoolManager.js +4 -2
- package/dist/worker/WorkerPoolManager.js.map +1 -1
- package/dist/worker/types.d.ts +4 -0
- package/dist/worker/types.d.ts.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 +13 -8
- 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,32 +1,69 @@
|
|
|
1
|
-
import { queueRender } from "../managers/UniversalRenderingManager.js";
|
|
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
|
|
23
|
+
function getShadowRootRange(shadowRoot) {
|
|
24
|
+
const selection = shadowRoot.getSelection?.();
|
|
25
|
+
if (selection == null || selection.rangeCount === 0) return;
|
|
26
|
+
const range = selection.getRangeAt(0);
|
|
27
|
+
return {
|
|
28
|
+
collapsed: range.collapsed,
|
|
29
|
+
startContainer: range.startContainer,
|
|
30
|
+
startOffset: range.startOffset,
|
|
31
|
+
endContainer: range.endContainer,
|
|
32
|
+
endOffset: range.endOffset
|
|
33
|
+
};
|
|
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
|
+
}
|
|
20
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";
|
|
21
46
|
var Editor = class {
|
|
22
47
|
#options;
|
|
23
48
|
#metrics = new Metrics();
|
|
24
49
|
#tokenizer;
|
|
25
50
|
#popoverManager;
|
|
51
|
+
#textDocumentCache = /* @__PURE__ */ new Map();
|
|
52
|
+
#stateStorage;
|
|
53
|
+
#stateStorageOption;
|
|
54
|
+
#pendingStateWrites = /* @__PURE__ */ new Map();
|
|
55
|
+
#pendingStateRestore;
|
|
56
|
+
#stateRestoreGeneration = 0;
|
|
57
|
+
#restoreStateOnNextSync = false;
|
|
26
58
|
#editorEventDisposes;
|
|
27
59
|
#globalEventDisposes;
|
|
28
60
|
#selectEventDisposes;
|
|
29
61
|
#detach;
|
|
62
|
+
#attachState = {
|
|
63
|
+
generation: 0,
|
|
64
|
+
callback: void 0,
|
|
65
|
+
delivered: false
|
|
66
|
+
};
|
|
30
67
|
#contentOffset;
|
|
31
68
|
#gutterWidthCache;
|
|
32
69
|
#contentWidthCache;
|
|
@@ -54,9 +91,11 @@ var Editor = class {
|
|
|
54
91
|
#markerRenderer;
|
|
55
92
|
#searchPanel;
|
|
56
93
|
#selectionAction;
|
|
94
|
+
#canMountSelectionAction = true;
|
|
57
95
|
#shouldIgnoreSelectionChange = false;
|
|
58
96
|
#suppressNativeSelectionSync = false;
|
|
59
97
|
#contentHasFocus = false;
|
|
98
|
+
#replacementFocusRequest;
|
|
60
99
|
#isComposing = false;
|
|
61
100
|
#isGutterMouseDown = false;
|
|
62
101
|
#isContentMouseDown = false;
|
|
@@ -75,13 +114,13 @@ var Editor = class {
|
|
|
75
114
|
#fontRemeasureScheduled = false;
|
|
76
115
|
#themeSelectionRefreshFrame;
|
|
77
116
|
#onDeferTokenize = (lines, themeType) => {
|
|
78
|
-
this.#fileInstance?.updateRenderCache(lines, themeType, false);
|
|
117
|
+
this.#fileInstance?.updateRenderCache(lines, themeType, false, false);
|
|
79
118
|
if (this.#renderRange !== void 0 && this.#renderRange.totalLines !== Infinity) {
|
|
80
119
|
const { startingLine, totalLines } = this.#renderRange;
|
|
81
120
|
const endLine = Math.min(startingLine + totalLines, this.#textDocument?.lineCount ?? 0);
|
|
82
121
|
for (const [line, tokens] of lines) if (line >= startingLine && line < endLine) {
|
|
83
122
|
const lineElement = this.#getLineElement(line);
|
|
84
|
-
if (lineElement !== void 0) lineElement.replaceChildren(...renderLineTokens(tokens
|
|
123
|
+
if (lineElement !== void 0) lineElement.replaceChildren(...renderLineTokens(tokens));
|
|
85
124
|
}
|
|
86
125
|
}
|
|
87
126
|
};
|
|
@@ -89,21 +128,38 @@ var Editor = class {
|
|
|
89
128
|
this.#options = options;
|
|
90
129
|
}
|
|
91
130
|
setOptions(options) {
|
|
92
|
-
this.#options
|
|
131
|
+
const previousStorageOption = this.#options.persistStateStorage ?? "inMemory";
|
|
132
|
+
const nextOptions = {
|
|
93
133
|
...this.#options,
|
|
94
134
|
...options
|
|
95
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
|
+
}
|
|
96
152
|
}
|
|
97
153
|
edit(fileInstance) {
|
|
98
|
-
|
|
99
|
-
|
|
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) {
|
|
100
160
|
fileInstance.setOptions({
|
|
101
|
-
...
|
|
102
|
-
useTokenTransformer: true
|
|
103
|
-
enableGutterUtility: false,
|
|
104
|
-
enableLineSelection: false,
|
|
105
|
-
expandUnchanged: true,
|
|
106
|
-
lineHoverHighlight: "disabled"
|
|
161
|
+
...fileInstance.options,
|
|
162
|
+
useTokenTransformer: true
|
|
107
163
|
});
|
|
108
164
|
fileInstance.rerender();
|
|
109
165
|
}
|
|
@@ -113,23 +169,22 @@ var Editor = class {
|
|
|
113
169
|
return () => this.cleanUp();
|
|
114
170
|
}
|
|
115
171
|
/**
|
|
116
|
-
* 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.
|
|
117
180
|
*/
|
|
118
|
-
applyEdits(edits, updateHistory =
|
|
181
|
+
applyEdits(edits, updateHistory = true) {
|
|
119
182
|
const textDocument = this.#textDocument;
|
|
120
183
|
if (textDocument == null) throw new Error("Editor is not attached");
|
|
121
184
|
const wasFocused = this.#contentHasFocus;
|
|
122
185
|
const selectionsBefore = this.#selections;
|
|
123
186
|
const selectionOffsetsBefore = selectionsBefore?.map((selection) => [textDocument.offsetAt(selection.start), textDocument.offsetAt(selection.end)]);
|
|
124
|
-
const resolvedEditOffsets = selectionsBefore === void 0 ? void 0 : edits.
|
|
125
|
-
const a = textDocument.offsetAt(edit.range.start);
|
|
126
|
-
const b = textDocument.offsetAt(edit.range.end);
|
|
127
|
-
return {
|
|
128
|
-
start: Math.min(a, b),
|
|
129
|
-
end: Math.max(a, b),
|
|
130
|
-
text: edit.newText
|
|
131
|
-
};
|
|
132
|
-
}).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);
|
|
133
188
|
const change = textDocument.applyEdits(edits, updateHistory, selectionsBefore);
|
|
134
189
|
if (change === void 0) return;
|
|
135
190
|
let nextSelections;
|
|
@@ -155,45 +210,58 @@ var Editor = class {
|
|
|
155
210
|
redo() {
|
|
156
211
|
this.#runCommand("redo");
|
|
157
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
|
+
}
|
|
158
227
|
getState() {
|
|
159
|
-
const
|
|
160
|
-
if (fileRef === void 0) throw new Error("Editor is not attached");
|
|
228
|
+
const fileInstance = this.#fileInstance;
|
|
161
229
|
return {
|
|
162
|
-
file: {
|
|
163
|
-
...fileRef,
|
|
164
|
-
cacheKey: "edited-at-" + Date.now()
|
|
165
|
-
},
|
|
166
230
|
selections: this.#selections,
|
|
167
|
-
|
|
168
|
-
renderRange: this.#renderRange
|
|
231
|
+
view: fileInstance != null ? { scrollLeft: fileInstance.getCodeScrollLeft() } : void 0
|
|
169
232
|
};
|
|
170
233
|
}
|
|
171
|
-
setState({
|
|
172
|
-
this.#
|
|
173
|
-
this.#
|
|
174
|
-
this.#
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
lineAnnotations,
|
|
181
|
-
renderRange
|
|
182
|
-
});
|
|
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();
|
|
183
243
|
}
|
|
184
244
|
setSelections(selections) {
|
|
185
245
|
const textDocument = this.#textDocument;
|
|
186
246
|
if (textDocument === void 0) throw new Error("Text document is not initialized");
|
|
187
247
|
const resolvedSelections = selections.map((selection) => {
|
|
188
|
-
|
|
189
|
-
|
|
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
|
+
}
|
|
190
255
|
return {
|
|
191
|
-
direction
|
|
256
|
+
direction,
|
|
192
257
|
start,
|
|
193
258
|
end
|
|
194
259
|
};
|
|
195
260
|
});
|
|
261
|
+
this.#canMountSelectionAction = false;
|
|
196
262
|
this.#updateSelections(resolvedSelections);
|
|
263
|
+
const primarySelection = resolvedSelections.at(-1);
|
|
264
|
+
if (primarySelection !== void 0) this.#revealLineIfCollapsed(getCaretPosition(primarySelection).line);
|
|
197
265
|
this.#scrollToPrimaryCaret();
|
|
198
266
|
}
|
|
199
267
|
setMarkers(markers) {
|
|
@@ -220,6 +288,19 @@ var Editor = class {
|
|
|
220
288
|
}
|
|
221
289
|
focus(options) {
|
|
222
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
|
+
}
|
|
223
304
|
const primarySelection = this.#selections?.at(-1);
|
|
224
305
|
if (primarySelection !== void 0) {
|
|
225
306
|
const pos = primarySelection.direction === -1 ? primarySelection.end : primarySelection.start;
|
|
@@ -227,9 +308,20 @@ var Editor = class {
|
|
|
227
308
|
} else this.#focus(void 0, preventScroll);
|
|
228
309
|
}
|
|
229
310
|
blur() {
|
|
311
|
+
this.#replacementFocusRequest = void 0;
|
|
312
|
+
this.#contentHasFocus = false;
|
|
313
|
+
this.#shouldIgnoreSelectionChange = false;
|
|
230
314
|
this.#contentElement?.blur();
|
|
231
315
|
}
|
|
232
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;
|
|
324
|
+
dequeueRender(this.#handleCustomPasteEvent);
|
|
233
325
|
this.#tokenizer?.cleanUp();
|
|
234
326
|
this.#tokenizer = void 0;
|
|
235
327
|
if (!recycle) {
|
|
@@ -242,7 +334,7 @@ var Editor = class {
|
|
|
242
334
|
this.#editorEventDisposes = void 0;
|
|
243
335
|
this.#selectEventDisposes?.forEach((dispose) => dispose());
|
|
244
336
|
this.#selectEventDisposes = void 0;
|
|
245
|
-
this.#detach?.();
|
|
337
|
+
this.#detach?.(recycle);
|
|
246
338
|
this.#detach = void 0;
|
|
247
339
|
this.#gutterWidthCache = void 0;
|
|
248
340
|
this.#contentWidthCache = void 0;
|
|
@@ -264,6 +356,7 @@ var Editor = class {
|
|
|
264
356
|
this.#gutterElement = void 0;
|
|
265
357
|
this.#contentElement?.removeAttribute("contentEditable");
|
|
266
358
|
this.#contentElement = void 0;
|
|
359
|
+
this.#replacementFocusRequest = void 0;
|
|
267
360
|
this.#contentHasFocus = false;
|
|
268
361
|
this.#overlayElement?.remove();
|
|
269
362
|
this.#overlayElement = void 0;
|
|
@@ -275,6 +368,24 @@ var Editor = class {
|
|
|
275
368
|
this.#themeSelectionRefreshFrame = void 0;
|
|
276
369
|
}
|
|
277
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
|
+
};
|
|
278
389
|
}
|
|
279
390
|
/** @internal */
|
|
280
391
|
__postponeBgTokenizeToNextFrame() {
|
|
@@ -287,7 +398,15 @@ var Editor = class {
|
|
|
287
398
|
}
|
|
288
399
|
}
|
|
289
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 */
|
|
290
407
|
__syncRenderView = (highlighter, fileContainer, fileOrDiff, lineAnnotations, renderRange) => {
|
|
408
|
+
const fileInstance = this.#fileInstance;
|
|
409
|
+
if (fileInstance == null) return;
|
|
291
410
|
const shadowRoot = fileContainer.shadowRoot;
|
|
292
411
|
if (shadowRoot == null) {
|
|
293
412
|
console.error("[editor] Could not find the shadow root.");
|
|
@@ -306,7 +425,10 @@ var Editor = class {
|
|
|
306
425
|
}
|
|
307
426
|
break;
|
|
308
427
|
}
|
|
309
|
-
if (codeElement === void 0 || contentEl === void 0)
|
|
428
|
+
if (codeElement === void 0 || contentEl === void 0) {
|
|
429
|
+
this.#replacementFocusRequest = void 0;
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
310
432
|
this.#getPopoverManager().setViewportElements(fileContainer, codeElement);
|
|
311
433
|
if (this.#fileContainer !== fileContainer) {
|
|
312
434
|
this.#fileContainer = fileContainer;
|
|
@@ -315,28 +437,42 @@ var Editor = class {
|
|
|
315
437
|
if (this.#themeStyleElement !== void 0) shadowRoot.appendChild(this.#themeStyleElement);
|
|
316
438
|
if (this.#spriteElement !== void 0) shadowRoot.prepend(this.#spriteElement);
|
|
317
439
|
}
|
|
318
|
-
|
|
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();
|
|
319
445
|
let contents = "";
|
|
320
446
|
if ("contents" in fileOrDiff) contents = fileOrDiff.contents;
|
|
321
447
|
else contents = fileOrDiff.additionLines.join("");
|
|
322
448
|
const editStack = new EditStack({ maxEntries: this.#options.historyMaxEntries });
|
|
323
|
-
const textDocument = new TextDocument(fileOrDiff.name, contents, fileOrDiff.lang ?? getFiletypeFromFileName(fileOrDiff.name), 0, editStack);
|
|
324
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);
|
|
325
452
|
this.#fileInfo = {
|
|
326
453
|
name,
|
|
327
454
|
lang,
|
|
328
455
|
cacheKey
|
|
329
456
|
};
|
|
330
457
|
this.#textDocument = textDocument;
|
|
458
|
+
if (persistedCacheKey !== void 0) {
|
|
459
|
+
this.#textDocumentCache.set(persistedCacheKey, textDocument);
|
|
460
|
+
persistedStateTarget = {
|
|
461
|
+
cacheKey: persistedCacheKey,
|
|
462
|
+
textDocument
|
|
463
|
+
};
|
|
464
|
+
}
|
|
331
465
|
this.#tokenizer?.cleanUp();
|
|
332
466
|
this.#tokenizer = void 0;
|
|
333
467
|
this.#resetState();
|
|
468
|
+
this.#wrapLineOffsetsCache.clear();
|
|
334
469
|
this.#selections = this.#initSelections;
|
|
335
|
-
requestAnimationFrame(() => {
|
|
336
|
-
this.#options.onAttach?.(this, this.#fileInstance);
|
|
337
|
-
});
|
|
338
470
|
if (this.#textDocument !== void 0 && this.#options.__debug === true) console.log("[diffs/editor] text document rebuilt from", fileOrDiff.name);
|
|
339
471
|
}
|
|
472
|
+
if (persistedStateTarget === void 0 && this.#restoreStateOnNextSync && persistedCacheKey !== void 0 && this.#textDocument !== void 0) persistedStateTarget = {
|
|
473
|
+
cacheKey: persistedCacheKey,
|
|
474
|
+
textDocument: this.#textDocument
|
|
475
|
+
};
|
|
340
476
|
const textDocument = this.#textDocument;
|
|
341
477
|
if (this.#tokenizer == null && textDocument != null) this.#tokenizer = new EditorTokenizer({
|
|
342
478
|
highlighter,
|
|
@@ -368,11 +504,12 @@ var Editor = class {
|
|
|
368
504
|
this.#listenContentElement(contentEl, gutterEl);
|
|
369
505
|
if (this.#contentElement !== void 0 && this.#options.__debug === true) console.log("[diffs/editor] full re-render triggered !!!");
|
|
370
506
|
}
|
|
507
|
+
if (this.#replacementFocusRequest != null) this.#replacementFocusRequest.target = contentEl;
|
|
371
508
|
if (contentEl.ariaLabel !== fileOrDiff.name) contentEl.ariaLabel = fileOrDiff.name;
|
|
372
|
-
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) {
|
|
373
510
|
const el = child;
|
|
374
|
-
const { lineAnnotation, lineType } = el.dataset;
|
|
375
|
-
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");
|
|
376
513
|
}
|
|
377
514
|
this.#resetCache();
|
|
378
515
|
this.#tokenizer?.syncTheme(this.#fileInstance?.options ?? {});
|
|
@@ -380,19 +517,140 @@ var Editor = class {
|
|
|
380
517
|
this.#renderRange = renderRange;
|
|
381
518
|
this.#viewportWindowLines = renderRange?.totalLines;
|
|
382
519
|
this.#tokenizer?.prebuildStateStack(renderRange);
|
|
383
|
-
this.#markerRenderer?.
|
|
520
|
+
this.#markerRenderer?.removePopover();
|
|
384
521
|
if (this.#selections !== void 0 || this.#matches !== void 0 || this.#markerRenderer !== void 0) this.#updateSelections(this.#selections ?? []);
|
|
385
522
|
if (this.#initSelections !== void 0 && this.#primaryCaretElement !== void 0) {
|
|
386
523
|
this.#initSelections = void 0;
|
|
387
524
|
this.#scrollToPrimaryCaret(false, "center");
|
|
388
525
|
} else if (this.#scrollingToLine !== void 0) this.#scrollToLine(this.#scrollingToLine, this.#scrollingToLineChar, this.#scrollingToLineNoFocus);
|
|
389
|
-
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 });
|
|
390
528
|
if (this.#retainSearchPanelFocus) this.#searchPanel?.focus();
|
|
391
529
|
if (this.#options.__debug === true && renderRange !== void 0) {
|
|
392
530
|
const { startingLine, totalLines } = renderRange;
|
|
393
531
|
console.log("[diffs/editor] render file:", fileOrDiff.name, "RenderRange:", startingLine + "-" + (startingLine + totalLines), "of", this.#textDocument?.lineCount, "lines");
|
|
394
532
|
}
|
|
533
|
+
if (persistedStateTarget !== void 0) {
|
|
534
|
+
this.#restoreStateOnNextSync = false;
|
|
535
|
+
this.#restorePersistedState(persistedStateTarget.cacheKey, persistedStateTarget.textDocument);
|
|
536
|
+
}
|
|
537
|
+
this.#scheduleOnAttach(fileInstance);
|
|
395
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
|
+
}
|
|
396
654
|
get #diffSyle() {
|
|
397
655
|
return this.#fileInstance?.options.diffStyle ?? "split";
|
|
398
656
|
}
|
|
@@ -404,21 +662,24 @@ var Editor = class {
|
|
|
404
662
|
}
|
|
405
663
|
#getPopoverManager() {
|
|
406
664
|
return this.#popoverManager ??= new PopoverManager({
|
|
407
|
-
hasActivePopover: () => this.#selectionAction !== void 0,
|
|
408
|
-
updateActivePopover: () =>
|
|
665
|
+
hasActivePopover: () => this.#selectionAction !== void 0 || this.#markerRenderer?.isPopoverVisible() === true,
|
|
666
|
+
updateActivePopover: () => {
|
|
667
|
+
this.#updateSelectionActionPopover();
|
|
668
|
+
this.#markerRenderer?.updatePopoverPosition();
|
|
669
|
+
}
|
|
409
670
|
});
|
|
410
671
|
}
|
|
411
672
|
#resetCache() {
|
|
412
673
|
this.#lineYCache.clear();
|
|
413
|
-
this.#wrapLineOffsetsCache.clear();
|
|
414
674
|
this.#lineElementsCache.clear();
|
|
415
675
|
this.#lastAccessedCharX = void 0;
|
|
416
676
|
}
|
|
417
677
|
#resetState() {
|
|
418
|
-
this.#
|
|
678
|
+
this.#setEditorActiveLineSafe(null);
|
|
419
679
|
this.#gutterWidthCache = void 0;
|
|
420
680
|
this.#contentWidthCache = void 0;
|
|
421
681
|
this.#shouldIgnoreSelectionChange = false;
|
|
682
|
+
this.#suppressNativeSelectionSync = false;
|
|
422
683
|
this.#overlayElements?.forEach((el) => el.remove());
|
|
423
684
|
this.#overlayElements = void 0;
|
|
424
685
|
this.#selections = void 0;
|
|
@@ -430,6 +691,33 @@ var Editor = class {
|
|
|
430
691
|
this.#searchPanel = void 0;
|
|
431
692
|
this.#selectionAction?.cleanup();
|
|
432
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);
|
|
433
721
|
}
|
|
434
722
|
#initialize() {
|
|
435
723
|
this.#globalStyleElement = h("style", {
|
|
@@ -448,14 +736,33 @@ var Editor = class {
|
|
|
448
736
|
this.#themeStyleElement = h("style", { dataset: "editorThemeCss" });
|
|
449
737
|
this.#spriteElement = createSpriteElement();
|
|
450
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
|
+
};
|
|
451
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
|
+
}),
|
|
452
759
|
addEventListener(document, "selectionchange", () => {
|
|
453
760
|
const shadowRoot = this.#fileContainer?.shadowRoot;
|
|
454
761
|
if (this.#shouldIgnoreSelectionChange || this.#suppressNativeSelectionSync || shadowRoot == null || !this.#contentHasFocus) return;
|
|
455
762
|
if (this.#selections !== void 0 && this.#selections.length > 1 && !this.#isContentMouseDown) return;
|
|
456
763
|
const selectionRaw = document.getSelection();
|
|
457
|
-
if (selectionRaw == null
|
|
458
|
-
const composedRange = selectionRaw.getComposedRanges({ shadowRoots: [shadowRoot] })?.[0];
|
|
764
|
+
if (selectionRaw == null) return;
|
|
765
|
+
const composedRange = typeof selectionRaw.getComposedRanges === "function" ? selectionRaw.getComposedRanges({ shadowRoots: [shadowRoot] })?.[0] : getShadowRootRange(shadowRoot);
|
|
459
766
|
if (composedRange === void 0 || !this.#rangeBelongsToEditor(composedRange)) return;
|
|
460
767
|
let selection = convertSelection(composedRange, 0);
|
|
461
768
|
if (selection === void 0) return;
|
|
@@ -501,6 +808,12 @@ var Editor = class {
|
|
|
501
808
|
this.#selectEventDisposes?.forEach((dispose) => dispose());
|
|
502
809
|
this.#selectEventDisposes = disposes;
|
|
503
810
|
}
|
|
811
|
+
#isLineSelectionEnabled() {
|
|
812
|
+
return this.#fileInstance?.options.enableLineSelection === true;
|
|
813
|
+
}
|
|
814
|
+
#preserveEditorSelectionsForGutterGesture() {
|
|
815
|
+
this.#suppressNativeSelectionSync = this.#selections !== void 0;
|
|
816
|
+
}
|
|
504
817
|
#listenContentElement(contentEl, gutterEl) {
|
|
505
818
|
const { onFocus, onBlur } = this.#options;
|
|
506
819
|
const targetIsContentElement = (e) => {
|
|
@@ -515,10 +828,15 @@ var Editor = class {
|
|
|
515
828
|
if (!this.#isContentMouseDown && !this.#shouldIgnoreSelectionChange && this.#selections !== void 0 && this.#selections.length > 0) this.#setWindowSelection(this.#selections.at(-1));
|
|
516
829
|
}, { passive: true }),
|
|
517
830
|
addEventListener(contentEl, "blur", () => {
|
|
831
|
+
if (this.#replacementFocusRequest?.target === contentEl) {
|
|
832
|
+
this.#replacementFocusRequest = void 0;
|
|
833
|
+
this.#shouldIgnoreSelectionChange = false;
|
|
834
|
+
}
|
|
518
835
|
this.#contentHasFocus = false;
|
|
519
836
|
onBlur?.();
|
|
520
837
|
}, { passive: true }),
|
|
521
838
|
addEventListener(contentEl, "pointerdown", (e) => {
|
|
839
|
+
this.#canMountSelectionAction = true;
|
|
522
840
|
this.#suppressNativeSelectionSync = false;
|
|
523
841
|
if (e.pointerType !== "mouse") return;
|
|
524
842
|
if (this.#isDeletedLineTarget(e)) {
|
|
@@ -527,7 +845,7 @@ var Editor = class {
|
|
|
527
845
|
return;
|
|
528
846
|
}
|
|
529
847
|
this.#setDeletedTextSelectionActive(false);
|
|
530
|
-
this.#markerRenderer?.
|
|
848
|
+
this.#markerRenderer?.removePopover();
|
|
531
849
|
if (isSafari() && this.#lineAnnotations !== void 0 && this.#lineAnnotations.length > 0) {
|
|
532
850
|
const annotationDisposes = [...contentEl.querySelectorAll("[data-line-annotation]")].map((el) => [addEventListener(el, "mouseenter", () => {
|
|
533
851
|
this.#shouldIgnoreSelectionChange = true;
|
|
@@ -555,32 +873,26 @@ var Editor = class {
|
|
|
555
873
|
addEventListener(contentEl, "keydown", (e) => {
|
|
556
874
|
if (e.key === "Escape") {
|
|
557
875
|
e.preventDefault();
|
|
558
|
-
this.#searchPanel?.
|
|
876
|
+
this.#searchPanel?.close();
|
|
559
877
|
this.#searchPanel = void 0;
|
|
560
878
|
this.#retainSearchPanelFocus = false;
|
|
561
879
|
this.#selectionAction?.cleanup();
|
|
562
880
|
this.#selectionAction = void 0;
|
|
563
|
-
|
|
564
|
-
const primarySelection = this.#selections.at(-1);
|
|
565
|
-
if (!isCollapsedSelection(primarySelection) || this.#selections.length > 1) {
|
|
566
|
-
const pos = getCaretPosition(primarySelection);
|
|
567
|
-
this.#updateSelections([{
|
|
568
|
-
start: pos,
|
|
569
|
-
end: pos,
|
|
570
|
-
direction: 0
|
|
571
|
-
}]);
|
|
572
|
-
this.#focus(pos);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
881
|
+
this.#runCommand("simplifySelection");
|
|
575
882
|
return;
|
|
576
883
|
}
|
|
577
884
|
if (!targetIsContentElement(e)) return;
|
|
885
|
+
this.#canMountSelectionAction = true;
|
|
578
886
|
this.#suppressNativeSelectionSync = false;
|
|
579
887
|
const mvShortcut = isMoveCursorShortcut(e);
|
|
580
888
|
const textDocument = this.#textDocument;
|
|
581
889
|
if (this.#selections !== void 0 && this.#selections.length > 0 && mvShortcut !== void 0 && textDocument !== void 0) {
|
|
582
|
-
|
|
583
|
-
|
|
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));
|
|
584
896
|
this.#scrollToPrimaryCaret();
|
|
585
897
|
e.preventDefault();
|
|
586
898
|
return;
|
|
@@ -613,19 +925,34 @@ var Editor = class {
|
|
|
613
925
|
addEventListener(contentEl, "copy", (e) => {
|
|
614
926
|
if (!targetIsContentElement(e)) return;
|
|
615
927
|
e.preventDefault();
|
|
616
|
-
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());
|
|
617
930
|
}),
|
|
618
931
|
addEventListener(contentEl, "cut", (e) => {
|
|
619
932
|
if (!targetIsContentElement(e)) return;
|
|
620
933
|
e.preventDefault();
|
|
621
|
-
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
|
+
}
|
|
622
939
|
}),
|
|
623
940
|
addEventListener(contentEl, "paste", (e) => {
|
|
624
941
|
if (!targetIsContentElement(e)) return;
|
|
625
942
|
e.preventDefault();
|
|
626
|
-
const
|
|
943
|
+
const clipboardData = e.clipboardData;
|
|
627
944
|
const textDocument = this.#textDocument;
|
|
628
|
-
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");
|
|
629
956
|
}),
|
|
630
957
|
addEventListener(contentEl, "beforeinput", (e) => {
|
|
631
958
|
if (!targetIsContentElement(e)) return;
|
|
@@ -652,9 +979,20 @@ var Editor = class {
|
|
|
652
979
|
];
|
|
653
980
|
const deletionsCode = this.#fileContainer?.shadowRoot?.querySelector("[data-deletions]");
|
|
654
981
|
if (deletionsCode != null) this.#editorEventDisposes.push(addEventListener(deletionsCode, "pointerdown", (e) => {
|
|
655
|
-
const
|
|
982
|
+
const path = e.composedPath();
|
|
983
|
+
if (isGutterUtilityPath(path)) {
|
|
984
|
+
this.#preserveEditorSelectionsForGutterGesture();
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
const target = path[0];
|
|
656
988
|
const gutterRow = target instanceof HTMLElement ? target.closest("[data-column-number]") : null;
|
|
657
|
-
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
|
+
}
|
|
658
996
|
this.#setDeletedTextSelectionActive(true);
|
|
659
997
|
if (this.#selections !== void 0) this.#updateSelections([]);
|
|
660
998
|
}, { passive: true }));
|
|
@@ -673,7 +1011,16 @@ var Editor = class {
|
|
|
673
1011
|
return lineNumber - 1;
|
|
674
1012
|
};
|
|
675
1013
|
this.#editorEventDisposes.push(addEventListener(gutterEl, "pointerdown", (e) => {
|
|
676
|
-
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
|
+
}
|
|
677
1024
|
if (gutterRow?.dataset.lineType === "change-deletion") {
|
|
678
1025
|
const code = gutterRow.closest("[data-code]");
|
|
679
1026
|
if (code != null) this.#beginDeletionGutterSelection(gutterRow, code, e.pointerType === "mouse");
|
|
@@ -683,7 +1030,8 @@ var Editor = class {
|
|
|
683
1030
|
const textDocument = this.#textDocument;
|
|
684
1031
|
const lineIndex = resolveEditableLine(gutterRow);
|
|
685
1032
|
if (lineIndex === void 0 || textDocument === void 0) return;
|
|
686
|
-
this.#
|
|
1033
|
+
this.#canMountSelectionAction = true;
|
|
1034
|
+
this.#markerRenderer?.removePopover();
|
|
687
1035
|
const selection = this.#spanLineSelection(lineIndex, lineIndex, textDocument);
|
|
688
1036
|
this.#isGutterMouseDown = true;
|
|
689
1037
|
this.#selectionStart = selection;
|
|
@@ -713,8 +1061,17 @@ var Editor = class {
|
|
|
713
1061
|
#handleCustomPasteEvent = async () => {
|
|
714
1062
|
const clipboard = this.#options.clipboard;
|
|
715
1063
|
if (clipboard !== void 0) {
|
|
716
|
-
|
|
717
|
-
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");
|
|
718
1075
|
}
|
|
719
1076
|
};
|
|
720
1077
|
#computeContentOffset(contentEl) {
|
|
@@ -750,9 +1107,11 @@ var Editor = class {
|
|
|
750
1107
|
this.#updateSelections(expanded);
|
|
751
1108
|
this.focus();
|
|
752
1109
|
} else {
|
|
753
|
-
const nextMatch =
|
|
1110
|
+
const nextMatch = findNextMatch(textDocument, selections);
|
|
754
1111
|
if (nextMatch !== void 0) {
|
|
755
1112
|
this.#updateSelections(nextMatch);
|
|
1113
|
+
const primaryMatch = nextMatch.at(-1);
|
|
1114
|
+
if (primaryMatch !== void 0) this.#revealLineIfCollapsed(getCaretPosition(primaryMatch).line);
|
|
756
1115
|
this.#scrollToPrimaryCaret();
|
|
757
1116
|
}
|
|
758
1117
|
}
|
|
@@ -762,18 +1121,74 @@ var Editor = class {
|
|
|
762
1121
|
case "moveLineDown":
|
|
763
1122
|
this.#moveSelectedLines(command === "moveLineUp" ? -1 : 1);
|
|
764
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
|
+
}
|
|
765
1170
|
case "indent":
|
|
766
1171
|
case "outdent":
|
|
1172
|
+
case "indentLess":
|
|
1173
|
+
case "indentMore":
|
|
767
1174
|
if (this.#selections !== void 0) {
|
|
768
1175
|
const edits = [];
|
|
769
1176
|
const nextSelections = [];
|
|
1177
|
+
const editedLines = /* @__PURE__ */ new Set();
|
|
770
1178
|
const sameLineIndents = [];
|
|
771
1179
|
for (const selection of this.#selections) {
|
|
772
1180
|
const startLine = selection.start.line;
|
|
773
|
-
const outdent = command === "outdent";
|
|
774
|
-
|
|
1181
|
+
const outdent = command === "outdent" || command === "indentLess";
|
|
1182
|
+
const lineBased = command === "indentLess" || command === "indentMore";
|
|
1183
|
+
if (startLine !== selection.end.line || outdent || lineBased) {
|
|
775
1184
|
const ret = resolveIndentEdits(textDocument, selection, this.#metrics.tabSize, outdent);
|
|
776
|
-
|
|
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
|
+
}
|
|
777
1192
|
nextSelections.push(ret[1]);
|
|
778
1193
|
} else {
|
|
779
1194
|
const text = textDocument.charAt({
|
|
@@ -825,17 +1240,19 @@ var Editor = class {
|
|
|
825
1240
|
const fullSelection = getDocumentFullSelection(textDocument);
|
|
826
1241
|
this.#updateSelections([fullSelection]);
|
|
827
1242
|
this.focus();
|
|
828
|
-
const
|
|
829
|
-
|
|
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) {
|
|
830
1247
|
this.#suppressNativeSelectionSync = true;
|
|
831
1248
|
this.#setWindowSelection({
|
|
832
1249
|
start: {
|
|
833
|
-
line:
|
|
1250
|
+
line: firstRenderedLine,
|
|
834
1251
|
character: 0
|
|
835
1252
|
},
|
|
836
1253
|
end: {
|
|
837
|
-
line:
|
|
838
|
-
character: textDocument.getLineLength(
|
|
1254
|
+
line: lastRenderedLine,
|
|
1255
|
+
character: textDocument.getLineLength(lastRenderedLine)
|
|
839
1256
|
},
|
|
840
1257
|
direction: 1
|
|
841
1258
|
});
|
|
@@ -845,8 +1262,9 @@ var Editor = class {
|
|
|
845
1262
|
case "moveCursorToDocStart":
|
|
846
1263
|
case "moveCursorToDocEnd":
|
|
847
1264
|
{
|
|
848
|
-
const
|
|
849
|
-
this.#updateSelections([
|
|
1265
|
+
const boundarySelection = getDocumentBoundarySelection(textDocument, command === "moveCursorToDocEnd", this.#isDiff);
|
|
1266
|
+
this.#updateSelections([boundarySelection]);
|
|
1267
|
+
this.#revealLineIfCollapsed(getCaretPosition(boundarySelection).line);
|
|
850
1268
|
this.#scrollToPrimaryCaret();
|
|
851
1269
|
}
|
|
852
1270
|
break;
|
|
@@ -856,25 +1274,191 @@ var Editor = class {
|
|
|
856
1274
|
const atEnd = command === "expandSelectionDocEnd";
|
|
857
1275
|
const selections = this.#selections;
|
|
858
1276
|
if (selections !== void 0) {
|
|
859
|
-
|
|
1277
|
+
const boundarySelection = getDocumentBoundarySelection(textDocument, atEnd, this.#isDiff);
|
|
1278
|
+
this.#updateSelections(extendSelections(selections, boundarySelection));
|
|
1279
|
+
this.#revealLineIfCollapsed(getCaretPosition(boundarySelection).line);
|
|
860
1280
|
this.#scrollToPrimaryCaret();
|
|
861
1281
|
}
|
|
862
1282
|
}
|
|
863
1283
|
break;
|
|
864
1284
|
case "undo":
|
|
865
|
-
if (this.#textDocument?.canUndo === true) {
|
|
866
|
-
const undoResult = this.#textDocument.undo();
|
|
867
|
-
if (undoResult !== void 0) this.#applyChange(...undoResult);
|
|
868
|
-
}
|
|
869
|
-
break;
|
|
870
1285
|
case "redo":
|
|
871
|
-
|
|
872
|
-
const redoResult = this.#textDocument.redo();
|
|
873
|
-
if (redoResult !== void 0) this.#applyChange(...redoResult);
|
|
874
|
-
}
|
|
1286
|
+
this.#applyHistoryChange(command);
|
|
875
1287
|
break;
|
|
876
1288
|
}
|
|
877
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
|
+
}
|
|
878
1462
|
#moveSelectedLines(direction) {
|
|
879
1463
|
const textDocument = this.#textDocument;
|
|
880
1464
|
const selections = this.#selections;
|
|
@@ -943,15 +1527,15 @@ var Editor = class {
|
|
|
943
1527
|
this.#lineElementsCache.clear();
|
|
944
1528
|
this.#lastAccessedCharX = void 0;
|
|
945
1529
|
this.#metrics.clearTextWidthCache();
|
|
946
|
-
if (contentWidthChanged
|
|
947
|
-
this.#lineYCache.clear();
|
|
1530
|
+
if (contentWidthChanged) {
|
|
948
1531
|
this.#wrapLineOffsetsCache.clear();
|
|
1532
|
+
if (this.#isWrap || lineAnnotations > 0) this.#lineYCache.clear();
|
|
949
1533
|
}
|
|
950
1534
|
if (this.#selections !== void 0 || this.#matches !== void 0 || this.#markerRenderer !== void 0) {
|
|
951
1535
|
this.#updateSelections(this.#selections ?? []);
|
|
952
|
-
if (this.#selections !== void 0) this.focus();
|
|
1536
|
+
if (this.#selections !== void 0 && this.#contentHasFocus) this.focus();
|
|
953
1537
|
}
|
|
954
|
-
this.#markerRenderer?.
|
|
1538
|
+
this.#markerRenderer?.removePopover();
|
|
955
1539
|
this.#computeContentOffset(this.#contentElement);
|
|
956
1540
|
};
|
|
957
1541
|
#remeasureMetricsOnFontLoad() {
|
|
@@ -964,8 +1548,9 @@ var Editor = class {
|
|
|
964
1548
|
this.#gutterWidthCache = void 0;
|
|
965
1549
|
this.#contentWidthCache = void 0;
|
|
966
1550
|
this.#resetCache();
|
|
1551
|
+
this.#wrapLineOffsetsCache.clear();
|
|
967
1552
|
if (this.#selections !== void 0 || this.#matches !== void 0 || this.#markerRenderer !== void 0) this.#updateSelections(this.#selections ?? []);
|
|
968
|
-
this.#markerRenderer?.
|
|
1553
|
+
this.#markerRenderer?.removePopover();
|
|
969
1554
|
});
|
|
970
1555
|
}
|
|
971
1556
|
#rerender(change, newLineAnnotations, renderRange = this.#renderRange, shouldUpdateBuffer) {
|
|
@@ -981,45 +1566,57 @@ var Editor = class {
|
|
|
981
1566
|
const t2 = performance.now();
|
|
982
1567
|
if (dirtyLines.size > 0) {
|
|
983
1568
|
const children = contentEl.children;
|
|
984
|
-
const dirtyLineIndexes = new Set(
|
|
985
|
-
const
|
|
986
|
-
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++) {
|
|
987
1572
|
const child = children[i];
|
|
988
1573
|
if (child !== void 0) {
|
|
989
1574
|
const lineNumber = getLineNumberAttr(child);
|
|
990
1575
|
const lineType = child.dataset.lineType;
|
|
991
1576
|
if (lineNumber === void 0 || lineType === "change-deletion") continue;
|
|
992
1577
|
const lineIndex = lineNumber - 1;
|
|
993
|
-
if (
|
|
1578
|
+
if (dirtyLineIndexes.has(lineIndex)) {
|
|
994
1579
|
const tokens = dirtyLines.get(lineIndex);
|
|
995
|
-
child.replaceChildren(...renderLineTokens(tokens
|
|
1580
|
+
child.replaceChildren(...renderLineTokens(tokens));
|
|
996
1581
|
dirtyLineIndexes.delete(lineIndex);
|
|
997
1582
|
if (dirtyLineIndexes.size === 0) break;
|
|
998
1583
|
}
|
|
999
1584
|
}
|
|
1000
1585
|
}
|
|
1001
|
-
if (dirtyLineIndexes.size > 0)
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
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
|
+
}
|
|
1023
1620
|
}
|
|
1024
1621
|
}
|
|
1025
1622
|
if (change.lineDelta < 0) for (const children of [contentEl.children, gutterEl?.children ?? []]) for (let i = children.length - 1; i >= 0; i--) {
|
|
@@ -1040,9 +1637,9 @@ var Editor = class {
|
|
|
1040
1637
|
contentEl.style.gridRow = "span " + gridRow;
|
|
1041
1638
|
if (gutterEl !== void 0) gutterEl.style.gridRow = "span " + gridRow;
|
|
1042
1639
|
}
|
|
1043
|
-
fileInstance.updateRenderCache(dirtyLines, tokenizer.themeType, !didLineCountChange);
|
|
1640
|
+
fileInstance.updateRenderCache(dirtyLines, tokenizer.themeType, !didLineCountChange, didLineCountChange);
|
|
1044
1641
|
if (didLineCountChange) fileInstance.applyDocumentChange(textDocument, newLineAnnotations, shouldUpdateBuffer);
|
|
1045
|
-
if (this.#isDiff &&
|
|
1642
|
+
if (didLineCountChange || this.#isDiff && this.#diffSyle === "unified") this.#resetCache();
|
|
1046
1643
|
if (newLineAnnotations !== void 0) {
|
|
1047
1644
|
this.#lineAnnotations = newLineAnnotations;
|
|
1048
1645
|
renderLineAnnotations(newLineAnnotations, contentEl, gutterEl);
|
|
@@ -1062,7 +1659,7 @@ var Editor = class {
|
|
|
1062
1659
|
case "insertCompositionText": break;
|
|
1063
1660
|
case "insertLineBreak":
|
|
1064
1661
|
case "insertParagraph":
|
|
1065
|
-
this.#replaceSelectionText("\n");
|
|
1662
|
+
this.#replaceSelectionText(this.#textDocument?.eol ?? "\n");
|
|
1066
1663
|
break;
|
|
1067
1664
|
case "deleteContentBackward":
|
|
1068
1665
|
this.#deleteSelectionText();
|
|
@@ -1074,9 +1671,6 @@ var Editor = class {
|
|
|
1074
1671
|
case "deleteHardLineBackward":
|
|
1075
1672
|
this.#deleteSoftLineBackward();
|
|
1076
1673
|
break;
|
|
1077
|
-
case "deleteHardLineForward":
|
|
1078
|
-
this.#deleteHardLineForward();
|
|
1079
|
-
break;
|
|
1080
1674
|
case "deleteWordBackward":
|
|
1081
1675
|
this.#deleteWordBackward();
|
|
1082
1676
|
break;
|
|
@@ -1088,7 +1682,7 @@ var Editor = class {
|
|
|
1088
1682
|
break;
|
|
1089
1683
|
}
|
|
1090
1684
|
}
|
|
1091
|
-
#focus(position, preventScroll = true) {
|
|
1685
|
+
#focus(position, preventScroll = true, shouldFocus) {
|
|
1092
1686
|
this.#contentHasFocus = true;
|
|
1093
1687
|
if (position !== void 0) {
|
|
1094
1688
|
this.#shouldIgnoreSelectionChange = true;
|
|
@@ -1097,13 +1691,115 @@ var Editor = class {
|
|
|
1097
1691
|
end: position,
|
|
1098
1692
|
direction: 0
|
|
1099
1693
|
});
|
|
1100
|
-
|
|
1694
|
+
queueRender(() => {
|
|
1695
|
+
if (shouldFocus?.() === false) {
|
|
1696
|
+
this.#shouldIgnoreSelectionChange = false;
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1101
1699
|
this.#contentElement?.focus({ preventScroll });
|
|
1102
|
-
|
|
1700
|
+
queueRender(() => {
|
|
1103
1701
|
this.#shouldIgnoreSelectionChange = false;
|
|
1104
1702
|
});
|
|
1105
1703
|
});
|
|
1106
|
-
} 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();
|
|
1107
1803
|
}
|
|
1108
1804
|
#setWindowSelection(selection) {
|
|
1109
1805
|
const winSelection = window.getSelection();
|
|
@@ -1136,7 +1832,11 @@ var Editor = class {
|
|
|
1136
1832
|
block: scrollPosition,
|
|
1137
1833
|
inline: "nearest"
|
|
1138
1834
|
});
|
|
1139
|
-
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
|
+
}
|
|
1140
1840
|
} else {
|
|
1141
1841
|
const pos = getCaretPosition(primarySelection);
|
|
1142
1842
|
this.#scrollToLine(pos.line, pos.character, noFocus);
|
|
@@ -1166,10 +1866,14 @@ var Editor = class {
|
|
|
1166
1866
|
block: "center",
|
|
1167
1867
|
inline: "nearest"
|
|
1168
1868
|
});
|
|
1169
|
-
if (!noFocus)
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
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
|
+
}
|
|
1173
1877
|
this.#scrollingToLine = void 0;
|
|
1174
1878
|
this.#scrollingToLineChar = void 0;
|
|
1175
1879
|
this.#scrollingToLineFixed = false;
|
|
@@ -1183,7 +1887,7 @@ var Editor = class {
|
|
|
1183
1887
|
block: "center",
|
|
1184
1888
|
inline: "nearest"
|
|
1185
1889
|
});
|
|
1186
|
-
if (modelLinePosition.height > 0) {
|
|
1890
|
+
if (modelLinePosition.height > 0 && this.#isLineRenderable(line)) {
|
|
1187
1891
|
this.#scrollingToLine = line;
|
|
1188
1892
|
this.#scrollingToLineChar = char;
|
|
1189
1893
|
this.#scrollingToLineNoFocus = noFocus;
|
|
@@ -1210,7 +1914,7 @@ var Editor = class {
|
|
|
1210
1914
|
block: "center",
|
|
1211
1915
|
inline: "nearest"
|
|
1212
1916
|
});
|
|
1213
|
-
if (this.#scrollingToLine === line && (yFix === 0 || this.#scrollingToLineFixed)) {
|
|
1917
|
+
if (this.#scrollingToLine === line && (yFix === 0 || this.#scrollingToLineFixed) || !this.#isLineRenderable(line)) {
|
|
1214
1918
|
this.#scrollingToLine = void 0;
|
|
1215
1919
|
this.#scrollingToLineChar = void 0;
|
|
1216
1920
|
this.#scrollingToLineFixed = false;
|
|
@@ -1264,7 +1968,7 @@ var Editor = class {
|
|
|
1264
1968
|
}
|
|
1265
1969
|
this.#setDeletedTextSelectionActive(true);
|
|
1266
1970
|
this.#updateSelections([]);
|
|
1267
|
-
|
|
1971
|
+
this.#setEditorActiveLineSafe(getLineNumberAttr(focusContent) ?? null, true, "deletions");
|
|
1268
1972
|
const lo = Math.min(anchorIndex, focusIndex);
|
|
1269
1973
|
const hi = Math.max(anchorIndex, focusIndex);
|
|
1270
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");
|
|
@@ -1302,19 +2006,18 @@ var Editor = class {
|
|
|
1302
2006
|
if (pre == null) return;
|
|
1303
2007
|
if (active) {
|
|
1304
2008
|
pre.setAttribute("data-deleted-text-selection", "");
|
|
1305
|
-
|
|
2009
|
+
this.#setEditorActiveLineSafe(null);
|
|
1306
2010
|
this.#deletedSelectionText = "";
|
|
1307
2011
|
} else {
|
|
1308
2012
|
pre.removeAttribute("data-deleted-text-selection");
|
|
1309
2013
|
this.#deletedSelectionText = "";
|
|
1310
2014
|
}
|
|
1311
2015
|
}
|
|
1312
|
-
#
|
|
2016
|
+
#setEditorActiveLineSafe(lineNumber, lineNumberOnly = false, side = "additions") {
|
|
1313
2017
|
try {
|
|
1314
|
-
this.#fileInstance?.
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
lineNumberOnly
|
|
2018
|
+
this.#fileInstance?.setEditorActiveLine(lineNumber, {
|
|
2019
|
+
lineNumberOnly,
|
|
2020
|
+
side
|
|
1318
2021
|
});
|
|
1319
2022
|
} catch {}
|
|
1320
2023
|
}
|
|
@@ -1328,7 +2031,7 @@ var Editor = class {
|
|
|
1328
2031
|
#updateSelections(selections) {
|
|
1329
2032
|
this.__postponeBgTokenizeToNextFrame();
|
|
1330
2033
|
this.#primaryCaretElement = void 0;
|
|
1331
|
-
this.#
|
|
2034
|
+
this.#setEditorActiveLineSafe(null);
|
|
1332
2035
|
if (selections.length === 0 && this.#matches === void 0 && this.#markerRenderer === void 0) {
|
|
1333
2036
|
this.#selections = void 0;
|
|
1334
2037
|
this.#overlayElements?.forEach((el) => el.remove());
|
|
@@ -1348,10 +2051,7 @@ var Editor = class {
|
|
|
1348
2051
|
this.#selections = normalizedSelections;
|
|
1349
2052
|
const hasNonEmptySelection = normalizedSelections.some((selection) => !isCollapsedSelection(selection));
|
|
1350
2053
|
const caretLine = getCaretPosition(primarySelection).line + 1;
|
|
1351
|
-
this.#
|
|
1352
|
-
start: caretLine,
|
|
1353
|
-
end: caretLine
|
|
1354
|
-
}, hasNonEmptySelection);
|
|
2054
|
+
this.#setEditorActiveLineSafe(caretLine, hasNonEmptySelection);
|
|
1355
2055
|
for (const selection of normalizedSelections) {
|
|
1356
2056
|
if (!isCollapsedSelection(selection)) this.#renderSelection(renderCtx, "selection", selection);
|
|
1357
2057
|
this.#renderCaret(renderCtx, selection, selection === primarySelection);
|
|
@@ -1361,16 +2061,50 @@ var Editor = class {
|
|
|
1361
2061
|
}
|
|
1362
2062
|
const textDocument = this.#textDocument;
|
|
1363
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;
|
|
1364
2067
|
const primarySelection = this.#selections?.at(-1);
|
|
1365
2068
|
const primaryStartOffset = primarySelection !== void 0 ? textDocument.offsetAt(primarySelection.start) : -1;
|
|
1366
2069
|
const primaryEndOffset = primarySelection !== void 0 ? textDocument.offsetAt(primarySelection.end) : -1;
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
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
|
+
}
|
|
1374
2108
|
}
|
|
1375
2109
|
}
|
|
1376
2110
|
if (this.#markerRenderer !== void 0 && textDocument !== void 0) for (const marker of this.#markerRenderer.markers) this.#renderSelection(renderCtx, "marker", marker, markerSeverityDatasetKey(marker.severity));
|
|
@@ -1383,7 +2117,14 @@ var Editor = class {
|
|
|
1383
2117
|
#renderSelection(renderCtx, type, range, extraDataset) {
|
|
1384
2118
|
if (this.#textDocument === void 0) return;
|
|
1385
2119
|
const { start, end } = range;
|
|
1386
|
-
|
|
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++) {
|
|
1387
2128
|
if (!this.#isLineVisible(line)) continue;
|
|
1388
2129
|
const isLastLine = line === end.line;
|
|
1389
2130
|
const startChar = line === start.line ? start.character : 0;
|
|
@@ -1440,8 +2181,10 @@ var Editor = class {
|
|
|
1440
2181
|
const rounded = (this.#options.roundedSelection ?? true) && type === "selection";
|
|
1441
2182
|
const addRoundedCorner = (line, wrapLine, left, radius) => {
|
|
1442
2183
|
const top = this.#getLineY(line) + wrapLine * lineHeight;
|
|
1443
|
-
const
|
|
1444
|
-
|
|
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}`;
|
|
1445
2188
|
const dataset = {
|
|
1446
2189
|
selectionCorner: "",
|
|
1447
2190
|
[radius]: ""
|
|
@@ -1560,13 +2303,14 @@ var Editor = class {
|
|
|
1560
2303
|
return;
|
|
1561
2304
|
}
|
|
1562
2305
|
if (this.#selectionAction === void 0) {
|
|
2306
|
+
if (!this.#canMountSelectionAction) return;
|
|
1563
2307
|
const getActiveSelection = () => this.#selections?.at(-1) ?? primarySelection;
|
|
1564
2308
|
const selectionActionElement = renderSelectionAction({
|
|
1565
2309
|
textDocument,
|
|
1566
2310
|
get selection() {
|
|
1567
2311
|
return getActiveSelection();
|
|
1568
2312
|
},
|
|
1569
|
-
applyEdits: (edits) => this.applyEdits(edits
|
|
2313
|
+
applyEdits: (edits) => this.applyEdits(edits),
|
|
1570
2314
|
getSelectionText: () => this.#textDocument?.getText(getActiveSelection()) ?? "",
|
|
1571
2315
|
replaceSelectionText: (text) => {
|
|
1572
2316
|
this.#replaceSelectionText(text, [getActiveSelection()]);
|
|
@@ -1577,7 +2321,7 @@ var Editor = class {
|
|
|
1577
2321
|
}
|
|
1578
2322
|
});
|
|
1579
2323
|
this.#selectionAction = new SelectionActionWidget(selectionActionElement, overlayElement, () => this.#updateSelectionActionPopover());
|
|
1580
|
-
this.#getPopoverManager().resetPlacement();
|
|
2324
|
+
this.#getPopoverManager().resetPlacement(SELECTION_ACTION_POPOVER_PLACEMENT_KEY);
|
|
1581
2325
|
}
|
|
1582
2326
|
const lineHeight = this.#metrics.lineHeight;
|
|
1583
2327
|
const isBackward = primarySelection.direction === -1;
|
|
@@ -1611,17 +2355,19 @@ var Editor = class {
|
|
|
1611
2355
|
const atDocumentEdge = isBackward ? head.line < 3 : head.line >= lineCount - 3;
|
|
1612
2356
|
const canUseFallback = this.#isLineVisible(fallback.anchor.line);
|
|
1613
2357
|
const popoverManager = this.#getPopoverManager();
|
|
2358
|
+
const viewport = popoverManager.getPlacementBounds();
|
|
1614
2359
|
const useFallback = canUseFallback && popoverManager.choosePlacement({
|
|
1615
2360
|
preferred: preferredGeometry,
|
|
1616
2361
|
fallback: fallbackGeometry,
|
|
1617
|
-
viewport
|
|
2362
|
+
viewport,
|
|
1618
2363
|
popoverHeight,
|
|
1619
|
-
atDocumentEdge
|
|
2364
|
+
atDocumentEdge,
|
|
2365
|
+
placementKey: SELECTION_ACTION_POPOVER_PLACEMENT_KEY
|
|
1620
2366
|
}) === "fallback";
|
|
1621
|
-
if (!canUseFallback) popoverManager.setPlacement("preferred");
|
|
2367
|
+
if (!canUseFallback) popoverManager.setPlacement("preferred", SELECTION_ACTION_POPOVER_PLACEMENT_KEY);
|
|
1622
2368
|
const { placeAbove } = useFallback ? fallback : preferred;
|
|
1623
2369
|
const { left, anchorTop } = useFallback ? fallbackGeometry : preferredGeometry;
|
|
1624
|
-
this.#selectionAction.reposition(left, anchorTop, this.#getGutterWidth(), placeAbove);
|
|
2370
|
+
this.#selectionAction.reposition(left, anchorTop, this.#getGutterWidth(), placeAbove, viewport);
|
|
1625
2371
|
}
|
|
1626
2372
|
#openSearchPanel(mode) {
|
|
1627
2373
|
if (this.#searchPanel !== void 0) {
|
|
@@ -1653,6 +2399,7 @@ var Editor = class {
|
|
|
1653
2399
|
const scrollToMatch = ([startOffset, endOffset], retainFocus) => {
|
|
1654
2400
|
const nextSelection = createSelectionFromAnchorAndFocusOffsets(textDocument, startOffset, endOffset);
|
|
1655
2401
|
this.#updateSelections([nextSelection]);
|
|
2402
|
+
this.#revealLineIfCollapsed(getCaretPosition(nextSelection).line);
|
|
1656
2403
|
this.#scrollToPrimaryCaret(true);
|
|
1657
2404
|
this.#retainSearchPanelFocus = retainFocus;
|
|
1658
2405
|
};
|
|
@@ -1719,6 +2466,18 @@ var Editor = class {
|
|
|
1719
2466
|
if (textDocument === void 0 || selections === void 0) return "";
|
|
1720
2467
|
return getSelectionText(textDocument, selections);
|
|
1721
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
|
+
}
|
|
1722
2481
|
#cutSelectionText() {
|
|
1723
2482
|
const textDocument = this.#textDocument;
|
|
1724
2483
|
const selections = this.#selections;
|
|
@@ -1749,15 +2508,15 @@ var Editor = class {
|
|
|
1749
2508
|
textDocument.setLastUndoSelectionsAfter(nextSelections);
|
|
1750
2509
|
this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change));
|
|
1751
2510
|
}
|
|
1752
|
-
#replaceSelectionText(text, selections = this.#selections, undoBoundary = false) {
|
|
2511
|
+
#replaceSelectionText(text, selections = this.#selections, undoBoundary = false, textOrder = "selection") {
|
|
1753
2512
|
if (selections === void 0) return;
|
|
1754
2513
|
const textDocument = this.#textDocument;
|
|
1755
2514
|
const primarySelection = selections.at(-1);
|
|
1756
2515
|
if (textDocument === void 0 || primarySelection === void 0) return;
|
|
1757
|
-
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, {
|
|
1758
2517
|
start: textDocument.offsetAt(primarySelection.start),
|
|
1759
2518
|
end: textDocument.offsetAt(primarySelection.end),
|
|
1760
|
-
text: Array.isArray(text) ? text.join(
|
|
2519
|
+
text: Array.isArray(text) ? text.join(textDocument.eol) : text
|
|
1761
2520
|
}, this.#lineAnnotations, void 0, undoBoundary);
|
|
1762
2521
|
if (change !== void 0) this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change));
|
|
1763
2522
|
}
|
|
@@ -1804,26 +2563,16 @@ var Editor = class {
|
|
|
1804
2563
|
const { nextSelections, change } = applyTransposeToSelections(textDocument, selections, this.#lineAnnotations);
|
|
1805
2564
|
if (change !== void 0) this.#applyChange(change, nextSelections, this.#applyChangeToLineAnnotations(change));
|
|
1806
2565
|
}
|
|
1807
|
-
#getFileRef() {
|
|
1808
|
-
const fileInfo = this.#fileInfo;
|
|
1809
|
-
const textDocument = this.#textDocument;
|
|
1810
|
-
if (fileInfo === void 0 || textDocument === void 0) return;
|
|
1811
|
-
const file = { ...fileInfo };
|
|
1812
|
-
Object.defineProperty(file, "contents", {
|
|
1813
|
-
enumerable: true,
|
|
1814
|
-
get: () => textDocument.getText()
|
|
1815
|
-
});
|
|
1816
|
-
return file;
|
|
1817
|
-
}
|
|
1818
2566
|
#applyChange(change, newSelections, newLineAnnotations, options) {
|
|
1819
|
-
const fileRef = this
|
|
2567
|
+
const fileRef = this.getFile();
|
|
1820
2568
|
const onChange = this.#options.onChange;
|
|
1821
2569
|
if (fileRef !== void 0 && onChange !== void 0) onChange(fileRef, newLineAnnotations ?? this.#lineAnnotations);
|
|
1822
2570
|
if (change.lineDelta !== 0 || this.#isWrap) {
|
|
1823
2571
|
for (const line of this.#lineYCache.keys()) if (line >= change.startLine) this.#lineYCache.delete(line);
|
|
1824
2572
|
}
|
|
1825
|
-
if (this.#
|
|
1826
|
-
|
|
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);
|
|
1827
2576
|
}
|
|
1828
2577
|
this.#lastAccessedCharX = void 0;
|
|
1829
2578
|
let renderRange = this.#renderRange;
|
|
@@ -1834,7 +2583,7 @@ var Editor = class {
|
|
|
1834
2583
|
if (primarySelection.end.line >= renderRangeEndLine) {
|
|
1835
2584
|
const widenedTotalLines = primarySelection.end.line - renderRange.startingLine + 1;
|
|
1836
2585
|
const maxWidenLines = this.#viewportWindowLines === void 0 || this.#viewportWindowLines === Infinity ? Infinity : this.#viewportWindowLines * MAX_EDIT_WIDEN_WINDOW_MULTIPLE;
|
|
1837
|
-
if (change.startLine <= renderRangeEndLine && widenedTotalLines <= maxWidenLines) {
|
|
2586
|
+
if (change.startLine <= renderRangeEndLine && widenedTotalLines <= maxWidenLines && this.#isLineRenderable(primarySelection.end.line)) {
|
|
1838
2587
|
if (primarySelection.end.line > renderRangeEndLine) shouldUpdateBuffer = true;
|
|
1839
2588
|
renderRange = {
|
|
1840
2589
|
...renderRange,
|
|
@@ -1849,6 +2598,8 @@ var Editor = class {
|
|
|
1849
2598
|
if (newSelections !== void 0) {
|
|
1850
2599
|
this.#updateSelections(newSelections);
|
|
1851
2600
|
if (options?.skipFocus !== true) {
|
|
2601
|
+
const revealTarget = newSelections.at(-1);
|
|
2602
|
+
if (revealTarget !== void 0) this.#revealLineIfCollapsed(getCaretPosition(revealTarget).line);
|
|
1852
2603
|
if (this.#primaryCaretElement !== void 0) requestAnimationFrame(() => {
|
|
1853
2604
|
this.#primaryCaretElement?.scrollIntoView({
|
|
1854
2605
|
block: "nearest",
|
|
@@ -1872,39 +2623,31 @@ var Editor = class {
|
|
|
1872
2623
|
}
|
|
1873
2624
|
}
|
|
1874
2625
|
}
|
|
1875
|
-
#
|
|
1876
|
-
const lineElement = this.#getLineElement(line);
|
|
1877
|
-
if (lineElement === void 0) return;
|
|
1878
|
-
if (navigator.userAgent.includes("jsdom")) return;
|
|
1879
|
-
const backgroundColor = getComputedStyle(lineElement, "::after").backgroundColor;
|
|
1880
|
-
return backgroundColor === "" || backgroundColor === "transparent" || backgroundColor === "rgba(0, 0, 0, 0)" ? void 0 : backgroundColor;
|
|
1881
|
-
}
|
|
1882
|
-
#getRenderedEditableLineRange() {
|
|
2626
|
+
#getRenderedEditableLineRanges() {
|
|
1883
2627
|
const contentElement = this.#contentElement;
|
|
1884
|
-
if (contentElement === void 0) return;
|
|
1885
|
-
|
|
1886
|
-
let last;
|
|
2628
|
+
if (contentElement === void 0) return [];
|
|
2629
|
+
const ranges = [];
|
|
1887
2630
|
for (const child of contentElement.children) {
|
|
1888
2631
|
const el = child;
|
|
1889
2632
|
const lineType = el.dataset.lineType;
|
|
1890
2633
|
const lineNumber = getLineNumberAttr(el);
|
|
1891
2634
|
if (lineNumber === void 0 || lineType === void 0 || !isLineEditable(lineType)) continue;
|
|
1892
2635
|
const line = lineNumber - 1;
|
|
1893
|
-
|
|
1894
|
-
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]);
|
|
1895
2639
|
}
|
|
1896
|
-
return
|
|
1897
|
-
first,
|
|
1898
|
-
last
|
|
1899
|
-
};
|
|
2640
|
+
return ranges;
|
|
1900
2641
|
}
|
|
1901
2642
|
#getLineElement(line) {
|
|
1902
2643
|
let lineElement = this.#lineElementsCache.get(line);
|
|
1903
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;
|
|
1904
2647
|
const contentElement = this.#contentElement;
|
|
1905
2648
|
if (contentElement === void 0) return;
|
|
1906
|
-
if (
|
|
1907
|
-
const { startingLine } =
|
|
2649
|
+
if (renderRange !== void 0) {
|
|
2650
|
+
const { startingLine } = renderRange;
|
|
1908
2651
|
const { children } = contentElement;
|
|
1909
2652
|
for (let i = line - startingLine; i <= children.length; i++) {
|
|
1910
2653
|
const child = children[i];
|
|
@@ -1995,6 +2738,7 @@ var Editor = class {
|
|
|
1995
2738
|
#wrapLineText(line) {
|
|
1996
2739
|
const cachedOffsets = this.#wrapLineOffsetsCache.get(line);
|
|
1997
2740
|
if (cachedOffsets !== void 0) return cachedOffsets;
|
|
2741
|
+
if (this.#wrapLineOffsetsCache.size >= MAX_WRAP_OFFSETS_CACHE_LINES) this.#wrapLineOffsetsCache.clear();
|
|
1998
2742
|
const lineText = this.#textDocument?.getLineText(line);
|
|
1999
2743
|
if (lineText === void 0 || lineText.length === 0) {
|
|
2000
2744
|
const offsets = new Uint32Array([0]);
|
|
@@ -2024,21 +2768,30 @@ var Editor = class {
|
|
|
2024
2768
|
try {
|
|
2025
2769
|
const unicodeOffsets = getUnicodeMeasurementOffsets(lineText);
|
|
2026
2770
|
const wrapLineStartLeft = div.getBoundingClientRect().left + this.#metrics.ch;
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
range.
|
|
2032
|
-
range.
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
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;
|
|
2038
2788
|
}
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
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;
|
|
2042
2795
|
}
|
|
2043
2796
|
const offsets = new Uint32Array(starts.length + 1);
|
|
2044
2797
|
for (let i = 0; i < starts.length; i++) offsets[i] = starts[i];
|