@pierre/diffs 1.2.8 → 1.3.0-beta.2
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 +0 -4
- package/dist/components/CodeView.d.ts.map +1 -1
- package/dist/components/CodeView.js +0 -38
- package/dist/components/CodeView.js.map +1 -1
- package/dist/components/File.d.ts +7 -2
- package/dist/components/File.d.ts.map +1 -1
- package/dist/components/File.js +36 -2
- package/dist/components/File.js.map +1 -1
- package/dist/components/FileDiff.d.ts +8 -2
- package/dist/components/FileDiff.d.ts.map +1 -1
- package/dist/components/FileDiff.js +73 -1
- package/dist/components/FileDiff.js.map +1 -1
- package/dist/components/UnresolvedFile.js +2 -2
- package/dist/components/VirtualizedFile.d.ts +2 -1
- package/dist/components/VirtualizedFile.d.ts.map +1 -1
- package/dist/components/VirtualizedFile.js +48 -48
- package/dist/components/VirtualizedFile.js.map +1 -1
- package/dist/components/VirtualizedFileDiff.js +42 -22
- package/dist/components/VirtualizedFileDiff.js.map +1 -1
- package/dist/components/Virtualizer.d.ts +1 -1
- package/dist/components/Virtualizer.d.ts.map +1 -1
- package/dist/components/Virtualizer.js +3 -5
- package/dist/components/Virtualizer.js.map +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/editor/command.d.ts +6 -0
- package/dist/editor/command.d.ts.map +1 -0
- package/dist/editor/command.js +31 -0
- package/dist/editor/command.js.map +1 -0
- package/dist/editor/css.d.ts +6 -0
- package/dist/editor/css.d.ts.map +1 -0
- package/dist/editor/css.js +218 -0
- package/dist/editor/css.js.map +1 -0
- package/dist/editor/editStack.d.ts +66 -0
- package/dist/editor/editStack.d.ts.map +1 -0
- package/dist/editor/editStack.js +218 -0
- package/dist/editor/editStack.js.map +1 -0
- package/dist/editor/editor.d.ts +22 -0
- package/dist/editor/editor.d.ts.map +1 -0
- package/dist/editor/editor.js +1323 -0
- package/dist/editor/editor.js.map +1 -0
- package/dist/editor/index.d.ts +3 -0
- package/dist/editor/index.js +4 -0
- package/dist/editor/lineAnnotations.d.ts +8 -0
- package/dist/editor/lineAnnotations.d.ts.map +1 -0
- package/dist/editor/lineAnnotations.js +32 -0
- package/dist/editor/lineAnnotations.js.map +1 -0
- package/dist/editor/pieceTable.d.ts +33 -0
- package/dist/editor/pieceTable.d.ts.map +1 -0
- package/dist/editor/pieceTable.js +590 -0
- package/dist/editor/pieceTable.js.map +1 -0
- package/dist/editor/platform.d.ts +12 -0
- package/dist/editor/platform.d.ts.map +1 -0
- package/dist/editor/platform.js +44 -0
- package/dist/editor/platform.js.map +1 -0
- package/dist/editor/quickEdit.d.ts +29 -0
- package/dist/editor/quickEdit.d.ts.map +1 -0
- package/dist/editor/quickEdit.js +81 -0
- package/dist/editor/quickEdit.js.map +1 -0
- package/dist/editor/searchPanel.d.ts +30 -0
- package/dist/editor/searchPanel.d.ts.map +1 -0
- package/dist/editor/searchPanel.js +219 -0
- package/dist/editor/searchPanel.js.map +1 -0
- package/dist/editor/selection.d.ts +126 -0
- package/dist/editor/selection.d.ts.map +1 -0
- package/dist/editor/selection.js +900 -0
- package/dist/editor/selection.js.map +1 -0
- package/dist/editor/textDocument.d.ts +139 -0
- package/dist/editor/textDocument.d.ts.map +1 -0
- package/dist/editor/textDocument.js +202 -0
- package/dist/editor/textDocument.js.map +1 -0
- package/dist/editor/textMeasure.d.ts +32 -0
- package/dist/editor/textMeasure.d.ts.map +1 -0
- package/dist/editor/textMeasure.js +108 -0
- package/dist/editor/textMeasure.js.map +1 -0
- package/dist/editor/tokenzier.d.ts +37 -0
- package/dist/editor/tokenzier.d.ts.map +1 -0
- package/dist/editor/tokenzier.js +348 -0
- package/dist/editor/tokenzier.js.map +1 -0
- package/dist/editor/utils.d.ts +16 -0
- package/dist/editor/utils.d.ts.map +1 -0
- package/dist/editor/utils.js +37 -0
- package/dist/editor/utils.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/react/EditorContext.d.ts +16 -0
- package/dist/react/EditorContext.d.ts.map +1 -0
- package/dist/react/EditorContext.js +26 -0
- package/dist/react/EditorContext.js.map +1 -0
- package/dist/react/File.d.ts +2 -1
- package/dist/react/File.d.ts.map +1 -1
- package/dist/react/File.js +3 -2
- package/dist/react/File.js.map +1 -1
- package/dist/react/FileDiff.d.ts +3 -1
- package/dist/react/FileDiff.d.ts.map +1 -1
- package/dist/react/FileDiff.js +3 -2
- package/dist/react/FileDiff.js.map +1 -1
- package/dist/react/MultiFileDiff.d.ts +3 -1
- package/dist/react/MultiFileDiff.d.ts.map +1 -1
- package/dist/react/MultiFileDiff.js +3 -2
- package/dist/react/MultiFileDiff.js.map +1 -1
- package/dist/react/PatchDiff.d.ts +3 -1
- package/dist/react/PatchDiff.d.ts.map +1 -1
- package/dist/react/PatchDiff.js +3 -2
- package/dist/react/PatchDiff.js.map +1 -1
- package/dist/react/index.d.ts +3 -2
- package/dist/react/index.js +2 -1
- package/dist/react/jsx.d.ts.map +1 -1
- package/dist/react/types.d.ts +1 -0
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/utils/useFileDiffInstance.d.ts +3 -1
- package/dist/react/utils/useFileDiffInstance.d.ts.map +1 -1
- package/dist/react/utils/useFileDiffInstance.js +31 -5
- package/dist/react/utils/useFileDiffInstance.js.map +1 -1
- package/dist/react/utils/useFileInstance.d.ts +4 -1
- package/dist/react/utils/useFileInstance.d.ts.map +1 -1
- package/dist/react/utils/useFileInstance.js +30 -5
- package/dist/react/utils/useFileInstance.js.map +1 -1
- package/dist/renderers/DiffHunksRenderer.d.ts +2 -2
- package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
- package/dist/renderers/DiffHunksRenderer.js +9 -5
- package/dist/renderers/DiffHunksRenderer.js.map +1 -1
- package/dist/renderers/FileRenderer.d.ts +5 -1
- package/dist/renderers/FileRenderer.d.ts.map +1 -1
- package/dist/renderers/FileRenderer.js +108 -41
- package/dist/renderers/FileRenderer.js.map +1 -1
- package/dist/ssr/index.d.ts +2 -2
- package/dist/style.js +1 -1
- package/dist/style.js.map +1 -1
- package/dist/types.d.ts +45 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/cleanLastNewline.js +6 -1
- package/dist/utils/cleanLastNewline.js.map +1 -1
- package/dist/utils/computeEstimatedDiffHeights.js +20 -9
- package/dist/utils/computeEstimatedDiffHeights.js.map +1 -1
- package/dist/utils/computeFileOffsets.d.ts +13 -0
- package/dist/utils/computeFileOffsets.d.ts.map +1 -0
- package/dist/utils/computeFileOffsets.js +33 -0
- package/dist/utils/computeFileOffsets.js.map +1 -0
- package/dist/utils/createTransformerWithState.js +9 -0
- package/dist/utils/createTransformerWithState.js.map +1 -1
- package/dist/utils/iterateOverDiff.js +182 -147
- package/dist/utils/iterateOverDiff.js.map +1 -1
- package/dist/utils/renderDiffWithHighlighter.js +1 -1
- package/dist/utils/renderFileWithHighlighter.js +5 -14
- package/dist/utils/renderFileWithHighlighter.js.map +1 -1
- package/dist/utils/virtualDiffLayout.d.ts +2 -23
- package/dist/utils/virtualDiffLayout.d.ts.map +1 -1
- package/dist/utils/virtualDiffLayout.js +1 -41
- package/dist/utils/virtualDiffLayout.js.map +1 -1
- package/dist/worker/WorkerPoolManager.js +2 -2
- package/dist/worker/WorkerPoolManager.js.map +1 -1
- package/dist/worker/{wasm-BaDzIkIn.js → wasm-D4DU5jgR.js} +2 -2
- package/dist/worker/wasm-D4DU5jgR.js.map +1 -0
- package/dist/worker/worker-portable.js +349 -363
- package/dist/worker/worker-portable.js.map +1 -1
- package/dist/worker/worker.js +222 -243
- package/dist/worker/worker.js.map +1 -1
- package/package.json +10 -3
- package/dist/utils/iterateOverFile.d.ts +0 -50
- package/dist/utils/iterateOverFile.d.ts.map +0 -1
- package/dist/utils/iterateOverFile.js +0 -49
- package/dist/utils/iterateOverFile.js.map +0 -1
- package/dist/worker/wasm-BaDzIkIn.js.map +0 -1
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
import { computeLineOffsets } from "../utils/computeFileOffsets.js";
|
|
2
|
+
|
|
3
|
+
//#region src/editor/pieceTable.ts
|
|
4
|
+
const MAX_FIND_MATCHES = 1e5;
|
|
5
|
+
const WORD_SEPARATORS = "`~!@#$%^&*()-=+[{]}\\|;:'\",.<>/?";
|
|
6
|
+
var Piece = class {
|
|
7
|
+
static Original = 0;
|
|
8
|
+
static Added = 1;
|
|
9
|
+
constructor(source, offset, length, lineOffsetStart, lineOffsetEnd) {
|
|
10
|
+
this.source = source;
|
|
11
|
+
this.offset = offset;
|
|
12
|
+
this.length = length;
|
|
13
|
+
this.lineOffsetStart = lineOffsetStart;
|
|
14
|
+
this.lineOffsetEnd = lineOffsetEnd;
|
|
15
|
+
}
|
|
16
|
+
get lineBreakCount() {
|
|
17
|
+
return this.lineOffsetEnd - this.lineOffsetStart;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var TextBuffer = class {
|
|
21
|
+
lineOffsets;
|
|
22
|
+
constructor(text) {
|
|
23
|
+
this.text = text;
|
|
24
|
+
this.lineOffsets = computeLineOffsets(text);
|
|
25
|
+
}
|
|
26
|
+
append(text) {
|
|
27
|
+
const offset = this.text.length;
|
|
28
|
+
const appendedLineOffsets = computeLineOffsets(text);
|
|
29
|
+
for (let i = 1; i < appendedLineOffsets.length; i++) this.lineOffsets.push(offset + appendedLineOffsets[i]);
|
|
30
|
+
this.text += text;
|
|
31
|
+
return offset;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var PieceNode = class {
|
|
35
|
+
left = null;
|
|
36
|
+
right = null;
|
|
37
|
+
parent = null;
|
|
38
|
+
constructor(piece, subtreeLength = piece.length, subtreeLineBreakCount = piece.lineBreakCount) {
|
|
39
|
+
this.piece = piece;
|
|
40
|
+
this.subtreeLength = subtreeLength;
|
|
41
|
+
this.subtreeLineBreakCount = subtreeLineBreakCount;
|
|
42
|
+
}
|
|
43
|
+
updateSubtreeLength() {
|
|
44
|
+
this.subtreeLength = (this.left?.subtreeLength ?? 0) + this.piece.length + (this.right?.subtreeLength ?? 0);
|
|
45
|
+
this.subtreeLineBreakCount = (this.left?.subtreeLineBreakCount ?? 0) + this.piece.lineBreakCount + (this.right?.subtreeLineBreakCount ?? 0);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* A piece table is a data structure that allows for efficient insertion and deletion of text.
|
|
50
|
+
* It is a tree of pieces, where each piece is a segment of text that is either original or added.
|
|
51
|
+
* The tree is rebuilt as a balanced tree after edits to keep lookups efficient.
|
|
52
|
+
* Inspired by https://code.visualstudio.com/blogs/2018/03/23/text-buffer-reimplementation
|
|
53
|
+
*/
|
|
54
|
+
var PieceTable = class {
|
|
55
|
+
#original;
|
|
56
|
+
#add = new TextBuffer("");
|
|
57
|
+
#root = null;
|
|
58
|
+
#piecesCache = [];
|
|
59
|
+
#length = 0;
|
|
60
|
+
#lineCount = 0;
|
|
61
|
+
#lastVisitedLine = null;
|
|
62
|
+
constructor(originalText) {
|
|
63
|
+
this.#original = new TextBuffer(originalText);
|
|
64
|
+
this.#setPieces([this.#createPiece(Piece.Original, 0, originalText.length)]);
|
|
65
|
+
}
|
|
66
|
+
get lineCount() {
|
|
67
|
+
return this.#lineCount;
|
|
68
|
+
}
|
|
69
|
+
getText(range) {
|
|
70
|
+
if (range === void 0) return this.#textFromPieces();
|
|
71
|
+
const start = this.offsetAt(range.start);
|
|
72
|
+
const end = this.offsetAt(range.end);
|
|
73
|
+
return this.getTextSlice(start, end);
|
|
74
|
+
}
|
|
75
|
+
getLineText(line, trimEOF = true) {
|
|
76
|
+
if (this.#lastVisitedLine !== null && this.#lastVisitedLine[0] === line) return this.#lastVisitedLine[1];
|
|
77
|
+
const offset = this.#getLineOffset(line);
|
|
78
|
+
if (offset === void 0) throw new Error(`Line index out of range: ${line}`);
|
|
79
|
+
const text = this.getTextSlice(offset[0], offset[1], trimEOF);
|
|
80
|
+
this.#lastVisitedLine = [line, text];
|
|
81
|
+
return text;
|
|
82
|
+
}
|
|
83
|
+
getTextSlice(start, end, trimEOF = false) {
|
|
84
|
+
if (start >= end) return "";
|
|
85
|
+
const sliceStart = clamp(start, 0, this.#length);
|
|
86
|
+
const sliceEnd = clamp(end, sliceStart, this.#length);
|
|
87
|
+
if (sliceStart >= sliceEnd) return "";
|
|
88
|
+
const location = this.#findPieceAtOffset(sliceStart);
|
|
89
|
+
if (location === void 0) return "";
|
|
90
|
+
const chunks = [];
|
|
91
|
+
let [node, offsetInPiece] = location;
|
|
92
|
+
let remaining = sliceEnd - sliceStart;
|
|
93
|
+
while (node !== null && remaining > 0) {
|
|
94
|
+
const takeLength = Math.min(node.piece.length - offsetInPiece, remaining);
|
|
95
|
+
const buffer = this.#bufferFor(node.piece.source);
|
|
96
|
+
const start$1 = node.piece.offset + offsetInPiece;
|
|
97
|
+
let end$1 = start$1 + takeLength;
|
|
98
|
+
if (trimEOF) while (end$1 > start$1 && isEOL(buffer.text.charCodeAt(end$1 - 1))) end$1--;
|
|
99
|
+
chunks.push(buffer.text.slice(start$1, end$1));
|
|
100
|
+
remaining -= takeLength;
|
|
101
|
+
offsetInPiece = 0;
|
|
102
|
+
node = this.#nextNode(node);
|
|
103
|
+
}
|
|
104
|
+
return chunks.join("");
|
|
105
|
+
}
|
|
106
|
+
charAt(offset) {
|
|
107
|
+
const location = this.#findPieceAtOffset(offset);
|
|
108
|
+
if (location === void 0) return "";
|
|
109
|
+
const [node, offsetInPiece] = location;
|
|
110
|
+
return this.#bufferFor(node.piece.source).text.charAt(node.piece.offset + offsetInPiece);
|
|
111
|
+
}
|
|
112
|
+
includes(needle) {
|
|
113
|
+
if (needle.length === 0) return true;
|
|
114
|
+
const prefixTable = createPrefixTable(needle);
|
|
115
|
+
let matched = 0;
|
|
116
|
+
let found = false;
|
|
117
|
+
this.#forEachPieceSegment((segment) => {
|
|
118
|
+
for (let offset = segment.start; offset < segment.end; offset++) {
|
|
119
|
+
const charCode = segment.text.charCodeAt(offset);
|
|
120
|
+
while (matched > 0 && charCode !== needle.charCodeAt(matched)) matched = prefixTable[matched - 1];
|
|
121
|
+
if (charCode === needle.charCodeAt(matched)) matched++;
|
|
122
|
+
if (matched === needle.length) {
|
|
123
|
+
found = true;
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return true;
|
|
128
|
+
});
|
|
129
|
+
return found;
|
|
130
|
+
}
|
|
131
|
+
findNextNonOverlappingSubstring(needle, occupied) {
|
|
132
|
+
if (needle.length === 0 || needle.length > this.#length) return;
|
|
133
|
+
const ranges = normalizeRanges(occupied, this.#length);
|
|
134
|
+
const pivot = ranges.reduce((max, [, end]) => Math.max(max, end), 0);
|
|
135
|
+
const prefixTable = createPrefixTable(needle);
|
|
136
|
+
let matched = 0;
|
|
137
|
+
let documentOffset = 0;
|
|
138
|
+
let wrappedOffset;
|
|
139
|
+
let foundOffset;
|
|
140
|
+
this.#forEachPieceSegment((segment) => {
|
|
141
|
+
for (let offset = segment.start; offset < segment.end; offset++) {
|
|
142
|
+
const charCode = segment.text.charCodeAt(offset);
|
|
143
|
+
while (matched > 0 && charCode !== needle.charCodeAt(matched)) matched = prefixTable[matched - 1];
|
|
144
|
+
if (charCode === needle.charCodeAt(matched)) matched++;
|
|
145
|
+
if (matched === needle.length) {
|
|
146
|
+
const start = documentOffset - needle.length + 1;
|
|
147
|
+
if (!rangeOverlaps(ranges, start, start + needle.length)) {
|
|
148
|
+
if (start >= pivot) {
|
|
149
|
+
foundOffset = start;
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
wrappedOffset ??= start;
|
|
153
|
+
}
|
|
154
|
+
matched = prefixTable[matched - 1];
|
|
155
|
+
}
|
|
156
|
+
documentOffset++;
|
|
157
|
+
}
|
|
158
|
+
return true;
|
|
159
|
+
});
|
|
160
|
+
return foundOffset ?? wrappedOffset;
|
|
161
|
+
}
|
|
162
|
+
search(kind, searchParams, range) {
|
|
163
|
+
if (searchParams.text.length === 0 || this.#length === 0) return [];
|
|
164
|
+
if (searchParams.text.includes("\n") || searchParams.text.includes("\r") || searchParams.regex && (searchParams.text.includes("\\n") || searchParams.text.includes("\\r"))) return [];
|
|
165
|
+
let pattern;
|
|
166
|
+
try {
|
|
167
|
+
pattern = compileSearchRegExp(searchParams.text, searchParams.regex, searchParams.caseSensitive);
|
|
168
|
+
} catch {
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
const matches = this.#collectSearchMatchesLineByLine(pattern, searchParams.wholeWord, MAX_FIND_MATCHES);
|
|
172
|
+
if (kind === "findAll" || kind === "replaceAll") return matches;
|
|
173
|
+
const caretOffset = range === void 0 ? 0 : kind === "findPrevious" ? this.offsetAt(range?.start) : this.offsetAt(range?.end);
|
|
174
|
+
if (kind === "findPrevious") {
|
|
175
|
+
const refOffset = getSearchFindPreviousReferenceOffset(range, (p) => this.offsetAt(p));
|
|
176
|
+
let best;
|
|
177
|
+
for (const m of matches) if (m[1] <= refOffset) best = m;
|
|
178
|
+
else break;
|
|
179
|
+
if (best !== void 0) return [best];
|
|
180
|
+
const last = matches[matches.length - 1];
|
|
181
|
+
return last !== void 0 ? [last] : [];
|
|
182
|
+
}
|
|
183
|
+
for (const m of matches) if (m[0] >= caretOffset) return [m];
|
|
184
|
+
const first = matches[0];
|
|
185
|
+
return first !== void 0 ? [first] : [];
|
|
186
|
+
}
|
|
187
|
+
#collectSearchMatchesLineByLine(pattern, wholeWord, limit) {
|
|
188
|
+
const out = [];
|
|
189
|
+
const docLength = this.#length;
|
|
190
|
+
const charAt = (offset) => this.charAt(offset);
|
|
191
|
+
for (let line = 0; line < this.#lineCount; line++) {
|
|
192
|
+
const lineText = this.getLineText(line);
|
|
193
|
+
const lineStart = this.offsetAt({
|
|
194
|
+
line,
|
|
195
|
+
character: 0
|
|
196
|
+
});
|
|
197
|
+
const re = new RegExp(pattern.source, pattern.flags);
|
|
198
|
+
re.lastIndex = 0;
|
|
199
|
+
let match;
|
|
200
|
+
while ((match = re.exec(lineText)) !== null) {
|
|
201
|
+
const rel = match.index;
|
|
202
|
+
const fragment = match[0];
|
|
203
|
+
if (fragment.length === 0) {
|
|
204
|
+
re.lastIndex = advancePastEmptyMatch(lineText, rel);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const docStart = lineStart + rel;
|
|
208
|
+
if (!wholeWord || isWholeWordAtDocOffsets(docStart, fragment.length, docLength, charAt)) {
|
|
209
|
+
out.push([docStart, docStart + fragment.length]);
|
|
210
|
+
if (out.length >= limit) return out;
|
|
211
|
+
}
|
|
212
|
+
if (rel === re.lastIndex) re.lastIndex = advancePastEmptyMatch(lineText, rel);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return out;
|
|
216
|
+
}
|
|
217
|
+
insert(text, offset) {
|
|
218
|
+
if (text.length === 0) return;
|
|
219
|
+
const insertOffset = clamp(offset, 0, this.#length);
|
|
220
|
+
const addOffset = this.#add.append(text);
|
|
221
|
+
const insertedPiece = this.#createPiece(Piece.Added, addOffset, text.length);
|
|
222
|
+
const pieces = this.#pieces();
|
|
223
|
+
const nextPieces = [];
|
|
224
|
+
let cursor = 0;
|
|
225
|
+
let inserted = false;
|
|
226
|
+
for (const piece of pieces) {
|
|
227
|
+
const pieceEnd = cursor + piece.length;
|
|
228
|
+
if (!inserted && insertOffset <= pieceEnd) {
|
|
229
|
+
const splitOffset = insertOffset - cursor;
|
|
230
|
+
if (splitOffset > 0) nextPieces.push(this.#createPiece(piece.source, piece.offset, splitOffset));
|
|
231
|
+
nextPieces.push(insertedPiece);
|
|
232
|
+
if (splitOffset < piece.length) nextPieces.push(this.#createPiece(piece.source, piece.offset + splitOffset, piece.length - splitOffset));
|
|
233
|
+
inserted = true;
|
|
234
|
+
} else nextPieces.push(piece);
|
|
235
|
+
cursor = pieceEnd;
|
|
236
|
+
}
|
|
237
|
+
if (!inserted) nextPieces.push(insertedPiece);
|
|
238
|
+
this.#setPieces(nextPieces);
|
|
239
|
+
this.#lastVisitedLine = null;
|
|
240
|
+
}
|
|
241
|
+
delete(offset, length) {
|
|
242
|
+
if (length <= 0 || this.#length === 0) return;
|
|
243
|
+
const start = clamp(offset, 0, this.#length);
|
|
244
|
+
const end = clamp(start + length, start, this.#length);
|
|
245
|
+
if (start === end) return;
|
|
246
|
+
const nextPieces = [];
|
|
247
|
+
let cursor = 0;
|
|
248
|
+
for (const piece of this.#pieces()) {
|
|
249
|
+
const pieceStart = cursor;
|
|
250
|
+
const pieceEnd = cursor + piece.length;
|
|
251
|
+
const keepBefore = clamp(start - pieceStart, 0, piece.length);
|
|
252
|
+
const keepAfter = clamp(pieceEnd - end, 0, piece.length);
|
|
253
|
+
if (keepBefore > 0) nextPieces.push(this.#createPiece(piece.source, piece.offset, keepBefore));
|
|
254
|
+
if (keepAfter > 0) nextPieces.push(this.#createPiece(piece.source, piece.offset + piece.length - keepAfter, keepAfter));
|
|
255
|
+
cursor = pieceEnd;
|
|
256
|
+
}
|
|
257
|
+
this.#setPieces(nextPieces);
|
|
258
|
+
this.#lastVisitedLine = null;
|
|
259
|
+
}
|
|
260
|
+
applyEdits(edits) {
|
|
261
|
+
if (edits.length === 0) return;
|
|
262
|
+
let pieceIndex = 0;
|
|
263
|
+
let pieceStart = 0;
|
|
264
|
+
let copyCursor = 0;
|
|
265
|
+
const pieces = this.#pieces();
|
|
266
|
+
const insertedPieces = edits.map((edit) => edit.text.length === 0 ? void 0 : this.#createPiece(Piece.Added, this.#add.append(edit.text), edit.text.length));
|
|
267
|
+
const nextPieces = [];
|
|
268
|
+
const advancePiece = () => {
|
|
269
|
+
const piece = pieces[pieceIndex];
|
|
270
|
+
if (piece !== void 0) {
|
|
271
|
+
pieceStart += piece.length;
|
|
272
|
+
pieceIndex++;
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
const appendRange = (start, end) => {
|
|
276
|
+
let rangeStart = clamp(start, 0, this.#length);
|
|
277
|
+
const rangeEnd = clamp(end, rangeStart, this.#length);
|
|
278
|
+
while (pieceIndex < pieces.length && pieceStart + pieces[pieceIndex].length <= rangeStart) advancePiece();
|
|
279
|
+
while (pieceIndex < pieces.length && rangeStart < rangeEnd) {
|
|
280
|
+
const piece = pieces[pieceIndex];
|
|
281
|
+
const pieceEnd = pieceStart + piece.length;
|
|
282
|
+
const offsetInPiece = clamp(rangeStart - pieceStart, 0, piece.length);
|
|
283
|
+
const takeEnd = Math.min(pieceEnd, rangeEnd);
|
|
284
|
+
const takeLength = takeEnd - (pieceStart + offsetInPiece);
|
|
285
|
+
if (takeLength > 0) nextPieces.push(offsetInPiece === 0 && takeLength === piece.length ? piece : this.#createPiece(piece.source, piece.offset + offsetInPiece, takeLength));
|
|
286
|
+
rangeStart = takeEnd;
|
|
287
|
+
if (rangeStart >= pieceEnd) advancePiece();
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
for (let i = 0; i < edits.length; i++) {
|
|
291
|
+
const edit = edits[i];
|
|
292
|
+
const start = clamp(edit.start, copyCursor, this.#length);
|
|
293
|
+
const end = clamp(edit.end, start, this.#length);
|
|
294
|
+
appendRange(copyCursor, start);
|
|
295
|
+
const insertedPiece = insertedPieces[i];
|
|
296
|
+
if (insertedPiece !== void 0) nextPieces.push(insertedPiece);
|
|
297
|
+
copyCursor = end;
|
|
298
|
+
}
|
|
299
|
+
appendRange(copyCursor, this.#length);
|
|
300
|
+
this.#setPieces(nextPieces);
|
|
301
|
+
this.#lastVisitedLine = null;
|
|
302
|
+
}
|
|
303
|
+
positionAt(offset) {
|
|
304
|
+
const clampedOffset = clamp(offset, 0, this.#length);
|
|
305
|
+
if (this.#length === 0) return {
|
|
306
|
+
line: 0,
|
|
307
|
+
character: 0
|
|
308
|
+
};
|
|
309
|
+
const line = this.#lineAtOffset(clampedOffset);
|
|
310
|
+
return {
|
|
311
|
+
line,
|
|
312
|
+
character: clampedOffset - (line === 0 ? 0 : this.#lineBreakOffset(line - 1))
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
positionsAt(offsets) {
|
|
316
|
+
const positions = Array.from({ length: offsets.length });
|
|
317
|
+
if (offsets.length === 0) return positions;
|
|
318
|
+
if (this.#length === 0) return positions.fill({
|
|
319
|
+
line: 0,
|
|
320
|
+
character: 0
|
|
321
|
+
});
|
|
322
|
+
for (let i = 0; i < offsets.length; i++) positions[i] = this.positionAt(offsets[i]);
|
|
323
|
+
return positions;
|
|
324
|
+
}
|
|
325
|
+
offsetAt(position) {
|
|
326
|
+
if (position.line < 0 || this.#length === 0) return 0;
|
|
327
|
+
if (position.line >= this.#lineCount) throw new Error(`Line index out of range: ${position.line}`);
|
|
328
|
+
const offset = this.#getLineOffset(position.line);
|
|
329
|
+
if (offset === void 0) throw new Error(`Line index out of range: ${position.line}`);
|
|
330
|
+
const character = clamp(position.character, 0, offset[1] - offset[0]);
|
|
331
|
+
return offset[0] + character;
|
|
332
|
+
}
|
|
333
|
+
offsetsAt(positions) {
|
|
334
|
+
const offsets = Array.from({ length: positions.length });
|
|
335
|
+
if (positions.length === 0) return offsets;
|
|
336
|
+
if (this.#length === 0) return offsets.fill(0);
|
|
337
|
+
for (let i = 0; i < positions.length; i++) offsets[i] = this.offsetAt(positions[i]);
|
|
338
|
+
return offsets;
|
|
339
|
+
}
|
|
340
|
+
#findPieceAtOffset(offset) {
|
|
341
|
+
if (offset < 0 || offset >= this.#length) return;
|
|
342
|
+
let node = this.#root;
|
|
343
|
+
let remaining = offset;
|
|
344
|
+
while (node !== null) {
|
|
345
|
+
const leftLength = node.left?.subtreeLength ?? 0;
|
|
346
|
+
if (remaining < leftLength) {
|
|
347
|
+
node = node.left;
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
remaining -= leftLength;
|
|
351
|
+
if (remaining < node.piece.length) return [node, remaining];
|
|
352
|
+
remaining -= node.piece.length;
|
|
353
|
+
node = node.right;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
#nextNode(node) {
|
|
357
|
+
if (node.right !== null) {
|
|
358
|
+
let next = node.right;
|
|
359
|
+
while (next.left !== null) next = next.left;
|
|
360
|
+
return next;
|
|
361
|
+
}
|
|
362
|
+
let current = node;
|
|
363
|
+
while (current.parent !== null && current === current.parent.right) current = current.parent;
|
|
364
|
+
return current.parent;
|
|
365
|
+
}
|
|
366
|
+
#getLineOffset(line) {
|
|
367
|
+
if (line < 0) throw new Error(`Line index out of range: ${line}`);
|
|
368
|
+
if (this.#length === 0) {
|
|
369
|
+
if (line === 0) return [0, 0];
|
|
370
|
+
throw new Error(`Line index out of range: ${line}`);
|
|
371
|
+
}
|
|
372
|
+
if (line >= this.#lineCount) throw new Error(`Line index out of range: ${line}`);
|
|
373
|
+
return [line === 0 ? 0 : this.#lineBreakOffset(line - 1), line < this.#lineCount - 1 ? this.#lineBreakOffset(line) : this.#length];
|
|
374
|
+
}
|
|
375
|
+
#lineAtOffset(offset) {
|
|
376
|
+
let node = this.#root;
|
|
377
|
+
let remaining = clamp(offset, 0, this.#length);
|
|
378
|
+
let line = 0;
|
|
379
|
+
while (node !== null) {
|
|
380
|
+
const leftLength = node.left?.subtreeLength ?? 0;
|
|
381
|
+
if (remaining < leftLength) {
|
|
382
|
+
node = node.left;
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
line += node.left?.subtreeLineBreakCount ?? 0;
|
|
386
|
+
remaining -= leftLength;
|
|
387
|
+
if (remaining <= node.piece.length) {
|
|
388
|
+
const buffer = this.#bufferFor(node.piece.source);
|
|
389
|
+
line += upperBound(buffer.lineOffsets, node.piece.offset + remaining) - node.piece.lineOffsetStart;
|
|
390
|
+
return line;
|
|
391
|
+
}
|
|
392
|
+
line += node.piece.lineBreakCount;
|
|
393
|
+
remaining -= node.piece.length;
|
|
394
|
+
node = node.right;
|
|
395
|
+
}
|
|
396
|
+
return this.#lineCount - 1;
|
|
397
|
+
}
|
|
398
|
+
#lineBreakOffset(lineBreakIndex) {
|
|
399
|
+
let node = this.#root;
|
|
400
|
+
let remaining = lineBreakIndex;
|
|
401
|
+
let documentOffset = 0;
|
|
402
|
+
while (node !== null) {
|
|
403
|
+
const leftLineBreakCount = node.left?.subtreeLineBreakCount ?? 0;
|
|
404
|
+
if (remaining < leftLineBreakCount) {
|
|
405
|
+
node = node.left;
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
const leftLength = node.left?.subtreeLength ?? 0;
|
|
409
|
+
documentOffset += leftLength;
|
|
410
|
+
remaining -= leftLineBreakCount;
|
|
411
|
+
if (remaining < node.piece.lineBreakCount) {
|
|
412
|
+
const bufferLineOffset = this.#bufferFor(node.piece.source).lineOffsets[node.piece.lineOffsetStart + remaining];
|
|
413
|
+
return documentOffset + (bufferLineOffset - node.piece.offset);
|
|
414
|
+
}
|
|
415
|
+
documentOffset += node.piece.length;
|
|
416
|
+
remaining -= node.piece.lineBreakCount;
|
|
417
|
+
node = node.right;
|
|
418
|
+
}
|
|
419
|
+
return this.#length;
|
|
420
|
+
}
|
|
421
|
+
#textFromPieces() {
|
|
422
|
+
const chunks = [];
|
|
423
|
+
this.#forEachPieceSegment((segment) => {
|
|
424
|
+
chunks.push(segment.text.slice(segment.start, segment.end));
|
|
425
|
+
});
|
|
426
|
+
return chunks.join("");
|
|
427
|
+
}
|
|
428
|
+
#forEachPieceSegment(callback) {
|
|
429
|
+
this.#walk(this.#root, (node) => {
|
|
430
|
+
const buffer = this.#bufferFor(node.piece.source);
|
|
431
|
+
return callback({
|
|
432
|
+
text: buffer.text,
|
|
433
|
+
lineOffsets: buffer.lineOffsets,
|
|
434
|
+
lineOffsetStart: node.piece.lineOffsetStart,
|
|
435
|
+
lineOffsetEnd: node.piece.lineOffsetEnd,
|
|
436
|
+
start: node.piece.offset,
|
|
437
|
+
end: node.piece.offset + node.piece.length
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
#bufferFor(source) {
|
|
442
|
+
return source === Piece.Original ? this.#original : this.#add;
|
|
443
|
+
}
|
|
444
|
+
#createPiece(source, offset, length) {
|
|
445
|
+
const buffer = this.#bufferFor(source);
|
|
446
|
+
return new Piece(source, offset, length, upperBound(buffer.lineOffsets, offset), upperBound(buffer.lineOffsets, offset + length));
|
|
447
|
+
}
|
|
448
|
+
#pieces() {
|
|
449
|
+
return this.#piecesCache;
|
|
450
|
+
}
|
|
451
|
+
#setPieces(pieces) {
|
|
452
|
+
const coalescedPieces = coalescePieces(pieces);
|
|
453
|
+
this.#piecesCache = coalescedPieces;
|
|
454
|
+
let length = 0;
|
|
455
|
+
let lineBreakCount = 0;
|
|
456
|
+
for (const piece of coalescedPieces) {
|
|
457
|
+
length += piece.length;
|
|
458
|
+
lineBreakCount += piece.lineBreakCount;
|
|
459
|
+
}
|
|
460
|
+
this.#root = this.#buildBalancedTree(coalescedPieces, 0, coalescedPieces.length, null);
|
|
461
|
+
this.#length = length;
|
|
462
|
+
this.#lineCount = lineBreakCount + 1;
|
|
463
|
+
}
|
|
464
|
+
#buildBalancedTree(pieces, start, end, parent) {
|
|
465
|
+
if (start >= end) return null;
|
|
466
|
+
const middle = start + Math.floor((end - start) / 2);
|
|
467
|
+
const node = new PieceNode(pieces[middle]);
|
|
468
|
+
node.parent = parent;
|
|
469
|
+
node.left = this.#buildBalancedTree(pieces, start, middle, node);
|
|
470
|
+
node.right = this.#buildBalancedTree(pieces, middle + 1, end, node);
|
|
471
|
+
node.updateSubtreeLength();
|
|
472
|
+
return node;
|
|
473
|
+
}
|
|
474
|
+
#walk(node, visit) {
|
|
475
|
+
if (node === null) return true;
|
|
476
|
+
if (!this.#walk(node.left, visit)) return false;
|
|
477
|
+
if (visit(node) === false) return false;
|
|
478
|
+
return this.#walk(node.right, visit);
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
function isEOL(charCode) {
|
|
482
|
+
return charCode === 10 || charCode === 13;
|
|
483
|
+
}
|
|
484
|
+
function clamp(value, min, max) {
|
|
485
|
+
return Math.min(Math.max(value, min), max);
|
|
486
|
+
}
|
|
487
|
+
function createPrefixTable(text) {
|
|
488
|
+
const table = Array.from({ length: text.length }).fill(0);
|
|
489
|
+
let matched = 0;
|
|
490
|
+
for (let i = 1; i < text.length; i++) {
|
|
491
|
+
const charCode = text.charCodeAt(i);
|
|
492
|
+
while (matched > 0 && charCode !== text.charCodeAt(matched)) matched = table[matched - 1];
|
|
493
|
+
if (charCode === text.charCodeAt(matched)) matched++;
|
|
494
|
+
table[i] = matched;
|
|
495
|
+
}
|
|
496
|
+
return table;
|
|
497
|
+
}
|
|
498
|
+
function normalizeRanges(ranges, length) {
|
|
499
|
+
const normalized = [];
|
|
500
|
+
for (const [rawStart, rawEnd] of ranges) {
|
|
501
|
+
const start = clamp(rawStart, 0, length);
|
|
502
|
+
const end = clamp(rawEnd, start, length);
|
|
503
|
+
if (start < end) normalized.push([start, end]);
|
|
504
|
+
}
|
|
505
|
+
normalized.sort((a, b) => a[0] - b[0]);
|
|
506
|
+
const merged = [];
|
|
507
|
+
for (const range of normalized) {
|
|
508
|
+
const previous = merged[merged.length - 1];
|
|
509
|
+
if (previous !== void 0 && range[0] <= previous[1]) {
|
|
510
|
+
previous[1] = Math.max(previous[1], range[1]);
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
merged.push(range);
|
|
514
|
+
}
|
|
515
|
+
return merged;
|
|
516
|
+
}
|
|
517
|
+
function rangeOverlaps(ranges, start, end) {
|
|
518
|
+
let low = 0;
|
|
519
|
+
let high = ranges.length;
|
|
520
|
+
while (low < high) {
|
|
521
|
+
const mid = low + Math.floor((high - low) / 2);
|
|
522
|
+
if (ranges[mid][1] <= start) low = mid + 1;
|
|
523
|
+
else high = mid;
|
|
524
|
+
}
|
|
525
|
+
const range = ranges[low];
|
|
526
|
+
return range !== void 0 && range[0] < end;
|
|
527
|
+
}
|
|
528
|
+
function coalescePieces(pieces) {
|
|
529
|
+
const coalescedPieces = [];
|
|
530
|
+
for (const piece of pieces) {
|
|
531
|
+
if (piece.length === 0) continue;
|
|
532
|
+
const previous = coalescedPieces[coalescedPieces.length - 1];
|
|
533
|
+
if (previous !== void 0 && previous.source === piece.source && previous.offset + previous.length === piece.offset) {
|
|
534
|
+
coalescedPieces[coalescedPieces.length - 1] = new Piece(previous.source, previous.offset, previous.length + piece.length, previous.lineOffsetStart, piece.lineOffsetEnd);
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
coalescedPieces.push(piece);
|
|
538
|
+
}
|
|
539
|
+
return coalescedPieces;
|
|
540
|
+
}
|
|
541
|
+
function upperBound(values, target) {
|
|
542
|
+
let lo = 0;
|
|
543
|
+
let hi = values.length;
|
|
544
|
+
while (lo < hi) {
|
|
545
|
+
const mid = lo + Math.floor((hi - lo) / 2);
|
|
546
|
+
if (values[mid] <= target) lo = mid + 1;
|
|
547
|
+
else hi = mid;
|
|
548
|
+
}
|
|
549
|
+
return lo;
|
|
550
|
+
}
|
|
551
|
+
function escapeRegExp(text) {
|
|
552
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
553
|
+
}
|
|
554
|
+
function isWordSeparatorCharCode(charCode) {
|
|
555
|
+
if (charCode <= 32 || charCode === 127) return true;
|
|
556
|
+
const ch = String.fromCharCode(charCode);
|
|
557
|
+
return WORD_SEPARATORS.includes(ch);
|
|
558
|
+
}
|
|
559
|
+
function isWholeWordAtDocOffsets(docStart, length, docLength, charAt) {
|
|
560
|
+
const beforeOk = docStart <= 0 || isWordSeparatorCharCode(charCodeUnitAt(charAt, docStart - 1));
|
|
561
|
+
const afterOk = docStart + length >= docLength || isWordSeparatorCharCode(charCodeUnitAt(charAt, docStart + length));
|
|
562
|
+
return beforeOk && afterOk;
|
|
563
|
+
}
|
|
564
|
+
function charCodeUnitAt(charAt, offset) {
|
|
565
|
+
const unit = charAt(offset);
|
|
566
|
+
return unit.length === 0 ? 0 : unit.charCodeAt(0);
|
|
567
|
+
}
|
|
568
|
+
function compileSearchRegExp(source, isRegex, caseSensitive) {
|
|
569
|
+
const body = isRegex ? source : escapeRegExp(source);
|
|
570
|
+
const flags = `g${caseSensitive ? "" : "i"}${isRegex ? "m" : ""}`;
|
|
571
|
+
return new RegExp(body, flags);
|
|
572
|
+
}
|
|
573
|
+
function advancePastEmptyMatch(text, index) {
|
|
574
|
+
if (index + 1 < text.length) {
|
|
575
|
+
const first = text.charCodeAt(index);
|
|
576
|
+
const second = text.charCodeAt(index + 1);
|
|
577
|
+
if (first >= 55296 && first <= 56319 && second >= 56320 && second <= 57343) return index + 2;
|
|
578
|
+
}
|
|
579
|
+
return index + 1;
|
|
580
|
+
}
|
|
581
|
+
function getSearchFindPreviousReferenceOffset(selection, offsetAt) {
|
|
582
|
+
if (selection === void 0) return 0;
|
|
583
|
+
const a = offsetAt(selection.start);
|
|
584
|
+
const b = offsetAt(selection.end);
|
|
585
|
+
return Math.min(a, b);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
//#endregion
|
|
589
|
+
export { PieceTable };
|
|
590
|
+
//# sourceMappingURL=pieceTable.js.map
|