From 3d14582d0fb33f3facc73f528a95eacd8eb61cee Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 24 Feb 2024 21:17:46 -0600 Subject: [PATCH 1/5] Devin J. Pohly Copyright Waiver I dedicate any and all copyright interest in this software to the public domain. I make this dedication for the benefit of the public at large and to the detriment of my heirs and successors. I intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. From a7a3451ea71581fd91612c374a2f9605eea8e0fd Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 24 Feb 2024 21:16:35 -0600 Subject: [PATCH 2/5] Allow options to have type Key --- doc/pages/options.asciidoc | 5 +++++ src/commands.cc | 5 ++++- src/keys.cc | 14 ++++++++++++++ src/keys.hh | 4 ++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/doc/pages/options.asciidoc b/doc/pages/options.asciidoc index 4f69cbfb7e..d196ab5f81 100644 --- a/doc/pages/options.asciidoc +++ b/doc/pages/options.asciidoc @@ -79,6 +79,11 @@ are exclusively available to built-in options. as a string but the set commands will complain if the entered text is not a valid regex +*key*:: + a single keypress using the same syntax as `map` (see + <>). If + multiple keys are entered, only the first will be used. + *coord*:: a line, column pair (separated by a comma) Cannot be used with `declare-option` diff --git a/src/commands.cc b/src/commands.cc index 3413d095b1..73dba29dbf 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -1827,6 +1827,7 @@ const CommandDesc declare_option_cmd = { " bool: boolean (true/false or yes/no)\n" " str: character string\n" " regex: regular expression\n" + " key: keystroke specifier\n" " int-list: list of integers\n" " str-list: list of character strings\n" " completions: list of completion candidates\n" @@ -1843,7 +1844,7 @@ const CommandDesc declare_option_cmd = { make_completer( [](const Context& context, CompletionFlags flags, StringView prefix, ByteCount cursor_pos) -> Completions { - auto c = {"int", "bool", "str", "regex", "int-list", "str-list", "completions", "line-specs", "range-specs", "str-to-str-map"}; + auto c = {"int", "bool", "str", "regex", "key", "int-list", "str-list", "completions", "line-specs", "range-specs", "str-to-str-map"}; return { 0_byte, cursor_pos, complete(prefix, cursor_pos, c), Completions::Flags::Menu }; }), [](const ParametersParser& parser, Context& context, const ShellContext&) @@ -1866,6 +1867,8 @@ const CommandDesc declare_option_cmd = { opt = ®.declare_option(parser[1], docstring, "", flags); else if (parser[0] == "regex") opt = ®.declare_option(parser[1], docstring, Regex{}, flags); + else if (parser[0] == "key") + opt = ®.declare_option(parser[1], docstring, Key(Key::Invalid), flags); else if (parser[0] == "int-list") opt = ®.declare_option>(parser[1], docstring, {}, flags); else if (parser[0] == "str-list") diff --git a/src/keys.cc b/src/keys.cc index 2e9b708b41..d8ae4b45a1 100644 --- a/src/keys.cc +++ b/src/keys.cc @@ -221,6 +221,20 @@ String to_string(Key key) return res; } +String option_to_string(const Key& key) +{ + return to_string(key); +} + +Key option_from_string(Meta::Type, StringView str) +{ + auto keys = parse_keys(str); + if (keys.empty()) + return Key(Key::Invalid); + + return keys.front(); +} + UnitTest test_keys{[]() { KeyList keys{ diff --git a/src/keys.hh b/src/keys.hh index ccafe336d3..e95c858d73 100644 --- a/src/keys.hh +++ b/src/keys.hh @@ -94,6 +94,8 @@ struct Key static Modifiers to_modifier(MouseButton button) { return Key::Modifiers{((int)button << 6) & (int)Modifiers::MouseButtonMask}; } Optional codepoint() const; + + static constexpr const char* option_type_name = "key"; }; constexpr bool with_bit_ops(Meta::Type) { return true; } @@ -107,6 +109,8 @@ KeyList parse_keys(StringView str); String to_string(Key key); StringView to_string(Key::MouseButton button); Key::MouseButton str_to_button(StringView str); +String option_to_string(const Key& key); +Key option_from_string(Meta::Type, StringView str); constexpr Key shift(Key key) { From 6ecf6bc06bb06182d1980f2f81b238baa986e9be Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 24 Feb 2024 21:18:38 -0600 Subject: [PATCH 3/5] Add interrupt_key and cancel_key options This allows the user to choose different interrupt and cancel keys, allowing and to be mapped to something else if desired. These two options are only applicable at the global scope. --- doc/pages/keys.asciidoc | 4 +++- doc/pages/options.asciidoc | 9 +++++++++ src/client.cc | 5 +++-- src/main.cc | 2 ++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/doc/pages/keys.asciidoc b/doc/pages/keys.asciidoc index 031a8ac2ff..fe7ade1f80 100644 --- a/doc/pages/keys.asciidoc +++ b/doc/pages/keys.asciidoc @@ -837,7 +837,9 @@ The following keys are recognized by this mode to help with editing These keys are used to cancel long-running operations, either inside Kakoune or outside it. Because they are intended as a safety mechanism when something goes wrong, these keys are handled very early on in -Kakoune's input processing, and therefore cannot be remapped in any mode. +Kakoune's input processing, so they can only be remapped by changing +the `interrupt_key` and `cancel_key` options (see +<>). **:: Stop any external processes. If you ever see Kakoune display a message diff --git a/doc/pages/options.asciidoc b/doc/pages/options.asciidoc index d196ab5f81..3ee7801458 100644 --- a/doc/pages/options.asciidoc +++ b/doc/pages/options.asciidoc @@ -317,6 +317,15 @@ are exclusively available to built-in options. *debug* `flags(hooks|shell|profile|keys|commands)`:: dump various debug information in the '\*debug*' buffer +*interrupt_key* `key`:: + _default_ + + key used to interrupt any running external processes + +*cancel_key* `key`:: + _default_ + + key used to cancel long-running Kakoune operations and clear the input + buffer + *idle_timeout* `int`:: _default_ 50 + timeout, in milliseconds, with no user input that will trigger the diff --git a/src/client.cc b/src/client.cc index 20123c2fe0..d21ac85511 100644 --- a/src/client.cc +++ b/src/client.cc @@ -47,13 +47,14 @@ Client::Client(std::unique_ptr&& ui, m_ui->set_ui_options(m_window->options()["ui_options"].get()); m_ui->set_on_key([this](Key key) { kak_assert(key != Key::Invalid); - if (key == ctrl('c')) + auto opts = context().options(); + if (key == opts["interrupt_key"].get()) { auto prev_handler = set_signal_handler(SIGINT, SIG_IGN); killpg(getpgrp(), SIGINT); set_signal_handler(SIGINT, prev_handler); } - else if (key == ctrl('g')) + else if (key == opts["cancel_key"].get()) { m_pending_keys.clear(); print_status({"operation cancelled", context().faces()["Error"]}); diff --git a/src/main.cc b/src/main.cc index 98181601fd..c0f1cc40b9 100644 --- a/src/main.cc +++ b/src/main.cc @@ -609,6 +609,8 @@ void register_options() "set of pair of characters to be considered as matching pairs", { '(', ')', '{', '}', '[', ']', '<', '>' }); reg.declare_option("startup_info_version", "version up to which startup info changes should be hidden", 0); + reg.declare_option("cancel_key", "key used to cancel long-running operations", ctrl('g')); + reg.declare_option("interrupt_key", "key used to stop external processes", ctrl('c')); } static Client* local_client = nullptr; From 862718d02103f23bd7fd318de09986855302b840 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 26 Feb 2024 09:27:12 -0600 Subject: [PATCH 4/5] Require setting "key" type option to exactly 1 key (following the example of "codepoint") --- doc/pages/options.asciidoc | 3 +-- src/keys.cc | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/pages/options.asciidoc b/doc/pages/options.asciidoc index 3ee7801458..54ec95e521 100644 --- a/doc/pages/options.asciidoc +++ b/doc/pages/options.asciidoc @@ -81,8 +81,7 @@ are exclusively available to built-in options. *key*:: a single keypress using the same syntax as `map` (see - <>). If - multiple keys are entered, only the first will be used. + <>) *coord*:: a line, column pair (separated by a comma) diff --git a/src/keys.cc b/src/keys.cc index d8ae4b45a1..e83cde6e75 100644 --- a/src/keys.cc +++ b/src/keys.cc @@ -229,8 +229,8 @@ String option_to_string(const Key& key) Key option_from_string(Meta::Type, StringView str) { auto keys = parse_keys(str); - if (keys.empty()) - return Key(Key::Invalid); + if (keys.size() != 1) + throw runtime_error(format("'{}' is not a single key", str)); return keys.front(); } From 13e0054f2d01ef7c3b9aaab8b240b6a2527619ea Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 26 Feb 2024 09:28:37 -0600 Subject: [PATCH 5/5] Use reference to avoid copying OptionManager --- src/client.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client.cc b/src/client.cc index d21ac85511..7b3e5e3d4d 100644 --- a/src/client.cc +++ b/src/client.cc @@ -47,7 +47,7 @@ Client::Client(std::unique_ptr&& ui, m_ui->set_ui_options(m_window->options()["ui_options"].get()); m_ui->set_on_key([this](Key key) { kak_assert(key != Key::Invalid); - auto opts = context().options(); + auto& opts = context().options(); if (key == opts["interrupt_key"].get()) { auto prev_handler = set_signal_handler(SIGINT, SIG_IGN);