@jerryan/pi-hashline-edit 0.7.4 → 0.8.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/LICENSE +21 -21
- package/README.md +6 -4
- package/index.ts +6 -0
- package/package.json +53 -53
- package/src/edit-diff.ts +201 -390
- package/src/edit.ts +81 -65
- package/src/file-kind.ts +130 -130
- package/src/fs-write.ts +76 -76
- package/src/hashline.ts +699 -1071
- package/src/package-info.ts +1 -1
- package/src/path-utils.ts +13 -13
- package/src/read.ts +3 -3
- package/src/runtime.ts +3 -3
- package/src/snapshot.ts +29 -29
- package/src/undo.ts +212 -0
- package/tool-descriptions/edit.md +23 -23
- package/tool-descriptions/read-guidelines.md +1 -1
- package/tool-descriptions/read.md +5 -5
- package/tool-descriptions/undo.md +8 -0
package/src/edit-diff.ts
CHANGED
|
@@ -1,390 +1,201 @@
|
|
|
1
|
-
import * as Diff from "diff";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
export function
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const line
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
let out = content;
|
|
204
|
-
for (let i = spans.length - 1; i >= 0; i--) {
|
|
205
|
-
const span = spans[i]!;
|
|
206
|
-
out =
|
|
207
|
-
out.substring(0, span.index) +
|
|
208
|
-
normalizedNew +
|
|
209
|
-
out.substring(span.index + span.matchLength);
|
|
210
|
-
}
|
|
211
|
-
return { content: out, count: spans.length };
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
const result = fuzzyFindText(content, oldText);
|
|
215
|
-
if (!result.found) return { content, count: 0 };
|
|
216
|
-
|
|
217
|
-
return {
|
|
218
|
-
content:
|
|
219
|
-
content.substring(0, result.index) +
|
|
220
|
-
normalizedNew +
|
|
221
|
-
content.substring(result.index + result.matchLength),
|
|
222
|
-
count: 1,
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// ─── Diff generation ────────────────────────────────────────────────────
|
|
227
|
-
|
|
228
|
-
export function generateDiffString(
|
|
229
|
-
oldContent: string,
|
|
230
|
-
newContent: string,
|
|
231
|
-
contextLines = 4,
|
|
232
|
-
): { diff: string } {
|
|
233
|
-
const patch = Diff.structuredPatch("a", "b", oldContent, newContent, undefined, undefined, {
|
|
234
|
-
context: contextLines,
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
if (!patch.hunks.length) {
|
|
238
|
-
return { diff: "" };
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const maxLineNum = Math.max(
|
|
242
|
-
oldContent.split("\n").length,
|
|
243
|
-
newContent.split("\n").length,
|
|
244
|
-
);
|
|
245
|
-
const lineNumWidth = String(maxLineNum).length;
|
|
246
|
-
const hashPad = " ".repeat(ANCHOR_SEP.length + 2); // align with `${ANCHOR_SEP}HH${CONTENT_SEP}`
|
|
247
|
-
const output: string[] = [];
|
|
248
|
-
|
|
249
|
-
for (let h = 0; h < patch.hunks.length; h++) {
|
|
250
|
-
const hunk = patch.hunks[h]!;
|
|
251
|
-
if (h > 0) {
|
|
252
|
-
output.push(" ...");
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
let oldLineNum = hunk.oldStart;
|
|
256
|
-
let newLineNum = hunk.newStart;
|
|
257
|
-
|
|
258
|
-
for (const line of hunk.lines) {
|
|
259
|
-
if (line === "\") continue;
|
|
260
|
-
|
|
261
|
-
const prefix = line[0] as " " | "+" | "-";
|
|
262
|
-
const text = line.slice(1);
|
|
263
|
-
|
|
264
|
-
if (prefix === "-") {
|
|
265
|
-
const padded = String(oldLineNum).padStart(lineNumWidth, " ");
|
|
266
|
-
output.push(`-${padded}${hashPad}${CONTENT_SEP}${text}`);
|
|
267
|
-
oldLineNum++;
|
|
268
|
-
} else if (prefix === "+") {
|
|
269
|
-
const padded = String(newLineNum).padStart(lineNumWidth, " ");
|
|
270
|
-
const hash = computeLineHash(newLineNum, text);
|
|
271
|
-
output.push(`+${padded}${ANCHOR_SEP}${hash}${CONTENT_SEP}${text}`);
|
|
272
|
-
newLineNum++;
|
|
273
|
-
} else {
|
|
274
|
-
const padded = String(newLineNum).padStart(lineNumWidth, " ");
|
|
275
|
-
const hash = computeLineHash(newLineNum, text);
|
|
276
|
-
output.push(` ${padded}${ANCHOR_SEP}${hash}${CONTENT_SEP}${text}`);
|
|
277
|
-
oldLineNum++;
|
|
278
|
-
newLineNum++;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return { diff: output.join("\n") };
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
export interface CompactHashlineDiffPreview {
|
|
287
|
-
preview: string;
|
|
288
|
-
addedLines: number;
|
|
289
|
-
removedLines: number;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
type DiffPreviewKind = "context" | "addition" | "deletion";
|
|
293
|
-
|
|
294
|
-
function classifyDiffPreviewLine(line: string): DiffPreviewKind | null {
|
|
295
|
-
if (line.startsWith("+")) return "addition";
|
|
296
|
-
if (line.startsWith("-")) return "deletion";
|
|
297
|
-
if (line.startsWith(" ")) return "context";
|
|
298
|
-
return null;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
function summarizeOmitted(count: number, label: string): string {
|
|
302
|
-
return `... ${count} more ${label} line${count === 1 ? "" : "s"}`;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
function collapseDiffPreviewRun(
|
|
306
|
-
lines: string[],
|
|
307
|
-
maxVisible: number,
|
|
308
|
-
label: string,
|
|
309
|
-
): string[] {
|
|
310
|
-
if (lines.length <= maxVisible) {
|
|
311
|
-
return lines;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
return [
|
|
315
|
-
...lines.slice(0, maxVisible),
|
|
316
|
-
summarizeOmitted(lines.length - maxVisible, label),
|
|
317
|
-
];
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
export function buildCompactHashlineDiffPreview(
|
|
321
|
-
diff: string,
|
|
322
|
-
options: {
|
|
323
|
-
maxUnchangedRun?: number;
|
|
324
|
-
maxAdditionRun?: number;
|
|
325
|
-
maxDeletionRun?: number;
|
|
326
|
-
maxOutputLines?: number;
|
|
327
|
-
} = {},
|
|
328
|
-
): CompactHashlineDiffPreview {
|
|
329
|
-
const {
|
|
330
|
-
maxUnchangedRun = 2,
|
|
331
|
-
maxAdditionRun = 4,
|
|
332
|
-
maxDeletionRun = 4,
|
|
333
|
-
maxOutputLines = 12,
|
|
334
|
-
} = options;
|
|
335
|
-
|
|
336
|
-
if (!diff.trim()) {
|
|
337
|
-
return { preview: "", addedLines: 0, removedLines: 0 };
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
const lines = diff.split("\n").filter((line) => line.length > 0);
|
|
341
|
-
const previewLines: string[] = [];
|
|
342
|
-
let addedLines = 0;
|
|
343
|
-
let removedLines = 0;
|
|
344
|
-
|
|
345
|
-
for (let index = 0; index < lines.length; ) {
|
|
346
|
-
const kind = classifyDiffPreviewLine(lines[index]!);
|
|
347
|
-
let end = index + 1;
|
|
348
|
-
while (end < lines.length && classifyDiffPreviewLine(lines[end]!) === kind) {
|
|
349
|
-
end += 1;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const run = lines.slice(index, end);
|
|
353
|
-
switch (kind) {
|
|
354
|
-
case "addition":
|
|
355
|
-
addedLines += run.length;
|
|
356
|
-
previewLines.push(...collapseDiffPreviewRun(run, maxAdditionRun, "added"));
|
|
357
|
-
break;
|
|
358
|
-
case "deletion":
|
|
359
|
-
removedLines += run.length;
|
|
360
|
-
previewLines.push(...collapseDiffPreviewRun(run, maxDeletionRun, "removed"));
|
|
361
|
-
break;
|
|
362
|
-
case "context":
|
|
363
|
-
previewLines.push(...collapseDiffPreviewRun(run, maxUnchangedRun, "unchanged"));
|
|
364
|
-
break;
|
|
365
|
-
default:
|
|
366
|
-
previewLines.push(...run);
|
|
367
|
-
break;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
index = end;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (previewLines.length > maxOutputLines) {
|
|
374
|
-
const visibleLines = previewLines.slice(0, maxOutputLines);
|
|
375
|
-
visibleLines.push(
|
|
376
|
-
summarizeOmitted(previewLines.length - maxOutputLines, "preview"),
|
|
377
|
-
);
|
|
378
|
-
return {
|
|
379
|
-
preview: visibleLines.join("\n"),
|
|
380
|
-
addedLines,
|
|
381
|
-
removedLines,
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
return {
|
|
386
|
-
preview: previewLines.join("\n"),
|
|
387
|
-
addedLines,
|
|
388
|
-
removedLines,
|
|
389
|
-
};
|
|
390
|
-
}
|
|
1
|
+
import * as Diff from "diff";
|
|
2
|
+
import { computeLineHash, ANCHOR_SEP, CONTENT_SEP } from "./hashline";
|
|
3
|
+
|
|
4
|
+
// ─── Line ending normalization ──────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
export function detectLineEnding(content: string): "\r\n" | "\n" {
|
|
7
|
+
const crlfIdx = content.indexOf("\r\n");
|
|
8
|
+
const lfIdx = content.indexOf("\n");
|
|
9
|
+
if (lfIdx === -1 || crlfIdx === -1) return "\n";
|
|
10
|
+
return crlfIdx < lfIdx ? "\r\n" : "\n";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function normalizeToLF(text: string): string {
|
|
14
|
+
return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function restoreLineEndings(
|
|
18
|
+
text: string,
|
|
19
|
+
ending: "\r\n" | "\n",
|
|
20
|
+
): string {
|
|
21
|
+
return ending === "\r\n" ? text.replace(/\n/g, "\r\n") : text;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function stripBom(content: string): { bom: string; text: string } {
|
|
25
|
+
return content.startsWith("\uFEFF")
|
|
26
|
+
? { bom: "\uFEFF", text: content.slice(1) }
|
|
27
|
+
: { bom: "", text: content };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ─── Diff generation ────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
export function generateDiffString(
|
|
33
|
+
oldContent: string,
|
|
34
|
+
newContent: string,
|
|
35
|
+
contextLines = 4,
|
|
36
|
+
): { diff: string } {
|
|
37
|
+
const patch = Diff.structuredPatch("a", "b", oldContent, newContent, undefined, undefined, {
|
|
38
|
+
context: contextLines,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (!patch.hunks.length) {
|
|
42
|
+
return { diff: "" };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const maxLineNum = Math.max(
|
|
46
|
+
oldContent.split("\n").length,
|
|
47
|
+
newContent.split("\n").length,
|
|
48
|
+
);
|
|
49
|
+
const lineNumWidth = String(maxLineNum).length;
|
|
50
|
+
const hashPad = " ".repeat(ANCHOR_SEP.length + 2); // align with `${ANCHOR_SEP}HH${CONTENT_SEP}`
|
|
51
|
+
const output: string[] = [];
|
|
52
|
+
|
|
53
|
+
// Build context array for hash computation (same normalization as getPreviewLines)
|
|
54
|
+
const newFileLines = newContent.length === 0
|
|
55
|
+
? []
|
|
56
|
+
: newContent.endsWith("\n")
|
|
57
|
+
? newContent.split("\n").slice(0, -1)
|
|
58
|
+
: newContent.split("\n");
|
|
59
|
+
|
|
60
|
+
for (let h = 0; h < patch.hunks.length; h++) {
|
|
61
|
+
const hunk = patch.hunks[h]!;
|
|
62
|
+
if (h > 0) {
|
|
63
|
+
output.push(" ...");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let oldLineNum = hunk.oldStart;
|
|
67
|
+
let newLineNum = hunk.newStart;
|
|
68
|
+
|
|
69
|
+
for (const line of hunk.lines) {
|
|
70
|
+
if (line === "\") continue;
|
|
71
|
+
|
|
72
|
+
const prefix = line[0] as " " | "+" | "-";
|
|
73
|
+
const text = line.slice(1);
|
|
74
|
+
|
|
75
|
+
if (prefix === "-") {
|
|
76
|
+
const padded = String(oldLineNum).padStart(lineNumWidth, " ");
|
|
77
|
+
output.push(`-${padded}${hashPad}${CONTENT_SEP}${text}`);
|
|
78
|
+
oldLineNum++;
|
|
79
|
+
} else if (prefix === "+") {
|
|
80
|
+
const padded = String(newLineNum).padStart(lineNumWidth, " ");
|
|
81
|
+
const hash = computeLineHash(newFileLines, newLineNum - 1);
|
|
82
|
+
output.push(`+${padded}${ANCHOR_SEP}${hash}${CONTENT_SEP}${text}`);
|
|
83
|
+
newLineNum++;
|
|
84
|
+
} else {
|
|
85
|
+
const padded = String(newLineNum).padStart(lineNumWidth, " ");
|
|
86
|
+
const hash = computeLineHash(newFileLines, newLineNum - 1);
|
|
87
|
+
output.push(` ${padded}${ANCHOR_SEP}${hash}${CONTENT_SEP}${text}`);
|
|
88
|
+
oldLineNum++;
|
|
89
|
+
newLineNum++;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return { diff: output.join("\n") };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface CompactHashlineDiffPreview {
|
|
98
|
+
preview: string;
|
|
99
|
+
addedLines: number;
|
|
100
|
+
removedLines: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
type DiffPreviewKind = "context" | "addition" | "deletion";
|
|
104
|
+
|
|
105
|
+
function classifyDiffPreviewLine(line: string): DiffPreviewKind | null {
|
|
106
|
+
if (line.startsWith("+")) return "addition";
|
|
107
|
+
if (line.startsWith("-")) return "deletion";
|
|
108
|
+
if (line.startsWith(" ")) return "context";
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function summarizeOmitted(count: number, label: string): string {
|
|
113
|
+
return `... ${count} more ${label} line${count === 1 ? "" : "s"}`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function collapseDiffPreviewRun(
|
|
117
|
+
lines: string[],
|
|
118
|
+
maxVisible: number,
|
|
119
|
+
label: string,
|
|
120
|
+
): string[] {
|
|
121
|
+
if (lines.length <= maxVisible) {
|
|
122
|
+
return lines;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return [
|
|
126
|
+
...lines.slice(0, maxVisible),
|
|
127
|
+
summarizeOmitted(lines.length - maxVisible, label),
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function buildCompactHashlineDiffPreview(
|
|
132
|
+
diff: string,
|
|
133
|
+
options: {
|
|
134
|
+
maxUnchangedRun?: number;
|
|
135
|
+
maxAdditionRun?: number;
|
|
136
|
+
maxDeletionRun?: number;
|
|
137
|
+
maxOutputLines?: number;
|
|
138
|
+
} = {},
|
|
139
|
+
): CompactHashlineDiffPreview {
|
|
140
|
+
const {
|
|
141
|
+
maxUnchangedRun = 2,
|
|
142
|
+
maxAdditionRun = 4,
|
|
143
|
+
maxDeletionRun = 4,
|
|
144
|
+
maxOutputLines = 12,
|
|
145
|
+
} = options;
|
|
146
|
+
|
|
147
|
+
if (!diff.trim()) {
|
|
148
|
+
return { preview: "", addedLines: 0, removedLines: 0 };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const lines = diff.split("\n").filter((line) => line.length > 0);
|
|
152
|
+
const previewLines: string[] = [];
|
|
153
|
+
let addedLines = 0;
|
|
154
|
+
let removedLines = 0;
|
|
155
|
+
|
|
156
|
+
for (let index = 0; index < lines.length; ) {
|
|
157
|
+
const kind = classifyDiffPreviewLine(lines[index]!);
|
|
158
|
+
let end = index + 1;
|
|
159
|
+
while (end < lines.length && classifyDiffPreviewLine(lines[end]!) === kind) {
|
|
160
|
+
end += 1;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const run = lines.slice(index, end);
|
|
164
|
+
switch (kind) {
|
|
165
|
+
case "addition":
|
|
166
|
+
addedLines += run.length;
|
|
167
|
+
previewLines.push(...collapseDiffPreviewRun(run, maxAdditionRun, "added"));
|
|
168
|
+
break;
|
|
169
|
+
case "deletion":
|
|
170
|
+
removedLines += run.length;
|
|
171
|
+
previewLines.push(...collapseDiffPreviewRun(run, maxDeletionRun, "removed"));
|
|
172
|
+
break;
|
|
173
|
+
case "context":
|
|
174
|
+
previewLines.push(...collapseDiffPreviewRun(run, maxUnchangedRun, "unchanged"));
|
|
175
|
+
break;
|
|
176
|
+
default:
|
|
177
|
+
previewLines.push(...run);
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
index = end;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (previewLines.length > maxOutputLines) {
|
|
185
|
+
const visibleLines = previewLines.slice(0, maxOutputLines);
|
|
186
|
+
visibleLines.push(
|
|
187
|
+
summarizeOmitted(previewLines.length - maxOutputLines, "preview"),
|
|
188
|
+
);
|
|
189
|
+
return {
|
|
190
|
+
preview: visibleLines.join("\n"),
|
|
191
|
+
addedLines,
|
|
192
|
+
removedLines,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
preview: previewLines.join("\n"),
|
|
198
|
+
addedLines,
|
|
199
|
+
removedLines,
|
|
200
|
+
};
|
|
201
|
+
}
|