序
ArchlinuxのVSCode(Code OSS (code)やVSCodium (codium))でSolargraphを使うと
Caching gem
の表示が出続け、全く進捗せずCPUだけを浪費するという問題がある。
これはSolargraphの問題というわけではなく、Ruby-LSPを使っていてもそれなりに問題がある。
また、
bundle exec solargraph gems --rebuildとしても落ちる。
これは、大きくわけて2つの問題があり、そのうち1つはArchlinuxに固有の問題(Manjaro等にも引き継がれている)である。
今回のコードはCodebergに置いてある。
ArchlinuxのRubyパッケージの問題
概要
Archlinuxのrubyパッケージはかなり問題のあるもので、目立つ点としてはかなり長い間EOLになった3.0に留まって放置されていたり、現在も3.4系のままだったりする。
そして、分割パッケージングしているのにruby-default-gemsはすべてのDefault Gemを含んでいないし、ruby-bundled-gemsに至ってはbundlerしか含んでいない。
今回の問題が起きる原因ものパッケージングのまずさにある。
例としてabbrevを挙げよう。
ruby-abbrevはDefault Gemで、Archlinuxでは分割パッケージになっている。
そして、その定義は/usr/lib/ruby/gems/3.4.0/specifications/default/abbrev-0.1.2.gemspecにある。
# -*- encoding: utf-8 -*-
# stub: abbrev 0.1.2 ruby lib
Gem::Specification.new do |s|
s.name = "abbrev".freeze
s.version = "0.1.2".freeze
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.metadata = { "homepage_uri" => "https://github.com/ruby/abbrev", "source_code_uri" => "https://github.com/ruby/abbrev" } if s.respond_to? :metadata=
s.require_paths = ["lib".freeze]
s.authors = ["Akinori MUSHA".freeze]
s.bindir = "exe".freeze
s.date = "2025-03-22"
s.description = "Calculates a set of unique abbreviations for a given set of strings".freeze
s.email = ["knu@idaemons.org".freeze]
s.files = [".github/dependabot.yml".freeze, ".github/workflows/test.yml".freeze, ".gitignore".freeze, "Gemfile".freeze, "LICENSE.txt".freeze, "README.md".freeze, "Rakefile".freeze, "abbrev.gemspec".freeze, "bin/console".freeze, "bin/setup".freeze, "lib/abbrev.rb".freeze]
s.homepage = "https://github.com/ruby/abbrev".freeze
s.licenses = ["Ruby".freeze, "BSD-2-Clause".freeze]
s.required_ruby_version = Gem::Requirement.new(">= 2.3.0".freeze)
s.rubygems_version = "3.6.2".freeze
s.summary = "Calculates a set of unique abbreviations for a given set of strings".freeze
endだが、 対応する/usr/lib/ruby/gems/3.4.0/gems/abbrev-0.1.2はない。
普通に/usr/lib/ruby/3.4.0/abbrev.rbに直置きされている。
このため、Solargraphはabbrevをドキュメンテーションしようとするが、あるべきファイルがないので失敗する。
解決策
ArchlinuxのRubyはパッケージングが腐っているので、システムパッケージを使わないのが一番簡単な解決策。
人気があるのはrbenvだと思うが、rbenvは使いづらすぎて私は好きではない。 asdfという選択肢もあるけれど、asdfもうまく動かないことがそれなりにある。
私はのおすすめはRust製のmiseだ。extraリポジトリにある。 Ruby以外も様々な処理系を扱うことができる。
まずは導入する
pacman -S mise欲しい処理系のインストール。自前ビルドとプレビルドが選べる。 またメジャーバージョンのみ指定、マイナーバージョンまでの指定、リビジョン/パッチバージョン指定まで任意にできる。
mise install ruby@4.0ユーザーデフォルトを指定。
これは~/.config/mise/config.tomlに書かれる。
mise use -g ruby@4.0-gをつけない場合はカレントディレクトリにmise.tomlが書かれる。
これが許容できるならプロジェクトルートに置くことでバージョン指定が可能。
ただ、miseはGemfileや.ruby-versionを読むので実際に必要な場合は少ない。
これを利用するにはmiseの環境変数が読まれている必要がある。
Zshならrcファイルにこんな感じ
eval "$(mise activate zsh)"端末から起動するならこれでいいけれど、Nemo Actionsを使うには?
mise execがmiseをロードした上で実行してくれるので、こうすればいい
mise exec -- code -n %P今回は決め打ちを避けたいし、今までのcode/codiumの分岐もあるから、スクリプトにしてしまおう。
[Nemo Action]
Name=Open VSCode
Comment=Visual Studio Code(ly) open on here
Exec=<abstract-code.zsh %P>
Icon-Name=code
Selection=Any
Extensions=any;abstract-code.zshはactionsディレクトリに置いて実行権限を与える。
ついでに、Ruby-LSPを使いたい場合もサポート。
#!/bin/zsh
profile_opt=()
code_cmd=code
# includes Ruby-LSP?
if [[ -f Gemfile.lock ]]
then
if grep -E -q "^\s*ruby-lsp \(" Gemfile.lock 2>/dev/null && [[ -e "$HOME/.config/vscode_rubylsp" ]]
then
profile_opt+=(--user-data-dir "$HOME/.config/vscode_rubylsp")
fi
fi
if (( $+commands[codium] ))
then
code_cmd=codium
fi
if (( $+commands[mise] ))
then
mise exec -- $code_cmd $profile_opt -n "$@"
else
$code_cmd $profile_opt -n "$@"
fi一度
code --user-data-dir=$HOME/.config/vscode_rubylspのようにしてから、
~/.config/vscode_rubylsp/User/settings.json -> ~/.config/Code - OSS/User/settings.json
みたいなシンボリックリンクにすればいい。あとは拡張機能のインストールで使い分けることができる。
bundle vs gem問題
概要
実はこれでもまだうまく動かない。ここから先はディストリビューションに依存しない問題だ。
実はSolargraphに限ってみてもプロジェクトは次のパターンがある。
- bundlerを使っていて、GemfileにSolargraphが含まれている
- bundlerを使っているが、GemfileにSolargraphが含まれていない
- bundlerを使っておらず、ユーザー環境のgemを使っている
- bundlerを使っておらず、ユーザー環境のgemも使っていない
3, 4のケースでは単にmiseを使えば解決する。
一方、それでは$GEM_PATHの問題でbundlerでインストールされているファイルが読めないので、1, 2のケースで問題が起きる。
1のケースだけを考えるのであればVSCodeのSolargraph拡張の設定からuseBundler: trueすればいい話なのだが、それだと2から4までがカバーできない。
特定の形式のプロジェクトしか触らないという想定は相当考えが浅いが、現状VSCodeはそうなっている。
そして、2のケースが最も難しい。
解決策
Geminiが完璧なコードを書いてくれたので、そのまま掲載。
#!/bin/bash
# 1. すでにVSCode側(ワークスペース設定など)が bundle exec を付与して呼び出している場合
if [ -n "$BUNDLE_BIN_PATH" ] || [ -n "$BUNDLE_GEMFILE" ]; then
exec solargraph "$@"
fi
if [ -f "Gemfile.lock" ]; then
# Gemfile.lock に solargraph 本体が含まれているかチェック
if grep -E -q "^\s*solargraph \(" Gemfile.lock 2>/dev/null; then
# パターンA: Bundler + Solargraphあり
# Gemfileに含まれているならbundle execで起動するのが確実
exec bundle exec solargraph "$@"
else
# パターンB: Bundler + Solargraphなし(★問題のケース)
# Bundler管理下のgemパスだけを抽出し、環境変数 GEM_PATH に追加する
BUNDLED_GEM_PATH=$(bundle exec ruby -e 'puts Gem.path.join(":")' 2>/dev/null)
echo $BUNDLED_GEM_PATH > ~/tmp/sol
if [ $? -eq 0 ] && [ -n "$BUNDLED_GEM_PATH" ]; then
export GEM_PATH="$BUNDLED_GEM_PATH:$GEM_PATH"
fi
# グローバルの(miseの)Solargraphを、BundlerのGEM_PATHが見える状態で起動
exec solargraph "$@"
fi
else
# パターンC & D: Bundlerなし
exec solargraph "$@"
fiVSCodeのSolargraph拡張はcode -n path_to_dirで開いた場合はpath_to_dirをカレントディレクトリにする。
一方、ワークスペース機能を使って開いた場合は、ワークスペースごとに固有のLSPインスタンスを起動し、それぞれがワークスペースのルートをカレントディレクトリにする。
このため、Gemfile.lockがあるディレクトリをプロジェクトルートとして開いているのであれば、./Gemfile.lockで見つけることができる。
あとはSolargraph拡張のCommand Pathにこれを設定すれば良い。