Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions packages/client/src/ui/lineRef.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,4 +193,48 @@ describe("resolveLineRefPath", () => {
}),
).toBe("nested/src/app.ts");
});

it("resolves a bare filename whose basename is unique in the repo", () => {
expect(
resolveLineRefPath({
rawPath: "Main.hs",
repoRoot,
cwd: repoRoot,
repoPaths,
}),
).toBe("packages/a/src/Main.hs");
});

it("returns null when the basename is ambiguous", () => {
expect(
resolveLineRefPath({
rawPath: "app.ts",
repoRoot,
cwd: repoRoot,
repoPaths,
}),
).toBeNull();
});

it("falls back to basename when a slash-containing path doesn't match", () => {
expect(
resolveLineRefPath({
rawPath: "wrong/Main.hs",
repoRoot,
cwd: repoRoot,
repoPaths,
}),
).toBe("packages/a/src/Main.hs");
});

it("prefers an exact path candidate over the basename fallback", () => {
expect(
resolveLineRefPath({
rawPath: "src/app.ts",
repoRoot,
cwd: repoRoot,
repoPaths,
}),
).toBe("src/app.ts");
});
});
30 changes: 28 additions & 2 deletions packages/client/src/ui/lineRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,13 @@ function hasRefBoundary(text: string, index: number): boolean {
* falls back to repo-relative interpretation only.
* - `repoPaths`: live `fsListAll` paths — repo-relative, no leading
* `/`. The resolver only returns a path that's actually in this
* set. */
* set.
*
* When path-based candidates miss, falls back to a basename match —
* compiler output often prints just `Foo.hs:42` without the
* `src/lib/` prefix (#898). The fallback only fires when the
* basename is unique in the repo; ambiguous matches stay null since
* opening the wrong file is worse than the toast. */
export function resolveLineRefPath(args: {
rawPath: string;
repoRoot: string;
Expand All @@ -115,7 +121,27 @@ export function resolveLineRefPath(args: {
for (const candidate of candidates(args)) {
if (set.has(candidate)) return candidate;
}
return null;
return resolveByBasename(args.rawPath, args.repoPaths);
}

function resolveByBasename(
rawPath: string,
repoPaths: readonly string[],
): string | null {
const target = basename(rawPath);
if (target === "") return null;
let unique: string | null = null;
for (const p of repoPaths) {
if (basename(p) !== target) continue;
if (unique !== null) return null;
unique = p;
}
return unique;
}

function basename(path: string): string {
const i = path.lastIndexOf("/");
return i < 0 ? path : path.slice(i + 1);
}

function* candidates(args: {
Expand Down
11 changes: 11 additions & 0 deletions packages/tests/features/file-ref-link.feature
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ Feature: File-ref autolinking in terminal
And I trigger the terminal file-ref link "range.txt:2-4"
Then the selected file should show content "three"

Scenario: Bare filename resolves when its basename is unique in the repo
When I run "git init /tmp/kolu-file-ref-898 && cd /tmp/kolu-file-ref-898"
And I run "git commit --allow-empty -m init"
And I run "mkdir -p src/lib && printf 'alpha\nbeta\ngamma\n' > src/lib/notes.txt"
And I run "echo 'see notes.txt:2 for the line'"
And I trigger the terminal file-ref link "notes.txt:2"
Then the right panel should be visible
And the Code tab should be active
And the Code tab mode should be "browse"
And the selected file should show content "beta"

Scenario: Re-clicking the same file-ref after closing the panel re-selects the line
When I run "git init /tmp/kolu-file-ref-861-reclick && cd /tmp/kolu-file-ref-861-reclick"
And I run "git commit --allow-empty -m init"
Expand Down