@ryoppippi

俺の denols/tsserver(vtsls) 共存術 for Neovim 2024

10 May 2024 ・ 6 min read


Note

この​記事はVim 駅伝の​ 5/10 の​記事です。

はじめに

Neovimの​LSPで​Denoの​LSP(denols) と​ tsserver(vtsls)を​共存させる​試みは​これまで​幾度と​なく​行われてきました。 LSPって​何?とか​問題意識を​詳しく​知りたい、と​いう方は​以下の​記事を​参照してください。

https://zenn.dev/kawarimidoll/articles/2b57745045b225 https://zenn.dev/mochi/articles/e6b2735108157c https://zenn.dev/vim_jp/articles/69d26e3f7b0e35

自分も​これまで​上の​記事を​参考にしながら​設定を​していたのですが、​いく​つか​不満点が​ありました。

  • node.jsの​monorepoの​一部で​denoを​使っている​場合、​denoの​LSPと​tsserver(vtsls)を​共存させるのが​難しい
  • これらの​設定では、single_file_support = falseに​してしまっているので、​例えば​書き捨ての​typescriptファイルを​開いた​時に​LSPが​効かない

そこで、​自分なりの​denoと​tsserver(vtsls)の​共存術を​紹介します。

判定ロジックに​ついて

先行研究と​してのkyoh86氏の​記事を​ベースに​以下のような​ロジックを​組みました。

  1. 現在開いている​Bufferの​ある​ディレクトリから、​deno関連の​ファイル^`deno.json` `deno.jsonc` `package.json` など。​`package.json`が​denoで​サポートされているのも​やや​こしい​ https://github.com/ryoppippi/dotfiles/blob/1c5bb5dcf827f4ca08120b6618031bd845f0ebde/nvim/lua/plugin/nvim-lspconfig/utils.lua#L104-L109 or node特有の​ファイル^`package-lock.json`など​ https://github.com/ryoppippi/dotfiles/blob/1c5bb5dcf827f4ca08120b6618031bd845f0ebde/nvim/lua/plugin/nvim-lspconfig/utils.lua#L111-L118 or git root^`.git`ディレクトリ が​見つかるまで​階層を​上がる。
  2. もし​その​階層に​node特有の​ファイルが​見つから​なかった​場合 -> denols を​起動。​tsserverは​落とす。
  3. もし node特有の​ファイルが​あるが、.vscode/settings.json 内で​許可されていた​場合 -> denols を​起動。​tsserverは​落とす。
  4. それ以外の​場合 -> denolsのroot_dirnilを​設定しSingle file modeで​起動。​もしtsserverが​起動していた​場合は​落とす。

と​しています。

具体的な​コードは​以下の​通り。

https://github.com/ryoppippi/dotfiles/blob/05ae64a18c348dd7989f1e67a98864948489f537/nvim/lua/plugin/nvim-lspconfig/servers/denols.lua#L6-L63

https://github.com/ryoppippi/dotfiles/blob/05ae64a18c348dd7989f1e67a98864948489f537/nvim/lua/plugin/nvim-lspconfig/servers/vtsls.lua#L43-L73

ロジックの​補足に​ついて​諸々

階層を​上がってファイルを​探す方​法

以前は​kyoh86氏のclimbdir.nvimを​愛用していましたが、​今回の​実装では​Neovimの​lua関数であるvim.fs.rootを​使っています。

vim.fs.rootについて

https://neovim.io/doc/user/lua.html#vim.fs.root()

vim.fs.root({source}, {marker}) _vim.fs.root()_
Find the first parent directory containing a specific "marker", relative
to a buffer's directory.

    Example: >lua
        -- Find the root of a Python project, starting from file 'main.py'
        vim.fs.root(vim.fs.joinpath(vim.env.PWD, 'main.py'), {'pyproject.toml', 'setup.py' })

        -- Find the root of a git repository
        vim.fs.root(0, '.git')

        -- Find the parent directory containing any file with a .csproj extension
        vim.fs.root(0, function(name, path)
          return name:match('%.csproj$') ~= nil
        end)

<

    Parameters: ~
      • {source}  (`integer|string`) Buffer number (0 for current buffer) or
                  file path to begin the search from.
      • {marker}  (`string|string[]|fun(name: string, path: string): boolean`)
                  A marker, or list of markers, to search for. If a function,
                  the function is called for each evaluated item and should
                  return true if {name} and {path} are a match.

    Return: ~
        (`string?`) Directory path containing one of the given markers, or nil
        if no directory was found.
これで、指定したファイルが見つかるまで階層を上がることができます。

また、​ファイル名を​指定して​その​存在を​確認するには​ vim.uv.fs_stat を​使うことができます。 fs_statの​戻り値がnilで​あれば​ファイルが​存在しない​ことを​示します。

.vscode/settings.jsonの​読み込み

denoの​vscode向け拡張機能は.vscode/settings.jsonでの​workspace設定を​サポートしています。

https://docs.deno.com/runtime/manual/references/vscode_deno/#workspace-folders

ここでは

  • deno.enable - denolsを​許可するか​どうか
  • deno.enablePaths - 許可する​ディレクトリパスの​リスト

を​設定する​ことができます。

しかし、​これは​標準では​Neovimでは​対応していません。

そこで、​neoconf.nvimを​使います。 https://github.com/folke/neoconf.nvim

neoconf.nvimは​json形式で​LSPの​設定を​行い、​また​それを​読み込むことができます。 また​ .vscode/settings.json も​サポートされています。

例えば​プロジェクトの​ルートに​ .vscode/settings.jsonを​置いて​以下のように​設定し、

{
	"deno.enable": true,
	"deno.enablePaths": ["./apps/deno-project"]
}

それを​読み込むように​設定する​ことができます。

こう​すれば​明示的に​プロジェクトごとに​deno or tsserverの​使い分けが​できるようになります。 また、​副次的に​vscodeユーザーと​共同作業する​際にも​便利です。

以下に​読み取りの​実装例を​示します。

https://github.com/ryoppippi/dotfiles/blob/05ae64a18c348dd7989f1e67a98864948489f537/nvim/lua/plugin/neoconf.lua#L7-L35 https://github.com/ryoppippi/dotfiles/blob/05ae64a18c348dd7989f1e67a98864948489f537/nvim/lua/plugin/nvim-lspconfig/servers/denols.lua#L27-L30

おわりに

今後も​色々試行錯誤していく​予定ですが、​この​方法で​denoと​tsserver(vtsls)を​共存させる​ことができるようになりました。 もし​何か​問題点や​改善点が​あれば、​ぜひ教えてください。

comment on bluesky / twitter
CC BY-NC-SA 4.0 2022-PRESENT © ryoppippi