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

Add a sticky mode for keymaps #635

Merged
merged 1 commit into from
Sep 5, 2021

Conversation

sudormrfbin
Copy link
Member

@sudormrfbin sudormrfbin commented Aug 21, 2021

Adds an option to turn keymap nodes sticky, which will temporarily make the sticky node the base keymap. Subsequent keys will be looked up from this sticky keymap node for commands to be executed. Pressing Escape will exit out of this sticky mode.

Usage with the keymap! macro:

let normal_mode = keymap!({ "Normal mode"
    "g" => { "Goto" sticky=true
        "g" => goto_file_start,
        "e" => goto_file_end,
    },
});

Use case includes keybindings for debug mode (#574), sticky view mode, etc.

@cessen
Copy link
Contributor

cessen commented Aug 21, 2021

I think(?) I was the one that originally suggested sticky modes. But I'm not totally sure that's actually what we want in the long term to solve this kind of thing. Still great for the time being, though! I'll write up an RFC at some point about the other approach I've been thinking about.

@sudormrfbin
Copy link
Member Author

@cessen Ah yeah you mentioned them in the multi key remap PR :) One problem I noticed with having a sticky mode (for debug mode specifically) is that movement keys will not be usable while in sticky mode. What is the other approach you were thinking of (as a quick summary) ?

@cessen
Copy link
Contributor

cessen commented Aug 22, 2021

What is the other approach you were thinking of (as a quick summary) ?

I don't think it's worth doing quite yet, because it would be a bigger change, and I'd like to get consensus before anyone codes it up (hence wanting to write up an RFC). But the short version is:

Users can create custom modes, which essentially are just named keymaps. Each mode can be of either a "normal" type or "insert" type, which determines what unmapped keys do: in normal-type they do nothing, in insert-type they insert text. And with this system, the standard built-in normal and insert modes would also just be internally-defined "custom" modes.

The desire for a new debug mode actually makes this seem even more useful than I was initially thinking. Initially I was just thinking of it as being part of letting the user customize how Helix works, but it seems core Helix will also want to define additional modes.

) => {
keymap!({ $label $($($key)|+ => $value,)+ })
keymap!({ $label $(sticky=$sticky)? $($($key)|+ => $value,)+ })
Copy link
Member

Choose a reason for hiding this comment

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

Could we turn this into keymap!(sticky; { })?

Copy link
Member

Choose a reason for hiding this comment

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

Ah I see, it needs to be per submap. I was thinking we could use a better format there too, maybe

"g" => (STICKY "Goto" {
  // keys..
})

Copy link
Member Author

Choose a reason for hiding this comment

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

I tried something to that effect in the first pass but couldn't figure out how to do it. This is what I have right now:

(
    { $label:literal $(sticky=$sticky:literal)? $($($key:literal)|+ => $value:tt,)+ }
) => {
// ...
    let mut _node = $crate::keymap::KeyTrieNode::new($label, _map, _order);
    $( _node.is_sticky = $sticky; )?
    $crate::keymap::KeyTrie::Node(_node)
}

Here $sticky is just a boolean literal so this works all okay. Is there any way to set is_sticky to true if the STICKY token is present in the macro input ?

Copy link
Member

Choose a reason for hiding this comment

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

You could either split it into two branches, or have one branch call the other one. I'm also fine with just duplicating the branches.

i.e., you could call the non-sticky branch, then set node.0.is_sticky = true

Copy link
Member Author

@sudormrfbin sudormrfbin Aug 27, 2021

Choose a reason for hiding this comment

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

I don't think duplicating the rules is a good idea since it'll make the macro even longer and force the duplication of new rules if we add them in the future. Also the gains from all that duplication doesn't seem very large in terms of readability or maintainability.

@archseer
Copy link
Member

is that movement keys will not be usable while in sticky mode.

I still think it might be useful to just overlay the sticky mode over the toplevel. That's what select mode currently simulates, with this change we'd be able to implement it via sticky mode instead.

@archseer
Copy link
Member

but it seems core Helix will also want to define additional modes.

Yeah, for example the view mode becomes a lot more useful if you're able to start it in sticky mode (shift-v), then hjkl scroll the view instead of moving until ESC is pressed. Right now you need to keep tapping vh vh vh.

@archseer
Copy link
Member

(Sidenote: I found an analogy in emacs, it's called hydra: https://github.com/abo-abo/hydra)

@Cons-Cat
Copy link
Contributor

Cons-Cat commented Aug 24, 2021

(Sidenote: I found an analogy in emacs, it's called hydra: https://github.com/abo-abo/hydra)

Hydra is supplanted by Transient, a more powerful and flexible technology invented for Magit. Transient is now built in to Emacs 28. Another cool example of what can be done with it is Sharper, a dotnet compiler frontend for Emacs.

Hydra is still used for the cool debugging interface of dap-mode, though!

Copy link
Member

@archseer archseer left a comment

Choose a reason for hiding this comment

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

Let's get this merged so it can be used for DAP 👍🏻

@archseer archseer merged commit 183dcce into helix-editor:master Sep 5, 2021
@archseer archseer mentioned this pull request Sep 5, 2021
66 tasks
sudormrfbin added a commit to sudormrfbin/helix that referenced this pull request Sep 5, 2021
Regression due to helix-editor#635 where escape key in insert mode
would not exit normal mode. This happened due to hard
coding the escape key to cancel a sticky keymap node.
archseer pushed a commit that referenced this pull request Sep 5, 2021
Regression due to #635 where escape key in insert mode
would not exit normal mode. This happened due to hard
coding the escape key to cancel a sticky keymap node.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants