Closed jojojames closed 6 years ago
I'm not sure how this would help. I don't know why evil-add-hjkl-bindings
does this for sure (I'd have to look into it), but my guess is that it just wants to keep the hjkl bindings consistent with what's bound in the global motion state map. So, for example, if the user bound j
and k
to their visual line movement equivalents, and then used evil-add-hjkl-bindings
afterwards, this change would be reflected. Maybe I'm wrong about the intention, but all this would do would be to give the wrong hjkl keybindings for someone who uses a different keyboard layout.
See my most recent suggestion in Ambrevar/evil-special-modes#4. Emacs allows binding a key to another key in a specific keymap (where the definition is (MAP . CHAR)). You couldn't swap keys like this if they were bound in the same keymap and pointing to the same keymap. However, you could potentially do something like this:
(evil-get-auxiliary-keymap Info-mode-map 'state t t)
)It wouldn't be too hard to provide convenience functions to do this. Maybe there is a better way, but this is my initial idea.
I'm curious if you wanted to drive this forward @noctuid.
I'm thinking the challenge here isn't "Can we do it?" but "Do we have time/energy to do it?"
I like the idea in supporting every type of user but 1. I don't use !Qwerty so testing it will be a big issue and 2. I'm a little wary of the complexity multiplier this might add.
If we can make some safe/big assumptions like QWERTY (or something very similar), most of the code doing the rebinding will be extremely simple/easy to read and will let anyone that needs to scratch an itch write their own binding and submit it. I'm not sure if supporting additional customization will add significant complexity bloat (it may not add any at all!)
Couple that with what I assume is an even smaller subset of users (maybe we can count them on one hand :)) that are doing Emacs -> Evil -> Colemak/Dvork/ETC seems tough.
We could use your help in this area if you were interested.
My idea was not to support any other layout directly but to provide a means for users of other layouts to make the key translations themselves. This method would also be useful for QWERTY users who just want to globally replace certain keys. Ideally, this package shouldn't have to do much itself. If you'd be okay with it, I think it would be nice to provide the user with some helper functions for translation in the base package. I could write these and add information about them to the README.
The question is what the best way to do this is. @Somelauw pointed out in the other issue that you could just copy and replace a keymap after swapping keys (maybe that's what you were initially suggesting and I didn't understand correctly). I think a method that preserves the original keymap might be a better solution (think vim's noremap
). For one, not all translations would need to be made at the same time. The user could also experiment with translations without having to reload the packages to try new ones (not as big a deal). This method would either require initially storing the original keymap in another variable for reference.
If you'd be okay with it, I think it would be nice to provide the user with some helper functions for translation in the base package. I could write these and add information about them to the README.
That'd be awesome. Would most likely resolve this too.
https://github.com/jojojames/evil-collection/issues/2
@Somelauw pointed out in the other issue that you could just copy and replace a keymap after swapping keys (maybe that's what you were initially suggesting and I didn't understand correctly).
Yeah, something along those lines. I've seen some key remapping type code using lookup-key (not sure if it was the evilify macro or some other emacs package I've looked through once).
I think the following kinds of remappings can be useful.
Swapping keys For example, swap "h" and "n". Can also be described as two rotations: h -> n n -> h
Rotations For example t -> f f -> j j -> t
Preserving leader keys SPC -> S-SPC -> C-S-SPC So actions that would otherwise be bound to SPC, automatically get bound to the following key. The code of evil-evilified-state already does this.
Note: Regardless of how this is implemented, it would be best if this remapping system works with existing evil integrations (not just evil-collection), so that no changes are necessary to packages such as evil-magit and evil-org(-agenda).
@Somelauw I added an experimental version to general.el if you want to try it. I'll make a pull request to add it here after testing it and getting some feedback. Here is an example call:
;; swap h and n in org-mode normal state
(general-translate-keys 'normal 'org-mode-map
"h" "n"
"n" "h")
This will work with any keymap (for non-evil keymaps, STATE can be nil). KEYMAP must be quoted. By default, it will store a backup of the keymap and do all lookups in the backup. This means that if you eval'd the previous example multiple times, the result would be the same. There is also a destructive option, so :destructive t
will modify the keymap without making a backup. Calling the previous example multiple times with :destructive t
would continuously swap and unswap "h" and "n".
@jojojames @Ambrevar The question is now how to best use something like this for all keymaps that evil-collection
alters. This needs to be run after evil-collection
creates its keybindings, so the current method would be something like:
(with-eval-after-load 'evil-proced
(evil-collection-translate-keys 'motion 'proced-mode-map
...))
This isn't too bad, but there would be a lot of code duplication in the user config. That said, do you think it might make sense add some code at the end of each setup function to run some user function(s) (basically hooks)? It would pass the function a list of states/keymaps keybindings were created in so that the user could just define their translations in one place.
As an alternative solution, evil-define-key
works fine even if the keymap does not exists (because of evil-delay
). This means that with-eval-after-load
is not necessary for keybindings created with evil-define-key
. I'd need to check that code delayed by evil-delay
will be executed in order (evil-define-key
appends to the after-load-functions
hook, so I'm guessing this is the case). If evil-collections
allows running all evil-define-key
s at once without eval-after-load
based on a list of modes or keymaps (like mentioned in #7), the user could just iterate through that list after running the evil-collection
setup function.
;; swap h and n in org-mode normal state
(general-translate-keys 'normal 'org-mode-map
"h" "n")
I wonder if we also want another function that achieves swaps instead of translations so the user can put in key pairs they want to swap.
;; swap h and n in org-mode normal state
(general-swap-keys 'normal 'org-mode-map
"h" "n")
That said, do you think it might make sense add some code at the end of each setup function to run some user function(s) (basically hooks)? It would pass the function a list of states/keymaps keybindings were created in so that the user could just define their translations in one place.
I like this better than the alternative solution. What do you think @Ambrevar?
I wonder if we also want another function that achieves swaps
The easiest way to do it would be to add a keyword argument :swap
, but at that point it would just be shorter to specify both translations (though I could also just add a wrapper function ...-swap-keys
that sets the keyword argument). If other people would prefer to have this, I can add it, but I personally wouldn't find it useful. I think it makes sense to just have one generic function that works for all cases.
If other people would prefer to have this, I can add it, but I personally wouldn't find it useful. I think it makes sense to just have one generic function that works for all cases.
Fair enough.
Looking at how evil-collection-init
is written currently, I think the simplest thing to do would just be to call a user setup function. Something like this:
(defcustom evil-collection-user-setup-function #'ignore ...)
....
;; last line in evil-collection-init
(funcall evil-collection-user-setup-function m)
The only concern I'd have is non-standard keymap naming (which probably will never be an issue but is worth mentioning). It might make sense to add an additional function evil-collection-get-keymap-for-mode
that will return the keymap symbol (or symbol-value) for the mode. Normally it would just add -map
. This arguably might be useful even if non-standard keymap naming is never an issue for packages evil-collections deals with. Thoughts?
Looking at how evil-collection-init is written currently, I think the simplest thing to do would just be to call a user setup function. Something like this:
Sounds good to me. Lets keep it simple.
The only concern I'd have is non-standard keymap naming (which probably will never be an issue but is worth mentioning). It might make sense to add an additional function evil-collection-get-keymap-for-mode that will return the keymap symbol (or symbol-value) for the mode.
Lets wait and see for a case to crop up before we fix it. It won't be too late then.
I would like to try general-translate-keys or have it make it here at some point.
I've been pretty busy recently. I can probably make improvements and create a pull request next week.
I've been pretty busy recently. I can probably make improvements and create a pull request next week.
Awesome. Sounds good.
Why not, instead of directly mapping the functions to new key names, bind them to "middle-man" symbols that change on a per-mode basis? This would do a lot to:
h
to the "evil-down-line" would do a lot to ease their transition if they want to do the equivalent of nmap h j
That said, this would appear require that we have an always-active minor mode to overwrite existing keybinds that's loaded last, and is smart about not stepping on other modes' feet when it shouldn't; however I have a couple proposals to solve this foot-stepping issue:
global < major mode < minor mode
.
gj
be in the default keymaps list and gr
be part of said hash tables.el
files are loaded they have certain keywords replaced by (kbd BIND)
eval-buffer
and instruct users about it if they want to make changes to their keymap on the fly, or possibly advise it based on extension and whether it's somewhere in load-path
I would be happy to implement either of these in my time off over the course of the next week with a bit of direction.
Why not, instead of directly mapping the functions to new key names, bind them to "middle-man" symbols that change on a per-mode basis?
I don't understand this suggestion, but it sounds significantly more convoluted than the current implementation, and I'm not sure what the benefit would be. Could you provide a concrete example of the basic idea in emacs lisp and more description about what evil-collection would actually do and how the user would use the interface?
Yeah, sorry that was rather poorly explained. The goal is to allow users to just do something more akin to (evil-collection-bind-key (kbd "h") 'evil-collection-down)
or (evil-define-key 'normal 'evil-collection-map (kbd "h") 'evil-collection-down), and then have that automatically translate all of the keybinds given by this package. Using something like
evli-collection-jor simply
jwould work too instead of
evil-collection-down`; what I'm after is a more consistent customization interface with existing key-binding.
Which appears to be what you're working on already, after looking at your comment from Dec 8th again. Sorry, I apparently got rather enthralled last night in the details of implementation and didn't read your comment as carefully as I should have. If you want any help with your implementation I'd be happy to provide it, but no big deal either way.
https://github.com/jojojames/evil-integrations/issues/1#issuecomment-341961849 https://github.com/Ambrevar/evil-special-modes/issues/4
I was looking at the expansion of
lookup-key may be what we're looking for?