feat(terminal): bare root-level filenames are clickable links - #177
Conversation
|
@claude review |
|
Claude finished @ghackett's task in 3m 50s —— View job Review complete
(No Nicely done overall — the regex reasoning is careful and the docstrings actually explain the why (boundary discipline, longest-first ordering, the name-set-not-monitor-event debounce guard). I traced 1. The PR body's testing claims outrun what's actually in the diff. 2. "Debounced 500ms" is actually a leading-edge throttle, not a debounce. 3. Home-directory fallback widens 4. Minor: I wasn't able to actually execute Nothing here blocks merging — #1 and #3 are worth a sentence of clarification, #2 is a wording nit. |
|
Addressed the review in 0734a86:
|
The path grammar demands a slash, so root-level references (README.md, pyproject.toml) never underlined. A shape can't take them — bare-filename shapes over-match prose — but an alternation of the names actually sitting at the project root can: the underline then only ever lands on a name that exists. bare_names_pattern builds that grammar (same boundary discipline and :line[:col] suffix as FILE_PATTERN, longest name first so an entry extending another wins), and _RootNameLinks keeps it registered per terminal — built on first map from the tab's link_root (the directory the editor opens at), rebuilt via a debounced directory monitor when the root's name set changes. Files only: root directory names are everyday prose words, and `docs/` already belongs to the path grammar. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBnWBFL3yHBnJy4kupKZgk
…fallback Addresses the PR 177 review: the _RootNameLinks wiring check now lives at scripts/check_root_name_links.py (a script, not a pytest test — conftest blocks the GTK namespaces suite-wide so local runs reproduce CI); the class docstring now calls the 500ms coalescing what it is, a leading-edge throttle chosen so steady churn can't starve the refresh; and link_root's comment spells out that inheriting editor_root's HOME fallback is deliberate — the whole tab is rooted at home in that case, so bare names follow the editor and quick open rather than going dark. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBnWBFL3yHBnJy4kupKZgk
0734a86 to
7c66860
Compare
Problem
Clickable file references (#173) require a slash:
collins/foo.py:12underlines, but root-level references likeREADME.mdorpyproject.toml— which Claude emits constantly — stay inert. The slash requirement exists for a good reason: a bare-filename shape (\w+\.\w+) would underlinee.g.,Node.js, and1.2.3in ordinary prose, and VTE has no per-match callback that would let the hover underline consult the filesystem.Approach
Match the actual names at the project root instead of a shape.
linkpatterns.bare_names_pattern(names)builds a PCRE2/re-shared alternation of literal names with the same boundary discipline asFILE_PATTERN::line[:col]suffix after;README.md.sheds its sentence period whileREADME.mdxnever half-matches an entryREADME.md;README.md.bakbeats its own prefix;:or/are unboundable/ambiguous and stay out (as agreed, spaces remain out of scope)._RootNameLinkskeeps the tag registered on each terminal (agent tab and shell-panel tabs alike):link_root— the directory the editor opens at (the tab's cwd, falling back to home — the fallback is deliberate: in that case the whole tab, editor and quick open included, is already rooted at home);Gio.FileMonitoron the root rebuilds the tag when the name set changes. Change events coalesce on a 500ms timer armed by the first event — a leading-edge throttle rather than a trailing-edge debounce, so steady churn can't starve the refresh; content-only writes never churn the tag;docs,tests) are everyday prose words, anddocs/already belongs to the slashed grammar;"file"kind end-to-end:resolve_file_referencealready handles bare relative candidates, so images still hit the lightbox and text files open in the editor at the referenced line.Performance: VTE evaluates match regexes only against the row under the pointer, and PCRE2 compiles a literal alternation efficiently — a typical root is 15–40 entries, a few hundred bytes of pattern.
Testing
bare_names_pattern(matching, suffixes, trailing-punctuation shedding, half-match boundaries, longest-first ordering, metacharacter escaping, unboundable-name filtering) — these run on CI.scripts/check_root_name_links.py(committed) covers the stateful side CI can't reach: map-time root resolution against a realTerminalTab, tag registration, monitor-driven rebuild on a created file, no rebuild on content-only writes, and monitor teardown. It's a script rather than a pytest test becausetests/conftest.pydeliberately blocks the GTK namespaces suite-wide so local runs reproduce CI (which has no gir packages or display). Run it on a dev machine; it shows no window and spawns no agent.re.No screenshots: the change is a hover underline over live terminal rows, which an in-process snapshot can't show without synthetic pointer motion.
🤖 Generated with Claude Code
https://claude.ai/code/session_01KBnWBFL3yHBnJy4kupKZgk