mason.nvim にインストールしたいパッケージを追加する

:::note info

この記事は [Vim 駅伝](https://vim-jp.org/ekiden/)の 11/22 の記事です。前回は staticWagomU さんによる [fall.vimいいぞ - 輪ごむの空き箱](https://wagomu.me/blog/2024-11-20-vim-ekiden)でした。

:::

## mason.nvim、使ってますか？

<img width="495" alt="スクリーンショット 2024-11-11 23.20.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8880/99655db2-50e3-5cf5-6960-9f308cd08e3d.png">

[mason.nvim](https://github.com/williamboman/mason.nvim) は Neovim で Language Server / Linter / Formatter を管理するツールです。分かり易い UI と共に、これらのツールのインストールや更新が可能で、インストールされたツールは mason.nvim 専用のフォルダに設置され環境を汚すことがありません。

mason.nvim はインストールできる各種ツールを、“package registry” という仕組みで管理しています。インストールできるパッケージの一覧は、以下のサイトから検索することができます。

> [Package list | mason-registry.dev](https://mason-registry.dev/registry/list)
> レポジトリはこちら → [mason-org/mason-registry: Core registry for mason.nvim.](https://github.com/mason-org/mason-registry/tree/main)

:::note warn

この記事では独自のパッケージを mason.nvim に追加して管理する方法を解説します。mason.nvim 自体の使い方については特に説明しませんので README や `:h mason.nvim` を参照してください。

:::


## 独自のパッケージを追加したい

通常は、mason-registry.dev に挙げられているパッケージを使えば不自由は無いのですが、以下のような場合に独自のパッケージをインストールしたくなります。

* mason-registry にまだ登録されていないツールを試したい。
* 登録されているバージョンとは違うものを使いたい。
* 外部に公開しない独自のツールを mason.nvim で管理したい。

このような用途のために、mason.nvim は package registry を複数同時に管理できるようになっています。

```lua
require("mason").setup {
  registries = {
    -- デフォルトの値です。
    "github:mason-org/mason-registry",
  },
}
```

このように、デフォルトでは公式の mason-registry のみが登録されています。ここに自前の package registry を追加してみましょう。

## 自前で package registry を用意する

package registry としては以下の三種が作成できます。

1. [Lua registry](https://github.com/williamboman/mason.nvim/blob/e2f7f9044ec30067bc11800a9e266664b88cda22/lua/mason-registry/sources/lua.lua)
2. [GitHub registry](https://github.com/williamboman/mason.nvim/blob/e2f7f9044ec30067bc11800a9e266664b88cda22/lua/mason-registry/sources/github.lua)
3. [File registry](https://github.com/williamboman/mason.nvim/blob/e2f7f9044ec30067bc11800a9e266664b88cda22/lua/mason-registry/sources/file.lua)

“1. Lua registry” は公式の mason-registry が作られる前に利用されていたもので、今から積極的に使いたくなることはないでしょう。

“2. GitHub registry” は mason-registry を指定するデフォルトのオプションで利用されています。これを使えば外に公開したくないツールを配布する package registry を用意できます（GitHub Enterprise も利用可能です）。

今回は “3. File registry” を使ってみます。管理したい独自のツールが増えてきたり、他の開発者と共有したくなったら “2. GitHub registry” に移行すると良いでしょう。

## File registry の作り方

### 今回のモチベーション

そもそも、どうしてこれが必要になったのかを説明していませんでした。僕は [PerlNavigator](https://github.com/bscan/PerlNavigator) を mason.nvim でインストールして使っています。今回この Language Server にバグを見付けてコントリビュートしました。

> [fix: show absolute paths by delphinus · Pull Request #148 · bscan/PerlNavigator](https://github.com/bscan/PerlNavigator/pull/148)

この Language Server は TypeScript で書かれており、通常は NPM でインストールします。ですが、今回はまだ僕の修正がマージ・リリースされていませんので、GitHub のレポジトリーから直接インストールして使いたいです。

### mason.nvim の設定

registry を作る前に、mason.nvim の設定から書いておきます。

```lua
require("mason").setup {
  registries = {
    "file:~/.config/nvim/mason",
    "github:mason-org/mason-registry",
  },
}
```

`"file:~/.config/nvim/mason"` というのが今回作成する File registry です。`registries` オプションに指定する値は順番が大切で、同じ名称のパッケージが複数の registry に存在する場合は先に指定したものが有効になります。今回追加したい `perlnavigator` パッケージは mason-registry にも存在しますから、それより先に追加しないといけません。

### インストールしたい package の指定

次に、`~/.config/nvim/mason/packages/perlnavigator/package.yaml` というファイルを作成します。ここにインストールしたいツールの情報を記述します。この YAML ファイルの仕様は mason-registry の [CONTRIBUTING.md](https://github.com/mason-org/mason-registry/blob/9d980747162934a1f9caa32df7eb17a26e37bbd0/CONTRIBUTING.md) にまとめられています。

```yaml
---
name: perlnavigator
description: Perl Language Server that includes perl critic and code navigation.
homepage: https://github.com/bscan/PerlNavigator
licenses:
  - MIT
languages:
  - Perl
categories:
  - LSP

source:
  id: pkg:github/delphinus/PerlNavigator@fix/detect-relative-modules
  build:
    run: |
      npm install
      npm run compile

schemas:
  lsp: vscode:https://raw.githubusercontent.com/delphinus/PerlNavigator/refs/heads/fix/detect-relative-modules/package.json

bin:
  perlnavigator: node:server/out/server.js
```

mason-registry にある[オリジナル](https://github.com/mason-org/mason-registry/blob/9d980747162934a1f9caa32df7eb17a26e37bbd0/packages/perlnavigator/package.yaml)と diff を取ってみると次のようになります。

```diff
--- /Users/jinnouchi.yasushi/git/github.com/mason-org/mason-registry/packages/perlnavigator/package.yaml        2024-11-11 21:43:25
+++ /Users/jinnouchi.yasushi/.config/nvim/mason/packages/perlnavigator/package.yaml     2024-11-11 21:50:42
@@ -10,10 +10,14 @@
   - LSP
 
 source:
-  id: pkg:npm/perlnavigator-server@0.8.15
+  id: pkg:github/delphinus/PerlNavigator@fix/detect-relative-modules
+  build:
+    run: |
+      npm install
+      npm run compile
 
 schemas:
-  lsp: vscode:https://raw.githubusercontent.com/bscan/PerlNavigator/v{{version}}/package.json
+  lsp: vscode:https://raw.githubusercontent.com/delphinus/PerlNavigator/refs/heads/fix/detect-relative-modules/package.json
 
 bin:
-  perlnavigator: node:node_modules/perlnavigator-server/out/server.js
+  perlnavigator: node:server/out/server.js
```

重要なのは `source` の指定です（[仕様へのリンク](https://github.com/mason-org/mason-registry/blob/9d980747162934a1f9caa32df7eb17a26e37bbd0/CONTRIBUTING.md#source)）。この `source.id` プロパティが最も重要で、この文字列から得られる情報を元にパッケージのインストールが行われます。ここに使われるのは purl（package URL）という形式の文字列です。

> [package-url/purl-spec: A minimal specification for purl aka. a package "mostly universal" URL, join the discussion at https://gitter.im/package-url/Lobby](https://github.com/package-url/purl-spec?tab=readme-ov-file)

デフォルトでは `pkg:npm/……` という指定により、NPM からパッケージがインストールされます。僕の設定では `pkg:github/……` となっていますので、GitHub からインストールするべきことが示されています。

より一般的な例は CONTRIBUTING.md の [GitHub build from source](https://github.com/mason-org/mason-registry/blob/9d980747162934a1f9caa32df7eb17a26e37bbd0/CONTRIBUTING.md#github-build-from-source) を見ると分かり易いでしょう。

:::note info

purl はパッケージを一意に特定する文字列であり、それを如何に処理すべきかは各アプリケーション（今回は mason.nvim）に委ねられています。mason.nvim はパッケージのタイプ（`npm`, `github`, `gem`, `nuget` 等々）毎にインストールする方法を `lua/mason-core/installer/managers` というディレクトリで管理しています。

[mason.nvim/lua/mason-core/installer/managers at e2f7f9044ec30067bc11800a9e266664b88cda22 · williamboman/mason.nvim](https://github.com/williamboman/mason.nvim/tree/e2f7f9044ec30067bc11800a9e266664b88cda22/lua/mason-core/installer/managers)

:::

## File registry からのインストール

これで registry の作成は完了です。mason.nvim は YAML ファイルの parse に [yq](https://github.com/mikefarah/yq) を使っておりますので、事前にインストールしておきましょう。

```bash
# macOS の場合
brew install yq
```

Neovim を起動して `:Mason` を開くと、PerlNavigator にアップデートがあるような記述が見えます。<kbd>Shift</kbd>+<kbd>U</kbd> を押すと、File registry に指定したバージョンがインストールされるはずです。

<img width="671" alt="スクリーンショット 2024-11-11 23.23.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8880/c21304b3-56fc-837b-b5dc-f635b2ea67fd.png">

上記の設定では `source.id` に `……@fix/detect-relative-modules` と、ブランチ名をバージョンのように使っています。そのためブランチを更新したとしても mason.nvim で更新が検知できません。本来ならばタグやコミットハッシュを指定するのが良いでしょうが、今回はそこまで作り込みませんでした。

## 終わりに

改めて mason.nvim の動作について調べてみたのですが、予想以上に大規模なプラグインでした。これはもうプラグインというより、Neovim 上でのツール管理のためのエコシステムと言っていいでしょう。毎度のことながら、これほどのシステムを OSS として開発してくれる有志には頭が上がりませんね。