QML binary modules

Published on dim 20 juillet 2014 in Nepomuk, (Comments)

Developing a new project is usually done in two phases: first, the most important features are added, and the feature list of the project quickly grows. Then, the features need to be polished, bugs fixed, and the overall usability of the project improves slowly. This second phase is often considered less interesting than the first one, but personally, I like to fix small annoyances. Fixing them makes the difference between an experimental project and one that can be reliably used by real users.

During this GSoC, I try to work in small steps, implementing a big feature and then improving it, then implementing the next feature, and so on and so forth. This works quite well and allows me to keep a high motivation in what I'm doing: implementing a big feature is exciting but also exhausting, and improving existing features mainly consists of many small and easy commits that each have a noticeable effect. This week, I'll talk about one major feature and several small corrections that I've implemented for the KDevelop QML/JS language plugin.

The fixes

Javascript has no syntax to express that "this function is a member of this class". It is not directly possible to use something like function Class.method() as in C++. Developers are therefore obliged to declare class methods using function expressions:

Class.prototype.method = function (a, b) { return a + b; };

The fact that method is assigned a function has an annoying implication for the QML/JS plugin: when method is declared, it is a simple variable, a member of Class that can contain anything (a function, an integer, etc). This means that method is declared as a Declaration, not a ClassFunctionDeclaration. This is not really a problem, but this prevents KDevelop from displaying the right icon alongside the methods:

Functions not recognized properly

The solution is quite simple: when a Declaration has a function type (when it is actually a function), then the QML/JS plugin ensures that a function icon is displayed. An icon may seem unimportant, but such a bug is very easy to fix (one or two lines), and not fixing it would have immensely annoyed all the users of the plugin.

Functions recognized properly

The other small feature that I've added is support for the __proto__ member of Javascript object instances. This member is used by several Javascript libraries and may also be used in a couple of QML files, so it is not going to be useless:

__proto__ and object inheritance

Other small bugs have been fixed. For instance, in a QML file, typing ANamespace. lists the components declared in the namespace, and nothing else (previously, there was some garbage, for instance the namespaces imported by ANamespace, or the built-in Javascript classes). Code-completion for import statements is now way more useful, but more on that later.

Binary QML module imports

Back to the QML world. The goal of my GSoC is to have the best possible QML support in KDevelop, because KDevelop is already the best-in-class IDE for C++ Qt development, and only lacks a good QML support. Javascript, on the other hand, already has many great IDEs and competing with them may be a bit out of the scope of a GSoC project.

My strategy to have great QML support in KDevelop is to concentrate most of my efforts on this language, while still having a robust enough Javascript support so that Javascript snippets in QML files work as expected. For instance, Javascript built-in classes were added because many of them are used in QML files.

So, this week was dedicated to importing QML binary modules and QML installed modules. First, some definitions:

  • QML installed module: QML module that is installed on the user's system, for instance by a KDE package. Unfortunately, QML does not have system-wide include paths as Python and Ruby do, each QML application provides its own search path. Luckily, most QML applications either use only Qt-provided modules (and they have been supported for a long time now), or use KDE-provided ones. KDE installs all its modules in /usr/lib(64)/kde4/imports, so they are quite easy to locate. An installed module consists of a directory that contains .qml files and possibly .so files. The library files (that can be mixed with plain .qml files) contain additional native components, implemented in C++.
  • QML binary module: Library that contains QML components. The .so files I was talking about hereabove are QML binary modules.

Supporting QML installed modules was quite easy: KDE's KStandardDirs allows me to easily locate them, and the infrastructure for importing whole directories was already in place.

Binary QML modules were a bit more difficult because they need to be dumped before the QML/JS plugin can use them. Dumping a binary module file is the action of running qmlplugindump on them, a tool provided by Qt that takes a .so file and writes a QML module file for it. The QML module file is a valid QML file and can be parsed by the plugin.

With directory imports working, binary QML modules dumped and the dumps correctly parsed, the all the pieces fit together:

A directory containing a .so file Uh oh! A .so file!

Enter qmlplugindump Enter qmlplugindump (the file is cached for later reuse)

Listing the components of a binary QML module Components exported by Plasma Core

Members of binary QML components Members of a binary QML component

Finally, as an added bonus, I've improved the code-completion for import statements. They now list only the imports that start with what the user has already typed, and they handle installed modules:

Nicer code-completion of import statements

Testing the QML/JS plugin on real QML files has shown that QML is very nicely handled, even if there are still some small glitches (for instance, several built-in QtQuick 1.0 types and properties are still missing). Sven Brauch will also send me a list of places where the code-completion could be more useful. In fact, don't hesitate to tell me which features you would like to see implemented, I've still four weeks ahead of me!

« ECMAScript 6 support in KDevelop   Node.js support in KDevelop »