Configuring meow for Friendly LaTeX Editing

TL;DR: We implement some configuration to recover functionality possible in evil-tex in the meow modal editing scheme.

Having ditched evil-collection to get familiar with the default emacs keybindings, I found myself satsisfied with many of the facilities emacs provides out-of-the-box for editing text. Alas, I feel modal editing is something I prefer, so I switched to meow. I chose meow for a few reasons:

  • Trivial to extend and hack on
  • The “selection-first” ethos is intriguing
  • No dependencies
  • Prefers built-in methods for various tasks
  • Keypad mode offers a great, lighter weight alternative to the SPC DOOM leader with evil mode

So far, it’s been an enjoyable experience, but one evil-mode package–evil-tex–gave me pause.

evil-tex

evil-tex is an extension to evil that adds support for “vimmable” text objects in LaTeX syntax. As an example, let | denote the location of the point when using evil mode. Suppose we’re given the situation:

The qu|ick brown fox jumps over the lazy dog.

In evil-normal-mode you can type the keys ciw to “Change Inner Word”, deleting the work quick and placing you in evil-insert-mode.

If w denotes the “word” evil object, evil-tex offers the math-mode text object, allowing this same loop to be performed but inside LaTeX syntax. Consider the following inline math expression with point |:

\( X\cong | Y \)

With evil-tex-mode enabled, and inside evil-normal-mode, we can press cim to “Change Inner Math”, deleting all the text within the \( \) delimiters and placing us in insert mode. This is just one example of what evil-tex offers; a more comprehensive picture is in the documentation.

Doing Our Own Thing

Meow uses things (lit.) to demarcate sections of text that you can navigate around and select. For example, some things that come preloaded with meow are sentences, defuns, paragraphs, buffers, windows, and lines. When a thing is defined, you can press , <THING_KEY> to select the inner part of the thing, where <THING_KEY> is the key associated with that thing (e.g. l for line, d for defun). Similarly, you can press . <THING_KEY> to select to the bounds of the thing. Here’s a demo on how that works with the symbol thing, mapped to e:

Figure 1: Demo of symbol thing in meow

Figure 1: Demo of symbol thing in meow

There’s a parallel between this behavior and the “inner <object>” and “all <object>” behavior in evil. For example, suppose we have a text object in evil that picks out the line the point is on, mapped to l. Then the key sequence c i l in evil mode (to “Change Inner Line”) could be replicated in meow with , l c.

In meow, it’s easy to define a thing with the function (meow-thing-register). Let’s register a thing that picks out the LaTeX inline math environment \( \). The simplest way to do this is using the pair matching:

(meow-thing-register 'inline-math
                     '(pair ("\\(") ("\\)"))
                     '(pair ("\\(") ("\\)") ) )

Now we can map this thing to a key:

(add-to-list 'meow-char-thing-table '(?m . inline-math))

Now, when we’re inside an inline math environment, we can press , m to select all the text within the math environment, and . m to select all of the math environment.

Figure 2: Demo of our user-defined math thing

Figure 2: Demo of our user-defined math thing

The bindings , m and . m replicate the evil-tex object identification i m and a m, respectively.

We can also use this same concept to extend functionality to full-scale LaTeX environments. That configuration is

(meow-thing-register 'latex-env
                     '(regexp "\\\\begin{.*?}\n\\(?:\\\\label{.*?}\n\\)?" "\n\\\\end{.*?}" )
                     '(regexp "\\\\begin{.*?}" "\\\\end{.*?}" ) )

(add-to-list 'meow-char-thing-table '(?E . latex-env))

This adds the same support for latex environments that look this like:

\begin{ENV}
...
\end{ENV}

The \label{...} directive sometimes appears in these environments, so the regex used to match these things are included as optional match groups.

Figure 3: Demo of our user-defined latex-env thing

Figure 3: Demo of our user-defined latex-env thing

In fact, in my own configuration, I’ve combined the latex-math and latex-env things into one thing, so I use just the key m to pick out either inline math environments or \begin{...} \end{...} environments.

These are all well and good, but one of my favorite evil-tex objects were the LaTeX delimiters: including \left( \right) and friends. Let’s do those too.

(setq latex-left-delim (rx "\\left"
                           (or "\\langle"
                               "("
                               "\["
                               "\\{"
                               "\\lbrace")))
(setq latex-right-delim (rx "\\right"
                            (or "\\rangle"
                                ")"
                                "\]"
                                "\\}"
                                "\\rbrace")))

(meow-thing-register 'latex-delim
                     `(regexp ,latex-left-delim ,latex-right-delim)
                     `(regexp ,latex-left-delim ,latex-right-delim))

(add-to-list 'meow-char-thing-table '(?j . latex-delim))

Here’s what that looks like:

Figure 4: Demo of our user-defined delimiter thing

Figure 4: Demo of our user-defined delimiter thing

Note that the way we have defined the delimiters makes it trivial to add/subtract delimiters from the list of things we want to match.

Edge Cases

Because of the way meow searches for the beginnings and ends of things, this implementation has obvious edge cases which I think are acceptable compromises. Notably, meow searches behind and in front of the point for the inner/outer/bounds of the thing. This implies that–for example–if the point was on a line containing \label{...} and you press , E in normal mode, it would select the line containing the label directive as well as the content before the \end{..}:

Figure 5: Edge case in latex-env implementation

Figure 5: Edge case in latex-env implementation

I strongly suspect (nay, I know) that more clever implementations are possible using emacs lisp.

Closing Thoughts

What I’ve shown here is a very small, quickly-put-together look at the hackability of meow. The documentation