deoplete.nvimとLSP補完での変数名prefix重複解消
年始休み中に古いままだったVim環境を更新して各種補完系をLSP(Language Server Protocol)のプラグインに差し替えた。しばらく快適なエディタ生活を送っていたつもりだったが、時折発生する入力誤りが補完起因であることに気がついた。
違和感があったのは、Rubyのインスタンス変数を入力する際に @@value
のように @
が二重入力されてしまうこと。そういえば、PHPなどを書いていたときにも $$
のようにprefixが重複する。
補完文字列重複の原因
これには複数の原因があったが、ひとつには補完の仕組みをLSPに移行したことで補完文字列が変化していた。
従来の動作はneocompleteによる補完で、変数名の補完などにはprefix部分は含まれていなかった。ところが、LSP経由で提示される補完候補は基本的に @value
のようにprefixを含むものが返ってくるため従来の動作との相違がある。
2点目として、自分の設定ではdeoplete.nvimの設定を公式ドキュメント同様の記述としていた。これは一般的なキーワード文字列にマッチするパターンではあるが、prefixを含まない文字列部分の表現となっている。
call deoplete#custom#option({ ... \ 'keyword_patterns': { \ '_': '[a-zA-Z_]\k*', \ 'ruby': '[a-zA-Z_]\w*[!?]?', \ },
deoplete.nvimでは補完開始位置(complete_position)の計算に、特に指定がなければkeyword_patternsを用いる実装となっている(rplugin/python3/deoplete/base/source.py)。このため、入力位置から判定された補完候補はprefixを含む文字列であるのに対して、上記のようなkeyword_patternsの場合はprefixを含まない位置が補完開始位置と判断されるため、prefixmの重複が発生してしまうのであった。
つまり、以下のような流れが発生することになる。
@val
まで入力した時点で入力補完を呼び出す- 補完候補として
@value
が返される - 補完開始位置は
keyword_patterns
にマッチするval
の先頭とされるため、@
の後に@value
が補完される @@value
の重複文字列となる
対処
原因が分かれば対処は容易なので keyword_patterns
を調整すれば良い。
変数名にprefixがつく言語は、メジャーなものでは Ruby, Perl , PHP くらいだろうか。よく利用するシーンを想定して、一旦以下のような定義に書き換えた。
\ 'keyword_patterns': { \ '_': '\$?[a-zA-Z_]\k*', \ 'ruby': '@?[a-zA-Z_]\w*[!?]?', \ },
Rubyのクラス変数を考えると @*
のほうが望ましいか?
Perlだと[\$@%]
あたりを定義しておいてもよいかもしれないと思ったが、もうPerlは10年以上書いていないので必要になったら考えることにする。
ともあれ、これで変数補完時の重複は解消された。
根本対処は可能か
問題は解決したものの、適切な設定値を指定しないと意図した動作にならないのはエディタの補完機能として頼りない。利用者が意識せずとも快適に動作する環境は作れるだろうか。
補完機能がエディタなどでの利用を想定している場合、補完ロジックを提供する側から置換位置の情報が返されてもよい気がするが、今のところLSPの補完仕様にそのような情報は含まれない(Language Server Protocol Specification - Completion Request)。
deoplete.nvimはsourceのget_complete_position
をオーバーライドすることで補完位置を調整することができるが、LSPの場合は通常LSPクライアント自体がsourceとなるため接続先サーバ毎に異なる挙動を提供することも難しいように思われる。
現時点では最適解を思いついていないので、せめてもの情報源としてこの記事を残した。