@pierre/diffs 1.3.6 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CodeView.d.ts +113 -68
- package/dist/components/CodeView.d.ts.map +1 -1
- package/dist/components/CodeView.js +153 -58
- package/dist/components/CodeView.js.map +1 -1
- package/dist/components/File.d.ts +75 -15
- package/dist/components/File.d.ts.map +1 -1
- package/dist/components/File.js +223 -62
- package/dist/components/File.js.map +1 -1
- package/dist/components/FileDiff.d.ts +97 -21
- package/dist/components/FileDiff.d.ts.map +1 -1
- package/dist/components/FileDiff.js +400 -100
- package/dist/components/FileDiff.js.map +1 -1
- package/dist/components/UnresolvedFile.d.ts +2 -2
- package/dist/components/UnresolvedFile.d.ts.map +1 -1
- package/dist/components/UnresolvedFile.js +2 -2
- package/dist/components/UnresolvedFile.js.map +1 -1
- package/dist/components/VirtualizedFile.d.ts +16 -7
- package/dist/components/VirtualizedFile.d.ts.map +1 -1
- package/dist/components/VirtualizedFile.js +140 -51
- package/dist/components/VirtualizedFile.js.map +1 -1
- package/dist/components/VirtualizedFileDiff.d.ts +14 -8
- package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
- package/dist/components/VirtualizedFileDiff.js +183 -93
- package/dist/components/VirtualizedFileDiff.js.map +1 -1
- package/dist/components/VirtulizerDevelopment.d.ts +1 -1
- package/dist/components/VirtulizerDevelopment.d.ts.map +1 -1
- package/dist/edit/index.d.ts +7 -4
- package/dist/edit/index.js +5 -1
- package/dist/editor/EditStateManager.d.ts +56 -0
- package/dist/editor/EditStateManager.d.ts.map +1 -0
- package/dist/editor/EditStateManager.js +135 -0
- package/dist/editor/EditStateManager.js.map +1 -0
- package/dist/editor/cloneRetainedDiffSessionSnapshot.d.ts +7 -0
- package/dist/editor/cloneRetainedDiffSessionSnapshot.d.ts.map +1 -0
- package/dist/editor/cloneRetainedDiffSessionSnapshot.js +16 -0
- package/dist/editor/cloneRetainedDiffSessionSnapshot.js.map +1 -0
- package/dist/editor/editPrediction.d.ts +76 -0
- package/dist/editor/editPrediction.d.ts.map +1 -0
- package/dist/editor/editPrediction.js +365 -0
- package/dist/editor/editPrediction.js.map +1 -0
- package/dist/editor/editStack.d.ts +19 -40
- package/dist/editor/editStack.d.ts.map +1 -1
- package/dist/editor/editStack.js +67 -4
- package/dist/editor/editStack.js.map +1 -1
- package/dist/editor/editor.d.ts +118 -29
- package/dist/editor/editor.d.ts.map +1 -1
- package/dist/editor/editor.js +1103 -383
- package/dist/editor/editor.js.map +1 -1
- package/dist/editor/editor2.js +1 -1
- package/dist/editor/languages.d.ts +3 -3
- package/dist/editor/languages.d.ts.map +1 -1
- package/dist/editor/languages.js.map +1 -1
- package/dist/editor/lineAnnotations.d.ts +3 -3
- package/dist/editor/lineAnnotations.d.ts.map +1 -1
- package/dist/editor/lineAnnotations.js +16 -7
- package/dist/editor/lineAnnotations.js.map +1 -1
- package/dist/editor/marker.d.ts +2 -2
- package/dist/editor/marker.d.ts.map +1 -1
- package/dist/editor/marker.js.map +1 -1
- package/dist/editor/matchBrackets.d.ts +2 -2
- package/dist/editor/matchBrackets.d.ts.map +1 -1
- package/dist/editor/matchBrackets.js.map +1 -1
- package/dist/editor/pieceTable.d.ts +1 -1
- package/dist/editor/pieceTable.js.map +1 -1
- package/dist/editor/searchPanel.d.ts +2 -2
- package/dist/editor/searchPanel.js +1 -1
- package/dist/editor/searchPanel.js.map +1 -1
- package/dist/editor/selection.d.ts +32 -23
- package/dist/editor/selection.d.ts.map +1 -1
- package/dist/editor/selection.js +3 -3
- package/dist/editor/selection.js.map +1 -1
- package/dist/editor/selectionAction.d.ts +3 -3
- package/dist/editor/selectionAction.d.ts.map +1 -1
- package/dist/editor/selectionAction.js.map +1 -1
- package/dist/editor/textDocument.d.ts +11 -8
- package/dist/editor/textDocument.d.ts.map +1 -1
- package/dist/editor/textDocument.js +28 -5
- package/dist/editor/textDocument.js.map +1 -1
- package/dist/editor/textDocumentChangeTransaction.d.ts +15 -0
- package/dist/editor/textDocumentChangeTransaction.d.ts.map +1 -0
- package/dist/editor/textDocumentChangeTransaction.js +12 -0
- package/dist/editor/textDocumentChangeTransaction.js.map +1 -0
- package/dist/editor/textMeasure.d.ts +5 -0
- package/dist/editor/textMeasure.d.ts.map +1 -1
- package/dist/editor/textMeasure.js +10 -0
- package/dist/editor/textMeasure.js.map +1 -1
- package/dist/editor/tokenizer.d.ts +1 -1
- package/dist/editor/tokenizer.js.map +1 -1
- package/dist/editor/types.d.ts +267 -0
- package/dist/editor/types.d.ts.map +1 -0
- package/dist/editor/types.js +0 -0
- package/dist/editor/utils.d.ts +2 -1
- package/dist/editor/utils.d.ts.map +1 -1
- package/dist/editor/utils.js +4 -1
- package/dist/editor/utils.js.map +1 -1
- package/dist/index.d.ts +8 -6
- package/dist/index.js +9 -8
- package/dist/managers/InteractionManager.d.ts.map +1 -1
- package/dist/managers/InteractionManager.js +7 -0
- package/dist/managers/InteractionManager.js.map +1 -1
- package/dist/react/CodeView.d.ts +36 -25
- package/dist/react/CodeView.d.ts.map +1 -1
- package/dist/react/CodeView.js +15 -14
- package/dist/react/CodeView.js.map +1 -1
- package/dist/react/EditContext.d.ts +8 -11
- package/dist/react/EditContext.d.ts.map +1 -1
- package/dist/react/EditContext.js +2 -9
- package/dist/react/EditContext.js.map +1 -1
- package/dist/react/File.d.ts +10 -6
- package/dist/react/File.d.ts.map +1 -1
- package/dist/react/File.js +8 -4
- package/dist/react/File.js.map +1 -1
- package/dist/react/FileDiff.d.ts +10 -5
- package/dist/react/FileDiff.d.ts.map +1 -1
- package/dist/react/FileDiff.js +8 -4
- package/dist/react/FileDiff.js.map +1 -1
- package/dist/react/MultiFileDiff.d.ts +8 -5
- package/dist/react/MultiFileDiff.d.ts.map +1 -1
- package/dist/react/MultiFileDiff.js +8 -12
- package/dist/react/MultiFileDiff.js.map +1 -1
- package/dist/react/PatchDiff.d.ts +7 -4
- package/dist/react/PatchDiff.d.ts.map +1 -1
- package/dist/react/PatchDiff.js +8 -4
- package/dist/react/PatchDiff.js.map +1 -1
- package/dist/react/UnresolvedFile.d.ts +3 -3
- package/dist/react/UnresolvedFile.d.ts.map +1 -1
- package/dist/react/UnresolvedFile.js.map +1 -1
- package/dist/react/index.d.ts +8 -5
- package/dist/react/types.d.ts +46 -9
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/utils/renderDiffChildren.d.ts +14 -12
- package/dist/react/utils/renderDiffChildren.d.ts.map +1 -1
- package/dist/react/utils/renderDiffChildren.js +2 -2
- package/dist/react/utils/renderDiffChildren.js.map +1 -1
- package/dist/react/utils/renderFileChildren.d.ts +14 -12
- package/dist/react/utils/renderFileChildren.d.ts.map +1 -1
- package/dist/react/utils/renderFileChildren.js +2 -2
- package/dist/react/utils/renderFileChildren.js.map +1 -1
- package/dist/react/utils/useFileDiffInstance.d.ts +22 -10
- package/dist/react/utils/useFileDiffInstance.d.ts.map +1 -1
- package/dist/react/utils/useFileDiffInstance.js +112 -22
- package/dist/react/utils/useFileDiffInstance.js.map +1 -1
- package/dist/react/utils/useFileInstance.d.ts +15 -9
- package/dist/react/utils/useFileInstance.d.ts.map +1 -1
- package/dist/react/utils/useFileInstance.js +84 -25
- package/dist/react/utils/useFileInstance.js.map +1 -1
- package/dist/renderers/DiffHunksRenderer.d.ts +30 -15
- package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
- package/dist/renderers/DiffHunksRenderer.js +172 -108
- package/dist/renderers/DiffHunksRenderer.js.map +1 -1
- package/dist/renderers/FileRenderer.d.ts +27 -11
- package/dist/renderers/FileRenderer.d.ts.map +1 -1
- package/dist/renderers/FileRenderer.js +138 -64
- package/dist/renderers/FileRenderer.js.map +1 -1
- package/dist/renderers/UnresolvedFileHunksRenderer.d.ts +2 -2
- package/dist/renderers/UnresolvedFileHunksRenderer.d.ts.map +1 -1
- package/dist/renderers/UnresolvedFileHunksRenderer.js +2 -2
- package/dist/renderers/UnresolvedFileHunksRenderer.js.map +1 -1
- package/dist/shiki-stream/tokenizer.d.ts.map +1 -1
- package/dist/shiki-stream/tokenizer.js +2 -1
- package/dist/shiki-stream/tokenizer.js.map +1 -1
- package/dist/ssr/index.d.ts +2 -2
- package/dist/ssr/preloadDiffs.d.ts +21 -21
- package/dist/ssr/preloadDiffs.d.ts.map +1 -1
- package/dist/ssr/preloadDiffs.js +1 -1
- package/dist/ssr/preloadDiffs.js.map +1 -1
- package/dist/ssr/preloadFile.d.ts +6 -6
- package/dist/ssr/preloadFile.d.ts.map +1 -1
- package/dist/ssr/preloadFile.js.map +1 -1
- package/dist/ssr/preloadPatchFile.d.ts +4 -4
- package/dist/ssr/preloadPatchFile.d.ts.map +1 -1
- package/dist/ssr/preloadPatchFile.js.map +1 -1
- package/dist/style.js +1 -1
- package/dist/types.d.ts +13 -263
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/appendItems.d.ts +5 -0
- package/dist/utils/appendItems.d.ts.map +1 -0
- package/dist/utils/appendItems.js +8 -0
- package/dist/utils/appendItems.js.map +1 -0
- package/dist/utils/areDiffTargetsEqual.js +1 -1
- package/dist/utils/areDiffTargetsEqual.js.map +1 -1
- package/dist/utils/areFileTargetsEqual.d.ts +7 -0
- package/dist/utils/areFileTargetsEqual.d.ts.map +1 -0
- package/dist/utils/areFileTargetsEqual.js +8 -0
- package/dist/utils/areFileTargetsEqual.js.map +1 -0
- package/dist/utils/areManagedSnapshotsEqual.d.ts +1 -1
- package/dist/utils/areManagedSnapshotsEqual.d.ts.map +1 -1
- package/dist/utils/areManagedSnapshotsEqual.js.map +1 -1
- package/dist/utils/areOptionsEqual.d.ts +3 -3
- package/dist/utils/areOptionsEqual.d.ts.map +1 -1
- package/dist/utils/areOptionsEqual.js.map +1 -1
- package/dist/utils/cloneFileDiffMetadata.d.ts +3 -2
- package/dist/utils/cloneFileDiffMetadata.d.ts.map +1 -1
- package/dist/utils/cloneFileDiffMetadata.js +8 -5
- package/dist/utils/cloneFileDiffMetadata.js.map +1 -1
- package/dist/utils/composeCacheKey.d.ts +6 -0
- package/dist/utils/composeCacheKey.d.ts.map +1 -0
- package/dist/utils/composeCacheKey.js +10 -0
- package/dist/utils/composeCacheKey.js.map +1 -0
- package/dist/utils/editSessionAnnotations.d.ts +69 -0
- package/dist/utils/editSessionAnnotations.d.ts.map +1 -0
- package/dist/utils/editSessionAnnotations.js +64 -0
- package/dist/utils/editSessionAnnotations.js.map +1 -0
- package/dist/utils/editSessionHunks.d.ts +5 -3
- package/dist/utils/editSessionHunks.d.ts.map +1 -1
- package/dist/utils/editSessionHunks.js +7 -5
- package/dist/utils/editSessionHunks.js.map +1 -1
- package/dist/utils/getDiffHunksRendererOptions.d.ts +1 -1
- package/dist/utils/getDiffHunksRendererOptions.d.ts.map +1 -1
- package/dist/utils/getDiffHunksRendererOptions.js.map +1 -1
- package/dist/utils/getFileRendererOptions.d.ts +1 -1
- package/dist/utils/getFileRendererOptions.d.ts.map +1 -1
- package/dist/utils/getFileRendererOptions.js.map +1 -1
- package/dist/utils/hydratePartialDiff.d.ts.map +1 -1
- package/dist/utils/hydratePartialDiff.js +2 -1
- package/dist/utils/hydratePartialDiff.js.map +1 -1
- package/dist/utils/lineAnnotationIdentity.d.ts +16 -0
- package/dist/utils/lineAnnotationIdentity.d.ts.map +1 -0
- package/dist/utils/lineAnnotationIdentity.js +17 -0
- package/dist/utils/lineAnnotationIdentity.js.map +1 -0
- package/dist/utils/parseDiffFromFile.d.ts +2 -2
- package/dist/utils/parseDiffFromFile.d.ts.map +1 -1
- package/dist/utils/parseDiffFromFile.js +6 -6
- package/dist/utils/parseDiffFromFile.js.map +1 -1
- package/dist/utils/parsePatchFiles.d.ts +6 -3
- package/dist/utils/parsePatchFiles.d.ts.map +1 -1
- package/dist/utils/parsePatchFiles.js +34 -9
- package/dist/utils/parsePatchFiles.js.map +1 -1
- package/dist/utils/renderDiffWithHighlighter.d.ts.map +1 -1
- package/dist/utils/renderDiffWithHighlighter.js +6 -5
- package/dist/utils/renderDiffWithHighlighter.js.map +1 -1
- package/dist/utils/renderFileWithHighlighter.d.ts.map +1 -1
- package/dist/utils/renderFileWithHighlighter.js +7 -4
- package/dist/utils/renderFileWithHighlighter.js.map +1 -1
- package/dist/utils/trimPatchContext.d.ts.map +1 -1
- package/dist/utils/trimPatchContext.js +3 -2
- package/dist/utils/trimPatchContext.js.map +1 -1
- package/dist/utils/updateDiffHunks.js +1 -1
- package/dist/utils/virtualDiffLayout.js +1 -1
- package/dist/worker/WorkerPoolManager.d.ts +4 -3
- package/dist/worker/WorkerPoolManager.d.ts.map +1 -1
- package/dist/worker/WorkerPoolManager.js +33 -4
- package/dist/worker/WorkerPoolManager.js.map +1 -1
- package/dist/worker/types.d.ts +5 -0
- package/dist/worker/types.d.ts.map +1 -1
- package/dist/worker/worker-portable.js +14 -6
- package/dist/worker/worker-portable.js.map +1 -1
- package/dist/worker/worker.js +14 -6
- package/dist/worker/worker.js.map +1 -1
- package/package.json +9 -1
- package/dist/editor/stateStorage.d.ts +0 -15
- package/dist/editor/stateStorage.d.ts.map +0 -1
- package/dist/editor/stateStorage.js +0 -65
- package/dist/editor/stateStorage.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pieceTable.js","names":["#original","#createPiece","#root","#createNode","#length","#lineCount","#textFromPieces","#lastVisitedLine","#getLineOffset","#lastVisitedLineLength","#findPieceAtOffset","#bufferFor","#nextNode","#forEachPieceSegment","#collectSearchMatchesLineByLine","#replaceRangeIncremental","#invalidateCaches","#lineAtOffset","#lineBreakOffset","#lastPosition","#pieceLineBreakOffset","#walk","#add","#split","#dropPrefix","#mergeNodes","#appendCoalescing","#popLeftmost","#priorityState","#nextPriority","#setLeft","#setRight"],"sources":["../../src/editor/pieceTable.ts"],"sourcesContent":["import type { Position, Range, ResolvedTextEdit } from '../types';\nimport { computeLineOffsets } from '../utils/computeFileOffsets';\nimport type { SearchParams } from './searchPanel';\n\nconst MAX_FIND_MATCHES = 100000;\nconst LINE_FEED = 10;\nconst CARRIAGE_RETURN = 13;\n// TODO(ije): use Intl.Segmenter instead of regex for word separators\nconst WORD_SEPARATORS = '`~!@#$%^&*()-=+[{]}\\\\|;:\\'\",.<>/?' as const;\n\n// A piece is a segment of text that is either original or added.\nclass Piece {\n static Original = 0;\n static Added = 1;\n\n constructor(\n public readonly source: number,\n public readonly offset: number,\n public readonly length: number,\n public readonly lineOffsetStart: number,\n public readonly lineOffsetEnd: number,\n public readonly hasVirtualTrailingCRBreak: boolean,\n public readonly firstCharCode: number,\n public readonly lastCharCode: number\n ) {}\n\n // A slice ending between a backing-buffer CRLF needs its own lone-CR break\n // until the tree reconnects it to a piece beginning with LF.\n get lineBreakCount(): number {\n return (\n this.lineOffsetEnd -\n this.lineOffsetStart +\n (this.hasVirtualTrailingCRBreak ? 1 : 0)\n );\n }\n}\n\n// A text buffer is a string with its line offsets.\nclass TextBuffer {\n lineOffsets: number[];\n\n constructor(public text: string) {\n this.lineOffsets = computeLineOffsets(text);\n }\n\n // the append operation is efficient because it only appends\n // elements to the lineOffsets array in the end\n append(text: string): number {\n const offset = this.text.length;\n for (let i = 0; i < text.length; i++) {\n const charCode = text.charCodeAt(i);\n if (charCode !== LINE_FEED && charCode !== CARRIAGE_RETURN) {\n continue;\n }\n if (\n charCode === CARRIAGE_RETURN &&\n text.charCodeAt(i + 1) === LINE_FEED\n ) {\n i++;\n }\n this.lineOffsets.push(offset + i + 1);\n }\n this.text += text;\n return offset;\n }\n}\n\n// A node in the balanced piece tree. `priority` keeps the tree balanced as a\n// treap (see PieceTable): the tree is a max-heap on priority while staying a\n// binary search tree on document offset.\nclass PieceNode {\n left: PieceNode | null = null;\n right: PieceNode | null = null;\n parent: PieceNode | null = null;\n priority = 0;\n\n constructor(\n public piece: Piece,\n public subtreeLength: number = piece.length,\n public subtreeLineBreakCount: number = piece.lineBreakCount,\n public subtreeFirstCharCode: number = piece.firstCharCode,\n public subtreeLastCharCode: number = piece.lastCharCode\n ) {}\n\n updateSubtreeLength(): void {\n this.subtreeLength =\n (this.left?.subtreeLength ?? 0) +\n this.piece.length +\n (this.right?.subtreeLength ?? 0);\n // Pieces count their edge characters in isolation. Collapse a CRLF only\n // when the two characters are actually adjacent in this subtree.\n this.subtreeLineBreakCount =\n (this.left?.subtreeLineBreakCount ?? 0) +\n this.piece.lineBreakCount +\n (this.right?.subtreeLineBreakCount ?? 0) -\n (formsCRLF(this.left?.subtreeLastCharCode, this.piece.firstCharCode)\n ? 1\n : 0) -\n (formsCRLF(this.piece.lastCharCode, this.right?.subtreeFirstCharCode)\n ? 1\n : 0);\n this.subtreeFirstCharCode =\n this.left?.subtreeFirstCharCode ?? this.piece.firstCharCode;\n this.subtreeLastCharCode =\n this.right?.subtreeLastCharCode ?? this.piece.lastCharCode;\n }\n}\n\n/**\n * A piece table is a data structure that allows for efficient insertion and deletion of text.\n * It is a tree of pieces, where each piece is a segment of text that is either original or added.\n * The tree is a treap (a binary search tree that also keeps each node's random priority in heap\n * order, which keeps the tree balanced without an explicit rebalancing pass). Each edit reshapes\n * only the nodes along one root-to-leaf path via split and merge in O(log P), instead of\n * rebuilding all P pieces.\n * Inspired by https://code.visualstudio.com/blogs/2018/03/23/text-buffer-reimplementation\n */\nexport class PieceTable {\n #original: TextBuffer;\n #add = new TextBuffer('');\n #root: PieceNode | null = null;\n #length = 0;\n #lineCount = 0;\n #lastVisitedLine: [number, boolean, string] | null = null;\n #lastVisitedLineLength: [number, boolean, number] | null = null;\n // Exact inverse of the latest positionAt lookup; edits invalidate it.\n #lastPosition: [line: number, character: number, offset: number] | null =\n null;\n // Seeds the treap priorities. 0x9e3779b9 is the 32-bit golden-ratio\n // constant (2^32 / phi), a well-mixed value commonly used to seed hashes\n // and PRNGs; any fixed nonzero seed works here. A fixed seed keeps the\n // tree shape, and thus performance, deterministic across runs.\n #priorityState = 0x9e3779b9;\n\n constructor(originalText: string) {\n this.#original = new TextBuffer(originalText);\n const piece = this.#createPiece(Piece.Original, 0, originalText.length);\n this.#root = piece.length > 0 ? this.#createNode(piece) : null;\n this.#length = this.#root?.subtreeLength ?? 0;\n this.#lineCount = (this.#root?.subtreeLineBreakCount ?? 0) + 1;\n }\n\n get lineCount(): number {\n return this.#lineCount;\n }\n\n getText(range?: Range): string {\n if (range === undefined) {\n return this.#textFromPieces();\n }\n const start = this.offsetAt(range.start);\n const end = this.offsetAt(range.end);\n return this.getTextSlice(start, end);\n }\n\n getLineText(line: number, includeLineBreak = false): string {\n if (\n this.#lastVisitedLine !== null &&\n this.#lastVisitedLine[0] === line &&\n this.#lastVisitedLine[1] === includeLineBreak\n ) {\n return this.#lastVisitedLine[2];\n }\n const offset = this.#getLineOffset(line);\n if (offset === undefined) {\n throw new Error(`Line index out of range: ${line}`);\n }\n const text = this.getTextSlice(offset[0], offset[1], !includeLineBreak);\n this.#lastVisitedLine = [line, includeLineBreak, text];\n this.#lastVisitedLineLength = [line, includeLineBreak, text.length];\n return text;\n }\n\n getLineLength(line: number, includeLineBreak = false): number {\n const lastVisitedLineLength = this.#lastVisitedLineLength;\n const lastVisitedLine = this.#lastVisitedLine;\n if (\n lastVisitedLineLength !== null &&\n lastVisitedLineLength[0] === line &&\n lastVisitedLineLength[1] === includeLineBreak\n ) {\n return lastVisitedLineLength[2];\n }\n if (\n lastVisitedLine !== null &&\n lastVisitedLine[0] === line &&\n lastVisitedLine[1] === includeLineBreak\n ) {\n const length = lastVisitedLine[2].length;\n this.#lastVisitedLineLength = [line, includeLineBreak, length];\n return length;\n }\n const offset = this.#getLineOffset(line);\n if (offset === undefined) {\n throw new Error(`Line index out of range: ${line}`);\n }\n const [start, end] = offset;\n let length = end - start;\n if (!includeLineBreak) {\n while (\n length > 0 &&\n isEOL(this.charAt(start + length - 1).charCodeAt(0))\n ) {\n length--;\n }\n }\n this.#lastVisitedLineLength = [line, includeLineBreak, length];\n return length;\n }\n\n getTextSlice(start: number, end: number, trimEOF = false): string {\n if (start >= end) {\n return '';\n }\n\n const sliceStart = clamp(start, 0, this.#length);\n const sliceEnd = clamp(end, sliceStart, this.#length);\n if (sliceStart >= sliceEnd) {\n return '';\n }\n\n const location = this.#findPieceAtOffset(sliceStart);\n if (location === undefined) {\n return '';\n }\n\n const chunks: string[] = [];\n let [node, offsetInPiece] = location as [PieceNode | null, number];\n let remaining = sliceEnd - sliceStart;\n while (node !== null && remaining > 0) {\n const takeLength = Math.min(node.piece.length - offsetInPiece, remaining);\n const buffer = this.#bufferFor(node.piece.source);\n const start = node.piece.offset + offsetInPiece;\n let end = start + takeLength;\n if (trimEOF) {\n while (end > start && isEOL(buffer.text.charCodeAt(end - 1))) {\n end--;\n }\n }\n chunks.push(buffer.text.slice(start, end));\n remaining -= takeLength;\n offsetInPiece = 0;\n node = this.#nextNode(node);\n }\n\n return chunks.join('');\n }\n\n charAt(offset: number): string {\n const location = this.#findPieceAtOffset(offset);\n if (location === undefined) {\n return '';\n }\n\n const [node, offsetInPiece] = location;\n const buffer = this.#bufferFor(node.piece.source);\n return buffer.text.charAt(node.piece.offset + offsetInPiece);\n }\n\n includes(needle: string): boolean {\n if (needle.length === 0) {\n return true;\n }\n\n const prefixTable = createPrefixTable(needle);\n let matched = 0;\n let found = false;\n this.#forEachPieceSegment((segment) => {\n for (let offset = segment.start; offset < segment.end; offset++) {\n const charCode = segment.text.charCodeAt(offset);\n while (matched > 0 && charCode !== needle.charCodeAt(matched)) {\n matched = prefixTable[matched - 1];\n }\n if (charCode === needle.charCodeAt(matched)) {\n matched++;\n }\n if (matched === needle.length) {\n found = true;\n return false;\n }\n }\n return true;\n });\n return found;\n }\n\n findNextNonOverlappingSubstring(\n needle: string,\n occupied: readonly [start: number, end: number][]\n ): number | undefined {\n if (needle.length === 0 || needle.length > this.#length) {\n return undefined;\n }\n\n const ranges = normalizeRanges(occupied, this.#length);\n const pivot = ranges.reduce((max, [, end]) => Math.max(max, end), 0);\n const prefixTable = createPrefixTable(needle);\n let matched = 0;\n let documentOffset = 0;\n let wrappedOffset: number | undefined;\n let foundOffset: number | undefined;\n\n this.#forEachPieceSegment((segment) => {\n for (let offset = segment.start; offset < segment.end; offset++) {\n const charCode = segment.text.charCodeAt(offset);\n while (matched > 0 && charCode !== needle.charCodeAt(matched)) {\n matched = prefixTable[matched - 1];\n }\n if (charCode === needle.charCodeAt(matched)) {\n matched++;\n }\n if (matched === needle.length) {\n const start = documentOffset - needle.length + 1;\n if (!rangeOverlaps(ranges, start, start + needle.length)) {\n if (start >= pivot) {\n foundOffset = start;\n return false;\n }\n wrappedOffset ??= start;\n }\n matched = prefixTable[matched - 1];\n }\n documentOffset++;\n }\n return true;\n });\n\n return foundOffset ?? wrappedOffset;\n }\n\n search(searchParams: SearchParams): [start: number, end: number][] {\n if (searchParams.text.length === 0 || this.#length === 0) {\n return [];\n }\n\n // Search currently operates line-by-line, so newline-spanning patterns are unsupported.\n if (\n searchParams.text.includes('\\n') ||\n searchParams.text.includes('\\r') ||\n (searchParams.regex &&\n (searchParams.text.includes('\\\\n') ||\n searchParams.text.includes('\\\\r')))\n ) {\n return [];\n }\n\n let pattern: RegExp;\n try {\n pattern = compileSearchRegExp(\n searchParams.text,\n searchParams.regex,\n searchParams.caseSensitive\n );\n } catch {\n return [];\n }\n\n return this.#collectSearchMatchesLineByLine(\n pattern,\n searchParams.wholeWord,\n MAX_FIND_MATCHES\n );\n }\n\n #collectSearchMatchesLineByLine(\n pattern: RegExp,\n wholeWord: boolean,\n limit: number\n ): [number, number][] {\n const out: [number, number][] = [];\n // Search visits the whole document, so flatten it once instead of making\n // several treap descents for every line and whole-word boundary.\n const documentText = this.#textFromPieces();\n const docLength = documentText.length;\n const lineOffsets = computeLineOffsets(documentText);\n\n // Reuse the single compiled pattern across every line, resetting its\n // lastIndex before each line, instead of allocating a fresh RegExp per\n // line. The pattern is global, so lastIndex tracks progress within a line.\n for (let line = 0; line < lineOffsets.length; line++) {\n const lineStart = lineOffsets[line];\n let lineEnd = lineOffsets[line + 1] ?? docLength;\n while (\n lineEnd > lineStart &&\n isEOL(documentText.charCodeAt(lineEnd - 1))\n ) {\n lineEnd--;\n }\n const lineText = documentText.slice(lineStart, lineEnd);\n pattern.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(lineText)) !== null) {\n const rel = match.index;\n const fragment = match[0];\n if (fragment.length === 0) {\n pattern.lastIndex = advancePastEmptyMatch(lineText, rel);\n continue;\n }\n const docStart = lineStart + rel;\n if (\n !wholeWord ||\n isWholeWordAtDocOffsets(documentText, docStart, fragment.length)\n ) {\n out.push([docStart, docStart + fragment.length]);\n if (out.length >= limit) {\n return out;\n }\n }\n if (rel === pattern.lastIndex) {\n pattern.lastIndex = advancePastEmptyMatch(lineText, rel);\n }\n }\n }\n return out;\n }\n\n insert(text: string, offset: number): void {\n if (text.length === 0) {\n return;\n }\n const start = clamp(offset, 0, this.#length);\n this.#replaceRangeIncremental(start, start, text);\n this.#invalidateCaches();\n }\n\n delete(offset: number, length: number): void {\n if (length <= 0 || this.#length === 0) {\n return;\n }\n const start = clamp(offset, 0, this.#length);\n const end = clamp(start + length, start, this.#length);\n if (start === end) {\n return;\n }\n this.#replaceRangeIncremental(start, end, '');\n this.#invalidateCaches();\n }\n\n applyEdits(edits: readonly ResolvedTextEdit[]): void {\n if (edits.length === 0) {\n return;\n }\n // Edits arrive sorted ascending and non-overlapping (TextDocument enforces\n // this). Applying them from last to first keeps each remaining edit's\n // offsets valid, since edits at higher offsets never shift lower ones, and\n // every edit becomes an O(log P) incremental replace.\n for (let i = edits.length - 1; i >= 0; i--) {\n const edit = edits[i];\n const start = clamp(edit.start, 0, this.#length);\n const end = clamp(edit.end, start, this.#length);\n this.#replaceRangeIncremental(start, end, edit.text);\n }\n this.#invalidateCaches();\n }\n\n positionAt(offset: number): Position {\n const clampedOffset = clamp(offset, 0, this.#length);\n if (this.#length === 0) {\n return { line: 0, character: 0 };\n }\n const line = this.#lineAtOffset(clampedOffset);\n const lineStart = line === 0 ? 0 : this.#lineBreakOffset(line - 1);\n const character = clampedOffset - lineStart;\n this.#lastPosition = [line, character, clampedOffset];\n return { line, character };\n }\n\n positionsAt(offsets: readonly number[]): Position[] {\n const positions: Position[] = Array.from({ length: offsets.length });\n if (offsets.length === 0) {\n return positions;\n }\n if (this.#length === 0) {\n return positions.fill({ line: 0, character: 0 });\n }\n\n for (let i = 0; i < offsets.length; i++) {\n positions[i] = this.positionAt(offsets[i]);\n }\n\n return positions;\n }\n\n offsetAt(position: Position): number {\n if (position.line < 0 || this.#length === 0) {\n return 0;\n }\n if (position.line >= this.#lineCount) {\n throw new Error(`Line index out of range: ${position.line}`);\n }\n const lastPosition = this.#lastPosition;\n if (\n lastPosition !== null &&\n lastPosition[0] === position.line &&\n lastPosition[1] === position.character\n ) {\n return lastPosition[2];\n }\n const offset = this.#getLineOffset(position.line);\n if (offset === undefined) {\n throw new Error(`Line index out of range: ${position.line}`);\n }\n const character = clamp(position.character, 0, offset[1] - offset[0]);\n return offset[0] + character;\n }\n\n #findPieceAtOffset(\n offset: number\n ): [node: PieceNode, offsetInPiece: number] | undefined {\n if (offset < 0 || offset >= this.#length) {\n return undefined;\n }\n\n let node = this.#root;\n let remaining = offset;\n while (node !== null) {\n const leftLength = node.left?.subtreeLength ?? 0;\n if (remaining < leftLength) {\n node = node.left;\n continue;\n }\n\n remaining -= leftLength;\n if (remaining < node.piece.length) {\n return [node, remaining];\n }\n\n remaining -= node.piece.length;\n node = node.right;\n }\n\n return undefined;\n }\n\n #nextNode(node: PieceNode): PieceNode | null {\n if (node.right !== null) {\n let next = node.right;\n while (next.left !== null) {\n next = next.left;\n }\n return next;\n }\n\n let current = node;\n while (current.parent !== null && current === current.parent.right) {\n current = current.parent;\n }\n return current.parent;\n }\n\n #getLineOffset(line: number): [start: number, end: number] | undefined {\n if (line < 0) {\n throw new Error(`Line index out of range: ${line}`);\n }\n if (this.#length === 0) {\n if (line === 0) {\n return [0, 0];\n }\n throw new Error(`Line index out of range: ${line}`);\n }\n if (line >= this.#lineCount) {\n throw new Error(`Line index out of range: ${line}`);\n }\n\n const start = line === 0 ? 0 : this.#lineBreakOffset(line - 1);\n const end =\n line < this.#lineCount - 1 ? this.#lineBreakOffset(line) : this.#length;\n return [start, end];\n }\n\n #lineAtOffset(offset: number): number {\n let node = this.#root;\n let remaining = clamp(offset, 0, this.#length);\n let line = 0;\n\n while (node !== null) {\n const leftLength = node.left?.subtreeLength ?? 0;\n if (remaining < leftLength) {\n node = node.left;\n continue;\n }\n\n line +=\n (node.left?.subtreeLineBreakCount ?? 0) -\n (formsCRLF(node.left?.subtreeLastCharCode, node.piece.firstCharCode)\n ? 1\n : 0);\n remaining -= leftLength;\n if (remaining <= node.piece.length) {\n const buffer = this.#bufferFor(node.piece.source);\n const lineOffsetEnd = Math.min(\n upperBound(buffer.lineOffsets, node.piece.offset + remaining),\n node.piece.lineOffsetEnd\n );\n line += lineOffsetEnd - node.piece.lineOffsetStart;\n if (\n remaining === node.piece.length &&\n node.piece.hasVirtualTrailingCRBreak\n ) {\n line++;\n }\n if (\n remaining === node.piece.length &&\n formsCRLF(\n node.piece.lastCharCode,\n this.#nextNode(node)?.piece.firstCharCode\n )\n ) {\n line--;\n }\n return line;\n }\n\n line +=\n node.piece.lineBreakCount -\n (formsCRLF(node.piece.lastCharCode, node.right?.subtreeFirstCharCode)\n ? 1\n : 0);\n remaining -= node.piece.length;\n node = node.right;\n }\n\n return this.#lineCount - 1;\n }\n\n #lineBreakOffset(lineBreakIndex: number): number {\n let node = this.#root;\n let remaining = lineBreakIndex;\n let documentOffset = 0;\n let followingCharCode: number | undefined;\n\n while (node !== null) {\n const leftLineBreakCount =\n (node.left?.subtreeLineBreakCount ?? 0) -\n (formsCRLF(node.left?.subtreeLastCharCode, node.piece.firstCharCode)\n ? 1\n : 0);\n if (remaining < leftLineBreakCount) {\n followingCharCode = node.piece.firstCharCode;\n node = node.left;\n continue;\n }\n\n const leftLength = node.left?.subtreeLength ?? 0;\n documentOffset += leftLength;\n remaining -= leftLineBreakCount;\n\n const nextCharCode =\n node.right?.subtreeFirstCharCode ?? followingCharCode;\n const pieceLineBreakCount =\n node.piece.lineBreakCount -\n (formsCRLF(node.piece.lastCharCode, nextCharCode) ? 1 : 0);\n if (remaining < pieceLineBreakCount) {\n return (\n documentOffset + this.#pieceLineBreakOffset(node.piece, remaining)\n );\n }\n\n documentOffset += node.piece.length;\n remaining -= pieceLineBreakCount;\n node = node.right;\n }\n\n return this.#length;\n }\n\n #pieceLineBreakOffset(piece: Piece, lineBreakIndex: number): number {\n const bufferLineBreakCount = piece.lineOffsetEnd - piece.lineOffsetStart;\n if (lineBreakIndex < bufferLineBreakCount) {\n const bufferLineOffset = this.#bufferFor(piece.source).lineOffsets[\n piece.lineOffsetStart + lineBreakIndex\n ];\n return bufferLineOffset - piece.offset;\n }\n return piece.length;\n }\n\n #textFromPieces(): string {\n const chunks: string[] = [];\n this.#forEachPieceSegment((segment) => {\n chunks.push(segment.text.slice(segment.start, segment.end));\n });\n return chunks.join('');\n }\n\n #forEachPieceSegment(\n callback: (segment: {\n readonly start: number;\n readonly end: number;\n readonly text: string;\n readonly lineOffsets: number[];\n readonly lineOffsetStart: number;\n readonly lineOffsetEnd: number;\n }) => boolean | void\n ): void {\n this.#walk(this.#root, (node) => {\n const buffer = this.#bufferFor(node.piece.source);\n return callback({\n text: buffer.text,\n lineOffsets: buffer.lineOffsets,\n lineOffsetStart: node.piece.lineOffsetStart,\n lineOffsetEnd: node.piece.lineOffsetEnd,\n start: node.piece.offset,\n end: node.piece.offset + node.piece.length,\n });\n });\n }\n\n #bufferFor(source: number): TextBuffer {\n return source === Piece.Original ? this.#original : this.#add;\n }\n\n #createPiece(source: number, offset: number, length: number): Piece {\n const buffer = this.#bufferFor(source);\n const end = offset + length;\n const lineOffsetStart = upperBound(buffer.lineOffsets, offset);\n const lineOffsetEnd = upperBound(buffer.lineOffsets, end);\n const lastCharCode = buffer.text.charCodeAt(end - 1);\n return new Piece(\n source,\n offset,\n length,\n lineOffsetStart,\n lineOffsetEnd,\n lastCharCode === CARRIAGE_RETURN &&\n buffer.lineOffsets[lineOffsetEnd - 1] !== end,\n buffer.text.charCodeAt(offset),\n lastCharCode\n );\n }\n\n // Replaces document range [start, end) with `text` by splitting at the start,\n // dropping the deleted prefix from the remainder, and joining the inserted\n // piece back in. Each tree operation touches only one root-to-leaf path, so\n // the edit is O(log P) instead of rebuilding all P pieces. `start`/`end` must\n // be clamped into [0, length] with start <= end.\n #replaceRangeIncremental(start: number, end: number, text: string): void {\n if (start === end && text.length === 0) {\n return;\n }\n\n const [left, rest] = this.#split(this.#root, start);\n const right = this.#dropPrefix(rest, end - start);\n\n let root: PieceNode | null;\n if (text.length > 0) {\n const insertedPiece = this.#createPiece(\n Piece.Added,\n this.#add.append(text),\n text.length\n );\n // The inserted text is fresh at the end of the add buffer, so it can only\n // sit next to the piece on its left, never the one on its right.\n root = this.#mergeNodes(\n this.#appendCoalescing(left, insertedPiece),\n right\n );\n } else {\n // Pure deletion: the pieces now meeting at the seam may be contiguous in\n // the same buffer, so re-attach the right side's first piece through the\n // coalescing append to merge them when possible.\n const [seamPiece, restRight] = this.#popLeftmost(right);\n root =\n seamPiece === undefined\n ? left\n : this.#mergeNodes(\n this.#appendCoalescing(left, seamPiece),\n restRight\n );\n }\n\n if (root !== null) {\n root.parent = null;\n }\n this.#root = root;\n this.#length = root?.subtreeLength ?? 0;\n this.#lineCount = (root?.subtreeLineBreakCount ?? 0) + 1;\n }\n\n // Public mutations clear read caches once, including multi-edit batches.\n #invalidateCaches(): void {\n this.#lastVisitedLine = null;\n this.#lastVisitedLineLength = null;\n this.#lastPosition = null;\n }\n\n #nextPriority(): number {\n this.#priorityState =\n (Math.imul(this.#priorityState, 1664525) + 1013904223) >>> 0;\n return this.#priorityState;\n }\n\n #createNode(piece: Piece): PieceNode {\n const node = new PieceNode(piece);\n node.priority = this.#nextPriority();\n return node;\n }\n\n // Attaches `child` as a side of `node`, keeping the parent pointer in sync so\n // read-side iteration (#nextNode) can still walk back up the tree.\n #setLeft(node: PieceNode, child: PieceNode | null): void {\n node.left = child;\n if (child !== null) {\n child.parent = node;\n }\n }\n\n #setRight(node: PieceNode, child: PieceNode | null): void {\n node.right = child;\n if (child !== null) {\n child.parent = node;\n }\n }\n\n // Splits the subtree into [0, offset) and [offset, length). When the offset\n // falls inside a piece, that piece is sliced in two so the split lands on a\n // clean boundary. Internal parent pointers stay correct; the caller fixes the\n // returned roots' own parents when it reattaches or stores them.\n #split(\n node: PieceNode | null,\n offset: number\n ): [left: PieceNode | null, right: PieceNode | null] {\n if (node === null) {\n return [null, null];\n }\n if (offset <= 0) {\n return [null, node];\n }\n if (offset >= node.subtreeLength) {\n return [node, null];\n }\n\n const leftLength = node.left?.subtreeLength ?? 0;\n if (offset <= leftLength) {\n const [left, right] = this.#split(node.left, offset);\n this.#setLeft(node, right);\n node.updateSubtreeLength();\n return [left, node];\n }\n\n const pieceLength = node.piece.length;\n if (offset >= leftLength + pieceLength) {\n const [left, right] = this.#split(\n node.right,\n offset - leftLength - pieceLength\n );\n this.#setRight(node, left);\n node.updateSubtreeLength();\n return [node, right];\n }\n\n // The offset is strictly inside this node's piece: slice it in two. Both\n // halves inherit this node's priority, which keeps the heap valid because\n // this node already outranked both of its children.\n const inPiece = offset - leftLength;\n const leftNode = new PieceNode(\n this.#createPiece(node.piece.source, node.piece.offset, inPiece)\n );\n const rightNode = new PieceNode(\n this.#createPiece(\n node.piece.source,\n node.piece.offset + inPiece,\n pieceLength - inPiece\n )\n );\n leftNode.priority = node.priority;\n rightNode.priority = node.priority;\n this.#setLeft(leftNode, node.left);\n this.#setRight(rightNode, node.right);\n leftNode.updateSubtreeLength();\n rightNode.updateSubtreeLength();\n return [leftNode, rightNode];\n }\n\n // Discards [0, offset), allocating only the surviving half of a cut piece.\n #dropPrefix(node: PieceNode | null, offset: number): PieceNode | null {\n if (node === null || offset >= node.subtreeLength) {\n return null;\n }\n if (offset <= 0) {\n return node;\n }\n\n const leftLength = node.left?.subtreeLength ?? 0;\n if (offset <= leftLength) {\n this.#setLeft(node, this.#dropPrefix(node.left, offset));\n node.updateSubtreeLength();\n return node;\n }\n\n const pieceEnd = leftLength + node.piece.length;\n if (offset >= pieceEnd) {\n return this.#dropPrefix(node.right, offset - pieceEnd);\n }\n\n const inPiece = offset - leftLength;\n const rightNode = new PieceNode(\n this.#createPiece(\n node.piece.source,\n node.piece.offset + inPiece,\n node.piece.length - inPiece\n )\n );\n rightNode.priority = node.priority;\n this.#setRight(rightNode, node.right);\n rightNode.updateSubtreeLength();\n return rightNode;\n }\n\n // Joins two subtrees where every offset in `left` precedes every offset in\n // `right`, taking the higher-priority root so the result stays balanced.\n #mergeNodes(\n left: PieceNode | null,\n right: PieceNode | null\n ): PieceNode | null {\n if (left === null) {\n return right;\n }\n if (right === null) {\n return left;\n }\n if (left.priority >= right.priority) {\n this.#setRight(left, this.#mergeNodes(left.right, right));\n left.updateSubtreeLength();\n return left;\n }\n this.#setLeft(right, this.#mergeNodes(left, right.left));\n right.updateSubtreeLength();\n return right;\n }\n\n // Appends `piece` after every node in `tree`. Compatible adjacent pieces in\n // the same buffer are merged in place, keeping sequential typing compact.\n #appendCoalescing(tree: PieceNode | null, piece: Piece): PieceNode {\n if (tree === null) {\n return this.#createNode(piece);\n }\n let last = tree;\n while (last.right !== null) {\n last = last.right;\n }\n if (canCoalescePieces(last.piece, piece)) {\n last.piece = coalesceTwoPieces(last.piece, piece);\n // The last piece grew, so refresh aggregates from it up to the root.\n for (let node: PieceNode | null = last; node !== null; ) {\n node.updateSubtreeLength();\n if (node === tree) {\n break;\n }\n node = node.parent;\n }\n return tree;\n }\n // `tree` is non-null here, so merging in the new node always yields a node.\n return this.#mergeNodes(tree, this.#createNode(piece)) as PieceNode;\n }\n\n // Removes and returns the first piece of the tree along with the remaining\n // tree, so a deletion seam can re-merge the piece that becomes adjacent to\n // the one on the other side of the deletion.\n #popLeftmost(\n tree: PieceNode | null\n ): [piece: Piece | undefined, rest: PieceNode | null] {\n if (tree === null) {\n return [undefined, null];\n }\n if (tree.left === null) {\n return [tree.piece, tree.right];\n }\n const [piece, newLeft] = this.#popLeftmost(tree.left);\n this.#setLeft(tree, newLeft);\n tree.updateSubtreeLength();\n return [piece, tree];\n }\n\n #walk(\n node: PieceNode | null,\n visit: (node: PieceNode) => boolean | void\n ): boolean {\n if (node === null) {\n return true;\n }\n if (!this.#walk(node.left, visit)) {\n return false;\n }\n if (visit(node) === false) {\n return false;\n }\n return this.#walk(node.right, visit);\n }\n}\n\nfunction isEOL(charCode: number): boolean {\n return charCode === /* \\n */ 10 || charCode === /* \\r */ 13;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction createPrefixTable(text: string): number[] {\n const table = Array.from<number>({ length: text.length }).fill(0);\n let matched = 0;\n for (let i = 1; i < text.length; i++) {\n const charCode = text.charCodeAt(i);\n while (matched > 0 && charCode !== text.charCodeAt(matched)) {\n matched = table[matched - 1];\n }\n if (charCode === text.charCodeAt(matched)) {\n matched++;\n }\n table[i] = matched;\n }\n return table;\n}\n\nfunction normalizeRanges(\n ranges: readonly [start: number, end: number][],\n length: number\n): [start: number, end: number][] {\n const normalized: [start: number, end: number][] = [];\n for (const [rawStart, rawEnd] of ranges) {\n const start = clamp(rawStart, 0, length);\n const end = clamp(rawEnd, start, length);\n if (start < end) {\n normalized.push([start, end]);\n }\n }\n normalized.sort((a, b) => a[0] - b[0]);\n\n const merged: [start: number, end: number][] = [];\n for (const range of normalized) {\n const previous = merged[merged.length - 1];\n if (previous !== undefined && range[0] <= previous[1]) {\n previous[1] = Math.max(previous[1], range[1]);\n continue;\n }\n merged.push(range);\n }\n return merged;\n}\n\nfunction rangeOverlaps(\n ranges: readonly [start: number, end: number][],\n start: number,\n end: number\n): boolean {\n let low = 0;\n let high = ranges.length;\n while (low < high) {\n const mid = low + Math.floor((high - low) / 2);\n if (ranges[mid][1] <= start) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n\n const range = ranges[low];\n return range !== undefined && range[0] < end;\n}\n\n// CRLF seams stay explicit because separately appended buffer chunks may each\n// record a break there; the tree aggregate reconciles the document-level pair.\nfunction canCoalescePieces(prev: Piece, next: Piece): boolean {\n return (\n prev.source === next.source &&\n prev.offset + prev.length === next.offset &&\n !formsCRLF(prev.lastCharCode, next.firstCharCode)\n );\n}\n\n// Joins two adjacent pieces (see canCoalescePieces) into one. Keeping the table\n// compact after every edit is what stops the piece count from growing without\n// bound during normal typing.\nfunction coalesceTwoPieces(prev: Piece, next: Piece): Piece {\n return new Piece(\n prev.source,\n prev.offset,\n prev.length + next.length,\n prev.lineOffsetStart,\n next.lineOffsetEnd,\n next.hasVirtualTrailingCRBreak,\n prev.firstCharCode,\n next.lastCharCode\n );\n}\n\nfunction formsCRLF(\n leftCharCode: number | undefined,\n rightCharCode: number | undefined\n): boolean {\n return leftCharCode === CARRIAGE_RETURN && rightCharCode === LINE_FEED;\n}\n\n// Returns the index of the first element in the array that is greater than the target.\nfunction upperBound(values: number[], target: number): number {\n let lo = 0;\n let hi = values.length;\n while (lo < hi) {\n const mid = lo + Math.floor((hi - lo) / 2);\n if (values[mid] <= target) {\n lo = mid + 1;\n } else {\n hi = mid;\n }\n }\n return lo;\n}\n\nfunction escapeRegExp(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction isWordSeparatorCharCode(charCode: number): boolean {\n if (charCode <= 32 || charCode === 127) {\n return true;\n }\n const ch = String.fromCharCode(charCode);\n return WORD_SEPARATORS.includes(ch);\n}\n\n// Checks if the given text is a whole word by checking if the\n// characters before and after are word separators.\nfunction isWholeWordAtDocOffsets(\n text: string,\n docStart: number,\n length: number\n): boolean {\n const beforeOk =\n docStart <= 0 || isWordSeparatorCharCode(text.charCodeAt(docStart - 1));\n const afterOk =\n docStart + length >= text.length ||\n isWordSeparatorCharCode(text.charCodeAt(docStart + length));\n return beforeOk && afterOk;\n}\n\nfunction compileSearchRegExp(\n source: string,\n isRegex: boolean,\n caseSensitive: boolean\n): RegExp {\n const body = isRegex ? source : escapeRegExp(source);\n const flags = `g${caseSensitive ? '' : 'i'}${isRegex ? 'm' : ''}`;\n return new RegExp(body, flags);\n}\n\n/** Expands `$&`, `$1`, `$$`, etc. in a regex replace string using a match. */\nfunction expandReplaceString(\n replacement: string,\n match: RegExpExecArray\n): string {\n return replacement.replace(/\\$([$&]|\\d+)/g, (_token, group: string) => {\n if (group === '$') {\n return '$';\n }\n if (group === '&') {\n return match[0] ?? '';\n }\n const index = Number(group);\n return match[index] ?? '';\n });\n}\n\n/**\n * Builds the text to insert for one search match, including regex capture\n * substitution when regex mode is enabled.\n */\nexport function buildSearchReplacementText(\n positionAt: (offset: number) => Position,\n offsetAt: (position: Position) => number,\n getLineText: (line: number) => string,\n searchParams: SearchParams,\n matchStart: number,\n matchEnd: number\n): string {\n if (!searchParams.regex) {\n return searchParams.replaceText;\n }\n\n const position = positionAt(matchStart);\n const lineText = getLineText(position.line);\n const lineStart = offsetAt({ line: position.line, character: 0 });\n const relStart = matchStart - lineStart;\n\n let pattern: RegExp;\n try {\n pattern = compileSearchRegExp(\n searchParams.text,\n true,\n searchParams.caseSensitive\n );\n } catch {\n return searchParams.replaceText;\n }\n\n // Re-run at the original line offset so lookaround can inspect context\n // outside the matched range while captures still come from this exact hit.\n pattern.lastIndex = relStart;\n const match = pattern.exec(lineText);\n if (\n match === null ||\n match.index !== relStart ||\n match[0].length !== matchEnd - matchStart\n ) {\n return searchParams.replaceText;\n }\n return expandReplaceString(searchParams.replaceText, match);\n}\n\nfunction advancePastEmptyMatch(text: string, index: number): number {\n if (index + 1 < text.length) {\n const first = text.charCodeAt(index);\n const second = text.charCodeAt(index + 1);\n if (\n first >= 0xd800 &&\n first <= 0xdbff &&\n second >= 0xdc00 &&\n second <= 0xdfff\n ) {\n return index + 2;\n }\n }\n return index + 1;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pieceTable.js","names":["#original","#createPiece","#root","#createNode","#length","#lineCount","#textFromPieces","#lastVisitedLine","#getLineOffset","#lastVisitedLineLength","#findPieceAtOffset","#bufferFor","#nextNode","#forEachPieceSegment","#collectSearchMatchesLineByLine","#replaceRangeIncremental","#invalidateCaches","#lineAtOffset","#lineBreakOffset","#lastPosition","#pieceLineBreakOffset","#walk","#add","#split","#dropPrefix","#mergeNodes","#appendCoalescing","#popLeftmost","#priorityState","#nextPriority","#setLeft","#setRight"],"sources":["../../src/editor/pieceTable.ts"],"sourcesContent":["import { computeLineOffsets } from '../utils/computeFileOffsets';\nimport type { SearchParams } from './searchPanel';\nimport type { Position, Range, ResolvedTextEdit } from './types';\n\nconst MAX_FIND_MATCHES = 100000;\nconst LINE_FEED = 10;\nconst CARRIAGE_RETURN = 13;\n// TODO(ije): use Intl.Segmenter instead of regex for word separators\nconst WORD_SEPARATORS = '`~!@#$%^&*()-=+[{]}\\\\|;:\\'\",.<>/?' as const;\n\n// A piece is a segment of text that is either original or added.\nclass Piece {\n static Original = 0;\n static Added = 1;\n\n constructor(\n public readonly source: number,\n public readonly offset: number,\n public readonly length: number,\n public readonly lineOffsetStart: number,\n public readonly lineOffsetEnd: number,\n public readonly hasVirtualTrailingCRBreak: boolean,\n public readonly firstCharCode: number,\n public readonly lastCharCode: number\n ) {}\n\n // A slice ending between a backing-buffer CRLF needs its own lone-CR break\n // until the tree reconnects it to a piece beginning with LF.\n get lineBreakCount(): number {\n return (\n this.lineOffsetEnd -\n this.lineOffsetStart +\n (this.hasVirtualTrailingCRBreak ? 1 : 0)\n );\n }\n}\n\n// A text buffer is a string with its line offsets.\nclass TextBuffer {\n lineOffsets: number[];\n\n constructor(public text: string) {\n this.lineOffsets = computeLineOffsets(text);\n }\n\n // the append operation is efficient because it only appends\n // elements to the lineOffsets array in the end\n append(text: string): number {\n const offset = this.text.length;\n for (let i = 0; i < text.length; i++) {\n const charCode = text.charCodeAt(i);\n if (charCode !== LINE_FEED && charCode !== CARRIAGE_RETURN) {\n continue;\n }\n if (\n charCode === CARRIAGE_RETURN &&\n text.charCodeAt(i + 1) === LINE_FEED\n ) {\n i++;\n }\n this.lineOffsets.push(offset + i + 1);\n }\n this.text += text;\n return offset;\n }\n}\n\n// A node in the balanced piece tree. `priority` keeps the tree balanced as a\n// treap (see PieceTable): the tree is a max-heap on priority while staying a\n// binary search tree on document offset.\nclass PieceNode {\n left: PieceNode | null = null;\n right: PieceNode | null = null;\n parent: PieceNode | null = null;\n priority = 0;\n\n constructor(\n public piece: Piece,\n public subtreeLength: number = piece.length,\n public subtreeLineBreakCount: number = piece.lineBreakCount,\n public subtreeFirstCharCode: number = piece.firstCharCode,\n public subtreeLastCharCode: number = piece.lastCharCode\n ) {}\n\n updateSubtreeLength(): void {\n this.subtreeLength =\n (this.left?.subtreeLength ?? 0) +\n this.piece.length +\n (this.right?.subtreeLength ?? 0);\n // Pieces count their edge characters in isolation. Collapse a CRLF only\n // when the two characters are actually adjacent in this subtree.\n this.subtreeLineBreakCount =\n (this.left?.subtreeLineBreakCount ?? 0) +\n this.piece.lineBreakCount +\n (this.right?.subtreeLineBreakCount ?? 0) -\n (formsCRLF(this.left?.subtreeLastCharCode, this.piece.firstCharCode)\n ? 1\n : 0) -\n (formsCRLF(this.piece.lastCharCode, this.right?.subtreeFirstCharCode)\n ? 1\n : 0);\n this.subtreeFirstCharCode =\n this.left?.subtreeFirstCharCode ?? this.piece.firstCharCode;\n this.subtreeLastCharCode =\n this.right?.subtreeLastCharCode ?? this.piece.lastCharCode;\n }\n}\n\n/**\n * A piece table is a data structure that allows for efficient insertion and deletion of text.\n * It is a tree of pieces, where each piece is a segment of text that is either original or added.\n * The tree is a treap (a binary search tree that also keeps each node's random priority in heap\n * order, which keeps the tree balanced without an explicit rebalancing pass). Each edit reshapes\n * only the nodes along one root-to-leaf path via split and merge in O(log P), instead of\n * rebuilding all P pieces.\n * Inspired by https://code.visualstudio.com/blogs/2018/03/23/text-buffer-reimplementation\n */\nexport class PieceTable {\n #original: TextBuffer;\n #add = new TextBuffer('');\n #root: PieceNode | null = null;\n #length = 0;\n #lineCount = 0;\n #lastVisitedLine: [number, boolean, string] | null = null;\n #lastVisitedLineLength: [number, boolean, number] | null = null;\n // Exact inverse of the latest positionAt lookup; edits invalidate it.\n #lastPosition: [line: number, character: number, offset: number] | null =\n null;\n // Seeds the treap priorities. 0x9e3779b9 is the 32-bit golden-ratio\n // constant (2^32 / phi), a well-mixed value commonly used to seed hashes\n // and PRNGs; any fixed nonzero seed works here. A fixed seed keeps the\n // tree shape, and thus performance, deterministic across runs.\n #priorityState = 0x9e3779b9;\n\n constructor(originalText: string) {\n this.#original = new TextBuffer(originalText);\n const piece = this.#createPiece(Piece.Original, 0, originalText.length);\n this.#root = piece.length > 0 ? this.#createNode(piece) : null;\n this.#length = this.#root?.subtreeLength ?? 0;\n this.#lineCount = (this.#root?.subtreeLineBreakCount ?? 0) + 1;\n }\n\n get lineCount(): number {\n return this.#lineCount;\n }\n\n getText(range?: Range): string {\n if (range === undefined) {\n return this.#textFromPieces();\n }\n const start = this.offsetAt(range.start);\n const end = this.offsetAt(range.end);\n return this.getTextSlice(start, end);\n }\n\n getLineText(line: number, includeLineBreak = false): string {\n if (\n this.#lastVisitedLine !== null &&\n this.#lastVisitedLine[0] === line &&\n this.#lastVisitedLine[1] === includeLineBreak\n ) {\n return this.#lastVisitedLine[2];\n }\n const offset = this.#getLineOffset(line);\n if (offset === undefined) {\n throw new Error(`Line index out of range: ${line}`);\n }\n const text = this.getTextSlice(offset[0], offset[1], !includeLineBreak);\n this.#lastVisitedLine = [line, includeLineBreak, text];\n this.#lastVisitedLineLength = [line, includeLineBreak, text.length];\n return text;\n }\n\n getLineLength(line: number, includeLineBreak = false): number {\n const lastVisitedLineLength = this.#lastVisitedLineLength;\n const lastVisitedLine = this.#lastVisitedLine;\n if (\n lastVisitedLineLength !== null &&\n lastVisitedLineLength[0] === line &&\n lastVisitedLineLength[1] === includeLineBreak\n ) {\n return lastVisitedLineLength[2];\n }\n if (\n lastVisitedLine !== null &&\n lastVisitedLine[0] === line &&\n lastVisitedLine[1] === includeLineBreak\n ) {\n const length = lastVisitedLine[2].length;\n this.#lastVisitedLineLength = [line, includeLineBreak, length];\n return length;\n }\n const offset = this.#getLineOffset(line);\n if (offset === undefined) {\n throw new Error(`Line index out of range: ${line}`);\n }\n const [start, end] = offset;\n let length = end - start;\n if (!includeLineBreak) {\n while (\n length > 0 &&\n isEOL(this.charAt(start + length - 1).charCodeAt(0))\n ) {\n length--;\n }\n }\n this.#lastVisitedLineLength = [line, includeLineBreak, length];\n return length;\n }\n\n getTextSlice(start: number, end: number, trimEOF = false): string {\n if (start >= end) {\n return '';\n }\n\n const sliceStart = clamp(start, 0, this.#length);\n const sliceEnd = clamp(end, sliceStart, this.#length);\n if (sliceStart >= sliceEnd) {\n return '';\n }\n\n const location = this.#findPieceAtOffset(sliceStart);\n if (location === undefined) {\n return '';\n }\n\n const chunks: string[] = [];\n let [node, offsetInPiece] = location as [PieceNode | null, number];\n let remaining = sliceEnd - sliceStart;\n while (node !== null && remaining > 0) {\n const takeLength = Math.min(node.piece.length - offsetInPiece, remaining);\n const buffer = this.#bufferFor(node.piece.source);\n const start = node.piece.offset + offsetInPiece;\n let end = start + takeLength;\n if (trimEOF) {\n while (end > start && isEOL(buffer.text.charCodeAt(end - 1))) {\n end--;\n }\n }\n chunks.push(buffer.text.slice(start, end));\n remaining -= takeLength;\n offsetInPiece = 0;\n node = this.#nextNode(node);\n }\n\n return chunks.join('');\n }\n\n charAt(offset: number): string {\n const location = this.#findPieceAtOffset(offset);\n if (location === undefined) {\n return '';\n }\n\n const [node, offsetInPiece] = location;\n const buffer = this.#bufferFor(node.piece.source);\n return buffer.text.charAt(node.piece.offset + offsetInPiece);\n }\n\n includes(needle: string): boolean {\n if (needle.length === 0) {\n return true;\n }\n\n const prefixTable = createPrefixTable(needle);\n let matched = 0;\n let found = false;\n this.#forEachPieceSegment((segment) => {\n for (let offset = segment.start; offset < segment.end; offset++) {\n const charCode = segment.text.charCodeAt(offset);\n while (matched > 0 && charCode !== needle.charCodeAt(matched)) {\n matched = prefixTable[matched - 1];\n }\n if (charCode === needle.charCodeAt(matched)) {\n matched++;\n }\n if (matched === needle.length) {\n found = true;\n return false;\n }\n }\n return true;\n });\n return found;\n }\n\n findNextNonOverlappingSubstring(\n needle: string,\n occupied: readonly [start: number, end: number][]\n ): number | undefined {\n if (needle.length === 0 || needle.length > this.#length) {\n return undefined;\n }\n\n const ranges = normalizeRanges(occupied, this.#length);\n const pivot = ranges.reduce((max, [, end]) => Math.max(max, end), 0);\n const prefixTable = createPrefixTable(needle);\n let matched = 0;\n let documentOffset = 0;\n let wrappedOffset: number | undefined;\n let foundOffset: number | undefined;\n\n this.#forEachPieceSegment((segment) => {\n for (let offset = segment.start; offset < segment.end; offset++) {\n const charCode = segment.text.charCodeAt(offset);\n while (matched > 0 && charCode !== needle.charCodeAt(matched)) {\n matched = prefixTable[matched - 1];\n }\n if (charCode === needle.charCodeAt(matched)) {\n matched++;\n }\n if (matched === needle.length) {\n const start = documentOffset - needle.length + 1;\n if (!rangeOverlaps(ranges, start, start + needle.length)) {\n if (start >= pivot) {\n foundOffset = start;\n return false;\n }\n wrappedOffset ??= start;\n }\n matched = prefixTable[matched - 1];\n }\n documentOffset++;\n }\n return true;\n });\n\n return foundOffset ?? wrappedOffset;\n }\n\n search(searchParams: SearchParams): [start: number, end: number][] {\n if (searchParams.text.length === 0 || this.#length === 0) {\n return [];\n }\n\n // Search currently operates line-by-line, so newline-spanning patterns are unsupported.\n if (\n searchParams.text.includes('\\n') ||\n searchParams.text.includes('\\r') ||\n (searchParams.regex &&\n (searchParams.text.includes('\\\\n') ||\n searchParams.text.includes('\\\\r')))\n ) {\n return [];\n }\n\n let pattern: RegExp;\n try {\n pattern = compileSearchRegExp(\n searchParams.text,\n searchParams.regex,\n searchParams.caseSensitive\n );\n } catch {\n return [];\n }\n\n return this.#collectSearchMatchesLineByLine(\n pattern,\n searchParams.wholeWord,\n MAX_FIND_MATCHES\n );\n }\n\n #collectSearchMatchesLineByLine(\n pattern: RegExp,\n wholeWord: boolean,\n limit: number\n ): [number, number][] {\n const out: [number, number][] = [];\n // Search visits the whole document, so flatten it once instead of making\n // several treap descents for every line and whole-word boundary.\n const documentText = this.#textFromPieces();\n const docLength = documentText.length;\n const lineOffsets = computeLineOffsets(documentText);\n\n // Reuse the single compiled pattern across every line, resetting its\n // lastIndex before each line, instead of allocating a fresh RegExp per\n // line. The pattern is global, so lastIndex tracks progress within a line.\n for (let line = 0; line < lineOffsets.length; line++) {\n const lineStart = lineOffsets[line];\n let lineEnd = lineOffsets[line + 1] ?? docLength;\n while (\n lineEnd > lineStart &&\n isEOL(documentText.charCodeAt(lineEnd - 1))\n ) {\n lineEnd--;\n }\n const lineText = documentText.slice(lineStart, lineEnd);\n pattern.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(lineText)) !== null) {\n const rel = match.index;\n const fragment = match[0];\n if (fragment.length === 0) {\n pattern.lastIndex = advancePastEmptyMatch(lineText, rel);\n continue;\n }\n const docStart = lineStart + rel;\n if (\n !wholeWord ||\n isWholeWordAtDocOffsets(documentText, docStart, fragment.length)\n ) {\n out.push([docStart, docStart + fragment.length]);\n if (out.length >= limit) {\n return out;\n }\n }\n if (rel === pattern.lastIndex) {\n pattern.lastIndex = advancePastEmptyMatch(lineText, rel);\n }\n }\n }\n return out;\n }\n\n insert(text: string, offset: number): void {\n if (text.length === 0) {\n return;\n }\n const start = clamp(offset, 0, this.#length);\n this.#replaceRangeIncremental(start, start, text);\n this.#invalidateCaches();\n }\n\n delete(offset: number, length: number): void {\n if (length <= 0 || this.#length === 0) {\n return;\n }\n const start = clamp(offset, 0, this.#length);\n const end = clamp(start + length, start, this.#length);\n if (start === end) {\n return;\n }\n this.#replaceRangeIncremental(start, end, '');\n this.#invalidateCaches();\n }\n\n applyEdits(edits: readonly ResolvedTextEdit[]): void {\n if (edits.length === 0) {\n return;\n }\n // Edits arrive sorted ascending and non-overlapping (TextDocument enforces\n // this). Applying them from last to first keeps each remaining edit's\n // offsets valid, since edits at higher offsets never shift lower ones, and\n // every edit becomes an O(log P) incremental replace.\n for (let i = edits.length - 1; i >= 0; i--) {\n const edit = edits[i];\n const start = clamp(edit.start, 0, this.#length);\n const end = clamp(edit.end, start, this.#length);\n this.#replaceRangeIncremental(start, end, edit.text);\n }\n this.#invalidateCaches();\n }\n\n positionAt(offset: number): Position {\n const clampedOffset = clamp(offset, 0, this.#length);\n if (this.#length === 0) {\n return { line: 0, character: 0 };\n }\n const line = this.#lineAtOffset(clampedOffset);\n const lineStart = line === 0 ? 0 : this.#lineBreakOffset(line - 1);\n const character = clampedOffset - lineStart;\n this.#lastPosition = [line, character, clampedOffset];\n return { line, character };\n }\n\n positionsAt(offsets: readonly number[]): Position[] {\n const positions: Position[] = Array.from({ length: offsets.length });\n if (offsets.length === 0) {\n return positions;\n }\n if (this.#length === 0) {\n return positions.fill({ line: 0, character: 0 });\n }\n\n for (let i = 0; i < offsets.length; i++) {\n positions[i] = this.positionAt(offsets[i]);\n }\n\n return positions;\n }\n\n offsetAt(position: Position): number {\n if (position.line < 0 || this.#length === 0) {\n return 0;\n }\n if (position.line >= this.#lineCount) {\n throw new Error(`Line index out of range: ${position.line}`);\n }\n const lastPosition = this.#lastPosition;\n if (\n lastPosition !== null &&\n lastPosition[0] === position.line &&\n lastPosition[1] === position.character\n ) {\n return lastPosition[2];\n }\n const offset = this.#getLineOffset(position.line);\n if (offset === undefined) {\n throw new Error(`Line index out of range: ${position.line}`);\n }\n const character = clamp(position.character, 0, offset[1] - offset[0]);\n return offset[0] + character;\n }\n\n #findPieceAtOffset(\n offset: number\n ): [node: PieceNode, offsetInPiece: number] | undefined {\n if (offset < 0 || offset >= this.#length) {\n return undefined;\n }\n\n let node = this.#root;\n let remaining = offset;\n while (node !== null) {\n const leftLength = node.left?.subtreeLength ?? 0;\n if (remaining < leftLength) {\n node = node.left;\n continue;\n }\n\n remaining -= leftLength;\n if (remaining < node.piece.length) {\n return [node, remaining];\n }\n\n remaining -= node.piece.length;\n node = node.right;\n }\n\n return undefined;\n }\n\n #nextNode(node: PieceNode): PieceNode | null {\n if (node.right !== null) {\n let next = node.right;\n while (next.left !== null) {\n next = next.left;\n }\n return next;\n }\n\n let current = node;\n while (current.parent !== null && current === current.parent.right) {\n current = current.parent;\n }\n return current.parent;\n }\n\n #getLineOffset(line: number): [start: number, end: number] | undefined {\n if (line < 0) {\n throw new Error(`Line index out of range: ${line}`);\n }\n if (this.#length === 0) {\n if (line === 0) {\n return [0, 0];\n }\n throw new Error(`Line index out of range: ${line}`);\n }\n if (line >= this.#lineCount) {\n throw new Error(`Line index out of range: ${line}`);\n }\n\n const start = line === 0 ? 0 : this.#lineBreakOffset(line - 1);\n const end =\n line < this.#lineCount - 1 ? this.#lineBreakOffset(line) : this.#length;\n return [start, end];\n }\n\n #lineAtOffset(offset: number): number {\n let node = this.#root;\n let remaining = clamp(offset, 0, this.#length);\n let line = 0;\n\n while (node !== null) {\n const leftLength = node.left?.subtreeLength ?? 0;\n if (remaining < leftLength) {\n node = node.left;\n continue;\n }\n\n line +=\n (node.left?.subtreeLineBreakCount ?? 0) -\n (formsCRLF(node.left?.subtreeLastCharCode, node.piece.firstCharCode)\n ? 1\n : 0);\n remaining -= leftLength;\n if (remaining <= node.piece.length) {\n const buffer = this.#bufferFor(node.piece.source);\n const lineOffsetEnd = Math.min(\n upperBound(buffer.lineOffsets, node.piece.offset + remaining),\n node.piece.lineOffsetEnd\n );\n line += lineOffsetEnd - node.piece.lineOffsetStart;\n if (\n remaining === node.piece.length &&\n node.piece.hasVirtualTrailingCRBreak\n ) {\n line++;\n }\n if (\n remaining === node.piece.length &&\n formsCRLF(\n node.piece.lastCharCode,\n this.#nextNode(node)?.piece.firstCharCode\n )\n ) {\n line--;\n }\n return line;\n }\n\n line +=\n node.piece.lineBreakCount -\n (formsCRLF(node.piece.lastCharCode, node.right?.subtreeFirstCharCode)\n ? 1\n : 0);\n remaining -= node.piece.length;\n node = node.right;\n }\n\n return this.#lineCount - 1;\n }\n\n #lineBreakOffset(lineBreakIndex: number): number {\n let node = this.#root;\n let remaining = lineBreakIndex;\n let documentOffset = 0;\n let followingCharCode: number | undefined;\n\n while (node !== null) {\n const leftLineBreakCount =\n (node.left?.subtreeLineBreakCount ?? 0) -\n (formsCRLF(node.left?.subtreeLastCharCode, node.piece.firstCharCode)\n ? 1\n : 0);\n if (remaining < leftLineBreakCount) {\n followingCharCode = node.piece.firstCharCode;\n node = node.left;\n continue;\n }\n\n const leftLength = node.left?.subtreeLength ?? 0;\n documentOffset += leftLength;\n remaining -= leftLineBreakCount;\n\n const nextCharCode =\n node.right?.subtreeFirstCharCode ?? followingCharCode;\n const pieceLineBreakCount =\n node.piece.lineBreakCount -\n (formsCRLF(node.piece.lastCharCode, nextCharCode) ? 1 : 0);\n if (remaining < pieceLineBreakCount) {\n return (\n documentOffset + this.#pieceLineBreakOffset(node.piece, remaining)\n );\n }\n\n documentOffset += node.piece.length;\n remaining -= pieceLineBreakCount;\n node = node.right;\n }\n\n return this.#length;\n }\n\n #pieceLineBreakOffset(piece: Piece, lineBreakIndex: number): number {\n const bufferLineBreakCount = piece.lineOffsetEnd - piece.lineOffsetStart;\n if (lineBreakIndex < bufferLineBreakCount) {\n const bufferLineOffset = this.#bufferFor(piece.source).lineOffsets[\n piece.lineOffsetStart + lineBreakIndex\n ];\n return bufferLineOffset - piece.offset;\n }\n return piece.length;\n }\n\n #textFromPieces(): string {\n const chunks: string[] = [];\n this.#forEachPieceSegment((segment) => {\n chunks.push(segment.text.slice(segment.start, segment.end));\n });\n return chunks.join('');\n }\n\n #forEachPieceSegment(\n callback: (segment: {\n readonly start: number;\n readonly end: number;\n readonly text: string;\n readonly lineOffsets: number[];\n readonly lineOffsetStart: number;\n readonly lineOffsetEnd: number;\n }) => boolean | void\n ): void {\n this.#walk(this.#root, (node) => {\n const buffer = this.#bufferFor(node.piece.source);\n return callback({\n text: buffer.text,\n lineOffsets: buffer.lineOffsets,\n lineOffsetStart: node.piece.lineOffsetStart,\n lineOffsetEnd: node.piece.lineOffsetEnd,\n start: node.piece.offset,\n end: node.piece.offset + node.piece.length,\n });\n });\n }\n\n #bufferFor(source: number): TextBuffer {\n return source === Piece.Original ? this.#original : this.#add;\n }\n\n #createPiece(source: number, offset: number, length: number): Piece {\n const buffer = this.#bufferFor(source);\n const end = offset + length;\n const lineOffsetStart = upperBound(buffer.lineOffsets, offset);\n const lineOffsetEnd = upperBound(buffer.lineOffsets, end);\n const lastCharCode = buffer.text.charCodeAt(end - 1);\n return new Piece(\n source,\n offset,\n length,\n lineOffsetStart,\n lineOffsetEnd,\n lastCharCode === CARRIAGE_RETURN &&\n buffer.lineOffsets[lineOffsetEnd - 1] !== end,\n buffer.text.charCodeAt(offset),\n lastCharCode\n );\n }\n\n // Replaces document range [start, end) with `text` by splitting at the start,\n // dropping the deleted prefix from the remainder, and joining the inserted\n // piece back in. Each tree operation touches only one root-to-leaf path, so\n // the edit is O(log P) instead of rebuilding all P pieces. `start`/`end` must\n // be clamped into [0, length] with start <= end.\n #replaceRangeIncremental(start: number, end: number, text: string): void {\n if (start === end && text.length === 0) {\n return;\n }\n\n const [left, rest] = this.#split(this.#root, start);\n const right = this.#dropPrefix(rest, end - start);\n\n let root: PieceNode | null;\n if (text.length > 0) {\n const insertedPiece = this.#createPiece(\n Piece.Added,\n this.#add.append(text),\n text.length\n );\n // The inserted text is fresh at the end of the add buffer, so it can only\n // sit next to the piece on its left, never the one on its right.\n root = this.#mergeNodes(\n this.#appendCoalescing(left, insertedPiece),\n right\n );\n } else {\n // Pure deletion: the pieces now meeting at the seam may be contiguous in\n // the same buffer, so re-attach the right side's first piece through the\n // coalescing append to merge them when possible.\n const [seamPiece, restRight] = this.#popLeftmost(right);\n root =\n seamPiece === undefined\n ? left\n : this.#mergeNodes(\n this.#appendCoalescing(left, seamPiece),\n restRight\n );\n }\n\n if (root !== null) {\n root.parent = null;\n }\n this.#root = root;\n this.#length = root?.subtreeLength ?? 0;\n this.#lineCount = (root?.subtreeLineBreakCount ?? 0) + 1;\n }\n\n // Public mutations clear read caches once, including multi-edit batches.\n #invalidateCaches(): void {\n this.#lastVisitedLine = null;\n this.#lastVisitedLineLength = null;\n this.#lastPosition = null;\n }\n\n #nextPriority(): number {\n this.#priorityState =\n (Math.imul(this.#priorityState, 1664525) + 1013904223) >>> 0;\n return this.#priorityState;\n }\n\n #createNode(piece: Piece): PieceNode {\n const node = new PieceNode(piece);\n node.priority = this.#nextPriority();\n return node;\n }\n\n // Attaches `child` as a side of `node`, keeping the parent pointer in sync so\n // read-side iteration (#nextNode) can still walk back up the tree.\n #setLeft(node: PieceNode, child: PieceNode | null): void {\n node.left = child;\n if (child !== null) {\n child.parent = node;\n }\n }\n\n #setRight(node: PieceNode, child: PieceNode | null): void {\n node.right = child;\n if (child !== null) {\n child.parent = node;\n }\n }\n\n // Splits the subtree into [0, offset) and [offset, length). When the offset\n // falls inside a piece, that piece is sliced in two so the split lands on a\n // clean boundary. Internal parent pointers stay correct; the caller fixes the\n // returned roots' own parents when it reattaches or stores them.\n #split(\n node: PieceNode | null,\n offset: number\n ): [left: PieceNode | null, right: PieceNode | null] {\n if (node === null) {\n return [null, null];\n }\n if (offset <= 0) {\n return [null, node];\n }\n if (offset >= node.subtreeLength) {\n return [node, null];\n }\n\n const leftLength = node.left?.subtreeLength ?? 0;\n if (offset <= leftLength) {\n const [left, right] = this.#split(node.left, offset);\n this.#setLeft(node, right);\n node.updateSubtreeLength();\n return [left, node];\n }\n\n const pieceLength = node.piece.length;\n if (offset >= leftLength + pieceLength) {\n const [left, right] = this.#split(\n node.right,\n offset - leftLength - pieceLength\n );\n this.#setRight(node, left);\n node.updateSubtreeLength();\n return [node, right];\n }\n\n // The offset is strictly inside this node's piece: slice it in two. Both\n // halves inherit this node's priority, which keeps the heap valid because\n // this node already outranked both of its children.\n const inPiece = offset - leftLength;\n const leftNode = new PieceNode(\n this.#createPiece(node.piece.source, node.piece.offset, inPiece)\n );\n const rightNode = new PieceNode(\n this.#createPiece(\n node.piece.source,\n node.piece.offset + inPiece,\n pieceLength - inPiece\n )\n );\n leftNode.priority = node.priority;\n rightNode.priority = node.priority;\n this.#setLeft(leftNode, node.left);\n this.#setRight(rightNode, node.right);\n leftNode.updateSubtreeLength();\n rightNode.updateSubtreeLength();\n return [leftNode, rightNode];\n }\n\n // Discards [0, offset), allocating only the surviving half of a cut piece.\n #dropPrefix(node: PieceNode | null, offset: number): PieceNode | null {\n if (node === null || offset >= node.subtreeLength) {\n return null;\n }\n if (offset <= 0) {\n return node;\n }\n\n const leftLength = node.left?.subtreeLength ?? 0;\n if (offset <= leftLength) {\n this.#setLeft(node, this.#dropPrefix(node.left, offset));\n node.updateSubtreeLength();\n return node;\n }\n\n const pieceEnd = leftLength + node.piece.length;\n if (offset >= pieceEnd) {\n return this.#dropPrefix(node.right, offset - pieceEnd);\n }\n\n const inPiece = offset - leftLength;\n const rightNode = new PieceNode(\n this.#createPiece(\n node.piece.source,\n node.piece.offset + inPiece,\n node.piece.length - inPiece\n )\n );\n rightNode.priority = node.priority;\n this.#setRight(rightNode, node.right);\n rightNode.updateSubtreeLength();\n return rightNode;\n }\n\n // Joins two subtrees where every offset in `left` precedes every offset in\n // `right`, taking the higher-priority root so the result stays balanced.\n #mergeNodes(\n left: PieceNode | null,\n right: PieceNode | null\n ): PieceNode | null {\n if (left === null) {\n return right;\n }\n if (right === null) {\n return left;\n }\n if (left.priority >= right.priority) {\n this.#setRight(left, this.#mergeNodes(left.right, right));\n left.updateSubtreeLength();\n return left;\n }\n this.#setLeft(right, this.#mergeNodes(left, right.left));\n right.updateSubtreeLength();\n return right;\n }\n\n // Appends `piece` after every node in `tree`. Compatible adjacent pieces in\n // the same buffer are merged in place, keeping sequential typing compact.\n #appendCoalescing(tree: PieceNode | null, piece: Piece): PieceNode {\n if (tree === null) {\n return this.#createNode(piece);\n }\n let last = tree;\n while (last.right !== null) {\n last = last.right;\n }\n if (canCoalescePieces(last.piece, piece)) {\n last.piece = coalesceTwoPieces(last.piece, piece);\n // The last piece grew, so refresh aggregates from it up to the root.\n for (let node: PieceNode | null = last; node !== null; ) {\n node.updateSubtreeLength();\n if (node === tree) {\n break;\n }\n node = node.parent;\n }\n return tree;\n }\n // `tree` is non-null here, so merging in the new node always yields a node.\n return this.#mergeNodes(tree, this.#createNode(piece)) as PieceNode;\n }\n\n // Removes and returns the first piece of the tree along with the remaining\n // tree, so a deletion seam can re-merge the piece that becomes adjacent to\n // the one on the other side of the deletion.\n #popLeftmost(\n tree: PieceNode | null\n ): [piece: Piece | undefined, rest: PieceNode | null] {\n if (tree === null) {\n return [undefined, null];\n }\n if (tree.left === null) {\n return [tree.piece, tree.right];\n }\n const [piece, newLeft] = this.#popLeftmost(tree.left);\n this.#setLeft(tree, newLeft);\n tree.updateSubtreeLength();\n return [piece, tree];\n }\n\n #walk(\n node: PieceNode | null,\n visit: (node: PieceNode) => boolean | void\n ): boolean {\n if (node === null) {\n return true;\n }\n if (!this.#walk(node.left, visit)) {\n return false;\n }\n if (visit(node) === false) {\n return false;\n }\n return this.#walk(node.right, visit);\n }\n}\n\nfunction isEOL(charCode: number): boolean {\n return charCode === /* \\n */ 10 || charCode === /* \\r */ 13;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction createPrefixTable(text: string): number[] {\n const table = Array.from<number>({ length: text.length }).fill(0);\n let matched = 0;\n for (let i = 1; i < text.length; i++) {\n const charCode = text.charCodeAt(i);\n while (matched > 0 && charCode !== text.charCodeAt(matched)) {\n matched = table[matched - 1];\n }\n if (charCode === text.charCodeAt(matched)) {\n matched++;\n }\n table[i] = matched;\n }\n return table;\n}\n\nfunction normalizeRanges(\n ranges: readonly [start: number, end: number][],\n length: number\n): [start: number, end: number][] {\n const normalized: [start: number, end: number][] = [];\n for (const [rawStart, rawEnd] of ranges) {\n const start = clamp(rawStart, 0, length);\n const end = clamp(rawEnd, start, length);\n if (start < end) {\n normalized.push([start, end]);\n }\n }\n normalized.sort((a, b) => a[0] - b[0]);\n\n const merged: [start: number, end: number][] = [];\n for (const range of normalized) {\n const previous = merged[merged.length - 1];\n if (previous !== undefined && range[0] <= previous[1]) {\n previous[1] = Math.max(previous[1], range[1]);\n continue;\n }\n merged.push(range);\n }\n return merged;\n}\n\nfunction rangeOverlaps(\n ranges: readonly [start: number, end: number][],\n start: number,\n end: number\n): boolean {\n let low = 0;\n let high = ranges.length;\n while (low < high) {\n const mid = low + Math.floor((high - low) / 2);\n if (ranges[mid][1] <= start) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n\n const range = ranges[low];\n return range !== undefined && range[0] < end;\n}\n\n// CRLF seams stay explicit because separately appended buffer chunks may each\n// record a break there; the tree aggregate reconciles the document-level pair.\nfunction canCoalescePieces(prev: Piece, next: Piece): boolean {\n return (\n prev.source === next.source &&\n prev.offset + prev.length === next.offset &&\n !formsCRLF(prev.lastCharCode, next.firstCharCode)\n );\n}\n\n// Joins two adjacent pieces (see canCoalescePieces) into one. Keeping the table\n// compact after every edit is what stops the piece count from growing without\n// bound during normal typing.\nfunction coalesceTwoPieces(prev: Piece, next: Piece): Piece {\n return new Piece(\n prev.source,\n prev.offset,\n prev.length + next.length,\n prev.lineOffsetStart,\n next.lineOffsetEnd,\n next.hasVirtualTrailingCRBreak,\n prev.firstCharCode,\n next.lastCharCode\n );\n}\n\nfunction formsCRLF(\n leftCharCode: number | undefined,\n rightCharCode: number | undefined\n): boolean {\n return leftCharCode === CARRIAGE_RETURN && rightCharCode === LINE_FEED;\n}\n\n// Returns the index of the first element in the array that is greater than the target.\nfunction upperBound(values: number[], target: number): number {\n let lo = 0;\n let hi = values.length;\n while (lo < hi) {\n const mid = lo + Math.floor((hi - lo) / 2);\n if (values[mid] <= target) {\n lo = mid + 1;\n } else {\n hi = mid;\n }\n }\n return lo;\n}\n\nfunction escapeRegExp(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction isWordSeparatorCharCode(charCode: number): boolean {\n if (charCode <= 32 || charCode === 127) {\n return true;\n }\n const ch = String.fromCharCode(charCode);\n return WORD_SEPARATORS.includes(ch);\n}\n\n// Checks if the given text is a whole word by checking if the\n// characters before and after are word separators.\nfunction isWholeWordAtDocOffsets(\n text: string,\n docStart: number,\n length: number\n): boolean {\n const beforeOk =\n docStart <= 0 || isWordSeparatorCharCode(text.charCodeAt(docStart - 1));\n const afterOk =\n docStart + length >= text.length ||\n isWordSeparatorCharCode(text.charCodeAt(docStart + length));\n return beforeOk && afterOk;\n}\n\nfunction compileSearchRegExp(\n source: string,\n isRegex: boolean,\n caseSensitive: boolean\n): RegExp {\n const body = isRegex ? source : escapeRegExp(source);\n const flags = `g${caseSensitive ? '' : 'i'}${isRegex ? 'm' : ''}`;\n return new RegExp(body, flags);\n}\n\n/** Expands `$&`, `$1`, `$$`, etc. in a regex replace string using a match. */\nfunction expandReplaceString(\n replacement: string,\n match: RegExpExecArray\n): string {\n return replacement.replace(/\\$([$&]|\\d+)/g, (_token, group: string) => {\n if (group === '$') {\n return '$';\n }\n if (group === '&') {\n return match[0] ?? '';\n }\n const index = Number(group);\n return match[index] ?? '';\n });\n}\n\n/**\n * Builds the text to insert for one search match, including regex capture\n * substitution when regex mode is enabled.\n */\nexport function buildSearchReplacementText(\n positionAt: (offset: number) => Position,\n offsetAt: (position: Position) => number,\n getLineText: (line: number) => string,\n searchParams: SearchParams,\n matchStart: number,\n matchEnd: number\n): string {\n if (!searchParams.regex) {\n return searchParams.replaceText;\n }\n\n const position = positionAt(matchStart);\n const lineText = getLineText(position.line);\n const lineStart = offsetAt({ line: position.line, character: 0 });\n const relStart = matchStart - lineStart;\n\n let pattern: RegExp;\n try {\n pattern = compileSearchRegExp(\n searchParams.text,\n true,\n searchParams.caseSensitive\n );\n } catch {\n return searchParams.replaceText;\n }\n\n // Re-run at the original line offset so lookaround can inspect context\n // outside the matched range while captures still come from this exact hit.\n pattern.lastIndex = relStart;\n const match = pattern.exec(lineText);\n if (\n match === null ||\n match.index !== relStart ||\n match[0].length !== matchEnd - matchStart\n ) {\n return searchParams.replaceText;\n }\n return expandReplaceString(searchParams.replaceText, match);\n}\n\nfunction advancePastEmptyMatch(text: string, index: number): number {\n if (index + 1 < text.length) {\n const first = text.charCodeAt(index);\n const second = text.charCodeAt(index + 1);\n if (\n first >= 0xd800 &&\n first <= 0xdbff &&\n second >= 0xdc00 &&\n second <= 0xdfff\n ) {\n return index + 2;\n }\n }\n return index + 1;\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ResolvedTextEdit } from "
|
|
1
|
+
import { ResolvedTextEdit } from "./types.js";
|
|
2
2
|
import { TextDocument } from "./textDocument.js";
|
|
3
3
|
|
|
4
4
|
//#region src/editor/searchPanel.d.ts
|
|
@@ -12,7 +12,7 @@ interface SearchParams {
|
|
|
12
12
|
regex: boolean;
|
|
13
13
|
}
|
|
14
14
|
interface SearchPanelOptions {
|
|
15
|
-
textDocument: TextDocument
|
|
15
|
+
textDocument: TextDocument;
|
|
16
16
|
containerElement: HTMLElement;
|
|
17
17
|
defaultQuery: string;
|
|
18
18
|
mode?: SearchPanelMode;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { buildSearchReplacementText } from "./pieceTable.js";
|
|
1
2
|
import { isPrimaryModifier } from "./platform.js";
|
|
2
3
|
import { resolveFindAgainShortcut } from "./command.js";
|
|
3
4
|
import { h } from "./utils.js";
|
|
4
|
-
import { buildSearchReplacementText } from "./pieceTable.js";
|
|
5
5
|
import { getEditorIconSvg } from "./sprite.js";
|
|
6
6
|
//#region src/editor/searchPanel.ts
|
|
7
7
|
var SearchPanelWidget = class {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"searchPanel.js","names":["#updateMatches","#navigate","#close","#inputElement","#applyMode","#container"],"sources":["../../src/editor/searchPanel.ts"],"sourcesContent":["import type { ResolvedTextEdit } from '../types';\nimport { resolveFindAgainShortcut } from './command';\nimport { buildSearchReplacementText } from './pieceTable';\nimport { isPrimaryModifier } from './platform';\nimport { getEditorIconSvg, type SVGSpriteNames } from './sprite';\nimport type { TextDocument } from './textDocument';\nimport { h } from './utils';\n\nexport type MatchRange = [startOffset: number, endOffset: number];\n\nexport type SearchPanelMode = 'find' | 'replace';\n\nexport interface SearchParams {\n text: string;\n replaceText: string;\n caseSensitive: boolean;\n wholeWord: boolean;\n regex: boolean;\n}\n\nexport interface SearchPanelOptions {\n textDocument: TextDocument<unknown>;\n containerElement: HTMLElement;\n defaultQuery: string;\n mode?: SearchPanelMode;\n initialMatch?: MatchRange;\n scrollToMatch: (nextMatch: MatchRange, retainFocus: boolean) => void;\n applyReplace: (edits: ResolvedTextEdit[]) => void;\n onUpdate: (\n matches: MatchRange[],\n options?: { syncSelection?: boolean }\n ) => MatchRange | undefined;\n onClose: () => void;\n}\n\nexport class SearchPanelWidget {\n #container: HTMLDivElement;\n #inputElement: HTMLInputElement;\n #updateMatches?: (options?: { syncSelection?: boolean }) => void;\n #applyMode?: (mode: SearchPanelMode) => void;\n #navigate?: (findPrevious: boolean) => void;\n #close?: () => void;\n\n constructor(options: SearchPanelOptions) {\n const {\n textDocument,\n containerElement,\n defaultQuery,\n mode = 'find',\n initialMatch,\n scrollToMatch,\n applyReplace,\n onUpdate,\n onClose,\n } = options;\n\n const searchParams: SearchParams = {\n text: defaultQuery,\n replaceText: '',\n caseSensitive: false,\n wholeWord: false,\n regex: false,\n };\n\n const matches = {\n all: [] as MatchRange[],\n current: undefined as MatchRange | undefined,\n };\n\n // Default to the empty-query \"no results\" state so it shows on open before\n // any search runs.\n const matchResultElement = h('div', {\n dataset: { matches: '', noMatches: '' },\n textContent: 'No results',\n });\n const updateMatches = (options?: { syncSelection?: boolean }) => {\n matches.all =\n searchParams.text !== '' ? textDocument.search(searchParams) : [];\n const noMatches = matches.all.length === 0;\n prevButton.disabled = noMatches;\n nextButton.disabled = noMatches;\n\n // An empty query and a query with zero hits are the same \"no results\"\n // state. Both push an empty match set through onUpdate so the editor\n // drops #matches and clears the painted highlights.\n if (noMatches) {\n matchResultElement.textContent = 'No results';\n matchResultElement.dataset.noMatches = '';\n matches.current = undefined;\n onUpdate([]);\n return;\n }\n\n delete matchResultElement.dataset.noMatches;\n if (options?.syncSelection === false) {\n const currentMatch = onUpdate(matches.all, { syncSelection: false });\n updateCurrentMatch(currentMatch);\n return;\n }\n\n updateCurrentMatch(onUpdate(matches.all));\n };\n this.#updateMatches = updateMatches;\n\n const updateCurrentMatch = (currentMatch: MatchRange | undefined) => {\n if (currentMatch === undefined) {\n matchResultElement.textContent = `${matches.all.length} results`;\n } else {\n const [start, end] = currentMatch;\n const index = matches.all.findIndex(\n (m) => m[0] === start && m[1] === end\n );\n matchResultElement.textContent = `${index + 1} of ${matches.all.length}`;\n }\n matches.current = currentMatch;\n };\n\n const updateSearchParam = <K extends keyof SearchParams>(\n key: K,\n value: SearchParams[K]\n ) => {\n searchParams[key] = value;\n updateMatches();\n };\n\n const findNextMatch = (\n findPrevious: boolean = false,\n retainFocus: boolean = false\n ) => {\n const allMatches = matches.all;\n let nextMatch: MatchRange | undefined = allMatches[0];\n if (allMatches.length > 0) {\n if (findPrevious) {\n const searchOffset = matches.current?.[0] ?? 0;\n nextMatch = allMatches.at(-1);\n for (const m of allMatches) {\n if (m[1] <= searchOffset) {\n nextMatch = m;\n } else {\n break;\n }\n }\n } else {\n const searchOffset = matches.current?.[1] ?? 0;\n for (const m of allMatches) {\n if (m[0] >= searchOffset) {\n nextMatch = m;\n break;\n }\n }\n }\n }\n if (nextMatch !== undefined) {\n updateCurrentMatch(nextMatch);\n scrollToMatch(nextMatch, retainFocus);\n }\n matches.current = nextMatch;\n };\n // Keep focus in the panel when stepping through matches from the inputs.\n this.#navigate = (findPrevious: boolean) =>\n findNextMatch(findPrevious, true);\n\n const buildReplacementEdit = (\n matchStart: number,\n matchEnd: number\n ): ResolvedTextEdit => ({\n start: matchStart,\n end: matchEnd,\n text: buildSearchReplacementText(\n (offset) => textDocument.positionAt(offset),\n (position) => textDocument.offsetAt(position),\n (line) => textDocument.getLineText(line),\n searchParams,\n matchStart,\n matchEnd\n ),\n });\n\n const replace = () => {\n if (searchParams.text === '' || matches.all.length === 0) {\n return;\n }\n\n let currentMatch = matches.current;\n if (currentMatch === undefined) {\n findNextMatch(false, true);\n currentMatch = matches.current;\n if (currentMatch === undefined) {\n return;\n }\n }\n\n const [start, end] = currentMatch;\n const edit = buildReplacementEdit(start, end);\n applyReplace([edit]);\n\n // Collapse after the replacement so the next search pass advances.\n scrollToMatch([start + edit.text.length, start + edit.text.length], true);\n matches.current = undefined;\n updateMatches();\n };\n\n const replaceAll = () => {\n if (searchParams.text === '' || matches.all.length === 0) {\n return;\n }\n\n applyReplace(\n matches.all.map(([start, end]) => buildReplacementEdit(start, end))\n );\n matches.current = undefined;\n updateMatches();\n };\n\n const close = () => {\n this.cleanup();\n onClose();\n };\n this.#close = close;\n\n // Shared builder for the panel's icon controls. Every clickable control is a\n // real <button> so it gets focus, keyboard activation, and native disabled\n // handling for free; `getEditorIconSvg` marks the glyph aria-hidden, so the\n // accessible name comes from `label` (also used as the hover title).\n const iconButton = (opts: {\n icon: SVGSpriteNames;\n label: string;\n size?: number;\n dataset?: Record<string, string>;\n onClick: () => void;\n }) =>\n h('button', {\n type: 'button',\n title: opts.label,\n ariaLabel: opts.label,\n dataset: { searchIcon: '', ...opts.dataset },\n innerHTML: getEditorIconSvg(opts.icon, opts.size ?? 16),\n onclick: opts.onClick,\n });\n\n // Builds an always-visible icon button that toggles one boolean search\n // option (case/whole-word/regex). The button reflects its on/off state via\n // `aria-pressed` so the stylesheet can highlight it and assistive tech can\n // announce it.\n const makeToggle = (\n icon: SVGSpriteNames,\n title: string,\n key: 'caseSensitive' | 'wholeWord' | 'regex'\n ) => {\n const button = iconButton({\n icon,\n label: title,\n size: 14,\n onClick: () => {\n const next = !searchParams[key];\n button.ariaPressed = String(next);\n updateSearchParam(key, next);\n },\n });\n button.ariaPressed = String(searchParams[key]);\n return button;\n };\n\n const caseSensitiveToggle = makeToggle(\n 'case',\n 'Match Case',\n 'caseSensitive'\n );\n const wholeWordToggle = makeToggle('whole-word', 'Whole Word', 'wholeWord');\n const regexToggle = makeToggle('regex', 'Regexp', 'regex');\n\n const replaceInputElement = h('input', {\n type: 'text',\n placeholder: 'Replace',\n dataset: 'replace',\n value: '',\n oninput: (e: Event) => {\n searchParams.replaceText = (e.target as HTMLInputElement).value;\n },\n onkeydown: (e: KeyboardEvent) => {\n if (e.isComposing || e.keyCode === 229) {\n return;\n }\n const findAgain = resolveFindAgainShortcut(e);\n if (e.key === 'Escape') {\n e.preventDefault();\n close();\n } else if (e.key === 'Enter') {\n e.preventDefault();\n replace();\n } else if (findAgain !== undefined) {\n e.preventDefault();\n findNextMatch(findAgain === 'previous', true);\n }\n },\n });\n\n this.#inputElement = h('input', {\n type: 'text',\n placeholder: 'Search',\n dataset: 'search',\n value: defaultQuery,\n oninput: (e: Event) => {\n searchParams.text = (e.target as HTMLInputElement).value;\n matches.current = undefined;\n updateMatches();\n },\n onkeydown: (e: KeyboardEvent) => {\n if (e.isComposing || e.keyCode === 229) {\n return;\n }\n const findAgain = resolveFindAgainShortcut(e);\n if (e.key === 'Escape') {\n e.preventDefault();\n close();\n } else if (e.key === 'Enter') {\n e.preventDefault();\n findNextMatch(e.shiftKey, true);\n } else if (findAgain !== undefined) {\n // Override the native browser find-again shortcut so cmd+g steps to\n // the next match and cmd+shift+g to the previous one.\n e.preventDefault();\n findNextMatch(findAgain === 'previous', true);\n } else if (\n isPrimaryModifier(e) &&\n (e.key === 'f' || e.code === 'KeyF')\n ) {\n // Prevent the default browser search panel and switch the panel mode\n // in place (cmd+f -> find, cmd+opt+f -> find/replace).\n e.preventDefault();\n applyMode(e.altKey ? 'replace' : 'find');\n }\n },\n });\n\n // The three search-option toggles are overlaid on the right edge of the\n // find input instead of sitting beside it.\n const searchTogglesElement = h('div', {\n dataset: 'searchToggles',\n children: [caseSensitiveToggle, wholeWordToggle, regexToggle],\n });\n\n const findInputBox = h('div', {\n dataset: { inputBox: '', find: '' },\n children: [this.#inputElement, searchTogglesElement],\n });\n\n // The replace input and its action buttons are tagged as replace cells so\n // they can be hidden together when the panel is in find-only mode.\n const replaceInputBox = h('div', {\n dataset: { inputBox: '', replace: '', replaceCell: '' },\n children: [replaceInputElement],\n });\n\n const replaceActionsElement = h('div', {\n dataset: { replaceActions: '', replaceCell: '' },\n children: [\n iconButton({ icon: 'replace', label: 'Replace', onClick: replace }),\n iconButton({\n icon: 'replace-all',\n label: 'Replace All',\n onClick: replaceAll,\n }),\n ],\n });\n\n // Held so the no-results state can toggle their native disabled flag.\n const prevButton = iconButton({\n icon: 'arrow-up',\n label: 'Previous',\n onClick: () => {\n findNextMatch(true);\n },\n });\n const nextButton = iconButton({\n icon: 'arrow-down',\n label: 'Next',\n onClick: () => {\n findNextMatch();\n },\n });\n prevButton.disabled = true;\n nextButton.disabled = true;\n\n const navElement = h('div', {\n dataset: 'searchNav',\n children: [prevButton, nextButton],\n });\n\n const closeElement = iconButton({\n icon: 'close',\n label: 'Close',\n onClick: close,\n dataset: { searchClose: '' },\n });\n\n // Cells are positioned by CSS grid-template-areas (see editor.css), so DOM\n // order here only drives tab/reading order, not layout. Keep the replace\n // input directly after the find input (and its toggles) so Tab walks\n // find -> replace before reaching the nav arrows and close button.\n const gridElement = h('div', {\n dataset: { searchGrid: '', mode },\n children: [\n findInputBox,\n replaceInputBox,\n replaceActionsElement,\n matchResultElement,\n navElement,\n closeElement,\n ],\n });\n\n // Toggles the panel between find and find/replace modes by showing or\n // hiding the replace cells, then returns focus to the find input.\n const applyMode = (next: SearchPanelMode) => {\n gridElement.dataset.mode = next;\n this.#inputElement.focus();\n this.#inputElement.select();\n };\n this.#applyMode = applyMode;\n\n this.#container = h('div', {\n dataset: 'searchPanel',\n children: [\n h('div', {\n dataset: 'editorWidget',\n children: [gridElement],\n }),\n ],\n });\n\n matches.current = initialMatch;\n containerElement.before(this.#container);\n\n requestAnimationFrame(() => {\n if (initialMatch !== undefined) {\n updateMatches();\n } else {\n onUpdate([]);\n }\n this.#inputElement.select();\n });\n }\n\n focus(): void {\n this.#inputElement.focus();\n }\n\n navigate(findPrevious: boolean): void {\n this.#navigate?.(findPrevious);\n }\n\n updateMatches(options?: { syncSelection?: boolean }): void {\n this.#updateMatches?.(options);\n }\n\n setMode(mode: SearchPanelMode): void {\n this.#applyMode?.(mode);\n }\n\n // Tears the panel down the way the close button and Escape-in-input do:\n // removes the DOM and runs onClose so the editor clears its match highlights.\n // Prefer this over cleanup() for any user-initiated dismissal.\n close(): void {\n this.#close?.();\n }\n\n // DOM-only teardown. Does NOT run onClose, so it leaves the editor's match\n // highlights in place; only use it when the caller clears that state itself\n // (e.g. a full editor reset). User-initiated dismissals should call close().\n cleanup(): void {\n this.#container.remove();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"searchPanel.js","names":["#updateMatches","#navigate","#close","#inputElement","#applyMode","#container"],"sources":["../../src/editor/searchPanel.ts"],"sourcesContent":["import { resolveFindAgainShortcut } from './command';\nimport { buildSearchReplacementText } from './pieceTable';\nimport { isPrimaryModifier } from './platform';\nimport { getEditorIconSvg, type SVGSpriteNames } from './sprite';\nimport type { TextDocument } from './textDocument';\nimport type { ResolvedTextEdit } from './types';\nimport { h } from './utils';\n\nexport type MatchRange = [startOffset: number, endOffset: number];\n\nexport type SearchPanelMode = 'find' | 'replace';\n\nexport interface SearchParams {\n text: string;\n replaceText: string;\n caseSensitive: boolean;\n wholeWord: boolean;\n regex: boolean;\n}\n\nexport interface SearchPanelOptions {\n textDocument: TextDocument;\n containerElement: HTMLElement;\n defaultQuery: string;\n mode?: SearchPanelMode;\n initialMatch?: MatchRange;\n scrollToMatch: (nextMatch: MatchRange, retainFocus: boolean) => void;\n applyReplace: (edits: ResolvedTextEdit[]) => void;\n onUpdate: (\n matches: MatchRange[],\n options?: { syncSelection?: boolean }\n ) => MatchRange | undefined;\n onClose: () => void;\n}\n\nexport class SearchPanelWidget {\n #container: HTMLDivElement;\n #inputElement: HTMLInputElement;\n #updateMatches?: (options?: { syncSelection?: boolean }) => void;\n #applyMode?: (mode: SearchPanelMode) => void;\n #navigate?: (findPrevious: boolean) => void;\n #close?: () => void;\n\n constructor(options: SearchPanelOptions) {\n const {\n textDocument,\n containerElement,\n defaultQuery,\n mode = 'find',\n initialMatch,\n scrollToMatch,\n applyReplace,\n onUpdate,\n onClose,\n } = options;\n\n const searchParams: SearchParams = {\n text: defaultQuery,\n replaceText: '',\n caseSensitive: false,\n wholeWord: false,\n regex: false,\n };\n\n const matches = {\n all: [] as MatchRange[],\n current: undefined as MatchRange | undefined,\n };\n\n // Default to the empty-query \"no results\" state so it shows on open before\n // any search runs.\n const matchResultElement = h('div', {\n dataset: { matches: '', noMatches: '' },\n textContent: 'No results',\n });\n const updateMatches = (options?: { syncSelection?: boolean }) => {\n matches.all =\n searchParams.text !== '' ? textDocument.search(searchParams) : [];\n const noMatches = matches.all.length === 0;\n prevButton.disabled = noMatches;\n nextButton.disabled = noMatches;\n\n // An empty query and a query with zero hits are the same \"no results\"\n // state. Both push an empty match set through onUpdate so the editor\n // drops #matches and clears the painted highlights.\n if (noMatches) {\n matchResultElement.textContent = 'No results';\n matchResultElement.dataset.noMatches = '';\n matches.current = undefined;\n onUpdate([]);\n return;\n }\n\n delete matchResultElement.dataset.noMatches;\n if (options?.syncSelection === false) {\n const currentMatch = onUpdate(matches.all, { syncSelection: false });\n updateCurrentMatch(currentMatch);\n return;\n }\n\n updateCurrentMatch(onUpdate(matches.all));\n };\n this.#updateMatches = updateMatches;\n\n const updateCurrentMatch = (currentMatch: MatchRange | undefined) => {\n if (currentMatch === undefined) {\n matchResultElement.textContent = `${matches.all.length} results`;\n } else {\n const [start, end] = currentMatch;\n const index = matches.all.findIndex(\n (m) => m[0] === start && m[1] === end\n );\n matchResultElement.textContent = `${index + 1} of ${matches.all.length}`;\n }\n matches.current = currentMatch;\n };\n\n const updateSearchParam = <K extends keyof SearchParams>(\n key: K,\n value: SearchParams[K]\n ) => {\n searchParams[key] = value;\n updateMatches();\n };\n\n const findNextMatch = (\n findPrevious: boolean = false,\n retainFocus: boolean = false\n ) => {\n const allMatches = matches.all;\n let nextMatch: MatchRange | undefined = allMatches[0];\n if (allMatches.length > 0) {\n if (findPrevious) {\n const searchOffset = matches.current?.[0] ?? 0;\n nextMatch = allMatches.at(-1);\n for (const m of allMatches) {\n if (m[1] <= searchOffset) {\n nextMatch = m;\n } else {\n break;\n }\n }\n } else {\n const searchOffset = matches.current?.[1] ?? 0;\n for (const m of allMatches) {\n if (m[0] >= searchOffset) {\n nextMatch = m;\n break;\n }\n }\n }\n }\n if (nextMatch !== undefined) {\n updateCurrentMatch(nextMatch);\n scrollToMatch(nextMatch, retainFocus);\n }\n matches.current = nextMatch;\n };\n // Keep focus in the panel when stepping through matches from the inputs.\n this.#navigate = (findPrevious: boolean) =>\n findNextMatch(findPrevious, true);\n\n const buildReplacementEdit = (\n matchStart: number,\n matchEnd: number\n ): ResolvedTextEdit => ({\n start: matchStart,\n end: matchEnd,\n text: buildSearchReplacementText(\n (offset) => textDocument.positionAt(offset),\n (position) => textDocument.offsetAt(position),\n (line) => textDocument.getLineText(line),\n searchParams,\n matchStart,\n matchEnd\n ),\n });\n\n const replace = () => {\n if (searchParams.text === '' || matches.all.length === 0) {\n return;\n }\n\n let currentMatch = matches.current;\n if (currentMatch === undefined) {\n findNextMatch(false, true);\n currentMatch = matches.current;\n if (currentMatch === undefined) {\n return;\n }\n }\n\n const [start, end] = currentMatch;\n const edit = buildReplacementEdit(start, end);\n applyReplace([edit]);\n\n // Collapse after the replacement so the next search pass advances.\n scrollToMatch([start + edit.text.length, start + edit.text.length], true);\n matches.current = undefined;\n updateMatches();\n };\n\n const replaceAll = () => {\n if (searchParams.text === '' || matches.all.length === 0) {\n return;\n }\n\n applyReplace(\n matches.all.map(([start, end]) => buildReplacementEdit(start, end))\n );\n matches.current = undefined;\n updateMatches();\n };\n\n const close = () => {\n this.cleanup();\n onClose();\n };\n this.#close = close;\n\n // Shared builder for the panel's icon controls. Every clickable control is a\n // real <button> so it gets focus, keyboard activation, and native disabled\n // handling for free; `getEditorIconSvg` marks the glyph aria-hidden, so the\n // accessible name comes from `label` (also used as the hover title).\n const iconButton = (opts: {\n icon: SVGSpriteNames;\n label: string;\n size?: number;\n dataset?: Record<string, string>;\n onClick: () => void;\n }) =>\n h('button', {\n type: 'button',\n title: opts.label,\n ariaLabel: opts.label,\n dataset: { searchIcon: '', ...opts.dataset },\n innerHTML: getEditorIconSvg(opts.icon, opts.size ?? 16),\n onclick: opts.onClick,\n });\n\n // Builds an always-visible icon button that toggles one boolean search\n // option (case/whole-word/regex). The button reflects its on/off state via\n // `aria-pressed` so the stylesheet can highlight it and assistive tech can\n // announce it.\n const makeToggle = (\n icon: SVGSpriteNames,\n title: string,\n key: 'caseSensitive' | 'wholeWord' | 'regex'\n ) => {\n const button = iconButton({\n icon,\n label: title,\n size: 14,\n onClick: () => {\n const next = !searchParams[key];\n button.ariaPressed = String(next);\n updateSearchParam(key, next);\n },\n });\n button.ariaPressed = String(searchParams[key]);\n return button;\n };\n\n const caseSensitiveToggle = makeToggle(\n 'case',\n 'Match Case',\n 'caseSensitive'\n );\n const wholeWordToggle = makeToggle('whole-word', 'Whole Word', 'wholeWord');\n const regexToggle = makeToggle('regex', 'Regexp', 'regex');\n\n const replaceInputElement = h('input', {\n type: 'text',\n placeholder: 'Replace',\n dataset: 'replace',\n value: '',\n oninput: (e: Event) => {\n searchParams.replaceText = (e.target as HTMLInputElement).value;\n },\n onkeydown: (e: KeyboardEvent) => {\n if (e.isComposing || e.keyCode === 229) {\n return;\n }\n const findAgain = resolveFindAgainShortcut(e);\n if (e.key === 'Escape') {\n e.preventDefault();\n close();\n } else if (e.key === 'Enter') {\n e.preventDefault();\n replace();\n } else if (findAgain !== undefined) {\n e.preventDefault();\n findNextMatch(findAgain === 'previous', true);\n }\n },\n });\n\n this.#inputElement = h('input', {\n type: 'text',\n placeholder: 'Search',\n dataset: 'search',\n value: defaultQuery,\n oninput: (e: Event) => {\n searchParams.text = (e.target as HTMLInputElement).value;\n matches.current = undefined;\n updateMatches();\n },\n onkeydown: (e: KeyboardEvent) => {\n if (e.isComposing || e.keyCode === 229) {\n return;\n }\n const findAgain = resolveFindAgainShortcut(e);\n if (e.key === 'Escape') {\n e.preventDefault();\n close();\n } else if (e.key === 'Enter') {\n e.preventDefault();\n findNextMatch(e.shiftKey, true);\n } else if (findAgain !== undefined) {\n // Override the native browser find-again shortcut so cmd+g steps to\n // the next match and cmd+shift+g to the previous one.\n e.preventDefault();\n findNextMatch(findAgain === 'previous', true);\n } else if (\n isPrimaryModifier(e) &&\n (e.key === 'f' || e.code === 'KeyF')\n ) {\n // Prevent the default browser search panel and switch the panel mode\n // in place (cmd+f -> find, cmd+opt+f -> find/replace).\n e.preventDefault();\n applyMode(e.altKey ? 'replace' : 'find');\n }\n },\n });\n\n // The three search-option toggles are overlaid on the right edge of the\n // find input instead of sitting beside it.\n const searchTogglesElement = h('div', {\n dataset: 'searchToggles',\n children: [caseSensitiveToggle, wholeWordToggle, regexToggle],\n });\n\n const findInputBox = h('div', {\n dataset: { inputBox: '', find: '' },\n children: [this.#inputElement, searchTogglesElement],\n });\n\n // The replace input and its action buttons are tagged as replace cells so\n // they can be hidden together when the panel is in find-only mode.\n const replaceInputBox = h('div', {\n dataset: { inputBox: '', replace: '', replaceCell: '' },\n children: [replaceInputElement],\n });\n\n const replaceActionsElement = h('div', {\n dataset: { replaceActions: '', replaceCell: '' },\n children: [\n iconButton({ icon: 'replace', label: 'Replace', onClick: replace }),\n iconButton({\n icon: 'replace-all',\n label: 'Replace All',\n onClick: replaceAll,\n }),\n ],\n });\n\n // Held so the no-results state can toggle their native disabled flag.\n const prevButton = iconButton({\n icon: 'arrow-up',\n label: 'Previous',\n onClick: () => {\n findNextMatch(true);\n },\n });\n const nextButton = iconButton({\n icon: 'arrow-down',\n label: 'Next',\n onClick: () => {\n findNextMatch();\n },\n });\n prevButton.disabled = true;\n nextButton.disabled = true;\n\n const navElement = h('div', {\n dataset: 'searchNav',\n children: [prevButton, nextButton],\n });\n\n const closeElement = iconButton({\n icon: 'close',\n label: 'Close',\n onClick: close,\n dataset: { searchClose: '' },\n });\n\n // Cells are positioned by CSS grid-template-areas (see editor.css), so DOM\n // order here only drives tab/reading order, not layout. Keep the replace\n // input directly after the find input (and its toggles) so Tab walks\n // find -> replace before reaching the nav arrows and close button.\n const gridElement = h('div', {\n dataset: { searchGrid: '', mode },\n children: [\n findInputBox,\n replaceInputBox,\n replaceActionsElement,\n matchResultElement,\n navElement,\n closeElement,\n ],\n });\n\n // Toggles the panel between find and find/replace modes by showing or\n // hiding the replace cells, then returns focus to the find input.\n const applyMode = (next: SearchPanelMode) => {\n gridElement.dataset.mode = next;\n this.#inputElement.focus();\n this.#inputElement.select();\n };\n this.#applyMode = applyMode;\n\n this.#container = h('div', {\n dataset: 'searchPanel',\n children: [\n h('div', {\n dataset: 'editorWidget',\n children: [gridElement],\n }),\n ],\n });\n\n matches.current = initialMatch;\n containerElement.before(this.#container);\n\n requestAnimationFrame(() => {\n if (initialMatch !== undefined) {\n updateMatches();\n } else {\n onUpdate([]);\n }\n this.#inputElement.select();\n });\n }\n\n focus(): void {\n this.#inputElement.focus();\n }\n\n navigate(findPrevious: boolean): void {\n this.#navigate?.(findPrevious);\n }\n\n updateMatches(options?: { syncSelection?: boolean }): void {\n this.#updateMatches?.(options);\n }\n\n setMode(mode: SearchPanelMode): void {\n this.#applyMode?.(mode);\n }\n\n // Tears the panel down the way the close button and Escape-in-input do:\n // removes the DOM and runs onClose so the editor clears its match highlights.\n // Prefer this over cleanup() for any user-initiated dismissal.\n close(): void {\n this.#close?.();\n }\n\n // DOM-only teardown. Does NOT run onClose, so it leaves the editor's match\n // highlights in place; only use it when the caller clears that state itself\n // (e.g. a full editor reset). User-initiated dismissals should call close().\n cleanup(): void {\n this.#container.remove();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EditorLineAnnotation, EditorSelection, EditorType, Position, Range, ResolvedTextEdit, SelectionDirection, TextEdit } from "./types.js";
|
|
2
2
|
import { TextDocument, TextDocumentChange } from "./textDocument.js";
|
|
3
3
|
|
|
4
4
|
//#region src/editor/selection.d.ts
|
|
@@ -23,7 +23,7 @@ declare function convertSelection(range: StaticRange, direction?: SelectionDirec
|
|
|
23
23
|
/**
|
|
24
24
|
* Resolves the indent edits for a selection.
|
|
25
25
|
*/
|
|
26
|
-
declare function resolveIndentEdits(textDocument: TextDocument
|
|
26
|
+
declare function resolveIndentEdits(textDocument: TextDocument, selection: EditorSelection, tabSize: number, outdent: boolean): [edits: TextEdit[], nextSelection: EditorSelection];
|
|
27
27
|
/**
|
|
28
28
|
* The nearest zero-based document line at or beyond `line` in `direction`
|
|
29
29
|
* that has a rendered row, or undefined when everything that way is hidden
|
|
@@ -34,16 +34,16 @@ type ResolveRenderableLine = (line: number, direction: 'up' | 'down') => number
|
|
|
34
34
|
/**
|
|
35
35
|
* Maps the cursor move to all selections.
|
|
36
36
|
*/
|
|
37
|
-
declare function mapCursorMove(textDocument: TextDocument
|
|
37
|
+
declare function mapCursorMove(textDocument: TextDocument, selections: EditorSelection[], shortcut: 'textStart' | 'start' | 'end' | 'up' | 'down' | 'left' | 'right', options?: CursorMoveOptions): EditorSelection[];
|
|
38
38
|
declare function snapCharacterToGraphemeBoundary(lineText: string, character: number): number;
|
|
39
39
|
/**
|
|
40
40
|
* Same as mapCursorMove, but with shift key pressed.
|
|
41
41
|
*/
|
|
42
|
-
declare function mapSelectionShift(textDocument: TextDocument
|
|
42
|
+
declare function mapSelectionShift(textDocument: TextDocument, selections: EditorSelection[], shortcut: 'textStart' | 'start' | 'end' | 'up' | 'down' | 'left' | 'right', options?: CursorMoveOptions): EditorSelection[];
|
|
43
43
|
/**
|
|
44
44
|
* Applies a text change to the given text document
|
|
45
45
|
*/
|
|
46
|
-
declare function applyTextChangeToSelections<LAnnotation>(textDocument: TextDocument<LAnnotation>, selections: EditorSelection[], edit: ResolvedTextEdit, lineAnnotations?:
|
|
46
|
+
declare function applyTextChangeToSelections<EType extends EditorType, LAnnotation>(textDocument: TextDocument<EType, LAnnotation>, selections: EditorSelection[], edit: ResolvedTextEdit, lineAnnotations?: EditorLineAnnotation<EType, LAnnotation>[], tabSize?: number, undoBoundary?: boolean): {
|
|
47
47
|
nextSelections: EditorSelection[];
|
|
48
48
|
change?: TextDocumentChange;
|
|
49
49
|
};
|
|
@@ -51,7 +51,7 @@ declare function applyTextChangeToSelections<LAnnotation>(textDocument: TextDocu
|
|
|
51
51
|
* Applies text replacements to multiple selections. Texts pair by selection
|
|
52
52
|
* index unless they are explicitly marked as document ordered.
|
|
53
53
|
*/
|
|
54
|
-
declare function applyTextReplaceToSelections<LAnnotation>(textDocument: TextDocument<LAnnotation>, selections: EditorSelection[], texts: string[], lineAnnotations?:
|
|
54
|
+
declare function applyTextReplaceToSelections<EType extends EditorType, LAnnotation>(textDocument: TextDocument<EType, LAnnotation>, selections: EditorSelection[], texts: string[], lineAnnotations?: EditorLineAnnotation<EType, LAnnotation>[], undoBoundary?: boolean, textOrder?: 'selection' | 'document'): {
|
|
55
55
|
nextSelections: EditorSelection[];
|
|
56
56
|
change?: TextDocumentChange;
|
|
57
57
|
};
|
|
@@ -60,12 +60,12 @@ type AutoSurround = 'default' | 'never' | 'brackets' | 'quotes' | 'languageDefin
|
|
|
60
60
|
* Returns per-selection replacement text when typing a surround character over
|
|
61
61
|
* non-collapsed selections, matching VS Code auto-surround behavior.
|
|
62
62
|
*/
|
|
63
|
-
declare function getAutoSurroundReplacementTexts
|
|
63
|
+
declare function getAutoSurroundReplacementTexts(textDocument: TextDocument, selections: EditorSelection[], char: string, autoSurround?: AutoSurround): string[] | undefined;
|
|
64
64
|
/**
|
|
65
65
|
* Swaps the two characters adjacent to a collapsed selection, matching browser
|
|
66
66
|
* insertTranspose (Ctrl+T) behavior.
|
|
67
67
|
*/
|
|
68
|
-
declare function applyTransposeToSelections<LAnnotation>(textDocument: TextDocument<LAnnotation>, selections: EditorSelection[], lineAnnotations?:
|
|
68
|
+
declare function applyTransposeToSelections<EType extends EditorType, LAnnotation>(textDocument: TextDocument<EType, LAnnotation>, selections: EditorSelection[], lineAnnotations?: EditorLineAnnotation<EType, LAnnotation>[]): {
|
|
69
69
|
nextSelections: EditorSelection[];
|
|
70
70
|
change?: TextDocumentChange;
|
|
71
71
|
};
|
|
@@ -74,7 +74,7 @@ declare function applyTransposeToSelections<LAnnotation>(textDocument: TextDocum
|
|
|
74
74
|
* when the caret is already at the end of a non-final line. Non-collapsed
|
|
75
75
|
* selections delete their selected text instead.
|
|
76
76
|
*/
|
|
77
|
-
declare function applyDeleteHardLineForwardToSelections<LAnnotation>(textDocument: TextDocument<LAnnotation>, selections: EditorSelection[], lineAnnotations?:
|
|
77
|
+
declare function applyDeleteHardLineForwardToSelections<EType extends EditorType, LAnnotation>(textDocument: TextDocument<EType, LAnnotation>, selections: EditorSelection[], lineAnnotations?: EditorLineAnnotation<EType, LAnnotation>[]): {
|
|
78
78
|
nextSelections: EditorSelection[];
|
|
79
79
|
change?: TextDocumentChange;
|
|
80
80
|
};
|
|
@@ -82,7 +82,7 @@ declare function applyDeleteHardLineForwardToSelections<LAnnotation>(textDocumen
|
|
|
82
82
|
* Deletes from each selection back to the start of its soft (visual) line.
|
|
83
83
|
* Non-collapsed selections delete their selected text instead.
|
|
84
84
|
*/
|
|
85
|
-
declare function applyDeleteSoftLineBackwardToSelections<LAnnotation>(textDocument: TextDocument<LAnnotation>, selections: EditorSelection[], getSoftLineStart?: (line: number, character: number) => number, lineAnnotations?:
|
|
85
|
+
declare function applyDeleteSoftLineBackwardToSelections<EType extends EditorType, LAnnotation>(textDocument: TextDocument<EType, LAnnotation>, selections: EditorSelection[], getSoftLineStart?: (line: number, character: number) => number, lineAnnotations?: EditorLineAnnotation<EType, LAnnotation>[]): {
|
|
86
86
|
nextSelections: EditorSelection[];
|
|
87
87
|
change?: TextDocumentChange;
|
|
88
88
|
};
|
|
@@ -90,7 +90,7 @@ declare function applyDeleteSoftLineBackwardToSelections<LAnnotation>(textDocume
|
|
|
90
90
|
* Deletes the word or separator group immediately before each selection.
|
|
91
91
|
* Non-collapsed selections delete their selected text instead.
|
|
92
92
|
*/
|
|
93
|
-
declare function applyDeleteWordBackwardToSelections<LAnnotation>(textDocument: TextDocument<LAnnotation>, selections: EditorSelection[], lineAnnotations?:
|
|
93
|
+
declare function applyDeleteWordBackwardToSelections<EType extends EditorType, LAnnotation>(textDocument: TextDocument<EType, LAnnotation>, selections: EditorSelection[], lineAnnotations?: EditorLineAnnotation<EType, LAnnotation>[]): {
|
|
94
94
|
nextSelections: EditorSelection[];
|
|
95
95
|
change?: TextDocumentChange;
|
|
96
96
|
};
|
|
@@ -98,11 +98,11 @@ declare function applyDeleteWordBackwardToSelections<LAnnotation>(textDocument:
|
|
|
98
98
|
* Resolves the document range deleted by Backspace or Delete at a collapsed
|
|
99
99
|
* caret. Non-collapsed selections delete their selected text instead.
|
|
100
100
|
*/
|
|
101
|
-
declare function resolveDeleteCharacterRange(textDocument: TextDocument
|
|
101
|
+
declare function resolveDeleteCharacterRange(textDocument: TextDocument, selection: EditorSelection, forward: boolean): [start: Position, end: Position];
|
|
102
102
|
/**
|
|
103
103
|
* Deletes one grapheme (or selected text) at each selection.
|
|
104
104
|
*/
|
|
105
|
-
declare function applyDeleteCharacterToSelections<LAnnotation>(textDocument: TextDocument<LAnnotation>, selections: EditorSelection[], forward: boolean, lineAnnotations?:
|
|
105
|
+
declare function applyDeleteCharacterToSelections<EType extends EditorType, LAnnotation>(textDocument: TextDocument<EType, LAnnotation>, selections: EditorSelection[], forward: boolean, lineAnnotations?: EditorLineAnnotation<EType, LAnnotation>[], tabSize?: number): {
|
|
106
106
|
nextSelections: EditorSelection[];
|
|
107
107
|
change?: TextDocumentChange;
|
|
108
108
|
};
|
|
@@ -129,7 +129,16 @@ declare function comparePosition(a: Position, b: Position): number;
|
|
|
129
129
|
/**
|
|
130
130
|
* Creates a selection from anchor and focus offsets.
|
|
131
131
|
*/
|
|
132
|
-
declare function createSelectionFromAnchorAndFocusOffsets(textDocument: TextDocument
|
|
132
|
+
declare function createSelectionFromAnchorAndFocusOffsets(textDocument: TextDocument, anchorOffset: number, focusOffset: number): EditorSelection;
|
|
133
|
+
/**
|
|
134
|
+
* Maps a single offset from the pre-edit document into the post-edit document.
|
|
135
|
+
* `edits` are resolved edits in pre-edit offsets, sorted ascending and
|
|
136
|
+
* non-overlapping. An offset at or after an edit's start shifts to the end of
|
|
137
|
+
* that edit's replacement (right gravity), so text inserted at the caret pushes
|
|
138
|
+
* the caret past it; an offset strictly before an edit is only shifted by the
|
|
139
|
+
* net length change of the edits that precede it.
|
|
140
|
+
*/
|
|
141
|
+
declare function remapOffsetThroughEdits(offset: number, edits: readonly ResolvedTextEdit[]): number;
|
|
133
142
|
/**
|
|
134
143
|
* Re-anchors selections after a batch of text edits has been applied, so the
|
|
135
144
|
* caret keeps pointing at the same logical location in the changed buffer.
|
|
@@ -139,7 +148,7 @@ declare function createSelectionFromAnchorAndFocusOffsets(textDocument: TextDocu
|
|
|
139
148
|
* `textDocument`, which must already reflect the applied edits. Selection
|
|
140
149
|
* direction is preserved by remapping each edge and re-deriving anchor/focus.
|
|
141
150
|
*/
|
|
142
|
-
declare function remapSelectionsAfterEdits(textDocument: TextDocument
|
|
151
|
+
declare function remapSelectionsAfterEdits(textDocument: TextDocument, selections: readonly EditorSelection[], selectionOffsets: ReadonlyArray<readonly [number, number]>, edits: readonly ResolvedTextEdit[]): EditorSelection[];
|
|
143
152
|
/**
|
|
144
153
|
* Creates a selection from a anchor and focus selection.
|
|
145
154
|
*/
|
|
@@ -172,20 +181,20 @@ declare function shiftSelectionLines(selection: EditorSelection, direction: -1 |
|
|
|
172
181
|
/**
|
|
173
182
|
* Finds the next matching word and updates the selections.
|
|
174
183
|
*/
|
|
175
|
-
declare function findNextMatch(textDocument: TextDocument
|
|
184
|
+
declare function findNextMatch(textDocument: TextDocument, selections: EditorSelection[]): EditorSelection[] | undefined;
|
|
176
185
|
/**
|
|
177
186
|
* Get the full selection of the document.
|
|
178
187
|
*/
|
|
179
|
-
declare function getDocumentFullSelection(textDocument: TextDocument
|
|
188
|
+
declare function getDocumentFullSelection(textDocument: TextDocument): EditorSelection;
|
|
180
189
|
/**
|
|
181
190
|
* Get the boundary selection of the document.
|
|
182
191
|
*/
|
|
183
|
-
declare function getDocumentBoundarySelection(textDocument: TextDocument
|
|
192
|
+
declare function getDocumentBoundarySelection(textDocument: TextDocument, atEnd: boolean, trimmedEndNewLine?: boolean): EditorSelection;
|
|
184
193
|
/**
|
|
185
194
|
* Gets the text contributed by each selection in document order, preserving
|
|
186
195
|
* the pairing needed to paste the values into another set of selections.
|
|
187
196
|
*/
|
|
188
|
-
declare function getSelectionClipboardTexts(textDocument: TextDocument
|
|
197
|
+
declare function getSelectionClipboardTexts(textDocument: TextDocument, selections: EditorSelection[]): string[];
|
|
189
198
|
/**
|
|
190
199
|
* Get the clipboard text of the selections for the given text document. Used by
|
|
191
200
|
* both copy and cut so the two stay in sync. Overlapping regions (e.g. several
|
|
@@ -193,8 +202,8 @@ declare function getSelectionClipboardTexts(textDocument: TextDocument<unknown>,
|
|
|
193
202
|
* line-ending separator is inserted only between regions that aren't already
|
|
194
203
|
* contiguous in the document.
|
|
195
204
|
*/
|
|
196
|
-
declare function getSelectionText(textDocument: TextDocument
|
|
197
|
-
declare function resolveSelectionCut(textDocument: TextDocument
|
|
205
|
+
declare function getSelectionText(textDocument: TextDocument, selections: EditorSelection[]): string;
|
|
206
|
+
declare function resolveSelectionCut(textDocument: TextDocument, selections: EditorSelection[]): {
|
|
198
207
|
text: string;
|
|
199
208
|
edits: ResolvedTextEdit[];
|
|
200
209
|
nextSelectionOffsets: number[];
|
|
@@ -206,7 +215,7 @@ declare function getSelectionAnchor(lineElement: HTMLElement, character: number)
|
|
|
206
215
|
/**
|
|
207
216
|
* Expands a zero-width selection to the word-like segment that contains the caret.
|
|
208
217
|
*/
|
|
209
|
-
declare function expandCollapsedSelectionToWord(textDocument: TextDocument
|
|
218
|
+
declare function expandCollapsedSelectionToWord(textDocument: TextDocument, selection: EditorSelection): EditorSelection;
|
|
210
219
|
//#endregion
|
|
211
|
-
export { AutoSurround, CursorMoveOptions, DirectionBackward, DirectionForward, DirectionNone, ResolveRenderableLine, 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, resolveDeleteCharacterRange, resolveIndentEdits, resolveSelectionCut, selectionIntersects, shiftSelectionLines, snapCharacterToGraphemeBoundary };
|
|
220
|
+
export { AutoSurround, CursorMoveOptions, DirectionBackward, DirectionForward, DirectionNone, ResolveRenderableLine, applyDeleteCharacterToSelections, applyDeleteHardLineForwardToSelections, applyDeleteSoftLineBackwardToSelections, applyDeleteWordBackwardToSelections, applyTextChangeToSelections, applyTextReplaceToSelections, applyTransposeToSelections, comparePosition, convertSelection, createSelectionFrom, createSelectionFromAnchorAndFocusOffsets, expandCollapsedSelectionToWord, extendSelection, extendSelections, findNextMatch, getAutoSurroundReplacementTexts, getCaretPosition, getDocumentBoundarySelection, getDocumentFullSelection, getSelectedLineBlocks, getSelectionAnchor, getSelectionClipboardTexts, getSelectionText, isCollapsedSelection, isLineEditable, mapCursorMove, mapSelectionShift, mergeOverlappingSelections, remapOffsetThroughEdits, remapSelectionsAfterEdits, resolveDeleteCharacterRange, resolveIndentEdits, resolveSelectionCut, selectionIntersects, shiftSelectionLines, snapCharacterToGraphemeBoundary };
|
|
212
221
|
//# 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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"}
|