Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable remapping of Ctrl-C and Ctrl-G #5107

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion doc/pages/keys.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this allows us to maybe make commands like map global normal <c-c> ...
throw an error if cancel_key=<c-c>, and point to that option.

Not sure if we want to do this; it would be a bit more user friendly on one
hand but also enforce an ordering of map/set-option commands in kakrc which
would be weird.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this sounds like it would introduce a little more complexity on both the user's and Kakoune's side. I do wonder if maybe the pointer about "warning, these two behave differently" belongs near the top of :doc mapping rather than at the very bottom of :doc keys?

the `interrupt_key` and `cancel_key` options (see
<<options#builtin-options,`:doc options builtin-options`>>).

*<c-c>*::
Stop any external processes. If you ever see Kakoune display a message
Expand Down
14 changes: 14 additions & 0 deletions doc/pages/options.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
<<mapping#mappable-keys,`:doc mapping mappable-keys`>>). If
multiple keys are entered, only the first will be used.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If multiple keys are entered, only the first will be used.

I think it's better to throw a runtime_error{"too many keys"}, then we can delete this line from docs.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it to require exactly 1, following the example of codepoint. However, I just realized that does prevent determined users from disabling it altogether if they want. Should set global interrupt_key '' be allowed, with a caveat of "be it upon your own head"?


*coord*::
a line, column pair (separated by a comma)
Cannot be used with `declare-option`
Expand Down Expand Up @@ -312,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_ <c-c> +
key used to interrupt any running external processes

*cancel_key* `key`::
_default_ <c-g> +
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
Expand Down
5 changes: 3 additions & 2 deletions src/client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@ Client::Client(std::unique_ptr<UserInterface>&& ui,
m_ui->set_ui_options(m_window->options()["ui_options"].get<UserInterface::Options>());
m_ui->set_on_key([this](Key key) {
kak_assert(key != Key::Invalid);
if (key == ctrl('c'))
auto opts = context().options();
djpohly marked this conversation as resolved.
Show resolved Hide resolved
if (key == opts["interrupt_key"].get<Key>())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so this means that if a user really wants to, they can map it to different
keys in different modes with ModeChange hooks.
I wonder if we want to make this more ergonomic, I don't have a strong
opionion on this, this seems fine.
I guess <c-g> is only ever useful in normal or prompt mode, so it likely doesn't matter.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a good way to sugar it occurs to us later, it could be added at that point, right?

{
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<Key>())
{
m_pending_keys.clear();
print_status({"operation cancelled", context().faces()["Error"]});
Expand Down
5 changes: 4 additions & 1 deletion src/commands.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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&)
Expand All @@ -1866,6 +1867,8 @@ const CommandDesc declare_option_cmd = {
opt = &reg.declare_option<String>(parser[1], docstring, "", flags);
else if (parser[0] == "regex")
opt = &reg.declare_option<Regex>(parser[1], docstring, Regex{}, flags);
else if (parser[0] == "key")
opt = &reg.declare_option<Key>(parser[1], docstring, Key(Key::Invalid), flags);
else if (parser[0] == "int-list")
opt = &reg.declare_option<Vector<int, MemoryDomain::Options>>(parser[1], docstring, {}, flags);
else if (parser[0] == "str-list")
Expand Down
14 changes: 14 additions & 0 deletions src/keys.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<Key>, StringView str)
{
auto keys = parse_keys(str);
if (keys.empty())
return Key(Key::Invalid);

return keys.front();
}

UnitTest test_keys{[]()
{
KeyList keys{
Expand Down
4 changes: 4 additions & 0 deletions src/keys.hh
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ struct Key
static Modifiers to_modifier(MouseButton button) { return Key::Modifiers{((int)button << 6) & (int)Modifiers::MouseButtonMask}; }

Optional<Codepoint> codepoint() const;

static constexpr const char* option_type_name = "key";
};

constexpr bool with_bit_ops(Meta::Type<Key::Modifiers>) { return true; }
Expand All @@ -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<Key>, StringView str);

constexpr Key shift(Key key)
{
Expand Down
2 changes: 2 additions & 0 deletions src/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,8 @@ void register_options()
"set of pair of characters to be considered as matching pairs",
{ '(', ')', '{', '}', '[', ']', '<', '>' });
reg.declare_option<int>("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;
Expand Down