Closed20

Nvim LSP周り

amttamtt

背景

  1. Denoで遊ぼうとしたらLSがインストールされていないことによるエラーが発生
  2. 今の構成どうなってる?→理解していない
  3. Cocとかあるけど何もわからず今の選定にしてるな
amttamtt

dependenciesに記述するだけでimportされる

公式ドキュメントによると

lazy.nvim automagically lazy-loads Lua modules. This means that if you have a plugin A that is lazy-loaded and a plugin B that requires a module of plugin A, then plugin A will be loaded on demand as expected.

「プラグインAがlazy-load対象で、プラグインBのdependenciesにプラグインAがあるならAはBがrequireされたときに読み込まれる」
よしなに遅延読込みしてくれるよ

amttamtt

build-in LSP

Neovim内蔵のLSP(nvim-lsp)を使っているらしい
昔はCoc.nvimとかが流行っていたが、built-inのエコシステムでも行けることが分かって乗り換える人多いっぽい

参考
https://zenn.dev/nazo6/articles/c2f16b07798bab

amttamtt

そのままだと使いづらいから、nvim-lspconfig.nvimmason.nvim, mason-lspconfig.nvimがセットで使われるのがスタンダードっぽい

amttamtt

mason-lspconfig.nvim

mason.nvimでインストールしたLSPサーバーを使うためのもの

初期インストールするLSの設定

        require("mason-lspconfig").setup({
            ensure_installed = { "lua_ls", "ts_ls" },
        })

MasonでインストールしたLSを一気にsetup()できて便利

        require("mason-lspconfig").setup_handlers({
            function(server_name)
                local opts = { capabilities = capabilities }

                if server_name == "lua_ls" then
                    opts.settings = {
                        Lua = {
                            runtime = { version = "LuaJIT", path = { "?.lua", "?/init.lua" } },
                            workspace = {
                                library = { vim.fn.stdpath("config") },
                                maxPreload = 1000,
                                preloadFileSize = 100,
                                checkThirdParty = false,
                            },
                            telemetry = { enable = false },
                        },
                    }
                end

                lspconfig[server_name].setup(opts)
            end
        })
amttamtt

参考記事のキーバインド設定のところで以下の書き方をしていた

vim.api.nvim_create_autocmd("LspAttach", {
    ...
})

LspAttachという書き方に覚えがあったのでその記事に飛んだ

LSPがアタッチしたBufferにのみキーバインドを設定できるようで便利(意訳)

なるほどわからん。調べよう

amttamtt

LSPの基礎知識とプラグインなしでLSPを動かしてみよう

https://zenn.dev/vim_jp/articles/40b2c348386f88
ubuntuにNvim入れて実験してみる

amttamtt

LSPとは

定義ジャンプや補完機能を提供するLS(Language Server)とそれを受け取るエディタとの通信の内容を取り決めたもの(プロトコル)

これによって、LS側とエディタ側にメリットが生まれる

  • LS側: エディタごとに機能を提供する必要がなくなり、楽
  • エディタ側: LSPの規格に沿ったクライアントを作っておけば全てのLSを利用できる
amttamtt

JSONでやりとりされてるらしい
RPCってでてきたぞ(Remote Procedure Call): 外部に実装された手続きを呼び出すこと

JSON RPCは、RPCに必要な記述をJSONで可能にしたやつ

ドキュメントもあるらしい
https://www.jsonrpc.org/specification

amttamtt

めっちゃ同期されてる

  • ファイル開いたよ!
  • 編集開始したよ!
  • 定義したよ!→定義ジャンプちょうだい!
  • ファイル閉じたよ!
amttamtt

特に coc.nvim は単なる LSP クライアントではなく nodeJS を利用した独自のエコシステムを構成しており、

そうなんだ

amttamtt

Ubuntuでパスを通す(アーキテクチャの確認も)

built-in LSPを試すところで少し手こずった

例として lua-language-server を起動してみます[2]。

  1. lua-language-serverは自分でインストールしないといけなかったのでUbuntu用を探したが見当たらない
  2. stackoverflowで同じような人発見

