evil-tex-ts is a modern, tree-sitter based toolkit for LaTeX editing in Evil mode. It is a re-imagining of the classic evil-tex package, utilizing Emacs 29+ built-in tree-sitter capabilities for superior performance and accuracy.
Why a new package? The original `evil-tex` has not seen updates in years and relies on regular expressions for text object detection. This approach can be brittle, inaccurate in complex cases, and inefficient on large files. evil-tex-ts leverages the structural understanding of the `tree-sitter-latex` parser to provide precise text objects, efficient toggles, and robust surround integration.
Warning: Initial Release This is an early version of the package. While functional, it may contain bugs. Please report issues on GitHub.
| marks point.
\foobar{barbaz} \foobar{barbaz} \foobar \foobar \foobar{} \foobar{}
└─────────────┘ └────┘ └─────┘ ┆ └───────┘ ┆
ac ic ac ic (empty) ac ic (empty)
┌\begin{foobar} \begin{foobar}
│ ┌
ae│ baz ie│ baz
│ └
└\end{foobar} \end{foobar}
\(foobar\) \(foobar\) \[foobar\] \[foobar\]
└────────┘ └────┘ └────────┘ └────┘
am im am im
(foobar) (foobar) \left(foobar\right) \left(foobar\right) \Bigl(foobar\Bigr) \Bigl(foobar\Bigr)
└──────┘ └────┘ └─────────────────┘ └────┘ └────────────────┘ └────┘
ad id ad id ad id
┌\section{foo} \section{foo}
│ ┌
aS│ baz iS│ baz
│\subsection*{} │\subsection*{}
└ qux └ qux
\chapter*{bar} \chapter*{bar}
a^{foo} a^{foo} a^b a^b a^\bar a^\bar
└────┘ └─┘ └╵ ╵ └───┘ └──┘
a^ i^ a^ i^ a^ i^
a_{foo} a_{foo} a_b a_b a_\bar a_\bar
└────┘ └─┘ └╵ ╵ └───┘ └──┘
a_ i_ a_ i_ a_ i_
& foobar & & foobar & & foobar \\ & foobar \\
└───────┘ └──────┘ └───────┘ └──────┘
aT iT aT iT
`foobar' `foobar' ``foobar'' ``foobar''
└──────┘ └────┘ └────────┘ └────┘
aq iq aQ iQ
die(delete inner environment)Before:
\begin{equation} x^2 + y^2 = z^2| \end{equation}Press
dieAfter:
\begin{equation} \end{equation}cic(change inner command)Point can be anywhere inside the command:
Before:
\textbf{hel|lo}Press
cicAfter:
\textbf{|}Before:
\tex|tbf{hello}Press
cicAfter:
\textbf{|}If point is outside a command, seeks the nearest command to the right on the same line:
Before:
s|ome text \textbf{hello}Press
cicAfter:
some text \textbf{|}dam(delete around math)Before:
\(x + y| = z\)Press
damAfter: (empty)
If point is outside math, seeks the nearest math to the right on the same line:
Before:
some te|xt \(x + y\)Press
damAfter:
some text|viS(select inner section)Select the content of a section until the next section (excluding the
\section{...}header).Before:
\section{Introduction} This is the intro.| More text here. \section{Methods}Press
viSAfter (selected region marked with ##):
\section{Introduction} ##This is the intro. ##More text here. ## \section{Methods}The empty line before
\section{Methods}is included. UsevaSto also include the\section{...}header.
All toggle commands use the mt prefix by default (mnemonic: “magnificent toggle”).
Note: The mt prefix overrides the m (set-marker) command for the t character. You have 25 other marks available. To change this behavior, see User Options.
mte(toggle environment *)Toggle the asterisk on environments. Useful for switching between numbered and unnumbered equations.
\begin{equation}| → \begin{equation*}Works with any environment:
\begin{align} ↔ \begin{align*} \begin{figure} ↔ \begin{figure*} \begin{table} ↔ \begin{table*}mtm(toggle math mode)Toggle between inline and display math modes.
\(x + y|\) → \[x + y\] → \(x + y\) $x + y|$ → \[x + y\] → $x + y$The inline math format is controlled by
evil-tex-ts-preferred-inline-math:dollar($...$, default) orparen(\(...\)).mtM(toggle math align)Toggle between display math and
align*environment.Before:
\[ x + y| \]Press
mtMAfter:
\begin{align*} x + y \end{align*}Press
mtMagain\[ x + y \]Also works starting from inline math (one-way to
align*):Before:
$x + |y$Press
mtMAfter:
\begin{align*} x + y \end{align*}Then toggles between
align*and\[...\].mtd(toggle delimiter sizing)Toggle automatic delimiter sizing with
\left=/\right=.(x + y|) ↔ \left(x + y\right) [a, b|] ↔ \left[a, b\right] \{1, 2|\} ↔ \left\{1, 2\right\}Also removes
\bigl=/\bigr= and similar sizing commands (one-way):\bigl(x + y\bigr) → (x + y) \Bigl[a, b\Bigr] → [a, b]mtc(toggle command *)Toggle the asterisk on commands. Useful for switching between numbered and unnumbered sections.
\section{Title|} → \section*{Title}Works with various sectioning commands:
\chapter{Title} ↔ \chapter*{Title} \subsection{Title} ↔ \subsection*{Title} \paragraph{Title} ↔ \paragraph*{Title}mtS(change section type)Change the type of the current section. A prompt appears with available section types.
Before:
\section{My Title|} Some content here.Press
mtS, selectsubsectionfrom promptAfter:
\subsection{My Title} Some content here.Available types:
part,chapter,section,subsection,subsubsection,paragraph,subparagraph
Surround commands follow the evil-surround patterns:
ys<text-object><surround-type>— add surround (e.g.,ysiwewraps inner word with environment)cs<old><new>— change surround (e.g.,cseechanges environment to environment)ds<type>— delete surround (e.g.,dsddeletes delimiter)
When <new> or <type> opens a keymap (like e for environment or d for delimiter), press the corresponding key to select (see Keymaps).
ysiwe(surround word with environment)Before: |word
Press
ysiwe, then typeequationAfter:
\begin{equation} word \end{equation}Se(visual surround with environment)Before:
|1 + 2Select the expression with
v$(or any visual selection), pressS, thenefor environment, thenaforalign(see Environment Keymap)After:
\begin{align} 1 + 2 \end{align}Content is automatically indented inside the environment.
VjSeA(linewise surround multiple lines with environment)Before:
|1 + 2 3 + 4Vjselects both lines,Sstarts surround,efor environment,Aforalign*After:
\begin{align*} 1 + 2 3 + 4 \end{align*}cseea(change environment to align)Before:
\begin{equation} x^2 + y^2| = z^2 \end{equation}Press
cs(change surround),e(old: environment),e(new: environment keymap),a(align)After:
\begin{align} x^2 + y^2 = z^2 \end{align}dsd(delete surrounding delimiter)Before:
\left(x + y|\right)Press
dsdAfter:
|x + yAlso works with simple delimiters:
Before:
(a + b|)Press
dsdAfter:
|a + b
- Emacs 29.1 or later (with tree-sitter support enabled)
- evil
- evil-surround (optional, but highly recommended)
- A generic `tree-sitter-latex` grammar (usually installed via `M-x treesit-install-language-grammar`)
Since this package is not yet on MELPA, clone the repository and add it to your load-path.
(add-to-list 'load-path "/path/to/evil-tex-ts")
(require 'evil-tex-ts)
;; Hook into LaTeX modes
(add-hook 'latex-ts-mode-hook #'evil-tex-ts-mode)
(add-hook 'LaTeX-mode-hook #'evil-tex-ts-mode) ;; If using AUCTeX(use-package evil-tex-ts
:load-path "/path/to/evil-tex-ts" ; or use :straight, :elpaca, etc.
:after (evil)
:hook ((LaTeX-mode . evil-tex-ts-mode)
(latex-mode . evil-tex-ts-mode))
:init
;; Register tree-sitter grammar source
(add-to-list 'treesit-language-source-alist
'(latex "https://github.com/latex-lsp/tree-sitter-latex"))
:config
;; Auto-install grammar if not available
(unless (treesit-language-available-p 'latex)
(treesit-install-language-grammar 'latex))
;; IMPORTANT: Set preferred inline math format
;; 'dollar for $...$ (default), 'paren for \(...\)
(setq evil-tex-ts-preferred-inline-math 'dollar))Important: The variable evil-tex-ts-preferred-inline-math controls which format is used when toggling math modes (mtm) and surrounding with inline math (m). Default value is 'dollar ($...$). Set to 'paren for \(...\).
This package defines precise text objects based on the AST:
| Key | Text Object Target | Notes |
|---|---|---|
c | LaTeX commands: \foo{...} | Handles optional args [...] and nested braces |
e | Environments: \begin{...} ... \end{...} | Selects proper range including newlines |
m | Math: \(...\), \[...\], $...$, environments | Detects inline vs display math automatically |
d | Delimiters: (), [], \{\}, \left(\right) | Handles balanced pairs |
S | Sections: \section{...} to end of section | Hierarchical selection |
^ | Superscripts: x^2, x^{...} | |
_ | Subscripts: x_i, x_{...} |
Default prefix is mt (“magnificent toggle”).
| Key | Behaviour | Example |
|---|---|---|
mte | Toggle environment asterisk | \begin{eq} ↔ \begin{eq*} |
mtc | Toggle command asterisk | \section ↔ \section* |
mtm | Toggle math mode (inline ↔ display) | \(...\) ↔ \[...\] |
mtM | Toggle math align (display ↔ align*) | \[...\] ↔ \begin{align*}... |
mtd | Toggle delimiter sizing | (x) ↔ \left(x\right) |
If `evil-surround` is installed, `evil-tex-ts` adds specific surround pairs.
| Key | Surrounds with | Example Input | Result |
|---|---|---|---|
c | Command (prompts) | text | \cmd{text} |
e | Environment (keymap) | text | \begin{env} text \end{env} |
m | Inline math | text | \(text\) or $text$ |
M | Display math | text | \[text\] |
d | Delimiter (keymap) | text | \left(text\right) |
^ | Superscript | x | ^{x} |
_ | Subscript | x | _{x} |
; | CDLaTeX accents (keymap) | x | \bar{x} |
See Keymaps for full keymaps.
| Key | Command | Description |
|---|---|---|
]] | evil-tex-ts-go-forward-section | Jump to next section heading |
[[ | evil-tex-ts-go-back-section | Jump to previous section heading |
Works in both normal and visual modes. Recognizes chapter, section, subsection, etc.
Customize these variables via M-x customize-group RET evil-tex-ts.
evil-tex-ts-select-newlines-with-envs(default:t)
Include newlines when selecting/deleting environments. Makesdaeremove the whole block cleanly.evil-tex-ts-toggle-override-m(default:t)
Bind toggles tomt. Set tonilto preserve themmarker key.evil-tex-ts-toggle-override-t(default:nil)
Bind toggles tots(vimtex style). Can be used alongside or instead ofmt.evil-tex-ts-preferred-inline-math(default:'dollar)
Preferred format for inline math toggles:dollar($...$) orparen(\(...\)).
Add your own environments, accents, and delimiters using these functions:
;; Add "quote" environment on "q"
(evil-tex-ts-bind-to-env-map '(("q" . "quote")))
;; Add figure with default [!ht] position
(evil-tex-ts-bind-to-env-map '(("F" "\\begin{figure}[!ht]" . "\\end{figure}")))
;; Add custom accent
(evil-tex-ts-bind-to-cdlatex-accents-map '(("B" . "fbox")))
;; Add custom delimiter
(evil-tex-ts-bind-to-delim-map '(("h" "\\huge(" . "\\huge)")))See docstrings of evil-tex-ts-bind-to-env-map for the complete format.
Bind your function to evil-tex-ts-toggle-map, it’s a normal keymap:
(define-key evil-tex-ts-toggle-map "x" #'my-custom-toggle-function)| Feature | evil-tex (Original) | evil-tex-ts (This package) |
|---|---|---|
| Parsing | Regular Expressions | Tree-sitter (AST) |
| Performance | Slower on large files | Fast & Incremental |
| Accuracy | Can be tricked by comments | Robust structural parsing |
| Dependencies | Emacs 25+, AUCTeX | Emacs 29+, tree-sitter grammar |
| Maintainability | Harder (complex regex) | Easier (standard grammar) |
- evil-tex by iyefrat for the original concept, features, and the visual reference diagram.
- vimtex for inspiration on toggles and text objects.
- evil-latex-textobjects for laying the groundwork.
| Key | Env | Key | Env |
|---|---|---|---|
x | prompt | m | multline |
e | equation | M | multline* |
E | equation* | c | cases |
a | align | z | tikzpicture |
A | align* | f | figure |
n | alignat | i | itemize |
N | alignat* | I | enumerate |
g | gather | b | frame |
G | gather* | y | array |
l | flalign | r | eqnarray |
L | flalign* |
Press t then the second key to select theorem-like environment.
Example: ysiwetd wraps word in \begin{definition}...\end{definition}
| Key | Env | Key | Env |
|---|---|---|---|
ta | axiom | tl | lemma |
tc | corollary | tp | proof |
tC | claim | tq | question |
td | definition | tr | remark |
te | examples | tt | theorem |
ts | exercise |
Uppercase = simple, lowercase = sized (\left~/\right~).
| Key | Simple | Key | Sized |
|---|---|---|---|
P | () | p | \left(\right) |
S | [] | s | \left[\right] |
C | \{\} | c | \left\{\right\} |
R | \langle\rangle | r | \left\langle\right\rangle |
V | \lvert\rvert | v | \left\lvert\right\rvert |
N | \lVert\rVert | n | \left\lVert\right\rVert |
Context sensitive: uses \mathbf in math, \textbf in text.
| Key | Accent | Key | Accent |
|---|---|---|---|
. | dot | : | ddot |
^ | hat | H | widehat |
| ~~~~ | tilde | N | widetilde |
- | bar | T | overline |
v | check | u | breve |
> | vec | / | grave |
" | acute | _ | underline |
{ | overbrace | } | underbrace |
q | sqrt |
| Key | Style | Key | Style |
|---|---|---|---|
b | bold (bf) | r | roman (rm) |
i | italic (it) | e | emph |
y | typewriter (tt) | f | sans-serif (sf) |
l | slanted (textsl) | c | calligraphic |
m | mbox |
Useful for controlling formula size, e.g. in fractions.
Example: ysiw;1 wraps word in {\displaystyle ...}
| Key | Style |
|---|---|
0 | {\textstyle } |
1 | {\displaystyle } |
2 | {\scriptstyle } |
3 | {\scriptscriptstyle } |