Skip to content

Commit 46f410b

Browse files
authored
add bashism !term to prefix search for last command beginning with term (#779)
* add bashism `!term` to prefix search for last command beginning with `term` * missed doc comment * one more try with doc comments * add ability to search session first, then globally * unrelates types for list_menu test * missed on
1 parent 9853df3 commit 46f410b

File tree

3 files changed

+71
-1
lines changed

3 files changed

+71
-1
lines changed

.typos.toml

+7
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,10 @@ descriptio = "descriptio"
1111
ot = "ot"
1212
# for sqlite backed history
1313
wheres = "wheres"
14+
# for list_menu tests
15+
an1other = "an1other"
16+
ver2y = "ver2y"
17+
l3ine = "l3ine"
18+
4should = "4should"
19+
wr5ap = "wr5ap"
20+
ine = "ine"

src/engine.rs

+41
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,8 @@ impl Reedline {
14951495
fn parse_bang_command(&mut self) -> Option<ReedlineEvent> {
14961496
let buffer = self.editor.get_buffer();
14971497
let parsed = parse_selection_char(buffer, '!');
1498+
let parsed_prefix = parsed.prefix.unwrap_or_default().to_string();
1499+
let parsed_marker = parsed.marker.unwrap_or_default().to_string();
14981500

14991501
if let Some(last) = parsed.remainder.chars().last() {
15001502
if last != ' ' {
@@ -1546,6 +1548,44 @@ impl Reedline {
15461548
history.command_line.clone(),
15471549
)
15481550
}),
1551+
ParseAction::BackwardPrefixSearch => {
1552+
let history_search_by_session = self
1553+
.history
1554+
.search(SearchQuery::last_with_prefix_and_cwd(
1555+
parsed.prefix.unwrap().to_string(),
1556+
self.get_history_session_id(),
1557+
))
1558+
.unwrap_or_else(|_| Vec::new())
1559+
.get(index.saturating_sub(1))
1560+
.map(|history| {
1561+
(
1562+
parsed.remainder.len(),
1563+
parsed_prefix.len() + parsed_marker.len(),
1564+
history.command_line.clone(),
1565+
)
1566+
});
1567+
// If we don't find any history searching by session id, then let's
1568+
// search everything, otherwise use the result from the session search
1569+
if history_search_by_session.is_none() {
1570+
eprintln!("Using global search");
1571+
self.history
1572+
.search(SearchQuery::last_with_prefix(
1573+
parsed_prefix.clone(),
1574+
self.get_history_session_id(),
1575+
))
1576+
.unwrap_or_else(|_| Vec::new())
1577+
.get(index.saturating_sub(1))
1578+
.map(|history| {
1579+
(
1580+
parsed.remainder.len(),
1581+
parsed_prefix.len() + parsed_marker.len(),
1582+
history.command_line.clone(),
1583+
)
1584+
})
1585+
} else {
1586+
history_search_by_session
1587+
}
1588+
}
15491589
ParseAction::ForwardSearch => self
15501590
.history
15511591
.search(SearchQuery {
@@ -1573,6 +1613,7 @@ impl Reedline {
15731613
)))
15741614
.unwrap_or_else(|_| Vec::new())
15751615
.first()
1616+
//BUGBUG: This returns the wrong results with paths with spaces in them
15761617
.and_then(|history| history.command_line.split_whitespace().next_back())
15771618
.map(|token| (parsed.remainder.len(), indicator.len(), token.to_string())),
15781619
});

src/menu/menu_functions.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ pub struct ParseResult<'buffer> {
1717
pub marker: Option<&'buffer str>,
1818
/// Direction of the search based on the marker
1919
pub action: ParseAction,
20+
/// Prefix to search for
21+
pub prefix: Option<&'buffer str>,
2022
}
2123

2224
/// Direction of the index found in the string
@@ -30,6 +32,8 @@ pub enum ParseAction {
3032
LastToken,
3133
/// Last executed command.
3234
LastCommand,
35+
/// Backward search for a prefix
36+
BackwardPrefixSearch,
3337
}
3438

3539
/// Splits a string that contains a marker character
@@ -46,7 +50,8 @@ pub enum ParseAction {
4650
/// remainder: "this is an example",
4751
/// index: Some(10),
4852
/// marker: Some("!10"),
49-
/// action: ParseAction::ForwardSearch
53+
/// action: ParseAction::ForwardSearch,
54+
/// prefix: None,
5055
/// }
5156
/// )
5257
///
@@ -58,6 +63,7 @@ pub fn parse_selection_char(buffer: &str, marker: char) -> ParseResult {
5863
index: None,
5964
marker: None,
6065
action: ParseAction::ForwardSearch,
66+
prefix: None,
6167
};
6268
}
6369

@@ -75,6 +81,7 @@ pub fn parse_selection_char(buffer: &str, marker: char) -> ParseResult {
7581
index: Some(0),
7682
marker: Some(&buffer[index..index + 2 * marker.len_utf8()]),
7783
action: ParseAction::LastCommand,
84+
prefix: None,
7885
}
7986
}
8087
#[cfg(feature = "bashisms")]
@@ -84,6 +91,7 @@ pub fn parse_selection_char(buffer: &str, marker: char) -> ParseResult {
8491
index: Some(0),
8592
marker: Some(&buffer[index..index + 2]),
8693
action: ParseAction::LastToken,
94+
prefix: None,
8795
}
8896
}
8997
Some(&x) if x.is_ascii_digit() || x == '-' => {
@@ -106,6 +114,7 @@ pub fn parse_selection_char(buffer: &str, marker: char) -> ParseResult {
106114
index: Some(count),
107115
marker: Some(&buffer[index..index + size]),
108116
action,
117+
prefix: None,
109118
};
110119
}
111120
}
@@ -114,14 +123,26 @@ pub fn parse_selection_char(buffer: &str, marker: char) -> ParseResult {
114123
index: Some(count),
115124
marker: Some(&buffer[index..index + size]),
116125
action,
126+
prefix: None,
117127
};
118128
}
129+
#[cfg(feature = "bashisms")]
130+
Some(&x) if x.is_ascii_alphabetic() => {
131+
return ParseResult {
132+
remainder: &buffer[0..index],
133+
index: Some(0),
134+
marker: Some(&buffer[index..index + marker.len_utf8()]),
135+
action: ParseAction::BackwardPrefixSearch,
136+
prefix: Some(&buffer[index + marker.len_utf8()..buffer.len()]),
137+
}
138+
}
119139
None => {
120140
return ParseResult {
121141
remainder: &buffer[0..index],
122142
index: Some(0),
123143
marker: Some(&buffer[index..buffer.len()]),
124144
action,
145+
prefix: Some(&buffer[index..buffer.len()]),
125146
}
126147
}
127148
_ => {}
@@ -135,6 +156,7 @@ pub fn parse_selection_char(buffer: &str, marker: char) -> ParseResult {
135156
index: None,
136157
marker: None,
137158
action,
159+
prefix: None,
138160
}
139161
}
140162

0 commit comments

Comments
 (0)