/popup-keys

Interactive keymaps with context and arguments

Primary LanguageEmacs LispGNU General Public License v3.0GPL-3.0

popup-keys — Interactive keymaps with context and arguments

Table of Contents

Introduction

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 sequence C-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 and C-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.

./img/kmacro.jpg

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.

./img/registers.jpg

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.

./img/projectile.jpg

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.

./img/org-speed.jpg

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.

./img/dired-mark.jpg

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) and S-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 or q: 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) and s-r (isearch-backward): alternate keybindings for isearch; useful for popups which use the usual bindings C-s and C-r for something else.
  • M-s-s (isearch-forward-regexp) and M-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.

./img/findtools.jpg

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

  1. 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 to popup-keys:run-registers, then the key sequence C-x r i still runs insert-register, after displaying the first few registers as context and reminding you what commands are available.
  2. 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.
  3. You can run isearch in a popup window (with the usual keybinding C-s, unless that binding is defined by the popup, in which case s-s will work). This makes finding rarely-used commands even easier.
  4. 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.
  5. When a popup window is open, pressing ? <key> displays a (configurable) help buffer for the command bound to <key>. By default this runs describe-command. The key sequence ? ? displays a (configurable) help buffer relevant to that popup. For example, typing ? ? in the popup-keys:run-kmacro popup opens the info node “(emacs) Keyboard Macros”.
  6. 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.
  7. It is easy to define popup commands using popup-keys:new.

Limitations

  1. 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 with popup-keys:current-args passed as keyword arguments. As a third option, a pre-action hook can use the value of popup-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.
  2. 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.