Let there be composable editing!
composable.el brings composable text editing to Emacs. It improves the basic editing power of Emacs by making commands combineable.
It's inspired by vim but implemented in a way that reuses existing Emacs concepts. This makes it simple and compatible with existing Emacs functionality and infrastructure. composable.el brings together existing features in a more powerful way.
Note: composable.el is in early stages. It has a healthy amount of features already but I am very open to ideas for additions. That as well as feedback on the documentation and implementation is greatly appreciated. I am aware that some people in the Emacs community are skeptical towards composable editing. There is more to say about the topic than this readme does. I'll probably write about that at some point.
Here are a few examples of usage. Refer to the tables with key bindings below to see the entire set of default commands.
- C-w l: Kill current line.
- M-w 3 f: Save 3 words to the kill ring.
- M-; s: Comment structured expression.
- C-M-\ h h: Reindent the current paragraph and the next. The last h repeats the action and object.
- C-w C-w: Kill the current line by. Lines are selected by calling the same composable action twice in a row.
composable.el ships with a default set of keybindings. These are
activated by composable-mode
. Using composable-mode
is optional, it
contains nothing but bindings. The mode overwrites a bunch of default
Emacs bindings with composable variants. For instance C-w
is bound to composable-kill-region
. Invocations must be proceeded by
an object. For instance C-w C-e kill to end of line.
Composable editing is a simple abstraction that makes it possible to
combine actions with objects. The key insight in composable.el is
that Emacs already provides all the primitives to implement composable
editing. An action is an Emacs command that operates on the region.
Thus kill-region
and comment-region
are actions. An object is
specified by a command that moves point and optionally sets the mark
as well. Examples are move-end-of-line
and mark-paragraph
.
Note for people familiar with vim: What composable.el calls an action is called an operator in vim and the term object covers both motion and text object in vim.
So actions and objects are just names for things already present in Emacs. The primary feature that composable.el introduces is a composable command. A composable command has an associated action. Invoking it works like this:
- If the region is active the associated action is invoked directly.
- Otherwise nothing happens, but the editor is now listening for an
object. This activates a set of bindings that makes it convenient
to input objects. For instance pressing
l
makes the action operate on the current line. - After the object has been entered the action is invoked on the specified object.
One of the primary benefits of composable editing is that actions and objects are orthogonal. When you learn a new motion you can apply all existing actions to it and vice versa. Thus if you use 4 actions and 8 objects you only need to remember 4+8=12 bindings instead of 4*8=32 bindings. As an additional benefit only actions need to be always accessible, object can be bound only on the object layer. Thus you can get away with only 4 regular bindings instead of 32.
- Based on normal Emacs functionality and concepts.
- Object input bindings layer that makes selecting object easy and fast.
- Easily repeat composable actions by repeating the final character that selected the object.
- Integration with the default mark command with composable mark mode.
- Reuse bindings for several purposes with prefix arguments
Install through MELPA with M-x package-install composable
.
Alternatively, download composable.el
and place it in your load
path.
(require 'composable)
(composable-mode) ; Activates the default keybindings
(composable-mark-mode) ; Use composable with C-SPC
composable.el ships with a default set of keybindings. These are
activated by composable-mode
. Using composable-mode
is optional, it
contains nothing but bindings. The mode overwrites a bunch of default
Emacs bindings with composable variants. For instance C-w
is bound to composable-kill-region
. Invocations must be proceeded by
an object. For instance C-w C-e kill to end of line.
Here are a few examples of usage. Refer to the tables with key bindings below to see the entire set of default commands.
- C-w l: Kill current line.
- M-w 3 f: Save 3 words to the kill ring.
- M-; s: Comment structured expression.
- C-M-\ h h: Reindent the current paragraph and the next. The last h repeats the action and object.
- C-w C-w: Kill the current line. Lines can be selected by calling the same composable action twice in a row.
- C-SPC m: Mark back to indentation.
The default bindings overwrite the "non-composable" default bindings
in Emacs. For instance C-w
is bound to composable-kill-region
instead of kill-region
.
Binding | Command |
---|---|
C-w | composable-kill-region |
M-w | composable-kill-ring-save |
M-; | composable-comment-or-uncomment-region |
C-M-\ | composable-indent-region |
C-x C-u | composable-upcase-region |
C-x C-l | composable-downcase-region |
A composable command has to be followed by an object (which is any command that moves point). It makes no sense to type a character after invoking a composable command. Therefore a special layer of bindings is activated after invoking a composable command. This makes it easy to select objects without using modifiers.
Note that all normal bindings, except for the ones overwritten, are still usable. You can for instance kill a word forward with both C-w f and C-w M-f.
Besides the bindings mentioned below 0-9 are bound to
digit-argument
, i.e. they work as numeric prefix arguments.
Binding | Command |
---|---|
. | composable-end-argument (discussed below) |
, | composable-begin-argument (discussed below) |
a | move-beginning-of-line |
f | forward-word |
b | backward-word |
u | mark-whole-buffer |
n | next-line |
p | previous-line |
l | composable-mark-line |
{ | backward-paragraph |
} | forward-paragraph |
s | mark-sexp |
w | mark-word |
y | composable-mark-symbol |
h | mark-paragraph |
m | back-to-indentation |
j | composable-mark-join |
o | composable-mark-up-list |
g | Leave composable-object-mode |
C-g | Leave composable-object-mode |
Suppose you have a function rename-variable-region
that replaces all occurrences of a variable
name in the region by another name; you can make it composable by using the function composable-def
. The
function must be passed a list of actions (commands that operate on the region):
(composable-def '(rename-variable-region))
The above example will define the composable command
composable-rename-variable-region
.
Repeating is a feature that allows you to repeat the last action object combination by pressing the last key in the sequence repeatedly.
For instance C-w l l l has the same effect as C-w lC-w lC-w l. Repetition can also be combined with numeric prefixes. C-w 10 l l l kills 12 lines.
The feature can be disabled by setting composable-repeat
to nil
.
Composable mark mode activates the
object bindings when the mark is
activated with C-SPC(set-mark-command
).
(composable-mark-mode 1)
This makes it possible to mark things easily using the object bindings. A few examples:
- C-SPC l: Mark line
- C-SPC y: Mark symbol
The layer is only active immediately after the mark has been set. This
insures that composable-mark-mode
does not interfere with
delete-selection-mode
. One can for instance perform C-SPC l
h. The l will select a line but after that the object
bindings will be turned of and the subsequent h will be a
self-insertion.
When a composable command is called twice in a row, then the action is
executed on the region specified by composable-twice-mark
. By
default this is composable-mark-line
. Thus by default C-w
C-w kills one line. This works with repetition as well. For
instance C-w C-w C-w kills two lines.
composable.el defines two prefix arguments composable-begin-argument
and composable-end-argument
. These modify how the chosen object is
used.
The idea is that if you can mark a thing then you know both where the thing start and ends. Thus you can not only perform an action on the entire thing, but also from point to the begining or end of the thing.
Similarly if you have have a pair of commands that move to the beginning and end of a thing you can use the two in unison to mark the entire thing.
This makes it possible to use bindings in multiple ways. For instance if you often perform actions on an entire paragraph but rarely beform actions from point to the end of a paragraph.
Given a prefix argument before selecting a region command only the end or the beginning of the region will be used. I.e. instead of applying the action to the entire marked region only the region between point and the begining or end of region will be used.
For instance C-w . l deletes to the end of the line—including the line break. This is because l marks the entire line but due to . only the end of the marked region is used.
Similarly C-w h will kill one paragraph from beginning to end. But C-w , h will kill one paragraph backwards and C-w . h will kill one paragraph forward.
The variable composable-fn-pair-alist
define movement commands
to be each others pair. For instance the following pair is defined by
default.
(add-to-list 'composable-fn-pair-alist '(forward-word . backward-word))
When a prefix argument is specified before a paired movement command (begin and end are treated the same) the two commands are used to establish a region. For instance both M-w , f and M-w . f will save the current word to the kill ring.
The default defined pairs are:
- forward-word && backward-word: To select the current work
- move-end-of-line && back-to-indentation: To select the line (without indentation)
- next-line && previous-line
- forward-paragraph && backward-paragraph
- forward-sentence && backward-sentence
A variable composable-mode-line-color
is defined to change the
mode-line background color when the composable mode is active. The
default value for this variable is "cyan" but it can be set to any
valid color.
If you don't want a color change in the mode-line, then just set this
variable to nil
.
Composable integrates out of the box with the package which-key if it is installed. When which-key-mode is enabled composable commands show a menu listing the possible commands.
To disable this feature just add:
(setq composable-which-keys nil)
to your config.
Composable has a way to control the messages printed in the minibuffer and/or the *Messages* using the variable `composable-mode-debug-level' which has the following valid values:
- 0: No debug info is printed at all.
- 1: The debug info is printed only in the *Messages* buffer but not in the minibuffer.
- 2: The debug info is printed in *Messages* and in the minibuffer.
Composable implements a special version for copy-region-as-kill
called composable-save-region
.
By default composable always highlights the copied region
independently if the region was active before calling the kill command
or not. This is different to copy-region-as-kill
which disables the
region after the copy. But in composable it makes some sense. Any way,
if the user wants the default behavior he can set:
(setq composable-copy-active-region-highlight nil)