Skip to main content

QML格式化工具新特性

Comments

Qt自带的QML文件格式工具qmlformat的最新更新带来了两项实用的改进:可配置的换行设置和自动导入排序功能。这些功能提高了代码的可读性和可维护性,是值得关注的新增功能。

指定长度后换行

冗长的代码行可能导致难以阅读和浏览,尤其是在处理具有众多属性的复杂QML组件时尤为明显。

从 Qt 6.9 起,qmlformat  可以自动对超过指定字符长度的代码行进行换行 。

选项 说明
-W,--column-width< width > 对超过指定宽度的行进行换行。使用-1可禁用换行(默认)。

实际换行示例

您可以指定一个最大行长,qmlformat会自动将超过此限制的行进行换行处理。例如:

// main.qml 
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Templates as T

T.ItemDelegate {
id: control highlighted: control.pressed || control.hovered || (control.ListView.view && control.ListView.view.currentIndex === index && control.ListView.view.highlightFollowsCurrentItem && control.enabled }

如果在使用qmlformat时未启用换行选项,会降低代码的可读性:

import QtQuick 
import QtQuick.Controls.Material
import QtQuick.Templates as T

T.ItemDelegate {
id: control highlighted: control.pressed || control.hovered || (control.ListView.view && control.ListView.view.currentIndex === index && control.ListView.view.highlightFollowsCurrentItem && control.enabled
}

使用qmlformat-W 80 main.qml生成的结果更具可读性:

import QtQuick 
import QtQuick.Controls.Material
import QtQuick.Templates as T

T.ItemDelegate {
id: control
highlighted: control.pressed ||control.hovered ||(
control.ListView.view
&& control.ListView.view.currentIndex
=== index
&& control.ListView.view.highlightFollowsCurrentItem
&& control.enabled

导入语句排序

管理杂乱无章或排序不一致的导入语句是一项挑战,当多个协作者修改同一文件时,往往会导致不必要的合并冲突。

为了解决这些问题,Qt 6.10在qmlformat中引入了--sort-imports选项。排序后的导入语句能清楚地显示使用了哪些模块,从而提高可读性,减少合并冲突带来的无效差异 ,并在所有 QML 文件中强制保持一致的结构。

选项 说明
-S,--sort-imports 按字母顺序对导入语句进行排序(警告:如果同一类型名在多个模块中存在,排序可能会改变语义!)。

 

⚠️警告:使用--sort-imports按字母顺序对导入语句进行排序,如果多个模块中存在相同的类型名,可能会改变QML文件的语义。请务必检查排序结果,以确保应用程序的行为符合预期。

导入语句排序示例

// Original main.qml 
import QtQuick.Templates
import MyCustomModule
Button {
}

运行qmlformat -S main.qml之后

import MyCustomModule 
import QtQuick.Templates

Button {
}

在本示例中,原始的main.qml使用了MyCustomModule众的Button。使用--sort-imports格式化后,语义就变成了使用QtQuick.Templates中的非可视化Button。

即将推出:可定制的分号规则

从Qt 6.10开始,我们引入了一个新的-semicolon-rule选项,让用户可以更方便地控制在格式化过程中如何处理 JavaScript 分号。新标志支持两种模式:always,即会在所有JavaScript语句添加分号;essential,即删除分号,除非因JavaScript的自动分号插入 (ASI) 机制,删除分号会导致歧义。

模式 说明
always 在所有JavaScript语句中添加分号(默认)
essential 删除分号,除非ASI(自动分号插入)机制可能引发歧义

 

注意:该选项只影响JavaScript语句末尾的分号。QML元素末尾的分号总是会被qmlformat移除,与-semicolon-rule设置无关。

作为分号规则改进的一部分,对EmptyStatements(空语句)的处理也进行了优化:在没有函数体的控制结构(如 if、for 或 while)后面的分号现在会直接出现在右结尾括号之后,而不是在新的缩进行上。此外,多个连续的空语句被合并为一个分号,使结果更简洁。


import QtQuick 
Item {
Component.onCompleted:{
for(;;);;
;;
if (true);;;


while (true);
;
;;
var a = [1, 2, 3];;;;
for (var i in a);;;;
}
}
// qmlformat output prior to 6.10
import QtQuick

Item {
Component.onCompleted:{
for (; ; )
;
;
;
;
if (true)
;
;
;

;
while (true)
;
;
;
;
var a = [1, 2, 3];
;
;
;
for (var i in a)
;
;
;
;
}
}
// qmlformat output as of 6.10
import QtQuick
Item{
Component.onCompleted:{
for (; ; );
if (true);
while (true)
var a = [1, 2, 3];
for (var i in a);
}
}

可定制分号规则示例

// original test.js 
let v = a;;;;;;
[1, 2, 3].forEach(x => console.log(x));;;;;
;;;
;;;
let x = v;;;
x = 10;;;
;
;;;
;
let y = x;;;
(function() { })();;
let a = b
+ c
;
let xx = y;
-x;

qmlformat --semicolon-rule=always test.js在JS语句末尾添加分号。

let v = a; 
[1, 2, 3].
forEach(x => console.log(x));
let x = v;
x = 10;
let y = x;
(function () {})();
let a = b + c;
let xx = y;
-x;

qmlformat --semicolon-rule=essential test.js删除所有分号,仅保留在删除可能引发歧义的情况下必须保留的分号。 

例如,let v = a; 后面的分号不会被删除,因为下面一行是以[ 符号开头的:

let v = a; 
[1, 2, 3].forEach(x => console.log(x))
let x = v
x = 10
let y = x;
(function () {})()
let a = b + c
let xx = y;
-x

在JavaScript中,某些以特定符号开头的行(如 (、[、+ 或 -)可能会导致自动分号插入(ASI)机制无法按预期工作。为了避免改变代码的语义,qmlformat 在这些情况下会保留分号。

Qt Creator 17中的qmlformat集成

我们很高兴地宣布,qmlformat现已直接集成至Qt Creator 17中,从而使QML 代码格式化在各项目之间更易访问且保持一致。

当您在Preference>Qt Quick>Code Style中选择qmlformat作为QML代码格式器时,Qt Creator会自动检测您系统上可用的最新qmlformat版本,并通过执行命令qmlformat--write-defaults生成默认配置。可以根据自己的格式化偏好自定义该配置;更多信息请点击此处。全局配置文件将存储于路径 QStandardPaths::GenericConfigLocation/.qmlformat.ini 中

了解qmlformat如何定位其配置设置非常重要。在格式化过程中,它首先会在源文件的目录中查找.qmlformat.ini文件。若未找到本地配置文件,则会逐级向上搜索目录层级,直至找到有效配置文件或抵达根目录。 若仍未发现本地配置,则最终回退到存储在通用配置位置的全局配置。

 以下流程图说明了qmlformat搜索配置文件的逻辑

[Start: QML file directory]

[Is .qmlformat.ini here?] → Yes → [Use it]
↓ No
[Go to parent directory]

[Repeat until root]

[Still not found?][Use global config in GenericConfigLocation]

 如果您遇到格式化结果与在 Qt Creator 中设置的格式规则不一致的情况,请检查您的项目目录树(directory tree)中是否存在 .qmlformat.ini 文件。此类文件的优先级高于全局配置,可能会覆盖您预期的样式化配置。

Qt Creator 17中的qmlformat实践

在Qt Creator 17中,我们新增了一个用于配置 QML 格式化选项的偏好设置板块 。要在QML文件中使用 qmlformat,您需要明确将其设置为首选格式化器 。

pref

在Qt Creator中设置qmlformat

  1. 导航至Preference>Qt Quick>Code Sytle
  2. Formatter Selection下拉菜单中,选择QmlFormat[LSP]
  3. Global qmlformat configuration中,根据您的偏好调整列宽或导入排序等其他选项

自定义全局qml格式配置后,右侧的预览窗口将实时展示格式化后的 QML 文件效果。

在QML文件中使用qmlformat

配置完成后,对QML文件进行格式化操作就非常简单了:

  1. 在编辑器中打开任意QML文件
  2. 右键点击打开上下文菜单
  3. 选择Reformat Document

或者,您也可以通过Tools>QQml/JS>Reformat Document进行重格式化操作。

请注意:如果您此前使用的是Qt Creator自带的内置格式化器 ,切换到qmlformat可能会产生不同的格式化结果。这是因为两者使用了不同的格式化算法和代码风格规则。建议在首次格式化后检查代码,以确保其符合您的预期。

使用其他版本的qmlformat


如果想使用特定版本的qmlformat或自己的分支版本,可以选择 Custom Formatter(自定义格式化器)选项 。需要在参数部分设置路径并指定选项。有关所有可用选项,请参阅qmlformat官方文档

您还可以使用shell脚本对qmlformat进行封装,从而加快工作流程。以下是一个简单的shell包装示例:

#!/bin/sh
# qmlformat-wrapper.sh

/path/to/your/qmlformat --indent-width 12 "$@"

chmod +x qmlformat-wrapper.sh

然后您可以将该脚本路径设置为 Custom Formatter,参数区域可以留空,或者根据需要添加额外选项。您也可以直接将Custom Formatter path指向qmlformat可执行文件,并在参数区域填写相关选项:

 

customformatter

注意:Qt Creator目前仍默认使用旧版格式化器,以保持兼容性和用户已有的使用习惯。由于旧版格式化器与 qmlformat 的输出并不完全一致,因此在Qt Creator中默认使用qmlformat可能会导致用户的代码风格造成意外影响 ,尤其是在已有项目或多人协作的项目中。在qmlformat的集成更加成熟和统一之前,Qt Creator 将选择权交由用户自行决定 。

下一步是什么?

 随着qmlformat集成进Qt Creator 17,QML开发工作变得更加轻松。 新功能(如自动换行和导入排序)有助于保持代码的整洁、可读性并且风格统一。而且格式化操作就集成在Qt Creator中,仅需点击几下即可完成。

接下来我们将致力于开发对整个项目进行批量格式化的功能。

欢迎您亲自体验这些新功能,并反馈您的使用感受。您的意见将帮助我们进一步优化QML工具,为所有开发者带来更好的体验。

感谢您成为Qt社区的一员--祝您在使用Qt Creator 17和qmlformat时编码愉快!

Blog Topics

Comments

Subscribe to our blog

Try Qt 6.11 Now!

Download the latest release here: www.qt.io/download

Qt 6.11 is now available, with new features and improvements for application developers and device creators.

We're Hiring

Check out all our open positions here and follow us on Instagram to see what it's like to be #QtPeople.