2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

NixOSでmason.nvimで入れたLSP serverを使えるようにする

Last updated at Posted at 2023-01-28

概要

mason.nvimはNeovim向けのLSP serverマネージャーで、OSに応じてビルド済みバイナリ取ってきたり、コンパイルを勝手にやってくれます。
ビルド済みバイナリはライブラリへのリンクがハードリンクでありFHS(Filesystem Hierarchy Standard)に準拠していないNixOSでは基本的にそのまま使うことができません。
この問題は2023/01/28現在、issue#428で議論されています。
ここではNixOSでmason.nvimで取ってきたLSP serverを使う方法を記します。

なお、mason.nvimを使わなくてもnixpkgsでLSP serverを管理する方法もあります。
長所と短所は以下の通りです。

  • mason.nvim
    • 長所
      • linux, mac, windowsでコードを共用できる
      • 常に最新版のパッケージが使える
      • グラフィカルにインストール状況がNeovimから参照できる
    • 短所
      • 最新のパッケージのbreaking changeを踏む可能性が高くなる
      • NixOSではpatchを当てないと使えないパッケージがある
        • mason.nvim上ではインストール完了扱いになっているためLSP起動時まで使えるか分からない
  • nixpkgs
    • 長所
      • インストールすれば即使える状態になっている
      • linuxとmacではnixpkgs周りのコードを共用できる
    • 短所
      • channelによっては更新が遅い
      • パッケージが長くメンテナンスされていないことがある
        • ArchLinuxと違ってNixOSでは更新が止まっているpkgsが散見される
      • windowsでは使えない

パッチの当て方

パッチはpatchelfを使って当てます。(事前にインストールが必要)
patchelfの使い方は下に書いた。

mason-registryでインストール後にhookが起動するようにし、そこでハードリンクを修正します。

local mason = require("mason")
local mason_lsp = require("mason-lspconfig")

-- Make mason packages work with nixos
-- We're using patchelf to mathe that work
-- Thanks to: https://github.com/williamboman/mason.nvim/issues/428#issuecomment-1357192515
local function return_exe_value(cmd)
    local handle = io.popen(cmd)
    local result = handle:read("*a")
    handle:close()

    return result
end

local mason_registry = require("mason-registry")
if is_linux and vim.api.nvim_exec("!cat /etc/os-release | grep '^NAME'", true):find("NixOS") ~= nil then
    mason_registry:on("package:install:success", function(pkg)
        pkg:get_receipt():if_present(function(receipt)
            -- Figure out the interpreter inspecting nvim itself
            -- This is the same for all packages, so compute only once
            -- Set the interpreter on the binary
            local nvim = return_exe_value("nix path-info -r /run/current-system | grep neovim-unwrapped"):sub(1, -2)
            local interpreter = return_exe_value(("patchelf --print-interpreter %q" .. "/bin/nvim"):format(nvim)):sub(1, -2)
            for _, rel_path in pairs(receipt.links.bin) do
                local bin_abs_path = pkg:get_install_path() .. "/" .. rel_path
                if pkg.name == "lua-language-server" then
                    bin_abs_path = pkg:get_install_path() .. "/extension/server/bin/lua-language-server"
                    os.execute(("patchelf --set-interpreter %s %s"):format(interpreter, bin_abs_path))
                elseif pkg.name == "marksman" then
                    bin_abs_path = pkg:get_install_path() .. "/marksman"
                    local libstdcpp = return_exe_value("nix path-info -r /run/current-system | grep gcc | grep lib | head -n1"):sub(1, -2) .. "/lib"
                    local zlib = return_exe_value("nix path-info -r /run/current-system | grep zlib | head -n1"):sub(1, -2) .. "/lib"
                    local icu4c = return_exe_value("nix path-info -r /run/current-system | grep icu4c | head -n1"):sub(1, -2) .. "/lib"
                    os.execute(("patchelf --set-interpreter %s --set-rpath %s:%s:%s %s"):format(interpreter, libstdcpp, zlib, icu4c, bin_abs_path))
                elseif pkg.name == "stylua" then
                    bin_abs_path = pkg:get_install_path() .. "/stylua"
                    os.execute(("patchelf --set-interpreter %s %s"):format(interpreter, bin_abs_path))
                elseif pkg.name == "texlab" then
                    bin_abs_path = pkg:get_install_path() .. "/texlab"
                    os.execute(("patchelf --set-interpreter %s %s"):format(interpreter, bin_abs_path))
                end
            end
        end)
    end)
end

patchelfの使い方

pathelfの基本的な使い方は以下の通り(詳しい説明はNixOS wiki: Packaging/Binaries

  1. patchelf --print-interpreter <nixpkgsでインストールした適当なバイナリ>
  2. patchelf --set-interpreter <1.で出力されたinterpreterのパス> <バイナリ>
    2.で動作しない場合
  3. patchelf --print-needed <バイナリ>で依存ライブラリを列挙
  4. fd <ライブラリ> /run/current-systemなどで依存ライブラリを探す
  5. patchelf --set-interpreter <1.で出力されたinterpreterのパス> --set-rpath <lib path 1>:<lib path 2>:... <バイナリ>

まとめ

patchelfを使ってmason.nvimでインストールしたLSP serverをNixOSで動作させる方法を記しました。
LSPを動作させるまでNixOS上で実行可能かどうか分からず、都度パッチを当てる必要があることからnixpkgsで管理した方が幸せになれる気もします。
(そうすると今度はwindowsためにコードを書かないといけないという...)

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?