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
This commit is contained in:
Aram Drevekenin 2025-07-18 10:05:32 +02:00 committed by GitHub
parent 32d4c7114e
commit 44a3c1dae9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 20 additions and 12 deletions

View file

@ -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: 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: 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: 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 ## [0.42.2] - 2025-04-15
* refactor(terminal): track scroll_region as tuple rather than Option (https://github.com/zellij-org/zellij/pull/4082) * refactor(terminal): track scroll_region as tuple rather than Option (https://github.com/zellij-org/zellij/pull/4082)

View file

@ -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 // 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) // trailing spaces that we had to get rid of here)
let base_replace = Regex::new(r"Alt <\[\]>  BASE \s*\n").unwrap(); 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 eol_arrow_replace = Regex::new(r"\s*\n").unwrap();
let snapshot = base_replace.replace_all(&snapshot, "\n").to_string(); 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") .replace_all(&snapshot, "\n")
.to_string(); .to_string();
let snapshot = eol_arrow_replace.replace_all(&snapshot, "\n").to_string(); let snapshot = eol_arrow_replace.replace_all(&snapshot, "\n").to_string();

View file

@ -1981,8 +1981,10 @@ impl Grid {
let position_row = self.viewport.get(position.line.0 as usize)?; 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_start = Position::new(position.line.0 as i32, 0);
let mut position_end = let mut position_end = Position::new(
Position::new(position.line.0 as i32, position_row.columns.len() as u16); 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; let mut found_canonical_row_start = position_row.is_canonical;
while !found_canonical_row_start { while !found_canonical_row_start {
@ -3802,7 +3804,8 @@ impl Row {
self.columns.len() self.columns.len()
} }
pub fn word_indices_around_character_index(&self, index: usize) -> Option<(usize, usize)> { 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) { if is_selection_boundary_character(character_at_index.character) {
return Some((index, index + 1)); return Some((index, index + 1));
} }
@ -3810,24 +3813,24 @@ impl Row {
.columns .columns
.iter() .iter()
.enumerate() .enumerate()
.skip(index) .skip(absolute_character_index)
.find_map(|(i, t_c)| { .find_map(|(i, t_c)| {
if is_selection_boundary_character(t_c.character) { if is_selection_boundary_character(t_c.character) {
Some(i) Some(i + self.excess_width_until(i))
} else { } else {
None None
} }
}) })
.unwrap_or_else(|| self.columns.len()); .unwrap_or_else(|| self.columns.len() + self.excess_width());
let start_position = self let start_position = self
.columns .columns
.iter() .iter()
.enumerate() .enumerate()
.take(index) .take(absolute_character_index)
.rev() .rev()
.find_map(|(i, t_c)| { .find_map(|(i, t_c)| {
if is_selection_boundary_character(t_c.character) { if is_selection_boundary_character(t_c.character) {
Some(i + 1) Some(i + 1 + self.excess_width_until(i))
} else { } else {
None None
} }
@ -3846,7 +3849,7 @@ impl Row {
.rev() .rev()
.find_map(|(i, t_c)| { .find_map(|(i, t_c)| {
if is_selection_boundary_character(t_c.character) { if is_selection_boundary_character(t_c.character) {
Some(i + 1) Some(self.absolute_character_index(i + 1))
} else { } else {
None None
} }
@ -3859,7 +3862,7 @@ impl Row {
.enumerate() .enumerate()
.find_map(|(i, t_c)| { .find_map(|(i, t_c)| {
if is_selection_boundary_character(t_c.character) { if is_selection_boundary_character(t_c.character) {
Some(i) Some(self.absolute_character_index(i))
} else { } else {
None None
} }