From 5c014e129a67bc47b90a72658e476434bfa4a24a Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 7 Feb 2021 16:59:07 -0600 Subject: [PATCH] Reduce input latency searching for readline function mappings The lookups are executed on all input events, so they are worth optimizing. Cache the list of names, use binary search to get a function code from a name, and stop enumerating mappings after `has_function` and `has_command` have been determined. --- src/input.cpp | 193 +++++++++++++++++++++++++++----------------------- src/input.h | 2 +- 2 files changed, 107 insertions(+), 88 deletions(-) diff --git a/src/input.cpp b/src/input.cpp index 53d357093..24d4c70ce 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -80,87 +80,89 @@ static constexpr size_t input_function_count = R_END_INPUT_FUNCTIONS; /// Input function metadata. This list should be kept in sync with the key code list in /// input_common.h. struct input_function_metadata_t { - readline_cmd_t code; const wchar_t *name; + readline_cmd_t code; }; +/// A static mapping of all readline commands as strings to their readline_cmd_t equivalent. +/// Keep this list sorted alphabetically! static const input_function_metadata_t input_function_metadata[] = { - {readline_cmd_t::beginning_of_line, L"beginning-of-line"}, - {readline_cmd_t::end_of_line, L"end-of-line"}, - {readline_cmd_t::forward_char, L"forward-char"}, - {readline_cmd_t::backward_char, L"backward-char"}, - {readline_cmd_t::forward_single_char, L"forward-single-char"}, - {readline_cmd_t::forward_word, L"forward-word"}, - {readline_cmd_t::backward_word, L"backward-word"}, - {readline_cmd_t::forward_bigword, L"forward-bigword"}, - {readline_cmd_t::backward_bigword, L"backward-bigword"}, - {readline_cmd_t::history_prefix_search_backward, L"history-prefix-search-backward"}, - {readline_cmd_t::history_prefix_search_forward, L"history-prefix-search-forward"}, - {readline_cmd_t::history_search_backward, L"history-search-backward"}, - {readline_cmd_t::history_search_forward, L"history-search-forward"}, - {readline_cmd_t::delete_char, L"delete-char"}, - {readline_cmd_t::backward_delete_char, L"backward-delete-char"}, - {readline_cmd_t::kill_line, L"kill-line"}, - {readline_cmd_t::yank, L"yank"}, - {readline_cmd_t::yank_pop, L"yank-pop"}, - {readline_cmd_t::complete, L"complete"}, - {readline_cmd_t::complete_and_search, L"complete-and-search"}, - {readline_cmd_t::pager_toggle_search, L"pager-toggle-search"}, - {readline_cmd_t::beginning_of_history, L"beginning-of-history"}, - {readline_cmd_t::end_of_history, L"end-of-history"}, - {readline_cmd_t::backward_kill_line, L"backward-kill-line"}, - {readline_cmd_t::kill_whole_line, L"kill-whole-line"}, - {readline_cmd_t::kill_word, L"kill-word"}, - {readline_cmd_t::kill_bigword, L"kill-bigword"}, - {readline_cmd_t::backward_kill_word, L"backward-kill-word"}, - {readline_cmd_t::backward_kill_path_component, L"backward-kill-path-component"}, - {readline_cmd_t::backward_kill_bigword, L"backward-kill-bigword"}, - {readline_cmd_t::history_token_search_backward, L"history-token-search-backward"}, - {readline_cmd_t::history_token_search_forward, L"history-token-search-forward"}, - {readline_cmd_t::self_insert, L"self-insert"}, - {readline_cmd_t::self_insert_notfirst, L"self-insert-notfirst"}, - {readline_cmd_t::transpose_chars, L"transpose-chars"}, - {readline_cmd_t::transpose_words, L"transpose-words"}, - {readline_cmd_t::upcase_word, L"upcase-word"}, - {readline_cmd_t::downcase_word, L"downcase-word"}, - {readline_cmd_t::capitalize_word, L"capitalize-word"}, - {readline_cmd_t::togglecase_char, L"togglecase-char"}, - {readline_cmd_t::togglecase_selection, L"togglecase-selection"}, - {readline_cmd_t::execute, L"execute"}, - {readline_cmd_t::beginning_of_buffer, L"beginning-of-buffer"}, - {readline_cmd_t::end_of_buffer, L"end-of-buffer"}, - {readline_cmd_t::repaint_mode, L"repaint-mode"}, - {readline_cmd_t::repaint, L"repaint"}, - {readline_cmd_t::force_repaint, L"force-repaint"}, - {readline_cmd_t::up_line, L"up-line"}, - {readline_cmd_t::down_line, L"down-line"}, - {readline_cmd_t::suppress_autosuggestion, L"suppress-autosuggestion"}, - {readline_cmd_t::accept_autosuggestion, L"accept-autosuggestion"}, - {readline_cmd_t::begin_selection, L"begin-selection"}, - {readline_cmd_t::swap_selection_start_stop, L"swap-selection-start-stop"}, - {readline_cmd_t::end_selection, L"end-selection"}, - {readline_cmd_t::kill_selection, L"kill-selection"}, - {readline_cmd_t::insert_line_under, L"insert-line-under"}, - {readline_cmd_t::insert_line_over, L"insert-line-over"}, - {readline_cmd_t::forward_jump, L"forward-jump"}, - {readline_cmd_t::backward_jump, L"backward-jump"}, - {readline_cmd_t::forward_jump_till, L"forward-jump-till"}, - {readline_cmd_t::backward_jump_till, L"backward-jump-till"}, - {readline_cmd_t::repeat_jump, L"repeat-jump"}, - {readline_cmd_t::reverse_repeat_jump, L"repeat-jump-reverse"}, - {readline_cmd_t::func_and, L"and"}, - {readline_cmd_t::func_or, L"or"}, - {readline_cmd_t::expand_abbr, L"expand-abbr"}, - {readline_cmd_t::delete_or_exit, L"delete-or-exit"}, - {readline_cmd_t::exit, L"exit"}, - {readline_cmd_t::cancel_commandline, L"cancel-commandline"}, - {readline_cmd_t::cancel, L"cancel"}, - {readline_cmd_t::undo, L"undo"}, - {readline_cmd_t::redo, L"redo"}, - {readline_cmd_t::begin_undo_group, L"begin-undo-group"}, - {readline_cmd_t::end_undo_group, L"end-undo-group"}, // NULL makes it unusable - this is specially inserted when we detect mouse input - {readline_cmd_t::disable_mouse_tracking, NULL}, + {L"", readline_cmd_t::disable_mouse_tracking}, + {L"accept-autosuggestion", readline_cmd_t::accept_autosuggestion}, + {L"and", readline_cmd_t::func_and}, + {L"backward-bigword", readline_cmd_t::backward_bigword}, + {L"backward-char", readline_cmd_t::backward_char}, + {L"backward-delete-char", readline_cmd_t::backward_delete_char}, + {L"backward-jump", readline_cmd_t::backward_jump}, + {L"backward-jump-till", readline_cmd_t::backward_jump_till}, + {L"backward-kill-bigword", readline_cmd_t::backward_kill_bigword}, + {L"backward-kill-line", readline_cmd_t::backward_kill_line}, + {L"backward-kill-path-component", readline_cmd_t::backward_kill_path_component}, + {L"backward-kill-word", readline_cmd_t::backward_kill_word}, + {L"backward-word", readline_cmd_t::backward_word}, + {L"begin-selection", readline_cmd_t::begin_selection}, + {L"begin-undo-group", readline_cmd_t::begin_undo_group}, + {L"beginning-of-buffer", readline_cmd_t::beginning_of_buffer}, + {L"beginning-of-history", readline_cmd_t::beginning_of_history}, + {L"beginning-of-line", readline_cmd_t::beginning_of_line}, + {L"cancel", readline_cmd_t::cancel}, + {L"cancel-commandline", readline_cmd_t::cancel_commandline}, + {L"capitalize-word", readline_cmd_t::capitalize_word}, + {L"complete", readline_cmd_t::complete}, + {L"complete-and-search", readline_cmd_t::complete_and_search}, + {L"delete-char", readline_cmd_t::delete_char}, + {L"delete-or-exit", readline_cmd_t::delete_or_exit}, + {L"down-line", readline_cmd_t::down_line}, + {L"downcase-word", readline_cmd_t::downcase_word}, + {L"end-of-buffer", readline_cmd_t::end_of_buffer}, + {L"end-of-history", readline_cmd_t::end_of_history}, + {L"end-of-line", readline_cmd_t::end_of_line}, + {L"end-selection", readline_cmd_t::end_selection}, + {L"end-undo-group", readline_cmd_t::end_undo_group}, + {L"execute", readline_cmd_t::execute}, + {L"exit", readline_cmd_t::exit}, + {L"expand-abbr", readline_cmd_t::expand_abbr}, + {L"force-repaint", readline_cmd_t::force_repaint}, + {L"forward-bigword", readline_cmd_t::forward_bigword}, + {L"forward-char", readline_cmd_t::forward_char}, + {L"forward-jump", readline_cmd_t::forward_jump}, + {L"forward-jump-till", readline_cmd_t::forward_jump_till}, + {L"forward-single-char", readline_cmd_t::forward_single_char}, + {L"forward-word", readline_cmd_t::forward_word}, + {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}, + {L"history-search-forward", readline_cmd_t::history_search_forward}, + {L"history-token-search-backward", readline_cmd_t::history_token_search_backward}, + {L"history-token-search-forward", readline_cmd_t::history_token_search_forward}, + {L"insert-line-over", readline_cmd_t::insert_line_over}, + {L"insert-line-under", readline_cmd_t::insert_line_under}, + {L"kill-bigword", readline_cmd_t::kill_bigword}, + {L"kill-line", readline_cmd_t::kill_line}, + {L"kill-selection", readline_cmd_t::kill_selection}, + {L"kill-whole-line", readline_cmd_t::kill_whole_line}, + {L"kill-word", readline_cmd_t::kill_word}, + {L"or", readline_cmd_t::func_or}, + {L"pager-toggle-search", readline_cmd_t::pager_toggle_search}, + {L"redo", readline_cmd_t::redo}, + {L"repaint", readline_cmd_t::repaint}, + {L"repaint-mode", readline_cmd_t::repaint_mode}, + {L"repeat-jump", readline_cmd_t::repeat_jump}, + {L"repeat-jump-reverse", readline_cmd_t::reverse_repeat_jump}, + {L"self-insert", readline_cmd_t::self_insert}, + {L"self-insert-notfirst", readline_cmd_t::self_insert_notfirst}, + {L"suppress-autosuggestion", readline_cmd_t::suppress_autosuggestion}, + {L"swap-selection-start-stop", readline_cmd_t::swap_selection_start_stop}, + {L"togglecase-char", readline_cmd_t::togglecase_char}, + {L"togglecase-selection", readline_cmd_t::togglecase_selection}, + {L"transpose-chars", readline_cmd_t::transpose_chars}, + {L"transpose-words", readline_cmd_t::transpose_words}, + {L"undo", readline_cmd_t::undo}, + {L"up-line", readline_cmd_t::up_line}, + {L"upcase-word", readline_cmd_t::upcase_word}, + {L"yank", readline_cmd_t::yank}, + {L"yank-pop", readline_cmd_t::yank_pop}, }; static_assert(sizeof(input_function_metadata) / sizeof(input_function_metadata[0]) == @@ -375,6 +377,10 @@ void inputter_t::mapping_execute(const input_mapping_t &m, } else { has_commands = true; } + + if (has_functions && has_commands) { + break; + } } // !has_functions && !has_commands: only set bind mode @@ -817,22 +823,35 @@ wcstring_list_t input_terminfo_get_names(bool skip_null) { return result; } -wcstring_list_t input_function_get_names() { - wcstring_list_t result; - result.reserve(input_function_count); - for (const auto &md : input_function_metadata) { - if (md.name) { - result.push_back(md.name); +const wcstring_list_t &input_function_get_names() { + // The list and names of input functions are hard-coded and never change + static wcstring_list_t result = ([&]() { + wcstring_list_t result; + result.reserve(input_function_count); + for (const auto &md : input_function_metadata) { + if (md.name[0]) { + result.push_back(md.name); + } } - } + return result; + })(); + return result; } maybe_t input_function_get_code(const wcstring &name) { - for (const auto &md : input_function_metadata) { - if (md.name && name == md.name) { - return md.code; - } + // `input_function_metadata` is required to be kept in asciibetical order, making it OK to do + // a binary search for the matching name. + constexpr auto end = &input_function_metadata[0] + input_function_count; + auto result = std::lower_bound( + &input_function_metadata[0], end, + input_function_metadata_t{name.data(), static_cast(-1)}, + [&](const input_function_metadata_t &lhs, const input_function_metadata_t &rhs) { + return wcscmp(lhs.name, rhs.name) < 0; + }); + + if (result != end && result->name[0] && name == result->name) { + return result->code; } return none(); } diff --git a/src/input.h b/src/input.h index 0132643ab..ed979fa5e 100644 --- a/src/input.h +++ b/src/input.h @@ -144,6 +144,6 @@ wcstring_list_t input_terminfo_get_names(bool skip_null); maybe_t input_function_get_code(const wcstring &name); /// Returns a list of all existing input function names. -wcstring_list_t input_function_get_names(void); +const wcstring_list_t &input_function_get_names(void); #endif