Vim / Neovim (vimtex)

要在 Vim 或 Neovim 中编写 LaTeX,事实上的标准是 Karl Yngve Lervåg 的 vimtex 插件。它提供了编译、目录和标签之间的导航、在环境和命令上操作的动作和文本对象、完成以及使用 PDF 查看器的双向跳转 - 所有这些都无需离开编辑器。本页逐步介绍通过 SyncTeX 安装和配置 vimtex、补全和反向搜索。

vimtex 是什么

Vim 和 Neovim 都不排版 TeX 本身;实际编译是由您安装的发行版完成的 - TeX Live (或 MiKTeX / MacTeX)。 vimtex 是桥梁:.tex 的文件类型和语法插件。当它检测到 tex 文件类型时,它会自动启用构建命令、语法突出显示、完成函数 (omnifunc)、目录视图、\ref/\cite 的跳转到定义以及环境、分隔符和命令的文本对象。其核心无需外部依赖即可工作;完成和片段与您选择的插件配对。

有三个先决条件。首先,TeX 命令必须可以在你的 PATH 上访问 — vimtex 将 latexmk 和朋友作为子进程生成,所以如果 latexmk --version 在终端中运行,你几乎肯定没问题。其次,根据当前 vimtex 的要求,使用 Vim 9.1 或更高版本,或者 Neovim 0.10 或更高版本。第三,Vim 必须启用文件类型检测和语法。您的 vimrc/init.vim 需要这两行(大多数配置已经有它们):

terminal
filetype plugin indent on
syntax enable

设置

您通常使用插件管理器安装 vimtex — vim-plug 用于 Vim,lazy.nvim 用于 Neovim,是常见的选择。一个重要的警告:不要延迟加载 vimtex。 正如项目文档明确指出的那样,在检测到文件类型后加载它会破坏下面描述的反向搜索。经验法则是lazy.nvim中的lazy = falsepacker中的显式加载,并且vim-plug中没有基于for的惰性规范。

下面是最小的 Vim(vim-plug)设置。声明插件,启用文件类型/语法,然后使用 g:vimtex_view_method 选择 PDF 查看器,然后它就开始工作。由于 maplocalleader (本地领导者)是 vimtex 命令的入口点,因此显式设置它会让人放心(默认为反斜杠 \)。

terminal
call plug#begin()
Plug 'lervag/vimtex'
call plug#end()

filetype plugin indent on
syntax enable

let maplocalleader = ' '          " use Space as <localleader>
let g:vimtex_view_method = 'zathura'   " 'skim' on macOS, 'sumatrapdf' on Windows
let g:vimtex_compiler_method = 'latexmk'

在 Neovim (lazy.nvim) 中,您可以在 Lua 中编写相同的内容。在 init 中设置变量可以使它们在插件主体加载之前生效。不要忘记 lazy = false

terminal
return {
  "lervag/vimtex",
  lazy = false,   -- do NOT lazy-load: it breaks inverse search
  init = function()
    vim.g.vimtex_view_method = "zathura"   -- "skim" / "sumatrapdf"
    vim.g.vimtex_compiler_method = "latexmk"
  end,
}

打开 .tex 文件,关闭 <localleader>l 的命令将变得可用。首先值得记住的四个是:

行动默认映射它的作用
compile\ll通过 latexmk 启动/停止连续编译;每次保存时自动重建
view\lv打开 PDF 并向前搜索(源 → PDF)到光标
clean\lc清理辅助文件(.aux.log、...)
stop\lk停止正在运行的编译(\lK停止全部)

实际上:按 \ll 一次,latexmk 保持常驻,此后每次保存时都会刷新 PDF。使用 \lv 显示 PDF 中的当前位置,使用 \le 将错误放入快速修复列表中并跳转到有问题的行。至于输入它们:如果本地领导者仍然是 \,则按 \ll;如果将其切换到空格键,则按 <Space>ll,依此类推。

