When LSP gets lost

A quick win that has saved me minutes of searching online documentation while I'm deep in a Ruby codebase is combining the power of a language server with predecessors like Robe.

Most of the time, LSP via eglot in Emacs is hugely empowering. The wealth of available server implementations provide some combination of formatting buffers, navigating code, and other capabilities.

Not all servers are equal however, and sometimes the most desirable of operations aren’t available. In those cases one can rely on language-specific tooling that came before Microsoft standardised so much off the back of VSCode.

The solution below is specific to Doom as it relies on the lookup module to maintain a list of functions capable of satisfying our requests. One by one, functions are applied until a non-nil response indicates a capable link in the chain.

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

  (after! ruby-mode
    (when (featurep! :tools lookup)
      (add-to-list '+lookup-definition-functions #'robe-jump 'append))))

The key is we append robe-jump to allow eglot its shot. Only if our language server fails miserably do we lean on a running Ruby process to navigate deep into the internals of Rails to find a function surprisingly lacking in transactionality.

As my heart sinks, I kinda wish I hadn’t seen that implementation now.