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?. 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. 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 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