Vim / Neovim(vimtex)

Vim や Neovim で LaTeX を書く人の事実上の標準が、Karl Yngve Lervåg 氏による vimtex プラグインです。コンパイル、目次・ラベル間の移動、環境やコマンドを単位とする操作(モーション/テキストオブジェクト)、補完、そして PDF ビューアとの相互ジャンプまでを、エディタを離れずに行えます。このページでは vimtex の導入と設定、補完、そして SyncTeX による逆検索を順に見ていきます。

vimtex とは

Vim も Neovim も、それ自体は TeX を組版しません。実際にコンパイルするのは、PC に入れた TeX Live(や MiKTeX・MacTeX)です。vimtex は、その橋渡しをする「.tex 用のファイルタイプ&シンタックスプラグイン」。tex ファイルタイプを検出すると、ビルドの起動、シンタックスハイライト、補完関数(omnifunc)、目次表示、\ref\cite のジャンプ、環境・括弧・コマンドのテキストオブジェクトなどを 自動的に有効化 します。コア部分は外部依存なしで動き、補完やスニペットは好みのプラグインと組み合わせます。

前提は二つだけです。第一に、TeX のコマンドが PATH から呼べる こと。vimtex は内部で latexmk などを子プロセスとして起動するため、ターミナルで latexmk --version が動けばまず問題ありません。第二に、Vim 側で ファイルタイプとシンタックスを有効化 していること。vimrcinit.vim に次の二行が要ります(多くの設定では既に入っています)。

vimrc
filetype plugin indent on
syntax enable

導入と設定

vimtex はプラグインマネージャで入れるのが普通です。Vim なら vim-plug、Neovim なら lazy.nvim が広く使われます。ここで重要な注意がひとつ。vimtex を遅延読み込み(lazy-load)してはいけません。 公式が明記している通り、ファイルタイプの検出より後に読み込むと、後述の逆検索が壊れます。lazy.nvim では lazy = falsepacker では明示ロード、vim-plug では for による遅延指定を付けない、が鉄則です。

Vim(vim-plug)の最小設定は次の通りです。プラグインを宣言し、ファイルタイプ/シンタックスを有効化し、g:vimtex_view_methodPDF ビューア を選ぶだけで動き始めます。maplocalleader(ローカルリーダー)は vimtex の操作の起点になるので、明示しておくと安心です(既定はバックスラッシュ \)。

vimrc
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 を忘れないこと。

lua
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 を開くと、ローカルリーダー l を起点とした操作が使えます。覚えておきたいのは次の四つです。

操作既定マッピング内容
compile\lllatexmk による 連続コンパイル を開始/停止。保存のたびに自動で組み直す
view\lvPDF を表示し、カーソル位置へ 前方検索(ソース → PDF)
clean\lc中間ファイル(.aux.log など)を掃除
stop\lk実行中のコンパイルを停止(\lK で全停止)

実際の流れはこうです。\ll を一度押すと latexmk が常駐し、以降は保存するたびに PDF が更新されます。\lv で該当箇所を PDF に表示し、エラーは \le で quickfix リストに出して該当行へ飛べます。\llll を含め、ローカルリーダーが \ のままなら \ll、Space に変えていれば <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 で PDF 化、4=LuaLaTeX)。注意点として、エンジン指定の -pdf-lualatex などは vimtex の options に足さず、.latexmkrcg:vimtex_compiler_latexmk_engines 側で指定します。次は upLaTeX + dvipdfmx(理系の日本語論文で定番)の例です。

.latexmkrc
$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;

要点は、$latexuplatex$dvipdfdvipdfmx を割り当て、$pdf_mode = 3 で「DVI を作ってから dvipdfmx で PDF 化する」経路を選ぶことです。$latex 側にも -synctex=1 を渡しておくと、DVI 経由でも SyncTeX 情報が PDF まで引き継がれます。LuaLaTeX で和文を組むなら .latexmkrc は不要で、g:vimtex_compiler_latexmk_engineslualatex を選ばせるか、$pdf_mode = 4 を書くだけで済みます(luatexjaltjsclasses 系を使う前提)。

補完

