QML module versions and automatic imports

Published on mar 17 juin 2014 in Nepomuk, (Comments)

This week is going to be a bit busy as I have an exam tomorrow, on Thursday and then next Monday. I still have had some time to implement cool new features for the QML/JS language support plugin of KDevelop. This blog post will not contain any screenshot because these features are not very visible, but are interesting nevertheless.

Automatic import of all the files of the current directory

Most Javascript developers tend to put all of their files in the same directory. This is not a problem (especially for a small application), because one of these files tend to be jQuery.js or any other Javascript library, and the others are small Javascript files specific to the website being developed.

Several weeks ago, I told you that plain Javascript does not have import statements. This is a problem because it makes difficult to know which files the user is going to use in the source-code being edited. For instance, myapp.js may refer to jquery.js, but nothing in the code gives this information.

The fact that KDevelop has to guess which files the user would like to have included, and the fact that related JS files tend to be put in the same directory oriented me towards a simple solution: each Javascript file imports all the others in the same directory. This way, every symbol defined in each file becomes visible to all the others. This is nice and fast, and the user can now use jQuery without having to import it.

There is a small technical difficulty here, though: if every file imports all the others, we get circular dependencies (A imports B and C, but B imports A and C, thus A can see itself by the way of B). The solution that I used is to "copy" the declarations of every file into the others instead of importing them. This way, A doesn't import anything but contains the declarations of B, C, etc. B does the same, but uses the version of A without the declarations of B, C, etc copied. This way, A cannot see itself by the way of B, and B cannot see itself by the way of A.

Copying declarations? And what happends if a declaration changes? What about memory usage? Those are valid questions, and I draw your attention to the fact that I've put quotes around "copy" :-). The plugin does not copy the top-level declarations of each other file, it creates alias declarations for them. An alias declaration is a declaration that points to another. For instance, "myVariable refers to myVariable in B.js". This way, if B is modified and the type of myVariable changes, A will immediately see the change. This is also a bit lighter that copying the declarations.

I described the automatic import for Javascript files, but it is also enabled for QML files. This way, QML files can refer to Javascript methods defined elsewhere, Javascript files can instantiate custom QML components, and (more importantly) QML files can see the other QML files and instantiate their top-level QML component. In QML, if a file named MyComponent.qml describes a component, all the files in the same directory, without any import statement, can instantiate the MyComponent component. This feature is therefore very useful to both Javascript and QML developers, and was fairly easy to implement.

Module versioning

This feature also refers to import statements. When the user imports a QML module, a version has to be given (as in import QtQuick 2.1). This version was not used until now, but is now understood by the QML/JS plugin. If you import QtQuick 2.0, you will not see the components that appeared in QtQuick 2.1, 2.2 and 2.3. More importantly, importing QtQuick will not import a mixture between QtQuick 1.0 (Qt4) and QtQuick 2.0 (Qt5). This solves the problem that was visible in my previous blog posts: there are no longer dozens of duplicated QML components and wrappers in the code-completion popup.

The module versioning point was quite invasive to implement and is still being reviewed. The first point is already in the kde:kdev-qmljs repository, ready to be tested.

One last thing that I would like to have fixed before the midterm evaluation (next Monday) is the support for built-in QML types (by "built-in", I mean "part of the core language"). For instance, QML recognizes the vector3d, font, color, date, time, point and rect types, that must be recognized, highlighted as types, and some of them even have attributes (rect has x, y, width and height). This should not be too difficult: I just have to add some components to the plugin.qmltypes files of QtQuick 2.0 and QtQuick 1.0 (both need to be modified because Font has a different definition in QtQuick 1.0 and QtQuick 2.0).

« Enhancements, cleanups and fixes for QML   Now come the holidays! »