自前で.gzファイルをダウンロードしてパスを通すとできるらしい

やってみる

  1. Ubuntuのアーキテクチャを確認し、正しくダウンロード
uname -m
> x86_64
  1. ディレクトリ作成&解凍
mkdir -p ./local/share/lua-language-server

tar -x --file {ダウンロードしたパス} -av -C ./local/share/lua-language-server

tarのオプション

  • -x: 解凍
  • --file: 対象ファイルの指定
  • -a: ファイルの拡張子によってよしなにやってくれる(auto?)
  • -v: 詳細(verbose)
  • -C: 解凍場所(事前に作成)
  1. シンボリックリンクの作成
sudo ln -s $HOME/.local/share/lua-language-server/bin/lua-language-server /usr/bin/lua-language-server

/usr/bin/である必要はない(パスが通ってたのでここに入れただけ)
.local/bin/とかでもいい

  1. 確認
lua-language-server

色々出てきたら完了
amttamtt

nvim -u launch-test.lua launch-test.lua などとして Neovim を起動すると lua-language-server が起動されて diagnostics の表示が出るのが確認できると思います。

出ないんだよなー
上手くインストールできてないのかなとおもって、macの方でもbrewでlua-language-server入れて確認したけど、ubuntu同様diagnostics出なかった

↓これが原因だった

root_dir = vim.fs.dirname(vim.fs.find({ ".luarc.json" }, { upward = true })[1]), -- プロジェクトのルートディレクトリを検索する

ディレクトリを上から検索して.luarc.jsonが見つかればルートディレクトリとする
macとUbuntu両環境ともこのファイル作ってなかったので動かなかったらしい
内容空でtouchしたらdiagnosticsでた嬉しい

amttamtt

↓ファイルタイプがluaのとき
lua-language-serverの起動とか

vim.api.nvim_create_autocmd("FileType", {
  desc = "launch lua-language-server",
  pattern = "lua",
...
})

↓LSP起動したとき
キーバインドはどの言語にも共通なので

vim.api.nvim_create_autocmd("LspAttach", {
  desc = "Attach key mappings for LSP functionalities",
  callback = function ()
    vim.keymap.set('n', 'gd', '<cmd>:lua vim.lsp.buf.definition()<CR>')
    vim.keymap.set('n', 'gr', '<cmd>:lua vim.lsp.buf.references()<CR>')
    -- more mappings ...
  end
})
amttamtt

プロジェクトによってts_lsdenolsの切り替え

参考
https://zenn.dev/kawarimidoll/articles/2b57745045b225

  1. プロジェクトにdeno.json, deno.jsoncがあればdenoプロジェクトとみなす
lspconfig.lua
        local is_deno = function(fname)
            return lspconfig.util.root_pattern("deno.json", "deno.jsonc")(fname) ~= nil
        end
  • lspconfig.util.root_pattern("deno.json", "deno.jsonc")
    deno.jsondeno.jsoncがあるか探す関数を返す
  • (fname)
    現在のファイルの位置から上へ探していく
  • ~= nil
    存在してるかどうかでboolを返す

  1. denolsを適用する条件
lspconfig.lua
                if server_name == "denols" then
                    opts.root_dir = lspconfig.util.root_pattern("deno.json", "deno.jsonc")
  • opts.roor_dir
    denolsを適用するディレクトリを設定する

  1. ts_lsを適用する条件
lspconfig.lua
                elseif server_name == "ts_ls" then
                    opts.root_dir = function(fname)
                        if is_deno(fname) then
                            return nil
                        end
                        return lspconfig.util.root_pattern("package.json")(fname)
                    end
                    opts.single_file_support = false
  • if Denoのプロジェクト: root_dirをnilに
    • Denoプロジェクトでなければ、root_dirをpackage.jsonのあるディレクトリにする
  • opts.single_file_support = false
    • 単一ファイルでのLSPの起動はしないようにする
    • プロジェクト単位でLSPが起動するようにする

↓Gist(パーマリンク指定できないんだね..)
https://gist.github.com/nematatu/30abf548319a6cf10395e058631d059e

このスクラップは3ヶ月前にクローズされました