Token search commands that only match the last token in each line

This add two commands history-last-token-search-backward and
history-last-token-search-forward which behaves like bash's yank-last-arg. So
similar to history-token-search-* but only considers the last argument for
each command.

Closes #10756
Closes #11258
This commit is contained in:
carsonzhu
2025-03-11 13:19:46 +08:00
committed by Johannes Altmanninger
parent 48306409ef
commit 4ce552bf94
5 changed files with 31 additions and 5 deletions

View File

@@ -265,6 +265,12 @@ The following special input functions are available:
``history-token-search-forward`` ``history-token-search-forward``
search the history for the next matching argument search the history for the next matching argument
``history-last-token-search-backward``
search the history for the previous matching last argument
``history-last-token-search-forward``
search the history for the next matching last argument
``forward-jump`` and ``backward-jump`` ``forward-jump`` and ``backward-jump``
read another character and jump to its next occurrence after/before the cursor read another character and jump to its next occurrence after/before the cursor

View File

@@ -175,6 +175,8 @@ const fn make_md(name: &'static wstr, code: ReadlineCmd) -> InputFunctionMetadat
make_md(L!("forward-token"), ReadlineCmd::ForwardToken), make_md(L!("forward-token"), ReadlineCmd::ForwardToken),
make_md(L!("forward-word"), ReadlineCmd::ForwardWord), make_md(L!("forward-word"), ReadlineCmd::ForwardWord),
make_md(L!("history-delete"), ReadlineCmd::HistoryDelete), make_md(L!("history-delete"), ReadlineCmd::HistoryDelete),
make_md(L!("history-last-token-search-backward"), ReadlineCmd::HistoryLastTokenSearchBackward),
make_md(L!("history-last-token-search-forward"), ReadlineCmd::HistoryLastTokenSearchForward),
make_md(L!("history-pager"), ReadlineCmd::HistoryPager), make_md(L!("history-pager"), ReadlineCmd::HistoryPager),
#[allow(deprecated)] #[allow(deprecated)]
make_md(L!("history-pager-delete"), ReadlineCmd::HistoryPagerDelete), make_md(L!("history-pager-delete"), ReadlineCmd::HistoryPagerDelete),

View File

@@ -90,6 +90,8 @@ pub enum ReadlineCmd {
BackwardKillToken, BackwardKillToken,
HistoryTokenSearchBackward, HistoryTokenSearchBackward,
HistoryTokenSearchForward, HistoryTokenSearchForward,
HistoryLastTokenSearchBackward,
HistoryLastTokenSearchForward,
SelfInsert, SelfInsert,
SelfInsertNotFirst, SelfInsertNotFirst,
TransposeChars, TransposeChars,

View File

@@ -3000,11 +3000,16 @@ fn handle_readline_command(&mut self, c: ReadlineCmd) {
| rl::HistorySearchBackward | rl::HistorySearchBackward
| rl::HistorySearchForward | rl::HistorySearchForward
| rl::HistoryTokenSearchBackward | rl::HistoryTokenSearchBackward
| rl::HistoryTokenSearchForward => { | rl::HistoryTokenSearchForward
| rl::HistoryLastTokenSearchBackward
| rl::HistoryLastTokenSearchForward => {
let mode = match c { let mode = match c {
rl::HistoryTokenSearchBackward | rl::HistoryTokenSearchForward => { rl::HistoryTokenSearchBackward | rl::HistoryTokenSearchForward => {
SearchMode::Token SearchMode::Token
} }
rl::HistoryLastTokenSearchBackward | rl::HistoryLastTokenSearchForward => {
SearchMode::LastToken
}
rl::HistoryPrefixSearchBackward | rl::HistoryPrefixSearchForward => { rl::HistoryPrefixSearchBackward | rl::HistoryPrefixSearchForward => {
SearchMode::Prefix SearchMode::Prefix
} }
@@ -3016,13 +3021,13 @@ fn handle_readline_command(&mut self, c: ReadlineCmd) {
if self.history_search.is_at_present() && mode != self.history_search.mode() { if self.history_search.is_at_present() && mode != self.history_search.mode() {
let el = &self.data.command_line; let el = &self.data.command_line;
if mode == SearchMode::Token { if matches!(mode, SearchMode::Token | SearchMode::LastToken) {
// Searching by token. // Searching by token.
let (token_range, _) = parse_util_token_extent(el.text(), el.position()); let (token_range, _) = parse_util_token_extent(el.text(), el.position());
self.data.history_search.reset_to_mode( self.data.history_search.reset_to_mode(
el.text()[token_range.clone()].to_owned(), el.text()[token_range.clone()].to_owned(),
self.history.clone(), self.history.clone(),
SearchMode::Token, mode,
token_range.start, token_range.start,
); );
} else { } else {
@@ -3048,9 +3053,11 @@ fn handle_readline_command(&mut self, c: ReadlineCmd) {
let dir = match c { let dir = match c {
rl::HistorySearchBackward rl::HistorySearchBackward
| rl::HistoryTokenSearchBackward | rl::HistoryTokenSearchBackward
| rl::HistoryLastTokenSearchBackward
| rl::HistoryPrefixSearchBackward => SearchDirection::Backward, | rl::HistoryPrefixSearchBackward => SearchDirection::Backward,
rl::HistorySearchForward rl::HistorySearchForward
| rl::HistoryTokenSearchForward | rl::HistoryTokenSearchForward
| rl::HistoryLastTokenSearchForward
| rl::HistoryPrefixSearchForward => SearchDirection::Forward, | rl::HistoryPrefixSearchForward => SearchDirection::Forward,
_ => unreachable!(), _ => unreachable!(),
}; };
@@ -5536,6 +5543,8 @@ fn command_ends_paging(c: ReadlineCmd, focused_on_search_field: bool) -> bool {
| rl::HistorySearchForward | rl::HistorySearchForward
| rl::HistoryTokenSearchBackward | rl::HistoryTokenSearchBackward
| rl::HistoryTokenSearchForward | rl::HistoryTokenSearchForward
| rl::HistoryLastTokenSearchBackward
| rl::HistoryLastTokenSearchForward
| rl::AcceptAutosuggestion | rl::AcceptAutosuggestion
| rl::DeleteOrExit | rl::DeleteOrExit
| rl::CancelCommandline | rl::CancelCommandline
@@ -5626,6 +5635,8 @@ fn command_ends_history_search(c: ReadlineCmd) -> bool {
| rl::HistorySearchForward | rl::HistorySearchForward
| rl::HistoryTokenSearchBackward | rl::HistoryTokenSearchBackward
| rl::HistoryTokenSearchForward | rl::HistoryTokenSearchForward
| rl::HistoryLastTokenSearchBackward
| rl::HistoryLastTokenSearchForward
| rl::HistoryDelete | rl::HistoryDelete
| rl::HistoryPagerDelete | rl::HistoryPagerDelete
| rl::BeginningOfHistory | rl::BeginningOfHistory

View File

@@ -42,6 +42,8 @@ pub enum SearchMode {
Prefix, Prefix,
/// searching by token /// searching by token
Token, Token,
/// search by the last token of the command
LastToken,
} }
/// Encapsulation of the reader's history search functionality. /// Encapsulation of the reader's history search functionality.
@@ -71,7 +73,7 @@ pub fn active(&self) -> bool {
self.mode != SearchMode::Inactive self.mode != SearchMode::Inactive
} }
pub fn by_token(&self) -> bool { pub fn by_token(&self) -> bool {
self.mode == SearchMode::Token matches!(self.mode, SearchMode::Token | SearchMode::LastToken)
} }
pub fn by_line(&self) -> bool { pub fn by_line(&self) -> bool {
self.mode == SearchMode::Line self.mode == SearchMode::Line
@@ -221,7 +223,7 @@ fn find(zelf: &ReaderHistorySearch, haystack: &wstr, needle: &wstr) -> Option<us
if let Some(offset) = find(self, text, needle) { if let Some(offset) = find(self, text, needle) {
self.add_if_new(SearchMatch::new(text.to_owned(), offset)); self.add_if_new(SearchMatch::new(text.to_owned(), offset));
} }
} else if self.mode == SearchMode::Token { } else if matches!(self.mode, SearchMode::Token | SearchMode::LastToken) {
let mut tok = Tokenizer::new(text, TOK_ACCEPT_UNFINISHED); let mut tok = Tokenizer::new(text, TOK_ACCEPT_UNFINISHED);
let mut local_tokens = vec![]; let mut local_tokens = vec![];
@@ -238,6 +240,9 @@ fn find(zelf: &ReaderHistorySearch, haystack: &wstr, needle: &wstr) -> Option<us
// Make sure tokens are added in reverse order. See #5150 // Make sure tokens are added in reverse order. See #5150
for tok in local_tokens.into_iter().rev() { for tok in local_tokens.into_iter().rev() {
self.add_if_new(tok); self.add_if_new(tok);
if self.mode == SearchMode::LastToken {
break;
}
} }
} }
self.matches.len() > before self.matches.len() > before