syl20bnr / spacemacs

A community-driven Emacs distribution - The best editor is neither Emacs nor Vim, it's Emacs *and* Vim!
http://spacemacs.org
GNU General Public License v3.0
23.66k stars 4.89k forks source link

SPC leader key with xah-fly-keys overridden by SLIME #12947

Closed gcentauri closed 4 years ago

gcentauri commented 4 years ago

Description :octocat:

Slime modes steal the xah-fly-keys SPC leader key.

Reproduction guide :beetle:

Observed behaviour: :eyes: :broken_heart:

invokes one of the slime-space functions, and not the xah-fly-keys leader

Expected behaviour: :heart: :smile:

xah-fly-keys leader is used in command mode, slime-space functions in insert mode

System Info :computer:

Backtrace :paw_prints:

gcentauri commented 4 years ago

This isn't exactly a Spacemacs issue, but I'm trying to integrate the xah-fly-keys package in a layer for an alternative modal map.

I've tested this out using Spacemacs with evil, and the Spacemacs leader key as SPC still works. However, I've been unable to determine how this is being accomplished. Perhaps it has something to do with how the evil keymaps are implemented, and the Spacemacs leader key is defined there?

gcentauri commented 4 years ago

ofc now that i made an issue, i discovered bind-map, which I think is how this is probably working :man_shrugging:

duianto commented 4 years ago

Confirmed.

Setup xah-fly-keys

After remembering how to setup xah-fly-keys 😄

In .spacemacs:

I remembered that the xah-fly-keys layer instructions mentioning changing to the emacs editing style, and to remove the leader key: https://github.com/gcentauri/xah-fly-keys-layer#install

The instructions also mention:

set dotspacemacs-colorize-cursor-according-to-state nil so that the cursor will reflect the xah-fly-keys state instead.

I tried changing it to nil but the cursor doesn't change shape/color, so maybe it's specific to the xah-fly-keys layer, therefore it's not important while testing this.

I also found the screenshot of the xah-fly-keys layout in the section: What Does Command Mode Do?

That helped with remembering how to navigate 😄

Testing

After figuring out how to open a slime repl: M-x slime

The slime buffer prompt opened in xah-fly-keys command mode. (should it open in insert mode? then one can start typing directly, or is it more consistent to default to command mode?)

When SPC is pressed, then it calls slime-autodoc-space

I noticed that the modeline shows the text: adoc When moving the mouse over the text, then the minibuffer shows: slime-autodoc-mode

I tried disabling the mode: M-x slime-autodoc-mode

And now SPC calls slime-space.

C-SPC works with slime-autodoc-mode either enabled or disabled.

With slime-autodoc-mode enabled.

C-h k SPC shows:

SPC runs the command slime-autodoc-space (found in slime-autodoc-mode-map), which is an interactive compiled Lisp function in ‘slime-autodoc.el’.

It is bound to SPC.

(slime-autodoc-space N)

Like ‘slime-space’ but nicer.

At first I checked the definition for slime-space:

    (" "          slime-space)

source: https://github.com/slime/slime/blob/26a79a673a1d0ed2ee3d6346b395aa8ba084ddde/slime.el#L585

