🥵

rubyLspをモノレポで動かす

2024/06/23に公開

問題

以下のようなモノレポの構成でrubyLspを動かしたい

my_project
├── backend
└── frontend

このとき.vscode/settings.jsonは以下のようになっているが、rubyLspによるフォーマットや補完などが効いている様子がない。

.vscode/setting.json
{
	"rubyLsp.formatter": "rubocop",
	"rubyLsp.rubyVersionManager": {
		"identifier": "rbenv"
	},
	"[ruby]": {
		"editor.formatOnSave": true,
		"editor.defaultFormatter": "Shopify.ruby-lsp"
	}
}

原因

workspaceごとにrubyやgemのバージョンが異なったりするかもしれないため、rubyLspはworkspaceごとに単一のlanguage serverを立てる。
rubyLspは各WorkspaceのルートにあるGemfileを参照していると思われるが、モノレポだとGemfileはルートにはないので動かなかった(のだと思っている)

https://github.com/Shopify/ruby-lsp/blob/main/vscode/README.md?tab=readme-ov-file#multi-root-workspaces

解決方法

以下のどちらかで解決できそうだった。

  • vscodeのmulti-root workspace機能を使う
  • devcontainer内で作業する

vscodeのmulti-root workspace機能を使う

vscodeにはmulti-root workspaceという機能があり、(任意の名前).code-workspaceといったファイルを用意することで以下のようなことが実現できる。

  • モノレポ内の1つ1つのフォルダをrootフォルダー的な扱いをする
  • workspace全体で共通の設定を記述できる
  • 各rootフォルダー内で更に.vscode/settings.jsonを作ることで、共通の設定を上書きできる

https://code.visualstudio.com/docs/editor/workspaces#_multiroot-workspace-settings:~:text=Multi-root workspace settings

rubyLspを動かすには.code-workspaceには以下のような内容を記述すればいい

.code-workspace
{
	"folders": [
		{
			"name": "my_project",
			"path": "."
		},
		{
			"name": "frontend",
			"path": "./frontend"
		},
		{
			"name": "backend",
			"path": "./backend"
		}
	],
	"settings": {
		// name: my_project には通常そのサブディレクトリであるfrontend、backendディレクトリも表示されてしまう
		// これらはそれぞれ個別に表示しているものと重複してしまうので、name: my_projectの方には表示しないようにする
		"files.exclude": {
			"frontend": true,
			"backend": true
		},
		"rubyLsp.formatter": "rubocop",
		"rubyLsp.rubyVersionManager": {
			"identifier": "rbenv"
		},
		"[ruby]": {
			"editor.formatOnSave": true,
			"editor.defaultFormatter": "Shopify.ruby-lsp"
		}
	}
}

devcontainer内で作業する

基本的には以下とかを参考にすればいいが、vscodeのgitの機能を使うためにルートを含めたりしなかったり、ビルドを毎回しないようにするための設定をする必要がある。

https://github.com/Shopify/ruby-lsp/blob/main/vscode/README.md?tab=readme-ov-file#developing-on-containers

以下のようにdevcontainer用のcompose.yamlを別で用意する。

compose.devcontainer.yaml
services:
  frontend:
    volumes:
      - .:/workspace:cached # .gitなどもdevcontainer内に含める

  backend:
    volumes:
      - .:/workspace:cached

さらに以下のようにdevcontainer.jsonを記載する

devcontainer.json
{
    ...
    "dockerComposeFile": [
        // compose.yamlをcompose.devcontainer.yamlで上書きする
        // これはdocker-compseの -f オプションで複数指定したときの挙動に相当する
        "../../compose.yaml",
        "../../compose.devcontainer.yaml"
    ],
    "shutdownAction": "none" // devcontainer間でswitchするときにcontainerを停止しない
    "customizations": {
        "vscode": {
            "extensions": [
                "Shopify.ruby-lsp",
            ],
            "settings": {
                "rubyLsp.formatter": "rubocop",
                "rubyLsp.rubyVersionManager": {
                    "identifier": "rbenv"
                },
                "[ruby]": {
                    "editor.defaultFormatter": "Shopify.ruby-lsp",
                    "editor.formatOnSave": true
                },
            }
        }
    },
    ...
}

最後に

ゴリ押し感があるので、なんかいい方法がある気がしている

Discussion