Neovim+vimtex+TeXLive 2021+SumatraPDFでTeX環境を作ってみる。

NeovimでのPythonの環境をLSPに変更したので、vimtexが使っている補完ソースもLSPにしてみます。

TeX用Language Serverのセットアップ

LSはtexlab/Relaseからバイナリをダウンロードします。
実行形式ファイルが1つだけなので、インストールはこのtexlab.exec:/Apps/languageServerにコピーするだけです。

次にdein.tomlのnvim-lspconfigのところにLSに関する設定を追加します。
[[plugins]]
# A collection of common configurations for Neovim's built-in language server client.
repo = 'neovim/nvim-lspconfig'
hook_add = '''
set runtimepath+=~/.cache/dein/repos/github.com/neovim/nvim-lspconfig/lua
lua << EOF
local nvim_lsp = require('lspconfig')

nvim_lsp.pyls_ms.setup{
    cmd = { "dotnet", "exec", "c:/Apps/languageServer/Microsoft.Python.LanguageServer.dll" },
	init_options = {
	  analysisUpdates = true,
	  asyncStartup = true,
	}
}
nvim_lsp.texlab.setup{
    cmd = { "c:/Apps/languageServer/texlab.exe" },
}
EOF
'''
一応これだけでLSPを利用した補完はできるようになります。 ただし、ALEを使っている場合にはALEとLSPで二重にチェックされたりするらしいので、問題がある場合にはその部分で変更が必要です。ALEの設定部分で let g:ale_disable_lsp = 1と設定するといいらしいです。

vimtexとSumatraPDFの連携

vimtexでコンパイルまでできるんだから、逆順検索もちゃんとできるようにします。

まず、逆順検索(inverse search)はRPCを使ってNeovimとSumatraPDFがやりとりします。具体的には、SumatraPDF側でクリックされた行情報をRPCでNeovimに送ります。Neovim側はRPCで受け取ったコマンドを実行して、PDFでのクリックされた行のソースにジャンプします。

ということでNeovimを外部から、コントロールするためのneovim-remoteが必要になります。
$ pip install neovim-remote
するとnvrというコマンドが利用できるようになります。

nvrでは予めサーバの場所を指定しておく必要があります。NeovimのRPCの場合には、\\.\pipe\nvim-1234-0というようにパイプが作成されるので、それを利用する必要がありますが、パイプ名は毎回異なります。
一方、SumatraPDFでは逆順検索コマンドで利用するためにサーバの場所を知りたいのですが、そのためにはNeovimと通信できないとわかりません。毎回手動で設定するのも大変ですし、現実的ではありません。

そこで、起動時にNeovimが作成するRPCサーバとは別に、SumatraPDF専用のRPCサーバを起動してそこと通信させればうまくいきます。

Neovim側で
if has('win32')
	call serverstart('\\.\pipe\nvim-vimtex-1234')
endif

let g:vimtex_view_general_options
			\ = '-reuse-instance'
			\ . ' -forward-search @tex @line @pdf'
			\ . ' -inverse-search "nvr --servername ' . '\\.\nvim-vimtex-1234'
			\ . ' --remote-send \"\%lG\""'
などとvimtexの設定時にRPCサーバを起動し、このパイプ名をSumatraPDFの逆順検索コマンドに指定してやればよいのです。
nvr --servername \\.\pipe\nvim-vimtex-1234 --remote-send "%lG"
これでPDFで行をクリックするとNeovim側で対応する行にジャンプします。 ・・・でも順方向がちゃんと動かない・・・。

deopleteのソースをJediからLSPに変更する。

ついでにvimtexまわりの変更も行います。

JediからLanguage Serverへ

約2年前にNeovim+vimtex+TeXLive2018でTeX環境を作ってみる。というエントリを書いたのですが、ここへきて Neovim の 0.5.0 がぼちぼち(予定では6月15日)にリリースされるということで、Nightly ビルドを持ってきて使っています。