默认编译器是 latexmk,vimtex 会为您传递 -verbose -file-line-error -synctex=1 -interaction=nonstopmode 等选项。特别是,-synctex=1 默认情况下处于打开状态,因此无需任何额外设置即可发出下面的正向和反向搜索所需的同步数据。要使用 Tectonic,请设置 g:vimtex_compiler_method = "tectonic"latexrunarara 也可用。

对于日语,最干净的方法是 .latexmkrc 选择引擎。 vimtex 的 latexmk 后端从 .latexmkrc 读取 $pdf_mode 来决定处理路径(直接 1 = pdfLaTeX,3 = 构建一个 DVI 然后用 dvipdfmx 转换,4 = LuaLaTeX)。请注意,像 -pdf/-lualatex 这样的引擎标志不应添加到 vimtex 的 options 中;在 .latexmkrc 或通过 g:vimtex_compiler_latexmk_engines 中指定它们。这是一个 upLaTeX + dvipdfmx 设置 - 日本科学论文的经典组合:

latex
$latex = 'uplatex -synctex=1 -interaction=nonstopmode -file-line-error %O %S';
$bibtex = 'upbibtex %O %B';
$biber = 'biber --bblencoding=utf8 -u -U --output_safechars %O %S';
$makeindex = 'upmendex %O -o %D %S';
$dvipdf = 'dvipdfmx %O -o %D %S';
$pdf_mode = 3;
$max_repeat = 5;

关键点:将uplatex赋值给$latex,将dvipdfmx赋值给$dvipdf,然后设置$pdf_mode = 3选择“构建一个DVI,然后用dvipdfmx转换为PDF”路径。将 -synctex=1 传递到 $latex 也会将 SyncTeX 数据传递到 PDF,甚至通过 DVI。如果您使用 LuaLaTeX 排版日语,则根本不需要 .latexmkrc — 只需让 g:vimtex_compiler_latexmk_engines 选择 lualatex,或编写 $pdf_mode = 4(假设 luatexja/ltjsclasses)。

完成

vimtex 通过 Vim 的标准补全函数 (omnifunc) 提供上下文感知候选者。在 tex 缓冲区中,它自动设置 omnifunc=vimtex#complete#omnifunc,因此只要 g:vimtex_complete_enabled(默认 1)打开,就可以使用。它主要完成的事情:

  • \cite{...} — 读取 .bib 文件和 \bibitems 以完成引用键。
  • \ref{...} / \eqref{...} — 收集文档中的 \label 以完成参考。
  • 命令和环境\commands 和 \begin{...} 环境名称根据您加载的包而定。
  • 文件名 — 图形、\input/\include\includepdf 等的路径。
  • 术语表和包名称glossaries 条目以及基于可用 .sty/.cls 文件的包名称。

