Formatting Ruby with Doomed LSP

I've found myself writing a lot more Ruby than expected recently, and waiting on Rubocop to tell me my code is bad enough that I can't ship my work. Something had to be done.

Rather than throw out a tool that does occasionally contribute meaningful feedback, I opted to integrated formatting into Emacs via Apheleia and eglot.

This approach saves me from a lot of tedious editing without wait times of several seconds on a codebase with fewer than 9,000 lines of code.

First, we teach Apheleia about eglot with a custom formatter that we’ll refer to later as (surprise, surprise) eglot.

(after! apheleia
  (cl-defun +apheleia-indent-eglot-managed-buffer
      (&key buffer scratch callback &allow-other-keys)
    "Copy BUFFER to SCRATCH, then format scratch, then call CALLBACK."
    (with-current-buffer scratch
      (setq-local eglot--cached-server
                  (with-current-buffer buffer
                    (eglot-current-server)))
      (let ((buffer-file-name (buffer-local-value 'buffer-file-name buffer)))
        (eglot-format-buffer))
      (funcall callback)))

  (add-to-list 'apheleia-formatters
               '(eglot . +apheleia-indent-eglot-managed-buffer)))

Now we only have to configure ruby-mode to both connect to our preferred LSP server and use our new eglot formatter. I’m using ruby-lsp but you can use any LSP implementation that takes your fancy.

(after! eglot
  (add-to-list 'eglot-server-programs
               `((ruby-mode ruby-ts-mode)
                 . ,(eglot-alternatives '(("ruby-lsp")
                                          ("solargraph" "socket" "--port" :autoport))))))

And then one additional piece of configuration, this time to eval-after-loading Apheleia.

(after! apheleia
  (add-to-list 'apheleia-mode-alist
               '(ruby-mode . eglot)))

At this point one should have lightning fast formatting of even the gnarliest of Ruby. And most importantly, you can ship and learn without a red build every time you open a pull request.

Bon voyage!