fix(links): require an indented continuation row before bridging a hard wrap
Two column-0 lines of shell output where the first happens to fill the row (a 40-char path at 40 cols, then src/main.rs) were glued into one bogus path. Claude Code draws continuation rows at least 2 cells in, so require that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a1cc9713a9
commit
c992a7959e
2 files changed
+15
-2
No files matched your search
@@ -88,6 +88,15 @@ describe("joinWrappedRows across Claude Code's hard wraps", () => {
|
||||
expect(joinWrappedRows(b, 1, { cols }).text).toBe(" src/main.rs for details");
|
||||
});
|
||||
|
||||
it("does not glue two column-0 lines together (continuations are indented)", () => {
|
||||
const cols = 40;
|
||||
const row0 = "/var/log/some/very/long/dir/file-aaa.txt";
|
||||
expect(row0.length).toBe(cols);
|
||||
const b = buffer([[row0, false], ["src/main.rs", false]]);
|
||||
expect(joinWrappedRows(b, 0, { cols }).text).toBe(row0);
|
||||
expect(joinWrappedRows(b, 1, { cols }).text).toBe("src/main.rs");
|
||||
});
|
||||
|
||||
it("does not bridge a row that stops short of the edge", () => {
|
||||
const b = buffer([["/tmp/claude-1000/-workspace/abc", false], ["def/notes.md", false]]);
|
||||
expect(joinWrappedRows(b, 1, { cols: 70 }).text).toBe("def/notes.md");
|
||||
|
||||
@@ -51,6 +51,8 @@ const TRAILING_PATH_RUN = /[A-Za-z0-9_.\-~+@/]+$/;
|
||||
const LEADING_PATH_RUN = /^[A-Za-z0-9_.\-~+@/]+/;
|
||||
/** Cells a full row may stop short of the right edge (Ink boxes pad by one or two). */
|
||||
const EDGE_SLACK = 2;
|
||||
/** Claude Code draws its transcript (and so every continuation row) at least this far in. */
|
||||
const MIN_CONTINUATION_INDENT = 2;
|
||||
|
||||
/**
|
||||
* Is `next` the rest of a word that Claude Code broke at the end of `prev`?
|
||||
@@ -62,7 +64,9 @@ const EDGE_SLACK = 2;
|
||||
* It breaks *inside* a word only when the word is longer than the line, which
|
||||
* is exactly what a long `/tmp/claude-<uid>/…` path is. So bridge only when
|
||||
* `prev` runs to (within {@link EDGE_SLACK} of) the right edge and ends in a
|
||||
* path character, `next` starts (after its indent) with one, and the two runs
|
||||
* path character, `next` starts (after an indent of at least
|
||||
* {@link MIN_CONTINUATION_INDENT} cells — so two column-0 lines of shell
|
||||
* output are never glued) with one, and the two runs
|
||||
* together would not have fit on one line — i.e. word wrap could not have
|
||||
* moved the word down whole. That last test is what keeps ordinary prose (a
|
||||
* word that happens to end at the edge above `src/main.rs`) from being glued.
|
||||
@@ -71,7 +75,7 @@ function hardWrapIndent(prev: string, next: string, cols: number): number | null
|
||||
if (prev.length < cols - EDGE_SLACK || prev.length > cols) return null;
|
||||
if (!PATH_CHAR.test(prev[prev.length - 1])) return null;
|
||||
const indent = next.search(/\S/);
|
||||
if (indent < 0 || !PATH_CHAR.test(next[indent])) return null;
|
||||
if (indent < MIN_CONTINUATION_INDENT || !PATH_CHAR.test(next[indent])) return null;
|
||||
const tail = TRAILING_PATH_RUN.exec(prev)?.[0].length ?? 0;
|
||||
const head = LEADING_PATH_RUN.exec(next.slice(indent))?.[0].length ?? 0;
|
||||
if (tail + head <= cols - indent - EDGE_SLACK) return null;
|
||||
|
||||
Reference in new issue
Block a user