VS Code as LaΤeΧ editor


LaTeX-Workshop is the de facto standard VS Code LaTeX editing extension. Originally I was not a fan because despite having many features it ended up being confusing when things went wrong. (Where WAS that syntax error?)

The new version is much better; you just need to make sure the little TeX sidebar is active and everything becomes apparent. Some things are still fiddly, but it provides nearly as good a LaTeX preview experience as, say, TeXShop, similar TeX integration and greatly superior text editing and integration with the rest of my workflow. As such, it what I do, because VS Code is better at editing non-LaTeX than TeXShop is and otherwise I would be switching constantly and never getting the keyboard shortcuts right.

VS Code supposedly can preview individual equations. This does not work for me on substantial documents. Only simple equations in trivial test documents have preview for me.

Snippets

It also has smart snippets with autocomplete.

Reproduced here for my offline reference are the most useful ones.

Prefix Environment name
BEQ equation
BSEQ equation*
BAL align
BSAL align*
BIT itemize
BEN enumerate
BSPL split
BCAS cases
BFR frame
BFI figure
Prefix Sectioning level
SPA part
SCH chapter
SSE section
SSS subsection
SS2 subsubsection
SPG paragraph
SSP subparagraph
Prefix Command
@( \left( $1 \right)
@{ \left\{ $1 \right\}
@[ \left[ $1 \right]
__ _{$1}
** ^{$1}
@8 \infty
@6 \partial
@/ \frac{$1}{$2}
@% \frac{$1}{$2}
@_ \bar{$1}
@I \int_{$1}^{$2}
@| |
@\ \setminus
@, \nonumber

Build recipes

Building using something modern or fancy? The default build workflow is some pdflatex+BibTeX system that is ok but pretty outdated. I would prefer a simpler more modern workflow that supports e.g. XeTeX. It doesn’t support XeTeX or latexmk out of the box. There are two ways to do set this up.

Firstly the old school way that the developers of LaTeX workshop seem to dislike, but which was far and away the easiest for me. Use the Latex Workshop-style TeX magic This looks like, e.g.

% !TEX program = latexmk
% !TEX options = -synctex=1 -file-line-error -halt-on-error -xelatex -outdir="%OUTDIR%" "%DOC%"

You put that at the start of the master document. Good. Also for this to actually be a useful build method, one would want to disable auto-clean (a.k.a. delete everything and rebuild from scratch all the time) so that latexmk can be smart about rebuilds. The price one pays for this is needing to manually clean up the detritus from time to time, which looks like the following setting:

"latex-workshop.latex.autoBuild.cleanAndRetry.enabled": false,

The preferred way of configuring the build that is difficult, verbose and and error-prone but supposedly ineffably better, is to make new “recipes”, and presumably keep the old ones too? The result is 😴.

    "latex-workshop.latex.recipes": [
        {
            "name": "latexmk 🔃",
            "tools": [
                "latexmk"
            ]
        },
        {
            "name": "xelatexmk 🔃",
            "tools": [
                "xelatexmk"
            ]
        },
        {
            "name": "platexmk 🔃",
            "tools": [
                "platexmk"
            ]
        },
        {
            "name": "pdflatex ➞ bibtex ➞ pdflatex`×2",
            "tools": [
                "pdflatex",
                "bibtex",
                "pdflatex",
                "pdflatex"
            ]
        },
        {
            "name": "xelatex ➞ biber ➞ xelatex",
            "tools": [
                "xelatex",
                "biber",
                "xelatex"
            ]
        }
    ],
    "latex-workshop.latex.tools": [
        {
            "name": "latexmk",
            "command": "latexmk",
            "args": [
                "-synctex=1",
                "-file-line-error",
                "-outdir=%OUTDIR%",
                "%DOC%"
            ],
            "env": {}
        },
        {
            "name": "platexmk",
            "command": "latexmk",
            "args": [
                "-synctex=1",
                "-file-line-error",
                "-pdf",
                "-outdir=%OUTDIR%",
                "%DOC%"
            ],
            "env": {}
        },
        {
            "name": "xelatexmk",
            "command": "latexmk",
            "args": [
                "-synctex=1",
                "-file-line-error",
                "-xelatex",
                "-outdir=%OUTDIR%",
                "-interaction=nonstopmode",
                "-halt-on-error",
                "%DOC%"
            ],
            "env": {}
        },
        {
            "name": "pdflatex",
            "command": "pdflatex",
            "args": [
                "-synctex=1",
                "-interaction=nonstopmode",
                "-halt-on-error",
                "-file-line-error",
                "%DOC%"
            ],
            "env": {}
        },
        {
            "name": "xelatex",
            "command": "xelatex",
            "args": [
                "-synctex=1",
                "-interaction=nonstopmode",
                "-halt-on-error",
                "-file-line-error",
                "%DOC%"
            ],
            "env": {}
        },
        {
            "name": "bibtex",
            "command": "bibtex",
            "args": [
                "%DOCFILE%"
            ],
            "env": {}
        },
        {
            "name": "biber",
            "command": "biber",
            "args": [
                "%DOCFILE%"
            ],
            "env": {}
        }
    ]

Having created a useful list of the recipes I need for different documents, I find myself in the tricky circumstance of needing a compact way to invoke them for different documents, and it is not really clear how to do this.

SyncTeX

SyncTeX makes working out what you are typing somewhat easier. The built-in implementation is OK, although I personally do not like everything being constrained to a single window by VS Code. (Although see VS Code’s dual window hack). External PDF viewers are not officially supported but more-or-less works for me and ideal for dual monitor setup.

Minuses:

  • Documentation is perfunctory.
  • Sync works in AFAICT one direction only — I can sync from VS Code to the PDF viewer, but not the reverse.

A good example of reverse engineering the config for kubuntu’s PDF viewer okular is given by Heiko/@miteron.

{
    "latex-workshop.view.pdf.viewer": "external",
    // @sync host=work-pc
    "latex-workshop.view.pdf.external.viewer.command": "okular",
    // @sync host=work-pc
    "latex-workshop.view.pdf.external.viewer.args": [
            "--unique",
            "%PDF%"
    ],
    // @sync host=work-pc
    "latex-workshop.view.pdf.external.synctex.command": "okular",
    // @sync host=work-pc
    "latex-workshop.view.pdf.external.synctex.args": [
        "--unique",
        "%PDF%#src:%LINE%%TEX%"
    ],
}

For GNOME doc viewer evince things are more complicated. Specifically, I needed a bridging script and special config:

{
    // @sync host=home-pc
    "latex-workshop.view.pdf.external.viewer.command": "evince2",
    // @sync host=home-pc
    "latex-workshop.view.pdf.external.viewer.args": [
        "%PDF%"
    ],
    // @sync host=home-pc
    "latex-workshop.view.pdf.external.synctex.command": "evince_forward_search",
    // @sync host=home-pc
    "latex-workshop.view.pdf.external.synctex.args": [
        "%PDF%",
        "%LINE%",
        "%TEX%"
    ],
}

Gotchas

Gotcha: If there are weird “provider errors” in the vscode exthost log, the problem might be that latexindent must be installed.

tlmgr install latexindent

Spell checking

Contra my normal VS Code spell checking advice, SpellRight is unsupported.

If I persist in using spellright, the following exclusions make things tidier:

    "spellright.ignoreRegExpsByClass": {
        "latex": [
            "/\\\\begin\\\\{{equation,align}\\\\}(.*?)\\\\end\\\\{{equation,align}\\\\}/mg",
            "/\\\\{autoref,autocites?,cites?}\\\\{(.*?)\\\\}/g",
        ],
    },

But probably better is to disable spellright for .tex files in favour of cspell instead. Supposedly LanguageTool is good for more diverse languages, but I have not tried it.