Region bindings and common lisp modes
I’ve published two new packages for Emacs: - region-bindings.el and common-lisp-modes.el.
Both are quite small and were a part of my configuration for a long time, but after small refactoring of my init.el
I’ve decided to extract them.
The first one, region-bindings
is a from-scratch re-implementation of region-bindings-mode.
This project had not seen development since 2014, and I had issues with how it works.
When I tried it I wasn’t using transient-mark-mode
(tmm
), yet, that package still activated its bindings, and did not properly disable it.
Even with tmm
enabled, it managed to leak its state when I used isearch
.
So I decided to make my own.
The implementation is not too different, but I made sure that it works both with and without tmm
.
As an added bonus, it features a global minor mode and some pre-defined keybindings.
I mostly use it with puni
and multiple-cursors.el
:
(use-package puni
:bind ( :map region-bindings-mode-map
("(" . puni-wrap-round)
("[" . puni-wrap-square)
("{" . puni-wrap-curly)
("<" . puni-wrap-angle)))
(use-package multiple-cursors
:bind
( :map region-bindings-mode-map
("n" . mc/mark-next-symbol-like-this)
("N" . mc/mark-next-like-this)
("p" . mc/mark-previous-symbol-like-this)
("P" . mc/mark-previous-like-this)
("a" . mc/mark-all-symbols-like-this)
("A" . mc/mark-all-like-this)
("s" . mc/mark-all-in-region-regexp)
("l" . mc/edit-ends-of-lines)))
The second package is called “common lisp modes” but it has nothing to do with Common Lisp. It’s a very small package that provides a minor mode that you can use to set up other packages that are common between various lisps. So it’s not “common-lisp modes” but rather “common lisp-modes”.
The main reason is that the lisp-mode
in Emacs is for Common Lisp and its derivatives that share the same syntax.
Unfortunately, other lisps like Clojure or Fennel can’t inherit from lisp-mode
and instead inherit their major modes from prog-mode
.
This works but comes at a cost that you can’t really add lisp-specific things that you want across all lisps, like paredit
, to the prog-mode-hook
because it will be enabled in other programming languages too, like in Erlang or OCaml, where it makes no sense.
There’s a lisp-data-mode
in Emacs, which can be used instead of prog-mode
, but there are already too many packages implemented in terms of prog-mode
and they don’t really need to change that, as they work fine.
The same goes for most of the REPLs - they use variants of comint-mode
as a parent mode, and like prog-mode
comint is used for much more than lisp REPLs.
So again, it’s hard to configure common features specific to lisp REPLs only.
Thus this package provides two minor modes common-lisp-modes-mode
and common-repl-modes-mode
, to which you can add hooks.
For example, my configuration does the following:
;; actual configs omitted
(use-package minibuffer
:hook (eval-expression-minibuffer-setup . common-lisp-modes-mode))
(use-package elisp-mode
:hook (emacs-lisp-mode . common-lisp-modes-mode))
(use-package fennel-mode
:hook ((fennel-mode fennel-repl-mode) . common-lisp-modes-mode))
(use-package lisp-mode
:hook ((lisp-mode lisp-data-mode) . common-lisp-modes-mode))
(use-package inf-lisp
:hook (inferior-lisp-mode . common-lisp-modes-mode))
(use-package sly
:hook (sly-mrepl-mode . common-lisp-modes-mode))
(use-package scheme
:hook (scheme-mode . common-lisp-modes-mode))
(use-package racket-mode
:hook ((racket-mode racket-repl-mode) . common-lisp-modes-mode))
(use-package clojure-mode
:hook ((clojure-mode clojurec-mode clojurescript-mode) . common-lisp-modes-mode))
(use-package cider
:hook (cider-repl-mode . common-lisp-modes-mode))
And then I simply use common-lisp-modes-mode-hook
to enable common features:
(use-package puni
:hook ((common-lisp-modes-mode nxml-mode) . puni-mode))
(use-package isayt
:hook (common-lisp-modes-mode . isayt-mode))
Maybe there are not that many common modes in my configuration, but I still appreciate the fact that I don’t need to list all lisps I use in both puni
and isayt
configuration, like this, or keep a list somewhere else:
(use-package puni
:hook ((eval-expression-minibuffer-setup-hook
emacs-lisp-mode-hook
fennel-mode-hook
fennel-repl-mode-hook
lisp-mode-hook
lisp-data-mode-hook
inferior-lisp-mode-hook
sly-mrepl-mode-hook
scheme-mode-hook
racket-mode-hook
racket-repl-mode-hook-hook
clojure-mode-hook
clojurec-mode-hook
clojurescript-mode-hook
cider-repl-mode-hook)
. puni-mode))
(use-package isayt
:hook ((eval-expression-minibuffer-setup-hook
emacs-lisp-mode-hook
fennel-mode-hook
fennel-repl-mode-hook
lisp-mode-hook
lisp-data-mode-hook
inferior-lisp-mode-hook
sly-mrepl-mode-hook
scheme-mode-hook
racket-mode-hook
racket-repl-mode-hook-hook
clojure-mode-hook
clojurec-mode-hook
clojurescript-mode-hook
cider-repl-mode-hook)
. isayt-mode))
It is harder to maintain, as I’ll need to add new lisps I want to try out or remove lisps once I’m done playing with them to prevent cluttering. I already have a lot of lisps I use semi-regularly, so I want the process to be more seamless.
I hope these packages will be helpful for others too, but I don’t think I’m going to submit them to MELPA or (Non-GNU)ELPA.
You can use straight.el
, el-get
, quelpa
and such to install these directly from git.