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: 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)

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
// 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();

View file

@ -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
}