在普通 Vim 中,在插入模式下按 Ctrl-X Ctrl-O (全方位完成触发器),就会出现候选者。例如,输入 \cite{,然后输入 Ctrl-X Ctrl-O,就会列出您的引文关键字。不过,每次按下这个按钮都很乏味,因此在实践中,人们将omnifunc连接到完成引擎中,该引擎会在您键入时自动弹出建议。

对于现代 Neovim 设置,nvim-cmp 是通常的选择。直接添加它的 omni 源和来自 vimtex 的omnifunc 流的候选:

terminal
local cmp = require("cmp")
cmp.setup({
  sources = cmp.config.sources({
    { name = "omni" },   -- pulls vimtex completion via omnifunc
  }),
})

如果您更喜欢 coc.nvim (可在 Vim 和 Neovim 中使用),请安装 coc-omni 扩展并在其全源中包含 tex 文件类型。在较旧的 deoplete 设置上,将其与 deoplete-vimtex 配对。在每种情况下,分工都是相同的:vimtex 生成候选项,完成引擎决定何时以及如何呈现它们。

SyncTeX记录了源代码行和PDF中位置的对应关系。它支持正向搜索(从编辑位置跳转到 PDF)和反向搜索(从 PDF 中的某个位置回到源代码行)。 vimtex 为您处理向前搜索,因此您只需按 \lv 即可。反向搜索是棘手的一半:它需要 PDF 查看器回调到 Vim。关键部分是 vimtex 提供的 :VimtexInverseSearch 命令。

这是机制。使用 SyncTeX,查看器可以计算出单击对应的源代码行和文件,然后运行 ​​shell 命令以该行号和文件作为参数来调用编辑器。在该命令中,您调用 VimtexInverseSearch <line> <file>vimtex#view#inverse_search 会找到正在运行的 Vim/Neovim 并打开该位置。 vimtex 为您处理多个实例和服务器名称,因此您通常不需要自己传递 --servername 。每个查看器的占位符名称有所不同:在 zathura 中,行和文件为 %{line}/%{input},在 Skim 中为 %line/%file,在 SumatraPDF 中为 %l/%f

Linux — zathura。 与 vimtex 的最久经考验的搭配。使用 g:vimtex_view_method = "zathura",您可以通过 \lv 进行正向搜索,并且在大多数情况下,基本上无需配置的反向搜索:vimtex 使用 -x 启动 zathura,并为您提供调用 VimtexInverseSearch 的编辑器命令(在没有 xdotool 的系统上,使用 zathura_simple)。在 zathura 中,Ctrl-click** 移动到匹配的源代码行。

如果您想自己配置它,或者也同步该列,请将其直接放入 zathurarc 中。 zathura 的占位符是 %{line}%{input}(注意:不是 %l/%f);通过列感知构建,您还可以使用 %{column}

terminal
set synctex true
set synctex-editor-command "nvim --headless -c 'VimtexInverseSearch %{line} %{input}'"

macOS — Skim。 设置 g:vimtex_view_method = "skim"。在 Skim 一侧,打开 首选项 → 同步,将预设设置为“自定义” 并注册命令和参数。 %line%file 被 Skim 替换,行号和文件:

terminal
Command:   nvim
Arguments: --headless -c "VimtexInverseSearch %line '%file'"

现在 Skim 中的 Cmd-Shift-click 执行反向搜索:Skim 在后台启动 Neovim,并且 VimtexInverseSearch 将位置中继到您的现有实例。

Windows — SumatraPDF。 设置 g:vimtex_view_method = "sumatrapdf"。在 SumatraPDF 中,在 Settings → 选项 → “设置反向搜索命令行”下, 注册如下命令(%l%f 是行号和文件):

terminal
cmd /c start /min "" nvim --headless -c "VimtexInverseSearch %l '%f'"

在 SumatraPDF 中,双击会触发反向搜索。对于普通 gVim,将 nvim --headless 部分替换为 vim -v --not-a-term -T dumb (gVim 和 Windows 上的 Vim 自动启动服务器)。

最后一个警告。 在终端 (Linux/macOS) 中运行的 Vim 默认情况下不会启动服务器, 因此 Vim 用户应将以下内容添加到 vimrc 以便反向搜索回调可访问(Neovim 不需要这些):

terminal
if empty(v:servername) && exists('*remote_startserver')
  call remote_startserver('VIM')
endif

在每个 OS 和查看器上,先决条件是文档是 使用 -synctex=1 编译的 - 但如上所述,这已经是 vimtex 默认 latexmk 选项的一部分,因此您很少需要考虑它。

vimtex 编辑循环

vimtex 不是一个在写作之前必须掌握的工具。首先锁定四步:\ll开始连续编译,保存,用\lv跳转到PDF,用\le只检查错误。添加方程或图形时,通过标签补全插入 \ref{...},并在分割章节之前决定如何检测根文件。如果首先解决了这个 vimtex 编辑循环,则可以稍后添加补全引擎和片段,而不会干扰主线的写作。

在移动到多文件文档之前,使带有 \documentclass 的文件成为项目的中心,并确认从子文件编译仍然构建相同的 PDF。如果章节中的 \ll 产生不同的目标,请在添加更多片段或完成层之前修复根检测和 .latexmkrc