基础概念

LaTeX WorkShop 插件的编译逻辑分为两层:第一层为 recipe,第二层为 tool,具有如下特点:

  • 一个 recipe 由若干个 tool 组成;
  • 在配置文件中可以提供多个 recipe 和多个 tool;
  • 直接点击编译按钮会自动选择第一个(或上一次使用的)recipe 来执行编译;
  • 一个 tool 通常包括一个单独的编译命令加上若干参数,例如 xelatex,lualatex 和 pdflatex 等,还可能是处理参考文献需要的 bibtex 或 biber;
  • 一个 recipe 会依次执行它所包含的 tool,例如:
1
xelatex -> bibtex -> xelatex -> xelatex

LaTeX WorkShop 插件其实只是对原本在命令行操作的 LaTeX 编译命令以及格式化命令进行了封装。
此外,LaTeX WorkShop 插件会在命令中传递特殊变量%DOC%%DOCFILE%代表当前文件,第一个是文件名(含完整路径,不含后缀),第二个则是文件名(不含后缀),略有区别。

由于插件也只是调用了pdflatexlatexmk等编译命令,如果环境中存在.latexmkrc等配置文件,可能会对插件的编译行为造成影响。
但是因为插件的选项是通过命令行参数传递的,优先级更高,影响应该不大。

LaTeX WorkShop 插件并不推荐使用例如% !TEX program = xelatex的魔法命令来指定编译命令,但是考虑到向后兼容性会为其提供有限的支持,需要在设置中开启相关选项。

编译配置(一)

第一种配置是使用 latexmk 分别调用 xelatex,lualatex 和 pdflatex,配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
"latex-workshop.latex.tools": [
{
"name": "xelatexmk",
"command": "latexmk",
"args": [
"-pdfxe",
"-file-line-error",
"-halt-on-error",
"-interaction=nonstopmode",
"-synctex=1",
"-auxdir=%DIR%/.aux",
"-outdir=%OUTDIR%",
"%DOC%"
]
},
{
"name": "lualatexmk",
"command": "latexmk",
"args": [
"-pdflua",
"-file-line-error",
"-halt-on-error",
"-interaction=nonstopmode",
"-synctex=1",
"-outdir=%OUTDIR%",
"-auxdir=%DIR%/.aux",
"%DOC%"
]
},
{
"name": "latexmk",
"command": "latexmk",
"args": [
"-pdf",
"-file-line-error",
"-halt-on-error",
"-interaction=nonstopmode",
"-synctex=1",
"-outdir=%OUTDIR%",
"-auxdir=%DIR%/.aux",
"%DOC%"
]
}
],
"latex-workshop.latex.recipes": [
{
"name": "XeLaTeXmk",
"tools": [
"xelatexmk"
]
},
{
"name": "LuaLaTeXmk",
"tools": [
"lualatexmk"
]
},
{
"name": "LaTeXmk",
"tools": [
"latexmk"
]
}
],
"latex-workshop.latex.clean.args": [
"-outdir=%OUTDIR%",
"-c",
"%TEX%",
"-auxdir=%DIR%/.aux"
],

这里使用.aux/辅助目录存储编译过程中生成的各种杂项文件,在清理时也需要指定。

在使用某些宏包时,可能需要加上 "-shell-escape" 选项才能成功编译。

编译配置(二)

