2

I am trying to set up something like Spacemacs, but fully under my control. I have already created a tree of my own keymaps with global commands, and it is available on M-SPC. But now I'd like to also make available local bindings for major modes, those that typically start with C-c, through the same prefix M-SPC, but without the C-c.

Clearly I cannot just get the part of the local map after C-c and graft it onto my M-SPC tree - it would have to be a function that looks up the binding on the fly. But how to read the key sequence? read-key-sequence only works on "active" maps according to the doc - indeed it has to because it has no keymap argument. But the keymap I need to consult when reading the keys is not active, it is the sub-keymap of the local map after C-c. So I seem to be stuck.

5
  • Please add an example, including not only the things you do want, but also things you want to avoid in the process (such as binding keys you don't want to bind). Make the example sufficient to demonstrate all the requirements. As it stands, I think your question is difficult to understand.
    – phils
    Commented Apr 10, 2020 at 0:16
  • @phils : right now, I have a bunch of global bindings starting with M-SPC. Now, the local map in C mode, for example , has a binding of C-c C-n to c-forward-conditional. I would like to bind M-SPC C-n to c-forward-conditional in C mode only; and not only that, but also all the parallel bindings, such as M-SPC C-p to c-backward-conditional, in a systematic way [1]. And similarly for other major modes I use. Is that more clear?
    – q.undertow
    Commented Apr 10, 2020 at 4:16
  • in a systematic way: ie. without coding each one by hand. What I want to avoid is that those mode-specific bindings stick around when I leave that particular major mode.
    – q.undertow
    Commented Apr 10, 2020 at 4:23
  • So are you saying you want every local binding under the C-c prefix (and only that prefix) to also be available under the M-SPC prefix? (And without losing the global bindings you already have under M-SPC).
    – phils
    Commented Apr 10, 2020 at 5:28
  • That sounds about right.
    – q.undertow
    Commented Apr 10, 2020 at 5:29

3 Answers 3

3

Based on the clarifications to the question, something like this might do the trick?

(defvar my-leader-map (make-sparse-keymap)
  "Prefix binding for custom things.")

(global-set-key (kbd "M-SPC") my-leader-map)

;; My global bindings go here.  E.g.:
(define-key my-leader-map (kbd "C-b") 'other-window)
;; etc...

;; Default binding.
(define-key my-leader-map [t] 'my-leader-map-default)

(defun my-leader-map-default ()
  "Use the binding under the C-c prefix of `current-local-map'."
  (interactive)
  (let ((prefix (lookup-key (current-local-map) (kbd "C-c"))))
    (when (keymapp prefix)
      (let ((map (make-sparse-keymap)))
        (set-keymap-parent map prefix)
        (define-key map [t] 'undefined)
        (set-transient-map map)
        (push last-command-event unread-command-events)))))

Or perhaps this, which would make the bindings more discoverable:

(defvar my-leader-map (make-sparse-keymap)
  "Prefix binding for custom things.")

(global-set-key (kbd "M-SPC") my-leader-map)

;; My global bindings go here.  E.g.:
(define-key my-leader-map (kbd "C-b") 'other-window)
;; etc...

(add-hook 'after-change-major-mode-hook #'my-leader-map-set-local)

(defun my-leader-map-set-local ()
  "Set `current-local-map' to include C-c bindings under `my-leader-map'."
  (when (current-local-map)
    (let ((prefix (lookup-key (current-local-map) (kbd "C-c"))))
      (when (keymapp prefix)
        (use-local-map
         (let ((map (make-sparse-keymap)))
           (set-keymap-parent map (current-local-map))
           (define-key map (kbd "M-SPC")
                       (make-composed-keymap my-leader-map prefix))
           map))))))

You might cache and re-use the latter maps on a per-major-mode basis.


Edit: One more... this somewhere between the others, functionality-wise: C-hkM-SPC<foo> will work for local-map bindings, but M-SPCC-h will only show your custom bindings.

Caveat: I'm not 100% certain that (lookup-key prefix (vector last-input-event)) is valid for all situations.

(defvar my-leader-map (make-sparse-keymap)
  "Prefix binding for custom things.")

(global-set-key (kbd "M-SPC") my-leader-map)

;; My global bindings go here.  E.g.:
(define-key my-leader-map (kbd "C-b") 'other-window)
;; etc...

(define-key my-leader-map [t]
  `(menu-item "" nil :filter ,#'my-leader-map-default-filter))

(defun my-leader-map-default-filter (_cmd)
  "Use the binding under the C-c prefix of `current-local-map'."
  (let ((prefix (lookup-key (current-local-map) (kbd "C-c"))))
    (when (keymapp prefix)
      (lookup-key prefix (vector last-input-event)))))
1
(general-def
  "SPC c" (general-simulate-key "C-c"))

Since C-c and C-x bindings will have conflicts if merged (like C-c @ and C-x @), this is probably the most doable method.

  • There is a builtin function, describe-buffer-bindings, that tells you which keys are bound to what in the current buffer, and it accepts a prefix as an argument, but it is a C function that inserts something like this into the current buffer:
(describe-buffer-bindings (current-buffer) (kbd "C-c")) ; in an Elisp buffer

`magit-file-mode' Minor Mode Bindings Starting With C-c:
key             binding
---             -------

C-c ESC         Prefix Command

C-c M-g         magit-file-dispatch


`magit-file-mode' Minor Mode Bindings Starting With C-c:
key             binding
---             -------

C-c ESC         Prefix Command


`hs-minor-mode' Minor Mode Bindings Starting With C-c:
key             binding
---             -------

C-c @           Prefix Command

C-c @ C-a       hs-show-all
C-c @ C-c       hs-toggle-hiding
C-c @ C-d       hs-hide-block
C-c @ C-e       hs-toggle-hiding
C-c @ C-h       hs-hide-block
C-c @ C-l       hs-hide-level
C-c @ C-s       hs-show-block
C-c @ C-t       hs-hide-all
C-c @ ESC       Prefix Command

C-c @ C-M-h     hs-hide-all
C-c @ C-M-s     hs-show-all


`nameless-mode' Minor Mode Bindings Starting With C-c:
key             binding
---             -------

C-c C--         nameless-insert-name


Global Bindings Starting With C-c:
key             binding
---             -------

C-c c           org-capture

So there doesn't seem to be a proper way to get bindings that you can use to get a raw keymap.

3
  • 1
    Please consider providing a link to general.el.
    – Drew
    Commented Apr 9, 2020 at 16:09
  • The problem is not to get the bindings: that can be done with map-keymap, for example. The main problem is exactly as I stated in the title: how to make these local bindings, once discovered/computed, available under the global prefix.
    – q.undertow
    Commented Apr 9, 2020 at 19:35
  • The closest existing question: [emacs.stackexchange.com/questions/707/…
    – q.undertow
    Commented Apr 9, 2020 at 19:38
0

I am a fool. There was never a problem to start with. Since bindings from active keymaps (including the global and local) are merged, all that is necessary is to add the desired bindings, starting with M-SPC, to each mode keymap where I want it to happen. This is much simpler and also a bit safer than the proposal by @phils because it keeps the effect isolated to an explicitly chosen handful of mode keymaps.

Nonetheless I'm acking @phils answer because I think it is correct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.