From 44a3c1dae916241d3c8e1a1268373b9131af8e5b Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 18 Jul 2025 10:05:32 +0200 Subject: [PATCH] fix(grid): account for wide characters when double-triple mouse clicking (#4302) * fix(grid): account for wide characters when double-triple mouse clicking * fix(e2e): moar race conditions * docs(changelog): add PR --- CHANGELOG.md | 1 + src/tests/e2e/cases.rs | 8 ++++++-- zellij-server/src/panes/grid.rs | 23 +++++++++++++---------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d6c6987..f4d17826 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * fix: don't show popups in the welcome screen (https://github.com/zellij-org/zellij/pull/4294) * fix: reap processes when using an external clipboard tool (https://github.com/zellij-org/zellij/pull/4298) * fix: out of bounds mouse release events (https://github.com/zellij-org/zellij/pull/4300) +* fix: account for emoji/widechars when double/triple-clicking to mark words (https://github.com/zellij-org/zellij/pull/4302) ## [0.42.2] - 2025-04-15 * refactor(terminal): track scroll_region as tuple rather than Option (https://github.com/zellij-org/zellij/pull/4082) diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs index c122f9f8..c0e50056 100644 --- a/src/tests/e2e/cases.rs +++ b/src/tests/e2e/cases.rs @@ -101,10 +101,14 @@ fn account_for_races_in_snapshot(snapshot: String) -> String { // once that happens, we should be able to remove this hack (and adjust the snapshots for the // trailing spaces that we had to get rid of here) let base_replace = Regex::new(r"Alt <\[\]>  BASE \s*\n").unwrap(); - let base_replace_tmux_mode = Regex::new(r"Alt \[\|SPACE\|Alt \]  BASE \s*\n").unwrap(); + let base_replace_tmux_mode_1 = Regex::new(r"Alt \[\|SPACE\|Alt \]  BASE \s*\n").unwrap(); + let base_replace_tmux_mode_2 = Regex::new(r"Alt \[\|Alt \]\|SPACE  BASE \s*\n").unwrap(); let eol_arrow_replace = Regex::new(r"\s*\n").unwrap(); let snapshot = base_replace.replace_all(&snapshot, "\n").to_string(); - let snapshot = base_replace_tmux_mode + let snapshot = base_replace_tmux_mode_1 + .replace_all(&snapshot, "\n") + .to_string(); + let snapshot = base_replace_tmux_mode_2 .replace_all(&snapshot, "\n") .to_string(); let snapshot = eol_arrow_replace.replace_all(&snapshot, "\n").to_string(); diff --git a/zellij-server/src/panes/grid.rs b/zellij-server/src/panes/grid.rs index 905d4750..db1760f7 100644 --- a/zellij-server/src/panes/grid.rs +++ b/zellij-server/src/panes/grid.rs @@ -1981,8 +1981,10 @@ impl Grid { let position_row = self.viewport.get(position.line.0 as usize)?; let mut position_start = Position::new(position.line.0 as i32, 0); - let mut position_end = - Position::new(position.line.0 as i32, position_row.columns.len() as u16); + let mut position_end = Position::new( + position.line.0 as i32, + (position_row.columns.len() + position_row.excess_width()) as u16, + ); let mut found_canonical_row_start = position_row.is_canonical; while !found_canonical_row_start { @@ -3802,7 +3804,8 @@ impl Row { self.columns.len() } pub fn word_indices_around_character_index(&self, index: usize) -> Option<(usize, usize)> { - let character_at_index = self.columns.get(index)?; + let absolute_character_index = self.absolute_character_index(index); + let character_at_index = self.columns.get(absolute_character_index)?; if is_selection_boundary_character(character_at_index.character) { return Some((index, index + 1)); } @@ -3810,24 +3813,24 @@ impl Row { .columns .iter() .enumerate() - .skip(index) + .skip(absolute_character_index) .find_map(|(i, t_c)| { if is_selection_boundary_character(t_c.character) { - Some(i) + Some(i + self.excess_width_until(i)) } else { None } }) - .unwrap_or_else(|| self.columns.len()); + .unwrap_or_else(|| self.columns.len() + self.excess_width()); let start_position = self .columns .iter() .enumerate() - .take(index) + .take(absolute_character_index) .rev() .find_map(|(i, t_c)| { if is_selection_boundary_character(t_c.character) { - Some(i + 1) + Some(i + 1 + self.excess_width_until(i)) } else { None } @@ -3846,7 +3849,7 @@ impl Row { .rev() .find_map(|(i, t_c)| { if is_selection_boundary_character(t_c.character) { - Some(i + 1) + Some(self.absolute_character_index(i + 1)) } else { None } @@ -3859,7 +3862,7 @@ impl Row { .enumerate() .find_map(|(i, t_c)| { if is_selection_boundary_character(t_c.character) { - Some(i) + Some(self.absolute_character_index(i)) } else { None }