Skip to content

feat(extensions): line-exact review navigation with ctx.navigation.revealLine - #727

Merged
elucid merged 7 commits into
mainfrom
elucid/reveal-line
Aug 14, 2026
Merged

feat(extensions): line-exact review navigation with ctx.navigation.revealLine#727
elucid merged 7 commits into
mainfrom
elucid/reveal-line

Conversation

@elucid

@elucid elucid commented Aug 13, 2026

Copy link
Copy Markdown
Member

What

Stacked on the line-highlights PR. Extension navigation gains its missing granularity:

ctx.navigation.revealLine(fileId, "new", 214);

selectHunk was previously the finest target an extension could name. That is fine until the hunk is large: a search extension can highlight a match on line 211, report "line 211" in its status — and the viewport still sits at the hunk anchor, pages above the match. revealLine scrolls the exact line into view and places the host's line cursor on it.

Design

  • One landing spot, host-owned. The revealed line lands at Hunk's existing reveal position — max(2, 25% of viewport) rows of context above — the same policy note-to-note navigation and hunk reveals already use. Deliberately no placement option in the API: the extension states intent ("show this line"), the host owns presentation, and one learned landing zone beats per-extension conventions. Users can nudge afterwards with the align-current-line commands.
  • Guarded like the rest of navigation. Live rather than snapshot, validated against the currently visible files; a hidden or unknown file warns and refuses. A line that currently has no rendered row (line-cursor stops disabled) degrades to the containing hunk — strictly no worse than selectHunk today. Only a line no hunk covers warns.
  • Context lines resolve from either side's number, matching how the review actually draws them.
  • Hardens the pane-actions contract. Implementation surfaced a latent stale-closure hazard: memoized pane hosts keep mount-time action callbacks, and revealLine was the first navigation member to read review state from its closure. It now reads through render-updated refs, so any held reference — memoized pane actions, an async handler navigating after an await — acts on the review as it is now, which is what the documented contract ("actions stay valid for as long as the component is mounted") always promised. Regression-tested at unit, host-PTY, and end-to-end-PTY levels, each demonstrated failing pre-fix.

What this unlocks

Paired with line highlights, marks and navigation compose into "look here":

  • Search that lands the eye, not just the viewport — the active match arrives near the top of the pane with context above it, its characters inverted, regardless of hunk size.
  • Agent-driven walkthroughs. Through the session daemon, an agent explaining a changeset can scroll the user's live terminal to the exact line it is discussing while marking the exact expression — today it can only jump hunk-to-hunk or leave comments and hope the user finds the spot.
  • Diagnostics that take you there — a lint extension can mark the offending column and put the review on that line in one gesture.
  • Anything that reports a position — coverage gaps, secret hits, TODO scanners — can now finish the job instead of naming a line the user scrolls for manually.

Notes

@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hunk-web Ignored Ignored Preview Aug 14, 2026 4:10pm

Request Review

@greptile-apps

greptile-apps Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds line-exact extension navigation and hardens live pane navigation callbacks. The previously reported race between cross-file selection retries and exact-line placement is fixed.

  • Adds ctx.navigation.revealLine(fileId, side, line) to the extension API and pane actions.
  • Resolves rendered lines through current line-cursor geometry, with containing-hunk fallback.
  • Gives explicit line reveals authority over pending selection and file-alignment retries.
  • Adds unit, component, and PTY regression coverage plus API documentation.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the previously reported selection retry is cancelled before exact-line scrolling and the regression path is covered.

Important Files Changed

Filename Overview
src/ui/lib/extensionNavigation.ts Adds guarded, live line navigation with validation and containing-hunk fallback.
src/ui/hooks/useReviewController.ts Connects exact-line targets to current cursor state and host-owned reveal placement.
src/ui/components/panes/DiffPane.tsx Makes explicit line reveals supersede queued selection and file-top alignment work, resolving the previously reported scroll race.
src/ui/lib/lineCursors.ts Adds source-side and line-number lookup over the rendered line-cursor model.
src/extension-api/types.ts Publishes the new line-granular navigation method through command and pane action contracts.
src/ui/components/ui-components.test.tsx Covers the cross-file retry race with a tall-hunk exact-line reveal regression test.
test/pty/extensions-integration.test.ts Verifies exact-line placement through the integrated terminal extension path.

Sequence Diagram

sequenceDiagram
  participant E as Extension
  participant N as Navigation Guard
  participant R as Review Controller
  participant D as DiffPane
  E->>N: revealLine(fileId, side, line)
  N->>N: Validate visible file and coordinates
  N->>R: Reveal matching cursor or select containing hunk
  R->>D: Selection and line-reveal request
  D->>D: Cancel pending selection retries
  D->>D: Scroll exact line to host reveal position
