One LaTeX setup
This is an account of how my LaTeX setup works, after about a
decade of writing mathematics in it. It was not encoded into what NoetherVim does to your
documents. The latex bundle assumes as little as it can get away with, and
everything below is something you can add yourself if you so choose.
It is written since some of the bundle’s snippets are only worth having if you adopt the conventions they were built against, and a worked setup is easier to understand.
Theorem environments carry their own name
Section titled “Theorem environments carry their own name”The usual amsthm declaration gives you numbered environments:
\newtheorem{theorem}{Theorem}[section]\newtheorem{defn}[theorem]{Definition}which you then use as \begin{defn} ... \end{defn}, and refer to with a
\label you have to remember to write.
The variant here takes two arguments instead:
\begin{defn}{Group Action}{grpAction} A group action of $G$ on $X$ is ...\end{defn}The first is the title shown in the rendered output, the second is the
label. One declaration site, so a definition can never be labelled and named
inconsistently, and \cref{df:grpAction} reads as a sentence rather than as
“Definition 3.7”.
That form comes from tcolorbox rather than from anything hand-rolled:
\newtcbtheorem declares environments taking exactly a title and a label, and
gives the label a per-environment prefix, which is where the df: comes from.
Typing :defn group action writes both from what you typed: the name is
title-cased and the label is a compact form of it. So the label is derived
rather than invented, and stays predictable across a book.
The cost is that it is not standard: a co-author’s \begin{defn} will not
compile against your preamble, and yours will not compile against theirs. For
a solo thesis or a book that is a fair trade. For a joint paper it is not,
which is the main reason this is a page rather than a default.
mathpkgs writes the package block. The theorem declarations are not among
what any snippet writes, since thmset produces the standard one-argument
\newtheorem form; the declarations below
are the ones the :defn, :prop and :lem snippets expect.
Every definition gets an index entry
Section titled “Every definition gets an index entry”The theorem snippets also emit \index{...} alongside the environment. For
a document long enough to need an index (lecture notes, a book) the
entry is only ever written when the definition is, so the index cannot drift
out of step with the text. For a paper it is noise, and you would want the
snippets without it.
Figures live in one folder, referenced relatively
Section titled “Figures live in one folder, referenced relatively”project/├── main.tex├── sections/│ └── chapter-3.tex└── images/ └── commutative-square.pdfSource files sit one level down, so figure paths are ../images/.... The
FIG snippet works this out rather than assuming it: it offers whichever of
figure_folders actually exists, beside the document or one level up, and
the offered path is still an editable field. Name your own folders with
figure_folders if neither default matches.
Figures are compiled separately as standalone documents where they are
expensive: TikZ diagrams that would otherwise be rebuilt on every run of
the main document. standalone writes that skeleton.
Bibliographies: shared for books, per-document for papers
Section titled “Bibliographies: shared for books, per-document for papers”Two patterns, and they want different things:
- A book or lecture-note series shares one bibliography across every
chapter, so a citation added in chapter 8 is available in chapter 2 and
the numbering is consistent. One
.bibat the project root. - A paper carries its own, so it can be handed to a journal as a self-contained directory.
The zotero bundle inserts a citation and appends the entry to whichever
.bib it resolves for the current document. The resolution is a function you
can replace, which is how the shared-bibliography case is expressed: point
every file in the project at the same one.
-- lua/user/plugins/zotero.luareturn { { "Chiarandini/snacks-zotero.nvim", opts = { ft = { tex = { locate_bib = function() -- Walk up for a project marker; fall back to the plugin's -- per-document search when there is none. local root = vim.fs.find(".project-root", { upward = true })[1] return root and (vim.fs.dirname(root) .. "/references.bib") or require("snacks_zotero.bib").locate_tex_bib() end, }, }, } },}Compilation is on request, and the PDF follows the cursor
Section titled “Compilation is on request, and the PDF follows the cursor”VimTeX compiles with latexmk once per request rather than continuously
(continuous = 0, against VimTeX’s own default of 1), so a large project does
not rebuild on every write. <LocalLeader>ll runs a build,
<LocalLeader>lv jumps the viewer to the cursor, and <LocalLeader>lf makes
the viewer follow the cursor as you move, so the PDF behaves like a second
view of the document rather than something you check on.
That last one is off until asked for, per buffer: a forward search is a viewer round-trip, and paying it on every cursor movement all session is a cost most editing does not want.
The cost of building on request is that the PDF drifts behind the source, and
SyncTeX resolves against the line numbers it recorded at the last build. So
<LocalLeader>lv checks first, and says how far behind the PDF is rather than
jumping somewhere plausible and wrong.
Prose is hard-wrapped
Section titled “Prose is hard-wrapped”The writing profile hard-wraps at textwidth, so a paragraph is several
lines in the file and one in the output. The reason is version control: a
one-line paragraph produces a one-line diff no matter which word changed,
and a review of “what changed in this proof” becomes unreadable.
The cost is that a search for a phrase can fail when the wrap falls between
two of its words. That is what the wrapsearch bundle is for, and it is
listed here because the two settings only make sense together.
What this means for the snippets
Section titled “What this means for the snippets”The bundle’s LaTeX snippets divide into three, and two of them are switches:
- On always: fractions, sectioning,
\textbf, alignment, the environment wrappers, the preamble skeletons. Nothing on this page matters for these; they work in any document. conventions: the theorem family, in the two-argument form with an\indexentry. Off by default, because\begin{defn}{a}{b}does not compile against a preamble that has not declared it. The declarations are below.acronyms: the prose shorthand. Off by default for a different reason; it has no dependency at all, it is simply one person’s vocabulary.
-- lua/user/plugins/noethervim-tex.luareturn { { "Chiarandini/NoetherVim-tex", opts = { snippets = { conventions = true, acronyms = true } } },}The declarations conventions expects
Section titled “The declarations conventions expects”The two-argument form is not a wrapper anyone has to write. It is tcolorbox’s
own interface: \newtcbtheorem declares an environment taking a title and a
label, and its final argument is a prefix that the label is given. So
\newtcbtheorem[number within=section]{defn}{Definition}{...}{df}makes \begin{defn}{Group Action}{grpAction} set Definition 1.1: Group
Action and write \label{df:grpAction}. Those prefixes are the same ones the
label picker maps in its transformations table, which is why \cref and
<Space>w agree about what a definition is called.
label type= names the cleveref type, so \crefname can be stated in the
ordinary way rather than reaching for the internal counter.
\usepackage{amsmath, amssymb, amsthm, mathtools}\usepackage[most]{tcolorbox}\tcbuselibrary{theorems}\usepackage[hidelinks]{hyperref}\usepackage{cleveref}
% Theorem-like environments, all sharing the thm counter within a section.% Last argument is the label prefix; `label type` is the cleveref type.\newtcbtheorem[number within=section]{thm}{Theorem}% {colback=green!5, colframe=green!35!black, fonttitle=\bfseries, label type=theorem}{th}\newtcbtheorem[number within=section, use counter from=thm]{lem}{Lemma}% {colback=blue!5, colframe=black!35!black, fonttitle=\bfseries, label type=lemma}{lm}\newtcbtheorem[number within=section, use counter from=thm]{prop}{Proposition}% {colback=white!5, colframe=white!35!black, fonttitle=\bfseries, label type=proposition}{pr}\newtcbtheorem[number within=section, use counter from=thm]{cor}{Corollary}% {colback=blue!5, colframe=blue!35!black, fonttitle=\bfseries, label type=corollary}{co}\newtcbtheorem[number within=section, use counter from=thm]{defn}{Definition}% {colback=red!5, colframe=red!35!black, fonttitle=\bfseries, label type=definition}{df}\newtcbtheorem[number within=section, use counter from=thm]{example}{Example}% {colback=black!2, colframe=black!50, fonttitle=\bfseries, label type=example}{ex}
% titledBox has no display name of its own: the title argument is the whole% heading, set inline in bold.\newtcbtheorem[number within=section, use counter from=thm]{titledBox}{}% {label type=box, enhanced, sharp corners, colback=white, colframe=black, attach title to upper, before upper={\textbf{\tcbtitletext}\quad}, fonttitle=\bfseries, separator sign={\quad}}{box}
% The theorem snippets close proofs with a capitalised Proof.\newenvironment{Proof}{\begin{proof}}{\end{proof}}
\crefname{theorem}{theorem}{theorems}\crefname{definition}{definition}{definitions}\crefname{box}{box}{boxes}:defn group action then writes \begin{defn}{Group Action}{grpAction},
and \cref{df:grpAction} reads definition 1.1. The :exercise snippet
additionally wants the exercises package, which supplies the Exercise and
Answer environments it writes.
The figure snippet is the case that used to be a trap and is now not a
switch: FIG reads figure_folders and offers whichever of them exists
beside your document or one level up, so it fits either layout instead of
assuming this one.
<Space>es opens the snippet files, with the ones a plugin ships marked
read-only and a route to start your own for the same filetype. Reading how
one is built is the fastest way to write a different one.
Navigating by structure, including your own environments
Section titled “Navigating by structure, including your own environments”]g and [g jump between theorem environments, ]p and ]x between proofs
and examples. The names each one matches are a list rather than something
fixed, because the names in a document belong to whoever wrote it. Out of the
box ]g covers both spellings, so \begin{theorem} and \begin{thm} are
both found.
Adding a motion is an entry in the same table. The exercises environment
that :exercise writes has none by default, so:
-- lua/user/plugins/noethervim-tex.luareturn { { "Chiarandini/NoetherVim-tex", opts = { textobjects = { exercise = { envs = { "Exercise" }, next = "]e", prev = "[e", desc = "exercise" }, }, } },}Pick the keys to suit your own map rather than copying these two: ]e and
[e are line-swap keys in some configurations, and ]c and [c, which would
otherwise be the obvious pair for chapters, are Vim’s diff-mode change motions
and gitsigns’ hunk motions. That is why chapter navigation ships unbound and
is one table entry away:
textobjects = { chapter = { node = "chapter", next = "]c", prev = "[c", desc = "chapter" },}