pi-hashline-edit-pro 4.2.11 → 4.3.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.
@@ -201,6 +201,74 @@ function trimPlacedSpans(
201
201
  return out;
202
202
  }
203
203
 
204
+ function createRowEmitter(
205
+ output: string[],
206
+ lineNumbers: (number | undefined)[],
207
+ maxLineBytes: number,
208
+ maxBytes: number,
209
+ ): {
210
+ readonly stopped: boolean;
211
+ readonly truncated: boolean;
212
+ emitPlain(line: string, num?: number): void;
213
+ emitRow(prefix: " " | "+" | "-", line: string, hash: string | undefined, num?: number): void;
214
+ emitDedup(line: string): boolean;
215
+ } {
216
+ let outBytes = 0;
217
+ let stopped = false;
218
+ let truncated = false;
219
+ const emitPlain = (line: string, num?: number): void => {
220
+ if (stopped) return;
221
+ const lineBytes = Buffer.byteLength(line, "utf-8") + 1;
222
+ if (outBytes + lineBytes > maxBytes) {
223
+ stopped = true;
224
+ truncated = true;
225
+ return;
226
+ }
227
+ outBytes += lineBytes;
228
+ output.push(line);
229
+ lineNumbers.push(num);
230
+ };
231
+ const emitRow = (prefix: " " | "+" | "-", line: string, hash: string | undefined, num?: number): void => {
232
+ if (stopped) return;
233
+ const full = fmtDiffLine(prefix, line, hash);
234
+ const rowBytes = Buffer.byteLength(full, "utf-8");
235
+ if (rowBytes > maxLineBytes) {
236
+ const marker = `[Row is ${formatSize(rowBytes)}, exceeds ${formatSize(maxLineBytes)}; content not shown. Use read to see the full line.]`;
237
+ emitPlain(fmtDiffLine(prefix, marker, hash), num);
238
+ return;
239
+ }
240
+ if (outBytes + rowBytes + 1 > maxBytes) {
241
+ stopped = true;
242
+ truncated = true;
243
+ return;
244
+ }
245
+ outBytes += rowBytes + 1;
246
+ output.push(full);
247
+ lineNumbers.push(num);
248
+ };
249
+ const emitDedup = (line: string): boolean => {
250
+ if (stopped) return false;
251
+ const row = fmtDedupRow(line);
252
+ const rowBytes = Buffer.byteLength(row, "utf-8") + 1;
253
+ if (outBytes + rowBytes > maxBytes) {
254
+ stopped = true;
255
+ truncated = true;
256
+ return false;
257
+ }
258
+ outBytes += rowBytes;
259
+ output.push(row);
260
+ lineNumbers.push(undefined);
261
+ return true;
262
+ };
263
+ return {
264
+ get stopped() { return stopped; },
265
+ get truncated() { return truncated; },
266
+ emitPlain,
267
+ emitRow,
268
+ emitDedup,
269
+ };
270
+ }
271
+
204
272
  function genSpanDiff(
205
273
  oldContent: string,
206
274
  newContent: string,
@@ -235,50 +303,18 @@ function genSpanDiff(
235
303
  let dedupEmitted = false;
236
304
  const lineNumbers: (number | undefined)[] = [];
237
305
  let firstChangedLine: number | undefined;
238
- let outBytes = 0;
239
- let stopped = false;
240
- let diffTruncated = false;
241
- const emitPlain = (line: string, num?: number): void => {
242
- if (stopped) return;
243
- const lineBytes = Buffer.byteLength(line, "utf-8") + 1;
244
- if (outBytes + lineBytes > maxBytes) {
245
- stopped = true;
246
- diffTruncated = true;
247
- return;
248
- }
249
- outBytes += lineBytes;
250
- output.push(line);
251
- lineNumbers.push(num);
252
- };
253
- const emitRow = (prefix: " " | "+" | "-", line: string, hash: string | undefined, num?: number): void => {
254
- if (stopped) return;
255
- const full = fmtDiffLine(prefix, line, hash);
256
- const rowBytes = Buffer.byteLength(full, "utf-8");
257
- if (rowBytes > maxLineBytes) {
258
- const marker = `[Row is ${formatSize(rowBytes)}, exceeds ${formatSize(maxLineBytes)}; content not shown. Use read to see the full line.]`;
259
- emitPlain(fmtDiffLine(prefix, marker, hash), num);
260
- return;
261
- }
262
- if (outBytes + rowBytes + 1 > maxBytes) {
263
- stopped = true;
264
- diffTruncated = true;
265
- return;
266
- }
267
- outBytes += rowBytes + 1;
268
- output.push(full);
269
- lineNumbers.push(num);
270
- };
306
+ const em = createRowEmitter(output, lineNumbers, maxLineBytes, maxBytes);
271
307
  const emitGap = (gapStartNew: number, gapLen: number, position: "leading" | "middle" | "trailing"): void => {
272
- if (stopped || gapLen <= 0) return;
308
+ if (em.stopped || gapLen <= 0) return;
273
309
  if (position === "leading") {
274
310
  let count = contextLines;
275
311
  if (contextLines > 0 && gapLen > count && isBlankLine(newLines[gapStartNew + gapLen - 1]!)) count += 1;
276
312
  count = Math.min(count, gapLen);
277
313
  const skipStart = gapLen - count;
278
- if (skipStart > 0) emitPlain(" ...", undefined);
279
- for (let k = gapLen - count; k < gapLen && !stopped; k++) {
314
+ if (skipStart > 0) em.emitPlain(" ...", undefined);
315
+ for (let k = gapLen - count; k < gapLen && !em.stopped; k++) {
280
316
  const newIdx = gapStartNew + k;
281
- emitRow(" ", newLines[newIdx]!, newHashes[newIdx], newIdx + 1);
317
+ em.emitRow(" ", newLines[newIdx]!, newHashes[newIdx], newIdx + 1);
282
318
  }
283
319
  return;
284
320
  }
@@ -286,11 +322,11 @@ function genSpanDiff(
286
322
  let count = contextLines;
287
323
  if (contextLines > 0 && gapLen > count && isBlankLine(newLines[gapStartNew]!)) count += 1;
288
324
  count = Math.min(count, gapLen);
289
- for (let k = 0; k < count && !stopped; k++) {
325
+ for (let k = 0; k < count && !em.stopped; k++) {
290
326
  const newIdx = gapStartNew + k;
291
- emitRow(" ", newLines[newIdx]!, newHashes[newIdx], newIdx + 1);
327
+ em.emitRow(" ", newLines[newIdx]!, newHashes[newIdx], newIdx + 1);
292
328
  }
293
- if (gapLen > count) emitPlain(" ...", undefined);
329
+ if (gapLen > count) em.emitPlain(" ...", undefined);
294
330
  return;
295
331
  }
296
332
  let headCount = contextLines;
@@ -299,56 +335,52 @@ function genSpanDiff(
299
335
  if (contextLines > 0 && gapLen - tailCount > headCount && isBlankLine(newLines[gapStartNew + gapLen - tailCount]!)) tailCount += 1;
300
336
  const middleLen = gapLen - headCount - tailCount;
301
337
  if (middleLen <= 0) {
302
- for (let k = 0; k < gapLen && !stopped; k++) {
338
+ for (let k = 0; k < gapLen && !em.stopped; k++) {
303
339
  const newIdx = gapStartNew + k;
304
- emitRow(" ", newLines[newIdx]!, newHashes[newIdx], newIdx + 1);
340
+ em.emitRow(" ", newLines[newIdx]!, newHashes[newIdx], newIdx + 1);
305
341
  }
306
342
  return;
307
343
  }
308
- for (let k = 0; k < headCount && !stopped; k++) {
344
+ for (let k = 0; k < headCount && !em.stopped; k++) {
309
345
  const newIdx = gapStartNew + k;
310
- emitRow(" ", newLines[newIdx]!, newHashes[newIdx], newIdx + 1);
346
+ em.emitRow(" ", newLines[newIdx]!, newHashes[newIdx], newIdx + 1);
311
347
  }
312
- if (!stopped) emitPlain(" ...", undefined);
313
- for (let k = gapLen - tailCount; k < gapLen && !stopped; k++) {
348
+ if (!em.stopped) em.emitPlain(" ...", undefined);
349
+ for (let k = gapLen - tailCount; k < gapLen && !em.stopped; k++) {
314
350
  const newIdx = gapStartNew + k;
315
- emitRow(" ", newLines[newIdx]!, newHashes[newIdx], newIdx + 1);
351
+ em.emitRow(" ", newLines[newIdx]!, newHashes[newIdx], newIdx + 1);
316
352
  }
317
353
  };
318
354
  oldPos = 0;
319
355
  newPos = 0;
320
- for (let spanIdx = 0; spanIdx < ordered.length && !stopped; spanIdx++) {
356
+ for (let spanIdx = 0; spanIdx < ordered.length && !em.stopped; spanIdx++) {
321
357
  const span = ordered[spanIdx]!;
322
358
  const gapLen = span.oldStart - oldPos;
323
359
  emitGap(newPos, gapLen, spanIdx === 0 ? "leading" : "middle");
324
- if (stopped) break;
360
+ if (em.stopped) break;
325
361
  if (firstChangedLine === undefined) firstChangedLine = span.newStart + 1;
326
362
  for (const dedup of span.dedupAbove ?? []) {
327
- if (stopped) break;
328
- dedupEmitted = true;
329
- output.push(fmtDedupRow(dedup));
330
- lineNumbers.push(undefined);
363
+ if (em.stopped) break;
364
+ if (em.emitDedup(dedup)) dedupEmitted = true;
331
365
  }
332
- for (let oldIdx = span.oldStart; oldIdx <= span.oldEnd && !stopped; oldIdx++) {
333
- emitRow("-", oldLines[oldIdx]!, oldHashes[oldIdx], oldIdx + 1);
366
+ for (let oldIdx = span.oldStart; oldIdx <= span.oldEnd && !em.stopped; oldIdx++) {
367
+ em.emitRow("-", oldLines[oldIdx]!, oldHashes[oldIdx], oldIdx + 1);
334
368
  }
335
- for (let newIdx = span.newStart; newIdx <= span.newEnd && !stopped; newIdx++) {
336
- emitRow("+", newLines[newIdx]!, newHashes[newIdx], newIdx + 1);
369
+ for (let newIdx = span.newStart; newIdx <= span.newEnd && !em.stopped; newIdx++) {
370
+ em.emitRow("+", newLines[newIdx]!, newHashes[newIdx], newIdx + 1);
337
371
  }
338
372
  for (const dedup of span.dedupBelow ?? []) {
339
- if (stopped) break;
340
- dedupEmitted = true;
341
- output.push(fmtDedupRow(dedup));
342
- lineNumbers.push(undefined);
373
+ if (em.stopped) break;
374
+ if (em.emitDedup(dedup)) dedupEmitted = true;
343
375
  }
344
376
  oldPos = span.oldEnd + 1;
345
377
  newPos = span.newEnd + 1;
346
378
  }
347
- if (!stopped) {
379
+ if (!em.stopped) {
348
380
  const trailingLen = oldLines.length - oldPos;
349
381
  emitGap(newPos, trailingLen, "trailing");
350
382
  }
351
- if (diffTruncated) {
383
+ if (em.truncated) {
352
384
  output.push(" ...");
353
385
  lineNumbers.push(undefined);
354
386
  output.push(`[diff truncated at ${formatSize(maxBytes)}; use read to see the rest.]`);
@@ -441,45 +473,12 @@ export function genDiff(
441
473
  let oldLineNum = 1;
442
474
  let lastWasChange = false;
443
475
  let firstChangedLine: number | undefined;
444
- let outBytes = 0;
445
- let stopped = false;
446
- let diffTruncated = false;
447
476
  const lineNumbers: (number|undefined)[] = [];
448
477
 
449
- const emitPlain = (line: string, num?: number): void => {
450
- if (stopped) return;
451
- const lineBytes = Buffer.byteLength(line, "utf-8") + 1;
452
- if (outBytes + lineBytes > maxBytes) {
453
- stopped = true;
454
- diffTruncated = true;
455
- return;
456
- }
457
- outBytes += lineBytes;
458
- output.push(line);
459
- lineNumbers.push(num);
460
- };
461
-
462
- const emitRow = (prefix: " " | "+" | "-", line: string, hash: string | undefined, num?: number): void => {
463
- if (stopped) return;
464
- const full = fmtDiffLine(prefix, line, hash);
465
- const rowBytes = Buffer.byteLength(full, "utf-8");
466
- if (rowBytes > maxLineBytes) {
467
- const marker = `[Row is ${formatSize(rowBytes)}, exceeds ${formatSize(maxLineBytes)}; content not shown. Use read to see the full line.]`;
468
- emitPlain(fmtDiffLine(prefix, marker, hash), num);
469
- return;
470
- }
471
- if (outBytes + rowBytes + 1 > maxBytes) {
472
- stopped = true;
473
- diffTruncated = true;
474
- return;
475
- }
476
- outBytes += rowBytes + 1;
477
- output.push(full);
478
- lineNumbers.push(num);
479
- };
478
+ const em = createRowEmitter(output, lineNumbers, maxLineBytes, maxBytes);
480
479
 
481
480
  for (let i = 0; i < parts.length; i++) {
482
- if (stopped) break;
481
+ if (em.stopped) break;
483
482
  const part = parts[i]!;
484
483
  const raw = part.value.split("\n");
485
484
  if (raw[raw.length - 1] === "") raw.pop();
@@ -488,18 +487,18 @@ export function genDiff(
488
487
  if (part.added || part.removed) {
489
488
  if (firstChangedLine === undefined) firstChangedLine = newLineNum;
490
489
  for (let k = 0; k < displayLines.length; k++) {
491
- if (stopped) break;
490
+ if (em.stopped) break;
492
491
  if (part.added) {
493
492
  const hash = effectiveNewHashes[newLineNum - 1];
494
- emitRow("+", displayLines[k]!, hash, newLineNum);
493
+ em.emitRow("+", displayLines[k]!, hash, newLineNum);
495
494
  newLineNum++;
496
495
  } else {
497
496
  const hash = oldContentHashes?.[oldLineNum - 1];
498
- emitRow("-", displayLines[k]!, hash, oldLineNum);
497
+ em.emitRow("-", displayLines[k]!, hash, oldLineNum);
499
498
  oldLineNum++;
500
499
  }
501
500
  }
502
- if (stopped) break;
501
+ if (em.stopped) break;
503
502
  lastWasChange = true;
504
503
  continue;
505
504
  }
@@ -562,25 +561,25 @@ export function genDiff(
562
561
  }
563
562
 
564
563
  if (skipStart > 0) {
565
- emitPlain(" ...", undefined);
564
+ em.emitPlain(" ...", undefined);
566
565
  newLineNum += skipStart;
567
566
  oldLineNum += skipStart;
568
567
  }
569
568
  for (const line of linesToShow) {
570
- if (stopped) break;
569
+ if (em.stopped) break;
571
570
  if (isEllipsisMarker(line)) {
572
- emitPlain(" ...", undefined);
571
+ em.emitPlain(" ...", undefined);
573
572
  newLineNum += skipMiddle;
574
573
  oldLineNum += skipMiddle;
575
574
  continue;
576
575
  }
577
576
  const hash = effectiveNewHashes[newLineNum - 1];
578
- emitRow(" ", line, hash, newLineNum);
577
+ em.emitRow(" ", line, hash, newLineNum);
579
578
  newLineNum++;
580
579
  oldLineNum++;
581
580
  }
582
581
  if (skipTail > 0) {
583
- emitPlain(" ...", undefined);
582
+ em.emitPlain(" ...", undefined);
584
583
  }
585
584
  } else {
586
585
  newLineNum += displayLines.length;
@@ -589,7 +588,7 @@ export function genDiff(
589
588
  lastWasChange = false;
590
589
  }
591
590
 
592
- if (diffTruncated) {
591
+ if (em.truncated) {
593
592
  output.push(" ...");
594
593
  lineNumbers.push(undefined);
595
594
  output.push(`[diff truncated at ${formatSize(maxBytes)}; use read to see the rest.]`);
@@ -1,3 +1,4 @@
1
+ import { formatSize, DEFAULT_MAX_BYTES } from "@earendil-works/pi-coding-agent";
1
2
  import type { NEdit } from "./hashline";
2
3
  import { HASH_SEP } from "./hashline";
3
4
  import type { ReplaceDetails } from "./replace";
@@ -140,9 +141,42 @@ export function isChangeRow(line: string): boolean {
140
141
  return line.startsWith("+") || line.startsWith("-");
141
142
  }
142
143
 
143
- export function withDedupRows(diff: string, lineNumbers: (number | undefined)[], above: string[] | undefined, below: string[] | undefined): { diff: string; lineNumbers: (number | undefined)[] } {
144
- const top = (above ?? []).map(fmtDedupRow);
145
- const bottom = (below ?? []).map(fmtDedupRow);
144
+ function capDedupRows(rows: string[], budget: number): { rows: string[]; bytes: number; omitted: number } {
145
+ const kept: string[] = [];
146
+ let bytes = 0;
147
+ for (const row of rows) {
148
+ const rowBytes = Buffer.byteLength(row, "utf-8") + 1;
149
+ if (bytes + rowBytes > budget) break;
150
+ kept.push(row);
151
+ bytes += rowBytes;
152
+ }
153
+ return { rows: kept, bytes, omitted: rows.length - kept.length };
154
+ }
155
+
156
+ function dedupOmissionNote(omitted: number, maxBytes: number): string {
157
+ return fmtDedupRow(`[${omitted} line(s) not shown again; output capped at ${formatSize(maxBytes)}.]`);
158
+ }
159
+
160
+ export function withDedupRows(diff: string, lineNumbers: (number | undefined)[], above: string[] | undefined, below: string[] | undefined, maxBytes = DEFAULT_MAX_BYTES): { diff: string; lineNumbers: (number | undefined)[] } {
161
+ const topAll = (above ?? []).map(fmtDedupRow);
162
+ const bottomAll = (below ?? []).map(fmtDedupRow);
163
+ if (topAll.length === 0 && bottomAll.length === 0) return { diff, lineNumbers };
164
+ const usedBytes = Buffer.byteLength(diff, "utf-8");
165
+ const fullBudget = Math.max(0, maxBytes - usedBytes);
166
+ const initialTop = capDedupRows(topAll, fullBudget);
167
+ const initialBottom = capDedupRows(bottomAll, Math.max(0, fullBudget - initialTop.bytes));
168
+ const anyOmitted = initialTop.omitted + initialBottom.omitted > 0;
169
+ const reserved = anyOmitted ? Buffer.byteLength(dedupOmissionNote(topAll.length + bottomAll.length, maxBytes), "utf-8") + 1 : 0;
170
+ const budget = Math.max(0, fullBudget - reserved);
171
+ const topCapped = anyOmitted ? capDedupRows(topAll, budget) : initialTop;
172
+ const bottomCapped = anyOmitted ? capDedupRows(bottomAll, Math.max(0, budget - topCapped.bytes)) : initialBottom;
173
+ const top = topCapped.rows;
174
+ const bottom = [...bottomCapped.rows];
175
+ const omitted = topCapped.omitted + bottomCapped.omitted;
176
+ if (omitted > 0) {
177
+ const note = dedupOmissionNote(omitted, maxBytes);
178
+ if (usedBytes + topCapped.bytes + bottomCapped.bytes + Buffer.byteLength(note, "utf-8") + 1 <= maxBytes) bottom.push(note);
179
+ }
146
180
  if (top.length === 0 && bottom.length === 0) return { diff, lineNumbers };
147
181
  if (diff.length === 0) return { diff: [...top, ...bottom].join("\n"), lineNumbers: [...lineNumbers, ...[...top, ...bottom].map(() => undefined)] };
148
182
  const lines = diff.split("\n");
@@ -4,10 +4,9 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
4
4
  import { withFileMutationQueue } from "@earendil-works/pi-coding-agent";
5
5
  import { Type } from "typebox";
6
6
  import { loadHashStore, persistSnapshot, upsertUndo, getUndoEntry, deleteUndo, type UndoRecord } from "./hash-store";
7
- import { servedHashesFromDiff, buildServedMap } from "./served";
8
- import { contentChecksum } from "./hashline/hasher";
9
- import { hashSource } from "./hashline";
10
- import { markServed as markServedScoped, freeAnchors, adoptAnchors, withAnchorSession } from "./anchor-registry";
7
+ import { servedHashesFromDiff, serveRows } from "./served";
8
+ import { lineChecksum } from "./hashline";
9
+ import { freeAnchors, adoptAnchors, withAnchorSession } from "./anchor-registry";
11
10
  import { resolveInCwd, writeAtomic, type FileIdentity } from "./fs-write";
12
11
  import { toLF, stripBOM, restoreEndings, type LineEnding } from "./normalize";
13
12
  import { genDiff, genPatch, spansFromHashes } from "./replace-diff";
@@ -200,16 +199,17 @@ export function regUndo(pi: ExtensionAPI): void {
200
199
  try {
201
200
  const store = await loadHashStore();
202
201
  const undoLines = splitLines(undo.content);
203
- persistSnapshot(store, mutationTargetPath, undo.content, undo.hashes, undoLines.map((line) => contentChecksum(hashSource(line))));
202
+ persistSnapshot(store, mutationTargetPath, undo.content, undo.hashes, undoLines.map(lineChecksum));
204
203
  freeAnchors(mutationTargetPath);
205
204
  adoptAnchors(
206
205
  mutationTargetPath,
207
- new Map(undoLines.map((line, i) => [undo.hashes[i]!, contentChecksum(hashSource(line))])),
206
+ new Map(undoLines.map((line, i) => [undo.hashes[i]!, lineChecksum(line)])),
208
207
  );
209
- markServedScoped(
208
+ serveRows(
210
209
  mutationTargetPath,
211
- buildServedMap(undo.hashes, undoLines, servedHashesFromDiff(undoDiff)),
212
- new Set(undo.hashes),
210
+ undo.hashes,
211
+ undoLines,
212
+ servedHashesFromDiff(undoDiff),
213
213
  );
214
214
  } catch (error) {
215
215
  console.error("Failed to restore hash store snapshot after undo:", error);
package/src/replace.ts CHANGED
@@ -90,7 +90,14 @@ export function hashSpan(hashes: string[], from: string, to: string): [number, n
90
90
  if (a < 0 || b < 0) return undefined;
91
91
  return [Math.min(a, b), Math.max(a, b)];
92
92
  }
93
- async function noteAnchorError(absolutePath: string, error: unknown, scopeHashes: string[], noPersist?: boolean): Promise<void> {
93
+
94
+ export function spanForEdit(originalHashes: string[], from: string, to: string, resultContent: string): DiffSpan | undefined {
95
+ const span = hashSpan(originalHashes, from, to);
96
+ if (!span) return undefined;
97
+ const replacementCount = splitLines(resultContent).length - (originalHashes.length - (span[1] - span[0] + 1));
98
+ return { start: span[0], end: span[1], replacementCount };
99
+ }
100
+ async function noteAnchorError(absolutePath: string, error: unknown, noPersist?: boolean): Promise<void> {
94
101
  if (noPersist === true) return;
95
102
  if (error instanceof RangeStaleError) {
96
103
  adoptAnchors(absolutePath, error.rangeServedMap);
@@ -159,7 +166,7 @@ export async function execPipeline(
159
166
  strictBoundaryDedup,
160
167
  );
161
168
  } catch (error) {
162
- await noteAnchorError(absolutePath, error, originalHashes, options?.noPersist);
169
+ await noteAnchorError(absolutePath, error, options?.noPersist);
163
170
  throw error;
164
171
  }
165
172
 
@@ -179,9 +186,8 @@ export async function execPipeline(
179
186
  );
180
187
 
181
188
  const sortedFixes = dedupRowsFromFixes(anchorResult.autoFixes ?? []);
182
- const pipeSpan = isNoop ? undefined : hashSpan(originalHashes, edit.hash_bounds[0].hash, edit.hash_bounds[1].hash);
183
- const pipeResultCount = splitLines(result).length;
184
- const pipeSpans = pipeSpan ? [{ start: pipeSpan[0], end: pipeSpan[1], replacementCount: pipeResultCount - (originalHashes.length - (pipeSpan[1] - pipeSpan[0] + 1)) }] : undefined;
189
+ const pipeSpan = isNoop ? undefined : spanForEdit(originalHashes, edit.hash_bounds[0].hash, edit.hash_bounds[1].hash, result);
190
+ const pipeSpans = pipeSpan ? [pipeSpan] : undefined;
185
191
  return {
186
192
  path: displayPath,
187
193
  originalNormalized,
package/src/served.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { HASH_CLASS } from "./hashline/alphabet";
2
- import { hashSource } from "./hashline";
3
- import { contentChecksum } from "./hashline/hasher";
2
+ import { lineChecksum } from "./hashline/hash";
3
+ import { markServed } from "./anchor-registry";
4
4
 
5
5
  const SERVED_DIFF_ROW_RE = new RegExp(`^[+ ](${HASH_CLASS})│`);
6
6
 
@@ -26,7 +26,11 @@ export function buildServedMap(
26
26
  const entries: Array<[string, string]> = [];
27
27
  for (const hash of wantedHashes) {
28
28
  const idx = index.get(hash);
29
- if (idx !== undefined) entries.push([hash, contentChecksum(hashSource(fileLines[idx]!))]);
29
+ if (idx !== undefined) entries.push([hash, lineChecksum(fileLines[idx]!)]);
30
30
  }
31
31
  return entries;
32
32
  }
33
+
34
+ export function serveRows(path: string, fileHashes: string[], fileLines: string[], wantedHashes: string[]): void {
35
+ markServed(path, buildServedMap(fileHashes, fileLines, wantedHashes), new Set(fileHashes));
36
+ }
package/src/utils.ts CHANGED
@@ -22,14 +22,16 @@ export function normalizeAnchors(record: Record<string, unknown>): void {
22
22
  }
23
23
  }
24
24
 
25
+ export function normalizeRequest(input: unknown): unknown {
26
+ if (!isRec(input)) return input;
27
+ const record: Record<string, unknown> = { ...input };
28
+ normalizeFilePath(record);
29
+ normalizeAnchors(record);
30
+ return record;
31
+ }
32
+
25
33
  export function makePrepareArguments(): (args: unknown) => any {
26
- return (args) => {
27
- if (!isRec(args)) return args;
28
- const record = { ...args };
29
- normalizeFilePath(record);
30
- normalizeAnchors(record);
31
- return record;
32
- };
34
+ return normalizeRequest;
33
35
  }
34
36
 
35
37
  export function splitLines(text: string): string[] {
@@ -293,7 +295,6 @@ export function decodeStringArray(value: unknown, warnings?: string[], label = "
293
295
  if (candidate === undefined) return undefined;
294
296
  const decoded = decodeArrayText(candidate);
295
297
  if (decoded !== undefined) {
296
- warnings?.push(`[W_BAD_SHAPE] Unwrapped JSON array syntax from a ${label} element.`);
297
298
  return decoded;
298
299
  }
299
300
  if (looksLikeStringArray(candidate)) {
package/src/write-hook.ts CHANGED
@@ -19,10 +19,6 @@ export function findServedHashEcho(content: string, served: ReadonlyMap<string,
19
19
  return searchEcho(splitLines(content), served);
20
20
  }
21
21
 
22
- export function findEditHashEcho(lines: string[], served: ReadonlyMap<string, string> | ReadonlySet<string>): { line: number; hash: string } | undefined {
23
- return searchEcho(lines, served);
24
- }
25
-
26
22
  export async function servedHashEchoDenial(rawPath: string, content: string, cwd: string, signal?: AbortSignal): Promise<string | undefined> {
27
23
  abortIf(signal);
28
24
  const { resolved } = await resolveInCwd(rawPath, cwd);