Skip to content

Copy the selected diff lines from the focused main view - #6034

Merged
stefanhaller merged 2 commits into
masterfrom
copy-diff-lines-from-main-view
Oct 5, 2026
Merged

stefanhaller merged 2 commits into
masterfrom
copy-diff-lines-from-main-view

Conversation

@stefanhaller

Copy link
Copy Markdown
Collaborator

Sixth PR in the stack of PRs towards staging hunks directly from the main view. This one is stacked on #6033.

ctrl+o in a focused diff copies the selected lines to the clipboard.

What lands on the clipboard is the corresponding lines of the plain diff, not the renderer's own output. You don't want delta's box drawing or difftastic's columns in a paste, and taking the lines from the diff also removes the question of whether a renderer preserved the +/- column. The selected rows are resolved to identities, the plain diff of just those files is fetched, and the contiguous span between the first and last matched line of each file is copied. Lines a renderer chose not to display are included, so the result is a valid patch fragment.

Headers count as selected lines. A hunk header names the first line of its hunk, and a selection touching any row of a file header takes the whole header. So selecting a hunk together with its header copies a patch fragment rather than stripped code. That is how the staging panel's copy behaved.

The rows above the diff — a commit's message and its diffstat — belong to no file, and are copied as they stand on screen. A selection spanning the message and the diff contributes both halves. The +/- column is dropped only when everything came out of the diff, since a message line may well begin with a -.

Known gap: a \ No newline at end of file marker at the very edge of a selection is not copied. It names no line, so it can't be the endpoint of a span.

@stefanhaller stefanhaller added the enhancement New feature or request label Sep 20, 2026
@stefanhaller
stefanhaller force-pushed the copy-diff-lines-from-main-view branch from d0b6f37 to a258a6c Compare September 20, 2026 17:51
@stefanhaller
stefanhaller force-pushed the copy-diff-lines-from-main-view branch 2 times, most recently from 2a67d47 to b45894e Compare September 21, 2026 13:46
@stefanhaller
stefanhaller force-pushed the copy-diff-lines-from-main-view branch 3 times, most recently from d73c978 to 6c70748 Compare September 27, 2026 10:41
@stefanhaller
stefanhaller force-pushed the copy-diff-lines-from-main-view branch from 6c70748 to 33e53a7 Compare September 27, 2026 16:27
@stefanhaller
stefanhaller force-pushed the copy-diff-lines-from-main-view branch from 33e53a7 to b6fa82c Compare October 1, 2026 15:28
@stefanhaller
stefanhaller force-pushed the copy-diff-lines-from-main-view branch from b6fa82c to f454f11 Compare October 4, 2026 13:48
@stefanhaller
stefanhaller force-pushed the copy-diff-lines-from-main-view branch from f454f11 to 9bb7552 Compare October 4, 2026 15:01
@stefanhaller
stefanhaller force-pushed the copy-diff-lines-from-main-view branch from 9bb7552 to 7d0ebc9 Compare October 4, 2026 17:01
@stefanhaller
stefanhaller force-pushed the copy-diff-lines-from-main-view branch 4 times, most recently from f4a75ed to 21eab20 Compare October 5, 2026 08:50
Base automatically changed from show-staged-changes-in-lower-pane to master October 5, 2026 09:01
The commit files context works out the two ends to diff from the ref (or
range of refs) it was entered for. The panels that hold those refs
themselves — commits, sub-commits, stash, reflog — are about to need the
same two ends, to hand out the diff behind what they render into the
focused main view. Pull the derivation out of the context so they can ask
for it rather than each spelling out the parent-of-from rule again.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The main view shows a diff renderer's picture of a diff, and that is not
what you want on your clipboard: a renderer may drop the +/- column, move
the line numbers into a gutter, group a hunk's deletions before its
additions, or leave lines out altogether. So copying takes the lines from
the diff itself, locating them by the identity of the selected rows.

Only the panel that produced the diff can produce it again, though: the
working tree's staged or unstaged side, a commit's, a stash entry's. The
new seam is there for that. It asks per file, so that copying three lines
of a commit's diff doesn't fetch the whole of it, and it will grow the
actions on a selection as staging and patch building arrive.

The clipboard gets the run of diff lines from the first selected line of
a file to the last, so that lines the renderer hid come along and the
result still reads as a diff. Headers count as selected lines too. A hunk
header names the first line of its hunk, so it is looked for the way a
line of the file is. A file header names no line at all, and a rendering
may draw it over any number of rows, so a selection touching one of them
takes the whole header.

As in the patch explorers, a selection that is all additions or all
deletions loses its +/- column, ready to be pasted into code. One that
reaches into a header keeps the columns, and what comes out is a patch
fragment rather than lines of code.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@stefanhaller
stefanhaller force-pushed the copy-diff-lines-from-main-view branch from 21eab20 to 5d1489b Compare October 5, 2026 09:01
@stefanhaller
stefanhaller merged commit dfdb893 into master Oct 5, 2026
12 checks passed
@stefanhaller
stefanhaller deleted the copy-diff-lines-from-main-view branch October 5, 2026 09:20
stefanhaller added a commit that referenced this pull request Oct 5, 2026
#6035)

Seventh PR in the stack of PRs towards staging hunks directly from the
main view. This one is stacked on #6034.

This is the change the series is building towards. Focus a file's diff
and act on it there, without entering a separate staging panel.

In the files panel's focused main view:

- `space` stages the selected line, range or hunk from the upper pane,
and unstages it from the lower one. Selections spanning several files
work.
- `d` discards the selection. This works the same as in the staging
panel: discarding from the staged pane unstages; discarding from the
unstaged pane reverse-applies, with the usual confirmation.
- `E` edits the selected hunk in your editor and applies what you save.
- The commit keys work there too, including finding a fixup base.

After an action the selection advances to the next change, and the focus
follows the lines into the other pane when the one you acted in goes
away. If your selection covered every change of a file, the file itself
is staged, so staging all of a deleted file's content gives you `D`
rather than `MD`. Applying a file's entire diff to the index amounts to
`git add` on the file.

**Renderers that can't be acted on fall back to git's own diff.**
Whether a renderer can be acted on is settled by running it on empty
input and looking for the OSC 1717 handshake, and the verdict is cached
per renderer signature. If a renderer restructures the diff but doesn't
support OSC 1717, a raw diff replaces it the moment you focus the view,
so staging always works; the same renderer keeps rendering the unfocused
view as before. `rawGit` entries are probed the same way, git announcing
itself for exactly the formats it describes; this prepares us for a
future git version that supports OSC 1717 for `--word-diff` or
`--color-words`. Entries with no arguments are already raw and skip the
probe.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant