diff --git a/.typos.toml b/.typos.toml index a1802fae..7a64bdaf 100644 --- a/.typos.toml +++ b/.typos.toml @@ -11,3 +11,10 @@ descriptio = "descriptio" ot = "ot" # for sqlite backed history wheres = "wheres" +# for list_menu tests +an1other = "an1other" +ver2y = "ver2y" +l3ine = "l3ine" +4should = "4should" +wr5ap = "wr5ap" +ine = "ine" diff --git a/src/engine.rs b/src/engine.rs index b26d3c1e..044ac27d 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1495,6 +1495,8 @@ impl Reedline { fn parse_bang_command(&mut self) -> Option { let buffer = self.editor.get_buffer(); let parsed = parse_selection_char(buffer, '!'); + let parsed_prefix = parsed.prefix.unwrap_or_default().to_string(); + let parsed_marker = parsed.marker.unwrap_or_default().to_string(); if let Some(last) = parsed.remainder.chars().last() { if last != ' ' { @@ -1546,6 +1548,44 @@ impl Reedline { history.command_line.clone(), ) }), + ParseAction::BackwardPrefixSearch => { + let history_search_by_session = self + .history + .search(SearchQuery::last_with_prefix_and_cwd( + parsed.prefix.unwrap().to_string(), + self.get_history_session_id(), + )) + .unwrap_or_else(|_| Vec::new()) + .get(index.saturating_sub(1)) + .map(|history| { + ( + parsed.remainder.len(), + parsed_prefix.len() + parsed_marker.len(), + history.command_line.clone(), + ) + }); + // If we don't find any history searching by session id, then let's + // search everything, otherwise use the result from the session search + if history_search_by_session.is_none() { + eprintln!("Using global search"); + self.history + .search(SearchQuery::last_with_prefix( + parsed_prefix.clone(), + self.get_history_session_id(), + )) + .unwrap_or_else(|_| Vec::new()) + .get(index.saturating_sub(1)) + .map(|history| { + ( + parsed.remainder.len(), + parsed_prefix.len() + parsed_marker.len(), + history.command_line.clone(), + ) + }) + } else { + history_search_by_session + } + } ParseAction::ForwardSearch => self .history .search(SearchQuery { @@ -1573,6 +1613,7 @@ impl Reedline { ))) .unwrap_or_else(|_| Vec::new()) .first() + //BUGBUG: This returns the wrong results with paths with spaces in them .and_then(|history| history.command_line.split_whitespace().next_back()) .map(|token| (parsed.remainder.len(), indicator.len(), token.to_string())), }); diff --git a/src/menu/menu_functions.rs b/src/menu/menu_functions.rs index cc48d85a..bf9ded7b 100644 --- a/src/menu/menu_functions.rs +++ b/src/menu/menu_functions.rs @@ -17,6 +17,8 @@ pub struct ParseResult<'buffer> { pub marker: Option<&'buffer str>, /// Direction of the search based on the marker pub action: ParseAction, + /// Prefix to search for + pub prefix: Option<&'buffer str>, } /// Direction of the index found in the string @@ -30,6 +32,8 @@ pub enum ParseAction { LastToken, /// Last executed command. LastCommand, + /// Backward search for a prefix + BackwardPrefixSearch, } /// Splits a string that contains a marker character @@ -46,7 +50,8 @@ pub enum ParseAction { /// remainder: "this is an example", /// index: Some(10), /// marker: Some("!10"), -/// action: ParseAction::ForwardSearch +/// action: ParseAction::ForwardSearch, +/// prefix: None, /// } /// ) /// @@ -58,6 +63,7 @@ pub fn parse_selection_char(buffer: &str, marker: char) -> ParseResult { index: None, marker: None, action: ParseAction::ForwardSearch, + prefix: None, }; } @@ -75,6 +81,7 @@ pub fn parse_selection_char(buffer: &str, marker: char) -> ParseResult { index: Some(0), marker: Some(&buffer[index..index + 2 * marker.len_utf8()]), action: ParseAction::LastCommand, + prefix: None, } } #[cfg(feature = "bashisms")] @@ -84,6 +91,7 @@ pub fn parse_selection_char(buffer: &str, marker: char) -> ParseResult { index: Some(0), marker: Some(&buffer[index..index + 2]), action: ParseAction::LastToken, + prefix: None, } } Some(&x) if x.is_ascii_digit() || x == '-' => { @@ -106,6 +114,7 @@ pub fn parse_selection_char(buffer: &str, marker: char) -> ParseResult { index: Some(count), marker: Some(&buffer[index..index + size]), action, + prefix: None, }; } } @@ -114,14 +123,26 @@ pub fn parse_selection_char(buffer: &str, marker: char) -> ParseResult { index: Some(count), marker: Some(&buffer[index..index + size]), action, + prefix: None, }; } + #[cfg(feature = "bashisms")] + Some(&x) if x.is_ascii_alphabetic() => { + return ParseResult { + remainder: &buffer[0..index], + index: Some(0), + marker: Some(&buffer[index..index + marker.len_utf8()]), + action: ParseAction::BackwardPrefixSearch, + prefix: Some(&buffer[index + marker.len_utf8()..buffer.len()]), + } + } None => { return ParseResult { remainder: &buffer[0..index], index: Some(0), marker: Some(&buffer[index..buffer.len()]), action, + prefix: Some(&buffer[index..buffer.len()]), } } _ => {} @@ -135,6 +156,7 @@ pub fn parse_selection_char(buffer: &str, marker: char) -> ParseResult { index: None, marker: None, action, + prefix: None, } }