Work on Generator 6.0 is progressing pretty nicely. We are code complete on our new parser, new compiler, new caching mechanism, out of process template execution, and WPF based property grid. There is a lot to talk about there, but I have also started working on implementing IntelliSense and I wanted to talk about that today.
Better working IntelliSense has been our biggest request probably from day one. People tend to take IntelliSense for granted because Microsoft has done a great job with it in Visual Studio and it almost becomes transparent. We just use it and expect it to work without thinking about what goes into it. I was just as guilty of this as everyone else until I started working on implementing it in CodeSmith Studio. Think about it for a second, you have a collection of text files and you need to make sense of them… what goes into that? Here is a simplified list of the steps and parts required for implementing IntelliSense.
- Tokenize – the first step is to take that text file and create a series of tokens from it.
- This sounds easy, but in and of itself it can be pretty hard because of ambiguities in languages where certain things have different meanings depending on their context.
- In our case, things are even more complicated by the fact that we are mixing random languages together.
- Also, keep in mind that this needs to be happening in real time as the user types without slowing the editor down.
- Parse – next we read the tokens in, make sense of them, validate them and construct an abstract syntax tree along the way so that we can have some sort of structure that can be more easily understood, traversed and transformed.
- This is expensive to build and needs to happen in a background thread and constantly be updated as the user is typing.
- Gather Type Information – we need a database of reflected and parsed type information from any referenced assemblies and source files so that we can use that information in resolving and knowing what members are available for any given type.
- It is expensive to reflect over every single type in every single referenced assembly. So you need to cache this information in a structure that makes it really fast to load and search.
- Gather Context – when an IntelliSense request is made, we need to figure out where we are and provide as much information about our surroundings as possible.
- Due to the fact that active typing is going on, we have to use a combination of token scanning and looking at our most recent AST to provide this information.
- Also due to active typing, we have to work with an AST that has information that is slightly out of date and we will need to translate that out of date information to the current source.
- Resolve – now we need to take all of the information that has been gathered and use it to figure out what we are looking at and what options are available to show in the IntelliSense list.
- There are a lot of rules that come into play here like member scope and precedence for extension methods to name a couple.
Another thing I would like to address is that many people in the past have said to us… "just use Microsoft's IntelliSense". Unfortunately, it's not that easy. Microsoft does not expose their IntelliSense services and, even if they did, we most likely would not be able to use them because they would be very much geared around their use cases which would make them unusable for ours.
As you can see, there is a LOT of work that goes into providing that handy little drop down menu that magically pops up and makes it so that you don't have to memorize an entire framework library. We are completely re-writing every single one of those pieces in CodeSmith Generator 6.0 and our end goal is to make it so good that you completely take us for granted because it just magically works.