diff --git a/README.asciidoc b/README.asciidoc index db933813..abcfdfcb 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -1402,6 +1402,15 @@ a single key name and `keys` a list of keys. `user` mode allows for user mapping behind the `,` key. Keys will be executed in normal mode. +Mappings can be removed with the unmap command + +---------------------------------------- +:unmap [] +---------------------------------------- + +If `` is specified, unmapping will only proceed if the current +mapping matches the expected keys. + Defining Commands ~~~~~~~~~~~~~~~~~ diff --git a/doc/manpages/commands.asciidoc b/doc/manpages/commands.asciidoc index 06356f87..c52491b8 100644 --- a/doc/manpages/commands.asciidoc +++ b/doc/manpages/commands.asciidoc @@ -113,8 +113,17 @@ command *q!* has to be used). define a new command (c.f. the 'Declaring new commands' section below) *map* :: - bind a combination of keys to another one (c.f. the 'commands' - documentation page) + make *key* behave as if *keys* were typed. with *scope* + being one of *global*, *buffer* or *window*, *mode* being + *insert*, *normal*, *prompt*, *menu* or *user*. + + *user* mode allows for user mapping behind the *,* key. Keys + will be executed in normal mode. + +*unmap* []:: + remove the mapping of *key* in given *scope* and *mode*, if + expected is specified, only remove the mapping it if matches + the expected keys. *hook* [-group ] :: execute a command whenever an event is triggered (c.f. the 'hooks' diff --git a/src/array_view.hh b/src/array_view.hh index 02844f70..1366805a 100644 --- a/src/array_view.hh +++ b/src/array_view.hh @@ -73,6 +73,25 @@ private: template using ConstArrayView = ArrayView; +template +bool operator==(ArrayView lhs, ArrayView rhs) +{ + if (lhs.size() != rhs.size()) + return false; + for (int i = 0; i < lhs.size(); ++i) + { + if (lhs[i] != rhs[i]) + return false; + } + return true; +} + +template +bool operator!=(ArrayView lhs, ArrayView rhs) +{ + return not (lhs == rhs); +} + } #endif // array_view_hh_INCLUDED diff --git a/src/commands.cc b/src/commands.cc index c96a302b..61bb0ab1 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -1328,6 +1328,23 @@ KeymapMode parse_keymap_mode(const String& str) throw runtime_error(format("unknown keymap mode '{}'", str)); } +auto map_key_completer = + [](const Context& context, CompletionFlags flags, + CommandParameters params, size_t token_to_complete, + ByteCount pos_in_token) -> Completions + { + if (token_to_complete == 0) + return { 0_byte, params[0].length(), + complete(params[0], pos_in_token, scopes) }; + if (token_to_complete == 1) + { + constexpr const char* modes[] = { "normal", "insert", "menu", "prompt", "goto", "view", "user", "object" }; + return { 0_byte, params[1].length(), + complete(params[1], pos_in_token, modes) }; + } + return {}; + }; + const CommandDesc map_key_cmd = { "map", nullptr, @@ -1344,21 +1361,7 @@ const CommandDesc map_key_cmd = { ParameterDesc{{}, ParameterDesc::Flags::None, 4, 4}, CommandFlags::None, CommandHelper{}, - [](const Context& context, CompletionFlags flags, - CommandParameters params, size_t token_to_complete, - ByteCount pos_in_token) -> Completions - { - if (token_to_complete == 0) - return { 0_byte, params[0].length(), - complete(params[0], pos_in_token, scopes) }; - if (token_to_complete == 1) - { - constexpr const char* modes[] = { "normal", "insert", "menu", "prompt", "goto", "view", "user", "object" }; - return { 0_byte, params[1].length(), - complete(params[1], pos_in_token, modes) }; - } - return {}; - }, + map_key_completer, [](const ParametersParser& parser, Context& context, const ShellContext&) { KeymapManager& keymaps = get_scope(parser[0], context).keymaps(); @@ -1373,6 +1376,41 @@ const CommandDesc map_key_cmd = { } }; +const CommandDesc unmap_key_cmd = { + "unmap", + nullptr, + "unmap []: unmap from given mode in .\n" + "If is specified, remove the mapping only if its value is \n" + " can be:\n" + " normal\n" + " insert\n" + " menu\n" + " prompt\n" + " goto\n" + " view\n" + " user\n" + " object\n", + ParameterDesc{{}, ParameterDesc::Flags::None, 4, 4}, + CommandFlags::None, + CommandHelper{}, + map_key_completer, + [](const ParametersParser& parser, Context& context, const ShellContext&) + { + KeymapManager& keymaps = get_scope(parser[0], context).keymaps(); + KeymapMode keymap_mode = parse_keymap_mode(parser[1]); + + KeyList key = parse_keys(parser[2]); + if (key.size() != 1) + throw runtime_error("only a single key can be mapped"); + + if (keymaps.is_mapped(key[0], keymap_mode) and + (parser.positional_count() < 4 or + (keymaps.get_mapping(key[0], keymap_mode) == + ConstArrayView{parse_keys(parser[3])}))) + keymaps.unmap_key(key[0], keymap_mode); + } +}; + const ParameterDesc context_wrap_params = { { { "client", { true, "run in given client context" } }, { "try-client", { true, "run in given client context if it exists, or else in the current one" } }, @@ -2001,6 +2039,7 @@ void register_commands() register_command(unset_option_cmd); register_command(declare_option_cmd); register_command(map_key_cmd); + register_command(unmap_key_cmd); register_command(exec_string_cmd); register_command(eval_string_cmd); register_command(prompt_cmd);