aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qmlls
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qmlls')
-rw-r--r--tests/auto/qmlls/CMakeLists.txt10
-rw-r--r--tests/auto/qmlls/cli/CMakeLists.txt34
-rw-r--r--tests/auto/qmlls/cli/data/ImportPath1/SomeModule/A.qml5
-rw-r--r--tests/auto/qmlls/cli/data/ImportPath1/SomeModule/qmldir2
-rw-r--r--tests/auto/qmlls/cli/data/ImportPath2/AnotherModule/B.qml5
-rw-r--r--tests/auto/qmlls/cli/data/ImportPath2/AnotherModule/qmldir2
-rw-r--r--tests/auto/qmlls/cli/data/sourceFolder/ImportFromBothPaths.qml10
-rw-r--r--tests/auto/qmlls/cli/data/sourceFolder/ImportFromImportPath1.qml5
-rw-r--r--tests/auto/qmlls/cli/tst_qmlls_cli.cpp309
-rw-r--r--tests/auto/qmlls/cli/tst_qmlls_cli.h38
-rw-r--r--tests/auto/qmlls/completions/CMakeLists.txt27
-rw-r--r--tests/auto/qmlls/completions/tst_qmllscompletions.cpp500
-rw-r--r--tests/auto/qmlls/lifecycle/CMakeLists.txt10
-rw-r--r--tests/auto/qmlls/lifecycle/qiopipe.cpp2
-rw-r--r--tests/auto/qmlls/lifecycle/qiopipe.h2
-rw-r--r--tests/auto/qmlls/lifecycle/tst_lifecycle.cpp4
-rw-r--r--tests/auto/qmlls/modules/CMakeLists.txt45
-rw-r--r--tests/auto/qmlls/modules/data/buildDir/BuildDir/BuildDirType.qml (renamed from tests/auto/qmlls/completions/data/buildDir/BuildDir/BuildDirType.qml)0
-rw-r--r--tests/auto/qmlls/modules/data/buildDir/BuildDir/qmldir (renamed from tests/auto/qmlls/completions/data/buildDir/BuildDir/qmldir)1
-rw-r--r--tests/auto/qmlls/modules/data/completions/SomeBase.qml5
-rw-r--r--tests/auto/qmlls/modules/data/completions/Yyy.qml (renamed from tests/auto/qmlls/completions/data/completions/Yyy.qml)3
-rw-r--r--tests/auto/qmlls/modules/data/completions/Zzz.qml (renamed from tests/auto/qmlls/completions/data/completions/Zzz.qml)0
-rw-r--r--tests/auto/qmlls/modules/data/completions/bindingAfterDot.qml14
-rw-r--r--tests/auto/qmlls/modules/data/completions/defaultBindingAfterDot.qml14
-rw-r--r--tests/auto/qmlls/modules/data/completions/fromBuildDir.qml (renamed from tests/auto/qmlls/completions/data/completions/fromBuildDir.qml)1
-rw-r--r--tests/auto/qmlls/modules/data/findDefinition/jsDefinitions.qml17
-rw-r--r--tests/auto/qmlls/modules/data/findUsages/jsIdentifierUsages.qml13
-rw-r--r--tests/auto/qmlls/modules/data/formatting/blanklines.formatted.qml4
-rw-r--r--tests/auto/qmlls/modules/data/formatting/blanklines.qml15
-rw-r--r--tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted1.qml23
-rw-r--r--tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted2.qml20
-rw-r--r--tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted3.qml23
-rw-r--r--tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted4.qml20
-rw-r--r--tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted5.qml20
-rw-r--r--tests/auto/qmlls/modules/data/formatting/rangeFormatting.qml23
-rw-r--r--tests/auto/qmlls/modules/data/highlighting/basic.qml11
-rw-r--r--tests/auto/qmlls/modules/data/highlighting/bigFile.qml351
-rw-r--r--tests/auto/qmlls/modules/data/hover/test.qml8
-rw-r--r--tests/auto/qmlls/modules/data/linting/SimpleItem.qml6
-rw-r--r--tests/auto/qmlls/modules/data/quickfixes/INeedAQuickFix.qml19
-rw-r--r--tests/auto/qmlls/modules/data/renameUsages/RenameMe.qml5
-rw-r--r--tests/auto/qmlls/modules/data/renameUsages/RenameMe2.ui.qml5
-rw-r--r--tests/auto/qmlls/modules/data/renameUsages/main.qml6
-rw-r--r--tests/auto/qmlls/modules/data/sourceDir/A.qml5
-rw-r--r--tests/auto/qmlls/modules/data/sourceDir/Main.qml6
-rw-r--r--tests/auto/qmlls/modules/data/sourceDir/qmldir3
-rw-r--r--tests/auto/qmlls/modules/tst_qmlls_modules.cpp1765
-rw-r--r--tests/auto/qmlls/modules/tst_qmlls_modules.h85
-rw-r--r--tests/auto/qmlls/qmlls/CMakeLists.txt12
-rw-r--r--tests/auto/qmlls/qmlls/tst_qmlls.cpp13
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/CMakeLists.txt33
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/FileA.qml5
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/FileA2.qml5
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/FileB.qml5
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/Main.qml5
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/mycppmodule.qmltypes36
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/qmldir5
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/Main.qml13
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/helloworld.cpp8
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/helloworld.h29
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.cpp10
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.h21
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.qml8
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement2.cpp10
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement2.hpp18
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/somecppclass.cpp9
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/somecppclass.h14
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/anotherqmlelement.cpp10
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/anotherqmlelement.h18
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.cpp10
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.h21
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.txt0
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/tst_qmlls_qqmlcodemodel.cpp185
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/tst_qmlls_qqmlcodemodel.h37
-rw-r--r--tests/auto/qmlls/utils/CMakeLists.txt63
-rw-r--r--tests/auto/qmlls/utils/data/BaseType.qml28
-rw-r--r--tests/auto/qmlls/utils/data/JSDefinitions.qml76
-rw-r--r--tests/auto/qmlls/utils/data/JSUsages.qml196
-rw-r--r--tests/auto/qmlls/utils/data/JSUsagesFromAnotherFile.qml16
-rw-r--r--tests/auto/qmlls/utils/data/Type.qml30
-rw-r--r--tests/auto/qmlls/utils/data/Yyy.qml143
-rw-r--r--tests/auto/qmlls/utils/data/Zzz.qml12
-rw-r--r--tests/auto/qmlls/utils/data/completions/afterDots.qml20
-rw-r--r--tests/auto/qmlls/utils/data/completions/attachedAndGroupedProperty.qml12
-rw-r--r--tests/auto/qmlls/utils/data/completions/attachedPropertyMissingRHS.qml8
-rw-r--r--tests/auto/qmlls/utils/data/completions/boundComponents.qml21
-rw-r--r--tests/auto/qmlls/utils/data/completions/commaExpression.qml7
-rw-r--r--tests/auto/qmlls/utils/data/completions/conditionalExpression.qml7
-rw-r--r--tests/auto/qmlls/utils/data/completions/continueAndBreakStatement.qml31
-rw-r--r--tests/auto/qmlls/utils/data/completions/functionBody.qml13
-rw-r--r--tests/auto/qmlls/utils/data/completions/groupedPropertyMissingRHS.qml8
-rw-r--r--tests/auto/qmlls/utils/data/completions/labelledStatement.qml20
-rw-r--r--tests/auto/qmlls/utils/data/completions/missingRHS.parserfail.qml9
-rw-r--r--tests/auto/qmlls/utils/data/completions/missingRHS.qml19
-rw-r--r--tests/auto/qmlls/utils/data/completions/parenthesizedExpression.qml10
-rw-r--r--tests/auto/qmlls/utils/data/completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml11
-rw-r--r--tests/auto/qmlls/utils/data/completions/returnStatement.qml10
-rw-r--r--tests/auto/qmlls/utils/data/completions/suggestContinueAndBreak.qml54
-rw-r--r--tests/auto/qmlls/utils/data/completions/switchStatements.qml37
-rw-r--r--tests/auto/qmlls/utils/data/completions/throwStatement.qml11
-rw-r--r--tests/auto/qmlls/utils/data/completions/tryStatements.qml7
-rw-r--r--tests/auto/qmlls/utils/data/completions/unaryExpression.qml17
-rw-r--r--tests/auto/qmlls/utils/data/completions/variableDeclaration.qml36
-rw-r--r--tests/auto/qmlls/utils/data/emptyFile.qml0
-rw-r--r--tests/auto/qmlls/utils/data/file1.qml55
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/attachedPropertyUsage/attachedPropertyUsage.qml14
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/binding/binding.qml11
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/bindings/bindings.qml39
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/connections/connections.qml37
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/enums/Enums.qml27
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/enums/EnumsFromAnotherFile.qml5
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/groupPropertyUsage/fontFamilyUsage.qml6
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/groupPropertyUsage/groupPropertyUsage.qml35
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/idUsages/idUsages.qml19
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/inlineComponents/InlineComponentProvider.qml23
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/inlineComponents/inlineComponents.qml26
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/inlineComponents/inlineComponents2.qml9
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/jsIdentifier/jsIdentifier.qml16
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/parametersAndDeconstruction/parametersAndDeconstruction.qml16
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/property/PropertyFromAnotherFile.qml5
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/property/property.qml48
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/propertyChanges/propertyChanges.qml35
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile.qml6
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile2.qml6
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile3.qml6
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile4.qml6
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/propertyInNested/propertyInNested.qml56
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/recursive/RecursiveInOtherFile.qml10
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/recursive/recursive.qml28
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/signalAndHandlers2.qml43
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/signalsAndHandlers.qml40
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/widthChangedInAnotherFile.qml6
-rw-r--r--tests/auto/qmlls/utils/data/highlights/Identifiers.qml37
-rw-r--r--tests/auto/qmlls/utils/data/highlights/bindings.qml12
-rw-r--r--tests/auto/qmlls/utils/data/highlights/comments.qml19
-rw-r--r--tests/auto/qmlls/utils/data/highlights/enums.qml11
-rw-r--r--tests/auto/qmlls/utils/data/highlights/identifiers.qml37
-rw-r--r--tests/auto/qmlls/utils/data/highlights/imports.qml9
-rw-r--r--tests/auto/qmlls/utils/data/highlights/literals.qml14
-rw-r--r--tests/auto/qmlls/utils/data/highlights/methodAndSignal.qml11
-rw-r--r--tests/auto/qmlls/utils/data/highlights/objectAndComponent.qml11
-rw-r--r--tests/auto/qmlls/utils/data/highlights/pragmas.qml10
-rw-r--r--tests/auto/qmlls/utils/data/highlights/properties.qml13
-rw-r--r--tests/auto/qmlls/utils/data/highlights/scriptExpressions.qml116
-rw-r--r--tests/auto/qmlls/utils/data/pragmas.qml8
-rw-r--r--tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtqml-qtobject-qt-5.html134
-rw-r--r--tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtqml-qtobject.html129
-rw-r--r--tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtquick-item.html1486
-rw-r--r--tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtquick-mousearea.html676
-rw-r--r--tests/auto/qmlls/utils/data/qualifiedModule.qml7
-rw-r--r--tests/auto/qmlls/utils/data/renaming/RenameMe.qml5
-rw-r--r--tests/auto/qmlls/utils/data/renaming/RenameMe2.ui.qml5
-rw-r--r--tests/auto/qmlls/utils/data/renaming/RenamedByQmldir.qml4
-rw-r--r--tests/auto/qmlls/utils/data/renaming/UnrelatedFile.qml0
-rw-r--r--tests/auto/qmlls/utils/data/renaming/main.qml7
-rw-r--r--tests/auto/qmlls/utils/data/renaming/qmldir6
-rw-r--r--tests/auto/qmlls/utils/data/resolveExpressionType/BaseType.qml12
-rw-r--r--tests/auto/qmlls/utils/data/resolveExpressionType/BindingsOnDeferred.qml19
-rw-r--r--tests/auto/qmlls/utils/data/resolveExpressionType/Derived1.qml6
-rw-r--r--tests/auto/qmlls/utils/data/resolveExpressionType/Derived2.qml6
-rw-r--r--tests/auto/qmlls/utils/data/resolveExpressionType/DerivedType.qml32
-rw-r--r--tests/auto/qmlls/utils/tst_qmlls_documentationHints.cpp134
-rw-r--r--tests/auto/qmlls/utils/tst_qmlls_documentationHints.h24
-rw-r--r--tests/auto/qmlls/utils/tst_qmlls_highlighting.cpp650
-rw-r--r--tests/auto/qmlls/utils/tst_qmlls_highlighting.h37
-rw-r--r--tests/auto/qmlls/utils/tst_qmlls_utils.cpp4156
-rw-r--r--tests/auto/qmlls/utils/tst_qmlls_utils.h95
167 files changed, 13000 insertions, 544 deletions
diff --git a/tests/auto/qmlls/CMakeLists.txt b/tests/auto/qmlls/CMakeLists.txt
index bcb8d121d3..daf7d64cd4 100644
--- a/tests/auto/qmlls/CMakeLists.txt
+++ b/tests/auto/qmlls/CMakeLists.txt
@@ -1,8 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-if (TARGET Qt::LanguageServerPrivate)
- add_subdirectory(qmlls)
+if (TARGET Qt::QmlLSPrivate)
add_subdirectory(lifecycle)
- add_subdirectory(completions)
+ add_subdirectory(utils)
+ add_subdirectory(qqmlcodemodel)
+ add_subdirectory(qmlls)
+ add_subdirectory(modules)
+ add_subdirectory(cli)
endif()
+
diff --git a/tests/auto/qmlls/cli/CMakeLists.txt b/tests/auto/qmlls/cli/CMakeLists.txt
new file mode 100644
index 0000000000..1ac8016f66
--- /dev/null
+++ b/tests/auto/qmlls/cli/CMakeLists.txt
@@ -0,0 +1,34 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlls_modules LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data)
+
+qt_internal_add_test(tst_qmlls_cli
+ SOURCES
+ tst_qmlls_cli.cpp
+ tst_qmlls_cli.h
+ DEFINES
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::LanguageServerPrivate
+ Qt::Test
+ Qt::QuickTestUtilsPrivate
+ Qt::QmlLSPrivate
+ TESTDATA ${test_data}
+)
+
+if (TARGET qmlls)
+ # standalone test builds do not know the qmlls target
+ # but if TARGET qmlls is known it should be built before this test
+ add_dependencies(tst_qmlls_cli qmlls)
+endif()
diff --git a/tests/auto/qmlls/cli/data/ImportPath1/SomeModule/A.qml b/tests/auto/qmlls/cli/data/ImportPath1/SomeModule/A.qml
new file mode 100644
index 0000000000..5468cae5e1
--- /dev/null
+++ b/tests/auto/qmlls/cli/data/ImportPath1/SomeModule/A.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property string helloSomeModule
+}
diff --git a/tests/auto/qmlls/cli/data/ImportPath1/SomeModule/qmldir b/tests/auto/qmlls/cli/data/ImportPath1/SomeModule/qmldir
new file mode 100644
index 0000000000..6e8de8bc53
--- /dev/null
+++ b/tests/auto/qmlls/cli/data/ImportPath1/SomeModule/qmldir
@@ -0,0 +1,2 @@
+module SomeModule
+A 254.0 A.qml
diff --git a/tests/auto/qmlls/cli/data/ImportPath2/AnotherModule/B.qml b/tests/auto/qmlls/cli/data/ImportPath2/AnotherModule/B.qml
new file mode 100644
index 0000000000..33af59274a
--- /dev/null
+++ b/tests/auto/qmlls/cli/data/ImportPath2/AnotherModule/B.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property string helloAnotherModule
+}
diff --git a/tests/auto/qmlls/cli/data/ImportPath2/AnotherModule/qmldir b/tests/auto/qmlls/cli/data/ImportPath2/AnotherModule/qmldir
new file mode 100644
index 0000000000..aa4ef803b1
--- /dev/null
+++ b/tests/auto/qmlls/cli/data/ImportPath2/AnotherModule/qmldir
@@ -0,0 +1,2 @@
+module AnotherModule
+B 254.0 B.qml
diff --git a/tests/auto/qmlls/cli/data/sourceFolder/ImportFromBothPaths.qml b/tests/auto/qmlls/cli/data/sourceFolder/ImportFromBothPaths.qml
new file mode 100644
index 0000000000..4a2775ce29
--- /dev/null
+++ b/tests/auto/qmlls/cli/data/sourceFolder/ImportFromBothPaths.qml
@@ -0,0 +1,10 @@
+import SomeModule
+import AnotherModule
+
+A {
+ helloSomeModule: "hello!"
+
+ B {
+ helloAnotherModule: "World!"
+ }
+} \ No newline at end of file
diff --git a/tests/auto/qmlls/cli/data/sourceFolder/ImportFromImportPath1.qml b/tests/auto/qmlls/cli/data/sourceFolder/ImportFromImportPath1.qml
new file mode 100644
index 0000000000..3417701352
--- /dev/null
+++ b/tests/auto/qmlls/cli/data/sourceFolder/ImportFromImportPath1.qml
@@ -0,0 +1,5 @@
+import SomeModule
+
+A {
+ helloSomeModule: "hello!"
+}
diff --git a/tests/auto/qmlls/cli/tst_qmlls_cli.cpp b/tests/auto/qmlls/cli/tst_qmlls_cli.cpp
new file mode 100644
index 0000000000..344ed48e64
--- /dev/null
+++ b/tests/auto/qmlls/cli/tst_qmlls_cli.cpp
@@ -0,0 +1,309 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_qmlls_cli.h"
+
+using namespace Qt::StringLiterals;
+
+void tst_qmlls_cli::initTestCase()
+{
+ QQmlDataTest::initTestCase();
+
+ m_qmllsPath = QLibraryInfo::path(QLibraryInfo::BinariesPath) + QLatin1String("/qmlls");
+#ifdef Q_OS_WIN
+ m_qmllsPath += QLatin1String(".exe");
+#endif
+ // allow overriding of the executable, to be able to use a qmlEcho script (as described in
+ // qmllanguageservertool.cpp)
+ m_qmllsPath = qEnvironmentVariable("QMLLS", m_qmllsPath);
+ m_server.setProgram(m_qmllsPath);
+}
+
+void tst_qmlls_cli::cleanup()
+{
+ m_server.closeWriteChannel();
+ m_server.waitForFinished();
+ QTRY_COMPARE(m_server.state(), QProcess::NotRunning);
+ QCOMPARE(m_server.exitStatus(), QProcess::NormalExit);
+}
+
+// Helper structs to avoid confusions between expected and unexpected messages and between expected
+// and unexpected diagnostics.
+struct ExpectedMessages : public QStringList
+{
+ using QStringList::QStringList;
+};
+struct UnexpectedMessages : public QStringList
+{
+ using QStringList::QStringList;
+};
+struct ExpectedDiagnostics : public QStringList
+{
+ using QStringList::QStringList;
+};
+struct UnexpectedDiagnostics : public QStringList
+{
+ using QStringList::QStringList;
+};
+
+// Extra environment variables to be added to qmlls's environment.
+struct Environment : public QList<QPair<QString, QString>>
+{
+ using QList<QPair<QString, QString>>::QList;
+};
+
+void tst_qmlls_cli::warnings_data()
+{
+ QTest::addColumn<QStringList>("args");
+ QTest::addColumn<Environment>("environment");
+ QTest::addColumn<QString>("filePath");
+ // messages are printed to stderr and not shown in editor:
+ QTest::addColumn<ExpectedMessages>("expectedMessages");
+ QTest::addColumn<UnexpectedMessages>("unexpectedMessages");
+ // diagnostics are passed via LSP to be shown in editor:
+ QTest::addColumn<ExpectedDiagnostics>("expectedDiagnostics");
+ QTest::addColumn<UnexpectedDiagnostics>("unexpectedDiagnostics");
+
+ const Environment defaultEnv;
+ const QString dir1 = testFile(u"ImportPath1"_s);
+ const QString dir2 = testFile(u"ImportPath2"_s);
+ const QString notDir = testFile(u"ImportPath1/SomeModule/qmldir"_s);
+ const QString wrongDir = testFile(u"ImportPathInexistent"_s);
+
+ const QString fileImportingDir1 = testFile(u"sourceFolder/ImportFromImportPath1.qml"_s);
+ const QString fileImportingBothDirs = testFile(u"sourceFolder/ImportFromBothPaths.qml"_s);
+
+ const QString importWarningDir1 = u"Warnings occurred while importing module \"SomeModule\""_s;
+ const QString importWarningDir2 = u"Warnings occurred while importing module \"AnotherModule\""_s;
+
+ const UnexpectedMessages noUnexpectedMessages;
+ const QString warnAboutQmllsIniFiles{
+ u"Using the build directories found in the .qmlls.ini file. Your build folder might not be found if no .qmlls.ini files are present in the root source folder."_s
+ };
+
+ QTest::addRow("2-build-dirs")
+ << QStringList{ u"--build-dir"_s, dir1, u"-b"_s, dir2 } << defaultEnv
+ << fileImportingDir1
+ << ExpectedMessages{ u"Using build directories passed by -b: \"%1\", \"%2\"."_s.arg(
+ dir1, dir2) }
+ << UnexpectedMessages{ warnAboutQmllsIniFiles } << ExpectedDiagnostics{}
+ << UnexpectedDiagnostics{ importWarningDir1 };
+
+ QTest::addRow("build-dir-not-dir")
+ << QStringList{ u"--build-dir"_s, notDir, u"-b"_s, dir2 } << defaultEnv
+ << fileImportingBothDirs
+ << ExpectedMessages{ u"Argument \"%1\" passed to -b is not a directory."_s.arg(notDir) }
+ << UnexpectedMessages{ warnAboutQmllsIniFiles }
+ << ExpectedDiagnostics{ importWarningDir1 }
+ << UnexpectedDiagnostics{ importWarningDir2 };
+
+ QTest::addRow("build-dir-not-existing")
+ << QStringList{ u"--build-dir"_s, wrongDir, u"-b"_s, dir2 } << defaultEnv
+ << fileImportingBothDirs
+ << ExpectedMessages{ u"Argument \"%1\" passed to -b does not exist."_s.arg(wrongDir) }
+ << UnexpectedMessages{ warnAboutQmllsIniFiles } << ExpectedDiagnostics{}
+ << UnexpectedDiagnostics{};
+
+ QTest::addRow("build-dir-from-environment")
+ << QStringList{}
+ << Environment{ { u"QMLLS_BUILD_DIRS"_s,
+ u"%1%2%3"_s.arg(dir1, QDir::listSeparator(), dir2) } }
+ << fileImportingBothDirs
+ << ExpectedMessages{ u"Using build directories passed from environment variable \"QMLLS_BUILD_DIRS\": \"%1\", \"%2\"."_s
+ .arg(dir1, dir2) }
+ << UnexpectedMessages{ warnAboutQmllsIniFiles } << ExpectedDiagnostics{}
+ << UnexpectedDiagnostics{ importWarningDir1, importWarningDir2 };
+
+ QTest::addRow("build-dir-from-environment-not-existing")
+ << QStringList{}
+ << Environment{ { u"QMLLS_BUILD_DIRS"_s,
+ QStringList{ dir1, wrongDir, notDir }.join(QDir::listSeparator()) } }
+ << fileImportingDir1
+ << ExpectedMessages{ u"Argument \"%1\" from environment variable \"QMLLS_BUILD_DIRS\" does not exist."_s
+ .arg(wrongDir),
+ u"Argument \"%1\" from environment variable \"QMLLS_BUILD_DIRS\" is not a directory."_s
+ .arg(notDir) }
+ << UnexpectedMessages{ warnAboutQmllsIniFiles } << ExpectedDiagnostics{}
+ << UnexpectedDiagnostics{ importWarningDir1, importWarningDir2 };
+
+ QTest::addRow("ignore-environment-with-option")
+ << QStringList{ u"--build-dir"_s, dir1 }
+ << Environment{ { u"QMLLS_BUILD_DIRS"_s, dir2 } } << fileImportingBothDirs
+ << ExpectedMessages{ u"Using build directories passed by -b: \"%1\"."_s.arg(dir1) }
+ << UnexpectedMessages{ dir2, warnAboutQmllsIniFiles }
+ << ExpectedDiagnostics{ importWarningDir2 }
+ << UnexpectedDiagnostics{ importWarningDir1 };
+
+ QTest::addRow("loadFromConfigFile")
+ << QStringList{} << Environment{} << fileImportingDir1
+ << ExpectedMessages{ warnAboutQmllsIniFiles } << UnexpectedMessages{}
+ << ExpectedDiagnostics{ importWarningDir1 } << UnexpectedDiagnostics{};
+
+ QTest::addRow("2-import-paths")
+ << QStringList{ u"-I"_s, dir1, u"-I"_s, dir2 } << Environment{} << fileImportingBothDirs
+ << ExpectedMessages{ u"Using import directories passed by -I: \"%1\", \"%2\"."_s.arg(
+ dir1, dir2) }
+ << UnexpectedMessages{} << ExpectedDiagnostics{}
+ << UnexpectedDiagnostics{ importWarningDir1, importWarningDir2 };
+
+ QTest::addRow("import-paths-ignore-env")
+ << QStringList{ u"-I"_s, dir1, } << Environment{ { u"QML_IMPORT_PATH"_s, dir2 } }
+ << fileImportingBothDirs
+ << ExpectedMessages{ u"Using import directories passed by -I: \"%1\"."_s.arg(dir1) }
+ << UnexpectedMessages{ u"Using import directories passed from environment variable \"QML_IMPORT_PATH\": \"%1\"."_s.arg(dir2)}
+ << ExpectedDiagnostics{importWarningDir2} << UnexpectedDiagnostics{ importWarningDir1 };
+
+ QTest::addRow("2-import-paths-mixed")
+ << QStringList{ u"-I"_s, dir1, u"-E"_s }
+ << Environment{ { u"QML_IMPORT_PATH"_s, dir2 } } << fileImportingBothDirs
+ << ExpectedMessages{ u"Using import directories passed by -I: \"%1\"."_s.arg(dir1),
+ u"Using import directories passed from environment variable \"QML_IMPORT_PATH\": \"%1\"."_s
+ .arg(dir2) }
+ << UnexpectedMessages{} << ExpectedDiagnostics{}
+ << UnexpectedDiagnostics{ importWarningDir1, importWarningDir2 };
+
+ QTest::addRow("2-import-paths-deprecated")
+ << QStringList{ u"-I"_s, dir1, u"-E"_s }
+ << Environment{ { u"QML2_IMPORT_PATH"_s, dir2 } } << fileImportingBothDirs
+ << ExpectedMessages{ u"Using import directories passed by -I: \"%1\"."_s.arg(dir1),
+ u"Using import directories passed from the deprecated environment variable \"QML2_IMPORT_PATH\": \"%1\"."_s
+ .arg(dir2) }
+ << UnexpectedMessages{} << ExpectedDiagnostics{}
+ << UnexpectedDiagnostics{ importWarningDir1, importWarningDir2 };
+}
+
+auto tst_qmlls_cli::startServerRAII()
+{
+ startServerImpl();
+ return qScopeGuard([this]() { this->stopServerImpl(); });
+}
+
+void tst_qmlls_cli::startServerImpl()
+{
+ m_protocol = std::make_unique<QLanguageServerProtocol>(
+ [this](const QByteArray &data) { m_server.write(data); });
+
+ connect(&m_server, &QProcess::readyReadStandardOutput, this, [this]() {
+ QByteArray data = m_server.readAllStandardOutput();
+ m_protocol->receiveData(data);
+ });
+
+ m_server.start();
+
+ QLspSpecification::InitializeParams clientInfo;
+ clientInfo.rootUri = QUrl::fromLocalFile(dataDirectory() + "/default").toString().toUtf8();
+
+ QLspSpecification::TextDocumentClientCapabilities tDoc;
+ tDoc.typeDefinition = QLspSpecification::TypeDefinitionClientCapabilities{ false, false };
+
+ QLspSpecification::PublishDiagnosticsClientCapabilities pDiag;
+ tDoc.publishDiagnostics = pDiag;
+ pDiag.versionSupport = true;
+ clientInfo.capabilities.textDocument = tDoc;
+ bool didInit = false;
+ m_protocol->requestInitialize(
+ clientInfo, [this, &didInit](const QLspSpecification::InitializeResult &serverInfo) {
+ Q_UNUSED(serverInfo);
+ m_protocol->notifyInitialized(QLspSpecification::InitializedParams());
+ didInit = true;
+ });
+ QTRY_COMPARE_WITH_TIMEOUT(didInit, true, 10000);
+}
+
+void tst_qmlls_cli::stopServerImpl()
+{
+ m_server.closeWriteChannel();
+ m_server.waitForFinished();
+ QTRY_COMPARE(m_server.state(), QProcess::NotRunning);
+ QCOMPARE(m_server.exitStatus(), QProcess::NormalExit);
+}
+
+void tst_qmlls_cli::warnings()
+{
+ QFETCH(QStringList, args);
+ QFETCH(Environment, environment);
+ QFETCH(ExpectedMessages, expectedMessages);
+ QFETCH(UnexpectedMessages, unexpectedMessages);
+ QFETCH(QString, filePath);
+ QFETCH(ExpectedDiagnostics, expectedDiagnostics);
+ QFETCH(UnexpectedDiagnostics, unexpectedDiagnostics);
+
+ QProcessEnvironment processEnvironment = QProcessEnvironment::systemEnvironment();
+ for (const auto &entry : environment)
+ processEnvironment.insert(entry.first, entry.second);
+ m_server.setProcessEnvironment(processEnvironment);
+ m_server.setArguments(args);
+
+ QList<int> countExpectedMessages(expectedMessages.size(), 0);
+ QList<int> countUnexpectedMessages(unexpectedMessages.size(), 0);
+ QList<int> countExpectedDiagnostics(expectedDiagnostics.size(), 0);
+ QList<int> countUnexpectedDiagnostics(unexpectedDiagnostics.size(), 0);
+
+ auto guard = qScopeGuard([this]() {
+ // note: the lambda used in the "connect"-call references local variables, so disconnect the
+ // lambda via QScopedGuard to avoid its captured references to dangle
+ disconnect(&m_server, &QProcess::readyReadStandardOutput, nullptr, nullptr);
+ });
+ connect(&m_server, &QProcess::readyReadStandardError, this,
+ [this, &expectedMessages, &countExpectedMessages, &unexpectedMessages,
+ &countUnexpectedMessages]() {
+ const auto data = QString::fromUtf8(m_server.readAllStandardError());
+ if (data.isEmpty())
+ return;
+
+ for (int i = 0; i < expectedMessages.size(); ++i) {
+ if (data.contains(expectedMessages[i]))
+ ++countExpectedMessages[i];
+ }
+ for (int i = 0; i < unexpectedMessages.size(); ++i) {
+ if (data.contains(unexpectedMessages[i]))
+ ++countUnexpectedMessages[i];
+ }
+ });
+
+ auto guard2 = startServerRAII();
+
+ // each expected message should appear exactly one time
+ QTRY_COMPARE_WITH_TIMEOUT(countExpectedMessages, QList<int>(expectedMessages.size(), 1), 500);
+ // each unexpected message should appear exactly zero times
+ QCOMPARE(countUnexpectedMessages, QList<int>(unexpectedMessages.size(), 0));
+
+ bool diagnosticOk = false;
+ m_protocol->registerPublishDiagnosticsNotificationHandler(
+ [&diagnosticOk, &expectedDiagnostics, &countExpectedDiagnostics, &unexpectedDiagnostics,
+ &countUnexpectedDiagnostics](const QByteArray &,
+ const QLspSpecification::PublishDiagnosticsParams &p) {
+ for (const auto &d : p.diagnostics) {
+ const QString message = QString::fromUtf8(d.message);
+ for (int i = 0; i < expectedDiagnostics.size(); ++i) {
+ if (message.contains(expectedDiagnostics[i]))
+ ++countExpectedDiagnostics[i];
+ }
+ for (int i = 0; i < unexpectedDiagnostics.size(); ++i) {
+ if (message.contains(unexpectedDiagnostics[i]))
+ ++countUnexpectedDiagnostics[i];
+ }
+ }
+ diagnosticOk = true;
+ });
+
+ QFile file(filePath);
+ QVERIFY(file.open(QIODevice::ReadOnly));
+
+ QLspSpecification::DidOpenTextDocumentParams oParams;
+ QLspSpecification::TextDocumentItem textDocument;
+ QByteArray uri = QUrl::fromLocalFile(filePath).toEncoded();
+ textDocument.uri = uri;
+ textDocument.text = file.readAll();
+ oParams.textDocument = textDocument;
+ m_protocol->notifyDidOpenTextDocument(oParams);
+
+ QTRY_VERIFY_WITH_TIMEOUT(diagnosticOk, 3000);
+ // each expected diagnostic should appear exactly one time
+ QCOMPARE(countExpectedDiagnostics, QList<int>(expectedDiagnostics.size(), 1));
+ // each unexpected diagnostic should appear exactly zero times
+ QCOMPARE(countUnexpectedDiagnostics, QList<int>(unexpectedDiagnostics.size(), 0));
+
+}
+
+QTEST_MAIN(tst_qmlls_cli)
diff --git a/tests/auto/qmlls/cli/tst_qmlls_cli.h b/tests/auto/qmlls/cli/tst_qmlls_cli.h
new file mode 100644
index 0000000000..3eab8b5d1b
--- /dev/null
+++ b/tests/auto/qmlls/cli/tst_qmlls_cli.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QMLLS_CLI_H
+#define TST_QMLLS_CLI_H
+
+#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qprocess.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qlibraryinfo.h>
+
+#include <QtTest/qtest.h>
+
+class tst_qmlls_cli: public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_qmlls_cli() : QQmlDataTest(QT_QMLTEST_DATADIR) { }
+ [[nodiscard]] auto startServerRAII();
+ void startServerImpl();
+ void stopServerImpl();
+
+private slots:
+ void initTestCase();
+ void cleanup();
+ void warnings_data();
+ void warnings();
+
+public:
+ QProcess m_server;
+ QString m_qmllsPath;
+ std::unique_ptr<QLanguageServerProtocol> m_protocol;
+};
+
+#endif // TST_QMLLS_CLI_H
diff --git a/tests/auto/qmlls/completions/CMakeLists.txt b/tests/auto/qmlls/completions/CMakeLists.txt
deleted file mode 100644
index 7b9a6111b2..0000000000
--- a/tests/auto/qmlls/completions/CMakeLists.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-file(GLOB_RECURSE test_data_glob
- RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
- data)
-list(APPEND test_data ${test_data_glob})
-
-qt_internal_add_test(tst_qmllscompletions
- SOURCES
- tst_qmllscompletions.cpp
- DEFINES
- QT_DEPRECATED_WARNINGS
- QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
- LIBRARIES
- Qt::Core
- Qt::QmlDomPrivate
- Qt::LanguageServerPrivate
- Qt::Test
- Qt::QuickTestUtilsPrivate
- TESTDATA ${test_data}
-)
-
-qt_internal_extend_target(tst_qmllscompletions CONDITION ANDROID OR IOS
- DEFINES
- QT_QMLTEST_DATADIR=":/domdata"
-)
diff --git a/tests/auto/qmlls/completions/tst_qmllscompletions.cpp b/tests/auto/qmlls/completions/tst_qmllscompletions.cpp
deleted file mode 100644
index e5d955a337..0000000000
--- a/tests/auto/qmlls/completions/tst_qmllscompletions.cpp
+++ /dev/null
@@ -1,500 +0,0 @@
-// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include <QtJsonRpc/private/qjsonrpcprotocol_p.h>
-#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
-#include <QtQuickTestUtils/private/qmlutils_p.h>
-#include <QtCore/private/qduplicatetracker_p.h>
-
-#include <QtCore/qobject.h>
-#include <QtCore/qprocess.h>
-#include <QtCore/qlibraryinfo.h>
-#include <QtCore/qstringlist.h>
-
-#include <QtTest/qtest.h>
-#include "../../../../tools/qmlls/lspcustomtypes.h"
-
-#include <iostream>
-#include <variant>
-
-// Check if QTest already has a QTEST_CHECKED macro
-#ifndef QTEST_CHECKED
-#define QTEST_CHECKED(...) \
-do { \
- __VA_ARGS__; \
- if (QTest::currentTestFailed()) \
- return; \
-} while (false)
-#endif
-
-QT_USE_NAMESPACE
-using namespace Qt::StringLiterals;
-using namespace QLspSpecification;
-
-class tst_QmllsCompletions : public QQmlDataTest
-{
- using ExpectedCompletion = QPair<QString, CompletionItemKind>;
- using ExpectedCompletions = QList<ExpectedCompletion>;
-
- using ExpectedDocumentation = std::tuple<QString, QString, QString>;
- using ExpectedDocumentations = QList<ExpectedDocumentation>;
-
- Q_OBJECT
-public:
- tst_QmllsCompletions();
- void checkCompletions(QByteArray uri, int lineNr, int character, ExpectedCompletions expected,
- QStringList notExpected);
-private slots:
- void initTestCase() final;
- void completions_data();
- void completions();
- void function_documentations_data();
- void function_documentations();
- void buildDir();
- void cleanupTestCase();
-
-private:
- QProcess m_server;
- QLanguageServerProtocol m_protocol;
- QString m_qmllsPath;
- QList<QByteArray> m_uriToClose;
-};
-
-tst_QmllsCompletions::tst_QmllsCompletions()
- : QQmlDataTest(QT_QMLTEST_DATADIR),
- m_protocol([this](const QByteArray &data) { m_server.write(data); })
-{
- connect(&m_server, &QProcess::readyReadStandardOutput, this, [this]() {
- QByteArray data = m_server.readAllStandardOutput();
- m_protocol.receiveData(data);
- });
-
- connect(&m_server, &QProcess::readyReadStandardError, this,
- [this]() {
- QProcess::ProcessChannel tmp = m_server.readChannel();
- m_server.setReadChannel(QProcess::StandardError);
- while (m_server.canReadLine())
- std::cerr << m_server.readLine().constData();
- m_server.setReadChannel(tmp);
- });
-
- m_qmllsPath =
- QLibraryInfo::path(QLibraryInfo::BinariesPath) + QLatin1String("/qmlls");
-#ifdef Q_OS_WIN
- m_qmllsPath += QLatin1String(".exe");
-#endif
- // allow overriding of the executable, to be able to use a qmlEcho script (as described in
- // qmllanguageservertool.cpp)
- m_qmllsPath = qEnvironmentVariable("QMLLS", m_qmllsPath);
- m_server.setProgram(m_qmllsPath);
- m_protocol.registerPublishDiagnosticsNotificationHandler([](const QByteArray &, auto) {
- // ignoring qmlint notifications
- });
-}
-
-void tst_QmllsCompletions::initTestCase()
-{
- QQmlDataTest::initTestCase();
- if (!QFileInfo::exists(m_qmllsPath)) {
- QString message =
- QStringLiteral("qmlls executable not found (looked for %0)").arg(m_qmllsPath);
- QSKIP(qPrintable(message)); // until we add a feature for this we avoid failing here
- }
- m_server.start();
- InitializeParams clientInfo;
- clientInfo.rootUri = QUrl::fromLocalFile(dataDirectory() + "/default").toString().toUtf8();
- TextDocumentClientCapabilities tDoc;
- PublishDiagnosticsClientCapabilities pDiag;
- tDoc.publishDiagnostics = pDiag;
- pDiag.versionSupport = true;
- clientInfo.capabilities.textDocument = tDoc;
- bool didInit = false;
- m_protocol.requestInitialize(clientInfo, [this, &didInit](const InitializeResult &serverInfo) {
- Q_UNUSED(serverInfo);
- m_protocol.notifyInitialized(InitializedParams());
- didInit = true;
- });
- QTRY_COMPARE_WITH_TIMEOUT(didInit, true, 10000);
-
- for (const QString &filePath :
- QStringList({ u"completions/Yyy.qml"_s, u"completions/fromBuildDir.qml"_s })) {
- QFile file(testFile(filePath));
- QVERIFY(file.open(QIODevice::ReadOnly));
- DidOpenTextDocumentParams oParams;
- TextDocumentItem textDocument;
- QByteArray uri = testFileUrl(filePath).toString().toUtf8();
- textDocument.uri = uri;
- textDocument.text = file.readAll();
- oParams.textDocument = textDocument;
- m_protocol.notifyDidOpenTextDocument(oParams);
- m_uriToClose.append(uri);
- }
-}
-
-void tst_QmllsCompletions::completions_data()
-{
- QTest::addColumn<QByteArray>("uri");
- QTest::addColumn<int>("lineNr");
- QTest::addColumn<int>("character");
- QTest::addColumn<ExpectedCompletions>("expected");
- QTest::addColumn<QStringList>("notExpected");
-
- QByteArray uri = testFileUrl("completions/Yyy.qml").toString().toUtf8();
-
- QTest::newRow("objEmptyLine") << uri << 8 << 0
- << ExpectedCompletions({
- { u"Rectangle"_s, CompletionItemKind::Class },
- { u"property"_s, CompletionItemKind::Keyword },
- { u"width"_s, CompletionItemKind::Property },
- { u"function"_s, CompletionItemKind::Keyword },
- })
- << QStringList({ u"QtQuick"_s, u"vector4d"_s });
-
- QTest::newRow("inBindingLabel") << uri << 5 << 9
- << ExpectedCompletions({
- { u"Rectangle"_s, CompletionItemKind::Class },
- { u"property"_s, CompletionItemKind::Keyword },
- { u"width"_s, CompletionItemKind::Property },
- })
- << QStringList({ u"QtQuick"_s, u"vector4d"_s });
-
- QTest::newRow("afterBinding") << uri << 5 << 10
- << ExpectedCompletions({
- { u"Rectangle"_s, CompletionItemKind::Field },
- { u"width"_s, CompletionItemKind::Field },
- { u"vector4d"_s, CompletionItemKind::Field },
- })
- << QStringList({ u"QtQuick"_s, u"property"_s });
-
- // suppress?
- QTest::newRow("afterId") << uri << 4 << 7
- << ExpectedCompletions({
- { u"import"_s, CompletionItemKind::Keyword },
- })
- << QStringList({ u"QtQuick"_s, u"property"_s, u"Rectangle"_s,
- u"width"_s, u"vector4d"_s });
-
- QTest::newRow("fileStart") << uri << 0 << 0
- << ExpectedCompletions({
- { u"Rectangle"_s, CompletionItemKind::Class },
- { u"import"_s, CompletionItemKind::Keyword },
- })
- << QStringList({ u"QtQuick"_s, u"vector4d"_s, u"width"_s });
-
- QTest::newRow("importImport") << uri << 0 << 3
- << ExpectedCompletions({
- { u"Rectangle"_s, CompletionItemKind::Class },
- { u"import"_s, CompletionItemKind::Keyword },
- })
- << QStringList({ u"QtQuick"_s, u"vector4d"_s, u"width"_s });
-
- QTest::newRow("importModuleStart")
- << uri << 0 << 7
- << ExpectedCompletions({
- { u"QtQuick"_s, CompletionItemKind::Module },
- })
- << QStringList({ u"vector4d"_s, u"width"_s, u"Rectangle"_s, u"import"_s });
-
- QTest::newRow("importVersionStart")
- << uri << 0 << 15
- << ExpectedCompletions({
- { u"2"_s, CompletionItemKind::Constant },
- { u"as"_s, CompletionItemKind::Keyword },
- })
- << QStringList({ u"Rectangle"_s, u"import"_s, u"vector4d"_s, u"width"_s });
-
- // QTest::newRow("importVersionMinor")
- // << uri << 0 << 17
- // << ExpectedCompletions({
- // { u"15"_s, CompletionItemKind::Constant },
- // })
- // << QStringList({ u"as"_s, u"Rectangle"_s, u"import"_s, u"vector4d"_s, u"width"_s });
-
- QTest::newRow("inScript") << uri << 6 << 14
- << ExpectedCompletions({
- { u"Rectangle"_s, CompletionItemKind::Field },
- { u"vector4d"_s, CompletionItemKind::Field },
- { u"lala()"_s, CompletionItemKind::Function },
- { u"longfunction()"_s, CompletionItemKind::Function },
- { u"documentedFunction()"_s,
- CompletionItemKind::Function },
- { u"lala()"_s, CompletionItemKind { 0 } },
- { u"width"_s, CompletionItemKind::Field },
- })
- << QStringList({ u"import"_s });
-
- QTest::newRow("expandBase1") << uri << 9 << 23
- << ExpectedCompletions({
- { u"width"_s, CompletionItemKind::Field },
- { u"foo"_s, CompletionItemKind::Field },
- })
- << QStringList({ u"import"_s, u"Rectangle"_s });
-
- QTest::newRow("expandBase2") << uri << 10 << 29
- << ExpectedCompletions({
- { u"width"_s, CompletionItemKind::Field },
- { u"color"_s, CompletionItemKind::Field },
- })
- << QStringList({ u"foo"_s, u"import"_s, u"Rectangle"_s });
-
- QTest::newRow("asCompletions")
- << uri << 25 << 8
- << ExpectedCompletions({
- { u"Rectangle"_s, CompletionItemKind::Field },
- })
- << QStringList({ u"foo"_s, u"import"_s, u"lala()"_s, u"width"_s });
-}
-
-void tst_QmllsCompletions::checkCompletions(QByteArray uri, int lineNr, int character,
- ExpectedCompletions expected, QStringList notExpected)
-{
- CompletionParams cParams;
- cParams.position.line = lineNr;
- cParams.position.character = character;
- cParams.textDocument.uri = uri;
- std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
- auto clean = [didFinish]() { *didFinish = true; };
-
- m_protocol.requestCompletion(
- cParams,
- [clean, uri, expected, notExpected](auto res) {
- QScopeGuard cleanup(clean);
- const QList<CompletionItem> *cItems = std::get_if<QList<CompletionItem>>(&res);
-
- if (!cItems) {
- return;
- }
-
- QSet<QString> labels;
- QDuplicateTracker<QByteArray> modulesTracker;
- QDuplicateTracker<QByteArray> keywordsTracker;
- QDuplicateTracker<QByteArray> classesTracker;
- QDuplicateTracker<QByteArray> fieldsTracker;
- QDuplicateTracker<QByteArray> propertiesTracker;
-
- for (const CompletionItem &c : *cItems) {
- if (c.kind->toInt() == int(CompletionItemKind::Module)) {
- QVERIFY2(!modulesTracker.hasSeen(c.label), "Duplicate module: " + c.label);
- } else if (c.kind->toInt() == int(CompletionItemKind::Keyword)) {
- QVERIFY2(!keywordsTracker.hasSeen(c.label),
- "Duplicate keyword: " + c.label);
- } else if (c.kind->toInt() == int(CompletionItemKind::Class)) {
- QVERIFY2(!classesTracker.hasSeen(c.label), "Duplicate class: " + c.label);
- } else if (c.kind->toInt() == int(CompletionItemKind::Field)) {
- QVERIFY2(!fieldsTracker.hasSeen(c.label), "Duplicate field: " + c.label);
- } else if (c.kind->toInt() == int(CompletionItemKind::Property)) {
- QVERIFY2(!propertiesTracker.hasSeen(c.label),
- "Duplicate property: " + c.label);
- QVERIFY2(c.insertText == c.label + u": "_s,
- "a property should end with a colon with a space for "
- "'insertText', for better coding experience");
- }
- labels << c.label;
- }
-
- for (const ExpectedCompletion &exp : expected) {
- QVERIFY2(labels.contains(exp.first),
- u"no %1 in %2"_s
- .arg(exp.first,
- QStringList(labels.begin(), labels.end()).join(u", "_s))
- .toUtf8());
- if (labels.contains(exp.first)) {
- for (const CompletionItem &c : *cItems) {
- const auto kind = static_cast<CompletionItemKind>(c.kind->toInt());
-
- bool foundEntry = false;
- bool hasCorrectKind = false;
- for (const ExpectedCompletion &e : expected) {
- if (c.label == e.first) {
- foundEntry = true;
- hasCorrectKind |= kind == e.second;
- }
- }
-
- // Ignore QVERIFY for those completions not in the expected list.
- if (!foundEntry)
- continue;
-
- QVERIFY2(hasCorrectKind,
- qPrintable(
- QString::fromLatin1(
- "Completion item '%1' has wrong kind '%2'")
- .arg(c.label)
- .arg(QMetaEnum::fromType<CompletionItemKind>()
- .valueToKey(int(kind)))));
- }
- }
- }
- for (const QString &nexp : notExpected) {
- QVERIFY2(!labels.contains(nexp),
- u"found unexpected completion %1"_s.arg(nexp).toUtf8());
- }
- },
- [clean](const ResponseError &err) {
- QScopeGuard cleanup(clean);
- ProtocolBase::defaultResponseErrorHandler(err);
- QVERIFY2(false, "error computing the completion");
- });
- QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 30000);
-}
-
-void tst_QmllsCompletions::completions()
-{
- QFETCH(QByteArray, uri);
- QFETCH(int, lineNr);
- QFETCH(int, character);
- QFETCH(ExpectedCompletions, expected);
- QFETCH(QStringList, notExpected);
-
- QTEST_CHECKED(checkCompletions(uri, lineNr, character, expected, notExpected));
-}
-
-void tst_QmllsCompletions::function_documentations_data()
-{
- QTest::addColumn<QByteArray>("uri");
- QTest::addColumn<int>("lineNr");
- QTest::addColumn<int>("character");
- QTest::addColumn<ExpectedDocumentations>("expectedDocs");
-
- QByteArray uri = testFileUrl("completions/Yyy.qml").toString().toUtf8();
-
- QTest::newRow("longfunction")
- << uri << 5 << 14
- << ExpectedDocumentations{
- std::make_tuple(u"lala()"_s, u"returns void"_s, u"lala()"_s),
- std::make_tuple(u"longfunction()"_s, u"returns string"_s,
- uR"(longfunction(a, b, c = "c", d = "d"))"_s),
- std::make_tuple(u"documentedFunction()"_s, u"returns string"_s,
- uR"(// documentedFunction: is documented
-// returns 'Good'
-documentedFunction(arg1, arg2 = "Qt"))"_s),
- };
-}
-
-void tst_QmllsCompletions::function_documentations()
-{
- QFETCH(QByteArray, uri);
- QFETCH(int, lineNr);
- QFETCH(int, character);
- QFETCH(ExpectedDocumentations, expectedDocs);
-
- CompletionParams cParams;
- cParams.position.line = lineNr;
- cParams.position.character = character;
- cParams.textDocument.uri = uri;
- std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
- auto clean = [didFinish]() { *didFinish = true; };
-
- m_protocol.requestCompletion(
- cParams,
- [clean, uri, expectedDocs](auto res) {
- const QList<CompletionItem> *cItems = std::get_if<QList<CompletionItem>>(&res);
-
- if (!cItems) {
- return;
- }
-
- for (const ExpectedDocumentation &exp : expectedDocs) {
- bool hasFoundExpected = false;
- const auto expectedLabel = std::get<0>(exp);
- for (const CompletionItem &c : *cItems) {
- if (c.kind->toInt() != int(CompletionItemKind::Function)) {
- // Only check functions.
- continue;
- }
-
- if (c.label == expectedLabel) {
- hasFoundExpected = true;
- }
- }
-
- QVERIFY2(hasFoundExpected,
- qPrintable(u"expected completion label '%1' wasn't found"_s.arg(
- expectedLabel)));
- }
-
- for (const CompletionItem &c : *cItems) {
- if (c.kind->toInt() != int(CompletionItemKind::Function)) {
- // Only check functions.
- continue;
- }
-
- QVERIFY(c.documentation != std::nullopt);
- // We currently don't support 'MarkupContent', change this when we do.
- QVERIFY(c.documentation->index() == 0);
- const QByteArray cDoc = std::get<0>(*c.documentation);
-
- for (const ExpectedDocumentation &exp : expectedDocs) {
- const auto &[label, details, docs] = exp;
-
- if (c.label != label)
- continue;
-
- QVERIFY2(c.detail == details,
- qPrintable(u"Completion item '%1' has wrong details '%2'"_s
- .arg(label).arg(*c.detail)));
- QVERIFY2(cDoc == docs,
- qPrintable(u"Completion item '%1' has wrong documentation '%2'"_s
- .arg(label).arg(cDoc)));
- }
- }
- clean();
- },
- [clean](const ResponseError &err) {
- ProtocolBase::defaultResponseErrorHandler(err);
- QVERIFY2(false, "error computing the completion");
- clean();
- });
- QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 30000);
-}
-
-void tst_QmllsCompletions::buildDir()
-{
- QString filePath = u"completions/fromBuildDir.qml"_s;
- QByteArray uri = testFileUrl(filePath).toString().toUtf8();
- QTEST_CHECKED(checkCompletions(uri, 3, 0,
- ExpectedCompletions({
- { u"property"_s, CompletionItemKind::Keyword },
- { u"function"_s, CompletionItemKind::Keyword },
- { u"Rectangle"_s, CompletionItemKind::Class },
- }),
- QStringList({ u"BuildDirType"_s, u"QtQuick"_s, u"width"_s, u"vector4d"_s })));
- Notifications::AddBuildDirsParams bDirs;
- UriToBuildDirs ub;
- ub.baseUri = uri;
- ub.buildDirs.append(testFile("buildDir").toUtf8());
- bDirs.buildDirsToSet.append(ub);
- m_protocol.typedRpc()->sendNotification(QByteArray(Notifications::AddBuildDirsMethod), bDirs);
- DidChangeTextDocumentParams didChange;
- didChange.textDocument.uri = uri;
- didChange.textDocument.version = 2;
- TextDocumentContentChangeEvent change;
- QFile file(testFile(filePath));
- QVERIFY(file.open(QIODevice::ReadOnly));
- change.text = file.readAll();
- didChange.contentChanges.append(change);
- m_protocol.notifyDidChangeTextDocument(didChange);
- QTEST_CHECKED(checkCompletions(uri, 3, 0,
- ExpectedCompletions({
- { u"BuildDirType"_s, CompletionItemKind::Class },
- { u"Rectangle"_s, CompletionItemKind::Class },
- { u"property"_s, CompletionItemKind::Keyword },
- { u"width"_s, CompletionItemKind::Property },
- { u"function"_s, CompletionItemKind::Keyword },
- }),
- QStringList({ u"QtQuick"_s, u"vector4d"_s })));
-}
-void tst_QmllsCompletions::cleanupTestCase()
-{
- for (const QByteArray &uri : m_uriToClose) {
- DidCloseTextDocumentParams closeP;
- closeP.textDocument.uri = uri;
- m_protocol.notifyDidCloseTextDocument(closeP);
- }
- m_server.closeWriteChannel();
- QTRY_COMPARE(m_server.state(), QProcess::NotRunning);
- QCOMPARE(m_server.exitStatus(), QProcess::NormalExit);
-}
-
-QTEST_MAIN(tst_QmllsCompletions)
-
-#include <tst_qmllscompletions.moc>
diff --git a/tests/auto/qmlls/lifecycle/CMakeLists.txt b/tests/auto/qmlls/lifecycle/CMakeLists.txt
index ec12884e98..fa97aca132 100644
--- a/tests/auto/qmlls/lifecycle/CMakeLists.txt
+++ b/tests/auto/qmlls/lifecycle/CMakeLists.txt
@@ -5,18 +5,22 @@
## tst_lifecycle Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_lifecycle LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_lifecycle
SOURCES
- ../../../../tools/qmlls/qlanguageserver.h ../../../../tools/qmlls/qlanguageserver.cpp
tst_lifecycle.cpp
qiopipe.h qiopipe.cpp
- INCLUDE_DIRECTORIES
- ../../../../tools/qmlls
DEFINES
QT_DEPRECATED_WARNINGS
LIBRARIES
Qt::CorePrivate
Qt::LanguageServerPrivate
Qt::Test
+ Qt::QmlLSPrivate
TESTDATA ${test_data}
)
diff --git a/tests/auto/qmlls/lifecycle/qiopipe.cpp b/tests/auto/qmlls/lifecycle/qiopipe.cpp
index 416fe8257d..44f34e6580 100644
--- a/tests/auto/qmlls/lifecycle/qiopipe.cpp
+++ b/tests/auto/qmlls/lifecycle/qiopipe.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qiopipe.h"
diff --git a/tests/auto/qmlls/lifecycle/qiopipe.h b/tests/auto/qmlls/lifecycle/qiopipe.h
index e6c6d8b9a4..1722e2e465 100644
--- a/tests/auto/qmlls/lifecycle/qiopipe.h
+++ b/tests/auto/qmlls/lifecycle/qiopipe.h
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QECHODEVICE_H
#define QECHODEVICE_H
diff --git a/tests/auto/qmlls/lifecycle/tst_lifecycle.cpp b/tests/auto/qmlls/lifecycle/tst_lifecycle.cpp
index 6c960f414e..efbdf10c37 100644
--- a/tests/auto/qmlls/lifecycle/tst_lifecycle.cpp
+++ b/tests/auto/qmlls/lifecycle/tst_lifecycle.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qiopipe.h"
@@ -8,7 +8,7 @@
#include <QtJsonRpc/private/qjsonrpcprotocol_p.h>
#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
-#include "qlanguageserver.h"
+#include <QtQmlLS/private/qlanguageserver_p.h>
#include <QtCore/qjsonarray.h>
#include <QtCore/qjsonobject.h>
diff --git a/tests/auto/qmlls/modules/CMakeLists.txt b/tests/auto/qmlls/modules/CMakeLists.txt
new file mode 100644
index 0000000000..565c8fb62a
--- /dev/null
+++ b/tests/auto/qmlls/modules/CMakeLists.txt
@@ -0,0 +1,45 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlls_modules LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data)
+file(GLOB_RECURSE test_data_glob_qmlformat
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/../../qml/qmlformat/
+ data)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qmlls_modules
+ SOURCES
+ tst_qmlls_modules.cpp
+ tst_qmlls_modules.h
+ DEFINES
+ QT_DEPRECATED_WARNINGS
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+ QT_QMLFORMATTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/../../qml/qmlformat/data"
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::LanguageServerPrivate
+ Qt::Test
+ Qt::QuickTestUtilsPrivate
+ Qt::QmlLSPrivate
+ TESTDATA ${test_data}
+)
+
+qt_internal_extend_target(tst_qmlls_modules CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=":/domdata"
+)
+
+if (TARGET qmlls)
+ # standalone test builds do not know the qmlls target
+ # but if TARGET qmlls is known it should be built before this test
+ add_dependencies(tst_qmlls_modules qmlls)
+endif()
diff --git a/tests/auto/qmlls/completions/data/buildDir/BuildDir/BuildDirType.qml b/tests/auto/qmlls/modules/data/buildDir/BuildDir/BuildDirType.qml
index 416ec33253..416ec33253 100644
--- a/tests/auto/qmlls/completions/data/buildDir/BuildDir/BuildDirType.qml
+++ b/tests/auto/qmlls/modules/data/buildDir/BuildDir/BuildDirType.qml
diff --git a/tests/auto/qmlls/completions/data/buildDir/BuildDir/qmldir b/tests/auto/qmlls/modules/data/buildDir/BuildDir/qmldir
index 18d90059da..df2320f1f0 100644
--- a/tests/auto/qmlls/completions/data/buildDir/BuildDir/qmldir
+++ b/tests/auto/qmlls/modules/data/buildDir/BuildDir/qmldir
@@ -1,2 +1,3 @@
module BuildDir
BuildDirType 1.0 BuildDirType.qml
+import QtQuick.Controls.Basic
diff --git a/tests/auto/qmlls/modules/data/completions/SomeBase.qml b/tests/auto/qmlls/modules/data/completions/SomeBase.qml
new file mode 100644
index 0000000000..9c36e13c5b
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/completions/SomeBase.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+Item {
+
+}
diff --git a/tests/auto/qmlls/completions/data/completions/Yyy.qml b/tests/auto/qmlls/modules/data/completions/Yyy.qml
index 23bbd057f7..9ac6f9968d 100644
--- a/tests/auto/qmlls/completions/data/completions/Yyy.qml
+++ b/tests/auto/qmlls/modules/data/completions/Yyy.qml
@@ -26,4 +26,7 @@ Zzz {
QQ.Rectangle {
color:"red"
}
+
+ component IC: Zzz { property SomeBase data }
+ property SomeBase mySomeBase
}
diff --git a/tests/auto/qmlls/completions/data/completions/Zzz.qml b/tests/auto/qmlls/modules/data/completions/Zzz.qml
index 165ea46394..165ea46394 100644
--- a/tests/auto/qmlls/completions/data/completions/Zzz.qml
+++ b/tests/auto/qmlls/modules/data/completions/Zzz.qml
diff --git a/tests/auto/qmlls/modules/data/completions/bindingAfterDot.qml b/tests/auto/qmlls/modules/data/completions/bindingAfterDot.qml
new file mode 100644
index 0000000000..2a8694342b
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/completions/bindingAfterDot.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+QtObject {
+ id: root
+ property int good
+ property var i: Item {
+ property int bad
+ property int myP: root.
+ Item { }
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/completions/defaultBindingAfterDot.qml b/tests/auto/qmlls/modules/data/completions/defaultBindingAfterDot.qml
new file mode 100644
index 0000000000..4cf47b6f31
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/completions/defaultBindingAfterDot.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+QtObject {
+ id: root
+ property int good
+ property var i: Item {
+ property int bad
+ property int myP2: root.
+ bad: 43
+ }
+}
diff --git a/tests/auto/qmlls/completions/data/completions/fromBuildDir.qml b/tests/auto/qmlls/modules/data/completions/fromBuildDir.qml
index 4a572a3950..2c35f5b864 100644
--- a/tests/auto/qmlls/completions/data/completions/fromBuildDir.qml
+++ b/tests/auto/qmlls/modules/data/completions/fromBuildDir.qml
@@ -6,4 +6,5 @@ BuildDirType {
width: 250
height: 10
}
+ Button {}
}
diff --git a/tests/auto/qmlls/modules/data/findDefinition/jsDefinitions.qml b/tests/auto/qmlls/modules/data/findDefinition/jsDefinitions.qml
new file mode 100644
index 0000000000..3243030466
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/findDefinition/jsDefinitions.qml
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.15
+
+Item {
+ id: rootId
+
+ property int i // (1)
+ function f(a /*(2)*/ , b) {return a /* go to definition on a leads to (2) */ > b} // (4)
+
+ Component.onCompleted: {
+ let x = 42 // (3)
+ f(x, i) // goto definition on f goes to 4, on x goes to (3) and on i goes to (1)
+ f(x, rootId.i) // goto definition on f goes to 4, on x goes to (3) and on i goes to (1)
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/findUsages/jsIdentifierUsages.qml b/tests/auto/qmlls/modules/data/findUsages/jsIdentifierUsages.qml
new file mode 100644
index 0000000000..6c099edc9e
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/findUsages/jsIdentifierUsages.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function f() {
+ let sum = 0, sum2 = 0
+ for(let i = 1; i < 42; i = i + 2) {
+ sum = sum + i
+ }
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/formatting/blanklines.formatted.qml b/tests/auto/qmlls/modules/data/formatting/blanklines.formatted.qml
new file mode 100644
index 0000000000..19548e9023
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/formatting/blanklines.formatted.qml
@@ -0,0 +1,4 @@
+// leading and trailing spaces
+import QtQuick
+
+Item {}
diff --git a/tests/auto/qmlls/modules/data/formatting/blanklines.qml b/tests/auto/qmlls/modules/data/formatting/blanklines.qml
new file mode 100644
index 0000000000..ae1a14daed
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/formatting/blanklines.qml
@@ -0,0 +1,15 @@
+
+
+
+
+// leading and trailing spaces
+import QtQuick
+
+
+Item {
+
+
+ }
+
+
+
diff --git a/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted1.qml b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted1.qml
new file mode 100644
index 0000000000..e167f829b2
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted1.qml
@@ -0,0 +1,23 @@
+pragma Strict
+
+import QtQuick
+
+Item {
+ property var test: [{
+ // Testing
+ "foo": "bar"
+ }]
+
+ Item {
+
+ function test() {
+var patron = "🐈test🐈";
+ let kivrik = 1;
+
+
+
+
+ const yumyum = 1;
+ }
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted2.qml b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted2.qml
new file mode 100644
index 0000000000..ebd68dbde2
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted2.qml
@@ -0,0 +1,20 @@
+pragma Strict
+
+import QtQuick
+
+Item {
+ property var test: [{
+// Testing
+ "foo": "bar"
+}]
+
+ Item {
+
+ function test() {
+ var patron = "🐈test🐈";
+ let kivrik = 1;
+
+ const yumyum = 1;
+ }
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted3.qml b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted3.qml
new file mode 100644
index 0000000000..83f91c6af5
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted3.qml
@@ -0,0 +1,23 @@
+pragma Strict
+
+import QtQuick
+
+Item {
+ property var test: [{
+// Testing
+ "foo": "bar"
+}]
+
+ Item {
+
+ function test() {
+var patron = "🐈test🐈";
+ let kivrik = 1;
+
+
+
+
+ const yumyum = 1;
+ }
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted4.qml b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted4.qml
new file mode 100644
index 0000000000..d6d7227588
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted4.qml
@@ -0,0 +1,20 @@
+pragma Strict
+
+import QtQuick
+
+Item {
+ property var test: [{
+ // Testing
+ "foo": "bar"
+ }]
+
+ Item {
+
+ function test() {
+ var patron = "🐈test🐈";
+ let kivrik = 1;
+
+ const yumyum = 1;
+ }
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted5.qml b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted5.qml
new file mode 100644
index 0000000000..8b3ddf8df1
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted5.qml
@@ -0,0 +1,20 @@
+pragma Strict
+
+import QtQuick
+
+Item {
+ property var test: [{
+// Testing
+ "foo": "bar"
+}]
+
+ Item {
+
+ function test() {
+ var patron = "🐈test🐈";
+ let kivrik = 1;
+
+ const yumyum = 1;
+ }
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/formatting/rangeFormatting.qml b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.qml
new file mode 100644
index 0000000000..1431fb10c3
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.qml
@@ -0,0 +1,23 @@
+pragma Strict
+
+import QtQuick
+
+Item {
+ property var test: [{
+// Testing
+ "foo": "bar"
+}]
+
+ Item {
+
+ function test() {
+var patron = "🐈test🐈";
+ let kivrik = 1;
+
+
+
+
+ const yumyum = 1;
+ }
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/highlighting/basic.qml b/tests/auto/qmlls/modules/data/highlighting/basic.qml
new file mode 100644
index 0000000000..264f553b22
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/highlighting/basic.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: rootId
+ function a() {
+
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/highlighting/bigFile.qml b/tests/auto/qmlls/modules/data/highlighting/bigFile.qml
new file mode 100644
index 0000000000..9832e8e98a
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/highlighting/bigFile.qml
@@ -0,0 +1,351 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: rootId
+ function f() {
+ let sum = 0, sum2 = 0;
+ for (let i = 1; i < 42; i = i + 2) {
+ sum = sum + i;
+ {
+ let sum = 42; // another unrelated sum
+ }
+ }
+ }
+
+ readonly property int helloProperty: 0
+ property int p2: 1
+
+ function withProperty() {
+ let sum = 0, sum2 = 0;
+ for (const i = 1; i < 42; i = i + 2) {
+ sum = sum + i;
+ helloProperty = helloProperty + sum - i * p2;
+ {
+ let helloProperty = "evil";
+ }
+ }
+ }
+ Item {
+ function f() {
+ return helloProperty + p2;
+ }
+ property string helloProperty
+ }
+ component IC: Item {
+ property var helloProperty
+ function f() {
+ return helloProperty + p2;
+ }
+ }
+ component NestedComponent: Item {
+ property NestedComponent2 inner: NestedComponent2 {}
+ property int p2
+ }
+ component NestedComponent2: Item {
+ property NestedComponent3 inner
+ property int p2
+ inner: NestedComponent3 {}
+ }
+ component NestedComponent3: Item {
+ property NestedComponent4 inner
+ property int p2
+ inner: NestedComponent4 {}
+ }
+ component NestedComponent4: Item {
+ property int helloProperty
+ property int p2
+ }
+ NestedComponent {
+ id: myNested
+ }
+ function nestedUsages() {
+ let x = myNested.inner.inner.inner.helloProperty + helloProperty;
+ let a = myNested.p2 + p2;
+ let b = myNested.inner.p2 + p2;
+ let c = myNested.inner.inner.p2 + p2;
+ let d = myNested.inner.inner.inner.p2 + p2;
+ }
+
+ function recursive(n: int): int {
+ if (n > 3)
+ return 1 + recursive(recursive(x - 1) + recursive(x - 2) - recursive(x - 3));
+ else
+ return recursive(0);
+ }
+
+ property int helloRecursive: recursive(42)
+ Rectangle {
+ function f() {
+ return rootId.recursive(123);
+ }
+ }
+
+ signal helloSignal
+
+ function callSignals() {
+ helloSignal();
+ if (false) {
+ helloSignal();
+ } else {
+ // helloSignal() // not a usage btw
+ if (true)
+ helloSignal();
+ }
+ }
+ function callSignals2() {
+ helloSignal();
+ if (false) {
+ widthChanged();
+ } else {
+ // helloSignal() // not a usage btw
+ if (true)
+ widthChanged();
+ rootId.widthChanged();
+ }
+ }
+ Item {
+ function callSignalsInChild() {
+ widthChanged();
+ rootId.widthChanged();
+ }
+ }
+
+ function myHelloHandler() {
+ let x = 32;
+ }
+ onHelloSignal: myHelloHandler
+
+ property int helloPropertyBinding
+ helloPropertyBinding: 123
+
+ property int checkHandlers
+ onCheckHandlersChanged: myHelloHandler
+ onChildrenChanged: myHelloHandler
+ function callChanged() {
+ checkHandlersChanged();
+ childrenChanged();
+ }
+ property int _: 48
+ property int ______42: 48
+ property int _123a: 48
+ on_Changed: myHelloHandler
+ on______42Changed: myHelloHandler
+ on_123AChanged: myHelloHandler
+ function weirdPropertynames() {
+ _Changed();
+ ______42Changed();
+ _123aChanged();
+ }
+
+ TapHandler {
+ onTapped: myHelloHandler
+ function f() {
+ tapped();
+ }
+ }
+
+ function anotherF() {
+ helloPropertyChanged();
+ }
+ onHelloPropertyChanged: myHelloHandler
+ // Type {}
+ function foo(mouse) {
+ }
+
+ MouseArea {
+ id: area1
+ onClicked: foo
+ property int insideMouseArea1
+ }
+
+ MouseArea {
+ id: area2
+ Connections {
+ function onClicked(mouse) {
+ area1.clicked();
+ area3.clicked();
+ }
+ }
+ property int insideMouseArea2
+
+ MouseArea {
+ id: area3
+ }
+ }
+
+ property Connections c: Connections {
+ target: area3
+ onClicked: function (mouse) {}
+ }
+ function useMouseAreas() {
+ area1.clicked();
+ area2.clicked();
+ area3.clicked();
+ }
+
+ function checkParameters(a: int, b: double, {
+ x,
+ y = {},
+ z = [x, y]
+ }) {
+ return a + b + c + x + y + z;
+ }
+
+ function deconstructingUsages(xxx) {
+ let {
+ a,
+ b
+ } = xxx;
+ let c = a + b;
+ }
+
+ function k() {
+ }
+
+ function mafik() {
+ var patron = 34;
+ const upperLimit = 42;
+ do {
+ ++patron;
+ if (patron < 2)
+ continue;
+ else
+ ++patron;
+ } while (patron < upperLimit)
+ switch (patron) {
+ case 1:
+ return 23;
+ default:
+ break;
+ }
+ try {
+ {}
+ } catch (error) {
+ {}
+ } finally {}
+ for (const a in [1, 2, 3]) {
+ throw 2;
+ }
+ }
+
+ enum Test {
+ LOG
+ }
+
+ readonly property int t: 34
+ signal tt
+ required property int k
+
+ signal kkk(string a)
+ signal yyy(string a)
+
+ function ttt() {
+ }
+
+ function createComplexExpression(...objects) {
+ // Create an object that holds some data
+ let data = {
+ a: 5,
+ b: 10,
+ c: 3
+ };
+
+ // Create a complex expression using the data object
+ let expression = ((data.a + data.b * data.c) / (data.a - data.b)) ** data.c;
+
+ return expression;
+ }
+
+ function set1() {
+ const array = [1, 2, 3, 4];
+ const [a, b] = [1, 2];
+ const [aa, , bb] = array;
+ const [aaa = 23, bbb] = array;
+ const [a1, b1, ...rest1] = array;
+ const [a2, , b2, ...rest2] = array;
+ const [a3, b3, ...{
+ pop,
+ push
+ }] = array;
+ const [a4, b4, ...[c, d]] = array;
+
+ const obj = {
+ _a: 1,
+ _b: 2
+ };
+ const {
+ a5,
+ b5
+ } = obj;
+ const {
+ a6: a_,
+ b6: b1_
+ } = obj;
+ const {
+ a7: a11 = 4,
+ b11 = 34,
+ c1: b111,
+ d1
+ } = obj;
+ let key = a;
+ const {
+ [key]: a___
+ } = obj;
+ }
+
+ function set2(s: int): int {
+ // declare first
+ let a, b, a1, b1, c, d, rest, pop, push;
+ const array = [1, 2, 3, 4];
+ [a, b] = array;
+ [a, , b] = array;
+ [a = aDefault, b] = array;
+ [a, b, ...rest] = array;
+ [a, , b, ...rest] = array;
+ [a, b, ...{
+ pop,
+ push
+ }] = array;
+ [a, b, ...[c, d]] = array;
+
+ const obj = {
+ _a: 1,
+ _b: 2
+ };
+ ({
+ a,
+ b
+ } = obj); // brackets are required
+ ({
+ a: a1,
+ b: b1
+ } = obj);
+
+ const complicatedObject = {
+ a: 1,
+ b: {
+ c: 2,
+ d: {
+ e: 3,
+ f: [4, 5, 6]
+ }
+ },
+ g: [7, 8, 9]
+ };
+
+ const {
+ patron,
+ b: {
+ mafik,
+ d: {
+ e,
+ f: [, secondF, ...restF]
+ }
+ },
+ g: [firstG, ...restG]
+ } = complicatedObject;
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/hover/test.qml b/tests/auto/qmlls/modules/data/hover/test.qml
new file mode 100644
index 0000000000..dd1d5abcb0
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/hover/test.qml
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+QtObject {
+ readonly property int t: ""
+}
diff --git a/tests/auto/qmlls/modules/data/linting/SimpleItem.qml b/tests/auto/qmlls/modules/data/linting/SimpleItem.qml
new file mode 100644
index 0000000000..b7b7fc5021
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/linting/SimpleItem.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ id: helloItem
+
+}
diff --git a/tests/auto/qmlls/modules/data/quickfixes/INeedAQuickFix.qml b/tests/auto/qmlls/modules/data/quickfixes/INeedAQuickFix.qml
new file mode 100644
index 0000000000..ccd2a55383
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/quickfixes/INeedAQuickFix.qml
@@ -0,0 +1,19 @@
+import QtQuick
+
+Item {
+ id: hello
+
+ signal s(xxx: string)
+ property int i: 42
+
+ // fix me! add '(xxx) =>' in front of console.log()
+ onS: console.log(xxx)
+
+ Item {
+ // fix me! prepend 'hello.' to 'i'!
+ function f() {
+ return i
+ }
+ }
+
+}
diff --git a/tests/auto/qmlls/modules/data/renameUsages/RenameMe.qml b/tests/auto/qmlls/modules/data/renameUsages/RenameMe.qml
new file mode 100644
index 0000000000..7680c63f95
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/renameUsages/RenameMe.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property int helloProperty
+} \ No newline at end of file
diff --git a/tests/auto/qmlls/modules/data/renameUsages/RenameMe2.ui.qml b/tests/auto/qmlls/modules/data/renameUsages/RenameMe2.ui.qml
new file mode 100644
index 0000000000..b9197def63
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/renameUsages/RenameMe2.ui.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property int helloProperty
+}
diff --git a/tests/auto/qmlls/modules/data/renameUsages/main.qml b/tests/auto/qmlls/modules/data/renameUsages/main.qml
new file mode 100644
index 0000000000..b59508f92f
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/renameUsages/main.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ RenameMe {}
+ RenameMe2 {}
+}
diff --git a/tests/auto/qmlls/modules/data/sourceDir/A.qml b/tests/auto/qmlls/modules/data/sourceDir/A.qml
new file mode 100644
index 0000000000..5560aee727
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/sourceDir/A.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+
+}
diff --git a/tests/auto/qmlls/modules/data/sourceDir/Main.qml b/tests/auto/qmlls/modules/data/sourceDir/Main.qml
new file mode 100644
index 0000000000..e9fc9d4298
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/sourceDir/Main.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ B {}
+ Button {}
+}
diff --git a/tests/auto/qmlls/modules/data/sourceDir/qmldir b/tests/auto/qmlls/modules/data/sourceDir/qmldir
new file mode 100644
index 0000000000..2dd55288e3
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/sourceDir/qmldir
@@ -0,0 +1,3 @@
+module SourceDir
+B 1.0 A.qml
+import QtQuick.Controls.Basic
diff --git a/tests/auto/qmlls/modules/tst_qmlls_modules.cpp b/tests/auto/qmlls/modules/tst_qmlls_modules.cpp
new file mode 100644
index 0000000000..fa050b0727
--- /dev/null
+++ b/tests/auto/qmlls/modules/tst_qmlls_modules.cpp
@@ -0,0 +1,1765 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_qmlls_modules.h"
+#include "QtQmlLS/private/qqmllsutils_p.h"
+#include "QtQmlLS/private/qqmlsemantictokens_p.h"
+#include <algorithm>
+#include <memory>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <variant>
+
+// Check if QTest already has a QTEST_CHECKED macro
+#ifndef QTEST_CHECKED
+#define QTEST_CHECKED(...) \
+do { \
+ __VA_ARGS__; \
+ if (QTest::currentTestFailed()) \
+ return; \
+} while (false)
+#endif
+
+QT_USE_NAMESPACE
+using namespace Qt::StringLiterals;
+using namespace QLspSpecification;
+
+static constexpr bool enable_debug_output = false;
+
+tst_qmlls_modules::tst_qmlls_modules() : QQmlDataTest(QT_QMLTEST_DATADIR)
+{
+ m_qmllsPath =
+ QLibraryInfo::path(QLibraryInfo::BinariesPath) + QLatin1String("/qmlls");
+#ifdef Q_OS_WIN
+ m_qmllsPath += QLatin1String(".exe");
+#endif
+ // allow overriding of the executable, to be able to use a qmlEcho script (as described in
+ // qmllanguageservertool.cpp)
+ m_qmllsPath = qEnvironmentVariable("QMLLS", m_qmllsPath);
+ // qputenv("QT_LOGGING_RULES",
+ // "qt.languageserver.codemodel.debug=true;qt.languageserver.codemodel.warning=true"); // helps
+ qputenv("QT_LOGGING_RULES", "*.debug=true;*.warning=true");
+ // when using EditingRecorder
+ m_server.setProgram(m_qmllsPath);
+ // m_server.setArguments(QStringList() << u"-v"_s << u"-w"_s << u"7"_s);
+}
+
+void tst_qmlls_modules::init()
+{
+ QQmlDataTest::init();
+
+ m_protocol = std::make_unique<QLanguageServerProtocol>(
+ [this](const QByteArray &data) { m_server.write(data); });
+
+ connect(&m_server, &QProcess::readyReadStandardOutput, this, [this]() {
+ QByteArray data = m_server.readAllStandardOutput();
+ m_protocol->receiveData(data);
+ });
+
+ if constexpr (enable_debug_output) {
+ connect(&m_server, &QProcess::readyReadStandardError, this, [this]() {
+ QProcess::ProcessChannel tmp = m_server.readChannel();
+ m_server.setReadChannel(QProcess::StandardError);
+ while (m_server.canReadLine())
+ qDebug() << m_server.readLine();
+ m_server.setReadChannel(tmp);
+ });
+ }
+
+ m_server.start();
+
+ InitializeParams clientInfo;
+ clientInfo.rootUri = QUrl::fromLocalFile(dataDirectory() + "/default").toString().toUtf8();
+
+ TextDocumentClientCapabilities tDoc;
+ tDoc.typeDefinition = TypeDefinitionClientCapabilities{ false, false };
+
+ PublishDiagnosticsClientCapabilities pDiag;
+ tDoc.publishDiagnostics = pDiag;
+ pDiag.versionSupport = true;
+ clientInfo.capabilities.textDocument = tDoc;
+ bool didInit = false;
+ m_protocol->requestInitialize(clientInfo, [this, &didInit](const InitializeResult &serverInfo) {
+ Q_UNUSED(serverInfo);
+ m_protocol->notifyInitialized(InitializedParams());
+ didInit = true;
+ });
+ QTRY_COMPARE_WITH_TIMEOUT(didInit, true, 10000);
+}
+
+void tst_qmlls_modules::cleanup()
+{
+ for (const QByteArray &uri : m_uriToClose) {
+ DidCloseTextDocumentParams closeP;
+ closeP.textDocument.uri = uri;
+ m_protocol->notifyDidCloseTextDocument(closeP);
+ }
+ m_uriToClose.clear();
+
+ // note: properly exit the language server
+ m_protocol->requestShutdown(nullptr, []() {});
+ m_protocol->notifyExit(nullptr);
+
+ m_server.waitForFinished();
+ QTRY_COMPARE(m_server.state(), QProcess::NotRunning);
+ QCOMPARE(m_server.exitStatus(), QProcess::NormalExit);
+}
+
+/*!
+\internal
+Opens a file from a relative filePath, and returns the loaded uri.
+Returns an empty option when the file could not be found.
+*/
+std::optional<QByteArray> tst_qmlls_modules::openFile(const QString &filePath)
+{
+ return openFileFromAbsolutePath(testFile(filePath));
+}
+
+/*!
+\internal
+Opens a file from an absolute filePath, and returns the loaded uri.
+Returns an empty option when the file could not be found.
+*/
+std::optional<QByteArray> tst_qmlls_modules::openFileFromAbsolutePath(const QString &filePath)
+{
+ QFile file(filePath);
+ if (!file.open(QIODevice::ReadOnly))
+ return {};
+ DidOpenTextDocumentParams oParams;
+ TextDocumentItem textDocument;
+ QByteArray uri = QUrl::fromLocalFile(filePath).toEncoded();
+ textDocument.uri = uri;
+ textDocument.text = file.readAll();
+ oParams.textDocument = textDocument;
+ m_protocol->notifyDidOpenTextDocument(oParams);
+ m_uriToClose.append(uri);
+ return uri;
+}
+
+/*!
+\internal
+Ignore qmllint warnings, when not needed for the test.
+*/
+void tst_qmlls_modules::ignoreDiagnostics()
+{
+ m_protocol->registerPublishDiagnosticsNotificationHandler(
+ [](const QByteArray &, const PublishDiagnosticsParams &) {});
+}
+
+void tst_qmlls_modules::initTestCase()
+{
+ QQmlDataTest::initTestCase();
+ if (!QFileInfo::exists(m_qmllsPath)) {
+ QString message =
+ QStringLiteral("qmlls executable not found (looked for %0)").arg(m_qmllsPath);
+ QSKIP(qPrintable(message)); // until we add a feature for this we avoid failing here
+ }
+}
+
+void tst_qmlls_modules::checkCompletions(const QByteArray &uri, int lineNr, int character,
+ ExpectedCompletions expected, QStringList notExpected)
+{
+ CompletionParams cParams;
+ cParams.position.line = lineNr;
+ cParams.position.character = character;
+ cParams.textDocument.uri = uri;
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ auto clean = [didFinish]() { *didFinish = true; };
+
+ m_protocol->requestCompletion(
+ cParams,
+ [clean, uri, expected, notExpected](auto res) {
+ QScopeGuard cleanup(clean);
+ const QList<CompletionItem> *cItems = std::get_if<QList<CompletionItem>>(&res);
+
+ if (!cItems) {
+ return;
+ }
+
+ QSet<QString> labels;
+ QDuplicateTracker<QByteArray> modulesTracker;
+ QDuplicateTracker<QByteArray> keywordsTracker;
+ QDuplicateTracker<QByteArray> classesTracker;
+ QDuplicateTracker<QByteArray> fieldsTracker;
+ QDuplicateTracker<QByteArray> propertiesTracker;
+
+ for (const CompletionItem &c : *cItems) {
+ if (c.kind->toInt() == int(CompletionItemKind::Module)) {
+ QVERIFY2(!modulesTracker.hasSeen(c.label), "Duplicate module: " + c.label);
+ } else if (c.kind->toInt() == int(CompletionItemKind::Keyword)) {
+ QVERIFY2(!keywordsTracker.hasSeen(c.label),
+ "Duplicate keyword: " + c.label);
+ } else if (c.kind->toInt() == int(CompletionItemKind::Class)) {
+ QVERIFY2(!classesTracker.hasSeen(c.label), "Duplicate class: " + c.label);
+ } else if (c.kind->toInt() == int(CompletionItemKind::Field)) {
+ QVERIFY2(!fieldsTracker.hasSeen(c.label), "Duplicate field: " + c.label);
+ } else if (c.kind->toInt() == int(CompletionItemKind::Property)) {
+ QVERIFY2(!propertiesTracker.hasSeen(c.label),
+ "Duplicate property: " + c.label);
+ }
+ labels << c.label;
+ }
+
+ for (const ExpectedCompletion &exp : expected) {
+ QVERIFY2(labels.contains(exp.first),
+ u"no %1 in %2"_s
+ .arg(exp.first,
+ QStringList(labels.begin(), labels.end()).join(u", "_s))
+ .toUtf8());
+ if (labels.contains(exp.first)) {
+ for (const CompletionItem &c : *cItems) {
+ const auto kind = static_cast<CompletionItemKind>(c.kind->toInt());
+
+ bool foundEntry = false;
+ bool hasCorrectKind = false;
+ for (const ExpectedCompletion &e : expected) {
+ if (c.label == e.first) {
+ foundEntry = true;
+ hasCorrectKind |= kind == e.second;
+ }
+ }
+
+ // Ignore QVERIFY for those completions not in the expected list.
+ if (!foundEntry)
+ continue;
+
+ QVERIFY2(hasCorrectKind,
+ qPrintable(
+ QString::fromLatin1(
+ "Completion item '%1' has wrong kind '%2'")
+ .arg(c.label)
+ .arg(QMetaEnum::fromType<CompletionItemKind>()
+ .valueToKey(int(kind)))));
+ }
+ }
+ }
+ for (const QString &nexp : notExpected) {
+ QVERIFY2(!labels.contains(nexp),
+ u"found unexpected completion %1"_s.arg(nexp).toUtf8());
+ }
+ },
+ [clean](const ResponseError &err) {
+ QScopeGuard cleanup(clean);
+ ProtocolBase::defaultResponseErrorHandler(err);
+ QVERIFY2(false, "error computing the completion");
+ });
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 30000);
+}
+
+void tst_qmlls_modules::function_documentations_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("lineNr");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<ExpectedDocumentations>("expectedDocs");
+
+ const QString filePath = u"completions/Yyy.qml"_s;
+
+ QTest::newRow("longfunction")
+ << filePath << 5 << 14
+ << ExpectedDocumentations{
+ std::make_tuple(u"lala"_s, u"returns void"_s, u"lala()"_s),
+ std::make_tuple(u"longfunction"_s, u"returns string"_s,
+ uR"(longfunction(a, b, c = "c", d = "d"))"_s),
+ std::make_tuple(u"documentedFunction"_s, u"returns string"_s,
+ uR"(// documentedFunction: is documented
+// returns 'Good'
+documentedFunction(arg1, arg2 = "Qt"))"_s),
+ };
+}
+
+void tst_qmlls_modules::function_documentations()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, lineNr);
+ QFETCH(int, character);
+ QFETCH(ExpectedDocumentations, expectedDocs);
+
+ ignoreDiagnostics();
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+
+ CompletionParams cParams;
+ cParams.position.line = lineNr;
+ cParams.position.character = character;
+ cParams.textDocument.uri = *uri;
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ auto clean = [didFinish]() { *didFinish = true; };
+
+ m_protocol->requestCompletion(
+ cParams,
+ [clean, uri, expectedDocs](auto res) {
+ const QList<CompletionItem> *cItems = std::get_if<QList<CompletionItem>>(&res);
+
+ if (!cItems) {
+ return;
+ }
+
+ for (const ExpectedDocumentation &exp : expectedDocs) {
+ bool hasFoundExpected = false;
+ const auto expectedLabel = std::get<0>(exp);
+ for (const CompletionItem &c : *cItems) {
+ if (c.kind->toInt() != int(CompletionItemKind::Method)) {
+ // Only check functions.
+ continue;
+ }
+
+ if (c.label == expectedLabel) {
+ hasFoundExpected = true;
+ }
+ }
+
+ QVERIFY2(hasFoundExpected,
+ qPrintable(u"expected completion label '%1' wasn't found"_s.arg(
+ expectedLabel)));
+ }
+
+ for (const CompletionItem &c : *cItems) {
+ if (c.kind->toInt() != int(CompletionItemKind::Function)) {
+ // Only check functions.
+ continue;
+ }
+
+ QVERIFY(c.documentation != std::nullopt);
+ // We currently don't support 'MarkupContent', change this when we do.
+ QVERIFY(c.documentation->index() == 0);
+ const QByteArray cDoc = std::get<0>(*c.documentation);
+
+ for (const ExpectedDocumentation &exp : expectedDocs) {
+ const auto &[label, details, docs] = exp;
+
+ if (c.label != label)
+ continue;
+
+ QVERIFY2(c.detail == details,
+ qPrintable(u"Completion item '%1' has wrong details '%2'"_s
+ .arg(label).arg(*c.detail)));
+ QVERIFY2(cDoc == docs,
+ qPrintable(u"Completion item '%1' has wrong documentation '%2'"_s
+ .arg(label).arg(cDoc)));
+ }
+ }
+ clean();
+ },
+ [clean](const ResponseError &err) {
+ ProtocolBase::defaultResponseErrorHandler(err);
+ QVERIFY2(false, "error computing the completion");
+ clean();
+ });
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 3000);
+}
+
+void tst_qmlls_modules::buildDir()
+{
+ ignoreDiagnostics();
+ const QString filePath = u"completions/fromBuildDir.qml"_s;
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ QTEST_CHECKED(checkCompletions(
+ *uri, 3, 0,
+ ExpectedCompletions({
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ }),
+ QStringList({ u"BuildDirType"_s, u"QtQuick"_s, u"width"_s, u"vector4d"_s })));
+ Notifications::AddBuildDirsParams bDirs;
+ UriToBuildDirs ub;
+ ub.baseUri = *uri;
+ ub.buildDirs.append(testFile("buildDir").toUtf8());
+ bDirs.buildDirsToSet.append(ub);
+ m_protocol->typedRpc()->sendNotification(QByteArray(Notifications::AddBuildDirsMethod), bDirs);
+
+ DidChangeTextDocumentParams didChange;
+ didChange.textDocument.uri = *uri;
+ didChange.textDocument.version = 2;
+
+ // change the file content to force qqml— to recreate a new DomItem
+ // if it reuses the old DomItem then it will not know about the added build directory
+ TextDocumentContentChangeEvent change;
+ change.range = Range{ Position{ 4, 0 }, Position{ 4, 0 } };
+ change.text = "\n";
+
+ didChange.contentChanges.append(change);
+ m_protocol->notifyDidChangeTextDocument(didChange);
+
+ QTEST_CHECKED(checkCompletions(*uri, 3, 0,
+ ExpectedCompletions({
+ { u"BuildDirType"_s, CompletionItemKind::Constructor },
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ { u"width"_s, CompletionItemKind::Property },
+ }),
+ QStringList({ u"QtQuick"_s, u"vector4d"_s })));
+}
+
+void tst_qmlls_modules::automaticSemicolonInsertionForCompletions_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("row");
+ QTest::addColumn<int>("column");
+
+ QTest::addRow("bindingAfterDot") << u"completions/bindingAfterDot.qml"_s << 11 << 32;
+ QTest::addRow("defaultBindingAfterDot")
+ << u"completions/defaultBindingAfterDot.qml"_s << 11 << 32;
+}
+
+void tst_qmlls_modules::automaticSemicolonInsertionForCompletions()
+{
+ ignoreDiagnostics();
+ QFETCH(QString, filePath);
+ QFETCH(int, row);
+ QFETCH(int, column);
+ row--;
+ column--;
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+
+ QTEST_CHECKED(checkCompletions(
+ *uri, row, column,
+ ExpectedCompletions({
+ { u"good"_s, CompletionItemKind::Property },
+ }),
+ QStringList({ u"bad"_s, u"BuildDirType"_s, u"QtQuick"_s, u"width"_s, u"vector4d"_s })));
+}
+
+void tst_qmlls_modules::checkQuickSnippets()
+{
+ ignoreDiagnostics();
+ const auto uri = openFile(u"completions/Yyy.qml"_s);
+ QVERIFY(uri);
+
+ // if at least one snippet is there, then the pluginloading works. To test the plugin itself,
+ // add tests in tst_qmlls_utils instead.
+ QTEST_CHECKED(checkCompletions(
+ *uri, 4, 3,
+ ExpectedCompletions({
+ { u"BorderImage snippet"_s, CompletionItemKind::Snippet },
+ }),
+ QStringList({})));
+}
+
+void tst_qmlls_modules::goToTypeDefinition_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<QString>("expectedFilePath");
+ QTest::addColumn<int>("expectedStartLine");
+ QTest::addColumn<int>("expectedStartCharacter");
+ QTest::addColumn<int>("expectedEndLine");
+ QTest::addColumn<int>("expectedEndCharacter");
+
+ const QString yyyPath = u"completions/Yyy.qml"_s;
+ const QString zzzPath = u"completions/Zzz.qml"_s;
+ const QString someBasePath = u"completions/SomeBase.qml"_s;
+
+ QTest::newRow("BaseOfYyy") << yyyPath << 3 << 1 << zzzPath << 2 << 0 << 9 << 1;
+ QTest::newRow("BaseOfIC") << yyyPath << 29 << 19 << zzzPath << 2 << 0 << 9 << 1;
+
+ QTest::newRow("PropertyType") << yyyPath << 30 << 14 << someBasePath << 2 << 0 << 4 << 1;
+
+ QTest::newRow("TypeInIC") << yyyPath << 29 << 36 << someBasePath << 2 << 0 << 4 << 1;
+ QTest::newRow("ICTypeDefinition") << yyyPath << 29 << 15 << yyyPath << 29 << 14 << 29 << 16;
+}
+
+void tst_qmlls_modules::goToTypeDefinition()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QString, expectedFilePath);
+ QFETCH(int, expectedStartLine);
+ QFETCH(int, expectedStartCharacter);
+ QFETCH(int, expectedEndLine);
+ QFETCH(int, expectedEndCharacter);
+
+ ignoreDiagnostics();
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ QVERIFY(uri->startsWith("file://"_ba));
+
+ // note: do not call openFile(expectedFilePath), the definition should be found even if
+ // the qmlls user did not open the qml file with the definition yet.
+ const auto expectedUri = testFileUrl(expectedFilePath).toEncoded();
+
+ // TODO
+ TypeDefinitionParams params;
+ params.position.line = line;
+ params.position.character = character;
+ params.textDocument.uri = *uri;
+
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ auto clean = [didFinish]() { *didFinish = true; };
+
+ m_protocol->requestTypeDefinition(
+ params,
+ [&](auto res) {
+ QScopeGuard cleanup(clean);
+ auto *result = std::get_if<QList<Location>>(&res);
+
+ QVERIFY(result);
+
+ QCOMPARE(result->size(), 1);
+
+ Location l = result->front();
+ QCOMPARE(l.uri, expectedUri);
+ QCOMPARE(l.range.start.line, expectedStartLine);
+ QCOMPARE(l.range.start.character, expectedStartCharacter);
+ QCOMPARE(l.range.end.line, expectedEndLine);
+ QCOMPARE(l.range.end.character, expectedEndCharacter);
+ },
+ [clean](const ResponseError &err) {
+ QScopeGuard cleanup(clean);
+ ProtocolBase::defaultResponseErrorHandler(err);
+ QVERIFY2(false, "error computing the completion");
+ });
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 30000);
+}
+
+void tst_qmlls_modules::goToDefinition_data()
+{
+ QTest::addColumn<QString>("filePath");
+ // keep in mind that line and character are starting at 1!
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+
+ QTest::addColumn<QByteArray>("expectedUri");
+ // set to -1 when unchanged from above line and character. 0-based.
+ QTest::addColumn<int>("expectedStartLine");
+ QTest::addColumn<int>("expectedStartCharacter");
+ QTest::addColumn<int>("expectedEndLine");
+ QTest::addColumn<size_t>("expectedEndCharacter");
+
+ const QByteArray JSDefinitionsQml =
+ testFileUrl(u"findDefinition/jsDefinitions.qml"_s).toEncoded();
+ const QString JSDefinitionsQmlPath = u"findDefinition/jsDefinitions.qml"_s;
+ const QByteArray noResultExpected;
+
+ QTest::addRow("JSIdentifierX") << JSDefinitionsQmlPath << 14 << 11 << JSDefinitionsQml << 13
+ << 13 << 13 << 13 + strlen("x");
+ QTest::addRow("propertyI") << JSDefinitionsQmlPath << 14 << 14 << JSDefinitionsQml << 9 << 18
+ << 9 << 18 + strlen("i");
+ QTest::addRow("qualifiedPropertyI") << JSDefinitionsQmlPath << 15 << 21 << JSDefinitionsQml << 9
+ << 18 << 9 << 18 + strlen("i");
+ QTest::addRow("id") << JSDefinitionsQmlPath << 15 << 17 << JSDefinitionsQml << 7 << 9 << 7
+ << 9 + strlen("rootId");
+
+ QTest::addRow("comment") << JSDefinitionsQmlPath << 10 << 21 << noResultExpected << -1 << -1
+ << -1 << size_t{};
+}
+
+void tst_qmlls_modules::goToDefinition()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QByteArray, expectedUri);
+ QFETCH(int, expectedStartLine);
+ QFETCH(int, expectedStartCharacter);
+ QFETCH(int, expectedEndLine);
+ QFETCH(size_t, expectedEndCharacter);
+
+ ignoreDiagnostics();
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ QVERIFY(uri->startsWith("file://"_ba));
+
+ DefinitionParams params;
+ params.position.line = line - 1;
+ params.position.character = character - 1;
+ params.textDocument.uri = *uri;
+
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ auto clean = [didFinish]() { *didFinish = true; };
+
+ m_protocol->requestDefinition(
+ params,
+ [&](auto res) {
+ QScopeGuard cleanup(clean);
+ auto *result = std::get_if<QList<Location>>(&res);
+ const QByteArray noResultExpected;
+
+ QVERIFY(result);
+ if (expectedUri == noResultExpected) {
+ QCOMPARE(result->size(), 0);
+ } else {
+ QCOMPARE(result->size(), 1);
+
+ Location l = result->front();
+ QCOMPARE(l.uri, expectedUri);
+ QCOMPARE(l.range.start.line, expectedStartLine - 1);
+ QCOMPARE(l.range.start.character, expectedStartCharacter - 1);
+ QCOMPARE(l.range.end.line, expectedEndLine - 1);
+ QCOMPARE(l.range.end.character, (int)(expectedEndCharacter - 1));
+ }
+ },
+ [clean](const ResponseError &err) {
+ QScopeGuard cleanup(clean);
+ ProtocolBase::defaultResponseErrorHandler(err);
+ QVERIFY2(false, "error computing the completion");
+ });
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 30000);
+}
+
+// startLine and startCharacter start at 1, not 0
+static QLspSpecification::Range rangeFrom(const QString &code, quint32 startLine,
+ quint32 startCharacter, quint32 length)
+{
+ QLspSpecification::Range range;
+
+ // the LSP works with lines and characters starting at 0
+ range.start.line = startLine - 1;
+ range.start.character = startCharacter - 1;
+
+ quint32 startOffset = QQmlLSUtils::textOffsetFrom(code, startLine - 1, startCharacter - 1);
+ auto end = QQmlLSUtils::textRowAndColumnFrom(code, startOffset + length);
+ range.end.line = end.line;
+ range.end.character = end.character;
+
+ return range;
+}
+
+// startLine and startCharacter start at 1, not 0
+static QLspSpecification::Location locationFrom(const QByteArray fileName, const QString &code,
+ quint32 startLine, quint32 startCharacter,
+ quint32 length)
+{
+ QLspSpecification::Location location;
+ location.uri = QQmlLSUtils::qmlUrlToLspUri(fileName);
+ location.range = rangeFrom(code, startLine, startCharacter, length);
+ return location;
+}
+
+void tst_qmlls_modules::findUsages_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<QList<QLspSpecification::Location>>("expectedUsages");
+
+ const QByteArray jsIdentifierUsagesUri =
+ testFileUrl("findUsages/jsIdentifierUsages.qml").toEncoded();
+ const QString jsIdentifierUsagesPath = u"findUsages/jsIdentifierUsages.qml"_s;
+
+ QString jsIdentifierUsagesContent;
+ {
+ QFile file(testFile("findUsages/jsIdentifierUsages.qml").toUtf8());
+ QVERIFY(file.open(QIODeviceBase::ReadOnly));
+ jsIdentifierUsagesContent = QString::fromUtf8(file.readAll());
+ }
+
+ // line and character start at 1!
+ const QList<QLspSpecification::Location> sumUsages = {
+ locationFrom(jsIdentifierUsagesUri, jsIdentifierUsagesContent, 8, 13, strlen("sum")),
+ locationFrom(jsIdentifierUsagesUri, jsIdentifierUsagesContent, 10, 13, strlen("sum")),
+ locationFrom(jsIdentifierUsagesUri, jsIdentifierUsagesContent, 10, 19, strlen("sum")),
+ };
+ QVERIFY(sumUsages.front().uri.startsWith("file://"_ba));
+
+ // line and character start at 1!
+ QTest::addRow("sumUsagesFromUsage") << jsIdentifierUsagesPath << 10 << 14 << sumUsages;
+ QTest::addRow("sumUsagesFromUsage2") << jsIdentifierUsagesPath << 10 << 20 << sumUsages;
+ QTest::addRow("sumUsagesFromDefinition") << jsIdentifierUsagesPath << 8 << 14 << sumUsages;
+}
+
+static bool locationsAreEqual(const QLspSpecification::Location &a,
+ const QLspSpecification::Location &b)
+{
+ return std::tie(a.uri, a.range.start.character, a.range.start.line, a.range.end.character,
+ a.range.end.line)
+ == std::tie(b.uri, b.range.start.character, b.range.start.line, b.range.end.character,
+ b.range.end.line);
+}
+
+static bool locationListsAreEqual(const QList<QLspSpecification::Location> &a,
+ const QList<QLspSpecification::Location> &b)
+{
+ return std::equal(a.cbegin(), a.cend(), b.cbegin(), b.cend(), locationsAreEqual);
+}
+
+static QString locationToString(const QLspSpecification::Location &l)
+{
+ QString s = u"%1: (%2, %3) - (%4, %5)"_s.arg(l.uri)
+ .arg(l.range.start.line)
+ .arg(l.range.start.character)
+ .arg(l.range.end.line)
+ .arg(l.range.end.character);
+ return s;
+}
+
+void tst_qmlls_modules::findUsages()
+{
+ QFETCH(QString, filePath);
+ // line and character start at 1!
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QList<QLspSpecification::Location>, expectedUsages);
+
+ ignoreDiagnostics();
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ QVERIFY(uri->startsWith("file://"_ba));
+
+ ReferenceParams params;
+ params.position.line = line - 1;
+ params.position.character = character - 1;
+ params.textDocument.uri = *uri;
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ auto clean = [didFinish]() { *didFinish = true; };
+ m_protocol->requestReference(
+ params,
+ [&](auto res) {
+ QScopeGuard cleanup(clean);
+ auto *result = std::get_if<QList<Location>>(&res);
+
+ QVERIFY(result);
+ if constexpr (enable_debug_output) {
+ if (!locationListsAreEqual(*result, expectedUsages)) {
+ qDebug() << "Got following locations:";
+ for (auto &x : *result) {
+ qDebug() << locationToString(x);
+ }
+ qDebug() << "But expected:";
+ for (auto &x : expectedUsages) {
+ qDebug() << locationToString(x);
+ }
+ }
+ } else {
+ // dont get warning on unused function when enable_debug_output is false
+ Q_UNUSED(locationToString);
+ }
+
+ QVERIFY(locationListsAreEqual(*result, expectedUsages));
+ },
+ [clean](const ResponseError &err) {
+ QScopeGuard cleanup(clean);
+ ProtocolBase::defaultResponseErrorHandler(err);
+ QVERIFY2(false, "error computing the completion");
+ });
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 3000);
+}
+
+void tst_qmlls_modules::documentFormatting_data()
+{
+ QTest::addColumn<QString>("originalFile");
+ QTest::addColumn<QString>("expectedFile");
+
+ QDir directory(QT_QMLFORMATTEST_DATADIR);
+
+ // Exclude some test files which require options support
+ QStringList excludedFiles;
+ excludedFiles << u"tests/auto/qml/qmlformat/data/checkIdsNewline.qml"_s;
+ excludedFiles << u"tests/auto/qml/qmlformat/data/normalizedFunctionsSpacing.qml"_s;
+ excludedFiles << u"tests/auto/qml/qmlformat/data/normalizedObjectsSpacing.qml"_s;
+
+ // excluded because it crashes Dom construction
+ // TODO: fix QQMLDomAstConstructor to not crash on these files, see QTBUG-116392
+ excludedFiles << u"tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.qml"_s;
+ excludedFiles << u"tests/auto/qml/qmlformat/data/Example1.qml"_s;
+ excludedFiles << u"tests/auto/qml/qmlformat/data/nestedFunctions.qml"_s;
+
+ const auto shouldSkip = [&excludedFiles](const QString &fileName) {
+ for (const QString &file : excludedFiles) {
+ if (fileName.endsWith(file))
+ return true;
+ }
+ return false;
+ };
+
+ // Filter to include files contain .formatted.
+ const auto formattedFilesInfo =
+ directory.entryInfoList(QStringList{ { "*.formatted.qml" } }, QDir::Files);
+ for (const auto &formattedFileInfo : formattedFilesInfo) {
+ const QFileInfo unformattedFileInfo(directory, formattedFileInfo.fileName().remove(".formatted"));
+ const auto unformattedFilePath = unformattedFileInfo.canonicalFilePath();
+ if (shouldSkip(unformattedFilePath))
+ continue;
+
+ QTest::newRow(qPrintable(unformattedFileInfo.fileName()))
+ << unformattedFilePath << formattedFileInfo.canonicalFilePath();
+ }
+
+ // Extra tests
+ QTest::newRow("leading-and-trailing-blanklines")
+ << testFile("formatting/blanklines.qml")
+ << testFile("formatting/blanklines.formatted.qml");
+}
+
+void tst_qmlls_modules::documentFormatting()
+{
+ QFETCH(QString, originalFile);
+ QFETCH(QString, expectedFile);
+
+ ignoreDiagnostics();
+
+ const auto uri = openFileFromAbsolutePath(originalFile);
+ QVERIFY(uri);
+
+ DocumentFormattingParams params;
+ params.textDocument.uri = *uri;
+
+ const auto lineCount = [](const QString &filePath) {
+ QFile file(filePath);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qWarning() << "Error while opening the file " << filePath;
+ return -1;
+ }
+ int lineCount = 0;
+ QString line;
+ while (!file.atEnd()) {
+ line = file.readLine();
+ ++lineCount;
+ }
+ if (line.endsWith('\n'))
+ ++lineCount;
+ return lineCount;
+ };
+
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ auto clean = [didFinish]() { *didFinish = true; };
+ auto &&responseHandler = [&](auto response) {
+ QScopeGuard cleanup(clean);
+ if (std::holds_alternative<QList<TextEdit>>(response)) {
+ const auto results = std::get<QList<TextEdit>>(response);
+ QVERIFY(results.size() == 1);
+ QFile file(expectedFile);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qWarning() << "Error while opening the file " << expectedFile;
+ return;
+ }
+
+ const auto &textEdit = results.first();
+ QCOMPARE(textEdit.range.start.line, 0);
+ QCOMPARE(textEdit.range.start.character, 0);
+ QCOMPARE(textEdit.range.end.line, lineCount(originalFile));
+ QCOMPARE(textEdit.range.end.character, 0);
+ QCOMPARE(textEdit.newText, file.readAll());
+ }
+ };
+
+ auto &&errorHandler = [&clean](const ResponseError &err) {
+ QScopeGuard cleanup(clean);
+ ProtocolBase::defaultResponseErrorHandler(err);
+ QVERIFY2(false, "error computing the completion");
+ };
+ m_protocol->requestDocumentFormatting(params, std::move(responseHandler),
+ std::move(errorHandler));
+
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 50000);
+}
+
+void tst_qmlls_modules::renameUsages_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<QString>("newName");
+ QTest::addColumn<QLspSpecification::WorkspaceEdit>("expectedEdit");
+ QTest::addColumn<QLspSpecification::ResponseError>("expectedError");
+
+ QLspSpecification::ResponseError noError;
+
+ const QString jsIdentifierUsagesPath = u"findUsages/jsIdentifierUsages.qml"_s;
+ const QByteArray jsIdentifierUsagesUri =
+ testFileUrl("findUsages/jsIdentifierUsages.qml").toEncoded();
+
+ QString jsIdentifierUsagesContent;
+ {
+ QFile file(testFile("findUsages/jsIdentifierUsages.qml").toUtf8());
+ QVERIFY(file.open(QIODeviceBase::ReadOnly));
+ jsIdentifierUsagesContent = QString::fromUtf8(file.readAll());
+ }
+ QString renamingContent;
+ {
+ QFile file(testFile("renameUsages/main.qml").toUtf8());
+ QVERIFY(file.open(QIODeviceBase::ReadOnly));
+ renamingContent = QString::fromUtf8(file.readAll());
+ }
+
+ // TODO: create workspace edit for the tests
+ QLspSpecification::WorkspaceEdit sumRenames{
+ std::nullopt, // TODO
+ QList<QLspSpecification::WorkspaceEdit::DocumentChange>{
+ TextDocumentEdit{
+ OptionalVersionedTextDocumentIdentifier{ { jsIdentifierUsagesUri } },
+ {
+ TextEdit{
+ rangeFrom(jsIdentifierUsagesContent, 8, 13, strlen("sum")),
+ "specialSum" },
+ TextEdit{
+ rangeFrom(jsIdentifierUsagesContent, 10, 13, strlen("sum")),
+ "specialSum" },
+ TextEdit{
+ rangeFrom(jsIdentifierUsagesContent, 10, 19, strlen("sum")),
+ "specialSum" },
+ } },
+ }
+ };
+
+ // line and character start at 1!
+ QTest::addRow("sumRenameFromUsage")
+ << jsIdentifierUsagesPath << 10 << 14 << u"specialSum"_s << sumRenames << noError;
+ QTest::addRow("sumRenameFromUsage2")
+ << jsIdentifierUsagesPath << 10 << 20 << u"specialSum"_s << sumRenames << noError;
+ QTest::addRow("sumRenameFromDefinition")
+ << jsIdentifierUsagesPath << 8 << 14 << u"specialSum"_s << sumRenames << noError;
+ QTest::addRow("invalidSumRenameFromDefinition")
+ << jsIdentifierUsagesPath << 8 << 14 << u"function"_s << sumRenames
+ << QLspSpecification::ResponseError{
+ 0,
+ "Invalid EcmaScript identifier!",
+ std::nullopt,
+ };
+
+ const QString renameUsagesPath = u"renameUsages/main.qml"_s;
+ const QByteArray renameUsagesUri = testFileUrl("renameUsages/main.qml").toEncoded();
+ const QByteArray renameMeUri = testFileUrl("renameUsages/RenameMe.qml").toEncoded();
+ const QByteArray renameMe2Uri = testFileUrl("renameUsages/RenameMe2.ui.qml").toEncoded();
+
+ const QByteArray newFileUri = testFileUrl("renameUsages/HelloWorld.qml").toEncoded();
+ const QByteArray newFileUri2 = testFileUrl("renameUsages/HelloWorld.ui.qml").toEncoded();
+
+ {
+
+ const QLspSpecification::WorkspaceEdit qmlComponentRename{
+ std::nullopt,
+ QList<QLspSpecification::WorkspaceEdit::DocumentChange>{
+ TextDocumentEdit{
+ OptionalVersionedTextDocumentIdentifier{ { renameUsagesUri } },
+ {
+ TextEdit{ rangeFrom(renamingContent, 4, 5,
+ strlen("RenameMe")),
+ "HelloWorld" },
+ } },
+ RenameFile{ "rename", renameMeUri, newFileUri } }
+ };
+
+ QTest::addRow("renameQmlComponent")
+ << renameUsagesPath << 4 << 8 << u"HelloWorld"_s << qmlComponentRename << noError;
+ }
+
+ {
+ QLspSpecification::WorkspaceEdit qmlComponentRename{
+ std::nullopt,
+ QList<QLspSpecification::WorkspaceEdit::DocumentChange>{
+ TextDocumentEdit{
+ OptionalVersionedTextDocumentIdentifier{ { renameUsagesUri } },
+ {
+ TextEdit{ rangeFrom(renamingContent, 5, 5,
+ strlen("RenameMe2")),
+ "HelloWorld" },
+ } },
+ RenameFile{ "rename", renameMe2Uri, newFileUri2 } }
+ };
+
+ QTest::addRow("renameUiQmlComponent")
+ << renameUsagesPath << 5 << 8 << u"HelloWorld"_s << qmlComponentRename << noError;
+ }
+}
+
+void tst_qmlls_modules::compareQTextDocumentEdit(const TextDocumentEdit &a,
+ const TextDocumentEdit &b)
+{
+
+ QCOMPARE(a.textDocument.uri, b.textDocument.uri);
+ QVERIFY(a.textDocument.uri.startsWith("file://"));
+ QCOMPARE(a.textDocument.version, b.textDocument.version);
+ QCOMPARE(a.edits.size(), b.edits.size());
+
+ for (qsizetype j = 0; j < a.edits.size(); ++j) {
+ std::visit(
+ [](auto &&textEdit, auto &&expectedTextEdit) {
+ using U = std::decay_t<decltype(textEdit)>;
+ using V = std::decay_t<decltype(expectedTextEdit)>;
+
+ if constexpr (std::conjunction_v<std::is_same<U, V>,
+ std::is_same<U, TextEdit>>) {
+ QCOMPARE(textEdit.range.start.line, expectedTextEdit.range.start.line);
+ QCOMPARE(textEdit.range.start.character,
+ expectedTextEdit.range.start.character);
+ QCOMPARE(textEdit.range.end.line, expectedTextEdit.range.end.line);
+ QCOMPARE(textEdit.range.end.character,
+ expectedTextEdit.range.end.character);
+ QCOMPARE(textEdit.newText, expectedTextEdit.newText);
+ } else {
+ QFAIL("Comparison not implemented");
+ }
+ },
+ a.edits[j], b.edits[j]);
+ }
+}
+
+void tst_qmlls_modules::renameUsages()
+{
+ QFETCH(QString, filePath);
+ // line and character start at 1!
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QString, newName);
+ QFETCH(QLspSpecification::WorkspaceEdit, expectedEdit);
+ QFETCH(QLspSpecification::ResponseError, expectedError);
+
+ ignoreDiagnostics();
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ QVERIFY(uri->startsWith("file://"_ba));
+
+ RenameParams params;
+ params.position.line = line - 1;
+ params.position.character = character - 1;
+ params.textDocument.uri = *uri;
+ params.newName = newName.toUtf8();
+
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ auto clean = [didFinish]() { *didFinish = true; };
+ m_protocol->requestRename(
+ params,
+ [&](auto &&res) {
+ QScopeGuard cleanup(clean);
+ auto *result = std::get_if<QLspSpecification::WorkspaceEdit>(&res);
+
+ QVERIFY(result);
+ QCOMPARE(result->changes.has_value(), expectedEdit.changes.has_value());
+ QCOMPARE(result->changeAnnotations.has_value(),
+ expectedEdit.changeAnnotations.has_value());
+ QCOMPARE(result->documentChanges.has_value(),
+ expectedEdit.documentChanges.has_value());
+
+ auto &documentChanges = *result->documentChanges;
+ auto &expectedDocumentChanges = *expectedEdit.documentChanges;
+
+ if (!expectedError.message.isEmpty())
+ QVERIFY2(false, "No expected error was thrown.");
+
+ QCOMPARE(documentChanges.size(), expectedDocumentChanges.size());
+
+ for (qsizetype i = 0; i < expectedDocumentChanges.size(); ++i) {
+ QCOMPARE(documentChanges[i].index(), expectedDocumentChanges[i].index());
+ if (std::holds_alternative<TextDocumentEdit>(documentChanges[i])) {
+ compareQTextDocumentEdit(
+ std::get<TextDocumentEdit>(documentChanges[i]),
+ std::get<TextDocumentEdit>(expectedDocumentChanges[i]));
+ } else if (std::holds_alternative<RenameFile>(documentChanges[i])) {
+ const auto &actual = std::get<RenameFile>(documentChanges[i]);
+ const auto &expected = std::get<RenameFile>(expectedDocumentChanges[i]);
+
+ QCOMPARE(actual.kind, expected.kind);
+ QCOMPARE(expected.kind, "rename");
+ QCOMPARE(actual.oldUri, expected.oldUri);
+ QCOMPARE(actual.newUri, expected.newUri);
+ QCOMPARE(actual.options.has_value(), expected.options.has_value());
+ if (expected.options.has_value()) {
+ QCOMPARE(actual.options->overwrite, expected.options->overwrite);
+ QCOMPARE(actual.options->ignoreIfExists,
+ expected.options->ignoreIfExists);
+ }
+ QCOMPARE(actual.annotationId, expected.annotationId);
+
+ } else {
+ QFAIL("TODO: implement me!");
+ }
+ }
+ },
+ [clean, &expectedError](const ResponseError &err) {
+ QScopeGuard cleanup(clean);
+ ProtocolBase::defaultResponseErrorHandler(err);
+ if (expectedError.message.isEmpty())
+ QVERIFY2(false, "unexpected error computing the completion");
+ QCOMPARE(err.code, expectedError.code);
+ QCOMPARE(err.message, expectedError.message);
+ });
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 3000);
+}
+
+struct EditingRecorder
+{
+ QList<DidChangeTextDocumentParams> actions;
+ QHash<int, QString> diagnosticsPerFileVersions;
+
+ /*!
+ All the indexes passed here must start at 1!
+
+ If you want to make sure that your own written changes make sense, use
+ \code
+ qputenv("QT_LOGGING_RULES",
+ "qt.languageserver.codemodel.debug=true;qt.languageserver.codemodel.warning=true"); \endcode
+ before starting qmlls. It will print the differences between the different versions, and helps
+ when some indices are off.
+ */
+ void changeText(int startLine, int startCharacter, int endLine, int endCharacter,
+ QString newText)
+ {
+ // The LSP starts at 0
+ QVERIFY(startLine > 0);
+ QVERIFY(startCharacter > 0);
+ QVERIFY(endLine > 0);
+ QVERIFY(endCharacter > 0);
+
+ --startLine;
+ --startCharacter;
+ --endLine;
+ --endCharacter;
+
+ DidChangeTextDocumentParams params;
+ params.textDocument = VersionedTextDocumentIdentifier{ { lastFileUri }, ++version };
+ params.contentChanges.append({
+ Range{ Position{ startLine, startCharacter }, Position{ endLine, endCharacter } },
+ std::nullopt, // deprecated range length
+ newText.toUtf8(),
+ });
+ actions.append(params);
+ }
+
+ void setFile(const QString &filePath) { lastFilePath = filePath; }
+
+ void setCurrentExpectedDiagnostic(const QString &diagnostic)
+ {
+ Q_ASSERT(diagnosticsPerFileVersions.find(version) == diagnosticsPerFileVersions.end());
+ diagnosticsPerFileVersions[version] = diagnostic;
+ }
+
+ QString lastFilePath;
+ QByteArray lastFileUri;
+ int version = 0;
+};
+
+static constexpr int characterAfter(const char *line)
+{
+ return std::char_traits<char>::length(line) + 1;
+}
+
+static EditingRecorder propertyTypoScenario(const QByteArray &fileUri)
+{
+ EditingRecorder propertyTypo;
+ propertyTypo.lastFileUri = fileUri;
+
+ propertyTypo.changeText(5, 1, 5, 1, u" property int t"_s);
+
+ // replace property by propertyt and expect a complaint from the parser
+ propertyTypo.changeText(5, characterAfter(" property"), 5, characterAfter(" property"),
+ u"t"_s);
+ propertyTypo.setCurrentExpectedDiagnostic(u"Expected token"_s);
+
+ // replace propertyt back to property and expect no complaint from the parser
+ propertyTypo.changeText(5, characterAfter(" property"), 5, characterAfter(" propertyt"),
+ u""_s);
+
+ // replace property by propertyt and expect a complaint from the parser
+ propertyTypo.changeText(5, characterAfter(" property"), 5, characterAfter(" property"),
+ u"t"_s);
+ propertyTypo.setCurrentExpectedDiagnostic(u"Expected token"_s);
+
+ // now, simulate some slow typing and expect the previous warning to not disappear
+ const QString data = u"Item {}\n"_s;
+ for (int i = 0; i < data.size(); ++i) {
+ propertyTypo.changeText(6, i + 1, 6, i + 1, data[i]);
+ propertyTypo.setCurrentExpectedDiagnostic(u"Expected token"_s);
+ }
+
+ // replace propertyt back to property and expect no complaint from the parser
+ propertyTypo.changeText(5, characterAfter(" property"), 5, characterAfter(" propertyt"),
+ u""_s);
+
+ return propertyTypo;
+}
+
+void tst_qmlls_modules::linting_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<EditingRecorder>("recorder");
+
+ QTest::addRow("property-typo")
+ << u"linting/SimpleItem.qml"_s
+ << propertyTypoScenario(testFileUrl(u"linting/SimpleItem.qml"_s).toEncoded());
+}
+
+void tst_qmlls_modules::linting()
+{
+ QFETCH(QString, filePath);
+ QFETCH(EditingRecorder, recorder);
+ bool diagnosticOk = false;
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ recorder.lastFileUri = *uri;
+ m_protocol->registerPublishDiagnosticsNotificationHandler(
+ [&recorder, &diagnosticOk, &uri](const QByteArray &,
+ const PublishDiagnosticsParams &p) {
+ if (p.uri != *uri || !p.version)
+ return;
+ auto expectedMessage = recorder.diagnosticsPerFileVersions.find(*p.version);
+ if (expectedMessage == recorder.diagnosticsPerFileVersions.end()) {
+ if constexpr (enable_debug_output) {
+ if (p.diagnostics.size() > 0)
+ qDebug() << "Did not expect message" << p.diagnostics.front().message;
+ }
+
+ QVERIFY(p.diagnostics.size() == 0);
+ diagnosticOk = true;
+ return;
+ }
+ QVERIFY(p.diagnostics.size() > 0);
+ if constexpr (enable_debug_output) {
+ if (!p.diagnostics.front().message.contains(expectedMessage->toUtf8())) {
+ qDebug() << "expected a message with" << *expectedMessage << "but got"
+ << p.diagnostics.front().message;
+ }
+ }
+ QVERIFY(p.diagnostics.front().message.contains(expectedMessage->toUtf8()));
+ diagnosticOk = true;
+ });
+ for (const auto &action : recorder.actions) {
+ m_protocol->notifyDidChangeTextDocument(action);
+ QTRY_VERIFY_WITH_TIMEOUT(diagnosticOk, 5000);
+ diagnosticOk = false;
+ }
+}
+
+void tst_qmlls_modules::rangeFormatting_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<QLspSpecification::Range>("selectedRange");
+ QTest::addColumn<QLspSpecification::Range>("expectedRange");
+ QTest::addColumn<QString>("expectedAfterFormat");
+
+ const QString filePath = u"formatting/rangeFormatting.qml"_s;
+
+ {
+ QLspSpecification::Range selectedRange = { { 5, 0 }, { 9, 0 } };
+ QLspSpecification::Range expectedRange = { { 0, 0 }, { 24, 0 } };
+ QTest::addRow("selectRegion1") << filePath << selectedRange << expectedRange
+ << u"formatting/rangeFormatting.formatted1.qml"_s;
+ }
+
+ {
+ QLspSpecification::Range selectedRange = { { 10, 25 }, { 23, 0 } };
+ QLspSpecification::Range expectedRange = { { 0, 0 }, { 24, 0 } };
+ QTest::addRow("selectRegion2") << filePath << selectedRange << expectedRange
+ << u"formatting/rangeFormatting.formatted2.qml"_s;
+ }
+
+ {
+ QLspSpecification::Range selectedRange = { { 14, 36 }, { 14, 45 } };
+ QLspSpecification::Range expectedRange = { { 0, 0 }, { 24, 0 } };
+ QTest::addRow("selectSingleLine") << filePath << selectedRange << expectedRange
+ << u"formatting/rangeFormatting.formatted3.qml"_s;
+ }
+
+ {
+ QLspSpecification::Range selectedRange = { { 0, 0 }, { 24, 0 } };
+ QLspSpecification::Range expectedRange = { { 0, 0 }, { 24, 0 } };
+ QTest::addRow("selectEntireFile") << filePath << selectedRange << expectedRange
+ << u"formatting/rangeFormatting.formatted4.qml"_s;
+ }
+
+ {
+ QLspSpecification::Range selectedRange = { { 10, 3 }, { 20, 4 } };
+ QLspSpecification::Range expectedRange = { { 0, 0 }, { 24, 0 } };
+ QTest::addRow("selectUnbalanced") << filePath << selectedRange << expectedRange
+ << u"formatting/rangeFormatting.formatted5.qml"_s;
+ }
+}
+
+void tst_qmlls_modules::rangeFormatting()
+{
+ QFETCH(QString, filePath);
+ QFETCH(QLspSpecification::Range, selectedRange);
+ QFETCH(QLspSpecification::Range, expectedRange);
+ QFETCH(QString, expectedAfterFormat);
+
+ ignoreDiagnostics();
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+
+ QLspSpecification::DocumentRangeFormattingParams params;
+ params.textDocument.uri = *uri;
+ params.range = selectedRange;
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ const auto clean = [didFinish]() { *didFinish = true; };
+
+ auto &&responseHandler = [&](auto res) {
+ Q_UNUSED(res);
+ QScopeGuard cleanup(clean);
+ auto result = std::get_if<QList<TextEdit>>(&res);
+ QVERIFY(result);
+
+ QFile file(testFile(expectedAfterFormat));
+ if (!file.open(QIODevice::ReadOnly))
+ QFAIL("Error while opening the file ");
+
+ const auto text = result->first();
+ QCOMPARE(text.range.start.line, expectedRange.start.line);
+ QCOMPARE(text.range.start.character, expectedRange.start.character);
+ QCOMPARE(text.range.end.line, expectedRange.end.line);
+ QCOMPARE(text.range.end.character, expectedRange.end.character);
+ QCOMPARE(text.newText, file.readAll());
+ };
+
+ auto &&errorHandler = [&clean](auto &error) {
+ QScopeGuard cleanup(clean);
+ ProtocolBase::defaultResponseErrorHandler(error);
+ QVERIFY2(false, "error occurred while range formatting");
+ };
+
+ m_protocol->requestDocumentRangeFormatting(params, std::move(responseHandler),
+ std::move(errorHandler));
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 10000);
+}
+
+void tst_qmlls_modules::hover_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<QLspSpecification::Position>("hoveredPosition");
+ QTest::addColumn<QLspSpecification::MarkupContent>("expectedResult");
+
+ const QString filePath = u"hover/test.qml"_s;
+ {
+ QLspSpecification::MarkupContent content{ MarkupKind::PlainText, "should fail" };
+ QTest::addRow("hover") << filePath << QLspSpecification::Position{ 7, 24 } << content;
+ }
+}
+
+void tst_qmlls_modules::hover()
+{
+ QFETCH(QString, filePath);
+ QFETCH(QLspSpecification::Position, hoveredPosition);
+ QFETCH(QLspSpecification::MarkupContent, expectedResult);
+
+ ignoreDiagnostics();
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+
+ QLspSpecification::HoverParams params;
+ params.textDocument.uri = *uri;
+ params.position = hoveredPosition;
+
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ const auto clean = [didFinish]() { *didFinish = true; };
+
+ auto &&responseHandler = [&](auto res) {
+ QScopeGuard cleanup(clean);
+ const auto *const result = std::get_if<QLspSpecification::Hover>(&res);
+ QVERIFY(result);
+
+ const auto *const markupContent =
+ std::get_if<QLspSpecification::MarkupContent>(&result->contents);
+ QVERIFY(markupContent);
+
+ QEXPECT_FAIL("hover", "Should fail until we get the actual documentation for hovered items",
+ Continue);
+ QCOMPARE(markupContent->value, expectedResult.value);
+ QCOMPARE(markupContent->kind, expectedResult.kind);
+ };
+
+ auto &&errorHandler = [&clean](auto &error) {
+ QScopeGuard cleanup(clean);
+ ProtocolBase::defaultResponseErrorHandler(error);
+ QVERIFY2(false, "error occurred on hovering");
+ };
+
+ m_protocol->requestHover(params, std::move(responseHandler), std::move(errorHandler));
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 10000);
+}
+
+enum AddBuildDirOption : bool { AddBuildDir, DoNotAddBuildDir };
+
+void tst_qmlls_modules::qmldirImports_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<AddBuildDirOption>("addBuildDirectory");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<QString>("expectedCompletion");
+
+ QTest::addRow("fromBuildFolder")
+ << u"completions/fromBuildDir.qml"_s << AddBuildDir << 3 << 1 << u"BuildDirType"_s;
+ QTest::addRow("fromSourceFolder")
+ << u"sourceDir/Main.qml"_s << DoNotAddBuildDir << 3 << 1 << u"Button"_s;
+}
+
+void tst_qmlls_modules::qmldirImports()
+{
+ QFETCH(QString, filePath);
+ QFETCH(AddBuildDirOption, addBuildDirectory);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QString, expectedCompletion);
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+
+ if (addBuildDirectory == AddBuildDir) {
+ Notifications::AddBuildDirsParams bDirs;
+ UriToBuildDirs ub;
+ ub.baseUri = *uri;
+ ub.buildDirs.append(testFile("buildDir").toUtf8());
+ bDirs.buildDirsToSet.append(ub);
+ m_protocol->typedRpc()->sendNotification(QByteArray(Notifications::AddBuildDirsMethod), bDirs);
+ }
+
+ bool diagnosticOk = false;
+ bool completionOk = false;
+ m_protocol->registerPublishDiagnosticsNotificationHandler(
+ [&diagnosticOk, &uri](const QByteArray &, const PublishDiagnosticsParams &p) {
+ if (p.uri != *uri)
+ return;
+
+ if constexpr (enable_debug_output) {
+ for (const auto &x : p.diagnostics) {
+ qDebug() << x.message;
+ }
+ }
+ QCOMPARE(p.diagnostics.size(), 0);
+ diagnosticOk = true;
+ });
+
+ // Currently, the Dom is created twice in qmlls: once for the linting and once for all other
+ // features. Therefore, also test that this second dom also uses the right resource files.
+ CompletionParams cParams;
+ cParams.position.line = line - 1; // LSP is 0 based
+ cParams.position.character = character - 1; // LSP is 0 based
+ cParams.textDocument.uri = *uri;
+
+ m_protocol->requestCompletion(cParams, [&completionOk, &expectedCompletion](auto res) {
+ const QList<CompletionItem> *cItems = std::get_if<QList<CompletionItem>>(&res);
+
+ QSet<QString> labels;
+ for (const CompletionItem &c : *cItems) {
+ labels << c.label;
+ }
+ QVERIFY(labels.contains(expectedCompletion));
+ completionOk = true;
+ });
+
+ QTRY_VERIFY_WITH_TIMEOUT(diagnosticOk && completionOk, 5000);
+}
+
+void tst_qmlls_modules::quickFixes_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<CodeActionParams>("codeActionParams");
+ QTest::addColumn<int>("diagnosticIndex");
+ QTest::addColumn<Range>("replacementRange");
+ QTest::addColumn<QString>("replacementText");
+
+ const QString filePath = u"quickfixes/INeedAQuickFix.qml"_s;
+
+ QString fileContent;
+ {
+ QFile file(testFile(filePath));
+ QVERIFY(file.open(QFile::Text | QFile::ReadOnly));
+ fileContent = file.readAll();
+ }
+
+ CodeActionParams firstCodeAction;
+ firstCodeAction.range = rangeFrom(fileContent, 10, 23, 1);
+ firstCodeAction.textDocument.uri = testFileUrl(filePath).toEncoded();
+
+ const Range firstRange = rangeFrom(fileContent, 10, 10, 0);
+ const QString firstReplacement = u"(xxx) => "_s;
+
+ QTest::addRow("injectedParameters")
+ << filePath << firstCodeAction << 0 << firstRange << firstReplacement;
+
+ CodeActionParams secondCodeAction;
+ secondCodeAction.textDocument.uri = testFileUrl(filePath).toEncoded();
+ secondCodeAction.range = rangeFrom(fileContent, 15, 20, 1);
+
+ const Range secondRange = rangeFrom(fileContent, 15, 20, 0);
+ const QString secondReplacement = u"hello."_s;
+
+ QTest::addRow("parentProperty")
+ << filePath << secondCodeAction << 1 << secondRange << secondReplacement;
+}
+
+std::tuple<int, int, int, int> rangeAsTuple(const Range &range)
+{
+ return std::make_tuple(range.start.line, range.start.character, range.end.line,
+ range.end.character);
+}
+
+void tst_qmlls_modules::quickFixes()
+{
+ QFETCH(QString, filePath);
+ QFETCH(CodeActionParams, codeActionParams);
+ // The index of the diagnostic that the quickFix belongs to.
+ // diagnostics are sorted by their range (= text position in the current file).
+ QFETCH(int, diagnosticIndex);
+ QFETCH(Range, replacementRange);
+ QFETCH(QString, replacementText);
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+
+ bool diagnosticOk = false;
+ QList<Diagnostic> diagnostics;
+
+ // run first the diagnostic that proposes a quickfix
+ m_protocol->registerPublishDiagnosticsNotificationHandler(
+ [&diagnosticOk, &uri, &diagnostics,
+ &diagnosticIndex](const QByteArray &, const PublishDiagnosticsParams &p) {
+ if (p.uri != *uri)
+ return;
+
+ if constexpr (enable_debug_output) {
+ for (const auto &x : p.diagnostics) {
+ qDebug() << x.message;
+ }
+ }
+ QCOMPARE_GE(p.diagnostics.size(), diagnosticIndex);
+
+ QList<Diagnostic> partially_sorted{ p.diagnostics };
+ std::nth_element(partially_sorted.begin(),
+ std::next(partially_sorted.begin(), diagnosticIndex),
+ partially_sorted.end(),
+ [](const Diagnostic &a, const Diagnostic &b) {
+ return rangeAsTuple(a.range) < rangeAsTuple(b.range);
+ });
+ diagnostics.append(partially_sorted[diagnosticIndex]);
+
+ diagnosticOk = true;
+ });
+
+ QTRY_VERIFY_WITH_TIMEOUT(diagnosticOk, 5000);
+
+ codeActionParams.context.diagnostics = diagnostics;
+
+ using InnerT = QList<std::variant<Command, CodeAction>>;
+ using T = std::variant<InnerT, std::nullptr_t>;
+
+ bool codeActionOk = false;
+
+ // request a quickfix with the obtained diagnostic
+ m_protocol->requestCodeAction(codeActionParams, [&](const T &result) {
+ QVERIFY(std::holds_alternative<InnerT>(result));
+ InnerT inner = std::get<InnerT>(result);
+ QCOMPARE(inner.size(), 1);
+ QVERIFY(std::holds_alternative<CodeAction>(inner.front()));
+ CodeAction codeAction = std::get<CodeAction>(inner.front());
+
+ QCOMPARE(codeAction.kind, "quickfix"); // everything else is ignored by QtC, VS Code, ...
+
+ QVERIFY(codeAction.edit);
+ QVERIFY(codeAction.edit->documentChanges);
+ const auto &edits = *codeAction.edit->documentChanges;
+ QCOMPARE(edits.size(), 1);
+ const auto& firstEdit = std::get<TextDocumentEdit>(edits.front());
+ QCOMPARE(firstEdit.edits.size(), 1);
+ QVERIFY(std::holds_alternative<TextEdit>(firstEdit.edits.front()));
+ auto textEdit = std::get<TextEdit>(firstEdit.edits.front());
+
+ // make sure that the quick fix does something
+ QCOMPARE(textEdit.newText, replacementText);
+ QCOMPARE(rangeAsTuple(textEdit.range), rangeAsTuple(replacementRange));
+
+ codeActionOk = true;
+ });
+
+ QTRY_VERIFY_WITH_TIMEOUT(codeActionOk, 5000);
+}
+
+static QQmlJS::Dom::DomItem fileObject(const QString &filePath)
+{
+ QFile f(filePath);
+ QQmlJS::Dom::DomItem file;
+ if (!f.open(QIODevice::ReadOnly))
+ return file;
+ QString code = f.readAll();
+ QQmlJS::Dom::DomCreationOptions options;
+ options.setFlag(QQmlJS::Dom::DomCreationOption::WithScriptExpressions);
+ options.setFlag(QQmlJS::Dom::DomCreationOption::WithSemanticAnalysis);
+ options.setFlag(QQmlJS::Dom::DomCreationOption::WithRecovery);
+
+ QStringList dirs = {QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath)};
+ auto envPtr = QQmlJS::Dom::DomEnvironment::create(dirs,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies, options);
+ envPtr->loadBuiltins();
+ envPtr->loadFile(QQmlJS::Dom::FileToLoad::fromMemory(envPtr, filePath, code),
+ [&file](QQmlJS::Dom::Path, const QQmlJS::Dom::DomItem &, const QQmlJS::Dom::DomItem &newIt) {
+ file = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+ return file;
+};
+
+void tst_qmlls_modules::semanticHighlightingFull_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addRow("bigfile") << u"highlighting/bigFile.qml"_s;
+}
+
+void tst_qmlls_modules::semanticHighlightingFull()
+{
+ QFETCH(QString, filePath);
+ const auto item = fileObject(testFile(filePath));
+ Highlights highlights;
+ const auto expectedData = highlights.collectTokens(item, std::nullopt);
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ QLspSpecification::SemanticTokensParams params;
+ params.textDocument.uri = *uri;
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ const auto cleanup = [didFinish]() { *didFinish = true; };
+
+ auto &&responseHandler = [&](auto res) {
+ QScopeGuard callAtExit(cleanup);
+ const auto *const result = std::get_if<QLspSpecification::SemanticTokens>(&res);
+ QVERIFY(result);
+ QList<int> data = result->data;
+ QCOMPARE(data.size(), expectedData.size());
+ QCOMPARE(data, expectedData);
+ };
+
+ auto &&errorHandler = [&](auto &error) {
+ QScopeGuard callAtExit(cleanup);
+ ProtocolBase::defaultResponseErrorHandler(error);
+ QVERIFY2(false, "error occurred on full semantic tokens");
+ };
+
+ m_protocol->requestSemanticTokens(params, std::move(responseHandler), std::move(errorHandler));
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 10000);
+}
+
+void tst_qmlls_modules::semanticHighlightingRange_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<QLspSpecification::Range>("range");
+ QTest::addRow("bigfile") << u"highlighting/bigFile.qml"_s
+ << QLspSpecification::Range{ { 6, 0 }, { 15, 0 } };
+}
+
+void tst_qmlls_modules::semanticHighlightingRange()
+{
+ QFETCH(QString, filePath);
+ QFETCH(QLspSpecification::Range, range);
+
+ const auto item = fileObject(testFile(filePath));
+ Highlights highlights;
+ const auto qmlFile = item.as<QQmlJS::Dom::QmlFile>();
+ const auto code = qmlFile->code();
+ const int startOffset = int(QQmlLSUtils::textOffsetFrom(code, range.start.line, range.end.character));
+ const int endOffset = int(QQmlLSUtils::textOffsetFrom(code, range.end.line, range.end.character));
+ const auto expectedData = highlights.collectTokens(item, HighlightsRange{startOffset, endOffset});
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ QLspSpecification::SemanticTokensRangeParams params;
+ params.textDocument.uri = *uri;
+ params.range = range;
+
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ const auto cleanup = [didFinish]() { *didFinish = true; };
+
+ auto &&responseHandler = [&](auto res) {
+ QScopeGuard callAtExit(cleanup);
+ const auto *const result = std::get_if<QLspSpecification::SemanticTokens>(&res);
+ QVERIFY(result);
+ QList<int> data = result->data;
+ QCOMPARE(data.size(), expectedData.size());
+ QCOMPARE(data, expectedData);
+ };
+
+ auto &&errorHandler = [&](auto &error) {
+ QScopeGuard callAtExit(cleanup);
+ ProtocolBase::defaultResponseErrorHandler(error);
+ QVERIFY2(false, "error occurred on full semantic tokens");
+ };
+
+ m_protocol->requestSemanticTokensRange(params, std::move(responseHandler),
+ std::move(errorHandler));
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 10000);
+}
+
+void tst_qmlls_modules::semanticHighlightingDelta_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addRow("basicDelta") << u"highlighting/basic.qml"_s;
+}
+
+void tst_qmlls_modules::semanticHighlightingDelta()
+{
+ QSKIP("This test should be skipped until QTBUG-124870 is fixed");
+ QFETCH(QString, filePath);
+ QFETCH(QString, deltaFilePath);
+
+ const auto fileItem = fileObject(testFile(filePath));
+ const auto deltaFileItem = fileObject(testFile(deltaFilePath));
+ Highlights highlights;
+ auto fullDocumentSemanticTokensData = highlights.collectTokens(fileItem, std::nullopt);
+ auto editedDocumentSemanticTokensData = highlights.collectTokens(deltaFileItem, std::nullopt);
+ const auto expectedEdits = HighlightingUtils::computeDiff(fullDocumentSemanticTokensData, editedDocumentSemanticTokensData);
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ const auto deltaUri = openFile(deltaFilePath);
+ QVERIFY(deltaUri);
+
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ const auto cleanup = [didFinish]() { *didFinish = true; };
+
+ QLspSpecification::SemanticTokensDeltaParams params;
+ QLspSpecification::Responses::SemanticTokensDeltaResultType result;
+
+ auto &&errorHandler = [&](auto &error) {
+ QScopeGuard callAtExit(cleanup);
+ ProtocolBase::defaultResponseErrorHandler(error);
+ QVERIFY2(false, "error occurred on semantic tokens/delta");
+ };
+
+ QLspSpecification::SemanticTokensParams fullParams;
+ fullParams.textDocument.uri = *uri;
+ m_protocol->requestSemanticTokens(fullParams,
+ [&](auto res) {
+ QScopeGuard callAtExit(cleanup);
+ if (auto r = std::get_if<QLspSpecification::SemanticTokens>(&res)) {
+ params.previousResultId = r->resultId.value();
+ fullDocumentSemanticTokensData = r->data;
+ }
+ }, errorHandler);
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 10000);
+
+ // Change the file
+ DidChangeTextDocumentParams didChange;
+ didChange.textDocument.uri = *uri;
+ didChange.textDocument.version = 2;
+
+ TextDocumentContentChangeEvent change;
+ change.range = Range{ Position{ 8, 4 }, Position{ 8, 4 } };
+ change.text = "const Patron = 42";
+
+ didChange.contentChanges.append(change);
+ m_protocol->notifyDidChangeTextDocument(didChange);
+
+ *didFinish = false;
+ params.textDocument.uri = *uri;
+ m_protocol->requestSemanticTokensDelta(params,
+ [&](auto res) {
+ QScopeGuard callAtExit(cleanup);
+ result = res;
+ }, std::move(errorHandler));
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 10000);
+
+ if (const auto *const delta = std::get_if<QLspSpecification::SemanticTokensDelta>(&result)) {
+ QVERIFY(delta);
+ const auto data = delta->edits.front().data;
+ const auto start = delta->edits.front().start;
+ const auto deleteCount = delta->edits.front().deleteCount;
+ QCOMPARE(start, expectedEdits.front().start);
+ QCOMPARE(deleteCount, expectedEdits.front().deleteCount);
+ QCOMPARE(data, expectedEdits.front().data);
+ } else {
+ const auto *const full = std::get_if<QLspSpecification::SemanticTokens>(&result);
+ QVERIFY(full);
+ QCOMPARE(full->data, expectedEdits.front().data);
+ }
+}
+
+QTEST_MAIN(tst_qmlls_modules)
diff --git a/tests/auto/qmlls/modules/tst_qmlls_modules.h b/tests/auto/qmlls/modules/tst_qmlls_modules.h
new file mode 100644
index 0000000000..d7b601cf5a
--- /dev/null
+++ b/tests/auto/qmlls/modules/tst_qmlls_modules.h
@@ -0,0 +1,85 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QMLLSMODULES_H
+#define TST_QMLLSMODULES_H
+
+#include <QtJsonRpc/private/qjsonrpcprotocol_p.h>
+#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtCore/private/qduplicatetracker_p.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qprocess.h>
+#include <QtCore/qlibraryinfo.h>
+#include <QtCore/qstringlist.h>
+
+#include <QtTest/qtest.h>
+#include <QtQmlLS/private/qlspcustomtypes_p.h>
+
+#include <iostream>
+#include <variant>
+
+
+class tst_qmlls_modules : public QQmlDataTest
+{
+ using ExpectedCompletion = QPair<QString, QLspSpecification::CompletionItemKind>;
+ using ExpectedCompletions = QList<ExpectedCompletion>;
+
+ using ExpectedDocumentation = std::tuple<QString, QString, QString>;
+ using ExpectedDocumentations = QList<ExpectedDocumentation>;
+
+ Q_OBJECT
+public:
+ tst_qmlls_modules();
+ void checkCompletions(const QByteArray &filePath, int lineNr, int character,
+ ExpectedCompletions expected, QStringList notExpected);
+ std::optional<QByteArray> openFile(const QString &uri);
+ std::optional<QByteArray> openFileFromAbsolutePath(const QString &uri);
+ void ignoreDiagnostics();
+ void compareQTextDocumentEdit(const QLspSpecification::TextDocumentEdit &a,
+ const QLspSpecification::TextDocumentEdit &b);
+private slots:
+ void init() final;
+ void cleanup();
+ void initTestCase() final;
+ void function_documentations_data();
+ void function_documentations();
+ void buildDir();
+ void goToTypeDefinition_data();
+ void goToTypeDefinition();
+ void goToDefinition_data();
+ void goToDefinition();
+ void findUsages_data();
+ void findUsages();
+ void documentFormatting_data();
+ void documentFormatting();
+ void renameUsages_data();
+ void renameUsages();
+ void linting_data();
+ void linting();
+ void rangeFormatting_data();
+ void rangeFormatting();
+ void qmldirImports_data();
+ void qmldirImports();
+ void quickFixes_data();
+ void quickFixes();
+ void automaticSemicolonInsertionForCompletions_data();
+ void automaticSemicolonInsertionForCompletions();
+ void hover_data();
+ void hover();
+ void checkQuickSnippets();
+ void semanticHighlightingFull_data();
+ void semanticHighlightingFull();
+ void semanticHighlightingRange_data();
+ void semanticHighlightingRange();
+ void semanticHighlightingDelta_data();
+ void semanticHighlightingDelta();
+private:
+ QProcess m_server;
+ std::unique_ptr<QLanguageServerProtocol> m_protocol;
+ QString m_qmllsPath;
+ QList<QByteArray> m_uriToClose;
+};
+
+#endif // TST_QMLLSMODULES_H
diff --git a/tests/auto/qmlls/qmlls/CMakeLists.txt b/tests/auto/qmlls/qmlls/CMakeLists.txt
index e5f4bf822c..b3580c74e7 100644
--- a/tests/auto/qmlls/qmlls/CMakeLists.txt
+++ b/tests/auto/qmlls/qmlls/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlls LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
data)
@@ -25,3 +31,9 @@ qt_internal_extend_target(tst_qmlls CONDITION ANDROID OR IOS
DEFINES
QT_QMLTEST_DATADIR=":/domdata"
)
+
+if (TARGET qmlls)
+ # in a standalone build, the qmlls target won't exist
+ # but we assume that it is available if you manually set up the test
+ add_dependencies(tst_qmlls qmlls)
+endif()
diff --git a/tests/auto/qmlls/qmlls/tst_qmlls.cpp b/tests/auto/qmlls/qmlls/tst_qmlls.cpp
index 49d2de0583..9e057992c4 100644
--- a/tests/auto/qmlls/qmlls/tst_qmlls.cpp
+++ b/tests/auto/qmlls/qmlls/tst_qmlls.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtJsonRpc/private/qjsonrpcprotocol_p.h>
#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -214,18 +214,17 @@ void tst_Qmlls::didOpenTextDocument()
QString title = QString::fromUtf8(action.title);
QVERIFY(action.kind.has_value());
- QCOMPARE(QString::fromUtf8(action.kind.value()),
- QLatin1StringView("refactor.rewrite"));
+ QCOMPARE(QString::fromUtf8(action.kind.value()), QLatin1StringView("quickfix"));
QVERIFY(action.edit.has_value());
WorkspaceEdit edit = action.edit.value();
QVERIFY(edit.documentChanges.has_value());
- auto docChangeVariant = edit.documentChanges.value();
- QVERIFY(std::holds_alternative<QList<TextDocumentEdit>>(docChangeVariant));
- auto documentChanges = std::get<QList<TextDocumentEdit>>(docChangeVariant);
+ auto documentChanges = edit.documentChanges.value();
QCOMPARE(documentChanges.size(), 1);
- TextDocumentEdit textDocEdit = documentChanges.first();
+ QVERIFY(std::holds_alternative<TextDocumentEdit>(documentChanges.first()));
+ TextDocumentEdit textDocEdit
+ = std::get<TextDocumentEdit>(documentChanges.first());
QCOMPARE(textDocEdit.textDocument.uri, textDocument.uri);
QVERIFY(std::holds_alternative<int>(textDocEdit.textDocument.version));
diff --git a/tests/auto/qmlls/qqmlcodemodel/CMakeLists.txt b/tests/auto/qmlls/qqmlcodemodel/CMakeLists.txt
new file mode 100644
index 0000000000..21b14a62a8
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/CMakeLists.txt
@@ -0,0 +1,33 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlls_qqmlcodemodel LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qmlls_qqmlcodemodel
+ SOURCES
+ tst_qmlls_qqmlcodemodel.cpp tst_qmlls_qqmlcodemodel.h
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::LanguageServerPrivate
+ Qt::Test
+ Qt::QuickTestUtilsPrivate
+ Qt::QmlLSPrivate
+ TESTDATA ${test_data}
+ DEFINES
+ QT_QQMLCODEMODEL_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+)
+
+qt_internal_extend_target(tst_qmlls_qqmlcodemodel CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QQMLCODEMODEL_DATADIR=":/data"
+)
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/FileA.qml b/tests/auto/qmlls/qqmlcodemodel/data/FileA.qml
new file mode 100644
index 0000000000..5560aee727
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/FileA.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/FileA2.qml b/tests/auto/qmlls/qqmlcodemodel/data/FileA2.qml
new file mode 100644
index 0000000000..7680c63f95
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/FileA2.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property int helloProperty
+} \ No newline at end of file
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/FileB.qml b/tests/auto/qmlls/qqmlcodemodel/data/FileB.qml
new file mode 100644
index 0000000000..03cd2f9fb3
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/FileB.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+FileA {
+ property int helloPropertyInB
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/Main.qml b/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/Main.qml
new file mode 100644
index 0000000000..6406952c27
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/Main.qml
@@ -0,0 +1,5 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+
+HelloWorld { myP: 55; myPPP: 55 }
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/mycppmodule.qmltypes b/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/mycppmodule.qmltypes
new file mode 100644
index 0000000000..8a2a1bc714
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/mycppmodule.qmltypes
@@ -0,0 +1,36 @@
+import QtQuick.tooling 1.2
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by qmltyperegistrar.
+
+Module {
+ Component {
+ file: "helloworld.h"
+ name: "HelloWorld"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ exports: ["MyCppModule/HelloWorld 1.0"]
+ exportMetaObjectRevisions: [256]
+ Property {
+ name: "myP"
+ type: "int"
+ read: "myP"
+ write: "setMyP"
+ notify: "myPChanged"
+ index: 0
+ isFinal: true
+ }
+ Property {
+ name: "myPPP"
+ type: "int"
+ read: "myP"
+ write: "setMyP"
+ notify: "myPChanged"
+ index: 1
+ isFinal: true
+ }
+ Signal { name: "myPChanged" }
+ }
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/qmldir b/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/qmldir
new file mode 100644
index 0000000000..741dd5cbf8
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/qmldir
@@ -0,0 +1,5 @@
+module MyCppModule
+typeinfo mycppmodule.qmltypes
+prefer :/MyCppModule/
+Main 1.0 Main.qml
+
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/Main.qml b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/Main.qml
new file mode 100644
index 0000000000..dbe18d54a6
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/Main.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import autoGenCMake
+
+Window {
+ width: 640
+ height: 480
+ visible: true
+ title: qsTr("Hello World")
+ HelloWorld { myP: 55; myPPP: 55 }
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/helloworld.cpp b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/helloworld.cpp
new file mode 100644
index 0000000000..445103c228
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/helloworld.cpp
@@ -0,0 +1,8 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "helloworld.h"
+
+HelloWorld::HelloWorld(QObject *parent)
+ : QObject{parent}
+{}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/helloworld.h b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/helloworld.h
new file mode 100644
index 0000000000..0a13344476
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/helloworld.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef HELLOWORLD_H
+#define HELLOWORLD_H
+
+#include <QObject>
+#include <QQmlEngine>
+
+class HelloWorld : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(int myP READ myP WRITE setMyP NOTIFY myPChanged FINAL)
+ Q_PROPERTY(int myPPP READ myP WRITE setMyP NOTIFY myPChanged FINAL)
+
+public:
+ explicit HelloWorld(QObject *parent = nullptr);
+
+ int myP() { return m_myP; }
+ void setMyP(int p) { m_myP = p; }
+private:
+ int m_myP;
+
+signals:
+ void myPChanged();
+};
+
+#endif // HELLOWORLD_H
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.cpp b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.cpp
new file mode 100644
index 0000000000..604e42f8b1
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "myqmlelement.h"
+
+MyQmlElement::MyQmlElement(QObject *parent)
+ : QObject{parent}
+{
+
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.h b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.h
new file mode 100644
index 0000000000..dd4e2e2cc4
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef MYQMLELEMENT_H
+#define MYQMLELEMENT_H
+
+#include <QObject>
+#include <QQmlEngine>
+
+class MyQmlElement : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ explicit MyQmlElement(QObject *parent = nullptr);
+
+signals:
+
+};
+
+#endif // MYQMLELEMENT_H
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.qml b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.qml
new file mode 100644
index 0000000000..226927e792
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.qml
@@ -0,0 +1,8 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.15
+
+Item {
+
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement2.cpp b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement2.cpp
new file mode 100644
index 0000000000..abeeb8f3b1
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement2.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "myqmlelement2.hpp"
+
+myQmlElement2::myQmlElement2(QObject *parent)
+ : QObject{parent}
+{
+
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement2.hpp b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement2.hpp
new file mode 100644
index 0000000000..c7adf50e2e
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement2.hpp
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef MYQMLELEMENT2_H
+#define MYQMLELEMENT2_H
+
+#include <QObject>
+#include <QQmlEngine>
+
+class myQmlElement2 : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ explicit myQmlElement2(QObject *parent = nullptr);
+};
+
+#endif // MYQMLELEMENT2_H
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/somecppclass.cpp b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/somecppclass.cpp
new file mode 100644
index 0000000000..a3a73413eb
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/somecppclass.cpp
@@ -0,0 +1,9 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "somecppclass.h"
+
+SomeCppClass::SomeCppClass()
+{
+
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/somecppclass.h b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/somecppclass.h
new file mode 100644
index 0000000000..06943515d8
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/somecppclass.h
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef SOMECPPCLASS_H
+#define SOMECPPCLASS_H
+
+
+class SomeCppClass
+{
+public:
+ SomeCppClass();
+};
+
+#endif // SOMECPPCLASS_H
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/anotherqmlelement.cpp b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/anotherqmlelement.cpp
new file mode 100644
index 0000000000..9a89751eb8
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/anotherqmlelement.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "anotherqmlelement.h"
+
+AnotherQmlElement::AnotherQmlElement(QObject *parent)
+ : QObject{parent}
+{
+
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/anotherqmlelement.h b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/anotherqmlelement.h
new file mode 100644
index 0000000000..1ddba88c6b
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/anotherqmlelement.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef ANOTHERQMLELEMENT_H
+#define ANOTHERQMLELEMENT_H
+
+#include <QObject>
+#include <QQmlEngine>
+
+class AnotherQmlElement : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ explicit AnotherQmlElement(QObject *parent = nullptr);
+};
+
+#endif // ANOTHERQMLELEMENT_H
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.cpp b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.cpp
new file mode 100644
index 0000000000..c448ea76f0
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "myqmlelementx.h"
+
+MyQmlElementX::MyQmlElementX(QObject *parent)
+ : QObject{parent}
+{
+
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.h b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.h
new file mode 100644
index 0000000000..f32756c7b1
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef MYQMLELEMENTX_H
+#define MYQMLELEMENTX_H
+
+#include <QObject>
+#include <QQmlEngine>
+
+class MyQmlElementX : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ explicit MyQmlElementX(QObject *parent = nullptr);
+
+signals:
+
+};
+
+#endif // MYQMLELEMENTX_H
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.txt b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.txt
diff --git a/tests/auto/qmlls/qqmlcodemodel/tst_qmlls_qqmlcodemodel.cpp b/tests/auto/qmlls/qqmlcodemodel/tst_qmlls_qqmlcodemodel.cpp
new file mode 100644
index 0000000000..a3293769e5
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/tst_qmlls_qqmlcodemodel.cpp
@@ -0,0 +1,185 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_qmlls_qqmlcodemodel.h"
+
+#include <QtQmlToolingSettings/private/qqmltoolingsettings_p.h>
+#include <QtQmlLS/private/qqmlcodemodel_p.h>
+#include <QtQmlLS/private/qqmllsutils_p.h>
+#include <QtQmlDom/private/qqmldomitem_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+
+tst_qmlls_qqmlcodemodel::tst_qmlls_qqmlcodemodel() : QQmlDataTest(QT_QQMLCODEMODEL_DATADIR) { }
+
+void tst_qmlls_qqmlcodemodel::buildPathsForFileUrl_data()
+{
+ QTest::addColumn<QString>("pathFromIniFile");
+ QTest::addColumn<QString>("pathFromEnvironmentVariable");
+ QTest::addColumn<QString>("pathFromCommandLine");
+ QTest::addColumn<QString>("expectedPath");
+
+ const QString path1 = u"/Users/helloWorld/build-myProject"_s;
+ const QString path2 = u"/Users/helloWorld/build-custom"_s;
+ const QString path3 = u"/Users/helloWorld/build-12345678"_s;
+
+ QTest::addRow("justCommandLine") << QString() << QString() << path1 << path1;
+ QTest::addRow("all3") << path1 << path2 << path3 << path3;
+
+ QTest::addRow("commandLineOverridesEnvironmentVariable")
+ << QString() << path2 << path3 << path3;
+ QTest::addRow("commandLineOverridesIniFile") << path2 << QString() << path3 << path3;
+
+ QTest::addRow("EnvironmentVariableOverridesIniFile") << path1 << path2 << QString() << path2;
+ QTest::addRow("iniFile") << path1 << QString() << QString() << path1;
+ QTest::addRow("environmentVariable") << QString() << path3 << QString() << path3;
+}
+
+void tst_qmlls_qqmlcodemodel::buildPathsForFileUrl()
+{
+ QFETCH(QString, pathFromIniFile);
+ QFETCH(QString, pathFromEnvironmentVariable);
+ QFETCH(QString, pathFromCommandLine);
+ QFETCH(QString, expectedPath);
+
+ QQmlToolingSettings settings(u"qmlls"_s);
+ if (!pathFromIniFile.isEmpty())
+ settings.addOption("buildDir", pathFromIniFile);
+
+ constexpr char environmentVariable[] = "QMLLS_BUILD_DIRS";
+ qunsetenv(environmentVariable);
+ if (!pathFromEnvironmentVariable.isEmpty()) {
+ qputenv(environmentVariable, pathFromEnvironmentVariable.toUtf8());
+ }
+
+ QmlLsp::QQmlCodeModel model(nullptr, &settings);
+ if (!pathFromCommandLine.isEmpty())
+ model.setBuildPathsForRootUrl(QByteArray(), QStringList{ pathFromCommandLine });
+
+ // use nonexistent path to avoid loading random .qmlls.ini files that might be laying around.
+ // in this case, it should abort the search and the standard value we set in the settings
+ const QByteArray nonExistentUrl =
+ QUrl::fromLocalFile(u"./___thispathdoesnotexist123___/abcdefghijklmnop"_s).toEncoded();
+
+ QStringList result = model.buildPathsForFileUrl(nonExistentUrl);
+ QCOMPARE(result.size(), 1);
+ QCOMPARE(result.front(), expectedPath);
+}
+
+void tst_qmlls_qqmlcodemodel::findFilePathsFromFileNames_data()
+{
+ QTest::addColumn<QStringList>("fileNames");
+ QTest::addColumn<QStringList>("expectedPaths");
+
+ const QString folder = testFile("sourceFolder");
+ const QString subfolder = testFile("sourceFolder/subSourceFolder/subsubSourceFolder");
+
+ QTest::addRow("notExistingFile") << QStringList{ u"notExistingFile.h"_s } << QStringList{};
+
+ QTest::addRow("myqmlelement") << QStringList{ u"myqmlelement.h"_s }
+ << QStringList{ folder + u"/myqmlelement.h"_s,
+ subfolder + u"/myqmlelement.h"_s };
+
+ QTest::addRow("myqmlelement2") << QStringList{ u"myqmlelement2.hpp"_s }
+ << QStringList{ folder + u"/myqmlelement2.hpp"_s };
+
+ QTest::addRow("anotherqmlelement") << QStringList{ u"anotherqmlelement.cpp"_s }
+ << QStringList{ subfolder + u"/anotherqmlelement.cpp"_s };
+}
+
+void tst_qmlls_qqmlcodemodel::findFilePathsFromFileNames()
+{
+ QFETCH(QStringList, fileNames);
+ QFETCH(QStringList, expectedPaths);
+
+ QmlLsp::QQmlCodeModel model;
+ model.setRootUrls({ testFileUrl(u"sourceFolder"_s).toEncoded() });
+
+ auto result = model.findFilePathsFromFileNames(fileNames);
+ // the order only is required for the QCOMPARE
+ std::sort(result.begin(), result.end());
+ std::sort(expectedPaths.begin(), expectedPaths.end());
+
+ QCOMPARE(result, expectedPaths);
+}
+
+using namespace QQmlJS::Dom;
+
+void tst_qmlls_qqmlcodemodel::fileNamesToWatch()
+{
+ DomItem qmlFile;
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+
+ auto envPtr = DomEnvironment::create(QStringList(),
+ DomEnvironment::Option::SingleThreaded
+ | DomEnvironment::Option::NoDependencies, options);
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile("MyCppModule/Main.qml")),
+ [&qmlFile](Path, const DomItem &, const DomItem &newIt) {
+ qmlFile = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+
+ const auto fileNames = QmlLsp::QQmlCodeModel::fileNamesToWatch(qmlFile);
+
+ // fileNames also contains some builtins it seems, like:
+ // QSet("qqmlcomponentattached_p.h", "qqmlcomponent.h", "qobject.h", "qqmllist.h",
+ // "helloworld.h", "qqmlengine_p.h")
+ QVERIFY(fileNames.contains(u"helloworld.h"_s));
+}
+
+QString tst_qmlls_qqmlcodemodel::readFile(const QString &filename) const
+{
+ QFile f(testFile(filename));
+ if (!f.open(QFile::ReadOnly)) {
+ QTest::qFail("Can't read test file", __FILE__, __LINE__);
+ return {};
+ }
+ return f.readAll();
+}
+
+void tst_qmlls_qqmlcodemodel::openFiles()
+{
+ QmlLsp::QQmlCodeModel model;
+
+ const QByteArray fileAUrl = testFileUrl(u"FileA.qml"_s).toEncoded();
+ const QString fileAPath = testFile(u"FileA.qml"_s);
+
+ // open file A
+ model.newOpenFile(fileAUrl, 0, readFile(u"FileA.qml"_s));
+
+ QTRY_VERIFY_WITH_TIMEOUT(model.validEnv().field(Fields::qmlFileWithPath).key(fileAPath), 3000);
+
+ {
+ const DomItem fileAComponents = model.validEnv()
+ .field(Fields::qmlFileWithPath)
+ .key(fileAPath)
+ .field(Fields::currentItem)
+ .field(Fields::components);
+ // if there is no component then the lazy qml file was not loaded correctly.
+ QCOMPARE(fileAComponents.size(), 1);
+ }
+
+ model.newDocForOpenFile(fileAUrl, 1, readFile(u"FileA2.qml"_s));
+
+ {
+ const DomItem fileAComponents = model.validEnv()
+ .field(Fields::qmlFileWithPath)
+ .key(fileAPath)
+ .field(Fields::currentItem)
+ .field(Fields::components);
+ // if there is no component then the lazy qml file was not loaded correctly.
+ QCOMPARE(fileAComponents.size(), 1);
+
+ // also check if the property is there
+ const DomItem properties = fileAComponents.key(QString())
+ .index(0)
+ .field(Fields::objects)
+ .index(0)
+ .field(Fields::propertyDefs);
+ QVERIFY(properties);
+ QVERIFY(properties.key(u"helloProperty"_s));
+ }
+}
+
+QTEST_MAIN(tst_qmlls_qqmlcodemodel)
diff --git a/tests/auto/qmlls/qqmlcodemodel/tst_qmlls_qqmlcodemodel.h b/tests/auto/qmlls/qqmlcodemodel/tst_qmlls_qqmlcodemodel.h
new file mode 100644
index 0000000000..a913f4bd19
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/tst_qmlls_qqmlcodemodel.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QMLLS_QQMLCODEMODEL_H
+#define TST_QMLLS_QQMLCODEMODEL_H
+
+#include <QtJsonRpc/private/qjsonrpcprotocol_p.h>
+#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qprocess.h>
+#include <QtCore/qlibraryinfo.h>
+
+#include <QtTest/qtest.h>
+
+#include <iostream>
+
+using namespace Qt::StringLiterals;
+
+class tst_qmlls_qqmlcodemodel : public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_qmlls_qqmlcodemodel();
+ QString readFile(const QString &filename) const;
+
+private slots:
+ void buildPathsForFileUrl_data();
+ void buildPathsForFileUrl();
+ void fileNamesToWatch();
+ void findFilePathsFromFileNames_data();
+ void findFilePathsFromFileNames();
+ void openFiles();
+};
+
+#endif // TST_QMLLS_QQMLCODEMODEL_H
diff --git a/tests/auto/qmlls/utils/CMakeLists.txt b/tests/auto/qmlls/utils/CMakeLists.txt
new file mode 100644
index 0000000000..ba81707b30
--- /dev/null
+++ b/tests/auto/qmlls/utils/CMakeLists.txt
@@ -0,0 +1,63 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlls_utils LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qmlls_utils
+ SOURCES
+ tst_qmlls_utils.cpp
+ DEFINES
+ QT_QMLLS_UTILS_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::LanguageServerPrivate
+ Qt::Test
+ Qt::QuickTestUtilsPrivate
+ Qt::QmlLSPrivate
+ TESTDATA ${test_data}
+)
+
+qt_internal_add_test(tst_qmlls_highlighting
+ SOURCES
+ tst_qmlls_highlighting.cpp
+ DEFINES
+ QT_QMLLS_HIGHLIGHTS_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::LanguageServerPrivate
+ Qt::Test
+ Qt::QuickTestUtilsPrivate
+ Qt::QmlLSPrivate
+ TESTDATA ${test_data}
+)
+
+qt_internal_add_test(tst_qmlls_documentationHints
+ SOURCES
+ tst_qmlls_documentationHints.cpp
+ DEFINES
+ QT_QMLLS_DOCUMENTATION_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::LanguageServerPrivate
+ Qt::Test
+ Qt::QuickTestUtilsPrivate
+ Qt::QmlLSPrivate
+ TESTDATA ${test_data}
+)
+
+qt_internal_extend_target(tst_qmlls_utils CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLLS_UTILS_DATADIR=":/domdata"
+)
diff --git a/tests/auto/qmlls/utils/data/BaseType.qml b/tests/auto/qmlls/utils/data/BaseType.qml
new file mode 100644
index 0000000000..168630f8ef
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/BaseType.qml
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.15
+
+Item {
+ property string inBaseTypeDotQml: "Hello from BaseType!"
+
+ component MyBaseInlineComponent: Item {
+ id: baseIC
+ }
+
+ Item {
+ id: child
+
+ Item {
+ id: nestedChild
+
+ component MyNestedInlineComponent: Item {
+ property string inMyNestedInlineComponent: "world"
+ }
+ }
+ }
+ property int helloProperty: 123
+ function helloFunction() {
+ return helloProperty
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/JSDefinitions.qml b/tests/auto/qmlls/utils/data/JSDefinitions.qml
new file mode 100644
index 0000000000..20a1d34fe2
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/JSDefinitions.qml
@@ -0,0 +1,76 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.15
+
+Item {
+ id: rootId
+
+ property int i // (1)
+ function f(a /*(2)*/ , b) {return a /* go to definition on a leads to (2) */ > b} // (4)
+
+ Component.onCompleted: {
+ let x = 42 // (3)
+ f(x, i) // goto definition on f goes to 4, on x goes to (3) and on i goes to (1)
+ f(x, rootId.i) // goto definition on f goes to 4, on x goes to (3) and on i goes to (1)
+ }
+
+ function ffff() {
+ let scoped = 42;
+ {
+ let scoped = 666;
+ f(scoped, i);
+ {
+ let a = 12345, i = 32;
+ f(scoped, i);
+ }
+ }
+ f(scoped, i);
+ }
+
+ Rectangle {
+ id: nested
+
+ property int i
+
+ function f(n: int): int {
+ let x = i, y = nested.i, z = rootId.i;
+ if (x > 3)
+ return 1 + f(f(x-1) + f(x-2) - f(x-3));
+ else
+ return f(0);
+ }
+ function fff(n: int, m: int): int {
+ return f(n + m) / 42 + ffff()
+ }
+ }
+ function abc() {
+ return nested.f(42);
+ }
+
+ component MyIC: Rectangle {
+ id: helloIC
+
+ property int data: 42
+ Item {
+ property int data: helloIC.data
+ }
+ }
+
+ property MyIC ic: MyIC {}
+ function icProperty() {
+ return ic.data
+ }
+ property int propertyInBinding: i
+ property int propertyInBinding2: i * 42
+ property int propertyInBinding3: abc()[rootId.i ** 42 - 7]
+
+ property BaseType bt: BaseType {}
+ property int helloProperty: 1234567890 // BaseType also has a property helloProperty
+ function helloFunction() {} // BaseType also has a method helloFunction
+ function fromDifferentFiles() {
+ let x = bt.helloProperty + bt.helloFunction()
+ }
+
+ property BaseType.MyBaseInlineComponent inlineCompFromDifferentFile
+}
diff --git a/tests/auto/qmlls/utils/data/JSUsages.qml b/tests/auto/qmlls/utils/data/JSUsages.qml
new file mode 100644
index 0000000000..ce0cd1d046
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/JSUsages.qml
@@ -0,0 +1,196 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function f() {
+ let sum = 0, sum2 = 0
+ for(let i = 1; i < 42; i = i + 2) {
+ sum = sum + i
+ {
+ let sum = 42; // another unrelated sum
+ }
+ }
+ }
+
+ property int helloProperty: 0
+ property int p2: 1
+
+ function withProperty() {
+ let sum = 0, sum2 = 0
+ for(let i = 1; i < 42; i = i + 2) {
+ sum = sum + i
+ helloProperty = helloProperty + sum - i * p2;
+ {
+ let helloProperty = "evil"
+ }
+ }
+ }
+ Item {
+ function f() {
+ return helloProperty + p2
+ }
+ property string helloProperty
+ }
+ component IC: Item {
+ property var helloProperty
+ function f() {
+ return helloProperty + p2
+ }
+ }
+ component NestedComponent: Item {
+ property NestedComponent2 inner: NestedComponent2 {}
+ property int p2
+ }
+ component NestedComponent2: Item {
+ property NestedComponent3 inner
+ property int p2
+ inner: NestedComponent3 {}
+ }
+ component NestedComponent3: Item {
+ property NestedComponent4 inner
+ property int p2
+ inner: NestedComponent4 {}
+
+ }
+ component NestedComponent4: Item {
+ property int helloProperty
+ property int p2
+ }
+ NestedComponent {
+ id: myNested
+ }
+ function nestedUsages() {
+ let x = myNested.inner.inner.inner.helloProperty + helloProperty;
+ let a = myNested.p2 + p2
+ let b = myNested.inner.p2 + p2
+ let c = myNested.inner.inner.p2 + p2
+ let d = myNested.inner.inner.inner.p2 + p2
+ }
+
+ function recursive(n: int): int {
+ if (n > 3)
+ return 1 + recursive(recursive(x-1) + recursive(x-2) - recursive(x-3));
+ else
+ return recursive(0);
+ }
+
+ property int helloRecursive: recursive(42)
+
+ id: rootId
+ Rectangle {
+ function f() {
+ return rootId.recursive(123)
+ }
+ }
+
+ signal helloSignal()
+
+ function callSignals() {
+ helloSignal()
+ if (false) {
+ helloSignal()
+ } else {
+ // helloSignal() // not a usage btw
+ if (true)
+ helloSignal()
+ }
+ }
+ function callSignals2() {
+ helloSignal()
+ if (false) {
+ widthChanged()
+ } else {
+ // helloSignal() // not a usage btw
+ if (true)
+ widthChanged()
+ rootId.widthChanged()
+ }
+ }
+ Item {
+ function callSignalsInChild() {
+ widthChanged()
+ rootId.widthChanged()
+ }
+ }
+
+ function myHelloHandler() { let x = 32; }
+ onHelloSignal: myHelloHandler
+
+ property int helloPropertyBinding
+ helloPropertyBinding: 123
+
+ property int checkHandlers
+ onCheckHandlersChanged: myHelloHandler
+ onChildrenChanged: myHelloHandler
+ function callChanged() {
+ checkHandlersChanged()
+ childrenChanged()
+ }
+ property int _: 48
+ property int ______42: 48
+ property int _123a: 48
+ on_Changed: myHelloHandler
+ on______42Changed: myHelloHandler
+ on_123AChanged: myHelloHandler
+ function weirdPropertynames() {
+ _Changed()
+ ______42Changed()
+ _123aChanged()
+ }
+
+ TapHandler {
+ onTapped: myHelloHandler
+ function f() {
+ tapped()
+ }
+ }
+
+ function anotherF() {
+ helloPropertyChanged()
+ }
+ onHelloPropertyChanged: myHelloHandler
+ Type {}
+ function foo(mouse) {}
+
+ MouseArea {
+ id: area1
+ onClicked: foo
+ property int insideMouseArea1
+ }
+
+ MouseArea {
+ id: area2
+ Connections {
+ function onClicked(mouse) {
+ area1.clicked()
+ area3.clicked()
+ }
+ }
+ property int insideMouseArea2
+
+ MouseArea {id: area3}
+ }
+
+ property Connections c: Connections {
+ target: area3
+ onClicked: function(mouse) {
+ //
+ }
+ }
+ function useMouseAreas() {
+ area1.clicked()
+ area2.clicked()
+ area3.clicked()
+ }
+
+ function checkParameters(a: int, b: double, {x, y={}, z=[x,y]}) {
+ return a + b + c + x + y + z
+ }
+
+ function deconstructingUsages(xxx) {
+ let {a, b} = xxx;
+ let c = a + b;
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/JSUsagesFromAnotherFile.qml b/tests/auto/qmlls/utils/data/JSUsagesFromAnotherFile.qml
new file mode 100644
index 0000000000..eb0cbf70be
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/JSUsagesFromAnotherFile.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+JSUsages {
+ function f() {
+ // sanity check: reuse variable names from function f in JSUsages. Those should not appear
+ // as usages of the sum of JSUsages.
+ let sum = 0;
+ sum += 1;
+ sum += helloProperty + 32 // valid usage of JSUsages's helloProperty
+ return sum
+ }
+
+}
diff --git a/tests/auto/qmlls/utils/data/Type.qml b/tests/auto/qmlls/utils/data/Type.qml
new file mode 100644
index 0000000000..2d0a32cfb4
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/Type.qml
@@ -0,0 +1,30 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.15
+
+BaseType {
+ id: derived
+ property int inTypeDotQml
+
+ component MyInlineComponent: BaseType {
+ id: derivedInIC
+ property int inMyInlineComponent
+ }
+
+ property BaseType inlineType: BaseType {
+ id: derivedInline
+ }
+
+ property MyInlineComponent icType: MyInlineComponent {
+ id:derivedInIcInline
+ }
+
+ property var icType2: BaseType.MyBaseInlineComponent {
+ id:derivedInIcInline2
+ }
+
+ property var nestedIcType: BaseType.MyNestedInlineComponent {
+ id:derivedInIcInline3
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/Yyy.qml b/tests/auto/qmlls/utils/data/Yyy.qml
new file mode 100644
index 0000000000..6c3886c0a4
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/Yyy.qml
@@ -0,0 +1,143 @@
+import QtQuick 2.0
+import QtQuick as QQ
+import QtCore
+Zzz {
+ id: root
+ width: height
+ Rectangle {
+ color: "green"
+ anchors.fill: parent
+ width: root.height
+ height: root.foo.height
+
+ }
+ component MyRectangle: Rectangle {}
+ function lala() {}
+ property Rectangle foo: Rectangle{ height: 200 }
+ function longfunction(a, b, c = "c", d = "d"): string {
+ return "hehe: " + c + d
+ }
+
+ // documentedFunction: is documented
+ // returns 'Good'
+ function documentedFunction(arg1, arg2 = "Qt"): string {
+ return "Good"
+ }
+ QQ.Rectangle {
+ color:"red"
+ }
+
+ Item {
+ id: someItem
+ property int helloProperty
+ }
+
+ function parameterCompletion(helloWorld, helloMe: int) {
+ let helloVar = 42;
+ let result = someItem.helloProperty + helloWorld;
+ return result;
+ }
+
+ component Base: QtObject {
+ property int propertyInBase
+ function functionInBase(jsParameterInBase) {
+ let jsIdentifierInBase;
+ return jsIdentifierInBase;
+ }
+ }
+
+ Base {
+ property int propertyInDerived
+ function functionInDerived(jsParameterInDerived) {
+ let jsIdentifierInDerived;
+ return jsIdentifierInDerived;
+ }
+
+ property Base child: Base {
+ property int propertyInChild
+ function functionInChild(jsParameterInChild) {
+ let jsIdentifierInChild;
+ return someItem.helloProperty;
+ }
+ }
+ }
+ function test1() {
+ {
+ var helloVarVariable = 42;
+ }
+ // this is fine, var has no block scope
+ console.log(helloVarVariable);
+ }
+ function test2() {
+ {
+ let helloLetVariable = 42;
+ }
+ // this is not fine, let variables have block scope
+ console.log(helloLetVariable);
+ }
+ property var testSingleton: SystemInformation.byteOrder
+
+
+
+ enum Hello { World }
+ enum MyEnum { ValueOne, ValueTwo }
+
+
+ property var testEnums: Yyy.World
+ property var testEnums2: Yyy.Hello.World
+
+ Component.onCompleted: {}
+ property var anything: Rectangle{ height: 200 }
+ function createRectangle(): Rectangle {}
+ function createItem(): Item {}
+ function createAnything() {}
+ function helloJSStatements() {
+ let x = 3;
+ }
+ required property int requiredProperty
+ readonly property int readonlyProperty: 456
+ default property int defaultProperty
+ property int builtin: Math.abs(43)
+ signal handleMe()
+ function helloForStatement() {
+ for(let i = 0; i < 5; ++i) {
+
+ }
+ for(let j = 0; j < 5; ++j)
+ helloForStatement()
+ }
+ function helloIfStatement(hello) {
+ if (hello)
+ hello = !hello
+ else
+ hello = hello
+ if (hello == !hello) {
+ hello = hello / hello
+ } else {
+ hello += hello
+ }
+ if (hello)
+ hello = hello
+ else if (hello)
+ hello = hello + hello / 2
+ }
+ function helloReturnStatement(hello) {
+ return hello
+ }
+ function helloWhileStatement(hello) {
+ while (hello) --hello
+ }
+ function helloDoWhileStatement(hello) {
+ do --hello; while (hello);
+ }
+ function helloForEachStatement(hello) {
+ for(variable in hello) ++hello;
+ for(element of hello) ++hello;
+ }
+ function qualifiedScriptIdentifiers() {
+ console.l()
+ }
+ QtObject {
+
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/Zzz.qml b/tests/auto/qmlls/utils/data/Zzz.qml
new file mode 100644
index 0000000000..fa0edf69dc
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/Zzz.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.11
+
+Item {
+ id: zzz
+ height: 333
+
+ Rectangle {
+ width: zzz.height
+ }
+
+ property int propertyOfZZZ
+}
diff --git a/tests/auto/qmlls/utils/data/completions/afterDots.qml b/tests/auto/qmlls/utils/data/completions/afterDots.qml
new file mode 100644
index 0000000000..0659425195
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/afterDots.qml
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+QtObject {
+ id: root
+ property int good
+ property var i: Item {
+ property int bad
+ property int myP: root.
+ Item { }
+ property int myP2: root.;
+ bad: 43
+ function f() {
+ root.;
+ for (;;) {}
+ }
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/attachedAndGroupedProperty.qml b/tests/auto/qmlls/utils/data/completions/attachedAndGroupedProperty.qml
new file mode 100644
index 0000000000..5736938d73
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/attachedAndGroupedProperty.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property int bad
+ Component.a: {}
+ Text {
+ font.f: ""
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/attachedPropertyMissingRHS.qml b/tests/auto/qmlls/utils/data/completions/attachedPropertyMissingRHS.qml
new file mode 100644
index 0000000000..38aca7ef7a
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/attachedPropertyMissingRHS.qml
@@ -0,0 +1,8 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ Component.on
+}
diff --git a/tests/auto/qmlls/utils/data/completions/boundComponents.qml b/tests/auto/qmlls/utils/data/completions/boundComponents.qml
new file mode 100644
index 0000000000..343e7a3c7b
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/boundComponents.qml
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: rootId
+ property int inRoot
+
+ DelegateModel {
+ delegate: Item {
+ id: childId
+
+ property int myInt: rootId.inRoot
+ property int inChild
+
+ }
+ }
+
+ property int myInt: childId.inChild
+}
diff --git a/tests/auto/qmlls/utils/data/completions/commaExpression.qml b/tests/auto/qmlls/utils/data/completions/commaExpression.qml
new file mode 100644
index 0000000000..de74d07c1d
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/commaExpression.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Item {
+ function f(a,b,c) {
+ f(a,a,a), b += 55,c *= 24;
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/conditionalExpression.qml b/tests/auto/qmlls/utils/data/completions/conditionalExpression.qml
new file mode 100644
index 0000000000..933c71d26c
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/conditionalExpression.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Item {
+ function f(a,b,c) {
+ a == b ? b == c ? c : b + 3 : 42
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/continueAndBreakStatement.qml b/tests/auto/qmlls/utils/data/completions/continueAndBreakStatement.qml
new file mode 100644
index 0000000000..243f206db9
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/continueAndBreakStatement.qml
@@ -0,0 +1,31 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function f(x) {
+ label1: f(f(x))
+
+ nestedLabel1: for (let i = 0; i < 3; ++i) {
+ nestedLabel2: for (let j = 0; j < 3; ++j) {
+ continue nestedLabel1;
+ break nestedLabel2;
+ }
+ }
+
+ multiLabel1:
+ multiLabel2: {
+ f(1 + f(x))
+ continue multiLabel1
+ break multiLabel2
+ }
+
+ for(;;) {
+ continue ;
+ break ;
+ }
+
+ return x + y
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/functionBody.qml b/tests/auto/qmlls/utils/data/completions/functionBody.qml
new file mode 100644
index 0000000000..755f136d7d
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/functionBody.qml
@@ -0,0 +1,13 @@
+import QtQuick
+
+Item {
+ function f(x) {
+
+ }
+
+ property int badProperty
+ component IC: Item { property int helloProperty }
+ function g(x: IC) {
+ x.helloProperty
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/groupedPropertyMissingRHS.qml b/tests/auto/qmlls/utils/data/completions/groupedPropertyMissingRHS.qml
new file mode 100644
index 0000000000..44fbd022ba
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/groupedPropertyMissingRHS.qml
@@ -0,0 +1,8 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Text {
+ font.fa
+}
diff --git a/tests/auto/qmlls/utils/data/completions/labelledStatement.qml b/tests/auto/qmlls/utils/data/completions/labelledStatement.qml
new file mode 100644
index 0000000000..e223d6465b
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/labelledStatement.qml
@@ -0,0 +1,20 @@
+import QtQuick
+
+Item {
+ function f(x) {
+ label1: f(f(x))
+
+ nestedLabel1: for (let i = 0; i < 3; ++i) {
+ nestedLabel2: for (let j = 0; j < 3; ++j) {
+ if (i === 1 && j === 1) {
+ continue nestedLabel1;
+ }
+ }
+ }
+
+ multilabel1:
+ multilabel2: f(1 + f(x))
+
+ return x + y
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/missingRHS.parserfail.qml b/tests/auto/qmlls/utils/data/completions/missingRHS.parserfail.qml
new file mode 100644
index 0000000000..ce594d9f16
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/missingRHS.parserfail.qml
@@ -0,0 +1,9 @@
+import QtQuick
+
+Item {
+ function f() {
+ let x = root.
+ let y = root.
+ for(;;) {}
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/missingRHS.qml b/tests/auto/qmlls/utils/data/completions/missingRHS.qml
new file mode 100644
index 0000000000..1423f5c17e
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/missingRHS.qml
@@ -0,0 +1,19 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+ import QtQuick
+
+ Item {
+ id: root
+ property int good
+ Item {
+ property int bad
+ function f() {
+ return root.
+ }
+ property int boom: root.
+ Item {
+ property int helloSubItem
+ }
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/parenthesizedExpression.qml b/tests/auto/qmlls/utils/data/completions/parenthesizedExpression.qml
new file mode 100644
index 0000000000..567b543154
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/parenthesizedExpression.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function f(x) {
+ (x + 1)
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml b/tests/auto/qmlls/utils/data/completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml
new file mode 100644
index 0000000000..50bc0976c8
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Templates as T
+
+Item {
+ T.Button {}
+ function f(x: T.Button) {}
+}
diff --git a/tests/auto/qmlls/utils/data/completions/returnStatement.qml b/tests/auto/qmlls/utils/data/completions/returnStatement.qml
new file mode 100644
index 0000000000..e5203652b7
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/returnStatement.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function f(x) {
+ return
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/suggestContinueAndBreak.qml b/tests/auto/qmlls/utils/data/completions/suggestContinueAndBreak.qml
new file mode 100644
index 0000000000..89855e1584
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/suggestContinueAndBreak.qml
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function f(x) {
+ // sanity check: no break or continue allowed in function body
+
+ for(let i = 0; i < 5; ++i) {
+ // break and continue allowed in loop
+ }
+
+ switch(x) {
+ // no break allowed here
+ case 3:
+ // break allowed in case
+ default:
+ // break allowed in default
+ case f("helloWorld"):
+ // break allowed in moreCase
+ }
+
+ helloLabel: {
+ // break allowed in labelledstatement
+ }
+
+ // combinations:
+ combiLabel: {
+ // break allowed in labelledstatement
+ for(let i = 0; i < 5; ++i) {
+ // break and continue allowed in loop
+
+ switch(x) {
+ default:
+ // break allowed in default + continue for loop
+ }
+ }
+
+ switch(x) {
+ case 3:
+ default:
+ case f("helloWorld"):
+ // no continue allowed here
+
+ for(let i = 0; i < 5; ++i) {
+ // break and continue allowed in loop
+ }
+ }
+ }
+
+ }
+
+}
diff --git a/tests/auto/qmlls/utils/data/completions/switchStatements.qml b/tests/auto/qmlls/utils/data/completions/switchStatements.qml
new file mode 100644
index 0000000000..f9ddf3f2a1
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/switchStatements.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property int myProperty;
+ function g(x) { return x + 1; }
+ function f(x) {
+ switch(x) {
+
+ case 1:
+ return 0;
+ case g(x) + 3: {
+ return 1;
+ }
+ }
+
+ switch(x) {
+ case 42:
+ myProperty = x + f(x)
+ myProperty = myProperty * 0.33
+ default:
+ return 123456
+ case 666:
+ for(;;) {
+ g(x)
+ }
+ myProperty = "hello"
+ }
+ switch(x) {
+ default:
+ break;
+ }
+ }
+
+}
diff --git a/tests/auto/qmlls/utils/data/completions/throwStatement.qml b/tests/auto/qmlls/utils/data/completions/throwStatement.qml
new file mode 100644
index 0000000000..927c885f54
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/throwStatement.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function f(x) {
+ throw 1;
+ throw { x: "myError" };
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/tryStatements.qml b/tests/auto/qmlls/utils/data/completions/tryStatements.qml
new file mode 100644
index 0000000000..5fb261a388
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/tryStatements.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Item {
+ function f() {
+ try { } catch(x) { } finally { }
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/unaryExpression.qml b/tests/auto/qmlls/utils/data/completions/unaryExpression.qml
new file mode 100644
index 0000000000..f2ba099a7a
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/unaryExpression.qml
@@ -0,0 +1,17 @@
+import QtQuick
+
+Item {
+ function f(x) {
+ -x;
+ +x;
+ ~x;
+ !x;
+ typeof x;
+ delete x;
+ void x;
+ x--;
+ x++;
+ --x;
+ ++x;
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/variableDeclaration.qml b/tests/auto/qmlls/utils/data/completions/variableDeclaration.qml
new file mode 100644
index 0000000000..2a32c2ff44
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/variableDeclaration.qml
@@ -0,0 +1,36 @@
+import QtQuick
+
+Item {
+ function data() { return 42; }
+
+ function f(x) {
+ let letStatement = data();
+ const constStatement = data();
+ var varStatement = data(); // bad?
+ }
+
+ function objects(x) {
+ let { deconstructed } = data();
+ let { deconstructedAloneWithInitializer = 55 } = data();
+ let { deconstructedWithInitializer1 = 55, deconstructedWithInitializer2 = 66 } = data(), { unused: deconstructedWithInitializer3 = 77, } = data() ;
+ }
+
+ function arrays(x) {
+ let [ deconstructed ] = data();
+ let [ deconstructedAloneWithInitializer = 55 ] = data();
+ let [ deconstructedWithInitializer1 = 55, deconstructedWithInitializer2 = 66 ] = data(), [ deconstructedWithInitializer3 = 77, ] = data() ;
+ }
+
+ function oneArrayingToRuleThemAll(x) {
+ let [ head, [headOfSecond = 44, secondOfSecond = 55], [_ = "useless", secondOfThird = g()], [ [ headOfHeadOfFourth = g() + 1] ] ] = data();
+ }
+
+ function needleInTheHarraystack(x) {
+ let [ head, [headOfSecond = 44, secondOfSecond = 55], [_ = "useless", secondOfThird = g(), { needle = "x" }], [ [ headOfHeadOfFourth = g() + 1] ] ] = data();
+ }
+
+ function arrayInTheObject(x) {
+ let { p: [first, second] } = { p: [1,2] };
+ }
+
+}
diff --git a/tests/auto/qmlls/utils/data/emptyFile.qml b/tests/auto/qmlls/utils/data/emptyFile.qml
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/emptyFile.qml
diff --git a/tests/auto/qmlls/utils/data/file1.qml b/tests/auto/qmlls/utils/data/file1.qml
new file mode 100644
index 0000000000..c069a62a43
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/file1.qml
@@ -0,0 +1,55 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.15
+
+Item {
+ component C: Item {}
+
+ property int a
+ property bool b
+ property C c
+ property var d
+ property list<int> e
+
+ component D: Item { id: icid }
+ C {id: firstC }D{id: firstD }
+ C { id: secondC } D{ id: secondD}
+ C {
+ C{}
+ C{
+ C {}
+ C {}
+ C {}
+ }
+ C{}
+ }
+
+ component IC: Item { property C myC }
+
+ a: 43
+ d: 123 + 7
+
+ function f(a: int, b: Item, c: C) : C {
+ return c;
+ }
+ function lala() {}
+
+ Rectangle {
+ color: "green"
+ anchors.fill: parent
+ width: root.height
+ height: root.foo.height
+ }
+ property Rectangle foo: Rectangle{ height: 200 }
+
+ c: C{}
+ property C ccc: c
+ property C cccc
+ cccc: c
+
+ function fff(a: int, b: Item, c: C) : C {
+ let x = c.children;
+ return x;
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/attachedPropertyUsage/attachedPropertyUsage.qml b/tests/auto/qmlls/utils/data/findUsages/attachedPropertyUsage/attachedPropertyUsage.qml
new file mode 100644
index 0000000000..897d153e84
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/attachedPropertyUsage/attachedPropertyUsage.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: root
+
+ Keys.onPressed: {}
+
+ MouseArea {
+ onClicked: root.Keys.enabled = false
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/binding/binding.qml b/tests/auto/qmlls/utils/data/findUsages/binding/binding.qml
new file mode 100644
index 0000000000..c0a06dd6fd
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/binding/binding.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Text {
+ id: textRoot
+
+ property int helloPropertyBinding
+ helloPropertyBinding: 123
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/bindings/bindings.qml b/tests/auto/qmlls/utils/data/findUsages/bindings/bindings.qml
new file mode 100644
index 0000000000..f4d4c74aff
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/bindings/bindings.qml
@@ -0,0 +1,39 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+
+Item {
+ id: root
+ property bool patronChanged // shouldn't be found
+
+ Item {
+ id: inner
+ property bool patronChanged
+
+ Binding on patronChanged {
+ value: !inner.patronChanged
+ when: root.patronChanged // // shouldn't be found
+ }
+
+ Binding {
+ target: inner
+ property: "patronChanged"
+ value: !inner.patronChanged
+ when: root.patronChanged // shouldn't be found
+ }
+
+ // generalized dot
+ Binding {
+ inner.patronChanged: !inner.patronChanged
+ when: root.patronChanged // // shouldn't be found
+ }
+
+ // generalized block
+ Binding {
+ inner {
+ patronChanged: false
+ }
+ when: root.patronChanged // // shouldn't be found
+ }
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/connections/connections.qml b/tests/auto/qmlls/utils/data/findUsages/connections/connections.qml
new file mode 100644
index 0000000000..c933faca09
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/connections/connections.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+Item {
+
+ MouseArea {
+ id: area1
+ onClicked: foo
+ property int insideMouseArea1
+ }
+
+ MouseArea {
+ id: area2
+ Connections {
+ function onClicked(mouse) {
+ area1.clicked()
+ area3.clicked()
+ }
+ }
+ property int insideMouseArea2
+
+ MouseArea {id: area3}
+ }
+
+ property Connections c: Connections {
+ target: area3
+ onClicked: function(mouse) {
+ //
+ }
+ }
+ function useMouseAreas() {
+ area1.clicked()
+ area2.clicked()
+ area3.clicked()
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/enums/Enums.qml b/tests/auto/qmlls/utils/data/findUsages/enums/Enums.qml
new file mode 100644
index 0000000000..a60bb38053
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/enums/Enums.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+
+ enum Cats {
+ Patron = 8,
+ Mafik = 7,
+ Kivrik = 2
+ }
+
+ property var inner: QtObject {
+ enum Cats {
+ Patron = -8, // Shouldn't be found
+ Mafik = -7,
+ Kivrik = -2
+ }
+ }
+
+ property int main: Enums.Cats.Patron
+ property int innerVal: Enums.Patron
+ property int illegal1: Cats.Patron // Shouldn't be found
+ property int illegal2: Patron // Shouldn't be found
+ property int alien: EnumsFromAnotherFile.FromAnotherUniverse
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/enums/EnumsFromAnotherFile.qml b/tests/auto/qmlls/utils/data/findUsages/enums/EnumsFromAnotherFile.qml
new file mode 100644
index 0000000000..9f5bf491fd
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/enums/EnumsFromAnotherFile.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.15
+
+Item {
+ enum FromAnotherFile { FromAnotherWorld, FromAnotherDimension, FromAnotherUniverse }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/groupPropertyUsage/fontFamilyUsage.qml b/tests/auto/qmlls/utils/data/findUsages/groupPropertyUsage/fontFamilyUsage.qml
new file mode 100644
index 0000000000..61880d13ff
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/groupPropertyUsage/fontFamilyUsage.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ property font myFont
+ property var family: myFont.family
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/groupPropertyUsage/groupPropertyUsage.qml b/tests/auto/qmlls/utils/data/findUsages/groupPropertyUsage/groupPropertyUsage.qml
new file mode 100644
index 0000000000..167ccbb30e
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/groupPropertyUsage/groupPropertyUsage.qml
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Text {
+ id: textRoot
+
+ Test {
+ id: test
+ myText {
+ font {
+ pixelSize: 12
+ family: "serif"
+ }
+ }
+ }
+
+ component Test : Text{
+ property Text myText
+ }
+
+ font.family: test.myText.font.family
+ font {
+ pixelSize: 12
+ }
+
+ Item {
+ property var family // should not be in groupPropertyUsages1
+ property int font // should not be in groupPropertyUsages2
+
+ property var realFont: textRoot.font // should be in groupPropertyUsages2
+ property var realFamily: textRoot.font.family // should be in groupPropertyUsages1
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/idUsages/idUsages.qml b/tests/auto/qmlls/utils/data/findUsages/idUsages/idUsages.qml
new file mode 100644
index 0000000000..14506b2202
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/idUsages/idUsages.qml
@@ -0,0 +1,19 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: rootId
+ Rectangle {
+ function f() {
+ if (widthChanged())
+ rootId.widthChanged();
+ return rootId.x
+ }
+ }
+
+ function t() {
+ rootId.widthChanged();
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/inlineComponents/InlineComponentProvider.qml b/tests/auto/qmlls/utils/data/findUsages/inlineComponents/InlineComponentProvider.qml
new file mode 100644
index 0000000000..bfe8d99020
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/inlineComponents/InlineComponentProvider.qml
@@ -0,0 +1,23 @@
+import QtQuick
+
+Item {
+ component IC1: Item { property int inIc1: 123 }
+ component IC2: Item { property IC1 inIc2 }
+
+ IC1 {
+ id: firstUsage
+ property int inFirstUsage
+ }
+ IC2 {
+ id: secondUsage
+ property int inSecondUsage
+ }
+ Item {
+ Item {
+ IC1 {
+ id: thirdUsage
+ property int inThirdUsage
+ }
+ }
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/inlineComponents/inlineComponents.qml b/tests/auto/qmlls/utils/data/findUsages/inlineComponents/inlineComponents.qml
new file mode 100644
index 0000000000..6631dc3448
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/inlineComponents/inlineComponents.qml
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+
+ component Patron: QtObject {
+ property int foo
+ Component.onCompleted: console.log(foo)
+ }
+
+ component Mafik: Patron {
+ property int bar: foo
+ }
+
+ property int foo // should not be in inlineUsages
+ property var realFoo: Mafik {
+ function f() {
+ return foo; // should be in inlineUsages
+ }
+ }
+
+ property InlineComponentProvider fromAnotherFile: InlineComponentProvider {}
+ property InlineComponentProvider.IC1 fromAnotherFile2: InlineComponentProvider.IC1 {}
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/inlineComponents/inlineComponents2.qml b/tests/auto/qmlls/utils/data/findUsages/inlineComponents/inlineComponents2.qml
new file mode 100644
index 0000000000..49430d4b7d
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/inlineComponents/inlineComponents2.qml
@@ -0,0 +1,9 @@
+import QtQuick
+
+Item {
+ component MyIC: Item {}
+ MyIC { MyIC{} MyIC {}}
+ function f(x: MyIC): MyIC {
+ return x;
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/jsIdentifier/jsIdentifier.qml b/tests/auto/qmlls/utils/data/findUsages/jsIdentifier/jsIdentifier.qml
new file mode 100644
index 0000000000..9d844eab3b
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/jsIdentifier/jsIdentifier.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ function f() {
+ let sum = 0, sum2 = 0
+ for(let i = 1; i < 42; i = i + 2) {
+ sum = sum + i
+ {
+ let sum = 42; // another unrelated sum
+ }
+ }
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/parametersAndDeconstruction/parametersAndDeconstruction.qml b/tests/auto/qmlls/utils/data/findUsages/parametersAndDeconstruction/parametersAndDeconstruction.qml
new file mode 100644
index 0000000000..566b8648bb
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/parametersAndDeconstruction/parametersAndDeconstruction.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+
+ function checkParameters(a: int, b: double, {x, y={}, z=[x,y]}) {
+ return a + b + c + x + y + z
+ }
+
+ function deconstructingUsages(xxx) {
+ let {a, b} = xxx;
+ let c = a + b;
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/property/PropertyFromAnotherFile.qml b/tests/auto/qmlls/utils/data/findUsages/property/PropertyFromAnotherFile.qml
new file mode 100644
index 0000000000..b9197def63
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/property/PropertyFromAnotherFile.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property int helloProperty
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/property/property.qml b/tests/auto/qmlls/utils/data/findUsages/property/property.qml
new file mode 100644
index 0000000000..25280c31f1
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/property/property.qml
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property int p2: 1
+ property int helloProperty: 0
+ function withProperty() {
+ let sum = 0, sum2 = 0
+ for(let i = 1; i < 42; i = i + 2) {
+ sum = sum + i
+ helloProperty = helloProperty + sum - i * p2;
+ {
+ let helloProperty = "evil"
+ }
+ }
+ }
+ function signalEmitter() {
+ helloProperty = 23;
+ helloPropertyChanged()
+ }
+ onHelloPropertyChanged: {}
+
+ // inline component
+ component IC: Item {
+ property var helloProperty
+ function f() {
+ return helloProperty + p2
+ }
+ }
+
+ //sub item
+ Item {
+ function f() {
+ return helloProperty + p2
+ }
+ property string helloProperty
+ }
+
+ PropertyFromAnotherFile {
+ helloProperty: 42
+ function f() {
+ return helloProperty + 53;
+ }
+ onHelloPropertyChanged: f()
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/propertyChanges/propertyChanges.qml b/tests/auto/qmlls/utils/data/findUsages/propertyChanges/propertyChanges.qml
new file mode 100644
index 0000000000..255f1c7ede
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/propertyChanges/propertyChanges.qml
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+Item {
+ MouseArea {
+ id: mouse11
+ onClicked: doSomething()
+ property rect r
+ }
+
+ states: [
+ State {
+ PropertyChanges {
+ mouse11 { // block notation
+ onClicked: doSomethingElse()
+ r : 34
+ }
+ mouse11.onClicked: doSomething(); // dot notation
+ }
+
+ // with target property
+ PropertyChanges {
+ target: mouse11
+ onClicked: doSomethingElse()
+ r: 45
+ }
+ }
+ ]
+ function doSomething() {}
+ function doSomethingElse() {}
+
+ property rect r // shouldn't be found
+ property int clicked // shouldn't be gound
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile.qml b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile.qml
new file mode 100644
index 0000000000..c11802ffc2
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ property NestedComponentInFile2 inner
+ property int p2
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile2.qml b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile2.qml
new file mode 100644
index 0000000000..ece2913758
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile2.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ property NestedComponentInFile3 inner
+ property int p2
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile3.qml b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile3.qml
new file mode 100644
index 0000000000..4cddcb5cfe
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile3.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ property NestedComponentInFile4 inner
+ property int p2
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile4.qml b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile4.qml
new file mode 100644
index 0000000000..2dee832ff8
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile4.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ property int helloProperty
+ property int p2
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/propertyInNested/propertyInNested.qml b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/propertyInNested.qml
new file mode 100644
index 0000000000..0eb493886a
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/propertyInNested.qml
@@ -0,0 +1,56 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property int p2: 1
+
+ component NestedComponent: Item {
+ property NestedComponent2 inner: NestedComponent2 {}
+ property int p2
+ }
+ component NestedComponent2: Item {
+ property NestedComponent3 inner
+ property int p2
+ inner: NestedComponent3 {}
+ }
+ component NestedComponent3: Item {
+ property NestedComponent4 inner
+ property int p2
+ inner: NestedComponent4 {}
+
+ }
+ component NestedComponent4: Item {
+ property int helloProperty
+ property int p2
+ }
+ NestedComponent {
+ id: myNested
+ }
+ function nestedUsages() {
+ let x = myNested.inner.inner.inner.helloProperty;
+ let a = myNested.p2 + p2
+ let b = myNested.inner.p2 + p2
+ let c = myNested.inner.inner.p2 + p2
+ let d = myNested.inner.inner.inner.p2 + p2
+ }
+
+ function f() {
+ {
+ let _p2 = 34;
+ return _p2 + p2
+ }
+ }
+
+ NestedComponentInFile {
+ id: myNestedInFile
+ }
+ function nestedUsagesInFile() {
+ let x = myNestedInFile.inner.inner.inner.helloProperty;
+ let a = myNestedInFile.p2
+ let b = myNestedInFile.inner.p2
+ let c = myNestedInFile.inner.inner.p2
+ let d = myNestedInFile.inner.inner.inner.p2
+ }
+} \ No newline at end of file
diff --git a/tests/auto/qmlls/utils/data/findUsages/recursive/RecursiveInOtherFile.qml b/tests/auto/qmlls/utils/data/findUsages/recursive/RecursiveInOtherFile.qml
new file mode 100644
index 0000000000..8a7f2d04ff
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/recursive/RecursiveInOtherFile.qml
@@ -0,0 +1,10 @@
+import QtQuick
+
+Item {
+ function recursive(n: int): int {
+ if (n > 3)
+ return 1 + recursive(recursive(x-1) + recursive(x-2) - recursive(x-3));
+ else
+ return recursive(0);
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/recursive/recursive.qml b/tests/auto/qmlls/utils/data/findUsages/recursive/recursive.qml
new file mode 100644
index 0000000000..45939bc89a
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/recursive/recursive.qml
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: rootId
+ function recursive(n: int): int {
+ if (n > 3)
+ return 1 + recursive(recursive(x-1) + recursive(x-2) - recursive(x-3));
+ else
+ return recursive(0);
+ }
+
+ property int helloRecursive: recursive(42)
+
+ Rectangle {
+ function f() {
+ return rootId.recursive(123)
+ }
+ }
+
+ RecursiveInOtherFile {
+ id: fromOtherFile
+ }
+
+ property int helloRecursiveFromOtherFile: fromOtherFile.recursive(42)
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/signalAndHandlers2.qml b/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/signalAndHandlers2.qml
new file mode 100644
index 0000000000..d10768dd42
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/signalAndHandlers2.qml
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function myHelloHandler() { let x = 32; }
+ onHelloSignal: myHelloHandler
+
+ property int helloPropertyBinding
+ helloPropertyBinding: 123
+
+ property int checkHandlers
+ onCheckHandlersChanged: myHelloHandler
+ onChildrenChanged: myHelloHandler
+ function callChanged() {
+ checkHandlersChanged()
+ childrenChanged()
+ }
+ property int _: 48
+ property int ______42: 48
+ property int _123a: 48
+ on_Changed: myHelloHandler
+ on______42Changed: myHelloHandler
+ on_123AChanged: myHelloHandler
+ function weirdPropertynames() {
+ _Changed()
+ ______42Changed()
+ _123aChanged()
+ }
+
+ TapHandler {
+ onTapped: myHelloHandler
+ function f() {
+ tapped()
+ }
+ }
+
+ function anotherF() {
+ helloPropertyChanged()
+ }
+ onHelloPropertyChanged: myHelloHandler
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/signalsAndHandlers.qml b/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/signalsAndHandlers.qml
new file mode 100644
index 0000000000..47280bca46
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/signalsAndHandlers.qml
@@ -0,0 +1,40 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: rootId
+ signal helloSignal()
+
+ function callSignals() {
+ helloSignal()
+ if (false) {
+ helloSignal()
+ } else {
+ // helloSignal() // not a usage btw
+ if (true)
+ helloSignal()
+ }
+ }
+ function callSignals2() {
+ helloSignal()
+ if (false) {
+ widthChanged()
+ } else {
+ // helloSignal() // not a usage btw
+ if (true)
+ widthChanged()
+ rootId.widthChanged()
+ }
+ }
+ Item {
+ function callSignalsInChild() {
+ widthChanged()
+ rootId.widthChanged()
+ }
+ }
+
+ function myHelloHandler() { let x = 32; }
+ onHelloSignal: myHelloHandler
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/widthChangedInAnotherFile.qml b/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/widthChangedInAnotherFile.qml
new file mode 100644
index 0000000000..aecee2437a
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/widthChangedInAnotherFile.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ function f() {}
+ onWidthChanged: f
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/Identifiers.qml b/tests/auto/qmlls/utils/data/highlights/Identifiers.qml
new file mode 100644
index 0000000000..7725b6d5e4
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/Identifiers.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ readonly property int test: 34
+ signal pressed()
+ function f() {
+ let sum = 0, sum2 = 0
+ for(let i = 1; i < 42; i = i + 2) {
+ sum = test + i
+ {
+ let sum = 42; // another unrelated sum
+ }
+ }
+ // signal and property changed
+ testChanged();
+ pressed();
+ }
+
+ // attached
+ Keys.onPressed: {
+ }
+
+ // propertychanged handler
+ onTestChanged: {
+ f(); // method identifier
+ }
+
+ // signal handler
+ onPressed: {}
+
+ enum K { Plus}
+ property int tt: Identifiers.Plus // component and enum value
+
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/bindings.qml b/tests/auto/qmlls/utils/data/highlights/bindings.qml
new file mode 100644
index 0000000000..ac1592e778
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/bindings.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property int x: 45
+
+ Behavior on width {}
+
+ x: width
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/comments.qml b/tests/auto/qmlls/utils/data/highlights/comments.qml
new file mode 100644
index 0000000000..351aaee36c
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/comments.qml
@@ -0,0 +1,19 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+Item {
+/*
+ multiline comment
+*/
+
+/* single line comment */
+// another
+
+ function inc() {
+ // in
+
+ /*
+ inside js
+ */
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/enums.qml b/tests/auto/qmlls/utils/data/highlights/enums.qml
new file mode 100644
index 0000000000..22183bf37f
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/enums.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ enum Osc {
+ Sin,
+ Saw = 1
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/identifiers.qml b/tests/auto/qmlls/utils/data/highlights/identifiers.qml
new file mode 100644
index 0000000000..7725b6d5e4
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/identifiers.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ readonly property int test: 34
+ signal pressed()
+ function f() {
+ let sum = 0, sum2 = 0
+ for(let i = 1; i < 42; i = i + 2) {
+ sum = test + i
+ {
+ let sum = 42; // another unrelated sum
+ }
+ }
+ // signal and property changed
+ testChanged();
+ pressed();
+ }
+
+ // attached
+ Keys.onPressed: {
+ }
+
+ // propertychanged handler
+ onTestChanged: {
+ f(); // method identifier
+ }
+
+ // signal handler
+ onPressed: {}
+
+ enum K { Plus}
+ property int tt: Identifiers.Plus // component and enum value
+
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/imports.qml b/tests/auto/qmlls/utils/data/highlights/imports.qml
new file mode 100644
index 0000000000..1e69077070
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/imports.qml
@@ -0,0 +1,9 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQml 2.15
+import "X" as Patron
+
+Item {
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/literals.qml b/tests/auto/qmlls/utils/data/highlights/literals.qml
new file mode 100644
index 0000000000..520ed5d2ef
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/literals.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property int a: 123
+ property string b: "single"
+ property string c: "multi
+ line string";
+ property bool d: true
+ property var e: null
+
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/methodAndSignal.qml b/tests/auto/qmlls/utils/data/highlights/methodAndSignal.qml
new file mode 100644
index 0000000000..4e8319f049
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/methodAndSignal.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ signal p()
+ signal q(int a)
+ signal r(a: int)
+ function a(b: int) : int {}
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/objectAndComponent.qml b/tests/auto/qmlls/utils/data/highlights/objectAndComponent.qml
new file mode 100644
index 0000000000..9165e4b1b5
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/objectAndComponent.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ component Patron: Item {}
+ Item {
+ id: inner
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/pragmas.qml b/tests/auto/qmlls/utils/data/highlights/pragmas.qml
new file mode 100644
index 0000000000..cf99c93584
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/pragmas.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Singleton
+pragma FunctionSignatureBehavior: Enforced
+pragma ValueTypeBehavior: Copy,Addressable
+
+import QtQml
+
+QtObject {}
diff --git a/tests/auto/qmlls/utils/data/highlights/properties.qml b/tests/auto/qmlls/utils/data/highlights/properties.qml
new file mode 100644
index 0000000000..bde60915ca
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/properties.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ Item {
+ property int k
+ readonly property int kk
+ required property int kkk
+ default property int kkkk
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/scriptExpressions.qml b/tests/auto/qmlls/utils/data/highlights/scriptExpressions.qml
new file mode 100644
index 0000000000..ee0b4ff5f8
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/scriptExpressions.qml
@@ -0,0 +1,116 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function k() {
+ }
+
+ function mafik() {
+ var patron = 34;
+ const upperLimit = 42;
+ do {
+ ++patron;
+ if (patron < 2)
+ continue;
+ else
+ ++patron;
+ } while (patron < upperLimit)
+ switch (patron) {
+ case 1:
+ return 23;
+ default:
+ break;
+ }
+ try {
+ {}
+ } catch (error) {
+ {}
+ } finally {}
+ for (const a in [1, 2, 3]) {
+ throw 2;
+ }
+ }
+
+ enum Test {
+ LOG
+ }
+
+ readonly property int t: 34
+ signal tt
+ required property int k
+
+ signal kkk(string a)
+ signal yyy(a: string)
+
+ function ttt() {
+
+ }
+
+function createComplexExpression(...objects) {
+ // Create an object that holds some data
+ let data = {
+ a: 5,
+ b: 10,
+ c: 3
+ };
+
+ // Create a complex expression using the data object
+ let expression = ((data.a + data.b * data.c) / (data.a - data.b)) ** data.c;
+
+ return expression;
+}
+
+ function set1() {
+ const array = [1,2,3,4];
+ const [a, b] = [1,2];
+ const [aa, , bb] = array;
+ const [aaa = 23, bbb] = array;
+ const [a1, b1, ...rest1] = array;
+ const [a2, , b2, ...rest2] = array;
+ const [a3, b3, ...{ pop, push }] = array;
+ const [a4, b4, ...[c, d]] = array;
+
+ const obj = {_a:1,_b:2};
+ const { a5, b5 } = obj;
+ const { a6: a_, b6: b1_ } = obj;
+ const { a7: a11 = 4, b11 = 34, c1: b111, d1 } = obj;
+ let key = a;
+ const { [key]: a___ } = obj;
+ }
+
+ function set2(s : int) : int {
+ // declare first
+ let a, b, a1, b1, c, d, rest, pop, push;
+ const array = [1,2,3,4];
+ [a, b] = array;
+ [a, , b] = array;
+ [a = aDefault, b] = array;
+ [a, b, ...rest] = array;
+ [a, , b, ...rest] = array;
+ [a, b, ...{ pop, push }] = array;
+ [a, b, ...[c, d]] = array;
+
+ const obj = {_a:1,_b:2};
+ ({ a, b } = obj); // brackets are required
+ ({ a: a1, b: b1 } = obj);
+
+ const complicatedObject = {
+ a: 1,
+ b: {
+ c: 2,
+ d: {
+ e: 3,
+ f: [4, 5, 6]
+ }
+ },
+ g: [7, 8, 9]
+ };
+
+ const { patron, b: { mafik, d: { e, f: [ , secondF, ...restF ] } }, g: [ firstG, ...restG ] } = complicatedObject;
+ }
+
+
+}
+
diff --git a/tests/auto/qmlls/utils/data/pragmas.qml b/tests/auto/qmlls/utils/data/pragmas.qml
new file mode 100644
index 0000000000..bcb73cf10b
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/pragmas.qml
@@ -0,0 +1,8 @@
+pragma Singleton
+pragma NativeMethodBehavior: AcceptThisObject;
+pragma ListPropertyAssignBehavior: Append, Replace;
+import QtQuick 2.15
+
+Item {
+
+}
diff --git a/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtqml-qtobject-qt-5.html b/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtqml-qtobject-qt-5.html
new file mode 100644
index 0000000000..9c3ce4e0f2
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtqml-qtobject-qt-5.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qqmlengine.cpp -->
+ <title>QtObject QML Type | Qt QML 5.15.16</title>
+ <link rel="stylesheet" type="text/css" href="style/offline-simple.css" />
+ <script type="text/javascript">
+ document.getElementsByTagName("link").item(0).setAttribute("href", "style/offline.css");
+ // loading style sheet breaks anchors that were jumped to before
+ // so force jumping to anchor again
+ setTimeout(function() {
+ var anchor = location.hash;
+ // need to jump to different anchor first (e.g. none)
+ location.hash = "#";
+ setTimeout(function() {
+ location.hash = anchor;
+ }, 0);
+ }, 0);
+ </script>
+</head>
+<body>
+<div class="header" id="qtdocheader">
+ <div class="main">
+ <div class="main-rounded">
+ <div class="navigationbar">
+ <ul>
+<li><a href="../qtdoc/index.html" translate="no">Qt 5.15</a></li>
+<li><a href="qtqml-index.html" translate="no">Qt QML</a></li>
+<li><a href="qtqml-qmlmodule.html" translate="no">QML Types</a></li>
+<li>QtObject QML Type</li>
+<li id="buildversion"><a href="qtqml-index.html" translate="no">Qt 5.15.16 Reference Documentation</a></li>
+ </ul>
+ </div>
+</div>
+<div class="content">
+<div class="line">
+<div class="content mainContent">
+<div class="sidebar">
+<div class="toc">
+<h3><a name="toc">Contents</a></h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">QtObject QML Type</h1>
+<span class="subtitle" translate="no"></span>
+<!-- $$$QtObject-brief -->
+<p>A basic QML type. <a href="#details">More...</a></p>
+<!-- @@@QtObject -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QtQml 2.15</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Instantiates:</td><td class="memItemRight bottomAlign"> <a href="qml-qtqml-qtobject.html" translate="no"><a href="../qtcore/qobject.html" translate="no">QObject</a></td></tr></table></div><ul>
+<li><a href="qml-qtqml-qtobject-members.html">List of all members, including inherited members</a></li>
+</ul>
+<a name="properties"></a>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><b><a href="qml-qtqml-qtobject.html#objectName-prop" translate="no">objectName</a></b></b> : string</li>
+</ul>
+<!-- $$$QtObject-description -->
+<a name="details"></a>
+<h2 id="details">Detailed Description</h2>
+<p>The QtObject type is a non-visual element which contains only the <a href="qml-qtqml-qtobject.html#objectName-prop" translate="no">objectName</a> property.</p>
+<p>It can be useful to create a QtObject if you need an extremely lightweight type to enclose a set of custom properties:</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="../qtquick/qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtqml-qtobject.html" translate="no">QtObject</a></span> {
+ <span class="name">id</span>: <span class="name">attributes</span>
+ property <span class="type"><a href="qml-string.html" translate="no">string</a></span> <span class="name">name</span>
+ property <span class="type"><a href="qml-int.html" translate="no">int</a></span> <span class="name">size</span>
+ property <span class="type"><a href="qml-variant.html" translate="no">variant</a></span> <span class="name">attributes</span>
+ }
+
+ <span class="type"><a href="../qtquick/qml-qtquick-text.html" translate="no">Text</a></span> { <span class="name">text</span>: <span class="name">attributes</span>.<span class="name">name</span> }
+ }
+</pre>
+<p>It can also be useful for C++ integration, as it is just a plain <a href="../qtcore/qobject.html" translate="no">QObject</a>. See the <a href="../qtcore/qobject.html" translate="no">QObject</a> documentation for further details.</p>
+<!-- @@@QtObject -->
+<h2>Property Documentation</h2>
+<!-- $$$objectName -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="objectName-prop">
+<td class="tblQmlPropNode"><p>
+<a name="objectName-prop"></a><span class="name">objectName</span> : <span class="type"><a href="qml-string.html" translate="no">string</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the <a href="../qtcore/qobject.html#objectName-prop" translate="no">QObject::objectName</a> for this specific object instance.</p>
+<p>This allows a C++ application to locate an item within a QML component using the <a href="../qtcore/qobject.html#findChild" translate="no">QObject::findChild()</a> method. For example, the following C++ application locates the child <a href="../qtquick/qml-qtquick-rectangle.html" translate="no">Rectangle</a> item and dynamically changes its <code translate="no">color</code> value:</p>
+<pre class="qml" translate="no">
+ <span class="comment">// MyRect.qml</span>
+
+ import QtQuick 2.0
+
+ <span class="type"><a href="../qtquick/qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">width</span>: <span class="number">200</span>; <span class="name">height</span>: <span class="number">200</span>
+
+ <span class="type"><a href="../qtquick/qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">objectName</span>: <span class="string">&quot;myRect&quot;</span>
+ }
+ }
+</pre>
+<pre class="cpp" translate="no">
+ <span class="comment">// main.cpp</span>
+
+ <span class="type">QQuickView</span> view;
+ view<span class="operator">.</span>setSource(<span class="type">QUrl</span><span class="operator">::</span>fromLocalFile(<span class="string">&quot;MyRect.qml&quot;</span>));
+ view<span class="operator">.</span>show();
+
+ <span class="type">QQuickItem</span> <span class="operator">*</span>item <span class="operator">=</span> view<span class="operator">.</span>rootObject()<span class="operator">-</span><span class="operator">&gt;</span>findChild<span class="operator">&lt;</span><span class="type">QQuickItem</span><span class="operator">*</span><span class="operator">&gt;</span>(<span class="string">&quot;myRect&quot;</span>);
+ <span class="keyword">if</span> (item)
+ item<span class="operator">-</span><span class="operator">&gt;</span>setProperty(<span class="string">&quot;color&quot;</span><span class="operator">,</span> <span class="type">QColor</span>(<span class="type"><a href="qml-qtqml-qt.html" translate="no">Qt</a></span><span class="operator">::</span>yellow));
+</pre>
+</div></div><!-- @@@objectName -->
+<br/>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<div class="footer">
+ <p>
+ <acronym title="Copyright">&copy;</acronym> 2023 The Qt Company Ltd.
+ Documentation contributions included herein are the copyrights of
+ their respective owners.<br/> The documentation provided herein is licensed under the terms of the <a href="http://www.gnu.org/licenses/fdl.html">GNU Free Documentation License version 1.3</a> as published by the Free Software Foundation.<br/> Qt and respective logos are <a href="https://doc.qt.io/qt/trademarks.html"> trademarks</a> of The Qt Company Ltd. in Finland and/or other countries
+ worldwide. All other trademarks are property of their respective owners. </p>
+</div>
+</body>
+</html>
diff --git a/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtqml-qtobject.html b/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtqml-qtobject.html
new file mode 100644
index 0000000000..16c218d2ff
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtqml-qtobject.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qqmlengine.cpp -->
+ <meta name="description" content="A basic QML type.">
+ <link rel="stylesheet" type="text/css" href="style/offline-simple.css" />
+ <script type="text/javascript">
+ document.getElementsByTagName("link").item(0).setAttribute("href", "style/offline.css");
+ // loading style sheet breaks anchors that were jumped to before
+ // so force jumping to anchor again
+ setTimeout(function() {
+ var anchor = location.hash;
+ // need to jump to different anchor first (e.g. none)
+ location.hash = "#";
+ setTimeout(function() {
+ location.hash = anchor;
+ }, 0);
+ }, 0);
+ </script>
+</head>
+<body>
+<div class="header" id="qtdocheader">
+ <div class="main">
+ <div class="main-rounded">
+ <div class="navigationbar">
+ <ul>
+<li><a href="qtqml-index.html" translate="no">Qt QML</a></li>
+<li><a href="qtqml-qmlmodule.html" translate="no">QML Types</a></li>
+<li>QtObject</li>
+ </ul>
+ </div>
+</div>
+<div class="content">
+<div class="line">
+<div class="content mainContent">
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">QtObject QML Type</h1>
+<!-- $$$QtObject-brief -->
+<p>A basic QML type. <a href="#details">More...</a></p>
+<!-- @@@QtObject -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QtQml</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Instantiates:</td><td class="memItemRight bottomAlign"> <a href="../qtcore/qobject.html" translate="no">QObject</a></td></tr></table></div><ul>
+<li><a href="qml-qtqml-qtobject-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qtqml-qtobject.html#objectName-prop" translate="no">objectName</a></b> : string</li>
+</ul>
+<!-- $$$QtObject-description -->
+<h2 id="details">Detailed Description</h2>
+<p>The QtObject type is a non-visual element which contains only the <a href="qml-qtqml-qtobject.html#objectName-prop" translate="no">objectName</a> property.</p>
+<p>It can be useful to create a QtObject if you need an extremely lightweight type to enclose a set of custom properties:</p>
+<pre class="qml" translate="no">
+ import QtQuick
+
+ <span class="type"><a href="../qtquick/qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtqml-qtobject.html" translate="no">QtObject</a></span> {
+ <span class="name">id</span>: <span class="name">attributes</span>
+ property <span class="type"><a href="qml-string.html" translate="no">string</a></span> <span class="name">name</span>
+ property <span class="type"><a href="qml-int.html" translate="no">int</a></span> <span class="name">size</span>
+ property <span class="type"><a href="qml-variant.html" translate="no">variant</a></span> <span class="name">attributes</span>
+ }
+
+ <span class="type"><a href="../qtquick/qml-qtquick-text.html" translate="no">Text</a></span> { <span class="name">text</span>: <span class="name">attributes</span>.<span class="name">name</span> }
+ }
+</pre>
+<p>It can also be useful for C++ integration, as it is just a plain <a href="../qtcore/qobject.html" translate="no">QObject</a>. See the <a href="../qtcore/qobject.html" translate="no">QObject</a> documentation for further details.</p>
+<!-- @@@QtObject -->
+<h2>Property Documentation</h2>
+<!-- $$$objectName -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="objectName-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">objectName</span> : <span class="type"><a href="qml-string.html" translate="no">string</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the <a href="../qtcore/qobject.html#objectName-prop" translate="no">QObject::objectName</a> for this specific object instance.</p>
+<p>This allows a C++ application to locate an item within a QML component using the <a href="../qtcore/qobject.html#findChild" translate="no">QObject::findChild</a>() method. For example, the following C++ application locates the child <a href="../qtquick/qml-qtquick-rectangle.html" translate="no">Rectangle</a> item and dynamically changes its <code translate="no">color</code> value:</p>
+<pre class="qml" translate="no">
+ <span class="comment">// MyRect.qml</span>
+
+ import QtQuick 2.0
+
+ <span class="type"><a href="../qtquick/qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">width</span>: <span class="number">200</span>; <span class="name">height</span>: <span class="number">200</span>
+
+ <span class="type"><a href="../qtquick/qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">objectName</span>: <span class="string">&quot;myRect&quot;</span>
+ }
+ }
+</pre>
+<pre class="cpp" translate="no">
+ <span class="comment">// main.cpp</span>
+
+ <span class="type">QQuickView</span> view;
+ view<span class="operator">.</span>setSource(<span class="type">QUrl</span><span class="operator">::</span>fromLocalFile(<span class="string">&quot;MyRect.qml&quot;</span>));
+ view<span class="operator">.</span>show();
+
+ <span class="type">QQuickItem</span> <span class="operator">*</span>item <span class="operator">=</span> view<span class="operator">.</span>rootObject()<span class="operator">-</span><span class="operator">&gt;</span>findChild<span class="operator">&lt;</span><span class="type">QQuickItem</span><span class="operator">*</span><span class="operator">&gt;</span>(<span class="string">&quot;myRect&quot;</span>);
+ <span class="keyword">if</span> (item)
+ item<span class="operator">-</span><span class="operator">&gt;</span>setProperty(<span class="string">&quot;color&quot;</span><span class="operator">,</span> <span class="type">QColor</span>(<span class="type"><a href="qml-qtqml-qt.html" translate="no">Qt</a></span><span class="operator">::</span>yellow));
+</pre>
+</div></div><!-- @@@objectName -->
+<br/>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<div class="footer">
+ <p>
+ <acronym title="Copyright">&copy;</acronym> 2023 The Qt Company Ltd.
+ Documentation contributions included herein are the copyrights of
+ their respective owners.<br/> The documentation provided herein is licensed under the terms of the <a href="http://www.gnu.org/licenses/fdl.html">GNU Free Documentation License version 1.3</a> as published by the Free Software Foundation.<br/> Qt and respective logos are <a href="https://doc.qt.io/qt/trademarks.html"> trademarks</a> of The Qt Company Ltd. in Finland and/or other countries
+ worldwide. All other trademarks are property of their respective owners. </p>
+</div>
+</body>
+</html>
diff --git a/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtquick-item.html b/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtquick-item.html
new file mode 100644
index 0000000000..fc34b2d5db
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtquick-item.html
@@ -0,0 +1,1486 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qquickitem.cpp -->
+ <meta name="description" content="A basic visual QML type.">
+ <title>Item QML Type | Qt Quick 6.8.0</title>
+ <link rel="stylesheet" type="text/css" href="style/offline-simple.css" />
+ <script type="text/javascript">
+ document.getElementsByTagName("link").item(0).setAttribute("href", "style/offline.css");
+ // loading style sheet breaks anchors that were jumped to before
+ // so force jumping to anchor again
+ setTimeout(function() {
+ var anchor = location.hash;
+ // need to jump to different anchor first (e.g. none)
+ location.hash = "#";
+ setTimeout(function() {
+ location.hash = anchor;
+ }, 0);
+ }, 0);
+ </script>
+</head>
+<body>
+<div class="header" id="qtdocheader">
+ <div class="main">
+ <div class="main-rounded">
+ <div class="navigationbar">
+ <ul>
+<li><a href="../qtdoc/index.html" translate="no">Qt 6.8</a></li>
+<li><a href="qtquick-index.html" translate="no">Qt Quick</a></li>
+<li><a href="qtquick-qmlmodule.html" translate="no">QML Types</a></li>
+<li>Item</li>
+<li id="buildversion"><a href="qtquick-index.html" translate="no">Qt 6.8.0 Reference Documentation</a></li>
+ </ul>
+ </div>
+</div>
+<div class="content">
+<div class="line">
+<div class="content mainContent">
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#methods">Methods</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#item-layers">Item Layers</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Item QML Type</h1>
+<!-- $$$Item-brief -->
+<p>A basic visual QML type. <a href="#details">More...</a></p>
+<!-- @@@Item -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QtQuick</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Instantiates:</td><td class="memItemRight bottomAlign"> <a href="qquickitem.html" translate="no">QQuickItem</a></td></tr><tr><td class="memItemLeft rightAlign topAlign"> Inherits:</td><td class="memItemRight bottomAlign"> <p><a href="../qtqml/qml-qtqml-qtobject.html" translate="no">QtObject</a></p>
+</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Inherited By:</td><td class="memItemRight bottomAlign"> <p><a href="qml-qtquick-animatedsprite.html" translate="no">AnimatedSprite</a>, <a href="qml-qtquick-borderimage.html" translate="no">BorderImage</a>, <a href="qml-qtquick-canvas.html" translate="no">Canvas</a>, <a href="qml-qtquick-column.html" translate="no">Column</a>, <a href="qml-qtquick-layouts-columnlayout.html" translate="no">ColumnLayout</a>, <a href="qml-qtquick-droparea.html" translate="no">DropArea</a>, <a href="qml-qtquick-flickable.html" translate="no">Flickable</a>, <a href="qml-qtquick-flipable.html" translate="no">Flipable</a>, <a href="qml-qtquick-flow.html" translate="no">Flow</a>, <a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a>, <a href="qml-qtquick-grid.html" translate="no">Grid</a>, <a href="qml-qtquick-layouts-gridlayout.html" translate="no">GridLayout</a>, <a href="qml-qtquick-image.html" translate="no">Image</a>, <a href="qml-qtquick-layouts-layoutitemproxy.html" translate="no">LayoutItemProxy</a>, <a href="qml-qtquick-loader.html" translate="no">Loader</a>, <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a>, <a href="qml-qtquick-effects-multieffect.html" translate="no">MultiEffect</a>, <a href="qml-qtquick-multipointtoucharea.html" translate="no">MultiPointTouchArea</a>, <a href="qml-qtquick-particles-particlepainter.html" translate="no">ParticlePainter</a>, <a href="qml-qtquick-pathview.html" translate="no">PathView</a>, <a href="qml-qtquick-pincharea.html" translate="no">PinchArea</a>, <a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a>, <a href="qml-qtquick-repeater.html" translate="no">Repeater</a>, <a href="qml-qtquick-row.html" translate="no">Row</a>, <a href="qml-qtquick-layouts-rowlayout.html" translate="no">RowLayout</a>, <a href="qml-qtquick-shadereffect.html" translate="no">ShaderEffect</a>, <a href="qml-qtquick-shadereffectsource.html" translate="no">ShaderEffectSource</a>, <a href="qml-qtquick-shapes-shape.html" translate="no">Shape</a>, <a href="qml-qtquick-spritesequence.html" translate="no">SpriteSequence</a>, <a href="qml-qtquick-layouts-stacklayout.html" translate="no">StackLayout</a>, <a href="qml-qtquick-text.html" translate="no">Text</a>, <a href="qml-qtquick-textedit.html" translate="no">TextEdit</a>, <a href="qml-qtquick-textinput.html" translate="no">TextInput</a>, and <a href="qml-qtquick-windowcontainer.html" translate="no">WindowContainer</a></p>
+</td></tr></table></div><ul>
+<li><a href="qml-qtquick-item-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#activeFocus-prop" translate="no">activeFocus</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#activeFocusOnTab-prop" translate="no">activeFocusOnTab</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors-prop" translate="no">anchors</a></b><ul>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.alignWhenCentered-prop" translate="no">anchors.alignWhenCentered</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.baseline-prop" translate="no">anchors.baseline</a></b> : AnchorLine</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.baselineOffset-prop" translate="no">anchors.baselineOffset</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.bottom-prop" translate="no">anchors.bottom</a></b> : AnchorLine</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.bottomMargin-prop" translate="no">anchors.bottomMargin</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.centerIn-prop" translate="no">anchors.centerIn</a></b> : Item</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.fill-prop" translate="no">anchors.fill</a></b> : Item</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.horizontalCenter-prop" translate="no">anchors.horizontalCenter</a></b> : AnchorLine</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.horizontalCenterOffset-prop" translate="no">anchors.horizontalCenterOffset</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.left-prop" translate="no">anchors.left</a></b> : AnchorLine</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.leftMargin-prop" translate="no">anchors.leftMargin</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.margins-prop" translate="no">anchors.margins</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.right-prop" translate="no">anchors.right</a></b> : AnchorLine</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.rightMargin-prop" translate="no">anchors.rightMargin</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.top-prop" translate="no">anchors.top</a></b> : AnchorLine</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.topMargin-prop" translate="no">anchors.topMargin</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.verticalCenter-prop" translate="no">anchors.verticalCenter</a></b> : AnchorLine</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.verticalCenterOffset-prop" translate="no">anchors.verticalCenterOffset</a></b> : real</li>
+</ul>
+</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#antialiasing-prop" translate="no">antialiasing</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#baselineOffset-prop" translate="no">baselineOffset</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#children-prop" translate="no">children</a></b> : list&lt;Item&gt;</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#childrenRect-prop" translate="no">childrenRect</a></b><ul>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#childrenRect.height-prop" translate="no">childrenRect.height</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#childrenRect.width-prop" translate="no">childrenRect.width</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#childrenRect.x-prop" translate="no">childrenRect.x</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#childrenRect.y-prop" translate="no">childrenRect.y</a></b> : real</li>
+</ul>
+</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#clip-prop" translate="no">clip</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#containmentMask-prop" translate="no">containmentMask</a></b> : QObject*</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#data-prop" translate="no">data</a></b> : list&lt;QtObject&gt;</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#enabled-prop" translate="no">enabled</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#focus-prop" translate="no">focus</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#focusPolicy-prop" translate="no">focusPolicy</a></b> : enumeration <code class="summary extra" translate="no">(since 6.7)</code></li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#height-prop" translate="no">height</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#implicitHeight-prop" translate="no">implicitHeight</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#implicitWidth-prop" translate="no">implicitWidth</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.effect-prop" translate="no">layer.effect</a></b> : Component</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.enabled-prop" translate="no">layer.enabled</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.format-prop" translate="no">layer.format</a></b> : enumeration</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.live-prop" translate="no">layer.live</a></b> : bool <code class="summary extra" translate="no">(since 6.5)</code></li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.mipmap-prop" translate="no">layer.mipmap</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.samplerName-prop" translate="no">layer.samplerName</a></b> : string</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.samples-prop" translate="no">layer.samples</a></b> : enumeration</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.smooth-prop" translate="no">layer.smooth</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.sourceRect-prop" translate="no">layer.sourceRect</a></b> : rect</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.textureMirroring-prop" translate="no">layer.textureMirroring</a></b> : enumeration</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.textureSize-prop" translate="no">layer.textureSize</a></b> : size</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.wrapMode-prop" translate="no">layer.wrapMode</a></b> : enumeration</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#opacity-prop" translate="no">opacity</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#palette-prop" translate="no">palette</a></b> : Palette <code class="summary extra" translate="no">(since 6.0)</code></li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#parent-prop" translate="no">parent</a></b> : Item</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#resources-prop" translate="no">resources</a></b> : list&lt;QtObject&gt;</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#rotation-prop" translate="no">rotation</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#scale-prop" translate="no">scale</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#smooth-prop" translate="no">smooth</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#state-prop" translate="no">state</a></b> : string</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#states-prop" translate="no">states</a></b> : list&lt;State&gt;</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#transform-prop" translate="no">transform</a></b> : list&lt;Transform&gt;</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#transformOrigin-prop" translate="no">transformOrigin</a></b> : enumeration</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#transitions-prop" translate="no">transitions</a></b> : list&lt;Transition&gt;</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#visible-prop" translate="no">visible</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#visibleChildren-prop" translate="no">visibleChildren</a></b> : list&lt;Item&gt;</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#width-prop" translate="no">width</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#x-prop" translate="no">x</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#y-prop" translate="no">y</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#z-prop" translate="no">z</a></b> : real</li>
+</ul>
+<h2 id="methods">Methods</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#childAt-method" translate="no">childAt</a></b>(real <i>x</i>, real <i>y</i>)</li>
+<li class="fn" translate="no">bool <b><a href="qml-qtquick-item.html#contains-method" translate="no">contains</a></b>(point <i>point</i>)</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#dumpItemTree-method" translate="no">dumpItemTree</a></b>() <code class="summary extra" translate="no">(since 6.3)</code></li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#forceActiveFocus-method" translate="no">forceActiveFocus</a></b>()</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#forceActiveFocus-method-1" translate="no">forceActiveFocus</a></b>(Qt::FocusReason <i>reason</i>)</li>
+<li class="fn" translate="no">bool <b><a href="qml-qtquick-item.html#grabToImage-method" translate="no">grabToImage</a></b>(<i>callback</i>, <i>targetSize</i>)</li>
+<li class="fn" translate="no">point <b><a href="qml-qtquick-item.html#mapFromGlobal-method" translate="no">mapFromGlobal</a></b>(real <i>x</i>, real <i>y</i>)</li>
+<li class="fn" translate="no">point <b><a href="qml-qtquick-item.html#mapFromItem-method" translate="no">mapFromItem</a></b>(Item <i>item</i>, real <i>x</i>, real <i>y</i>)</li>
+<li class="fn" translate="no">point <b><a href="qml-qtquick-item.html#mapFromItem-method-1" translate="no">mapFromItem</a></b>(Item <i>item</i>, point <i>p</i>)</li>
+<li class="fn" translate="no">rect <b><a href="qml-qtquick-item.html#mapFromItem-method-2" translate="no">mapFromItem</a></b>(Item <i>item</i>, real <i>x</i>, real <i>y</i>, real <i>width</i>, real <i>height</i>)</li>
+<li class="fn" translate="no">rect <b><a href="qml-qtquick-item.html#mapFromItem-method-3" translate="no">mapFromItem</a></b>(Item <i>item</i>, rect <i>r</i>)</li>
+<li class="fn" translate="no">point <b><a href="qml-qtquick-item.html#mapToGlobal-method" translate="no">mapToGlobal</a></b>(real <i>x</i>, real <i>y</i>)</li>
+<li class="fn" translate="no">point <b><a href="qml-qtquick-item.html#mapToItem-method" translate="no">mapToItem</a></b>(Item <i>item</i>, real <i>x</i>, real <i>y</i>)</li>
+<li class="fn" translate="no">point <b><a href="qml-qtquick-item.html#mapToItem-method-1" translate="no">mapToItem</a></b>(Item <i>item</i>, point <i>p</i>)</li>
+<li class="fn" translate="no">rect <b><a href="qml-qtquick-item.html#mapToItem-method-2" translate="no">mapToItem</a></b>(Item <i>item</i>, real <i>x</i>, real <i>y</i>, real <i>width</i>, real <i>height</i>)</li>
+<li class="fn" translate="no">rect <b><a href="qml-qtquick-item.html#mapToItem-method-3" translate="no">mapToItem</a></b>(Item <i>item</i>, rect <i>r</i>)</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#nextItemInFocusChain-method" translate="no">nextItemInFocusChain</a></b>(bool <i>forward</i>)</li>
+</ul>
+<!-- $$$Item-description -->
+<h2 id="details">Detailed Description</h2>
+<p>The Item type is the base type for all visual items in Qt Quick.</p>
+<p>All visual items in Qt Quick inherit from Item. Although an Item object has no visual appearance, it defines all the attributes that are common across visual items, such as x and y position, width and height, <a href="qtquick-positioning-anchors.html" translate="no">anchoring</a> and key handling support.</p>
+<p>The Item type can be useful for grouping several items under a single root visual item. For example:</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-image.html" translate="no">Image</a></span> {
+ <span class="name">source</span>: <span class="string">&quot;tile.png&quot;</span>
+ }
+ <span class="type"><a href="qml-qtquick-image.html" translate="no">Image</a></span> {
+ <span class="name">x</span>: <span class="number">80</span>
+ <span class="name">width</span>: <span class="number">100</span>
+ <span class="name">height</span>: <span class="number">100</span>
+ <span class="name">source</span>: <span class="string">&quot;tile.png&quot;</span>
+ }
+ <span class="type"><a href="qml-qtquick-image.html" translate="no">Image</a></span> {
+ <span class="name">x</span>: <span class="number">190</span>
+ <span class="name">width</span>: <span class="number">100</span>
+ <span class="name">height</span>: <span class="number">100</span>
+ <span class="name">fillMode</span>: <span class="name">Image</span>.<span class="name">Tile</span>
+ <span class="name">source</span>: <span class="string">&quot;tile.png&quot;</span>
+ }
+ }
+</pre>
+<h3 id="event-handling">Event Handling</h3>
+<p>All Item-based visual types can use <a href="qtquickhandlers-index.html" translate="no">Input Handlers</a> to handle incoming input events (subclasses of <a href="../qtgui/qinputevent.html" translate="no">QInputEvent</a>), such as mouse, touch and key events. This is the preferred declarative way to handle events.</p>
+<p>An alternative way to handle touch events is to subclass <a href="qquickitem.html" translate="no">QQuickItem</a>, call setAcceptTouchEvents() in the constructor, and override touchEvent(). <a href="../qtcore/qevent.html#accepted-prop" translate="no">Accept</a> the entire event to stop delivery to items underneath, and to exclusively grab for all the event's touch points. Use <a href="../qtgui/qpointerevent.html#setExclusiveGrabber" translate="no">QPointerEvent::setExclusiveGrabber</a>() to grab only certain touchpoints, and allow the event to be delivered further.</p>
+<p>Likewise, a <a href="qquickitem.html" translate="no">QQuickItem</a> subclass can call setAcceptedMouseButtons() to register to receive mouse button events, setAcceptHoverEvents() to receive hover events (mouse movements while no button is pressed), and override the virtual functions mousePressEvent(), mouseMoveEvent(), and mouseReleaseEvent(). Those can also accept the event to prevent further delivery and get an implicit grab at the same time; or explicitly <a href="../qtgui/qpointerevent.html#setExclusiveGrabber" translate="no">grab</a> the single <a href="../qtgui/qeventpoint.html" translate="no">QEventPoint</a> that the <a href="../qtgui/qmouseevent.html" translate="no">QMouseEvent</a> carries.</p>
+<p>Key handling is available to all Item-based visual types via the <a href="qml-qtquick-keys.html" translate="no">Keys</a> attached property. The <i>Keys</i> attached property provides basic signals such as <a href="qml-qtquick-keys.html#pressed-signal" translate="no">pressed</a> and <a href="qml-qtquick-keys.html#released-signal" translate="no">released</a>, as well as signals for specific keys, such as <a href="qml-qtquick-keys.html#spacePressed-signal" translate="no">spacePressed</a>. The example below assigns <a href="qtquick-input-focus.html" translate="no">keyboard focus</a> to the item and handles the left key via the general <code translate="no">onPressed</code> handler and the return key via the <code translate="no">onReturnPressed</code> handler:</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">focus</span>: <span class="number">true</span>
+ <span class="name">Keys</span>.onPressed: (<span class="keyword"></span>event)=&gt; {
+ <span class="keyword">if</span> (<span class="name">event</span>.<span class="name">key</span> <span class="operator">==</span> <span class="name">Qt</span>.<span class="name">Key_Left</span>) {
+ <span class="name">console</span>.<span class="name">log</span>(<span class="string">&quot;move left&quot;</span>);
+ <span class="name">event</span>.<span class="name">accepted</span> <span class="operator">=</span> <span class="number">true</span>;
+ }
+ }
+ <span class="name">Keys</span>.onReturnPressed: <span class="name">console</span>.<span class="name">log</span>(<span class="string">&quot;Pressed return&quot;</span>);
+ }
+</pre>
+<p>See the <a href="qml-qtquick-keys.html" translate="no">Keys</a> attached property for detailed documentation.</p>
+<h3 id="layout-mirroring">Layout Mirroring</h3>
+<p>Item layouts can be mirrored using the <a href="qml-qtquick-layoutmirroring.html" translate="no">LayoutMirroring</a> attached property. This causes <a href="qml-qtquick-item.html#anchors.top-prop" translate="no">anchors</a> to be horizontally reversed, and also causes items that lay out or position their children (such as <a href="qml-qtquick-listview.html" translate="no">ListView</a> or <a href="qml-qtquick-row.html" translate="no">Row</a>) to horizontally reverse the direction of their layouts.</p>
+<p>See <a href="qml-qtquick-layoutmirroring.html" translate="no">LayoutMirroring</a> for more details.</p>
+<h2 id="item-layers">Item Layers</h2>
+<p>An Item will normally be rendered directly into the window it belongs to. However, by setting <a href="qml-qtquick-item.html#layer.enabled-prop" translate="no">layer.enabled</a>, it is possible to delegate the item and its entire subtree into an offscreen surface. Only the offscreen surface, a texture, will be then drawn into the window.</p>
+<p>If it is desired to have a texture size different from that of the item, this is possible using <a href="qml-qtquick-item.html#layer.textureSize-prop" translate="no">layer.textureSize</a>. To render only a section of the item into the texture, use <a href="qml-qtquick-item.html#layer.sourceRect-prop" translate="no">layer.sourceRect</a>. It is also possible to specify <a href="qml-qtquick-item.html#layer.sourceRect-prop" translate="no">layer.sourceRect</a> so it extends beyond the bounds of the item. In this case, the exterior will be padded with transparent pixels.</p>
+<p>The item will use linear interpolation for scaling if <a href="qml-qtquick-item.html#layer.smooth-prop" translate="no">layer.smooth</a> is set to <code translate="no">true</code> and will use mipmap for downsampling if <a href="qml-qtquick-item.html#layer.mipmap-prop" translate="no">layer.mipmap</a> is set to <code translate="no">true</code>. Mipmapping may improve visual quality of downscaled items. For mipmapping of single Image items, prefer <a href="qml-qtquick-image.html#mipmap-prop" translate="no">Image::mipmap</a>.</p>
+<h3 id="layer-opacity-vs-item-opacity">Layer Opacity vs Item Opacity</h3>
+<p>When applying <a href="qml-qtquick-item.html#opacity-prop" translate="no">opacity</a> to an item hierarchy the opacity is applied to each item individually. This can lead to undesired visual results when the opacity is applied to a subtree. Consider the following example:</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><img src="images/qml-blending-nonlayered.png" alt="" /></td><td ><b>Non-layered Opacity</b><pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">id</span>: <span class="name">nonLayered</span>
+
+ <span class="name">opacity</span>: <span class="number">0.5</span>
+
+ <span class="name">width</span>: <span class="number">100</span>
+ <span class="name">height</span>: <span class="number">100</span>
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> { <span class="name">width</span>: <span class="number">80</span>; <span class="name">height</span>: <span class="number">80</span>; <span class="name">border</span>.width: <span class="number">1</span> }
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> { <span class="name">x</span>: <span class="number">20</span>; <span class="name">y</span>: <span class="number">20</span>; <span class="name">width</span>: <span class="number">80</span>; <span class="name">height</span>: <span class="number">80</span>; <span class="name">border</span>.width: <span class="number">1</span> }
+ }
+</pre>
+</td></tr>
+</table></div>
+<p>A layer is rendered with the root item's opacity being 1, and then the root item's opacity is applied to the texture when it is drawn. This means that fading in a large item hierarchy from transparent to opaque, or vice versa, can be done without the overlap artifacts that the normal item by item alpha blending has. Here is the same example with layer enabled:</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/qml-blending-layered.png" alt="" /></p></td><td ><b>Layered Opacity</b><pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">id</span>: <span class="name">layered</span>
+
+ <span class="name">opacity</span>: <span class="number">0.5</span>
+
+ <span class="name">layer</span>.enabled: <span class="number">true</span>
+
+ <span class="name">width</span>: <span class="number">100</span>
+ <span class="name">height</span>: <span class="number">100</span>
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> { <span class="name">width</span>: <span class="number">80</span>; <span class="name">height</span>: <span class="number">80</span>; <span class="name">border</span>.width: <span class="number">1</span> }
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> { <span class="name">x</span>: <span class="number">20</span>; <span class="name">y</span>: <span class="number">20</span>; <span class="name">width</span>: <span class="number">80</span>; <span class="name">height</span>: <span class="number">80</span>; <span class="name">border</span>.width: <span class="number">1</span> }
+ }
+</pre>
+</td></tr>
+</table></div>
+<h3 id="combined-with-shadereffects">Combined with ShaderEffects</h3>
+<p>Setting <a href="qml-qtquick-item.html#layer.enabled-prop" translate="no">layer.enabled</a> to true will turn the item into a <a href="qquickitem.html#isTextureProvider" translate="no">texture provider</a>, making it possible to use the item directly as a texture, for instance in combination with the <a href="qml-qtquick-shadereffect.html" translate="no">ShaderEffect</a> type.</p>
+<p>It is possible to apply an effect on a layer at runtime using layer.effect:</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">id</span>: <span class="name">layerRoot</span>
+ <span class="name">layer</span>.enabled: <span class="number">true</span>
+ <span class="name">layer</span>.effect: <span class="name">ShaderEffect</span> {
+ <span class="name">fragmentShader</span>: <span class="string">&quot;effect.frag.qsb&quot;</span>
+ }
+ }
+</pre>
+<p>See <a href="qml-qtquick-shadereffect.html" translate="no">ShaderEffect</a> for more information about using effects.</p>
+<div class="admonition note">
+<p><b>Note: </b><a href="qml-qtquick-item.html#layer.enabled-prop" translate="no">layer.enabled</a> is actually just a more convenient way of using <a href="qml-qtquick-shadereffectsource.html" translate="no">ShaderEffectSource</a>.</p>
+</div>
+<h3 id="memory-and-performance">Memory and Performance</h3>
+<p>When an item's layer is enabled, the scene graph will allocate memory in the GPU equal to <code translate="no">width x height x 4</code>. In memory constrained configurations, large layers should be used with care.</p>
+<p>In the <a href="../qtgui/qpainter.html" translate="no">QPainter</a> / <a href="../qtwidgets/qwidget.html" translate="no">QWidget</a> world, it is sometimes favorable to cache complex content in a pixmap, image or texture. In Qt Quick, because of the techniques already applied by the <a href="qtquick-visualcanvas-scenegraph-renderer.html" translate="no">scene graph renderer</a>, this will in most cases not be the case. Excessive draw calls are already reduced because of batching and a cache will in most cases end up blending more pixels than the original content. The overhead of rendering to an offscreen and the blending involved with drawing the resulting texture is therefore often more costly than simply letting the item and its children be drawn normally.</p>
+<p>Also, an item using a layer can not be <a href="qtquick-visualcanvas-scenegraph-renderer.html#batching" translate="no">batched</a> during rendering. This means that a scene with many layered items may have performance problems.</p>
+<p>Layering can be convenient and useful for visual effects, but should in most cases be enabled for the duration of the effect and disabled afterwards.</p>
+<!-- @@@Item -->
+<h2>Property Documentation</h2>
+<!-- $$$ -->
+<div class="qmlitem"><div class="fngroup">
+<div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="children-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">children</span> : <span class="type"><a href="../qtqml/qml-list.html" translate="no">list</a></span>&lt;<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span>&gt;</p></td></tr>
+<tr valign="top" class="odd" id="resources-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">resources</span> : <span class="type"><a href="../qtqml/qml-list.html" translate="no">list</a></span>&lt;<span class="type"><a href="../qtqml/qml-qtqml-qtobject.html" translate="no">QtObject</a></span>&gt;</p></td></tr>
+</table></div></div>
+</div><div class="qmldoc"><p>The children property contains the list of visual children of this item. The resources property contains non-visual resources that you want to reference by name.</p>
+<p>It is not generally necessary to refer to these properties when adding child items or resources, as the default <a href="qml-qtquick-item.html#data-prop" translate="no">data</a> property will automatically assign child objects to the <code translate="no">children</code> and <code translate="no">resources</code> properties as appropriate. See the <a href="qml-qtquick-item.html#data-prop" translate="no">data</a> documentation for details.</p>
+</div></div><!-- @@@ -->
+<br/>
+<!-- $$$ -->
+<div class="qmlitem"><div class="fngroup">
+<div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="height-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">height</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="width-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">width</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="x-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">x</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="y-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">y</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+</div><div class="qmldoc"><p>Defines the item's position and size. The default value is <code translate="no">0</code>.</p>
+<p>The (x,y) position is relative to the <a href="qml-qtquick-item.html#parent-prop" translate="no">parent</a>.</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> { <span class="name">x</span>: <span class="number">100</span>; <span class="name">y</span>: <span class="number">100</span>; <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span> }
+</pre>
+</div></div><!-- @@@ -->
+<br/>
+<!-- $$$ -->
+<div class="qmlitem"><div class="fngroup">
+<div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="implicitHeight-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">implicitHeight</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="implicitWidth-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">implicitWidth</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+</div><div class="qmldoc"><p>Defines the preferred width or height of the Item.</p>
+<p>If <a href="qml-qtquick-item.html#width-prop" translate="no">width</a> or <a href="qml-qtquick-item.html#height-prop" translate="no">height</a> is not specified, an item's effective size will be determined by its <a href="qml-qtquick-item.html#implicitWidth-prop" translate="no">implicitWidth</a> or <a href="qml-qtquick-item.html#implicitHeight-prop" translate="no">implicitHeight</a>.</p>
+<p>However, if an item is the child of a <a href="qtquicklayouts-index.html" translate="no">layout</a>, the layout will determine the item's preferred size using its implicit size. In such a scenario, the explicit <a href="qml-qtquick-item.html#width-prop" translate="no">width</a> or <a href="qml-qtquick-item.html#height-prop" translate="no">height</a> will be ignored.</p>
+<p>The default implicit size for most items is 0x0, however some items have an inherent implicit size which cannot be overridden, for example, <a href="qml-qtquick-image.html" translate="no">Image</a> and <a href="qml-qtquick-text.html" translate="no">Text</a>.</p>
+<p>Setting the implicit size is useful for defining components that have a preferred size based on their content, for example:</p>
+<pre class="qml" translate="no">
+ <span class="comment">// Label.qml</span>
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ property <span class="type">alias</span> <span class="name">icon</span>: <span class="name">image</span>.<span class="name">source</span>
+ property <span class="type">alias</span> <span class="name">label</span>: <span class="name">text</span>.<span class="name">text</span>
+ <span class="name">implicitWidth</span>: <span class="name">text</span>.<span class="name">implicitWidth</span> <span class="operator">+</span> <span class="name">image</span>.<span class="name">implicitWidth</span>
+ <span class="name">implicitHeight</span>: <span class="name">Math</span>.<span class="name">max</span>(<span class="name">text</span>.<span class="name">implicitHeight</span>, <span class="name">image</span>.<span class="name">implicitHeight</span>)
+ <span class="type"><a href="qml-qtquick-image.html" translate="no">Image</a></span> { <span class="name">id</span>: <span class="name">image</span> }
+ <span class="type"><a href="qml-qtquick-text.html" translate="no">Text</a></span> {
+ <span class="name">id</span>: <span class="name">text</span>
+ <span class="name">wrapMode</span>: <span class="name">Text</span>.<span class="name">Wrap</span>
+ <span class="name">anchors</span>.left: <span class="name">image</span>.<span class="name">right</span>; <span class="name">anchors</span>.right: <span class="name">parent</span>.<span class="name">right</span>
+ <span class="name">anchors</span>.verticalCenter: <span class="name">parent</span>.<span class="name">verticalCenter</span>
+ }
+ }
+</pre>
+<div class="admonition note">
+<p><b>Note: </b>Using <a href="qml-qtquick-item.html#implicitWidth-prop" translate="no">implicitWidth</a> of <a href="qml-qtquick-text.html" translate="no">Text</a> or <a href="qml-qtquick-textedit.html" translate="no">TextEdit</a> and setting the width explicitly incurs a performance penalty as the text must be laid out twice.</p>
+</div>
+</div></div><!-- @@@ -->
+<br/>
+<!-- $$$activeFocus -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="activeFocus-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">activeFocus</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span> <code class="details extra" translate="no">[read-only]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This read-only property indicates whether the item has active focus.</p>
+<p>If activeFocus is true, either this item is the one that currently receives keyboard input, or it is a <a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a> ancestor of the item that currently receives keyboard input.</p>
+<p>Usually, activeFocus is gained by setting <a href="qml-qtquick-item.html#focus-prop" translate="no">focus</a> on an item and its enclosing <a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a> objects. In the following example, the <code translate="no">input</code> and <code translate="no">focusScope</code> objects will have active focus, while the root rectangle object will not.</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+
+ <span class="type"><a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a></span> {
+ <span class="name">id</span>: <span class="name">focusScope</span>
+ <span class="name">focus</span>: <span class="number">true</span>
+
+ <span class="type"><a href="qml-qtquick-textinput.html" translate="no">TextInput</a></span> {
+ <span class="name">id</span>: <span class="name">input</span>
+ <span class="name">focus</span>: <span class="number">true</span>
+ }
+ }
+ }
+</pre>
+<p><b>See also </b><a href="qml-qtquick-item.html#focus-prop" translate="no">focus</a> and <a href="qtquick-input-focus.html" translate="no">Keyboard Focus in Qt Quick</a>.</p>
+</div></div><!-- @@@activeFocus -->
+<br/>
+<!-- $$$activeFocusOnTab -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="activeFocusOnTab-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">activeFocusOnTab</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether the item wants to be in the tab focus chain. By default, this is set to <code translate="no">false</code>.</p>
+<p>The tab focus chain traverses elements by first visiting the parent, and then its children in the order they occur in the children property. Pressing the tab key on an item in the tab focus chain will move keyboard focus to the next item in the chain. Pressing BackTab (normally Shift+Tab) will move focus to the previous item.</p>
+<p>To set up a manual tab focus chain, see <a href="qml-qtquick-keynavigation.html" translate="no">KeyNavigation</a>. Tab key events used by Keys or <a href="qml-qtquick-keynavigation.html" translate="no">KeyNavigation</a> have precedence over focus chain behavior; ignore the events in other key handlers to allow it to propagate.</p>
+</div></div><!-- @@@activeFocusOnTab -->
+<br/>
+<!-- $$$anchors -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="even" id="anchors-prop"><th class="centerAlign"><p><b>anchors group</b></p></th></tr>
+<tr valign="top" class="odd" id="anchors.alignWhenCentered-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.alignWhenCentered</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.baseline-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.baseline</span> : <span class="type">AnchorLine</span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.baselineOffset-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.baselineOffset</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.bottom-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.bottom</span> : <span class="type">AnchorLine</span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.bottomMargin-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.bottomMargin</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.centerIn-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.centerIn</span> : <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.fill-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.fill</span> : <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.horizontalCenter-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.horizontalCenter</span> : <span class="type">AnchorLine</span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.horizontalCenterOffset-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.horizontalCenterOffset</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.left-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.left</span> : <span class="type">AnchorLine</span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.leftMargin-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.leftMargin</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.margins-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.margins</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.right-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.right</span> : <span class="type">AnchorLine</span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.rightMargin-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.rightMargin</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.top-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.top</span> : <span class="type">AnchorLine</span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.topMargin-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.topMargin</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.verticalCenter-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.verticalCenter</span> : <span class="type">AnchorLine</span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.verticalCenterOffset-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.verticalCenterOffset</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Anchors provide a way to position an item by specifying its relationship with other items.</p>
+<p>Margins apply to top, bottom, left, right, and fill anchors. The <a href="qml-qtquick-item.html#anchors.margins-prop" translate="no">anchors.margins</a> property can be used to set all of the various margins at once, to the same value. It will not override a specific margin that has been previously set; to clear an explicit margin set its value to <code translate="no">undefined</code>. Note that margins are anchor-specific and are not applied if an item does not use anchors.</p>
+<p>Offsets apply for horizontal center, vertical center, and baseline anchors.</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/declarative-anchors_example.png" alt="" /></p></td><td >Text anchored to Image, horizontally centered and vertically below, with a margin.<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-image.html" translate="no">Image</a></span> {
+ <span class="name">id</span>: <span class="name">pic</span>
+ <span class="comment">// ...</span>
+ }
+ <span class="type"><a href="qml-qtquick-text.html" translate="no">Text</a></span> {
+ <span class="name">id</span>: <span class="name">label</span>
+ <span class="name">anchors</span>.horizontalCenter: <span class="name">pic</span>.<span class="name">horizontalCenter</span>
+ <span class="name">anchors</span>.top: <span class="name">pic</span>.<span class="name">bottom</span>
+ <span class="name">anchors</span>.topMargin: <span class="number">5</span>
+ <span class="comment">// ...</span>
+ }
+ }
+</pre>
+</td></tr>
+<tr valign="top" class="even"><td ><p class="centerAlign"><img src="images/declarative-anchors_example2.png" alt="" /></p></td><td >Left of Text anchored to right of Image, with a margin. The y property of both defaults to 0.<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-image.html" translate="no">Image</a></span> {
+ <span class="name">id</span>: <span class="name">pic</span>
+ <span class="comment">// ...</span>
+ }
+ <span class="type"><a href="qml-qtquick-text.html" translate="no">Text</a></span> {
+ <span class="name">id</span>: <span class="name">label</span>
+ <span class="name">anchors</span>.left: <span class="name">pic</span>.<span class="name">right</span>
+ <span class="name">anchors</span>.leftMargin: <span class="number">5</span>
+ <span class="comment">// ...</span>
+ }
+ }
+</pre>
+</td></tr>
+</table></div>
+<p><a href="qml-qtquick-item.html#anchors.fill-prop" translate="no">anchors.fill</a> provides a convenient way for one item to have the same geometry as another item, and is equivalent to connecting all four directional anchors.</p>
+<p>To clear an anchor value, set it to <code translate="no">undefined</code>.</p>
+<p><a href="qml-qtquick-item.html#anchors.alignWhenCentered-prop" translate="no">anchors.alignWhenCentered</a> (default <code translate="no">true</code>) forces centered anchors to align to a whole pixel; if the item being centered has an odd <a href="qml-qtquick-item.html#width-prop" translate="no">width</a> or <a href="qml-qtquick-item.html#height-prop" translate="no">height</a>, the item will be positioned on a whole pixel rather than being placed on a half-pixel. This ensures the item is painted crisply. There are cases where this is not desirable, for example when rotating the item jitters may be apparent as the center is rounded.</p>
+<div class="admonition note">
+<p><b>Note: </b>You can only anchor an item to siblings or a parent.</p>
+</div>
+<p>For more information see <a href="qtquick-positioning-anchors.html" translate="no">Anchor Layouts</a>.</p>
+</div></div><!-- @@@anchors -->
+<br/>
+<!-- $$$antialiasing -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="antialiasing-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">antialiasing</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Used by visual elements to decide if the item should use antialiasing or not. In some cases items with antialiasing require more memory and are potentially slower to render (see <a href="qtquick-visualcanvas-scenegraph-renderer.html#antialiasing" translate="no">Antialiasing</a> for more details).</p>
+<p>The default is false, but may be overridden by derived elements.</p>
+</div></div><!-- @@@antialiasing -->
+<br/>
+<!-- $$$baselineOffset -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="baselineOffset-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">baselineOffset</span> : <span class="type"><a href="../qtqml/qml-int.html" translate="no">int</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Specifies the position of the item's baseline in local coordinates.</p>
+<p>The baseline of a <a href="qml-qtquick-text.html" translate="no">Text</a> item is the imaginary line on which the text sits. Controls containing text usually set their baseline to the baseline of their text.</p>
+<p>For non-text items, a default baseline offset of 0 is used.</p>
+</div></div><!-- @@@baselineOffset -->
+<br/>
+<!-- $$$childrenRect -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="even" id="childrenRect-prop"><th class="centerAlign"><p><b>childrenRect group</b></p></th></tr>
+<tr valign="top" class="odd" id="childrenRect.height-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">childrenRect.height</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <code class="details extra" translate="no">[read-only]</code></p></td></tr>
+<tr valign="top" class="odd" id="childrenRect.width-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">childrenRect.width</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <code class="details extra" translate="no">[read-only]</code></p></td></tr>
+<tr valign="top" class="odd" id="childrenRect.x-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">childrenRect.x</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <code class="details extra" translate="no">[read-only]</code></p></td></tr>
+<tr valign="top" class="odd" id="childrenRect.y-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">childrenRect.y</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <code class="details extra" translate="no">[read-only]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This read-only property holds the collective position and size of the item's children.</p>
+<p>This property is useful if you need to access the collective geometry of an item's children in order to correctly size the item.</p>
+<p>The geometry that is returned is local to the item. For example:</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">x</span>: <span class="number">50</span>
+ <span class="name">y</span>: <span class="number">100</span>
+
+ <span class="comment">// prints: QRectF(-10, -20, 30, 40)</span>
+ <span class="name">Component</span>.onCompleted: <span class="name">print</span>(<span class="name">childrenRect</span>)
+
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">x</span>: -<span class="number">10</span>
+ <span class="name">y</span>: -<span class="number">20</span>
+ <span class="name">width</span>: <span class="number">30</span>
+ <span class="name">height</span>: <span class="number">40</span>
+ }
+ }
+</pre>
+</div></div><!-- @@@childrenRect -->
+<br/>
+<!-- $$$clip -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="clip-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">clip</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether clipping is enabled. The default clip value is <code translate="no">false</code>.</p>
+<p>If clipping is enabled, an item will clip its own painting, as well as the painting of its children, to its bounding rectangle.</p>
+<div class="admonition note">
+<p><b>Note: </b>Clipping can affect rendering performance. See <a href="qtquick-visualcanvas-scenegraph-renderer.html#clipping" translate="no">Clipping</a> for more information.</p>
+</div>
+</div></div><!-- @@@clip -->
+<br/>
+<!-- $$$containmentMask -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="containmentMask-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">containmentMask</span> : <span class="type">QObject</span>*</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds an optional mask for the Item to be used in the <a href="qml-qtquick-item.html#contains-method" translate="no">contains</a>() method. Its main use is currently to determine whether a <a href="../qtgui/qpointerevent.html" translate="no">pointer event</a> has landed into the item or not.</p>
+<p>By default the <code translate="no">contains()</code> method will return true for any point within the Item's bounding box. <code translate="no">containmentMask</code> allows for more fine-grained control. For example, if a custom C++ <a href="qquickitem.html" translate="no">QQuickItem</a> subclass with a specialized <a href="qml-qtquick-item.html#contains-method" translate="no">contains</a>() method is used as containmentMask:</p>
+<pre class="cpp" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> { <span class="name">id</span>: <span class="name">item</span>; <span class="name">containmentMask</span>: <span class="name">AnotherItem</span> { <span class="name">id</span>: <span class="name">anotherItem</span> } }
+</pre>
+<p><i>item</i>'s contains method would then return <code translate="no">true</code> only if <i>anotherItem</i>'s <a href="qml-qtquick-item.html#contains-method" translate="no">contains</a>() implementation returns <code translate="no">true</code>.</p>
+<p>A <a href="qml-qtquick-shapes-shape.html" translate="no">Shape</a> can be used as a mask, to make an item react to <a href="../qtgui/qpointerevent.html" translate="no">pointer events</a> only within a non-rectangular region:</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/containmentMask-shape.gif" alt="" /></p></td><td ><pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">width</span>: <span class="number">90</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="name">color</span>: <span class="name">hoverHandler</span>.<span class="name">hovered</span> ? <span class="string">&quot;wheat&quot;</span> : <span class="string">&quot;lightgray&quot;</span>
+ <span class="name">containmentMask</span>: <span class="name">shape</span>
+
+ <span class="type"><a href="qml-qtquick-hoverhandler.html" translate="no">HoverHandler</a></span> { <span class="name">id</span>: <span class="name">hoverHandler</span> }
+
+ <span class="type"><a href="qml-qtquick-shapes-shape.html" translate="no">Shape</a></span> {
+ <span class="name">id</span>: <span class="name">shape</span>
+ <span class="name">containsMode</span>: <span class="name">Shape</span>.<span class="name">FillContains</span>
+
+ <span class="type"><a href="qml-qtquick-shapes-shapepath.html" translate="no">ShapePath</a></span> {
+ <span class="name">fillColor</span>: <span class="string">&quot;lightsteelblue&quot;</span>
+ <span class="name">startX</span>: <span class="number">10</span>; <span class="name">startY</span>: <span class="number">20</span>
+ <span class="type"><a href="qml-qtquick-patharc.html" translate="no">PathArc</a></span> {
+ <span class="name">x</span>: <span class="number">10</span>; <span class="name">y</span>: <span class="number">80</span>
+ <span class="name">radiusX</span>: <span class="number">40</span>; <span class="name">radiusY</span>: <span class="number">40</span>
+ <span class="name">useLargeArc</span>: <span class="number">true</span>
+ }
+ <span class="type"><a href="qml-qtquick-pathline.html" translate="no">PathLine</a></span> {
+ <span class="name">x</span>: <span class="number">10</span>; <span class="name">y</span>: <span class="number">20</span>
+ }
+ }
+ }
+ }
+</pre>
+</td></tr>
+</table></div>
+<p>It is also possible to define the contains method in QML. For example, to create a circular item that only responds to events within its actual bounds:</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/containmentMask-circle.gif" alt="" /></p></td><td ><pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">id</span>: <span class="name">circle</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="name">width</span>
+ <span class="name">radius</span>: <span class="name">width</span> <span class="operator">/</span> <span class="number">2</span>
+ <span class="name">color</span>: <span class="name">tapHandler</span>.<span class="name">pressed</span> ? <span class="string">&quot;tomato&quot;</span> : <span class="name">hoverHandler</span>.<span class="name">hovered</span> ? <span class="string">&quot;darkgray&quot;</span> : <span class="string">&quot;lightgray&quot;</span>
+
+ <span class="type"><a href="qml-qtquick-taphandler.html" translate="no">TapHandler</a></span> { <span class="name">id</span>: <span class="name">tapHandler</span> }
+ <span class="type"><a href="qml-qtquick-hoverhandler.html" translate="no">HoverHandler</a></span> { <span class="name">id</span>: <span class="name">hoverHandler</span> }
+
+ <span class="name">containmentMask</span>: <span class="name">QtObject</span> {
+ property <span class="type">alias</span> <span class="name">radius</span>: <span class="name">circle</span>.<span class="name">radius</span>
+ <span class="keyword">function </span><span class="name">contains</span>(point: <span class="name">point</span>) : bool {
+ <span class="keyword">return</span> (<span class="name">Math</span>.<span class="name">pow</span>(<span class="name">point</span>.<span class="name">x</span> <span class="operator">-</span> <span class="name">radius</span>, <span class="number">2</span>) <span class="operator">+</span> <span class="name">Math</span>.<span class="name">pow</span>(<span class="name">point</span>.<span class="name">y</span> <span class="operator">-</span> <span class="name">radius</span>, <span class="number">2</span>)) <span class="operator">&lt;</span> <span class="name">Math</span>.<span class="name">pow</span>(<span class="name">radius</span>, <span class="number">2</span>)
+ }
+ }
+ }
+</pre>
+</td></tr>
+</table></div>
+<p><b>See also </b><a href="qtquick-shapes-example.html" translate="no">Qt Quick Examples - Shapes</a>.</p>
+</div></div><!-- @@@containmentMask -->
+<br/>
+<!-- $$$data -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="data-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">data</span> : <span class="type"><a href="../qtqml/qml-list.html" translate="no">list</a></span>&lt;<span class="type"><a href="../qtqml/qml-qtqml-qtobject.html" translate="no">QtObject</a></span>&gt; <code class="details extra" translate="no">[default]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>The data property allows you to freely mix visual children and resources in an item. If you assign a visual item to the data list it becomes a child and if you assign any other object type, it is added as a resource.</p>
+<p>So you can write:</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-text.html" translate="no">Text</a></span> {}
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {}
+ <span class="type"><a href="../qtqml/qml-qtqml-timer.html" translate="no">Timer</a></span> {}
+ }
+</pre>
+<p>instead of:</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">children</span>: [
+ <span class="type"><a href="qml-qtquick-text.html" translate="no">Text</a></span> {},
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {}
+ ]
+ <span class="name">resources</span>: [
+ <span class="type"><a href="../qtqml/qml-qtqml-timer.html" translate="no">Timer</a></span> {}
+ ]
+ }
+</pre>
+<p>It should not generally be necessary to refer to the <code translate="no">data</code> property, as it is the default property for Item and thus all child items are automatically assigned to this property.</p>
+</div></div><!-- @@@data -->
+<br/>
+<!-- $$$enabled -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="enabled-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">enabled</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether the item receives mouse and keyboard events. By default this is true.</p>
+<p>Setting this property directly affects the <code translate="no">enabled</code> value of child items. When set to <code translate="no">false</code>, the <code translate="no">enabled</code> values of all child items also become <code translate="no">false</code>. When set to <code translate="no">true</code>, the <code translate="no">enabled</code> values of child items are returned to <code translate="no">true</code>, unless they have explicitly been set to <code translate="no">false</code>.</p>
+<p>Setting this property to <code translate="no">false</code> automatically causes <a href="qml-qtquick-item.html#activeFocus-prop" translate="no">activeFocus</a> to be set to <code translate="no">false</code>, and this item will no longer receive keyboard events.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#visible-prop" translate="no">visible</a>.</p>
+</div></div><!-- @@@enabled -->
+<br/>
+<!-- $$$focus -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="focus-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">focus</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether the item has focus within the enclosing <a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a>. If true, this item will gain active focus when the enclosing <a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a> gains active focus.</p>
+<p>In the following example, <code translate="no">input</code> will be given active focus when <code translate="no">scope</code> gains active focus:</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+
+ <span class="type"><a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a></span> {
+ <span class="name">id</span>: <span class="name">scope</span>
+
+ <span class="type"><a href="qml-qtquick-textinput.html" translate="no">TextInput</a></span> {
+ <span class="name">id</span>: <span class="name">input</span>
+ <span class="name">focus</span>: <span class="number">true</span>
+ }
+ }
+ }
+</pre>
+<p>For the purposes of this property, the scene as a whole is assumed to act like a focus scope. On a practical level, that means the following QML will give active focus to <code translate="no">input</code> on startup.</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+
+ <span class="type"><a href="qml-qtquick-textinput.html" translate="no">TextInput</a></span> {
+ <span class="name">id</span>: <span class="name">input</span>
+ <span class="name">focus</span>: <span class="number">true</span>
+ }
+ }
+</pre>
+<p><b>See also </b><a href="qml-qtquick-item.html#activeFocus-prop" translate="no">activeFocus</a> and <a href="qtquick-input-focus.html" translate="no">Keyboard Focus in Qt Quick</a>.</p>
+</div></div><!-- @@@focus -->
+<br/>
+<!-- $$$focusPolicy -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="focusPolicy-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">focusPolicy</span> : <span class="type"><a href="../qtqml/qml-enumeration.html" translate="no">enumeration</a></span> <code class="details extra" translate="no">[since 6.7]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property determines the way the item accepts focus.</p>
+<div class="table"><table class="valuelist"><tr valign="top" class="odd"><th class="tblConst">Constant</th><th class="tbldscr">Description</th></tr>
+<tr><td class="topAlign"><code translate="no">Qt.TabFocus</code></td><td class="topAlign">The item accepts focus by tabbing.</td></tr>
+<tr><td class="topAlign"><code translate="no">Qt.ClickFocus</code></td><td class="topAlign">The item accepts focus by clicking.</td></tr>
+<tr><td class="topAlign"><code translate="no">Qt.StrongFocus</code></td><td class="topAlign">The item accepts focus by both tabbing and clicking.</td></tr>
+<tr><td class="topAlign"><code translate="no">Qt.WheelFocus</code></td><td class="topAlign">The item accepts focus by tabbing, clicking, and using the mouse wheel.</td></tr>
+<tr><td class="topAlign"><code translate="no">Qt.NoFocus</code></td><td class="topAlign">The item does not accept focus.</td></tr>
+</table></div>
+<div class="admonition note">
+<p><b>Note: </b>This property was a member of Control until Qt 6.7.</p>
+</div>
+<p>This property was introduced in Qt 6.7.</p>
+</div></div><!-- @@@focusPolicy -->
+<br/>
+<!-- $$$layer.effect -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.effect-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.effect</span> : <span class="type">Component</span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Holds the effect that is applied to this layer.</p>
+<p>The effect is typically a <a href="qml-qtquick-shadereffect.html" translate="no">ShaderEffect</a> component, although any <a href="qml-qtquick-item.html" translate="no">Item</a> component can be assigned. The effect should have a source texture property with a name matching <a href="qml-qtquick-item.html#layer.samplerName-prop" translate="no">layer.samplerName</a>.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#layer.samplerName-prop" translate="no">layer.samplerName</a> and <a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.effect -->
+<br/>
+<!-- $$$layer.enabled -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.enabled-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.enabled</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Holds whether the item is layered or not. Layering is disabled by default.</p>
+<p>A layered item is rendered into an offscreen surface and cached until it is changed. Enabling layering for complex QML item hierarchies can sometimes be an optimization.</p>
+<p>None of the other layer properties have any effect when the layer is disabled.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.enabled -->
+<br/>
+<!-- $$$layer.format -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.format-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.format</span> : <span class="type"><a href="../qtqml/qml-enumeration.html" translate="no">enumeration</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property defines the format of the backing texture. Modifying this property makes most sense when the <i translate="no">layer.effect</i> is also specified.</p>
+<div class="table"><table class="valuelist"><tr valign="top" class="odd"><th class="tblConst">Constant</th><th class="tbldscr">Description</th></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.RGBA8</code></td><td class="topAlign">&nbsp;</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.RGBA16F</code></td><td class="topAlign">&nbsp;</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.RGBA32F</code></td><td class="topAlign">&nbsp;</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.Alpha</code></td><td class="topAlign">Starting with Qt 6.0, this value is not in use and has the same effect as <code translate="no">RGBA8</code> in practice.</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.RGB</code></td><td class="topAlign">Starting with Qt 6.0, this value is not in use and has the same effect as <code translate="no">RGBA8</code> in practice.</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.RGBA</code></td><td class="topAlign">Starting with Qt 6.0, this value is not in use and has the same effect as <code translate="no">RGBA8</code> in practice.</td></tr>
+</table></div>
+<p><b>See also </b><a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.format -->
+<br/>
+<!-- $$$layer.live -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.live-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.live</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span> <code class="details extra" translate="no">[since 6.5]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>When this property is true the layer texture is updated whenever the item updates. Otherwise it will always be a frozen image.</p>
+<p>By default, this property is set to <code translate="no">true</code>.</p>
+<p>This property was introduced in Qt 6.5.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.live -->
+<br/>
+<!-- $$$layer.mipmap -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.mipmap-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.mipmap</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>If this property is true, mipmaps are generated for the texture.</p>
+<div class="admonition note">
+<p><b>Note: </b>Some OpenGL ES 2 implementations do not support mipmapping of non-power-of-two textures.</p>
+</div>
+<p><b>See also </b><a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.mipmap -->
+<br/>
+<!-- $$$layer.samplerName -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.samplerName-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.samplerName</span> : <span class="type"><a href="../qtqml/qml-string.html" translate="no">string</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Holds the name of the effect's source texture property.</p>
+<p>This value must match the name of the effect's source texture property so that the Item can pass the layer's offscreen surface to the effect correctly.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#layer.effect-prop" translate="no">layer.effect</a>, <a href="qml-qtquick-shadereffect.html" translate="no">ShaderEffect</a>, and <a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.samplerName -->
+<br/>
+<!-- $$$layer.samples -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.samples-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.samples</span> : <span class="type"><a href="../qtqml/qml-enumeration.html" translate="no">enumeration</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property allows requesting multisampled rendering in the layer.</p>
+<p>By default multisampling is enabled whenever multisampling is enabled for the entire window, assuming the scenegraph renderer in use and the underlying graphics API supports this.</p>
+<p>By setting the value to 2, 4, etc. multisampled rendering can be requested for a part of the scene without enabling multisampling for the entire scene. This way multisampling is applied only to a given subtree, which can lead to significant performance gains since multisampling is not applied to other parts of the scene.</p>
+<div class="admonition note">
+<p><b>Note: </b>Enabling multisampling can be potentially expensive regardless of the layer's size, as it incurs a hardware and driver dependent performance and memory cost.</p>
+</div>
+<div class="admonition note">
+<p><b>Note: </b>This property is only functional when support for multisample renderbuffers and framebuffer blits is available. Otherwise the value is silently ignored.</p>
+</div>
+</div></div><!-- @@@layer.samples -->
+<br/>
+<!-- $$$layer.smooth -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.smooth-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.smooth</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Holds whether the layer is smoothly transformed. When enabled, sampling the layer's texture is performed using <code translate="no">linear</code> interpolation, while non-smooth results in using the <code translate="no">nearest</code> filtering mode.</p>
+<p>By default, this property is set to <code translate="no">false</code>.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.smooth -->
+<br/>
+<!-- $$$layer.sourceRect -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.sourceRect-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.sourceRect</span> : <span class="type"><a href="../qtqml/qml-rect.html" translate="no">rect</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property defines the rectangular area of the item that should be rendered into the texture. The source rectangle can be larger than the item itself. If the rectangle is null, which is the default, then the whole item is rendered to the texture.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.sourceRect -->
+<br/>
+<!-- $$$layer.textureMirroring -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.textureMirroring-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.textureMirroring</span> : <span class="type"><a href="../qtqml/qml-enumeration.html" translate="no">enumeration</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property defines how the generated texture should be mirrored. The default value is <code translate="no">ShaderEffectSource.MirrorVertically</code>. Custom mirroring can be useful if the generated texture is directly accessed by custom shaders, such as those specified by <a href="qml-qtquick-shadereffect.html" translate="no">ShaderEffect</a>. If no effect is specified for the layered item, mirroring has no effect on the UI representation of the item.</p>
+<div class="table"><table class="valuelist"><tr valign="top" class="odd"><th class="tblConst">Constant</th><th class="tbldscr">Description</th></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.NoMirroring</code></td><td class="topAlign">No mirroring</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.MirrorHorizontally</code></td><td class="topAlign">The generated texture is flipped along X-axis.</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.MirrorVertically</code></td><td class="topAlign">The generated texture is flipped along Y-axis.</td></tr>
+</table></div>
+</div></div><!-- @@@layer.textureMirroring -->
+<br/>
+<!-- $$$layer.textureSize -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.textureSize-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.textureSize</span> : <span class="type"><a href="../qtqml/qml-size.html" translate="no">size</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the requested pixel size of the layers texture. If it is empty, which is the default, the size of the item is used.</p>
+<div class="admonition note">
+<p><b>Note: </b>Some platforms have a limit on how small framebuffer objects can be, which means the actual texture size might be larger than the requested size.</p>
+</div>
+<p><b>See also </b><a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.textureSize -->
+<br/>
+<!-- $$$layer.wrapMode -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.wrapMode-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.wrapMode</span> : <span class="type"><a href="../qtqml/qml-enumeration.html" translate="no">enumeration</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property defines the wrap modes associated with the texture. Modifying this property makes most sense when the <i translate="no">layer.effect</i> is specified.</p>
+<div class="table"><table class="valuelist"><tr valign="top" class="odd"><th class="tblConst">Constant</th><th class="tbldscr">Description</th></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.ClampToEdge</code></td><td class="topAlign">GL_CLAMP_TO_EDGE both horizontally and vertically</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.RepeatHorizontally</code></td><td class="topAlign">GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.RepeatVertically</code></td><td class="topAlign">GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.Repeat</code></td><td class="topAlign">GL_REPEAT both horizontally and vertically</td></tr>
+</table></div>
+<div class="admonition note">
+<p><b>Note: </b>Some OpenGL ES 2 implementations do not support the GL_REPEAT wrap mode with non-power-of-two textures.</p>
+</div>
+<p><b>See also </b><a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.wrapMode -->
+<br/>
+<!-- $$$opacity -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="opacity-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">opacity</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the opacity of the item. Opacity is specified as a number between 0.0 (fully transparent) and 1.0 (fully opaque). The default value is 1.0.</p>
+<p>When this property is set, the specified opacity is also applied individually to child items. This may have an unintended effect in some circumstances. For example in the second set of rectangles below, the red rectangle has specified an opacity of 0.5, which affects the opacity of its blue child rectangle even though the child has not specified an opacity.</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/declarative-item_opacity1.png" alt="" /></p></td><td ><pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">x</span>: <span class="number">50</span>; <span class="name">y</span>: <span class="number">50</span>; <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ }
+ }
+ }
+</pre>
+</td></tr>
+<tr valign="top" class="even"><td ><p class="centerAlign"><img src="images/declarative-item_opacity2.png" alt="" /></p></td><td ><pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">opacity</span>: <span class="number">0.5</span>
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">x</span>: <span class="number">50</span>; <span class="name">y</span>: <span class="number">50</span>; <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ }
+ }
+ }
+</pre>
+</td></tr>
+</table></div>
+<p>Changing an item's opacity does not affect whether the item receives user input events. (In contrast, setting <a href="qml-qtquick-item.html#visible-prop" translate="no">visible</a> property to <code translate="no">false</code> stops mouse events, and setting the <a href="qml-qtquick-item.html#enabled-prop" translate="no">enabled</a> property to <code translate="no">false</code> stops mouse and keyboard events, and also removes active focus from the item.)</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#visible-prop" translate="no">visible</a>.</p>
+</div></div><!-- @@@opacity -->
+<br/>
+<!-- $$$palette -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="palette-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">palette</span> : <span class="type"><a href="qml-qtquick-palette.html" translate="no">Palette</a></span> <code class="details extra" translate="no">[since 6.0]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the palette currently set for the item.</p>
+<p>This property describes the item's requested palette. The palette is used by the item's style when rendering all controls, and is available as a means to ensure that custom controls can maintain consistency with the native platform's native look and feel. It's common that different platforms, or different styles, define different palettes for an application.</p>
+<p>The default palette depends on the system environment. <a href="../qtquickcontrols/qml-qtquick-controls-applicationwindow.html" translate="no">ApplicationWindow</a> maintains a system/theme palette which serves as a default for all controls. There may also be special palette defaults for certain types of controls. You can also set the default palette for controls by either:</p>
+<ul>
+<li>passing a custom palette to <a href="../qtgui/qguiapplication.html#setPalette" translate="no">QGuiApplication::setPalette</a>(), before loading any QML; or</li>
+<li>specifying the colors in the <a href="../qtquickcontrols/qtquickcontrols-configuration.html" translate="no">qtquickcontrols2.conf file</a>.</li>
+</ul>
+<p>Items propagate explicit palette properties from parents to children. If you change a specific property on a items's palette, that property propagates to all of the item's children, overriding any system defaults for that property.</p>
+<pre class="cpp" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type">palette</span> {
+ <span class="name">buttonText</span>: <span class="string">&quot;maroon&quot;</span>
+ <span class="name">button</span>: <span class="string">&quot;lavender&quot;</span>
+ }
+
+ <span class="type"><a href="../qtquickcontrols/qml-qtquick-controls-button.html" translate="no">Button</a></span> {
+ <span class="name">text</span>: <span class="string">&quot;Click Me&quot;</span>
+ }
+ }
+</pre>
+<p>This property was introduced in Qt 6.0.</p>
+<p><b>See also </b><a href="qml-qtquick-window.html#palette-prop" translate="no">Window::palette</a>, <a href="../qtquickcontrols/qml-qtquick-controls-popup.html#palette-prop" translate="no">Popup::palette</a>, <a href="qml-qtquick-colorgroup.html" translate="no">ColorGroup</a>, <a href="qml-qtquick-palette.html" translate="no">Palette</a>, and <a href="qml-qtquick-systempalette.html" translate="no">SystemPalette</a>.</p>
+</div></div><!-- @@@palette -->
+<br/>
+<!-- $$$parent -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="parent-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">parent</span> : <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the visual parent of the item.</p>
+<div class="admonition note">
+<p><b>Note: </b>The concept of the <i>visual parent</i> differs from that of the <i><a href="../qtcore/qobject.html" translate="no">QObject</a> parent</i>. An item's visual parent may not necessarily be the same as its object parent. See <a href="qtquick-visualcanvas-visualparent.html" translate="no">Concepts - Visual Parent in Qt Quick</a> for more details.</p>
+</div>
+</div></div><!-- @@@parent -->
+<br/>
+<!-- $$$rotation -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="rotation-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">rotation</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the rotation of the item in degrees clockwise around its <a href="qml-qtquick-item.html#transformOrigin-prop" translate="no">transformOrigin</a>.</p>
+<p>The default value is 0 degrees (that is, no rotation).</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/declarative-rotation.png" alt="" /></p></td><td ><pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">x</span>: <span class="number">25</span>; <span class="name">y</span>: <span class="number">25</span>; <span class="name">width</span>: <span class="number">50</span>; <span class="name">height</span>: <span class="number">50</span>
+ <span class="name">rotation</span>: <span class="number">30</span>
+ }
+ }
+</pre>
+</td></tr>
+</table></div>
+<p><b>See also </b><a href="qml-qtquick-transform.html" translate="no">Transform</a> and <a href="qml-qtquick-rotation.html" translate="no">Rotation</a>.</p>
+</div></div><!-- @@@rotation -->
+<br/>
+<!-- $$$scale -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="scale-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">scale</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the scale factor for this item.</p>
+<p>A scale of less than 1.0 causes the item to be rendered at a smaller size, and a scale greater than 1.0 renders the item at a larger size. A negative scale causes the item to be mirrored when rendered.</p>
+<p>The default value is 1.0.</p>
+<p>Scaling is applied from the <a href="qml-qtquick-item.html#transformOrigin-prop" translate="no">transformOrigin</a>.</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/declarative-scale.png" alt="" /></p></td><td ><pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;green&quot;</span>
+ <span class="name">width</span>: <span class="number">25</span>; <span class="name">height</span>: <span class="number">25</span>
+ }
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">x</span>: <span class="number">25</span>; <span class="name">y</span>: <span class="number">25</span>; <span class="name">width</span>: <span class="number">50</span>; <span class="name">height</span>: <span class="number">50</span>
+ <span class="name">scale</span>: <span class="number">1.4</span>
+ <span class="name">transformOrigin</span>: <span class="name">Item</span>.<span class="name">TopLeft</span>
+ }
+ }
+</pre>
+</td></tr>
+</table></div>
+<p><b>See also </b><a href="qml-qtquick-transform.html" translate="no">Transform</a> and <a href="qml-qtquick-scale.html" translate="no">Scale</a>.</p>
+</div></div><!-- @@@scale -->
+<br/>
+<!-- $$$smooth -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="smooth-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">smooth</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Primarily used in image based items to decide if the item should use smooth sampling or not. Smooth sampling is performed using linear interpolation, while non-smooth is performed using nearest neighbor.</p>
+<p>In Qt Quick 2.0, this property has minimal impact on performance.</p>
+<p>By default, this property is set to <code translate="no">true</code>.</p>
+</div></div><!-- @@@smooth -->
+<br/>
+<!-- $$$state -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="state-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">state</span> : <span class="type"><a href="../qtqml/qml-string.html" translate="no">string</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the name of the current state of the item.</p>
+<p>If the item is in its default state, that is, no explicit state has been set, then this property holds an empty string. Likewise, you can return an item to its default state by setting this property to an empty string.</p>
+<p><b>See also </b><a href="qtquick-statesanimations-states.html" translate="no">Qt Quick States</a>.</p>
+</div></div><!-- @@@state -->
+<br/>
+<!-- $$$states -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="states-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">states</span> : <span class="type"><a href="../qtqml/qml-list.html" translate="no">list</a></span>&lt;<span class="type"><a href="qml-qtquick-state.html" translate="no">State</a></span>&gt;</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the list of possible states for this item. To change the state of this item, set the <a href="qml-qtquick-item.html#state-prop" translate="no">state</a> property to one of these states, or set the <a href="qml-qtquick-item.html#state-prop" translate="no">state</a> property to an empty string to revert the item to its default state.</p>
+<p>This property is specified as a list of <a href="qml-qtquick-state.html" translate="no">State</a> objects. For example, below is an item with &quot;red_color&quot; and &quot;blue_color&quot; states:</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">id</span>: <span class="name">root</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+
+ <span class="name">states</span>: [
+ <span class="type"><a href="qml-qtquick-state.html" translate="no">State</a></span> {
+ <span class="name">name</span>: <span class="string">&quot;red_color&quot;</span>
+ <span class="type"><a href="qml-qtquick-propertychanges.html" translate="no">PropertyChanges</a></span> { <span class="name">root</span>.color: <span class="string">&quot;red&quot;</span> }
+ },
+ <span class="type"><a href="qml-qtquick-state.html" translate="no">State</a></span> {
+ <span class="name">name</span>: <span class="string">&quot;blue_color&quot;</span>
+ <span class="type"><a href="qml-qtquick-propertychanges.html" translate="no">PropertyChanges</a></span> { <span class="name">root</span>.color: <span class="string">&quot;blue&quot;</span> }
+ }
+ ]
+ }
+</pre>
+<p>See <a href="qtquick-statesanimations-states.html" translate="no">Qt Quick States</a> and <a href="qtquick-statesanimations-animations.html" translate="no">Animation and Transitions in Qt Quick</a> for more details on using states and transitions.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#transitions-prop" translate="no">transitions</a>.</p>
+</div></div><!-- @@@states -->
+<br/>
+<!-- $$$transform -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="transform-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">transform</span> : <span class="type"><a href="../qtqml/qml-list.html" translate="no">list</a></span>&lt;<span class="type"><a href="qml-qtquick-transform.html" translate="no">Transform</a></span>&gt; <code class="details extra" translate="no">[read-only]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the list of transformations to apply.</p>
+<p>For more information see <a href="qml-qtquick-transform.html" translate="no">Transform</a>.</p>
+</div></div><!-- @@@transform -->
+<br/>
+<!-- $$$transformOrigin -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="transformOrigin-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">transformOrigin</span> : <span class="type"><a href="../qtqml/qml-enumeration.html" translate="no">enumeration</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the origin point around which scale and rotation transform.</p>
+<p>Nine transform origins are available, as shown in the image below. The default transform origin is <code translate="no">Item.Center</code>.</p>
+<p class="centerAlign"><img src="images/declarative-transformorigin.png" alt="" /></p><p>This example rotates an image around its bottom-right corner.</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-image.html" translate="no">Image</a></span> {
+ <span class="name">source</span>: <span class="string">&quot;myimage.png&quot;</span>
+ <span class="name">transformOrigin</span>: <span class="name">Item</span>.<span class="name">BottomRight</span>
+ <span class="name">rotation</span>: <span class="number">45</span>
+ }
+</pre>
+<p>To set an arbitrary transform origin point use the <a href="qml-qtquick-scale.html" translate="no">Scale</a> or <a href="qml-qtquick-rotation.html" translate="no">Rotation</a> transform types with <a href="qml-qtquick-item.html#transform-prop" translate="no">transform</a>.</p>
+</div></div><!-- @@@transformOrigin -->
+<br/>
+<!-- $$$transitions -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="transitions-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">transitions</span> : <span class="type"><a href="../qtqml/qml-list.html" translate="no">list</a></span>&lt;<span class="type"><a href="qml-qtquick-transition.html" translate="no">Transition</a></span>&gt;</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the list of transitions for this item. These define the transitions to be applied to the item whenever it changes its <a href="qml-qtquick-item.html#state-prop" translate="no">state</a>.</p>
+<p>This property is specified as a list of <a href="qml-qtquick-transition.html" translate="no">Transition</a> objects. For example:</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">transitions</span>: [
+ <span class="type"><a href="qml-qtquick-transition.html" translate="no">Transition</a></span> {
+ <span class="comment">//...</span>
+ },
+ <span class="type"><a href="qml-qtquick-transition.html" translate="no">Transition</a></span> {
+ <span class="comment">//...</span>
+ }
+ ]
+ }
+</pre>
+<p>See <a href="qtquick-statesanimations-states.html" translate="no">Qt Quick States</a> and <a href="qtquick-statesanimations-animations.html" translate="no">Animation and Transitions in Qt Quick</a> for more details on using states and transitions.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#states-prop" translate="no">states</a>.</p>
+</div></div><!-- @@@transitions -->
+<br/>
+<!-- $$$visible -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="visible-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">visible</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether the item is visible. By default this is true.</p>
+<p>Setting this property directly affects the <code translate="no">visible</code> value of child items. When set to <code translate="no">false</code>, the <code translate="no">visible</code> values of all child items also become <code translate="no">false</code>. When set to <code translate="no">true</code>, the <code translate="no">visible</code> values of child items are returned to <code translate="no">true</code>, unless they have explicitly been set to <code translate="no">false</code>.</p>
+<p>(Because of this flow-on behavior, using the <code translate="no">visible</code> property may not have the intended effect if a property binding should only respond to explicit property changes. In such cases it may be better to use the <a href="qml-qtquick-item.html#opacity-prop" translate="no">opacity</a> property instead.)</p>
+<p>If this property is set to <code translate="no">false</code>, the item will no longer receive mouse events, but will continue to receive key events and will retain the keyboard <a href="qml-qtquick-item.html#focus-prop" translate="no">focus</a> if it has been set. (In contrast, setting the <a href="qml-qtquick-item.html#enabled-prop" translate="no">enabled</a> property to <code translate="no">false</code> stops both mouse and keyboard events, and also removes focus from the item.)</p>
+<div class="admonition note">
+<p><b>Note: </b>This property's value is only affected by changes to this property or the parent's <code translate="no">visible</code> property. It does not change, for example, if this item moves off-screen, or if the <a href="qml-qtquick-item.html#opacity-prop" translate="no">opacity</a> changes to 0.</p>
+</div>
+<p><b>See also </b><a href="qml-qtquick-item.html#opacity-prop" translate="no">opacity</a> and <a href="qml-qtquick-item.html#enabled-prop" translate="no">enabled</a>.</p>
+</div></div><!-- @@@visible -->
+<br/>
+<!-- $$$visibleChildren -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="visibleChildren-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">visibleChildren</span> : <span class="type"><a href="../qtqml/qml-list.html" translate="no">list</a></span>&lt;<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span>&gt;</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This read-only property lists all of the item's children that are currently visible. Note that a child's visibility may have changed explicitly, or because the visibility of this (it's parent) item or another grandparent changed.</p>
+</div></div><!-- @@@visibleChildren -->
+<br/>
+<!-- $$$z -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="z-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">z</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Sets the stacking order of sibling items. By default the stacking order is 0.</p>
+<p>Items with a higher stacking value are drawn on top of siblings with a lower stacking order. Items with the same stacking value are drawn bottom up in the order they appear. Items with a negative stacking value are drawn under their parent's content.</p>
+<p>The following example shows the various effects of stacking order.</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/declarative-item_stacking1.png" alt="" /></p></td><td >Same <code translate="no">z</code> - later children above earlier children:<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ }
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">x</span>: <span class="number">50</span>; <span class="name">y</span>: <span class="number">50</span>; <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ }
+ }
+</pre>
+</td></tr>
+<tr valign="top" class="even"><td ><p class="centerAlign"><img src="images/declarative-item_stacking2.png" alt="" /></p></td><td >Higher <code translate="no">z</code> on top:<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">z</span>: <span class="number">1</span>
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ }
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">x</span>: <span class="number">50</span>; <span class="name">y</span>: <span class="number">50</span>; <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ }
+ }
+</pre>
+</td></tr>
+<tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/declarative-item_stacking3.png" alt="" /></p></td><td >Same <code translate="no">z</code> - children above parents:<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">x</span>: <span class="number">50</span>; <span class="name">y</span>: <span class="number">50</span>; <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ }
+ }
+ }
+</pre>
+</td></tr>
+<tr valign="top" class="even"><td ><p class="centerAlign"><img src="images/declarative-item_stacking4.png" alt="" /></p></td><td >Lower <code translate="no">z</code> below:<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">z</span>: -<span class="number">1</span>
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">x</span>: <span class="number">50</span>; <span class="name">y</span>: <span class="number">50</span>; <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ }
+ }
+ }
+</pre>
+</td></tr>
+</table></div>
+</div></div><!-- @@@z -->
+<br/>
+<h2>Method Documentation</h2>
+<!-- $$$ -->
+<div class="qmlitem"><div class="fngroup">
+<div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="mapFromItem-method-1">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <span class="name">mapFromItem</span>(<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> <i>item</i>, <span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <i>p</i>)</p></td></tr>
+<tr valign="top" class="odd" id="mapFromItem-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <span class="name">mapFromItem</span>(<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> <i>item</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>x</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>y</i>)</p></td></tr>
+<tr valign="top" class="odd" id="mapFromItem-method-2">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-rect.html" translate="no">rect</a></span> <span class="name">mapFromItem</span>(<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> <i>item</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>x</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>y</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>width</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>height</i>)</p></td></tr>
+<tr valign="top" class="odd" id="mapFromItem-method-3">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-rect.html" translate="no">rect</a></span> <span class="name">mapFromItem</span>(<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> <i>item</i>, <span class="type"><a href="../qtqml/qml-rect.html" translate="no">rect</a></span> <i>r</i>)</p></td></tr>
+</table></div></div>
+</div><div class="qmldoc"><p>Maps the point (<i translate="no">x</i>, <i translate="no">y</i>) or rect (<i translate="no">x</i>, <i translate="no">y</i>, <i translate="no">width</i>, <i translate="no">height</i>), which is in <i translate="no">item</i>'s coordinate system, to this item's coordinate system, and returns a <a href="../qtqml/qml-point.html" translate="no">point</a> or <a href="../qtqml/qml-rect.html" translate="no">rect</a> matching the mapped coordinate.</p>
+<p>The following properties of the item are used in the mapping: <a href="qml-qtquick-item.html#x-prop" translate="no">x</a>, <a href="qml-qtquick-item.html#y-prop" translate="no">y</a>, <a href="qml-qtquick-item.html#scale-prop" translate="no">scale</a>, <a href="qml-qtquick-item.html#rotation-prop" translate="no">rotation</a>, <a href="qml-qtquick-item.html#transformOrigin-prop" translate="no">transformOrigin</a>, and <a href="qml-qtquick-item.html#transform-prop" translate="no">transform</a>.</p>
+<p>If the items are part of different scenes, the mapping includes the relative position of the two scenes.</p>
+<p>If <i translate="no">item</i> is a <code translate="no">null</code> value, this maps the point or rect from the coordinate system of the <a href="qtquick-visualcanvas-coordinates.html#scene-coordinates" translate="no">scene</a>.</p>
+<p>The versions accepting point and rect are since Qt 5.15.</p>
+</div></div><!-- @@@ -->
+<br/>
+<!-- $$$ -->
+<div class="qmlitem"><div class="fngroup">
+<div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="mapToItem-method-1">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <span class="name">mapToItem</span>(<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> <i>item</i>, <span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <i>p</i>)</p></td></tr>
+<tr valign="top" class="odd" id="mapToItem-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <span class="name">mapToItem</span>(<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> <i>item</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>x</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>y</i>)</p></td></tr>
+<tr valign="top" class="odd" id="mapToItem-method-2">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-rect.html" translate="no">rect</a></span> <span class="name">mapToItem</span>(<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> <i>item</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>x</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>y</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>width</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>height</i>)</p></td></tr>
+<tr valign="top" class="odd" id="mapToItem-method-3">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-rect.html" translate="no">rect</a></span> <span class="name">mapToItem</span>(<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> <i>item</i>, <span class="type"><a href="../qtqml/qml-rect.html" translate="no">rect</a></span> <i>r</i>)</p></td></tr>
+</table></div></div>
+</div><div class="qmldoc"><p>Maps the point (<i translate="no">x</i>, <i translate="no">y</i>) or rect (<i translate="no">x</i>, <i translate="no">y</i>, <i translate="no">width</i>, <i translate="no">height</i>), which is in this item's coordinate system, to <i translate="no">item</i>'s coordinate system, and returns a <a href="../qtqml/qml-point.html" translate="no">point</a> or <a href="../qtqml/qml-rect.html" translate="no">rect</a> matching the mapped coordinate.</p>
+<p>The following properties of the item are used in the mapping: <a href="qml-qtquick-item.html#x-prop" translate="no">x</a>, <a href="qml-qtquick-item.html#y-prop" translate="no">y</a>, <a href="qml-qtquick-item.html#scale-prop" translate="no">scale</a>, <a href="qml-qtquick-item.html#rotation-prop" translate="no">rotation</a>, <a href="qml-qtquick-item.html#transformOrigin-prop" translate="no">transformOrigin</a>, and <a href="qml-qtquick-item.html#transform-prop" translate="no">transform</a>.</p>
+<p>If the items are part of different scenes, the mapping includes the relative position of the two scenes.</p>
+<p>If <i translate="no">item</i> is a <code translate="no">null</code> value, this maps the point or rect to the coordinate system of the <a href="qtquick-visualcanvas-coordinates.html#scene-coordinates" translate="no">scene</a>.</p>
+<p>The versions accepting point and rect are since Qt 5.15.</p>
+</div></div><!-- @@@ -->
+<br/>
+<!-- $$$childAt[overload1]$$$childAtrealreal -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="childAt-method">
+<td class="tblQmlFuncNode"><p>
+<span class="name">childAt</span>(<span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>x</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>y</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Returns the first visible child item found at point (<i translate="no">x</i>, <i translate="no">y</i>) within the coordinate system of this item.</p>
+<p>Returns <code translate="no">null</code> if there is no such item.</p>
+</div></div><!-- @@@childAt -->
+<br/>
+<!-- $$$contains[overload1]$$$containspoint -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="contains-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span> <span class="name">contains</span>(<span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <i>point</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Returns <code translate="no">true</code> if this item contains <i translate="no">point</i>, which is in local coordinates; returns <code translate="no">false</code> otherwise. This is the same check that is used for hit-testing a <a href="../qtgui/qeventpoint.html" translate="no">QEventPoint</a> during event delivery, and is affected by <a href="qml-qtquick-item.html#containmentMask-prop" translate="no">containmentMask</a> if it is set.</p>
+</div></div><!-- @@@contains -->
+<br/>
+<!-- $$$dumpItemTree[overload1]$$$dumpItemTree -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="dumpItemTree-method">
+<td class="tblQmlFuncNode"><p>
+<code class="details extra" translate="no">[since 6.3]</code> <span class="name">dumpItemTree</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Dumps some details about the <a href="qtquick-visualcanvas-visualparent.html" translate="no">visual tree of Items</a> starting with this item and its children, recursively.</p>
+<p>The output looks similar to that of this QML code:</p>
+<pre class="qml" translate="no">
+ function dump(object, indent) {
+ console.log(indent + object)
+ for (const i in object.children)
+ dump(object.children[i], indent + &quot; &quot;)
+ }
+
+ dump(myItem, &quot;&quot;)
+</pre>
+<p>So if you want more details, you can implement your own function and add extra output to the console.log, such as values of specific properties.</p>
+<p>This method was introduced in Qt 6.3.</p>
+<p><b>See also </b><a href="../qtcore/qobject.html#dumpObjectTree" translate="no">QObject::dumpObjectTree</a>().</p>
+</div></div><!-- @@@dumpItemTree -->
+<br/>
+<!-- $$$forceActiveFocus[overload1]$$$forceActiveFocus -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="forceActiveFocus-method">
+<td class="tblQmlFuncNode"><p>
+<span class="name">forceActiveFocus</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Forces active focus on the item.</p>
+<p>This method sets focus on the item and ensures that all ancestor <a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a> objects in the object hierarchy are also given <a href="qml-qtquick-item.html#focus-prop" translate="no">focus</a>.</p>
+<p>The reason for the focus change will be <a href="../qtcore/qt.html#FocusReason-enum" translate="no">Qt::OtherFocusReason</a>. Use the overloaded method to specify the focus reason to enable better handling of the focus change.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#activeFocus-prop" translate="no">activeFocus</a>.</p>
+</div></div><!-- @@@forceActiveFocus -->
+<br/>
+<!-- $$$forceActiveFocus$$$forceActiveFocusQt::FocusReason -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="forceActiveFocus-method-1">
+<td class="tblQmlFuncNode"><p>
+<span class="name">forceActiveFocus</span>(<span class="type">Qt::FocusReason</span> <i>reason</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This is an overloaded function.</p>
+<p>Forces active focus on the item with the given <i translate="no">reason</i>.</p>
+<p>This method sets focus on the item and ensures that all ancestor <a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a> objects in the object hierarchy are also given <a href="qml-qtquick-item.html#focus-prop" translate="no">focus</a>.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#activeFocus-prop" translate="no">activeFocus</a> and <a href="../qtcore/qt.html#FocusReason-enum" translate="no">Qt::FocusReason</a>.</p>
+</div></div><!-- @@@forceActiveFocus -->
+<br/>
+<!-- $$$grabToImage[overload1]$$$grabToImage -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="grabToImage-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span> <span class="name">grabToImage</span>(<i>callback</i>, <i>targetSize</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Grabs the item into an in-memory image.</p>
+<p>The grab happens asynchronously and the JavaScript function <i translate="no">callback</i> is invoked when the grab is completed. The callback takes one argument, which is the result of the grab operation; an <a href="qml-qtquick-itemgrabresult.html" translate="no">ItemGrabResult</a> object.</p>
+<p>Use <i translate="no">targetSize</i> to specify the size of the target image. By default, the result will have the same size as the item.</p>
+<p>If the grab could not be initiated, the function returns <code translate="no">false</code>.</p>
+<p>The following snippet shows how to grab an item and store the results in a file:</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">id</span>: <span class="name">sourceRectangle</span>
+ <span class="name">width</span>: <span class="number">100</span>
+ <span class="name">height</span>: <span class="number">100</span>
+ <span class="name">focus</span>: <span class="number">true</span>
+ <span class="name">gradient</span>: <span class="name">Gradient</span> {
+ <span class="type"><a href="qml-qtquick-gradientstop.html" translate="no">GradientStop</a></span> { <span class="name">position</span>: <span class="number">0</span>; <span class="name">color</span>: <span class="string">&quot;steelblue&quot;</span> }
+ <span class="type"><a href="qml-qtquick-gradientstop.html" translate="no">GradientStop</a></span> { <span class="name">position</span>: <span class="number">1</span>; <span class="name">color</span>: <span class="string">&quot;black&quot;</span> }
+ }
+
+ <span class="name">Keys</span>.onSpacePressed: {
+ <span class="name">sourceRectangle</span>.<span class="name">grabToImage</span>(<span class="keyword">function</span>(result) {
+ <span class="name">result</span>.<span class="name">saveToFile</span>(<span class="string">&quot;something.png&quot;</span>)
+ })
+ }
+ }
+</pre>
+<p>The following snippet shows how to grab an item and use the results in another image element:</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-image.html" translate="no">Image</a></span> {
+ <span class="name">id</span>: <span class="name">image</span>
+ }
+
+ <span class="name">Keys</span>.onSpacePressed: {
+ <span class="name">sourceRectangle</span>.<span class="name">grabToImage</span>(<span class="keyword">function</span>(result) {
+ <span class="name">image</span>.<span class="name">source</span> <span class="operator">=</span> <span class="name">result</span>.<span class="name">url</span>
+ }, <span class="name">Qt</span>.<span class="name">size</span>(<span class="number">50</span>, <span class="number">50</span>))
+ }
+</pre>
+<div class="admonition note">
+<p><b>Note: </b>This function will render the item to an offscreen surface and copy that surface from the GPU's memory into the CPU's memory, which can be quite costly. For &quot;live&quot; preview, use <a href="qml-qtquick-item.html#layer.enabled-prop" translate="no">layers</a> or <a href="qml-qtquick-shadereffectsource.html" translate="no">ShaderEffectSource</a>.</p>
+</div>
+</div></div><!-- @@@grabToImage -->
+<br/>
+<!-- $$$mapFromGlobal[overload1]$$$mapFromGlobalrealreal -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="mapFromGlobal-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <span class="name">mapFromGlobal</span>(<span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>x</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>y</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Maps the point (<i translate="no">x</i>, <i translate="no">y</i>), which is in the global coordinate system, to the item's coordinate system, and returns a <a href="../qtqml/qml-point.html" translate="no">point</a> matching the mapped coordinate.</p>
+<p>The following properties of the item are used in the mapping: <a href="qml-qtquick-item.html#x-prop" translate="no">x</a>, <a href="qml-qtquick-item.html#y-prop" translate="no">y</a>, <a href="qml-qtquick-item.html#scale-prop" translate="no">scale</a>, <a href="qml-qtquick-item.html#rotation-prop" translate="no">rotation</a>, <a href="qml-qtquick-item.html#transformOrigin-prop" translate="no">transformOrigin</a>, and <a href="qml-qtquick-item.html#transform-prop" translate="no">transform</a>.</p>
+<p>If the items are part of different scenes, the mapping includes the relative position of the two scenes.</p>
+</div></div><!-- @@@mapFromGlobal -->
+<br/>
+<!-- $$$mapToGlobal[overload1]$$$mapToGlobalrealreal -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="mapToGlobal-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <span class="name">mapToGlobal</span>(<span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>x</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>y</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Maps the point (<i translate="no">x</i>, <i translate="no">y</i>), which is in this item's coordinate system, to the global coordinate system, and returns a <a href="../qtqml/qml-point.html" translate="no">point</a> matching the mapped coordinate.</p>
+<p>The following properties of the item are used in the mapping: <a href="qml-qtquick-item.html#x-prop" translate="no">x</a>, <a href="qml-qtquick-item.html#y-prop" translate="no">y</a>, <a href="qml-qtquick-item.html#scale-prop" translate="no">scale</a>, <a href="qml-qtquick-item.html#rotation-prop" translate="no">rotation</a>, <a href="qml-qtquick-item.html#transformOrigin-prop" translate="no">transformOrigin</a>, and <a href="qml-qtquick-item.html#transform-prop" translate="no">transform</a>.</p>
+<p>If the items are part of different scenes, the mapping includes the relative position of the two scenes.</p>
+</div></div><!-- @@@mapToGlobal -->
+<br/>
+<!-- $$$nextItemInFocusChain[overload1]$$$nextItemInFocusChainbool -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="nextItemInFocusChain-method">
+<td class="tblQmlFuncNode"><p>
+<span class="name">nextItemInFocusChain</span>(<span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span> <i>forward</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Returns the item in the focus chain which is next to this item. If <i translate="no">forward</i> is <code translate="no">true</code>, or not supplied, it is the next item in the forwards direction. If <i translate="no">forward</i> is <code translate="no">false</code>, it is the next item in the backwards direction.</p>
+</div></div><!-- @@@nextItemInFocusChain -->
+<br/>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<div class="footer">
+ <p>
+ <acronym title="Copyright">&copy;</acronym> 2024 <span translate="no">The Qt Company Ltd.</span>
+ Documentation contributions included herein are the copyrights of
+ their respective owners.<br/> The documentation provided herein is licensed under the terms of the <a href="http://www.gnu.org/licenses/fdl.html">GNU Free Documentation License version 1.3</a> as published by the <span translate="no">Free Software Foundation</span>.<br/> <span translate="no">Qt</span> and respective logos are <a href="https://doc.qt.io/qt/trademarks.html"> trademarks</a> of <span translate="no">The Qt Company Ltd.</span> in Finland and/or other countries
+ worldwide. All other trademarks are property of their respective owners. </p>
+</div>
+</body>
+</html>
diff --git a/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtquick-mousearea.html b/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtquick-mousearea.html
new file mode 100644
index 0000000000..bbdd6345e8
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtquick-mousearea.html
@@ -0,0 +1,676 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qquickmousearea.cpp -->
+ <title>MouseArea QML Type | Qt Quick 5.15.16</title>
+ <link rel="stylesheet" type="text/css" href="style/offline-simple.css" />
+ <script type="text/javascript">
+ document.getElementsByTagName("link").item(0).setAttribute("href", "style/offline.css");
+ // loading style sheet breaks anchors that were jumped to before
+ // so force jumping to anchor again
+ setTimeout(function() {
+ var anchor = location.hash;
+ // need to jump to different anchor first (e.g. none)
+ location.hash = "#";
+ setTimeout(function() {
+ location.hash = anchor;
+ }, 0);
+ }, 0);
+ </script>
+</head>
+<body>
+<div class="header" id="qtdocheader">
+ <div class="main">
+ <div class="main-rounded">
+ <div class="navigationbar">
+ <ul>
+<li><a href="../qtdoc/index.html" translate="no">Qt 5.15</a></li>
+<li><a href="qtquick-index.html" translate="no">Qt Quick</a></li>
+<li><a href="qtquick-qmlmodule.html" translate="no">QML Types</a></li>
+<li>MouseArea QML Type</li>
+<li id="buildversion"><a href="qtquick-index.html" translate="no">Qt 5.15.16 Reference Documentation</a></li>
+ </ul>
+ </div>
+</div>
+<div class="content">
+<div class="line">
+<div class="content mainContent">
+<div class="sidebar">
+<div class="toc">
+<h3><a name="toc">Contents</a></h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#signals">Signals</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#example-usage">Example Usage</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">MouseArea QML Type</h1>
+<span class="subtitle" translate="no"></span>
+<!-- $$$MouseArea-brief -->
+<p>Enables simple mouse handling. <a href="#details">More...</a></p>
+<!-- @@@MouseArea -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QtQuick 2.15</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Inherits:</td><td class="memItemRight bottomAlign"> <p><a href="qml-qtquick-item.html" translate="no">Item</a></p>
+</td></tr></table></div><ul>
+<li><a href="qml-qtquick-mousearea-members.html">List of all members, including inherited members</a></li>
+</ul>
+<a name="properties"></a>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#acceptedButtons-prop" translate="no">acceptedButtons</a></b></b> : Qt::MouseButtons</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#containsMouse-prop" translate="no">containsMouse</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#containsPress-prop" translate="no">containsPress</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#cursorShape-prop" translate="no">cursorShape</a></b></b> : Qt::CursorShape</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag-prop" translate="no">drag</a></b></b><ul>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.active-prop" translate="no">drag.active</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.axis-prop" translate="no">drag.axis</a></b></b> : enumeration</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.filterChildren-prop" translate="no">drag.filterChildren</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.maximumX-prop" translate="no">drag.maximumX</a></b></b> : real</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.maximumY-prop" translate="no">drag.maximumY</a></b></b> : real</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.minimumX-prop" translate="no">drag.minimumX</a></b></b> : real</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.minimumY-prop" translate="no">drag.minimumY</a></b></b> : real</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.smoothed-prop" translate="no">drag.smoothed</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.target-prop" translate="no">drag.target</a></b></b> : Item</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.threshold-prop" translate="no">drag.threshold</a></b></b> : real</li>
+</ul>
+</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#enabled-prop" translate="no">enabled</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#mouseX-prop" translate="no">mouseX</a></b></b> : real</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#mouseY-prop" translate="no">mouseY</a></b></b> : real</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#pressAndHoldInterval-prop" translate="no">pressAndHoldInterval</a></b></b> : int</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#pressed-prop" translate="no">pressed</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#pressedButtons-prop" translate="no">pressedButtons</a></b></b> : MouseButtons</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#preventStealing-prop" translate="no">preventStealing</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#propagateComposedEvents-prop" translate="no">propagateComposedEvents</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#scrollGestureEnabled-prop" translate="no">scrollGestureEnabled</a></b></b> : bool</li>
+</ul>
+<a name="signals"></a>
+<h2 id="signals">Signals</h2>
+<ul>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#canceled-signal" translate="no">canceled</a></b></b>()</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#clicked-signal" translate="no">clicked</a></b></b>(MouseEvent <i>mouse</i>)</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#doubleClicked-signal" translate="no">doubleClicked</a></b></b>(MouseEvent <i>mouse</i>)</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#entered-signal" translate="no">entered</a></b></b>()</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#exited-signal" translate="no">exited</a></b></b>()</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#positionChanged-signal" translate="no">positionChanged</a></b></b>(MouseEvent <i>mouse</i>)</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#pressAndHold-signal" translate="no">pressAndHold</a></b></b>(MouseEvent <i>mouse</i>)</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#pressed-signal" translate="no">pressed</a></b></b>(MouseEvent <i>mouse</i>)</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#released-signal" translate="no">released</a></b></b>(MouseEvent <i>mouse</i>)</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#wheel-signal" translate="no">wheel</a></b></b>(WheelEvent <i>wheel</i>)</li>
+</ul>
+<!-- $$$MouseArea-description -->
+<a name="details"></a>
+<h2 id="details">Detailed Description</h2>
+<p>A MouseArea is an invisible item that is typically used in conjunction with a visible item in order to provide mouse handling for that item. By effectively acting as a proxy, the logic for mouse handling can be contained within a MouseArea item.</p>
+<p>The <a href="qml-qtquick-mousearea.html#enabled-prop" translate="no">enabled</a> property is used to enable and disable mouse handling for the proxied item. When disabled, the mouse area becomes transparent to mouse events.</p>
+<p>MouseArea is an invisible Item, but it has a visible property. When set to false, the mouse area becomes transparent to mouse events.</p>
+<p>The <a href="qml-qtquick-mousearea.html#pressed-signal" translate="no">pressed</a> read-only property indicates whether or not the user is holding down a mouse button over the mouse area. This property is often used in bindings between properties in a user interface. The <a href="qml-qtquick-mousearea.html#containsMouse-prop" translate="no">containsMouse</a> read-only property indicates the presence of the mouse cursor over the mouse area but, by default, only when a mouse button is held down; see the <a href="qml-qtquick-mousearea.html#containsMouse-prop" translate="no">containsMouse</a> documentation for details.</p>
+<p>Information about the mouse position and button clicks are provided via signals for which event handler properties are defined. The most commonly used involved handling mouse presses and clicks: onClicked, onDoubleClicked, onPressed, onReleased and onPressAndHold. It's also possible to handle mouse wheel events via the onWheel signal.</p>
+<p>If a MouseArea overlaps with the area of other MouseArea items, you can choose to propagate <code translate="no">clicked</code>, <code translate="no">doubleClicked</code> and <code translate="no">pressAndHold</code> events to these other items by setting <a href="qml-qtquick-mousearea.html#propagateComposedEvents-prop" translate="no">propagateComposedEvents</a> to true and rejecting events that should be propagated. See the <a href="qml-qtquick-mousearea.html#propagateComposedEvents-prop" translate="no">propagateComposedEvents</a> documentation for details.</p>
+<p>By default, MouseArea items only report mouse clicks and not changes to the position of the mouse cursor. Setting the <a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a> property ensures that handlers defined for onPositionChanged, onEntered and onExited are used and that the <a href="qml-qtquick-mousearea.html#containsMouse-prop" translate="no">containsMouse</a> property is updated even when no mouse buttons are pressed.</p>
+<a name="example-usage"></a>
+<h2 id="example-usage">Example Usage</h2>
+<div class="float-right"><p><img src="images/qml-mousearea-snippet.png" alt="" /></p>
+</div><p>The following example uses a MouseArea in a <a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a> that changes the <a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a> color to red when clicked:</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="name">color</span>: <span class="string">&quot;green&quot;</span>
+
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">onClicked</span>: { <span class="name">parent</span>.<span class="name">color</span> <span class="operator">=</span> <span class="string">'red'</span> }
+ }
+ }
+</pre>
+<br style="clear: both" /><p>Many MouseArea signals pass a <a href="qml-qtquick-mouseevent.html" translate="no">mouse</a> parameter that contains additional information about the mouse event, such as the position, button, and any key modifiers.</p>
+<p>Here is an extension of the previous example that produces a different color when the area is right clicked:</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="name">color</span>: <span class="string">&quot;green&quot;</span>
+
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">acceptedButtons</span>: <span class="name">Qt</span>.<span class="name">LeftButton</span> <span class="operator">|</span> <span class="name">Qt</span>.<span class="name">RightButton</span>
+ <span class="name">onClicked</span>: {
+ <span class="keyword">if</span> (<span class="name">mouse</span>.<span class="name">button</span> <span class="operator">==</span> <span class="name">Qt</span>.<span class="name">RightButton</span>)
+ <span class="name">parent</span>.<span class="name">color</span> <span class="operator">=</span> <span class="string">'blue'</span>;
+ <span class="keyword">else</span>
+ <span class="name">parent</span>.<span class="name">color</span> <span class="operator">=</span> <span class="string">'red'</span>;
+ }
+ }
+ }
+</pre>
+<p><b>See also </b><a href="qml-qtquick-mouseevent.html" translate="no">MouseEvent</a>, <a href="qtquick-mousearea-example.html" translate="no">MouseArea example</a>, and <a href="qtquick-input-topic.html" translate="no">Important Concepts In Qt Quick - User Input</a>.</p>
+<!-- @@@MouseArea -->
+<h2>Property Documentation</h2>
+<!-- $$$ -->
+<div class="qmlitem"><div class="fngroup">
+<div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="mouseX-prop">
+<td class="tblQmlPropNode"><p>
+<a name="mouseX-prop"></a><span class="name">mouseX</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="mouseY-prop">
+<td class="tblQmlPropNode"><p>
+<a name="mouseY-prop"></a><span class="name">mouseY</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+</div><div class="qmldoc"><p>These properties hold the coordinates of the mouse cursor.</p>
+<p>If the <a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a> property is false then these properties will only be valid while a button is pressed, and will remain valid as long as the button is held down even if the mouse is moved outside the area.</p>
+<p>By default, this property is false.</p>
+<p>If <a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a> is true then these properties will be valid when:</p>
+<ul>
+<li>no button is pressed, but the mouse is within the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> (<a href="qml-qtquick-mousearea.html#containsMouse-prop" translate="no">containsMouse</a> is true).</li>
+<li>a button is pressed and held, even if it has since moved out of the area.</li>
+</ul>
+<p>The coordinates are relative to the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a>.</p>
+</div></div><!-- @@@ -->
+<br/>
+<!-- $$$acceptedButtons -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="acceptedButtons-prop">
+<td class="tblQmlPropNode"><p>
+<a name="acceptedButtons-prop"></a><span class="name">acceptedButtons</span> : <span class="type">Qt::MouseButtons</span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the mouse buttons that the mouse area reacts to.</p>
+<p>To specify that the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> will react to multiple buttons, <a href="../qtcore/qt.html#MouseButton-enum" translate="no">Qt::MouseButtons</a> flag values are combined using the &quot;|&quot; (or) operator:</p>
+<pre class="cpp" translate="no">
+ MouseArea { acceptedButtons: <span class="type"><a href="../qtqml/qml-qtqml-qt.html" translate="no">Qt</a></span><span class="operator">.</span>LeftButton <span class="operator">|</span> <span class="type"><a href="../qtqml/qml-qtqml-qt.html" translate="no">Qt</a></span><span class="operator">.</span>RightButton }
+</pre>
+<p>To indicate that all possible mouse buttons are to be accepted, the special value 'Qt.AllButtons' may be used:</p>
+<pre class="cpp" translate="no">
+ MouseArea { acceptedButtons: <span class="type"><a href="../qtqml/qml-qtqml-qt.html" translate="no">Qt</a></span><span class="operator">.</span>AllButtons }
+</pre>
+<p>The default value is <code translate="no">Qt.LeftButton</code>.</p>
+</div></div><!-- @@@acceptedButtons -->
+<br/>
+<!-- $$$containsMouse -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="containsMouse-prop">
+<td class="tblQmlPropNode"><p>
+<a name="containsMouse-prop"></a><span class="name">containsMouse</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether the mouse is currently inside the mouse area.</p>
+<div class="admonition warning">
+<p><b>Warning: </b>If <a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a> is false, containsMouse will only be valid when the mouse is pressed while the mouse cursor is inside the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a>.</p>
+</div>
+</div></div><!-- @@@containsMouse -->
+<br/>
+<!-- $$$containsPress -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="containsPress-prop">
+<td class="tblQmlPropNode"><p>
+<a name="containsPress-prop"></a><span class="name">containsPress</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This is a convenience property equivalent to <code translate="no">pressed &amp;&amp; containsMouse</code>, i.e&#x2e; it holds whether any of the <a href="qml-qtquick-mousearea.html#acceptedButtons-prop" translate="no">acceptedButtons</a> are currently pressed and the mouse is currently within the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a>.</p>
+<p>This property is particularly useful for highlighting an item while the mouse is pressed within its bounds.</p>
+<p>This property was introduced in Qt 5.4.</p>
+<p><b>See also </b><a href="qml-qtquick-mousearea.html#pressed-signal" translate="no">pressed</a> and <a href="qml-qtquick-mousearea.html#containsMouse-prop" translate="no">containsMouse</a>.</p>
+</div></div><!-- @@@containsPress -->
+<br/>
+<!-- $$$cursorShape -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="cursorShape-prop">
+<td class="tblQmlPropNode"><p>
+<a name="cursorShape-prop"></a><span class="name">cursorShape</span> : <span class="type">Qt::CursorShape</span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the cursor shape for this mouse area. Note that on platforms that do not display a mouse cursor this may have no effect.</p>
+<p>The available cursor shapes are:</p>
+<ul>
+<li>Qt.ArrowCursor</li>
+<li>Qt.UpArrowCursor</li>
+<li>Qt.CrossCursor</li>
+<li>Qt.WaitCursor</li>
+<li>Qt.IBeamCursor</li>
+<li>Qt.SizeVerCursor</li>
+<li>Qt.SizeHorCursor</li>
+<li>Qt.SizeBDiagCursor</li>
+<li>Qt.SizeFDiagCursor</li>
+<li>Qt.SizeAllCursor</li>
+<li>Qt.BlankCursor</li>
+<li>Qt.SplitVCursor</li>
+<li>Qt.SplitHCursor</li>
+<li>Qt.PointingHandCursor</li>
+<li>Qt.ForbiddenCursor</li>
+<li>Qt.WhatsThisCursor</li>
+<li>Qt.BusyCursor</li>
+<li>Qt.OpenHandCursor</li>
+<li>Qt.ClosedHandCursor</li>
+<li>Qt.DragCopyCursor</li>
+<li>Qt.DragMoveCursor</li>
+<li>Qt.DragLinkCursor</li>
+</ul>
+<p>In order to only set a mouse cursor shape for a region without reacting to mouse events set the <a href="qml-qtquick-mousearea.html#acceptedButtons-prop" translate="no">acceptedButtons</a> to none:</p>
+<pre class="cpp" translate="no">
+ MouseArea { cursorShape: <span class="type"><a href="../qtqml/qml-qtqml-qt.html" translate="no">Qt</a></span><span class="operator">.</span>IBeamCursor; acceptedButtons: <span class="type"><a href="../qtqml/qml-qtqml-qt.html" translate="no">Qt</a></span><span class="operator">.</span>NoButton }
+</pre>
+<p>The default value is <code translate="no">Qt.ArrowCursor</code>.</p>
+<p><b>See also </b><a href="../qtcore/qt.html#CursorShape-enum" translate="no">Qt::CursorShape</a>.</p>
+</div></div><!-- @@@cursorShape -->
+<br/>
+<!-- $$$drag -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="even" id="drag-prop"><th class="centerAlign"><p><a name="drag-prop"></a><b>drag group</b></p></th></tr>
+<tr valign="top" class="odd" id="drag.active-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.active-prop"></a><span class="name">drag.active</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.axis-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.axis-prop"></a><span class="name">drag.axis</span> : <span class="type"><a href="../qtqml/qml-enumeration.html" translate="no">enumeration</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.filterChildren-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.filterChildren-prop"></a><span class="name">drag.filterChildren</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.maximumX-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.maximumX-prop"></a><span class="name">drag.maximumX</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.maximumY-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.maximumY-prop"></a><span class="name">drag.maximumY</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.minimumX-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.minimumX-prop"></a><span class="name">drag.minimumX</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.minimumY-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.minimumY-prop"></a><span class="name">drag.minimumY</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.smoothed-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.smoothed-prop"></a><span class="name">drag.smoothed</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.target-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.target-prop"></a><span class="name">drag.target</span> : <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.threshold-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.threshold-prop"></a><span class="name">drag.threshold</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p><code translate="no">drag</code> provides a convenient way to make an item draggable.</p>
+<ul>
+<li><code translate="no">drag.target</code> specifies the id of the item to drag.</li>
+<li><code translate="no">drag.active</code> specifies if the target item is currently being dragged.</li>
+<li><code translate="no">drag.axis</code> specifies whether dragging can be done horizontally (<code translate="no">Drag.XAxis</code>), vertically (<code translate="no">Drag.YAxis</code>), or both (<code translate="no">Drag.XAndYAxis</code>)</li>
+<li><code translate="no">drag.minimum</code> and <code translate="no">drag.maximum</code> limit how far the target can be dragged along the corresponding axes.</li>
+</ul>
+<p>The following example displays a <a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a> that can be dragged along the X-axis. The opacity of the rectangle is reduced when it is dragged to the right.</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">id</span>: <span class="name">container</span>
+ <span class="name">width</span>: <span class="number">600</span>; <span class="name">height</span>: <span class="number">200</span>
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">id</span>: <span class="name">rect</span>
+ <span class="name">width</span>: <span class="number">50</span>; <span class="name">height</span>: <span class="number">50</span>
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">opacity</span>: (<span class="number">600.0</span> <span class="operator">-</span> <span class="name">rect</span>.<span class="name">x</span>) <span class="operator">/</span> <span class="number">600</span>
+
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">drag</span>.target: <span class="name">rect</span>
+ <span class="name">drag</span>.axis: <span class="name">Drag</span>.<span class="name">XAxis</span>
+ <span class="name">drag</span>.minimumX: <span class="number">0</span>
+ <span class="name">drag</span>.maximumX: <span class="name">container</span>.<span class="name">width</span> <span class="operator">-</span> <span class="name">rect</span>.<span class="name">width</span>
+ }
+ }
+ }
+</pre>
+<div class="admonition note">
+<p><b>Note: </b>Items cannot be dragged if they are anchored for the requested <code translate="no">drag.axis</code>. For example, if <code translate="no">anchors.left</code> or <code translate="no">anchors.right</code> was set for <code translate="no">rect</code> in the above example, it cannot be dragged along the X-axis. This can be avoided by settng the anchor value to <code translate="no">undefined</code> in an <a href="qml-qtquick-mousearea.html#pressed-signal" translate="no">onPressed</a> handler.</p>
+</div>
+<p>If <code translate="no">drag.filterChildren</code> is set to true, a drag can override descendant MouseAreas. This enables a parent <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> to handle drags, for example, while descendants handle clicks:</p>
+<p><code translate="no">drag.threshold</code> determines the threshold in pixels of when the drag operation should start. By default this is bound to a platform dependent value. This property was added in Qt Quick 2.2&#x2e;</p>
+<p>If <code translate="no">drag.smoothed</code> is <code translate="no">true</code>, the target will be moved only after the drag operation has started. If set to <code translate="no">false</code>, the target will be moved straight to the current mouse position. By default, this property is <code translate="no">true</code>. This property was added in Qt Quick 2.4</p>
+<p>See the <a href="qml-qtquick-drag.html" translate="no">Drag</a> attached property and <a href="qml-qtquick-droparea.html" translate="no">DropArea</a> if you want to make a drop.</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">width</span>: <span class="number">480</span>
+ <span class="name">height</span>: <span class="number">320</span>
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">x</span>: <span class="number">30</span>; <span class="name">y</span>: <span class="number">30</span>
+ <span class="name">width</span>: <span class="number">300</span>; <span class="name">height</span>: <span class="number">240</span>
+ <span class="name">color</span>: <span class="string">&quot;lightsteelblue&quot;</span>
+
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">drag</span>.target: <span class="name">parent</span>;
+ <span class="name">drag</span>.axis: <span class="string">&quot;XAxis&quot;</span>
+ <span class="name">drag</span>.minimumX: <span class="number">30</span>
+ <span class="name">drag</span>.maximumX: <span class="number">150</span>
+ <span class="name">drag</span>.filterChildren: <span class="number">true</span>
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;yellow&quot;</span>
+ <span class="name">x</span>: <span class="number">50</span>; <span class="name">y</span> : <span class="number">50</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">onClicked</span>: <span class="name">console</span>.<span class="name">log</span>(<span class="string">&quot;Clicked&quot;</span>)
+ }
+ }
+ }
+ }
+ }
+</pre>
+</div></div><!-- @@@drag -->
+<br/>
+<!-- $$$enabled -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="enabled-prop">
+<td class="tblQmlPropNode"><p>
+<a name="enabled-prop"></a><span class="name">enabled</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether the item accepts mouse events.</p>
+<div class="admonition note">
+<p><b>Note: </b>Due to historical reasons, this property is not equivalent to Item.enabled. It only affects mouse events, and its effect does not propagate to child items.</p>
+</div>
+<p>By default, this property is true.</p>
+</div></div><!-- @@@enabled -->
+<br/>
+<!-- $$$hoverEnabled -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="hoverEnabled-prop">
+<td class="tblQmlPropNode"><p>
+<a name="hoverEnabled-prop"></a><span class="name">hoverEnabled</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether hover events are handled.</p>
+<p>By default, mouse events are only handled in response to a button event, or when a button is pressed. Hover enables handling of all mouse events even when no mouse button is pressed.</p>
+<p>This property affects the <a href="qml-qtquick-mousearea.html#containsMouse-prop" translate="no">containsMouse</a> property and the onEntered, onExited and onPositionChanged signals.</p>
+</div></div><!-- @@@hoverEnabled -->
+<br/>
+<!-- $$$pressAndHoldInterval -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="pressAndHoldInterval-prop">
+<td class="tblQmlPropNode"><p>
+<a name="pressAndHoldInterval-prop"></a><span class="name">pressAndHoldInterval</span> : <span class="type"><a href="../qtqml/qml-int.html" translate="no">int</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property overrides the elapsed time in milliseconds before <code translate="no">pressAndHold</code> is emitted.</p>
+<p>If not explicitly set -- or after reset -- the value follows <code translate="no">QStyleHints::mousePressAndHoldInterval</code>.</p>
+<p>Typically it's sufficient to set this property globally using the application style hint. This property should be used when varying intervals are needed for certain MouseAreas.</p>
+<p>This property was introduced in Qt 5.9.</p>
+<p><b>See also </b><a href="qml-qtquick-mousearea.html#pressAndHold-signal" translate="no">pressAndHold</a>.</p>
+</div></div><!-- @@@pressAndHoldInterval -->
+<br/>
+<!-- $$$pressed -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="pressed-prop">
+<td class="tblQmlPropNode"><p>
+<a name="pressed-prop"></a><span class="name">pressed</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether any of the <a href="qml-qtquick-mousearea.html#acceptedButtons-prop" translate="no">acceptedButtons</a> are currently pressed.</p>
+</div></div><!-- @@@pressed -->
+<br/>
+<!-- $$$pressedButtons -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="pressedButtons-prop">
+<td class="tblQmlPropNode"><p>
+<a name="pressedButtons-prop"></a><span class="name">pressedButtons</span> : <span class="type">MouseButtons</span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the mouse buttons currently pressed.</p>
+<p>It contains a bitwise combination of:</p>
+<ul>
+<li>Qt.LeftButton</li>
+<li>Qt.RightButton</li>
+<li>Qt.MiddleButton</li>
+</ul>
+<p>The code below displays &quot;right&quot; when the right mouse buttons is pressed:</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-text.html" translate="no">Text</a></span> {
+ <span class="name">text</span>: <span class="name">mouseArea</span>.<span class="name">pressedButtons</span> <span class="operator">&amp;</span> <span class="name">Qt</span>.<span class="name">RightButton</span> ? <span class="string">&quot;right&quot;</span> : <span class="string">&quot;&quot;</span>
+ <span class="name">horizontalAlignment</span>: <span class="name">Text</span>.<span class="name">AlignHCenter</span>
+ <span class="name">verticalAlignment</span>: <span class="name">Text</span>.<span class="name">AlignVCenter</span>
+
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">id</span>: <span class="name">mouseArea</span>
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">acceptedButtons</span>: <span class="name">Qt</span>.<span class="name">LeftButton</span> <span class="operator">|</span> <span class="name">Qt</span>.<span class="name">RightButton</span>
+ }
+ }
+</pre>
+<div class="admonition note">
+<p><b>Note: </b>this property only handles buttons specified in <a href="qml-qtquick-mousearea.html#acceptedButtons-prop" translate="no">acceptedButtons</a>.</p>
+</div>
+<p><b>See also </b><a href="qml-qtquick-mousearea.html#acceptedButtons-prop" translate="no">acceptedButtons</a>.</p>
+</div></div><!-- @@@pressedButtons -->
+<br/>
+<!-- $$$preventStealing -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="preventStealing-prop">
+<td class="tblQmlPropNode"><p>
+<a name="preventStealing-prop"></a><span class="name">preventStealing</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether the mouse events may be stolen from this <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a>.</p>
+<p>If a <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> is placed within an item that filters child mouse events, such as Flickable, the mouse events may be stolen from the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> if a gesture is recognized by the parent item, e.g&#x2e; a flick gesture. If preventStealing is set to true, no item will steal the mouse events.</p>
+<p>Note that setting preventStealing to true once an item has started stealing events will have no effect until the next press event.</p>
+<p>By default this property is false.</p>
+</div></div><!-- @@@preventStealing -->
+<br/>
+<!-- $$$propagateComposedEvents -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="propagateComposedEvents-prop">
+<td class="tblQmlPropNode"><p>
+<a name="propagateComposedEvents-prop"></a><span class="name">propagateComposedEvents</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether composed mouse events will automatically propagate to other MouseAreas that overlap with this <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> but are lower in the visual stacking order. By default, this property is false.</p>
+<p><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> contains several composed events: <code translate="no">clicked</code>, <code translate="no">doubleClicked</code> and <code translate="no">pressAndHold</code>. These are composed of basic mouse events, like <code translate="no">pressed</code>, and can be propagated differently in comparison to basic events.</p>
+<p>If propagateComposedEvents is set to true, then composed events will be automatically propagated to other MouseAreas in the same location in the scene. Each event is propagated to the next <a href="qml-qtquick-mousearea.html#enabled-prop" translate="no">enabled</a> <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> beneath it in the stacking order, propagating down this visual hierarchy until a <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> accepts the event. Unlike <code translate="no">pressed</code> events, composed events will not be automatically accepted if no handler is present.</p>
+<p>For example, below is a yellow <a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a> that contains a blue <a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a>. The blue rectangle is the top-most item in the hierarchy of the visual stacking order; it will visually rendered above the yellow rectangle. Since the blue rectangle sets propagateComposedEvents to true, and also sets <a href="qml-qtquick-mouseevent.html#accepted-prop" translate="no">MouseEvent::accepted</a> to false for all received <code translate="no">clicked</code> events, any <code translate="no">clicked</code> events it receives are propagated to the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> of the yellow rectangle beneath it.</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;yellow&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">onClicked</span>: <span class="name">console</span>.<span class="name">log</span>(<span class="string">&quot;clicked yellow&quot;</span>)
+ }
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">width</span>: <span class="number">50</span>; <span class="name">height</span>: <span class="number">50</span>
+
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">propagateComposedEvents</span>: <span class="number">true</span>
+ <span class="name">onClicked</span>: {
+ <span class="name">console</span>.<span class="name">log</span>(<span class="string">&quot;clicked blue&quot;</span>)
+ <span class="name">mouse</span>.<span class="name">accepted</span> <span class="operator">=</span> <span class="number">false</span>
+ }
+ }
+ }
+ }
+</pre>
+<p>Clicking on the blue rectangle will cause the <code translate="no">onClicked</code> handler of its child <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> to be invoked; the event will then be propagated to the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> of the yellow rectangle, causing its own <code translate="no">onClicked</code> handler to be invoked.</p>
+<p>This property greatly simplifies the usecase of when you want to have overlapping MouseAreas handling the composed events together. For example: if you want one <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> to handle <code translate="no">clicked</code> signals and the other to handle <code translate="no">pressAndHold</code>, or if you want one <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> to handle <code translate="no">clicked</code> most of the time, but pass it through when certain conditions are met.</p>
+</div></div><!-- @@@propagateComposedEvents -->
+<br/>
+<!-- $$$scrollGestureEnabled -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="scrollGestureEnabled-prop">
+<td class="tblQmlPropNode"><p>
+<a name="scrollGestureEnabled-prop"></a><span class="name">scrollGestureEnabled</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property controls whether this <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> responds to scroll gestures from non-mouse devices, such as the 2-finger flick gesture on a trackpad. If set to false, the <a href="qml-qtquick-mousearea.html#wheel-signal" translate="no">wheel</a> signal be emitted only when the wheel event comes from an actual mouse with a wheel, while scroll gesture events will pass through to any other Item that will handle them. For example, the user might perform a flick gesture while the cursor is over an item containing a <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a>, intending to interact with a Flickable which is underneath. Setting this property to false will allow the <a href="qml-qtquick-pincharea.html" translate="no">PinchArea</a> to handle the mouse wheel or the pinch gesture, while the Flickable handles the flick gesture.</p>
+<p>By default, this property is true.</p>
+<p>This property was introduced in Qt 5.5.</p>
+</div></div><!-- @@@scrollGestureEnabled -->
+<br/>
+<h2>Signal Documentation</h2>
+<!-- $$$canceled[overload1]$$$canceled -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="canceled-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="canceled-signal"></a><span class="name">canceled</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when mouse events have been canceled, because another item stole the mouse event handling.</p>
+<p>This signal is for advanced use: it is useful when there is more than one <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> that is handling input, or when there is a <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> inside a <a href="qml-qtquick-flickable.html" translate="no">Flickable</a>. In the latter case, if you execute some logic in the <code translate="no">onPressed</code> signal handler and then start dragging, the <a href="qml-qtquick-flickable.html" translate="no">Flickable</a> will steal the mouse handling from the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a>. In these cases, to reset the logic when the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> has lost the mouse handling to the <a href="qml-qtquick-flickable.html" translate="no">Flickable</a>, <code translate="no">canceled</code> should be handled in addition to <a href="qml-qtquick-mousearea.html#released-signal" translate="no">released</a>.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onCanceled</code>.</p>
+</div></div><!-- @@@canceled -->
+<br/>
+<!-- $$$clicked[overload1]$$$clickedMouseEvent -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="clicked-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="clicked-signal"></a><span class="name">clicked</span>(<span class="type"><a href="qml-qtquick-mouseevent.html" translate="no">MouseEvent</a></span> <i>mouse</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when there is a click. A click is defined as a press followed by a release, both inside the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> (pressing, moving outside the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a>, and then moving back inside and releasing is also considered a click).</p>
+<p>The <a href="qml-qtquick-mouseevent.html" translate="no">mouse</a> parameter provides information about the click, including the x and y position of the release of the click, and whether the click was held.</p>
+<p>When handling this signal, changing the <a href="qml-qtquick-mouseevent.html#accepted-prop" translate="no">accepted</a> property of the <i translate="no">mouse</i> parameter has no effect, unless the <a href="qml-qtquick-mousearea.html#propagateComposedEvents-prop" translate="no">propagateComposedEvents</a> property is <code translate="no">true</code>.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onClicked</code>.</p>
+</div></div><!-- @@@clicked -->
+<br/>
+<!-- $$$doubleClicked[overload1]$$$doubleClickedMouseEvent -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="doubleClicked-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="doubleClicked-signal"></a><span class="name">doubleClicked</span>(<span class="type"><a href="qml-qtquick-mouseevent.html" translate="no">MouseEvent</a></span> <i>mouse</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when there is a double-click (a press followed by a release followed by a press). The <a href="qml-qtquick-mouseevent.html" translate="no">mouse</a> parameter provides information about the click, including the x and y position of the release of the click, and whether the click was held.</p>
+<p>When handling this signal, if the <a href="qml-qtquick-mouseevent.html#accepted-prop" translate="no">accepted</a> property of the <i translate="no">mouse</i> parameter is set to false, the pressed/released/clicked signals will be emitted for the second click; otherwise they are suppressed. The <code translate="no">accepted</code> property defaults to true.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onDoubleClicked</code>.</p>
+</div></div><!-- @@@doubleClicked -->
+<br/>
+<!-- $$$entered[overload1]$$$entered -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="entered-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="entered-signal"></a><span class="name">entered</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when the mouse enters the mouse area.</p>
+<p>By default this signal is only emitted if a button is currently pressed. Set <a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a> to true to emit this signal even when no mouse button is pressed.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onEntered</code>.</p>
+<p><b>See also </b><a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a>.</p>
+</div></div><!-- @@@entered -->
+<br/>
+<!-- $$$exited[overload1]$$$exited -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="exited-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="exited-signal"></a><span class="name">exited</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when the mouse exits the mouse area.</p>
+<p>By default this signal is only emitted if a button is currently pressed. Set <a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a> to true to emit this signal even when no mouse button is pressed.</p>
+<p>The example below shows a fairly typical relationship between two MouseAreas, with <code translate="no">mouseArea2</code> on top of <code translate="no">mouseArea1</code>. Moving the mouse into <code translate="no">mouseArea2</code> from <code translate="no">mouseArea1</code> will cause <code translate="no">mouseArea1</code> to emit the <code translate="no">exited</code> signal.</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">width</span>: <span class="number">400</span>; <span class="name">height</span>: <span class="number">400</span>
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">id</span>: <span class="name">mouseArea1</span>
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">hoverEnabled</span>: <span class="number">true</span>
+ }
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">id</span>: <span class="name">mouseArea2</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="name">anchors</span>.centerIn: <span class="name">parent</span>
+ <span class="name">hoverEnabled</span>: <span class="number">true</span>
+ }
+ }
+</pre>
+<p>If instead you give the two MouseAreas a parent-child relationship, moving the mouse into <code translate="no">mouseArea2</code> from <code translate="no">mouseArea1</code> will <b>not</b> cause <code translate="no">mouseArea1</code> to emit <code translate="no">exited</code>. Instead, they will both be considered to be simultaneously hovered.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onExited</code>.</p>
+<p><b>See also </b><a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a>.</p>
+</div></div><!-- @@@exited -->
+<br/>
+<!-- $$$positionChanged[overload1]$$$positionChangedMouseEvent -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="positionChanged-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="positionChanged-signal"></a><span class="name">positionChanged</span>(<span class="type"><a href="qml-qtquick-mouseevent.html" translate="no">MouseEvent</a></span> <i>mouse</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when the mouse position changes.</p>
+<p>The <a href="qml-qtquick-mouseevent.html" translate="no">mouse</a> parameter provides information about the mouse, including the x and y position, and any buttons currently pressed.</p>
+<p>By default this signal is only emitted if a button is currently pressed. Set <a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a> to true to emit this signal even when no mouse button is pressed.</p>
+<p>When handling this signal, changing the <a href="qml-qtquick-mouseevent.html#accepted-prop" translate="no">accepted</a> property of the <i translate="no">mouse</i> parameter has no effect.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onPositionChanged</code>.</p>
+</div></div><!-- @@@positionChanged -->
+<br/>
+<!-- $$$pressAndHold[overload1]$$$pressAndHoldMouseEvent -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="pressAndHold-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="pressAndHold-signal"></a><span class="name">pressAndHold</span>(<span class="type"><a href="qml-qtquick-mouseevent.html" translate="no">MouseEvent</a></span> <i>mouse</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when there is a long press (currently 800ms). The <a href="qml-qtquick-mouseevent.html" translate="no">mouse</a> parameter provides information about the press, including the x and y position of the press, and which button is pressed.</p>
+<p>When handling this signal, changing the <a href="qml-qtquick-mouseevent.html#accepted-prop" translate="no">accepted</a> property of the <i translate="no">mouse</i> parameter has no effect, unless the <a href="qml-qtquick-mousearea.html#propagateComposedEvents-prop" translate="no">propagateComposedEvents</a> property is <code translate="no">true</code>.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onPressAndHold</code>.</p>
+</div></div><!-- @@@pressAndHold -->
+<br/>
+<!-- $$$pressed[overload1]$$$pressedMouseEvent -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="pressed-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="pressed-signal"></a><span class="name">pressed</span>(<span class="type"><a href="qml-qtquick-mouseevent.html" translate="no">MouseEvent</a></span> <i>mouse</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when there is a press. The <a href="qml-qtquick-mouseevent.html" translate="no">mouse</a> parameter provides information about the press, including the x and y position and which button was pressed.</p>
+<p>When handling this signal, use the <a href="qml-qtquick-mouseevent.html#accepted-prop" translate="no">accepted</a> property of the <i translate="no">mouse</i> parameter to control whether this <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> handles the press and all future mouse events until release. The default is to accept the event and not allow other MouseAreas beneath this one to handle the event. If <i>accepted</i> is set to false, no further events will be sent to this <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> until the button is next pressed.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onPressed</code>.</p>
+</div></div><!-- @@@pressed -->
+<br/>
+<!-- $$$released[overload1]$$$releasedMouseEvent -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="released-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="released-signal"></a><span class="name">released</span>(<span class="type"><a href="qml-qtquick-mouseevent.html" translate="no">MouseEvent</a></span> <i>mouse</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when there is a release. The <a href="qml-qtquick-mouseevent.html" translate="no">mouse</a> parameter provides information about the click, including the x and y position of the release of the click, and whether the click was held.</p>
+<p>When handling this signal, changing the <a href="qml-qtquick-mouseevent.html#accepted-prop" translate="no">accepted</a> property of the <i translate="no">mouse</i> parameter has no effect.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onReleased</code>.</p>
+<p><b>See also </b><a href="qml-qtquick-mousearea.html#canceled-signal" translate="no">canceled</a>.</p>
+</div></div><!-- @@@released -->
+<br/>
+<!-- $$$wheel[overload1]$$$wheelWheelEvent -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="wheel-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="wheel-signal"></a><span class="name">wheel</span>(<span class="type"><a href="qml-qtquick-wheelevent.html" translate="no">WheelEvent</a></span> <i>wheel</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted in response to both mouse wheel and trackpad scroll gestures.</p>
+<p>The <i translate="no">wheel</i> parameter provides information about the event, including the x and y position, any buttons currently pressed, and information about the wheel movement, including angleDelta and pixelDelta.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onWheel</code>.</p>
+</div></div><!-- @@@wheel -->
+<br/>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<div class="footer">
+ <p>
+ <acronym title="Copyright">&copy;</acronym> 2023 The Qt Company Ltd.
+ Documentation contributions included herein are the copyrights of
+ their respective owners.<br/> The documentation provided herein is licensed under the terms of the <a href="http://www.gnu.org/licenses/fdl.html">GNU Free Documentation License version 1.3</a> as published by the Free Software Foundation.<br/> Qt and respective logos are <a href="https://doc.qt.io/qt/trademarks.html"> trademarks</a> of The Qt Company Ltd. in Finland and/or other countries
+ worldwide. All other trademarks are property of their respective owners. </p>
+</div>
+</body>
+</html>
diff --git a/tests/auto/qmlls/utils/data/qualifiedModule.qml b/tests/auto/qmlls/utils/data/qualifiedModule.qml
new file mode 100644
index 0000000000..4695f57d3f
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/qualifiedModule.qml
@@ -0,0 +1,7 @@
+import QtQuick as QQ
+
+QQ.Item {
+ property Item helloWorld: QQ.Item {}
+ QQ.
+
+} \ No newline at end of file
diff --git a/tests/auto/qmlls/utils/data/renaming/RenameMe.qml b/tests/auto/qmlls/utils/data/renaming/RenameMe.qml
new file mode 100644
index 0000000000..adc3da9800
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/renaming/RenameMe.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property int i42
+} \ No newline at end of file
diff --git a/tests/auto/qmlls/utils/data/renaming/RenameMe2.ui.qml b/tests/auto/qmlls/utils/data/renaming/RenameMe2.ui.qml
new file mode 100644
index 0000000000..35320e03ff
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/renaming/RenameMe2.ui.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property string i42
+} \ No newline at end of file
diff --git a/tests/auto/qmlls/utils/data/renaming/RenamedByQmldir.qml b/tests/auto/qmlls/utils/data/renaming/RenamedByQmldir.qml
new file mode 100644
index 0000000000..f97cbcf115
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/renaming/RenamedByQmldir.qml
@@ -0,0 +1,4 @@
+import QtQuick
+
+Item {
+}
diff --git a/tests/auto/qmlls/utils/data/renaming/UnrelatedFile.qml b/tests/auto/qmlls/utils/data/renaming/UnrelatedFile.qml
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/renaming/UnrelatedFile.qml
diff --git a/tests/auto/qmlls/utils/data/renaming/main.qml b/tests/auto/qmlls/utils/data/renaming/main.qml
new file mode 100644
index 0000000000..10afda9773
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/renaming/main.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Item {
+ RenameMe {}
+ RenameMe2 {}
+ HelloWorld {}
+}
diff --git a/tests/auto/qmlls/utils/data/renaming/qmldir b/tests/auto/qmlls/utils/data/renaming/qmldir
new file mode 100644
index 0000000000..8cff297e26
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/renaming/qmldir
@@ -0,0 +1,6 @@
+module renaming
+RenameMe 254.0 RenameMe.qml
+RenameMe2 254.0 RenameMe2.ui.qml
+RenameMe3 254.0 subfolder/RenameMe3.qml
+main 254.0 main.qml
+HelloWorld 254.0 RenamedByQmldir.qml \ No newline at end of file
diff --git a/tests/auto/qmlls/utils/data/resolveExpressionType/BaseType.qml b/tests/auto/qmlls/utils/data/resolveExpressionType/BaseType.qml
new file mode 100644
index 0000000000..210f55519d
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/resolveExpressionType/BaseType.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function helloMethod() {}
+ property int helloProperty
+ signal helloSignal
+ enum HelloEnum { HelloEnumValue1, HelloEnumValue2, HelloEnumValue3 }
+ property font helloFont
+}
diff --git a/tests/auto/qmlls/utils/data/resolveExpressionType/BindingsOnDeferred.qml b/tests/auto/qmlls/utils/data/resolveExpressionType/BindingsOnDeferred.qml
new file mode 100644
index 0000000000..f0d82f3318
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/resolveExpressionType/BindingsOnDeferred.qml
@@ -0,0 +1,19 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls.Basic
+
+Item {
+ property var c: Control {
+ id: inner
+
+ property var myBinding: Binding {
+ inner.background: Item {}
+
+ inner {
+ background: Item {}
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/auto/qmlls/utils/data/resolveExpressionType/Derived1.qml b/tests/auto/qmlls/utils/data/resolveExpressionType/Derived1.qml
new file mode 100644
index 0000000000..687c190094
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/resolveExpressionType/Derived1.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+BaseType {
+
+}
diff --git a/tests/auto/qmlls/utils/data/resolveExpressionType/Derived2.qml b/tests/auto/qmlls/utils/data/resolveExpressionType/Derived2.qml
new file mode 100644
index 0000000000..3208d97778
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/resolveExpressionType/Derived2.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+Derived1 {
+
+}
diff --git a/tests/auto/qmlls/utils/data/resolveExpressionType/DerivedType.qml b/tests/auto/qmlls/utils/data/resolveExpressionType/DerivedType.qml
new file mode 100644
index 0000000000..ab0b542ce8
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/resolveExpressionType/DerivedType.qml
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Derived2 {
+ id: self
+ function f() {
+ helloMethod()
+ let x = helloProperty;
+ helloSignal()
+ let y = HelloEnum.HelloEnumValue1
+ }
+
+ property var someMethod: helloMethod()
+ property int someProperty: helloProperty
+ property var someEnum: HelloEnum.HelloEnumValue2
+ function someHandler() { helloSignal(); }
+ onHelloSignal: someHandler
+
+ Item {
+ property var someMethod: self.helloMethod()
+ property int someProperty: self.helloProperty
+ function f() {
+ self.helloSignal()
+ }
+ }
+ helloFont.family: "helloFamily"
+ Keys.onBackPressed: someHandler
+
+ helloProperty: 42
+}
diff --git a/tests/auto/qmlls/utils/tst_qmlls_documentationHints.cpp b/tests/auto/qmlls/utils/tst_qmlls_documentationHints.cpp
new file mode 100644
index 0000000000..dcb6f47df9
--- /dev/null
+++ b/tests/auto/qmlls/utils/tst_qmlls_documentationHints.cpp
@@ -0,0 +1,134 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_qmlls_documentationHints.h"
+
+#include <QtQmlLS/private/qdochtmlparser_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+
+tst_qmlls_documentationHints::tst_qmlls_documentationHints()
+ : QQmlDataTest(QT_QMLLS_DOCUMENTATION_DATADIR) , m_documentationDataDir(QT_QMLLS_DOCUMENTATION_DATADIR + "/documentationHints"_L1)
+{
+}
+
+void tst_qmlls_documentationHints::qdochtmlparser_data()
+{
+ using namespace QQmlJS::Dom;
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<QString>("keyword");
+ QTest::addColumn<DomType>("domType");
+ QTest::addColumn<HtmlExtractor::ExtractionMode>("extractionMode");
+ QTest::addColumn<QString>("expectedDocumentation");
+
+ QTest::addRow("qml-object-type-extended-plaintext")
+ << testFile("qdochtmlparser/qml-qtqml-qtobject.html")
+ << "QtObject"
+ << DomType::QmlObject
+ << HtmlExtractor::ExtractionMode::Extended
+ << R"(The QtObject type is a non-visual element which contains only the objectName property.
+It can be useful to create a QtObject if you need an extremely lightweight type to enclose a set of custom properties:
+
+ import QtQuick
+
+ Item {
+ QtObject {
+ id: attributes
+ property string name
+ property int size
+ property variant attributes
+ }
+
+ Text { text: attributes.name }
+ }
+
+It can also be useful for C++ integration, as it is just a plain QObject. See the QObject documentation for further details.)";
+
+ QTest::addRow("qml-object-type-simplified-plaintext")
+ << testFile("qdochtmlparser/qml-qtqml-qtobject.html")
+ << "QtObject"
+ << DomType::QmlObject
+ << HtmlExtractor::ExtractionMode::Simplified
+ << R"(A basic QML type.)";
+
+ QTest::addRow("qml-property-simplified-plaintext")
+ << testFile("qdochtmlparser/qml-qtqml-qtobject.html")
+ << "objectName"
+ << DomType::PropertyDefinition
+ << HtmlExtractor::ExtractionMode::Simplified
+ << R"(This property holds the QObject::objectName for this specific object instance.)";
+
+ QTest::addRow("qml-property-simplified-plaintext-from-Qt5")
+ << testFile("qdochtmlparser/qml-qtqml-qtobject-qt-5.html")
+ << "objectName"
+ << DomType::PropertyDefinition
+ << HtmlExtractor::ExtractionMode::Simplified
+ << R"(This property holds the QObject::objectName for this specific object instance.)";
+
+ QTest::addRow("qml-property-simplified-plaintext")
+ << testFile("qdochtmlparser/qml-qtquick-item.html")
+ << "width"
+ << DomType::PropertyDefinition
+ << HtmlExtractor::ExtractionMode::Simplified
+ << R"(Defines the item's position and size. The default value is 0.)";
+
+ QTest::addRow("qml-group-property-simplified-plaintext")
+ << testFile("qdochtmlparser/qml-qtquick-item.html")
+ << "anchors.fill"
+ << DomType::PropertyDefinition
+ << HtmlExtractor::ExtractionMode::Simplified
+ << R"(Anchors provide a way to position an item by specifying its relationship with other items.)";
+ QTest::addRow("qml-functions")
+ << testFile("qdochtmlparser/qml-qtquick-item.html")
+ << "mapFromGlobal"
+ << DomType::MethodInfo
+ << HtmlExtractor::ExtractionMode::Simplified
+ << "Maps the point (x, y), which is in the global coordinate system, to the item's coordinate system,"
+ " and returns a point matching the mapped coordinate.";
+
+ QTest::addRow("qml-functions-list")
+ << testFile("qdochtmlparser/qml-qtquick-item.html")
+ << "mapFromItem"
+ << DomType::MethodInfo
+ << HtmlExtractor::ExtractionMode::Simplified
+ << "Maps the point (x, y) or rect (x, y, width, height), which is in item's coordinate system,"
+ " to this item's coordinate system, and returns a point or rect matching the mapped coordinate.";
+ QTest::addRow("qml-signal")
+ << testFile("qdochtmlparser/qml-qtquick-mousearea.html")
+ << "pressAndHold"
+ << DomType::MethodInfo
+ << HtmlExtractor::ExtractionMode::Simplified
+ << "This signal is emitted when there is a long press (currently 800ms). The mouse parameter provides information about the press, "
+ "including the x and y position of the press, and which button is pressed.";
+
+ // Some properties and methods can be shown as in groups in qt-docs, like width and height of Item.
+ QTest::addRow("multiple-entries")
+ << testFile("qdochtmlparser/qml-qtquick-mousearea.html")
+ << "pressAndHold"
+ << DomType::MethodInfo
+ << HtmlExtractor::ExtractionMode::Simplified
+ << "This signal is emitted when there is a long press (currently 800ms). The mouse parameter provides information about the press, "
+ "including the x and y position of the press, and which button is pressed.";
+}
+
+void tst_qmlls_documentationHints::qdochtmlparser()
+{
+ using namespace QQmlJS::Dom;
+ QFETCH(QString, filePath);
+ QFETCH(QString, keyword);
+ QFETCH(DomType, domType);
+ QFETCH(HtmlExtractor::ExtractionMode, extractionMode);
+ QFETCH(QString, expectedDocumentation);
+
+ const auto htmlCode = [](const QString &testFileName) {
+ QFile file(testFileName);
+ if (file.open(QIODeviceBase::ReadOnly | QIODevice::Text))
+ return QString::fromUtf8(file.readAll());
+ return QString{};
+ }(filePath);
+
+ ExtractDocumentation extractor(domType);
+ const auto actual = extractor.execute(htmlCode, keyword, extractionMode);
+ QCOMPARE(actual, expectedDocumentation);
+}
+
+QTEST_MAIN(tst_qmlls_documentationHints)
diff --git a/tests/auto/qmlls/utils/tst_qmlls_documentationHints.h b/tests/auto/qmlls/utils/tst_qmlls_documentationHints.h
new file mode 100644
index 0000000000..f3b0f4ddd1
--- /dev/null
+++ b/tests/auto/qmlls/utils/tst_qmlls_documentationHints.h
@@ -0,0 +1,24 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QMLLS_DOCUMENTATION_H
+#define TST_QMLLS_DOCUMENTATION_H
+
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtCore/qobject.h>
+#include <QtTest/qtest.h>
+
+class tst_qmlls_documentationHints : public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_qmlls_documentationHints();
+private slots:
+ void qdochtmlparser_data();
+ void qdochtmlparser();
+
+private:
+ QString m_documentationDataDir;
+};
+
+#endif // TST_QMLLS_DOCUMENTATION_H
diff --git a/tests/auto/qmlls/utils/tst_qmlls_highlighting.cpp b/tests/auto/qmlls/utils/tst_qmlls_highlighting.cpp
new file mode 100644
index 0000000000..f21de11990
--- /dev/null
+++ b/tests/auto/qmlls/utils/tst_qmlls_highlighting.cpp
@@ -0,0 +1,650 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_qmlls_highlighting.h"
+
+#include <QtQml/private/qqmljsengine_p.h>
+#include <QtQml/private/qqmljslexer_p.h>
+#include <QtQml/private/qqmljsparser_p.h>
+#include <QtQmlDom/private/qqmldomitem_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+#include <QtQmlLS/private/qqmlsemantictokens_p.h>
+#include <QtCore/qlibraryinfo.h>
+#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
+
+#include <qlist.h>
+
+using namespace QLspSpecification;
+
+tst_qmlls_highlighting::tst_qmlls_highlighting()
+ : QQmlDataTest(QT_QMLLS_HIGHLIGHTS_DATADIR) , m_highlightingDataDir(QT_QMLLS_HIGHLIGHTS_DATADIR + "/highlights"_L1)
+{
+}
+
+// Token encoding as in:
+// https://microsoft.github.io/language-server-protocol/specifications/specification-3-16/#textDocument_semanticTokens
+void tst_qmlls_highlighting::encodeSemanticTokens_data()
+{
+ QTest::addColumn<Highlights>("highlights");
+ QTest::addColumn<QList<int>>("expectedMemoryLayout");
+
+ {
+ Highlights c;
+ c.highlights().insert(0, Token());
+ QTest::addRow("empty-token-single") << c << QList {0, 0, 0, 0, 0};
+ }
+ {
+ Highlights c;
+ QQmlJS::SourceLocation loc(0, 1, 1, 1);
+ c.highlights().insert(0, Token(loc, 0, 0));
+ QTest::addRow("single-token") << c << QList {0, 0, 1, 0, 0};
+ }
+ {
+ Highlights c;
+ Token t1(QQmlJS::SourceLocation(0, 1, 1, 1), 0, 0);
+ Token t2(QQmlJS::SourceLocation(1, 1, 3, 3), 0, 0);
+ c.highlights().insert(t1.offset, t1);
+ c.highlights().insert(t2.offset, t2);
+ QTest::addRow("different-lines") << c << QList {0, 0, 1, 0, 0, 2, 2, 1, 0, 0};
+ }
+ {
+ Highlights c;
+ Token t1(QQmlJS::SourceLocation(0, 1, 1, 1), 0, 0);
+ Token t2(QQmlJS::SourceLocation(1, 1, 1, 3), 0, 0);
+ c.highlights().insert(t1.offset, t1);
+ c.highlights().insert(t2.offset, t2);
+ QTest::addRow("same-line-different-column") << c << QList {0, 0, 1, 0, 0, 0, 2, 1, 0, 0};
+ }
+ {
+ Highlights c;
+ Token t1(QQmlJS::SourceLocation(0, 1, 1, 1), 1, 0);
+ c.highlights().insert(t1.offset, t1);
+ QTest::addRow("token-type") << c << QList {0, 0, 1, 1, 0};
+ }
+ {
+ Highlights c;
+ Token t1(QQmlJS::SourceLocation(0, 1, 1, 1), 1, 1);
+ c.highlights().insert(t1.offset, t1);
+ QTest::addRow("token-modifier") << c << QList {0, 0, 1, 1, 1};
+ }
+}
+
+void tst_qmlls_highlighting::encodeSemanticTokens()
+{
+ QFETCH(Highlights, highlights);
+ QFETCH(QList<int>, expectedMemoryLayout);
+ const auto encoded = HighlightingUtils::encodeSemanticTokens(highlights);
+ QCOMPARE(encoded, expectedMemoryLayout);
+}
+
+struct LineLength
+{
+ quint32 startLine;
+ quint32 length;
+};
+
+void tst_qmlls_highlighting::sourceLocationsFromMultiLineToken_data()
+{
+ QTest::addColumn<QString>("source");
+ QTest::addColumn<QList<LineLength>>("expectedLines");
+
+ QTest::addRow("multilineComment1") << R"("line 1
+line 2
+line 3 ")" << QList{ LineLength{ 1, 7 }, LineLength{ 2, 6 }, LineLength{ 3, 8 } };
+
+ QTest::addRow("prePostNewlines") <<
+ R"("
+
+")" << QList{ LineLength{ 1, 1 }, LineLength{ 2, 0 }, LineLength{ 3, 1 } };
+ QTest::addRow("windows-newline")
+ << QString::fromUtf8("\"test\r\nwindows\r\nnewline\"")
+ << QList{ LineLength{ 1, 5 }, LineLength{ 2, 7 }, LineLength{ 3, 8 } };
+}
+
+void tst_qmlls_highlighting::sourceLocationsFromMultiLineToken()
+{
+ QFETCH(QString, source);
+ QFETCH(QList<LineLength>, expectedLines);
+ using namespace QQmlJS::AST;
+
+ QQmlJS::Engine jsEngine;
+ QQmlJS::Lexer lexer(&jsEngine);
+ lexer.setCode(source, 1, true);
+ QQmlJS::Parser parser(&jsEngine);
+ parser.parseExpression();
+ const auto expression = parser.expression();
+
+ auto *literal = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(expression);
+ const auto locs =
+ HighlightingUtils::sourceLocationsFromMultiLineToken(source, literal->literalToken);
+
+ [&]() {
+ QCOMPARE(locs.size(), expectedLines.size());
+
+ for (auto i = 0; i < locs.size(); ++i) {
+ QCOMPARE(locs[i].startLine, expectedLines[i].startLine);
+ QCOMPARE(locs[i].length, expectedLines[i].length);
+ }
+ }();
+
+ if (QTest::currentTestFailed()) {
+
+ qDebug() << "Actual locations";
+ for (auto i = 0; i < locs.size(); ++i) {
+ qDebug() << "Startline :" << locs[i].startLine << "Length " << locs[i].length;
+ }
+
+ qDebug() << "Expected locations";
+ for (auto i = 0; i < expectedLines.size(); ++i) {
+ qDebug() << "Startline :" << expectedLines[i].startLine
+ << "Length :" << expectedLines[i].length;
+ }
+ }
+}
+
+void tst_qmlls_highlighting::highlights_data()
+{
+ using namespace QQmlJS::Dom;
+ QTest::addColumn<DomItem>("fileItem");
+ QTest::addColumn<Token>("expectedHighlightedToken");
+
+ const auto fileObject = [](const QString &filePath){
+ QFile f(filePath);
+ DomItem file;
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
+ return file;
+ QString code = f.readAll();
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+ options.setFlag(DomCreationOption::WithRecovery);
+
+ QStringList dirs = {QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath)};
+ auto envPtr = DomEnvironment::create(dirs,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies, options);
+ envPtr->loadBuiltins();
+ envPtr->loadFile(FileToLoad::fromMemory(envPtr, filePath, code),
+ [&file](Path, const DomItem &, const DomItem &newIt) {
+ file = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+ return file;
+ };
+
+ { // Comments
+ const auto filePath = m_highlightingDataDir + "/comments.qml";
+ const auto fileItem = fileObject(filePath);
+ // Copyright (C) 2023 The Qt Company Ltd.
+ QTest::addRow("single-line-1")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(0, 41, 1, 1), int(SemanticTokenTypes::Comment), 0);
+
+ /* single line comment */
+ QTest::addRow("single-line-2") << fileItem
+ << Token(QQmlJS::SourceLocation(162, 28, 9, 1),
+ int(SemanticTokenTypes::Comment), 0);
+
+ // Multiline comments are split into multiple locations
+ QTest::addRow("multiline-first-line")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(133, 2, 5, 1), int(SemanticTokenTypes::Comment), 0);
+ QTest::addRow("multiline-second-line") << fileItem
+ << Token(QQmlJS::SourceLocation(136, 21, 6, 1),
+ int(SemanticTokenTypes::Comment), 0);
+ QTest::addRow("multiline-third-line")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(158, 2, 7, 1), int(SemanticTokenTypes::Comment), 0);
+
+ // Comments Inside Js blocks
+ QTest::addRow("inside-js") << fileItem
+ << Token(QQmlJS::SourceLocation(232, 5, 13, 9),
+ int(SemanticTokenTypes::Comment), 0);
+ }
+ { // Imports
+ const auto filePath = m_highlightingDataDir + "/imports.qml";
+ const auto fileItem = fileObject(filePath);
+ QTest::addRow("import-keyword")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(112, 6, 4, 1), int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("module-uri") << fileItem
+ << Token(QQmlJS::SourceLocation(119, 7, 4, 8),
+ int(SemanticTokenTypes::Namespace), 0);
+ QTest::addRow("directory-uri")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(152, 3, 6, 8), int(SemanticTokenTypes::String), 0);
+ QTest::addRow("as-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(156, 2, 6, 12),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("version-number")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(140, 4, 5, 14), int(SemanticTokenTypes::Number), 0);
+ QTest::addRow("qualified-namespace") << fileItem
+ << Token(QQmlJS::SourceLocation(159, 6, 6, 15),
+ int(SemanticTokenTypes::Namespace), 0);
+ }
+ { // Bindings
+ const auto filePath = m_highlightingDataDir + "/bindings.qml";
+ const auto fileItem = fileObject(filePath);
+
+ // normal binding
+ QTest::addRow("normalBinding") << fileItem
+ << Token(QQmlJS::SourceLocation(189, 1, 11, 5),
+ int(SemanticTokenTypes::Property), 0);
+ // on binding
+ QTest::addRow("on-binding") << fileItem
+ << Token(QQmlJS::SourceLocation(175, 5, 9, 17),
+ int(SemanticTokenTypes::Property), 0);
+ QTest::addRow("on-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(172, 2, 9, 14),
+ int(SemanticTokenTypes::Keyword), 0);
+ }
+ { // Pragmas
+ const auto filePath = m_highlightingDataDir + "/pragmas.qml";
+ const auto fileItem = fileObject(filePath);
+ QTest::addRow("pragma-keyword")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(112, 6, 4, 1), int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("pragma-name") << fileItem
+ << Token(QQmlJS::SourceLocation(136, 25, 5, 8),
+ int(SemanticTokenTypes::Variable), 0);
+ QTest::addRow("pragma-value") << fileItem
+ << Token(QQmlJS::SourceLocation(198, 4, 6, 27),
+ int(SemanticTokenTypes::Variable), 0);
+ }
+ { // Enums
+ const auto filePath = m_highlightingDataDir + "/enums.qml";
+ const auto fileItem = fileObject(filePath);
+ QTest::addRow("enum-keyword")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(141, 4, 7, 5), int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("enum-name")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(146, 3, 7, 10), int(SemanticTokenTypes::Enum), 0);
+ QTest::addRow("enum-item") << fileItem
+ << Token(QQmlJS::SourceLocation(160, 3, 8, 9),
+ int(SemanticTokenTypes::EnumMember), 0);
+ QTest::addRow("enum-value")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(179, 1, 9, 15), int(SemanticTokenTypes::Number), 0);
+ }
+ { // objects and inline components
+ const auto filePath = m_highlightingDataDir + "/objectAndComponent.qml";
+ const auto fileItem = fileObject(filePath);
+
+ // object
+ QTest::addRow("object-identifier")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(169, 4, 8, 5), int(SemanticTokenTypes::Type), 0);
+ QTest::addRow("object-id-property") << fileItem
+ << Token(QQmlJS::SourceLocation(184, 2, 9, 9),
+ int(SemanticTokenTypes::Property), 0);
+ QTest::addRow("object-id-name") << fileItem
+ << Token(QQmlJS::SourceLocation(188, 5, 9, 13),
+ int(SemanticTokenTypes::Variable), 0);
+
+ // component
+ QTest::addRow("component-keyword")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(139, 9, 7, 5), int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("component-name")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(149, 6, 7, 15), int(SemanticTokenTypes::Type), 0);
+ }
+ { // property definition
+ const auto filePath = m_highlightingDataDir + "/properties.qml";
+ const auto fileItem = fileObject(filePath);
+
+ int definitionModifier = 1 << int(SemanticTokenModifiers::Definition);
+ QTest::addRow("property-keyword")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(154, 8, 8, 9), int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("property-type")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(163, 3, 8, 18), int(SemanticTokenTypes::Type), 0);
+ QTest::addRow("property-name")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(167, 1, 8, 22), int(SemanticTokenTypes::Property),
+ definitionModifier);
+ int readOnlyModifier = definitionModifier | (1 << int(SemanticTokenModifiers::Readonly));
+ QTest::addRow("readonly-keyword")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(177, 8, 9, 9), int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("readonly-modifier")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(199, 2, 9, 31), int(SemanticTokenTypes::Property),
+ readOnlyModifier);
+ int requiredModifier = definitionModifier | (1 << int(SemanticTokenModifiers::Abstract));
+ QTest::addRow("required-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(210, 8, 10, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("required-modifier")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(232, 3, 10, 31), int(SemanticTokenTypes::Property),
+ requiredModifier);
+ int defaultModifier =
+ definitionModifier | (1 << int(SemanticTokenModifiers::DefaultLibrary));
+ QTest::addRow("default-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(244, 7, 11, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("default-modifier")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(265, 4, 11, 30), int(SemanticTokenTypes::Property),
+ defaultModifier);
+ }
+ {
+ // methods and signals
+ const auto filePath = m_highlightingDataDir + "/methodAndSignal.qml";
+ const auto fileItem = fileObject(filePath);
+
+ QTest::addRow("signal-keyword")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(139, 6, 7, 5), int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("signal-name")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(146, 1, 7, 12), int(SemanticTokenTypes::Method), 0);
+ QTest::addRow("signal-type")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(163, 3, 8, 14), int(SemanticTokenTypes::Type), 0);
+ QTest::addRow("signal-type-2")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(186, 3, 9, 17), int(SemanticTokenTypes::Type), 0);
+ QTest::addRow("function-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(195, 9, 10, 5),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("function-name") << fileItem
+ << Token(QQmlJS::SourceLocation(204, 1, 10, 14),
+ int(SemanticTokenTypes::Method), 0);
+ QTest::addRow("function-prm-type")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(209, 3, 10, 19), int(SemanticTokenTypes::Type), 0);
+ QTest::addRow("function-prm-name") << fileItem
+ << Token(QQmlJS::SourceLocation(206, 1, 10, 16),
+ int(SemanticTokenTypes::Parameter), 0);
+ QTest::addRow("function-rtn-type")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(216, 3, 10, 26), int(SemanticTokenTypes::Type), 0);
+ }
+ { // literals
+ const auto filePath = m_highlightingDataDir + "/literals.qml";
+ const auto fileItem = fileObject(filePath);
+
+ QTest::addRow("number") << fileItem
+ << Token(QQmlJS::SourceLocation(155, 3, 7, 21),
+ int(SemanticTokenTypes::Number), 0);
+ QTest::addRow("singleline-string")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(182, 8, 8, 24), int(SemanticTokenTypes::String), 0);
+ QTest::addRow("multiline-string-first")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(214, 6, 9, 24), int(SemanticTokenTypes::String), 0);
+ QTest::addRow("multiline-string-second") << fileItem
+ << Token(QQmlJS::SourceLocation(221, 16, 10, 1),
+ int(SemanticTokenTypes::String), 0);
+ QTest::addRow("boolean") << fileItem
+ << Token(QQmlJS::SourceLocation(260, 4, 11, 22),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("null") << fileItem
+ << Token(QQmlJS::SourceLocation(285, 4, 12, 21),
+ int(SemanticTokenTypes::Keyword), 0);
+ }
+ { // identifiers
+ const auto filePath = m_highlightingDataDir + "/Identifiers.qml";
+ const auto fileItem = fileObject(filePath);
+ QTest::addRow("js-property") << fileItem
+ << Token(QQmlJS::SourceLocation(222, 3, 10, 13),
+ int(SemanticTokenTypes::Variable), 0);
+ QTest::addRow("property-id")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(302, 4, 12, 19), int(SemanticTokenTypes::Property),
+ (1 << int(SemanticTokenModifiers::Readonly)));
+ QTest::addRow("property-changed") << fileItem
+ << Token(QQmlJS::SourceLocation(451, 11, 18, 9),
+ int(SemanticTokenTypes::Method), 0);
+ QTest::addRow("signal") << fileItem
+ << Token(QQmlJS::SourceLocation(474, 7, 19, 9),
+ int(SemanticTokenTypes::Method), 0);
+
+ QTest::addRow("attached-id")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(512, 4, 23, 5), int(SemanticTokenTypes::Type), 0);
+ QTest::addRow("attached-signalhandler") << fileItem
+ << Token(QQmlJS::SourceLocation(517, 9, 23, 10),
+ int(SemanticTokenTypes::Method), 0);
+ QTest::addRow("propchanged-handler") << fileItem
+ << Token(QQmlJS::SourceLocation(572, 13, 27, 5),
+ int(SemanticTokenTypes::Method), 0);
+ QTest::addRow("method-id")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(597, 1, 28, 9), int(SemanticTokenTypes::Method), 0);
+ QTest::addRow("signal-handler")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(656, 9, 32, 5), int(SemanticTokenTypes::Method), 0);
+ }
+ { // script expressions
+ const auto filePath = m_highlightingDataDir + "/scriptExpressions.qml";
+ const auto fileItem = fileObject(filePath);
+
+ QTest::addRow("var-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(192, 3, 11, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("const-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(217, 5, 12, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ const auto modifier = (1 << int(SemanticTokenModifiers::Readonly));
+ QTest::addRow("const-name") << fileItem
+ << Token(QQmlJS::SourceLocation(223, 10, 12, 15),
+ int(SemanticTokenTypes::Variable), modifier);
+ QTest::addRow("do-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(248, 2, 13, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("if-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(287, 2, 15, 13),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("continue-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(319, 8, 16, 17),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("else-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(341, 4, 17, 13),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("while-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(382, 5, 19, 11),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("switch-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(418, 6, 20, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("case-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(444, 4, 21, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("return-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(464, 6, 22, 13),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("default-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(483, 7, 23, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("break-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(504, 5, 24, 13),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("try-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(529, 3, 26, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("catch-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(560, 5, 28, 11),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("finally-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(601, 7, 30, 11),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("for-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(620, 3, 31, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("throw-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(661, 5, 32, 13),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("for-declaration") << fileItem
+ << Token(QQmlJS::SourceLocation(625, 5, 31, 14),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("destructuring")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(1511, 2, 73, 16), int(SemanticTokenTypes::Variable),
+ (1 << int(SemanticTokenModifiers::Readonly)));
+ QTest::addRow("obj-destructuring")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(1589, 2, 76, 17), int(SemanticTokenTypes::Variable),
+ (1 << int(SemanticTokenModifiers::Readonly)));
+ }
+}
+
+void tst_qmlls_highlighting::highlights()
+{
+ using namespace QQmlJS::Dom;
+ QFETCH(DomItem, fileItem);
+ QFETCH(Token, expectedHighlightedToken);
+
+ Highlights h;
+ HighlightingVisitor hv(h, std::nullopt);
+
+ fileItem.visitTree(QQmlJS::Dom::Path(), hv, VisitOption::Default, emptyChildrenVisitor,
+ emptyChildrenVisitor);
+
+ const auto highlights = h.highlights();
+ QVERIFY(highlights.contains(expectedHighlightedToken.offset));
+ QCOMPARE(highlights.value(expectedHighlightedToken.offset), expectedHighlightedToken);
+}
+
+void tst_qmlls_highlighting::rangeOverlapsWithSourceLocation_data()
+{
+ QTest::addColumn<QQmlJS::SourceLocation>("sourceLocation");
+ QTest::addColumn<HighlightsRange>("range");
+ QTest::addColumn<bool>("overlaps");
+
+ QTest::addRow("sl-inside-range")
+ << QQmlJS::SourceLocation(5, 1, 1, 1) << HighlightsRange{ 0, 100 } << true;
+ QTest::addRow("sl-exceeds-rightBoundRange")
+ << QQmlJS::SourceLocation(5, 1000, 1, 1) << HighlightsRange{ 0, 100 } << true;
+ QTest::addRow("sl-exceeds-leftRightBoundRange")
+ << QQmlJS::SourceLocation(5, 1000, 1, 1) << HighlightsRange{ 8, 100 } << true;
+ QTest::addRow("sl-exceeds-leftBoundRange")
+ << QQmlJS::SourceLocation(5, 100, 1, 1) << HighlightsRange{ 8, 1000 } << true;
+ QTest::addRow("no-overlaps") << QQmlJS::SourceLocation(5, 100, 1, 1)
+ << HighlightsRange{ 8000, 100000 } << false;
+}
+
+void tst_qmlls_highlighting::rangeOverlapsWithSourceLocation()
+{
+ QFETCH(QQmlJS::SourceLocation, sourceLocation);
+ QFETCH(HighlightsRange, range);
+ QFETCH(bool, overlaps);
+ QVERIFY(overlaps == HighlightingUtils::rangeOverlapsWithSourceLocation(sourceLocation, range));
+}
+
+void tst_qmlls_highlighting::updateResultID_data()
+{
+ QTest::addColumn<QByteArray>("currentId");
+ QTest::addColumn<QByteArray>("expectedNextId");
+
+ QTest::addRow("zero-to-one") << QByteArray("0") << QByteArray("1");
+ QTest::addRow("nine-to-ten") << QByteArray("9") << QByteArray("10");
+ QTest::addRow("nineteen-to-twenty") << QByteArray("19") << QByteArray("20");
+ QTest::addRow("twodigit-to-threedigit") << QByteArray("99") << QByteArray("100");
+}
+
+void tst_qmlls_highlighting::updateResultID()
+{
+ QFETCH(QByteArray, currentId);
+ QFETCH(QByteArray, expectedNextId);
+
+ HighlightingUtils::updateResultID(currentId);
+ QCOMPARE(currentId, expectedNextId);
+}
+
+void tst_qmlls_highlighting::computeDiff_data()
+{
+ QTest::addColumn<QList<int>>("oldData");
+ QTest::addColumn<QList<int>>("newData");
+ QTest::addColumn<QList<SemanticTokensEdit>>("expected");
+
+ {
+ QList<int> oldData { 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0};
+ QList<int> newData { 3,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0};
+ SemanticTokensEdit expected;
+ expected.start = 0;
+ expected.deleteCount = 1;
+ expected.data = QList{3};
+ QTest::addRow("simple") << oldData << newData << QList{expected};
+ }
+ {
+ QList<int> oldData { 0, 0, 5, 5, 0};
+ QList<int> newData { 3, 3, 3, 3, 3, 0, 0, 5, 5, 0};
+ SemanticTokensEdit expected;
+ expected.start = 0;
+ expected.deleteCount = 0;
+ expected.data = QList{3, 3, 3, 3, 3};
+ QTest::addRow("prepend") << oldData << newData << QList{expected};
+ }
+ {
+ QList<int> oldData { 3, 3, 3, 3, 3, 0, 0, 5, 5, 0};
+ QList<int> newData { 0, 0, 5, 5, 0};
+ SemanticTokensEdit expected;
+ expected.start = 0;
+ expected.deleteCount = 5;
+ expected.data = {};
+ QTest::addRow("remove-front") << oldData << newData << QList{expected};
+ }
+ {
+ QList<int> oldData { 0, 0, 5, 5, 0};
+ QList<int> newData { 0, 0, 5, 5, 0, 1, 0, 23, 5, 0};
+ SemanticTokensEdit expected;
+ expected.start = 5;
+ expected.deleteCount = 0;
+ expected.data = QList{1, 0, 23, 5, 0};
+ QTest::addRow("append") << oldData << newData << QList{expected};
+ }
+ {
+ QList<int> oldData { 0, 0, 5, 5, 0, 1, 0, 23, 5, 0};
+ QList<int> newData { 0, 0, 5, 5, 0};
+ SemanticTokensEdit expected;
+ expected.start = 5;
+ expected.deleteCount = 5;
+ expected.data = {};
+ QTest::addRow("remove-back") << oldData << newData << QList{expected};
+ }
+ {
+ QList<int> oldData { 0, 0, 5, 5, 0, 1, 0, 23, 5, 0};
+ QList<int> newData { 0, 0, 5, 5, 0, 3, 3, 3, 3, 3, 1, 0, 23, 5, 0};
+ SemanticTokensEdit expected;
+ expected.start = 5;
+ expected.deleteCount = 0;
+ expected.data = QList{3, 3, 3, 3, 3};
+ QTest::addRow("insert-middle") << oldData << newData << QList{expected};
+ }
+ {
+ QList<int> oldData { 0, 0, 5, 5, 0, 3, 3, 3, 3, 3, 1, 0, 23, 5, 0};
+ QList<int> newData { 0, 0, 5, 5, 0, 1, 0, 23, 5, 0};
+ SemanticTokensEdit expected;
+ expected.start = 5;
+ expected.deleteCount = 5;
+ expected.data = {};
+ QTest::addRow("remove-middle") << oldData << newData << QList{expected};
+ }
+}
+
+void tst_qmlls_highlighting::computeDiff()
+{
+ QFETCH(QList<int>, oldData);
+ QFETCH(QList<int>, newData);
+ QFETCH(QList<SemanticTokensEdit>, expected);
+
+ const auto edits = HighlightingUtils::computeDiff(oldData, newData);
+ QCOMPARE(edits.size(), expected.size());
+
+ qsizetype i = 0;
+ for (const auto &edit : edits) {
+ QCOMPARE(edit.start, expected.at(i).start);
+ QCOMPARE(edit.deleteCount, expected.at(i).deleteCount);
+ QCOMPARE(edit.data, expected.at(i).data);
+ ++i;
+ }
+}
+
+
+QTEST_MAIN(tst_qmlls_highlighting)
diff --git a/tests/auto/qmlls/utils/tst_qmlls_highlighting.h b/tests/auto/qmlls/utils/tst_qmlls_highlighting.h
new file mode 100644
index 0000000000..a1d0e3c9b1
--- /dev/null
+++ b/tests/auto/qmlls/utils/tst_qmlls_highlighting.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QMLLS_HIGHLIGHTING_H
+#define TST_QMLLS_HIGHLIGHTING_H
+
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtCore/qobject.h>
+#include <QtTest/qtest.h>
+
+class tst_qmlls_highlighting : public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_qmlls_highlighting();
+private slots:
+ void encodeSemanticTokens_data();
+ void encodeSemanticTokens();
+ void sourceLocationsFromMultiLineToken_data();
+ void sourceLocationsFromMultiLineToken();
+
+ void highlights_data();
+ void highlights();
+
+ void rangeOverlapsWithSourceLocation_data();
+ void rangeOverlapsWithSourceLocation();
+
+ void updateResultID_data();
+ void updateResultID();
+
+ void computeDiff_data();
+ void computeDiff();
+private:
+ QString m_highlightingDataDir;
+};
+
+#endif // TST_QMLLS_HIGHLIGHTING_H
diff --git a/tests/auto/qmlls/utils/tst_qmlls_utils.cpp b/tests/auto/qmlls/utils/tst_qmlls_utils.cpp
new file mode 100644
index 0000000000..332dc13590
--- /dev/null
+++ b/tests/auto/qmlls/utils/tst_qmlls_utils.cpp
@@ -0,0 +1,4156 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_qmlls_utils.h"
+#include <algorithm>
+#include <optional>
+
+#include <QtCore/private/qduplicatetracker_p.h>
+#include <QtQmlLS/private/qdochtmlparser_p.h>
+
+// some helper constants for the tests
+const static int positionAfterOneIndent = 5;
+const static QString noResultExpected;
+// constants for resultIndex
+const static int firstResult = 0;
+const static int secondResult = 1;
+// constants for expectedItemsCount
+const static int outOfOne = 1;
+const static int outOfTwo = 2;
+
+// enable/disable additional debug output
+constexpr static bool enable_debug_output = true;
+
+static QString printSet(const QSet<QString> &s)
+{
+ const QString r = QStringList(s.begin(), s.end()).join(u", "_s);
+ return r;
+}
+
+static QString readFileContent(const QString &testFileName) {
+ QFile file(testFileName);
+ if (file.open(QIODeviceBase::ReadOnly))
+ return QString::fromUtf8(file.readAll());
+ return QString{};
+};
+
+std::tuple<QQmlJS::Dom::DomItem, QQmlJS::Dom::DomItem>
+tst_qmlls_utils::createEnvironmentAndLoadFile(const QString &filePath)
+{
+ CacheKey cacheKey = QDir::cleanPath(filePath + u"/.."_s);
+ if (auto entry = cache.find(cacheKey); entry != cache.end()) {
+ QQmlJS::Dom::DomItem env{ *entry };
+ return { env, env.field(QQmlJS::Dom::Fields::qmlFileWithPath).key(filePath) };
+ };
+
+ QStringList qmltypeDirs =
+ QStringList({ dataDirectory(), QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) });
+
+ // This should be exactly the same options as qmlls uses in qqmlcodemodel.
+ // Otherwise, this test will not test the codepaths also used by qmlls and will be useless.
+ const QQmlJS::Dom::DomCreationOptions options = QQmlJS::Dom::DomCreationOptions{}
+ | QQmlJS::Dom::DomCreationOption::WithSemanticAnalysis
+ | QQmlJS::Dom::DomCreationOption::WithScriptExpressions
+ | QQmlJS::Dom::DomCreationOption::WithRecovery;
+
+ auto envPtr = QQmlJS::Dom::DomEnvironment::create(
+ qmltypeDirs, QQmlJS::Dom::DomEnvironment::Option::SingleThreaded, options);
+
+ QQmlJS::Dom::DomItem file;
+ QQmlJS::Dom::DomItem env(envPtr);
+ envPtr->loadFile(QQmlJS::Dom::FileToLoad::fromFileSystem(envPtr, filePath),
+ [&file](QQmlJS::Dom::Path, const QQmlJS::Dom::DomItem &,
+ const QQmlJS::Dom::DomItem &newIt) { file = newIt; });
+
+ envPtr->loadPendingDependencies();
+ envPtr->loadBuiltins();
+
+ cache[cacheKey] = envPtr;
+ return std::make_tuple(env, file);
+}
+
+void tst_qmlls_utils::textOffsetRowColumnConversions_data()
+{
+ QTest::addColumn<QString>("code");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<qsizetype>("expectedOffset");
+ QTest::addColumn<QChar>("expectedChar");
+ // in case they differ from line and character, e.g. when accessing non-existing line or rows
+ // set to -1 when same as before
+ QTest::addColumn<int>("expectedLine");
+ QTest::addColumn<int>("expectedCharacter");
+
+ QTest::newRow("oneline") << u"Hello World!"_s << 0 << 6 << 6ll << QChar('W') << -1 << -1;
+ QTest::newRow("multi-line") << u"Hello World!\n How are you? \n Bye!\n"_s << 0 << 6 << 6ll
+ << QChar('W') << -1 << -1;
+ QTest::newRow("multi-line2") << u"Hello World!\n How are you? \n Bye!\n"_s << 1 << 5 << 18ll
+ << QChar('a') << -1 << -1;
+ QTest::newRow("multi-line3") << u"Hello World!\n How are you? \n Bye!\n"_s << 2 << 1 << 29ll
+ << QChar('B') << -1 << -1;
+
+ QTest::newRow("newlines") << u"A\nB\r\nC\n\r\nD\r\n\r"_s << 0 << 0 << 0ll << QChar('A') << -1
+ << -1;
+ QTest::newRow("newlines2") << u"A\nB\r\nC\n\r\nD\r\n\r"_s << 1 << 0 << 2ll << QChar('B') << -1
+ << -1;
+
+ // try to access '\r'
+ QTest::newRow("newlines3") << u"A\nB\r\nC\n\r\nD\r\n\r"_s << 1 << 1 << 3ll << QChar('\r') << -1
+ << -1;
+ // try to access '\n', should return the last character of the line (which is '\r' in this case)
+ QTest::newRow("newlines4") << u"A\nB\r\nC\n\r\nD\r\n\r"_s << 1 << 2 << 3ll << QChar('\r') << -1
+ << 1;
+ // try to access after the end of the line, should return the last character of the line (which
+ // is '\r' in this case)
+ QTest::newRow("afterLineEnd") << u"A\nB\r\nC\n\r\nD\r\n\r"_s << 1 << 42 << 3ll << QChar('\r')
+ << -1 << 1;
+
+ // try to access an inexisting column, seems to return the last character of the last line.
+ QTest::newRow("afterColumnEnd")
+ << u"A\nB\r\nC\n\r\nD\r\n\rAX"_s << 42 << 0 << 15ll << QChar('X') << 5 << 2;
+}
+
+void tst_qmlls_utils::textOffsetRowColumnConversions()
+{
+ QFETCH(QString, code);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(qsizetype, expectedOffset);
+ QFETCH(QChar, expectedChar);
+ QFETCH(int, expectedLine);
+ QFETCH(int, expectedCharacter);
+
+ qsizetype offset = QQmlLSUtils::textOffsetFrom(code, line, character);
+
+ QCOMPARE(offset, expectedOffset);
+ if (offset < code.size())
+ QCOMPARE(code[offset], expectedChar);
+
+ auto [computedRow, computedColumn] = QQmlLSUtils::textRowAndColumnFrom(code, expectedOffset);
+ if (expectedLine == -1)
+ expectedLine = line;
+ if (expectedCharacter == -1)
+ expectedCharacter = character;
+
+ QCOMPARE(computedRow, expectedLine);
+ QCOMPARE(computedColumn, expectedCharacter);
+}
+
+void tst_qmlls_utils::findItemFromLocation_data()
+{
+ QTest::addColumn<QString>("filePath");
+ // keep in mind that line and character are starting at 1!
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ // in case there are multiple items to be found (e.g. for a location between two objects), the
+ // item to be checked against
+ QTest::addColumn<int>("resultIndex");
+ QTest::addColumn<int>("expectedItemsCount");
+ QTest::addColumn<QQmlJS::Dom::DomType>("expectedType");
+ // set to -1 when unchanged from above line and character, starts at 1
+ QTest::addColumn<int>("expectedLine");
+ QTest::addColumn<int>("expectedCharacter");
+
+ const QString file1Qml = testFile(u"file1.qml"_s);
+
+ QTest::addRow("findIntProperty") << file1Qml << 9 << 18 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::PropertyDefinition
+ // start of the "property"-token of the "a" property
+ << -1 << positionAfterOneIndent;
+ QTest::addRow("findIntProperty2") << file1Qml << 9 << 10 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::PropertyDefinition
+ // start of the "property"-token of the "a" property
+ << -1 << positionAfterOneIndent;
+ QTest::addRow("findIntBinding")
+ << file1Qml << 30 << positionAfterOneIndent << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression
+ // start of the a identifier of the "a" binding
+ << -1 << positionAfterOneIndent;
+ QTest::addRow("findIntBinding2") << file1Qml << 30 << 8 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptLiteral
+ << -1 << 8;
+
+ QTest::addRow("colorBinding") << file1Qml << 39 << 13 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1
+ << 2 * positionAfterOneIndent - 1;
+
+ QTest::addRow("findVarProperty") << file1Qml << 12 << 12 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::PropertyDefinition
+ // start of the "property"-token of the "d" property
+ << -1 << positionAfterOneIndent;
+ QTest::addRow("findVarBinding") << file1Qml << 31 << 8 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptLiteral << -1 << 8;
+ QTest::addRow("beforeEProperty")
+ << file1Qml << 13 << positionAfterOneIndent << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::PropertyDefinition
+ // start of the "property"-token of the "e" property
+ << -1 << -1;
+ QTest::addRow("onEProperty") << file1Qml << 13 << 24 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::PropertyDefinition
+ // start of the "property"-token of the "e" property
+ << -1 << positionAfterOneIndent;
+ QTest::addRow("afterEProperty") << file1Qml << 13 << 25 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::PropertyDefinition
+ // start of the "property"-token of the "e" property
+ << -1 << positionAfterOneIndent;
+
+ QTest::addRow("property-in-ic") << file1Qml << 28 << 38 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::PropertyDefinition << -1 << 26;
+
+ QTest::addRow("onCChild") << file1Qml << 16 << positionAfterOneIndent << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1
+ << positionAfterOneIndent;
+
+ // check for off-by-one/overlapping items
+ QTest::addRow("closingBraceOfC")
+ << file1Qml << 16 << 19 << firstResult << outOfOne << QQmlJS::Dom::DomType::QmlObject
+ << -1 << positionAfterOneIndent;
+ QTest::addRow("beforeClosingBraceOfC")
+ << file1Qml << 16 << 18 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 12;
+ QTest::addRow("firstBetweenCandD")
+ << file1Qml << 16 << 20 << secondResult << outOfTwo << QQmlJS::Dom::DomType::QmlObject
+ << -1 << positionAfterOneIndent;
+ QTest::addRow("secondBetweenCandD")
+ << file1Qml << 16 << 20 << firstResult << outOfTwo
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << -1;
+
+ QTest::addRow("afterD") << file1Qml << 16 << 21 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 20;
+
+ // check what happens between items (it should not crash)
+
+ QTest::addRow("onWhitespaceBeforeC")
+ << file1Qml << 16 << 1 << firstResult << outOfOne << QQmlJS::Dom::DomType::Map << 9
+ << positionAfterOneIndent;
+
+ QTest::addRow("onWhitespaceAfterC")
+ << file1Qml << 17 << 8 << firstResult << outOfOne << QQmlJS::Dom::DomType::QmlObject
+ << -1 << positionAfterOneIndent;
+
+ QTest::addRow("onWhitespaceBetweenCAndD") << file1Qml << 17 << 23 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::Map << 16 << 8;
+ QTest::addRow("onWhitespaceBetweenCAndD2") << file1Qml << 17 << 24 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::Map << 16 << 8;
+
+ QTest::addRow("ic") << file1Qml << 15 << 5 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::QmlComponent << -1 << 5;
+ QTest::addRow("ic2") << file1Qml << 15 << 20 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 18;
+ QTest::addRow("ic3") << file1Qml << 15 << 33 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 29;
+
+ QTest::addRow("function") << file1Qml << 33 << 5 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::MethodInfo << -1 << positionAfterOneIndent;
+ QTest::addRow("function-parameter")
+ << file1Qml << 33 << 20 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 19;
+ // The return type of a function has no own DomItem. Instead, the return type of a function
+ // is saved into the MethodInfo.
+ QTest::addRow("function-return")
+ << file1Qml << 33 << 41 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 41;
+ QTest::addRow("function2") << file1Qml << 36 << 17 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::MethodInfo << -1
+ << positionAfterOneIndent;
+
+ // check rectangle property
+ QTest::addRow("rectangle-property")
+ << file1Qml << 44 << 31 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 29;
+}
+
+void tst_qmlls_utils::findItemFromLocation()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(int, resultIndex);
+ QFETCH(int, expectedItemsCount);
+ QFETCH(QQmlJS::Dom::DomType, expectedType);
+ QFETCH(int, expectedLine);
+ QFETCH(int, expectedCharacter);
+
+ if (expectedLine == -1)
+ expectedLine = line;
+ if (expectedCharacter == -1)
+ expectedCharacter = character;
+
+ // they all start at 1.
+ Q_ASSERT(line > 0);
+ Q_ASSERT(character > 0);
+ Q_ASSERT(expectedLine > 0);
+ Q_ASSERT(expectedCharacter > 0);
+
+ auto [env, file] = createEnvironmentAndLoadFile(filePath);
+
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+
+ QVERIFY(resultIndex < locations.size());
+ QCOMPARE(locations.size(), expectedItemsCount);
+
+ QQmlJS::Dom::DomItem itemToTest = locations[resultIndex].domItem;
+ // ask for the type in the args
+ if constexpr (enable_debug_output) {
+ if (itemToTest.internalKind() != expectedType) {
+ qDebug() << itemToTest.internalKindStr() << " has not the expected kind "
+ << expectedType << " for item " << itemToTest.toString();
+ }
+ }
+ QCOMPARE(itemToTest.internalKind(), expectedType);
+
+ QQmlJS::Dom::FileLocations::Tree locationToTest = locations[resultIndex].fileLocation;
+ QCOMPARE(locationToTest->info().fullRegion.startLine, quint32(expectedLine));
+ QCOMPARE(locationToTest->info().fullRegion.startColumn, quint32(expectedCharacter));
+}
+
+void tst_qmlls_utils::findTypeDefinitionFromLocation_data()
+{
+ QTest::addColumn<QString>("filePath");
+ // keep in mind that line and character are starting at 1!
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ // in case there are multiple items to be found (e.g. for a location between two objects), the
+ // item to be checked against
+ QTest::addColumn<int>("resultIndex");
+ QTest::addColumn<int>("expectedItemsCount");
+ QTest::addColumn<QString>("expectedFilePath");
+ // set to -1 when unchanged from above line and character. 0-based.
+ QTest::addColumn<int>("expectedLine");
+ QTest::addColumn<int>("expectedCharacter");
+
+ const QString file1Qml = testFile(u"file1.qml"_s);
+ const QString TypeQml = testFile(u"Type.qml"_s);
+ // pass this as file when no result is expected, e.g. for type definition of "var".
+
+ QTest::addRow("onCProperty") << file1Qml << 11 << 16 << firstResult << outOfOne << file1Qml << 7
+ << positionAfterOneIndent;
+
+ QTest::addRow("onCProperty2") << file1Qml << 28 << 37 << firstResult << outOfOne << file1Qml
+ << 7 << positionAfterOneIndent;
+
+ QTest::addRow("onCProperty3") << file1Qml << 28 << 35 << firstResult << outOfOne << file1Qml
+ << 7 << positionAfterOneIndent;
+
+ QTest::addRow("onCBinding") << file1Qml << 46 << 8 << firstResult << outOfOne << file1Qml << 7
+ << positionAfterOneIndent;
+
+ QTest::addRow("onDefaultBinding") << file1Qml << 16 << positionAfterOneIndent << firstResult
+ << outOfOne << file1Qml << 7 << positionAfterOneIndent;
+
+ QTest::addRow("onDefaultBindingId")
+ << file1Qml << 16 << 28 << firstResult << outOfOne << file1Qml << 16 << 20;
+
+ QTest::addRow("findIntProperty") << file1Qml << 9 << 18 << firstResult << outOfOne << file1Qml
+ << -1 << positionAfterOneIndent;
+ QTest::addRow("colorBinding") << file1Qml << 39 << 8 << firstResult << outOfOne << file1Qml
+ << -1 << positionAfterOneIndent;
+
+ // check what happens between items (it should not crash)
+
+ QTest::addRow("onWhitespaceBeforeC")
+ << file1Qml << 16 << 1 << firstResult << outOfOne << noResultExpected << -1 << -1;
+
+ QTest::addRow("onWhitespaceAfterC")
+ << file1Qml << 17 << 23 << firstResult << outOfOne << noResultExpected << -1 << -1;
+
+ QTest::addRow("onWhitespaceBetweenCAndD")
+ << file1Qml << 17 << 24 << firstResult << outOfOne << noResultExpected << -1 << -1;
+
+ QTest::addRow("ic") << file1Qml << 15 << 15 << firstResult << outOfOne << file1Qml << 15 << 15;
+ QTest::addRow("icBase") << file1Qml << 15 << 20 << firstResult << outOfOne
+ << u"TODO: file location for C++ defined types?"_s << -1 << -1;
+ QTest::addRow("ic3") << file1Qml << 15 << 33 << firstResult << outOfOne << file1Qml << -1 << 18;
+
+ // TODO: type definition of function = type definition of return type?
+ // if not, this might need fixing:
+ // currently, asking the type definition of the "function" keyword returns
+ // the type definitin of the return type (when available).
+ QTest::addRow("function-keyword") << file1Qml << 33 << 5 << firstResult << outOfOne << file1Qml
+ << 7 << positionAfterOneIndent;
+ QTest::addRow("function-parameter-builtin")
+ << file1Qml << 33 << 20 << firstResult << outOfOne << file1Qml << -1 << -1;
+ QTest::addRow("function-parameter-item") << file1Qml << 33 << 36 << firstResult << outOfOne
+ << file1Qml << 7 << positionAfterOneIndent;
+
+ QTest::addRow("function-return") << file1Qml << 33 << 41 << firstResult << outOfOne << file1Qml
+ << 7 << positionAfterOneIndent;
+
+ QTest::addRow("void-function")
+ << file1Qml << 36 << 17 << firstResult << outOfOne << noResultExpected << -1 << -1;
+
+ QTest::addRow("rectangle-property") << file1Qml << 44 << 31 << firstResult << outOfOne
+ << "TODO: c++ type location" << -1 << -1;
+
+ QTest::addRow("functionParameterICUsage")
+ << file1Qml << 34 << 16 << firstResult << outOfOne << file1Qml << 7 << 15;
+
+ QTest::addRow("ICBindingUsage")
+ << file1Qml << 47 << 21 << firstResult << outOfOne << file1Qml << 7 << 15;
+ QTest::addRow("ICBindingUsage2")
+ << file1Qml << 49 << 11 << firstResult << outOfOne << file1Qml << 7 << 15;
+ QTest::addRow("ICBindingUsage3")
+ << file1Qml << 52 << 17 << firstResult << outOfOne << file1Qml << 7 << 15;
+}
+
+void tst_qmlls_utils::findTypeDefinitionFromLocation()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(int, resultIndex);
+ QFETCH(int, expectedItemsCount);
+ QFETCH(QString, expectedFilePath);
+ QFETCH(int, expectedLine);
+ QFETCH(int, expectedCharacter);
+
+ if (expectedLine == -1)
+ expectedLine = line;
+ if (expectedCharacter == -1)
+ expectedCharacter = character;
+
+ // they all start at 1.
+ Q_ASSERT(line > 0);
+ Q_ASSERT(character > 0);
+ Q_ASSERT(expectedLine > 0);
+ Q_ASSERT(expectedCharacter > 0);
+
+ auto [env, file] = createEnvironmentAndLoadFile(filePath);
+
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+
+ QCOMPARE(locations.size(), expectedItemsCount);
+
+ auto base = QQmlLSUtils::findTypeDefinitionOf(locations[resultIndex].domItem);
+
+ // if expectedFilePath is empty, we probably just want to make sure that it does
+ // not crash
+ if (expectedFilePath == noResultExpected) {
+ QVERIFY(!base);
+ return;
+ }
+
+ QEXPECT_FAIL("findIntProperty", "Builtins not supported yet", Abort);
+ QEXPECT_FAIL("function-parameter-builtin", "Base types defined in C++ are not supported yet",
+ Abort);
+ QEXPECT_FAIL("colorBinding", "Types from C++ bases not supported yet", Abort);
+ QEXPECT_FAIL("rectangle-property", "Types from C++ bases not supported yet", Abort);
+ QEXPECT_FAIL("icBase", "Base types defined in C++ are not supported yet", Abort);
+ QVERIFY(base);
+
+ auto fileObject =
+ locations[resultIndex].domItem.goToFile(base->filename).as<QQmlJS::Dom::QmlFile>();
+
+ // print some debug message when failing, instead of using QVERIFY2
+ // (printing the type every time takes a lot of time).
+ if constexpr (enable_debug_output) {
+ if (!fileObject)
+ qDebug() << "Could not find the file" << base->filename << "in the Dom.";
+ }
+
+ QVERIFY(fileObject);
+ QCOMPARE(base->filename, expectedFilePath);
+ QCOMPARE(fileObject->canonicalFilePath(), expectedFilePath);
+
+ QCOMPARE(base->sourceLocation.startLine, quint32(expectedLine));
+ QCOMPARE(base->sourceLocation.startColumn, quint32(expectedCharacter));
+}
+
+void tst_qmlls_utils::findLocationOfItem_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<int>("expectedLine");
+ QTest::addColumn<int>("expectedCharacter");
+
+ const QString file1Qml = testFile(u"file1.qml"_s);
+
+ QTest::addRow("root-element") << file1Qml << 6 << 2 << -1 << 1;
+
+ QTest::addRow("property-a") << file1Qml << 9 << 18 << -1 << positionAfterOneIndent;
+ QTest::addRow("property-a2") << file1Qml << 9 << 10 << -1 << positionAfterOneIndent;
+ QTest::addRow("nested-C") << file1Qml << 20 << 9 << -1 << -1;
+ QTest::addRow("nested-C2") << file1Qml << 23 << 13 << -1 << -1;
+ QTest::addRow("D") << file1Qml << 17 << 33 << -1 << 32;
+ QTest::addRow("property-d-var-type") << file1Qml << 12 << 15 << -1 << 14;
+
+ QTest::addRow("import") << file1Qml << 4 << 6 << -1 << 1;
+}
+
+void tst_qmlls_utils::findLocationOfItem()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(int, expectedLine);
+ QFETCH(int, expectedCharacter);
+
+ if (expectedLine == -1)
+ expectedLine = line;
+ if (expectedCharacter == -1)
+ expectedCharacter = character;
+
+ // they all start at 1.
+ Q_ASSERT(line > 0);
+ Q_ASSERT(character > 0);
+ Q_ASSERT(expectedLine > 0);
+ Q_ASSERT(expectedCharacter > 0);
+
+ auto [env, file] = createEnvironmentAndLoadFile(filePath);
+
+ // grab item using already tested QQmlLSUtils::findLastItemsContaining
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+ QCOMPARE(locations.size(), 1);
+
+ // once the item is grabbed, make sure its line/character position can be obtained back
+ auto t = QQmlJS::Dom::FileLocations::treeOf(locations.front().domItem);
+
+ QCOMPARE(t->info().fullRegion.startLine, quint32(expectedLine));
+ QCOMPARE(t->info().fullRegion.startColumn, quint32(expectedCharacter));
+}
+
+void tst_qmlls_utils::findBaseObject_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ // to avoid mixing up the types (because they are all called Item or QQuickItem eitherway)
+ // mark them with properties and detect right types by their marker property,
+ // usually called (in<Filename>DotQml or in<Inline component Name>)
+ QTest::addColumn<QSet<QString>>("expectedPropertyName");
+ // because types inherit properties, make sure that derived type properties are not in the base
+ // type, to correctly detect mixups between types and their base types
+ QTest::addColumn<QSet<QString>>("unExpectedPropertyName");
+
+ // (non) Expected Properties Names = ePN (nEPN)
+ // marker properties for the root object in BaseType.qml
+ QSet<QString> ePNBaseType;
+ ePNBaseType << u"inBaseTypeDotQml"_s;
+ QSet<QString> nEPNBaseType;
+ nEPNBaseType << u"inTypeDotQml"_s;
+
+ // marker properties for the root object in Type.qml
+ QSet<QString> ePNType;
+ ePNType << u"inBaseTypeDotQml"_s << u"inTypeDotQml"_s;
+ QSet<QString> nEPNType;
+
+ // marker properties for QQuickItem (e.g. the base of "Item")
+ QSet<QString> ePNQQuickItem;
+ QSet<QString> nEPNQQuickItem;
+ nEPNQQuickItem << u"inBaseTypeDotQml"_s << u"inTypeDotQml"_s;
+
+ // marker properties for MyInlineComponent
+ QSet<QString> ePNMyInlineComponent;
+ QSet<QString> nEPNMyInlineComponent;
+ ePNMyInlineComponent << u"inBaseTypeDotQml"_s << u"inTypeDotQml"_s << u"inMyInlineComponent"_s;
+
+ // marker properties for MyNestedInlineComponent
+ const QSet<QString> ePNMyNestedInlineComponent{ u"inMyNestedInlineComponent"_s };
+ const QSet<QString> nEPNMyNestedInlineComponent{ u"inBaseTypeDotQml"_s, u"inTypeDotQml"_s,
+ u"inMyInlineComponent"_s };
+
+ // marker properties for MyBaseInlineComponent
+ const QSet<QString> ePNMyBaseInlineComponent{ u"inBaseTypeDotQml"_s };
+ const QSet<QString> nEPNMyBaseInlineComponent{ u"inTypeDotQml"_s, u"inMyInlineComponent"_s };
+
+ const int rootElementDefLine = 6;
+ QTest::addRow("root-element") << testFile(u"Type.qml"_s) << rootElementDefLine << 5
+ << ePNQQuickItem << nEPNQQuickItem;
+ QTest::addRow("root-element-from-id") << testFile(u"Type.qml"_s) << rootElementDefLine + 1 << 12
+ << ePNBaseType << nEPNBaseType;
+
+ const int myInlineComponentDefLine = 10;
+ // on the component name: go to BaseType
+ QTest::addRow("ic-name") << testFile(u"Type.qml"_s) << myInlineComponentDefLine << 26
+ << ePNBaseType << nEPNBaseType;
+ // on the "BaseType" type: go to QQuickitem (base type of BaseType).
+ QTest::addRow("ic-basetypename") << testFile(u"Type.qml"_s) << myInlineComponentDefLine << 37
+ << ePNQQuickItem << nEPNQQuickItem;
+ QTest::addRow("ic-from-id") << testFile(u"Type.qml"_s) << myInlineComponentDefLine + 1 << 19
+ << ePNBaseType << nEPNBaseType;
+
+ const int inlineTypeDefLine = 15;
+ QTest::addRow("inline") << testFile(u"Type.qml"_s) << inlineTypeDefLine << 23 << ePNQQuickItem
+ << nEPNQQuickItem;
+ QTest::addRow("inline2") << testFile(u"Type.qml"_s) << inlineTypeDefLine << 38 << ePNQQuickItem
+ << nEPNQQuickItem;
+ QTest::addRow("inline3") << testFile(u"Type.qml"_s) << inlineTypeDefLine << 15 << ePNQQuickItem
+ << nEPNQQuickItem;
+ QTest::addRow("inline-from-id") << testFile(u"Type.qml"_s) << inlineTypeDefLine + 1 << 24
+ << ePNBaseType << nEPNBaseType;
+
+ const int inlineIcDefLine = 23;
+ QTest::addRow("inline-ic") << testFile(u"Type.qml"_s) << inlineIcDefLine << 38
+ << ePNMyBaseInlineComponent << nEPNMyBaseInlineComponent;
+ QTest::addRow("inline-ic-from-id") << testFile(u"Type.qml"_s) << inlineIcDefLine + 1 << 28
+ << ePNMyBaseInlineComponent << nEPNMyBaseInlineComponent;
+
+ const int inlineNestedIcDefLine = 27;
+ QTest::addRow("inline-ic2") << testFile(u"Type.qml"_s) << inlineNestedIcDefLine << 46
+ << ePNMyNestedInlineComponent << nEPNMyNestedInlineComponent;
+ QTest::addRow("inline-ic2-from-id")
+ << testFile(u"Type.qml"_s) << inlineNestedIcDefLine + 1 << 23
+ << ePNMyNestedInlineComponent << nEPNMyNestedInlineComponent;
+}
+
+void tst_qmlls_utils::findBaseObject()
+{
+ const QByteArray failOnInlineComponentsMessage =
+ "The Dom cannot resolve inline components from the basetype yet.";
+
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QSet<QString>, expectedPropertyName);
+ QFETCH(QSet<QString>, unExpectedPropertyName);
+
+ // they all start at 1.
+ Q_ASSERT(line > 0);
+ Q_ASSERT(character > 0);
+
+ auto [env, file] = createEnvironmentAndLoadFile(filePath);
+
+ // grab item using already tested QQmlLSUtils::findLastItemsContaining
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+ if constexpr (enable_debug_output) {
+ if (locations.size() > 1) {
+ for (auto &x : locations)
+ qDebug() << x.domItem.toString();
+ }
+ }
+ QCOMPARE(locations.size(), 1);
+
+ auto typeLocation = QQmlLSUtils::findTypeDefinitionOf(locations.front().domItem);
+ QEXPECT_FAIL("inline-ic", failOnInlineComponentsMessage, Abort);
+ QEXPECT_FAIL("inline-ic2", failOnInlineComponentsMessage, Abort);
+ QVERIFY(typeLocation);
+ QQmlJS::Dom::DomItem type = QQmlLSUtils::sourceLocationToDomItem(
+ locations.front().domItem.goToFile(typeLocation->filename),
+ typeLocation->sourceLocation);
+ auto base = QQmlLSUtils::baseObject(type);
+
+ if constexpr (enable_debug_output) {
+ if (!base)
+ qDebug() << u"Could not find the base of type "_s << type << u" from item:\n"_s
+ << locations.front().domItem.toString();
+ }
+
+ QEXPECT_FAIL("inline-ic-from-id", failOnInlineComponentsMessage, Abort);
+ QEXPECT_FAIL("inline-ic2-from-id", failOnInlineComponentsMessage, Abort);
+ QVERIFY(base);
+
+ const QSet<QString> propertyDefs = base.field(QQmlJS::Dom::Fields::propertyDefs).keys();
+ expectedPropertyName.subtract(propertyDefs);
+ QVERIFY2(expectedPropertyName.empty(),
+ u"Incorrect baseType found: it is missing following marker properties: "_s
+ .append(printSet(expectedPropertyName))
+ .toLatin1());
+ unExpectedPropertyName.intersect(propertyDefs);
+ QVERIFY2(unExpectedPropertyName.empty(),
+ u"Incorrect baseType found: it has an unexpected marker properties: "_s
+ .append(printSet(unExpectedPropertyName))
+ .toLatin1());
+}
+
+/*! \internal
+ \brief Wrapper for findUsages data.
+*/
+struct UsageData
+{
+ QString testFileName;
+ QQmlLSUtils::Usages expectedUsages;
+};
+
+void tst_qmlls_utils::findUsages_data()
+{
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<UsageData>("data");
+
+ const auto readFileContent = [](const QString &testFileName) {
+ QFile file(testFileName);
+ if (file.open(QIODeviceBase::ReadOnly))
+ return QString::fromUtf8(file.readAll());
+ return QString{};
+ };
+
+ const auto makeUsages = [](const QString &fileName, QList<QQmlLSUtils::Location> &locations) {
+ UsageData data;
+ std::sort(locations.begin(), locations.end());
+ data.expectedUsages = { locations, {} };
+ data.testFileName = fileName;
+ return data;
+ };
+
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ const auto testFileName = testFile("findUsages/jsIdentifier/jsIdentifier.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ {
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 13,
+ strlen("sum"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 13,
+ strlen("sum"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 19,
+ strlen("sum"));
+ const auto sumUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findSumFromDeclaration") << 8 << 13 << sumUsages;
+ QTest::addRow("findSumFromUsage") << 10 << 20 << sumUsages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 17,
+ strlen("i"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 24,
+ strlen("i"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 32,
+ strlen("i"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 36,
+ strlen("i"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 25,
+ strlen("i"));
+ const auto iUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findIFromDeclaration") << 9 << 17 << iUsages;
+ QTest::addRow("findIFromUsage") << 9 << 24 << iUsages;
+ QTest::addRow("findIFromUsage2") << 10 << 25 << iUsages;
+ }
+ }
+ {
+ const auto testFileName = testFile("findUsages/property/property.qml");
+ const auto otherFile = testFile("findUsages/property/PropertyFromAnotherFile.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ const auto otherFileContent = readFileContent(otherFile);
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 18,
+ strlen("helloProperty"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 13, 13,
+ strlen("helloProperty"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 13, 29,
+ strlen("helloProperty"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 20, 9,
+ strlen("helloProperty"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 21, 9,
+ strlen("helloPropertyChanged"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 5,
+ strlen("onHelloPropertyChanged"));
+ const auto helloPropertyUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findPropertyFromDeclaration") << 8 << 18 << helloPropertyUsages;
+ QTest::addRow("findPropertyFromUsage") << 13 << 13 << helloPropertyUsages;
+ QTest::addRow("findPropertyFromUsage2") << 13 << 29 << helloPropertyUsages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 36, 20,
+ strlen("helloProperty"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 38, 25,
+ strlen("helloProperty"));
+ const auto subItemHelloPropertyUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findPropertyFromDeclarationInSubItem") << 38 << 25 << subItemHelloPropertyUsages;
+ QTest::addRow("findPropertyFromUsageInSubItem") << 36 << 20 << subItemHelloPropertyUsages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 27, 22,
+ strlen("helloProperty"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 29, 20,
+ strlen("helloProperty"));
+ const auto ICHelloPropertyUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findPropertyFromDeclarationInIC") << 27 << 22 << ICHelloPropertyUsages;
+ QTest::addRow("findPropertyFromUsageInIC") << 29 << 20 << ICHelloPropertyUsages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(otherFile, otherFileContent, 4, 18,
+ strlen("helloProperty"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 42, 9,
+ strlen("helloProperty"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 44, 20,
+ strlen("helloProperty"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 46, 9,
+ strlen("OnHelloPropertyChanged"));
+ const auto helloPropertyUsages = makeUsages(testFileName, expectedUsages);
+
+ QTest::addRow("findPropertyFromOtherFile") << 42 << 13 << helloPropertyUsages;
+ }
+ }
+ {
+ const auto testFileName = testFile("findUsages/propertyInNested/propertyInNested.qml");
+ const auto testFileContent = readFileContent(testFileName);
+
+ const auto componentFileName =
+ testFile("findUsages/propertyInNested/NestedComponentInFile.qml");
+ const auto componentFileContent = readFileContent(componentFileName);
+
+ const auto componentFileName3 =
+ testFile("findUsages/propertyInNested/NestedComponentInFile3.qml");
+ const auto componentFileContent3 = readFileContent(componentFileName);
+
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 7, 18,
+ strlen("p2"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 33, 31,
+ strlen("p2"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 34, 37,
+ strlen("p2"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 35, 43,
+ strlen("p2"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 36, 49,
+ strlen("p2"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 42, 26,
+ strlen("p2"));
+ const auto p2Usages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findPropertyFromDeclaration2") << 7 << 18 << p2Usages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 29, 13,
+ strlen("myNested"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 32, 17,
+ strlen("myNested"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 33, 17,
+ strlen("myNested"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 34, 17,
+ strlen("myNested"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 35, 17,
+ strlen("myNested"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 36, 17,
+ strlen("myNested"));
+ const auto nestedUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findIdFromUsage") << 36 << 20 << nestedUsages;
+ QTest::addRow("findIdFromDefinition") << 29 << 17 << nestedUsages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 14, 35,
+ strlen("inner"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 32, 32,
+ strlen("inner"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 35, 32,
+ strlen("inner"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 36, 32,
+ strlen("inner"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 16, 9,
+ strlen("inner"));
+ const auto nestedComponent3Usages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findPropertyFromUsageInFieldMemberExpression")
+ << 36 << 34 << nestedComponent3Usages;
+
+ QTest::addRow("findFieldMemberExpressionUsageFromPropertyDefinition")
+ << 14 << 38 << nestedComponent3Usages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(componentFileName, componentFileContent,
+ 4, 37, strlen("inner"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 50, 32,
+ strlen("inner"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 52, 32,
+ strlen("inner"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 53, 32,
+ strlen("inner"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 54, 32,
+ strlen("inner"));
+ const auto nestedComponent3Usages = makeUsages(testFileName, expectedUsages);
+ const auto nestedComponent3UsagesFromOtherFile =
+ makeUsages(componentFileName, expectedUsages);
+ QTest::addRow("findPropertyFromUsageInFieldMemberExpressionFromOtherFile")
+ << 50 << 33 << nestedComponent3Usages;
+
+ QTest::addRow("findFieldMemberExpressionUsageFromPropertyDefinitionFromOtherFile")
+ << 4 << 38 << nestedComponent3UsagesFromOtherFile;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 35, 38,
+ strlen("p2"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 20, 22,
+ strlen("p2"));
+ const auto nestedComponent3P2Usages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findProperty2FromUsageInFieldMemberExpression")
+ << 35 << 39 << nestedComponent3P2Usages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(componentFileName3, componentFileContent3,
+ 5, 18, strlen("p2"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 53, 44,
+ strlen("p2"));
+ const auto nestedComponent3P2Usages = makeUsages(testFileName, expectedUsages);
+ const auto nestedComponent3P2UsagesFromOtherFile = makeUsages(componentFileName3, expectedUsages);
+ QTest::addRow("findProperty2FromUsageInFieldMemberExpressionInOtherFile")
+ << 53 << 44 << nestedComponent3P2Usages;
+ QTest::addRow("findProperty2FromUsageInDefinitionInOtherFile")
+ << 5 << 19 << nestedComponent3P2UsagesFromOtherFile;
+ }
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ const auto testFileName = testFile("findUsages/idUsages/idUsages.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 7, 9,
+ strlen("rootId"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 11, 17,
+ strlen("rootId"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 12, 20,
+ strlen("rootId"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 17, 9,
+ strlen("rootId"));
+ const auto rootIdUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findIdFromUsageInChild") << 12 << 20 << rootIdUsages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ const auto testFileName = testFile("findUsages/recursive/recursive.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 14,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 24,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 34,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 51,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 68,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 12, 20,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 15, 34,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 19, 27,
+ strlen("recursive"));
+ const auto recursiveUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findFunctionUsage") << 10 << 30 << recursiveUsages;
+ QTest::addRow("findFunctionUsage2") << 12 << 24 << recursiveUsages;
+ QTest::addRow("findQualifiedFunctionUsage") << 19 << 31 << recursiveUsages;
+ QTest::addRow("findFunctionUsageFromDefinition") << 8 << 17 << recursiveUsages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ const auto testFileName = testFile("findUsages/recursive/recursive.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ const auto otherFileName = testFile("findUsages/recursive/RecursiveInOtherFile.qml");
+ const auto otherFileContent = readFileContent(otherFileName);
+
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 27, 61,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 4, 14,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 6, 24,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 6, 34,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 6, 51,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 6, 68,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 8, 20,
+ strlen("recursive"));
+
+ const auto recursiveUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findFunctionUsageFromOtherFile") << 27 << 64 << recursiveUsages;
+ const auto recursiveUsagesFromOtherFile = makeUsages(otherFileName, expectedUsages);
+ QTest::addRow("findFunctionUsageFromSameFile") << 6 << 39 << recursiveUsagesFromOtherFile;
+ QTest::addRow("findFunctionUsageFromDefinitionInOtherFile")
+ << 4 << 14 << recursiveUsagesFromOtherFile;
+ }
+ {
+ const auto testFileName = testFile("findUsages/signalsAndHandlers/signalsAndHandlers.qml");
+ const auto testFileContent = readFileContent(testFileName);
+
+ const auto otherFileName = testFile("findUsages/signalsAndHandlers/widthChangedInAnotherFile.qml");
+ const auto otherFileContent = readFileContent(otherFileName);
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 12,
+ strlen("helloSignal"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 11, 9,
+ strlen("helloSignal"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 13, 13,
+ strlen("helloSignal"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 17, 17,
+ strlen("helloSignal"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 21, 9,
+ strlen("helloSignal"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 39, 5,
+ strlen("onHelloSignal"));
+ const auto helloSignalUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findQmlSignalUsageFromDefinition") << 8 << 17 << helloSignalUsages;
+ QTest::addRow("findQmlSignalUsageFromUsage") << 13 << 17 << helloSignalUsages;
+ QTest::addRow("findQmlSignalUsageFromHandler") << 39 << 11 << helloSignalUsages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 5, 5,
+ strlen("onWidthChanged"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 13,
+ strlen("widthChanged"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 27, 17,
+ strlen("widthChanged"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 28, 20,
+ strlen("widthChanged"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 34, 20,
+ strlen("widthChanged"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 33, 13,
+ strlen("widthChanged"));
+ const auto widthChangedUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findCppSignalUsageFromUsage") << 27 << 23 << widthChangedUsages;
+ QTest::addRow("findCppSignalUsageFromQualifiedUsage") << 28 << 23 << widthChangedUsages;
+ QTest::addRow("findCppSignalUsageFromQualifiedUsage2") << 34 << 24 << widthChangedUsages;
+ }
+ }
+ {
+ const auto testFileName = testFile("findUsages/binding/binding.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 18,
+ strlen("helloPropertyBinding"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 5,
+ strlen("helloPropertyBinding"));
+ const auto helloPropertyBindingUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findBindingUsagesFromDefinition") << 9 << 21 << helloPropertyBindingUsages;
+ QTest::addRow("findBindingUsagesFromBinding") << 10 << 19 << helloPropertyBindingUsages;
+ }
+ {
+ const auto testFileName = testFile("findUsages/signalsAndHandlers/signalAndHandlers2.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 7, 14,
+ strlen("myHelloHandler"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 20,
+ strlen("myHelloHandler"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 14, 29,
+ strlen("myHelloHandler"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 15, 24,
+ strlen("myHelloHandler"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 17,
+ strlen("myHelloHandler"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 24, 24,
+ strlen("myHelloHandler"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 25, 21,
+ strlen("myHelloHandler"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 33, 19,
+ strlen("myHelloHandler"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 42, 29,
+ strlen("myHelloHandler"));
+ const auto myHelloHandlerUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findJSMethodFromUsageInBinding") << 8 << 27 << myHelloHandlerUsages;
+ QTest::addRow("findJSMethodFromDefinition") << 7 << 22 << myHelloHandlerUsages;
+ QTest::addRow("findJSMethodFromDefinition2") << 7 << 9 << myHelloHandlerUsages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 13, 18,
+ strlen("checkHandlers"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 14, 5,
+ strlen("onCheckHandlersChanged"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 17, 9,
+ strlen("checkHandlersChanged"));
+ const auto checkHandlersUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findQmlPropertyHandlerFromDefinition") << 13 << 18 << checkHandlersUsages;
+ QTest::addRow("findQmlPropertyHandlerFromHandler") << 14 << 5 << checkHandlersUsages;
+ QTest::addRow("findQmlPropertyHandlerFromSignalCall") << 17 << 9 << checkHandlersUsages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 15, 5,
+ strlen("onChildrenChanged"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 18, 9,
+ strlen("childrenChanged"));
+ const auto checkCppHandlersUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findCppPropertyHandlerFromHandler") << 15 << 5 << checkCppHandlersUsages;
+ QTest::addRow("findCppPropertyHandlerFromSignalCall") << 18 << 9 << checkCppHandlersUsages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 20, 18,
+ strlen("_"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 5,
+ strlen("on_Changed"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 27, 9,
+ strlen("_Changed"));
+ const auto checkHandlersUsages2 = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findQmlPropertyHandler2FromDefinition") << 20 << 18 << checkHandlersUsages2;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 21, 18,
+ strlen("______42"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 24, 5,
+ strlen("on______42Changed"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 28, 9,
+ strlen("______42Changed"));
+ const auto checkHandlersUsages3 = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findQmlPropertyHandler3FromDefinition") << 21 << 18 << checkHandlersUsages3;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 22, 18,
+ strlen("_123a"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 25, 5,
+ strlen("on_123AChanged"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 29, 9,
+ strlen("_123aChanged"));
+ const auto checkHandlersUsages4 = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findQmlPropertyHandler4FromDefinition") << 22 << 18 << checkHandlersUsages4;
+ }
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ const auto testFileName = testFile("findUsages/connections/connections.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 9,
+ strlen("onClicked"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 17, 23,
+ strlen("clicked"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 33, 15,
+ strlen("clicked"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 16, 22,
+ strlen("onClicked"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 34, 15,
+ strlen("clicked"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 18, 23,
+ strlen("clicked"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 28, 9,
+ strlen("onClicked"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 35, 15,
+ strlen("clicked"));
+ const auto signalInConnection = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findSignalsInConnectionFromSignal") << 33 << 15 << signalInConnection;
+ QTest::addRow("findSignalsInConnectionFromHandler") << 9 << 9 << signalInConnection;
+ QTest::addRow("findSignalsInConnectionFromFunction") << 16 << 22 << signalInConnection;
+ }
+ {
+ const auto testFileName =
+ testFile("findUsages/parametersAndDeconstruction/parametersAndDeconstruction.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 30,
+ strlen("a"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 16,
+ strlen("a"));
+ const auto aParamUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findMethodParameterA") << 9 << 16 << aParamUsages;
+ QTest::addRow("findMethodParameterAFromUsage") << 8 << 30 << aParamUsages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 50,
+ strlen("x"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 28,
+ strlen("x"));
+ const auto xParamUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findMethodParameterXDeconstructed") << 8 << 50 << xParamUsages;
+ QTest::addRow("findMethodParameterXDeconstructedFromUsage") << 9 << 28 << xParamUsages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 53,
+ strlen("y"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 32,
+ strlen("y"));
+ const auto yParamUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findMethodParameterYDeconstructed") << 8 << 53 << yParamUsages;
+ QTest::addRow("findMethodParameterYDeconstructedFromUsage") << 9 << 32 << yParamUsages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 59,
+ strlen("z"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 36,
+ strlen("z"));
+ const auto zParamUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findMethodParameterZDeconstructed") << 8 << 59 << zParamUsages;
+ QTest::addRow("findMethodParameterZDeconstructedFromUsage") << 9 << 36 << zParamUsages;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 13, 14,
+ strlen("a"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 14, 17,
+ strlen("a"));
+ const auto deconstructedAUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("deconstructed") << 14 << 17 << deconstructedAUsages;
+ QTest::addRow("deconstructedFromDefinition") << 13 << 14 << deconstructedAUsages;
+ }
+ }
+ {
+ const auto testFileName = testFile("findUsages/groupPropertyUsage/groupPropertyUsage.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ const auto otherFileName = testFile("findUsages/groupPropertyUsage/fontFamilyUsage.qml");
+ const auto otherFileContent = readFileContent(otherFileName);
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 5, 34,
+ strlen("family"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 14, 17,
+ strlen("family"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 35,
+ strlen("family"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 10,
+ strlen("family"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 33, 48,
+ strlen("family"));
+ const auto groupPropertyUsages1 = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("groupPropertyUsages1") << 14 << 17 << groupPropertyUsages1;
+ const auto groupPropertyUsages1FromOtherFile =
+ makeUsages(otherFileName, expectedUsages);
+ QTest::addRow("groupPropertyUsages1FromOtherFile")
+ << 5 << 37 << groupPropertyUsages1FromOtherFile;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 5,
+ strlen("font"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 24, 5,
+ strlen("font"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 12, 13,
+ strlen("font"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 30,
+ strlen("font"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 32, 41,
+ strlen("font"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 33, 43,
+ strlen("font"));
+ const auto groupPropertyUsages2 = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("groupPropertyUsages2") << 23 << 5 << groupPropertyUsages2;
+ }
+ }
+ {
+ const auto testFileName =
+ testFile("findUsages/attachedPropertyUsage/attachedPropertyUsage.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 5,
+ strlen("Keys"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 12, 25,
+ strlen("Keys"));
+ const auto attachedPropertyUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("attachedPropertyUsages") << 12 << 25 << attachedPropertyUsages;
+ }
+ {
+ const auto testFileName = testFile("findUsages/inlineComponents/inlineComponents.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 22,
+ strlen("foo"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 44,
+ strlen("foo"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 14, 27,
+ strlen("foo"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 20, 20,
+ strlen("foo"));
+ const auto inlineUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("inlineUsagesFromProperty") << 9 << 22 << inlineUsages;
+ QTest::addRow("inlineUsagesFromUsageOfBaseProperty") << 14 << 27 << inlineUsages;
+ QTest::addRow("inlineUsagesFromJsScope") << 20 << 20 << inlineUsages;
+ }
+ {
+ const auto testFileName = testFile("findUsages/propertyChanges/propertyChanges.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 9,
+ strlen("onClicked"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 16, 21,
+ strlen("onClicked"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 19, 25,
+ strlen("onClicked"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 25, 17,
+ strlen("onClicked"));
+ const auto propertyChanges = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("propertyChanges1") << 16 << 21 << propertyChanges;
+ }
+ {
+ const auto testFileName = testFile("findUsages/bindings/bindings.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 11, 23,
+ strlen("patronChanged"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 14, 27,
+ strlen("patronChanged"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 21, 27,
+ strlen("patronChanged"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 27, 19,
+ strlen("patronChanged"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 27, 41,
+ strlen("patronChanged"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 34, 17,
+ strlen("patronChanged"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 13, 20,
+ strlen("patronChanged"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 20, 23,
+ strlen("\"patronChanged\""));
+ const auto bindings = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("propertyInBindingsFromDecl") << 11 << 23 << bindings;
+ QTest::addRow("generalizedGroupPropertyBindings") << 27 << 19 << bindings;
+ }
+ {
+ const auto testFileName = testFile("findUsages/enums/Enums.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ const auto otherFileName = testFile("findUsages/enums/EnumsFromAnotherFile.qml");
+ const auto otherFileContent = readFileContent(otherFileName);
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 9,
+ strlen("Patron"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 22, 35,
+ strlen("Patron"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 34,
+ strlen("Patron"));
+ const auto enums = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("enumValuesFromDeclaration") << 9 << 9 << enums;
+ QTest::addRow("enumValuesFromUsage") << 22 << 35 << enums;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 10,
+ strlen("Cats"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 22, 30,
+ strlen("Cats"));
+ const auto enums = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("enumNameFromDeclaration") << 8 << 10 << enums;
+ QTest::addRow("enumNameFromUsage") << 22 << 30 << enums;
+ }
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 26, 46,
+ strlen("FromAnotherUniverse"));
+ expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 4, 68,
+ strlen("FromAnotherUniverse"));
+ const auto enums = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("enumNameFromDeclarationInOtherFile") << 26 << 50 << enums;
+ const auto enumsFromOtherFile = makeUsages(otherFileName, expectedUsages);
+ QTest::addRow("enumNameFromUsageFromOtherFile") << 4 << 81 << enumsFromOtherFile;
+ }
+ }
+ {
+ const auto testFileName = testFile("findUsages/inlineComponents/inlineComponents2.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 4, 15,
+ strlen("MyIC"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 5, 5,
+ strlen("MyIC"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 5, 12,
+ strlen("MyIC"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 5, 19,
+ strlen("MyIC"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 6, 19,
+ strlen("MyIC"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 6, 26,
+ strlen("MyIC"));
+ const auto inlineComponents = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findICUsagesFromDefinition") << 4 << 16 << inlineComponents;
+ QTest::addRow("findICUsagesFromDefinition2") << 4 << 9 << inlineComponents;
+ QTest::addRow("findICUsagesFromUsage") << 5 << 19 << inlineComponents;
+ QTest::addRow("findICUsagesFromTypeUsage") << 6 << 19 << inlineComponents;
+ }
+ }
+ {
+ const auto testFileName = testFile("findUsages/inlineComponents/inlineComponents.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ const auto providerFileName =
+ testFile("findUsages/inlineComponents/InlineComponentProvider.qml");
+ const auto providerFileContent = readFileContent(providerFileName);
+ {
+ QList<QQmlLSUtils::Location> expectedUsages;
+ expectedUsages << QQmlLSUtils::Location::from(providerFileName, providerFileContent, 4,
+ 15, strlen("IC1"));
+ expectedUsages << QQmlLSUtils::Location::from(providerFileName, providerFileContent, 5,
+ 36, strlen("IC1"));
+ expectedUsages << QQmlLSUtils::Location::from(providerFileName, providerFileContent, 7,
+ 5, strlen("IC1"));
+ expectedUsages << QQmlLSUtils::Location::from(providerFileName, providerFileContent, 17,
+ 13, strlen("IC1"));
+
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 25, 38,
+ strlen("IC1"));
+ expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 25, 84,
+ strlen("IC1"));
+
+ {
+ const auto usagesForTestFile = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findICUsagesFromTypeAnnotationInOtherFiles")
+ << 25 << 39 << usagesForTestFile;
+ QTest::addRow("findICUsagesFromInstantiationInOtherFiles")
+ << 25 << 84 << usagesForTestFile;
+ }
+
+ {
+ const auto usagesInProviderFile = makeUsages(providerFileName, expectedUsages);
+
+ QTest::addRow("findICUsagesFromDefinitionInOtherFiles")
+ << 4 << 16 << usagesInProviderFile;
+ QTest::addRow("findICUsagesFromInstantiationInOtherFiles2")
+ << 17 << 14 << usagesInProviderFile;
+ }
+ }
+ }
+}
+
+void tst_qmlls_utils::findUsages()
+{
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(UsageData, data);
+
+ {
+ auto usagesInFilename = data.expectedUsages.usagesInFilename();
+ QVERIFY(std::is_sorted(usagesInFilename.begin(), usagesInFilename.end()));
+ auto usagesInFile = data.expectedUsages.usagesInFile();
+ QVERIFY(std::is_sorted(usagesInFile.begin(), usagesInFile.end()));
+ }
+
+ auto [env, file] = createEnvironmentAndLoadFile(data.testFileName);
+
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+
+ if constexpr (enable_debug_output) {
+ if (locations.size() > 1) {
+ for (auto &x : locations)
+ qDebug() << x.domItem.toString();
+ }
+ }
+ QCOMPARE(locations.size(), 1);
+
+ auto usages = QQmlLSUtils::findUsagesOf(locations.front().domItem);
+
+ if constexpr (enable_debug_output) {
+ if (usages != data.expectedUsages) {
+ qDebug() << "Got:\n";
+ for (auto &x : usages.usagesInFile()) {
+ qDebug() << x.filename << "(" << x.sourceLocation.startLine << ", "
+ << x.sourceLocation.startColumn << "), " << x.sourceLocation.offset << "+"
+ << x.sourceLocation.length;
+ }
+ qDebug() << "with usages in filenames:" << usages.usagesInFilename();
+ qDebug() << "But expected: \n";
+ for (auto &x : data.expectedUsages.usagesInFile()) {
+ qDebug() << x.filename << "(" << x.sourceLocation.startLine << ", "
+ << x.sourceLocation.startColumn << "), " << x.sourceLocation.offset << "+"
+ << x.sourceLocation.length;
+ }
+ qDebug() << "with usages in filenames:" << data.expectedUsages.usagesInFilename();
+ }
+ }
+
+ QCOMPARE(usages, data.expectedUsages);
+}
+
+
+void tst_qmlls_utils::renameUsages_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<QString>("newName");
+ QTest::addColumn<QQmlLSUtils::RenameUsages>("expectedRenames");
+ QTest::addColumn<QString>("expectedError");
+
+ const QString testFileName = testFile(u"JSUsages.qml"_s);
+ const QString testFileNameFromAnotherFile = testFile(u"JSUsagesFromAnotherFile.qml"_s);
+ const QString testFileContent = readFileContent(testFileName);
+ const QString testFileFromAnotherFileContent = readFileContent(testFileNameFromAnotherFile);
+
+ const QString noError;
+ const QQmlLSUtils::RenameUsages noRenames;
+
+ QQmlLSUtils::RenameUsages methodFRename{
+ {
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 72, 14, strlen("recursive"),
+ u"newNameNewMe"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 74, 24, strlen("recursive"),
+ u"newNameNewMe"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 74, 34, strlen("recursive"),
+ u"newNameNewMe"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 74, 51, strlen("recursive"),
+ u"newNameNewMe"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 74, 68, strlen("recursive"),
+ u"newNameNewMe"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 76, 20, strlen("recursive"),
+ u"newNameNewMe"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 79, 34, strlen("recursive"),
+ u"newNameNewMe"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 84, 27, strlen("recursive"),
+ u"newNameNewMe"_s),
+ },
+ {}
+ };
+
+ QQmlLSUtils::RenameUsages JSIdentifierSumRename{
+ {
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 8, 13, strlen("sum"),
+ u"sumsumsum123"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 10, 13, strlen("sum"),
+ u"sumsumsum123"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 10, 19, strlen("sum"),
+ u"sumsumsum123"_s),
+ },
+ {}
+ };
+
+ QQmlLSUtils::RenameUsages qmlSignalRename{
+ {
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 88, 12,
+ strlen("helloSignal"), u"finalSignal"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 91, 9, strlen("helloSignal"),
+ u"finalSignal"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 93, 13,
+ strlen("helloSignal"), u"finalSignal"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 97, 17,
+ strlen("helloSignal"), u"finalSignal"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 101, 9,
+ strlen("helloSignal"), u"finalSignal"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 119, 5,
+ strlen("onHelloSignal"), u"onFinalSignal"_s),
+ },
+ {}
+ };
+
+ QQmlLSUtils::RenameUsages helloPropertyRename{
+ {
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 17, 18,
+ strlen("helloProperty"), u"freshPropertyName"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 24, 13,
+ strlen("helloProperty"), u"freshPropertyName"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 24, 29,
+ strlen("helloProperty"), u"freshPropertyName"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 65, 60,
+ strlen("helloProperty"), u"freshPropertyName"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 151, 9,
+ strlen("helloPropertyChanged"),
+ u"freshPropertyNameChanged"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 153, 5,
+ strlen("onHelloPropertyChanged"),
+ u"onFreshPropertyNameChanged"_s),
+ QQmlLSUtils::Edit::from(testFileNameFromAnotherFile, testFileFromAnotherFileContent,
+ 12, 16, strlen("helloProperty"), u"freshPropertyName"_s),
+ },
+ {}
+ };
+
+ QQmlLSUtils::RenameUsages nestedComponentRename{
+ {
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 42, 15,
+ strlen("NestedComponent"), u"SuperInlineComponent"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 61, 5,
+ strlen("NestedComponent"), u"SuperInlineComponent"_s),
+ },
+ {}
+ };
+
+ QQmlLSUtils::RenameUsages myNestedIdRename{
+ {
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 62, 13, strlen("myNested"),
+ u"freshNewIdForMyNested"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 65, 17, strlen("myNested"),
+ u"freshNewIdForMyNested"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 66, 17, strlen("myNested"),
+ u"freshNewIdForMyNested"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 67, 17, strlen("myNested"),
+ u"freshNewIdForMyNested"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 68, 17, strlen("myNested"),
+ u"freshNewIdForMyNested"_s),
+ QQmlLSUtils::Edit::from(testFileName, testFileContent, 69, 17, strlen("myNested"),
+ u"freshNewIdForMyNested"_s),
+ },
+ {}
+ };
+
+ const QString renameFileQml = testFile("renaming/main.qml");
+ const QString renameFileQmlContent = readFileContent(renameFileQml);
+ const QQmlLSUtils::RenameUsages renameComponent1{
+ {
+ QQmlLSUtils::Edit::from(renameFileQml, renameFileQmlContent, 4, 5,
+ strlen("RenameMe"), u"FreshNewComponentName"_s),
+ },
+ {
+ { testFile("renaming/RenameMe.qml"),
+ testFile(u"renaming/FreshNewComponentName.qml"_s) },
+ }
+ };
+ const QQmlLSUtils::RenameUsages renameComponent2{
+ {
+ QQmlLSUtils::Edit::from(renameFileQml, renameFileQmlContent, 5, 5,
+ strlen("RenameMe2"), u"AnotherOneThankYou"_s),
+ },
+ {
+ { testFile("renaming/RenameMe2.ui.qml"),
+ testFile(u"renaming/AnotherOneThankYou.ui.qml"_s) },
+ }
+ };
+ const QQmlLSUtils::RenameUsages renameComponentNamedByQmldir{
+ {
+ QQmlLSUtils::Edit::from(renameFileQml, renameFileQmlContent, 6, 5,
+ strlen("HelloWorld"), u"AnotherOneThankYou"_s),
+ },
+ // make sure that the file itself does not get renamed
+ {}
+ };
+
+ const QString parserError = u"Invalid EcmaScript identifier!"_s;
+
+ QTest::addRow("renameMethod") << testFileName << 72 << 19 << u"newNameNewMe"_s << methodFRename
+ << noError;
+ QTest::addRow("renameJSIdentifier")
+ << testFileName << 10 << 19 << u"sumsumsum123"_s << JSIdentifierSumRename << noError;
+ QTest::addRow("renameQmlSignal")
+ << testFileName << 93 << 19 << u"finalSignal"_s << qmlSignalRename << noError;
+ QTest::addRow("renameQmlSignalHandler")
+ << testFileName << 119 << 10 << u"onFinalSignal"_s << qmlSignalRename << noError;
+
+ QTest::addRow("renameQmlProperty")
+ << testFileName << 17 << 20 << u"freshPropertyName"_s << helloPropertyRename << noError;
+ QTest::addRow("renameQmlPropertyChanged")
+ << testFileName << 151 << 18 << u"freshPropertyNameChanged"_s << helloPropertyRename
+ << noError;
+ QTest::addRow("renameQmlPropertyChangedHandler")
+ << testFileName << 153 << 22 << u"onFreshPropertyNameChanged"_s << helloPropertyRename
+ << noError;
+
+ QTest::addRow("renameQmlObjectId") << testFileName << 65 << 21 << u"freshNewIdForMyNested"_s
+ << myNestedIdRename << noError;
+
+ // rename forbidden stuff
+ QTest::addRow("renameCPPDefinedItem") << testFileName << 144 << 13 << u"onHelloWorld"_s
+ << noRenames << u"defined in non-QML files."_s;
+ QTest::addRow("renameFunctionKeyword") << testFileName << 8 << 10 << u"HelloWorld"_s
+ << noRenames << "Requested item cannot be renamed";
+ QTest::addRow("invalidCharactersInIdentifier")
+ << testFileName << 12 << 22 << u"\""_s << noRenames << parserError;
+ QTest::addRow("invalidCharactersInIdentifier2")
+ << testFileName << 12 << 22 << u"hello world"_s << noRenames << parserError;
+ QTest::addRow("invalidCharactersInIdentifier3")
+ << testFileName << 12 << 22 << u"sum.sum.sum"_s << noRenames << parserError;
+ QTest::addRow("emptyIdentifier")
+ << testFileName << 12 << 22 << QString() << noRenames << parserError;
+ QTest::addRow("usingKeywordAsIdentifier")
+ << testFileName << 12 << 22 << u"function"_s << noRenames << parserError;
+
+ QTest::addRow("changedSignalHandlerMissingOnChanged")
+ << testFileName << 134 << 9 << u"___"_s << noRenames
+ << u"Invalid name for a property changed handler identifier"_s;
+ QTest::addRow("changedSignalHandlerMissingChanged")
+ << testFileName << 134 << 9 << u"on___"_s << noRenames
+ << u"Invalid name for a property changed handler identifier"_s;
+ QTest::addRow("changedSignalHandlerMissingOn")
+ << testFileName << 134 << 9 << u"___Changed"_s << noRenames
+ << u"Invalid name for a property changed handler identifier"_s;
+ QTest::addRow("changedSignalHandlerTypoInChanged")
+ << testFileName << 134 << 9 << u"on___Chänged"_s << noRenames
+ << u"Invalid name for a property changed handler identifier"_s;
+
+ QTest::addRow("signalHandlerMissingOn")
+ << testFileName << 119 << 10 << u"helloSuperSignal"_s << noRenames
+ << u"Invalid name for a signal handler identifier"_s;
+ QTest::addRow("signalHandlerMissingCapitalization")
+ << testFileName << 119 << 10 << u"onhelloSuperSignal"_s << noRenames
+ << u"Invalid name for a signal handler identifier"_s;
+
+ QTest::addRow("JSIdentifierStartsWithNumber")
+ << testFileName << 67 << 13 << u"123"_s << noRenames << parserError;
+
+ QTest::addRow("renameQmlFile") << testFile(u"renaming/main.qml"_s) << 4 << 9
+ << u"FreshNewComponentName"_s << renameComponent1 << noError;
+
+ QTest::addRow("renameUiQmlFile") << testFile(u"renaming/main.qml"_s) << 5 << 9
+ << u"AnotherOneThankYou"_s << renameComponent2 << noError;
+
+ QTest::addRow("renameQmlFileRenamedByQmldir")
+ << testFile(u"renaming/main.qml"_s) << 6 << 8 << u"AnotherOneThankYou"_s
+ << renameComponentNamedByQmldir << noError;
+}
+
+void tst_qmlls_utils::renameUsages()
+{
+ // findAndRenameUsages() already tests if all usages will be renamed
+ // now test that the new name is correctly passed
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QString, newName);
+ QFETCH(QQmlLSUtils::RenameUsages, expectedRenames);
+ QFETCH(QString, expectedError);
+
+ {
+ const auto renameInFile = expectedRenames.renameInFile();
+ QVERIFY(std::is_sorted(renameInFile.constBegin(), renameInFile.constEnd()));
+ const auto renameInFilename = expectedRenames.renameInFilename();
+ QVERIFY(std::is_sorted(renameInFilename.begin(), renameInFilename.end()));
+ }
+
+ auto [env, file] = createEnvironmentAndLoadFile(filePath);
+
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+
+ if constexpr (enable_debug_output) {
+ if (locations.size() > 1) {
+ for (auto &x : locations)
+ qDebug() << x.domItem.toString();
+ }
+ }
+ QCOMPARE(locations.size(), 1);
+
+ if (auto errors = QQmlLSUtils::checkNameForRename(locations.front().domItem, newName)) {
+ if constexpr (enable_debug_output) {
+ if (expectedError.isEmpty())
+ qDebug() << "Expected no error but got" << errors->message;
+ if (!errors->message.contains(expectedError))
+ qDebug() << "Cannot find" << expectedError << "in" << errors->message;
+ }
+ QVERIFY(!expectedError.isEmpty());
+ QVERIFY(errors->message.contains(expectedError));
+ return;
+ }
+ auto edits = QQmlLSUtils::renameUsagesOf(locations.front().domItem, newName);
+
+ if constexpr (enable_debug_output) {
+ if (edits != expectedRenames) {
+ qDebug() << "Got:\n";
+ for (auto &x : edits.renameInFile()) {
+ qDebug() << x.replacement << x.location.filename << "("
+ << x.location.sourceLocation.startLine << ", "
+ << x.location.sourceLocation.startColumn << "), "
+ << x.location.sourceLocation.offset << "+"
+ << x.location.sourceLocation.length;
+ }
+ qDebug() << "with renames in filenames:";
+ for (auto &x : edits.renameInFilename()) {
+ qDebug() << x.oldFilename << "->" << x.newFilename;
+ }
+ qDebug() << "But expected: \n";
+ for (auto &x : expectedRenames.renameInFile()) {
+ qDebug() << x.replacement << x.location.filename << "("
+ << x.location.sourceLocation.startLine << ", "
+ << x.location.sourceLocation.startColumn << "), "
+ << x.location.sourceLocation.offset << "+"
+ << x.location.sourceLocation.length;
+ }
+ qDebug() << "with renames in filenames:";
+ for (auto &x : expectedRenames.renameInFilename()) {
+ qDebug() << x.oldFilename << "->" << x.newFilename;
+ }
+ }
+ }
+ QCOMPARE(edits, expectedRenames);
+}
+
+void tst_qmlls_utils::findDefinitionFromLocation_data()
+{
+ QTest::addColumn<QString>("filePath");
+ // keep in mind that line and character are starting at 1!
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+
+ QTest::addColumn<QString>("expectedFilePath");
+ // set to -1 when unchanged from above line and character. 0-based.
+ QTest::addColumn<int>("expectedLine");
+ QTest::addColumn<int>("expectedCharacter");
+ QTest::addColumn<size_t>("expectedLength");
+
+ const QString JSDefinitionsQml = testFile(u"JSDefinitions.qml"_s);
+ const QString BaseTypeQml = testFile(u"BaseType.qml"_s);
+
+ QTest::addRow("JSIdentifierX")
+ << JSDefinitionsQml << 14 << 11 << JSDefinitionsQml << 13 << 13 << strlen("x");
+ QTest::addRow("JSIdentifierX2")
+ << JSDefinitionsQml << 15 << 11 << JSDefinitionsQml << 13 << 13 << strlen("x");
+ QTest::addRow("propertyI") << JSDefinitionsQml << 14 << 14 << JSDefinitionsQml << 9 << 18
+ << strlen("i");
+ QTest::addRow("qualifiedPropertyI")
+ << JSDefinitionsQml << 15 << 21 << JSDefinitionsQml << 9 << 18 << strlen("i");
+ QTest::addRow("inlineComponentProperty")
+ << JSDefinitionsQml << 62 << 21 << JSDefinitionsQml << 54 << 22 << strlen("data");
+
+ QTest::addRow("parameterA") << JSDefinitionsQml << 10 << 16 << JSDefinitionsQml << 10 << 16
+ << strlen("a");
+ QTest::addRow("parameterAUsage")
+ << JSDefinitionsQml << 10 << 39 << JSDefinitionsQml << -1 << 16 << strlen("a");
+
+ QTest::addRow("parameterB") << JSDefinitionsQml << 10 << 28 << JSDefinitionsQml << 10 << 28
+ << strlen("b");
+ QTest::addRow("parameterBUsage")
+ << JSDefinitionsQml << 10 << 86 << JSDefinitionsQml << -1 << 28 << strlen("b");
+
+ QTest::addRow("comment") << JSDefinitionsQml << 10 << 21 << noResultExpected << -1 << -1
+ << size_t{};
+
+ QTest::addRow("scopedX") << JSDefinitionsQml << 22 << 18 << JSDefinitionsQml << 21 << 17
+ << strlen("scoped");
+ QTest::addRow("scopedX2") << JSDefinitionsQml << 25 << 22 << JSDefinitionsQml << 21 << 17
+ << strlen("scoped");
+ QTest::addRow("scopedX3") << JSDefinitionsQml << 28 << 14 << JSDefinitionsQml << 19 << 13
+ << strlen("scoped");
+
+ QTest::addRow("normalI") << JSDefinitionsQml << 22 << 23 << JSDefinitionsQml << 9 << 18
+ << strlen("i");
+ QTest::addRow("scopedI") << JSDefinitionsQml << 25 << 27 << JSDefinitionsQml << 24 << 32
+ << strlen("i");
+
+ QTest::addRow("shadowingProperty")
+ << JSDefinitionsQml << 37 << 21 << JSDefinitionsQml << 34 << 22 << strlen("i");
+ QTest::addRow("shadowingQualifiedProperty")
+ << JSDefinitionsQml << 37 << 35 << JSDefinitionsQml << 34 << 22 << strlen("i");
+ QTest::addRow("shadowedProperty")
+ << JSDefinitionsQml << 37 << 49 << JSDefinitionsQml << 9 << 18 << strlen("i");
+
+ QTest::addRow("propertyInBinding")
+ << JSDefinitionsQml << 64 << 37 << JSDefinitionsQml << 9 << 18 << strlen("i");
+ QTest::addRow("propertyInBinding2")
+ << JSDefinitionsQml << 65 << 38 << JSDefinitionsQml << 9 << 18 << strlen("i");
+ QTest::addRow("propertyInBinding3")
+ << JSDefinitionsQml << 66 << 51 << JSDefinitionsQml << 9 << 18 << strlen("i");
+
+ QTest::addRow("propertyFromDifferentFile")
+ << JSDefinitionsQml << 72 << 20 << BaseTypeQml << 24 << 18 << strlen("helloProperty");
+
+ QTest::addRow("id") << JSDefinitionsQml << 15 << 17 << JSDefinitionsQml << 7 << 9
+ << strlen("rootId");
+ QTest::addRow("onId") << JSDefinitionsQml << 32 << 16 << JSDefinitionsQml << 32 << 13
+ << strlen("nested");
+ QTest::addRow("parentId") << JSDefinitionsQml << 37 << 44 << JSDefinitionsQml << 7 << 9
+ << strlen("rootId");
+ QTest::addRow("currentId") << JSDefinitionsQml << 37 << 30 << JSDefinitionsQml << 32 << 13
+ << strlen("nested");
+ QTest::addRow("inlineComponentId")
+ << JSDefinitionsQml << 56 << 35 << JSDefinitionsQml << 52 << 13 << strlen("helloIC");
+
+ QTest::addRow("recursiveFunction")
+ << JSDefinitionsQml << 39 << 28 << JSDefinitionsQml << 36 << 18 << strlen("f");
+ QTest::addRow("recursiveFunction2")
+ << JSDefinitionsQml << 39 << 39 << JSDefinitionsQml << 36 << 18 << strlen("f");
+ QTest::addRow("functionFromFunction")
+ << JSDefinitionsQml << 44 << 20 << JSDefinitionsQml << 36 << 18 << strlen("f");
+ QTest::addRow("qualifiedFunctionName")
+ << JSDefinitionsQml << 48 << 23 << JSDefinitionsQml << 36 << 18 << strlen("f");
+
+ QTest::addRow("functionInParent")
+ << JSDefinitionsQml << 44 << 37 << JSDefinitionsQml << 18 << 14 << strlen("ffff");
+ QTest::addRow("functionFromDifferentFile")
+ << JSDefinitionsQml << 72 << 47 << BaseTypeQml << 25 << 14 << strlen("helloFunction");
+ QTest::addRow("componentFromFile")
+ << JSDefinitionsQml << 68 << 28 << BaseTypeQml << 6 << 1 << strlen("Item");
+ QTest::addRow("inlineComponentFromDifferentFile")
+ << JSDefinitionsQml << 75 << 27 << BaseTypeQml << 9 << 38 << strlen("Item");
+}
+
+void tst_qmlls_utils::findDefinitionFromLocation()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QString, expectedFilePath);
+ QFETCH(int, expectedLine);
+ QFETCH(int, expectedCharacter);
+ QFETCH(size_t, expectedLength);
+
+ if (expectedLine == -1)
+ expectedLine = line;
+ if (expectedCharacter == -1)
+ expectedCharacter = character;
+
+ // they all start at 1.
+ Q_ASSERT(line > 0);
+ Q_ASSERT(character > 0);
+ Q_ASSERT(expectedLine > 0);
+ Q_ASSERT(expectedCharacter > 0);
+
+ auto [env, file] = createEnvironmentAndLoadFile(filePath);
+
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+
+ QCOMPARE(locations.size(), 1);
+
+ auto definition = QQmlLSUtils::findDefinitionOf(locations.front().domItem);
+
+ // if expectedFilePath is empty, we probably just want to make sure that it does
+ // not crash
+ if (expectedFilePath == noResultExpected) {
+ QVERIFY(!definition);
+ return;
+ }
+
+ QVERIFY(definition);
+
+ QCOMPARE(definition->filename, expectedFilePath);
+
+ QCOMPARE(definition->sourceLocation.startLine, quint32(expectedLine));
+ QCOMPARE(definition->sourceLocation.startColumn, quint32(expectedCharacter));
+ QCOMPARE(definition->sourceLocation.length, quint32(expectedLength));
+}
+
+void tst_qmlls_utils::resolveExpressionType_data()
+{
+ QTest::addColumn<QString>("filePath");
+ // keep in mind that line and character are starting at 1!
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<QQmlLSUtils::ResolveOptions>("resolveOption");
+ QTest::addColumn<QString>("expectedFile");
+ // startline of the owners definition
+ QTest::addColumn<int>("expectedLine");
+ QTest::addColumn<QQmlLSUtils::IdentifierType>("expectedType");
+
+ using namespace QQmlLSUtils;
+
+ const int noLine = -1;
+ const QString noFile;
+
+ {
+ const QString JSDefinitionsQml = testFile(u"JSDefinitions.qml"_s);
+ const int parentLine = 6;
+ const int childLine = 31;
+
+ QTest::addRow("id") << JSDefinitionsQml << 15 << 17 << ResolveOwnerType << JSDefinitionsQml
+ << parentLine << QmlObjectIdIdentifier;
+ QTest::addRow("childIddInChild") << JSDefinitionsQml << 37 << 30 << ResolveOwnerType
+ << JSDefinitionsQml << childLine << QmlObjectIdIdentifier;
+ QTest::addRow("parentIdInChild") << JSDefinitionsQml << 37 << 43 << ResolveOwnerType
+ << JSDefinitionsQml << parentLine << QmlObjectIdIdentifier;
+
+ QTest::addRow("propertyI") << JSDefinitionsQml << 14 << 14 << ResolveOwnerType
+ << JSDefinitionsQml << parentLine << PropertyIdentifier;
+ QTest::addRow("qualifiedPropertyI") << JSDefinitionsQml << 15 << 21 << ResolveOwnerType
+ << JSDefinitionsQml << parentLine << PropertyIdentifier;
+ QTest::addRow("propertyIInChild") << JSDefinitionsQml << 37 << 21 << ResolveOwnerType
+ << JSDefinitionsQml << childLine << PropertyIdentifier;
+ QTest::addRow("qualifiedChildPropertyIInChild")
+ << JSDefinitionsQml << 37 << 35 << ResolveOwnerType << JSDefinitionsQml
+ << childLine << PropertyIdentifier;
+ QTest::addRow("qualifiedParentPropertyIInChild")
+ << JSDefinitionsQml << 37 << 49 << ResolveOwnerType << JSDefinitionsQml
+ << parentLine << PropertyIdentifier;
+
+ QTest::addRow("childMethod") << JSDefinitionsQml << 48 << 23 << ResolveOwnerType
+ << JSDefinitionsQml << childLine << MethodIdentifier;
+ QTest::addRow("childMethod2") << JSDefinitionsQml << 44 << 20 << ResolveOwnerType
+ << JSDefinitionsQml << childLine << MethodIdentifier;
+ QTest::addRow("parentMethod") << JSDefinitionsQml << 14 << 9 << ResolveOwnerType
+ << JSDefinitionsQml << parentLine << MethodIdentifier;
+ }
+
+ {
+ const QString bindingsOnDeferredQml =
+ testFile(u"resolveExpressionType/BindingsOnDeferred.qml"_s);
+ const QString qQuickControl = u"private/qquickcontrol_p.h"_s;
+ const QString qQuickKeysAttachedType = u"private/qquickitem_p.h"_s;
+ QTest::addRow("bindingOnId") << bindingsOnDeferredQml << 12 << 14 << ResolveOwnerType
+ << bindingsOnDeferredQml << 8 << QmlObjectIdIdentifier;
+ QTest::addRow("bindingOnQualifiedDeferredProperty")
+ << bindingsOnDeferredQml << 12 << 24 << ResolveOwnerType << qQuickControl << noLine
+ << PropertyIdentifier;
+ QTest::addRow("groupedPropertyBindingOnId")
+ << bindingsOnDeferredQml << 14 << 14 << ResolveOwnerType << bindingsOnDeferredQml
+ << 8 << QmlObjectIdIdentifier;
+ QTest::addRow("someDeferredProperty")
+ << bindingsOnDeferredQml << 15 << 22 << ResolveOwnerType << qQuickControl << noLine
+ << PropertyIdentifier;
+ }
+
+ {
+ const QString JSUsagesQml = testFile(u"JSUsages.qml"_s);
+ const int rootLine = 6;
+ const int nestedComponent2Line = 46;
+ const int nestedComponent3Line = 51;
+ const int nestedComponent4Line = 57;
+ QTest::addRow("propertyAccess:inner.inner") << JSUsagesQml << 68 << 34 << ResolveOwnerType
+ << JSUsagesQml << nestedComponent2Line << PropertyIdentifier;
+ QTest::addRow("propertyAccess:inner.inner2") << JSUsagesQml << 69 << 34 << ResolveOwnerType
+ << JSUsagesQml << nestedComponent2Line << PropertyIdentifier;
+ QTest::addRow("propertyAccess:inner.inner.inner")
+ << JSUsagesQml << 69 << 40 << ResolveOwnerType << JSUsagesQml
+ << nestedComponent3Line << PropertyIdentifier;
+ QTest::addRow("propertyAccess:inner.inner.inner.p2")
+ << JSUsagesQml << 69 << 44 << ResolveOwnerType << JSUsagesQml
+ << nestedComponent4Line << PropertyIdentifier;
+
+ QTest::addRow("propertyAccess:helloProperty")
+ << JSUsagesQml << 65 << 68 << ResolveOwnerType << JSUsagesQml << rootLine << PropertyIdentifier;
+ QTest::addRow("propertyAccess:nestedHelloProperty")
+ << JSUsagesQml << 65 << 46 << ResolveOwnerType << JSUsagesQml
+ << nestedComponent4Line << PropertyIdentifier;
+ }
+
+ {
+ const QString derivedType = testFile(u"resolveExpressionType/DerivedType.qml"_s);
+ const QString derived2Type = testFile(u"resolveExpressionType/Derived2.qml"_s);
+ const QString baseType = testFile(u"resolveExpressionType/BaseType.qml"_s);
+ const QString qQuickValueTypes = u"private/qquickvaluetypes_p.h"_s;
+ const QString qQuickKeysAttachedType = u"private/qquickitem_p.h"_s;
+
+ const int baseTypeLine = 6;
+ const int derivedTypeLine = 6;
+ const int keysLine = 29;
+
+ QTest::addRow("ownerOfMethod")
+ << derivedType << 9 << 13 << ResolveOwnerType << baseType << baseTypeLine << MethodIdentifier;
+ QTest::addRow("ownerOfMethod2")
+ << derivedType << 15 << 33 << ResolveOwnerType << baseType << baseTypeLine << MethodIdentifier;
+ QTest::addRow("ownerOfQualifiedMethod")
+ << derivedType << 22 << 46 << ResolveOwnerType << baseType << baseTypeLine << MethodIdentifier;
+
+ QTest::addRow("ownerOfProperty")
+ << derivedType << 10 << 22 << ResolveOwnerType << baseType << baseTypeLine << PropertyIdentifier;
+ QTest::addRow("ownerOfProperty2")
+ << derivedType << 16 << 37 << ResolveOwnerType << baseType << baseTypeLine << PropertyIdentifier;
+ QTest::addRow("ownerOfQualifiedProperty")
+ << derivedType << 23 << 46 << ResolveOwnerType << baseType << baseTypeLine << PropertyIdentifier;
+
+ QTest::addRow("ownerOfOwnProperty")
+ << derivedType << 16 << 23 << ResolveOwnerType << derivedType << derivedTypeLine << PropertyIdentifier;
+
+ QTest::addRow("ownerOfSignal")
+ << derivedType << 11 << 13 << ResolveOwnerType << baseType << baseTypeLine << SignalIdentifier;
+ QTest::addRow("ownerOfSignal2")
+ << derivedType << 18 << 37 << ResolveOwnerType << baseType << baseTypeLine << SignalIdentifier;
+ QTest::addRow("ownerOfSignalHandler")
+ << derivedType << 19 << 10 << ResolveOwnerType << baseType << baseTypeLine << SignalHandlerIdentifier;
+ QTest::addRow("ownerOfQualifiedSignal")
+ << derivedType << 25 << 22 << ResolveOwnerType << baseType << baseTypeLine << SignalIdentifier;
+
+ QTest::addRow("ownerOfGroupedProperty")
+ << derivedType << 28 << 7 << ResolveOwnerType << baseType << baseTypeLine << GroupedPropertyIdentifier;
+ QTest::addRow("ownerOfGroupedProperty2")
+ << derivedType << 28 << 17 << ResolveOwnerType << qQuickValueTypes << noLine
+ << PropertyIdentifier;
+
+ QTest::addRow("ownerOfAttachedProperty")
+ << derivedType << 29 << 6 << ResolveOwnerType << derivedType << keysLine << AttachedTypeIdentifier;
+ QTest::addRow("ownerOfAttachedProperty2")
+ << derivedType << 29 << 14 << ResolveOwnerType << qQuickKeysAttachedType << noLine
+ << SignalHandlerIdentifier;
+
+ QTest::addRow("actualTypeOfAttachedProperty")
+ << derivedType << 29 << 14 << ResolveActualTypeForFieldMemberExpression << noFile
+ << noLine << SignalHandlerIdentifier;
+
+ QTest::addRow("id")
+ << derivedType << 7 << 10 << ResolveOwnerType << derivedType << 6 << QmlObjectIdIdentifier;
+ QTest::addRow("propertyBinding")
+ << derivedType << 31 << 13 << ResolveOwnerType << baseType << baseTypeLine << PropertyIdentifier;
+
+ QTest::addRow("qmlObject")
+ << derivedType << 6 << 4 << ResolveOwnerType << derived2Type << 4 << QmlComponentIdentifier;
+ }
+}
+
+void tst_qmlls_utils::resolveExpressionType()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QQmlLSUtils::ResolveOptions, resolveOption);
+ QFETCH(QString, expectedFile);
+ QFETCH(int, expectedLine);
+ QFETCH(QQmlLSUtils::IdentifierType, expectedType);
+
+ // they all start at 1.
+ Q_ASSERT(line > 0);
+ Q_ASSERT(character > 0);
+
+
+ auto [env, file] = createEnvironmentAndLoadFile(filePath);
+
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+
+ QCOMPARE(locations.size(), 1);
+
+ auto definition = QQmlLSUtils::resolveExpressionType(locations.front().domItem, resolveOption);
+
+ QVERIFY(definition);
+ if (!expectedFile.isEmpty()) {
+ QVERIFY(definition->semanticScope);
+ QCOMPARE(definition->semanticScope->filePath(), expectedFile);
+
+ if (expectedLine != -1) {
+ QQmlJS::SourceLocation location = definition->semanticScope->sourceLocation();
+ QCOMPARE((int)location.startLine, expectedLine);
+ }
+ } else {
+ QVERIFY(!definition->semanticScope);
+ }
+ QCOMPARE(definition->type, expectedType);
+}
+
+void tst_qmlls_utils::isValidEcmaScriptIdentifier_data()
+{
+ QTest::addColumn<QString>("identifier");
+ QTest::addColumn<bool>("isValid");
+
+ QTest::addRow("f") << u"f"_s << true;
+ QTest::addRow("f-unicode") << u"\\u0046"_s << true;
+ QTest::addRow("starts-with-digit") << u"8helloWorld"_s << false;
+ QTest::addRow("starts-with-unicode-digit") << u"\\u0038helloWorld"_s << false; // \u0038 == '8'
+ QTest::addRow("keyword") << u"return"_s << false;
+ QTest::addRow("not-keyword") << u"returny"_s << true;
+}
+
+void tst_qmlls_utils::isValidEcmaScriptIdentifier()
+{
+ QFETCH(QString, identifier);
+ QFETCH(bool, isValid);
+
+ QCOMPARE(QQmlLSUtils::isValidEcmaScriptIdentifier(identifier), isValid);
+}
+
+using namespace QLspSpecification;
+
+enum InsertOption { None, InsertColon };
+
+void tst_qmlls_utils::completions_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<ExpectedCompletions>("expected");
+ QTest::addColumn<QStringList>("notExpected");
+
+ const QString file = testFile(u"Yyy.qml"_s);
+ const QString emptyFile = testFile(u"emptyFile.qml"_s);
+ const QString pragmaFile = testFile(u"pragmas.qml"_s);
+
+ const QString singletonName = u"SystemInformation"_s;
+ const QString attachedTypeName = u"Component"_s;
+ const QString attachedTypeName2 = u"Keys"_s;
+ const auto attachedTypes = ExpectedCompletions({
+ { attachedTypeName, CompletionItemKind::Class },
+ { attachedTypeName2, CompletionItemKind::Class },
+ });
+
+ const auto keywords = ExpectedCompletions({
+ { u"function"_s, CompletionItemKind::Keyword },
+ { u"required"_s, CompletionItemKind::Keyword },
+ { u"enum"_s, CompletionItemKind::Keyword },
+ { u"component"_s, CompletionItemKind::Keyword },
+ });
+
+ const auto mixedTypes = ExpectedCompletions({
+ { u"Zzz"_s, CompletionItemKind::Class },
+ { u"Item"_s, CompletionItemKind::Class },
+ { u"int"_s, CompletionItemKind::Class },
+ { u"date"_s, CompletionItemKind::Class },
+ });
+ const auto constructorTypes = ExpectedCompletions({
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ { u"MyRectangle"_s, CompletionItemKind::Constructor },
+ { u"Zzz"_s, CompletionItemKind::Constructor },
+ { u"Item"_s, CompletionItemKind::Constructor },
+ { u"QtObject"_s, CompletionItemKind::Constructor },
+ });
+ const auto rectangleTypes = ExpectedCompletions({
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ { u"MyRectangle"_s, CompletionItemKind::Constructor },
+ });
+
+ QTest::newRow("objEmptyLine") << file << 9 << 1
+ << ExpectedCompletions({
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ { u"width"_s, CompletionItemKind::Property },
+ })
+ << QStringList({ u"QtQuick"_s, u"vector4d"_s });
+
+ const QString propertyCompletion = u"property type name: value;"_s;
+ const QString functionCompletion = u"function name(args...): returnType { statements...}"_s;
+
+ const ExpectedCompletions quickSnippetsWithQualifier{
+ { u"QQ.BorderImage snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.BorderImage {\n"
+ u"\tid: ${1:name}\n"
+ u"\tsource: \"${2:file}\"\n"
+ u"\twidth: ${3:100}; height: ${4:100}\n"
+ u"\tborder.left: ${5: 5}; border.top: ${5}\n"
+ u"\tborder.right: ${5}; border.bottom: ${5}\n"
+ u"}"_s },
+ { u"QQ.ColorAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.ColorAnimation {\n"
+ u"\tfrom: \"${1:white}\"\n"
+ u"\tto: \"${2:black}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"}"_s },
+ { u"QQ.Image snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.Image {\n"
+ u"\tid: ${1:name}\n"
+ u"\tsource: \"${2:file}\"\n"
+ u"}"_s },
+ { u"QQ.Item snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.Item {\n"
+ u"\tid: ${1:name}\n"
+ u"}"_s },
+ { u"QQ.NumberAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.NumberAnimation {\n"
+ u"\ttarget: ${1:object}\n"
+ u"\tproperty: \"${2:name}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"\teasing.type: QQ.Easing.${4:InOutQuad}\n"
+ u"}"_s },
+ { u"QQ.NumberAnimation with targets snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.NumberAnimation {\n"
+ u"\ttargets: [${1:object}]\n"
+ u"\tproperties: \"${2:name}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"}"_s },
+ { u"QQ.PauseAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.PauseAnimation {\n"
+ u"\tduration: ${1:200}\n"
+ u"}"_s },
+ { u"QQ.PropertyAction snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.PropertyAction {\n"
+ u"\ttarget: ${1:object}\n"
+ u"\tproperty: \"${2:name}\"\n"
+ "}"_s },
+ { u"QQ.PropertyAction with targets snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.PropertyAction {\n"
+ u"\ttargets: [${1:object}]\n"
+ u"\tproperties: \"${2:name}\"\n"
+ u"}"_s },
+ { u"QQ.PropertyChanges snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.PropertyChanges {\n"
+ u"\ttarget: ${1:object}\n"
+ u"}"_s },
+ { u"QQ.State snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.State {\n"
+ u"\tname: ${1:name}\n"
+ u"\tQQ.PropertyChanges {\n"
+ u"\t\ttarget: ${2:object}\n"
+ u"\t}\n"
+ u"}"_s },
+ { u"QQ.Text snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.Text {\n"
+ u"\tid: ${1:name}\n"
+ u"\ttext: qsTr(\"${2:text}\")\n"
+ u"}"_s },
+ { u"QQ.Transition snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.Transition {\n"
+ u"\tfrom: \"${1:fromState}\"\n"
+ u"\tto: \"${2:toState}\"\n"
+ u"}"_s },
+ { u"states binding with PropertyChanges in State"_s, CompletionItemKind::Snippet,
+ u"states: [\n"
+ u"\tQQ.State {\n"
+ u"\t\tname: \"${1:name}\"\n"
+ u"\t\tQQ.PropertyChanges {\n"
+ u"\t\t\ttarget: ${2:object}\n"
+ u"\t\t}\n"
+ u"\t}\n"
+ u"]"_s },
+ { u"transitions binding with Transition"_s, CompletionItemKind::Snippet,
+ u"transitions: [\n"
+ u"\tQQ.Transition {\n"
+ u"\t\tfrom: \"${1:fromState}\"\n"
+ u"\t\tto: \"${2:fromState}\"\n"
+ u"\t}\n"
+ u"]"_s }
+ };
+ const ExpectedCompletions quickSnippetsWithoutQualifier{
+ { { u"BorderImage snippet"_s, CompletionItemKind::Snippet,
+ u"BorderImage {\n"
+ u"\tid: ${1:name}\n"
+ u"\tsource: \"${2:file}\"\n"
+ u"\twidth: ${3:100}; height: ${4:100}\n"
+ u"\tborder.left: ${5: 5}; border.top: ${5}\n"
+ u"\tborder.right: ${5}; border.bottom: ${5}\n"
+ u"}"_s },
+ { u"ColorAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"ColorAnimation {\n"
+ u"\tfrom: \"${1:white}\"\n"
+ u"\tto: \"${2:black}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"}"_s },
+ { u"Image snippet"_s, CompletionItemKind::Snippet,
+ u"Image {\n"
+ u"\tid: ${1:name}\n"
+ u"\tsource: \"${2:file}\"\n"
+ u"}"_s },
+ { u"Item snippet"_s, CompletionItemKind::Snippet,
+ u"Item {\n"
+ u"\tid: ${1:name}\n"
+ u"}"_s },
+ { u"NumberAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"NumberAnimation {\n"
+ u"\ttarget: ${1:object}\n"
+ u"\tproperty: \"${2:name}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"\teasing.type: Easing.${4:InOutQuad}\n"
+ u"}"_s },
+ { u"NumberAnimation with targets snippet"_s, CompletionItemKind::Snippet,
+ u"NumberAnimation {\n"
+ u"\ttargets: [${1:object}]\n"
+ u"\tproperties: \"${2:name}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"}"_s },
+ { u"PauseAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"PauseAnimation {\n"
+ u"\tduration: ${1:200}\n"
+ u"}"_s },
+ { u"PropertyAction snippet"_s, CompletionItemKind::Snippet,
+ u"PropertyAction {\n"
+ u"\ttarget: ${1:object}\n"
+ u"\tproperty: \"${2:name}\"\n"
+ "}"_s },
+ { u"PropertyAction with targets snippet"_s, CompletionItemKind::Snippet,
+ u"PropertyAction {\n"
+ u"\ttargets: [${1:object}]\n"
+ u"\tproperties: \"${2:name}\"\n"
+ u"}"_s },
+ { u"PropertyChanges snippet"_s, CompletionItemKind::Snippet,
+ u"PropertyChanges {\n"
+ u"\ttarget: ${1:object}\n"
+ u"}"_s },
+ { u"State snippet"_s, CompletionItemKind::Snippet,
+ u"State {\n"
+ u"\tname: ${1:name}\n"
+ u"\tPropertyChanges {\n"
+ u"\t\ttarget: ${2:object}\n"
+ u"\t}\n"
+ u"}"_s },
+ { u"Text snippet"_s, CompletionItemKind::Snippet,
+ u"Text {\n"
+ u"\tid: ${1:name}\n"
+ u"\ttext: qsTr(\"${2:text}\")\n"
+ u"}"_s },
+ { u"Transition snippet"_s, CompletionItemKind::Snippet,
+ u"Transition {\n"
+ u"\tfrom: \"${1:fromState}\"\n"
+ u"\tto: \"${2:toState}\"\n"
+ u"}"_s } }
+ };
+ const ExpectedCompletions quickSnippetsWithoutQualifierWithBindings = ExpectedCompletions{
+ { { u"states binding with PropertyChanges in State"_s, CompletionItemKind::Snippet,
+ u"states: [\n"
+ u"\tState {\n"
+ u"\t\tname: \"${1:name}\"\n"
+ u"\t\tPropertyChanges {\n"
+ u"\t\t\ttarget: ${2:object}\n"
+ u"\t\t}\n"
+ u"\t}\n"
+ u"]"_s },
+ { u"transitions binding with Transition"_s, CompletionItemKind::Snippet,
+ u"transitions: [\n"
+ u"\tTransition {\n"
+ u"\t\tfrom: \"${1:fromState}\"\n"
+ u"\t\tto: \"${2:fromState}\"\n"
+ u"\t}\n"
+ u"]"_s } }
+ } += quickSnippetsWithoutQualifier;
+ QTest::newRow("objEmptyLineSnippets")
+ << file << 9 << 1
+ << (ExpectedCompletions({
+ { propertyCompletion, CompletionItemKind::Snippet,
+ u"property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"readonly property type name: value;"_s, CompletionItemKind::Snippet,
+ u"readonly property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"default property type name: value;"_s, CompletionItemKind::Snippet,
+ u"default property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"default required property type name: value;"_s,
+ CompletionItemKind::Snippet,
+ u"default required property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"required default property type name: value;"_s,
+ CompletionItemKind::Snippet,
+ u"required default property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"required property type name: value;"_s, CompletionItemKind::Snippet,
+ u"required property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"property type name;"_s, CompletionItemKind::Snippet,
+ u"property ${1:type} ${0:name};"_s },
+ { u"required property type name;"_s, CompletionItemKind::Snippet,
+ u"required property ${1:type} ${0:name};"_s },
+ { u"default property type name;"_s, CompletionItemKind::Snippet,
+ u"default property ${1:type} ${0:name};"_s },
+ { u"default required property type name;"_s, CompletionItemKind::Snippet,
+ u"default required property ${1:type} ${0:name};"_s },
+ { u"required default property type name;"_s, CompletionItemKind::Snippet,
+ u"required default property ${1:type} ${0:name};"_s },
+ { u"signal name(arg1:type1, ...)"_s, CompletionItemKind::Snippet,
+ u"signal ${1:name}($0)"_s },
+ { u"signal name;"_s, CompletionItemKind::Snippet, u"signal ${0:name};"_s },
+ { u"required name;"_s, CompletionItemKind::Snippet,
+ u"required ${0:name};"_s },
+ { functionCompletion, CompletionItemKind::Snippet,
+ u"function ${1:name}($2): ${3:returnType} {\n\t$0\n}"_s },
+ { u"enum name { Values...}"_s, CompletionItemKind::Snippet,
+ u"enum ${1:name} {\n\t${0:values}\n}"_s },
+ { u"component Name: BaseType { ... }"_s, CompletionItemKind::Snippet,
+ u"component ${1:name}: ${2:baseType} {\n\t$0\n}"_s },
+ }) += quickSnippetsWithoutQualifierWithBindings)
+ // not allowed because required properties need an initializer
+ << QStringList({ u"readonly property type name;"_s });
+
+ QTest::newRow("quickSnippetsForQualifiedQuickImport")
+ << testFile("qualifiedModule.qml") << 5 << 1
+ << quickSnippetsWithQualifier
+ // not allowed because required properties need an initializer
+ << QStringList({ u"readonly property type name;"_s });
+
+ QTest::newRow("quickSnippetsForQualifiedQuickImportBeforeDot")
+ << testFile("qualifiedModule.qml") << 5 << 7
+ << quickSnippetsWithQualifier
+ // not allowed because required properties need an initializer
+ << QStringList({ u"readonly property type name;"_s });
+
+ QTest::newRow("quickSnippetsForQualifiedQuickImportAfterDot")
+ << testFile("qualifiedModule.qml") << 5 << 8
+ << quickSnippetsWithoutQualifier
+ // not allowed because required properties need an initializer
+ << QStringList({ u"readonly property type name;"_s,
+ u"states binding with PropertyChanges in State"_s,
+ u"transitions binding with Transition"_s });
+
+ QTest::newRow("quickSnippetsForQualifiedQuickImportBeforeDotInBinding")
+ << testFile("qualifiedModule.qml") << 4 << 33 << quickSnippetsWithQualifier
+ << QStringList();
+
+ QTest::newRow("quickSnippetsForQualifiedQuickImportAfterDotInBinding")
+ << testFile("qualifiedModule.qml") << 4 << 34 << quickSnippetsWithoutQualifier
+ << QStringList({ u"states binding with PropertyChanges in State"_s,
+ u"transitions binding with Transition"_s });
+
+ // forbid transitions and states because QtObject does not inherit from Item
+ QTest::newRow("qtObjectEmptyLineSnippets")
+ << file << 141 << 8
+ << ExpectedCompletions{ { u"Item"_s, CompletionItemKind::Constructor } }
+ << QStringList({ u"transitions binding with Transition"_s,
+ u"states binding with PropertyChanges in State"_s });
+
+ QTest::newRow("handlers") << file << 5 << 1
+ << ExpectedCompletions{ {
+ { u"onHandleMe"_s, CompletionItemKind::Method },
+ { u"onDefaultPropertyChanged"_s,
+ CompletionItemKind::Method },
+ } }
+ << QStringList({ u"QtQuick"_s, u"vector4d"_s });
+
+ QTest::newRow("attachedTypes")
+ << file << 9 << 1 << attachedTypes << QStringList{ u"QtQuick"_s, u"vector4d"_s };
+
+ QTest::newRow("attachedTypesInScript")
+ << file << 6 << 12 << attachedTypes << QStringList{ u"QtQuick"_s, u"vector4d"_s };
+ QTest::newRow("attachedTypesInLongScript")
+ << file << 10 << 16 << attachedTypes << QStringList{ u"QtQuick"_s, u"vector4d"_s };
+
+ QTest::newRow("completionFromRootId") << file << 10 << 21
+ << ExpectedCompletions({
+ { u"width"_s, CompletionItemKind::Property },
+ { u"lala"_s, CompletionItemKind::Method },
+ { u"foo"_s, CompletionItemKind::Property },
+ })
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s };
+
+ QTest::newRow("attachedProperties") << file << 89 << 15
+ << ExpectedCompletions({
+ { u"onCompleted"_s, CompletionItemKind::Method },
+ })
+ << QStringList{ u"QtQuick"_s,
+ u"vector4d"_s,
+ attachedTypeName,
+ u"Rectangle"_s,
+ u"property"_s,
+ u"foo"_s,
+ u"onActiveFocusOnTabChanged"_s };
+
+ QTest::newRow("inBindingLabel") << file << 6 << 10
+ << ExpectedCompletions({
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ { u"width"_s, CompletionItemKind::Property },
+ })
+ << QStringList({ u"QtQuick"_s, u"vector4d"_s, u"property"_s });
+
+ QTest::newRow("afterBinding") << file << 6 << 11
+ << (ExpectedCompletions({
+ { u"height"_s, CompletionItemKind::Property },
+ { u"width"_s, CompletionItemKind::Property },
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ { singletonName, CompletionItemKind::Class },
+ })
+ + attachedTypes)
+ << QStringList({ u"QtQuick"_s, u"property"_s, u"vector4d"_s });
+
+ QTest::newRow("jsGlobals") << file << 6 << 11
+ << ExpectedCompletions{ {
+ { u"console"_s, CompletionItemKind::Property },
+ { u"Math"_s, CompletionItemKind::Property },
+ } }
+ << QStringList({ u"QtQuick"_s, u"property"_s, u"vector4d"_s });
+
+ QTest::newRow("jsGlobals2") << file << 100 << 32
+ << ExpectedCompletions{ {
+ { u"abs"_s, CompletionItemKind::Method },
+ { u"log"_s, CompletionItemKind::Method },
+ { u"E"_s, CompletionItemKind::Property },
+ } }
+ << QStringList({ u"QtQuick"_s, u"property"_s, u"vector4d"_s,
+ u"foo"_s, u"lala"_s });
+
+ QTest::newRow("afterLongBinding")
+ << file << 10 << 16
+ << ExpectedCompletions({
+ { u"height"_s, CompletionItemKind::Property },
+ { u"width"_s, CompletionItemKind::Property },
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ })
+ << QStringList({ u"QtQuick"_s, u"property"_s, u"vector4d"_s });
+
+ QTest::newRow("afterId") << file << 5 << 8 << ExpectedCompletions({})
+ << QStringList({
+ u"QtQuick"_s,
+ u"property"_s,
+ u"Rectangle"_s,
+ u"width"_s,
+ u"vector4d"_s,
+ u"import"_s,
+ });
+
+ QTest::newRow("emptyFile") << emptyFile << 1 << 1
+ << ExpectedCompletions({
+ { u"import"_s, CompletionItemKind::Keyword },
+ { u"pragma"_s, CompletionItemKind::Keyword },
+ })
+ << QStringList({ u"QtQuick"_s, u"vector4d"_s, u"width"_s });
+
+ QTest::newRow("importImport") << file << 1 << 4
+ << ExpectedCompletions({
+ { u"import"_s, CompletionItemKind::Keyword },
+ })
+ << QStringList({ u"QtQuick"_s, u"vector4d"_s, u"width"_s,
+ u"Rectangle"_s });
+
+ QTest::newRow("importModuleStart")
+ << file << 1 << 8
+ << ExpectedCompletions({
+ { u"QtQuick"_s, CompletionItemKind::Module },
+ })
+ << QStringList({ u"vector4d"_s, u"width"_s, u"Rectangle"_s, u"import"_s });
+
+ QTest::newRow("importVersionStart")
+ << file << 1 << 16
+ << ExpectedCompletions({
+ { u"2"_s, CompletionItemKind::Constant },
+ { u"as"_s, CompletionItemKind::Keyword },
+ })
+ << QStringList({ u"Rectangle"_s, u"import"_s, u"vector4d"_s, u"width"_s });
+
+ // QTest::newRow("importVersionMinor")
+ // << uri << 1 << 18
+ // << ExpectedCompletions({
+ // { u"15"_s, CompletionItemKind::Constant },
+ // })
+ // << QStringList({ u"as"_s, u"Rectangle"_s, u"import"_s, u"vector4d"_s, u"width"_s });
+
+ QTest::newRow("expandBase1") << file << 10 << 24
+ << ExpectedCompletions({
+ { u"width"_s, CompletionItemKind::Property },
+ { u"foo"_s, CompletionItemKind::Property },
+ })
+ << QStringList({ u"import"_s, u"Rectangle"_s });
+
+ QTest::newRow("expandBase2") << file << 11 << 30
+ << ExpectedCompletions({
+ { u"width"_s, CompletionItemKind::Property },
+ { u"color"_s, CompletionItemKind::Property },
+ })
+ << QStringList({ u"foo"_s, u"import"_s, u"Rectangle"_s });
+
+ QTest::newRow("qualifiedTypeCompletionBeforeDot")
+ << testFile(u"qualifiedModule.qml"_s) << 4 << 31
+ << ExpectedCompletions({
+ { u"QQ.Rectangle"_s, CompletionItemKind::Constructor },
+ })
+ << QStringList({ u"foo"_s, u"import"_s, u"lala"_s, });
+
+ QTest::newRow("qualifiedTypeCompletionAfterDot")
+ << testFile(u"qualifiedModule.qml"_s) << 4 << 35
+ << ExpectedCompletions({
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ })
+ << QStringList({ u"foo"_s, u"import"_s, u"lala"_s, u"width"_s });
+
+ QTest::newRow("qualifiedTypeCompletionBeforeDotInDefaultBinding")
+ << testFile(u"qualifiedModule.qml"_s) << 5 << 5
+ << ExpectedCompletions({
+ { u"QQ.Rectangle"_s, CompletionItemKind::Constructor },
+ })
+ << QStringList({ u"foo"_s, u"import"_s, u"lala"_s });
+
+ QTest::newRow("qualifiedTypeCompletionAfterDotInDefaultBinding")
+ << testFile(u"qualifiedModule.qml"_s) << 5 << 8
+ << ExpectedCompletions({
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ })
+ << QStringList({ u"foo"_s, u"import"_s, u"lala"_s, u"width"_s });
+
+ QTest::newRow("parameterCompletion")
+ << file << 36 << 24
+ << ExpectedCompletions({
+ { u"helloWorld"_s, CompletionItemKind::Variable },
+ { u"helloMe"_s, CompletionItemKind::Variable },
+ })
+ << QStringList();
+
+ QTest::newRow("inMethodName") << file << 15 << 14 << ExpectedCompletions({})
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s, u"foo"_s,
+ u"root"_s, u"Item"_s, singletonName };
+
+ QTest::newRow("inMethodReturnType") << file << 17 << 54 << mixedTypes
+ << QStringList{
+ u"QtQuick"_s,
+ u"foo"_s,
+ u"root"_s,
+ };
+
+ QTest::newRow("letStatement") << file << 95 << 13 << ExpectedCompletions({})
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s, u"root"_s };
+
+ QTest::newRow("inParameterCompletion") << file << 35 << 39 << ExpectedCompletions({})
+ << QStringList{
+ u"helloWorld"_s,
+ u"helloMe"_s,
+ };
+
+ QTest::newRow("parameterTypeCompletion") << file << 35 << 55 << mixedTypes
+ << QStringList{
+ u"helloWorld"_s,
+ u"helloMe"_s,
+ };
+
+ QTest::newRow("propertyTypeCompletion") << file << 16 << 14 << mixedTypes
+ << QStringList{
+ u"helloWorld"_s,
+ u"helloMe"_s,
+ };
+ QTest::newRow("propertyTypeCompletion2") << file << 16 << 23 << mixedTypes
+ << QStringList{
+ u"helloWorld"_s,
+ u"helloMe"_s,
+ };
+ QTest::newRow("propertyNameCompletion")
+ << file << 16 << 24 << ExpectedCompletions({})
+ << QStringList{
+ u"helloWorld"_s, u"helloMe"_s, u"Zzz"_s, u"Item"_s, u"int"_s, u"date"_s,
+ };
+ QTest::newRow("propertyNameCompletion2")
+ << file << 16 << 25 << ExpectedCompletions({})
+ << QStringList{
+ u"helloWorld"_s, u"helloMe"_s, u"Zzz"_s, u"Item"_s, u"int"_s, u"date"_s,
+ };
+
+ QTest::newRow("propertyDefinitionBinding")
+ << file << 90 << 27
+ << (ExpectedCompletions({
+ { u"lala"_s, CompletionItemKind::Method },
+ { u"createRectangle"_s, CompletionItemKind::Method },
+ { u"createItem"_s, CompletionItemKind::Method },
+ { u"createAnything"_s, CompletionItemKind::Method },
+ }) += constructorTypes)
+ << QStringList{
+ u"helloWorld"_s,
+ u"helloMe"_s,
+ u"int"_s,
+ u"date"_s,
+ };
+
+ QTest::newRow("ignoreNonRelatedTypesForPropertyDefinitionBinding")
+ << file << 16 << 28
+ << (ExpectedCompletions({
+ { u"createRectangle"_s, CompletionItemKind::Method },
+ { u"createItem"_s, CompletionItemKind::Method },
+ { u"createAnything"_s, CompletionItemKind::Method },
+ }) += rectangleTypes)
+ << QStringList{
+ u"Item"_s, u"Zzz"_s, u"helloWorld"_s, u"helloMe"_s,
+ u"int"_s, u"date"_s, u"Item"_s, u"QtObject"_s,
+ };
+
+ QTest::newRow("inBoundObject")
+ << file << 16 << 40
+ << (ExpectedCompletions({
+ { u"objectName"_s, CompletionItemKind::Property },
+ { u"width"_s, CompletionItemKind::Property },
+ { propertyCompletion, CompletionItemKind::Snippet },
+ { functionCompletion, CompletionItemKind::Snippet },
+ }) += constructorTypes)
+ << QStringList{
+ u"helloWorld"_s, u"helloMe"_s, u"int"_s, u"date"_s, u"QtQuick"_s, u"vector4d"_s,
+ };
+
+ QTest::newRow("qualifiedIdentifierCompletion")
+ << file << 37 << 36
+ << ExpectedCompletions({
+ { u"helloProperty"_s, CompletionItemKind::Property },
+ { u"childAt"_s, CompletionItemKind::Method },
+ })
+ << QStringList{ u"helloVar"_s, u"someItem"_s, u"color"_s, u"helloWorld"_s,
+ u"propertyOfZZZ"_s };
+
+ QTest::newRow("scriptExpressionCompletion")
+ << file << 60 << 16
+ << ExpectedCompletions({
+ // parameters
+ { u"jsParameterInChild"_s, CompletionItemKind::Variable },
+ // own properties
+ { u"jsIdentifierInChild"_s, CompletionItemKind::Variable },
+ { u"functionInChild"_s, CompletionItemKind::Method },
+ { u"propertyInChild"_s, CompletionItemKind::Property },
+ // inherited properties from QML
+ { u"functionInBase"_s, CompletionItemKind::Method },
+ { u"propertyInBase"_s, CompletionItemKind::Property },
+ // inherited properties (transitive) from C++
+ { u"objectName"_s, CompletionItemKind::Property },
+ { u"someItem"_s, CompletionItemKind::Value },
+ { u"true"_s, CompletionItemKind::Value },
+ { u"false"_s, CompletionItemKind::Value },
+ { u"null"_s, CompletionItemKind::Value },
+ })
+ << QStringList{
+ u"helloVar"_s,
+ u"color"_s,
+ u"helloWorld"_s,
+ u"propertyOfZZZ"_s,
+ u"propertyInDerived"_s,
+ u"functionInDerived"_s,
+ u"jsIdentifierInDerived"_s,
+ u"jsIdentifierInBase"_s,
+ u"lala"_s,
+ u"foo"_s,
+ u"jsParameterInBase"_s,
+ u"jsParameterInDerived"_s,
+ };
+
+ QTest::newRow("qualifiedScriptExpressionCompletion")
+ << file << 60 << 34
+ << ExpectedCompletions({
+ // own properties
+ { u"helloProperty"_s, CompletionItemKind::Property },
+ // inherited properties (transitive) from C++
+ { u"width"_s, CompletionItemKind::Property },
+ })
+ << QStringList{
+ u"helloVar"_s,
+ u"color"_s,
+ u"helloWorld"_s,
+ u"propertyOfZZZ"_s,
+ u"propertyInDerived"_s,
+ u"functionInDerived"_s,
+ u"jsIdentifierInDerived"_s,
+ u"jsIdentifierInBase"_s,
+ u"jsIdentifierInChild"_s,
+ u"lala"_s,
+ u"foo"_s,
+ u"jsParameterInBase"_s,
+ u"jsParameterInDerived"_s,
+ u"jsParameterInChild"_s,
+ u"functionInChild"_s,
+ };
+
+ QTest::newRow("pragma") << pragmaFile << 1 << 8
+ << ExpectedCompletions({
+ { u"NativeMethodBehavior"_s, CompletionItemKind::Value },
+ { u"ComponentBehavior"_s, CompletionItemKind::Value },
+ { u"ListPropertyAssignBehavior"_s,
+ CompletionItemKind::Value },
+ { u"Singleton"_s, CompletionItemKind::Value },
+ // note: only complete the Addressible/Inaddressible part of
+ // ValueTypeBehavior!
+ { u"ValueTypeBehavior"_s, CompletionItemKind::Value },
+ })
+ << QStringList{
+ u"int"_s,
+ u"Rectangle"_s,
+ u"FunctionSignatureBehavior"_s,
+ u"Strict"_s,
+ };
+
+ QTest::newRow("pragmaValue") << pragmaFile << 2 << 30
+ << ExpectedCompletions({
+ { u"AcceptThisObject"_s, CompletionItemKind::Value },
+ { u"RejectThisObject"_s, CompletionItemKind::Value },
+ })
+ << QStringList{
+ u"int"_s,
+ u"Rectangle"_s,
+ u"FunctionSignatureBehavior"_s,
+ u"Strict"_s,
+ u"NativeMethodBehavior"_s,
+ u"ComponentBehavior"_s,
+ u"ListPropertyAssignBehavior"_s,
+ u"Singleton"_s,
+ u"ValueTypeBehavior"_s,
+ u"Unbound"_s,
+ };
+
+ QTest::newRow("pragmaMultiValue")
+ << pragmaFile << 3 << 43
+ << ExpectedCompletions({
+ { u"ReplaceIfNotDefault"_s, CompletionItemKind::Value },
+ { u"Append"_s, CompletionItemKind::Value },
+ { u"Replace"_s, CompletionItemKind::Value },
+ })
+ << QStringList{
+ u"int"_s,
+ u"Rectangle"_s,
+ u"FunctionSignatureBehavior"_s,
+ u"Strict"_s,
+ u"NativeMethodBehavior"_s,
+ u"ComponentBehavior"_s,
+ u"ListPropertyAssignBehavior"_s,
+ u"Singleton"_s,
+ u"ValueTypeBehavior"_s,
+ u"Unbound"_s,
+ };
+
+ QTest::newRow("pragmaWithoutValue")
+ << pragmaFile << 1 << 17
+ << ExpectedCompletions({
+ { u"NativeMethodBehavior"_s, CompletionItemKind::Value },
+ { u"ComponentBehavior"_s, CompletionItemKind::Value },
+ { u"ListPropertyAssignBehavior"_s, CompletionItemKind::Value },
+ { u"Singleton"_s, CompletionItemKind::Value },
+ // note: only complete the Addressible/Inaddressible part of
+ // ValueTypeBehavior!
+ { u"ValueTypeBehavior"_s, CompletionItemKind::Value },
+ })
+ << QStringList{
+ u"int"_s,
+ u"Rectangle"_s,
+ u"FunctionSignatureBehavior"_s,
+ u"Strict"_s,
+ };
+
+ QTest::newRow("non-block-scoped-variable")
+ << file << 69 << 21
+ << ExpectedCompletions({
+ { u"helloVarVariable"_s, CompletionItemKind::Variable },
+ })
+ << QStringList{};
+ QTest::newRow("block-scoped-variable")
+ << file << 76 << 21 << ExpectedCompletions{ { u"test2"_s, CompletionItemKind::Method } }
+ << QStringList{ u"helloLetVariable"_s, u"helloVarVariable"_s };
+
+ QTest::newRow("singleton") << file << 78 << 33
+ << ExpectedCompletions({
+ { singletonName, CompletionItemKind::Class },
+ })
+ << QStringList{};
+
+ QTest::newRow("singletonPropertyAndEnums")
+ << file << 78 << 52
+ << ExpectedCompletions({
+ { u"byteOrder"_s, CompletionItemKind::Property },
+ { u"Little"_s, CompletionItemKind::EnumMember },
+ { u"Endian"_s, CompletionItemKind::Enum },
+ })
+ << QStringList{
+ u"int"_s,
+ u"Rectangle"_s,
+ u"foo"_s,
+ };
+
+ QTest::newRow("enumsFromItem") << file << 86 << 33
+ << ExpectedCompletions({
+ { u"World"_s, CompletionItemKind::EnumMember },
+ { u"ValueOne"_s, CompletionItemKind::EnumMember },
+ { u"ValueTwo"_s, CompletionItemKind::EnumMember },
+ { u"Hello"_s, CompletionItemKind::Enum },
+ { u"MyEnum"_s, CompletionItemKind::Enum },
+ })
+ << QStringList{
+ u"int"_s,
+ u"Rectangle"_s,
+ };
+
+ QTest::newRow("enumsFromEnumName")
+ << file << 87 << 40
+ << ExpectedCompletions({
+ { u"World"_s, CompletionItemKind::EnumMember },
+ })
+ << QStringList{
+ u"int"_s, u"Rectangle"_s, u"foo"_s, u"ValueOne"_s,
+ u"ValueTwo"_s, u"Hello"_s, u"MyEnum"_s,
+ };
+
+ QTest::newRow("requiredProperty")
+ << file << 97 << 14
+ << ExpectedCompletions({
+ { u"property"_s, CompletionItemKind::Keyword },
+ { u"default"_s, CompletionItemKind::Keyword },
+ })
+ << QStringList{
+ u"readonly"_s, u"required"_s, u"int"_s, u"Rectangle"_s, u"foo"_s,
+ u"ValueOne"_s, u"ValueTwo"_s, u"Hello"_s, u"MyEnum"_s,
+ };
+
+ QTest::newRow("readonlyProperty")
+ << file << 98 << 13
+ << ExpectedCompletions({
+ { u"property"_s, CompletionItemKind::Keyword },
+ { u"default"_s, CompletionItemKind::Keyword },
+ })
+ << QStringList{
+ u"required"_s, u"readonly"_s, u"int"_s, u"Rectangle"_s, u"foo"_s,
+ u"ValueOne"_s, u"ValueTwo"_s, u"Hello"_s, u"MyEnum"_s,
+ };
+
+ QTest::newRow("defaultProperty")
+ << file << 99 << 12
+ << ExpectedCompletions({
+ { u"property"_s, CompletionItemKind::Keyword },
+ { u"readonly"_s, CompletionItemKind::Keyword },
+ { u"required"_s, CompletionItemKind::Keyword },
+ })
+ << QStringList{
+ u"default"_s, u"int"_s, u"Rectangle"_s, u"foo"_s,
+ u"ValueOne"_s, u"ValueTwo"_s, u"Hello"_s, u"MyEnum"_s,
+ };
+
+ QTest::newRow("defaultProperty2")
+ << file << 99 << 20
+ << ExpectedCompletions({
+ { u"property"_s, CompletionItemKind::Keyword },
+ { u"readonly"_s, CompletionItemKind::Keyword },
+ { u"required"_s, CompletionItemKind::Keyword },
+ })
+ << QStringList{
+ u"default"_s, u"int"_s, u"Rectangle"_s, u"foo"_s,
+ u"ValueOne"_s, u"ValueTwo"_s, u"Hello"_s, u"MyEnum"_s,
+ };
+
+ QTest::newRow("defaultProperty3")
+ << file << 99 << 21 << ExpectedCompletions{ { u"int"_s, CompletionItemKind::Class } }
+ << QStringList{
+ u"property"_s,
+ u"readonly"_s,
+ u"required"_s,
+ };
+
+ const QString forStatementCompletion = u"for (initializer; condition; increment) { statements... }"_s;
+ const QString ifStatementCompletion = u"if (condition) statement"_s;
+ const QString letStatementCompletion = u"let variable = value;"_s;
+ const QString constStatementCompletion = u"const variable = value;"_s;
+ const QString varStatementCompletion = u"var variable = value;"_s;
+
+ // for the for loop
+ const QString letStatementCompletionWithoutSemicolon = letStatementCompletion.chopped(1);
+ const QString constStatementCompletionWithoutSemicolon = constStatementCompletion.chopped(1);
+ const QString varStatementCompletionWithoutSemicolon = varStatementCompletion.chopped(1);
+
+ const QString caseStatementCompletion = u"case value: statements..."_s;
+ const QString caseStatement2Completion = u"case value: { statements... }"_s;
+ const QString defaultStatementCompletion = u"default: statements..."_s;
+ const QString defaultStatement2Completion = u"default: { statements... }"_s;
+
+ // warning: the completion strings in the test below were all tested by hand in VS Code to
+ // make sure they are easy to use. Make sure to check the code snippets by hand before changing
+ // them.
+ QTest::newRow("jsStatements")
+ << file << 104 << 1
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet,
+ u"let ${1:variable} = $0;"_s },
+ { u"const variable = value;"_s, CompletionItemKind::Snippet,
+ u"const ${1:variable} = $0;"_s },
+ { u"var variable = value;"_s, CompletionItemKind::Snippet,
+ u"var ${1:variable} = $0;"_s },
+ { u"{ statements... }"_s, CompletionItemKind::Snippet,
+ u"{\n\t$0\n}"_s },
+ { u"if (condition) { statements }"_s,
+ CompletionItemKind::Snippet, u"if ($1) {\n\t$0\n}"_s },
+ { u"do { statements } while (condition);"_s,
+ CompletionItemKind::Snippet, u"do {\n\t$1\n} while ($0);"_s },
+ { u"while (condition) { statements...}"_s,
+ CompletionItemKind::Snippet, u"while ($1) {\n\t$0\n}"_s },
+ { forStatementCompletion,
+ CompletionItemKind::Snippet, u"for ($1;$2;$3) {\n\t$0\n}"_s },
+ { u"try { statements... } catch(error) { statements... }"_s,
+ CompletionItemKind::Snippet, u"try {\n\t$1\n} catch($2) {\n\t$0\n}"_s },
+ { u"try { statements... } finally { statements... }"_s,
+ CompletionItemKind::Snippet, u"try {\n\t$1\n} finally {\n\t$0\n}"_s },
+ { u"try { statements... } catch(error) { statements... } finally { statements... }"_s,
+ CompletionItemKind::Snippet, u"try {\n\t$1\n} catch($2) {\n\t$3\n} finally {\n\t$0\n}"_s },
+ { u"for (property in object) { statements... }"_s,
+ CompletionItemKind::Snippet, u"for ($1 in $2) {\n\t$0\n}"_s },
+ { u"for (element of array) { statements... }"_s,
+ CompletionItemKind::Snippet, u"for ($1 of $2) {\n\t$0\n}"_s },
+ { u"continue"_s, CompletionItemKind::Keyword },
+ { u"break"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ caseStatementCompletion,
+ caseStatement2Completion,
+ defaultStatementCompletion,
+ defaultStatement2Completion,
+ };
+
+ QTest::newRow("forStatementLet")
+ << file << 103 << 13
+ << ExpectedCompletions{ { letStatementCompletionWithoutSemicolon,
+ CompletionItemKind::Snippet, u"let ${1:variable} = $0"_s },
+ { constStatementCompletionWithoutSemicolon,
+ CompletionItemKind::Snippet, u"const ${1:variable} = $0"_s },
+ { varStatementCompletionWithoutSemicolon,
+ CompletionItemKind::Snippet, u"var ${1:variable} = $0"_s },
+ { u"helloJSStatements"_s, CompletionItemKind::Method } }
+ << QStringList{ u"property"_s,
+ u"readonly"_s,
+ u"required"_s,
+ forStatementCompletion,
+ ifStatementCompletion,
+ letStatementCompletion,
+ constStatementCompletion,
+ varStatementCompletion };
+
+ QTest::newRow("forStatementCondition")
+ << file << 103 << 25
+ << ExpectedCompletions{
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { u"i"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ u"property"_s, u"readonly"_s, u"required"_s,
+ forStatementCompletion, ifStatementCompletion, varStatementCompletion,
+ letStatementCompletion, constStatementCompletion, }
+ ;
+
+ QTest::newRow("forStatementIncrement")
+ << file << 103 << 30
+ << ExpectedCompletions{
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { u"i"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ u"property"_s, u"readonly"_s, u"required"_s,
+ forStatementCompletion, ifStatementCompletion, varStatementCompletion,
+ letStatementCompletion, constStatementCompletion, }
+ ;
+
+ QTest::newRow("forStatementIncrement2")
+ << file << 103 << 33
+ << ExpectedCompletions{ { u"helloJSStatements"_s, CompletionItemKind::Method } }
+ << QStringList{
+ u"property"_s, u"readonly"_s,
+ u"required"_s, forStatementCompletion,
+ ifStatementCompletion, varStatementCompletion,
+ letStatementCompletion, constStatementCompletion,
+ };
+
+ QTest::newRow("forStatementWithoutBlock")
+ << file << 107 << 12
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { u"j"_s, CompletionItemKind::Variable },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("blockStatementBeforeBracket")
+ << file << 103 << 36
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { u"i"_s, CompletionItemKind::Variable },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("blockStatementAfterBracket")
+ << file << 103 << 37
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("ifStatementCondition")
+ << file << 110 << 15
+ << ExpectedCompletions{
+ { u"hello"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion, constStatementCompletion }
+ ;
+
+ QTest::newRow("ifStatementConsequence")
+ << file << 111 << 12
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"hello"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion }
+ ;
+
+ QTest::newRow("ifStatementAlternative")
+ << file << 113 << 12
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"hello"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion }
+ ;
+
+ QTest::newRow("binaryExpressionCompletionInsideStatement")
+ << file << 113 << 21
+ << ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable }, }
+ << QStringList{ propertyCompletion, forStatementCompletion }
+ ;
+
+ QTest::newRow("elseIfStatement")
+ << file << 121 << 18
+ << ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable }, }
+ << QStringList{ propertyCompletion, letStatementCompletion, ifStatementCompletion }
+ ;
+ QTest::newRow("returnStatement")
+ << file << 125 << 16
+ << ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+ QTest::newRow("returnStatement2")
+ << testFile("completions/returnStatement.qml") << 8 << 15
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable }, }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("whileCondition")
+ << file << 128 << 16
+ << ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable }, }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("whileConsequence")
+ << file << 128 << 22
+ << ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable },
+ { letStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("doWhileCondition")
+ << file << 131 << 30
+ << ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable }, }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("doWhileConsequence")
+ << file << 131 << 12
+ << ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable },
+ { letStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("forInStatementLet")
+ << file << 134 << 13
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"helloJSStatements"_s, CompletionItemKind::Method } }
+ << QStringList{
+ u"property"_s, u"readonly"_s, u"required"_s,
+ forStatementCompletion, ifStatementCompletion,
+ };
+
+ QTest::newRow("forOfStatementLet")
+ << file << 135 << 13
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"helloJSStatements"_s, CompletionItemKind::Method } }
+ << QStringList{
+ u"property"_s, u"readonly"_s, u"required"_s,
+ forStatementCompletion, ifStatementCompletion,
+ };
+
+ QTest::newRow("forInStatementTarget")
+ << file << 134 << 25
+ << ExpectedCompletions{
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { u"hello"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ u"property"_s, u"readonly"_s, u"required"_s,
+ forStatementCompletion, ifStatementCompletion, varStatementCompletion,
+ letStatementCompletion, constStatementCompletion, }
+ ;
+
+ QTest::newRow("forOfStatementTarget")
+ << file << 135 << 24
+ << ExpectedCompletions{
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { u"hello"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ u"property"_s, u"readonly"_s, u"required"_s,
+ forStatementCompletion, ifStatementCompletion, varStatementCompletion,
+ letStatementCompletion, constStatementCompletion, }
+ ;
+
+ QTest::newRow("forInStatementConsequence")
+ << file << 134 << 31
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { u"hello"_s, CompletionItemKind::Variable },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("forOfStatementConsequence")
+ << file << 135 << 30
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { u"hello"_s, CompletionItemKind::Variable },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("binaryExpressionRHS") << file << 138 << 17
+ << ExpectedCompletions{
+ { u"log"_s, CompletionItemKind::Method },
+ { u"error"_s, CompletionItemKind::Method },
+ }
+ << QStringList{ propertyCompletion, u"helloVarVariable"_s,
+ u"test1"_s, u"width"_s,
+ u"height"_s, u"layer"_s,
+ u"left"_s, forStatementCompletion }
+ ;
+ QTest::newRow("binaryExpressionLHS") << file << 138 << 12
+ << ExpectedCompletions{
+ { u"qualifiedScriptIdentifiers"_s, CompletionItemKind::Method },
+ { u"width"_s, CompletionItemKind::Property },
+ { u"layer"_s, CompletionItemKind::Property },
+ }
+ << QStringList{ u"log"_s, u"error"_s, forStatementCompletion}
+ ;
+
+ const QString missingRHSFile = testFile(u"completions/missingRHS.qml"_s);
+ QTest::newRow("binaryExpressionMissingRHS") << missingRHSFile << 12 << 25
+ << ExpectedCompletions{
+ { u"good"_s, CompletionItemKind::Property },
+ }
+ << QStringList{ propertyCompletion, u"bad"_s }
+ ;
+ QTest::newRow("binaryExpressionMissingRHSWithDefaultProperty") << missingRHSFile << 14 << 33
+ << ExpectedCompletions{
+ { u"good"_s, CompletionItemKind::Property },
+ }
+ << QStringList{ propertyCompletion, u"bad"_s, u"helloSubItem"_s }
+ ;
+
+ QTest::newRow("binaryExpressionMissingRHSWithSemicolon")
+ << testFile(u"completions/missingRHS.parserfail.qml"_s)
+ << 5 << 22
+ << ExpectedCompletions{
+ { u"good"_s, CompletionItemKind::Property },
+ }
+ << QStringList{ propertyCompletion, u"bad"_s, u"helloSubItem"_s }
+ ;
+
+ QTest::newRow("binaryExpressionMissingRHSWithStatement") <<
+ testFile(u"completions/missingRHS.parserfail.qml"_s)
+ << 6 << 22
+ << ExpectedCompletions{
+ { u"good"_s, CompletionItemKind::Property },
+ }
+ << QStringList{ propertyCompletion, u"bad"_s, u"helloSubItem"_s }
+ ;
+
+ QTest::newRow("tryStatements")
+ << testFile(u"completions/tryStatements.qml"_s) << 5 << 14
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{};
+
+ QTest::newRow("tryStatementsCatchParameter")
+ << testFile(u"completions/tryStatements.qml"_s) << 5 << 23 << ExpectedCompletions{}
+ << QStringList{ letStatementCompletion, forStatementCompletion };
+
+ QTest::newRow("tryStatementsCatchBlock")
+ << testFile(u"completions/tryStatements.qml"_s) << 5 << 27
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{};
+
+ QTest::newRow("tryStatementsFinallyBlock")
+ << testFile(u"completions/tryStatements.qml"_s) << 5 << 39
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{};
+
+ QTest::newRow("inSwitchExpression")
+ << testFile(u"completions/switchStatements.qml"_s) << 10 << 16
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{
+ letStatementCompletion,
+ propertyCompletion,
+ };
+
+ QTest::newRow("beforeCaseStatement")
+ << testFile(u"completions/switchStatements.qml"_s) << 11 << 1
+ << ExpectedCompletions{ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { caseStatement2Completion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatement2Completion, CompletionItemKind::Snippet } }
+ << QStringList{ letStatementCompletion, propertyCompletion, u"x"_s, u"myProperty"_s };
+ QTest::newRow("inCaseExpression")
+ << testFile(u"completions/switchStatements.qml"_s) << 12 << 14
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{ letStatementCompletion, propertyCompletion, caseStatementCompletion };
+ QTest::newRow("inCaseStatementList")
+ << testFile(u"completions/switchStatements.qml"_s) << 13 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"break"_s, CompletionItemKind::Keyword },
+ { u"return"_s, CompletionItemKind::Keyword },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{ propertyCompletion };
+ QTest::newRow("inDefaultStatementList")
+ << testFile(u"completions/switchStatements.qml"_s) << 24 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"break"_s, CompletionItemKind::Keyword },
+ { u"return"_s, CompletionItemKind::Keyword },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{ propertyCompletion };
+ QTest::newRow("inMoreCasesStatementList")
+ << testFile(u"completions/switchStatements.qml"_s) << 26 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"break"_s, CompletionItemKind::Keyword },
+ { u"return"_s, CompletionItemKind::Keyword },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("inCaseBeforeBlock")
+ << testFile(u"completions/switchStatements.qml"_s) << 14 << 23
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{
+ propertyCompletion,
+ };
+ QTest::newRow("inCaseBeforeBlock2")
+ << testFile(u"completions/switchStatements.qml"_s) << 14 << 24
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{
+ propertyCompletion,
+ };
+
+ QTest::newRow("inCaseNestedStatement")
+ << testFile(u"completions/switchStatements.qml"_s) << 16 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{ propertyCompletion, caseStatementCompletion,
+ defaultStatementCompletion };
+
+ QTest::newRow("inCaseAfterBlock")
+ << testFile(u"completions/switchStatements.qml"_s) << 22 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{
+ propertyCompletion,
+ };
+
+ QTest::newRow("inCaseBeforeDefault")
+ << testFile(u"completions/switchStatements.qml"_s) << 23 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{
+ propertyCompletion,
+ };
+
+ QTest::newRow("inCaseAfterDefault")
+ << testFile(u"completions/switchStatements.qml"_s) << 25 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{
+ propertyCompletion,
+ };
+
+ QTest::newRow("beforeAnyCase")
+ << testFile(u"completions/switchStatements.qml"_s) << 20 << 1
+ << ExpectedCompletions{ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion, letStatementCompletion, u"myProperty"_s, u"x"_s,
+ u"f"_s };
+
+ QTest::newRow("beforeAnyDefault")
+ << testFile(u"completions/switchStatements.qml"_s) << 32 << 1
+ << ExpectedCompletions{ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion, letStatementCompletion, u"myProperty"_s, u"x"_s,
+ u"f"_s };
+ QTest::newRow("inDefaultAfterDefault")
+ << testFile(u"completions/switchStatements.qml"_s) << 33 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{
+ propertyCompletion,
+ };
+
+ // variableDeclaration.qml tests for let/const/var statements + destructuring
+
+ QTest::newRow("letStatement") << testFile(u"completions/variableDeclaration.qml"_s) << 7 << 13
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion,
+ u"x"_s, u"data"_s };
+
+ QTest::newRow("letStatement2")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 7 << 26
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("letStatementBehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 7 << 28
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("letStatementBehindEqual2")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 7 << 33
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("constStatement")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 8 << 19
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("constStatementBehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 8 << 32
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("varStatement") << testFile(u"completions/variableDeclaration.qml"_s) << 9 << 17
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion,
+ u"x"_s, u"data"_s };
+
+ QTest::newRow("varStatementBehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 9 << 28
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("objectDeconstruction")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 13 << 20
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("objectDeconstructionAloneBehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 14 << 51
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("objectDeconstructionAloneBehindEqual2")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 14 << 58
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("objectDeconstruction2BehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 15 << 83
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("objectDeconstruction2BehindEqual2")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 15 << 90
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("objectDeconstruction3BehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 15 << 140
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("objectDeconstructionBehindComma")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 15 << 143
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("objectDeconstructionBetweenObjects")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 15 << 50
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("objectDeconstructionBetweenDeconstructions")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 15 << 97
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("arrayDeconstructionAlone")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 19 << 24
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("arrayDeconstructionAloneBehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 19 << 33
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("arrayDeconstruction2")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 21 << 71
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("arrayDeconstruction2BehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 21 << 83
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+ QTest::newRow("arrayDeconstruction3")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 21 << 125
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("arrayDeconstruction3BehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 21 << 139
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("arrayDeconstructionIn_Wildcard")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 25 << 64
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("arrayDeconstructionBehind+")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 25 << 132
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("objectDeconstructionForNeedle")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 29 << 111
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("arrayInObjectDeconstructionInObjectInitializer")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 33 << 44
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("arrayInObjectDeconstructionInObjectPropertyName")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 33 << 26
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("throwStatement")
+ << testFile(u"completions/throwStatement.qml"_s) << 8 << 15
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method } }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("throwStatement2")
+ << testFile(u"completions/throwStatement.qml"_s) << 9 << 20
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method } }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("labelledStatement")
+ << testFile(u"completions/labelledStatement.qml"_s) << 5 << 16
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { forStatementCompletion, CompletionItemKind::Snippet },
+ }
+ << QStringList{ propertyCompletion, };
+
+ QTest::newRow("nestedLabel")
+ << testFile(u"completions/labelledStatement.qml"_s) << 7 << 22
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { forStatementCompletion, CompletionItemKind::Snippet },
+ }
+ << QStringList{ propertyCompletion, };
+
+ QTest::newRow("nestedLabel2")
+ << testFile(u"completions/labelledStatement.qml"_s) << 8 << 26
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { forStatementCompletion, CompletionItemKind::Snippet },
+ }
+ << QStringList{ propertyCompletion, };
+
+ QTest::newRow("multiLabel")
+ << testFile(u"completions/labelledStatement.qml"_s) << 15 << 21
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { forStatementCompletion, CompletionItemKind::Snippet },
+ }
+ << QStringList{ propertyCompletion, };
+
+ QTest::newRow("multiLabel2")
+ << testFile(u"completions/labelledStatement.qml"_s) << 16 << 21
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { forStatementCompletion, CompletionItemKind::Snippet },
+ }
+ << QStringList{ propertyCompletion, };
+
+ QTest::newRow("continueNested")
+ << testFile(u"completions/continueAndBreakStatement.qml"_s) << 12 << 26
+ << ExpectedCompletions{ { u"nestedLabel1"_s, CompletionItemKind::Value },
+ { u"nestedLabel2"_s, CompletionItemKind::Value }, }
+ << QStringList{ propertyCompletion, u"x"_s, u"f"_s, u"multiLabel1"_s };
+
+ QTest::newRow("breakNested")
+ << testFile(u"completions/continueAndBreakStatement.qml"_s) << 13 << 23
+ << ExpectedCompletions{ { u"nestedLabel1"_s, CompletionItemKind::Value },
+ { u"nestedLabel2"_s, CompletionItemKind::Value }, }
+ << QStringList{ propertyCompletion, u"x"_s, u"f"_s, u"multiLabel1"_s };
+
+ QTest::newRow("continueMulti")
+ << testFile(u"completions/continueAndBreakStatement.qml"_s) << 20 << 22
+ << ExpectedCompletions{ { u"multiLabel1"_s, CompletionItemKind::Value },
+ { u"multiLabel2"_s, CompletionItemKind::Value }, }
+ << QStringList{ propertyCompletion, u"x"_s, u"f"_s, u"nestedLabel1"_s };
+
+ QTest::newRow("breakMulti")
+ << testFile(u"completions/continueAndBreakStatement.qml"_s) << 21 << 19
+ << ExpectedCompletions{ { u"multiLabel1"_s, CompletionItemKind::Value },
+ { u"multiLabel2"_s, CompletionItemKind::Value }, }
+ << QStringList{ propertyCompletion, u"x"_s, u"f"_s, u"nestedLabel1"_s };
+
+ QTest::newRow("continueNoLabel") << testFile(u"completions/continueAndBreakStatement.qml"_s)
+ << 25 << 22 << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, u"x"_s, u"f"_s,
+ u"nestedLabel1"_s, u"multiLabel1"_s };
+
+ QTest::newRow("breakNoLabel") << testFile(u"completions/continueAndBreakStatement.qml"_s) << 26
+ << 19 << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, u"x"_s, u"f"_s,
+ u"nestedLabel1"_s, u"multiLabel1"_s };
+
+ QTest::newRow("insideMethodBody")
+ << testFile(u"completions/functionBody.qml"_s) << 5 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("insideMethodBody2")
+ << testFile(u"completions/functionBody.qml"_s) << 11 << 11
+ << ExpectedCompletions{ { u"helloProperty"_s, CompletionItemKind::Property }, }
+ << QStringList{ u"badProperty"_s, forStatementCompletion };
+
+ QTest::newRow("insideMethodBodyStart")
+ << testFile(u"completions/functionBody.qml"_s) << 11 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ u"helloProperty"_s };
+
+ QTest::newRow("insideMethodBodyEnd")
+ << testFile(u"completions/functionBody.qml"_s) << 12 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ u"helloProperty"_s };
+
+ QTest::newRow("noBreakInMethodBody")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 8 << 8
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable } }
+ << QStringList{ u"break"_s, u"continue"_s };
+
+ QTest::newRow("breakAndContinueInForLoop")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 11 << 12
+ << ExpectedCompletions{ { u"break"_s, CompletionItemKind::Keyword },
+ { u"continue"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{};
+
+ QTest::newRow("noBreakInSwitch")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 15 << 12
+ << ExpectedCompletions{ { caseStatementCompletion, CompletionItemKind::Snippet }, }
+ << QStringList{ u"continue"_s, u"break"_s };
+
+ QTest::newRow("breakInSwitchCase")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 17 << 12
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"break"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ u"continue"_s };
+
+ QTest::newRow("breakInSwitchDefault")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 19 << 12
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"break"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ u"continue"_s };
+
+ QTest::newRow("breakInSwitchSecondCase")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 21 << 12
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"break"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ u"continue"_s };
+
+ QTest::newRow("breakInLabel")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 25 << 12
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"break"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ u"continue"_s };
+
+ QTest::newRow("forLoopInsideOfLabel")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 33 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"break"_s, CompletionItemKind::Keyword },
+ { u"continue"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ };
+
+ QTest::newRow("switchInsideForLoopInsideOfLabel")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 36 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"break"_s, CompletionItemKind::Keyword },
+ { u"continue"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ };
+
+ QTest::newRow("switchInsideOfLabel")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 45 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"break"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ u"continue"_s };
+
+ QTest::newRow("forLoopInSwitchInsideOfLabel")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 47 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"break"_s, CompletionItemKind::Keyword },
+ { u"continue"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ };
+
+ QTest::newRow("commaExpression")
+ << testFile(u"completions/commaExpression.qml"_s) << 5 << 18
+ << ExpectedCompletions{ { u"a"_s, CompletionItemKind::Variable },
+ { u"b"_s, CompletionItemKind::Variable },
+ { u"c"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("conditionalExpressionConsequence")
+ << testFile(u"completions/conditionalExpression.qml"_s) << 5 << 17
+ << ExpectedCompletions{ { u"a"_s, CompletionItemKind::Variable },
+ { u"b"_s, CompletionItemKind::Variable },
+ { u"c"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("conditionalExpressionAlternative")
+ << testFile(u"completions/conditionalExpression.qml"_s) << 5 << 30
+ << ExpectedCompletions{ { u"a"_s, CompletionItemKind::Variable },
+ { u"b"_s, CompletionItemKind::Variable },
+ { u"c"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("unaryMinus")
+ << testFile(u"completions/unaryExpression.qml"_s) << 5 << 10
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("unaryPlus")
+ << testFile(u"completions/unaryExpression.qml"_s) << 6 << 10
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("unaryTilde")
+ << testFile(u"completions/unaryExpression.qml"_s) << 7 << 10
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("unaryNot")
+ << testFile(u"completions/unaryExpression.qml"_s) << 8 << 10
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("typeof")
+ << testFile(u"completions/unaryExpression.qml"_s) << 9 << 16
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("delete")
+ << testFile(u"completions/unaryExpression.qml"_s) << 10 << 16
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("void")
+ << testFile(u"completions/unaryExpression.qml"_s) << 11 << 14
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("postDecrement")
+ << testFile(u"completions/unaryExpression.qml"_s) << 12 << 9
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("postIncrement")
+ << testFile(u"completions/unaryExpression.qml"_s) << 13 << 9
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("preDecrement")
+ << testFile(u"completions/unaryExpression.qml"_s) << 14 << 11
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("preIncrement")
+ << testFile(u"completions/unaryExpression.qml"_s) << 15 << 11
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("attachedPropertyAfterDot")
+ << testFile("completions/attachedAndGroupedProperty.qml") << 8 << 15
+ << ExpectedCompletions({
+ { u"onCompleted"_s, CompletionItemKind::Method },
+ })
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s, attachedTypeName, u"Rectangle"_s,
+ u"bad"_s };
+
+ QTest::newRow("groupedPropertyAfterDot")
+ << testFile("completions/attachedAndGroupedProperty.qml") << 10 << 15
+ << ExpectedCompletions({
+ { u"family"_s, CompletionItemKind::Property },
+ })
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s, attachedTypeName, u"Rectangle"_s,
+ u"bad"_s, u"onCompleted"_s };
+
+ QTest::newRow("attachedPropertyAfterDotMissingRHS")
+ << testFile("completions/attachedPropertyMissingRHS.qml") << 7 << 17
+ << ExpectedCompletions({
+ { u"onCompleted"_s, CompletionItemKind::Method },
+ })
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s, attachedTypeName, u"Rectangle"_s,
+ u"bad"_s };
+
+ QTest::newRow("groupedPropertyAfterDotMissingRHS")
+ << testFile("completions/groupedPropertyMissingRHS.qml") << 7 << 11
+ << ExpectedCompletions({
+ { u"family"_s, CompletionItemKind::Property },
+ })
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s, attachedTypeName, u"Rectangle"_s,
+ u"bad"_s, u"onCompleted"_s };
+
+ QTest::newRow("dotFollowedByDefaultBinding")
+ << testFile("completions/afterDots.qml") << 11 << 31
+ << ExpectedCompletions({
+ { u"good"_s, CompletionItemKind::Property },
+ })
+ << QStringList{ u"bad"_s, u"QtQuick"_s, u"vector4d"_s,
+ attachedTypeName, u"Rectangle"_s, u"onCompleted"_s };
+
+ QTest::newRow("dotFollowedByBinding")
+ << testFile("completions/afterDots.qml") << 13 << 32
+ << ExpectedCompletions({
+ { u"good"_s, CompletionItemKind::Property },
+ })
+ << QStringList{ u"bad"_s, u"QtQuick"_s, u"vector4d"_s,
+ attachedTypeName, u"Rectangle"_s, u"onCompleted"_s };
+
+ QTest::newRow("dotFollowedByForStatement")
+ << testFile("completions/afterDots.qml") << 16 << 17
+ << ExpectedCompletions({
+ { u"good"_s, CompletionItemKind::Property },
+ })
+ << QStringList{
+ u"bad"_s, u"QtQuick"_s, u"vector4d"_s, attachedTypeName,
+ u"Rectangle"_s, u"onCompleted"_s, forStatementCompletion
+ };
+
+ QTest::newRow("qualifiedTypeCompletionWithoutQualifier")
+ << testFile("completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml")
+ << 9 << 5
+ << ExpectedCompletions({
+ { u"T.Button"_s, CompletionItemKind::Constructor },
+ { u"Button"_s, CompletionItemKind::Constructor },
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ })
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s, u"bad"_s, u"onCompleted"_s };
+
+ QTest::newRow("qualifiedTypeCompletionWithoutQualifier2")
+ << testFile("completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml")
+ << 10 << 19
+ << ExpectedCompletions({
+ { u"T.Button"_s, CompletionItemKind::Class },
+ { u"Button"_s, CompletionItemKind::Class },
+ { u"Rectangle"_s, CompletionItemKind::Class },
+ })
+ << QStringList{ u"QtQuick"_s, u"bad"_s, u"onCompleted"_s };
+
+ QTest::newRow("qualifiedTypeCompletionWithQualifier")
+ << testFile("completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml")
+ << 9 << 7
+ << ExpectedCompletions({
+ { u"Button"_s, CompletionItemKind::Constructor },
+ })
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s, attachedTypeName, u"Rectangle"_s,
+ u"bad"_s, u"onCompleted"_s, u"T.Button"_s };
+
+ QTest::newRow("qualifiedTypeCompletionWithQualifier2")
+ << testFile("completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml")
+ << 10 << 21
+ << ExpectedCompletions({
+ { u"Button"_s, CompletionItemKind::Class },
+ })
+ << QStringList{ u"QtQuick"_s, attachedTypeName, u"Rectangle"_s,
+ u"bad"_s, u"onCompleted"_s, u"T.Button"_s };
+
+ QTest::newRow("parenthesizedExpression")
+ << testFile("completions/parenthesizedExpression.qml") << 8 << 10
+ << ExpectedCompletions({
+ { u"x"_s, CompletionItemKind::Variable },
+ })
+ << QStringList{ u"QtQuick"_s, u"Rectangle"_s, forStatementCompletion };
+
+ QTest::newRow("behindParenthesizedExpression")
+ << testFile("completions/parenthesizedExpression.qml") << 8 << 16
+ << ExpectedCompletions({})
+ << QStringList{ u"QtQuick"_s, attachedTypeName, u"Rectangle"_s, forStatementCompletion,
+ u"x"_s };
+
+ QTest::newRow("assumeBoundComponentsIdFromParent")
+ << testFile("completions/boundComponents.qml") << 14 << 33
+ << ExpectedCompletions{ { u"rootId"_s, CompletionItemKind::Value } }
+ << QStringList{ u"inRoot"_s };
+
+ QTest::newRow("assumeBoundComponentsPropertyFromParent")
+ << testFile("completions/boundComponents.qml") << 14 << 40
+ << ExpectedCompletions{ { u"inRoot"_s, CompletionItemKind::Property } }
+ << QStringList{ u"root"_s };
+}
+
+void tst_qmlls_utils::completions()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(ExpectedCompletions, expected);
+ QFETCH(QStringList, notExpected);
+
+ auto [env, file] = createEnvironmentAndLoadFile(filePath);
+
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+
+ QEXPECT_FAIL("binaryExpressionMissingRHSWithSemicolon",
+ "Current parser cannot recover from this error yet!", Abort);
+ QEXPECT_FAIL("binaryExpressionMissingRHSWithStatement",
+ "Current parser cannot recover from this error yet!", Abort);
+ QCOMPARE(locations.size(), 1);
+
+ QString code;
+ {
+ QFile file(filePath);
+ QVERIFY(file.open(QIODeviceBase::ReadOnly));
+ code = QString::fromUtf8(file.readAll());
+ }
+
+ qsizetype pos = QQmlLSUtils::textOffsetFrom(code, line - 1, character - 1);
+ CompletionContextStrings ctxt{ code, pos };
+ QQmlLSCompletion completionEngine(m_pluginLoader);
+ QList<CompletionItem> completions =
+ completionEngine.completions(locations.front().domItem, ctxt);
+
+ if (expected.isEmpty()) {
+ if constexpr (enable_debug_output) {
+ if (!completions.isEmpty()) {
+ QStringList unexpected;
+ for (const auto &current : completions) {
+ unexpected << current.label;
+ }
+ qDebug() << "Received unexpected completions:" << unexpected.join(u", ");
+ }
+ }
+ QEXPECT_FAIL("singleton", "completion not implemented yet!", Abort);
+ QVERIFY(completions.isEmpty());
+ return;
+ }
+
+ QSet<QString> labels;
+ QStringList sortedLabels;
+ QDuplicateTracker<QByteArray> modulesTracker;
+ QDuplicateTracker<QByteArray> keywordsTracker;
+ QDuplicateTracker<QByteArray> classesTracker;
+ QDuplicateTracker<QByteArray> fieldsTracker;
+ QDuplicateTracker<QByteArray> propertiesTracker;
+ QDuplicateTracker<QByteArray> snippetTracker;
+
+ // avoid QEXPECT_FAIL tests to XPASS when completion order changes
+ std::sort(completions.begin(), completions.end(),
+ [](const CompletionItem&a, const CompletionItem&b) {return a.label < b.label;});
+
+ for (const CompletionItem &c : completions) {
+ // explicitly forbid marker structs created by QQmlJSImporter
+ QVERIFY(!c.label.contains("$internal$."));
+ QVERIFY(!c.label.contains("$module$."));
+ QVERIFY(!c.label.contains("$anonymous$."));
+
+ if (c.kind->toInt() == int(CompletionItemKind::Module)) {
+ QVERIFY2(!modulesTracker.hasSeen(c.label), "Duplicate module: " + c.label);
+ } else if (c.kind->toInt() == int(CompletionItemKind::Keyword)) {
+ QVERIFY2(!keywordsTracker.hasSeen(c.label), "Duplicate keyword: " + c.label);
+ } else if (c.kind->toInt() == int(CompletionItemKind::Class)) {
+ QVERIFY2(!classesTracker.hasSeen(c.label), "Duplicate class: " + c.label);
+ } else if (c.kind->toInt() == int(CompletionItemKind::Field)) {
+ QVERIFY2(!fieldsTracker.hasSeen(c.label), "Duplicate field: " + c.label);
+ } else if (c.kind->toInt() == int(CompletionItemKind::Snippet)) {
+ QVERIFY2(!snippetTracker.hasSeen(c.label), "Duplicate field: " + c.label);
+ if (c.insertText->contains('\n') || c.insertText->contains('\r')) {
+ QCOMPARE(c.insertTextMode, InsertTextMode::AdjustIndentation);
+ }
+ } else if (c.kind->toInt() == int(CompletionItemKind::Property)) {
+ QVERIFY2(!propertiesTracker.hasSeen(c.label), "Duplicate property: " + c.label);
+ QCOMPARE(c.insertText, std::nullopt);
+ }
+ labels << c.label;
+ sortedLabels << c.label;
+ }
+ const QString labelsForPrinting = sortedLabels.join(u", "_s);
+
+ for (const ExpectedCompletion &exp : expected) {
+ QEXPECT_FAIL("letStatementAfterEqual", "Completion not implemented yet!", Abort);
+
+ QVERIFY2(labels.contains(exp.label),
+ u"no %1 in %2"_s.arg(exp.label, labelsForPrinting).toUtf8());
+ if (labels.contains(exp.label)) {
+
+ bool foundEntry = false;
+ bool hasCorrectKind = false;
+ CompletionItemKind foundKind;
+ for (const CompletionItem &c : completions) {
+ if (c.label == exp.label) {
+ foundKind = static_cast<CompletionItemKind>(c.kind->toInt());
+ foundEntry = true;
+ if (foundKind == exp.kind) {
+ hasCorrectKind = true;
+ if (!exp.snippet.isEmpty()) {
+ QCOMPARE(QString::fromUtf8(c.insertText.value_or(QByteArray())),
+ exp.snippet);
+ }
+ break;
+ }
+ }
+ }
+
+ // Ignore QVERIFY for those completions not in the expected list.
+ if (!foundEntry)
+ continue;
+
+ QVERIFY2(hasCorrectKind,
+ qPrintable(QString::fromLatin1("Completion item '%1' has wrong kind '%2'")
+ .arg(exp.label)
+ .arg(QMetaEnum::fromType<CompletionItemKind>().valueToKey(
+ int(foundKind)))));
+ }
+ }
+ for (const QString &nexp : notExpected) {
+ QEXPECT_FAIL("ignoreNonRelatedTypesForPropertyDefinitionBinding",
+ "Filtering by Type not implemented yet, for example to avoid proposing "
+ "binding Items to Rectangle properties.",
+ Abort);
+ QVERIFY2(!labels.contains(nexp), u"found unexpected completion %1"_s.arg(nexp).toUtf8());
+ }
+}
+
+void tst_qmlls_utils::cmakeBuildCommand()
+{
+ const QString path = u"helloWorldPath"_s;
+ const QPair<QString, QStringList> expected{
+ u"cmake"_s, { u"--build"_s, path, u"-t"_s, u"all_qmltyperegistrations"_s }
+ };
+ QCOMPARE(QQmlLSUtils::cmakeBuildCommand(path), expected);
+}
+
+QTEST_MAIN(tst_qmlls_utils)
diff --git a/tests/auto/qmlls/utils/tst_qmlls_utils.h b/tests/auto/qmlls/utils/tst_qmlls_utils.h
new file mode 100644
index 0000000000..2f1ea19a2c
--- /dev/null
+++ b/tests/auto/qmlls/utils/tst_qmlls_utils.h
@@ -0,0 +1,95 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QMLLS_UTILS_H
+#define TST_QMLLS_UTILS_H
+
+#include <QtJsonRpc/private/qjsonrpcprotocol_p.h>
+#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtCore/private/qfactoryloader_p.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qprocess.h>
+#include <QtCore/qlibraryinfo.h>
+
+#include <QtTest/qtest.h>
+
+#include <QtQmlLS/private/qqmllsutils_p.h>
+#include <QtQmlLS/private/qqmllscompletion_p.h>
+
+#include <iostream>
+
+using namespace Qt::StringLiterals;
+
+class tst_qmlls_utils : public QQmlDataTest
+{
+ Q_OBJECT
+
+ struct ExpectedCompletion
+ {
+ QString label;
+ QLspSpecification::CompletionItemKind kind;
+ QString snippet = {};
+ };
+ using ExpectedCompletions = QList<ExpectedCompletion>;
+
+ using ExpectedDocumentation = std::tuple<QString, QString, QString>;
+ using ExpectedDocumentations = QList<ExpectedDocumentation>;
+
+public:
+ tst_qmlls_utils()
+ : QQmlDataTest(QT_QMLLS_UTILS_DATADIR),
+ m_pluginLoader(QmlLSPluginInterface_iid, u"/qmlls"_s)
+ {
+ }
+
+private slots:
+ void textOffsetRowColumnConversions_data();
+ void textOffsetRowColumnConversions();
+
+ void findItemFromLocation_data();
+ void findItemFromLocation();
+
+ void findTypeDefinitionFromLocation_data();
+ void findTypeDefinitionFromLocation();
+
+ void findDefinitionFromLocation_data();
+ void findDefinitionFromLocation();
+
+ void findLocationOfItem_data();
+ void findLocationOfItem();
+
+ void findBaseObject();
+ void findBaseObject_data();
+
+ void findUsages();
+ void findUsages_data();
+
+ void renameUsages();
+ void renameUsages_data();
+
+ void resolveExpressionType();
+ void resolveExpressionType_data();
+
+ void isValidEcmaScriptIdentifier();
+ void isValidEcmaScriptIdentifier_data();
+
+ void completions_data();
+ void completions();
+
+ void cmakeBuildCommand();
+
+private:
+ using EnvironmentAndFile = std::tuple<QQmlJS::Dom::DomItem, QQmlJS::Dom::DomItem>;
+
+ EnvironmentAndFile createEnvironmentAndLoadFile(const QString &file);
+
+ using CacheKey = QString;
+ // avoid loading the same file over and over when running all the tests
+ QHash<CacheKey, std::shared_ptr<QQmlJS::Dom::DomEnvironment>> cache;
+ QFactoryLoader m_pluginLoader;
+
+};
+
+#endif // TST_QMLLS_UTILS_H