vim-lsp で textDocument/formatting を特定の Language Server だけで行う

3 min read読了の目安(約2900字

vim-lsp を使いつつも Lint, Format が上手く設定できず ALE を併用してきましたが、ようやく vim-lsp で Lint, Format を設定出来る目処が立ったので、記載していきます。

背景

TypeScript のコードを保存時に Prettier でフォーマットするため、vim-lsp で補完や定義ジャンプを行いつつ diagnostics は無効にし ALE で Lint, Format を実行する環境を作っていました。

.vimrc
Plug 'prabirshrestha/vim-lsp'
Plug 'mattn/vim-lsp-settings'
let g:lsp_diagnostics_enabled = 0

Plug 'dense-analysis/ale'
let g:ale_echo_msg_format = '[%linter%] %code: %%s'
let g:ale_lint_on_text_changed = 'never'
let g:ale_fix_on_save = 1
let g:ale_fixers = {
\ 'typescript': ['eslint', 'prettier'],
\ 'typescriptreact': ['eslint', 'prettier']
\ }

しかし ALE も LSP を使うようになり、vim-lsp と役割が重複しつつあったため efm-langserver を利用して Lint, Format も vim-lsp 経由で実行する構成への変更を考えていました。

意図しない Format

以下のように vim-lsp と efm-langserver を設定し :LspDocumentFormatSync を実行してみたのですが Prettier によるフォーマットではなく typescript-language-server によるフォーマットが実行されてしまいました。

意図しない LspDocumentFormatSync の結果

.vimrc
Plug 'prabirshrestha/vim-lsp'
Plug 'mattn/vim-lsp-settings'
let g:lsp_diagnostics_echo_cursor = 1
let g:lsp_settings = {
\ 'efm-langserver': {
\   'disabled': v:false
\ }
\ }
efm-langserver/config.yaml
version: 2
root-markers:
  - .git/
tools:
  prettier: &prettier
    format-command: 'npx --no-install prettier --stdin-filepath ${INPUT}'
    format-stdin: true
languages:
  typescript:
    - <<: *prettier
  typescriptreact:
    - <<: *prettier

このフォーマットの制御方法が分からず、今まで vim-lsp, efm-langserver の構成に踏み切れていませんでした。

vim-lsp でのフォーマット時に server を指定

しかし vim-lsp でのフォーマット実行時に server を指定する方法があることに気づきました。
:LspDocumentFormatSync --server=efm-langserver で efm-langserver の textDocument/formatting が実行されます。

LspDocumentFormatSync で server を指定した実行結果

--server が使えることに気づいたのは以下の issue です。

https://github.com/prabirshrestha/vim-lsp/issues/1056

当初の目的である、保存時に Prettier によるフォーマットを適用するためには以下のような設定を行うことで実現できるようになりました。

.vimrc
autocmd BufWritePre *.ts,*.tsx call execute('LspDocumentFormatSync --server=efm-langserver')

まとめ

vim-lsp の :LspDocumentFormatSync--server=efm-langserver のように、実行するサーバを引数で指定することが出来ます。

.vimrc
Plug 'prabirshrestha/vim-lsp'
Plug 'mattn/vim-lsp-settings'
let g:lsp_diagnostics_echo_cursor = 1
let g:lsp_settings = {
\ 'efm-langserver': {
\   'disabled': v:false
\ },
\ }

autocmd BufWritePre *.ts,*.tsx call execute('LspDocumentFormatSync --server=efm-langserver')
efm-langserver/config.yaml
version: 2
root-markers:
  - .git/
tools:
  prettier: &prettier
    format-command: 'npx --no-install prettier --stdin-filepath ${INPUT}'
    format-stdin: true
languages:
  typescript:
    - <<: *prettier
  typescriptreact:
    - <<: *prettier