diff options
| author | Akira TSURUDA <jas39.gripen@gmail.com> | 2026-08-01 22:40:57 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-08-01 16:40:57 +0300 |
| commit | f459a0df315e0963aec5e4dcf2fc394a252ea320 (patch) | |
| tree | d355041531395cdb0325b2fa5311d00fa2e45d32 | |
| parent | f99647b2c2d7fbfd11269330a25ea4f9be59bafa (diff) | |
fix: expand copy-mode word-end window past a final wide glyph (#2145)
point_is_final_atom compared an atom's terminating column against a
word-motion target's starting column. The two are equal for narrow
cells but never for wide ones, so the read window stopped expanding
and copy-mode `e` halted at the soft-wrap boundary on CJK text.
Co-authored-by: Can Celik <ogulcancelik@gmail.com>
| -rw-r--r-- | src/pane/terminal.rs | 32 |
1 files changed, 28 insertions, 4 deletions
diff --git a/src/pane/terminal.rs b/src/pane/terminal.rs index 3f8d8aea..e464554c 100644 --- a/src/pane/terminal.rs +++ b/src/pane/terminal.rs @@ -855,14 +855,14 @@ impl RetainedTextBuffer { } fn point_is_final_atom(&self, point: TerminalTextPoint) -> bool { + // Word motion targets are atom start points, so compare against the + // final atom's start point. Comparing against `end_col` would never + // match a wide glyph, whose end column is one past its start. self.atoms .iter() .rev() .find(|atom| atom.point.is_some()) - .is_some_and(|atom| { - atom.point - .is_some_and(|start| start.row == point.row && atom.end_col == point.col) - }) + .is_some_and(|atom| atom.point == Some(point)) } } @@ -3291,6 +3291,30 @@ mod tests { ); } + #[test] + fn live_terminal_word_end_expands_through_a_long_wide_soft_wrap() { + let (tx, _rx) = mpsc::channel(4); + let mut terminal = crate::ghostty::Terminal::new(2, 3, 200).unwrap(); + let word = "界".repeat(66); + terminal.write(word.as_bytes()); + let pane = PaneTerminal::new(GhosttyPaneTerminal::new(terminal, tx).unwrap()); + let text_match = pane.search_text_matches(&word, true)[0]; + + // The word end sits on the head cell of the final wide glyph, past the + // initial read window, so the window has to expand to reach it. + assert_eq!( + pane.word_motion_target( + text_match.start.row, + text_match.start.col, + TerminalWordMotion::NextEnd, + ), + Some(TerminalTextPoint { + row: text_match.end.row, + col: 0, + }) + ); + } + fn current_palette_color(pane: &GhosttyPaneTerminal, index: u8) -> crate::ghostty::RgbColor { let mut core = pane.core.lock().unwrap(); let GhosttyPaneCore { |
