diff options
| author | user <user@clank> | 2026-08-17 14:02:54 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-17 14:02:54 +0200 |
| commit | d90aadfc636cf6b655ed5e5488944b5385b451ec (patch) | |
| tree | 0d5504febde4e13087610b2e81c2616c3fba5fa8 /src | |
| parent | sl0ppty: double-click a word to select it (diff) | |
| download | slosh-d90aadfc636cf6b655ed5e5488944b5385b451ec.tar.gz slosh-d90aadfc636cf6b655ed5e5488944b5385b451ec.tar.xz slosh-d90aadfc636cf6b655ed5e5488944b5385b451ec.zip | |
sl0ppty: a wrapped line is one line when you copy it
Select a line too long for the pane and the clipboard came back with a `\n` in
the middle of it, at whatever column the pane happened to end. Paste that into a
shell and it runs half a path.
`pane_selection_text` set `trim` and left `unwrap` at whatever a zeroed struct
gives, which is false. lib-vt's header names the exact trio the clipboard wants --
plain output with `unwrap` and `trim` both true, matching Ghostty's own
`Screen.selectionString()` -- and we had two of the three. A row boundary in the
grid is not a newline in the text: a long line occupies two rows and is still one
line, and where it breaks is a fact about how wide the pane is rather than about
what was copied.
Hard newlines are untouched, which is the half a fix for this could easily have
broken: lib-vt joins only the rows that are continuations, so three separate lines
still come back as three lines. Both directions are now asserted, because "copy
joins everything" would be a worse bug than the one being fixed and would look
identical from the inside.
`emit` is now set explicitly as well, to `GHOSTTY_FORMATTER_FORMAT_PLAIN`. It is
already zero, so this changes nothing today -- but this is the API where a zeroed
field means "whatever the enum happens to start with" (DESIGN.md's note on
`ghostty_terminal_set`), and a silently defaulted field is how the wrap flag came
to be missing. There is one call site, checked.
Verified: three checks in `tests/test_clipboard.py` -- a 50-character line printed
into a 40-column pane, dragged across the row boundary, arrives as the string that
was printed with no newline in it; three short lines keep their two newlines; and a
double-click on a word in a wrapped line brings back no newline either. 46 checks in
that file, 1795 overall, all green.
Diffstat (limited to 'src')
| -rw-r--r-- | src/pane.c | 16 |
1 files changed, 16 insertions, 0 deletions
@@ -1026,6 +1026,22 @@ bool pane_select_word(pane_t *p, uint16_t x, uint16_t y, const char *seps) { char *pane_selection_text(pane_t *p) { GhosttyTerminalSelectionFormatOptions opts = GHOSTTY_INIT_SIZED(GhosttyTerminalSelectionFormatOptions); + /* Plain text, and both flags on: lib-vt documents this exact trio as the + * clipboard behaviour, and getting it wrong is invisible until somebody pastes. + * + * `unwrap` is the one that matters. A row boundary in the grid is not a + * newline in the text -- a line too long for the pane occupies two rows and is + * still one line -- so without this, copying a wrapped path or a wrapped URL + * pasted a `\n` into the middle of it. The newline came from how wide the pane + * happened to be, which is not a fact about what was copied. Hard newlines are + * untouched: lib-vt joins only the rows that are continuations. + * + * `emit` is set rather than left zero even though PLAIN *is* zero. This is the + * API where a zero means "whatever the enum happens to start with" (see + * DESIGN.md on ghostty_terminal_set), and a silent default is how the wrap flag + * came to be missing in the first place. */ + opts.emit = GHOSTTY_FORMATTER_FORMAT_PLAIN; + opts.unwrap = true; opts.trim = true; uint8_t *ptr = NULL; size_t len = 0; |
