diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 601bcfe65..6ded29a5e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -23,6 +23,7 @@ Interactive improvements Such a malicious file can still potentially insert arbitrary text into the command line but can no longer execute it directly (:issue:`10987`). - The history search now preserves ordering between :kbd:`ctrl-s` forward and :kbd:`ctrl-r` backward searches. - Left mouse click now can select pager items. +- Instead of flashing all the text to the left of the cursor, fish now flashes the matched token during history token search, the completed token during completion (:issue:`11050`), and the full command line in all other cases. New or improved bindings ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/reader.rs b/src/reader.rs index 42432198e..973d02c58 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -504,7 +504,7 @@ pub struct ReaderData { /// Whether this is the first prompt. first_prompt: bool, - /// The time when the last flash() completed + /// The time when the last flash() completed. last_flash: Option, /// The representation of the current screen contents. @@ -2717,7 +2717,7 @@ impl<'a> Reader<'a> { rl::PagerToggleSearch => { if let Some(history_pager) = &self.history_pager { if history_pager.start == 0 { - self.flash(); + self.flash(0..self.command_line.len()); return; } self.fill_history_pager( @@ -2972,7 +2972,12 @@ impl<'a> Reader<'a> { // Signal that we've found nothing if !found { - self.flash(); + let result_range = self.history_search.search_result_range(); + self.flash(if !result_range.is_empty() { + result_range + } else { + 0..self.command_line.len() + }) } if !found && !was_active_before { @@ -2986,7 +2991,7 @@ impl<'a> Reader<'a> { rl::HistoryPager => { if let Some(history_pager) = &self.history_pager { if history_pager.end > self.history.size() { - self.flash(); + self.flash(0..self.command_line.len()); return; } self.fill_history_pager( @@ -3042,7 +3047,7 @@ impl<'a> Reader<'a> { if is_history_search || is_autosuggestion { self.input_data.function_set_status(true); if is_autosuggestion && !self.autosuggestion.is_whole_item_from_history { - self.flash(); + self.flash(0..self.command_line.position()); return; } self.history.remove(if is_history_search { @@ -3714,7 +3719,7 @@ impl<'a> Reader<'a> { self.redo(elt) }; if !ok { - self.flash(); + self.flash(0..self.command_line.len()); return; } self.suppress_autosuggestion = false; @@ -3993,7 +3998,7 @@ impl<'a> Reader<'a> { } /// Flash the screen. This function changes the color of the current line momentarily. - fn flash(&mut self) { + fn flash(&mut self, flash_range: Range) { // Multiple flashes may be enqueued by keypress repeat events and can pile up to cause a // significant delay in processing future input while all the flash() calls complete, as we // effectively sleep for 100ms each go. See #8610. @@ -4010,7 +4015,7 @@ impl<'a> Reader<'a> { // Save off the colors and set the background. let saved_colors = data.colors.clone(); - for color in &mut data.colors[0..self.command_line.position()] { + for color in &mut data.colors[flash_range] { color.foreground = HighlightRole::search_match; color.background = HighlightRole::search_match; } @@ -6174,7 +6179,7 @@ impl<'a> Reader<'a> { ExpandResultCode::overflow => { // This may come about if we exceeded the max number of matches. // Return "success" to suppress normal completions. - self.flash(); + self.flash(token_range); return; } ExpandResultCode::wildcard_no_match => {} @@ -6264,7 +6269,7 @@ impl<'a> Reader<'a> { let len = comp.len(); if len == 0 { // No suitable completions found, flash screen and return. - self.flash(); + self.flash(token_range); return false; } else if len == 1 { // Exactly one suitable completion found - insert it. diff --git a/src/reader_history_search.rs b/src/reader_history_search.rs index eb37640fb..4140c3a4c 100644 --- a/src/reader_history_search.rs +++ b/src/reader_history_search.rs @@ -6,6 +6,7 @@ use crate::tokenizer::{TokenType, Tokenizer, TOK_ACCEPT_UNFINISHED}; use crate::wchar::prelude::*; use crate::wcstringutil::ifind; use std::collections::HashSet; +use std::ops::Range; use std::sync::Arc; // Make the search case-insensitive unless we have an uppercase character. @@ -110,6 +111,12 @@ impl ReaderHistorySearch { self.search().original_term() } + /// Return the range of the current match in the command line. + pub fn search_result_range(&self) -> Range { + assert!(self.active()); + self.token_offset..self.token_offset + self.matches[self.match_index].text.len() + } + /// Return the range of the original search string in the new command line. pub fn search_range_if_active(&self) -> Option { if !self.active() || self.is_at_end() {