NeovimでLaTeX統合開発環境を構築する

#1. はじめに
こんにちは。初投稿です。皆さんはVim / Neovimをお使いでしょうか？今回は「Vim / NeovimでLaTeXの編集をしてみたいけれど、設定を考える時間もないし面倒くさい」という方を対象に、筆者の作った環境を紹介したいと思います。

なお、詳しくは以下で説明しますが、今回の方法はVimではなくNeovimでないとすべては実現できません。

##1.1. 実現する機能
統合開発環境を目指すに当たって、つぎの機能を実現します。

* テキスト編集: Neovim
* コンパイル: vimtex
* コード補完: vimtex, deoplete.nvim, pynvim
* 静的コード解析: ALE (Asynchronous Linting Engine)
* pdfプレビュー: qpdfview
    * forward search: vimtex
    * inverse search: qpdfview, neovim-remote

##1.2. 使用した環境

* Ubuntu18.04LTS
* Texlive 2017 (Ubuntuリポジトリのtexlive-fullパッケージ)

Texliveはすでにインストールされ、PATHが通っているものとします。
##1.3. なぜVimではなくNeovimなのか
SynctexのInverse Search (pdfビューアをクリックするとエディタの該当の箇所にジャンプする機能) を実現するのにNeovimのremote機能を利用するためです。Vimにもremote機能はありますが、Neovimの方が単一のインスタンスに対して複数サーバーを起動でき柔軟性が高いため、Inverse Search向きです。
#2. インストールと設定

##2.1. NeovimとPythonのインストール
まずはNeovimのインストールです。Ubuntu 18.04のリポジトリにあるものはバージョンが古くdeopleteが使用できないので、snapからインストールします。

```
$sudo snap install --beta --classic nvim
```
snap版Neovimは2020年4月現在stable版が提供されていないので、`--beta`オプションを付けています。`--classic`オプションはNeovimがシステム全体にアクセスすることを許可します。

つぎに、NeovimのPython拡張とremote機能をインストールします。Python拡張はdeopleteを動作させるのに必要です。

```
$sudo apt install python3-pip
$pip3 install pynvim neovim-remote
```
以上でNeovimとプラグインを使う準備が整いました。

##2.2. qpdfviewのインストールと設定
pdfのプレビューはqpdfviewで行います。つぎのコマンドでインストールします。

```
$sudo apt install qpdfview
```
Inverse Searchを利用するために、Neovimとqpdfviewの両方に設定を行います。先にも述べたとおり、remote機能を使うので、まずはそのための設定を`~/.config/nvim/init.vim`に記述します。

```vim:~/.config/nvim/init.vim
" Starting server for LaTeX inverse search.
function! s:myinversetex()
	if !filereadable('/tmp/sv4nvim' . expand("%:p"))
		call mkdir('/tmp/sv4nvim' . expand("%:p:h"),"p")
		call serverstart('/tmp/sv4nvim' . expand("%:p"))
	endif
endfunction
command! Serverorig call s:myinversetex()

augroup latex_new
" texソースのときに自動実行
autocmd!
autocmd BufRead *.tex Serverorig
autocmd BufRead *.ltx Serverorig
autocmd BufWritePost *.tex Serverorig
autocmd BufWritePost *.ltx Serverorig
augroup END
```

Neovimではサーバーは空の一時ファイルとして実現されています。上記の設定は、Neovimがtexソースを開いたときに、`/tmp/sv4nvim/`以下に当該のtexファイルのフルパスを再現したサーバーを設置します。こうすることで、Neovimが`/PATH/TO/hoge.tex`ファイルを開いているとすると、コマンド

```
$nvr --servername "/tmp/sv4nvim/PATH/TO/hoge.tex" --remote-silent +XX "/PATH/TO/hoge.tex"
```
を実行したときに、texソースのXX行目にジャンプしてくれます。この方法だとNeovimがいくつ起動していても当該のtexソースを開いているものでジャンプしてくれるため便利です。

上記のコマンドをqpdfviewが自動的に行なうために、メニューバーのEdit>>Settingsから、つぎの画像のように設定します。

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639183/0df1ae51-ba6b-a656-11d5-070e044fd23e.png" width=70%>

Auto-refreshにチェックを入れると開いているpdfファイルに変更があるたびに表示に反映されるようになります。
Source editor欄に記述するコマンドは

```
nvr --servername "/tmp/sv4vim%1" --remote-silent +%2 "%1"
```
です。
以上の設定を行なうことで、qpdfviewで開いているpdfファイルをダブルクリック、もしくは右クリックで表示されるコンテキストメニューからNeovim上のソースの該当箇所にジャンプしてくれるようになります。

一点注意していただきたいのは、texソースを分割している場合です。pdfから分割先のtexソースにジャンプしたい場合、あらかじめ該当するtexファイルすべてを`:e`で一つ一つ開くか、別々にNeovimを立ち上げて開いておく必要があります。

