@ladbabynpm/picc-edit 0.1.0 → 0.1.1
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 +2 -0
- package/package.json +1 -1
- package/src/diff.ts +46 -44
- package/src/renderDiff.ts +15 -10
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# picc-edit
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@ladbabynpm/picc-edit)
|
|
4
|
+
|
|
3
5
|
Claude Code style **Edit** tool for [pi](https://pi.dev) — a faithful port of Claude Code's `Edit` tool, overriding pi's built-in `edit`.
|
|
4
6
|
|
|
5
7
|
Part of [picc](https://github.com/Ladbaby/picc), a pi agent setup mirroring Claude Code's harness.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ladbabynpm/picc-edit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Claude Code style Edit tool for pi — a faithful port of Claude Code's `Edit` (exact string replacement, quote normalization, read-first guard, replace_all, structured diff).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
package/src/diff.ts
CHANGED
|
@@ -62,13 +62,18 @@ export function countLinesChanged(
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
|
-
* Generate a display-oriented diff string
|
|
66
|
-
*
|
|
65
|
+
* Generate a display-oriented diff string matching Claude Code's `Edit`
|
|
66
|
+
* output format:
|
|
67
|
+
* - The line number is right-aligned, then the `+`/`-` marker sits to its
|
|
68
|
+
* right (context lines leave the marker column blank).
|
|
69
|
+
* - Line numbering uses **two independent counters**:
|
|
70
|
+
* - Added: new-file line number (counter advances)
|
|
71
|
+
* - Removed: old-file line number (old counter advances; the new counter
|
|
72
|
+
* does NOT advance for removed lines)
|
|
73
|
+
* - Context: new-file line number (both counters advance)
|
|
67
74
|
*
|
|
68
|
-
*
|
|
69
|
-
* -
|
|
70
|
-
* - Added: `+NNN content`, counter advances
|
|
71
|
-
* - Removed: `-NNN content`, counter does NOT advance
|
|
75
|
+
* Example (old = `a,b,c,d,e`; new = `a,c,e` — removes `b` and `d`):
|
|
76
|
+
* ` 1 a`, ` 2 - b`, ` 2 c`, ` 4 - d`, ` 3 e`
|
|
72
77
|
*
|
|
73
78
|
* Context collapsing mirrors pi's `generateDiffString`: only up to
|
|
74
79
|
* `contextLines` lines are shown before/after a change; larger gaps are
|
|
@@ -87,9 +92,19 @@ export function generateDisplayDiff(
|
|
|
87
92
|
const maxLineNum = Math.max(oldLines.length, newLines.length);
|
|
88
93
|
const lineNumWidth = String(maxLineNum).length;
|
|
89
94
|
|
|
95
|
+
let oldLineNum = 1;
|
|
90
96
|
let newLineNum = 1;
|
|
91
97
|
let lastWasChange = false;
|
|
92
98
|
|
|
99
|
+
// Emit one diff line. `marker` is "+", "-" or " " (blank column for
|
|
100
|
+
// context). Layout: `<gutter-space><right-aligned num><space><marker><space><content>`,
|
|
101
|
+
// so changed/context columns align and the marker sits to the right of the
|
|
102
|
+
// line number (matching Claude Code's `7 +` / `10 -` rendering).
|
|
103
|
+
const emitLine = (num: number, marker: string, line: string) => {
|
|
104
|
+
const lineNum = String(num).padStart(lineNumWidth, " ");
|
|
105
|
+
output.push(` ${lineNum} ${marker} ${line}`);
|
|
106
|
+
};
|
|
107
|
+
|
|
93
108
|
for (let i = 0; i < parts.length; i++) {
|
|
94
109
|
const part = parts[i];
|
|
95
110
|
const raw = part.value.split("\n");
|
|
@@ -99,12 +114,12 @@ export function generateDisplayDiff(
|
|
|
99
114
|
|
|
100
115
|
if (part.added || part.removed) {
|
|
101
116
|
for (const line of raw) {
|
|
102
|
-
const lineNum = String(newLineNum).padStart(lineNumWidth, " ");
|
|
103
117
|
if (part.added) {
|
|
104
|
-
|
|
118
|
+
emitLine(newLineNum, "+", line);
|
|
105
119
|
newLineNum++;
|
|
106
120
|
} else {
|
|
107
|
-
|
|
121
|
+
emitLine(oldLineNum, "-", line);
|
|
122
|
+
oldLineNum++;
|
|
108
123
|
}
|
|
109
124
|
}
|
|
110
125
|
lastWasChange = true;
|
|
@@ -115,54 +130,41 @@ export function generateDisplayDiff(
|
|
|
115
130
|
const hasLeadingChange = lastWasChange;
|
|
116
131
|
const hasTrailingChange = nextPartIsChange;
|
|
117
132
|
|
|
133
|
+
const emitContext = (lines: string[]) => {
|
|
134
|
+
for (const line of lines) {
|
|
135
|
+
emitLine(newLineNum, " ", line);
|
|
136
|
+
oldLineNum++;
|
|
137
|
+
newLineNum++;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
const emitEllipsis = (skipped: number) => {
|
|
141
|
+
output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
|
|
142
|
+
oldLineNum += skipped;
|
|
143
|
+
newLineNum += skipped;
|
|
144
|
+
};
|
|
145
|
+
|
|
118
146
|
if (hasLeadingChange && hasTrailingChange) {
|
|
119
147
|
if (raw.length <= contextLines * 2) {
|
|
120
|
-
|
|
121
|
-
const lineNum = String(newLineNum).padStart(lineNumWidth, " ");
|
|
122
|
-
output.push(` ${lineNum} ${line}`);
|
|
123
|
-
newLineNum++;
|
|
124
|
-
}
|
|
148
|
+
emitContext(raw);
|
|
125
149
|
} else {
|
|
126
150
|
const leading = raw.slice(0, contextLines);
|
|
127
151
|
const trailing = raw.slice(raw.length - contextLines);
|
|
128
152
|
const skipped = raw.length - leading.length - trailing.length;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
newLineNum++;
|
|
133
|
-
}
|
|
134
|
-
output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
|
|
135
|
-
newLineNum += skipped;
|
|
136
|
-
for (const line of trailing) {
|
|
137
|
-
const lineNum = String(newLineNum).padStart(lineNumWidth, " ");
|
|
138
|
-
output.push(` ${lineNum} ${line}`);
|
|
139
|
-
newLineNum++;
|
|
140
|
-
}
|
|
153
|
+
emitContext(leading);
|
|
154
|
+
emitEllipsis(skipped);
|
|
155
|
+
emitContext(trailing);
|
|
141
156
|
}
|
|
142
157
|
} else if (hasLeadingChange) {
|
|
143
158
|
const shown = raw.slice(0, contextLines);
|
|
144
159
|
const skipped = raw.length - shown.length;
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
output.push(` ${lineNum} ${line}`);
|
|
148
|
-
newLineNum++;
|
|
149
|
-
}
|
|
150
|
-
if (skipped > 0) {
|
|
151
|
-
output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
|
|
152
|
-
newLineNum += skipped;
|
|
153
|
-
}
|
|
160
|
+
emitContext(shown);
|
|
161
|
+
if (skipped > 0) emitEllipsis(skipped);
|
|
154
162
|
} else if (hasTrailingChange) {
|
|
155
163
|
const skipped = Math.max(0, raw.length - contextLines);
|
|
156
|
-
if (skipped > 0)
|
|
157
|
-
|
|
158
|
-
newLineNum += skipped;
|
|
159
|
-
}
|
|
160
|
-
for (const line of raw.slice(skipped)) {
|
|
161
|
-
const lineNum = String(newLineNum).padStart(lineNumWidth, " ");
|
|
162
|
-
output.push(` ${lineNum} ${line}`);
|
|
163
|
-
newLineNum++;
|
|
164
|
-
}
|
|
164
|
+
if (skipped > 0) emitEllipsis(skipped);
|
|
165
|
+
emitContext(raw.slice(skipped));
|
|
165
166
|
} else {
|
|
167
|
+
oldLineNum += raw.length;
|
|
166
168
|
newLineNum += raw.length;
|
|
167
169
|
}
|
|
168
170
|
lastWasChange = false;
|
package/src/renderDiff.ts
CHANGED
|
@@ -26,16 +26,21 @@ function renderAddedLine(line: string): string {
|
|
|
26
26
|
return `${ADDED_LINE_FG}${line}${FG_RESET}`;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
/** Parse diff line to extract
|
|
30
|
-
*
|
|
29
|
+
/** Parse diff line to extract line number, marker, and content.
|
|
30
|
+
* Claude Code layout: `<gutter-space><right-aligned num><space><marker><space><content>`
|
|
31
|
+
* where the marker is "+" (added), "-" (removed) or " " (context), i.e.
|
|
32
|
+
* " 7 + content" / " 10 - content" / " 7 content". The number is
|
|
33
|
+
* right-padded, so `\s*\d+` captures the padding (kept for alignment). The
|
|
34
|
+
* collapsed ` ...` marker has no digits and falls through (returns null).
|
|
35
|
+
*/
|
|
31
36
|
function parseDiffLine(line: string): {
|
|
32
37
|
prefix: string;
|
|
33
38
|
lineNum: string;
|
|
34
39
|
content: string;
|
|
35
40
|
} | null {
|
|
36
|
-
const match = line.match(/^(
|
|
41
|
+
const match = line.match(/^ (\s*\d+) ([-+ ]) (.*)$/);
|
|
37
42
|
if (!match) return null;
|
|
38
|
-
return { prefix: match[
|
|
43
|
+
return { prefix: match[2], lineNum: match[1], content: match[3] };
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
/** Replace tabs with spaces for consistent rendering. */
|
|
@@ -144,30 +149,30 @@ export function renderDiff(
|
|
|
144
149
|
result.push(
|
|
145
150
|
theme.fg(
|
|
146
151
|
"toolDiffRemoved",
|
|
147
|
-
|
|
152
|
+
` ${removed.lineNum} - ${removedLine}`,
|
|
148
153
|
),
|
|
149
154
|
);
|
|
150
|
-
result.push(renderAddedLine(
|
|
155
|
+
result.push(renderAddedLine(` ${added.lineNum} + ${addedLine}`));
|
|
151
156
|
} else {
|
|
152
157
|
// Show all removed lines first, then all added lines
|
|
153
158
|
for (const removed of removedLines) {
|
|
154
159
|
result.push(
|
|
155
160
|
theme.fg(
|
|
156
161
|
"toolDiffRemoved",
|
|
157
|
-
|
|
162
|
+
` ${removed.lineNum} - ${replaceTabs(removed.content)}`,
|
|
158
163
|
),
|
|
159
164
|
);
|
|
160
165
|
}
|
|
161
166
|
for (const added of addedLines) {
|
|
162
167
|
result.push(
|
|
163
|
-
renderAddedLine(
|
|
168
|
+
renderAddedLine(` ${added.lineNum} + ${replaceTabs(added.content)}`),
|
|
164
169
|
);
|
|
165
170
|
}
|
|
166
171
|
}
|
|
167
172
|
} else if (parsed.prefix === "+") {
|
|
168
173
|
// Standalone added line
|
|
169
174
|
result.push(
|
|
170
|
-
renderAddedLine(
|
|
175
|
+
renderAddedLine(` ${parsed.lineNum} + ${replaceTabs(parsed.content)}`),
|
|
171
176
|
);
|
|
172
177
|
i++;
|
|
173
178
|
} else {
|
|
@@ -175,7 +180,7 @@ export function renderDiff(
|
|
|
175
180
|
result.push(
|
|
176
181
|
theme.fg(
|
|
177
182
|
"toolDiffContext",
|
|
178
|
-
` ${parsed.lineNum}
|
|
183
|
+
` ${parsed.lineNum} ${replaceTabs(parsed.content)}`,
|
|
179
184
|
),
|
|
180
185
|
);
|
|
181
186
|
i++;
|