From f139d8ebedb98099334573d5f4772d5fea60ddaf Mon Sep 17 00:00:00 2001 From: kerty Date: Tue, 14 Jan 2025 09:48:59 +0300 Subject: [PATCH] Improve mouse support --- CHANGELOG.rst | 1 + src/pager.rs | 21 ++++++++--------- src/reader.rs | 19 +++++++++++----- src/screen.rs | 62 +++++++++++++++++++++++++++++++++++---------------- 4 files changed, 67 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8af5c8c23..b95c9762b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -18,6 +18,7 @@ Interactive improvements when typing a command and :kbd:`enter` while the previous one is still running, the new one will no longer execute immediately. Similarly, keys that are bound to shell commands will be ignored. This mitigates a security issue where a command like ``cat malicious-file.txt`` could write terminal escape codes prompting the terminal to write arbitrary text to fish's standard input. Such a malicious file can still potentially insert arbitrary text into the command line but can no longer execute it directly (:issue:`10987`). +- Left mouse click now can select pager items. New or improved bindings ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/pager.rs b/src/pager.rs index 430182ace..e529cf5d5 100644 --- a/src/pager.rs +++ b/src/pager.rs @@ -13,7 +13,7 @@ use crate::future::IsSomeAnd; use crate::highlight::{highlight_shell, HighlightRole, HighlightSpec}; use crate::libc::MB_CUR_MAX; use crate::operation_context::OperationContext; -use crate::screen::{wcswidth_rendered, wcwidth_rendered, Line, ScreenData}; +use crate::screen::{wcswidth_rendered, wcwidth_rendered, CharOffset, Line, ScreenData}; use crate::termsize::Termsize; use crate::wchar::prelude::*; use crate::wcstringutil::string_fuzzy_match_string; @@ -253,9 +253,7 @@ impl Pager { assert!(stop_row <= row_count); assert!(stop_row - start_row <= term_height); // This always printed at the end of the command line. - let offset_in_cmdline = usize::MAX; self.completion_print( - offset_in_cmdline, cols, &width_by_column, start_row, @@ -300,7 +298,7 @@ impl Pager { HighlightRole::pager_progress, ); print_max( - offset_in_cmdline, + CharOffset::None, &progress_text, spec, term_width, @@ -329,7 +327,7 @@ impl Pager { let mut search_field_remaining = term_width - 1; search_field_remaining -= print_max( - offset_in_cmdline, + CharOffset::None, wgettext!(SEARCH_FIELD_PROMPT), HighlightSpec::new(), search_field_remaining, @@ -337,7 +335,7 @@ impl Pager { search_field, ); search_field_remaining -= print_max( - offset_in_cmdline, + CharOffset::None, &search_field_text, underline, search_field_remaining, @@ -407,7 +405,6 @@ impl Pager { /// \param lst The list of completions to print fn completion_print( &self, - offset_in_cmdline: usize, cols: usize, width_by_column: &[usize; PAGER_MAX_COLS], row_start: usize, @@ -437,7 +434,7 @@ impl Pager { // Print this completion on its own "line". let mut line = self.completion_print_item( - offset_in_cmdline, + CharOffset::Pager(idx), prefix, el, col_width, @@ -447,7 +444,7 @@ impl Pager { // If there's more to come, append two spaces. if col + 1 < cols { - line.append_str(PAGER_SPACER_STRING, HighlightSpec::new(), offset_in_cmdline); + line.append_str(PAGER_SPACER_STRING, HighlightSpec::new(), CharOffset::None); } // Append this to the real line. @@ -462,7 +459,7 @@ impl Pager { /// Print the specified item using at the specified amount of space. fn completion_print_item( &self, - offset_in_cmdline: usize, + offset_in_cmdline: CharOffset, prefix: &wstr, c: &PagerComp, width: usize, @@ -1100,7 +1097,7 @@ fn divide_round_up(numer: usize, denom: usize) -> usize { /// \param has_more if this flag is true, this is not the entire string, and the string should be /// ellipsized even if the string fits but takes up the whole space. fn print_max_impl( - offset_in_cmdline: usize, + offset_in_cmdline: CharOffset, s: &wstr, color: impl Fn(usize) -> HighlightSpec, max: usize, @@ -1136,7 +1133,7 @@ fn print_max_impl( } fn print_max( - offset_in_cmdline: usize, + offset_in_cmdline: CharOffset, s: &wstr, color: HighlightSpec, max: usize, diff --git a/src/reader.rs b/src/reader.rs index 33604113a..03a8cbc78 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -120,7 +120,7 @@ use crate::proc::{ print_exit_warning_for_jobs, proc_update_jiffies, }; use crate::reader_history_search::{smartcase_flags, ReaderHistorySearch, SearchMode}; -use crate::screen::{screen_clear, screen_force_clear_to_end, Screen}; +use crate::screen::{screen_clear, screen_force_clear_to_end, CharOffset, Screen}; use crate::signal::{ signal_check_cancel, signal_clear_cancel, signal_reset_handlers, signal_set_handlers, signal_set_handlers_once, @@ -1403,11 +1403,20 @@ impl ReaderData { "; received left mouse click at", click_position ); - let new_pos = self + match self .screen - .offset_in_cmdline_given_cursor(click_position, cursor); - let (elt, _el) = self.active_edit_line(); - self.update_buff_pos(elt, Some(new_pos)); + .offset_in_cmdline_given_cursor(click_position, cursor) + { + CharOffset::Cmd(new_pos) | CharOffset::Pointer(new_pos) => { + let (elt, _el) = self.active_edit_line(); + self.update_buff_pos(elt, Some(new_pos)); + } + CharOffset::Pager(idx) if self.pager.selected_completion_idx != Some(idx) => { + self.pager.selected_completion_idx = Some(idx); + self.pager_selection_changed(); + } + _ => {} + } } } diff --git a/src/screen.rs b/src/screen.rs index 03adf13cd..d94dbde7b 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -42,12 +42,21 @@ use crate::wchar::prelude::*; use crate::wcstringutil::string_prefixes_string; use crate::wutil::fstat; +#[derive(Copy, Clone, Default)] +pub enum CharOffset { + #[default] + None, + Cmd(usize), + Pointer(usize), + Pager(usize), +} + #[derive(Clone, Default)] pub struct HighlightedChar { highlight: HighlightSpec, character: char, // Logical offset within the command line. - offset_in_cmdline: usize, + offset_in_cmdline: CharOffset, } /// A class representing a single line of a screen. @@ -70,7 +79,12 @@ impl Line { } /// Append a single character `txt` to the line with color `c`. - pub fn append(&mut self, character: char, highlight: HighlightSpec, offset_in_cmdline: usize) { + pub fn append( + &mut self, + character: char, + highlight: HighlightSpec, + offset_in_cmdline: CharOffset, + ) { self.text.push(HighlightedChar { highlight, character: rendered_character(character), @@ -79,7 +93,12 @@ impl Line { } /// Append a nul-terminated string `txt` to the line, giving each character `color`. - pub fn append_str(&mut self, txt: &wstr, highlight: HighlightSpec, offset_in_cmdline: usize) { + pub fn append_str( + &mut self, + txt: &wstr, + highlight: HighlightSpec, + offset_in_cmdline: CharOffset, + ) { for c in txt.chars() { self.append(c, highlight, offset_in_cmdline); } @@ -101,7 +120,7 @@ impl Line { } /// Return the logical offset corresponding to this cell - pub fn offset_in_cmdline_at(&self, idx: usize) -> usize { + pub fn offset_in_cmdline_at(&self, idx: usize) -> CharOffset { self.text[idx].offset_in_cmdline } @@ -338,9 +357,14 @@ impl Screen { self.desired.cursor.y = 0; // Append spaces for the left prompt. + let prompt_offset = if pager.search_field_shown { + CharOffset::None + } else { + CharOffset::Pointer(0) + }; for _ in 0..layout.left_prompt_space { let _ = self.desired_append_char( - /*offset_in_cmdline=*/ 0, + prompt_offset, usize::MAX, ' ', HighlightSpec::new(), @@ -386,11 +410,14 @@ impl Screen { break scrolled_cursor.unwrap(); } if !self.desired_append_char( - /*offset_in_cmdline=*/ - if i <= explicit_before_suggestion.len() + layout.autosuggestion.len() { - i.min(explicit_before_suggestion.len()) + if pager.search_field_shown { + CharOffset::None + } else if i < explicit_before_suggestion.len() { + CharOffset::Cmd(i) + } else if i < explicit_before_suggestion.len() + layout.autosuggestion.len() { + CharOffset::Pointer(explicit_before_suggestion.len()) } else { - i - layout.autosuggestion.len() + CharOffset::Cmd(i - layout.autosuggestion.len()) }, if is_final_rendering { usize::MAX @@ -553,7 +580,7 @@ impl Screen { &mut self, viewport_position: ViewportPosition, viewport_cursor: ViewportPosition, - ) -> usize { + ) -> CharOffset { let viewport_prompt_y = self.command_line_y_given_cursor_y(viewport_cursor.y); let y = viewport_position .y @@ -574,14 +601,11 @@ impl Screen { let x = viewport_position.x - viewport_prompt_x; let line = self.actual.line(y); let x = x.max(line.indentation); - if x >= line.len() { - if self.actual.line_count() == 1 { - 0 - } else { - line.text.last().unwrap().offset_in_cmdline + 1 - } - } else { - line.offset_in_cmdline_at(x) + let char = line.text.get(x).or(line.text.last()).unwrap(); + match char.offset_in_cmdline { + CharOffset::Cmd(value) if x >= line.len() => CharOffset::Cmd(value + 1), + CharOffset::Pager(_) if x >= line.len() => CharOffset::None, + offset => offset, } } @@ -706,7 +730,7 @@ impl Screen { /// automatically handles linebreaks and lines longer than the screen width. fn desired_append_char( &mut self, - offset_in_cmdline: usize, + offset_in_cmdline: CharOffset, max_y: usize, b: char, c: HighlightSpec,