I tried rebinding " " in several slime key maps:

      (define-key slime-repl-mode-map (kbd " ") 'xah-fly-leader-key-map)
      (define-key slime-autodoc-mode-map (kbd " ") 'xah-fly-leader-key-map)
      (define-key slime-doc-map (kbd " ") 'xah-fly-leader-key-map)
      (define-key slime-prefix-map (kbd " ") 'xah-fly-leader-key-map)
      (define-key slime-parent-map (kbd " ") 'xah-fly-leader-key-map)
      (define-key slime-mode-map (kbd " ") 'xah-fly-leader-key-map)
      (define-key slime-mode-indirect-map (kbd " ") 'xah-fly-leader-key-map)

but none of them worked.

Then I saw that slime-autodoc-space was bound with "SPC" in the slime-autodoc-mode minor mode definition: https://github.com/slime/slime/blob/26a79a673a1d0ed2ee3d6346b395aa8ba084ddde/contrib/slime-autodoc.el#L183

A possible solution

And when I tried:

      (define-key slime-autodoc-mode-map (kbd "SPC") 'xah-fly-leader-key-map)
      (define-key slime-repl-mode-map (kbd "SPC") 'xah-fly-leader-key-map)

Now SPC opens the xah-fly-keys-leader-key-map both when slime-autodoc-space is enabled or disabled.

However it does that in both xah-fly-keys command and insert mode.

Maybe a hook can be used to only apply those key bindings when the xah-fly-keys command mode is active. You probably know more about how to use hooks.

An issue with , (comma)

I also noticed that when the cursor is at the beginning of the slime prompt. when , (comma) is pressed in both command and insert state, then slime-handle-repl-shortcut gets called.

If another character is typed first, for example a, then , inserts a comma as expected. It seems to only be an issue at the prompts first position.

gcentauri commented 4 years ago

thanks for all the work @duianto ! - i really should have put more of that in my original issue.

The main issue really comes down to the fact that xah-fly-keys is implemented as a minor-mode. There is no proper way to give precedence to minor mode key bindings. It seems that Evil gets around it by using emulation-mode or something like that. I'm torn whether or not I should just do work-arounds with the xah-fly-keys layer or try to improve the package itself. I'll keep this issue open and report back with what I end up doing to make the modes cooperate better.

gcentauri commented 4 years ago

I discovered that Spacemacs is using bind-map to declare the spacemacs leader key in an override map:

(defun spacemacs-bootstrap/init-bind-map ()
  (require 'bind-map)
  (bind-map spacemacs-default-map
    :prefix-cmd spacemacs-cmds
    :keys (dotspacemacs-emacs-leader-key)
    :evil-keys (dotspacemacs-leader-key)
    :override-minor-modes t
    :override-mode-name spacemacs-leader-override-mode))

It seems like perhaps bind-map is responsible for clearing the override of the SPC key in insert mode and activating it in other modes. I think it is working because Evil defines separate minor modes and bind-map will only do the override if one of the evil modes is specified here:

(defcustom bind-map-default-evil-states '(normal motion visual)
  "Default states for evil bindings."
  :group 'bind-map
  :type  '(repeat symbol))

Unfortunately, as is, I can't figure out a way to use bind map to at least give the xah-fly-keys leader precedence over the other minor mode keymaps.

gcentauri commented 4 years ago

I came up with a pretty hacky way to make it work without rebinding everything in this commit to the layer: https://github.com/gcentauri/xah-fly-keys-layer/commit/b288808663efb7cf04ce068cd9567c60de849525

I defined a new minor mode xah-fly-keys-command-mode in the layer which just serves as an indicator that it is active so that bind-map can give leader-key precedence to SPC only when that minor mode is active. Without that, SPC acts as a leader key even when in xah-fly-keys insert mode. The package only defines one mode and one top level keymap and the bind-key behavior as used in Spacemacs/Evil depends on the different minor modes being active.

I'd appreciate any insight on how to improve this and respect package ownership in layers. I'm using bind-key directly because it is required in the spacemacs boostrap process. The funny thing is, if I don't run the bind-map command until after slime is loaded, it won't work. I can't quite figure out why. The way I have it now seems to do the trick, but feels "smelly". I can now fire up Spacemacs, open a lisp file, and SPC will act as a leader key in command mode, and turn off in insert mode. It is still slightly funny after opening a Slime REPL, as it will initially insert spaces, but after toggling into command mode again, it works fine. Seems like something about Slime is messing with the minor mode lists?

gcentauri commented 4 years ago

i found a better way. i think the issue is just that minor modes that are higher up in the order of the minor-mode-map-alist get precedence over shared bindings. since xah-fly-keys loads early in my configuration, the slime modes get pushed onto the list later and take precedence. i added these lines to re-push the xah-fly-key-map onto it after slime loads. seems slightly hacky but cleaner than before. it also takes care of the , binding conflict and allows the slime keys to work as expected in insert mode

 (with-eval-after-load 'slime
    (push `(xah-fly-keys . ,xah-fly-key-map) minor-mode-map-alist))
lkyhfx commented 4 years ago
 (with-eval-after-load 'slime
    (push `(xah-fly-keys . ,xah-fly-key-map) minor-mode-map-alist))

This doesn't work for me after upgrade to emacs 27 and the newest version of slime and xah-fly-keys (2020.2.27) For anyone encounters this problem, add this to your init file (after slime and xah-fly-keys):

 (with-eval-after-load 'slime-repl
    (push `(xah-fly-keys . ,xah-fly-key-map) minor-mode-map-alist))