##2.3. Vimプラグインのインストールと設定
[vimtex](https://github.com/lervag/vimtex/)、[deoplete](https://github.com/Shougo/deoplete.nvim)、[ALE](https://github.com/dense-analysis/ale)をインストールします。[vim-plug](https://github.com/junegunn/vim-plug)を用いる場合、`~/.config/nvim/init.vim`に

```vimscript:~/.config/nvim/init.vim
call plug#begin('~/.vim/plugged')

Plug 'Shougo/deoplete.nvim'
Plug 'lervag/vimtex'
Plug 'dense-analysis/ale'

call plug#end()
```

と記述し、`:source ~/.config/nvim/init.vim`などで設定を読み込み、`:PlugInstall`でインストールします。

###2.3.1. vimtex
vimtexはLaTeXを扱う上で便利な機能を多く提供してくれます。今回はそのうちコンパイルとforward search (エディタからpdfの該当箇所にジャンプ) を設定します。

```vimscript:~/.config/nvim/init.vim

" 手動でコンパイル
let g:vimtex_compiler_latexmk = { 'continuous' : 0,}
" qpdfviewでpdfプレビュー
let g:vimtex_view_general_viewer = 'qpdfview'
let g:vimtex_view_general_options = '--unique @pdf\#src:@tex:@line:@col'
let g:vimtex_view_general_options_latexmk = '--unique'
```
上記のように設定することで、ノーマルモードで`<Local Leader>ll`を実行するたびにコンパイルしてくれるようになるほか、`<Local Leader>lv`でqpdfviewにforward searchしてくれます。なお、デフォルトでは`<Local Leader>`は`\`です。
下３行はvimtexの[issues](https://github.com/lervag/vimtex/issues/952)から拝借しました。

###2.3.2. deoplete.nvim

vimtexはLaTeXのコード補完をオムニ補完として提供してくれますが、補完したいと思うたびに挿入モードで`<C-x><C-o>`を押さなければならず面倒です。そこで、deopleteと連携させることで補完候補が自動的に表示されるようにします。

```vimscript:~/.config/nvim/init.vim
" Auto start deoplete package.
let g:deoplete#enable_at_startup = 1
" vimtexのオムニ補完をdeopleteから呼び出す
call deoplete#custom#var('omni', 'input_patterns', {
	\ 'tex': g:vimtex#re#deoplete
	\})
```
補完候補は`<C-p>`と`<C-n>`で選択し、`<C-e>`で補完を中止します。

###2.3.3. ALE (Asynchronous Linting Engine)

ALEは多くの言語の静的コード解析を行なうことができ、LaTeXにも対応しています。バックグラウンドでそれぞれの言語に対応したLinterを実行し、LaTeXではchktexとlacheckを利用します。しかし、chktexの警告は余計なものが多いので、以下の例のような設定を`~/.chktexrc`に記述します。

```text:~/.chktexrc
CmdLine{
-wall -n22 -n30 -e16 -n19 -n3
}
```

上記の設定は、Atomの[linter-chktex](https://atom.io/packages/linter-chktex)パッケージのデフォルト設定に手を加えたものです。お好みで調整したほうが良いと思います。

さらに、ALEの挙動を`~/.config/nvim/init.vim`に設定します。

```vimscript:~/.config/nvim/init.vim
" Setting ALE (Asyncronus Linting Engine)
let g:ale_echo_msg_error_str = 'E'
let g:ale_echo_msg_warning_str = 'W'
let g:ale_echo_msg_format = '[%linter%] %s [%severity%]'
let g:ale_lint_on_text_changed = 'always'
```

コメントアウト以下の3行はALE配布ページの[FAQ](https://github.com/dense-analysis/ale#faq-echo-format)に載っていたサンプルそのままです。警告メッセージが`[chktex] Use \( ... \) instead of $ ... $. (46) [w]`のように表示されるようになります。
最後の行は、挿入モードでテキストを打ち込んでいるときでも警告を更新し続ける設定です（デフォルトではノーマルモードでしか警告を更新しません）。

##2.4. latexmkの設定

vimtexはコンパイルにlatexmkを利用します。latexmkを実行するときには、`~/.latexmkrc`などにタイプセットの設定をする必要があります。以下にplatexを利用する場合の設定例を載せます。

```perl:~/.latexmkrc
 #!/usr/bin/perl 
$latex         = 'platex -synctex=1 %O %S'; 
$bibtex        = 'pbibtex %O %B'; 
$dvipdf        = 'dvipdfmx %O %S'; 
$pdf_mode      = 3; # use dvipdf
```
latexmkは実行時にソースファイルが何回コンパイルが必要か自動で判断して実行してくれます。したがって、platexなどを直接実行するときと異なり一度の実行で図表や文献、式の番号をpdfに出力することができます (要するに「文献 [??]」がなくなる) 。


#3. おわりに
お疲れ様でした。以上で快適なLaTeX執筆環境が構築できたと思います。最後まで読んで頂きありがとうございます。なお、筆者はVimもqiitaも初心者なので、おかしな点、間違っている点などがございましたら、わかりやすくご教示ください。