Skip to content

feat: open a changed file whole, highlighted, with the change in the gutter - #29

Merged
JacobAtchley merged 1 commit into
mainfrom
feat/file-view
Sep 19, 2026
Merged

JacobAtchley merged 1 commit into
mainfrom
feat/file-view

Conversation

@JacobAtchley

Copy link
Copy Markdown
Owner

The diff pane shows hunks — the changed lines and three either side. That is the right amount of code to skim a change by and the wrong amount to judge one by: the reason a change is wrong is usually in the part of the file the hunk does not reach — the caller two hundred lines up, the early return it now skips.

enter on a changed file opens it whole.

   1 package tail
   2
   3 import "context"
   4
   5 func tail(ctx context.Context) error {
-  6     go poll()
+  6     defer close(ch)
    ● unresolved  line 6
    Dev Example
      does this still race?
+  7     go poll(ctx)
   8     return nil
   9 }

Per the design: the change goes in the gutter and the code keeps its syntax colours, rather than the diff pane's whole-line green and red. d takes the marks away and leaves the file. Removed lines sit where they were, dimmed, and go with d.

It costs no request

buildFileDiff already reads both sides of the file to diff them and threw the text away. It keeps the side the view shows now — the new one, or the old one for a deleted file, which is still worth opening and reads as entirely removed.

composeFile is the piece worth reviewing

Both line numbers are carried per row, because the two sides advance at different rates. A row numbered from the wrong one puts a + against code nobody touched — which looks entirely plausible on screen and is only catchable by asserting the numbers directly, so that is what the tests do, rendering each row as mark old new text and comparing whole layouts.

The old-side number for unchanged lines outside a hunk is derived from the shift each hunk leaves behind. The test caught the first implementation, which assumed the two sides stayed level and numbered most of a changed file wrongly.

Deletions splice in at the new-side position they were removed from; ones past the last surviving line have nothing to sit in front of and are collected at the end rather than dropped.

Highlighting

Chroma, lexed by the file's name, style chosen from lipgloss.HasDarkBackground — not by asking the terminal, which is the trap the glamour fix closed earlier in this session. That is now guarded by a test scanning the whole package for termenv. and WithAutoStyle outside comments.

Chroma re-emits its style on every line and closes each with a reset, even mid-token, so splitting its output by line is safe and a block comment's third line keeps its colour — verified before designing around it. A language with no lexer is shown plainly rather than guessed at, and a lexer that somehow changed the line count is discarded: every marker and comment is indexed by line, and a silent shift would be a confidently wrong pane rather than an obviously broken one.

Not a terminal means no highlighting at all, which keeps tests comparing against raw source.

Also

Threads already flow detail → diff → file from #25, so a review comment appears under its line here too. enter refuses to open a file whose content has not arrived, or one declined as binary or over the size cap, rather than pushing an empty pane.

Tests

composeFile: no hunks, insertions and deletions together, untouched lines outside the hunks, two hunks with a gap, a deletion at the end of the file, a deleted file, no trailing newline, an empty file, and every new line carrying a number.

highlightLines: one entry per line including across a multi-line token, the text intact under the styling, an unknown extension, an empty file, the style choice, and the terminal-query guard.

FileView: the whole file rather than the hunk, gutter marks, line numbers, the toggle both ways, the cursor moving and stopping at both ends, the anchor line a comment would use (and a removed line offering none), threads from this file only, esc, the title, a file with no hunks, enter wired from the diff pane, and enter refusing before the content lands.

Full suite and go vet pass. README documents it.

Inline feedback is the follow-up and sits on anchorLine.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KT78DJemENKXRQsfKzZPmn

…gutter

The diff pane shows hunks — the changed lines and three either side.
That is the right amount of code to skim a change by and the wrong
amount to judge one by: the reason a change is wrong is usually in the
part of the file the hunk does not reach, the caller two hundred lines
up or the early return it now skips.

enter on a changed file opens it whole. The change is marked in the
gutter and the code is syntax highlighted, rather than the diff pane's
approach of colouring whole lines by what happened to them — three
lines of context are not worth reading as code, a whole file is. d
takes the marks away and leaves the file.

It costs no request. buildFileDiff already read both sides of the file
to diff them and threw the text away; it keeps the side the view shows
now.

composeFile lays the hunks over the file and is the piece worth
reading. Both line numbers are carried per row, because the two sides
advance at different rates: a row numbered from the wrong one puts a +
against code nobody touched, which looks entirely plausible on screen.
Removed lines are spliced back in at the position they were removed
from, so a rewrite reads as a rewrite rather than as an unexplained
pair of additions. The old-side number for unchanged lines outside a
hunk is derived from the shift each hunk leaves behind — the test for
that caught the first implementation, which assumed the sides stayed
level.

Highlighting is chroma, lexed by the file's name, with the style chosen
from lipgloss rather than from asking the terminal — the trap the
glamour fix closed, now guarded by a test over the whole package. A
language with no lexer is shown plainly rather than guessed at, and a
lexer that somehow changed the line count is discarded, since every
marker and comment is indexed by line and a silent shift would be a
confidently wrong pane.

Threads already flow detail → diff → file, so a review comment appears
under the line it was written against here too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KT78DJemENKXRQsfKzZPmn
@JacobAtchley
JacobAtchley merged commit 53e96bb into main Sep 19, 2026
3 checks passed
@JacobAtchley
JacobAtchley deleted the feat/file-view branch September 19, 2026 19:38
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