4

I've only gotten into the ruby world on emacs, and I'm looking for a lot of the good stuff I normally get with rubymine like code completion and jumping into modules. I'm attempting to use robe with autocomplete for the most part.

Most robe stuff requires a server running which can be done by running robe-start. I may be doing things wrong but it appears the application has to be running and set-up completely to be even able to use the functionalities that robe offers. By set-up I mean, making sure configs are all set-up, gems have all been downloaded via bundler, rbenv is working correctly and whatnot.

I find it weird that to edit my code, a server has to be running the same code; code which can be broken at many points during my development. I'm not thinking of ruby debuggers yet so please don't go there yet. This brings me to my question, am I even looking at what robe can do correctly?

For some context, I come from the PHP world where I can generate phpctags and use ac-php to do all the same cool stuff, no servers and none of that stuff.

Enlighten this ruby emacs neophyte please.

turntwo
  • 143
  • 5
  • A running server is needed to cross the boundary between strings in your buffer and living, capable of introspection objects. – gboffi May 18 '17 at 10:07

1 Answers1

6

Short answer

Because making use of a live process has been easier to implement than writing a static indexer. And because Ruby's metaprogramming abilities make almost any external indexer necessarily imprecise. With Ruby's introspection capabilities, a dynamic indexer can access very accurate information about of the set of defined methods and their source locations.

Even reusing an existing indexer like YARD comes with its challenges. But yeah, nothing's stopping us from having a pluggable indexer backend, aside from the seeming lack of developers interested in implementing that.

Static vs dynamic indexing

Historically, Emacs's language tooling took one of the two distinct approaches.

The first is using an external indexer program which scans the source files in the project (and maybe its dependencies), writes that information to a file, for Emacs to read the file later. The common drawbacks are precision, the limited amount of information that can be stored in such index, and the need to continually rebuild the index manually. Also often (though not necessarily so), Emacs tooling that integrates with such indexers doesn't pay much attention to the context, like the types of objects around point, and which methods they might respond to.

Examples of such tools are etags (comes with Emacs), ctags, ripper-tags, Global, and phpctags.

In the other direction, there are more integrated solutions like SLIME, Geiser, CIDER, Indium. They normally offer more capabilities, maintain a connection to a running project, and feature a REPL of some sort. A smaller example is the built-in python-mode which uses a Python REPL for code completion (when it's running).

There are also tools like ENSIME and CEDET which somewhat straddle the middle. But it seems to me that that direction works best for statically typed languages. It also requires a significant effort to develop.

Drawbacks

Indeed, the requirement to have the project already set up and running makes it less than ideal for new developers, and still somewhat of a bother for experienced ones.

That brings us to priorities. Robe was built with more veteran users in mind, developers who can grasp its architecture, and get their project into a runnable state without much external assistance. The main goal has been to make hard things (like understanding a complex code base) easier, rather than making easy things trivial.

Code completion, on a related note, has been more of an afterthought. It is only usable when mixed together with a "all symbols from buffers" completion source, which is definitely not ideal.

Advantages

When you do have a project up and running, having it in a REPL opens up nice exploration and debugging possibilities. REPL-driven development is a thing, even if we prefer testing more in the Ruby world.

Using Ruby introspection, we know about the methods defined by macros, such as ActiveRecord::Associations::ClassMethods#has_one. has_one :foo defines several methods, create_foo among them. Provided that the class defining this association has its database schema loaded (AR does this lazily), Robe can jump to these methods, and will get you to ActiveRecord::Associations::Builder::SingularAssociation. For a static indexer to get this right, it will have to carry predefined information (probably created manually) about has_one and other macros, and will have to handle every such gem on a case by case basis.

Easy (or easier) to implement, like already mentioned. Also easier to debug, in the same REPL buffer that the project is running in.

What if my code is broken during development?

It only really has to be valid when the project REPL is booting.

After that, you yourself choose when to ask Robe (or inf-ruby) to reload the project files. In Rails projects, you can do that for all project files together (with C-c C-k). And also you can reload individual files with C-c C-l in any Ruby project.

Dmitry
  • 3,508
  • 15
  • 20
  • 1
    Great answer, probably the most informative one I've gotten in a while. Definitely accepted! One of those few bad takeaways from docker-focused development. – turntwo May 20 '17 at 08:41
  • 1
    You're welcome! Could you clarify, maybe: what "bad takeaways"? – Dmitry May 24 '17 at 12:27