Neovim and LaTeX with LazyVim, part 3: custom commands

This is a follow-up to the first post on Neovim and LaTeX and the second one.

If you write LaTeX in Neovim using the LazyVim distribution, you already get a fantastic editing experience thanks to VimTeX and texlab. But there’s one thing that often trips people up: how do you quickly wrap selected text in \emph{…}, \textbf{…}, or other LaTeX commands?

The answer is mini.surround — and with a small buffer-local configuration, it becomes a LaTeX wrapping powerhouse.


What mini.surround Gives You (Out of the Box)

LazyVim ships with the mini.surround extra, which you can enable in your lazy.lua (or wherever you manage extras):

Once enabled, you get operations like:

Keymap Action
gsa Add surrounding
gsd Delete surrounding
gsr Replace surrounding
gsf Find surrounding

For example, visual-select a word and press gsa" to wrap it in double quotes, or gsa) for parentheses. Great — but what about LaTeX-specific commands?


The Problem with LaTeX Commands

VimTeX gives you text objects, motions, completion, and much more — but it doesn’t ship a built-in “surround this selection with \emph{…}” feature. That’s where mini.surround‘s custom surroundings come in.

We can teach mini.surround to understand LaTeX commands by defining custom surroundings in a filetype-specific Lua file.


Setting Up Custom LaTeX Surroundings

Create (or edit) the file ~/.config/nvim/after/ftplugin/tex.lua. This file is automatically sourced by Neovim whenever you open a .tex file.

Why ftplugin? Files in this directory are loaded after all plugin configuration, and only for the matching filetype. This means our settings are buffer-local and don’t interfere with any other filetype.


How It Works

The cmd_surround helper function does two things:

  1. input: A Lua pattern that matches \cmd{...} so that gsd and gsr know what to find and remove.
  2. output: The left (\cmd{) and right (}) strings used when adding a surrounding.

We then assign short, memorable single-character IDs to each command.


Using the Surroundings

Here’s what you can do in any .tex buffer:

Adding a Surrounding

Visual-select some text, then:

Keymap Result
gsae \emph{selected text}
gsab \textbf{selected text}
gsai \textit{selected text}
gsat \texttt{selected text}
gsau \underline{selected text}
gsas \textsc{selected text}
gsaq (add opening and closing LaTeX quotations, i.e., two backticks and ”)

You can also use gsa in normal mode with a motion: gsaeiw wraps the current word in \emph{…}.

Deleting a Surrounding

Place your cursor inside a command and press:

Keymap Action
gsde Remove \emph{…}, leaving the inner text
gsdb Remove \textbf{…}
gsdi Remove \textit{…}

Replacing a Surrounding

Want to change \emph{…} to \textbf{…}? No problem:

That’s gsr (replace surrounding), then e (find \emph{…}), then b (replace with \textbf{…}).


A Practical Example

Suppose you have this text:

You visual-select “very important” and press gsab. You get:

Now you decide that emphasis is better. With the cursor inside the \textbf{…}, press gsrbe:

And if you want to remove the markup entirely: gsde:


Extending with Your Own Commands

The pattern is easy to extend. Just add more entries to cfg.custom_surroundings:

Pick single characters that are easy to remember and don’t conflict with built-in mini.surround ids (avoid (, ), [, ], {, }, ', ", `).


A Note on Limitations

The input patterns use Lua’s pattern matching, which cannot handle nested braces. For example, \emph{foo {bar} baz} may not be matched correctly by gsd or gsr. For the vast majority of LaTeX writing, this is not an issue — but it’s worth keeping in mind if you work heavily with nested commands.


Happy LaTeX writing! 🎓

2 thoughts on “Neovim and LaTeX with LazyVim, part 3: custom commands

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.