vimtex は文脈に応じた補完候補を、Vim 標準の補完関数(omnifunc)として提供します。tex バッファでは omnifunc=vimtex#complete#omnifunc自動で設定 されるので、g:vimtex_complete_enabled(既定 1)が有効なら、すぐに使えます。補える主なものは次の通りです。

  • \cite{...}** — .bib\bibitem を読み、引用キーを補完。
  • \ref{...} / \eqref{...}** — 文書中の \label を集めて参照を補完。
  • コマンド・環境名 — 読み込んだパッケージに応じた \command\begin{...} の環境名。
  • ファイル名 — 図・\input\include\includepdf などのパス。
  • 用語集・パッケージ名glossaries の項目や、利用可能な .sty.cls に基づくパッケージ名。

素の Vim では、挿入モードで **Ctrl-X Ctrl-O**(omni 補完の呼び出し)を押せば候補が出ます。たとえば \cite{ まで打って Ctrl-X Ctrl-O とすると、文献キーの一覧が並びます。ただ毎回このキーを押すのは煩雑なので、実際には 補完エンジン に omnifunc を橋渡しさせ、入力に応じて自動でポップアップさせる構成が一般的です。

Neovim でモダンに組むなら nvim-cmp が定番です。omni ソースを足すだけで、vimtex の omnifunc 由来の候補がそのまま流れ込みます。

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

Vim と Neovim のどちらでも使える coc.nvim を選ぶ場合は、coc-omni 拡張を入れて tex ファイルタイプを omni ソースに含めます。古い deoplete を使っているなら deoplete-vimtex を併用します。いずれの場合も、補完候補の中身を作るのは vimtex 側、それを「いつどう見せるか」を受け持つのが補完エンジン側、という分担です。

SyncTeX は、ソースの行と PDF 上の位置との対応を記録する仕組みです。これにより、編集位置から PDF へ飛ぶ 前方検索 と、PDF 上の場所からソース行へ戻る 逆検索 ができます。前方検索は vimtex が面倒を見てくれるので、\lv を押すだけ。問題は逆検索で、こちらは PDF ビューア側から Vim を呼び戻す 設定が要ります。鍵になるのが、vimtex が用意する :VimtexInverseSearch コマンドです。

仕組みはこうです。ビューアは SyncTeX で「クリックされた位置はソースの何行目・どのファイルか」を割り出し、その行番号とファイルを引数に、エディタを呼び出すシェルコマンドを実行します。そのコマンドの中で VimtexInverseSearch <行> <ファイル> を呼べば、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 は zathura を -x 付きで起動し、VimtexInverseSearch を呼ぶエディタコマンドを自動で渡すためです(xdotool が無い環境では zathura_simple を使います)。zathura 上で **Ctrl + 左クリック** すると、対応するソース行へ移動します。

自分で設定したい場合や、列番号まで同期したい場合は、zathurarc に直接書きます。zathura のプレースホルダは %{line}%{input}%l%f ではない点に注意)。列に対応した実装なら %{column} も使えます。

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

macOS — Skim。 g:vimtex_view_method = "skim" にします。Skim 側は、環境設定の 「Sync」タブ → プリセットを「カスタム」 にし、コマンドとオプションを登録します。%line%file は Skim が行番号とファイルに置換します。

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

これで Skim 上の **Cmd + Shift + クリック** が逆検索になります。Skim はバックグラウンドで Neovim を起動し、VimtexInverseSearch が既存インスタンスへ該当箇所を伝えます。

Windows — SumatraPDF。 g:vimtex_view_method = "sumatrapdf" にします。SumatraPDF の 設定 → オプション → 「逆順検索のコマンドライン」 に、次のようなコマンドを登録します(%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 はサーバーを自動で起動します)。

最後にひとつ注意。端末で動かす Vim(Linux/macOS)は、既定ではサーバーを起動しません。 そのため Vim 利用者は vimrc に次を足し、逆検索の呼び戻しを受けられるようにします(Neovim はこの設定は不要です)。

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

なお、いずれの OS・ビューアでも前提として **-synctex=1 でコンパイルしてある** ことが必要ですが、前述の通りこれは vimtex の latexmk 既定オプションに含まれているため、通常は意識せずに済みます。