Loading

Reviews (2): Last reviewed commit: "fix(ui): let a revealed line survive the..." | Re-trigger Greptile

elucid added a commit that referenced this pull request Aug 14, 2026
A cross-file revealLine changes the selected file, and the selection reveal
that schedules runs a zero-delay retry after this layout effect — dragging the
viewport from the named line back to the hunk anchor, 51 rows away in the
regression test. The neighbouring explicit-alignment effect already retires
that work through supersedePendingSelectionReveal; a named line is just as
authoritative, so it does the same.

Reported by Greptile on #727.
elucid added a commit that referenced this pull request Aug 14, 2026
A cross-file revealLine changes the selected file, and the selection reveal
that schedules runs a zero-delay retry after this layout effect — dragging the
viewport from the named line back to the hunk anchor, 51 rows away in the
regression test. The neighbouring explicit-alignment effect already retires
that work through supersedePendingSelectionReveal; a named line is just as
authoritative, so it does the same.

Reported by Greptile on #727.
@elucid
elucid force-pushed the elucid/reveal-line branch from e616156 to f756f53 Compare August 14, 2026 03:59
@elucid
elucid changed the base branch from elucid/line-highlights to main August 14, 2026 03:59
@elucid elucid closed this Aug 14, 2026
@elucid elucid reopened this Aug 14, 2026
@elucid
elucid force-pushed the elucid/reveal-line branch from f756f53 to cf68c9f Compare August 14, 2026 04:50
@elucid
elucid changed the base branch from main to fix/line-highlight-review-followups August 14, 2026 04:50
@elucid
elucid changed the base branch from fix/line-highlight-review-followups to main August 14, 2026 15:52
elucid added 7 commits August 14, 2026 11:55
selectHunk was the finest target an extension had, so a match on line 211
of a 300-line hunk landed the viewport on the hunk anchor, pages above the
thing the extension had just marked. revealLine names the line itself, and
the guard refuses a hidden file, a side outside the two diff sides, and a
line number no patch could have written.
The line-cursor machinery already resolves a measured row and scrolls to
it, but its stepping policy moves the minimum distance, which leaves a
jump to a line that happens to be on screen exactly where it was. Give
the reveal request an explicit placement so a jump lands the line where
hunk and note reveals land theirs, and resolve (side, line) against the
stops measured for every visible file so a cross-file jump needs no
second pass. A line no measured row covers degrades to its hunk.
…ssion

The regression this guards is spatial, so assert it where the user sees
it: the marked line is off screen while the hunk anchor is selected, and
one revealLine command puts it in the top half of the terminal.
An extension author needs to know two things the signature does not say:
where the revealed line lands (the app's own reveal position, not the
caller's choice) and what happens to a line the stream cannot draw. The
pack check exercises the new method so the published surface stays
honest, and the proposal's companion-gap section now records the design
rather than proposing it.
The session's first deferred search jump landed on the hunk anchor - the
exact behavior revealLine exists to fix. A memoized extension pane keeps
its mount-time actions (the API documents them as valid while mounted),
and the revealLine minted on that first render closed over App's cursor
state before the diff pane had published any measured stops, so
findLineCursorAt saw an empty list and silently degraded to the hunk
fallback. selectHunk never showed this because it only dispatches through
stable store callbacks; revealLine is the first navigation member that
reads data. Read the cursors and visible files through refs so any held
reference - a memoized pane's actions, an async handler after an await -
resolves against the review as it is now.
A cross-file revealLine changes the selected file, and the selection reveal
that schedules runs a zero-delay retry after this layout effect — dragging the
viewport from the named line back to the hunk anchor, 51 rows away in the
regression test. The neighbouring explicit-alignment effect already retires
that work through supersedePendingSelectionReveal; a named line is just as
authoritative, so it does the same.

Reported by Greptile on #727.
The proposal was pre-implementation design rationale — the kind of
document that belongs in a PR description, not shipped docs — and it
kept drawing rebase conflicts as the stack evolved. Everything it
documented that users need survives in the real documentation: the
line-highlight and revealLine API contracts in docs/extensions.md and
website/src/content/docs/docs/extend/extension-api.md, the subsystem
map in docs/extension-architecture.md, and the corrective notes from
the post-merge review (zero-width ranges paint nothing; transparent
cells resolve tints against an assumed background) in those same pages
and src/extension-api/types.ts.
@elucid
elucid force-pushed the elucid/reveal-line branch from cf68c9f to 026dd45 Compare August 14, 2026 16:10
@elucid
elucid merged commit b941e0f into main Aug 14, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant