What’s new in QML Language Server (qmlls) shipped with Qt 6.6

The QML Language Server shipped with Qt 6.6 comes with exciting new code-navigation features that we will demonstrate in this blog post. But first things first, what is QML Language Server?

What is QML Language Server?

QML Language Server is a language model for QML that can be used for a wide variety of editors. That means it allows an editor to display warnings or errors, support rich code navigation, and format code, for example. If you want to read more about QML Language Server, you should check out its new (at the time of writing) documentation page here!

Your favorite editor needs to implement the Language Server Protocol (LSP) to support QML Language Server, see here for the official LSP page.

Setting up QML Language Server in VS Code

Let’s set up your favorite (LSP-implementing) editor to explore the new features of the QML Language Server!

As the setup instructions vary from editor to editor, I will also explain in this blog post how to set up QML Language Server in VS Code, but feel free to skip this part if you already have QML Language Server set up and running in your editor!

Note that Qt Creator 11, which is the current version at the time of writing, uses an internal implementation for the functionality of the QML Language Server. You can still add the QML Language Server to Qt Creator's list of "Language Clients" in Preferences or enable the qmlls support in the "Qt Quick" settings via the checkbox, but this will not override the use of the internal implementation. This will change in the future as the implementation of QML Language Server progresses.

Getting an Example Project

For this blog post, I created an example project to try out the different functionality of QML Language Server. You can find it here.

Clone it using:

git clone https://git.qt.io/Sami.Shalayel/demoforqmllswhatsnew66.git

Create a build folder, cd into it and configure the project:

mkdir build-demoforqmllswhatsnew66-debug
<path/to/QtInstallation>/bin/qt-cmake -S ../demoforqmllswhatsnew66 -B . -G Ninja

Installing the Language Client for VS Code

I will use VS Code with an experimental and unofficial extension for this blog post, you can download it from here.

Note that some editors do not require an extension to use language servers. Some just require the path to the binary and the file extensions to use with the language server. In this case you can skip this step and go to the setup part.

First, you will need to install VS Code and npm. Then, you can clone the repo using:

git clone https://git.qt.io/Sami.Shalayel/qmlls-vscode.git

Go inside the qmlls-vscode folder and download and install the required dependencies using:

npm install && npm run compile

Once this step is done, you should be able to start VS Code in the current folder using:

code .

A VS Code window will open and show you the project for the unofficial extension for VS Code.

Setting up the QML Language Server Extension

In VS Code, open the client/src/extension.ts file: opening the extension.ts file in VS Code

You should be able to find some code that looks like this:

const serverExecutable : Executable = {
        command: "sh",
        args: [ '-c', 'tee /tmp/qmllsInput | $HOME/projects/qt5-build/qtbase/bin/qmlls | tee /tmp/qmllsOutput'],
        // prints VSCode commands to qmlls into qmllsInput file and 
        // qmlls responses into qmllsOutput file
    };

This setting needs to point to your actual ‘qmlls’ binary. If you installed Qt using the official installer, you should be able to find the ‘qmlls’ binary under <QtInstallationPath>/bin.

If you compiled Qt 6.6 or the dev branch yourself, you can generate the binary by running ninja qtdeclarative_src qmlls. You will find the binary in the bin folder of your installation folder, qtbase/bin/qmlls for development builds, for example.

On my machine, the binary is located in /Users/sami/Qt/6.6.0/macos/bin/qmlls, where /Users/sami/Qt/ is the installation path that I used for the Qt installer. Furthermore, QML Language Server needs to know the build folder of the current project, so we end up with the following lines:

const serverExecutable : Executable = {
    command: "/Users/sami/Qt/6.6.0/macos/bin/qmlls",
    args: [ '--build-dir', '<path/to/build-demoforqmllswhatsnew66-debug>'],
};

Have a look at the documentation to see how to pass the build directory to QML Language Server when command-line arguments cannot be used.

Also, if you are not using the example project I linked above, make sure to pass the folder where your QML modules will be built, in case it differs from your general build folder! QML Modules defined in your project are recognized if and only if their qmldir can be found under path/to/build-directory/ModuleName/qmldir. QML Language Server should print out a message about which build directory it will use to find project QML modules.

Now that the QML Language Server extension is configured, install the QML extension by Baptist BENOIST for VS Code as shown in the screenshot: Steps to install the QML extension

Thats it! Save the file and run the project using the “play” button: Steps to install the QML extension

Exploring new QML Language Server features on the example project

Now, open a QML project in the editor you have just prepared for QML Language Server. You can select the folder of the project using Ctrl+O or Command+O to open it in VS Code.

Showing errors in the editor

The first thing you should see, when you open the Main.qml file, is the following warning: Import cannot be found because module was not imported yet

If you do not see any warning, you might either have an invalid QML document or QML Language Server might not have been set up correctly. You might have missed something from ‘Setting up QML Language Server in VS Code’.

QML Language Server finds QML module in your project by inspecting the build directory. Build your project by calling ninja inside of the build-demoforqmllswhatsnew66-debug folder.

Once you have built the project, you can close and reopen the editor to make sure that QML Language Server attempts to load the missing module, and the warning about the missing module should be gone: Import can be found now

As proof that QML Language Server is still doing something, I added a QmlComponent that was not defined anywhere, and that is why QML Language Server complains about this UnknownComponent. You can make the warning go away by adding a definition before it:

 component UnknownComponent: Item {} // add this line before the line with 'UnknownComponent {}'

Once you added the missing line, you might realize that you did not need to recompile the project, or call qmllint by hand, to check if the file had more errors like this: QML Language Server took care of updating the warnings for you.

Showing off new features of QML Language Server in 6.6

Note that QML Language Server is still in active development and might crash or be buggy. You can report any crashes or bugs here.

To be able to take screenshots, I decided to trigger the different QML Language Server features from the context menu: of course you can instead use the shortkeys provided by your editor if you know them.

Go to definition

One of the new features of QML Language Server that arrived in 6.6 and will get refined further in 6.7 is the ability to find the definition of certain objects. Lets say you would like to know where that myItem.i was defined because someone decided to name all of their properties to ‘i’. Right-click on the i in myItem.i and choose ‘Go to definition’:

Ask for the definition of i.

And QML Language Server will make your editor jump to the property definition of i:

The definition of i.

Over time, as QML Language Server gains in functionality, the number of actions available from the right-click menu will increase.

This also works for ids, for example:

Ask for the definition of myItems.

will find the definition of myItems which is somewhere above:

The definition of myItems.

Try it out yourself! Write some QML code and let QML Language Server find its definition for. You can report any unexpected behavior here.

Go to type definition

While this feature sounds quite similar to the previous one, it allows you to find the definition of the type of a property, like myComponent, for example:

Ask for the definition of myComponent.

and it will take you to the definition of the type of the myComponent property:

The definition of myComponent.

Full text formatting support

To format your code with qmlformat without leaving your editor, right-click anywhere in the code and select ‘Format Document’:

Format document from context menu.

You should see a beautifully formatted QML file now:

Formatted document.

Finding usages

Another new feature in QML Language Server in 6.6 is the ability to find usages of symbols. Function f seems to use the property i a lot, for example. Lets ask QML Language Server to find all occurences of the property i in the current QML file!

Ask for usages of i.

You should see following list of usages:

Usages of i.

Note that the definition of i is also included in the list of usages, and that QML Language Server managed to distinguish between the JavaScript Identifier i defined inside f and the JavaScript Idenfifier i defined inside the block of the while-loop. The latter i shadows the first i. If you are curious, you can also take a look at the inner identifier’s usages, and see that those do not get mixed up with the homonymous property:

Ask for usages of i.

And the result is:

Usages of i.

You can also find usages of a certain id, for example with myItems:

Usages of myItems.

And the results are:

Usages of myItems.

Known Limitations

QML Language Server has some known limitations in 6.6, here is an (non-exhaustive) list of them.

Method names

Currently, method names are not supported, QML Language Server cannot find references or go to the definition of a method.

Unimplemented JavaScript Language Constructs

Some JavaScript language constructs like loops (for, while, etc…) did not make it into 6.6 and are not supported by QML Language Server. In such case, it might crash (you can report it here) or show incomplete or no information when asking for definitions, usages, and so on.

If your code uses unimplemented constructs, consider using the version of QML Language Server from dev as it already supports most JavaScript constructs.

Renaming Symbols

The LSP-feature renaming symbol will be available on from QML Language Server in 6.7. You can get a peek of its current functionality if you use the QML Language Server from dev.

Partial Text Formatting Support

While the current QML Language Server version allows to format the entire file, it is not possible yet to format parts of a file. In the future, it should be possible to format only selected code instead of the entire file.

Conclusion

I hope you learned a lot about the QML Language Server and that I could motivate you to take a look at QML Language Server, try it out on your own code, and to open bugs here when encountering bugs or crashes, either for QML Language Server from 6.6 or from dev.


Blog Topics:

Comments