popup-keys — Interactive keymaps with context and arguments
Table of Contents
Introduction
This package is not maintained for public distribution. The magit-key-mode has become a standalone package called magit-popup, which is more or less equivalent to this package That said, it works very well for me, so if popup-keys seems useful to you, feel free to use it.
popup-keys is a user interface package based on the extremely useful magit-key-mode in the Magit package. It is an interactive keymap supporting context and arguments.
- Interactive keymap: Two of the things that make Emacs so powerful are the
enormous number of commands available, and the flexibility with which those
commands are bound to keys. In order to take advantage of this power, one has
to be able to remember which commands are available, and to which keys they
are bound. Emacs has reasonably good facilities for documenting its available
keymaps, but it is somewhat inconvenient to interrupt what you are doing to
open a help buffer or run
describe-keymap
to find a binding for a command whose name you can’t remember. Moreover, once you are familiar with a set of commonly-used commands in a particular keymap you may forget about the other available commands entirely.popup-keys can be used as an interactive keymap. A prefix key, for example
C-x v
, can be bound to a popup command, which opens a small window listing available keys in that keymap with a brief description, for example “d: vc directory”. The key sequenceC-x v d
works exactly as before, with the popup reminding you of all of the VC commands you’ve forgotten about since you read the info page. popup-keys is much more flexible than a prefix key, however. See the use cases described below. - Context: Certain keymaps or collections of commands operate on a common set of
data or are affected by a common set of global parameters. For example, the
registers keymap
C-x r
operates on the values of the currently stored registers. In order to use those keys, one has to remember what those registers currently store. popup-keys buffers can be configured to display arbitrary information, for example the values of the first few registers. See the use cases described below.
- Arguments: Emacs supports passing simple parameters to commands via prefix
arguments. For instance,
C-u 10 C-f
moves the point ten characters forward. This system does not work well when a command has a large number of string arguments or boolean switches; in these cases the command generally resorts to prompting the user several times for minibuffer input. popup-keys allows the user to set any number of preconfigured arguments and switches in its buffer before executing any command. This facility is the raison d’être for magit-key-mode, which enables the user to choose the command-line arguments used to run git. See the use cases described below for an example of using arguments with commands from the Projectile keymap.
Use cases
popup-keys is best illustrated by example, so here are several. These and many
other examples can be found ready to use in the included library
popup-keys-examples.el
. For usage see usage.
Keyboard macros
Emacs has a powerful keyboard macro creation and editing library called kmacro
which is accessed by default via the prefix key sequence C-x C-k
. This is one
case where Emacs’s facilities for viewing documentation on keymaps and commands
is very inconvenient to use: you can’t run describe-keymap
on kmacro-keymap
while defining a keyboard macro! By reassigning C-x C-k
to a popup-keys popup
you can be reminded while you’re defining a macro that C-u 10 C-x C-k C-a
adds
10 to the current macro counter, without any change to the key sequence you
enter. Actually, that’s not quite true: you can specify the prefix argument
after you open the popup; the key sequence is then mangled so that the correct
keys are recorded by kmacro. The included popup-keys:run-kmacro
command has the
following additional features:
- Most actions are executed without closing the popup window. For instance, to
execute the second keyboard macro in the macro ring repeatedly, open the
popup and just keep pressing
C-l
. This is an elegant way of implementing the repeat-key functionality of the standard kmacro key bindings, which of course can be used in many other contexts. See also smartrep. - It displays the top two macros in the key macro ring as context. These can
be cycled with the default bindings
C-n
andC-p
without closing the popup window. - It dynamically adds new actions to the popup when you bind a keyboard macro to a number or a capital letter. Running the action keeps the buffer open, as above. The help text for that action is the expanded macro definition.
Registers
The registers keymap ctl-x-r-map
, by default bound to C-x r
, is a large
multipurpose keymap used for manipulating registers, rectangles, and bookmarks.
In order to use it effectively, one has to remember what is stored in which
registers, in addition to knowing the commands for accessing them. The included
popup-keys:run-registers
command displays (a summary of) the contents of the
first few registers as context, much like helm-register
from the Helm
package. When executing a command like jump-to-register
, it keeps the popup
window open while the interactive arguments are being read (i.e. while the
command prompts for a register key), then closes the window to actually execute
the command. This decoupling of the interactive part of the command from the
functional part is useful because you can still see the context while you’re
being prompted for it.
Projectile
The Projectile package is a way to manipulate files and buffers from the same
project as a set. It comes with a large number of commands which are by default
installed under the C-c p
prefix key. By default all of these commands operate
on the package containing default-directory
(usually the directory containing
the buffer’s file). The included popup-keys:run-projectile
command uses an
argument to allow you to change the active Projectile project root before
running any of these commands, in addition to reminding you that, for instance,
C-c p 4 C-o
displays a project buffer in the other window.
Org speed commands
org-mode has a feature called speed commands which allows you to quickly navigate
around and perform operations on the document structure with single keystrokes
when the point is located at the beginning of a header line. The included
popup-keys:run-org-speed
command reimplements this feature in a popup. Since
most actions do not close the popup window, after executing
popup-keys:run-org-speed
(the suggested keybinding is M-S-s
) each command only
requires a single keystroke, so this recovers the “speed” part of the feature
without needing to remap self-insert-command
. In addition, the available
actions can be run from anywhere, not just at the beginning of a header line.
And of course the popup window reminds you which commands are available.
Special modes
In Emacs, a special mode is a mode where the user does not directly insert text;
instead most single keys are bound to commands. Examples are dired-mode
and
ibuffer-mode
. When the mode defines a very large number of commands, like
dired
, it is very convenient to bind ?
to a popup, like the included
popup-keys:run-dired
command. This popup essentially just displays the base
keymap, as opposed to a keymap bound to a prefix key as in the other use cases.
Most commands do not close the popup window, so you can (if you want) operate as
normal on the dired buffer with a keymap reference on the screen. Perhaps more
useful is popup-keys:run-dired-mark
, which allows you to mark and unmark files
with a command reference and a mark count always visible, without having to type
the *
prefix every time.
Usage
Installation
To install popup-keys just put popup-keys.el
in a place where Emacs can find it
and require it using (require 'popup-keys)
somewhere in your .emacs
file. The
library alone doesn’t do anything; you still have to define popup commands using
popup-keys:new
(see below).
There are many ready-to-use example popups included in popup-keys-examples.el
.
To use them, put (require 'popup-keys-examples)
somewhere in your .emacs
file
and define keybindings for the commands you want to use. There is Lisp code for
defining suggested keybindings in the comments in popup-keys-examples.el
.
You may not have to explicitly require popup-keys
or popup-keys-examples
if your
package manager extracted the autoloads correctly.
TODO: El-Get and MELPA recipes
Interface
The popup-keys user interface is simple. When the popup window is open, typing
the key (or key sequence) shown before an action, argument, or switch executes
that item. Alternatively, you can execute an item by placing the cursor on that
item and pressing RET
. The usual cursor navigation keys (C-f
, C-p
, etc.) work
as expected, unless they are bound to actions by the popup. In any case TAB
and
S-TAB
(backtab) place the cursor at the next (respectively, previous) item in
the popup.
By default, executing an action closes the popup window and resets the window
configuration to its state before the popup was opened. This can be configured
(by the popup definition) with the :keepbuf
argument and the
popup-keys:keep-buffer
variable.
You can enter prefix arguments either before invoking the popup, or while the
popup is open before executing an action. In other words, if popup-keys:run-vc
is bound to C-x v
, then the key sequences C-u C-x v D
and C-x v C-u D
have the
same effect.
The following “base” keybindings are available in any popup and cannot be
overridden by popup commands (the modifier key S-
is “shift” and s-
is “super”):
RET
(popup-keys:exec-at-point
): execute the action, change the argument, or toggle the switch at point.TAB
(popup-keys:jump-to-next-exec
) andS-TAB
(popup-keys:jump-to-prev-exec
): move point to the next or previous item.?
(popup-keys:dispatch-help
): prompt for a key and run the configurable help action associated to that key. The key sequence? ?
runs a general help action that applies to the whole popup, like displaying an info page.C-g
orq
: quit the popup and reset the window configuration to its state before the popup was opened.s-l
(popup-keys:reset-windows
): reset the window configuration to its state just after the popup was opened. This is useful for closing help windows.s-s
(isearch-forward
) ands-r
(isearch-backward
): alternate keybindings for isearch; useful for popups which use the usual bindingsC-s
andC-r
for something else.M-s-s
(isearch-forward-regexp
) andM-s-r
(isearch-backward-regexp
): likewise for regexp isearches.
There are a number of customization options available in the popup-keys
group.
To customize this package use M-x customize-group RET popup-keys RET
.
Defining new popups
Use the popup-keys:new
function to define new popup commands. The first
parameter is the command name and the rest are keyword arguments. The following
simple popup definition can be found in popup-keys-examples.el
:
(popup-keys:new
'popup-keys:run-findtool
:buf-name "*find tools*"
:actions '(("d" "find-name-dired" find-name-dired)
("D" "find-dired" find-dired)
("h" "helm-find (C-u: prompt)" helm-find)
("l" "helm-locate" helm-locate)
("a" "ack-find-file" ack-find-file)
("A" "ack-find-file-same" ack-find-file-same)
("F" "helm-for-files" helm-for-files)
("f" "helm-find-files" helm-find-files)))
This makes a popup command popup-keys:run-findtools
which collects eight actions
used for finding files on the filesystem from within Emacs.
See the documentation string for popup-keys:new
for a full list of keyword
arguments and their meanings. Also see the comments at the beginning of
popup-keys.el
. The best place to start is probably by looking at the examples
in popup-keys-examples.el
.
Advantages
- When using a popup to replace a keymap assigned to a prefix key, the key
sequences you already know will continue to work (assuming you assign the
keys in the popup to the same commands as the original keymap). In other
words, if you assign
C-x r
topopup-keys:run-registers
, then the key sequenceC-x r i
still runsinsert-register
, after displaying the first few registers as context and reminding you what commands are available. - Frequently seeing the full list of commands available in a keymap is a great way to learn about new commands and remember commands you’ve forgotten.
- You can run
isearch
in a popup window (with the usual keybindingC-s
, unless that binding is defined by the popup, in which cases-s
will work). This makes finding rarely-used commands even easier. - Popup actions can be configured to keep the popup window open after executing. This allows you to execute several commands from the same keymap with single keystrokes. Alternatively, the popup window can be kept open while the command reads its interactive arguments (so you can refer to any displayed context), and then closed before the command executes. See the use cases.
- When a popup window is open, pressing
? <key>
displays a (configurable) help buffer for the command bound to<key>
. By default this runsdescribe-command
. The key sequence? ?
displays a (configurable) help buffer relevant to that popup. For example, typing? ?
in thepopup-keys:run-kmacro
popup opens the info node “(emacs) Keyboard Macros”. - Prefix arguments for commands contained in a popup can be entered before
opening the popup or after the popup is opened, before entering command key.
This even works while defining keyboard macros when using the
popup-keys:run-kmacro
popup. See usage. - It is easy to define popup commands using
popup-keys:new
.
Limitations
- The method for passing arguments from the popup to commands is a bit
unnatural. Interactive commands do not take arguments, so the current
argument values are stored in the property list
popup-keys:current-args
before the command is executed; the command itself must parse the arguments. Alternatively, with the:pass-kwargs
action option, the command will be run noninteractively withpopup-keys:current-args
passed as keyword arguments. As a third option, a pre-action hook can use the value ofpopup-keys:current-args
to setup the environment in which the command will be run, e.g. by let-binding certain variables. None of these options is elegant. - It is not currently possible to run a popup in “invisible” mode. For
example, one might want to use
popup-keys:run-org-speed
(see use cases) as a prefix keymap that doesn’t require you to retype the prefix key each time, without actually opening the popup window. Such a feature is not planned; if this is what you want, see smartrep.