pi-readseek 0.7.7 → 0.7.9
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/README.md +28 -46
- package/dist/index.js +7333 -0
- package/package.json +8 -7
- package/index.ts +0 -147
- package/src/binary-detect.ts +0 -22
- package/src/check.ts +0 -92
- package/src/coerce-obvious-int.ts +0 -41
- package/src/confusable-hyphens.ts +0 -6
- package/src/def.ts +0 -143
- package/src/diff-data.ts +0 -302
- package/src/edit-classify.ts +0 -58
- package/src/edit-diff.ts +0 -359
- package/src/edit-output.ts +0 -110
- package/src/edit-render-helpers.ts +0 -141
- package/src/edit-syntax-validate.ts +0 -65
- package/src/edit.ts +0 -686
- package/src/file-map.ts +0 -17
- package/src/fs-error.ts +0 -33
- package/src/grep-budget.ts +0 -35
- package/src/grep-output.ts +0 -181
- package/src/grep-render-helpers.ts +0 -77
- package/src/grep-symbol-scope.ts +0 -184
- package/src/grep.ts +0 -608
- package/src/hashline.ts +0 -736
- package/src/hover.ts +0 -140
- package/src/path-utils.ts +0 -24
- package/src/pending-diff-preview.ts +0 -269
- package/src/read-local-bundle.ts +0 -75
- package/src/read-output.ts +0 -186
- package/src/read-render-helpers.ts +0 -104
- package/src/read.ts +0 -661
- package/src/readseek/constants.ts +0 -17
- package/src/readseek/enums.ts +0 -38
- package/src/readseek/formatter.ts +0 -350
- package/src/readseek/symbol-error-format.ts +0 -18
- package/src/readseek/symbol-lookup.ts +0 -240
- package/src/readseek/symbol-tree.ts +0 -21
- package/src/readseek/types.ts +0 -49
- package/src/readseek-client.ts +0 -1250
- package/src/readseek-params.ts +0 -39
- package/src/readseek-settings.ts +0 -263
- package/src/readseek-value.ts +0 -184
- package/src/refs-output.ts +0 -47
- package/src/refs.ts +0 -161
- package/src/register-tool.ts +0 -29
- package/src/rename.ts +0 -182
- package/src/replace-symbol.ts +0 -77
- package/src/runtime.ts +0 -3
- package/src/search-output.ts +0 -50
- package/src/search.ts +0 -214
- package/src/session-anchors.ts +0 -34
- package/src/stat-search-path.ts +0 -29
- package/src/syntax-validate-mode.ts +0 -15
- package/src/tool-prompt-metadata.ts +0 -107
- package/src/tool-types.ts +0 -12
- package/src/tui-diff-component.ts +0 -108
- package/src/tui-diff-renderer.ts +0 -92
- package/src/tui-render-utils.ts +0 -286
- package/src/view.ts +0 -158
- package/src/write.ts +0 -357
package/src/diff-data.ts
DELETED
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
export type DiffEntry =
|
|
2
|
-
| { kind: "context"; oldLine: number; newLine: number; text: string }
|
|
3
|
-
| { kind: "add"; newLine: number; text: string }
|
|
4
|
-
| { kind: "remove"; oldLine: number; text: string }
|
|
5
|
-
| { kind: "meta"; text: string };
|
|
6
|
-
|
|
7
|
-
export type DiffSpan =
|
|
8
|
-
| { kind: "equal"; text: string }
|
|
9
|
-
| { kind: "add"; text: string }
|
|
10
|
-
| { kind: "remove"; text: string };
|
|
11
|
-
|
|
12
|
-
export type InlineDiff = {
|
|
13
|
-
removeLineIndex: number;
|
|
14
|
-
addLineIndex: number;
|
|
15
|
-
removeSpans: DiffSpan[];
|
|
16
|
-
addSpans: DiffSpan[];
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export type DiffBlockRange = {
|
|
20
|
-
kind: "add" | "remove";
|
|
21
|
-
startLine: number;
|
|
22
|
-
endLine: number;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export type DiffData = {
|
|
26
|
-
version: 1;
|
|
27
|
-
entries: DiffEntry[];
|
|
28
|
-
stats: { added: number; removed: number; context: number };
|
|
29
|
-
language?: string;
|
|
30
|
-
blockRanges?: DiffBlockRange[];
|
|
31
|
-
inlineDiffs?: InlineDiff[];
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export type BuildDiffDataInput = {
|
|
35
|
-
path: string;
|
|
36
|
-
oldContent: string;
|
|
37
|
-
newContent: string;
|
|
38
|
-
diff: string;
|
|
39
|
-
blockRanges?: DiffBlockRange[];
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export const MAX_INLINE_DIFF_LINE_LENGTH = 4096;
|
|
43
|
-
export const MAX_INLINE_DIFF_TOKENS = 512;
|
|
44
|
-
export const MAX_INLINE_DIFF_CELLS = 200_000;
|
|
45
|
-
export const MAX_INLINE_DIFF_PAIRS = 200;
|
|
46
|
-
|
|
47
|
-
const INLINE_SIMILARITY_THRESHOLD = 0.35;
|
|
48
|
-
const INLINE_TOKEN_PATTERN = /([A-Za-z_$][\w$]*|\d+|\s+|[^A-Za-z_$\w\s]+)/gu;
|
|
49
|
-
|
|
50
|
-
const LANGUAGE_BY_EXTENSION = new Map<string, string>([
|
|
51
|
-
[".ts", "typescript"],
|
|
52
|
-
[".tsx", "typescript"],
|
|
53
|
-
[".js", "javascript"],
|
|
54
|
-
[".jsx", "javascript"],
|
|
55
|
-
[".py", "python"],
|
|
56
|
-
[".rs", "rust"],
|
|
57
|
-
[".java", "java"],
|
|
58
|
-
]);
|
|
59
|
-
|
|
60
|
-
function inferLanguage(path: string): string | undefined {
|
|
61
|
-
const extensionMatch = path.match(/\.[^.\/\\]+$/);
|
|
62
|
-
if (!extensionMatch) return undefined;
|
|
63
|
-
return LANGUAGE_BY_EXTENSION.get(extensionMatch[0].toLowerCase());
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function parseFullDiffEntries(diff: string): DiffEntry[] {
|
|
67
|
-
const entries: DiffEntry[] = [];
|
|
68
|
-
let nextOldLine = 1;
|
|
69
|
-
let nextNewLine = 1;
|
|
70
|
-
|
|
71
|
-
for (const line of diff ? diff.split("\n") : []) {
|
|
72
|
-
const removeMatch = line.match(/^-\s*(\d+) (.*)$/);
|
|
73
|
-
if (removeMatch) {
|
|
74
|
-
const oldLine = Number(removeMatch[1]);
|
|
75
|
-
entries.push({ kind: "remove", oldLine, text: removeMatch[2] ?? "" });
|
|
76
|
-
nextOldLine = oldLine + 1;
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const addMatch = line.match(/^\+\s*(\d+) (.*)$/);
|
|
81
|
-
if (addMatch) {
|
|
82
|
-
const newLine = Number(addMatch[1]);
|
|
83
|
-
entries.push({ kind: "add", newLine, text: addMatch[2] ?? "" });
|
|
84
|
-
nextNewLine = newLine + 1;
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const contextMatch = line.match(/^ \s*(\d+) (.*)$/);
|
|
89
|
-
if (contextMatch) {
|
|
90
|
-
const oldLine = Number(contextMatch[1]);
|
|
91
|
-
const lineDelta = nextNewLine - nextOldLine;
|
|
92
|
-
const newLine = oldLine + lineDelta;
|
|
93
|
-
entries.push({ kind: "context", oldLine, newLine, text: contextMatch[2] ?? "" });
|
|
94
|
-
nextOldLine = oldLine + 1;
|
|
95
|
-
nextNewLine = newLine + 1;
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
entries.push({ kind: "meta", text: line });
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return entries;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function parseCompactDiffEntries(diff: string, oldContent: string, newContent: string): DiffEntry[] | undefined {
|
|
106
|
-
const oldLines = oldContent.split("\n");
|
|
107
|
-
const newLines = newContent.split("\n");
|
|
108
|
-
|
|
109
|
-
const compactDelete = diff.match(/^(\d+):[0-9a-f]{3}\|(.*) → \[deleted\]$/);
|
|
110
|
-
if (compactDelete) {
|
|
111
|
-
const oldLine = Number(compactDelete[1]);
|
|
112
|
-
const text = compactDelete[2] ?? "";
|
|
113
|
-
if (oldLines[oldLine - 1] === text) return [{ kind: "remove", oldLine, text }];
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const compactPrefix = diff.match(/^(\d+):[0-9a-f]{3}\|/);
|
|
117
|
-
if (!compactPrefix) return undefined;
|
|
118
|
-
|
|
119
|
-
const oldLine = Number(compactPrefix[1]);
|
|
120
|
-
const oldTextStart = compactPrefix[0].length;
|
|
121
|
-
const separatorPattern = / → (\d+):[0-9a-f]{3}\|/g;
|
|
122
|
-
let separator: RegExpExecArray | null;
|
|
123
|
-
while ((separator = separatorPattern.exec(diff)) !== null) {
|
|
124
|
-
const newLine = Number(separator[1]);
|
|
125
|
-
const oldText = diff.slice(oldTextStart, separator.index);
|
|
126
|
-
const newText = diff.slice(separator.index + separator[0].length);
|
|
127
|
-
if (oldLines[oldLine - 1] === oldText && newLines[newLine - 1] === newText) {
|
|
128
|
-
return [
|
|
129
|
-
{ kind: "remove", oldLine, text: oldText },
|
|
130
|
-
{ kind: "add", newLine, text: newText },
|
|
131
|
-
];
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return undefined;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function buildStats(entries: DiffEntry[]): DiffData["stats"] {
|
|
139
|
-
return entries.reduce(
|
|
140
|
-
(stats, entry) => {
|
|
141
|
-
if (entry.kind === "add") stats.added++;
|
|
142
|
-
else if (entry.kind === "remove") stats.removed++;
|
|
143
|
-
else if (entry.kind === "context") stats.context++;
|
|
144
|
-
return stats;
|
|
145
|
-
},
|
|
146
|
-
{ added: 0, removed: 0, context: 0 },
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function tokenizeInlineDiff(text: string): string[] {
|
|
151
|
-
return text.match(INLINE_TOKEN_PATTERN) ?? (text ? [text] : []);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function longestCommonSubsequence(a: string[], b: string[]): Array<[number, number]> {
|
|
155
|
-
const rows = a.length + 1;
|
|
156
|
-
const cols = b.length + 1;
|
|
157
|
-
const table: number[][] = Array.from({ length: rows }, () => Array<number>(cols).fill(0));
|
|
158
|
-
|
|
159
|
-
for (let i = a.length - 1; i >= 0; i--) {
|
|
160
|
-
for (let j = b.length - 1; j >= 0; j--) {
|
|
161
|
-
table[i]![j] = a[i] === b[j] ? table[i + 1]![j + 1]! + 1 : Math.max(table[i + 1]![j]!, table[i]![j + 1]!);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const pairs: Array<[number, number]> = [];
|
|
166
|
-
let i = 0;
|
|
167
|
-
let j = 0;
|
|
168
|
-
while (i < a.length && j < b.length) {
|
|
169
|
-
if (a[i] === b[j]) {
|
|
170
|
-
pairs.push([i, j]);
|
|
171
|
-
i++;
|
|
172
|
-
j++;
|
|
173
|
-
} else if (table[i + 1]![j]! >= table[i]![j + 1]!) {
|
|
174
|
-
i++;
|
|
175
|
-
} else {
|
|
176
|
-
j++;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return pairs;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function pushMergedSpan(spans: DiffSpan[], span: DiffSpan): void {
|
|
184
|
-
if (!span.text) return;
|
|
185
|
-
const previous = spans[spans.length - 1];
|
|
186
|
-
if (previous?.kind === span.kind) {
|
|
187
|
-
previous.text += span.text;
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
spans.push({ ...span });
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function buildInlineSpans(removeText: string, addText: string): { removeSpans: DiffSpan[]; addSpans: DiffSpan[] } | undefined {
|
|
194
|
-
if (removeText.length > MAX_INLINE_DIFF_LINE_LENGTH || addText.length > MAX_INLINE_DIFF_LINE_LENGTH) return undefined;
|
|
195
|
-
|
|
196
|
-
const removeTokens = tokenizeInlineDiff(removeText);
|
|
197
|
-
const addTokens = tokenizeInlineDiff(addText);
|
|
198
|
-
if (!removeTokens.length || !addTokens.length) return undefined;
|
|
199
|
-
if (removeTokens.length > MAX_INLINE_DIFF_TOKENS || addTokens.length > MAX_INLINE_DIFF_TOKENS) return undefined;
|
|
200
|
-
if ((removeTokens.length + 1) * (addTokens.length + 1) > MAX_INLINE_DIFF_CELLS) return undefined;
|
|
201
|
-
|
|
202
|
-
const pairs = longestCommonSubsequence(removeTokens, addTokens);
|
|
203
|
-
const meaningfulRemoveTokenCount = removeTokens.filter((token) => token.trim().length > 0).length;
|
|
204
|
-
const meaningfulAddTokenCount = addTokens.filter((token) => token.trim().length > 0).length;
|
|
205
|
-
const meaningfulEqualTokenCount = pairs.filter(([removeIndex, addIndex]) => {
|
|
206
|
-
const removeToken = removeTokens[removeIndex] ?? "";
|
|
207
|
-
const addToken = addTokens[addIndex] ?? "";
|
|
208
|
-
return removeToken === addToken && removeToken.trim().length > 0 && addToken.trim().length > 0;
|
|
209
|
-
}).length;
|
|
210
|
-
const similarity = meaningfulEqualTokenCount / Math.max(meaningfulRemoveTokenCount, meaningfulAddTokenCount);
|
|
211
|
-
if (similarity < INLINE_SIMILARITY_THRESHOLD) return undefined;
|
|
212
|
-
|
|
213
|
-
const removeSpans: DiffSpan[] = [];
|
|
214
|
-
const addSpans: DiffSpan[] = [];
|
|
215
|
-
let removeCursor = 0;
|
|
216
|
-
let addCursor = 0;
|
|
217
|
-
|
|
218
|
-
for (const [removeIndex, addIndex] of pairs) {
|
|
219
|
-
if (removeCursor < removeIndex) {
|
|
220
|
-
pushMergedSpan(removeSpans, { kind: "remove", text: removeTokens.slice(removeCursor, removeIndex).join("") });
|
|
221
|
-
}
|
|
222
|
-
if (addCursor < addIndex) {
|
|
223
|
-
pushMergedSpan(addSpans, { kind: "add", text: addTokens.slice(addCursor, addIndex).join("") });
|
|
224
|
-
}
|
|
225
|
-
pushMergedSpan(removeSpans, { kind: "equal", text: removeTokens[removeIndex]! });
|
|
226
|
-
pushMergedSpan(addSpans, { kind: "equal", text: addTokens[addIndex]! });
|
|
227
|
-
removeCursor = removeIndex + 1;
|
|
228
|
-
addCursor = addIndex + 1;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (removeCursor < removeTokens.length) {
|
|
232
|
-
pushMergedSpan(removeSpans, { kind: "remove", text: removeTokens.slice(removeCursor).join("") });
|
|
233
|
-
}
|
|
234
|
-
if (addCursor < addTokens.length) {
|
|
235
|
-
pushMergedSpan(addSpans, { kind: "add", text: addTokens.slice(addCursor).join("") });
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (!removeSpans.some((span) => span.kind === "remove") || !addSpans.some((span) => span.kind === "add")) return undefined;
|
|
239
|
-
return { removeSpans, addSpans };
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function buildInlineDiffs(entries: DiffEntry[]): InlineDiff[] | undefined {
|
|
243
|
-
const inlineDiffs: InlineDiff[] = [];
|
|
244
|
-
let remainingPairs = MAX_INLINE_DIFF_PAIRS;
|
|
245
|
-
|
|
246
|
-
for (let index = 0; index < entries.length;) {
|
|
247
|
-
if (entries[index]?.kind !== "remove") {
|
|
248
|
-
index++;
|
|
249
|
-
continue;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const removeStart = index;
|
|
253
|
-
while (entries[index]?.kind === "remove") index++;
|
|
254
|
-
const addStart = index;
|
|
255
|
-
while (entries[index]?.kind === "add") index++;
|
|
256
|
-
|
|
257
|
-
const removeCount = addStart - removeStart;
|
|
258
|
-
const addCount = index - addStart;
|
|
259
|
-
if (removeCount === 0 || addCount === 0 || removeCount !== addCount) continue;
|
|
260
|
-
|
|
261
|
-
for (let offset = 0; offset < removeCount; offset++) {
|
|
262
|
-
if (remainingPairs <= 0) break;
|
|
263
|
-
remainingPairs--;
|
|
264
|
-
|
|
265
|
-
const removeIndex = removeStart + offset;
|
|
266
|
-
const addIndex = addStart + offset;
|
|
267
|
-
const removeEntry = entries[removeIndex];
|
|
268
|
-
const addEntry = entries[addIndex];
|
|
269
|
-
if (removeEntry?.kind !== "remove" || addEntry?.kind !== "add") continue;
|
|
270
|
-
|
|
271
|
-
const spans = buildInlineSpans(removeEntry.text, addEntry.text);
|
|
272
|
-
if (!spans) continue;
|
|
273
|
-
|
|
274
|
-
inlineDiffs.push({
|
|
275
|
-
removeLineIndex: removeIndex,
|
|
276
|
-
addLineIndex: addIndex,
|
|
277
|
-
removeSpans: spans.removeSpans,
|
|
278
|
-
addSpans: spans.addSpans,
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return inlineDiffs.length ? inlineDiffs : undefined;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function finalizeDiffData(path: string, entries: DiffEntry[], blockRanges: DiffBlockRange[] | undefined): DiffData {
|
|
287
|
-
const language = inferLanguage(path);
|
|
288
|
-
const inlineDiffs = buildInlineDiffs(entries);
|
|
289
|
-
return {
|
|
290
|
-
version: 1,
|
|
291
|
-
entries,
|
|
292
|
-
stats: buildStats(entries),
|
|
293
|
-
...(language ? { language } : {}),
|
|
294
|
-
...(blockRanges?.length ? { blockRanges: [...blockRanges] } : {}),
|
|
295
|
-
...(inlineDiffs ? { inlineDiffs } : {}),
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
export function buildDiffData(input: BuildDiffDataInput): DiffData {
|
|
300
|
-
const entries = parseCompactDiffEntries(input.diff, input.oldContent, input.newContent) ?? parseFullDiffEntries(input.diff);
|
|
301
|
-
return finalizeDiffData(input.path, entries, input.blockRanges);
|
|
302
|
-
}
|
package/src/edit-classify.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import * as Diff from "diff";
|
|
2
|
-
|
|
3
|
-
export type EditClassification = "no-op" | "whitespace-only" | "semantic" | "mixed";
|
|
4
|
-
|
|
5
|
-
export interface EditClassifyResult {
|
|
6
|
-
classification: EditClassification;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function classifyEdit(oldContent: string, newContent: string): EditClassifyResult {
|
|
10
|
-
if (oldContent === newContent) {
|
|
11
|
-
return { classification: "no-op" };
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const oldLines = oldContent.split("\n");
|
|
15
|
-
const newLines = newContent.split("\n");
|
|
16
|
-
|
|
17
|
-
// When line counts match, do pairwise comparison
|
|
18
|
-
if (oldLines.length === newLines.length) {
|
|
19
|
-
let hasWhitespaceChange = false;
|
|
20
|
-
let hasSemanticChange = false;
|
|
21
|
-
for (let i = 0; i < oldLines.length; i++) {
|
|
22
|
-
if (oldLines[i] === newLines[i]) continue;
|
|
23
|
-
if (oldLines[i].trim() === newLines[i].trim()) {
|
|
24
|
-
hasWhitespaceChange = true;
|
|
25
|
-
} else {
|
|
26
|
-
hasSemanticChange = true;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
if (hasSemanticChange && hasWhitespaceChange) return { classification: "mixed" };
|
|
30
|
-
if (hasWhitespaceChange) return { classification: "whitespace-only" };
|
|
31
|
-
return { classification: "semantic" };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// When line counts differ, use diff library to find changes
|
|
35
|
-
const parts = Diff.diffLines(oldContent, newContent);
|
|
36
|
-
let hasWhitespaceChange = false;
|
|
37
|
-
let hasSemanticChange = false;
|
|
38
|
-
|
|
39
|
-
for (const part of parts) {
|
|
40
|
-
if (!part.added && !part.removed) continue;
|
|
41
|
-
|
|
42
|
-
const lines = part.value.split("\n");
|
|
43
|
-
if (lines[lines.length - 1] === "") lines.pop();
|
|
44
|
-
|
|
45
|
-
for (const line of lines) {
|
|
46
|
-
if (line.trim() === "") {
|
|
47
|
-
hasWhitespaceChange = true;
|
|
48
|
-
} else {
|
|
49
|
-
hasSemanticChange = true;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (hasSemanticChange && hasWhitespaceChange) return { classification: "mixed" };
|
|
55
|
-
if (hasWhitespaceChange) return { classification: "whitespace-only" };
|
|
56
|
-
return { classification: "semantic" };
|
|
57
|
-
}
|
|
58
|
-
|