diff --git a/doc_src/cmds/bind.rst b/doc_src/cmds/bind.rst index 451fa3d00..b0ad70fa0 100644 --- a/doc_src/cmds/bind.rst +++ b/doc_src/cmds/bind.rst @@ -202,6 +202,9 @@ The following special input functions are available: ``history-pager`` invoke the searchable pager on history (incremental search); or if the history pager is already active, search further backwards in time. +``history-pager-delete`` + permanently delete the history item selected in the history pager + ``history-search-backward`` search the history for the previous match diff --git a/share/functions/__fish_shared_key_bindings.fish b/share/functions/__fish_shared_key_bindings.fish index ccaacb3f8..d6e37c576 100644 --- a/share/functions/__fish_shared_key_bindings.fish +++ b/share/functions/__fish_shared_key_bindings.fish @@ -38,6 +38,7 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod bind --preset $argv \cs pager-toggle-search # shift-tab does a tab complete followed by a search. bind --preset $argv --key btab complete-and-search + bind --preset $argv -k sdc history-pager-delete or backward-delete-char # shifted delete bind --preset $argv \e\n "commandline -f expand-abbr; commandline -i \n" bind --preset $argv \e\r "commandline -f expand-abbr; commandline -i \n" diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index 4875700f0..2a4836d3e 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -51,7 +51,6 @@ function fish_default_key_bindings -d "emacs-like key binds" bind --preset $argv -k home beginning-of-line bind --preset $argv -k end end-of-line - bind --preset $argv -k sdc backward-delete-char # shifted delete bind --preset $argv \ca beginning-of-line bind --preset $argv \ce end-of-line diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index 9fc512238..998d30a66 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -125,8 +125,6 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' bind -s --preset -M default \ch backward-char bind -s --preset -M insert \x7f backward-delete-char bind -s --preset -M default \x7f backward-char - bind -s --preset -M insert -k sdc backward-delete-char # shifted delete - bind -s --preset -M default -k sdc backward-delete-char # shifted delete bind -s --preset dd kill-whole-line bind -s --preset D kill-line diff --git a/src/input.cpp b/src/input.cpp index 4831953ff..cd4a858b3 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -129,6 +129,7 @@ static constexpr const input_function_metadata_t input_function_metadata[] = { {L"forward-single-char", readline_cmd_t::forward_single_char}, {L"forward-word", readline_cmd_t::forward_word}, {L"history-pager", readline_cmd_t::history_pager}, + {L"history-pager-delete", readline_cmd_t::history_pager_delete}, {L"history-prefix-search-backward", readline_cmd_t::history_prefix_search_backward}, {L"history-prefix-search-forward", readline_cmd_t::history_prefix_search_forward}, {L"history-search-backward", readline_cmd_t::history_search_backward}, diff --git a/src/input_common.h b/src/input_common.h index 88c85cec8..2cf8490cf 100644 --- a/src/input_common.h +++ b/src/input_common.h @@ -29,6 +29,7 @@ enum class readline_cmd_t { history_prefix_search_backward, history_prefix_search_forward, history_pager, + history_pager_delete, delete_char, backward_delete_char, kill_line, diff --git a/src/pager.cpp b/src/pager.cpp index 3dae7b358..6aafe7d77 100644 --- a/src/pager.cpp +++ b/src/pager.cpp @@ -883,6 +883,15 @@ const completion_t *pager_t::selected_completion(const page_rendering_t &renderi return result; } +size_t pager_t::selected_completion_index() const { return selected_completion_idx; } + +void pager_t::set_selected_completion_index(size_t new_index) { + // Current users are off by one at most. + assert(new_index == PAGER_SELECTION_NONE || new_index <= completion_infos.size()); + if (new_index == completion_infos.size()) --new_index; + selected_completion_idx = new_index; +} + /// Get the selected row and column. Completions are rendered column first, i.e. we go south before /// we go west. So if we have N rows, and our selected index is N + 2, then our row is 2 (mod by N) /// and our column is 1 (divide by N). diff --git a/src/pager.h b/src/pager.h index ff74d055b..1ca1b2222 100644 --- a/src/pager.h +++ b/src/pager.h @@ -165,6 +165,9 @@ class pager_t { // Returns the currently selected completion for the given rendering. const completion_t *selected_completion(const page_rendering_t &rendering) const; + size_t selected_completion_index() const; + void set_selected_completion_index(size_t new_index); + // Indicates the row and column for the given rendering. Returns -1 if no selection. size_t get_selected_row(const page_rendering_t &rendering) const; size_t get_selected_column(const page_rendering_t &rendering) const; diff --git a/src/reader.cpp b/src/reader.cpp index 384eb3a88..5d55da193 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -726,6 +726,8 @@ class reader_data_t : public std::enable_shared_from_this { reader_history_search_t history_search{}; /// Whether the in-pager history search is active. bool history_pager_active{false}; + /// The direction of the last successful history pager search. + history_search_direction_t history_pager_direction{}; /// The range in history covered by the history pager's current page. size_t history_pager_history_index_start{static_cast(-1)}; size_t history_pager_history_index_end{static_cast(-1)}; @@ -789,8 +791,14 @@ class reader_data_t : public std::enable_shared_from_this { /// Do what we need to do whenever our command line changes. void command_line_changed(const editable_line_t *el); void maybe_refilter_pager(const editable_line_t *el); - void fill_history_pager(bool new_search, history_search_direction_t direction = - history_search_direction_t::backward); + enum class history_pager_invocation_t { + anew, + advance, + refresh, + }; + void fill_history_pager( + history_pager_invocation_t why, + history_search_direction_t direction = history_search_direction_t::backward); /// Do what we need to do whenever our pager selection changes. void pager_selection_changed(); @@ -1264,7 +1272,8 @@ void reader_data_t::command_line_changed(const editable_line_t *el) { s_generation.store(1 + read_generation_count(), std::memory_order_relaxed); } else if (el == &this->pager.search_field_line) { if (history_pager_active) { - fill_history_pager(true, history_search_direction_t::backward); + fill_history_pager(history_pager_invocation_t::anew, + history_search_direction_t::backward); return; } this->pager.refilter_completions(); @@ -1317,16 +1326,29 @@ static history_pager_result_t history_pager_search(const std::shared_ptr old_pager_index; + switch (why) { + case history_pager_invocation_t::anew: + assert(direction == history_search_direction_t::backward); + index = 0; + break; + case history_pager_invocation_t::advance: + if (direction == history_search_direction_t::forward) { + index = history_pager_history_index_start; + } else { + assert(direction == history_search_direction_t::backward); + index = history_pager_history_index_end; + } + break; + case history_pager_invocation_t::refresh: + // Redo the previous search previous direction. + direction = history_pager_direction; + index = history_pager_history_index_start; + old_pager_index = pager.selected_completion_index(); + break; } const wcstring &search_term = pager.search_field_line.text(); auto shared_this = this->shared_from_this(); @@ -1335,11 +1357,12 @@ void reader_data_t::fill_history_pager(bool new_search, history_search_direction [=](const history_pager_result_t &result) { if (search_term != shared_this->pager.search_field_line.text()) return; // Stale request. - if (result.matched_commands.empty() && !new_search) { + if (result.matched_commands.empty() && why == history_pager_invocation_t::advance) { // No more matches, keep the existing ones and flash. shared_this->flash(); return; } + history_pager_direction = direction; if (direction == history_search_direction_t::forward) { shared_this->history_pager_history_index_start = result.final_index; shared_this->history_pager_history_index_end = index; @@ -1350,7 +1373,12 @@ void reader_data_t::fill_history_pager(bool new_search, history_search_direction shared_this->pager.extra_progress_text = result.have_more_results ? _(L"Search again for more results") : L""; shared_this->pager.set_completions(result.matched_commands); - shared_this->select_completion_in_direction(selection_motion_t::next, true); + if (why == history_pager_invocation_t::refresh) { + pager.set_selected_completion_index(*old_pager_index); + pager_selection_changed(); + } else { + shared_this->select_completion_in_direction(selection_motion_t::next, true); + } shared_this->super_highlight_me_plenty(); shared_this->layout_and_repaint(L"history-pager"); }); @@ -3540,7 +3568,8 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat } case rl::pager_toggle_search: { if (history_pager_active) { - fill_history_pager(false, history_search_direction_t::forward); + fill_history_pager(history_pager_invocation_t::advance, + history_search_direction_t::forward); break; } if (!pager.empty()) { @@ -3754,7 +3783,8 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat } case rl::history_pager: { if (history_pager_active) { - fill_history_pager(false, history_search_direction_t::backward); + fill_history_pager(history_pager_invocation_t::advance, + history_search_direction_t::backward); break; } @@ -3778,6 +3808,20 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat } break; } + case rl::history_pager_delete: { + if (!history_pager_active) { + inputter.function_set_status(false); + break; + } + inputter.function_set_status(true); + if (auto completion = pager.selected_completion(current_page_rendering)) { + history->remove(completion->completion); + history->save(); + fill_history_pager(history_pager_invocation_t::refresh, + history_search_direction_t::backward); + } + break; + } case rl::backward_char: { editable_line_t *el = active_edit_line(); if (is_navigating_pager_contents()) {