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.
This is the first release of the package. While functional, it may contain bugs or rough edges. If you encounter any issues, please report them on GitHub.
If you find evil-tex-ts useful, please consider giving it a star on GitHub — it helps others discover the project!
| 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`)
(use-package evil-tex-ts
:ensure t
: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))Or install manually with M-x package-install RET evil-tex-ts RET.
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 AUCTeXImportant: 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 } |