Add separation of "preset" bindings

This allows for marking certain bindings as part of a preset, which allows us to

- only erase those when switching presets
- go back to the preset binding when erasing a user binding
- only show user customization if requested
- make bare bind statements in config.fish work (!!!11elf!!!)

Fixes #5191.
Fixes #3699.
This commit is contained in:
Fabian Homborg
2018-09-18 11:52:25 +02:00
parent 36a149337b
commit 444f9f8715
12 changed files with 528 additions and 353 deletions

View File

@@ -142,6 +142,7 @@ wcstring describe_char(wint_t c) {
/// Mappings for the current input mode.
static std::vector<input_mapping_t> mapping_list;
static std::vector<input_mapping_t> preset_mapping_list;
/// Terminfo map list.
static std::vector<terminfo_mapping_t> terminfo_mappings;
@@ -205,15 +206,17 @@ static bool specification_order_is_less_than(const input_mapping_t &m1, const in
/// 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) {
static void input_mapping_insert_sorted(const input_mapping_t &new_mapping, bool user = true) {
auto& ml = user ? mapping_list : preset_mapping_list;
std::vector<input_mapping_t>::iterator loc = std::lower_bound(
mapping_list.begin(), mapping_list.end(), new_mapping, length_is_greater_than);
mapping_list.insert(loc, new_mapping);
ml.begin(), ml.end(), new_mapping, length_is_greater_than);
ml.insert(loc, new_mapping);
}
/// Adds an input mapping.
void input_mapping_add(const wchar_t *sequence, const wchar_t *const *commands, size_t commands_len,
const wchar_t *mode, const wchar_t *sets_mode) {
const wchar_t *mode, const wchar_t *sets_mode, bool user) {
CHECK(sequence, );
CHECK(commands, );
CHECK(mode, );
@@ -226,8 +229,10 @@ void input_mapping_add(const wchar_t *sequence, const wchar_t *const *commands,
// Remove existing mappings with this sequence.
const wcstring_list_t commands_vector(commands, commands + commands_len);
for (size_t i = 0; i < mapping_list.size(); i++) {
input_mapping_t &m = mapping_list.at(i);
auto& ml = user ? mapping_list : preset_mapping_list;
for (size_t i = 0; i < ml.size(); i++) {
input_mapping_t &m = ml.at(i);
if (m.seq == sequence && m.mode == mode) {
m.commands = commands_vector;
m.sets_mode = sets_mode;
@@ -237,12 +242,12 @@ void input_mapping_add(const wchar_t *sequence, const wchar_t *const *commands,
// Add a new mapping, using the next order.
const input_mapping_t new_mapping = input_mapping_t(sequence, commands_vector, mode, sets_mode);
input_mapping_insert_sorted(new_mapping);
input_mapping_insert_sorted(new_mapping, user);
}
void input_mapping_add(const wchar_t *sequence, const wchar_t *command, const wchar_t *mode,
const wchar_t *sets_mode) {
input_mapping_add(sequence, &command, 1, mode, sets_mode);
const wchar_t *sets_mode, bool user) {
input_mapping_add(sequence, &command, 1, mode, sets_mode, user);
}
/// Handle interruptions to key reading by reaping finshed jobs and propagating the interrupt to the
@@ -267,20 +272,20 @@ void init_input() {
init_input_terminfo();
// If we have no keybindings, add a few simple defaults.
if (mapping_list.empty()) {
input_mapping_add(L"", L"self-insert");
input_mapping_add(L"\n", L"execute");
input_mapping_add(L"\r", L"execute");
input_mapping_add(L"\t", L"complete");
input_mapping_add(L"\x3", L"commandline ''");
input_mapping_add(L"\x4", L"exit");
input_mapping_add(L"\x5", L"bind");
input_mapping_add(L"\x7f", L"backward-delete-char");
if (preset_mapping_list.empty()) {
input_mapping_add(L"", L"self-insert", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false);
input_mapping_add(L"\n", L"execute", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false);
input_mapping_add(L"\r", L"execute", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false);
input_mapping_add(L"\t", L"complete", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false);
input_mapping_add(L"\x3", L"commandline ''", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false);
input_mapping_add(L"\x4", L"exit", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false);
input_mapping_add(L"\x5", L"bind", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false);
input_mapping_add(L"\x7f", L"backward-delete-char", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false);
// Arrows - can't have functions, so *-or-search isn't available.
input_mapping_add(L"\x1B[A", L"up-line");
input_mapping_add(L"\x1B[B", L"down-line");
input_mapping_add(L"\x1B[C", L"forward-char");
input_mapping_add(L"\x1B[D", L"backward-char");
input_mapping_add(L"\x1B[A", L"up-line", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false);
input_mapping_add(L"\x1B[B", L"down-line", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false);
input_mapping_add(L"\x1B[C", L"forward-char", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false);
input_mapping_add(L"\x1B[D", L"backward-char", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false);
}
input_initialized = true;
@@ -415,16 +420,8 @@ static void input_mapping_execute_matching_or_generic(bool allow_commands) {
const wcstring bind_mode = input_get_bind_mode();
for (size_t i = 0; i < mapping_list.size(); i++) {
const input_mapping_t &m = mapping_list.at(i);
// debug(0, L"trying mapping (%ls,%ls,%ls)\n", escape_string(m.seq.c_str(),
// ESCAPE_ALL).c_str(),
// m.mode.c_str(), m.sets_mode.c_str());
for (auto& m : mapping_list) {
if (m.mode != bind_mode) {
// debug(0, L"skipping mapping because mode %ls != %ls\n", m.mode.c_str(),
// input_get_bind_mode().c_str());
continue;
}
@@ -436,6 +433,21 @@ static void input_mapping_execute_matching_or_generic(bool allow_commands) {
}
}
// HACK: This is ugly duplication.
for (auto& m : preset_mapping_list) {
if (m.mode != bind_mode) {
continue;
}
if (m.seq.length() == 0) {
// Only use this generic if the user list didn't have one.
if (!generic) generic = &m;
} else if (input_mapping_is_match(m)) {
input_mapping_execute(m, allow_commands);
return;
}
}
if (generic) {
input_mapping_execute(*generic, allow_commands);
} else {
@@ -515,10 +527,10 @@ wint_t input_readch(bool allow_commands) {
}
}
std::vector<input_mapping_name_t> input_mapping_get_names() {
std::vector<input_mapping_name_t> input_mapping_get_names(bool user) {
// Sort the mappings by the user specification order, so we can return them in the same order
// that the user specified them in.
std::vector<input_mapping_t> local_list = mapping_list;
std::vector<input_mapping_t> local_list = user ? mapping_list : preset_mapping_list;
std::sort(local_list.begin(), local_list.end(), specification_order_is_less_than);
std::vector<input_mapping_name_t> result;
result.reserve(local_list.size());
@@ -530,14 +542,25 @@ std::vector<input_mapping_name_t> input_mapping_get_names() {
return result;
}
bool input_mapping_erase(const wcstring &sequence, const wcstring &mode) {
void input_mapping_clear(const wchar_t *mode, bool user) {
auto& ml = user ? mapping_list : preset_mapping_list;
for (std::vector<input_mapping_t>::iterator it = ml.begin(), end = ml.end();
it != end; ++it) {
if (mode == NULL || mode == it->mode) {
ml.erase(it);
}
}
}
bool input_mapping_erase(const wcstring &sequence, const wcstring &mode, bool user) {
ASSERT_IS_MAIN_THREAD();
bool result = false;
for (std::vector<input_mapping_t>::iterator it = mapping_list.begin(), end = mapping_list.end();
auto& ml = user ? mapping_list : preset_mapping_list;
for (std::vector<input_mapping_t>::iterator it = ml.begin(), end = ml.end();
it != end; ++it) {
if (sequence == it->seq && mode == it->mode) {
mapping_list.erase(it);
ml.erase(it);
result = true;
break;
}
@@ -545,11 +568,12 @@ bool input_mapping_erase(const wcstring &sequence, const wcstring &mode) {
return result;
}
bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_list_t *out_cmds,
bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_list_t *out_cmds, bool user,
wcstring *out_sets_mode) {
bool result = false;
for (std::vector<input_mapping_t>::const_iterator it = mapping_list.begin(),
end = mapping_list.end();
auto& ml = user ? mapping_list : preset_mapping_list;
for (std::vector<input_mapping_t>::const_iterator it = ml.begin(),
end = ml.end();
it != end; ++it) {
if (sequence == it->seq && mode == it->mode) {
*out_cmds = it->commands;