UPDATE: this is way out of date these days. Check out the gem that implements this stuff for you: https://github.com/iftheshoefritz/solargraph-rails

I haven’t yet seen an IDE that can do really good code completion for Ruby and Ruby on Rails. LSP and Solargraph brought me one big step closer.

Language Server Protocol (LSP) is a protocol for communication between editors and a Language Server (background process). The editor tells the server which file it has open and sends updates containing the cursor position and edits as they are being made. The server can respond with hints (e.g. a list of methods for autocompletion). The editor can also query for things like “find me the definition of the method at this point”, etc.

LSP was created by Microsoft (I guess probably for VS Code) and there are a bunch of implementations for different languages. I work with Ruby and a co-worker (thanks Wilhelm) pointed me to Solargraph. We are both Emacs users (although he is on the wrong [holy] side of the evil divide) and there is an emacs package (obviously) that implements the client side of LSP. Of course, since it was a microsoft thing, there is a plugin for VS Code, and many other editors too.

Out of the box I wasn’t that impressed (It gave me fewer features than my previous setup, which was enh-ruby-mode + Robe). The problem with code completion in Ruby is that all the dynamic magic and meta-programming goodness makes it really hard to work out what is actually going to execute at runtime as a result of any given line of code. Rails doubles down on this: the code you write is a fraction of the code you can call. Consider:

class MyModel < ApplicationRecord
end

There’s nothing in the source, but in a database-backed Rails app, ActiveRecord will find a table named my_models and add attributes to this model that you can then call with plain Ruby elsewhere in your code: MyModel.new.my_column_1 etc. It will also add a setter: MyModel.new.my_column_1 = 'my value' and of course everything gets persisted when you call my_model_instance.save. .save is just ordinary inheritance, so not that hard to find, but the magic attributes are … difficult.

I have not yet seen an editor that can work out from the source that these magic attributes exist and offer them in code completion (apparently RubyMine might be good at this, but people don’t like how long it takes to index). Solargraph did not fail to disappoint either.

Fortunately the documentation on the Solargraph guide for Rails made me aware that there was a lot of extra magic that could be added by opening classes and adding YARD documentation to them. Then asking around on the StimulusReflex discord (honestly, I didn’t know where else to find experienced Rails people who might know Emacs on a day when my colleagues were all out of the office), I found @julian_rubisch. Julian pointed out that Rails has lots of reflection that can programmatically discover stuff about your code.

image

As he says, we’d be done!

A few hours of coding and chatting with him later (thanks to South Africa and it’s generous public holidays I got to do some coding that wasn’t work), I had something working, and then a few days later he had an improvement. Julian sent me this Rake task:

The rake task runs through all your models and writes a bunch of files with YARD comments to help Solargraph work out what fields are available. Now my editor magically knows that when I type MyModel.new. it should suggest my_column_1, my_column_2 etc.

Work in Progress

Some challenges remain for future implementation:

  1. Rails models do not remain constant. It would be nice to have this run automatically after every Rails db:migrate, just like the Annotate gem.
  2. There’s some more features to be added to the generator: reflection on what types ActiveRecord associations return, for instance
  3. Finding the definition of these attributes takes you to the generated files instead of to your own source.
  4. The lsp package for Emacs requires me to restart the editor when I change the generated files. Irritatingly, VSCode just picks the changes up without any hassles.
  5. Starting up the solargraph server is crazy slow (Julian noticed the same thing) after we started writing these files. Since the files are only written once, this is a mystery. Irritatingly, VSCode has no problems here either.

These will all be things we can chip away at over time. Maybe this is worth building a gem for?

If you’re interested in understanding LSP in more detail and you’re prepared to slog through protocol definitions: try the Microsoft LSP specification. Otherwise, solargraph.org is a good option.