• Stars
    star
    115
  • Rank 295,579 (Top 6 %)
  • Language
    Emacs Lisp
  • License
    GNU General Publi...
  • Created about 8 years ago
  • Updated almost 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Composable text editing for Emacs.

composable.el

Build Status Coverage Status MELPA MELPA Stable

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.

Introduction

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:

  1. If the region is active the associated action is invoked directly.
  2. 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.
  3. After the object has been entered the action is invoked on the specified object.

Benefits

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.

Features

Installation

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

Basic usage

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.

Documentation

The default bindings

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

The default object bindings

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

Create custom composable command

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

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

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.

Successively calling a composable command

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.

Prefix arguments

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.

With region commands

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.

With pair movements

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:

  1. forward-word && backward-word: To select the current work
  2. move-end-of-line && back-to-indentation: To select the line (without indentation)
  3. next-line && previous-line
  4. forward-paragraph && backward-paragraph
  5. forward-sentence && backward-sentence

Mode-line Color indicator

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.

Which-key integration

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.

Debug messages

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.

Kill specific options

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)

More Repositories

1

flyd

The minimalistic but powerful, modular, functional reactive programming library in JavaScript.
JavaScript
1,566
star
2

functional-frontend-architecture

A functional frontend framework.
JavaScript
1,444
star
3

union-type

A small JavaScript library for defining and using union types.
JavaScript
475
star
4

synceddb

Makes it easy to write offline-first applications with realtime syncing and server side persistence.
JavaScript
397
star
5

dffptch

A micro library for diffing and patching JSON objects using a compact diff format
JavaScript
170
star
6

functionize

A library which aids in making any JavaScript library more functional.
JavaScript
49
star
7

projectdo

Context-aware single-letter project commands to speed up your terminal workflow.
Shell
47
star
8

Kran

An entity system written in JavaScript.
JavaScript
42
star
9

Gtk98Icons

An icon theme for GTK that looks like Windows 98
PHP
40
star
10

smart-comment

Smarter commenting for Emacs
Emacs Lisp
40
star
11

sync-promise

Compact synchronized promise implementation. Promises/A+ incompliant. Works inside IdexedDB transactions.
JavaScript
32
star
12

dot-compose

Function composition with dot as a composition operator.
JavaScript
23
star
13

seamless-fantasy

Make fantasy land seamlessly compatible with plain JavaScript data structures.
JavaScript
10
star
14

list-difference

Fast algorithm for finding edits between lists.
JavaScript
10
star
15

duck

๐Ÿฆ† Turns a TypeScript file into JSON describing the files exports.
TypeScript
8
star
16

find-the-function

A tiny tool for finding functions from libraries
TypeScript
7
star
17

ryter

A tiny JavaScript router
JavaScript
7
star
18

web-swipe-view

Horizontal swipe views for mobile web applications
JavaScript
7
star
19

finger-tree

Highly optimized implementation of finger trees in JavaScript
TypeScript
6
star
20

flyview

Efficient views powered by streams/ovservables/functional reactive properties.
JavaScript
6
star
21

fake-raf

A fake requestAnimationFrame perfect for unit testing.
JavaScript
5
star
22

flyd-forwardto

Create a new stream that passes all values through a function and forwards them to a target stream.
JavaScript
4
star
23

maxima-calculus2

Maxima funktioner til lรธsning af eksamensopgaver i kurset Calculus 2 pรฅ Aarhus Universitet
4
star
24

turing-patterns

Multi-Scale Turing Patterns
JavaScript
4
star
25

reflex-examples

A collection of examples using Reflex.
Haskell
3
star
26

flyd-scanmerge

Flyd module for conveniently merging and reducing several streams into one.
JavaScript
3
star
27

flyd-obj

Functions for working with stream in objects.
JavaScript
3
star
28

category-theory-notes

TeX
3
star
29

matmod

Handy functions for matMod written in R for Jupyter
Jupyter Notebook
3
star
30

dnd-scroll

Proper edge scroll when dragging with HTML 5 drag and drop!
JavaScript
3
star
31

dffptch-haskell

A small library for diffing and patching JSON objects using a compact diff format
Haskell
2
star
32

keyano-vscode

Next-generation keyboard-driven editing language. Edit code at the speed of light.
TypeScript
2
star
33

flyd-filter

Filter function for Flyd.
JavaScript
2
star
34

planetsimulator

A physical simulation of planetary motion written in JavaScript
JavaScript
2
star
35

flyd-every

Takes a time interval t and creates a stream of the current time updated every t.
JavaScript
1
star
36

paldepind.github.io

CSS
1
star
37

hareactive-old

Experimental WIP.
JavaScript
1
star
38

vdom-benchmark-snabbdom

Virtual DOM Benchmark implementation for Snabbdom library.
JavaScript
1
star
39

simple-frp

An attempt at creating a very simple FRP library for educational purposes.
TypeScript
1
star
40

flyd-sampleon

sampleOn for Flyd.
JavaScript
1
star
41

software-foundations

My solutions to exercises in Benjamin C. Pierce's Software Foundations
HTML
1
star
42

flyd-keepwhen

keepWhen function for Flyd.
JavaScript
1
star
43

flyd-lift

Lift function for Flyd.
JavaScript
1
star
44

dotfiles

Repository containing my dotfiles.
Emacs Lisp
1
star
45

react-native-chainable-stylesheet

TypeScript
1
star
46

flyd-aftersilence

Flyd module that buffers values from a stream and emits them after a specified duration of silience.
JavaScript
1
star
47

domain-theory

CSS
1
star