First off - speed. Bluntly stated, if the code completion information isn’t there NOW, I’m not going to use it. And by NOW I mean less than half a second. I use Microsoft’s C# IntelliSense all the time when developing Ruby in Steel and it’s just there. I don’t have to think about it and it doesn’t distract me from typing. If I had to wait two seconds, I wouldn’t use it: it would interfere with my typing too much. I really can’t emphasise this enough: if code completion isn’t fast, it’s useless.
Secondly, accuracy. It’s important just to have only the correct information displayed. For example, the information displayed after a dot is different from that displayed if there is just whitespace and you type CTRL-SPACE. Also, the methods available in ‘class’ context are different to those displayed in ‘method’ context (I’ll explain and demonstrate this below).
Lastly, relevance. If you look at some of the ‘code completion’ from our competitors, you’ll see right at the top of the list __FILE__. It is actually in scope most of the time, but when was the last time you wanted to use this identifier? Right. Yet, it’s at the top of the list. Similarly with all those operator methods like <==>. Relevance means trying to be reasonable in what is displayed in a completion list.
Now, let’s see how we stack up. Speed wise, I cannot detect any delay whatsoever when I CTRL-SPACE for a completion list. Here’s the result:
You might (reasonably) say that you’d expect no delay on a 2.4GHz Core Duo with 3GB of memory (which is what I use most of the time). Ok. So I tried exactly the same program on a somewhat older machine – an arthritic seven year old 1GHz Celeron with 1GB memory which is well overdue for its appointment with the scrapheap but which I keep in a dusty corner in order to be able to run tests like this. And the result ... exactly the same. No delay whatsoever.
So how do we do this? Well, first off we don’t use Ruby. I repeat: we do not use Ruby (or Rails) in any way to get code completion data. Ruby is just way, way too slow.
What we do instead is read in all the relevant parts of Rails, parse them in exactly the same way as we do for any Ruby program that you might write. Then we output the resulting data as ‘stubs’, removing the innards of methods, since the interior of a Ruby method (generally) can’t be seen externally. Having got the stubs, we then re-parse them and compile them to a binary format. This is then the IntelliSense database. This is done once (only once) when you first start Ruby In Steel and it takes less than 10 seconds on my machine. It’s this ‘compiled IntelliSense’ that gives us the speed.
So to summarize:
We don’t use Ruby
We parse the Rails files using exactly the same parser algorithms that are used in our standard Ruby parsing.
We compile the IntelliSense for code completion.
Now it also turns out that parsing Rails is a very good test of the parser. If you can parse Rails, you’re in good shape. While we don’t handle everything Ruby can throw at us yet (that will have to wait until I incorporate Antlr 3), we deal with over 99% of it.
This brings us on to accuracy. A good example of this can be found in the little example I used above. Notice that I did the CTRL-SPACE in the ‘class context’, not in a method. But if you look at the definition of has_many say, you’ll find that it isn’t a class method at all: it isn’t a singleton. It should only be visible in a method like this:
If you look into Rails, you’ll find that Rails makes methods like has_many available as class methods by using extend. Here’s an example:
Here, the methods of M are being included in class Y as singleton or class methods.
In fact Rails does something like this:
And here’s what we get:
Which is right (of course!).
Just a couple of points here. I’ve colored the entries in the completion list to bring out the various class type (inheritied, included and so on). We haven’t decided whether to release this as a feature yet – it’s more of a diagnostic aid to help in development. Also, we’re still testing and debugging this release, so while I think the completion lists are correct, I’m still checking them and fixing things. Finally, all of this – repeat all - applies to standard Ruby. We have improved the base Ruby IntelliSense system to deal with Rails - and not hacked the Ruby side to do the Rails stuff. If you only use Ruby and never use Rails, you still benefit!
In the next article I’ll cover the final point “relevance”, and the difference between CTRL-SPACE and “dot” operations, some “killer” IntelliSense constructs that I know only Ruby In Steel can deal with and show you some other very nice new features.
And this is before we even get onto the Visual Rails Workbench!