feature: Add ctrl-w and ctrl-h support in the search (#409)

Ctrl-w deletes one word backwards from the current cursor location. Ctrl-h is just an alias for backspace.
This commit is contained in:
Clement Tsang 2021-02-16 18:07:41 -05:00 committed by GitHub
parent e437b14922
commit cf14abe37d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 101 additions and 18 deletions

View File

@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#406](https://github.com/ClementTsang/bottom/pull/406): Adds the Nord colour scheme, as well as a light variant.
- [#409](https://github.com/ClementTsang/bottom/pull/409): Adds `Ctrl-w` and `Ctrl-h` shortcuts in search, to delete a word and delete a character respectively.
## Changes
- [#372](https://github.com/ClementTsang/bottom/pull/372): Hides the SWAP graph and legend in normal mode if SWAP is 0.

View File

@ -333,6 +333,8 @@ Use `btm --help` for more information.
| `Ctrl-a` | Skip to the start of the search query |
| `Ctrl-e` | Skip to the end of the search query |
| `Ctrl-u` | Clear the current search query |
| `Ctrl-w` | Delete a word behind the cursor |
| `Ctrl-h` | Delete the character behind the cursor |
| `Backspace` | Delete the character behind the cursor |
| `Delete` | Delete the character at the cursor |
| `Alt-c`, `F1` | Toggle matching case |

View File

@ -725,17 +725,22 @@ impl App {
.current_search_query
.len()
{
let current_cursor = proc_widget_state.get_search_cursor_position();
proc_widget_state
.search_walk_forward(proc_widget_state.get_search_cursor_position());
let _removed_chars: String = proc_widget_state
.process_search_state
.search_state
.current_search_query
.remove(proc_widget_state.get_search_cursor_position());
.drain(current_cursor..proc_widget_state.get_search_cursor_position())
.collect();
proc_widget_state
.process_search_state
.search_state
.grapheme_cursor = GraphemeCursor::new(
proc_widget_state.get_search_cursor_position(),
current_cursor,
proc_widget_state
.process_search_state
.search_state
@ -769,14 +774,16 @@ impl App {
.is_enabled
&& proc_widget_state.get_search_cursor_position() > 0
{
let current_cursor = proc_widget_state.get_search_cursor_position();
proc_widget_state
.search_walk_back(proc_widget_state.get_search_cursor_position());
let removed_char = proc_widget_state
let removed_chars: String = proc_widget_state
.process_search_state
.search_state
.current_search_query
.remove(proc_widget_state.get_search_cursor_position());
.drain(proc_widget_state.get_search_cursor_position()..current_cursor)
.collect();
proc_widget_state
.process_search_state
@ -794,7 +801,8 @@ impl App {
proc_widget_state
.process_search_state
.search_state
.char_cursor_position -= UnicodeWidthChar::width(removed_char).unwrap_or(0);
.char_cursor_position -= UnicodeWidthStr::width(removed_chars.as_str());
proc_widget_state
.process_search_state
.search_state
@ -1167,6 +1175,82 @@ impl App {
}
}
pub fn clear_previous_word(&mut self) {
if let BottomWidgetType::ProcSearch = self.current_widget.widget_type {
if let Some(proc_widget_state) = self
.proc_state
.widget_states
.get_mut(&(self.current_widget.widget_id - 1))
{
// Traverse backwards from the current cursor location until you hit non-whitespace characters,
// then continue to traverse (and delete) backwards until you hit a whitespace character. Halt.
// So... first, let's get our current cursor position using graphemes...
let end_index = proc_widget_state.get_char_cursor_position();
// Then, let's crawl backwards until we hit our location, and store the "head"...
let query = proc_widget_state.get_current_search_query();
let mut start_index = 0;
let mut saw_non_whitespace = false;
for (itx, c) in query
.chars()
.rev()
.enumerate()
.skip(query.len() - end_index)
{
if c.is_whitespace() {
if saw_non_whitespace {
start_index = query.len() - itx;
break;
}
} else {
saw_non_whitespace = true;
}
}
let removed_chars: String = proc_widget_state
.process_search_state
.search_state
.current_search_query
.drain(start_index..end_index)
.collect();
proc_widget_state
.process_search_state
.search_state
.grapheme_cursor = GraphemeCursor::new(
start_index,
proc_widget_state
.process_search_state
.search_state
.current_search_query
.len(),
true,
);
proc_widget_state
.process_search_state
.search_state
.char_cursor_position -= UnicodeWidthStr::width(removed_chars.as_str());
proc_widget_state
.process_search_state
.search_state
.cursor_direction = CursorDirection::Left;
proc_widget_state.update_query();
self.proc_state.force_update = Some(self.current_widget.widget_id - 1);
// Now, convert this range into a String-friendly range and remove it all at once!
// Now make sure to also update our current cursor positions...
self.proc_state.force_update = Some(self.current_widget.widget_id - 1);
}
}
}
pub fn start_dd(&mut self) {
self.reset_multi_tap_keys();

View File

@ -549,7 +549,7 @@ impl ProcessTableWidget for Painter {
})
.collect::<Vec<_>>();
if cursor_position >= query.len() {
if cursor_position == query.len() {
res.push(Span::styled(" ", currently_selected_text_style))
}
@ -558,17 +558,7 @@ impl ProcessTableWidget for Painter {
// This is easier - we just need to get a range of graphemes, rather than
// dealing with possibly inserting a cursor (as none is shown!)
grapheme_indices
.filter_map(|grapheme| {
current_grapheme_posn += UnicodeWidthStr::width(grapheme.1);
if current_grapheme_posn <= start_position {
None
} else {
let styled = Span::styled(grapheme.1, text_style);
Some(styled)
}
})
.collect::<Vec<_>>()
vec![Span::styled(query.to_string(), text_style)]
}
}
@ -622,6 +612,7 @@ impl ProcessTableWidget for Painter {
},
)];
search_vec.extend(query_with_cursor);
search_vec
})];

View File

@ -271,13 +271,15 @@ pub const PROCESS_HELP_TEXT: [&str; 14] = [
"+, -, click Collapse/expand a branch while in tree mode",
];
pub const SEARCH_HELP_TEXT: [&str; 46] = [
pub const SEARCH_HELP_TEXT: [&str; 48] = [
"4 - Process search widget",
"Tab Toggle between searching for PID and name",
"Esc Close the search widget (retains the filter)",
"Ctrl-a Skip to the start of the search query",
"Ctrl-e Skip to the end of the search query",
"Ctrl-u Clear the current search query",
"Ctrl-w Delete a word behind the cursor",
"Ctrl-h Delete the character behind the cursor",
"Backspace Delete the character behind the cursor",
"Delete Delete the character at the cursor",
"Alt-c, F1 Toggle matching case",

View File

@ -157,6 +157,8 @@ pub fn handle_key_event_or_break(
KeyCode::Char('a') => app.skip_cursor_beginning(),
KeyCode::Char('e') => app.skip_cursor_end(),
KeyCode::Char('u') => app.clear_search(),
KeyCode::Char('w') => app.clear_previous_word(),
KeyCode::Char('h') => app.on_backspace(),
// KeyCode::Char('j') => {}, // Move down
// KeyCode::Char('k') => {}, // Move up
// KeyCode::Char('h') => {}, // Move right