diff --git a/builtin.cpp b/builtin.cpp index fb5fbeb43c9c..8b8b8d5c2655 100644 --- a/builtin.cpp +++ b/builtin.cpp @@ -556,8 +556,7 @@ static int builtin_bind(parser_t &parser, wchar_t **argv) BIND_ERASE, BIND_KEY_NAMES, BIND_FUNCTION_NAMES - } - ; + }; int argc=builtin_count_args(argv); int mode = BIND_INSERT; diff --git a/fish_tests.cpp b/fish_tests.cpp index 984c740bed6b..1b66991c8080 100644 --- a/fish_tests.cpp +++ b/fish_tests.cpp @@ -61,6 +61,7 @@ #include "parse_tree.h" #include "parse_util.h" #include "pager.h" +#include "input.h" static const char * const * s_arguments; static int s_test_run_count = 0; @@ -1798,6 +1799,30 @@ static bool history_contains(history_t *history, const wcstring &txt) return result; } +static void test_input() +{ + say(L"Testing input"); + /* Ensure sequences are order independent. Here we add two bindings where the first is a prefix of the second, and then emit the second key list. The second binding should be invoked, not the first! */ + wcstring prefix_binding = L"qqqqqqqa"; + wcstring desired_binding = prefix_binding + L'a'; + input_mapping_add(prefix_binding.c_str(), L"up-line"); + input_mapping_add(desired_binding.c_str(), L"down-line"); + + /* Push the desired binding on the stack (backwards!) */ + size_t idx = desired_binding.size(); + while (idx--) + { + input_unreadch(desired_binding.at(idx)); + } + + /* Now test */ + wint_t c = input_readch(); + if (c != R_DOWN_LINE) + { + err(L"Expected to read char R_DOWN_LINE, but instead got %ls\n", describe_char(c).c_str()); + } +} + class history_tests_t { public: @@ -2836,6 +2861,7 @@ int main(int argc, char **argv) if (should_test_function("is_potential_path")) test_is_potential_path(); if (should_test_function("colors")) test_colors(); if (should_test_function("complete")) test_complete(); + if (should_test_function("input")) test_input(); if (should_test_function("completion_insertions")) test_completion_insertions(); if (should_test_function("autosuggestion_combining")) test_autosuggestion_combining(); if (should_test_function("autosuggest_suggest_special")) test_autosuggest_suggest_special(); diff --git a/input.cpp b/input.cpp index bd855c2b5d90..e4edaa3018ef 100644 --- a/input.cpp +++ b/input.cpp @@ -62,16 +62,20 @@ #define DEFAULT_TERM L"ansi" -/** - Struct representing a keybinding. Returned by input_get_mappings. - */ +/** Struct representing a keybinding. Returned by input_get_mappings. */ struct input_mapping_t { wcstring seq; /**< Character sequence which generates this event */ wcstring command; /**< command that should be evaluated by this mapping */ + + /* We wish to preserve the user-specified order. This is just an incrementing value. */ + unsigned int specification_order; - - input_mapping_t(const wcstring &s, const wcstring &c) : seq(s), command(c) {} + input_mapping_t(const wcstring &s, const wcstring &c) : seq(s), command(c) + { + static unsigned int s_last_input_mapping_specification_order = 0; + specification_order = ++s_last_input_mapping_specification_order; + } }; /** @@ -81,7 +85,6 @@ struct terminfo_mapping_t { const wchar_t *name; /**< Name of key */ const char *seq; /**< Character sequence generated on keypress. Constant string. */ - }; @@ -133,7 +136,7 @@ static const wchar_t * const name_arr[] = L"cancel" }; -wcstring describe_char(wchar_t c) +wcstring describe_char(wint_t c) { wchar_t initial_cmd_char = R_BEGINNING_OF_LINE; size_t name_count = sizeof name_arr / sizeof *name_arr; @@ -257,28 +260,44 @@ static bool is_init = false; */ static void input_terminfo_init(); +/* Helper function to compare the lengths of sequences */ +static bool length_is_greater_than(const input_mapping_t &m1, const input_mapping_t &m2) +{ + return m1.seq.size() > m2.seq.size(); +} + +static bool specification_order_is_less_than(const input_mapping_t &m1, const input_mapping_t &m2) +{ + return m1.specification_order < m2.specification_order; +} -/** - Returns the function description for the given function code. -*/ +/* Inserts an input mapping at the correct position. We sort them in descending order by length, so that we test longer sequences first. */ +static void input_mapping_insert_sorted(const input_mapping_t &new_mapping) +{ + std::vector::iterator loc = std::lower_bound(mapping_list.begin(), mapping_list.end(), new_mapping, length_is_greater_than); + mapping_list.insert(loc, new_mapping); +} + +/* Adds an input mapping */ void input_mapping_add(const wchar_t *sequence, const wchar_t *command) { CHECK(sequence,); CHECK(command,); - - // debug( 0, L"Add mapping from %ls to %ls", escape(sequence, 1), escape(command, 1 ) ); - - for (size_t i=0; i local_list = mapping_list; + std::sort(local_list.begin(), local_list.end(), specification_order_is_less_than); + + for (size_t i=0; i