🛠️

Neovim Lua のための LuaLS セットアップ

2024/01/02に公開

はじめに

Neovim には Lua が組込まれており、設定ファイル (init.lua) やプラグインを Lua で書けます。
しかし Lua の言語機能はなかなか貧弱であり、LSP のサポートがないと体験は悪いです。
ですので sumneko 氏が開発しているこちらの Language server (以下 LuaLS と表記) を入れている方は多いでしょう。

https://github.com/LuaLS/lua-language-server

今回は LuaLS をちゃんと設定し、vim.api やプラグインの補完などをバリバリ効くようにして快適にしていきます。

ターゲット

この記事の目的は LuaLS をいい感じに設定することだけです。
nvim-lspconfig や mason.nvim などには触れません。

参考元

公式のドキュメントはこちら。

https://luals.github.io/wiki/

型の付け方

LuaLS に型情報を伝えるには Annotations と呼ばれるコメント記法を使います。
ここを見てください。

https://luals.github.io/wiki/annotations/

設定方法

LuaLS の設定はここに一覧があります。

https://luals.github.io/wiki/settings/

StyLua を使うから LuaLS の formatter は無効にしたい、だとか semantic highlight 要らないから切りたい、などの設定ができます。

そして今回の肝になるのは workspace.library です。

lazy.nvim の場合

先にコードを載せます。

全てのプラグインを有効化する場合は以下のようにします。
ただし遅延起動を設定しており、このタイミングでまだ読み込まれていないプラグインは見つからないことに注意してください。

lspconfig.lua_ls.setup({
  settings = {
    Lua = {
      runtime = {
        version = "LuaJIT",
        pathStrict = true,
        path = { "?.lua", "?/init.lua" },
      },
      workspace = {
        library = vim.list_extend(vim.api.nvim_get_runtime_file("lua", true), {
          "${3rd}/luv/library",
          "${3rd}/busted/library",
          "${3rd}/luassert/library",
        }),
        checkThirdParty = "Disable",
      },
    },
  },
})

一部のプラグインだけを有効化したい場合は以下のようにします。

get_plugin_paths() はプラグインマネージャーによって実装が変わります。
筆者は lazy.nvim を使っています。

このようにプラグインマネージャーを経由すれば、遅延起動を設定しているプラグインであっても見つけられます。

local lspconfig = require("lspconfig")

---@param names string[]
---@return string[]
local function get_plugin_paths(names)
  local plugins = require("lazy.core.config").plugins
  local paths = {}
  for _, name in ipairs(names) do
    if plugins[name] then
      table.insert(paths, plugins[name].dir .. "/lua")
    else
      vim.notify("Invalid plugin name: " .. name)
    end
  end
  return paths
end

---@param plugins string[]
---@return string[]
local function library(plugins)
  local paths = get_plugin_paths(plugins)
  table.insert(paths, vim.fn.stdpath("config") .. "/lua")
  table.insert(paths, vim.env.VIMRUNTIME .. "/lua")
  table.insert(paths, "${3rd}/luv/library")
  table.insert(paths, "${3rd}/busted/library")
  table.insert(paths, "${3rd}/luassert/library")
  return paths
end

lspconfig.lua_ls.setup({
  settings = {
    Lua = {
      runtime = {
        version = "LuaJIT",
        pathStrict = true,
        path = { "?.lua", "?/init.lua" },
      },
      workspace = {
        library = library({ "lazy.nvim", "nvim-insx" }),
        checkThirdParty = "Disable",
      },
    },
  },
})

解説

workspace.library はパスの配列で、ライブラリの場所を示します。

library() 関数は指定したプラグインのパスを検索し、更に Neovim や LuaLS の組込みライブラリへのパスを追加して返します。

LuaLS は workspace.library の各ディレクトリに対して runtime.path のパターンに一致するファイルを読み込みます。
このとき runtime.pathStrict を設定しておかないと、ファジーに検索されるので上手くいかないことがあります。

例: nvim-cmp の場合 lua/cmp/init.lualua/cmp/types/cmp.lua があり、require("cmp") がどちらにもマッチしてしまうので library.path の順序から lua/cmp/types/cmp.lua が優先されてしまいます。

これを回避するため runtime.pathStrict を有効化し、プラグインのルートに /lua を追加したパスを返すようにします。

vim.fn.stdpath("config") はユーザーの設定ファイルの配置場所です。
自分で定義した便利関数などの補完が効くようになります。

vim.env.VIMRUNTIMEdoc/ftplugin/ などがインストールされたパスです。
これを追加することで vim.fn.*vim.api.* などの補完が効くようになります。

https://github.com/neovim/neovim/tree/master/runtime/lua/vim/_meta

${3rd} は LuaLS 組込みのライブラリへのパスに展開されます。

bustedluassert はテスト用のライブラリなので init.lua を書くだけなら不要かと思います。
プラグイン開発者向けの設定です。
luvvim.uv (旧 vim.loop) なので、入れておいた方が便利かと思います。

ただ、これは将来的に削除されるらしいです。

https://luals.github.io/wiki/addons/#built-in-addons

終わり

快適な Lua ライフを!

GitHubで編集を提案

Discussion