Repository navigation
Copying text from an attached pane includes trailing spaces on every line (xshell; tmux does not) #217
Description
Summary
When selecting and copying text from an rmux-attached pane in xshell, every
copied line is padded with trailing spaces up to the terminal column width. The
same selection under tmux in the same terminal does not include those
trailing spaces. Reproduced on rmux 0.10.0.
Steps to reproduce
- In xshell, run rmux (or rmux attach -t ). Terminal is wide
(e.g. ~200 columns). - Run any command that prints short lines, e.g. ifconfig enp0s8 .
- Select the output with the mouse and copy.
- Paste into any editor.
Expected behavior
Each pasted line ends right after its content, with no trailing whitespace
(what tmux produces in the identical terminal).
Actual behavior
Every line is padded with spaces to the full terminal width. Example (trailing
spaces shown as · ):
tony@tony-2204-dt:~$ ifconfig enp0s8···············································
enp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500·······················
inet 192.168.1.20 netmask 255.255.255.0 broadcast 192.168.1.255··········
Environment
• rmux: 0.10.0 (also reproduced on latest main , pulled today)
• Outer terminal: xshell
• OS: Ubuntu 22.04 ( tony-2204-dt )
• Control: tmux in the same xshell session → no trailing spaces
Root cause from AI, just FYI
rmux renders each internal grid row to the outer terminal using
GridRenderOptions { include_empty_cells: true, trim_spaces: false } . This
materializes trailing blank cells as literal space ( 0x20 ) bytes out to the
pane's full column width. The EL sequence ( \x1b[K , erase-to-end-of-line) that
would clear those spaces is only emitted under a narrow optimization gate, so in
the common case the literal spaces survive in the outer terminal's grid and a
selection copy includes them.
tmux, by contrast, clears to end-of-line on every rendered row, so the outer
terminal's trailing cells stay empty and copies are clean.
Full-redraw path — crates/rmux-server/src/renderer/pane_screen.rs
• The clean path is gated by sparse_full_width_clear (line 146), which requires
pane.x == 0 && pane.cols == terminal.cols && pane_default_style(...).is_none()
&& copy_mode_overlays.is_none() .
• include_empty_cells: !sparse_full_width_clear and trim_spaces: false
(lines 162–163) — when the gate is false, trailing blank cells become literal
spaces.
• \x1b[K is emitted only inside if sparse_full_width_clear { ... }
(lines 213–218).
Incremental render path — crates/rmux-server/src/renderer/pane_delta.rs
• Also uses include_empty_cells: true, trim_spaces: false (lines 204–205).
• \x1b[K is emitted only when compact_plain_ascii_line_delta(...) returns
clear_to_eol == true (lines 301–307). Per the tests, that is limited to the
full-width-line-shrinking case ( pane_delta_clears_full_width_line_when_plain_ascii_shrinks ,
line 1213) — ordinary output lines do not clear to EOL.
Supporting evidence
• The cell-level renderer writes spaces for blank trailing cells when
include_empty_cells: true + trim_spaces: false
( crates/rmux-core/src/grid/cell.rs , around lines 944–946 and 1349).
• The team already exercises the EL mechanism in
crates/rmux-server/src/renderer/erase_wrap_tests.rs:36
( \x1b[2;1H\x1b[0m\x1b[K ) — but only on the sparse path.
Suggested fix
Decouple "clear trailing cells to end-of-line" from the "pane is full-width /
has no style" optimization. In both pane_screen.rs and pane_delta.rs , emit
\x1b[K at the end of every rendered row — applying the pane's default
background style beforehand and resetting after, exactly as tmux does. This keeps
the outer terminal's trailing cells empty (clean copies) while still allowing
background-color fills to render correctly. When include_empty_cells must stay
true for background fill, the literal fill spaces should be followed by an EL
so they do not survive in the outer terminal's grid.
Activity