第二种配置是不使用 latexmk,直接使用 xelatex 等编译命令,
此时由于参考文献(bibtex 或 biber)和双向引用等原因,需要配置 recipe 进行多次编译,配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
"latex-workshop.latex.tools": [
{
"name": "xelatex",
"command": "xelatex",
"args": [
"-file-line-error",
"-halt-on-error",
"-interaction=nonstopmode",
"-synctex=1",
"%DOC%"
]
},
{
"name": "pdflatex",
"command": "pdflatex",
"args": [
"-file-line-error",
"-halt-on-error",
"-interaction=nonstopmode",
"-synctex=1",
"%DOC%"
]
},
{
"name": "bibtex",
"command": "bibtex",
"args": [
"%DOCFILE%"
]
}
],
"latex-workshop.latex.recipes": [
{
"name": "xelatex->xelatex",
"tools": [
"xelatex",
"xelatex"
]
},
{
"name": "xelatex",
"tools": [
"xelatex"
]
},
{
"name": "pdflatex",
"tools": [
"pdflatex"
]
},
{
"name": "BibTeX",
"tools": [
"bibtex"
]
},
{
"name": "xe->bib->xe*2",
"tools": [
"xelatex",
"bibtex",
"xelatex",
"xelatex"
]
},
{
"name": "pdf->bib->pdf*2",
"tools": [
"pdflatex",
"bibtex",
"pdflatex",
"pdflatex"
]
}
],

这组配置并没有使用.aux/子目录存放辅助文件,因为这些编译命令虽然支持指定输出目录,但是无法自动创建目录。

VSCode 与 SumatraPDF 双向跳转

除了 tools 和 recipes 这两个主要的配置,还有一个重要的功能是双向跳转,
通过 VSCode 内部预览 PDF 时,可以直接进行双向跳转,无需任何配置。
默认的双向跳转操作是:

  • ctrl + 单击 PDF 相应位置,从 PDF 跳转到源文件对应位置
  • ctrl+alt+j,从源文件跳转到 PDF 对应位置

由于内部预览无法拆分窗口,在外部使用 SumatraPDF 进行 PDF 预览可能更加方便,下面考虑配置支持 VSCode 和 SumatraPDF 之间的双向跳转。

VSCode 也可以把 PDF 预览页面单独拆开了,因此也可以直接用 VSCode 预览,但是有时内置预览页面的清晰度实在太差了,例如截图时最好还是使用外部的 PDF 浏览器。

参考 LaTeX-Workshop 的 wiki
以及 SumatraPDF 的相关讨论

对于 VSCode 的配置如下:

1
2
3
4
5
6
7
8
9
10
11
"latex-workshop.view.pdf.viewer": "external",
"latex-workshop.view.pdf.external.synctex.command": "<path-to-SumatraPDF>/SumatraPDF.exe",
"latex-workshop.view.pdf.external.synctex.args": [
"-forward-search",
"%TEX%",
"%LINE%",
"-reuse-instance",
"-inverse-search",
"\"<path-to-vscode>/Code.exe\" \"<path-to-vscode>/resources/app/out/cli.js\" --ms-enable-electron-run-as-node -r -g \"%f:%l\"",
"%PDF%"
],

对于 SumatraPDF 的高级选项配置为:

1
2
InverseSearchCmdLine = "<path-to-vscode>/Code.exe" "<path-to-vscode>/resources/app/out/cli.js" --ms-enable-electron-run-as-node  -r -g "%f:%l"
EnableTeXEnhancements = true

如果打开 SumatraPDF 的设置面板找不到Set inverse command-line这个项,
需要在高级选项中首先打开 Tex 增强选项:EnableTeXEnhancements = true
Set inverse command-line在配置文件中实际上是 InverseSearchCmdLine = ... 这一项。

注意配置中的参数顺序,如果跳转中断在 cli.js 文件,可以尝试将 --ms-enable-electron-run-as-node 移动到 cli.js 之前。
如果 VS Code 始终无法打开 SumatraPDF,可以试着把 SumatraPDF.exe 的路径添加到环境变量 PATH。

测试发现,上面的这个配置仍然只能在受限的条件下正常工作,
如果在 VSCode 中通过源文件跳转的方式启动 SumatraPDF,很可能对于 PDF 文件无法进行跳转回到源文件,
这个问题可能和 SumatraPDF 的启动方式有关,通过 VSCode 启动时继承了 VSCode 的某些上下文,使得双向跳转失败。

