Vim で LaTeX を書く人は、LaTeX のために Vim を選んだわけではありません。すでに Vim か Neovim に住んでいて、LaTeX のほうに来てもらいたいだけです。素の Vim も .tex が何かは知っていて、構文ファイルとファイルタイププラグインを同梱しています——ただしその ftplugin/tex.vim の先頭には、いまも Last Change: Wed 19 Apr 2006 と書かれたままです。コンパイルも、PDF の表示も、\ref の補完も、節の一覧もそこにはありません。それを全部持ってくるのが Karl Yngve Lervåg 氏の vimtex です。このページでは、素の Vim との差、\ll の常駐コンパイル、環境や命令を「文法」として編集するテキストオブジェクト、そして Vim と Neovim で本当に違う部分を扱います。
素の Vim が LaTeX について知っていること
素の Vim が .tex について知っているのは、色付けと三つの小技だけです。[d で \newcommand や \def の定義へ飛べること、gf や [i が \include{...} の中身をファイル名として解釈すること、そして matchit を有効にすれば % が \begin{...} と \end{...} を行き来すること。実装は ftplugin/tex.vim の 40 行ほどに収まっていて、Benji Fisher 氏の署名と Version: 1.4 / Last Change: Wed 19 Apr 2006 という日付が付いたまま今日の Vim 9.1 に同梱されています。構文ファイル syntax/tex.vim のほうは Version 121 まで育っていますが、その冒頭には「このランタイムファイルは新しいメンテナを探しています」と書かれています。
もう一つ、素の Vim にはコンパイラ定義 compiler/tex.vim が入っています。:compiler tex を実行すると makeprg が latex -interaction=nonstopmode になり、errorformat に LaTeX のログを読む長大なパターンが設定されるので、:make だけで quickfix リストにエラーが並びます。つまり素の Vim でも「書いて :make してエラー行へ飛ぶ」まではできる。できないのは、PDF を開くこと、ソースと PDF を往復すること、\ref や \cite を補完すること、文書の構造をたどることです。vimtex はそのすべてを引き受け、ファイルタイプ tex に対しては Vim 内蔵の tex プラグインを自分で置き換えます。
" What bare Vim gives you, without any plugin at all.
packadd! matchit " % jumps between \begin{...} and \end{...}
compiler tex " :make runs latex and fills the quickfix list新しい .tex が plaintex になり vimtex が動かない理由
答えは単純で、Vim は .tex の中身を読んで推測しているからです。Vim 9.1 の autoload/dist/ft.vim にある FTtex() は、まず 1 行目の %&形式 を見て、次に先頭の非コメント行から 1000 行ぶんを走査して \documentclass・\usepackage・\begin{・\newcommand・\renewcommand のいずれかを探します。どれも見つからなければ既定値に落ち、その既定値は g:tex_flavor が未設定なら plain——つまりファイルタイプは tex ではなく plaintex になります。空の新規ファイルや、まだ \documentclass を書いていない断片が、まさにこれに当たります。
ここで面白いのは、この件についてよく見かける助言——「vimrc に let g:tex_flavor = 'latex' を書け」——が vimtex では逆向き だということです。vimtex は自前の ftdetect/tex.vim でファイルタイプ判定そのものを上書きし、g:tex_flavor を latex に設定してしまいます。ドキュメントもその意図を明言していて、.tex が既定で plaintex と認識される想定外の挙動を防ぐため、と説明しています。したがって vimtex を入れているなら g:tex_flavor を自分で書く必要はありません。逆に、latex 以外の値を入れておくと vimtex の上書きを拒否できる——設定する意味があるのはそちら側です。
vimtex の導入——遅延読み込みをしてはいけない理由
結論から言うと、vimtex を lazy load してはいけません。理由は好みの問題ではなく仕組みの問題です。逆検索(PDF 側からソースへ戻る操作)は :VimtexInverseSearch というグローバルなコマンドに依存していて、ビューアはエディタの外からそれを呼びます。プラグイン本体がまだ読み込まれていなければ、そのコマンドは存在しません。しかも vimtex はファイルタイププラグインであり autoload 機構を使っているので、もともと必要になるまで読み込まれない——プラグインマネージャに遅延させてもらう利点自体がありません。lazy.nvim なら lazy = false、vim-plug なら for を付けない、が正解です。
前提はもう二つあります。第一にエディタのバージョン。vimtex 2.18(2026 年 7 月公開)以降は Vim 9.2 または Neovim 0.12.4 を要求し、それより古い環境では読み込まれません。古い Vim を使い続けるなら v2.17 タグを指定して固定するのが正しい対処で、let g:vimtex_version_check = 0 で検査を黙らせることもできます。第二に filetype plugin on と syntax enable。前者が無いと vimtex 自体が読み込まれず、後者が無いと構文情報に依存する機能——数式領域の判定や i$ テキストオブジェクト——が動きません。Neovim で Tree-sitter に寄せる場合も、Vim の構文機能は切らないでおくのが安全です。
call plug#begin()
Plug 'lervag/vimtex'
" Pin an older tag if you are stuck on Vim < 9.2:
" Plug 'lervag/vimtex', { 'tag': 'v2.17' }
call plug#end()
filetype plugin indent on " required (indent is optional)
syntax enable " required for math zones, i$ and friends
set encoding=utf-8 " needed in Vim, not in Neovim
let maplocalleader = ' ' " Space as <localleader>; default is backslash
let g:vimtex_view_method = 'zathura'Neovim では同じことを Lua で書きます。設定は init の中に置いて、プラグイン本体が読み込まれる前に効かせるのが定石です。maplocalleader は vimtex のほぼ全コマンドの入口——既定のプレフィックスは g:vimtex_mappings_prefix、その既定値は <localleader>l で、<localleader> 自体の既定はバックスラッシュ——なので、明示しておくと後で混乱しません。バックスラッシュのままなら \ll、Space に変えたなら Space に続けて ll と打つことになります。
return {
"lervag/vimtex",
lazy = false, -- never lazy-load: it breaks :VimtexInverseSearch
init = function()
vim.g.maplocalleader = " "
vim.g.vimtex_view_method = "zathura" -- "skim" on macOS
vim.g.vimtex_compiler_method = "latexmk"
end,
}\ll ——latexmk が常駐し、保存のたびに PDF が追いつく
\ll を一度押すと latexmk が常駐モードで走りはじめ、もう一度押すと止まります。トグルになっているのは、既定のコンパイラ設定 g:vimtex_compiler_latexmk が continuous を有効にしているからで、内部では latexmk の連続監視が使われます。以後は保存するたびに再コンパイルが走り、ビューアの PDF が勝手に追いついてきます。単発でよいなら \lS(:VimtexCompileSS)、止めたいなら \lk(プロジェクト全部なら \lK)です。
| キー | コマンド | 働き |
|---|---|---|
\ll | :VimtexCompile | 常駐コンパイルの開始と停止(トグル) |
\lS | :VimtexCompileSS | 単発コンパイル。CI と同じ一回きりの実行 |
\lv | :VimtexView | PDF を開き、カーソル位置へ前方検索する |
\lt | :VimtexTocOpen | 目次バッファを開く(\lT はトグル) |
\le | :VimtexErrors | エラーと警告を quickfix に並べる |
\lo | :VimtexCompileOutput | コンパイラの生の出力を表示する |
\lc | :VimtexClean | 補助ファイルを掃除(\lC は出力も消す) |
\li | :VimtexInfo | 認識中のルートファイル・パッケージなどを表示 |
\ls | :VimtexToggleMain | いま編集中のファイルをルート扱いするか切り替える |
既定で latexmk に渡されるオプションは -verbose・-file-line-error・-synctex=1・-interaction=nonstopmode の四つです。ここに -synctex=1 が最初から入っているので、後述の前方・逆検索に必要な同期データは何もしなくても書き出されます。エラーの扱いは g:vimtex_quickfix_mode が既定 2——quickfix ウィンドウは自動で開くが、フォーカスは奪わない——なので、書きながら横目でエラーを見る運用に合います。コンパイラそのものを差し替えたいときは g:vimtex_compiler_method に latexmk(既定)・latexrun・tectonic・arara・texpresso・generic のいずれかを指定します。
\ll が別のエンジンを呼ぶとき——-pdf と $pdf_mode の衝突
.latexmkrc に $pdf_mode = 3 と書いたのに DVI 経由にならない——原因は、vimtex が毎回エンジンのフラグをコマンドラインに足していることです。エンジンは g:vimtex_compiler_latexmk_engines という対応表から選ばれ、既定のキー _ は -pdf。コマンドラインの -pdf は .latexmkrc の $pdf_mode より強いので、設定ファイルの指定が黙って上書きされます。vimtex が $pdf_mode からエンジンを推測してくれるのは、ドキュメントが明記しているとおり 1(pdfLaTeX)・4(LuaLaTeX)・5(XeLaTeX) の三つだけで、DVI 経由の 3 は含まれません。
正しい直し方は、主ファイルの先頭に TeX プログラム指定のコメントを書くことです。右辺は上の対応表のキーでなければならないので、LuaLaTeX なら lualatex、DVI 経由なら pdfdvi(-pdfdvi に対応)を指定します。そのうえで upLaTeX と dvipdfmx の実体は .latexmkrc に書く——これが和文論文で定番の組み合わせです。latexmk の設定そのものは自動ビルドのページが持ち主なので、詳細はそちらを参照してください。
% In the main .tex file, first line: pick the key, not the binary name.
% !TeX program = pdfdvi
# .latexmkrc -- upLaTeX and dvipdfmx do the actual work
$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';
$max_repeat = 5;$latex にも -synctex=1 を渡しておくのが肝心で、そうすれば DVI を経由しても同期データが PDF まで運ばれます。なお latexmk 以外を使うなら話は別で、Tectonic に切り替えたい場合は g:vimtex_compiler_method を tectonic にするだけ——エンジンの選択も .latexmkrc も要りません。
テキストオブジェクト——\begin{...} を Vim の文法で編集する
ここが、Vim で LaTeX を書く唯一の代えのきかない理由です。Vim の編集は「動詞 + 対象」という文法でできていて、d(削除)や c(変更)に iw(単語の内側)や ap(段落全体)を組み合わせます。vimtex はこの語彙に LaTeX の構造そのものを足します。dae は \begin{...} から \end{...} までを丸ごと消し、cie は中身だけを入れ替え、ci$ はインライン数式の中だけを打ち直します。三十行の align 環境を消すのに、行数を数える必要はもうありません。
| オブジェクト | 対象 | 使いどころ |
|---|---|---|
ie / ae | 環境(最上位の document を除く) | dae で環境ごと削除、cie で中身だけ差し替え |
i$ / a$ | 数式環境($...$ や \[...\]) | ci$ で数式の中だけ打ち直す |
ic / ac | 命令とその引数 | dac で \textbf{...} を丸ごと外す |
id / ad | 対になる区切り記号 | ci( の LaTeX 版。\left(...\right) も掴む |
iP / aP | 節(section) | daP で一節まるごと移動・削除する |
im / am | \item 一項目 | 箇条書きの一項目だけを掴む |
さらに、構造を書き換えるための三つ組があります。ds は囲みを外す、cs は囲みを変える、ts は切り替える。dse で環境を外して中身だけ残し、cse で itemize を enumerate に変え(候補は補完で出ます)、dsc/csc は命令を外す・変える、dsd/csd は区切り記号に同じことをします。tse は環境の切り替え、tss は環境の星付き・星なしの切り替え、tsc は命令の星の切り替え、tsd は (...) と \left(...\right) の行き来です。移動系では % が対応する対を往復し、]]/[[ が次と前の節の先頭へ、][/[] が節の末尾へ、]m/[m が環境へ、]n/[n が数式へ飛びます。カーソルを命令の上に置いて K を押せば、そのパッケージのドキュメントが開きます。
長い文書を歩く——目次バッファと \ref の補完
\lt を押すと、文書全体の目次が普通のバッファとして開きます。普通のバッファなので、/ で検索でき、j/k で歩けて、Enter でその節へ飛べます。複数ファイルに割った文書でも、vimtex はルートファイルから \input/\include をたどるので、章をまたいだ一枚の目次になります。既定では幅 50 桁の分割ウィンドウで、見た目や挙動は g:vimtex_toc_config で調整します。いま編集しているファイルをルートとして扱うかどうかは \ls で切り替えられ、\li で vimtex がどのファイルをルートと判断しているかを確認できます。
補完は Vim 標準の仕組みに乗ります。tex バッファでは omnifunc が自動で vimtex#complete#omnifunc に設定される(g:vimtex_complete_enabled は既定で有効)ので、挿入モードで Ctrl-X Ctrl-O を押せば候補が出ます。\cite{ の直後なら .bib と \bibitem から文献キーが、\ref{ の直後なら文書中の \label が、\usepackage{ なら手元の .sty が、\includegraphics{ ならファイル名が並びます。毎回そのキーを叩くのは面倒なので、実務では補完エンジンに橋渡しします——Neovim なら nvim-cmp の omni ソース、Vim・Neovim 両対応なら coc.nvim の coc-omni 拡張。役割分担は一定で、候補を作るのは vimtex、いつどう見せるかは補完エンジンです。
local cmp = require("cmp")
cmp.setup({
sources = cmp.config.sources({
{ name = "omni" }, -- pulls vimtex candidates through omnifunc
}),
})Vim と Neovim で本当に違うところ
編集機能に関しては、テキストオブジェクトも目次も補完も 両者でまったく同じです。差が出るのは一点、エディタが外から呼び出せるかどうかです。逆検索ではビューアがエディタを呼び返す必要があり、Vim ではその通路が +clientserver 機能。vimtex のドキュメントは、Windows か gVim ならサーバが自動的に立つが、Linux や macOS の端末で動く Vim では立たないとはっきり書いています。Neovim には clientserver がなく、代わりに MessagePack-RPC を使いますが、vimtex 側の扱いは同じで、どちらも v:servername に相手先が入ります。
しかも「端末の Vim ならサーバを立てればよい」で済まない場合があります。macOS に最初から入っている /usr/bin/vim は -clientserver——つまり機能そのものが無効でビルドされているので、remote_startserver() は存在せず、下のスニペットを書いても何も起きません。この場合の選択肢は、MacVim や Homebrew の Vim のように +clientserver 付きのビルドを使うか、Neovim に移るかです。逆に Neovim 側には設定が要らない、というのがこの節の実務的な結論になります。細かい差はもう二つ。Vim では encoding を utf-8 にする必要がありますが Neovim では不要で、必要なエディタのバージョンも Vim 9.2 と Neovim 0.12.4 で別々に指定されています。
" Vim only, and only in a build that has +clientserver.
if empty(v:servername) && exists('*remote_startserver')
call remote_startserver('VIM')
endifビューアの選択と逆検索のコマンドライン
ここで誤解が多いのは g:vimtex_view_method の既定値です。プラットフォームごとに賢く選ばれるのではなく、どの OS でも既定は general——xdg-open(Linux)、open(macOS)、SumatraPDF など(Windows)といった汎用の起動に落ちます。この汎用ビューアは g:vimtex_view_general_viewer で指定します。専用の方法として用意されているのは zathura・zathura_simple・skim・mupdf・galley、それに sioyek で、sumatrapdf という値は存在しません。Windows で SumatraPDF を使う場合は general 経由になります。SyncTeX そのものの仕組みは専用ページにあるので、ここでは設定だけを見ます。
前方検索(ソース → PDF)は \lv を押すだけで、設定はほぼ要りません。手間がかかるのは逆検索(PDF → ソース)のほうで、ビューア側に「クリックされたらこのコマンドを実行しろ」と教える必要があります。そのコマンドの中身が VimtexInverseSearch <行> <ファイル> です。行番号とファイル名を埋め込むプレースホルダの綴りは ビューアごとに違う ので、そこだけ注意してください——zathura は %{line}/%{input}、Skim は %line/%file、SumatraPDF は %l/%f です。zathura は g:vimtex_view_method = 'zathura' にしておけば vimtex が -x 付きで起動してこの設定を渡してくれるため、多くの環境では何も書かなくても Ctrl クリックで戻れます。
# Linux: zathura. Ctrl-click in the PDF jumps back to the source.
set synctex true
set synctex-editor-command "nvim --headless -c 'VimtexInverseSearch %{line} %{input}'"macOS の Skim では、環境設定の同期タブでプリセットを Custom にし、コマンドと引数を登録します(Cmd-Shift-クリックで逆検索)。Windows の SumatraPDF なら、設定の逆検索コマンドライン欄に同じ形の一行を入れます(ダブルクリックで逆検索)。gVim なら nvim --headless の部分を vim -v --not-a-term -T dumb に置き換えてください。
# macOS, Skim: Preferences > Sync > Preset: Custom
Command: nvim
Arguments: --headless -c "VimtexInverseSearch %line '%file'"
# Windows, SumatraPDF: Settings > Options > inverse search command-line
cmd /c start /min "" nvim --headless -c "VimtexInverseSearch %l '%f'"最初に固めるべき四つの動き
vimtex は、書きはじめる前に習得しておく道具ではありません。\ll で常駐コンパイルを始め、保存し、\lv で PDF の該当箇所を見て、\le でエラーだけを見る——この四つが指に入れば、あとの機能は必要になった日に一つずつ足せば十分です。テキストオブジェクトも dae と cse の二つから始めるのがよく、そこから ci$ や tsd へ広げていくと、自分がどの操作を毎日しているかが見えてきます。
複数ファイルに割る前に、一度だけ確かめておくべきことがあります。章のファイルから \ll を押しても同じ PDF ができるかです。\li でルートファイルの判定を見て、違っていれば \ls で切り替えるか、主ファイルを % !TeX root = main.tex で指し示します。ここを先に決めておけば、補完エンジンやスニペットを足していく段階で、ビルドの側が揺れる心配はなくなります。