To ensure the robustness of the AutoComplete library, I decided to see what it would take to implement some basic Java code completion. This is of course much more complex than completion for markup languages, and presents a different set of challenges, but it’s something I’ve always been interested in.
The first thing that I needed was a Java parser, one that could break a source file down into an AST suitable for code completion. This means you don’t have to go as far as parsing actual method bodies, you just have to grab imports, class/interface/enum names, method and field names, and local variables. Of course, this isn’t a trivial task by any means; especially for local variables, you have to parse method bodies “well enough” to be able to get the variable names and types, without getting caught up in validating the method body’s structure.
Luckily, I had already been working on such a beast off and on (yes, I’m a geek) so I had something to go on. It can currently parse all source files in the 1.6u16 JDK (well, classes and interfaces, I’ve put off enums for now to work on other areas), so I think it’s in pretty good shape. Its error recovery (ability to keep parsing after seeing invalid Java source) is pretty terrible, mostly due to my lack of real education in parsing/compiler design.
So with the Java parser at least functional, the next step was to integrate it into RSyntaxTextArea’s Parser API. That way the parser runs whenever the text component’s document is modified, allowing the user to get up-to-date completion choices automatically. This part was easy, as RSTA’s Parser API was already up to the job.
Here’s what’s included in completion suggestions so far:
- Field and method names for members of the class being edited.
- Field and method names for all super classes and implemented interfaces. Access flags are honored – e.g. you will not see suggestions for private members of extended classes.
- Local variable names for variables accessible at the current caret position.
- Names of imported classes (e.g. “import java.io.*;” allows everything in the java.io package to be suggested). Again, access flags are honored – package private classes, etc. are not suggested (unless the source being parsed is part of that package).
- Anything fully-qualified is suggested as well, so typing “com.sun.java.swing.<ctrl+space>” will suggest everything in that package, whether you’ve imported it or not.
Of course, this is no Eclipse (yet!), so there’s plenty of issues to work out:
- It can be slow when loading classes from imports of large packages the first time(e.g. importing java.util.*, java.io.*, javax.swing.* all together). Once the classes are loaded, code completion is fast, it’s just the initial caching that can take some time. This is because this package actually parses the completion info out of the jar (e.g. scans the .class files in a tools.jar), which can take some time. We should be smart and load this stuff off the EDT when possible (e.g. when the user adds the import, but before they hit ctrl+space).
- It does not yet offer available methods for local variables or members. For example, for the local variable “String str”, typing “str.get<ctr+space>” will not return “getBytes()”. The parser does know that “str” is a String however, so this functionality is on the to-do list. This could get very complicated however, with situations such as chained methods.
- Javadoc is not served up with the completion choices. This can also be added, but probably won’t be seriously looked at until other issues are sorted out first. Expect to see Javadoc returned for members in the being-parsed source before general purpose, src.zip-style documentation support.