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. - [#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 ## Changes
- [#372](https://github.com/ClementTsang/bottom/pull/372): Hides the SWAP graph and legend in normal mode if SWAP is 0. - [#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-a` | Skip to the start of the search query |
| `Ctrl-e` | Skip to the end of the search query | | `Ctrl-e` | Skip to the end of the search query |
| `Ctrl-u` | Clear the current 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 | | `Backspace` | Delete the character behind the cursor |
| `Delete` | Delete the character at the cursor | | `Delete` | Delete the character at the cursor |
| `Alt-c`, `F1` | Toggle matching case | | `Alt-c`, `F1` | Toggle matching case |

View File

@ -725,17 +725,22 @@ impl App {
.current_search_query .current_search_query
.len() .len()
{ {
let current_cursor = proc_widget_state.get_search_cursor_position();
proc_widget_state proc_widget_state
.search_walk_forward(proc_widget_state.get_search_cursor_position());
let _removed_chars: String = proc_widget_state
.process_search_state .process_search_state
.search_state .search_state
.current_search_query .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 proc_widget_state
.process_search_state .process_search_state
.search_state .search_state
.grapheme_cursor = GraphemeCursor::new( .grapheme_cursor = GraphemeCursor::new(
proc_widget_state.get_search_cursor_position(), current_cursor,
proc_widget_state proc_widget_state
.process_search_state .process_search_state
.search_state .search_state
@ -769,14 +774,16 @@ impl App {
.is_enabled .is_enabled
&& proc_widget_state.get_search_cursor_position() > 0 && proc_widget_state.get_search_cursor_position() > 0
{ {
let current_cursor = proc_widget_state.get_search_cursor_position();
proc_widget_state proc_widget_state
.search_walk_back(proc_widget_state.get_search_cursor_position()); .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 .process_search_state
.search_state .search_state
.current_search_query .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 proc_widget_state
.process_search_state .process_search_state
@ -794,7 +801,8 @@ impl App {
proc_widget_state proc_widget_state
.process_search_state .process_search_state
.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 proc_widget_state
.process_search_state .process_search_state
.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) { pub fn start_dd(&mut self) {
self.reset_multi_tap_keys(); self.reset_multi_tap_keys();

View File

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

View File

@ -271,13 +271,15 @@ pub const PROCESS_HELP_TEXT: [&str; 14] = [
"+, -, click Collapse/expand a branch while in tree mode", "+, -, 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", "4 - Process search widget",
"Tab Toggle between searching for PID and name", "Tab Toggle between searching for PID and name",
"Esc Close the search widget (retains the filter)", "Esc Close the search widget (retains the filter)",
"Ctrl-a Skip to the start of the search query", "Ctrl-a Skip to the start of the search query",
"Ctrl-e Skip to the end of the search query", "Ctrl-e Skip to the end of the search query",
"Ctrl-u Clear the current 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", "Backspace Delete the character behind the cursor",
"Delete Delete the character at the cursor", "Delete Delete the character at the cursor",
"Alt-c, F1 Toggle matching case", "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('a') => app.skip_cursor_beginning(),
KeyCode::Char('e') => app.skip_cursor_end(), KeyCode::Char('e') => app.skip_cursor_end(),
KeyCode::Char('u') => app.clear_search(), 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('j') => {}, // Move down
// KeyCode::Char('k') => {}, // Move up // KeyCode::Char('k') => {}, // Move up
// KeyCode::Char('h') => {}, // Move right // KeyCode::Char('h') => {}, // Move right