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.
This commit is contained in:
Mahmoud Al-Qudsi 2021-02-07 16:59:07 -06:00
parent 6dd6a57c60
commit 5c014e129a
2 changed files with 107 additions and 88 deletions

View file

@ -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 function metadata. This list should be kept in sync with the key code list in
/// input_common.h. /// input_common.h.
struct input_function_metadata_t { struct input_function_metadata_t {
readline_cmd_t code;
const wchar_t *name; 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[] = { 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 // 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]) == 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 { } else {
has_commands = true; has_commands = true;
} }
if (has_functions && has_commands) {
break;
}
} }
// !has_functions && !has_commands: only set bind mode // !has_functions && !has_commands: only set bind mode
@ -817,22 +823,35 @@ wcstring_list_t input_terminfo_get_names(bool skip_null) {
return result; return result;
} }
wcstring_list_t input_function_get_names() { const wcstring_list_t &input_function_get_names() {
wcstring_list_t result; // The list and names of input functions are hard-coded and never change
result.reserve(input_function_count); static wcstring_list_t result = ([&]() {
for (const auto &md : input_function_metadata) { wcstring_list_t result;
if (md.name) { result.reserve(input_function_count);
result.push_back(md.name); for (const auto &md : input_function_metadata) {
if (md.name[0]) {
result.push_back(md.name);
}
} }
} return result;
})();
return result; return result;
} }
maybe_t<readline_cmd_t> input_function_get_code(const wcstring &name) { maybe_t<readline_cmd_t> input_function_get_code(const wcstring &name) {
for (const auto &md : input_function_metadata) { // `input_function_metadata` is required to be kept in asciibetical order, making it OK to do
if (md.name && name == md.name) { // a binary search for the matching name.
return md.code; 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<readline_cmd_t>(-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(); return none();
} }

View file

@ -144,6 +144,6 @@ wcstring_list_t input_terminfo_get_names(bool skip_null);
maybe_t<readline_cmd_t> input_function_get_code(const wcstring &name); maybe_t<readline_cmd_t> input_function_get_code(const wcstring &name);
/// Returns a list of all existing input function names. /// 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 #endif