Last week, I talked about how the QML/JS language support plugin for KDevelop started to support basic code-completion. It was able to understand QML import statements and to display the list of all the properties and methods accessible from within a QML component instance.
This was already quite useful, but what most developers want is to be able to see the available properties and methods as they type. If "object" is of type Label
, and they type object.
, KDevelop should display the list of all the properties and method of Label
. This week, I've implemented just that :-).
Recognizing object members
The first thing that has to work is the proper recognition of object members in the use builder (the use builder is the part of the plugin that tells KDevelop where each variable, function or class is used; this way, KDevelop can highlight every occurence of a variable if you put your cursor on it). This was easier than expected, and the QML/JS plugin was soon able to recognize that "field" in object.field
is an use of the "field" attribute of "object".
Adding this support to the Javascript language needed a small additional step: "objects" in Javascript are declared like dictionaries in Python (they can also be declared using prototypes or simple assignations, but this is not yet supported). I therefore had to add support for object literals. Once this was in place, the uses were properly recognized:
Code-completion of object members and array subscripts
Finding the uses of object members is cool but is only the first step towards usefulness. As I said in the intro, what most developers want is to have code-completion items for the object they have just typed. The details of this support are a bit complex, but the general idea is to detect that the user has typed object.
, to remove the last dot, and then to parse the object
string (that can be a complex expression, for instance func()[0].prototype
. Once this string is parsed, the use builder presented above can be used to find what is the declaration corresponding to the whole expression, and if it is an object, which fields are accessible from it.
The Javascript support for this was trivial (in fact, the interesting part of the code that I've shown above is in Javascript), but I added one small feature: sometimes, object members are not accessed using object.member
, but object["member"]
. The QML/JS plugin is therefore able to recognize the latter syntax in addition to the former one, but only when the array subscript is a string literal (it's impossible to guess that object[foo()]
will represent a particular member of object, because foo can return anything).
This ends this blog post. I hope you enjoyed the screenshots and the new features! It's very cool to work on all this, and KDevelop is surprisingly powerful. A lot of features appear "for free" (like renaming a variable and having a tooltip that asks me if I want all of its uses to be updated), and the features that must be implemented are easy to implement because KDevelop provides everything one can dream of. Moreover, there a now many language plugins for KDevelop (have a look at kdev-python, kdev-ruby and kdev-php, that work very well, and kdev-clang that is very promising). Having such an amount of plugins eases everything because I can look at them and discover how they have handled a particular problem.
For the curious, here is a list of future features that I plan to implement during the following weeks:
- Support for "this" and "parent" in QML files, and adding support for "Behavior on ..." and thinks like that (called "object bindings" in the QML jargon)
- Fixing some bugs in how QML slots are handled
- If I declare
var a = {};
, I would likea.foo = "bar"
to declare a new string member of a. Same fora["key"] = false;
, that would later be listed in the completion proposals ofa.
- Support for built-in JS types (String, Int, all of these have built-in methods that should be listed). This can be very complicated and I need to think about it
- Support for function prototypes and object instantiation in Javascript. This is very interesting and I already have some ideas because I've discussed the problem with Sven Brauch (my mentor)
- The most complex thing in Javascript: on web-pages, JS code is executed in the order in which it has been included in the HTML file. This is a problem because most web-developers start by including, say, jQuery.js, and then all of their JS scripts. The JS scripts can access all the declarations of jQuery, but nothing in those files say that jQuery has been included, and where to find it. Node.js has
require
, but plain JS doesn't. Maybe a special comment, like/*-- include /path/to/file.js */
may solve this problem, if developers accept to put special comments targeting KDevelop. Maybe just importing all the files of the current directory may work, but is poses the problem of "everyone sees everyone" as each file should be able to access the declarations of all the others.