Improve mouse support
Some checks failed
make test / ubuntu (push) Has been cancelled
make test / ubuntu-32bit-static-pcre2 (push) Has been cancelled
make test / ubuntu-asan (push) Has been cancelled
make test / macos (push) Has been cancelled
Rust checks / rustfmt (push) Has been cancelled
Rust checks / clippy (push) Has been cancelled
Lock threads / lock (push) Has been cancelled

This commit is contained in:
kerty 2025-01-14 09:48:59 +03:00 committed by Johannes Altmanninger
parent a0e687965e
commit f139d8ebed
4 changed files with 67 additions and 36 deletions

View File

@ -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. 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. 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`). 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 New or improved bindings
^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -13,7 +13,7 @@ use crate::future::IsSomeAnd;
use crate::highlight::{highlight_shell, HighlightRole, HighlightSpec}; use crate::highlight::{highlight_shell, HighlightRole, HighlightSpec};
use crate::libc::MB_CUR_MAX; use crate::libc::MB_CUR_MAX;
use crate::operation_context::OperationContext; 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::termsize::Termsize;
use crate::wchar::prelude::*; use crate::wchar::prelude::*;
use crate::wcstringutil::string_fuzzy_match_string; use crate::wcstringutil::string_fuzzy_match_string;
@ -253,9 +253,7 @@ impl Pager {
assert!(stop_row <= row_count); assert!(stop_row <= row_count);
assert!(stop_row - start_row <= term_height); assert!(stop_row - start_row <= term_height);
// This always printed at the end of the command line. // This always printed at the end of the command line.
let offset_in_cmdline = usize::MAX;
self.completion_print( self.completion_print(
offset_in_cmdline,
cols, cols,
&width_by_column, &width_by_column,
start_row, start_row,
@ -300,7 +298,7 @@ impl Pager {
HighlightRole::pager_progress, HighlightRole::pager_progress,
); );
print_max( print_max(
offset_in_cmdline, CharOffset::None,
&progress_text, &progress_text,
spec, spec,
term_width, term_width,
@ -329,7 +327,7 @@ impl Pager {
let mut search_field_remaining = term_width - 1; let mut search_field_remaining = term_width - 1;
search_field_remaining -= print_max( search_field_remaining -= print_max(
offset_in_cmdline, CharOffset::None,
wgettext!(SEARCH_FIELD_PROMPT), wgettext!(SEARCH_FIELD_PROMPT),
HighlightSpec::new(), HighlightSpec::new(),
search_field_remaining, search_field_remaining,
@ -337,7 +335,7 @@ impl Pager {
search_field, search_field,
); );
search_field_remaining -= print_max( search_field_remaining -= print_max(
offset_in_cmdline, CharOffset::None,
&search_field_text, &search_field_text,
underline, underline,
search_field_remaining, search_field_remaining,
@ -407,7 +405,6 @@ impl Pager {
/// \param lst The list of completions to print /// \param lst The list of completions to print
fn completion_print( fn completion_print(
&self, &self,
offset_in_cmdline: usize,
cols: usize, cols: usize,
width_by_column: &[usize; PAGER_MAX_COLS], width_by_column: &[usize; PAGER_MAX_COLS],
row_start: usize, row_start: usize,
@ -437,7 +434,7 @@ impl Pager {
// Print this completion on its own "line". // Print this completion on its own "line".
let mut line = self.completion_print_item( let mut line = self.completion_print_item(
offset_in_cmdline, CharOffset::Pager(idx),
prefix, prefix,
el, el,
col_width, col_width,
@ -447,7 +444,7 @@ impl Pager {
// If there's more to come, append two spaces. // If there's more to come, append two spaces.
if col + 1 < cols { 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. // Append this to the real line.
@ -462,7 +459,7 @@ impl Pager {
/// Print the specified item using at the specified amount of space. /// Print the specified item using at the specified amount of space.
fn completion_print_item( fn completion_print_item(
&self, &self,
offset_in_cmdline: usize, offset_in_cmdline: CharOffset,
prefix: &wstr, prefix: &wstr,
c: &PagerComp, c: &PagerComp,
width: usize, 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 /// \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. /// ellipsized even if the string fits but takes up the whole space.
fn print_max_impl( fn print_max_impl(
offset_in_cmdline: usize, offset_in_cmdline: CharOffset,
s: &wstr, s: &wstr,
color: impl Fn(usize) -> HighlightSpec, color: impl Fn(usize) -> HighlightSpec,
max: usize, max: usize,
@ -1136,7 +1133,7 @@ fn print_max_impl(
} }
fn print_max( fn print_max(
offset_in_cmdline: usize, offset_in_cmdline: CharOffset,
s: &wstr, s: &wstr,
color: HighlightSpec, color: HighlightSpec,
max: usize, max: usize,

View File

@ -120,7 +120,7 @@ use crate::proc::{
print_exit_warning_for_jobs, proc_update_jiffies, print_exit_warning_for_jobs, proc_update_jiffies,
}; };
use crate::reader_history_search::{smartcase_flags, ReaderHistorySearch, SearchMode}; 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::{ use crate::signal::{
signal_check_cancel, signal_clear_cancel, signal_reset_handlers, signal_set_handlers, signal_check_cancel, signal_clear_cancel, signal_reset_handlers, signal_set_handlers,
signal_set_handlers_once, signal_set_handlers_once,
@ -1403,12 +1403,21 @@ impl ReaderData {
"; received left mouse click at", "; received left mouse click at",
click_position click_position
); );
let new_pos = self match self
.screen .screen
.offset_in_cmdline_given_cursor(click_position, cursor); .offset_in_cmdline_given_cursor(click_position, cursor)
{
CharOffset::Cmd(new_pos) | CharOffset::Pointer(new_pos) => {
let (elt, _el) = self.active_edit_line(); let (elt, _el) = self.active_edit_line();
self.update_buff_pos(elt, Some(new_pos)); 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();
}
_ => {}
}
}
} }
/// Given a command line and an autosuggestion, return the string that gets shown to the user. /// Given a command line and an autosuggestion, return the string that gets shown to the user.

View File

@ -42,12 +42,21 @@ use crate::wchar::prelude::*;
use crate::wcstringutil::string_prefixes_string; use crate::wcstringutil::string_prefixes_string;
use crate::wutil::fstat; use crate::wutil::fstat;
#[derive(Copy, Clone, Default)]
pub enum CharOffset {
#[default]
None,
Cmd(usize),
Pointer(usize),
Pager(usize),
}
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct HighlightedChar { pub struct HighlightedChar {
highlight: HighlightSpec, highlight: HighlightSpec,
character: char, character: char,
// Logical offset within the command line. // Logical offset within the command line.
offset_in_cmdline: usize, offset_in_cmdline: CharOffset,
} }
/// A class representing a single line of a screen. /// 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`. /// 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 { self.text.push(HighlightedChar {
highlight, highlight,
character: rendered_character(character), character: rendered_character(character),
@ -79,7 +93,12 @@ impl Line {
} }
/// Append a nul-terminated string `txt` to the line, giving each character `color`. /// 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() { for c in txt.chars() {
self.append(c, highlight, offset_in_cmdline); self.append(c, highlight, offset_in_cmdline);
} }
@ -101,7 +120,7 @@ impl Line {
} }
/// Return the logical offset corresponding to this cell /// 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 self.text[idx].offset_in_cmdline
} }
@ -338,9 +357,14 @@ impl Screen {
self.desired.cursor.y = 0; self.desired.cursor.y = 0;
// Append spaces for the left prompt. // 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 { for _ in 0..layout.left_prompt_space {
let _ = self.desired_append_char( let _ = self.desired_append_char(
/*offset_in_cmdline=*/ 0, prompt_offset,
usize::MAX, usize::MAX,
' ', ' ',
HighlightSpec::new(), HighlightSpec::new(),
@ -386,11 +410,14 @@ impl Screen {
break scrolled_cursor.unwrap(); break scrolled_cursor.unwrap();
} }
if !self.desired_append_char( if !self.desired_append_char(
/*offset_in_cmdline=*/ if pager.search_field_shown {
if i <= explicit_before_suggestion.len() + layout.autosuggestion.len() { CharOffset::None
i.min(explicit_before_suggestion.len()) } 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 { } else {
i - layout.autosuggestion.len() CharOffset::Cmd(i - layout.autosuggestion.len())
}, },
if is_final_rendering { if is_final_rendering {
usize::MAX usize::MAX
@ -553,7 +580,7 @@ impl Screen {
&mut self, &mut self,
viewport_position: ViewportPosition, viewport_position: ViewportPosition,
viewport_cursor: ViewportPosition, viewport_cursor: ViewportPosition,
) -> usize { ) -> CharOffset {
let viewport_prompt_y = self.command_line_y_given_cursor_y(viewport_cursor.y); let viewport_prompt_y = self.command_line_y_given_cursor_y(viewport_cursor.y);
let y = viewport_position let y = viewport_position
.y .y
@ -574,14 +601,11 @@ impl Screen {
let x = viewport_position.x - viewport_prompt_x; let x = viewport_position.x - viewport_prompt_x;
let line = self.actual.line(y); let line = self.actual.line(y);
let x = x.max(line.indentation); let x = x.max(line.indentation);
if x >= line.len() { let char = line.text.get(x).or(line.text.last()).unwrap();
if self.actual.line_count() == 1 { match char.offset_in_cmdline {
0 CharOffset::Cmd(value) if x >= line.len() => CharOffset::Cmd(value + 1),
} else { CharOffset::Pager(_) if x >= line.len() => CharOffset::None,
line.text.last().unwrap().offset_in_cmdline + 1 offset => offset,
}
} else {
line.offset_in_cmdline_at(x)
} }
} }
@ -706,7 +730,7 @@ impl Screen {
/// automatically handles linebreaks and lines longer than the screen width. /// automatically handles linebreaks and lines longer than the screen width.
fn desired_append_char( fn desired_append_char(
&mut self, &mut self,
offset_in_cmdline: usize, offset_in_cmdline: CharOffset,
max_y: usize, max_y: usize,
b: char, b: char,
c: HighlightSpec, c: HighlightSpec,