@pierre/diffs 1.3.0-beta.6 → 1.3.0-beta.8
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 +90 -5
- package/dist/components/CodeView.d.ts.map +1 -1
- package/dist/components/CodeView.js +291 -38
- package/dist/components/CodeView.js.map +1 -1
- package/dist/components/File.d.ts +1 -0
- package/dist/components/File.d.ts.map +1 -1
- package/dist/components/File.js +20 -6
- package/dist/components/File.js.map +1 -1
- package/dist/components/FileDiff.d.ts +38 -23
- package/dist/components/FileDiff.d.ts.map +1 -1
- package/dist/components/FileDiff.js +144 -61
- package/dist/components/FileDiff.js.map +1 -1
- package/dist/components/UnresolvedFile.d.ts +3 -2
- package/dist/components/UnresolvedFile.d.ts.map +1 -1
- package/dist/components/UnresolvedFile.js +4 -2
- package/dist/components/UnresolvedFile.js.map +1 -1
- package/dist/components/VirtualizedFile.d.ts.map +1 -1
- package/dist/components/VirtualizedFile.js +6 -8
- package/dist/components/VirtualizedFile.js.map +1 -1
- package/dist/components/VirtualizedFileDiff.d.ts +13 -5
- package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
- package/dist/components/VirtualizedFileDiff.js +192 -55
- package/dist/components/VirtualizedFileDiff.js.map +1 -1
- package/dist/components/Virtualizer.d.ts +2 -0
- package/dist/components/Virtualizer.d.ts.map +1 -1
- package/dist/components/Virtualizer.js +16 -3
- package/dist/components/Virtualizer.js.map +1 -1
- package/dist/constants.d.ts +3 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +3 -1
- package/dist/constants.js.map +1 -1
- package/dist/editor/command.d.ts +1 -1
- package/dist/editor/command.d.ts.map +1 -1
- package/dist/editor/command.js +8 -0
- package/dist/editor/command.js.map +1 -1
- package/dist/editor/editor.d.ts +10 -3
- package/dist/editor/editor.d.ts.map +1 -1
- package/dist/editor/editor.js +325 -135
- package/dist/editor/editor.js.map +1 -1
- package/dist/editor/editor2.js +1 -1
- package/dist/editor/lineAnnotations.d.ts.map +1 -1
- package/dist/editor/lineAnnotations.js +84 -12
- 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 +35 -12
- package/dist/editor/marker.js.map +1 -1
- package/dist/editor/matchBrackets.d.ts +8 -0
- package/dist/editor/matchBrackets.d.ts.map +1 -0
- package/dist/editor/matchBrackets.js +146 -0
- package/dist/editor/matchBrackets.js.map +1 -0
- package/dist/editor/popover.d.ts +45 -0
- package/dist/editor/popover.d.ts.map +1 -0
- package/dist/editor/popover.js +145 -0
- package/dist/editor/popover.js.map +1 -0
- package/dist/editor/selection.d.ts +14 -2
- package/dist/editor/selection.d.ts.map +1 -1
- package/dist/editor/selection.js +142 -41
- package/dist/editor/selection.js.map +1 -1
- package/dist/editor/selectionAction.d.ts +20 -3
- package/dist/editor/selectionAction.d.ts.map +1 -1
- package/dist/editor/selectionAction.js +41 -10
- package/dist/editor/selectionAction.js.map +1 -1
- package/dist/editor/textDocument.d.ts +2 -0
- package/dist/editor/textDocument.d.ts.map +1 -1
- package/dist/editor/textDocument.js +22 -6
- package/dist/editor/textDocument.js.map +1 -1
- package/dist/editor/textMeasure.d.ts.map +1 -1
- package/dist/editor/textMeasure.js +25 -7
- package/dist/editor/textMeasure.js.map +1 -1
- package/dist/editor/tokenzier.d.ts +6 -7
- package/dist/editor/tokenzier.d.ts.map +1 -1
- package/dist/editor/tokenzier.js +83 -34
- package/dist/editor/tokenzier.js.map +1 -1
- package/dist/editor/utils.d.ts +3 -1
- package/dist/editor/utils.d.ts.map +1 -1
- package/dist/editor/utils.js +14 -1
- package/dist/editor/utils.js.map +1 -1
- package/dist/index.d.ts +9 -7
- package/dist/index.js +6 -4
- package/dist/managers/InteractionManager.d.ts.map +1 -1
- package/dist/managers/InteractionManager.js +0 -1
- package/dist/managers/InteractionManager.js.map +1 -1
- package/dist/managers/ResizeManager.d.ts +10 -2
- package/dist/managers/ResizeManager.d.ts.map +1 -1
- package/dist/managers/ResizeManager.js +33 -13
- package/dist/managers/ResizeManager.js.map +1 -1
- package/dist/react/CodeView.d.ts +25 -1
- package/dist/react/CodeView.d.ts.map +1 -1
- package/dist/react/CodeView.js +76 -17
- package/dist/react/CodeView.js.map +1 -1
- package/dist/react/EditorContext.d.ts +2 -2
- package/dist/react/EditorContext.d.ts.map +1 -1
- package/dist/react/EditorContext.js.map +1 -1
- package/dist/react/MultiFileDiff.d.ts +3 -4
- package/dist/react/MultiFileDiff.d.ts.map +1 -1
- package/dist/react/MultiFileDiff.js.map +1 -1
- package/dist/react/index.d.ts +2 -2
- package/dist/react/utils/useFileDiffInstance.js +14 -15
- package/dist/react/utils/useFileDiffInstance.js.map +1 -1
- package/dist/renderers/DiffHunksRenderer.d.ts +2 -2
- package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
- package/dist/renderers/DiffHunksRenderer.js +45 -23
- package/dist/renderers/DiffHunksRenderer.js.map +1 -1
- package/dist/renderers/FileRenderer.d.ts +2 -1
- package/dist/renderers/FileRenderer.d.ts.map +1 -1
- package/dist/renderers/FileRenderer.js +58 -38
- package/dist/renderers/FileRenderer.js.map +1 -1
- package/dist/ssr/index.d.ts +2 -2
- package/dist/ssr/preloadDiffs.d.ts +11 -10
- package/dist/ssr/preloadDiffs.d.ts.map +1 -1
- package/dist/ssr/preloadDiffs.js +14 -6
- package/dist/ssr/preloadDiffs.js.map +1 -1
- package/dist/types.d.ts +100 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/applyLineTextWithNewline.d.ts +5 -0
- package/dist/utils/applyLineTextWithNewline.d.ts.map +1 -0
- package/dist/utils/applyLineTextWithNewline.js +11 -0
- package/dist/utils/applyLineTextWithNewline.js.map +1 -0
- package/dist/utils/areHunkDataEqual.js +1 -1
- package/dist/utils/areHunkDataEqual.js.map +1 -1
- package/dist/utils/areManagedSnapshotsEqual.d.ts +2 -2
- package/dist/utils/areManagedSnapshotsEqual.d.ts.map +1 -1
- package/dist/utils/areManagedSnapshotsEqual.js +5 -0
- package/dist/utils/areManagedSnapshotsEqual.js.map +1 -1
- package/dist/utils/awaitWithTimeout.d.ts +5 -0
- package/dist/utils/awaitWithTimeout.d.ts.map +1 -0
- package/dist/utils/awaitWithTimeout.js +15 -0
- package/dist/utils/awaitWithTimeout.js.map +1 -0
- package/dist/utils/cloneFileDiffMetadata.d.ts +7 -0
- package/dist/utils/cloneFileDiffMetadata.d.ts.map +1 -0
- package/dist/utils/cloneFileDiffMetadata.js +16 -0
- package/dist/utils/cloneFileDiffMetadata.js.map +1 -0
- package/dist/utils/computeEstimatedDiffHeights.d.ts +3 -1
- package/dist/utils/computeEstimatedDiffHeights.d.ts.map +1 -1
- package/dist/utils/computeEstimatedDiffHeights.js +8 -1
- package/dist/utils/computeEstimatedDiffHeights.js.map +1 -1
- package/dist/utils/createCodeViewHeaderFooterHostElement.d.ts +5 -0
- package/dist/utils/createCodeViewHeaderFooterHostElement.d.ts.map +1 -0
- package/dist/utils/createCodeViewHeaderFooterHostElement.js +19 -0
- package/dist/utils/createCodeViewHeaderFooterHostElement.js.map +1 -0
- package/dist/utils/createPreElement.js +0 -1
- package/dist/utils/createPreElement.js.map +1 -1
- package/dist/utils/getDiffFileInput.d.ts +14 -0
- package/dist/utils/getDiffFileInput.d.ts.map +1 -0
- package/dist/utils/getDiffFileInput.js +24 -0
- package/dist/utils/getDiffFileInput.js.map +1 -0
- package/dist/utils/getDiffHunksRendererOptions.js +1 -0
- package/dist/utils/getDiffHunksRendererOptions.js.map +1 -1
- package/dist/utils/getFiletypeFromFileName.d.ts.map +1 -1
- package/dist/utils/getFiletypeFromFileName.js +2 -0
- package/dist/utils/getFiletypeFromFileName.js.map +1 -1
- package/dist/utils/hydratePartialDiff.d.ts +10 -0
- package/dist/utils/hydratePartialDiff.d.ts.map +1 -0
- package/dist/utils/hydratePartialDiff.js +140 -0
- package/dist/utils/hydratePartialDiff.js.map +1 -0
- package/dist/utils/iterateOverDiff.js +3 -3
- package/dist/utils/iterateOverDiff.js.map +1 -1
- package/dist/utils/parseDiffFromFile.d.ts +1 -1
- package/dist/utils/parseDiffFromFile.d.ts.map +1 -1
- package/dist/utils/parseDiffFromFile.js +26 -5
- package/dist/utils/parseDiffFromFile.js.map +1 -1
- package/dist/utils/setWrapperNodeProps.js +0 -1
- package/dist/utils/setWrapperNodeProps.js.map +1 -1
- package/dist/utils/updateDiffHunks.d.ts +5 -1
- package/dist/utils/updateDiffHunks.d.ts.map +1 -1
- package/dist/utils/updateDiffHunks.js +48 -4
- package/dist/utils/updateDiffHunks.js.map +1 -1
- package/dist/worker/WorkerPoolManager.d.ts +7 -2
- package/dist/worker/WorkerPoolManager.d.ts.map +1 -1
- package/dist/worker/WorkerPoolManager.js +78 -15
- package/dist/worker/WorkerPoolManager.js.map +1 -1
- package/dist/worker/index.d.ts +2 -2
- package/dist/worker/types.d.ts +7 -1
- package/dist/worker/types.d.ts.map +1 -1
- package/dist/worker/worker-portable.js +5 -3
- package/dist/worker/worker-portable.js.map +1 -1
- package/dist/worker/worker.js +5 -3
- package/dist/worker/worker.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matchBrackets.js","names":[],"sources":["../../src/editor/matchBrackets.ts"],"sourcesContent":["import type { Position, Range, TextDocument } from './textDocument';\nimport type { EditorTokenizer } from './tokenzier';\n\nconst OPEN_BRACKETS = new Map([\n ['(', ')'],\n ['[', ']'],\n ['{', '}'],\n]);\n\nconst CLOSE_BRACKETS = new Map(\n [...OPEN_BRACKETS].map(([open, close]) => [close, open])\n);\n\nconst MAX_BRACKET_SCAN_LINES = 1_000;\nconst MAX_BRACKET_SCAN_CHARACTERS = 50_000;\n\ninterface BracketPosition extends Position {\n char: string;\n}\n\nexport function findBracketMatchRanges<LAnnotation>(\n textDocument: TextDocument<LAnnotation>,\n tokenizer: EditorTokenizer,\n position: Position\n): [open: Range, close: Range] | undefined {\n const bracketPosition = findAdjacentBracket(\n textDocument,\n tokenizer,\n textDocument.normalizePosition(position)\n );\n if (bracketPosition === undefined) {\n return undefined;\n }\n\n const closingBracket = OPEN_BRACKETS.get(bracketPosition.char);\n const openingBracket = CLOSE_BRACKETS.get(bracketPosition.char);\n if (closingBracket !== undefined) {\n const matchPosition = findClosingBracket(\n textDocument,\n tokenizer,\n bracketPosition,\n closingBracket\n );\n return createBracketMatchRanges(bracketPosition, matchPosition);\n }\n if (openingBracket !== undefined) {\n const matchPosition = findOpeningBracket(\n textDocument,\n tokenizer,\n bracketPosition,\n openingBracket\n );\n return createBracketMatchRanges(matchPosition, bracketPosition);\n }\n return undefined;\n}\n\nfunction findAdjacentBracket<LAnnotation>(\n textDocument: TextDocument<LAnnotation>,\n tokenizer: EditorTokenizer,\n position: Position\n): BracketPosition | undefined {\n const previousPosition = getPreviousCharacterPosition(textDocument, position);\n if (previousPosition !== undefined) {\n const previousBracket = getBracketAtPosition(\n textDocument,\n tokenizer,\n previousPosition\n );\n if (previousBracket !== undefined) {\n return previousBracket;\n }\n }\n return getBracketAtPosition(textDocument, tokenizer, position);\n}\n\nfunction getPreviousCharacterPosition<LAnnotation>(\n textDocument: TextDocument<LAnnotation>,\n position: Position\n): Position | undefined {\n if (position.character > 0) {\n return { line: position.line, character: position.character - 1 };\n }\n if (position.line <= 0) {\n return undefined;\n }\n const previousLine = position.line - 1;\n const previousLineLength = textDocument.getLineText(previousLine).length;\n if (previousLineLength === 0) {\n return undefined;\n }\n return { line: previousLine, character: previousLineLength - 1 };\n}\n\nfunction getBracketAtPosition<LAnnotation>(\n textDocument: TextDocument<LAnnotation>,\n tokenizer: EditorTokenizer,\n position: Position\n): BracketPosition | undefined {\n const lineText = textDocument.getLineText(position.line);\n const char = lineText[position.character];\n if (\n char === undefined ||\n (!OPEN_BRACKETS.has(char) && !CLOSE_BRACKETS.has(char)) ||\n isInIgnoredTokenRange(tokenizer, position)\n ) {\n return undefined;\n }\n return { ...position, char };\n}\n\nfunction findClosingBracket<LAnnotation>(\n textDocument: TextDocument<LAnnotation>,\n tokenizer: EditorTokenizer,\n bracketPosition: BracketPosition,\n closingBracket: string\n): BracketPosition | undefined {\n let depth = 0;\n let scannedLines = 0;\n let scannedCharacters = 0;\n for (let line = bracketPosition.line; line < textDocument.lineCount; line++) {\n if (scannedLines >= MAX_BRACKET_SCAN_LINES) {\n return undefined;\n }\n scannedLines++;\n const lineText = textDocument.getLineText(line);\n const ignoredRanges = tokenizer.getStringCommentRegexpRangesInLine(line);\n const ignoredRangeCursor = { index: 0 };\n const startCharacter =\n line === bracketPosition.line ? bracketPosition.character : 0;\n for (\n let character = startCharacter;\n character < lineText.length;\n character++\n ) {\n if (scannedCharacters >= MAX_BRACKET_SCAN_CHARACTERS) {\n return undefined;\n }\n scannedCharacters++;\n if (\n isCharacterIgnoredForward(ignoredRanges, character, ignoredRangeCursor)\n ) {\n continue;\n }\n const char = lineText[character];\n if (char === bracketPosition.char) {\n depth++;\n } else if (char === closingBracket) {\n depth--;\n if (depth === 0) {\n return { line, character, char };\n }\n }\n }\n }\n return undefined;\n}\n\nfunction findOpeningBracket<LAnnotation>(\n textDocument: TextDocument<LAnnotation>,\n tokenizer: EditorTokenizer,\n bracketPosition: BracketPosition,\n openingBracket: string\n): BracketPosition | undefined {\n let depth = 0;\n let scannedLines = 0;\n let scannedCharacters = 0;\n for (let line = bracketPosition.line; line >= 0; line--) {\n if (scannedLines >= MAX_BRACKET_SCAN_LINES) {\n return undefined;\n }\n scannedLines++;\n const lineText = textDocument.getLineText(line);\n const ignoredRanges = tokenizer.getStringCommentRegexpRangesInLine(line);\n const ignoredRangeCursor = {\n index: ignoredRanges === null ? -1 : ignoredRanges.length - 1,\n };\n const startCharacter =\n line === bracketPosition.line\n ? bracketPosition.character\n : lineText.length - 1;\n for (let character = startCharacter; character >= 0; character--) {\n if (scannedCharacters >= MAX_BRACKET_SCAN_CHARACTERS) {\n return undefined;\n }\n scannedCharacters++;\n if (\n isCharacterIgnoredBackward(ignoredRanges, character, ignoredRangeCursor)\n ) {\n continue;\n }\n const char = lineText[character];\n if (char === bracketPosition.char) {\n depth++;\n } else if (char === openingBracket) {\n depth--;\n if (depth === 0) {\n return { line, character, char };\n }\n }\n }\n }\n return undefined;\n}\n\nfunction isInIgnoredTokenRange(\n tokenizer: EditorTokenizer,\n position: Position\n): boolean {\n const ranges = tokenizer.getStringCommentRegexpRangesInLine(position.line);\n return isCharacterInIgnoredRanges(ranges, position.character);\n}\n\nfunction isCharacterInIgnoredRanges(\n ranges: [start: number, end: number][] | null,\n character: number\n): boolean {\n if (ranges === null) {\n return false;\n }\n for (const [start, end] of ranges) {\n if (character < start) {\n return false;\n }\n if (character < end) {\n return true;\n }\n }\n return false;\n}\n\nfunction isCharacterIgnoredForward(\n ranges: [start: number, end: number][] | null,\n character: number,\n cursor: { index: number }\n): boolean {\n if (ranges === null) {\n return false;\n }\n while (cursor.index < ranges.length && character >= ranges[cursor.index][1]) {\n cursor.index++;\n }\n const range = ranges[cursor.index];\n return range !== undefined && character >= range[0];\n}\n\nfunction isCharacterIgnoredBackward(\n ranges: [start: number, end: number][] | null,\n character: number,\n cursor: { index: number }\n): boolean {\n if (ranges === null) {\n return false;\n }\n while (cursor.index >= 0 && character < ranges[cursor.index][0]) {\n cursor.index--;\n }\n const range = ranges[cursor.index];\n return range !== undefined && character < range[1];\n}\n\nfunction createBracketMatchRanges(\n firstPosition: Position | undefined,\n secondPosition: Position | undefined\n): [open: Range, close: Range] | undefined {\n if (firstPosition === undefined || secondPosition === undefined) {\n return;\n }\n return [\n createCharacterRange(firstPosition),\n createCharacterRange(secondPosition),\n ];\n}\n\nfunction createCharacterRange(position: Position): Range {\n return {\n start: { line: position.line, character: position.character },\n end: { line: position.line, character: position.character + 1 },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region src/editor/popover.d.ts
|
|
2
|
+
declare const POPOVER_BOUNDARY_LINES = 3;
|
|
3
|
+
declare const POPOVER_FLIP_HYSTERESIS_PX = 4;
|
|
4
|
+
/**
|
|
5
|
+
* The visible scroll viewport expressed in the overlay's coordinate space (the
|
|
6
|
+
* same space --popover-y uses), or undefined when no usable layout geometry
|
|
7
|
+
* exists (e.g. a detached unit-test DOM, where every rect is zero).
|
|
8
|
+
*/
|
|
9
|
+
interface PopoverPlacementBounds {
|
|
10
|
+
/** The top of the popover in overlay coordinate space. */
|
|
11
|
+
top: number;
|
|
12
|
+
/** The bottom of the popover in overlay coordinate space. */
|
|
13
|
+
bottom: number;
|
|
14
|
+
}
|
|
15
|
+
interface PopoverManagerOptions {
|
|
16
|
+
hasActivePopover: () => boolean;
|
|
17
|
+
updateActivePopover: () => void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Shared placement logic for the editor's overlay popovers: each anchors below
|
|
21
|
+
* or above a document position, flipping to the opposite side only when the
|
|
22
|
+
* preferred side would be clipped by the visible scrollport.
|
|
23
|
+
*/
|
|
24
|
+
declare class PopoverManager {
|
|
25
|
+
#private;
|
|
26
|
+
constructor(options: PopoverManagerOptions);
|
|
27
|
+
setViewportElements(fileContainer: HTMLElement, codeElement: HTMLElement): void;
|
|
28
|
+
cleanUp(): void;
|
|
29
|
+
resetPlacement(): void;
|
|
30
|
+
setPlacement(placement: 'preferred' | 'fallback'): void;
|
|
31
|
+
choosePlacement(input: {
|
|
32
|
+
/** Bounds for the anchor's preferred side (e.g. above for backward, below for forward). */preferred: PopoverPlacementBounds; /** Bounds for the opposite edge we flip to when the preferred side has no room. */
|
|
33
|
+
fallback: PopoverPlacementBounds; /** The visible scrollport in overlay coordinate space, or undefined without layout geometry (e.g. a detached test DOM). */
|
|
34
|
+
viewport: PopoverPlacementBounds | undefined; /** The popover's measured height; 0 before it has laid out. */
|
|
35
|
+
popoverHeight: number; /** Whether the anchor is within the document's first/last rows; only used as a fallback signal when `viewport` is unavailable. */
|
|
36
|
+
atDocumentEdge: boolean;
|
|
37
|
+
}): 'preferred' | 'fallback';
|
|
38
|
+
/**
|
|
39
|
+
* Returns the bounds of the popover in overlay coordinate space.
|
|
40
|
+
*/
|
|
41
|
+
getPlacementBounds(): PopoverPlacementBounds | undefined;
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { POPOVER_BOUNDARY_LINES, POPOVER_FLIP_HYSTERESIS_PX, PopoverManager, PopoverManagerOptions, PopoverPlacementBounds };
|
|
45
|
+
//# sourceMappingURL=popover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popover.d.ts","names":[],"sources":["../../src/editor/popover.ts"],"mappings":";cAIa,sBAAA;AAAA,cAKA,0BAAA;;;;AALsB;AAKnC;UAOiB,sBAAA;;EAEf,GAAA;EATqC;EAWrC,MAAM;AAAA;AAAA,UAGS,qBAAA;EACf,gBAAA;EACA,mBAAmB;AAAA;;;;AAAA;AAQrB;cAAa,cAAA;EAAA;EAcX,WAAA,CAAY,OAAA,EAAS,qBAAA;EAKrB,mBAAA,CACE,aAAA,EAAe,WAAA,EACf,WAAA,EAAa,WAAA;EAaf,OAAA;EAaA,cAAA;EAIA,YAAA,CAAa,SAAA;EAOb,eAAA,CAAgB,KAAA;IAwCM,2FAtCpB,SAAA,EAAW,sBAAA,EAsC+B;IApC1C,QAAA,EAAU,sBAAA,EAhDZ;IAkDE,QAAA,EAAU,sBAAA,cAlDA;IAoDV,aAAA,UA9Ce;IAgDf,cAAA;EAAA;EA/CA;;;EA6EF,kBAAA,IAAsB,sBAAA;AAAA"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { addEventListener } from "./utils.js";
|
|
2
|
+
//#region src/editor/popover.ts
|
|
3
|
+
const POPOVER_BOUNDARY_LINES = 3;
|
|
4
|
+
const POPOVER_FLIP_HYSTERESIS_PX = 4;
|
|
5
|
+
/**
|
|
6
|
+
* Shared placement logic for the editor's overlay popovers: each anchors below
|
|
7
|
+
* or above a document position, flipping to the opposite side only when the
|
|
8
|
+
* preferred side would be clipped by the visible scrollport.
|
|
9
|
+
*/
|
|
10
|
+
var PopoverManager = class {
|
|
11
|
+
#fileContainer;
|
|
12
|
+
#codeElement;
|
|
13
|
+
#scrollContainer;
|
|
14
|
+
#scrollContainerSource;
|
|
15
|
+
#cachedCodeRect;
|
|
16
|
+
#cachedScrollContainerRect;
|
|
17
|
+
#viewportRectsDirty = true;
|
|
18
|
+
#viewportRectListenersDisposes;
|
|
19
|
+
#placement;
|
|
20
|
+
#hasActivePopover;
|
|
21
|
+
#updateActivePopover;
|
|
22
|
+
constructor(options) {
|
|
23
|
+
this.#hasActivePopover = options.hasActivePopover;
|
|
24
|
+
this.#updateActivePopover = options.updateActivePopover;
|
|
25
|
+
}
|
|
26
|
+
setViewportElements(fileContainer, codeElement) {
|
|
27
|
+
this.#fileContainer = fileContainer;
|
|
28
|
+
if (this.#codeElement === codeElement) return;
|
|
29
|
+
this.#codeElement = codeElement;
|
|
30
|
+
this.#viewportRectsDirty = true;
|
|
31
|
+
}
|
|
32
|
+
cleanUp() {
|
|
33
|
+
this.#codeElement = void 0;
|
|
34
|
+
this.#fileContainer = void 0;
|
|
35
|
+
this.#scrollContainer = void 0;
|
|
36
|
+
this.#scrollContainerSource = void 0;
|
|
37
|
+
this.#cachedCodeRect = void 0;
|
|
38
|
+
this.#cachedScrollContainerRect = void 0;
|
|
39
|
+
this.#viewportRectsDirty = true;
|
|
40
|
+
this.#viewportRectListenersDisposes?.forEach((dispose) => dispose());
|
|
41
|
+
this.#viewportRectListenersDisposes = void 0;
|
|
42
|
+
this.#placement = void 0;
|
|
43
|
+
}
|
|
44
|
+
resetPlacement() {
|
|
45
|
+
this.#placement = void 0;
|
|
46
|
+
}
|
|
47
|
+
setPlacement(placement) {
|
|
48
|
+
this.#placement = placement;
|
|
49
|
+
}
|
|
50
|
+
choosePlacement(input) {
|
|
51
|
+
const { preferred, fallback, viewport, popoverHeight, atDocumentEdge } = input;
|
|
52
|
+
let placement;
|
|
53
|
+
if (viewport !== void 0 && popoverHeight > 0) {
|
|
54
|
+
const fits = (bounds, margin = 0) => bounds.top >= viewport.top + margin && bounds.bottom <= viewport.bottom - margin;
|
|
55
|
+
if (this.#placement === "fallback" && fits(fallback) && !fits(preferred, 4)) placement = "fallback";
|
|
56
|
+
else if (!fits(preferred) && fits(fallback)) placement = "fallback";
|
|
57
|
+
else placement = "preferred";
|
|
58
|
+
} else placement = atDocumentEdge ? "fallback" : "preferred";
|
|
59
|
+
this.#placement = placement;
|
|
60
|
+
return placement;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Returns the bounds of the popover in overlay coordinate space.
|
|
64
|
+
*/
|
|
65
|
+
getPlacementBounds() {
|
|
66
|
+
const codeRect = this.#getCodeRect();
|
|
67
|
+
if (codeRect === void 0) return;
|
|
68
|
+
const scrollContainerRect = this.#getScrollContainerRect();
|
|
69
|
+
let topScreen;
|
|
70
|
+
let bottomScreen;
|
|
71
|
+
if (scrollContainerRect !== void 0) {
|
|
72
|
+
topScreen = scrollContainerRect.top;
|
|
73
|
+
bottomScreen = scrollContainerRect.bottom;
|
|
74
|
+
} else {
|
|
75
|
+
topScreen = 0;
|
|
76
|
+
bottomScreen = window.innerHeight;
|
|
77
|
+
}
|
|
78
|
+
if (bottomScreen <= topScreen) return;
|
|
79
|
+
return {
|
|
80
|
+
top: topScreen - codeRect.top,
|
|
81
|
+
bottom: bottomScreen - codeRect.top
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
#getScrollContainer() {
|
|
85
|
+
const fileContainer = this.#fileContainer;
|
|
86
|
+
if (fileContainer === void 0) return;
|
|
87
|
+
if (this.#scrollContainerSource === fileContainer) {
|
|
88
|
+
if (this.#scrollContainer === void 0 || this.#scrollContainer.isConnected) return this.#scrollContainer;
|
|
89
|
+
}
|
|
90
|
+
let element = fileContainer.parentElement;
|
|
91
|
+
while (element !== null) {
|
|
92
|
+
const overflowY = getComputedStyle(element).overflowY;
|
|
93
|
+
if (overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") {
|
|
94
|
+
this.#scrollContainer = element;
|
|
95
|
+
this.#scrollContainerSource = fileContainer;
|
|
96
|
+
return element;
|
|
97
|
+
}
|
|
98
|
+
element = element.parentElement;
|
|
99
|
+
}
|
|
100
|
+
this.#scrollContainer = void 0;
|
|
101
|
+
this.#scrollContainerSource = fileContainer;
|
|
102
|
+
}
|
|
103
|
+
#refreshViewportRectsIfNeeded() {
|
|
104
|
+
if (!this.#viewportRectsDirty) return;
|
|
105
|
+
this.#ensureViewportRectListeners();
|
|
106
|
+
this.#cachedCodeRect = this.#codeElement?.getBoundingClientRect();
|
|
107
|
+
this.#cachedScrollContainerRect = this.#getScrollContainer()?.getBoundingClientRect();
|
|
108
|
+
this.#viewportRectsDirty = false;
|
|
109
|
+
}
|
|
110
|
+
#getCodeRect() {
|
|
111
|
+
this.#refreshViewportRectsIfNeeded();
|
|
112
|
+
return this.#cachedCodeRect;
|
|
113
|
+
}
|
|
114
|
+
#getScrollContainerRect() {
|
|
115
|
+
this.#refreshViewportRectsIfNeeded();
|
|
116
|
+
return this.#cachedScrollContainerRect;
|
|
117
|
+
}
|
|
118
|
+
#ensureViewportRectListeners() {
|
|
119
|
+
if (this.#viewportRectListenersDisposes !== void 0) return;
|
|
120
|
+
let repositionRafId;
|
|
121
|
+
const markDirty = () => {
|
|
122
|
+
this.#viewportRectsDirty = true;
|
|
123
|
+
if (!this.#hasActivePopover() || repositionRafId !== void 0) return;
|
|
124
|
+
repositionRafId = requestAnimationFrame(() => {
|
|
125
|
+
repositionRafId = void 0;
|
|
126
|
+
this.#updateActivePopover();
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
const scroller = this.#getScrollContainer();
|
|
130
|
+
this.#viewportRectListenersDisposes = [
|
|
131
|
+
scroller !== void 0 ? addEventListener(scroller, "scroll", markDirty, { passive: true }) : addEventListener(window, "scroll", markDirty, { passive: true }),
|
|
132
|
+
addEventListener(window, "resize", markDirty, { passive: true }),
|
|
133
|
+
() => {
|
|
134
|
+
if (repositionRafId !== void 0) {
|
|
135
|
+
cancelAnimationFrame(repositionRafId);
|
|
136
|
+
repositionRafId = void 0;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
];
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
//#endregion
|
|
143
|
+
export { POPOVER_BOUNDARY_LINES, POPOVER_FLIP_HYSTERESIS_PX, PopoverManager };
|
|
144
|
+
|
|
145
|
+
//# sourceMappingURL=popover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popover.js","names":["#hasActivePopover","#updateActivePopover","#fileContainer","#codeElement","#viewportRectsDirty","#scrollContainer","#scrollContainerSource","#cachedCodeRect","#cachedScrollContainerRect","#viewportRectListenersDisposes","#placement","#getCodeRect","#getScrollContainerRect","#ensureViewportRectListeners","#getScrollContainer","#refreshViewportRectsIfNeeded"],"sources":["../../src/editor/popover.ts"],"sourcesContent":["import { addEventListener } from './utils';\n\n// Lines from the document edge at which the no-viewport fallback heuristic\n// (see `atDocumentEdge` below) flips placement.\nexport const POPOVER_BOUNDARY_LINES = 3;\n\n// Extra clearance (px) `preferred` must regain before a popover that flipped\n// to `fallback` flips back, so an anchor sitting right at the flip boundary\n// doesn't flicker between sides on every recompute.\nexport const POPOVER_FLIP_HYSTERESIS_PX = 4;\n\n/**\n * The visible scroll viewport expressed in the overlay's coordinate space (the\n * same space --popover-y uses), or undefined when no usable layout geometry\n * exists (e.g. a detached unit-test DOM, where every rect is zero).\n */\nexport interface PopoverPlacementBounds {\n /** The top of the popover in overlay coordinate space. */\n top: number;\n /** The bottom of the popover in overlay coordinate space. */\n bottom: number;\n}\n\nexport interface PopoverManagerOptions {\n hasActivePopover: () => boolean;\n updateActivePopover: () => void;\n}\n\n/**\n * Shared placement logic for the editor's overlay popovers: each anchors below\n * or above a document position, flipping to the opposite side only when the\n * preferred side would be clipped by the visible scrollport.\n */\nexport class PopoverManager {\n #fileContainer?: HTMLElement;\n #codeElement?: HTMLElement;\n #scrollContainer?: HTMLElement;\n #scrollContainerSource?: HTMLElement;\n #cachedCodeRect?: DOMRect;\n #cachedScrollContainerRect?: DOMRect;\n #viewportRectsDirty = true;\n #viewportRectListenersDisposes?: (() => void)[];\n #placement?: 'preferred' | 'fallback';\n\n readonly #hasActivePopover: () => boolean;\n readonly #updateActivePopover: () => void;\n\n constructor(options: PopoverManagerOptions) {\n this.#hasActivePopover = options.hasActivePopover;\n this.#updateActivePopover = options.updateActivePopover;\n }\n\n setViewportElements(\n fileContainer: HTMLElement,\n codeElement: HTMLElement\n ): void {\n this.#fileContainer = fileContainer;\n if (this.#codeElement === codeElement) {\n return;\n }\n this.#codeElement = codeElement;\n // A new [data-code] element invalidates the cached viewport rects (e.g. a\n // full re-render loaded a different file, possibly at a different on-screen\n // position) even without an intervening scroll/resize event.\n this.#viewportRectsDirty = true;\n }\n\n cleanUp(): void {\n this.#codeElement = undefined;\n this.#fileContainer = undefined;\n this.#scrollContainer = undefined;\n this.#scrollContainerSource = undefined;\n this.#cachedCodeRect = undefined;\n this.#cachedScrollContainerRect = undefined;\n this.#viewportRectsDirty = true;\n this.#viewportRectListenersDisposes?.forEach((dispose) => dispose());\n this.#viewportRectListenersDisposes = undefined;\n this.#placement = undefined;\n }\n\n resetPlacement(): void {\n this.#placement = undefined;\n }\n\n setPlacement(placement: 'preferred' | 'fallback'): void {\n this.#placement = placement;\n }\n\n // Flips to the opposite side only when the preferred side would be clipped by\n // the real scrollport and the fallback side fits; without viewport geometry,\n // falls back to the document-edge signal so the first/last rows still flip.\n choosePlacement(input: {\n /** Bounds for the anchor's preferred side (e.g. above for backward, below for forward). */\n preferred: PopoverPlacementBounds;\n /** Bounds for the opposite edge we flip to when the preferred side has no room. */\n fallback: PopoverPlacementBounds;\n /** The visible scrollport in overlay coordinate space, or undefined without layout geometry (e.g. a detached test DOM). */\n viewport: PopoverPlacementBounds | undefined;\n /** The popover's measured height; 0 before it has laid out. */\n popoverHeight: number;\n /** Whether the anchor is within the document's first/last rows; only used as a fallback signal when `viewport` is unavailable. */\n atDocumentEdge: boolean;\n }): 'preferred' | 'fallback' {\n const { preferred, fallback, viewport, popoverHeight, atDocumentEdge } =\n input;\n let placement: 'preferred' | 'fallback';\n if (viewport !== undefined && popoverHeight > 0) {\n const fits = (bounds: PopoverPlacementBounds, margin = 0): boolean =>\n bounds.top >= viewport.top + margin &&\n bounds.bottom <= viewport.bottom - margin;\n if (\n this.#placement === 'fallback' &&\n fits(fallback) &&\n !fits(preferred, POPOVER_FLIP_HYSTERESIS_PX)\n ) {\n placement = 'fallback';\n } else if (!fits(preferred) && fits(fallback)) {\n placement = 'fallback';\n } else {\n placement = 'preferred';\n }\n } else {\n placement = atDocumentEdge ? 'fallback' : 'preferred';\n }\n this.#placement = placement;\n return placement;\n }\n\n /**\n * Returns the bounds of the popover in overlay coordinate space.\n */\n getPlacementBounds(): PopoverPlacementBounds | undefined {\n const codeRect = this.#getCodeRect();\n if (codeRect === undefined) {\n return undefined;\n }\n const scrollContainerRect = this.#getScrollContainerRect();\n let topScreen: number;\n let bottomScreen: number;\n if (scrollContainerRect !== undefined) {\n topScreen = scrollContainerRect.top;\n bottomScreen = scrollContainerRect.bottom;\n } else {\n // No scrollable ancestor: the page itself scrolls, so the window is the\n // clip region.\n topScreen = 0;\n bottomScreen = window.innerHeight;\n }\n if (bottomScreen <= topScreen) {\n return undefined;\n }\n return {\n top: topScreen - codeRect.top,\n bottom: bottomScreen - codeRect.top,\n };\n }\n\n // The nearest scrollable ancestor of the file container -- the element whose\n // overflow clips overlay widgets. Walks the light-DOM ancestor chain (the\n // overlay lives in the file container's shadow root) and caches the result per\n // file container so it is not re-resolved on every render.\n //\n // Known limitations: this only inspects light-DOM ancestors outside the\n // shadow root, so it cannot see a clip boundary inside it (e.g. `[data-code]`\n // itself uses `overflow-y: clip`, which also is not one of the values matched\n // below) and cannot cross into an outer shadow root if the file container is\n // itself nested in another web component. It also only considers the\n // *nearest* scrollable ancestor, not the intersection of every clipping\n // ancestor, so a popover could still be clipped by an outer scroll context\n // even when it fits within this nearer one.\n #getScrollContainer(): HTMLElement | undefined {\n const fileContainer = this.#fileContainer;\n if (fileContainer === undefined) {\n return undefined;\n }\n if (this.#scrollContainerSource === fileContainer) {\n // A cached element can detach across re-mounts; re-resolve in that case\n // but otherwise trust the cache (including a cached \"no scroller\" result).\n if (\n this.#scrollContainer === undefined ||\n this.#scrollContainer.isConnected\n ) {\n return this.#scrollContainer;\n }\n }\n let element: HTMLElement | null = fileContainer.parentElement;\n while (element !== null) {\n const overflowY = getComputedStyle(element).overflowY;\n if (\n overflowY === 'auto' ||\n overflowY === 'scroll' ||\n overflowY === 'overlay'\n ) {\n this.#scrollContainer = element;\n this.#scrollContainerSource = fileContainer;\n return element;\n }\n element = element.parentElement;\n }\n this.#scrollContainer = undefined;\n this.#scrollContainerSource = fileContainer;\n return undefined;\n }\n\n // Lazily measures and caches the screen-space rects getOverlayViewport needs,\n // refreshing only when a scroll or resize event marks them dirty rather than\n // on every call. Selection-action placement can run once per keystroke while a\n // ranged selection stays open, so re-measuring via getBoundingClientRect on\n // every call would force a synchronous layout reflow on that hot path.\n #refreshViewportRectsIfNeeded(): void {\n if (!this.#viewportRectsDirty) {\n return;\n }\n this.#ensureViewportRectListeners();\n this.#cachedCodeRect = this.#codeElement?.getBoundingClientRect();\n this.#cachedScrollContainerRect =\n this.#getScrollContainer()?.getBoundingClientRect();\n this.#viewportRectsDirty = false;\n }\n\n #getCodeRect(): DOMRect | undefined {\n this.#refreshViewportRectsIfNeeded();\n return this.#cachedCodeRect;\n }\n\n #getScrollContainerRect(): DOMRect | undefined {\n this.#refreshViewportRectsIfNeeded();\n return this.#cachedScrollContainerRect;\n }\n\n // Subscribes once to events that can move or resize the cached rects: a scroll\n // on the resolved scroll container (or the window, when the page itself\n // scrolls) and a window resize. Scrolling/resizing can change whether the open\n // popover still fits, so this re-runs placement rAF-throttled.\n #ensureViewportRectListeners(): void {\n if (this.#viewportRectListenersDisposes !== undefined) {\n return;\n }\n let repositionRafId: number | undefined;\n const markDirty = () => {\n this.#viewportRectsDirty = true;\n if (!this.#hasActivePopover() || repositionRafId !== undefined) {\n return;\n }\n repositionRafId = requestAnimationFrame(() => {\n repositionRafId = undefined;\n this.#updateActivePopover();\n });\n };\n const scroller = this.#getScrollContainer();\n this.#viewportRectListenersDisposes = [\n scroller !== undefined\n ? addEventListener(scroller, 'scroll', markDirty, { passive: true })\n : addEventListener(window, 'scroll', markDirty, { passive: true }),\n addEventListener(window, 'resize', markDirty, { passive: true }),\n () => {\n if (repositionRafId !== undefined) {\n cancelAnimationFrame(repositionRafId);\n repositionRafId = undefined;\n }\n },\n ];\n }\n}\n"],"mappings":"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"}
|
|
@@ -141,6 +141,18 @@ declare function extendSelections(selections: EditorSelection[], target: EditorS
|
|
|
141
141
|
* Merges overlapping selections.
|
|
142
142
|
*/
|
|
143
143
|
declare function mergeOverlappingSelections(selections: EditorSelection[]): EditorSelection[];
|
|
144
|
+
/**
|
|
145
|
+
* Converts selections into merged line blocks for line-based commands.
|
|
146
|
+
*/
|
|
147
|
+
declare function getSelectedLineBlocks(selections: EditorSelection[]): {
|
|
148
|
+
startLine: number;
|
|
149
|
+
endLine: number;
|
|
150
|
+
}[];
|
|
151
|
+
/**
|
|
152
|
+
* Moves a selection's line positions after its lines are shifted, clamping to
|
|
153
|
+
* the target document bounds.
|
|
154
|
+
*/
|
|
155
|
+
declare function shiftSelectionLines(selection: EditorSelection, direction: -1 | 1, lineCount: number, getLineLength: (line: number) => number): EditorSelection;
|
|
144
156
|
/**
|
|
145
157
|
* Finds the next matching word and updates the selections.
|
|
146
158
|
*/
|
|
@@ -152,7 +164,7 @@ declare function getDocumentFullSelection(textDocument: TextDocument<unknown>):
|
|
|
152
164
|
/**
|
|
153
165
|
* Get the boundary selection of the document.
|
|
154
166
|
*/
|
|
155
|
-
declare function getDocumentBoundarySelection(textDocument: TextDocument<unknown>, atEnd: boolean): EditorSelection;
|
|
167
|
+
declare function getDocumentBoundarySelection(textDocument: TextDocument<unknown>, atEnd: boolean, trimmedEndNewLine?: boolean): EditorSelection;
|
|
156
168
|
/**
|
|
157
169
|
* Get the clipboard text of the selections for the given text document. Used by
|
|
158
170
|
* both copy and cut so the two stay in sync. Overlapping regions (e.g. several
|
|
@@ -175,5 +187,5 @@ declare function getSelectionAnchor(lineElement: HTMLElement, character: number)
|
|
|
175
187
|
*/
|
|
176
188
|
declare function expandCollapsedSelectionToWord(textDocument: TextDocument<unknown>, selection: EditorSelection): EditorSelection;
|
|
177
189
|
//#endregion
|
|
178
|
-
export { AutoSurround, DirectionBackward, DirectionForward, DirectionNone, EditorSelection, SelectionDirection, applyDeleteCharacterToSelections, applyDeleteHardLineForwardToSelections, applyDeleteSoftLineBackwardToSelections, applyDeleteWordBackwardToSelections, applyTextChangeToSelections, applyTextReplaceToSelections, applyTransposeToSelections, comparePosition, convertSelection, createSelectionFrom, createSelectionFromAnchorAndFocusOffsets, expandCollapsedSelectionToWord, extendSelection, extendSelections, findNexMatch, getAutoSurroundReplacementTexts, getCaretPosition, getDocumentBoundarySelection, getDocumentFullSelection, getSelectionAnchor, getSelectionText, isCollapsedSelection, isLineEditable, mapCursorMove, mapSelectionShift, mergeOverlappingSelections, remapSelectionsAfterEdits, resolveDeleteCharacterRange, resolveIndentEdits, resolveSelectionCut, selectionIntersects };
|
|
190
|
+
export { AutoSurround, DirectionBackward, DirectionForward, DirectionNone, EditorSelection, SelectionDirection, applyDeleteCharacterToSelections, applyDeleteHardLineForwardToSelections, applyDeleteSoftLineBackwardToSelections, applyDeleteWordBackwardToSelections, applyTextChangeToSelections, applyTextReplaceToSelections, applyTransposeToSelections, comparePosition, convertSelection, createSelectionFrom, createSelectionFromAnchorAndFocusOffsets, expandCollapsedSelectionToWord, extendSelection, extendSelections, findNexMatch, getAutoSurroundReplacementTexts, getCaretPosition, getDocumentBoundarySelection, getDocumentFullSelection, getSelectedLineBlocks, getSelectionAnchor, getSelectionText, isCollapsedSelection, isLineEditable, mapCursorMove, mapSelectionShift, mergeOverlappingSelections, remapSelectionsAfterEdits, resolveDeleteCharacterRange, resolveIndentEdits, resolveSelectionCut, selectionIntersects, shiftSelectionLines };
|
|
179
191
|
//# sourceMappingURL=selection.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection.d.ts","names":[],"sources":["../../src/editor/selection.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"selection.d.ts","names":[],"sources":["../../src/editor/selection.ts"],"mappings":"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"}
|
package/dist/editor/selection.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { endsWithLineBreak } from "./utils.js";
|
|
1
|
+
import { createSegmenter, endsWithLineBreak, getGraphemeSegmenter } from "./utils.js";
|
|
2
2
|
import { applyDocumentChangeToLineAnnotations } from "./lineAnnotations.js";
|
|
3
3
|
//#region src/editor/selection.ts
|
|
4
4
|
const DirectionBackward = -1;
|
|
@@ -81,12 +81,13 @@ function mapCursorMove(textDocument, selections, shortcut) {
|
|
|
81
81
|
return selections.map((selection) => {
|
|
82
82
|
let { line, character } = shortcut === "up" || shortcut === "left" ? selection.start : selection.end;
|
|
83
83
|
if (shortcut === "textStart" || shortcut === "start" || shortcut === "end") {
|
|
84
|
+
const caret = getCaretPosition(selection);
|
|
85
|
+
line = caret.line;
|
|
86
|
+
character = caret.character;
|
|
84
87
|
if (shortcut === "textStart") {
|
|
85
88
|
const indent = getLeadingSpaces(textDocument.getLineText(line));
|
|
86
89
|
character = character === indent ? 0 : indent;
|
|
87
90
|
} else character = shortcut === "start" ? 0 : textDocument.getLineLength(line);
|
|
88
|
-
if (selection.direction === -1) line = selection.start.line;
|
|
89
|
-
else line = selection.end.line;
|
|
90
91
|
} else if (shortcut === "up") line = Math.max(0, line - 1);
|
|
91
92
|
else if (shortcut === "down") line = Math.min(Math.max(lineCount - 1, 0), line + 1);
|
|
92
93
|
else if (isCollapsedSelection(selection)) {
|
|
@@ -221,7 +222,7 @@ function getNextSelectionOffsetPairAfterReplace(textDocument, entry, offsetDelta
|
|
|
221
222
|
const insertStart = entry.start + offsetDelta;
|
|
222
223
|
const insertEnd = insertStart + newText.length;
|
|
223
224
|
if (entry.end - entry.start > 0) {
|
|
224
|
-
const originalText = textDocument.
|
|
225
|
+
const originalText = textDocument.getTextSlice(entry.start, entry.end);
|
|
225
226
|
const preservedOffset = newText.indexOf(originalText);
|
|
226
227
|
if (preservedOffset !== -1 && preservedOffset + originalText.length <= newText.length) {
|
|
227
228
|
const rangeStart = insertStart + preservedOffset;
|
|
@@ -375,7 +376,6 @@ function getAutoSurroundReplacementTexts(textDocument, selections, char, autoSur
|
|
|
375
376
|
* insertTranspose (Ctrl+T) behavior.
|
|
376
377
|
*/
|
|
377
378
|
function applyTransposeToSelections(textDocument, selections, lineAnnotations) {
|
|
378
|
-
const text = textDocument.getText();
|
|
379
379
|
const edits = [];
|
|
380
380
|
const nextOffsetPairs = [];
|
|
381
381
|
for (const selection of selections) {
|
|
@@ -420,7 +420,7 @@ function applyTransposeToSelections(textDocument, selections, lineAnnotations) {
|
|
|
420
420
|
const prevGraphemeStart = prevLength > 0 ? findClusterBreak(prevLineText, prevLength, false, getLineGraphemeStarts(prevLineText)) : prevLength;
|
|
421
421
|
const firstEnd = findClusterBreak(lineText, 0, true, graphemeStarts);
|
|
422
422
|
const prevStart = prevEnd - (prevLength - prevGraphemeStart);
|
|
423
|
-
const newText = lineText.slice(0, firstEnd) +
|
|
423
|
+
const newText = lineText.slice(0, firstEnd) + textDocument.getTextSlice(prevEnd, offset) + prevLineText.slice(prevGraphemeStart, prevLength);
|
|
424
424
|
edit = {
|
|
425
425
|
start: prevStart,
|
|
426
426
|
end: offset + firstEnd,
|
|
@@ -738,30 +738,91 @@ function extendSelections(selections, target) {
|
|
|
738
738
|
*/
|
|
739
739
|
function mergeOverlappingSelections(selections) {
|
|
740
740
|
if (selections.length <= 1) return selections;
|
|
741
|
-
const
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
741
|
+
const ordered = selections.map((selection, index) => ({
|
|
742
|
+
index,
|
|
743
|
+
selection
|
|
744
|
+
})).sort((a, b) => {
|
|
745
|
+
const startOrder = comparePosition(a.selection.start, b.selection.start);
|
|
746
|
+
if (startOrder !== 0) return startOrder;
|
|
747
|
+
const endOrder = comparePosition(a.selection.end, b.selection.end);
|
|
748
|
+
return endOrder !== 0 ? endOrder : a.index - b.index;
|
|
749
|
+
});
|
|
750
|
+
const merged = [];
|
|
751
|
+
let current = ordered[0];
|
|
752
|
+
for (const entry of ordered.slice(1)) {
|
|
753
|
+
if (selectionIntersects(current.selection, entry.selection)) {
|
|
754
|
+
const latest = entry.index > current.index ? entry : current;
|
|
755
|
+
const start = comparePosition(entry.selection.start, current.selection.start) < 0 ? entry.selection.start : current.selection.start;
|
|
756
|
+
const end = comparePosition(entry.selection.end, current.selection.end) > 0 ? entry.selection.end : current.selection.end;
|
|
757
|
+
let direction = latest.selection.direction;
|
|
758
|
+
if (direction === 0 && comparePosition(start, end) !== 0) direction = comparePosition(latest.selection.start, start) === 0 ? -1 : 1;
|
|
759
|
+
current = {
|
|
760
|
+
index: latest.index,
|
|
761
|
+
selection: {
|
|
762
|
+
direction,
|
|
763
|
+
end,
|
|
764
|
+
start
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
continue;
|
|
754
768
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
if (previous !== void 0 && selectionIntersects(previous, selection) || next !== void 0 && selectionIntersects(next, selection)) continue;
|
|
758
|
-
accepted.splice(left, 0, {
|
|
759
|
-
index: i,
|
|
760
|
-
selection
|
|
761
|
-
});
|
|
762
|
-
selected.add(i);
|
|
769
|
+
merged.push(current);
|
|
770
|
+
current = entry;
|
|
763
771
|
}
|
|
764
|
-
|
|
772
|
+
merged.push(current);
|
|
773
|
+
return merged.sort((a, b) => a.index - b.index).map(({ selection }) => selection);
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Converts selections into merged line blocks for line-based commands.
|
|
777
|
+
*/
|
|
778
|
+
function getSelectedLineBlocks(selections) {
|
|
779
|
+
const blocks = selections.map((selection) => {
|
|
780
|
+
let endLine = selection.end.line;
|
|
781
|
+
if (selection.end.character === 0 && comparePosition(selection.start, selection.end) !== 0) endLine--;
|
|
782
|
+
return {
|
|
783
|
+
startLine: selection.start.line,
|
|
784
|
+
endLine: Math.max(selection.start.line, endLine)
|
|
785
|
+
};
|
|
786
|
+
}).sort((a, b) => {
|
|
787
|
+
const startOrder = a.startLine - b.startLine;
|
|
788
|
+
return startOrder !== 0 ? startOrder : a.endLine - b.endLine;
|
|
789
|
+
});
|
|
790
|
+
const merged = [];
|
|
791
|
+
for (const block of blocks) {
|
|
792
|
+
const previous = merged.at(-1);
|
|
793
|
+
if (previous !== void 0 && block.startLine <= previous.endLine + 1) previous.endLine = Math.max(previous.endLine, block.endLine);
|
|
794
|
+
else merged.push({ ...block });
|
|
795
|
+
}
|
|
796
|
+
return merged;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Moves a selection's line positions after its lines are shifted, clamping to
|
|
800
|
+
* the target document bounds.
|
|
801
|
+
*/
|
|
802
|
+
function shiftSelectionLines(selection, direction, lineCount, getLineLength) {
|
|
803
|
+
const shiftPosition = (position) => {
|
|
804
|
+
const line = position.line + direction;
|
|
805
|
+
if (line >= lineCount) {
|
|
806
|
+
const lastLine = Math.max(0, lineCount - 1);
|
|
807
|
+
return {
|
|
808
|
+
line: lastLine,
|
|
809
|
+
character: getLineLength(lastLine)
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
if (line < 0) return {
|
|
813
|
+
line: 0,
|
|
814
|
+
character: 0
|
|
815
|
+
};
|
|
816
|
+
return {
|
|
817
|
+
line,
|
|
818
|
+
character: position.character
|
|
819
|
+
};
|
|
820
|
+
};
|
|
821
|
+
return {
|
|
822
|
+
start: shiftPosition(selection.start),
|
|
823
|
+
end: shiftPosition(selection.end),
|
|
824
|
+
direction: selection.direction
|
|
825
|
+
};
|
|
765
826
|
}
|
|
766
827
|
/**
|
|
767
828
|
* Finds the next matching word and updates the selections.
|
|
@@ -801,11 +862,16 @@ function getDocumentFullSelection(textDocument) {
|
|
|
801
862
|
/**
|
|
802
863
|
* Get the boundary selection of the document.
|
|
803
864
|
*/
|
|
804
|
-
function getDocumentBoundarySelection(textDocument, atEnd) {
|
|
805
|
-
|
|
865
|
+
function getDocumentBoundarySelection(textDocument, atEnd, trimmedEndNewLine) {
|
|
866
|
+
let line = 0;
|
|
867
|
+
if (atEnd) {
|
|
868
|
+
const lastLine = textDocument.lineCount - 1;
|
|
869
|
+
line = trimmedEndNewLine === true && lastLine > 0 && textDocument.getLineLength(lastLine) === 0 ? lastLine - 1 : lastLine;
|
|
870
|
+
}
|
|
871
|
+
const character = atEnd ? textDocument.getLineLength(line) : 0;
|
|
806
872
|
const start = {
|
|
807
873
|
line,
|
|
808
|
-
character
|
|
874
|
+
character
|
|
809
875
|
};
|
|
810
876
|
return {
|
|
811
877
|
start,
|
|
@@ -1021,11 +1087,24 @@ function expandCollapsedSelectionToWord(textDocument, selection) {
|
|
|
1021
1087
|
};
|
|
1022
1088
|
}
|
|
1023
1089
|
function expandCollapsedLineWord(lineText, character) {
|
|
1024
|
-
const segmenter =
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1090
|
+
const segmenter = createSegmenter({ granularity: "word" });
|
|
1091
|
+
if (segmenter !== void 0) {
|
|
1092
|
+
for (const seg of segmenter.segment(lineText)) {
|
|
1093
|
+
if (seg.isWordLike !== true) continue;
|
|
1094
|
+
const lo = seg.index;
|
|
1095
|
+
const hi = lo + seg.segment.length;
|
|
1096
|
+
if (character >= lo && character <= hi) return {
|
|
1097
|
+
start: lo,
|
|
1098
|
+
end: hi
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
const wordRe = /[\p{Alphabetic}\p{Number}_]+/gu;
|
|
1104
|
+
let match;
|
|
1105
|
+
while ((match = wordRe.exec(lineText)) !== null) {
|
|
1106
|
+
const lo = match.index;
|
|
1107
|
+
const hi = lo + match[0].length;
|
|
1029
1108
|
if (character >= lo && character <= hi) return {
|
|
1030
1109
|
start: lo,
|
|
1031
1110
|
end: hi
|
|
@@ -1078,10 +1157,18 @@ function findClusterBreak(text, pos, forward, graphemeStarts) {
|
|
|
1078
1157
|
}
|
|
1079
1158
|
return 0;
|
|
1080
1159
|
}
|
|
1081
|
-
const graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
1082
1160
|
function getLineGraphemeStarts(lineText) {
|
|
1083
1161
|
const graphemeStarts = [0];
|
|
1084
|
-
|
|
1162
|
+
const segmenter = getGraphemeSegmenter();
|
|
1163
|
+
if (segmenter !== void 0) {
|
|
1164
|
+
for (const segment of segmenter.segment(lineText)) if (segment.index > 0) graphemeStarts.push(segment.index);
|
|
1165
|
+
return graphemeStarts;
|
|
1166
|
+
}
|
|
1167
|
+
let index = 0;
|
|
1168
|
+
for (const codePoint of lineText) {
|
|
1169
|
+
if (index > 0) graphemeStarts.push(index);
|
|
1170
|
+
index += codePoint.length;
|
|
1171
|
+
}
|
|
1085
1172
|
return graphemeStarts;
|
|
1086
1173
|
}
|
|
1087
1174
|
function stepCharacterByGrapheme(textDocument, line, character, forward) {
|
|
@@ -1093,7 +1180,12 @@ function stepCharacterByGrapheme(textDocument, line, character, forward) {
|
|
|
1093
1180
|
character: 0
|
|
1094
1181
|
});
|
|
1095
1182
|
const suffix = textDocument.getTextSlice(lineStart + character, lineStart + lineLength);
|
|
1096
|
-
|
|
1183
|
+
const segmenter = getGraphemeSegmenter();
|
|
1184
|
+
if (segmenter !== void 0) {
|
|
1185
|
+
for (const segment of segmenter.segment(suffix)) return character + segment.segment.length;
|
|
1186
|
+
return lineLength;
|
|
1187
|
+
}
|
|
1188
|
+
for (const codePoint of suffix) return character + codePoint.length;
|
|
1097
1189
|
return lineLength;
|
|
1098
1190
|
}
|
|
1099
1191
|
if (character <= 0) return 0;
|
|
@@ -1103,7 +1195,16 @@ function stepCharacterByGrapheme(textDocument, line, character, forward) {
|
|
|
1103
1195
|
});
|
|
1104
1196
|
const prefix = textDocument.getTextSlice(lineStart, lineStart + character);
|
|
1105
1197
|
let prevStart = 0;
|
|
1106
|
-
|
|
1198
|
+
const segmenter = getGraphemeSegmenter();
|
|
1199
|
+
if (segmenter !== void 0) {
|
|
1200
|
+
for (const segment of segmenter.segment(prefix)) prevStart = segment.index;
|
|
1201
|
+
return prevStart;
|
|
1202
|
+
}
|
|
1203
|
+
let index = 0;
|
|
1204
|
+
for (const codePoint of prefix) {
|
|
1205
|
+
prevStart = index;
|
|
1206
|
+
index += codePoint.length;
|
|
1207
|
+
}
|
|
1107
1208
|
return prevStart;
|
|
1108
1209
|
}
|
|
1109
1210
|
function getSelectionAnchorAndFocusOffsets(textDocument, selection) {
|
|
@@ -1392,6 +1493,6 @@ function getTextOffset(text, offset) {
|
|
|
1392
1493
|
return Math.min(offset, lineBreakIndex === -1 ? value.length : lineBreakIndex);
|
|
1393
1494
|
}
|
|
1394
1495
|
//#endregion
|
|
1395
|
-
export { DirectionBackward, DirectionForward, DirectionNone, applyDeleteCharacterToSelections, applyDeleteHardLineForwardToSelections, applyDeleteSoftLineBackwardToSelections, applyDeleteWordBackwardToSelections, applyTextChangeToSelections, applyTextReplaceToSelections, applyTransposeToSelections, comparePosition, convertSelection, createSelectionFrom, createSelectionFromAnchorAndFocusOffsets, expandCollapsedSelectionToWord, extendSelection, extendSelections, findNexMatch, getAutoSurroundReplacementTexts, getCaretPosition, getDocumentBoundarySelection, getDocumentFullSelection, getSelectionAnchor, getSelectionText, isCollapsedSelection, isLineEditable, mapCursorMove, mapSelectionShift, mergeOverlappingSelections, remapSelectionsAfterEdits, resolveDeleteCharacterRange, resolveIndentEdits, resolveSelectionCut, selectionIntersects };
|
|
1496
|
+
export { DirectionBackward, DirectionForward, DirectionNone, applyDeleteCharacterToSelections, applyDeleteHardLineForwardToSelections, applyDeleteSoftLineBackwardToSelections, applyDeleteWordBackwardToSelections, applyTextChangeToSelections, applyTextReplaceToSelections, applyTransposeToSelections, comparePosition, convertSelection, createSelectionFrom, createSelectionFromAnchorAndFocusOffsets, expandCollapsedSelectionToWord, extendSelection, extendSelections, findNexMatch, getAutoSurroundReplacementTexts, getCaretPosition, getDocumentBoundarySelection, getDocumentFullSelection, getSelectedLineBlocks, getSelectionAnchor, getSelectionText, isCollapsedSelection, isLineEditable, mapCursorMove, mapSelectionShift, mergeOverlappingSelections, remapSelectionsAfterEdits, resolveDeleteCharacterRange, resolveIndentEdits, resolveSelectionCut, selectionIntersects, shiftSelectionLines };
|
|
1396
1497
|
|
|
1397
1498
|
//# sourceMappingURL=selection.js.map
|