“The power of emacs lisp at your fingertips”
emacs-buttons
is an emacs lisp mini-language to help organize
emacs-lisp commands and frequently-occurring code templates into a tree-like hierarchy
of keymaps, and to facilitate installing and visualizing these keymaps.
- Inheritance
- Supports defining keymaps for a given mode by building on top of existing keymaps.
- Auto-loading
- emacs-buttons handles auto-installing newly-defined bindings to well-known keymap symbols that may be unbound now and will be loaded in the future, avoiding the need to manually write custom hook functions, eval-after-load, and similar set up code.
- Code templates
- Provides a simple but flexible template macro for defining code templates (also known as snippets or keyboard macros).
- Visualization
- Each keymap provides a keybinding to help describe itself
- The following
defbuttons
form
(buttons-macrolet ;; make short aliases available within body
((nli () `(newline-and-indent)));; additional aliases
;; add the super modifier to all keys in any (but ...) form within this compile-time let-binding
(let-when-compile ((buttons-make-key-mapper #'buttons-modifier-add-super))
(defbuttons
emacs-lisp-buttons;;var name
nil;; starter keymap
(emacs-lisp-mode-map read-expression-map inferior-emacs-lisp-mode-map);; target keymaps
;; lastly the actual keymap
(but;; (but (KEY DEF)...) defines an anonymous keymap
;; (cmd ...) defines an anonymous command
("a" (cmd (ins "(lambda ({}) {})")))
;; ins inserts a code template, interpreting {} as directives
("z" (cmd (ins "(if {})")))
("x" (cmd (ins "(when {})")))
("c" (cmd (ins "(unless {})")))
("v" (cmd (ins "(progn {})")))
("d"
;; DEF may be a nested keymap
(but
("v" (cmd (ins "(defvar {}){(nli)}")))
("f" (cmd (ins "(defun {} ({}){(nli)}{})")))
("m" (cmd (ins "(defmacro {} ({}){(nli)}{})")))
("s" (cmd (ins "(defstruct {}{(nli)}{})")))
("b" (cmd (ins "(destructuring-bind ({}){})")))))))))
is roughly equivalent to
(defvar emacs-lisp-buttons nil "emacs-lisp-buttons buttons map")
(setq emacs-lisp-buttons
(let ((kmap434 (make-sparse-keymap)))
(define-key kmap434 (kbd "s-a") (lambda () (interactive) (insert "(lambda ({}) {})")))
(define-key kmap434 (kbd "s-z") (lambda () (interactive) (insert "(if {})")))
(define-key kmap434 (kbd "s-x") (lambda () (interactive) (insert "(when {})")))
(define-key kmap434 (kbd "s-c") (lambda () (interactive) (insert "(unless {})")))
(define-key kmap434 (kbd "s-v") (lambda () (interactive) (insert "(progn {})")))
(define-key kmap434 (kbd "s-d")
(let
((kmap435 (make-sparse-keymap)))
(define-key kmap435 (kbd "s-?") (lambda () (interactive) (buttons-display kmap435)))
(define-key kmap435 (kbd "s-v") (lambda () (interactive) (insert "(defvar {}){(nli)}")))
(define-key kmap435 (kbd "s-f") (lambda () (interactive) (insert "(defun {} ({}){(nli)}{})")))
(define-key kmap435 (kbd "s-m") (lambda () (interactive) (insert "(defmacro {} ({}){(nli)}{})")))
(define-key kmap435 (kbd "s-s") (lambda () (interactive) (insert "(defstruct {}{(nli)}{})")))
(define-key kmap435 (kbd "s-b") (lambda () (interactive) (insert "(destructuring-bind ({}){})")))
kmap435))
kmap434))
(add-hook 'emacs-lisp-mode-hook
(lambda () (setq emacs-lisp-mode-map
(make-composed-keymap
emacs-lisp-buttons emacs-lisp-mode-map))))
(add-hook 'inferior-emacs-lisp-mode-hook
(lambda () (setq inferior-emacs-lisp-mode-map
(make-composed-keymap
emacs-lisp-buttons inferior-emacs-lisp-mode-map))))
(setq read-expression-map
(make-composed-keymap
emacs-lisp-buttons read-expression-map))
With some differences:
(ins ...)
is not plaininsert
but refers to a code-template-generating macro which interprets directives within{...}
brackets- other aliases within the scope of
buttons-macrolet
are(but)
,(cmd)
and(nli)
- other aliases within the scope of
- installing the keymap
emacs-lisp-buttons
onto the specified destination keymaps:(emacs-lisp-mode-map read-expression-map inferior-emacs-lisp-mode-map)
- is not done with
make-composed-keymap
but by recursive merging - is not done with
add-hook
but once for each feature, viaafter-load-functions
- is not done with
M-x package-refresh-contents
andM-x package-install RET buttons
- Alternatively
Place buttons.el
somewhere in the load-path and require the feature:
(push "/path/to/buttons/parent/directory" load-path)
(require 'buttons)
defvar-like wrapper that defines keymap KEYMAP
as KEYMAP-VAR
.
ANCESTOR
is a keymap used as a starting point on top of which to add new key bindings.TARGET-KEYMAPS
specifies a list of keymap symbols onto which to installKEYMAP-VAR
whenever those symbols become bound in emacs after a file load.- How inheritance works
- Placing
KEYMAP
on top ofANCESTOR
, as well as placing the newly-definedKEYMAP-VAR
on top of each keymap inTARGET-KEYMAPS
as they become available, is done by recursive merging of keymaps via the internal functionbuttons-define-keymap-onto-keymap
, which differs from(set-keymap-parent ...)
in that nested keymaps (or bindings for prefix keys) are merged instead of one definition clobbering the other one. This allows a child keymap to both inherit and extend a parent’s nested keymaps
- Placing
The following example defines a keymap c++buttons
using a previously-defined c-buttons
as a starting point. The c++-buttons
keymap bindings are automatically installed to c++-mode-map
whenever that symbol is loaded in emacs.
(defbuttons c++-buttons c-buttons
(c++-mode-map)
(let ((kmap (make-sparse-keymap)))
(define-key kmap (kbd "s-m") (lambda () (interactive) (insert "#include ")))
kmap))
creates a sparse keymap of bindings specified as (KEY TARGET)
pairs.
KEY
is a key-bindingTARGET
may be any define-keyDEF
target, including a command, a plain string, a nestedbuttons-make
form, etc(but ... ((kbd "s-E") #'eval-defun) ((kbd "s-i") "(interactive)") ((kbd "s-7") (but ((kbd "s-r") "&rest ") ((kbd "s-k") "&key ") ((kbd "s-b") "&body ") ((kbd "s-o") "&optional "))) ...)
- if the variable
buttons-make-key-mapper
is bound to a function that adds a super modifier, the above form is equivalent to:(let-when-compile ((buttons-make-key-mapper #'modifier-add-super)) (but ... ("E" #'eval-defun) ("i" "(interactive)") ("7" (but ("r" "&rest ") ("k" "&key ") ("b" "&body ") ("o" "&optional "))) ...))
A macro to define a code template.
It it similar to python’s format syntax in that it interprets directives
contained within {}
braces.
Directives are interpreted as follows:
- An empty
{}
prompts the user to enter custom text {N}
whereN
is a number, prompts the user to enter custom text and records it on the first occurrence, and on subsequent occurrences the recorded text is entered without prompt.- Anything else within the
{...}
directive regexp is interpreted as a lisp expression. If the expression evaluates to a string, it is inserted. - Example macroexpansion of a typical for-loop:
(macroexpand ' (buttons-template-insert
"for ( int {0} = 0; {0} < {}; {0}++ )" (newline-and-indent)))
;; expands to:
(let (rec-capture-0-86054)
(insert "for ( int ")
(setf rec-capture-0-86054 (buttons-record-template-var))
(insert " = 0; ")
(insert rec-capture-0-86054)
(insert " < ")
(recursive-edit)
(insert "; ")
(insert rec-capture-0-86054)
(insert "++ )")
(let* ((expr-val-86055 (newline-and-indent)))
(when (stringp expr-val-86055)
(insert expr-val-86055))))
- It is possible to change the directive regexp from matching
{...}
to something else, like<...>
, by bindingBUTTONS-TEMPLATE-INSERT-DIRECTIVE-REGEXP
at compile-time throughlet-when-compile
:(let-when-compile ((buttons-template-insert-directive-regexp "<\\(.*?\\)>")) ;; insert a bash variable surrounded by double quotes (buttons-template-insert "\"${<>}\""))
- A simpler way to achieve the same result is to break up the directive
across multiple strings, since a valid directive must be fully contained
within one string:
(macroexpand '(buttons-template-insert "\"${" "{}" "}\"") => (let nil (insert "\"${") (buttons-record-template-var) (insert "}\"")))
- A simpler way to achieve the same result is to break up the directive
across multiple strings, since a valid directive must be fully contained
within one string:
A convenience macro for defining an auto-documented, auto-named 0-ary command. Used to make frequent use of
(lambda () (interactive) "documentation"...)
look more concise and to provide automatic documentation:
> (buttons-defcmd (message "hello world") (insert "goodbye"))
> autogen-cmd5457
> (describe-function #'autogen-cmd5457)
> ...
- It also defines a tag that may be thrown to atomically abort the
currently executing command. The command
buttons-abort-cmd
throws this tag.
Provides short aliases to frequently used functions and macros to
make defbuttons
forms more concise. Within a buttons-macrolet
form, these
are default aliases:
shortcut | function/macro |
but | buttons-make |
cmd | buttons-defcmd |
ins | buttons-template-insert |
nli | newline-and-indent |
cbd | buttons-insert-c-style-code-block |
rec | recursive-edit |
idt | indent-for-tab-command |
cmt | comint-send-input |
cmd-ins | (cmd (ins …)) |
Visualize a keymap. Automatically bound to buttons-make-self-help-binding
on
all buttons-make
-defined keymaps. With a prefix argument, all currently active
keymaps are displayed.