3

I'm trying to make Workgroups2 more convenient by displaying a list of workgroups in the mini-buffer on pressing the prefix key (C-c z), e.g.:

List of workgroups

This makes it convenient to use the index 0-9 to switch workgroups:

(defvar wg-prefixed-map
  (wg-fill-keymap
   (make-sparse-keymap)

   ;; workgroup switching
   (kbd "C-j")        'wg-switch-to-workgroup-at-index
   (kbd "j")          'wg-switch-to-workgroup-at-index
   (kbd "0")          'wg-switch-to-workgroup-at-index-0
   (kbd "1")          'wg-switch-to-workgroup-at-index-1
   (kbd "2")          'wg-switch-to-workgroup-at-index-2
   (kbd "3")          'wg-switch-to-workgroup-at-index-3
   (kbd "4")          'wg-switch-to-workgroup-at-index-4
   (kbd "5")          'wg-switch-to-workgroup-at-index-5
   (kbd "6")          'wg-switch-to-workgroup-at-index-6
   (kbd "7")          'wg-switch-to-workgroup-at-index-7
   (kbd "8")          'wg-switch-to-workgroup-at-index-8
   (kbd "9")          'wg-switch-to-workgroup-at-index-9
 )
"The keymap that sits on `wg-prefix-key'.")

(defun wg-fill-keymap (keymap &rest binds)
  "Return KEYMAP after defining in it all keybindings in BINDS."
  (while binds
    (define-key keymap (car binds) (cadr binds))
    (setq binds (cddr binds)))
  keymap)

The list can be generated with:

(wg-fontified-message
   (wg-workgroup-list-display))

Question:
Given C-c z as the prefix key for Workgroups2, is it possible to bind it to

(wg-fontified-message
   (wg-workgroup-list-display))

so that it displays the list of workgroups in the mini-buffer? C-c z should still be the prefix after the binding.

Note:
This question is similar to Can the prefix of a key-sequence have an effect?. The comments suggest set-transient-map or keymapp. If either of these is useful, please kindly show how the above code can be modified to implement the function. Thank you.

Yang
  • 165
  • 5
  • The question is unclear to me. If you use `(define-prefix-command 'wg-prefixed-map)` and then you do `(define-key MAP (kbd "C-c z") 'wg-prefixed-map)` for each minibuffer keymap `MAP` that you are interested in, then you get your prefix-key behavior in the minibuffer. How that relates to another use of `C-c z` as the prefix key for "Workgroups2", or how or why you want to bind that same key to `(wg-fontified-message (wg-workgroup-list-display))`, which would have to return a command or keymap, is unclear. – Drew Mar 29 '16 at 20:34
  • @Drew `C-c z` is the default prefix key for Workgroups2. When I `C-c z`, I use `j`, `0`, `1`, etc, to execute a command. Now I want `C-c z` to show a list of workgroups in the mini-buffer using `wg-fontified-message` while waiting to accept the next key. – Yang Mar 29 '16 at 20:43
  • Still unclear to me, but it sounds like you want to advise each of the commands bound to `j`, `0`, etc., to have them first display what you want to display. – Drew Mar 29 '16 at 21:00
  • @Drew Before I press `0`, `1`, etc, and after I press the prefix key `C-c z`, I would like a list of workgroups (e.g., 0: Default 1: Test 1 2:Test 2 as shown in the image) displayed in the mini-buffer. – Yang Mar 29 '16 at 21:18
  • Then bind `C-c z` to a single command that reads a key (e.g. `0` etc.) and dispatches to a function that does what `0` etc. does today. And reread the question you cited: [Can the prefix of a key-sequence have an effect?](http://emacs.stackexchange.com/questions/16696/can-the-prefix-of-a-key-sequence-have-an-effect). A key sequence is bound to a command or to a keymap, or it is unbound. A command can read keys. A keymap performs no actions. – Drew Mar 29 '16 at 21:40
  • @Drew Thank you for your suggestion. The `wg-prefixed-map` is defined by the Workgroups2 library. In addition to `0`-`9`, there are many other commands (https://github.com/pashinin/workgroups2/blob/master/src/workgroups2.el#L1725). I don't think it's the best way to restructure the library. I have read that a key sequence is bound to a command or to a keymap, or it is unbound, and a keymap performs no actions. That's why I'm asking how I should correctly implement the desired behavior. – Yang Mar 29 '16 at 21:50
  • This question is an exact duplicate of the [other one](http://emacs.stackexchange.com/q/16696/105). Unfortunately, there are no upvoted answers there, so this cannot be deleted as a duplicate. @Gilles's answer here repeats comments there. One of the two should eventually be closed as a duplicate, IMHO. The answer needs to explain that a key can be bound to a command or a keymap. If bound to a command, that command can read key(s) and dispatch to different behaviors, either by explicitly reading or by using `set-transient-map`. There are not 36 ways to look at this. – Drew Mar 30 '16 at 01:43
  • Now that this question has an upvoted answer, I will vote to close the other one. – Drew Mar 30 '16 at 01:43
  • Actually, even that other question this OP cited was a duplicate. I've voted to close this one and that one. [This](http://emacs.stackexchange.com/q/6037/105) was the first Q&A about this question. – Drew Mar 30 '16 at 01:46

1 Answers1

2

One way to do it is, instead of binding C-c z to a prefix key, bind it to a command that displays what you want and then calls set-transient-map to read a key sequence and interpret it in another map. Any key that isn't bound in the other map will be interpreted in the global map, so rather than wg-prefixed-map, you should provide a keymap where unknown bindings are made undefined.

(defun wg-prefix-show-information ()
  "Display information and read a key from \\[wg-filled-map].

\\{wg-filled-map}"
  (interactive)
  (message …)
  (set-transient-map `(keymap (t . undefined) ,@wg-filled-map)))

This isn't ideal because it doesn't integrate well with help. For example C-h C-c z will say that wg-prefix-show-information is that function above, hence the prominent mention of wg-filled-map in the function's docstring. Also C-c z C-h will not propose a list of bindings (I think this can be fixed by setting help-form but I haven't investigated further). If the user types a multi-key sequence slowly, they'll be shown the keys they typed not including the initial C-c z which might be confusing.

If all you need to do is display something in the echo area, an alternative approach would be to leverage the prompt string of a keymap. This can only be a constant string, but if it only needs to be regenerated due to a few events as opposed to recomputed on the fly, you can make those events change the keymap object:

(defvar wg-filled-map
    (let ((map (make-sparse-key-map "nothing going on")))
      …))
(defun wg-something-changed-hook ()
  (setcdr wg-filled-map (format "the system state is …")))

If you want some fancier processing, an alternative approach is to switch to the minibuffer or to a new, transient buffer where wg-prefix-map is the local map. This is what isearch does, for example (with a much more complex use case as one typically enters many keys, not just one). Another package that uses this kind of approach is magit.

  • Thanks, Gilles. I have taken your first approach. I think even using `wg-prefixed-map` works for me. If a key isn't bound, it says `x is undefined`, so that's fine. – Yang Mar 30 '16 at 01:45
  • I recommend against the use of `(t . undefined)` in your transient map. As a matter of fact, `set-transient-map` was introduced in large part to try and reduce the use of such "catch all" bindings because they have undesirable side-effects (the most obvious one is that they prevent `function-key-map` and `input-decode-map` from doing their job). – Stefan Apr 12 '16 at 13:13