pi-hashline-edit-pro 0.15.2 → 0.15.3
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/package.json +1 -1
- package/src/replace-diff.ts +34 -17
- package/src/replace-flat.ts +2 -1
- package/src/replace-response.ts +43 -42
- package/src/replace.ts +4 -3
package/package.json
CHANGED
package/src/replace-diff.ts
CHANGED
|
@@ -45,10 +45,25 @@ export function genDiff(
|
|
|
45
45
|
newContent: string,
|
|
46
46
|
contextLines = 2,
|
|
47
47
|
newContentHashes?: string[],
|
|
48
|
+
oldHashes?: string[],
|
|
48
49
|
): { diff: string; firstChangedLine: number | undefined } {
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
// Annotate each line with its hash so Diff.diffLines can distinguish
|
|
51
|
+
// identical lines at different positions. The \0 separator cannot appear
|
|
52
|
+
// in valid text file content.
|
|
53
|
+
const oldLines = oldContent.split("\n");
|
|
54
|
+
const effectiveOldHashes = oldHashes ?? _lineHashesPure(oldContent);
|
|
55
|
+
const annotatedOld = oldLines
|
|
56
|
+
.map((line, i) => `${line}\0${effectiveOldHashes[i] ?? ""}`)
|
|
57
|
+
.join("\n");
|
|
58
|
+
|
|
59
|
+
const newLines = newContent.split("\n");
|
|
51
60
|
const effectiveNewHashes = newContentHashes ?? _lineHashesPure(newContent);
|
|
61
|
+
const annotatedNew = newLines
|
|
62
|
+
.map((line, i) => `${line}\0${effectiveNewHashes[i] ?? ""}`)
|
|
63
|
+
.join("\n");
|
|
64
|
+
|
|
65
|
+
const parts = Diff.diffLines(annotatedOld, annotatedNew);
|
|
66
|
+
const output: string[] = [];
|
|
52
67
|
let oldLineNum = 1;
|
|
53
68
|
let newLineNum = 1;
|
|
54
69
|
let lastWasChange = false;
|
|
@@ -58,18 +73,21 @@ export function genDiff(
|
|
|
58
73
|
const part = parts[i]!;
|
|
59
74
|
const raw = part.value.split("\n");
|
|
60
75
|
if (raw[raw.length - 1] === "") raw.pop();
|
|
76
|
+
// Strip \0hash annotation from each line for display
|
|
77
|
+
const displayLines = raw.map((l) => {
|
|
78
|
+
const nullIdx = l.indexOf("\0");
|
|
79
|
+
return nullIdx >= 0 ? l.slice(0, nullIdx) : l;
|
|
80
|
+
});
|
|
61
81
|
|
|
62
82
|
if (part.added || part.removed) {
|
|
63
83
|
if (firstChangedLine === undefined) firstChangedLine = newLineNum;
|
|
64
|
-
for (
|
|
84
|
+
for (let k = 0; k < displayLines.length; k++) {
|
|
65
85
|
if (part.added) {
|
|
66
86
|
const hash = effectiveNewHashes[newLineNum - 1];
|
|
67
|
-
output.push(fmtDiffLine("+",
|
|
87
|
+
output.push(fmtDiffLine("+", displayLines[k]!, hash));
|
|
68
88
|
newLineNum++;
|
|
69
89
|
} else {
|
|
70
|
-
output.push(
|
|
71
|
-
fmtDiffLine("-", line, undefined),
|
|
72
|
-
);
|
|
90
|
+
output.push(fmtDiffLine("-", displayLines[k]!, undefined));
|
|
73
91
|
oldLineNum++;
|
|
74
92
|
}
|
|
75
93
|
}
|
|
@@ -80,18 +98,18 @@ export function genDiff(
|
|
|
80
98
|
const nextPartIsChange =
|
|
81
99
|
i < parts.length - 1 && (parts[i + 1]!.added || parts[i + 1]!.removed);
|
|
82
100
|
if (lastWasChange || nextPartIsChange) {
|
|
83
|
-
let linesToShow =
|
|
101
|
+
let linesToShow = displayLines;
|
|
84
102
|
let skipStart = 0;
|
|
85
103
|
let skipEnd = 0;
|
|
86
104
|
let skipMiddle = 0;
|
|
87
105
|
|
|
88
106
|
if (!lastWasChange) {
|
|
89
|
-
skipStart = Math.max(0,
|
|
90
|
-
linesToShow =
|
|
91
|
-
} else if (nextPartIsChange &&
|
|
92
|
-
const tail =
|
|
93
|
-
linesToShow = [...
|
|
94
|
-
skipMiddle =
|
|
107
|
+
skipStart = Math.max(0, displayLines.length - contextLines);
|
|
108
|
+
linesToShow = displayLines.slice(skipStart);
|
|
109
|
+
} else if (nextPartIsChange && displayLines.length > contextLines * 2) {
|
|
110
|
+
const tail = displayLines.slice(-contextLines);
|
|
111
|
+
linesToShow = [...displayLines.slice(0, contextLines), "__ELLIPSIS__", ...tail];
|
|
112
|
+
skipMiddle = displayLines.length - contextLines * 2;
|
|
95
113
|
} else if (linesToShow.length > contextLines) {
|
|
96
114
|
skipEnd = linesToShow.length - contextLines;
|
|
97
115
|
linesToShow = linesToShow.slice(0, contextLines);
|
|
@@ -111,13 +129,12 @@ export function genDiff(
|
|
|
111
129
|
}
|
|
112
130
|
const hash = effectiveNewHashes[newLineNum - 1];
|
|
113
131
|
output.push(fmtDiffLine(" ", line, hash));
|
|
114
|
-
|
|
115
132
|
oldLineNum++;
|
|
116
133
|
newLineNum++;
|
|
117
134
|
}
|
|
118
135
|
} else {
|
|
119
|
-
oldLineNum +=
|
|
120
|
-
newLineNum +=
|
|
136
|
+
oldLineNum += displayLines.length;
|
|
137
|
+
newLineNum += displayLines.length;
|
|
121
138
|
}
|
|
122
139
|
lastWasChange = false;
|
|
123
140
|
}
|
package/src/replace-flat.ts
CHANGED
|
@@ -238,6 +238,7 @@ export function buildToolDef(): ToolDef {
|
|
|
238
238
|
|
|
239
239
|
const {
|
|
240
240
|
originalNormalized,
|
|
241
|
+
originalHashes,
|
|
241
242
|
result,
|
|
242
243
|
bom,
|
|
243
244
|
originalEnding,
|
|
@@ -294,13 +295,13 @@ export function buildToolDef(): ToolDef {
|
|
|
294
295
|
const successInput = {
|
|
295
296
|
path,
|
|
296
297
|
originalNormalized,
|
|
298
|
+
originalHashes,
|
|
297
299
|
result,
|
|
298
300
|
resultHashes,
|
|
299
301
|
warnings,
|
|
300
302
|
snapshotId: updatedSnapshotId,
|
|
301
303
|
editMeta,
|
|
302
304
|
};
|
|
303
|
-
|
|
304
305
|
return buildChanged(successInput);
|
|
305
306
|
});
|
|
306
307
|
},
|
package/src/replace-response.ts
CHANGED
|
@@ -40,13 +40,14 @@ export interface NoopInput {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export interface SuccessInput {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
path: string;
|
|
44
|
+
originalNormalized: string;
|
|
45
|
+
originalHashes: string[];
|
|
46
|
+
result: string;
|
|
47
|
+
resultHashes: string[];
|
|
48
|
+
warnings: string[] | undefined;
|
|
49
|
+
snapshotId: string;
|
|
50
|
+
editMeta: RMeta;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
function cntDiff(diff: string, marker: "+" | "-"): number {
|
|
@@ -139,39 +140,39 @@ export function buildNoop(input: NoopInput): TResult {
|
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
export function buildChanged(input: SuccessInput): TResult {
|
|
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
|
-
|
|
143
|
+
const { path, result, warnings, snapshotId, originalNormalized, originalHashes, editMeta, resultHashes } = input;
|
|
144
|
+
|
|
145
|
+
const resultLines = visLines(result);
|
|
146
|
+
const diffResult = genDiff(originalNormalized, result, 2, resultHashes, originalHashes);
|
|
147
|
+
const addedLines = cntDiff(diffResult.diff, "+");
|
|
148
|
+
const removedLines = cntDiff(diffResult.diff, "-");
|
|
149
|
+
const warningsBlock = warnBlock(warnings);
|
|
150
|
+
const successPrefix = `Successfully replaced in ${path}.`;
|
|
151
|
+
const text = resultLines.length === 0
|
|
152
|
+
? "File is empty. Use replace to insert content."
|
|
153
|
+
: warningsBlock
|
|
154
|
+
? `${successPrefix}${warningsBlock}`
|
|
155
|
+
: successPrefix;
|
|
156
|
+
|
|
157
|
+
const metrics = buildM({
|
|
158
|
+
classification: "applied",
|
|
159
|
+
editsAttempted: editMeta.editsAttempted,
|
|
160
|
+
noopEditsCount: editMeta.noopEditsCount,
|
|
161
|
+
warningsCount: warnings?.length ?? 0,
|
|
162
|
+
firstChangedLine: editMeta.firstChangedLine,
|
|
163
|
+
lastChangedLine: editMeta.lastChangedLine,
|
|
164
|
+
addedLines,
|
|
165
|
+
removedLines,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
content: [{ type: "text", text }],
|
|
170
|
+
details: {
|
|
171
|
+
diff: diffResult.diff,
|
|
172
|
+
firstChangedLine:
|
|
173
|
+
editMeta.firstChangedLine ?? diffResult.firstChangedLine,
|
|
174
|
+
snapshotId,
|
|
175
|
+
metrics,
|
|
176
|
+
},
|
|
177
|
+
};
|
|
177
178
|
}
|
package/src/replace.ts
CHANGED
|
@@ -228,7 +228,7 @@ export async function compPreview(
|
|
|
228
228
|
try {
|
|
229
229
|
const normalized = normReq(request);
|
|
230
230
|
assertReq(normalized);
|
|
231
|
-
const { path, originalNormalized, result, resultHashes } = await execPipeline(
|
|
231
|
+
const { path, originalNormalized, originalHashes, result, resultHashes } = await execPipeline(
|
|
232
232
|
normalized,
|
|
233
233
|
cwd,
|
|
234
234
|
constants.R_OK,
|
|
@@ -240,7 +240,7 @@ export async function compPreview(
|
|
|
240
240
|
};
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
-
return { diff: genDiff(originalNormalized, result, 4, resultHashes).diff };
|
|
243
|
+
return { diff: genDiff(originalNormalized, result, 4, resultHashes, originalHashes).diff };
|
|
244
244
|
} catch (error: unknown) {
|
|
245
245
|
return { error: error instanceof Error ? error.message : String(error) };
|
|
246
246
|
}
|
|
@@ -391,6 +391,7 @@ export function buildToolDef(): ToolDef {
|
|
|
391
391
|
|
|
392
392
|
const {
|
|
393
393
|
originalNormalized,
|
|
394
|
+
originalHashes,
|
|
394
395
|
result,
|
|
395
396
|
bom,
|
|
396
397
|
originalEnding,
|
|
@@ -449,13 +450,13 @@ export function buildToolDef(): ToolDef {
|
|
|
449
450
|
const successInput = {
|
|
450
451
|
path,
|
|
451
452
|
originalNormalized,
|
|
453
|
+
originalHashes,
|
|
452
454
|
result,
|
|
453
455
|
resultHashes,
|
|
454
456
|
warnings,
|
|
455
457
|
snapshotId: updatedSnapshotId,
|
|
456
458
|
editMeta,
|
|
457
459
|
};
|
|
458
|
-
|
|
459
460
|
return buildChanged(successInput);
|
|
460
461
|
});
|
|
461
462
|
},
|