RuboCopにLSPを標準搭載した

タイトルのとおり。RuboCop 1.53 で LSP (言語サーバー) を標準搭載しました。

最初に3行まとめを書いておきます。

  • RuboCop を使っているけれど LSP を使っていない場合は、高速なリアルタイム性で開発体験が変わると思います。速い!
  • VS Code ユーザーを使っている方は、後述する vscode-rubocop という VS Code 拡張をインストールすれば OK です。
  • Emacs や Vi などのユーザーは、LSP クライアントの設定で rubocop --lsp を起動するように LSP 設定してください (VS Code では不要) 。

公式の使い方としては以下のドキュメントを更新していくことになるものの、実装者が自分なのでこちらに軽く書き記します。

docs.rubocop.org

rubocop --lsp コマンドは直接ユーザーが手動実行するものではなく、以下のような LSP クライアント (設定) にて実行するものなので、コマンドラインからの手動での起動は不要です。例えば、VS Code 拡張の vscode-rubocop 内部で rubocop --lsp を起動します。本記事で例示しているような Emacs で自前で LSP クライアント設定を書く場合などに知っておくコマンドです。

ここからは LSP を使えるようにするための、それぞれのインストール方法を中心に記します。

VS Code

VS Code ユーザー向けに、VS Code 拡張の LSP クライアントを用意しています。

marketplace.visualstudio.com

Visual Studio Marketplace にリリース済みなので、VS Code の「Install Extensions」で「vscode-rubocop」を検索するか、Marketplace から直接などでインストール可能です。

もちろんエディタ上からオートコレクトの実行も可能です。vscode-rubocop の README 記載を参照してください。

とりわけ Format on Safe は有効にしておくと便利だと思います。 デフォルトで有効にするかどうかも悩んだのですが、いちおうテキストファイルに変更を加える振る舞いの性質から、ユーザーに意図的にチェックしたもらった方が良いかなと、デフォルトで無効にしています。

おまけ情報としては、LSP を策定しているのが Microsoft ということで一番相性 (LSP クライアントとしての出来栄え?) が良いと思います。自分の中で引き続き言語サーバー機能を拡張する計画が立っているので、この VS Code 拡張については言語サーバーにあわせて引き続きアップデートしていく予定です。

手元で今後の PoC を試すのはこちら。LSP 開発では Microsoft ということで手厚いんですよね。

他のエディタでの LSP クライアント設定は以下です。LSP クライアントからの bundle exec rubocop --lsp での起動をベースとした例ですが、見つからなかった際はグローバルの rubocop --lsp としたいなどあれば、適宜用途にあわせて編集してください。

Emacs (Eglot)

全体設定の .emacs/init.el あるいは、対象リポジトリ固有の .dir-locals.el に以下のような設定を足すことで有効にできます。

(require 'eglot)

(add-to-list 'eglot-server-programs '(ruby-mode . ("bundle" "exec" "rubocop" "--lsp")))
(add-hook 'ruby-mode-hook 'eglot-ensure)

保存時に autocorrect を掛けたい場合は以下のような設定を足してください。

(add-hook 'ruby-mode-hook (lambda () (add-hook 'before-save-hook 'eglot-format-buffer nil 'local)))

Emacs 29 で標準搭載になるらしい Eglot でのみ確認しているため、他の LSP クライアントはわかりません。自分は Emacs 28 なので別途 Eglot をインストールしています。

こちらはあくまでベースとなる設定例のため、それぞれ良い感じの使い勝手に elisp 設定して下さい。M-x forever。

2023年12月22日追記

tmtms さんが、RuboCop の LSP と他の LSP を共存可能な LSP ルーターを実装されました。LSP を併用したいケースで参照してみてください。

blog.tmtms.net

Emacs (LSP Mode)

LSP Mode には、以下の PR が取り込まれています。

github.com

MELPA から lsp-mode パッケージを入手してください。

詳しくは以下のドキュメントを参照してください。

emacs-lsp.github.io

Vim, Neovim (coc.nvim)

coc-settings.json への設定例です。

{
  "languageserver": {
    "rubocop": {
      "command": "bundle",
      "args" : ["exec", "rubocop", "--lsp"],
      "filetypes": ["ruby"],
      "rootPatterns": [".git", "Gemfile"],
      "requireRootPattern": true
    }
  }
}

保存時に autocorrect を掛けたい場合は以下のような設定を足してください。

{
  "coc.preferences.formatOnSave": true
}

coc.vim は Neovim で動作確認したのみなので Neovim 向けとしていますが、Vim でも動くと思います。 (2023年7月20日更新) Vim でも動作検証した。

Neovim (nvim-lspconfig)

~/.config/nvim/init.lua の編集例です。

vim.opt.signcolumn = "yes"
vim.api.nvim_create_autocmd("FileType", {
  pattern = "ruby",
  callback = function()
    vim.lsp.start {
      name = "rubocop",
      cmd = { "bundle", "exec", "rubocop", "--lsp" },
    }
  end,
})

Below is an example of additional setting for autocorrecting on save:

vim.api.nvim_create_autocmd("BufWritePre", {
  pattern = "ruby",
  callback = function()
    vim.lsp.buf.format()
  end,
})

保存時に autocorrect を掛けたい場合は以下のような設定を足してください。

なお公式でのサポートも進めてくれているようです。

github.com

未だ検証できていませんが、そちらを取り込めば以下で行けるようです。

require("lspconfig").rubocop.setup {}

@delphinus さんに教えてもらいました。情報ありがとうございます!

Helix

helix 23.05 (7f5940be) 時点での、~/.config/helix/languages.toml への設定例です。

[[language]]
name = "ruby"
language-server = { command = "bundle", args = ["exec", "rubocop", "--lsp"] }
auto-format = true

詳しくは Helix のドキュメントを参照してください。 https://docs.helix-editor.com/languages.html

他のエディター

LSP の仕様から Sublime Text などのエディタでも動くと思いますが、自分が普段使いのエディターではないので、環境を整えるところということでまだ見れていません。

開発後記

LSP 実装は以前から考えていて、RubyKaigi 2023 でジャスティンと話しをしたのが最終的に進めるモチベーションになったもの。LSP の話は帰りの松本駅で偶然ジャスティンと会ったときに話したような。

実装としては Standard に組み込まれた LSP がベースになっていて、さらに LSP を実現するあたっては RubyKaigi 2017 で発表されている mtsmfmlanguage_server-protocol を使っています。RubyKaigi は発表動画がアップロードされているので、後追い視聴できて開発が捗って良いですね (現地でも見ていますが何年も経つと忘れている部分があったり、当時理解が追いついていない部分があったりするので) 。

インストールの注意点として、language_server-protocol は Steep なんかでも使われているので、bundle update rubocop で RuboCop のアップデートが 1.53 にアップデートされない場合は、bundle update rubocop language_server-protocol として language_server-protocol もあわせてアップデートしてみてください。

RuboCop を使っているけれど LSP を使っていない場合は、高速なリアルタイム性で開発体験が変わると思います。ご活用ください。