もともとは Python プログラミングの補完などのためにdeoplete-jediを使っていましたが、jediよりも速いという話もあってLSPを利用した入力サポートへの変更を行いました。

Python用のLanguage Server(LS)はいくつかありますが、VS Codeと併用するならばVS Codeで使用されている Pylance のベースとなっているMicrosoft Python Language Serverが、一番違和感なく使えるのではないかということでこれを候補にしました。
他にもpalantir/python-language-serverhttps://github.com/python-lsp/python-lsp-serverにありますが、こちらはエンジン?にJediを利用するということで、deoplete-jediで使っているのと変わらないためにパスしています。また、NeovimをVS Codeのような感じにするというCoC(Conquer of Completion)というフレームワークがあり、そのプラグインで使うという方法も調べてみましたが、こちらはNode.jsで動くということで、Node.jsを使う予定はないのでこれもパスしました。

結果、構成としては以下のようになります。
  • Neovim + deoplete を利用する
  • deoplete からLSを利用するため、deoplete-lsp プラグインを利用する
  • LSPとしてはMicrosoft Python Language Server(pyls_ms)を使用する
  • pyls_msの設定を簡単に行うため、nvim-lspconfigを使用する

pyls_msをビルドする

Microsoft Python Language ServerはC#で記述されているので、.NETのビルド環境が必要です。うちではVisual Studio 2019がインストールされているので、コマンドプロンプトから以下のコマンドを叩けばビルドできます。
$ git clone https://github.com/Microsoft/python-language-server.git
$ cd python-language-server/src/LanguageServer/Impl
$ dotnet build -c Release
ビルドが正常に終了したらpython-language-server/output/binディレクトリにある全ファイルをc:/Apps/languageServerディレクトリにコピーします。

deoplete-lspを導入する

dein.tomlを編集し、deoplete-jediを削除したあとで以下の記述を追加します。
[[plugins]]
# LSP Completion source for deoplete
repo = 'deoplete-plugins/deoplete-lsp'
hook_add = '''
let g:deoplete#lsp#handler_enabled=1
let g:deoplete#lsp#use_icons_for_candidates=0

'''

nvim-lspconfigを導入する

続けて以下の記述を同じくdein.tomlに追加します。
[[plugins]]
# A collection of common configurations for Neovim's built-in language server client.
repo = 'neovim/nvim-lspconfig'
hook_add = '''
set runtimepath+=~/.cache/dein/repos/github.com/neovim/nvim-lspconfig/lua
lua << EOF
local nvim_lsp = require('lspconfig')

nvim_lsp.pyls_ms.setup{
    cmd = { "dotnet", "exec", "c:/Apps/languageServer/Microsoft.Python.LanguageServer.dll" },
	init_options = {
	  analysisUpdates = true,
	  asyncStartup = true,
	}
}
EOF
'''
一応このとき、dein_lazy.tomlでのdeopleteの設定は以下のようになります。
[[plugins]]
# Dark powered asynchronous completion framework for Neovim / Vim8
# require: python3
repo = 'Shougo/deoplete.nvim'
depends = 'context_filetype.vim'
on_i = 1
hook_post_source = '''
  call deoplete#enable()
  source $XDG_CONFIG_HOME/nvim/rc/deoplete.rc.vim
'''

起動する

設定が終わったらインストールとキャッシュの作り直しをしておきます。
:UpdateRemotePlugins
:call dein#update()
:call dein#recache_runtimepath()
Neovimを再起動してPythonファイルを編集し、補完候補の右側に[LSP]と出てくればちゃんと動いています。

Vimの補完プラグインをインストール。その4

Vimの補完プラグインをインストール。その3 で、 ddc-tabnine が使えそうです、などと書いたのですが、早速やってみました。 まず、tabnineのバイナリを用意しないといけません。がどうにもTabNineのサイトがわかりにくいので、 tabnine-nvim にあるダ...