最好的使用方式是:单独使用 vscode 打开 tex 文件(或者对应的文件夹),使用 SumatraPDF 打开 PDF 文件,然后在两者之间可以进行双向跳转。

VSCode 补充配置

除了最主要的编译部分和双向跳转,还有一些可能有用的,与 LaTeX 相关的辅助性配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
"[latex]": {
"editor.defaultFormatter": "James-Yu.latex-workshop",
"editor.wordWrap": "on",
"editor.unicodeHighlight.allowedLocales": {
"zh-hans": true,
"zh-hant": true
},
"editor.formatOnSave": false,
},
"latex-workshop.showContextMenu": true,
"latex-workshop.view.pdf.internal.synctex.keybinding": "double-click",
"latex-workshop.latex.autoBuild.cleanAndRetry.enabled": false,
"latex-workshop.latex.recipe.default": "lastUsed",
"latex-workshop.message.error.show": false,
"latex-workshop.message.warning.show": false,
"latex-workshop.latex.build.rootfileInStatus": true,
"latex-workshop.latex.build.clearLog.everyRecipeStep.enabled": false,
"latex-workshop.bibtex-fields.order": [
"author",
"title",
"journal",
"year"
],
"latex-workshop.formatting.latex": "latexindent",
"latex-workshop.latex.autoClean.run": "onFailed",
"latex-workshop.bibtex-fields.sort.enabled": true,
"latex-workshop.intellisense.citation.backend": "biblatex",
"latex-workshop.bibtex-format.sort.enabled": true,

以及定义快捷键,这里只定义了 F5 编译

1
2
3
4
5
6
7
{
{
"key": "f5",
"command": "latex-workshop.build",
"when": "editorLangId =~ /^latex$|^latex-expl3$|^doctex$|^rsweave$|^jlweave$|^pweave$/"
}
}

还有常用环境的 snippets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
{
"latex-table": {
"prefix": "\\table",
"body": [
"\\begin{table}[htbp]",
" \\centering",
" % \\caption{XXX} % \\label{tab:XXX}",
" \\begin{tabular}{c|ccc}",
" \\hline",
" Error & 5.00e-1 & 2.50e-1 & 1.25e-1 \\\\\\",
" \\hline",
" Order & - & 1.00 & 1.00 \\\\\\",
" \\hline",
" \\end{tabular}",
"\\end{table}"
]
},
"latex-figure": {
"prefix": "\\figure",
"body": [
"\\begin{figure}[htbp]",
" \\centering",
" \\includegraphics[width=0.8\\textwidth]{$1}",
" % \\caption{XXX} % \\label{fig:XXX}",
"\\end{figure}"
]
},
"latex-figures": {
"prefix": "\\figures",
"body": [
"\\begin{figure}[htbp]",
" \\centering",
" \\begin{subfigure}[c]{0.47\\textwidth}",
" \\centering",
" \\includegraphics[width=\\textwidth]{$1}",
" \\end{subfigure}",
" \\begin{subfigure}[c]{0.47\\textwidth}",
" \\centering",
" \\includegraphics[width=\\textwidth]{$2}",
" \\end{subfigure}",
" % \\caption{XXX} % \\label{fig:XXX}",
"\\end{figure}",
""
]
},
"beamer-columns": {
"prefix": "\\columns",
"body": [
"\\begin{columns}[T]",
" \\begin{column}{0.4\\textwidth}",
"",
" \\end{column}",
"",
" \\begin{column}{0.6\\textwidth}",
"",
" \\end{column}",
"\\end{columns}",
"$0"
],
}
}

智能补全

对于 LaTeX 来说,智能补全还是非常有必要的,LaTeX workshop 插件在这方面提供了很多支持,可以查看对应的 官方文档

首先,对于一些最常见的公式环境,可以使用BXY(或BSXY代表加星号版本,不区分大小写)的 prefix 来自动补全完整的环境,这里的XY取自公式环境名称的缩写,如下表:

