Initial commit
This commit is contained in:
commit
902b4c5aa8
|
@ -0,0 +1,90 @@
|
|||
# define-repeat-map.el
|
||||
|
||||
Easily define repeat-maps
|
||||
|
||||
Emacs 28 comes built-in with repeat.el (which see), which allows users to
|
||||
define their own maps to repeat common commands easily. This package
|
||||
attempts to make the definition of those maps a one-sexp affair, through the
|
||||
macro `define-repeat-map'. See its docstring for details.
|
||||
|
||||
## The problem
|
||||
|
||||
Most of the time, defining a repeat-map and adding commands to keys is a
|
||||
multi-step affair, involving something like the following:
|
||||
|
||||
```lisp
|
||||
(defvar other-window-repeat-map (make-sparse-keymap)
|
||||
"A map for repeating `other-window' keys.")
|
||||
|
||||
(define-key other-window-repeat-map "o" #'other-window)
|
||||
|
||||
(put 'other-window 'repeat-map 'other-window-repeat-map))
|
||||
```
|
||||
|
||||
With more keys, it gets more complex; there has to be a better way!
|
||||
|
||||
## Enter `define-repeat-map`
|
||||
|
||||
With this macro, you can easily define these maps, and bind keys to them, using
|
||||
only one form. The code above looks like this with `define-repeat-map`:
|
||||
|
||||
```lisp
|
||||
(define-repeat-map other-window
|
||||
("o" other-window))
|
||||
```
|
||||
|
||||
Which isn't *so* much more convenient, but the convenience compounds with more
|
||||
complex maps. For example, here's a snippet from my `init.el`,
|
||||
pre-`define-repeat-map`:
|
||||
|
||||
```lisp
|
||||
(defalias 'forward-word-with-case 'forward-word
|
||||
"Alias for `forward-word' for use in `case-repeat-map'.")
|
||||
(defalias 'backward-word-with-case 'backward-word
|
||||
"Alias for `backward-word for use in `case-repeat-map'.")
|
||||
|
||||
(defvar case-repeat-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map "c" #'capitalize-word)
|
||||
(define-key map "u" #'upcase-word)
|
||||
(define-key map "l" #'downcase-word)
|
||||
;; movement
|
||||
(define-key map "f" #'forward-word-with-case)
|
||||
(define-key map "b" #'backward-word-with-case)
|
||||
map)
|
||||
"A map to repeat word-casing commands. For use with `repeat-mode'.")
|
||||
|
||||
(dolist (command '(capitalize-word
|
||||
capitalize-dwim
|
||||
upcase-word
|
||||
upcase-dwim
|
||||
downcase-word
|
||||
downcase-dwim
|
||||
forward-word-with-case
|
||||
backward-word-with-case))
|
||||
(put command 'repeat-map 'case-repeat-map))
|
||||
```
|
||||
|
||||
And here it is using this macro:
|
||||
|
||||
```lisp
|
||||
(define-repeat-map case
|
||||
("c" capitalize-word
|
||||
"u" upcase-word
|
||||
"l" downcase-word)
|
||||
(:continue "f" forward-word
|
||||
"b" backward-word)
|
||||
(:enter downcase-dwim
|
||||
upcase-dwim
|
||||
capitalize-dwim))
|
||||
```
|
||||
|
||||
Much easier, in this author's humble opinion.
|
||||
|
||||
## Similar packages
|
||||
|
||||
I've found one other package that tries to simplify repeat-map definition, but
|
||||
it's not quite as flexible as this. In fact, that's why I wrote
|
||||
`define-repeat-map`. But here's the other:
|
||||
|
||||
- [repeaters.el](https://github.com/mmarshall540/repeaters)
|
|
@ -0,0 +1,127 @@
|
|||
;;; define-repeat-map.el --- Easy-define repeat-maps -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2021 Case Duckworth
|
||||
|
||||
;; Author: Case Duckworth <acdw@acdw.net>
|
||||
;; Keywords: convenience
|
||||
;; URL:
|
||||
|
||||
;;; License:
|
||||
|
||||
;; Everyone is permitted to do whatever with this software, without
|
||||
;; limitation. This software comes without any warranty whatsoever,
|
||||
;; but with two pieces of advice:
|
||||
;; - Don't hurt yourself.
|
||||
;; - Make good choices.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Emacs 28 comes built-in with repeat.el (which see), which allows users to
|
||||
;; define their own maps to repeat common commands easily. This package
|
||||
;; attempts to make the definition of those maps a one-sexp affair, through the
|
||||
;; macro `define-repeat-map'. See its docstring for details.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(defun define-repeat-map--make-alias (cmd map)
|
||||
(intern (concat (symbol-name cmd) "|"
|
||||
(symbol-name map))))
|
||||
|
||||
(defun define-repeat-map--map-commands (map fn args)
|
||||
(let (result)
|
||||
(dolist (arg args)
|
||||
(unless (stringp arg)
|
||||
(push (funcall fn arg) result)))
|
||||
(nreverse result)))
|
||||
|
||||
(defun define-repeat-map--define-keys (map fn args)
|
||||
(unless (zerop (mod (length args) 2))
|
||||
(error "Wrong number of args"))
|
||||
(let (result)
|
||||
(while args
|
||||
(let ((key (pop args))
|
||||
(cmd (funcall fn (pop args))))
|
||||
(push `(define-key ,map (kbd ,key) #',cmd) result)))
|
||||
(nreverse result)))
|
||||
|
||||
;;;###autoload
|
||||
(defmacro define-repeat-map (name &rest args)
|
||||
"Define a repeat-map, NAME-repeat-map, and bind keys to it.
|
||||
Each ARG is a list of lists containing keybind definitions of
|
||||
the form (KEY DEFINITION) KEY is anything `kbd' can recognize,
|
||||
and DEFINITION is passed directly to `define-key'.
|
||||
|
||||
Optionally, the car of an arglist can contain the following
|
||||
symbols, which changes the behavior of the key definitions in the
|
||||
rest of the list:
|
||||
|
||||
:enter - Provided commands can enter the repeat-map, but aren't
|
||||
bound in the map. They need to be bound elsewhere, however.
|
||||
|
||||
:exit - Keys are bound in the repeat-map, but can't enter the
|
||||
map. Their invocation exits the repeat-map.
|
||||
|
||||
:continue - Keys are bound in the repeat-map, but can't enter the
|
||||
map. However, their invocations keep the repeat-map active."
|
||||
(declare (indent 1))
|
||||
(let ((result)
|
||||
(map (intern (concat (symbol-name name) "-repeat-map"))))
|
||||
;; Create the keymap
|
||||
(push `(defvar ,map (make-sparse-keymap)
|
||||
"Defined by `define-repeat-map'.")
|
||||
result)
|
||||
|
||||
;; Iterate through ARGS
|
||||
(dolist (arg args)
|
||||
(pcase (car arg)
|
||||
(:enter
|
||||
;; Add the map to the commands' repeat-map property.
|
||||
(push `(progn
|
||||
,@(define-repeat-map--map-commands
|
||||
`,map
|
||||
(lambda (cmd) `(put ',cmd 'repeat-map ',map))
|
||||
(cdr arg)))
|
||||
result))
|
||||
|
||||
(:exit
|
||||
;; Bind the commands in the map.
|
||||
(push `(progn
|
||||
,@(define-repeat-map--define-keys
|
||||
`,map #'identity (cdr arg)))
|
||||
result))
|
||||
|
||||
(:continue
|
||||
;; Make an alias for each command, and process that alias like the
|
||||
;; default, below.
|
||||
(push `(progn
|
||||
,@(define-repeat-map--define-keys
|
||||
`,map
|
||||
(lambda (cmd) (define-repeat-map--make-alias cmd map))
|
||||
(cdr arg))
|
||||
,@(define-repeat-map--map-commands
|
||||
`,map
|
||||
(lambda (cmd)
|
||||
(let ((alias (define-repeat-map--make-alias cmd map)))
|
||||
`(progn
|
||||
(defalias ',alias ',cmd
|
||||
"Defined by `define-repeat-map'.")
|
||||
(put ',alias
|
||||
'repeat-map ',map))))
|
||||
(cdr arg)))
|
||||
result))
|
||||
|
||||
(_
|
||||
;; Default: bind the commands in the map, and add the map to the
|
||||
;; commands' repeat-map property.
|
||||
(push `(progn
|
||||
,@(define-repeat-map--define-keys `,map #'identity arg)
|
||||
,@(define-repeat-map--map-commands
|
||||
`,map
|
||||
(lambda (cmd) `(put ',cmd 'repeat-map ',map))
|
||||
arg))
|
||||
result))))
|
||||
|
||||
`(with-eval-after-load 'repeat
|
||||
,@(nreverse result))))
|
||||
|
||||
;;; define-repeat-map.el ends here
|
Loading…
Reference in New Issue