prefix name
BEQ, BSEQ equation, equation*
BAL, BSAL align, align*
BGA, BSGA gather, gather*

除了公式,常见的列表环境和浮动体环境也是支持的:

prefix name
BIT itemize
BEN enumerate

对于数学公式中的常用字体也有支持(可以选中需要改变字体的片段后再输入),如下表

prefix command
MRM \mathrm{${1}}
MSF \mathsf{${1}}
MBF \mathbf{${1}}
MBB \mathbb{${1}}
MCA \mathcal{${1}}
MIT \mathit{${1}}
MTT \mathtt{${1}}

实际上插件还提供了一组快捷键对应上述命令,例如 ctrl+M,ctrl+B 对应 \mathbf{},完整列表可以在快捷键绑定中查看。

插件还对一些常见的数学元素进行了自动替换,例如

prefix command
__ _{$1}
** ^{$1}
... \dots

对于很多的数学符号,插件还提供了@X方式的快捷输入方式,其中X与数学符号的对应关系很有意思,例如

prefix command
@%,@/ \frac{}{}
@2 \sqrt{}
@6 \partial
@8 \infty
@( \left( \right)
@[ \left[ \right]
@{ \left\{ \right\}
@| \left| \right|
@a \alpha
@b \beta
@d \delta
@D \Delta
@ve \varepsilon
@vf \varphi
@vq \vartheta

这些智能补全只会在编辑 LaTeX 文档时触发,对于其它文档的编辑不会造成干扰。

补充

.gitignore

当前 LaTeX 项目使用的。gitignore 如下

1
2
3
4
5
6
.aux/

*.pdf
*.synctex.gz
*.synctex.gz.sum.synctex
indent.log

含义为忽略.aux/文件夹、所有 PDF 文件以及相关的辅助文件。注意如果需要插入 PDF 格式的图片,还需要加上其它规则将其包含,例如

1
!figure/*.pdf

主文件查找

对于多文件项目,在非主文件中点击编译按钮,LaTeX Workshop 插件需要找到对应的主文件,这涉及到对主文件的查找策略(wiki of LaTeX-Workshop),

当打开新文档、更改活动编辑器或执行任何 LaTeX Workshop 命令时,LaTeX Workshop 会自动进行主文件查找。

主文件的查找规则:

  1. 使用魔法注释指定,例如 % !TEX root = ../main.tex,但是需要修改插件的对应选项,因为默认不会使用这些魔法注释;
  2. 如果当前文件包含 \documentclass[...]{...},就会被视作主文件;
  3. 遍历当前 VSCode 打开的文件夹中的所有 .tex 文件。第一个包含 \documentclass[...]{...} 且包含当前编辑器中的文件的 .tex 文件将被设置为根文件。

主文件必须位于 VSCode 打开的当前文件夹中。可以通过设置把查找主文件使用的指示器从 \documentclass[...]{...} 改成 \begin{document}

因此,如果插件找错了主文件(例如当前文件实际被多个主文件共用,当然这种做法并不建议),最简单的做法就是打开一下正确的主文件。

其他方案

目前主流的 LaTeX 解决方案包括:

  • local:
    • VSCode + LaTeX Workshop
    • TeXstudio
  • remote:
    • Overleaf

下面提供一种比较折腾的方案,纯 Linux 风格,绕过 VSCode,适合基于 vim/nvim 在无图形界面的远程服务器使用,包括如下步骤:

  • ssh 登陆远程服务器
  • Tmux 开启如下创建
    • vim/nvim 编辑文档
    • latexmk 持续编译:latexmk -xelatex -pvc -interaction=nonstopmode main.tex
    • 在服务器上持续开启 http 服务器,监听本地 8000 端口:python3 -m http.server 8000
  • 在本地持续开启 ssh 端口转发:ssh -N -L 8000:127.0.0.1:8000 user@server
  • 在浏览器打开 http://127.0.0.1:8000,打开 PDF 文档(文档更新后还是需要手动刷新)