aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/android/androidconfigurations.cpp79
-rw-r--r--src/plugins/android/androidconfigurations.h10
-rw-r--r--src/plugins/android/androiddeployqtstep.cpp3
-rw-r--r--src/plugins/android/androiddevice.cpp12
-rw-r--r--src/plugins/android/androidqmlpreviewworker.cpp4
-rw-r--r--src/plugins/android/androidrunconfiguration.cpp9
-rw-r--r--src/plugins/android/androidrunnerworker.cpp2
-rw-r--r--src/plugins/android/androidsdkmanager.cpp26
-rw-r--r--src/plugins/android/androidsdkmanagerwidget.cpp11
-rw-r--r--src/plugins/android/androidsdkmodel.cpp47
-rw-r--r--src/plugins/android/androidsdkmodel.h3
-rw-r--r--src/plugins/android/avdmanageroutputparser.cpp3
-rw-r--r--src/plugins/clangcodemodel/clangcodemodelplugin.cpp2
-rw-r--r--src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp37
-rw-r--r--src/plugins/clangcodemodel/clangdclient.cpp277
-rw-r--r--src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp24
-rw-r--r--src/plugins/clangcodemodel/clangdiagnostictooltipwidget.h3
-rw-r--r--src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp3
-rw-r--r--src/plugins/clangcodemodel/clangmodelmanagersupport.cpp8
-rw-r--r--src/plugins/clangcodemodel/clangtextmark.cpp4
-rw-r--r--src/plugins/clangcodemodel/clangutils.cpp13
-rw-r--r--src/plugins/clangcodemodel/clangutils.h4
-rw-r--r--src/plugins/clangformat/clangformatfile.cpp2
-rw-r--r--src/plugins/clangtools/clangtoolsdiagnostic.cpp2
-rw-r--r--src/plugins/clangtools/clangtoolsdiagnostic.h4
-rw-r--r--src/plugins/clearcase/clearcaseplugin.cpp8
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp12
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeconfigitem.h3
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprocess.cpp12
-rw-r--r--src/plugins/cmakeprojectmanager/fileapidataextractor.cpp75
-rw-r--r--src/plugins/cmakeprojectmanager/fileapidataextractor.h2
-rw-r--r--src/plugins/coreplugin/dialogs/codecselector.cpp2
-rw-r--r--src/plugins/coreplugin/documentmanager.cpp5
-rw-r--r--src/plugins/coreplugin/editormanager/editormanager.cpp2
-rw-r--r--src/plugins/coreplugin/locator/ilocatorfilter.h9
-rw-r--r--src/plugins/coreplugin/locator/locatorsearchutils.cpp25
-rw-r--r--src/plugins/coreplugin/manhattanstyle.cpp13
-rw-r--r--src/plugins/coreplugin/textdocument.cpp8
-rw-r--r--src/plugins/coreplugin/textdocument.h1
-rw-r--r--src/plugins/cppcheck/cppcheckdiagnostic.cpp2
-rw-r--r--src/plugins/cppcheck/cppcheckdiagnostic.h3
-rw-r--r--src/plugins/cppeditor/compileroptionsbuilder.cpp35
-rw-r--r--src/plugins/cppeditor/compileroptionsbuilder.h1
-rw-r--r--src/plugins/cppeditor/cppcodemodelsettings.cpp3
-rw-r--r--src/plugins/cppeditor/cppcodemodelsettingspage.cpp52
-rw-r--r--src/plugins/cppeditor/cppcompletionassist.cpp34
-rw-r--r--src/plugins/cppeditor/cppfollowsymbolundercursor.cpp17
-rw-r--r--src/plugins/cppeditor/cppmodelmanager.cpp4
-rw-r--r--src/plugins/cppeditor/cppprojectfile.cpp14
-rw-r--r--src/plugins/cppeditor/cppprojectfile.h1
-rw-r--r--src/plugins/cppeditor/cppprojectupdater.h2
-rw-r--r--src/plugins/cppeditor/cppquickfixes.cpp2
-rw-r--r--src/plugins/cppeditor/cpptoolsreuse.cpp40
-rw-r--r--src/plugins/cppeditor/cpptoolsreuse.h6
-rw-r--r--src/plugins/cppeditor/semantichighlighter.cpp1
-rw-r--r--src/plugins/debugger/debuggerengine.cpp4
-rw-r--r--src/plugins/debugger/debuggerengine.h2
-rw-r--r--src/plugins/debugger/debuggeritem.cpp2
-rw-r--r--src/plugins/debugger/debuggerruncontrol.cpp2
-rw-r--r--src/plugins/debugger/gdb/gdbengine.cpp8
-rw-r--r--src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp8
-rw-r--r--src/plugins/debugger/watchhandler.cpp2
-rw-r--r--src/plugins/designer/codemodelhelpers.cpp2
-rw-r--r--src/plugins/designer/formwindowfile.cpp5
-rw-r--r--src/plugins/designer/formwindowfile.h1
-rw-r--r--src/plugins/docker/dockerdevice.cpp24
-rw-r--r--src/plugins/docker/dockerdevice.h1
-rw-r--r--src/plugins/fakevim/fakevimhandler.cpp15
-rw-r--r--src/plugins/fakevim/fakevimplugin.cpp13
-rw-r--r--src/plugins/ios/iosprobe.cpp4
-rw-r--r--src/plugins/ios/iosprobe.h7
-rw-r--r--src/plugins/languageclient/client.cpp9
-rw-r--r--src/plugins/languageclient/client.h3
-rw-r--r--src/plugins/languageclient/languageclientcompletionassist.cpp2
-rw-r--r--src/plugins/modeleditor/modelindexer.cpp5
-rw-r--r--src/plugins/modeleditor/modelindexer.h4
-rw-r--r--src/plugins/perforce/perforceplugin.cpp8
-rw-r--r--src/plugins/projectexplorer/buildtargetinfo.h3
-rw-r--r--src/plugins/projectexplorer/deployablefile.cpp2
-rw-r--r--src/plugins/projectexplorer/deployablefile.h3
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicemanager.cpp12
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevice.cpp5
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevice.h1
-rw-r--r--src/plugins/projectexplorer/expanddata.cpp2
-rw-r--r--src/plugins/projectexplorer/expanddata.h4
-rw-r--r--src/plugins/projectexplorer/gccparser.cpp8
-rw-r--r--src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp162
-rw-r--r--src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h6
-rw-r--r--src/plugins/projectexplorer/parseissuesdialog.cpp32
-rw-r--r--src/plugins/projectexplorer/project.cpp10
-rw-r--r--src/plugins/projectexplorer/projectexplorer.cpp2
-rw-r--r--src/plugins/projectexplorer/projecttree.cpp4
-rw-r--r--src/plugins/projectexplorer/projecttree.h9
-rw-r--r--src/plugins/projectexplorer/runconfigurationaspects.cpp4
-rw-r--r--src/plugins/projectexplorer/runcontrol.cpp7
-rw-r--r--src/plugins/projectexplorer/task.cpp2
-rw-r--r--src/plugins/projectexplorer/task.h11
-rw-r--r--src/plugins/projectexplorer/treescanner.cpp2
-rw-r--r--src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp4
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeparsernodes.h5
-rw-r--r--src/plugins/qmldesigner/CMakeLists.txt2
-rw-r--r--src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp9
-rw-r--r--src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.h3
-rw-r--r--src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp7
-rw-r--r--src/plugins/qmldesigner/components/bindingeditor/actioneditor.h2
-rw-r--r--src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp49
-rw-r--r--src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h10
-rw-r--r--src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp22
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp4
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanager.h3
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp41
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h11
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp21
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorview.cpp57
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorview.h2
-rw-r--r--src/plugins/qmldesigner/components/integration/designdocument.cpp5
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp5
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp52
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h9
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp1
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp52
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h23
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp169
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h34
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp8
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp83
-rw-r--r--src/plugins/qmldesigner/designercore/include/import.h4
-rw-r--r--src/plugins/qmldesigner/designercore/include/projectstorageids.h2
-rw-r--r--src/plugins/qmldesigner/designercore/include/rewriterview.h2
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp3
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp2
-rw-r--r--src/plugins/qmldesigner/designercore/model/import.cpp2
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp2
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp2
-rw-r--r--src/plugins/qmldesigner/designercore/model/rewriterview.cpp33
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h873
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h6
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h11
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h166
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp86
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h24
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp197
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h63
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp307
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h63
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h2
-rw-r--r--src/plugins/qmldesigner/generatecmakelists.cpp272
-rw-r--r--src/plugins/qmldesigner/generatecmakelists.h45
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.cpp3
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.pri2
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.qbs2
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/component-icon.pngbin0 -> 626 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/component-icon16.pngbin0 -> 438 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/component-icon@2x.pngbin0 -> 1107 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/loader-icon.pngbin0 -> 321 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/loader-icon16.pngbin0 -> 222 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/loader-icon@2x.pngbin0 -> 483 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/repeater-icon.pngbin0 -> 191 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/repeater-icon16.pngbin0 -> 187 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/repeater-icon@2x.pngbin0 -> 196 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc9
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/quick.metainfo12
-rw-r--r--src/plugins/qmlpreview/qmldebugtranslationclient.cpp8
-rw-r--r--src/plugins/qmlpreview/qmldebugtranslationclient.h2
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertraceclient.cpp2
-rw-r--r--src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp8
-rw-r--r--src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp12
-rw-r--r--src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h8
-rw-r--r--src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp3
-rw-r--r--src/plugins/qmlprojectmanager/qmlproject.cpp18
-rw-r--r--src/plugins/qmlprojectmanager/qmlproject.h2
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectconstants.h2
-rw-r--r--src/plugins/remotelinux/deploymenttimeinfo.cpp2
-rw-r--r--src/plugins/resourceeditor/resourceeditorplugin.cpp45
-rw-r--r--src/plugins/resourceeditor/resourcenode.cpp16
-rw-r--r--src/plugins/resourceeditor/resourcenode.h1
-rw-r--r--src/plugins/scxmleditor/scxmleditordocument.cpp5
-rw-r--r--src/plugins/scxmleditor/scxmleditordocument.h1
-rw-r--r--src/plugins/studiowelcome/CMakeLists.txt1
-rw-r--r--src/plugins/studiowelcome/studiowelcomeplugin.cpp4
-rw-r--r--src/plugins/texteditor/codeassist/genericproposalmodel.cpp2
-rw-r--r--src/plugins/texteditor/fontsettings.cpp4
-rw-r--r--src/plugins/texteditor/snippets/snippetoverlay.cpp8
-rw-r--r--src/plugins/texteditor/texteditor.cpp4
-rw-r--r--src/plugins/texteditor/texteditor.h3
-rw-r--r--src/plugins/vcsbase/vcsbaseeditor.cpp27
-rw-r--r--src/plugins/webassembly/webassemblyrunconfiguration.cpp39
189 files changed, 3206 insertions, 1321 deletions
diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp
index c769a744d2..7bc1c6310d 100644
--- a/src/plugins/android/androidconfigurations.cpp
+++ b/src/plugins/android/androidconfigurations.cpp
@@ -418,6 +418,28 @@ QString AndroidConfig::apiLevelNameFor(const SdkPlatform *platform)
QString("android-%1").arg(platform->apiLevel()) : "";
}
+int AndroidConfig::platformNameToApiLevel(const QString &platformName)
+{
+ int apiLevel = -1;
+ static const QRegularExpression re("(android-)(?<apiLevel>[0-9A-Z]{1,})",
+ QRegularExpression::CaseInsensitiveOption);
+ QRegularExpressionMatch match = re.match(platformName);
+ if (match.hasMatch()) {
+ QString apiLevelStr = match.captured("apiLevel");
+ bool isUInt;
+ apiLevel = apiLevelStr.toUInt(&isUInt);
+ if (!isUInt) {
+ if (apiLevelStr == 'Q')
+ apiLevel = 29;
+ else if (apiLevelStr == 'R')
+ apiLevel = 30;
+ else if (apiLevelStr == 'S')
+ apiLevel = 31;
+ }
+ }
+ return apiLevel;
+}
+
bool AndroidConfig::isCmdlineSdkToolsInstalled() const
{
QString toolPath("cmdline-tools/latest/bin/sdkmanager");
@@ -563,15 +585,10 @@ FilePath AndroidConfig::keytoolPath() const
QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(QString *error) const
{
- return connectedDevices(adbToolPath(), error);
-}
-
-QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(const FilePath &adbToolPath, QString *error)
-{
QVector<AndroidDeviceInfo> devices;
QtcProcess adbProc;
adbProc.setTimeoutS(30);
- CommandLine cmd{adbToolPath, {"devices"}};
+ CommandLine cmd{adbToolPath(), {"devices"}};
adbProc.setCommand(cmd);
adbProc.runBlocking();
if (adbProc.result() != QtcProcess::FinishedWithSuccess) {
@@ -597,8 +614,8 @@ QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(const FilePath &adbTo
AndroidDeviceInfo dev;
dev.serialNumber = serialNo;
dev.type = serialNo.startsWith(QLatin1String("emulator")) ? AndroidDeviceInfo::Emulator : AndroidDeviceInfo::Hardware;
- dev.sdk = getSDKVersion(adbToolPath, dev.serialNumber);
- dev.cpuAbi = getAbis(adbToolPath, dev.serialNumber);
+ dev.sdk = getSDKVersion(dev.serialNumber);
+ dev.cpuAbi = getAbis(dev.serialNumber);
if (deviceType == QLatin1String("unauthorized"))
dev.state = AndroidDeviceInfo::UnAuthorizedState;
else if (deviceType == QLatin1String("offline"))
@@ -633,10 +650,11 @@ bool AndroidConfig::isConnected(const QString &serialNumber) const
return false;
}
-QString AndroidConfig::getDeviceProperty(const FilePath &adbToolPath, const QString &device, const QString &property)
+QString AndroidConfig::getDeviceProperty(const QString &device, const QString &property)
{
// workaround for '????????????' serial numbers
- CommandLine cmd(adbToolPath, AndroidDeviceInfo::adbSelector(device));
+ CommandLine cmd(AndroidConfigurations::currentConfig().adbToolPath(),
+ AndroidDeviceInfo::adbSelector(device));
cmd.addArgs({"shell", "getprop", property});
QtcProcess adbProc;
@@ -649,9 +667,9 @@ QString AndroidConfig::getDeviceProperty(const FilePath &adbToolPath, const QStr
return adbProc.allOutput();
}
-int AndroidConfig::getSDKVersion(const FilePath &adbToolPath, const QString &device)
+int AndroidConfig::getSDKVersion(const QString &device)
{
- QString tmp = getDeviceProperty(adbToolPath, device, "ro.build.version.sdk");
+ QString tmp = getDeviceProperty(device, "ro.build.version.sdk");
if (tmp.isEmpty())
return -1;
return tmp.trimmed().toInt();
@@ -690,15 +708,37 @@ QString AndroidConfig::getAvdName(const QString &serialnumber)
return QString::fromLatin1(name).trimmed();
}
+static SdkToolResult emulatorNameAdbCommand(const QString &serialNumber)
+{
+ QStringList args = AndroidDeviceInfo::adbSelector(serialNumber);
+ args.append({"emu", "avd", "name"});
+ return AndroidManager::runAdbCommand(args);
+}
+
+QString AndroidConfig::getRunningAvdsSerialNumber(const QString &name) const
+{
+ for (const AndroidDeviceInfo &dev : connectedDevices()) {
+ if (!dev.serialNumber.startsWith("emulator"))
+ continue;
+ SdkToolResult result = emulatorNameAdbCommand(dev.serialNumber);
+ const QString stdOut = result.stdOut();
+ if (stdOut.isEmpty())
+ continue; // Not an avd
+ const QStringList outputLines = stdOut.split('\n');
+ if (outputLines.size() > 1 && outputLines.first() == name)
+ return dev.serialNumber;
+ }
+
+ return {};
+}
+
QStringList AndroidConfig::getRunningAvdsFromDevices(const QVector<AndroidDeviceInfo> &devs)
{
QStringList runningDevs;
for (const AndroidDeviceInfo &dev : devs) {
if (!dev.serialNumber.startsWith("emulator"))
continue;
- QStringList args = AndroidDeviceInfo::adbSelector(dev.serialNumber);
- args.append({"emu", "avd", "name"});
- SdkToolResult result = AndroidManager::runAdbCommand(args);
+ SdkToolResult result = emulatorNameAdbCommand(dev.serialNumber);
const QString stdOut = result.stdOut();
if (stdOut.isEmpty())
continue; // Not an avd
@@ -742,7 +782,7 @@ QString AndroidConfig::getProductModel(const QString &device) const
if (m_serialNumberToDeviceName.contains(device))
return m_serialNumberToDeviceName.value(device);
- QString model = getDeviceProperty(adbToolPath(), device, "ro.product.model").trimmed();
+ QString model = getDeviceProperty(device, "ro.product.model").trimmed();
if (model.isEmpty())
return device;
@@ -751,15 +791,16 @@ QString AndroidConfig::getProductModel(const QString &device) const
return model;
}
-QStringList AndroidConfig::getAbis(const FilePath &adbToolPath, const QString &device)
+QStringList AndroidConfig::getAbis(const QString &device)
{
+ const FilePath adbTool = AndroidConfigurations::currentConfig().adbToolPath();
QStringList result;
// First try via ro.product.cpu.abilist
QStringList arguments = AndroidDeviceInfo::adbSelector(device);
arguments << "shell" << "getprop" << "ro.product.cpu.abilist";
QtcProcess adbProc;
adbProc.setTimeoutS(10);
- adbProc.setCommand({adbToolPath, arguments});
+ adbProc.setCommand({adbTool, arguments});
adbProc.runBlocking();
if (adbProc.result() != QtcProcess::FinishedWithSuccess)
return result;
@@ -782,7 +823,7 @@ QStringList AndroidConfig::getAbis(const FilePath &adbToolPath, const QString &d
QtcProcess abiProc;
abiProc.setTimeoutS(10);
- abiProc.setCommand({adbToolPath, arguments});
+ abiProc.setCommand({adbTool, arguments});
abiProc.runBlocking();
if (abiProc.result() != QtcProcess::FinishedWithSuccess)
return result;
diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h
index 09f7ac85f1..2fc22ddbc2 100644
--- a/src/plugins/android/androidconfigurations.h
+++ b/src/plugins/android/androidconfigurations.h
@@ -91,6 +91,7 @@ public:
static QStringList apiLevelNamesFor(const SdkPlatformList &platforms);
static QString apiLevelNameFor(const SdkPlatform *platform);
+ static int platformNameToApiLevel(const QString &platformName);
Utils::FilePath sdkLocation() const;
void setSdkLocation(const Utils::FilePath &sdkLocation);
@@ -144,7 +145,6 @@ public:
Utils::FilePath keytoolPath() const;
QVector<AndroidDeviceInfo> connectedDevices(QString *error = nullptr) const;
- static QVector<AndroidDeviceInfo> connectedDevices(const Utils::FilePath &adbToolPath, QString *error = nullptr);
QString bestNdkPlatformMatch(int target, const QtSupport::BaseQtVersion *qtVersion) const;
@@ -171,16 +171,16 @@ public:
void setOpenSslLocation(const Utils::FilePath &openSslLocation);
static Utils::FilePath getJdkPath();
- static QStringList getAbis(const Utils::FilePath &adbToolPath, const QString &device);
+ static QStringList getAbis(const QString &device);
+ QString getRunningAvdsSerialNumber(const QString &name) const;
static QStringList getRunningAvdsFromDevices(const QVector<AndroidDeviceInfo> &devs);
private:
- static QString getDeviceProperty(const Utils::FilePath &adbToolPath,
- const QString &device, const QString &property);
+ static QString getDeviceProperty(const QString &device, const QString &property);
Utils::FilePath openJDKBinPath() const;
- static int getSDKVersion(const Utils::FilePath &adbToolPath, const QString &device);
+ static int getSDKVersion(const QString &device);
static QString getAvdName(const QString &serialnumber);
void parseDependenciesJson();
diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp
index 8c50c7a52c..0910f483d6 100644
--- a/src/plugins/android/androiddeployqtstep.cpp
+++ b/src/plugins/android/androiddeployqtstep.cpp
@@ -88,10 +88,11 @@ AndroidDeployQtStep::AndroidDeployQtStep(BuildStepList *parent, Utils::Id id)
: BuildStep(parent, id)
{
setImmutable(true);
+ setUserExpanded(true);
m_uninstallPreviousPackage = addAspect<BoolAspect>();
m_uninstallPreviousPackage->setSettingsKey(UninstallPreviousPackageKey);
- m_uninstallPreviousPackage->setLabel(tr("Uninstall the existing app first"),
+ m_uninstallPreviousPackage->setLabel(tr("Uninstall the existing app before deployment"),
BoolAspect::LabelPlacement::AtCheckBox);
m_uninstallPreviousPackage->setValue(false);
diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp
index bba148dda8..ea4ddbf8e1 100644
--- a/src/plugins/android/androiddevice.cpp
+++ b/src/plugins/android/androiddevice.cpp
@@ -151,7 +151,7 @@ AndroidDevice::AndroidDevice()
setOsType(Utils::OsTypeOtherUnix);
setDeviceState(DeviceConnected);
- addDeviceAction({tr("Refresh"), [](const IDevice::Ptr &device, QWidget *parent) {
+ addDeviceAction({tr("Refresh"), [](const IDevice::Ptr &, QWidget *) {
AndroidDeviceManager::instance()->updateDevicesListOnce();
}});
@@ -334,7 +334,11 @@ bool AndroidDevice::isValid() const
QString AndroidDevice::serialNumber() const
{
- return extraData(Constants::AndroidSerialNumber).toString();
+ const QString serialNumber = extraData(Constants::AndroidSerialNumber).toString();
+ if (machineType() == Hardware)
+ return serialNumber;
+
+ return AndroidConfigurations::currentConfig().getRunningAvdsSerialNumber(avdName());
}
QString AndroidDevice::avdName() const
@@ -614,8 +618,8 @@ AndroidDeviceManager *AndroidDeviceManager::instance()
AndroidDeviceManager::AndroidDeviceManager(QObject *parent)
: QObject(parent),
- m_androidConfig(AndroidConfigurations::currentConfig()),
- m_avdManager(m_androidConfig)
+ m_avdManager(m_androidConfig),
+ m_androidConfig(AndroidConfigurations::currentConfig())
{
connect(qApp, &QCoreApplication::aboutToQuit, this, [this]() {
m_devicesUpdaterTimer.stop();
diff --git a/src/plugins/android/androidqmlpreviewworker.cpp b/src/plugins/android/androidqmlpreviewworker.cpp
index 3c2adca572..bb95616eac 100644
--- a/src/plugins/android/androidqmlpreviewworker.cpp
+++ b/src/plugins/android/androidqmlpreviewworker.cpp
@@ -254,7 +254,7 @@ bool AndroidQmlPreviewWorker::ensureAvdIsRunning()
appendMessage(tr("Could not start AVD."), ErrorMessageFormat);
} else {
m_serialNumber = devInfoLocal.serialNumber;
- m_avdAbis = m_androidConfig.getAbis(m_androidConfig.adbToolPath(), m_serialNumber);
+ m_avdAbis = m_androidConfig.getAbis(m_serialNumber);
}
return !devInfoLocal.serialNumber.isEmpty();
} else {
@@ -262,7 +262,7 @@ bool AndroidQmlPreviewWorker::ensureAvdIsRunning()
}
return false;
}
- m_avdAbis = m_androidConfig.getAbis(m_androidConfig.adbToolPath(), m_serialNumber);
+ m_avdAbis = m_androidConfig.getAbis(m_serialNumber);
return true;
}
diff --git a/src/plugins/android/androidrunconfiguration.cpp b/src/plugins/android/androidrunconfiguration.cpp
index e1477c2615..b0b3c58f99 100644
--- a/src/plugins/android/androidrunconfiguration.cpp
+++ b/src/plugins/android/androidrunconfiguration.cpp
@@ -90,17 +90,10 @@ AndroidRunConfiguration::AndroidRunConfiguration(Target *target, Utils::Id id)
auto amStartArgsAspect = addAspect<StringAspect>();
amStartArgsAspect->setId(Constants::ANDROID_AM_START_ARGS);
amStartArgsAspect->setSettingsKey("Android.AmStartArgsKey");
- amStartArgsAspect->setLabelText(tr("Activity manager start options:"));
+ amStartArgsAspect->setLabelText(tr("Activity manager start arguments:"));
amStartArgsAspect->setDisplayStyle(StringAspect::LineEditDisplay);
amStartArgsAspect->setHistoryCompleter("Android.AmStartArgs.History");
- auto warning = addAspect<StringAspect>();
- warning->setDisplayStyle(StringAspect::LabelDisplay);
- warning->setLabelPixmap(Icons::WARNING.pixmap());
- warning->setValue(tr("If the \"am start\" options conflict, the application might not start.\n"
- "%1 uses: am start -n <package_name>/<Activity_name> [-D].")
- .arg(Core::Constants::IDE_DISPLAY_NAME));
-
auto preStartShellCmdAspect = addAspect<BaseStringListAspect>();
preStartShellCmdAspect->setDisplayStyle(StringAspect::TextEditDisplay);
preStartShellCmdAspect->setId(Constants::ANDROID_PRESTARTSHELLCMDLIST);
diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp
index 83c512a46a..759eba59e4 100644
--- a/src/plugins/android/androidrunnerworker.cpp
+++ b/src/plugins/android/androidrunnerworker.cpp
@@ -552,7 +552,6 @@ void AndroidRunnerWorker::asyncStartHelper()
runAdb(entry.split(' ', Qt::SkipEmptyParts));
QStringList args({"shell", "am", "start"});
- args << m_amStartExtraArgs;
args << "-n" << m_intentName;
if (m_useCppDebugger) {
args << "-D";
@@ -636,6 +635,7 @@ void AndroidRunnerWorker::asyncStartHelper()
}
}
+ args << m_amStartExtraArgs;
if (!m_extraAppParams.isEmpty()) {
QStringList appArgs =
diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp
index 943e371178..eaa6996014 100644
--- a/src/plugins/android/androidsdkmanager.cpp
+++ b/src/plugins/android/androidsdkmanager.cpp
@@ -69,28 +69,6 @@ Q_GLOBAL_STATIC_WITH_ARGS(QRegularExpression, assertionReg,
using namespace Utils;
using SdkCmdFutureInterface = QFutureInterface<AndroidSdkManager::OperationOutput>;
-int platformNameToApiLevel(const QString &platformName)
-{
- int apiLevel = -1;
- QRegularExpression re("(android-)(?<apiLevel>[0-9A-Z]{1,})",
- QRegularExpression::CaseInsensitiveOption);
- QRegularExpressionMatch match = re.match(platformName);
- if (match.hasMatch()) {
- QString apiLevelStr = match.captured("apiLevel");
- bool isUInt;
- apiLevel = apiLevelStr.toUInt(&isUInt);
- if (!isUInt) {
- if (apiLevelStr == 'Q')
- apiLevel = 29;
- else if (apiLevelStr == 'R')
- apiLevel = 30;
- else if (apiLevelStr == 'S')
- apiLevel = 31;
- }
- }
- return apiLevel;
-}
-
/*!
Parses the \a line for a [spaces]key[spaces]value[spaces] pattern and returns
\c true if \a key is found, false otherwise. Result is copied into \a value.
@@ -714,7 +692,7 @@ AndroidSdkPackage *SdkManagerOutputParser::parsePlatform(const QStringList &data
SdkPlatform *platform = nullptr;
GenericPackageData packageData;
if (parseAbstractData(packageData, data, 2, "Platform")) {
- int apiLevel = platformNameToApiLevel(packageData.headerParts.at(1));
+ const int apiLevel = AndroidConfig::platformNameToApiLevel(packageData.headerParts.at(1));
if (apiLevel == -1) {
qCDebug(sdkManagerLog) << "Platform: Cannot parse api level:"<< data;
return nullptr;
@@ -734,7 +712,7 @@ QPair<SystemImage *, int> SdkManagerOutputParser::parseSystemImage(const QString
QPair <SystemImage *, int> result(nullptr, -1);
GenericPackageData packageData;
if (parseAbstractData(packageData, data, 4, "System-image")) {
- int apiLevel = platformNameToApiLevel(packageData.headerParts.at(1));
+ const int apiLevel = AndroidConfig::platformNameToApiLevel(packageData.headerParts.at(1));
if (apiLevel == -1) {
qCDebug(sdkManagerLog) << "System-image: Cannot parse api level:"<< data;
return result;
diff --git a/src/plugins/android/androidsdkmanagerwidget.cpp b/src/plugins/android/androidsdkmanagerwidget.cpp
index c576571d32..0fe8b83d31 100644
--- a/src/plugins/android/androidsdkmanagerwidget.cpp
+++ b/src/plugins/android/androidsdkmanagerwidget.cpp
@@ -98,12 +98,10 @@ AndroidSdkManagerWidget::AndroidSdkManagerWidget(AndroidConfig &config,
auto proxyModel = new PackageFilterModel(m_sdkModel);
m_ui->packagesView->setModel(proxyModel);
+ m_ui->packagesView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
m_ui->packagesView->header()->setSectionResizeMode(AndroidSdkModel::packageNameColumn,
- QHeaderView::ResizeToContents);
- m_ui->packagesView->header()->setSectionResizeMode(AndroidSdkModel::apiLevelColumn,
- QHeaderView::ResizeToContents);
- m_ui->packagesView->header()->setSectionResizeMode(AndroidSdkModel::packageRevisionColumn,
- QHeaderView::ResizeToContents);
+ QHeaderView::Stretch);
+ m_ui->packagesView->header()->setStretchLastSection(false);
connect(m_ui->expandCheck, &QCheckBox::stateChanged, [this](int state) {
if (state == Qt::Checked)
m_ui->packagesView->expandAll();
@@ -133,7 +131,6 @@ AndroidSdkManagerWidget::AndroidSdkManagerWidget(AndroidConfig &config,
m_ui->searchField->setPlaceholderText("Filter");
connect(m_ui->searchField, &QLineEdit::textChanged, [this, proxyModel](const QString &text) {
- const bool isExpanded = m_ui->expandCheck->isChecked();
proxyModel->setAcceptedSearchPackage(text);
m_sdkModel->resetSelection();
// It is more convenient to expand the view with the results
@@ -513,7 +510,7 @@ bool PackageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour
}
}
- return showTopLevel || (packageState(srcIndex) & m_packageState) && packageFound(srcIndex);
+ return showTopLevel || ((packageState(srcIndex) & m_packageState) && packageFound(srcIndex));
}
OptionsDialog::OptionsDialog(AndroidSdkManager *sdkManager, const QStringList &args,
diff --git a/src/plugins/android/androidsdkmodel.cpp b/src/plugins/android/androidsdkmodel.cpp
index 2f83e86156..2d0e7af83e 100644
--- a/src/plugins/android/androidsdkmodel.cpp
+++ b/src/plugins/android/androidsdkmodel.cpp
@@ -40,7 +40,7 @@ static Q_LOGGING_CATEGORY(androidSdkModelLog, "qtc.android.sdkmodel", QtWarningM
namespace Android {
namespace Internal {
-const int packageColCount = 4;
+const int packageColCount = 3;
AndroidSdkModel::AndroidSdkModel(const AndroidConfig &config, AndroidSdkManager *sdkManager,
QObject *parent)
@@ -74,9 +74,6 @@ QVariant AndroidSdkModel::headerData(int section, Qt::Orientation orientation, i
case apiLevelColumn:
data = tr("API");
break;
- case operationColumn:
- data = tr("Operation");
- break;
default:
break;
}
@@ -162,7 +159,6 @@ QVariant AndroidSdkModel::data(const QModelIndex &index, int role) const
if (!index.isValid())
return QVariant();
-
if (!index.parent().isValid()) {
// Top level tools
if (index.row() == 0) {
@@ -202,25 +198,29 @@ QVariant AndroidSdkModel::data(const QModelIndex &index, int role) const
return p->revision().toString();
case apiLevelColumn:
return apiLevelStr;
- case operationColumn:
- if (p->type() == AndroidSdkPackage::SdkToolsPackage &&
- p->state() == AndroidSdkPackage::Installed) {
- return tr("Update Only");
- } else {
- return p->state() == AndroidSdkPackage::Installed ? tr("Uninstall") : tr("Install");
- }
default:
break;
}
}
- if (role == Qt::DecorationRole && index.column() == packageNameColumn) {
- return p->state() == AndroidSdkPackage::Installed ? Utils::Icons::OK.icon() :
- Utils::Icons::EMPTY16.icon();
+ if (index.column() == packageNameColumn) {
+ if (role == Qt::CheckStateRole) {
+ if (p->state() == AndroidSdkPackage::Installed)
+ return m_changeState.contains(p) ? Qt::Unchecked : Qt::Checked;
+ else
+ return m_changeState.contains(p) ? Qt::Checked : Qt::Unchecked;
+ }
+
+ if (role == Qt::FontRole) {
+ QFont font;
+ if (m_changeState.contains(p))
+ font.setBold(true);
+ return font;
+ }
}
- if (role == Qt::CheckStateRole && index.column() == operationColumn )
- return m_changeState.contains(p) ? Qt::Checked : Qt::Unchecked;
+ if (role == Qt::TextAlignmentRole && index.column() == packageRevisionColumn)
+ return Qt::AlignRight;
if (role == Qt::ToolTipRole)
return QString("%1 - (%2)").arg(p->descriptionText()).arg(p->sdkStylePath());
@@ -245,14 +245,14 @@ QHash<int, QByteArray> AndroidSdkModel::roleNames() const
Qt::ItemFlags AndroidSdkModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags f = QAbstractItemModel::flags(index);
- if (index.column() == operationColumn)
+ if (index.column() == packageNameColumn)
f |= Qt::ItemIsUserCheckable;
void *ip = index.internalPointer();
- if (ip && index.column() == operationColumn) {
+ if (ip && index.column() == packageNameColumn) {
auto package = static_cast<const AndroidSdkPackage *>(ip);
- if (package->state() == AndroidSdkPackage::Installed &&
- package->type() == AndroidSdkPackage::SdkToolsPackage) {
+ if (package->state() == AndroidSdkPackage::Installed
+ && package->type() == AndroidSdkPackage::SdkToolsPackage) {
f &= ~Qt::ItemIsEnabled;
}
}
@@ -264,11 +264,14 @@ bool AndroidSdkModel::setData(const QModelIndex &index, const QVariant &value, i
void *ip = index.internalPointer();
if (ip && role == Qt::CheckStateRole) {
auto package = static_cast<const AndroidSdkPackage *>(ip);
- if (value.toInt() == Qt::Checked) {
+ if (value.toInt() == Qt::Checked && package->state() != AndroidSdkPackage::Installed) {
m_changeState << package;
emit dataChanged(index, index, {Qt::CheckStateRole});
} else if (m_changeState.remove(package)) {
emit dataChanged(index, index, {Qt::CheckStateRole});
+ } else if (value.toInt() == Qt::Unchecked) {
+ m_changeState.insert(package);
+ emit dataChanged(index, index, {Qt::CheckStateRole});
}
return true;
}
diff --git a/src/plugins/android/androidsdkmodel.h b/src/plugins/android/androidsdkmodel.h
index af25edfbb8..4a8d363a13 100644
--- a/src/plugins/android/androidsdkmodel.h
+++ b/src/plugins/android/androidsdkmodel.h
@@ -42,8 +42,7 @@ public:
enum PackageColumn {
packageNameColumn = 0,
apiLevelColumn,
- packageRevisionColumn,
- operationColumn
+ packageRevisionColumn
};
enum ExtraRoles {
diff --git a/src/plugins/android/avdmanageroutputparser.cpp b/src/plugins/android/avdmanageroutputparser.cpp
index 0ca8a74e73..ac758f41c7 100644
--- a/src/plugins/android/avdmanageroutputparser.cpp
+++ b/src/plugins/android/avdmanageroutputparser.cpp
@@ -24,6 +24,7 @@
****************************************************************************/
#include "avdmanageroutputparser.h"
+#include "androidconfigurations.h"
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/algorithm.h>
@@ -97,7 +98,7 @@ static Utils::optional<AndroidDeviceInfo> parseAvd(const QStringList &deviceInfo
QSettings avdInfo(avdInfoFile.toString(), QSettings::IniFormat);
value = avdInfo.value(avdInfoTargetKey).toString();
if (!value.isEmpty())
- avd.sdk = value.section('-', -1).toInt();
+ avd.sdk = AndroidConfig::platformNameToApiLevel(value);
else
qCDebug(avdOutputParserLog)
<< "Avd Parsing: Cannot find sdk API:" << avdInfoFile.toString();
diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp
index a6ead8c3fe..3a13c4542d 100644
--- a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp
+++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp
@@ -82,7 +82,7 @@ void ClangCodeModelPlugin::generateCompilationDB()
QFuture<GenerateCompilationDbResult> task
= QtConcurrent::run(&Internal::generateCompilationDB, projectInfo,
- CompilationDbPurpose::Project,
+ projectInfo->buildRoot(), CompilationDbPurpose::Project,
warningsConfigForProject(target->project()),
optionsForProject(target->project()));
Core::ProgressManager::addTask(task, tr("Generating Compilation DB"), "generate compilation db");
diff --git a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp
index af964f5be9..1d924f8b2d 100644
--- a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp
+++ b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp
@@ -36,6 +36,7 @@
#include <cppeditor/cppdoxygen.h>
#include <cppeditor/cppmodelmanager.h>
+#include <cppeditor/cpptoolsreuse.h>
#include <cppeditor/editordocumenthandle.h>
#include <texteditor/codeassist/assistproposalitem.h>
@@ -44,10 +45,7 @@
#include <texteditor/codeassist/ifunctionhintproposalmodel.h>
#include <texteditor/texteditorsettings.h>
-#include <cplusplus/BackwardsScanner.h>
-#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/Icons.h>
-#include <cplusplus/SimpleLexer.h>
#include <clangsupport/filecontainer.h>
@@ -431,37 +429,8 @@ bool ClangCompletionAssistProcessor::accepts() const
if (pos - startOfName >= TextEditorSettings::completionSettings().m_characterThreshold) {
const QChar firstCharacter = m_interface->characterAt(startOfName);
if (firstCharacter.isLetter() || firstCharacter == QLatin1Char('_')) {
- // Finally check that we're not inside a comment or string (code copied from startOfOperator)
- QTextCursor tc(m_interface->textDocument());
- tc.setPosition(pos);
-
- SimpleLexer tokenize;
- LanguageFeatures lf = tokenize.languageFeatures();
- lf.qtMocRunEnabled = true;
- lf.objCEnabled = true;
- tokenize.setLanguageFeatures(lf);
- tokenize.setSkipComments(false);
- const Tokens &tokens = tokenize(tc.block().text(), BackwardsScanner::previousBlockState(tc.block()));
- const int tokenIdx = SimpleLexer::tokenBefore(tokens, qMax(0, tc.positionInBlock() - 1));
- const Token tk = (tokenIdx == -1) ? Token() : tokens.at(tokenIdx);
-
- if (!tk.isComment() && !tk.isLiteral()) {
- return true;
- } else if (tk.isLiteral()
- && tokens.size() == 3
- && tokens.at(0).kind() == T_POUND
- && tokens.at(1).kind() == T_IDENTIFIER) {
- const QString &line = tc.block().text();
- const Token &idToken = tokens.at(1);
- QStringView identifier = Utils::midView(line,
- idToken.utf16charsBegin(),
- idToken.utf16chars());
- if (identifier == QLatin1String("include")
- || identifier == QLatin1String("include_next")
- || (m_interface->objcEnabled() && identifier == QLatin1String("import"))) {
- return true;
- }
- }
+ return !CppEditor::isInCommentOrString(m_interface.data(),
+ m_interface->languageFeatures());
}
}
}
diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp
index 5b3e76bee8..2b6044f3ac 100644
--- a/src/plugins/clangcodemodel/clangdclient.cpp
+++ b/src/plugins/clangcodemodel/clangdclient.cpp
@@ -94,8 +94,22 @@ static Q_LOGGING_CATEGORY(clangdLogServer, "qtc.clangcodemodel.clangd.server", Q
static Q_LOGGING_CATEGORY(clangdLogAst, "qtc.clangcodemodel.clangd.ast", QtWarningMsg);
static Q_LOGGING_CATEGORY(clangdLogHighlight, "qtc.clangcodemodel.clangd.highlight", QtWarningMsg);
static Q_LOGGING_CATEGORY(clangdLogTiming, "qtc.clangcodemodel.clangd.timing", QtWarningMsg);
+static Q_LOGGING_CATEGORY(clangdLogCompletion, "qtc.clangcodemodel.clangd.completion",
+ QtWarningMsg);
static QString indexingToken() { return "backgroundIndexProgress"; }
+static QStringView subViewLen(const QString &s, qsizetype start, qsizetype length)
+{
+ if (start < 0 || length < 0 || start + length > s.length())
+ return {};
+ return QStringView(s).mid(start, length);
+}
+
+static QStringView subViewEnd(const QString &s, qsizetype start, qsizetype end)
+{
+ return subViewLen(s, start, end - start);
+}
+
class AstNode : public JsonObject
{
public:
@@ -277,6 +291,58 @@ public:
- openingQuoteOffset - 1);
}
+ enum class FileStatus { Ours, Foreign, Mixed, Unknown };
+ FileStatus fileStatus(const Utils::FilePath &thisFile) const
+ {
+ const Utils::optional<QString> arcanaString = arcana();
+ if (!arcanaString)
+ return FileStatus::Unknown;
+
+ // Example arcanas:
+ // "FunctionDecl 0x7fffb5d0dbd0 </tmp/test.cpp:1:1, line:5:1> line:1:6 func 'void ()'"
+ // "VarDecl 0x7fffb5d0dcf0 </tmp/test.cpp:2:5, /tmp/test.h:1:1> /tmp/test.cpp:2:10 b 'bool' cinit"
+ // The second one is for a particularly silly construction where the RHS of an
+ // initialization comes from an included header.
+ const int openPos = arcanaString->indexOf('<');
+ if (openPos == -1)
+ return FileStatus::Unknown;
+ const int closePos = arcanaString->indexOf('>', openPos + 1);
+ if (closePos == -1)
+ return FileStatus::Unknown;
+ bool hasOurs = false;
+ bool hasOther = false;
+ for (int startPos = openPos + 1; startPos < closePos;) {
+ int colon1Pos = arcanaString->indexOf(':', startPos);
+ if (colon1Pos == -1 || colon1Pos > closePos)
+ break;
+ if (Utils::HostOsInfo::isWindowsHost())
+ colon1Pos = arcanaString->indexOf(':', colon1Pos + 1);
+ if (colon1Pos == -1 || colon1Pos > closePos)
+ break;
+ const int colon2Pos = arcanaString->indexOf(':', colon1Pos + 2);
+ if (colon2Pos == -1 || colon2Pos > closePos)
+ break;
+ const int line = subViewEnd(*arcanaString, colon1Pos + 1, colon2Pos).toString().toInt(); // TODO: Drop toString() once we require >= Qt 5.15
+ if (line == 0)
+ break;
+ const QStringView fileOrLineString = subViewEnd(*arcanaString, startPos, colon1Pos);
+ if (fileOrLineString != QLatin1String("line")) {
+ if (Utils::FilePath::fromUserInput(fileOrLineString.toString()) == thisFile)
+ hasOurs = true;
+ else
+ hasOther = true;
+ }
+ const int commaPos = arcanaString->indexOf(',', colon2Pos + 2);
+ if (commaPos != -1)
+ startPos = commaPos + 2;
+ else
+ break;
+ }
+ if (hasOurs)
+ return hasOther ? FileStatus::Mixed : FileStatus::Ours;
+ return hasOther ? FileStatus::Foreign : FileStatus::Unknown;
+ }
+
// For debugging.
void print(int indent = 0) const
{
@@ -298,6 +364,7 @@ static QList<AstNode> getAstPath(const AstNode &root, const Range &range)
QList<AstNode> path;
QList<AstNode> queue{root};
bool isRoot = true;
+
while (!queue.isEmpty()) {
AstNode curNode = queue.takeFirst();
if (!isRoot && !curNode.hasRange())
@@ -309,13 +376,39 @@ static QList<AstNode> getAstPath(const AstNode &root, const Range &range)
const auto children = curNode.children();
if (!children)
break;
- queue = children.value();
+ if (curNode.kind() == "Function" || curNode.role() == "expression") {
+ // Functions and expressions can contain implicit nodes that make the list unsorted.
+ // They cannot be ignored, as we need to consider them in certain contexts.
+ // Therefore, the binary search cannot be used here.
+ queue = *children;
+ } else {
+ queue.clear();
+
+ // Class and struct nodes can contain implicit constructors, destructors and
+ // operators, which appear at the end of the list, but whose range is the same
+ // as the class name. Therefore, we must force them not to compare less to
+ // anything else.
+ static const auto leftOfRange = [](const AstNode &node, const Range &range) {
+ return node.range().isLeftOf(range) && !node.arcanaContains(" implicit ");
+ };
+
+ for (auto it = std::lower_bound(children->cbegin(), children->cend(), range,
+ leftOfRange);
+ it != children->cend() && !range.isLeftOf(it->range()); ++it) {
+ queue << *it;
+ }
+ }
}
isRoot = false;
}
return path;
}
+static QList<AstNode> getAstPath(const AstNode &root, const Position &pos)
+{
+ return getAstPath(root, Range(pos, pos));
+}
+
static Usage::Type getUsageType(const QList<AstNode> &path)
{
bool potentialWrite = false;
@@ -685,20 +778,12 @@ private:
case CustomAssistMode::Preprocessor:
static QIcon macroIcon = Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Macro);
for (const QString &completion
- : CppEditor::CppCompletionAssistProcessor::preprocessorCompletions())
+ : CppEditor::CppCompletionAssistProcessor::preprocessorCompletions()) {
completions << createItem(completion, macroIcon);
- const CppEditor::ProjectFile::Kind fileType
- = CppEditor::ProjectFile::classify(interface->filePath().toString());
- switch (fileType) {
- case CppEditor::ProjectFile::ObjCHeader:
- case CppEditor::ProjectFile::ObjCXXHeader:
- case CppEditor::ProjectFile::ObjCSource:
- case CppEditor::ProjectFile::ObjCXXSource:
- completions << createItem("import", macroIcon);
- break;
- default:
- break;
}
+ if (CppEditor::ProjectFile::isObjC(interface->filePath().toString()))
+ completions << createItem("import", macroIcon);
+ break;
}
GenericProposalModelPtr model(new GenericProposalModel);
model->loadContent(completions);
@@ -1030,12 +1115,14 @@ public:
ClangdCompletionAssistProvider(ClangdClient *client);
private:
- IAssistProcessor *createProcessor(const AssistInterface *assistInterface) const override;
+ IAssistProcessor *createProcessor(const AssistInterface *interface) const override;
int activationCharSequenceLength() const override { return 3; }
bool isActivationCharSequence(const QString &sequence) const override;
bool isContinuationChar(const QChar &c) const override;
+ bool isInCommentOrString(const AssistInterface *interface) const;
+
ClangdClient * const m_client;
};
@@ -1048,6 +1135,7 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir)
"text/x-c++hdr", "text/x-c++src", "text/x-objc++src", "text/x-objcsrc"};
setSupportedLanguage(langFilter);
setActivateDocumentAutomatically(true);
+ setLogTarget(LogTarget::Console);
setCompletionAssistProvider(new ClangdCompletionAssistProvider(this));
if (!project) {
QJsonObject initOptions;
@@ -1643,7 +1731,7 @@ void ClangdClient::findLocalUsages(TextDocument *document, const QTextCursor &cu
}
const Position linkPos(link.targetLine - 1, link.targetColumn);
- const QList<AstNode> astPath = getAstPath(ast, Range(linkPos, linkPos));
+ const QList<AstNode> astPath = getAstPath(ast, linkPos);
bool isVar = false;
for (auto it = astPath.rbegin(); it != astPath.rend(); ++it) {
if (it->role() == "declaration" && it->kind() == "Function") {
@@ -2128,7 +2216,8 @@ class ExtraHighlightingResultsCollector
{
public:
ExtraHighlightingResultsCollector(QFutureInterface<HighlightingResult> &future,
- HighlightingResults &results, const AstNode &ast,
+ HighlightingResults &results,
+ const Utils::FilePath &filePath, const AstNode &ast,
const QTextDocument *doc, const QString &docContent);
void collect();
@@ -2145,6 +2234,7 @@ private:
QFutureInterface<HighlightingResult> &m_future;
HighlightingResults &m_results;
+ const Utils::FilePath m_filePath;
const AstNode &m_ast;
const QTextDocument * const m_doc;
const QString &m_docContent;
@@ -2174,7 +2264,7 @@ static QList<BlockRange> cleanupDisabledCode(HighlightingResults &results, const
if (!wasIfdefedOut)
rangeStartPos = doc->findBlockByNumber(it->line - 1).position();
const int pos = Utils::Text::positionInText(doc, it->line, it->column);
- const QStringView content(QStringView(docContent).mid(pos, it->length).trimmed());
+ const QStringView content = subViewLen(docContent, pos, it->length).trimmed();
if (!content.startsWith(QLatin1String("#if"))
&& !content.startsWith(QLatin1String("#elif"))
&& !content.startsWith(QLatin1String("#else"))
@@ -2210,6 +2300,7 @@ static QList<BlockRange> cleanupDisabledCode(HighlightingResults &results, const
}
static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
+ const Utils::FilePath &filePath,
const QList<ExpandedSemanticToken> &tokens,
const QString &docContents, const AstNode &ast,
const QPointer<TextEditorWidget> &widget,
@@ -2222,13 +2313,17 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
}
const QTextDocument doc(docContents);
- const auto isOutputParameter = [&ast](const ExpandedSemanticToken &token) {
+ const auto tokenRange = [&doc](const ExpandedSemanticToken &token) {
+ const Position startPos(token.line - 1, token.column - 1);
+ const Position endPos = startPos.withOffset(token.length, &doc);
+ return Range(startPos, endPos);
+ };
+ const auto isOutputParameter = [&ast, &doc, &tokenRange](const ExpandedSemanticToken &token) {
if (token.modifiers.contains("usedAsMutableReference"))
return true;
if (token.type != "variable" && token.type != "property" && token.type != "parameter")
return false;
- const Position pos(token.line - 1, token.column - 1);
- const QList<AstNode> path = getAstPath(ast, Range(pos, pos));
+ const QList<AstNode> path = getAstPath(ast, tokenRange(token));
if (path.size() < 2)
return false;
if (path.last().hasConstType())
@@ -2249,7 +2344,8 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
};
const std::function<HighlightingResult(const ExpandedSemanticToken &)> toResult
- = [&ast, &isOutputParameter, &clangdVersion](const ExpandedSemanticToken &token) {
+ = [&ast, &isOutputParameter, &clangdVersion, &tokenRange]
+ (const ExpandedSemanticToken &token) {
TextStyles styles;
if (token.type == "variable") {
if (token.modifiers.contains("functionScope")) {
@@ -2263,8 +2359,7 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
} else if (token.type == "function" || token.type == "method") {
styles.mainStyle = token.modifiers.contains("virtual") ? C_VIRTUAL_METHOD : C_FUNCTION;
if (ast.isValid()) {
- const Position pos(token.line - 1, token.column - 1);
- const QList<AstNode> path = getAstPath(ast, Range(pos, pos));
+ const QList<AstNode> path = getAstPath(ast, tokenRange(token));
if (path.length() > 1) {
const AstNode declNode = path.at(path.length() - 2);
if (declNode.kind() == "Function" || declNode.kind() == "CXXMethod") {
@@ -2283,8 +2378,7 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
// clang hardly ever differentiates between constructors and the associated class,
// whereas we highlight constructors as functions.
if (ast.isValid()) {
- const Position pos(token.line - 1, token.column - 1);
- const QList<AstNode> path = getAstPath(ast, Range(pos, pos));
+ const QList<AstNode> path = getAstPath(ast, tokenRange(token));
if (!path.isEmpty()) {
if (path.last().kind() == "CXXConstructor") {
if (!path.last().arcanaContains("implicit"))
@@ -2338,7 +2432,7 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
if (widget && widget->textDocument()->document()->revision() == docRevision)
widget->setIfdefedOutBlocks(ifdefedOutBlocks);
}, Qt::QueuedConnection);
- ExtraHighlightingResultsCollector(future, results, ast, &doc, docContents).collect();
+ ExtraHighlightingResultsCollector(future, results, filePath, ast, &doc, docContents).collect();
if (!future.isCanceled()) {
qCDebug(clangdLog) << "reporting" << results.size() << "highlighting results";
future.reportResults(QVector<HighlightingResult>(results.cbegin(),
@@ -2378,10 +2472,12 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc,
IEditor * const editor = Utils::findOrDefault(EditorManager::visibleEditors(),
[doc](const IEditor *editor) { return editor->document() == doc; });
const auto editorWidget = TextEditorWidget::fromEditor(editor);
- const auto runner = [tokens, text = doc->document()->toPlainText(), ast,
+ const auto runner = [tokens, filePath = doc->filePath(),
+ text = doc->document()->toPlainText(), ast,
w = QPointer(editorWidget), rev = doc->document()->revision(),
clangdVersion = q->versionNumber()] {
- return Utils::runAsync(semanticHighlighter, tokens, text, ast, w, rev, clangdVersion);
+ return Utils::runAsync(semanticHighlighter, filePath, tokens, text, ast, w, rev,
+ clangdVersion);
};
if (isTesting) {
@@ -2543,21 +2639,28 @@ ClangdClient::ClangdCompletionAssistProvider::ClangdCompletionAssistProvider(Cla
{}
IAssistProcessor *ClangdClient::ClangdCompletionAssistProvider::createProcessor(
- const AssistInterface *assistInterface) const
-{
- ClangCompletionContextAnalyzer contextAnalyzer(assistInterface->textDocument(),
- assistInterface->position(), false, {});
+ const AssistInterface *interface) const
+{
+ qCDebug(clangdLogCompletion) << "completion processor requested for" << interface->filePath();
+ qCDebug(clangdLogCompletion) << "text before cursor is"
+ << interface->textAt(interface->position(), -10);
+ qCDebug(clangdLogCompletion) << "text after cursor is"
+ << interface->textAt(interface->position(), 10);
+ ClangCompletionContextAnalyzer contextAnalyzer(interface->textDocument(),
+ interface->position(), false, {});
contextAnalyzer.analyze();
switch (contextAnalyzer.completionAction()) {
case ClangCompletionContextAnalyzer::PassThroughToLibClangAfterLeftParen:
- qCDebug(clangdLog) << "completion changed to function hint";
+ qCDebug(clangdLogCompletion) << "creating function hint processor";
return new ClangdFunctionHintProcessor(m_client);
case ClangCompletionContextAnalyzer::CompleteDoxygenKeyword:
+ qCDebug(clangdLogCompletion) << "creating doxygen processor";
return new CustomAssistProcessor(m_client,
contextAnalyzer.positionForProposal(),
contextAnalyzer.completionOperator(),
CustomAssistMode::Doxygen);
case ClangCompletionContextAnalyzer::CompletePreprocessorDirective:
+ qCDebug(clangdLogCompletion) << "creating macro processor";
return new CustomAssistProcessor(m_client,
contextAnalyzer.positionForProposal(),
contextAnalyzer.completionOperator(),
@@ -2565,9 +2668,11 @@ IAssistProcessor *ClangdClient::ClangdCompletionAssistProvider::createProcessor(
default:
break;
}
- const QString snippetsGroup = contextAnalyzer.addSnippets()
+ const QString snippetsGroup = contextAnalyzer.addSnippets() && !isInCommentOrString(interface)
? CppEditor::Constants::CPP_SNIPPETS_GROUP_ID
: QString();
+ qCDebug(clangdLogCompletion) << "creating proper completion processor"
+ << (snippetsGroup.isEmpty() ? "without" : "with") << "snippets";
return new ClangdCompletionAssistProcessor(m_client, snippetsGroup);
}
@@ -2588,6 +2693,7 @@ bool ClangdClient::ClangdCompletionAssistProvider::isActivationCharSequence(cons
// contexts, such as '(', '<' or '/'.
switch (kind) {
case T_DOT: case T_COLON_COLON: case T_ARROW: case T_DOT_STAR: case T_ARROW_STAR: case T_POUND:
+ qCDebug(clangdLogCompletion) << "detected" << sequence << "as activation char sequence";
return true;
}
return false;
@@ -2598,6 +2704,14 @@ bool ClangdClient::ClangdCompletionAssistProvider::isContinuationChar(const QCha
return CppEditor::isValidIdentifierChar(c);
}
+bool ClangdClient::ClangdCompletionAssistProvider::isInCommentOrString(
+ const AssistInterface *interface) const
+{
+ LanguageFeatures features = LanguageFeatures::defaultFeatures();
+ features.objCEnabled = CppEditor::ProjectFile::isObjC(interface->filePath().toString());
+ return CppEditor::isInCommentOrString(interface, features);
+}
+
void ClangdCompletionItem::apply(TextDocumentManipulatorInterface &manipulator,
int /*basePosition*/) const
{
@@ -2607,35 +2721,27 @@ void ClangdCompletionItem::apply(TextDocumentManipulatorInterface &manipulator,
if (!edit)
return;
- const auto kind = static_cast<CompletionItemKind::Kind>(
- item.kind().value_or(CompletionItemKind::Text));
- if (kind != CompletionItemKind::Function && kind != CompletionItemKind::Method
- && kind != CompletionItemKind::Constructor) {
- applyTextEdit(manipulator, *edit, true);
- return;
- }
-
const QString rawInsertText = edit->newText();
const int firstParenOffset = rawInsertText.indexOf('(');
const int lastParenOffset = rawInsertText.lastIndexOf(')');
- if (firstParenOffset == -1 || lastParenOffset == -1) {
- applyTextEdit(manipulator, *edit, true);
- return;
- }
-
const QString detail = item.detail().value_or(QString());
const CompletionSettings &completionSettings = TextEditorSettings::completionSettings();
QString textToBeInserted = rawInsertText.left(firstParenOffset);
QString extraCharacters;
+ int extraLength = 0;
int cursorOffset = 0;
bool setAutoCompleteSkipPos = false;
- const QTextDocument * const doc = manipulator.textCursorAt(
- manipulator.currentPosition()).document();
+ int currentPos = manipulator.currentPosition();
+ const QTextDocument * const doc = manipulator.textCursorAt(currentPos).document();
const Range range = edit->range();
const int rangeStart = range.start().toPositionInDocument(doc);
- const int rangeLength = range.end().toPositionInDocument(doc) - rangeStart;
- if (completionSettings.m_autoInsertBrackets) {
+ const auto kind = static_cast<CompletionItemKind::Kind>(
+ item.kind().value_or(CompletionItemKind::Text));
+ const bool isFunctionLike = kind == CompletionItemKind::Function
+ || kind == CompletionItemKind::Method || kind == CompletionItemKind::Constructor
+ || (firstParenOffset != -1 && lastParenOffset != -1);
+ if (isFunctionLike && completionSettings.m_autoInsertBrackets) {
// If the user typed the opening parenthesis, they'll likely also type the closing one,
// in which case it would be annoying if we put the cursor after the already automatically
// inserted closing parenthesis.
@@ -2663,7 +2769,7 @@ void ClangdCompletionItem::apply(TextDocumentManipulatorInterface &manipulator,
// If the function doesn't return anything, automatically place the semicolon,
// unless we're doing a scope completion (then it might be function definition).
- const QChar characterAtCursor = manipulator.characterAt(manipulator.currentPosition());
+ const QChar characterAtCursor = manipulator.characterAt(currentPos);
bool endWithSemicolon = typedChar == ';';
const QChar semicolon = typedChar.isNull() ? QLatin1Char(';') : typedChar;
if (endWithSemicolon && characterAtCursor == semicolon) {
@@ -2679,7 +2785,7 @@ void ClangdCompletionItem::apply(TextDocumentManipulatorInterface &manipulator,
typedChar = {};
}
} else {
- const QChar lookAhead = manipulator.characterAt(manipulator.currentPosition() + 1);
+ const QChar lookAhead = manipulator.characterAt(currentPos + 1);
if (MatchingText::shouldInsertMatchingText(lookAhead)) {
extraCharacters += ')';
--cursorOffset;
@@ -2701,9 +2807,26 @@ void ClangdCompletionItem::apply(TextDocumentManipulatorInterface &manipulator,
--cursorOffset;
}
- textToBeInserted += extraCharacters;
+ // Avoid inserting characters that are already there
+ QTextCursor cursor = manipulator.textCursorAt(rangeStart);
+ cursor.movePosition(QTextCursor::EndOfWord);
+ const QString textAfterCursor = manipulator.textAt(currentPos, cursor.position() - currentPos);
+ if (textToBeInserted != textAfterCursor
+ && textToBeInserted.indexOf(textAfterCursor, currentPos - rangeStart) >= 0) {
+ currentPos = cursor.position();
+ }
+ for (int i = 0; i < extraCharacters.length(); ++i) {
+ const QChar a = extraCharacters.at(i);
+ const QChar b = manipulator.characterAt(currentPos + i);
+ if (a == b)
+ ++extraLength;
+ else
+ break;
+ }
- const bool isReplaced = manipulator.replace(rangeStart, rangeLength, textToBeInserted);
+ textToBeInserted += extraCharacters;
+ const int length = currentPos - rangeStart + extraLength;
+ const bool isReplaced = manipulator.replace(rangeStart, length, textToBeInserted);
manipulator.setCursorPosition(rangeStart + textToBeInserted.length());
if (isReplaced) {
if (cursorOffset)
@@ -2806,10 +2929,11 @@ MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc,
ExtraHighlightingResultsCollector::ExtraHighlightingResultsCollector(
QFutureInterface<HighlightingResult> &future, HighlightingResults &results,
- const AstNode &ast, const QTextDocument *doc, const QString &docContent)
- : m_future(future), m_results(results), m_ast(ast), m_doc(doc), m_docContent(docContent)
+ const Utils::FilePath &filePath, const AstNode &ast, const QTextDocument *doc,
+ const QString &docContent)
+ : m_future(future), m_results(results), m_filePath(filePath), m_ast(ast), m_doc(doc),
+ m_docContent(docContent)
{
-
}
void ExtraHighlightingResultsCollector::collect()
@@ -2881,7 +3005,7 @@ void ExtraHighlightingResultsCollector::insertAngleBracketInfo(int searchStart1,
int searchStart2, int searchEnd2)
{
const int openingAngleBracketPos = onlyIndexOf(
- QStringView(m_docContent).mid(searchStart1, searchEnd1 - searchStart1),
+ subViewEnd(m_docContent, searchStart1, searchEnd1),
QStringView(QStringLiteral("<")));
if (openingAngleBracketPos == -1)
return;
@@ -2891,7 +3015,7 @@ void ExtraHighlightingResultsCollector::insertAngleBracketInfo(int searchStart1,
if (searchStart2 >= searchEnd2)
return;
const int closingAngleBracketPos = onlyIndexOf(
- QStringView(m_docContent).mid(searchStart2, searchEnd2 - searchStart2),
+ subViewEnd(m_docContent, searchStart2, searchEnd2),
QStringView(QStringLiteral(">")));
if (closingAngleBracketPos == -1)
return;
@@ -2926,6 +3050,8 @@ void ExtraHighlightingResultsCollector::setResultPosFromRange(HighlightingResult
void ExtraHighlightingResultsCollector::collectFromNode(const AstNode &node)
{
+ if (node.kind() == "UserDefinedLiteral")
+ return;
if (node.kind().endsWith("Literal")) {
HighlightingResult result;
result.useTextSyles = true;
@@ -2969,16 +3095,14 @@ void ExtraHighlightingResultsCollector::collectFromNode(const AstNode &node)
// sub-expressions 2 and 3.
const int searchStartPosQuestionMark = posForNodeEnd(children.first());
const int searchEndPosQuestionMark = posForNodeStart(children.at(1));
- QStringView content = QStringView(m_docContent).mid(
- searchStartPosQuestionMark,
- searchEndPosQuestionMark - searchStartPosQuestionMark);
+ QStringView content = subViewEnd(m_docContent, searchStartPosQuestionMark,
+ searchEndPosQuestionMark);
const int questionMarkPos = onlyIndexOf(content, QStringView(QStringLiteral("?")));
if (questionMarkPos == -1)
return;
const int searchStartPosColon = posForNodeEnd(children.at(1));
const int searchEndPosColon = posForNodeStart(children.at(2));
- content = QStringView(m_docContent).mid(searchStartPosColon,
- searchEndPosColon - searchStartPosColon);
+ content = subViewEnd(m_docContent, searchStartPosColon, searchEndPosColon);
const int colonPos = onlyIndexOf(content, QStringView(QStringLiteral(":")));
if (colonPos == -1)
return;
@@ -3155,8 +3279,7 @@ void ExtraHighlightingResultsCollector::collectFromNode(const AstNode &node)
if (isDeclaration)
result.textStyles.mixinStyles.push_back(C_DECLARATION);
- const QStringView nodeText = QStringView(m_docContent)
- .mid(nodeStartPos, nodeEndPos - nodeStartPos);
+ const QStringView nodeText = subViewEnd(m_docContent, nodeStartPos, nodeEndPos);
if (isCallToNew || isCallToDelete) {
result.line = node.range().start().line() + 1;
@@ -3255,12 +3378,22 @@ void ExtraHighlightingResultsCollector::visitNode(const AstNode &node)
{
if (m_future.isCanceled())
return;
- collectFromNode(node);
- const auto children = node.children();
- if (!children)
+ switch (node.fileStatus(m_filePath)) {
+ case AstNode::FileStatus::Foreign:
return;
- for (const AstNode &childNode : *children)
- visitNode(childNode);
+ case AstNode::FileStatus::Ours:
+ case AstNode::FileStatus::Unknown:
+ collectFromNode(node);
+ [[fallthrough]];
+ case ClangCodeModel::Internal::AstNode::FileStatus::Mixed: {
+ const auto children = node.children();
+ if (!children)
+ return;
+ for (const AstNode &childNode : *children)
+ visitNode(childNode);
+ break;
+ }
+ }
}
} // namespace Internal
diff --git a/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp b/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp
index ffdd3c4396..195c4f4ead 100644
--- a/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp
+++ b/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp
@@ -104,9 +104,9 @@ public:
}
QWidget *createWidget(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
- const std::function<bool()> &canApplyFixIt)
+ const std::function<bool()> &canApplyFixIt, const QString &source)
{
- const QString text = htmlText(diagnostics);
+ const QString text = htmlText(diagnostics, source);
auto *label = new QLabel;
label->setTextFormat(Qt::RichText);
@@ -154,13 +154,20 @@ public:
return label;
}
- QString htmlText(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics)
+ QString htmlText(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
+ const QString &source)
{
// For debugging, add: style='border-width:1px;border-color:black'
QString text = "<table cellspacing='0' cellpadding='0' width='100%'>";
foreach (const ClangBackEnd::DiagnosticContainer &diagnostic, diagnostics)
text.append(tableRows(diagnostic));
+ if (!source.isEmpty()) {
+ text.append(QString::fromUtf8("<tr><td colspan='2' align='left'>"
+ "<font color='gray'>%1</font></td></tr>")
+ .arg(QCoreApplication::translate("ClangDiagnosticWidget", "[Source: %1]"))
+ .arg(source));
+ }
text.append("</table>");
@@ -396,7 +403,8 @@ QString ClangDiagnosticWidget::createText(
const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
const ClangDiagnosticWidget::Destination &destination)
{
- const QString htmlText = WidgetFromDiagnostics(toHints(destination, {})).htmlText(diagnostics);
+ const QString htmlText = WidgetFromDiagnostics(toHints(destination, {}))
+ .htmlText(diagnostics, {});
QTextDocument document;
document.setHtml(htmlText);
@@ -410,11 +418,13 @@ QString ClangDiagnosticWidget::createText(
return text;
}
-QWidget *ClangDiagnosticWidget::createWidget(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
- const Destination &destination, const std::function<bool()> &canApplyFixIt)
+QWidget *ClangDiagnosticWidget::createWidget(
+ const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
+ const Destination &destination, const std::function<bool()> &canApplyFixIt,
+ const QString &source)
{
return WidgetFromDiagnostics(toHints(destination, canApplyFixIt))
- .createWidget(diagnostics, canApplyFixIt);
+ .createWidget(diagnostics, canApplyFixIt, source);
}
} // namespace Internal
diff --git a/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.h b/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.h
index afe35812ae..92f98a59ca 100644
--- a/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.h
+++ b/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.h
@@ -48,7 +48,8 @@ public:
static QWidget *createWidget(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
const Destination &destination,
- const std::function<bool()> &canApplyFixIt);
+ const std::function<bool()> &canApplyFixIt,
+ const QString &source);
};
} // namespace Internal
diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp
index a9d4f01c72..94a4408505 100644
--- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp
+++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp
@@ -506,7 +506,8 @@ ClangEditorDocumentProcessor::creatorForHeaderErrorDiagnosticWidget(
vbox->setSpacing(2);
vbox->addWidget(ClangDiagnosticWidget::createWidget({firstHeaderErrorDiagnostic},
- ClangDiagnosticWidget::InfoBar, {}));
+ ClangDiagnosticWidget::InfoBar, {},
+ "libclang"));
auto widget = new QWidget;
widget->setLayout(vbox);
diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
index 52be361305..d1c6b0d0ce 100644
--- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
+++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
@@ -286,7 +286,7 @@ void ClangModelManagerSupport::updateLanguageClient(
if (const ProjectExplorer::Target * const target = project->activeTarget()) {
if (const ProjectExplorer::BuildConfiguration * const bc
= target->activeBuildConfiguration()) {
- return bc->buildDirectory();
+ return bc->buildDirectory() / ".qtc_clangd";
}
}
return Utils::FilePath();
@@ -363,7 +363,7 @@ void ClangModelManagerSupport::updateLanguageClient(
});
});
- auto future = Utils::runAsync(&Internal::generateCompilationDB, projectInfo,
+ auto future = Utils::runAsync(&Internal::generateCompilationDB, projectInfo, jsonDbDir,
CompilationDbPurpose::CodeModel,
warningsConfigForProject(project),
optionsForProject(project));
@@ -483,10 +483,6 @@ void ClangModelManagerSupport::onEditorOpened(Core::IEditor *editor)
// TODO: Ensure that not fully loaded documents are updated?
- // TODO: If the file does not belong to any project and it is a header file,
- // it might make sense to check whether the file is included by any file
- // that does belong to a project, and if so, use the respective client
- // instead. Is this feasible?
ProjectExplorer::Project * const project
= ProjectExplorer::SessionManager::projectForFile(document->filePath());
if (ClangdClient * const client = clientForProject(project))
diff --git a/src/plugins/clangcodemodel/clangtextmark.cpp b/src/plugins/clangcodemodel/clangtextmark.cpp
index 38227008a9..01cfae6ae6 100644
--- a/src/plugins/clangcodemodel/clangtextmark.cpp
+++ b/src/plugins/clangcodemodel/clangtextmark.cpp
@@ -281,7 +281,7 @@ bool ClangTextMark::addToolTipContent(QLayout *target) const
&& diagMgr->diagnosticsWithFixIts().contains(diag);
};
QWidget *widget = ClangDiagnosticWidget::createWidget(
- {m_diagnostic}, ClangDiagnosticWidget::ToolTip, canApplyFixIt);
+ {m_diagnostic}, ClangDiagnosticWidget::ToolTip, canApplyFixIt, "libclang");
target->addWidget(widget);
return true;
@@ -398,7 +398,7 @@ bool ClangdTextMark::addToolTipContent(QLayout *target) const
return c && c->reachable() && c->hasDiagnostic(DocumentUri::fromFilePath(fp), diag);
};
target->addWidget(ClangDiagnosticWidget::createWidget({m_diagnostic},
- ClangDiagnosticWidget::ToolTip, canApplyFixIt));
+ ClangDiagnosticWidget::ToolTip, canApplyFixIt, "clangd"));
return true;
}
diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp
index bbe9817380..a089248d03 100644
--- a/src/plugins/clangcodemodel/clangutils.cpp
+++ b/src/plugins/clangcodemodel/clangutils.cpp
@@ -372,18 +372,15 @@ static QJsonObject createFileObject(const FilePath &buildDir,
}
GenerateCompilationDbResult generateCompilationDB(const CppEditor::ProjectInfo::ConstPtr projectInfo,
+ const Utils::FilePath &baseDir,
CompilationDbPurpose purpose,
const ClangDiagnosticConfig &warningsConfig,
const QStringList &projectOptions)
{
- const FilePath buildDir = projectInfo->buildRoot();
- QTC_ASSERT(!buildDir.isEmpty(), return GenerateCompilationDbResult(QString(),
+ QTC_ASSERT(!baseDir.isEmpty(), return GenerateCompilationDbResult(QString(),
QCoreApplication::translate("ClangUtils", "Could not retrieve build directory.")));
-
- QDir dir(buildDir.toString());
- if (!dir.exists())
- dir.mkpath(dir.path());
- QFile compileCommandsFile(buildDir.toString() + "/compile_commands.json");
+ QTC_CHECK(baseDir.ensureWritableDir());
+ QFile compileCommandsFile(baseDir.toString() + "/compile_commands.json");
const bool fileOpened = compileCommandsFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
if (!fileOpened) {
return GenerateCompilationDbResult(QString(),
@@ -397,7 +394,7 @@ GenerateCompilationDbResult generateCompilationDB(const CppEditor::ProjectInfo::
if (purpose == CompilationDbPurpose::Project)
args = projectPartArguments(*projectPart);
for (const ProjectFile &projFile : projectPart->files) {
- const QJsonObject json = createFileObject(buildDir, args, *projectPart, projFile,
+ const QJsonObject json = createFileObject(baseDir, args, *projectPart, projFile,
purpose, warningsConfig, projectOptions);
if (compileCommandsFile.size() > 1)
compileCommandsFile.write(",");
diff --git a/src/plugins/clangcodemodel/clangutils.h b/src/plugins/clangcodemodel/clangutils.h
index 1d46e6a49b..c3a496f779 100644
--- a/src/plugins/clangcodemodel/clangutils.h
+++ b/src/plugins/clangcodemodel/clangutils.h
@@ -88,8 +88,8 @@ public:
enum class CompilationDbPurpose { Project, CodeModel };
GenerateCompilationDbResult generateCompilationDB(const CppEditor::ProjectInfo::ConstPtr projectInfo,
- CompilationDbPurpose purpose, const CppEditor::ClangDiagnosticConfig &warningsConfig,
- const QStringList &projectOptions);
+ const Utils::FilePath &baseDir, CompilationDbPurpose purpose,
+ const CppEditor::ClangDiagnosticConfig &warningsConfig, const QStringList &projectOptions);
class DiagnosticTextInfo
{
diff --git a/src/plugins/clangformat/clangformatfile.cpp b/src/plugins/clangformat/clangformatfile.cpp
index 8e532ea797..c9955ef2a3 100644
--- a/src/plugins/clangformat/clangformatfile.cpp
+++ b/src/plugins/clangformat/clangformatfile.cpp
@@ -105,7 +105,7 @@ void ClangFormatFile::saveNewFormat()
// workaround: configurationAsText() add comment "# " before BasedOnStyle line
const int pos = style.find("# BasedOnStyle");
- if (pos < style.size())
+ if (pos < int(style.size()))
style.erase(pos, 2);
m_filePath.writeFileContents(QByteArray::fromStdString(style));
}
diff --git a/src/plugins/clangtools/clangtoolsdiagnostic.cpp b/src/plugins/clangtools/clangtoolsdiagnostic.cpp
index 7fe7098c22..42ec9f5f2a 100644
--- a/src/plugins/clangtools/clangtoolsdiagnostic.cpp
+++ b/src/plugins/clangtools/clangtoolsdiagnostic.cpp
@@ -62,7 +62,7 @@ QIcon Diagnostic::icon() const
return {};
}
-quint32 qHash(const Diagnostic &diagnostic)
+Utils::QHashValueType qHash(const Diagnostic &diagnostic)
{
return qHash(diagnostic.name)
^ qHash(diagnostic.description)
diff --git a/src/plugins/clangtools/clangtoolsdiagnostic.h b/src/plugins/clangtools/clangtoolsdiagnostic.h
index e64d91a29f..10534ff0b2 100644
--- a/src/plugins/clangtools/clangtoolsdiagnostic.h
+++ b/src/plugins/clangtools/clangtoolsdiagnostic.h
@@ -27,6 +27,8 @@
#include <debugger/analyzer/diagnosticlocation.h>
+#include <utils/porting.h>
+
#include <QMetaType>
#include <QString>
#include <QVector>
@@ -68,7 +70,7 @@ bool operator==(const Diagnostic &lhs, const Diagnostic &rhs);
using Diagnostics = QList<Diagnostic>;
-quint32 qHash(const Diagnostic &diagnostic);
+Utils::QHashValueType qHash(const Diagnostic &diagnostic);
} // namespace Internal
} // namespace ClangTools
diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp
index 6d0486650f..a878f8864c 100644
--- a/src/plugins/clearcase/clearcaseplugin.cpp
+++ b/src/plugins/clearcase/clearcaseplugin.cpp
@@ -1826,12 +1826,8 @@ bool ClearCasePluginPrivate::vcsOpen(const FilePath &workingDir, const QString &
setStatus(absPath, FileStatus::CheckedOut);
}
- foreach (DocumentModel::Entry *e, DocumentModel::entries()) {
- if (e->fileName().toString() == absPath) {
- e->document->checkPermissions();
- break;
- }
- }
+ if (DocumentModel::Entry *e = DocumentModel::entryForFilePath(FilePath::fromString(absPath)))
+ e->document->checkPermissions();
return !response.error;
}
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
index d1c9d9a22b..4eb6fdc97f 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
@@ -934,18 +934,10 @@ void CMakeBuildSystem::runCTest()
process.setCommand(cmd);
process.start();
- if (!process.waitForStarted(1000) || !process.waitForFinished()) {
- if (process.state() == QProcess::NotRunning)
- return;
- process.terminate();
- if (process.waitForFinished(1000))
- return;
- process.kill();
- process.waitForFinished(1000);
+ if (!process.waitForStarted(1000) || !process.waitForFinished()
+ || process.exitCode() || process.exitStatus() != QProcess::NormalExit) {
return;
}
- if (process.exitCode() || process.exitStatus() != QProcess::NormalExit)
- return;
futureInterface.reportResult(process.readAllStandardOutput());
});
diff --git a/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp b/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp
index 93e6e6232a..bd5556d215 100644
--- a/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp
@@ -472,7 +472,7 @@ bool CMakeConfigItem::operator==(const CMakeConfigItem &o) const
return o.key == key && o.value == value && o.isUnset == isUnset;
}
-uint qHash(const CMakeConfigItem &it)
+Utils::QHashValueType qHash(const CMakeConfigItem &it)
{
return ::qHash(it.key) ^ ::qHash(it.value) ^ ::qHash(it.isUnset);
}
diff --git a/src/plugins/cmakeprojectmanager/cmakeconfigitem.h b/src/plugins/cmakeprojectmanager/cmakeconfigitem.h
index dae93455d7..19bbbfd7d6 100644
--- a/src/plugins/cmakeprojectmanager/cmakeconfigitem.h
+++ b/src/plugins/cmakeprojectmanager/cmakeconfigitem.h
@@ -27,6 +27,7 @@
#include "cmake_global.h"
+#include <utils/porting.h>
#include <utils/optional.h>
#include <QByteArray>
@@ -78,7 +79,7 @@ public:
QStringList values;
};
-uint qHash(const CMakeConfigItem &it); // needed for MSVC
+Utils::QHashValueType qHash(const CMakeConfigItem &it); // needed for MSVC
class CMAKE_EXPORT CMakeConfig : public QList<CMakeConfigItem>
{
diff --git a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp
index 64e59109dd..d8f24b4362 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp
@@ -92,10 +92,8 @@ void CMakeProcess::run(const BuildDirParameters &parameters, const QStringList &
}
}
- const QString srcDir = parameters.sourceDirectory.path();
-
const auto parser = new CMakeParser;
- parser->setSourceDirectory(srcDir);
+ parser->setSourceDirectory(parameters.sourceDirectory.path());
m_parser.addLineParser(parser);
// Always use the sourceDir: If we are triggered because the build directory is getting deleted
@@ -121,7 +119,13 @@ void CMakeProcess::run(const BuildDirParameters &parameters, const QStringList &
connect(process.get(), &QtcProcess::finished,
this, &CMakeProcess::handleProcessFinished);
- CommandLine commandLine(cmake->cmakeExecutable(), QStringList({"-S", srcDir, "-B", buildDirectory.path()}) + arguments);
+ const FilePath cmakeExecutable = cmake->cmakeExecutable();
+ const FilePath sourceDirectory = parameters.sourceDirectory.onDevice(cmakeExecutable);
+
+ CommandLine commandLine(cmakeExecutable);
+ commandLine.addArgs({"-S", sourceDirectory.mapToDevicePath(),
+ "-B", buildDirectory.mapToDevicePath()});
+ commandLine.addArgs(arguments);
TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM);
diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
index cabac0b3b9..d5836605f4 100644
--- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
+++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
@@ -28,9 +28,10 @@
#include "fileapiparser.h"
#include "projecttreehelper.h"
-#include <cppeditor/cppprojectfilecategorizer.h>
+#include <cppeditor/cppeditorconstants.h>
#include <utils/algorithm.h>
+#include <utils/mimetypes/mimedatabase.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <utils/utilsicons.h>
@@ -336,16 +337,6 @@ RawProjectParts generateRawProjectParts(const PreprocessedData &input,
int counter = 0;
for (const TargetDetails &t : input.targetDetails) {
QDir sourceDir(sourceDirectory.toString());
-
- // Do not tread generated files and CMake precompiled headers as project files
- const auto sourceFiles = Utils::filtered(t.sources, [buildDirectory](const SourceInfo &si) {
- return !si.isGenerated && !isPchFile(buildDirectory, FilePath::fromString(si.path));
- });
- CppEditor::ProjectFileCategorizer
- categorizer({}, transform<QList>(sourceFiles, [&sourceDir](const SourceInfo &si) {
- return sourceDir.absoluteFilePath(si.path);
- }));
-
bool needPostfix = t.compileGroups.size() > 1;
int count = 1;
for (const CompileInfo &ci : t.compileGroups) {
@@ -387,20 +378,45 @@ RawProjectParts generateRawProjectParts(const PreprocessedData &input,
QStringList fragments = splitFragments(ci.fragments);
+ // Get all sources from the compiler group, except generated sources
+ QStringList sources;
+ for (auto idx: ci.sources) {
+ SourceInfo si = t.sources.at(idx);
+ if (si.isGenerated)
+ continue;
+ sources.push_back(sourceDir.absoluteFilePath(si.path));
+ }
+
+ // If we are not in a pch compiler group, add all the headers that are not generated
+ const bool hasPchSource = anyOf(sources, [buildDirectory](const QString &path) {
+ return isPchFile(buildDirectory, FilePath::fromString(path));
+ });
+ if (!hasPchSource) {
+ QString headerMimeType;
+ if (ci.language == "C")
+ headerMimeType = CppEditor::Constants::C_HEADER_MIMETYPE;
+ else if (ci.language == "CXX")
+ headerMimeType = CppEditor::Constants::CPP_HEADER_MIMETYPE;
+
+ for (const SourceInfo &si : t.sources) {
+ if (si.isGenerated)
+ continue;
+ const auto mimeTypes = Utils::mimeTypesForFileName(si.path);
+ for (auto mime : mimeTypes)
+ if (mime.name() == headerMimeType)
+ sources.push_back(sourceDir.absoluteFilePath(si.path));
+ }
+ }
+
+ // Set project files except pch files
+ rpp.setFiles(Utils::filtered(sources, [buildDirectory](const QString &path) {
+ return !isPchFile(buildDirectory, FilePath::fromString(path));
+ }));
+
FilePath precompiled_header
= FilePath::fromString(findOrDefault(t.sources, [&ending](const SourceInfo &si) {
return si.path.endsWith(ending);
}).path);
-
- CppEditor::ProjectFiles sources;
- if (ci.language == "C")
- sources = categorizer.cSources();
- else if (ci.language == "CXX")
- sources = categorizer.cxxSources();
-
- rpp.setFiles(transform<QList>(sources, [](const CppEditor::ProjectFile &pf) {
- return pf.path;
- }));
if (!precompiled_header.isEmpty()) {
if (precompiled_header.toFileInfo().isRelative()) {
const FilePath parentDir = FilePath::fromString(sourceDir.absolutePath());
@@ -595,9 +611,20 @@ void addTargets(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cm
const FilePath &sourceDir,
const FilePath &buildDir)
{
+ QHash<QString, const TargetDetails *> targetDetailsHash;
+ for (const TargetDetails &t : targetDetails)
+ targetDetailsHash.insert(t.id, &t);
+ const TargetDetails defaultTargetDetails;
+ auto getTargetDetails = [&targetDetailsHash, &defaultTargetDetails](const QString &id)
+ -> const TargetDetails & {
+ auto it = targetDetailsHash.constFind(id);
+ if (it != targetDetailsHash.constEnd())
+ return *it.value();
+ return defaultTargetDetails;
+ };
+
for (const FileApiDetails::Target &t : config.targets) {
- const TargetDetails &td = Utils::findOrDefault(targetDetails,
- Utils::equal(&TargetDetails::id, t.id));
+ const TargetDetails &td = getTargetDetails(t.id);
const FilePath dir = directorySourceDir(config, sourceDir, t.directory);
@@ -730,7 +757,7 @@ FileApiQtcData extractData(FileApiData &input,
result.projectParts = generateRawProjectParts(data, sourceDirectory, buildDirectory);
auto rootProjectNode = generateRootProjectNode(data, sourceDirectory, buildDirectory);
- ProjectTree::applyTreeManager(rootProjectNode.get()); // QRC nodes
+ ProjectTree::applyTreeManager(rootProjectNode.get(), ProjectTree::AsyncPhase); // QRC nodes
result.rootProjectNode = std::move(rootProjectNode);
setupLocationInfoForTargets(result.rootProjectNode.get(), result.buildTargets);
diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.h b/src/plugins/cmakeprojectmanager/fileapidataextractor.h
index 2b65c76e07..1bc99bf1a8 100644
--- a/src/plugins/cmakeprojectmanager/fileapidataextractor.h
+++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.h
@@ -56,7 +56,7 @@ public:
bool isGenerated = false;
};
-inline uint qHash(const CMakeFileInfo &info, uint seed = 0) { return info.path.hash(seed); }
+inline auto qHash(const CMakeFileInfo &info, uint seed = 0) { return info.path.hash(seed); }
class FileApiQtcData
{
diff --git a/src/plugins/coreplugin/dialogs/codecselector.cpp b/src/plugins/coreplugin/dialogs/codecselector.cpp
index a84b0fa112..9b5786c1b4 100644
--- a/src/plugins/coreplugin/dialogs/codecselector.cpp
+++ b/src/plugins/coreplugin/dialogs/codecselector.cpp
@@ -87,6 +87,8 @@ CodecSelector::CodecSelector(QWidget *parent, Core::BaseTextDocument *doc)
int currentIndex = -1;
foreach (int mib, sortedMibs) {
QTextCodec *c = QTextCodec::codecForMib(mib);
+ if (!doc->supportsCodec(c))
+ continue;
if (!buf.isEmpty()) {
// slow, should use a feature from QTextCodec or QTextDecoder (but those are broken currently)
diff --git a/src/plugins/coreplugin/documentmanager.cpp b/src/plugins/coreplugin/documentmanager.cpp
index 6fa127e255..4fd4213dbb 100644
--- a/src/plugins/coreplugin/documentmanager.cpp
+++ b/src/plugins/coreplugin/documentmanager.cpp
@@ -55,6 +55,7 @@
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
#include <utils/reloadpromptutils.h>
+#include <utils/threadutils.h>
#include <QStringList>
#include <QDateTime>
@@ -326,6 +327,7 @@ static void addFileInfo(IDocument *document, const FilePath &filePath, const Fil
(The added file names are guaranteed to be absolute and cleaned.) */
static void addFileInfos(const QList<IDocument *> &documents)
{
+ QTC_ASSERT(isMainThread(), return);
FilePaths pathsToWatch;
FilePaths linkPathsToWatch;
for (IDocument *document : documents) {
@@ -400,6 +402,7 @@ void DocumentManager::addDocuments(const QList<IDocument *> &documents, bool add
*/
static void removeFileInfo(IDocument *document)
{
+ QTC_ASSERT(isMainThread(), return);
if (!d->m_documentsWithWatch.contains(document))
return;
foreach (const FilePath &filePath, d->m_documentsWithWatch.value(document)) {
@@ -1186,10 +1189,10 @@ void DocumentManager::checkForReload()
bool success = true;
QString errorString;
// we've got some modification
+ document->checkPermissions();
// check if it's contents or permissions:
if (!type) {
// Only permission change
- document->checkPermissions();
success = true;
// now we know it's a content change or file was removed
} else if (defaultBehavior == IDocument::ReloadUnmodified && type == IDocument::TypeContents
diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp
index c2b432d05a..7944c88285 100644
--- a/src/plugins/coreplugin/editormanager/editormanager.cpp
+++ b/src/plugins/coreplugin/editormanager/editormanager.cpp
@@ -2110,7 +2110,7 @@ void EditorManagerPrivate::updateWindowTitleForDocument(IDocument *document, QWi
if (!documentName.isEmpty())
windowTitle.append(documentName);
- const QString filePath = document ? document->filePath().toFileInfo().absoluteFilePath()
+ const QString filePath = document ? document->filePath().absoluteFilePath().path()
: QString();
const QString windowTitleAddition = d->m_titleAdditionHandler
? d->m_titleAdditionHandler(filePath)
diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.h b/src/plugins/coreplugin/locator/ilocatorfilter.h
index bc44cfb1f7..4867987869 100644
--- a/src/plugins/coreplugin/locator/ilocatorfilter.h
+++ b/src/plugins/coreplugin/locator/ilocatorfilter.h
@@ -31,9 +31,10 @@
#include <utils/id.h>
#include <utils/optional.h>
-#include <QVariant>
#include <QFutureInterface>
#include <QIcon>
+#include <QMetaType>
+#include <QVariant>
namespace Core {
@@ -74,12 +75,6 @@ struct LocatorFilterEntry
, displayIcon(icon)
{}
- bool operator==(const LocatorFilterEntry &other) const {
- if (internalData.canConvert(QVariant::String))
- return (internalData.toString() == other.internalData.toString());
- return internalData.constData() == other.internalData.constData();
- }
-
/* backpointer to creating filter */
ILocatorFilter *filter = nullptr;
/* displayed string */
diff --git a/src/plugins/coreplugin/locator/locatorsearchutils.cpp b/src/plugins/coreplugin/locator/locatorsearchutils.cpp
index a9eb1a7de2..cefe9481c0 100644
--- a/src/plugins/coreplugin/locator/locatorsearchutils.cpp
+++ b/src/plugins/coreplugin/locator/locatorsearchutils.cpp
@@ -29,21 +29,10 @@
#include <QString>
#include <QVariant>
-namespace Core {
-
-uint qHash(const LocatorFilterEntry &entry)
-{
- if (entry.internalData.canConvert(QVariant::String))
- return QT_PREPEND_NAMESPACE(qHash)(entry.internalData.toString());
- return QT_PREPEND_NAMESPACE(qHash)(entry.internalData.constData());
-}
-
-} // namespace Core
-
void Core::Internal::runSearch(QFutureInterface<Core::LocatorFilterEntry> &future,
const QList<ILocatorFilter *> &filters, const QString &searchText)
{
- QSet<LocatorFilterEntry> alreadyAdded;
+ QSet<QString> alreadyAdded;
const bool checkDuplicates = (filters.size() > 1);
for (ILocatorFilter *filter : filters) {
if (future.isCanceled())
@@ -53,11 +42,15 @@ void Core::Internal::runSearch(QFutureInterface<Core::LocatorFilterEntry> &futur
QVector<LocatorFilterEntry> uniqueFilterResults;
uniqueFilterResults.reserve(filterResults.size());
for (const LocatorFilterEntry &entry : filterResults) {
- if (checkDuplicates && alreadyAdded.contains(entry))
- continue;
+ if (checkDuplicates) {
+ const QString stringData = entry.internalData.toString();
+ if (!stringData.isEmpty()) {
+ if (alreadyAdded.contains(stringData))
+ continue;
+ alreadyAdded.insert(stringData);
+ }
+ }
uniqueFilterResults.append(entry);
- if (checkDuplicates)
- alreadyAdded.insert(entry);
}
if (!uniqueFilterResults.isEmpty())
future.reportResults(uniqueFilterResults);
diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp
index 50837d9f15..a6b94660f6 100644
--- a/src/plugins/coreplugin/manhattanstyle.cpp
+++ b/src/plugins/coreplugin/manhattanstyle.cpp
@@ -73,10 +73,13 @@ bool styleEnabled(const QWidget *widget)
return true;
}
-static bool isInDialogOrPopup(const QWidget *widget)
+static bool isInUnstyledDialogOrPopup(const QWidget *widget)
{
- // Do not style dialogs or explicitly ignored widgets
- const Qt::WindowType windowType = widget->window()->windowType();
+ // Do not style contents of dialogs or popups without "panelwidget" property
+ const QWidget *window = widget->window();
+ if (window->property("panelwidget").toBool())
+ return false;
+ const Qt::WindowType windowType = window->windowType();
return (windowType == Qt::Dialog || windowType == Qt::Popup);
}
@@ -86,7 +89,7 @@ bool panelWidget(const QWidget *widget)
if (!widget)
return false;
- if (isInDialogOrPopup(widget))
+ if (isInUnstyledDialogOrPopup(widget))
return false;
if (qobject_cast<const FancyMainWindow *>(widget))
@@ -113,7 +116,7 @@ bool lightColored(const QWidget *widget)
if (!widget)
return false;
- if (isInDialogOrPopup(widget))
+ if (isInUnstyledDialogOrPopup(widget))
return false;
const QWidget *p = widget;
diff --git a/src/plugins/coreplugin/textdocument.cpp b/src/plugins/coreplugin/textdocument.cpp
index 4d6c45dadf..448854926d 100644
--- a/src/plugins/coreplugin/textdocument.cpp
+++ b/src/plugins/coreplugin/textdocument.cpp
@@ -183,7 +183,13 @@ void BaseTextDocument::setCodec(const QTextCodec *codec)
{
if (debug)
qDebug() << Q_FUNC_INFO << this << (codec ? codec->name() : QByteArray());
- d->m_format.codec = codec;
+ if (supportsCodec(codec))
+ d->m_format.codec = codec;
+}
+
+bool BaseTextDocument::supportsCodec(const QTextCodec *) const
+{
+ return true;
}
void BaseTextDocument::switchUtf8Bom()
diff --git a/src/plugins/coreplugin/textdocument.h b/src/plugins/coreplugin/textdocument.h
index 7d16b6fa81..83450b8395 100644
--- a/src/plugins/coreplugin/textdocument.h
+++ b/src/plugins/coreplugin/textdocument.h
@@ -46,6 +46,7 @@ public:
Utils::TextFileFormat format() const;
const QTextCodec *codec() const;
void setCodec(const QTextCodec *);
+ virtual bool supportsCodec(const QTextCodec *) const;
void switchUtf8Bom();
bool supportsUtf8Bom() const;
Utils::TextFileFormat::LineTerminationMode lineTerminationMode() const;
diff --git a/src/plugins/cppcheck/cppcheckdiagnostic.cpp b/src/plugins/cppcheck/cppcheckdiagnostic.cpp
index 6534018fc5..a1b8235e82 100644
--- a/src/plugins/cppcheck/cppcheckdiagnostic.cpp
+++ b/src/plugins/cppcheck/cppcheckdiagnostic.cpp
@@ -34,7 +34,7 @@ bool Diagnostic::operator==(const Diagnostic &r) const
== std::tie(r.severity, r.message, r.fileName, r.lineNumber);
}
-quint32 qHash(const Diagnostic &diagnostic)
+Utils::QHashValueType qHash(const Diagnostic &diagnostic)
{
return qHash(diagnostic.message) ^ qHash(diagnostic.fileName) ^ diagnostic.lineNumber;
}
diff --git a/src/plugins/cppcheck/cppcheckdiagnostic.h b/src/plugins/cppcheck/cppcheckdiagnostic.h
index eea6ef430e..1983af68de 100644
--- a/src/plugins/cppcheck/cppcheckdiagnostic.h
+++ b/src/plugins/cppcheck/cppcheckdiagnostic.h
@@ -26,6 +26,7 @@
#pragma once
#include <utils/fileutils.h>
+#include <utils/porting.h>
namespace Cppcheck {
namespace Internal {
@@ -49,7 +50,7 @@ public:
int lineNumber = 0;
};
-quint32 qHash(const Diagnostic &diagnostic);
+Utils::QHashValueType qHash(const Diagnostic &diagnostic);
} // namespace Internal
} // namespace Cppcheck
diff --git a/src/plugins/cppeditor/compileroptionsbuilder.cpp b/src/plugins/cppeditor/compileroptionsbuilder.cpp
index 14921c51eb..09f71fa93f 100644
--- a/src/plugins/cppeditor/compileroptionsbuilder.cpp
+++ b/src/plugins/cppeditor/compileroptionsbuilder.cpp
@@ -123,7 +123,7 @@ CompilerOptionsBuilder::~CompilerOptionsBuilder() = default;
QStringList CompilerOptionsBuilder::build(ProjectFile::Kind fileKind,
UsePrecompiledHeaders usePrecompiledHeaders)
{
- m_options.clear();
+ reset();
evaluateCompilerFlags();
if (fileKind == ProjectFile::CHeader || fileKind == ProjectFile::CSource) {
@@ -251,9 +251,12 @@ void CompilerOptionsBuilder::addWordWidth()
void CompilerOptionsBuilder::addTargetTriple()
{
+ const QString target = m_explicitTarget.isEmpty()
+ ? m_projectPart.toolChainTargetTriple : m_explicitTarget;
+
// Only "--target=" style is accepted in both g++ and cl driver modes.
- if (!m_projectPart.toolChainTargetTriple.isEmpty())
- add("--target=" + m_projectPart.toolChainTargetTriple);
+ if (!target.isEmpty())
+ add("--target=" + target);
}
void CompilerOptionsBuilder::addExtraCodeModelFlags()
@@ -771,6 +774,7 @@ void CompilerOptionsBuilder::undefineClangVersionMacrosForMsvc()
void CompilerOptionsBuilder::reset()
{
m_options.clear();
+ m_explicitTarget.clear();
}
// Some example command lines for a "Qt Console Application":
@@ -786,12 +790,18 @@ void CompilerOptionsBuilder::evaluateCompilerFlags()
const Id toolChain = m_projectPart.toolchainType;
bool containsDriverMode = false;
bool skipNext = false;
- const QStringList allFlags = m_projectPart.compilerFlags + m_projectPart.extraCodeModelFlags;
+ bool nextIsTarget = false;
+ const QStringList allFlags = m_projectPart.extraCodeModelFlags + m_projectPart.compilerFlags;
for (const QString &option : allFlags) {
if (skipNext) {
skipNext = false;
continue;
}
+ if (nextIsTarget) {
+ nextIsTarget = false;
+ m_explicitTarget = option;
+ continue;
+ }
if (userBlackList.contains(option))
continue;
@@ -812,14 +822,15 @@ void CompilerOptionsBuilder::evaluateCompilerFlags()
continue;
}
- // As we always set the target explicitly, filter out target args.
- if (!m_projectPart.toolChainTargetTriple.isEmpty()) {
- if (option.startsWith("--target="))
- continue;
- if (option == "-target") {
- skipNext = true;
- continue;
- }
+ // An explicit target triple from the build system takes precedence over the generic one
+ // from the toolchain.
+ if (option.startsWith("--target=")) {
+ m_explicitTarget = option.mid(9);
+ continue;
+ }
+ if (option == "-target") {
+ nextIsTarget = true;
+ continue;
}
if (option == includeUserPathOption || option == includeSystemPathOption
diff --git a/src/plugins/cppeditor/compileroptionsbuilder.h b/src/plugins/cppeditor/compileroptionsbuilder.h
index 0211d58a18..cc75e984be 100644
--- a/src/plugins/cppeditor/compileroptionsbuilder.h
+++ b/src/plugins/cppeditor/compileroptionsbuilder.h
@@ -122,6 +122,7 @@ private:
} m_compilerFlags;
QStringList m_options;
+ QString m_explicitTarget;
bool m_clStyle = false;
};
diff --git a/src/plugins/cppeditor/cppcodemodelsettings.cpp b/src/plugins/cppeditor/cppcodemodelsettings.cpp
index 1a7cf1255f..66c07bed7a 100644
--- a/src/plugins/cppeditor/cppcodemodelsettings.cpp
+++ b/src/plugins/cppeditor/cppcodemodelsettings.cpp
@@ -402,7 +402,8 @@ QVariantMap ClangdSettings::Data::toMap() const
{
QVariantMap map;
map.insert(useClangdKey(), useClangd);
- map.insert(clangdPathKey(), executableFilePath.toString());
+ if (executableFilePath != fallbackClangdFilePath())
+ map.insert(clangdPathKey(), executableFilePath.toString());
map.insert(clangdIndexingKey(), enableIndexing);
map.insert(clangdThreadLimitKey(), workerThreadLimit);
map.insert(clangdDocumentThresholdKey(), documentUpdateThreshold);
diff --git a/src/plugins/cppeditor/cppcodemodelsettingspage.cpp b/src/plugins/cppeditor/cppcodemodelsettingspage.cpp
index 2c775e3843..1c6621e070 100644
--- a/src/plugins/cppeditor/cppcodemodelsettingspage.cpp
+++ b/src/plugins/cppeditor/cppcodemodelsettingspage.cpp
@@ -33,11 +33,14 @@
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
+#include <utils/infolabel.h>
#include <utils/pathchooser.h>
+#include <utils/qtcprocess.h>
#include <QFormLayout>
#include <QSpinBox>
#include <QTextStream>
+#include <QVersionNumber>
namespace CppEditor::Internal {
@@ -197,6 +200,7 @@ public:
QSpinBox threadLimitSpinBox;
QSpinBox documentUpdateThreshold;
Utils::PathChooser clangdChooser;
+ Utils::InfoLabel versionWarningLabel;
};
ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsData)
@@ -230,6 +234,7 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
const auto formLayout = new QFormLayout;
const auto chooserLabel = new QLabel(tr("Path to executable:"));
formLayout->addRow(chooserLabel, &d->clangdChooser);
+ formLayout->addRow(QString(), &d->versionWarningLabel);
const auto indexingLabel = new QLabel(tr("Enable background indexing:"));
formLayout->addRow(indexingLabel, &d->indexingCheckBox);
const auto threadLimitLayout = new QHBoxLayout;
@@ -251,11 +256,58 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
indexingLabel->setEnabled(checked);
d->indexingCheckBox.setEnabled(checked);
d->threadLimitSpinBox.setEnabled(checked);
+ d->versionWarningLabel.setEnabled(checked);
};
connect(&d->useClangdCheckBox, &QCheckBox::toggled, toggleEnabled);
toggleEnabled(d->useClangdCheckBox.isChecked());
d->threadLimitSpinBox.setEnabled(d->useClangdCheckBox.isChecked());
+ d->versionWarningLabel.setType(Utils::InfoLabel::Warning);
+ const auto updateWarningLabel = [this] {
+ class WarningLabelSetter {
+ public:
+ WarningLabelSetter(QLabel &label) : m_label(label) { m_label.clear(); }
+ ~WarningLabelSetter() { m_label.setVisible(!m_label.text().isEmpty()); }
+ void setWarning(const QString &text) { m_label.setText(text); }
+ private:
+ QLabel &m_label;
+ };
+ WarningLabelSetter labelSetter(d->versionWarningLabel);
+
+ if (!d->clangdChooser.isValid())
+ return;
+ const Utils::FilePath clangdPath = d->clangdChooser.filePath();
+ Utils::QtcProcess clangdProc;
+ clangdProc.setCommand({clangdPath, {"--version"}});
+ clangdProc.start();
+ if (!clangdProc.waitForStarted() || !clangdProc.waitForFinished()) {
+ labelSetter.setWarning(tr("Failed to retrieve clangd version: %1")
+ .arg(clangdProc.exitMessage()));
+ return;
+ }
+ const QString output = clangdProc.allOutput();
+ static const QString versionPrefix = "clangd version ";
+ const int prefixOffset = output.indexOf(versionPrefix);
+ QVersionNumber clangdVersion;
+ if (prefixOffset != -1) {
+ clangdVersion = QVersionNumber::fromString(output.mid(prefixOffset
+ + versionPrefix.length()));
+ }
+ if (clangdVersion.isNull()) {
+ labelSetter.setWarning(tr("Failed to retrieve clangd version: "
+ "Unexpected clangd output."));
+ return;
+ }
+ if (clangdVersion < QVersionNumber(13)) {
+ labelSetter.setWarning(tr("The clangd version is %1, but %2 or greater is "
+ "recommended for full functionality.")
+ .arg(clangdVersion.toString()).arg(13));
+ return;
+ }
+ };
+ connect(&d->clangdChooser, &Utils::PathChooser::pathChanged, this, updateWarningLabel);
+ updateWarningLabel();
+
connect(&d->useClangdCheckBox, &QCheckBox::toggled,
this, &ClangdSettingsWidget::settingsDataChanged);
connect(&d->indexingCheckBox, &QCheckBox::toggled,
diff --git a/src/plugins/cppeditor/cppcompletionassist.cpp b/src/plugins/cppeditor/cppcompletionassist.cpp
index cd2c70b78c..ee04a15957 100644
--- a/src/plugins/cppeditor/cppcompletionassist.cpp
+++ b/src/plugins/cppeditor/cppcompletionassist.cpp
@@ -858,38 +858,8 @@ bool InternalCppCompletionAssistProcessor::accepts() const
if (pos - startOfName >= TextEditorSettings::completionSettings().m_characterThreshold) {
const QChar firstCharacter = m_interface->characterAt(startOfName);
if (isValidFirstIdentifierChar(firstCharacter)) {
- // Finally check that we're not inside a comment or string (code copied from startOfOperator)
- QTextCursor tc(m_interface->textDocument());
- tc.setPosition(pos);
-
- SimpleLexer tokenize;
- tokenize.setLanguageFeatures(m_interface->languageFeatures());
- tokenize.setSkipComments(false);
-
- const Tokens &tokens = tokenize(tc.block().text(), BackwardsScanner::previousBlockState(tc.block()));
- const int tokenIdx = SimpleLexer::tokenBefore(tokens, qMax(0, tc.positionInBlock() - 1));
- const Token tk = (tokenIdx == -1) ? Token() : tokens.at(tokenIdx);
-
- if (!tk.isComment() && !tk.isLiteral()) {
- return true;
- } else if (tk.isLiteral()
- && tokens.size() == 3
- && tokens.at(0).kind() == T_POUND
- && tokens.at(1).kind() == T_IDENTIFIER) {
- const QString &line = tc.block().text();
- const Token &idToken = tokens.at(1);
- QStringView identifier = idToken.utf16charsEnd() > line.size()
- ? QStringView(line).mid(
- idToken.utf16charsBegin())
- : QStringView(line)
- .mid(idToken.utf16charsBegin(),
- idToken.utf16chars());
- if (identifier == QLatin1String("include")
- || identifier == QLatin1String("include_next")
- || (m_interface->languageFeatures().objCEnabled && identifier == QLatin1String("import"))) {
- return true;
- }
- }
+ return !isInCommentOrString(m_interface.data(),
+ m_interface->languageFeatures());
}
}
}
diff --git a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp
index ac2747cad7..11a3a70be0 100644
--- a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp
+++ b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp
@@ -417,17 +417,18 @@ bool maybeAppendArgumentOrParameterList(QString *expression, const QTextCursor &
bool isCursorOnTrailingReturnType(const QList<AST *> &astPath)
{
- for (auto it = astPath.cend() - 1, begin = astPath.cbegin(); it >= begin; --it) {
+ if (astPath.size() < 3)
+ return false;
+ for (auto it = astPath.cend() - 3, begin = astPath.cbegin(); it >= begin; --it) {
+ if (!(*it)->asTrailingReturnType())
+ continue;
const auto nextIt = it + 1;
const auto nextNextIt = nextIt + 1;
- if (nextNextIt != astPath.cend() && (*it)->asTrailingReturnType()) {
- return (*nextIt)->asNamedTypeSpecifier()
- && ((*nextNextIt)->asSimpleName()
- || (*nextNextIt)->asQualifiedName()
- || (*nextNextIt)->asTemplateId());
- }
+ return (*nextIt)->asNamedTypeSpecifier()
+ && ((*nextNextIt)->asSimpleName()
+ || (*nextNextIt)->asQualifiedName()
+ || (*nextNextIt)->asTemplateId());
}
-
return false;
}
diff --git a/src/plugins/cppeditor/cppmodelmanager.cpp b/src/plugins/cppeditor/cppmodelmanager.cpp
index 40b37263d8..4ddd690b42 100644
--- a/src/plugins/cppeditor/cppmodelmanager.cpp
+++ b/src/plugins/cppeditor/cppmodelmanager.cpp
@@ -694,6 +694,10 @@ CppModelManager::CppModelManager()
connect(KitManager::instance(), &KitManager::kitsChanged, this,
&CppModelManager::setupFallbackProjectPart);
+ connect(this, &CppModelManager::projectPartsRemoved, this,
+ &CppModelManager::setupFallbackProjectPart);
+ connect(this, &CppModelManager::projectPartsUpdated, this,
+ &CppModelManager::setupFallbackProjectPart);
setupFallbackProjectPart();
qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");
diff --git a/src/plugins/cppeditor/cppprojectfile.cpp b/src/plugins/cppeditor/cppprojectfile.cpp
index fa3bf8c023..58196521ca 100644
--- a/src/plugins/cppeditor/cppprojectfile.cpp
+++ b/src/plugins/cppeditor/cppprojectfile.cpp
@@ -87,6 +87,20 @@ bool ProjectFile::isAmbiguousHeader(const QString &filePath)
return filePath.endsWith(".h");
}
+bool ProjectFile::isObjC(const QString &filePath)
+{
+ const Kind kind = classify(filePath);
+ switch (kind) {
+ case CppEditor::ProjectFile::ObjCHeader:
+ case CppEditor::ProjectFile::ObjCXXHeader:
+ case CppEditor::ProjectFile::ObjCSource:
+ case CppEditor::ProjectFile::ObjCXXSource:
+ return true;
+ default:
+ return false;
+ }
+}
+
ProjectFile::Kind ProjectFile::sourceForHeaderKind(ProjectFile::Kind kind)
{
ProjectFile::Kind sourceKind;
diff --git a/src/plugins/cppeditor/cppprojectfile.h b/src/plugins/cppeditor/cppprojectfile.h
index 078b93cce9..1bd80fdf65 100644
--- a/src/plugins/cppeditor/cppprojectfile.h
+++ b/src/plugins/cppeditor/cppprojectfile.h
@@ -61,6 +61,7 @@ public:
static bool isC(Kind kind);
static bool isCxx(Kind kind);
static bool isAmbiguousHeader(const QString &filePath);
+ static bool isObjC(const QString &filePath);
bool isHeader() const;
bool isSource() const;
diff --git a/src/plugins/cppeditor/cppprojectupdater.h b/src/plugins/cppeditor/cppprojectupdater.h
index 165ec27910..b716ef3183 100644
--- a/src/plugins/cppeditor/cppprojectupdater.h
+++ b/src/plugins/cppeditor/cppprojectupdater.h
@@ -47,7 +47,7 @@ public:
CppProjectUpdaterFactory();
// keep the namespace, for the type name in the invokeMethod call
- Q_INVOKABLE CppProjectUpdaterInterface *create();
+ Q_INVOKABLE CppEditor::CppProjectUpdaterInterface *create();
};
} // namespace Internal
diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp
index 8b9fdce190..91974761c2 100644
--- a/src/plugins/cppeditor/cppquickfixes.cpp
+++ b/src/plugins/cppeditor/cppquickfixes.cpp
@@ -2483,7 +2483,7 @@ void CompleteSwitchCaseStatement::match(const CppQuickFixInterface &interface,
AST *ast = path.at(depth);
SwitchStatementAST *switchStatement = ast->asSwitchStatement();
if (switchStatement) {
- if (!switchStatement->statement)
+ if (!switchStatement->statement || !switchStatement->symbol)
return;
CompoundStatementAST *compoundStatement = switchStatement->statement->asCompoundStatement();
if (!compoundStatement) // we ignore pathologic case "switch (t) case A: ;"
diff --git a/src/plugins/cppeditor/cpptoolsreuse.cpp b/src/plugins/cppeditor/cpptoolsreuse.cpp
index 15a1bea88a..04f679824a 100644
--- a/src/plugins/cppeditor/cpptoolsreuse.cpp
+++ b/src/plugins/cppeditor/cpptoolsreuse.cpp
@@ -39,11 +39,15 @@
#include <coreplugin/idocument.h>
#include <coreplugin/messagemanager.h>
#include <projectexplorer/session.h>
+#include <texteditor/codeassist/assistinterface.h>
#include <texteditor/textdocument.h>
-#include <cplusplus/Overview.h>
+#include <cplusplus/BackwardsScanner.h>
#include <cplusplus/LookupContext.h>
+#include <cplusplus/Overview.h>
+#include <cplusplus/SimpleLexer.h>
#include <utils/algorithm.h>
+#include <utils/porting.h>
#include <utils/textutils.h>
#include <utils/qtcassert.h>
@@ -300,6 +304,40 @@ const Macro *findCanonicalMacro(const QTextCursor &cursor, Document::Ptr documen
return nullptr;
}
+bool isInCommentOrString(const TextEditor::AssistInterface *interface,
+ CPlusPlus::LanguageFeatures features)
+{
+ QTextCursor tc(interface->textDocument());
+ tc.setPosition(interface->position());
+
+ SimpleLexer tokenize;
+ features.qtMocRunEnabled = true;
+ tokenize.setLanguageFeatures(features);
+ tokenize.setSkipComments(false);
+ const Tokens &tokens = tokenize(tc.block().text(),
+ BackwardsScanner::previousBlockState(tc.block()));
+ const int tokenIdx = SimpleLexer::tokenBefore(tokens, qMax(0, tc.positionInBlock() - 1));
+ const Token tk = (tokenIdx == -1) ? Token() : tokens.at(tokenIdx);
+
+ if (tk.isComment())
+ return true;
+ if (!tk.isLiteral())
+ return false;
+ if (tokens.size() == 3 && tokens.at(0).kind() == T_POUND
+ && tokens.at(1).kind() == T_IDENTIFIER) {
+ const QString &line = tc.block().text();
+ const Token &idToken = tokens.at(1);
+ QStringView identifier = Utils::midView(line, idToken.utf16charsBegin(),
+ idToken.utf16chars());
+ if (identifier == QLatin1String("include")
+ || identifier == QLatin1String("include_next")
+ || (features.objCEnabled && identifier == QLatin1String("import"))) {
+ return false;
+ }
+ }
+ return true;
+}
+
CppCodeModelSettings *codeModelSettings()
{
return Internal::CppEditorPlugin::instance()->codeModelSettings();
diff --git a/src/plugins/cppeditor/cpptoolsreuse.h b/src/plugins/cppeditor/cpptoolsreuse.h
index b3fd7a274b..e7023b7be7 100644
--- a/src/plugins/cppeditor/cpptoolsreuse.h
+++ b/src/plugins/cppeditor/cpptoolsreuse.h
@@ -35,6 +35,7 @@
#include <cplusplus/ASTVisitor.h>
#include <cplusplus/CppDocument.h>
+#include <cplusplus/Token.h>
QT_BEGIN_NAMESPACE
class QChar;
@@ -48,6 +49,8 @@ class Symbol;
class LookupContext;
} // namespace CPlusPlus
+namespace TextEditor { class AssistInterface; }
+
namespace CppEditor {
class CppRefactoringFile;
class ProjectInfo;
@@ -71,6 +74,9 @@ bool CPPEDITOR_EXPORT isOwnershipRAIIType(CPlusPlus::Symbol *symbol,
const CPlusPlus::Macro CPPEDITOR_EXPORT *findCanonicalMacro(const QTextCursor &cursor,
CPlusPlus::Document::Ptr document);
+bool CPPEDITOR_EXPORT isInCommentOrString(const TextEditor::AssistInterface *interface,
+ CPlusPlus::LanguageFeatures features);
+
enum class CacheUsage { ReadWrite, ReadOnly };
QString CPPEDITOR_EXPORT correspondingHeaderOrSource(const QString &fileName, bool *wasHeader = nullptr,
diff --git a/src/plugins/cppeditor/semantichighlighter.cpp b/src/plugins/cppeditor/semantichighlighter.cpp
index f3112a725b..eb6e90fb2b 100644
--- a/src/plugins/cppeditor/semantichighlighter.cpp
+++ b/src/plugins/cppeditor/semantichighlighter.cpp
@@ -180,6 +180,7 @@ void SemanticHighlighter::onHighlighterResultAvailable(int from, int to)
QPair<QTextBlock, Parentheses> parentheses;
for (int i = from; i < to; ++i) {
const HighlightingResult &result = m_watcher->future().resultAt(i);
+ QTC_ASSERT(result.line <= m_baseTextDocument->document()->blockCount(), continue);
if (result.kind != AngleBracketOpen && result.kind != AngleBracketClose
&& result.kind != DoubleAngleBracketClose
&& result.kind != TernaryIf && result.kind != TernaryElse) {
diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp
index debdcaafdd..87d52ad74b 100644
--- a/src/plugins/debugger/debuggerengine.cpp
+++ b/src/plugins/debugger/debuggerengine.cpp
@@ -1891,7 +1891,7 @@ QString DebuggerEngine::nativeStartupCommands() const
return !trimmed.isEmpty() && !trimmed.startsWith('#');
});
- return lines.join('\n');
+ return expand(lines.join('\n'));
}
Perspective *DebuggerEngine::perspective() const
@@ -2827,7 +2827,7 @@ void CppDebuggerEngine::validateRunParameters(DebuggerRunParameters &rp)
globalRegExpSourceMap.reserve(sourcePathMap.size());
for (auto it = sourcePathMap.begin(), end = sourcePathMap.end(); it != end; ++it) {
if (it.key().startsWith('(')) {
- const QString expanded = Utils::globalMacroExpander()->expand(it.value());
+ const QString expanded = rp.macroExpander->expand(it.value());
if (!expanded.isEmpty())
globalRegExpSourceMap.push_back(
qMakePair(QRegularExpression(it.key()), expanded));
diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h
index 8611e4668d..2099bc2f95 100644
--- a/src/plugins/debugger/debuggerengine.h
+++ b/src/plugins/debugger/debuggerengine.h
@@ -195,7 +195,7 @@ public:
bool isCppDebugging() const;
bool isNativeMixedDebugging() const;
- Utils::MacroExpander *macroExpander = nullptr;
+ const Utils::MacroExpander *macroExpander = nullptr;
Utils::optional<int> exitCode = {};
diff --git a/src/plugins/debugger/debuggeritem.cpp b/src/plugins/debugger/debuggeritem.cpp
index 36e066e09a..3a5060cbe5 100644
--- a/src/plugins/debugger/debuggeritem.cpp
+++ b/src/plugins/debugger/debuggeritem.cpp
@@ -225,7 +225,7 @@ void DebuggerItem::reinitializeFromFile(const Environment &sysEnv, QString *erro
return;
}
- qWarning() << "Unable to determine gdb target ABI";
+ qWarning() << "Unable to determine gdb target ABI via" << proc.commandLine().toUserOutput();
//! \note If unable to determine the GDB ABI, no ABI is appended to m_abis here.
return;
}
diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp
index ffece20708..8b92c7d31d 100644
--- a/src/plugins/debugger/debuggerruncontrol.cpp
+++ b/src/plugins/debugger/debuggerruncontrol.cpp
@@ -885,7 +885,7 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm
QTC_ASSERT(kit, return);
m_runParameters.sysRoot = SysRootKitAspect::sysRoot(kit);
- m_runParameters.macroExpander = kit->macroExpander();
+ m_runParameters.macroExpander = runControl->macroExpander();
m_runParameters.debugger = DebuggerKitAspect::runnable(kit);
m_runParameters.cppEngineType = DebuggerKitAspect::engineType(kit);
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index 8109a3f91e..2251f4d9e6 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -3800,8 +3800,11 @@ static SourcePathMap mergeStartParametersSourcePathMap(const DebuggerRunParamete
{
// Do not overwrite user settings.
SourcePathMap rc = sp.sourcePathMap;
- for (auto it = in.constBegin(), end = in.constEnd(); it != end; ++it)
- rc.insert(it.key(), it.value());
+ for (auto it = in.constBegin(), end = in.constEnd(); it != end; ++it) {
+ // Entries that start with parenthesis are handled in CppDebuggerEngine::validateRunParameters
+ if (!it.key().startsWith('('))
+ rc.insert(it.key(), sp.macroExpander->expand(it.value()));
+ }
return rc;
}
@@ -3850,6 +3853,7 @@ void GdbEngine::setupEngine()
gdbCommand.addArg("-n");
Environment gdbEnv = rp.debugger.environment;
+ gdbEnv.setupEnglishOutput();
if (rp.runAsRoot) {
CommandLine wrapped("sudo", {"-A"});
wrapped.addCommandLineAsArgs(gdbCommand);
diff --git a/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp b/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp
index 18fcdce5dc..9ac3cee8ee 100644
--- a/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp
+++ b/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp
@@ -215,9 +215,9 @@ bool CdbSymbolPathListEditor::isSymbolServerPath(const QString &path, QString *c
if (!path.startsWith(QLatin1String(symbolServerPrefixC)) || !path.endsWith(QLatin1String(symbolServerPostfixC)))
return false;
if (cacheDir) {
- static const unsigned prefixLength = qstrlen(symbolServerPrefixC);
- static const unsigned postfixLength = qstrlen(symbolServerPostfixC);
- if (path.length() == int(prefixLength + postfixLength))
+ static const unsigned prefixLength = unsigned(qstrlen(symbolServerPrefixC));
+ static const unsigned postfixLength = unsigned(qstrlen(symbolServerPostfixC));
+ if (unsigned(path.length()) == prefixLength + postfixLength)
return true;
// Split apart symbol server post/prefixes
*cacheDir = path.mid(prefixLength, path.size() - prefixLength - qstrlen(symbolServerPostfixC) + 1);
@@ -230,7 +230,7 @@ bool CdbSymbolPathListEditor::isSymbolCachePath(const QString &path, QString *ca
if (!path.startsWith(QLatin1String(symbolCachePrefixC)))
return false;
if (cacheDir) {
- static const unsigned prefixLength = qstrlen(symbolCachePrefixC);
+ static const unsigned prefixLength = unsigned(qstrlen(symbolCachePrefixC));
// Split apart symbol cach prefixes
*cacheDir = path.mid(prefixLength);
}
diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp
index 1dc061c508..20a5c95108 100644
--- a/src/plugins/debugger/watchhandler.cpp
+++ b/src/plugins/debugger/watchhandler.cpp
@@ -133,7 +133,7 @@ template <class T>
void readNumericVectorHelper(std::vector<double> *v, const QByteArray &ba)
{
const auto p = (const T*)ba.data();
- const int n = ba.size() / sizeof(T);
+ const int n = int(ba.size() / sizeof(T));
v->resize(n);
// Losing precision in case of 64 bit ints is ok here, as the result
// is only used to plot data.
diff --git a/src/plugins/designer/codemodelhelpers.cpp b/src/plugins/designer/codemodelhelpers.cpp
index b0d91f0a74..009bf83f53 100644
--- a/src/plugins/designer/codemodelhelpers.cpp
+++ b/src/plugins/designer/codemodelhelpers.cpp
@@ -82,7 +82,7 @@ private:
};
SearchFunction::SearchFunction(const char *name) :
- m_length(qstrlen(name)),
+ m_length(uint(qstrlen(name))),
m_name(name)
{
}
diff --git a/src/plugins/designer/formwindowfile.cpp b/src/plugins/designer/formwindowfile.cpp
index 5744256813..192a1383eb 100644
--- a/src/plugins/designer/formwindowfile.cpp
+++ b/src/plugins/designer/formwindowfile.cpp
@@ -247,6 +247,11 @@ QString FormWindowFile::fallbackSaveAsFileName() const
return m_suggestedName;
}
+bool FormWindowFile::supportsCodec(const QTextCodec *codec) const
+{
+ return codec == QTextCodec::codecForName("UTF-8");
+}
+
bool FormWindowFile::writeFile(const Utils::FilePath &filePath, QString *errorString) const
{
if (Designer::Constants::Internal::debug)
diff --git a/src/plugins/designer/formwindowfile.h b/src/plugins/designer/formwindowfile.h
index ca0ad50b3f..845a8ce0dc 100644
--- a/src/plugins/designer/formwindowfile.h
+++ b/src/plugins/designer/formwindowfile.h
@@ -58,6 +58,7 @@ public:
bool isSaveAsAllowed() const override;
bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override;
QString fallbackSaveAsFileName() const override;
+ bool supportsCodec(const QTextCodec *codec) const override;
// Internal
void setFallbackSaveAsFileName(const QString &fileName);
diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp
index 638fb91db0..4461c7a1e8 100644
--- a/src/plugins/docker/dockerdevice.cpp
+++ b/src/plugins/docker/dockerdevice.cpp
@@ -833,14 +833,7 @@ void DockerDevicePrivate::startContainer()
for (QString mount : qAsConst(m_data.mounts)) {
if (mount.isEmpty())
continue;
- // make sure to convert windows style paths to unix style paths with the file system case:
- // C:/dev/src -> /c/dev/src
- if (const FilePath mountPath = FilePath::fromUserInput(mount).normalizedPathName();
- mountPath.startsWithDriveLetter()) {
- const QChar lowerDriveLetter = mountPath.path().at(0).toLower();
- const FilePath path = FilePath::fromUserInput(mountPath.path().mid(2)); // strip C:
- mount = '/' + lowerDriveLetter + path.path();
- }
+ mount = q->mapToDevicePath(FilePath::fromUserInput(mount));
dockerCreate.addArgs({"-v", mount + ':' + mount});
}
@@ -1069,6 +1062,19 @@ FilePath DockerDevice::mapToGlobalPath(const FilePath &pathOnDevice) const
return result;
}
+QString DockerDevice::mapToDevicePath(const Utils::FilePath &globalPath) const
+{
+ // make sure to convert windows style paths to unix style paths with the file system case:
+ // C:/dev/src -> /c/dev/src
+ const FilePath normalized = FilePath::fromString(globalPath.path()).normalizedPathName();
+ QString path = normalized.path();
+ if (normalized.startsWithDriveLetter()) {
+ const QChar lowerDriveLetter = path.at(0).toLower();
+ path = '/' + lowerDriveLetter + path.mid(2); // strip C:
+ }
+ return path;
+}
+
bool DockerDevice::handlesFile(const FilePath &filePath) const
{
return filePath.scheme() == "docker" && filePath.host() == d->m_data.imageId;
@@ -1570,7 +1576,7 @@ void DockerDevice::runProcess(QtcProcess &process) const
CommandLine cmd{"docker", {"exec"}};
if (!workingDir.isEmpty()) {
- cmd.addArgs({"-w", workingDir.path()});
+ cmd.addArgs({"-w", mapToDevicePath(workingDir)});
if (QTC_GUARD(workingDir.needsDevice())) // warn on local working directory for docker cmd
process.setWorkingDirectory(FileUtils::homePath()); // reset working dir for docker exec
}
diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h
index ded9a93b06..c4d0efe703 100644
--- a/src/plugins/docker/dockerdevice.h
+++ b/src/plugins/docker/dockerdevice.h
@@ -76,6 +76,7 @@ public:
ProjectExplorer::DeviceEnvironmentFetcher::Ptr environmentFetcher() const override;
Utils::FilePath mapToGlobalPath(const Utils::FilePath &pathOnDevice) const override;
+ QString mapToDevicePath(const Utils::FilePath &globalPath) const override;
bool handlesFile(const Utils::FilePath &filePath) const override;
bool isExecutableFile(const Utils::FilePath &filePath) const override;
diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp
index 862dfc12b5..38ab02e0a9 100644
--- a/src/plugins/fakevim/fakevimhandler.cpp
+++ b/src/plugins/fakevim/fakevimhandler.cpp
@@ -1285,7 +1285,7 @@ public:
<< quoteUnprintable(m_text);
}
- friend uint qHash(const Input &i)
+ friend auto qHash(const Input &i)
{
return ::qHash(i.m_key);
}
@@ -6598,13 +6598,12 @@ bool FakeVimHandler::Private::handleExSourceCommand(const ExCommand &cmd)
while (!file.atEnd() || !line.isEmpty()) {
QByteArray nextline = !file.atEnd() ? file.readLine() : QByteArray();
- // remove comment
- int i = nextline.lastIndexOf('"');
- if (i != -1)
- nextline = nextline.remove(i, nextline.size() - i);
-
nextline = nextline.trimmed();
+ // remove full line comment. for being precise, check :help comment in vim.
+ if (nextline.startsWith('"'))
+ continue;
+
// multi-line command?
if (nextline.startsWith('\\')) {
line += nextline.mid(1);
@@ -8201,9 +8200,9 @@ void FakeVimHandler::Private::saveLastVisualMode()
if (isVisualMode() && g.mode == CommandMode && g.submode == NoSubMode) {
setMark('<', markLessPosition());
setMark('>', markGreaterPosition());
+ m_buffer->lastVisualModeInverted = anchor() > position();
+ m_buffer->lastVisualMode = g.visualMode;
}
- m_buffer->lastVisualModeInverted = anchor() > position();
- m_buffer->lastVisualMode = g.visualMode;
}
QWidget *FakeVimHandler::Private::editor() const
diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp
index 8be735072d..fae4f7c835 100644
--- a/src/plugins/fakevim/fakevimplugin.cpp
+++ b/src/plugins/fakevim/fakevimplugin.cpp
@@ -380,7 +380,6 @@ private:
void copyTextEditorSettings();
void setQtStyle();
void setPlainStyle();
- void updateVimRcWidgets();
};
void FakeVimOptionPage::layoutPage(QWidget *widget)
@@ -449,15 +448,14 @@ void FakeVimOptionPage::layoutPage(QWidget *widget)
}.attachTo(widget, true);
+ s.vimRcPath.setEnabler(&s.readVimRc);
+
connect(copyTextEditorSettings, &QAbstractButton::clicked,
this, &FakeVimOptionPage::copyTextEditorSettings);
connect(setQtStyle, &QAbstractButton::clicked,
this, &FakeVimOptionPage::setQtStyle);
connect(setPlainStyle, &QAbstractButton::clicked,
this, &FakeVimOptionPage::setPlainStyle);
- connect(&s.readVimRc, &FvBaseAspect::changed,
- this, &FakeVimOptionPage::updateVimRcWidgets);
- updateVimRcWidgets();
}
void FakeVimOptionPage::copyTextEditorSettings()
@@ -503,13 +501,6 @@ void FakeVimOptionPage::setPlainStyle()
s.passKeys.setVolatileValue(false);
}
-void FakeVimOptionPage::updateVimRcWidgets()
-{
- FakeVimSettings &s = *fakeVimSettings();
- s.vimRcPath.setEnabled(s.readVimRc.value());
-}
-
-
///////////////////////////////////////////////////////////////////////
//
// FakeVimPluginPrivate
diff --git a/src/plugins/ios/iosprobe.cpp b/src/plugins/ios/iosprobe.cpp
index 79fddd3779..7fafba0ef4 100644
--- a/src/plugins/ios/iosprobe.cpp
+++ b/src/plugins/ios/iosprobe.cpp
@@ -160,12 +160,12 @@ bool XcodePlatform::operator==(const XcodePlatform &other) const
return developerPath == other.developerPath;
}
-uint qHash(const XcodePlatform &platform)
+Utils::QHashValueType qHash(const XcodePlatform &platform)
{
return qHash(platform.developerPath);
}
-uint qHash(const XcodePlatform::ToolchainTarget &target)
+Utils::QHashValueType qHash(const XcodePlatform::ToolchainTarget &target)
{
return qHash(target.name);
}
diff --git a/src/plugins/ios/iosprobe.h b/src/plugins/ios/iosprobe.h
index 52cf3015c5..966f669d08 100644
--- a/src/plugins/ios/iosprobe.h
+++ b/src/plugins/ios/iosprobe.h
@@ -24,6 +24,9 @@
****************************************************************************/
#pragma once
+
+#include <utils/porting.h>
+
#include <QSettings>
#include <QSharedPointer>
#include <QString>
@@ -61,8 +64,8 @@ public:
bool operator==(const XcodePlatform &other) const;
};
-uint qHash(const XcodePlatform &platform);
-uint qHash(const XcodePlatform::ToolchainTarget &target);
+Utils::QHashValueType qHash(const XcodePlatform &platform);
+Utils::QHashValueType qHash(const XcodePlatform::ToolchainTarget &target);
class XcodeProbe
{
diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp
index 5f2b44d5e9..b177f82c52 100644
--- a/src/plugins/languageclient/client.cpp
+++ b/src/plugins/languageclient/client.cpp
@@ -1133,7 +1133,14 @@ void Client::handleMessage(const BaseMessage &message)
void Client::log(const QString &message) const
{
- Core::MessageManager::writeFlashing(QString("LanguageClient %1: %2").arg(name(), message));
+ switch (m_logTarget) {
+ case LogTarget::Ui:
+ Core::MessageManager::writeFlashing(QString("LanguageClient %1: %2").arg(name(), message));
+ break;
+ case LogTarget::Console:
+ qCDebug(LOGLSPCLIENT) << message;
+ break;
+ }
}
const ServerCapabilities &Client::capabilities() const
diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h
index bf06359ef4..59f23599a4 100644
--- a/src/plugins/languageclient/client.h
+++ b/src/plugins/languageclient/client.h
@@ -189,6 +189,8 @@ public:
void setCompletionAssistProvider(LanguageClientCompletionAssistProvider *provider);
// logging
+ enum class LogTarget { Console, Ui };
+ void setLogTarget(LogTarget target) { m_logTarget = target; }
void log(const QString &message) const;
template<typename Error>
void log(const LanguageServerProtocol::ResponseError<Error> &responseError) const
@@ -288,6 +290,7 @@ private:
QString m_serverName;
QString m_serverVersion;
LanguageServerProtocol::SymbolStringifier m_symbolStringifier;
+ LogTarget m_logTarget = LogTarget::Ui;
bool m_locatorsEnabled = true;
bool m_autoRequestCodeActions = true;
};
diff --git a/src/plugins/languageclient/languageclientcompletionassist.cpp b/src/plugins/languageclient/languageclientcompletionassist.cpp
index ca38029dc1..3eec6cbbd9 100644
--- a/src/plugins/languageclient/languageclientcompletionassist.cpp
+++ b/src/plugins/languageclient/languageclientcompletionassist.cpp
@@ -425,7 +425,7 @@ void LanguageClientCompletionAssistProcessor::handleCompletionResponse(
items = Utils::get<QList<CompletionItem>>(*result);
}
auto proposalItems = generateCompletionItems(items);
- if (!m_snippetsGroup.isEmpty()) {
+ if (!proposalItems.isEmpty() && !m_snippetsGroup.isEmpty()) {
proposalItems << TextEditor::SnippetAssistCollector(
m_snippetsGroup, QIcon(":/texteditor/images/snippet.png")).collect();
}
diff --git a/src/plugins/modeleditor/modelindexer.cpp b/src/plugins/modeleditor/modelindexer.cpp
index be4e49d9cd..d05cf9ecb3 100644
--- a/src/plugins/modeleditor/modelindexer.cpp
+++ b/src/plugins/modeleditor/modelindexer.cpp
@@ -46,6 +46,7 @@
#include <utils/mimetypes/mimetype.h>
#include <utils/mimetypes/mimedatabase.h>
#include <utils/qtcassert.h>
+#include <utils/porting.h>
#include <QQueue>
#include <QMutex>
@@ -62,7 +63,7 @@ namespace Internal {
class ModelIndexer::QueuedFile
{
- friend uint qHash(const ModelIndexer::QueuedFile &queuedFile);
+ friend Utils::QHashValueType qHash(const ModelIndexer::QueuedFile &queuedFile);
friend bool operator==(const ModelIndexer::QueuedFile &lhs,
const ModelIndexer::QueuedFile &rhs);
@@ -99,7 +100,7 @@ bool operator==(const ModelIndexer::QueuedFile &lhs, const ModelIndexer::QueuedF
return lhs.m_file == rhs.m_file && lhs.m_project == rhs.m_project;
}
-uint qHash(const ModelIndexer::QueuedFile &queuedFile)
+Utils::QHashValueType qHash(const ModelIndexer::QueuedFile &queuedFile)
{
return qHash(queuedFile.m_project) + qHash(queuedFile.m_project);
}
diff --git a/src/plugins/modeleditor/modelindexer.h b/src/plugins/modeleditor/modelindexer.h
index 708ec26e9b..e6df8bdeed 100644
--- a/src/plugins/modeleditor/modelindexer.h
+++ b/src/plugins/modeleditor/modelindexer.h
@@ -25,6 +25,8 @@
#pragma once
+#include <utils/porting.h>
+
#include <QObject>
namespace qmt { class Uid; }
@@ -50,7 +52,7 @@ class ModelIndexer :
class DiagramsCollectorVisitor;
class ModelIndexerPrivate;
- friend uint qHash(const ModelIndexer::QueuedFile &queuedFile);
+ friend Utils::QHashValueType qHash(const ModelIndexer::QueuedFile &queuedFile);
friend bool operator==(const ModelIndexer::QueuedFile &lhs,
const ModelIndexer::QueuedFile &rhs);
diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp
index be00bbf30c..16dd673959 100644
--- a/src/plugins/perforce/perforceplugin.cpp
+++ b/src/plugins/perforce/perforceplugin.cpp
@@ -36,6 +36,7 @@
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/coreconstants.h>
+#include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/icore.h>
@@ -1043,7 +1044,12 @@ bool PerforcePluginPrivate::vcsOpen(const FilePath &workingDir, const QString &f
flags |= SilentStdOut;
}
const PerforceResponse result = runP4Cmd(workingDir, args, flags);
- return !result.error;
+ if (result.error)
+ return false;
+ const FilePath absPath = workingDir.resolvePath(fileName);
+ if (DocumentModel::Entry *e = DocumentModel::entryForFilePath(absPath))
+ e->document->checkPermissions();
+ return true;
}
bool PerforcePluginPrivate::vcsAdd(const FilePath &workingDir, const QString &fileName)
diff --git a/src/plugins/projectexplorer/buildtargetinfo.h b/src/plugins/projectexplorer/buildtargetinfo.h
index 7d786de3ca..34d5f19624 100644
--- a/src/plugins/projectexplorer/buildtargetinfo.h
+++ b/src/plugins/projectexplorer/buildtargetinfo.h
@@ -30,6 +30,7 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
+#include <utils/porting.h>
#include <QList>
@@ -48,7 +49,7 @@ public:
bool isQtcRunnable = true;
bool usesTerminal = false;
- uint runEnvModifierHash = 0; // Make sure to update this when runEnvModifier changes!
+ Utils::QHashValueType runEnvModifierHash = 0; // Make sure to update this when runEnvModifier changes!
std::function<void(Utils::Environment &, bool)> runEnvModifier;
};
diff --git a/src/plugins/projectexplorer/deployablefile.cpp b/src/plugins/projectexplorer/deployablefile.cpp
index 78fbcb165e..7ecfb59409 100644
--- a/src/plugins/projectexplorer/deployablefile.cpp
+++ b/src/plugins/projectexplorer/deployablefile.cpp
@@ -53,7 +53,7 @@ bool DeployableFile::isExecutable() const
return m_type == TypeExecutable;
}
-uint qHash(const DeployableFile &d)
+Utils::QHashValueType qHash(const DeployableFile &d)
{
return qHash(qMakePair(d.localFilePath().toString(), d.remoteDirectory()));
}
diff --git a/src/plugins/projectexplorer/deployablefile.h b/src/plugins/projectexplorer/deployablefile.h
index 4709206b16..e3760e4a02 100644
--- a/src/plugins/projectexplorer/deployablefile.h
+++ b/src/plugins/projectexplorer/deployablefile.h
@@ -28,6 +28,7 @@
#include "projectexplorer_export.h"
#include <utils/fileutils.h>
+#include <utils/porting.h>
#include <QString>
@@ -71,6 +72,6 @@ inline bool operator!=(const DeployableFile &d1, const DeployableFile &d2)
return !(d1 == d2);
}
-PROJECTEXPLORER_EXPORT uint qHash(const DeployableFile &d);
+PROJECTEXPLORER_EXPORT Utils::QHashValueType qHash(const DeployableFile &d);
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
index f7470cd079..1e671e5fe3 100644
--- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
@@ -484,6 +484,18 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager
return device->symLinkTarget(filePath);
};
+ deviceHooks.mapToGlobalPath = [](const FilePath &filePath) {
+ auto device = DeviceManager::deviceForPath(filePath);
+ QTC_ASSERT(device, return FilePath{});
+ return device->mapToGlobalPath(filePath);
+ };
+
+ deviceHooks.mapToDevicePath = [](const FilePath &filePath) {
+ auto device = DeviceManager::deviceForPath(filePath);
+ QTC_ASSERT(device, return QString{});
+ return device->mapToDevicePath(filePath);
+ };
+
deviceHooks.dirEntries = [](const FilePath &filePath, const QStringList &nameFilters,
QDir::Filters filters, QDir::SortFlags sort) {
auto device = DeviceManager::deviceForPath(filePath);
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp
index 8c90a7fc14..f1bedcc441 100644
--- a/src/plugins/projectexplorer/devicesupport/idevice.cpp
+++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp
@@ -212,6 +212,11 @@ FilePath IDevice::mapToGlobalPath(const FilePath &pathOnDevice) const
return pathOnDevice;
}
+QString IDevice::mapToDevicePath(const FilePath &globalPath) const
+{
+ return globalPath.path();
+}
+
bool IDevice::handlesFile(const FilePath &filePath) const
{
Q_UNUSED(filePath);
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h
index 873eb380bb..56bdbb5bd2 100644
--- a/src/plugins/projectexplorer/devicesupport/idevice.h
+++ b/src/plugins/projectexplorer/devicesupport/idevice.h
@@ -237,6 +237,7 @@ public:
bool isAnyUnixDevice() const;
virtual Utils::FilePath mapToGlobalPath(const Utils::FilePath &pathOnDevice) const;
+ virtual QString mapToDevicePath(const Utils::FilePath &globalPath) const;
virtual bool handlesFile(const Utils::FilePath &filePath) const;
virtual bool isExecutableFile(const Utils::FilePath &filePath) const;
diff --git a/src/plugins/projectexplorer/expanddata.cpp b/src/plugins/projectexplorer/expanddata.cpp
index bf3876475d..4b005b8fe5 100644
--- a/src/plugins/projectexplorer/expanddata.cpp
+++ b/src/plugins/projectexplorer/expanddata.cpp
@@ -50,7 +50,7 @@ QVariant ExpandData::toSettings() const
return QVariant::fromValue(QStringList({path, displayName}));
}
-int ProjectExplorer::Internal::qHash(const ExpandData &data)
+Utils::QHashValueType ProjectExplorer::Internal::qHash(const ExpandData &data)
{
return qHash(data.path) ^ qHash(data.displayName);
}
diff --git a/src/plugins/projectexplorer/expanddata.h b/src/plugins/projectexplorer/expanddata.h
index 9d6a656449..a7ff078680 100644
--- a/src/plugins/projectexplorer/expanddata.h
+++ b/src/plugins/projectexplorer/expanddata.h
@@ -25,6 +25,8 @@
#pragma once
+#include <utils/porting.h>
+
#include <QString>
#include <QHash>
#include <QDebug>
@@ -46,7 +48,7 @@ public:
QString displayName;
};
-int qHash(const ExpandData &data);
+Utils::QHashValueType qHash(const ExpandData &data);
} // namespace Internal
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/gccparser.cpp b/src/plugins/projectexplorer/gccparser.cpp
index 6479240c2b..30ebe45bf8 100644
--- a/src/plugins/projectexplorer/gccparser.cpp
+++ b/src/plugins/projectexplorer/gccparser.cpp
@@ -1367,6 +1367,14 @@ void ProjectExplorerPlugin::testGccOutputParsers_data()
CompileTask(Task::Error, ".pch/Qt6Core5Compat: No such file or directory", ".pch/Qt6Core5Compat"),
CompileTask(Task::Warning, "-Wformat-security ignored without -Wformat [-Wformat-security]")}
<< QString();
+
+ QTest::newRow("clean path")
+ << QString("/home/tim/path/to/sources/./and/more.h:15:22: error: blubb")
+ << OutputParserTester::STDERR
+ << QString() << QString()
+ << Tasks{CompileTask(Task::Error, "blubb", "/home/tim/path/to/sources/and/more.h",
+ 15, 22)}
+ << QString();
}
void ProjectExplorerPlugin::testGccOutputParsers()
diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp
index ac73d2a047..31155c276c 100644
--- a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp
+++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp
@@ -87,6 +87,7 @@ const char PAGE_SHORT_TITLE_KEY[] = "trShortTitle";
const char PAGE_INDEX_KEY[] = "index";
const char OPTIONS_KEY[] = "options";
const char PLATFORM_INDEPENDENT_KEY[] = "platformIndependent";
+const char DEFAULT_VALUES[] = "defaultValues";
static QList<JsonWizardPageFactory *> s_pageFactories;
static QList<JsonWizardGeneratorFactory *> s_generatorFactories;
@@ -153,7 +154,131 @@ static JsonWizardFactory::Generator parseGenerator(const QVariant &value, QStrin
return gen;
}
-static JsonWizardFactory::Page parsePage(const QVariant &value, QString *errorMessage)
+//FIXME: createWizardFactories() has an almost identical loop. Make the loop return the results instead of
+//internal processing and create a separate function for it. Then process the results in
+//loadDefaultValues() and createWizardFactories()
+QVariantMap JsonWizardFactory::loadDefaultValues(const QString &fileName)
+{
+ QString verboseLog;
+
+ if (fileName.isEmpty()) {
+ return {};
+ }
+
+ QList <Core::IWizardFactory *> result;
+ foreach (const Utils::FilePath &path, searchPaths()) {
+ if (path.isEmpty())
+ continue;
+
+ FilePath dir = FilePath::fromString(path.toString());
+ if (!dir.exists()) {
+ if (verbose())
+ verboseLog.append(tr("Path \"%1\" does not exist when checking Json wizard search paths.\n")
+ .arg(path.toUserOutput()));
+ continue;
+ }
+
+ const QDir::Filters filters = QDir::Dirs|QDir::Readable|QDir::NoDotAndDotDot;
+ FilePaths dirs = dir.dirEntries(filters);
+
+ while (!dirs.isEmpty()) {
+ const FilePath current = dirs.takeFirst();
+ if (verbose())
+ verboseLog.append(tr("Checking \"%1\" for %2.\n")
+ .arg(QDir::toNativeSeparators(current.absolutePath().toString()))
+ .arg(fileName));
+ if (current.pathAppended(fileName).exists()) {
+ QFile configFile(current.pathAppended(fileName).toString());
+ configFile.open(QIODevice::ReadOnly);
+ QJsonParseError error;
+ const QByteArray fileData = configFile.readAll();
+ const QJsonDocument json = QJsonDocument::fromJson(fileData, &error);
+ configFile.close();
+
+ if (error.error != QJsonParseError::NoError) {
+ int line = 1;
+ int column = 1;
+ for (int i = 0; i < error.offset; ++i) {
+ if (fileData.at(i) == '\n') {
+ ++line;
+ column = 1;
+ } else {
+ ++column;
+ }
+ }
+ verboseLog.append(tr("* Failed to parse \"%1\":%2:%3: %4\n")
+ .arg(configFile.fileName())
+ .arg(line).arg(column)
+ .arg(error.errorString()));
+ continue;
+ }
+
+ if (!json.isObject()) {
+ verboseLog.append(tr("* Did not find a JSON object in \"%1\".\n")
+ .arg(configFile.fileName()));
+ continue;
+ }
+
+ if (verbose())
+ verboseLog.append(tr("* Configuration found and parsed.\n"));
+
+ return json.object().toVariantMap();
+ }
+ FilePaths subDirs = current.dirEntries(filters);
+ if (!subDirs.isEmpty()) {
+ // There is no QList::prepend(QList)...
+ dirs.swap(subDirs);
+ dirs.append(subDirs);
+ } else if (verbose()) {
+ verboseLog.append(tr("JsonWizard: \"%1\" not found\n").arg(fileName));
+ }
+ }
+ }
+
+ if (verbose()) { // Print to output pane for Windows.
+ qWarning("%s", qPrintable(verboseLog));
+ Core::MessageManager::writeDisrupting(verboseLog);
+ }
+
+ return {};
+}
+
+QVariant JsonWizardFactory::mergeDataValueMaps(const QVariant &valueMap, const QVariant &defaultValueMap)
+{
+ QVariantMap retVal;
+
+#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
+ const QVariantMap &map = defaultValueMap.toMap();
+ for (auto it = map.begin(), end = map.end(); it != end; ++it)
+ retVal.insert(it.key(), it.value());
+
+ const QVariantMap &map2 = valueMap.toMap();
+ for (auto it = map2.begin(), end = map2.end(); it != end; ++it)
+ retVal.insert(it.key(), it.value());
+#else
+ retVal.insert(defaultValueMap.toMap());
+ retVal.insert(valueMap.toMap());
+#endif
+ return retVal;
+}
+
+QVariant JsonWizardFactory::getDataValue(const QLatin1String &key, const QVariantMap &valueSet,
+ const QVariantMap &defaultValueSet, const QVariant &notExistValue)
+{
+ QVariant retVal = {};
+
+ if ((valueSet.contains(key) && valueSet.value(key).type() == QVariant::Map) ||
+ (defaultValueSet.contains(key) && defaultValueSet.value(key).type() == QVariant::Map)) {
+ retVal = mergeDataValueMaps(valueSet.value(key), defaultValueSet.value(key));
+ } else {
+ QVariant defaultValue = defaultValueSet.value(key, notExistValue);
+ retVal = valueSet.value(key, defaultValue);
+ }
+
+ return retVal;
+}
+
+JsonWizardFactory::Page JsonWizardFactory::parsePage(const QVariant &value, QString *errorMessage)
{
JsonWizardFactory::Page p;
@@ -163,7 +288,12 @@ static JsonWizardFactory::Page parsePage(const QVariant &value, QString *errorMe
}
const QVariantMap data = value.toMap();
- const QString strVal = data.value(QLatin1String(TYPE_ID_KEY)).toString();
+ QString defaultValueFile = data.value(QLatin1String(DEFAULT_VALUES)).toString();
+ if (!defaultValueFile.isEmpty())
+ defaultValueFile.append(QLatin1String(".json"));
+ const QVariantMap defaultData = loadDefaultValues(defaultValueFile);
+
+ const QString strVal = getDataValue(QLatin1String(TYPE_ID_KEY), data, defaultData).toString();
if (strVal.isEmpty()) {
*errorMessage = QCoreApplication::translate("ProjectExplorer::JsonWizardFactory", "Page has no typeId set.");
return p;
@@ -180,21 +310,31 @@ static JsonWizardFactory::Page parsePage(const QVariant &value, QString *errorMe
return p;
}
- const QString title = JsonWizardFactory::localizedString(data.value(QLatin1String(DISPLAY_NAME_KEY)));
- const QString subTitle = JsonWizardFactory::localizedString(data.value(QLatin1String(PAGE_SUB_TITLE_KEY)));
- const QString shortTitle = JsonWizardFactory::localizedString(data.value(QLatin1String(PAGE_SHORT_TITLE_KEY)));
+ const QString title = JsonWizardFactory::localizedString(getDataValue(QLatin1String(DISPLAY_NAME_KEY), data, defaultData));
+ const QString subTitle = JsonWizardFactory::localizedString(getDataValue(QLatin1String(PAGE_SUB_TITLE_KEY), data, defaultData));
+ const QString shortTitle = JsonWizardFactory::localizedString(getDataValue(QLatin1String(PAGE_SHORT_TITLE_KEY), data, defaultData));
bool ok;
- int index = data.value(QLatin1String(PAGE_INDEX_KEY), -1).toInt(&ok);
+ int index = getDataValue(QLatin1String(PAGE_INDEX_KEY), data, defaultData, -1).toInt(&ok);
if (!ok) {
*errorMessage = QCoreApplication::translate("ProjectExplorer::JsonWizardFactory", "Page with typeId \"%1\" has invalid \"index\".")
.arg(typeId.toString());
return p;
}
- QVariant enabled = data.value(QLatin1String(ENABLED_EXPRESSION_KEY), true);
+ QVariant enabled = getDataValue(QLatin1String(ENABLED_EXPRESSION_KEY), data, defaultData, true);
+
+ QVariant specifiedSubData = data.value(QLatin1String(DATA_KEY));
+ QVariant defaultSubData = defaultData.value(QLatin1String(DATA_KEY));
+ QVariant subData;
+
+ if (specifiedSubData.isNull())
+ subData = defaultSubData;
+ else if (specifiedSubData.type() == QVariant::Map)
+ subData = mergeDataValueMaps(specifiedSubData.toMap(), defaultSubData.toMap());
+ else if (specifiedSubData.type() == QVariant::List)
+ subData = specifiedSubData;
- QVariant subData = data.value(QLatin1String(DATA_KEY));
if (!factory->validateData(typeId, subData, errorMessage))
return p;
@@ -209,6 +349,9 @@ static JsonWizardFactory::Page parsePage(const QVariant &value, QString *errorMe
return p;
}
+//FIXME: loadDefaultValues() has an almost identical loop. Make the loop return the results instead of
+//internal processing and create a separate function for it. Then process the results in
+//loadDefaultValues() and loadDefaultValues()
QList<Core::IWizardFactory *> JsonWizardFactory::createWizardFactories()
{
QString errorMessage;
@@ -258,14 +401,12 @@ QList<Core::IWizardFactory *> JsonWizardFactory::createWizardFactories()
.arg(currentFile.fileName())
.arg(line).arg(column)
.arg(error.errorString()));
- qWarning() << "Failed to parse wizard: " << currentFile.fileName();
continue;
}
if (!json.isObject()) {
verboseLog.append(tr("* Did not find a JSON object in \"%1\".\n")
.arg(currentFile.fileName()));
- qWarning() << "Failed to parse wizard: " << currentFile.fileName();
continue;
}
@@ -283,7 +424,6 @@ QList<Core::IWizardFactory *> JsonWizardFactory::createWizardFactories()
JsonWizardFactory *factory = createWizardFactory(data, currentDir, &errorMessage);
if (!factory) {
verboseLog.append(tr("* Failed to create: %1\n").arg(errorMessage));
- qWarning() << "Failed to create wizard: " << currentFile.fileName();
continue;
}
diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h
index c3c6c5bd34..d69aaa00f0 100644
--- a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h
+++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h
@@ -100,6 +100,12 @@ private:
static void destroyAllFactories();
bool initialize(const QVariantMap &data, const Utils::FilePath &baseDir, QString *errorMessage);
+ JsonWizardFactory::Page parsePage(const QVariant &value, QString *errorMessage);
+ QVariantMap loadDefaultValues(const QString &fileName);
+ QVariant getDataValue(const QLatin1String &key, const QVariantMap &valueSet,
+ const QVariantMap &defaultValueSet, const QVariant &notExistValue={});
+ QVariant mergeDataValueMaps(const QVariant &valueMap, const QVariant &defaultValueMap);
+
QVariant m_enabledExpression;
Utils::FilePath m_wizardDir;
QList<Generator> m_generators;
diff --git a/src/plugins/projectexplorer/parseissuesdialog.cpp b/src/plugins/projectexplorer/parseissuesdialog.cpp
index 75be7e7bcb..f81db2da7d 100644
--- a/src/plugins/projectexplorer/parseissuesdialog.cpp
+++ b/src/plugins/projectexplorer/parseissuesdialog.cpp
@@ -32,9 +32,6 @@
#include "projectexplorerconstants.h"
#include "taskhub.h"
-#include <coreplugin/progressmanager/progressmanager.h>
-#include <utils/runextensions.h>
-
#include <QButtonGroup>
#include <QCheckBox>
#include <QDialogButtonBox>
@@ -137,21 +134,6 @@ ParseIssuesDialog::~ParseIssuesDialog()
delete d;
}
-static void parse(QFutureInterface<void> &future, const QString &output,
- const std::unique_ptr<Utils::OutputFormatter> &parser, bool isStderr)
-{
- const QStringList lines = output.split('\n');
- future.setProgressRange(0, lines.count());
- const Utils::OutputFormat format = isStderr ? Utils::StdErrFormat : Utils::StdOutFormat;
- for (const QString &line : lines) {
- parser->appendMessage(line + '\n', format);
- future.setProgressValue(future.progressValue() + 1);
- if (future.isCanceled())
- return;
- }
- parser->flush();
-}
-
void ParseIssuesDialog::accept()
{
const QList<Utils::OutputLineParser *> lineParsers =
@@ -161,14 +143,16 @@ void ParseIssuesDialog::accept()
"not provide an output parser."));
return;
}
- std::unique_ptr<Utils::OutputFormatter> parser(new Utils::OutputFormatter);
- parser->setLineParsers(lineParsers);
+ Utils::OutputFormatter parser;
+ parser.setLineParsers(lineParsers);
if (d->clearTasksCheckBox.isChecked())
TaskHub::clearTasks();
- const QFuture<void> f = Utils::runAsync(&parse, d->compileOutputEdit.toPlainText(),
- std::move(parser), d->stderrCheckBox.isChecked());
- Core::ProgressManager::addTask(f, tr("Parsing build output"),
- "ProgressExplorer.ParseExternalBuildOutput");
+ const QStringList lines = d->compileOutputEdit.toPlainText().split('\n');
+ const Utils::OutputFormat format = d->stderrCheckBox.isChecked()
+ ? Utils::StdErrFormat : Utils::StdOutFormat;
+ for (const QString &line : lines)
+ parser.appendMessage(line + '\n', format);
+ parser.flush();
QDialog::accept();
}
diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp
index a49011bc41..b283862c46 100644
--- a/src/plugins/projectexplorer/project.cpp
+++ b/src/plugins/projectexplorer/project.cpp
@@ -376,7 +376,7 @@ void Project::setExtraProjectFiles(const QSet<FilePath> &projectDocumentPaths,
const QSet<FilePath> toAdd = uniqueNewFiles - existingWatches;
const QSet<FilePath> toRemove = existingWatches - uniqueNewFiles;
- erase(d->m_extraProjectDocuments, [&toRemove](const std::unique_ptr<IDocument> &d) {
+ Utils::erase(d->m_extraProjectDocuments, [&toRemove](const std::unique_ptr<IDocument> &d) {
return toRemove.contains(d->filePath());
});
if (docUpdater) {
@@ -586,7 +586,8 @@ void Project::setRootProjectNode(std::unique_ptr<ProjectNode> &&root)
}
if (root) {
- ProjectTree::applyTreeManager(root.get());
+ ProjectTree::applyTreeManager(root.get(), ProjectTree::AsyncPhase);
+ ProjectTree::applyTreeManager(root.get(), ProjectTree::FinalPhase);
root->setParentFolderNode(d->m_containerNode.get());
}
@@ -804,8 +805,9 @@ void Project::createTargetFromMap(const QVariantMap &map, int index)
deviceTypeId = Constants::DESKTOP_DEVICE_TYPE;
const QString formerKitName = targetMap.value(Target::displayNameKey()).toString();
k = KitManager::registerKit([deviceTypeId, &formerKitName](Kit *kit) {
- const QString tempKitName = makeUniquelyNumbered(
- tr("Replacement for \"%1\"").arg(formerKitName),
+ const QString kitNameSuggestion = formerKitName.contains(tr("Replacement for"))
+ ? formerKitName : tr("Replacement for \"%1\"").arg(formerKitName);
+ const QString tempKitName = makeUniquelyNumbered(kitNameSuggestion,
transform(KitManager::kits(), &Kit::unexpandedDisplayName));
kit->setUnexpandedDisplayName(tempKitName);
DeviceTypeKitAspect::setDeviceTypeId(kit, deviceTypeId);
diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp
index f7fe67d213..46db91993a 100644
--- a/src/plugins/projectexplorer/projectexplorer.cpp
+++ b/src/plugins/projectexplorer/projectexplorer.cpp
@@ -2602,7 +2602,7 @@ void ProjectExplorerPluginPrivate::restoreSession()
dd->m_arguments = arguments;
// delay opening projects from the command line even more
QTimer::singleShot(0, m_instance, []() {
- ICore::openFiles(Utils::transform(dd->m_arguments, &FilePath::fromString),
+ ICore::openFiles(Utils::transform(dd->m_arguments, &FilePath::fromUserInput),
ICore::OpenFilesFlags(ICore::CanContainLineAndColumnNumbers | ICore::SwitchMode));
emit m_instance->finishedInitialization();
});
diff --git a/src/plugins/projectexplorer/projecttree.cpp b/src/plugins/projectexplorer/projecttree.cpp
index aa8e79f9ef..ee040d5a37 100644
--- a/src/plugins/projectexplorer/projecttree.cpp
+++ b/src/plugins/projectexplorer/projecttree.cpp
@@ -402,13 +402,13 @@ void ProjectTree::registerTreeManager(const TreeManagerFunction &treeChange)
s_instance->m_treeManagers.append(treeChange);
}
-void ProjectTree::applyTreeManager(FolderNode *folder)
+void ProjectTree::applyTreeManager(FolderNode *folder, ConstructionPhase phase)
{
if (!folder)
return;
for (TreeManagerFunction &f : s_instance->m_treeManagers)
- f(folder);
+ f(folder, phase);
}
bool ProjectTree::hasNode(const Node *node)
diff --git a/src/plugins/projectexplorer/projecttree.h b/src/plugins/projectexplorer/projecttree.h
index 060dad2290..3947666d3a 100644
--- a/src/plugins/projectexplorer/projecttree.h
+++ b/src/plugins/projectexplorer/projecttree.h
@@ -68,6 +68,11 @@ public:
const bool m_active = false;
};
+ enum ConstructionPhase {
+ AsyncPhase,
+ FinalPhase
+ };
+
// Integration with ProjectTreeWidget
static void registerWidget(Internal::ProjectTreeWidget *widget);
static void unregisterWidget(Internal::ProjectTreeWidget *widget);
@@ -79,9 +84,9 @@ public:
static void highlightProject(Project *project, const QString &message);
- using TreeManagerFunction = std::function<void(FolderNode *)>;
+ using TreeManagerFunction = std::function<void(FolderNode *, ConstructionPhase)>;
static void registerTreeManager(const TreeManagerFunction &treeChange);
- static void applyTreeManager(FolderNode *folder);
+ static void applyTreeManager(FolderNode *folder, ConstructionPhase phase);
// Nodes:
static bool hasNode(const Node *node);
diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp
index 0a3a2634b0..2291a86fbd 100644
--- a/src/plugins/projectexplorer/runconfigurationaspects.cpp
+++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp
@@ -251,7 +251,9 @@ FilePath WorkingDirectoryAspect::workingDirectory() const
const Environment env = m_envAspect ? m_envAspect->environment()
: Environment::systemEnvironment();
FilePath res = m_workingDirectory;
- const QString workingDir = m_workingDirectory.path();
+ QString workingDir = m_workingDirectory.path();
+ if (m_macroExpander)
+ workingDir = m_macroExpander->expandProcessArgs(workingDir);
res.setPath(PathChooser::expandedDirectory(workingDir, env, QString()));
return res;
}
diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp
index 4cd0665f09..9ca0e228c3 100644
--- a/src/plugins/projectexplorer/runcontrol.cpp
+++ b/src/plugins/projectexplorer/runcontrol.cpp
@@ -349,7 +349,7 @@ public:
IDevice::ConstPtr device;
Utils::Id runMode;
Utils::Icon icon;
- const MacroExpander *macroExpander;
+ const MacroExpander *macroExpander = nullptr;
QPointer<RunConfiguration> runConfiguration; // Not owned. Avoid use.
QString buildKey;
QMap<Utils::Id, QVariantMap> settingsData;
@@ -389,11 +389,12 @@ void RunControl::setRunConfiguration(RunConfiguration *runConfig)
d->runConfigId = runConfig->id();
d->runnable = runConfig->runnable();
d->displayName = runConfig->expandedDisplayName();
- d->macroExpander = runConfig->macroExpander();
d->buildKey = runConfig->buildKey();
d->settingsData = runConfig->aspectData();
setTarget(runConfig->target());
+
+ d->macroExpander = runConfig->macroExpander();
}
void RunControl::setTarget(Target *target)
@@ -412,6 +413,7 @@ void RunControl::setTarget(Target *target)
}
setKit(target->kit());
+ d->macroExpander = target->macroExpander();
d->project = target->project();
}
@@ -420,6 +422,7 @@ void RunControl::setKit(Kit *kit)
QTC_ASSERT(kit, return);
QTC_CHECK(!d->kit);
d->kit = kit;
+ d->macroExpander = kit->macroExpander();
if (d->runnable.device)
setDevice(d->runnable.device);
diff --git a/src/plugins/projectexplorer/task.cpp b/src/plugins/projectexplorer/task.cpp
index fe880442ba..3ef9e397a9 100644
--- a/src/plugins/projectexplorer/task.cpp
+++ b/src/plugins/projectexplorer/task.cpp
@@ -171,7 +171,7 @@ bool operator<(const Task &a, const Task &b)
}
-uint qHash(const Task &task)
+Utils::QHashValueType qHash(const Task &task)
{
return task.taskId;
}
diff --git a/src/plugins/projectexplorer/task.h b/src/plugins/projectexplorer/task.h
index a21332c708..b95d51d34e 100644
--- a/src/plugins/projectexplorer/task.h
+++ b/src/plugins/projectexplorer/task.h
@@ -29,6 +29,7 @@
#include <utils/id.h>
#include <utils/fileutils.h>
+#include <utils/porting.h>
#include <QIcon>
#include <QMetaType>
@@ -135,13 +136,13 @@ public:
using Tasks = QVector<Task>;
-bool PROJECTEXPLORER_EXPORT operator==(const Task &t1, const Task &t2);
-uint PROJECTEXPLORER_EXPORT qHash(const Task &task);
+PROJECTEXPLORER_EXPORT bool operator==(const Task &t1, const Task &t2);
+PROJECTEXPLORER_EXPORT Utils::QHashValueType qHash(const Task &task);
-bool PROJECTEXPLORER_EXPORT operator<(const Task &a, const Task &b);
+PROJECTEXPLORER_EXPORT bool operator<(const Task &a, const Task &b);
-QString PROJECTEXPLORER_EXPORT toHtml(const Tasks &issues);
-bool PROJECTEXPLORER_EXPORT containsType(const Tasks &issues, Task::TaskType);
+PROJECTEXPLORER_EXPORT QString toHtml(const Tasks &issues);
+PROJECTEXPLORER_EXPORT bool containsType(const Tasks &issues, Task::TaskType);
} //namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/treescanner.cpp b/src/plugins/projectexplorer/treescanner.cpp
index ce6da40b1c..88e7b8d4a8 100644
--- a/src/plugins/projectexplorer/treescanner.cpp
+++ b/src/plugins/projectexplorer/treescanner.cpp
@@ -159,7 +159,7 @@ static std::unique_ptr<FolderNode> createFolderNode(const Utils::FilePath &direc
std::unique_ptr<FileNode> node(fn->clone());
fileSystemNode->addNestedNode(std::move(node));
}
- ProjectTree::applyTreeManager(fileSystemNode.get()); // QRC nodes
+ ProjectTree::applyTreeManager(fileSystemNode.get(), ProjectTree::AsyncPhase); // QRC nodes
return fileSystemNode;
}
diff --git a/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp b/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp
index c481975753..7e3c31c378 100644
--- a/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp
+++ b/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp
@@ -232,7 +232,7 @@ QbsProjectNode *QbsNodeTreeBuilder::buildTree(const QString &projectName,
}
buildSystemFiles->compress();
root->addNode(std::move(buildSystemFiles));
- ProjectTree::applyTreeManager(root.get()); // QRC nodes
+ ProjectTree::applyTreeManager(root.get(), ProjectTree::AsyncPhase); // QRC nodes
return root.release();
}
diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
index f64aecd296..b9c048764e 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
@@ -69,8 +69,8 @@ namespace QmakeProjectManager {
static Q_LOGGING_CATEGORY(qmakeParse, "qtc.qmake.parsing", QtWarningMsg);
-uint qHash(Variable key, uint seed) { return ::qHash(static_cast<int>(key), seed); }
-uint qHash(FileOrigin fo) { return ::qHash(int(fo)); }
+Utils::QHashValueType qHash(Variable key, uint seed) { return ::qHash(static_cast<int>(key), seed); }
+Utils::QHashValueType qHash(FileOrigin fo) { return ::qHash(int(fo)); }
namespace Internal {
diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
index cfa86b181f..9a4a22690e 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
+++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
@@ -31,6 +31,7 @@
#include <coreplugin/idocument.h>
#include <cppeditor/generatedcodemodelsupport.h>
+#include <utils/porting.h>
#include <utils/textfileformat.h>
#include <QFutureWatcher>
@@ -109,7 +110,7 @@ enum class Variable {
QmakeCc,
QmakeCxx
};
-uint qHash(Variable key, uint seed = 0);
+Utils::QHashValueType qHash(Variable key, uint seed = 0);
namespace Internal {
Q_DECLARE_LOGGING_CATEGORY(qmakeNodesLog)
@@ -121,7 +122,7 @@ class QmakePriFileEvalResult;
class InstallsList;
enum class FileOrigin { ExactParse, CumulativeParse };
-uint qHash(FileOrigin fo);
+Utils::QHashValueType qHash(FileOrigin fo);
using SourceFile = QPair<Utils::FilePath, FileOrigin>;
using SourceFiles = QSet<SourceFile>;
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt
index ce73af5161..0fe3867a7e 100644
--- a/src/plugins/qmldesigner/CMakeLists.txt
+++ b/src/plugins/qmldesigner/CMakeLists.txt
@@ -4,6 +4,7 @@ if (APPLE)
endif()
add_qtc_plugin(QmlDesigner
+ CONDITION TARGET Qt5::QuickWidgets
DEPENDS
QmlJS LanguageUtils QmlEditorWidgets AdvancedDockingSystem
Qt5::QuickWidgets Qt5::CorePrivate Sqlite
@@ -27,6 +28,7 @@ add_qtc_plugin(QmlDesigner
documentmanager.cpp documentmanager.h
documentwarningwidget.cpp documentwarningwidget.h
generateresource.cpp generateresource.h
+ generatecmakelists.cpp generatecmakelists.h
openuiqmlfiledialog.cpp openuiqmlfiledialog.h openuiqmlfiledialog.ui
qmldesignerconstants.h
qmldesignericons.h
diff --git a/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp
index 872a004fcc..5b8a4934dd 100644
--- a/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp
+++ b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp
@@ -46,7 +46,7 @@ AbstractEditorDialog::AbstractEditorDialog(QWidget *parent, const QString &title
{
setWindowFlag(Qt::Tool, true);
setWindowTitle(defaultTitle());
- setModal(false);
+ setModal(true);
setupJSEditor();
setupUIComponents();
@@ -111,11 +111,10 @@ void AbstractEditorDialog::setupJSEditor()
{
static BindingEditorFactory f;
m_editor = qobject_cast<TextEditor::BaseTextEditor*>(f.createEditor());
- m_editorWidget = qobject_cast<BindingEditorWidget*>(m_editor->editorWidget());
+ Q_ASSERT(m_editor);
- Core::Context context = m_editor->context();
- context.prepend(BINDINGEDITOR_CONTEXT_ID);
- m_editorWidget->m_context->setContext(context);
+ m_editorWidget = qobject_cast<BindingEditorWidget*>(m_editor->editorWidget());
+ Q_ASSERT(m_editorWidget);
auto qmlDesignerEditor = QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor();
diff --git a/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.h b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.h
index ed8cdd0a13..76de79b195 100644
--- a/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.h
+++ b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.h
@@ -26,9 +26,10 @@
#ifndef ABSTRACTEDITORDIALOG_H
#define ABSTRACTEDITORDIALOG_H
-#include <bindingeditor/bindingeditorwidget.h>
#include <qmldesignercorelib_global.h>
+
#include <texteditor/texteditor.h>
+#include <bindingeditor/bindingeditorwidget.h>
#include <QDialog>
diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp
index 89364c5116..1f2a8224d3 100644
--- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp
+++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp
@@ -286,10 +286,13 @@ void ActionEditor::prepareConnections()
m_dialog->setAllConnections(connections, singletons, states);
}
-void ActionEditor::updateWindowName()
+void ActionEditor::updateWindowName(const QString &targetName)
{
if (!m_dialog.isNull()) {
- m_dialog->setWindowTitle(m_dialog->defaultTitle());
+ if (targetName.isEmpty())
+ m_dialog->setWindowTitle(m_dialog->defaultTitle());
+ else
+ m_dialog->setWindowTitle(m_dialog->defaultTitle() + " [" + targetName + "]");
m_dialog->raise();
}
}
diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h
index c0356e81c4..09597bc8d1 100644
--- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h
+++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h
@@ -64,7 +64,7 @@ public:
void prepareConnections();
- Q_INVOKABLE void updateWindowName();
+ Q_INVOKABLE void updateWindowName(const QString &targetName = {});
signals:
void accepted();
diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp
index d28457e1f5..eff90366a0 100644
--- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp
+++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp
@@ -42,8 +42,6 @@
namespace QmlDesigner {
-static BindingEditor *s_lastBindingEditor = nullptr;
-
BindingEditor::BindingEditor(QObject *)
{
}
@@ -62,11 +60,6 @@ void BindingEditor::prepareDialog()
{
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_BINDINGEDITOR_OPENED);
- if (s_lastBindingEditor)
- s_lastBindingEditor->hideWidget();
-
- s_lastBindingEditor = this;
-
m_dialog = new BindingEditorDialog(Core::ICore::dialogParent());
QObject::connect(m_dialog, &AbstractEditorDialog::accepted,
@@ -91,9 +84,6 @@ void BindingEditor::showWidget(int x, int y)
void BindingEditor::hideWidget()
{
- if (s_lastBindingEditor == this)
- s_lastBindingEditor = nullptr;
-
if (m_dialog) {
m_dialog->unregisterAutoCompletion(); //we have to do it separately, otherwise we have an autocompletion action override
m_dialog->close();
@@ -125,6 +115,12 @@ void BindingEditor::setBackendValue(const QVariant &backendValue)
if (node.isValid()) {
m_backendValueTypeName = node.metaInfo().propertyTypeName(propertyEditorValue->name());
+ QString nodeId = node.id();
+ if (nodeId.isEmpty())
+ nodeId = node.simplifiedTypeName();
+
+ m_targetName = nodeId + "." + propertyEditorValue->name();
+
if (m_backendValueTypeName == "alias" || m_backendValueTypeName == "unknown")
if (QmlObjectNode::isValidQmlObjectNode(node))
m_backendValueTypeName = QmlObjectNode(node).instanceType(propertyEditorValue->name());
@@ -164,6 +160,12 @@ void BindingEditor::setStateModelNode(const QVariant &stateModelNode)
}
}
+void BindingEditor::setStateName(const QString &name)
+{
+ m_targetName = name;
+ m_targetName += ".when";
+}
+
void BindingEditor::setModelNode(const ModelNode &modelNode)
{
if (modelNode.isValid())
@@ -177,6 +179,11 @@ void BindingEditor::setBackendValueTypeName(const TypeName &backendValueTypeName
emit backendValueChanged();
}
+void BindingEditor::setTargetName(const QString &target)
+{
+ m_targetName = target;
+}
+
void BindingEditor::prepareBindings()
{
if (!m_modelNode.isValid() || m_backendValueTypeName.isEmpty())
@@ -279,8 +286,26 @@ void BindingEditor::prepareBindings()
void BindingEditor::updateWindowName()
{
- if (!m_dialog.isNull() && !m_backendValueTypeName.isEmpty())
- m_dialog->setWindowTitle(m_dialog->defaultTitle() + " [" + m_backendValueTypeName + "]");
+ if (!m_dialog.isNull() && !m_backendValueTypeName.isEmpty()) {
+ const QString targetString = " ["
+ + (m_targetName.isEmpty() ? QString() : (m_targetName + ": "))
+ + QString::fromUtf8(m_backendValueTypeName) + "]";
+
+ m_dialog->setWindowTitle(m_dialog->defaultTitle() + targetString);
+ }
+}
+
+QString BindingEditor::targetName() const
+{
+ return m_targetName;
+}
+
+QString BindingEditor::stateName() const
+{
+ if (m_targetName.endsWith(".when"))
+ return m_targetName.chopped(5);
+
+ return {};
}
QVariant BindingEditor::backendValue() const
diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h
index 495462128f..738d9c7101 100644
--- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h
+++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h
@@ -44,6 +44,7 @@ class BindingEditor : public QObject
Q_PROPERTY(QVariant backendValueProperty READ backendValue WRITE setBackendValue NOTIFY backendValueChanged)
Q_PROPERTY(QVariant modelNodeBackendProperty READ modelNodeBackend WRITE setModelNodeBackend NOTIFY modelNodeBackendChanged)
Q_PROPERTY(QVariant stateModelNodeProperty READ stateModelNode WRITE setStateModelNode NOTIFY stateModelNodeChanged)
+ Q_PROPERTY(QString stateNameProperty READ stateName WRITE setStateName)
public:
BindingEditor(QObject *parent = nullptr);
@@ -64,15 +65,21 @@ public:
void setModelNodeBackend(const QVariant &modelNodeBackend);
//2. modelnode (this one also sets backend value type name to bool)
+ //State Name is not mandatory, but used in bindingEditor dialog name
void setStateModelNode(const QVariant &stateModelNode);
+ void setStateName(const QString &name);
- //3. modelnode + backend value type name
+ //3. modelnode + backend value type name + optional target name
void setModelNode(const ModelNode &modelNode);
void setBackendValueTypeName(const TypeName &backendValueTypeName);
+ void setTargetName(const QString &target);
Q_INVOKABLE void prepareBindings();
Q_INVOKABLE void updateWindowName();
+ QString targetName() const;
+ QString stateName() const;
+
signals:
void accepted();
void rejected();
@@ -93,6 +100,7 @@ private:
QVariant m_stateModelNode;
QmlDesigner::ModelNode m_modelNode;
TypeName m_backendValueTypeName;
+ QString m_targetName;
};
}
diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp
index 6cd810df37..5b9c54b4d4 100644
--- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp
+++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp
@@ -35,6 +35,10 @@
#include <qmljseditor/qmljseditordocument.h>
#include <qmljseditor/qmljssemantichighlighter.h>
#include <qmljstools/qmljsindenter.h>
+#include <qmljstools/qmljstoolsconstants.h>
+
+#include <projectexplorer/projectexplorerconstants.h>
+#include <utils/fancylineedit.h>
#include <QAction>
@@ -43,17 +47,19 @@ namespace QmlDesigner {
BindingEditorWidget::BindingEditorWidget()
: m_context(new Core::IContext(this))
{
+ Core::Context context(BINDINGEDITOR_CONTEXT_ID,
+ ProjectExplorer::Constants::QMLJS_LANGUAGE_ID);
+
m_context->setWidget(this);
+ m_context->setContext(context);
Core::ICore::addContextObject(m_context);
- const Core::Context context(BINDINGEDITOR_CONTEXT_ID);
-
/*
* We have to register our own active auto completion shortcut, because the original short cut will
* use the cursor position of the original editor in the editor manager.
*/
-
m_completionAction = new QAction(tr("Trigger Completion"), this);
+
Core::Command *command = Core::ActionManager::registerAction(
m_completionAction, TextEditor::Constants::COMPLETE_THIS, context);
command->setDefaultKeySequence(QKeySequence(
@@ -84,11 +90,9 @@ bool BindingEditorWidget::event(QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
- if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) {
+ if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && !keyEvent->modifiers()) {
emit returnKeyClicked();
return true;
- } else {
- return QmlJSEditor::QmlJSEditorWidget::event(event);
}
}
return QmlJSEditor::QmlJSEditorWidget::event(event);
@@ -133,8 +137,12 @@ void BindingDocument::triggerPendingUpdates()
BindingEditorFactory::BindingEditorFactory()
{
setId(BINDINGEDITOR_CONTEXT_ID);
- setDisplayName(QCoreApplication::translate("OpenWith::Editors", QmlDesigner::BINDINGEDITOR_CONTEXT_ID));
+ setDisplayName(QCoreApplication::translate("OpenWith::Editors", BINDINGEDITOR_CONTEXT_ID));
setEditorActionHandlers(0);
+ addMimeType(BINDINGEDITOR_CONTEXT_ID);
+ addMimeType(QmlJSTools::Constants::QML_MIMETYPE);
+ addMimeType(QmlJSTools::Constants::QMLTYPES_MIMETYPE);
+ addMimeType(QmlJSTools::Constants::JS_MIMETYPE);
setDocumentCreator([]() { return new BindingDocument; });
setEditorWidgetCreator([]() { return new BindingEditorWidget; });
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
index 8ca977e742..d9a67a8400 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
@@ -290,8 +290,8 @@ QHash<QString, QStringList> DesignerActionManager::handleExternalAssetsDrop(cons
for (const QString &category : categories) {
AddResourceOperation operation = categoryOperation.value(category);
QStringList files = categoryFiles.value(category);
- bool success = operation(files, {});
- if (success)
+ AddFilesResult result = operation(files, {});
+ if (result == AddFilesResult::Succeeded)
addedCategoryFiles.insert(category, files);
}
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
index 60e2c7562f..e86e441b14 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
@@ -28,6 +28,7 @@
#include <qmldesignercorelib_global.h>
#include "actioninterface.h"
#include "modelnode.h"
+#include "modelnodeoperations.h"
#include <coreplugin/actionmanager/command.h>
#include <utils/styledbar.h>
@@ -45,7 +46,7 @@ namespace QmlDesigner {
class DesignerActionManagerView;
-using AddResourceOperation = std::function<bool (const QStringList &, const QString &)>;
+using AddResourceOperation = std::function<AddFilesResult (const QStringList &, const QString &)>;
using ModelNodePreviewImageOperation = std::function<QVariant (const ModelNode &)>;
struct AddResourceHandler
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
index ab2853354c..b486740495 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
@@ -992,13 +992,15 @@ Utils::FilePath projectFilePath()
return Utils::FilePath();
}
-static bool addFilesToProject(const QStringList &fileNames, const QString &defaultDirectory)
+static AddFilesResult addFilesToProject(const QStringList &fileNames, const QString &defaultDirectory)
{
QString directory = AddImagesDialog::getDirectory(fileNames, defaultDirectory);
if (directory.isEmpty())
- return false;
+ return AddFilesResult::Cancelled;
+
+ DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument();
+ QTC_ASSERT(document, return AddFilesResult::Failed);
- bool allSuccessful = true;
QList<QPair<QString, QString>> copyList;
QStringList removeList;
for (const QString &fileName : fileNames) {
@@ -1021,26 +1023,21 @@ static bool addFilesToProject(const QStringList &fileNames, const QString &defau
// unnecessarily refreshing file models multiple times during the operation
for (const auto &file : qAsConst(removeList))
QFile::remove(file);
+
for (const auto &filePair : qAsConst(copyList)) {
const bool success = QFile::copy(filePair.first, filePair.second);
-
- auto document = QmlDesignerPlugin::instance()->currentDesignDocument();
-
- QTC_ASSERT(document, return false);
-
- if (success) {
- ProjectExplorer::Node *node = ProjectExplorer::ProjectTree::nodeForFile(document->fileName());
- if (node) {
- ProjectExplorer::FolderNode *containingFolder = node->parentFolderNode();
- if (containingFolder)
- containingFolder->addFiles({Utils::FilePath::fromString(filePair.second)});
- }
- } else {
- allSuccessful = false;
+ if (!success)
+ return AddFilesResult::Failed;
+
+ ProjectExplorer::Node *node = ProjectExplorer::ProjectTree::nodeForFile(document->fileName());
+ if (node) {
+ ProjectExplorer::FolderNode *containingFolder = node->parentFolderNode();
+ if (containingFolder)
+ containingFolder->addFiles({Utils::FilePath::fromString(filePair.second)});
}
}
- return allSuccessful;
+ return AddFilesResult::Succeeded;
}
static QString getAssetDefaultDirectory(const QString &assetDir, const QString &defaultDirectory)
@@ -1060,22 +1057,22 @@ static QString getAssetDefaultDirectory(const QString &assetDir, const QString &
return adjustedDefaultDirectory;
}
-bool addFontToProject(const QStringList &fileNames, const QString &defaultDirectory)
+AddFilesResult addFontToProject(const QStringList &fileNames, const QString &defaultDirectory)
{
return addFilesToProject(fileNames, getAssetDefaultDirectory("fonts", defaultDirectory));
}
-bool addSoundToProject(const QStringList &fileNames, const QString &defaultDirectory)
+AddFilesResult addSoundToProject(const QStringList &fileNames, const QString &defaultDirectory)
{
return addFilesToProject(fileNames, getAssetDefaultDirectory("sounds", defaultDirectory));
}
-bool addShaderToProject(const QStringList &fileNames, const QString &defaultDirectory)
+AddFilesResult addShaderToProject(const QStringList &fileNames, const QString &defaultDirectory)
{
return addFilesToProject(fileNames, getAssetDefaultDirectory("shaders", defaultDirectory));
}
-bool addImageToProject(const QStringList &fileNames, const QString &defaultDirectory)
+AddFilesResult addImageToProject(const QStringList &fileNames, const QString &defaultDirectory)
{
return addFilesToProject(fileNames, getAssetDefaultDirectory("images", defaultDirectory));
}
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
index 0e302a3c65..feb7faa556 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
@@ -28,6 +28,9 @@
#include "selectioncontext.h"
namespace QmlDesigner {
+
+enum class AddFilesResult { Succeeded, Failed, Cancelled };
+
namespace ModelNodeOperations {
bool goIntoComponent(const ModelNode &modelNode);
@@ -73,10 +76,10 @@ void addItemToStackedContainer(const SelectionContext &selectionContext);
void increaseIndexOfStackedContainer(const SelectionContext &selectionContext);
void decreaseIndexOfStackedContainer(const SelectionContext &selectionContext);
void addTabBarToStackedContainer(const SelectionContext &selectionContext);
-bool addImageToProject(const QStringList &fileNames, const QString &directory);
-bool addFontToProject(const QStringList &fileNames, const QString &directory);
-bool addSoundToProject(const QStringList &fileNames, const QString &directory);
-bool addShaderToProject(const QStringList &fileNames, const QString &directory);
+AddFilesResult addImageToProject(const QStringList &fileNames, const QString &directory);
+AddFilesResult addFontToProject(const QStringList &fileNames, const QString &directory);
+AddFilesResult addSoundToProject(const QStringList &fileNames, const QString &directory);
+AddFilesResult addShaderToProject(const QStringList &fileNames, const QString &directory);
void createFlowActionArea(const SelectionContext &selectionContext);
void addTransition(const SelectionContext &selectionState);
void addFlowEffect(const SelectionContext &selectionState, const TypeName &typeName);
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp
index 11bfebe5e7..095b8b6262 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp
@@ -159,17 +159,20 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
QMenu menu(this);
- menu.addAction(tr("Open Connection Editor"), [&]() {
+ menu.addAction(tr("Open Connection Editor"), this, [&]() {
auto *connectionModel = qobject_cast<ConnectionModel *>(targetView->model());
const SignalHandlerProperty property = connectionModel->signalHandlerPropertyForRow(index.row());
const ModelNode node = property.parentModelNode();
+ const QString targetName = index.siblingAtColumn(ConnectionModel::TargetModelNodeRow).data().toString()
+ + "." + property.name();
+
m_connectionEditor->showWidget();
m_connectionEditor->setConnectionValue(index.data().toString());
m_connectionEditor->setModelIndex(index);
m_connectionEditor->setModelNode(node);
m_connectionEditor->prepareConnections();
- m_connectionEditor->updateWindowName();
+ m_connectionEditor->updateWindowName(targetName);
});
QMap<QString, QVariant> data;
@@ -179,7 +182,7 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
const auto actions = designerActionManager.actionsForTargetView(
ActionInterface::TargetView::ConnectionEditor);
- for (auto actionInterface : actions) {
+ for (const auto &actionInterface : actions) {
auto *action = actionInterface->action();
action->setData(data);
menu.addAction(action);
@@ -198,7 +201,7 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
QMenu menu(this);
- menu.addAction(tr("Open Binding Editor"), [&]() {
+ menu.addAction(tr("Open Binding Editor"), this, [&]() {
BindingModel *bindingModel = qobject_cast<BindingModel*>(targetView->model());
const BindingProperty property = bindingModel->bindingPropertyForRow(index.row());
@@ -209,10 +212,13 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
const TypeName typeName = property.isDynamic() ? property.dynamicTypeName()
: node.metaInfo().propertyTypeName(property.name());
+ const QString targetName = node.displayName() + "." + property.name();
+
m_bindingEditor->showWidget();
m_bindingEditor->setBindingValue(property.expression());
m_bindingEditor->setModelNode(node);
m_bindingEditor->setBackendValueTypeName(typeName);
+ m_bindingEditor->setTargetName(targetName);
m_bindingEditor->prepareBindings();
m_bindingEditor->updateWindowName();
@@ -232,7 +238,7 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
DynamicPropertiesModel *propertiesModel = qobject_cast<DynamicPropertiesModel *>(targetView->model());
QMenu menu(this);
- menu.addAction(tr("Open Binding Editor"), [&]() {
+ menu.addAction(tr("Open Binding Editor"), this, [&]() {
AbstractProperty abstractProperty = propertiesModel->abstractPropertyForRow(index.row());
if (!abstractProperty.isValid())
return;
@@ -247,17 +253,20 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
else
return;
+ const QString targetName = node.displayName() + "." + abstractProperty.name();
+
m_dynamicEditor->showWidget();
m_dynamicEditor->setBindingValue(newExpression);
m_dynamicEditor->setModelNode(node);
m_dynamicEditor->setBackendValueTypeName(abstractProperty.dynamicTypeName());
+ m_dynamicEditor->setTargetName(targetName);
m_dynamicEditor->prepareBindings();
m_dynamicEditor->updateWindowName();
m_dynamicIndex = index;
});
- menu.addAction(tr("Reset Property"), [&]() {
+ menu.addAction(tr("Reset Property"), this, [&]() {
propertiesModel->resetProperty(propertiesModel->abstractPropertyForRow(index.row()).name());
});
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
index 10e1373dde..187bc4ca17 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
@@ -339,26 +339,13 @@ static inline bool hasNodeSourceParent(const ModelNode &node)
void FormEditorView::nodeReparented(const ModelNode &node, const NodeAbstractProperty &/*newPropertyParent*/, const NodeAbstractProperty &/*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/)
{
- // If node is not connected to scene root, don't do anything yet to avoid duplicated effort,
- // as any removal or addition will remove or add descendants as well.
- if (!node.isInHierarchy())
- return;
+ addOrRemoveFormEditorItem(node);
+}
- QmlItemNode itemNode(node);
- if (hasNodeSourceParent(node)) {
- if (FormEditorItem *item = m_scene->itemForQmlItemNode(itemNode)) {
- QList<FormEditorItem *> removed = scene()->itemsForQmlItemNodes(itemNode.allSubModelNodes());
- removed.append(item);
- m_currentTool->itemsAboutToRemoved(removed);
- removeNodeFromScene(itemNode);
- }
- } else if (itemNode.isValid() && node.nodeSourceType() == ModelNode::NodeWithoutSource) {
- if (!m_scene->itemForQmlItemNode(itemNode)) {
- setupFormEditorItemTree(itemNode);
- // Simulate selection change to refresh tools
- selectedNodesChanged(selectedModelNodes(), {});
- }
- }
+void FormEditorView::nodeSourceChanged(const ModelNode &node, const QString &newNodeSource)
+{
+ Q_UNUSED(newNodeSource)
+ addOrRemoveFormEditorItem(node);
}
WidgetInfo FormEditorView::widgetInfo()
@@ -863,6 +850,38 @@ void FormEditorView::resetNodeInstanceView()
resetPuppet();
}
+void FormEditorView::addOrRemoveFormEditorItem(const ModelNode &node)
+{
+ // If node is not connected to scene root, don't do anything yet to avoid duplicated effort,
+ // as any removal or addition will remove or add descendants as well.
+ if (!node.isInHierarchy())
+ return;
+
+ QmlItemNode itemNode(node);
+
+ auto removeItemFromScene = [this, &itemNode]() {
+ if (FormEditorItem *item = m_scene->itemForQmlItemNode(itemNode)) {
+ QList<FormEditorItem *> removed = scene()->itemsForQmlItemNodes(itemNode.allSubModelNodes());
+ removed.append(item);
+ m_currentTool->itemsAboutToRemoved(removed);
+ removeNodeFromScene(itemNode);
+ }
+ };
+ if (hasNodeSourceParent(node)) {
+ removeItemFromScene();
+ } else if (itemNode.isValid()) {
+ if (node.nodeSourceType() == ModelNode::NodeWithoutSource) {
+ if (!m_scene->itemForQmlItemNode(itemNode)) {
+ setupFormEditorItemTree(itemNode);
+ // Simulate selection change to refresh tools
+ selectedNodesChanged(selectedModelNodes(), {});
+ }
+ } else {
+ removeItemFromScene();
+ }
+ }
+}
+
void FormEditorView::reset()
{
QTimer::singleShot(200, this, &FormEditorView::delayedReset);
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.h b/src/plugins/qmldesigner/components/formeditor/formeditorview.h
index 04b7d1e83e..ca3fb72bb0 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorview.h
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.h
@@ -70,6 +70,7 @@ public:
void nodeCreated(const ModelNode &createdNode) override;
void nodeAboutToBeRemoved(const ModelNode &removedNode) override;
void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) override;
+ void nodeSourceChanged(const ModelNode &node, const QString &newNodeSource) override;
void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) override;
void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList) override;
void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override;
@@ -147,6 +148,7 @@ private:
void createFormEditorWidget();
void temporaryBlockView(int duration = 1000);
void resetNodeInstanceView();
+ void addOrRemoveFormEditorItem(const ModelNode &node);
QPointer<FormEditorWidget> m_formEditorWidget;
QPointer<FormEditorScene> m_scene;
diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp
index 9374e61776..0b7d7fa2c9 100644
--- a/src/plugins/qmldesigner/components/integration/designdocument.cpp
+++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp
@@ -634,8 +634,11 @@ void DesignDocument::setEditor(Core::IEditor *editor)
connect(Core::EditorManager::instance(), &Core::EditorManager::aboutToSave,
this, [this](Core::IDocument *document) {
if (m_textEditor && m_textEditor->document() == document) {
- if (m_documentModel && m_documentModel->rewriterView())
+ if (m_documentModel && m_documentModel->rewriterView()) {
+ if (fileName().completeSuffix() == "ui.qml")
+ m_documentModel->rewriterView()->sanitizeModel();
m_documentModel->rewriterView()->writeAuxiliaryData();
+ }
}
});
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
index 04bc51284b..135eaaf5aa 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
@@ -788,7 +788,10 @@ void ItemLibraryAssetImportDialog::onClose()
addInfo(tr("Canceling import."));
m_importer.cancelImport();
} else {
- reject();
+ if (ui->progressBar->value() == 100) // import done successfully
+ accept();
+ else
+ reject();
close();
deleteLater();
}
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp
index d351232e36..4962f5d6b4 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp
@@ -147,20 +147,19 @@ void ItemLibraryCategoriesModel::resetModel()
bool ItemLibraryCategoriesModel::isAllCategoriesHidden() const
{
for (const auto &category : std::as_const(m_categoryList)) {
- // ignore "All Other Components" as its categoryVisible is always true
- if (category->isCategoryVisible() && category->categoryName() != "All Other Components")
+ if (category->isCategoryVisible())
return false;
}
return true;
}
-void ItemLibraryCategoriesModel::showAllCategories(bool show)
+void ItemLibraryCategoriesModel::showAllCategories()
{
for (const auto &category : std::as_const(m_categoryList)) {
- if (category->isCategoryVisible() != show) {
- category->setCategoryVisible(show);
- ItemLibraryModel::saveCategoryVisibleState(show, category->categoryName(),
+ if (!category->isCategoryVisible()) {
+ category->setCategoryVisible(true);
+ ItemLibraryModel::saveCategoryVisibleState(true, category->categoryName(),
category->ownerImport()->importName());
}
}
@@ -168,37 +167,56 @@ void ItemLibraryCategoriesModel::showAllCategories(bool show)
emit dataChanged(index(0), index(m_categoryList.size() - 1), {m_roleNames.key("categoryVisible")});
}
-QObject *ItemLibraryCategoriesModel::selectFirstVisibleCategory()
+void ItemLibraryCategoriesModel::hideCategory(const QString &categoryName)
+{
+ for (int i = 0; i < m_categoryList.size(); ++i) {
+ const auto category = m_categoryList.at(i);
+ if (category->categoryName() == categoryName) {
+ category->setCategoryVisible(false);
+ ItemLibraryModel::saveCategoryVisibleState(false, category->categoryName(),
+ category->ownerImport()->importName());
+ emit dataChanged(index(i), index(i), {m_roleNames.key("categoryVisible")});
+ break;
+ }
+ }
+}
+
+int ItemLibraryCategoriesModel::selectFirstVisibleCategory()
{
for (int i = 0; i < m_categoryList.length(); ++i) {
const auto category = m_categoryList.at(i);
if (category->isCategoryVisible()) {
category->setCategorySelected(true);
- emit dataChanged(index(i),index(i), {m_roleNames.key("categorySelected")});
- return category;
+ emit dataChanged(index(i), index(i), {m_roleNames.key("categorySelected")});
+ return i;
}
}
- return nullptr;
+ return -1;
}
-void ItemLibraryCategoriesModel::clearSelectedCategories()
+void ItemLibraryCategoriesModel::clearSelectedCategory(int categoryIndex)
{
- for (const auto &category : std::as_const(m_categoryList))
- category->setCategorySelected(false);
+ if (categoryIndex == -1 || m_categoryList.isEmpty())
+ return;
- emit dataChanged(index(0), index(m_categoryList.size() - 1), {m_roleNames.key("categorySelected")});
+ m_categoryList.at(categoryIndex)->setCategorySelected(false);
+ emit dataChanged(index(categoryIndex), index(categoryIndex), {m_roleNames.key("categorySelected")});
}
-void ItemLibraryCategoriesModel::selectCategory(int categoryIndex)
+QPointer<ItemLibraryCategory> ItemLibraryCategoriesModel::selectCategory(int categoryIndex)
{
- const auto category = m_categoryList.at(categoryIndex);
+ if (categoryIndex == -1 || m_categoryList.isEmpty())
+ return nullptr;
+
+ const QPointer<ItemLibraryCategory> category = m_categoryList.at(categoryIndex);
if (!category->categorySelected()) {
- clearSelectedCategories();
category->setCategorySelected(true);
emit dataChanged(index(categoryIndex),index(categoryIndex), {m_roleNames.key("categorySelected")});
}
+
+ return category;
}
void ItemLibraryCategoriesModel::addRoleNames()
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h
index 9433af804f..36437a0ed6 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h
@@ -55,10 +55,11 @@ public:
bool isAllCategoriesHidden() const;
void sortCategorySections();
void resetModel();
- void showAllCategories(bool show = true);
- void clearSelectedCategories();
- QObject *selectFirstVisibleCategory();
- void selectCategory(int categoryIndex);
+ void showAllCategories();
+ void hideCategory(const QString &categoryName);
+ void clearSelectedCategory(int categoryIndex);
+ int selectFirstVisibleCategory();
+ QPointer<ItemLibraryCategory> selectCategory(int categoryIndex);
private:
void addRoleNames();
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp
index 0744b7b2b3..ce04b298fe 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp
@@ -137,6 +137,7 @@ void ItemLibraryCategory::setExpanded(bool expanded)
void ItemLibraryCategory::setCategorySelected(bool selected)
{
m_categorySelected = selected;
+ emit categorySelectedChanged();
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp
index 7e696e43ef..ebf13d0fe5 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp
@@ -133,28 +133,38 @@ bool ItemLibraryImport::updateCategoryVisibility(const QString &searchText, bool
return hasVisibleCategories;
}
-void ItemLibraryImport::showAllCategories(bool show)
+void ItemLibraryImport::showAllCategories()
{
- m_categoryModel.showAllCategories(show);
+ m_categoryModel.showAllCategories();
+ setAllCategoriesVisible(true);
}
-void ItemLibraryImport::selectCategory(int categoryIndex)
+void ItemLibraryImport::hideCategory(const QString &categoryName)
{
- m_categoryModel.selectCategory(categoryIndex);
+ m_categoryModel.hideCategory(categoryName);
+ setAllCategoriesVisible(false);
}
-QObject *ItemLibraryImport::selectFirstVisibleCategory()
+ItemLibraryCategory *ItemLibraryImport::selectCategory(int categoryIndex)
+{
+ return m_categoryModel.selectCategory(categoryIndex);
+}
+
+int ItemLibraryImport::selectFirstVisibleCategory()
{
return m_categoryModel.selectFirstVisibleCategory();
}
-void ItemLibraryImport::clearSelectedCategories()
+void ItemLibraryImport::clearSelectedCategory(int categoryIndex)
{
- m_categoryModel.clearSelectedCategories();
+ m_categoryModel.clearSelectedCategory(categoryIndex);
}
bool ItemLibraryImport::isAllCategoriesHidden() const
{
+ if (!m_isVisible)
+ return true;
+
return m_categoryModel.isAllCategoriesHidden();
}
@@ -221,7 +231,7 @@ void ItemLibraryImport::setImportExpanded(bool expanded)
}
}
-ItemLibraryCategory *ItemLibraryImport::getCategorySection(const QString &categoryName) const
+ItemLibraryCategory *ItemLibraryImport::getCategoryByName(const QString &categoryName) const
{
for (ItemLibraryCategory *catSec : std::as_const(m_categoryModel.categorySections())) {
if (catSec->categoryName() == categoryName)
@@ -231,6 +241,16 @@ ItemLibraryCategory *ItemLibraryImport::getCategorySection(const QString &catego
return nullptr;
}
+ItemLibraryCategory *ItemLibraryImport::getCategoryAt(int categoryIndex) const
+{
+ const QList<QPointer<ItemLibraryCategory>> categories = m_categoryModel.categorySections();
+
+ if (categoryIndex != -1 && !categories.isEmpty())
+ return categories.at(categoryIndex);
+
+ return nullptr;
+}
+
// static
QString ItemLibraryImport::userComponentsTitle()
{
@@ -264,22 +284,14 @@ void ItemLibraryImport::updateRemovable()
}
}
-// returns true if all categories are visible, otherwise false
-bool ItemLibraryImport::importCatVisibleState() const
+bool ItemLibraryImport::allCategoriesVisible() const
{
- if (m_categoryModel.rowCount() > 0) {
- for (ItemLibraryCategory *cat : m_categoryModel.categorySections()) {
- if (!cat->isCategoryVisible())
- return false;
- }
- }
-
- return true;
+ return m_allCategoriesVisible;
}
-void ItemLibraryImport::setImportCatVisibleState(bool show)
+void ItemLibraryImport::setAllCategoriesVisible(bool visible)
{
- m_categoryModel.showAllCategories(show);
+ m_allCategoriesVisible = visible;
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h
index f5a1cd0279..89aaca8e02 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h
@@ -43,7 +43,7 @@ class ItemLibraryImport : public QObject
Q_PROPERTY(bool importExpanded READ importExpanded WRITE setImportExpanded NOTIFY importExpandChanged FINAL)
Q_PROPERTY(bool importRemovable READ importRemovable NOTIFY importRemovableChanged FINAL)
Q_PROPERTY(bool importUnimported READ importUnimported FINAL)
- Q_PROPERTY(bool importCatVisibleState READ importCatVisibleState WRITE setImportCatVisibleState NOTIFY importCatVisibleStateChanged FINAL)
+ Q_PROPERTY(bool allCategoriesVisible READ allCategoriesVisible WRITE setAllCategoriesVisible NOTIFY allCategoriesVisibleChanged FINAL)
Q_PROPERTY(QObject *categoryModel READ categoryModel NOTIFY categoryModelChanged FINAL)
public:
@@ -65,11 +65,12 @@ public:
bool importVisible() const;
bool importUsed() const;
bool importRemovable() const;
- bool importCatVisibleState() const;
+ bool allCategoriesVisible() const;
bool hasCategories() const;
bool hasSingleCategory() const;
bool isAllCategoriesHidden() const;
- ItemLibraryCategory *getCategorySection(const QString &categoryName) const;
+ ItemLibraryCategory *getCategoryByName(const QString &categoryName) const;
+ ItemLibraryCategory *getCategoryAt(int categoryIndex) const;
void addCategory(ItemLibraryCategory *category);
QObject *categoryModel();
@@ -78,12 +79,14 @@ public:
void setImportUsed(bool importUsed);
void sortCategorySections();
void setImportExpanded(bool expanded = true);
- void setImportCatVisibleState(bool show);
+ void setAllCategoriesVisible(bool visible);
void expandCategories(bool expand = true);
- void showAllCategories(bool show = true);
- void selectCategory(int categoryIndex);
- QObject *selectFirstVisibleCategory();
- void clearSelectedCategories();
+ void showAllCategories();
+ void hideCategory(const QString &categoryName);
+ ItemLibraryCategory *selectCategory(int categoryIndex);
+ int selectFirstVisibleCategory();
+ void clearSelectedCategory(int categoryIndex);
+ bool importUnimported() const { return m_sectionType == SectionType::Unimported; }
static QString userComponentsTitle();
static QString quick3DAssetsTitle();
@@ -97,17 +100,17 @@ signals:
void importUsedChanged();
void importExpandChanged();
void importRemovableChanged();
- void importCatVisibleStateChanged();
+ void allCategoriesVisibleChanged();
private:
void updateRemovable();
- bool importUnimported() const { return m_sectionType == SectionType::Unimported; }
Import m_import;
bool m_importExpanded = true;
bool m_isVisible = true;
bool m_importUsed = false;
bool m_importRemovable = false;
+ bool m_allCategoriesVisible = true;
SectionType m_sectionType = SectionType::Default;
ItemLibraryCategoriesModel m_categoryModel;
};
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
index ebef71f63f..b571cc4981 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
@@ -59,8 +59,8 @@ bool ItemLibraryModel::loadExpandedState(const QString &sectionName)
return expandedStateHash.value(sectionName, true);
}
-void ItemLibraryModel::saveCategoryVisibleState(bool isVisible, const QString &categoryName, const
- QString &importName)
+void ItemLibraryModel::saveCategoryVisibleState(bool isVisible, const QString &categoryName,
+ const QString &importName)
{
categoryVisibleStateHash.insert(categoryName + '_' + importName, isVisible);
}
@@ -70,58 +70,61 @@ bool ItemLibraryModel::loadCategoryVisibleState(const QString &categoryName, con
return categoryVisibleStateHash.value(categoryName + '_' + importName, true);
}
-void ItemLibraryModel::showHiddenCategories()
+void ItemLibraryModel::selectImportCategory(const QString &importUrl, int categoryIndex)
{
- for (const QPointer<ItemLibraryImport> &import : std::as_const(m_importList)) {
- if (import->hasCategories())
- import->showAllCategories(true);
- }
-
- categoryVisibleStateHash.clear();
-}
+ clearSelectedCategory();
-bool ItemLibraryModel::getIsAnyCategoryHidden() const
-{
- for (const bool &catState : std::as_const(categoryVisibleStateHash)) {
- if (!catState)
- return true;
- }
+ m_selectedImportUrl = importUrl;
+ m_selectedCategoryIndex = categoryIndex;
- return false;
+ updateSelection();
}
-void ItemLibraryModel::selectImportCategory(const QString importUrl, int categoryIndex)
+void ItemLibraryModel::clearSelectedCategory()
{
- ItemLibraryImport *selectedCategoryImport = importByUrl(importUrl);
-
- for (int i = 0; i < m_importList.length(); ++i) {
- const auto importToSelect = m_importList.at(i);
-
- if (selectedCategoryImport == importToSelect)
- importToSelect->selectCategory(categoryIndex);
- else
- importToSelect->clearSelectedCategories();
+ if (m_selectedCategoryIndex != -1) {
+ ItemLibraryImport *selectedImport = importByUrl(m_selectedImportUrl);
+ if (selectedImport)
+ selectedImport->clearSelectedCategory(m_selectedCategoryIndex);
}
}
-bool ItemLibraryModel::isAllCategoriesHidden() const
+void ItemLibraryModel::selectImportFirstVisibleCategory()
{
- for (int i = 0; i < m_importList.length(); ++i) {
- if (!m_importList.at(i)->isAllCategoriesHidden())
- return false;
- }
+ if (m_selectedCategoryIndex != -1) {
+ ItemLibraryImport *selectedImport = importByUrl(m_selectedImportUrl);
+ if (selectedImport) {
+ ItemLibraryCategory *selectedCategory = selectedImport->getCategoryAt(m_selectedCategoryIndex);
+ if (selectedCategory) {
+ bool isUnimported = selectedImport->sectionType() == ItemLibraryImport::SectionType::Unimported;
+ // unimported category is always visible so checking its Import visibility instead
+ bool isVisible = isUnimported ? selectedImport->importVisible()
+ : selectedCategory->isCategoryVisible();
+ if (isVisible)
+ return; // there is already a selected visible category
- return true;
-}
+ clearSelectedCategory();
+ }
+ }
+ }
-QObject *ItemLibraryModel::selectImportFirstVisibleCategory()
-{
for (const QPointer<ItemLibraryImport> &import : std::as_const(m_importList)) {
- if (!import->isAllCategoriesHidden())
- return import->selectFirstVisibleCategory();
+ if (!import->isAllCategoriesHidden()) {
+ m_selectedImportUrl = import->importUrl();
+ m_selectedCategoryIndex = import->selectFirstVisibleCategory();
+
+ ItemLibraryCategory *selectedCategory = import->getCategoryAt(m_selectedCategoryIndex);
+ if (selectedCategory) {
+ setItemsModel(selectedCategory->itemModel());
+ setImportUnimportedSelected(import->importUnimported());
+ return;
+ }
+ }
}
- return nullptr;
+ m_selectedImportUrl.clear();
+ m_selectedCategoryIndex = -1;
+ setItemsModel(nullptr);
}
bool ItemLibraryModel::isAnyCategoryHidden() const
@@ -137,6 +140,30 @@ void ItemLibraryModel::setIsAnyCategoryHidden(bool state)
}
}
+bool ItemLibraryModel::importUnimportedSelected() const
+{
+ return m_importUnimportedSelected;
+}
+
+void ItemLibraryModel::setImportUnimportedSelected(bool state)
+{
+ if (state != m_importUnimportedSelected) {
+ m_importUnimportedSelected = state;
+ emit importUnimportedSelectedChanged();
+ }
+}
+
+QObject *ItemLibraryModel::itemsModel() const
+{
+ return m_itemsModel;
+}
+
+void ItemLibraryModel::setItemsModel(QObject *model)
+{
+ m_itemsModel = model;
+ emit itemsModelChanged();
+}
+
void ItemLibraryModel::expandAll()
{
int i = 0;
@@ -164,6 +191,46 @@ void ItemLibraryModel::collapseAll()
}
}
+void ItemLibraryModel::hideCategory(const QString &importUrl, const QString &categoryName)
+{
+ ItemLibraryImport *import = importByUrl(importUrl);
+ if (!import)
+ return;
+
+ import->hideCategory(categoryName);
+
+ selectImportFirstVisibleCategory();
+ setIsAnyCategoryHidden(true);
+}
+
+void ItemLibraryModel::showImportHiddenCategories(const QString &importUrl)
+{
+ ItemLibraryImport *targetImport = nullptr;
+ bool hiddenCatsExist = false;
+ for (const QPointer<ItemLibraryImport> &import : std::as_const(m_importList)) {
+ if (import->importUrl() == importUrl)
+ targetImport = import;
+ else
+ hiddenCatsExist |= !import->allCategoriesVisible();
+ }
+
+ if (targetImport) {
+ targetImport->showAllCategories();
+ updateSelection(); // useful when all categories are hidden
+ setIsAnyCategoryHidden(hiddenCatsExist);
+ }
+}
+
+void ItemLibraryModel::showAllHiddenCategories()
+{
+ for (const QPointer<ItemLibraryImport> &import : std::as_const(m_importList))
+ import->showAllCategories();
+
+ updateSelection(); // useful when all categories are hidden
+ setIsAnyCategoryHidden(false);
+ categoryVisibleStateHash.clear();
+}
+
void ItemLibraryModel::setFlowMode(bool b)
{
m_flowMode = b;
@@ -242,6 +309,8 @@ void ItemLibraryModel::setSearchText(const QString &searchText)
bool changed = false;
updateVisibility(&changed);
+
+ selectImportFirstVisibleCategory();
}
}
@@ -411,7 +480,7 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model)
}
// get or create category section
- ItemLibraryCategory *categorySection = importSection->getCategorySection(catName);
+ ItemLibraryCategory *categorySection = importSection->getCategoryByName(catName);
if (!categorySection) {
categorySection = new ItemLibraryCategory(catName, importSection);
importSection->addCategory(categorySection);
@@ -428,8 +497,11 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model)
}
sortSections();
+
bool changed = false;
updateVisibility(&changed);
+
+ updateSelection();
endResetModel();
}
@@ -453,6 +525,23 @@ void ItemLibraryModel::clearSections()
m_importList.clear();
}
+void ItemLibraryModel::updateSelection()
+{
+ if (m_selectedCategoryIndex != -1) {
+ ItemLibraryImport *selectedImport = importByUrl(m_selectedImportUrl);
+ if (selectedImport) {
+ ItemLibraryCategory *selectedCategory = selectedImport->selectCategory(m_selectedCategoryIndex);
+ if (selectedCategory) {
+ setItemsModel(selectedCategory->itemModel());
+ setImportUnimportedSelected(selectedImport->importUnimported());
+ return;
+ }
+ }
+ }
+
+ selectImportFirstVisibleCategory();
+}
+
void ItemLibraryModel::registerQmlTypes()
{
qmlRegisterAnonymousType<QmlDesigner::ItemLibraryModel>("ItemLibraryModel", 1);
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h
index ad9803821f..7852999edd 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h
@@ -33,15 +33,19 @@ QT_FORWARD_DECLARE_CLASS(QMimeData)
namespace QmlDesigner {
-class ItemLibraryInfo;
+class ItemLibraryCategory;
class ItemLibraryEntry;
-class Model;
class ItemLibraryImport;
+class ItemLibraryInfo;
+class Model;
class ItemLibraryModel : public QAbstractListModel
{
Q_OBJECT
+
Q_PROPERTY(bool isAnyCategoryHidden READ isAnyCategoryHidden WRITE setIsAnyCategoryHidden NOTIFY isAnyCategoryHiddenChanged FINAL)
+ Q_PROPERTY(QObject *itemsModel READ itemsModel WRITE setItemsModel NOTIFY itemsModelChanged)
+ Q_PROPERTY(bool importUnimportedSelected READ importUnimportedSelected WRITE setImportUnimportedSelected NOTIFY importUnimportedSelectedChanged)
public:
explicit ItemLibraryModel(QObject *parent = nullptr);
@@ -66,20 +70,26 @@ public:
bool isAnyCategoryHidden() const;
void setIsAnyCategoryHidden(bool state);
+ bool importUnimportedSelected() const;
+ void setImportUnimportedSelected(bool state);
+
+ QObject *itemsModel() const;
+ void setItemsModel(QObject *model);
+
static void registerQmlTypes();
static void saveExpandedState(bool expanded, const QString &sectionName);
static bool loadExpandedState(const QString &sectionName);
static void saveCategoryVisibleState(bool isVisible, const QString &categoryName, const QString
&importName);
static bool loadCategoryVisibleState(const QString &categoryName, const QString &importName);
+ void selectImportFirstVisibleCategory();
Q_INVOKABLE void expandAll();
Q_INVOKABLE void collapseAll();
- Q_INVOKABLE void showHiddenCategories();
- Q_INVOKABLE bool getIsAnyCategoryHidden() const;
- Q_INVOKABLE void selectImportCategory(const QString importUrl, int categoryIndex);
- Q_INVOKABLE QObject *selectImportFirstVisibleCategory();
- Q_INVOKABLE bool isAllCategoriesHidden() const;
+ Q_INVOKABLE void hideCategory(const QString &importUrl, const QString &categoryName);
+ Q_INVOKABLE void showImportHiddenCategories(const QString &importUrl);
+ Q_INVOKABLE void showAllHiddenCategories();
+ Q_INVOKABLE void selectImportCategory(const QString &importUrl, int categoryIndex);
Import entryToImport(const ItemLibraryEntry &entry);
@@ -87,12 +97,18 @@ public:
signals:
void isAnyCategoryHiddenChanged();
+ void importUnimportedSelectedChanged();
+ void selectedCategoryChanged();
+ void selectedImportUrlChanged();
+ void itemsModelChanged();
private:
void updateVisibility(bool *changed);
void addRoleNames();
void sortSections();
void clearSections();
+ void updateSelection();
+ void clearSelectedCategory();
QList<QPointer<ItemLibraryImport>> m_importList;
QHash<int, QByteArray> m_roleNames;
@@ -100,6 +116,10 @@ private:
QString m_searchText;
bool m_flowMode = false;
bool m_isAnyCategoryHidden = false;
+ bool m_importUnimportedSelected = false;
+ QString m_selectedImportUrl;
+ int m_selectedCategoryIndex = -1;
+ QObject *m_itemsModel = nullptr; // items model for the horizontal layout
inline static QHash<QString, bool> expandedStateHash;
inline static QHash<QString, bool> categoryVisibleStateHash;
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
index 06b84d83f8..9c8b4e09f9 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
@@ -245,13 +245,15 @@ void ItemLibraryView::updateImport3DSupport(const QVariantMap &supportMap)
m_importableExtensions3DMap = extMap;
- auto import3DModelOperation = [this](const QStringList &fileNames, const QString &defaultDir) -> bool {
+ AddResourceOperation import3DModelOperation = [this](const QStringList &fileNames,
+ const QString &defaultDir) -> AddFilesResult {
auto importDlg = new ItemLibraryAssetImportDialog(fileNames, defaultDir,
m_importableExtensions3DMap,
m_importOptions3DMap, {}, {},
Core::ICore::mainWindow());
- importDlg->exec();
- return true;
+ int result = importDlg->exec();
+
+ return result == QDialog::Accepted ? AddFilesResult::Succeeded : AddFilesResult::Cancelled;
};
auto add3DHandler = [&](const QString &group, const QString &ext) {
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
index de15fde7cf..ef1e85287a 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
@@ -38,6 +38,7 @@
#include <itemlibrarymodel.h>
#include <itemlibraryaddimportmodel.h>
#include "itemlibraryassetsiconprovider.h"
+#include "modelnodeoperations.h"
#include <metainfo.h>
#include <model.h>
#include <rewritingexception.h>
@@ -560,45 +561,31 @@ void ItemLibraryWidget::addImportForItem(const QString &importUrl)
void ItemLibraryWidget::addResources(const QStringList &files)
{
- auto document = QmlDesignerPlugin::instance()->currentDesignDocument();
+ DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument();
QTC_ASSERT(document, return);
- QList<AddResourceHandler> handlers = QmlDesignerPlugin::instance()->viewManager().designerActionManager().addResourceHandler();
-
- QMultiMap<QString, QString> map;
- for (const AddResourceHandler &handler : handlers) {
- map.insert(handler.category, handler.filter);
- }
-
- QMap<QString, QString> reverseMap;
- for (const AddResourceHandler &handler : handlers) {
- reverseMap.insert(handler.filter, handler.category);
- }
-
- QMap<QString, int> priorities;
- for (const AddResourceHandler &handler : handlers) {
- priorities.insert(handler.category, handler.piority);
- }
-
- QStringList sortedKeys = map.uniqueKeys();
- Utils::sort(sortedKeys, [&priorities](const QString &first,
- const QString &second){
- return priorities.value(first) < priorities.value(second);
- });
+ const QList<AddResourceHandler> handlers = QmlDesignerPlugin::instance()->viewManager()
+ .designerActionManager().addResourceHandler();
QStringList fileNames = files;
- if (fileNames.isEmpty()) {
- QStringList filters;
-
- for (const QString &key : qAsConst(sortedKeys)) {
- QString str = key + " (";
- str.append(map.values(key).join(" "));
- str.append(")");
- filters.append(str);
+ if (fileNames.isEmpty()) { // if no files, show the "add assets" dialog
+ QMultiMap<QString, QString> map;
+ QHash<QString, int> priorities;
+ for (const AddResourceHandler &handler : handlers) {
+ map.insert(handler.category, handler.filter);
+ priorities.insert(handler.category, handler.piority);
}
- filters.prepend(tr("All Files (%1)").arg(map.values().join(" ")));
+ QStringList sortedKeys = map.uniqueKeys();
+ Utils::sort(sortedKeys, [&priorities](const QString &first, const QString &second) {
+ return priorities.value(first) < priorities.value(second);
+ });
+
+ QStringList filters { tr("All Files (%1)").arg(map.values().join(' ')) };
+ QString filterTemplate = "%1 (%2)";
+ for (const QString &key : qAsConst(sortedKeys))
+ filters.append(filterTemplate.arg(key, map.values(key).join(' ')));
static QString lastDir;
const QString currentDir = lastDir.isEmpty() ? document->fileName().parentDir().toString() : lastDir;
@@ -616,24 +603,30 @@ void ItemLibraryWidget::addResources(const QStringList &files)
}
}
- QMultiMap<QString, QString> partitionedFileNames;
+ QHash<QString, QString> filterToCategory;
+ QHash<QString, AddResourceOperation> categoryToOperation;
+ for (const AddResourceHandler &handler : handlers) {
+ filterToCategory.insert(handler.filter, handler.category);
+ categoryToOperation.insert(handler.category, handler.operation);
+ }
+
+ QMultiMap<QString, QString> categoryFileNames; // filenames grouped by category
for (const QString &fileName : qAsConst(fileNames)) {
const QString suffix = "*." + QFileInfo(fileName).suffix().toLower();
- const QString category = reverseMap.value(suffix);
- partitionedFileNames.insert(category, fileName);
+ const QString category = filterToCategory.value(suffix);
+ categoryFileNames.insert(category, fileName);
}
- for (const QString &category : partitionedFileNames.uniqueKeys()) {
- for (const AddResourceHandler &handler : handlers) {
- QStringList fileNames = partitionedFileNames.values(category);
- if (handler.category == category) {
- QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_RESOURCE_IMPORTED + category);
- if (!handler.operation(fileNames, document->fileName().parentDir().toString()))
- Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"), tr("Could not add %1 to project.").arg(fileNames.join(" ")));
- break;
- }
- }
+ for (const QString &category : categoryFileNames.uniqueKeys()) {
+ QStringList fileNames = categoryFileNames.values(category);
+ AddResourceOperation operation = categoryToOperation.value(category);
+ QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_RESOURCE_IMPORTED + category);
+ AddFilesResult result = operation(fileNames, document->fileName().parentDir().toString());
+ if (result == AddFilesResult::Failed) {
+ Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"),
+ tr("Could not add %1 to project.").arg(fileNames.join(' ')));
+ }
}
}
diff --git a/src/plugins/qmldesigner/designercore/include/import.h b/src/plugins/qmldesigner/designercore/include/import.h
index 99612fa2ad..c797a19ae4 100644
--- a/src/plugins/qmldesigner/designercore/include/import.h
+++ b/src/plugins/qmldesigner/designercore/include/import.h
@@ -25,6 +25,8 @@
#pragma once
+#include <utils/porting.h>
+
#include <QString>
#include <QStringList>
#include <QMetaType>
@@ -74,7 +76,7 @@ private:
QStringList m_importPathList;
};
-QMLDESIGNERCORE_EXPORT uint qHash(const Import &import);
+QMLDESIGNERCORE_EXPORT Utils::QHashValueType qHash(const Import &import);
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/include/projectstorageids.h b/src/plugins/qmldesigner/designercore/include/projectstorageids.h
index 76f547eeb1..765f2c9c65 100644
--- a/src/plugins/qmldesigner/designercore/include/projectstorageids.h
+++ b/src/plugins/qmldesigner/designercore/include/projectstorageids.h
@@ -37,7 +37,7 @@ public:
constexpr explicit BasicId() = default;
- BasicId(const char *) = delete;
+ constexpr BasicId(const char *) = delete;
constexpr explicit BasicId(InternalIntergerType id)
: id{id}
diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h
index ac251ef064..44e804f25d 100644
--- a/src/plugins/qmldesigner/designercore/include/rewriterview.h
+++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h
@@ -177,6 +177,8 @@ public:
ModelNode getNodeForCanonicalIndex(int index);
+ void sanitizeModel();
+
signals:
void modelInterfaceProjectUpdated();
diff --git a/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp b/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp
index 28d8e02826..46ad1270e7 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp
@@ -89,6 +89,7 @@ void MetaInfoReader::setQualifcation(const TypeName &qualification)
void MetaInfoReader::elementStart(const QString &name, const QmlJS::SourceLocation &nameLocation)
{
+ Q_UNUSED(nameLocation)
switch (parserState()) {
case ParsingDocument: setParserState(readDocument(name)); break;
case ParsingMetaInfo: setParserState(readMetaInfoRootElement(name)); break;
@@ -133,6 +134,8 @@ void MetaInfoReader::propertyDefinition(const QString &name,
const QVariant &value,
const QmlJS::SourceLocation &valueLocation)
{
+ Q_UNUSED(nameLocation)
+ Q_UNUSED(valueLocation)
switch (parserState()) {
case ParsingType: readTypeProperty(name, value); break;
case ParsingImports: readImportsProperty(name, value); break;
diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
index 33693210a9..97ff30850f 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
@@ -1505,6 +1505,8 @@ QVariant NodeMetaInfo::propertyCastedValue(const PropertyName &propertyName, con
return variant.toFloat();
} else if (typeName == "<cpp>.int") {
return variant.toInt();
+ } else if (typeName == "<cpp>.bool") {
+ return variant.toBool();
} else if (copyVariant.convert(typeId)) {
return copyVariant;
}
diff --git a/src/plugins/qmldesigner/designercore/model/import.cpp b/src/plugins/qmldesigner/designercore/model/import.cpp
index d895b07764..1c217bf47b 100644
--- a/src/plugins/qmldesigner/designercore/model/import.cpp
+++ b/src/plugins/qmldesigner/designercore/model/import.cpp
@@ -109,7 +109,7 @@ int Import::majorFromVersion(const QString &version)
return version.split('.').first().toInt();
}
-uint qHash(const Import &import)
+Utils::QHashValueType qHash(const Import &import)
{
return ::qHash(import.url()) ^ ::qHash(import.file()) ^ ::qHash(import.version()) ^ ::qHash(import.alias());
}
diff --git a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp
index 55305c72d1..2445042fbb 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp
@@ -332,7 +332,7 @@ static void removeStateOperationsForChildren(const QmlObjectNode &node)
stateOperation.modelNode().destroy(); //remove of belonging StatesOperations
}
- for (const QmlObjectNode &childNode : node.modelNode().directSubModelNodes()) {
+ for (const QmlObjectNode childNode : node.modelNode().directSubModelNodes()) {
removeStateOperationsForChildren(childNode);
}
}
diff --git a/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp b/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp
index 85eb51de44..924a4e0ee6 100644
--- a/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp
@@ -310,7 +310,7 @@ QList<QmlTimelineKeyframeGroup> QmlTimelineKeyframeGroup::allInvalidTimelineKeyf
QTC_ASSERT(view->rootModelNode().isValid(), return ret);
const auto groups = view->rootModelNode().subModelNodesOfType("QtQuick.Timeline.KeyframeGroup");
- for (const QmlTimelineKeyframeGroup &group : groups) {
+ for (const QmlTimelineKeyframeGroup group : groups) {
if (group.isDangling())
ret.append(group);
}
diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp
index af066b81c2..e33c82b124 100644
--- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp
+++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp
@@ -39,6 +39,8 @@
#include <modelnodepositionstorage.h>
#include <modelnode.h>
#include <nodeproperty.h>
+#include <qmlobjectnode.h>
+#include <qmltimelinekeyframegroup.h>
#ifndef QMLDESIGNER_TEST
#include <qmldesignerplugin.h>
@@ -57,6 +59,7 @@
#include <utility>
#include <vector>
+#include <algorithm>
using namespace QmlDesigner::Internal;
@@ -658,6 +661,36 @@ ModelNode RewriterView::getNodeForCanonicalIndex(int index)
return m_canonicalIntModelNode.value(index);
}
+void RewriterView::sanitizeModel()
+{
+ if (inErrorState())
+ return;
+
+ QmlObjectNode root = rootModelNode();
+
+ QTC_ASSERT(root.isValid(), return);
+
+ QList<ModelNode> danglingNodes;
+
+ const auto danglingStates = root.allInvalidStateOperations();
+ const auto danglingKeyframeGroups = QmlTimelineKeyframeGroup::allInvalidTimelineKeyframeGroups(this);
+
+ std::transform(danglingStates.begin(),
+ danglingStates.end(),
+ std::back_inserter(danglingNodes),
+ [](const auto &node) { return node.modelNode(); });
+
+ std::transform(danglingKeyframeGroups.begin(),
+ danglingKeyframeGroups.end(),
+ std::back_inserter(danglingNodes),
+ [](const auto &node) { return node.modelNode(); });
+
+ executeInTransaction("RewriterView::sanitizeModel", [&]() {
+ for (auto node : std::as_const(danglingNodes))
+ node.destroy();
+ });
+}
+
Internal::ModelNodePositionStorage *RewriterView::positionStorage() const
{
return m_positionStorage.data();
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h
index 2a428c6533..5df23b7385 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h
@@ -28,6 +28,7 @@
#include "projectstorageexceptions.h"
#include "projectstorageinterface.h"
#include "sourcepathcachetypes.h"
+#include "storagecache.h"
#include <sqlitealgorithms.h>
#include <sqlitetable.h>
@@ -35,7 +36,9 @@
#include <utils/algorithm.h>
#include <utils/optional.h>
+#include <utils/set_algorithm.h>
+#include <algorithm>
#include <tuple>
namespace QmlDesigner {
@@ -53,65 +56,73 @@ public:
ProjectStorage(Database &database, bool isInitialized)
: database{database}
, initializer{database, isInitialized}
- {}
+ {
+ moduleCache.populate();
+ }
- void synchronize(Storage::Modules modules,
- Storage::Imports imports,
- Storage::Types types,
- SourceIds sourceIds,
- FileStatuses fileStatuses) override
+ void synchronize(Storage::SynchronizationPackage package) override
{
Sqlite::ImmediateTransaction transaction{database};
- std::vector<AliasPropertyDeclaration> insertedAliasPropertyDeclarations;
- std::vector<AliasPropertyDeclaration> updatedAliasPropertyDeclarations;
+ AliasPropertyDeclarations insertedAliasPropertyDeclarations;
+ AliasPropertyDeclarations updatedAliasPropertyDeclarations;
+
+ AliasPropertyDeclarations relinkableAliasPropertyDeclarations;
+ PropertyDeclarations relinkablePropertyDeclarations;
+ Prototypes relinkablePrototypes;
+ TypeIds deletedTypeIds;
TypeIds updatedTypeIds;
- updatedTypeIds.reserve(types.size());
+ updatedTypeIds.reserve(package.types.size());
TypeIds typeIdsToBeDeleted;
- auto sourceIdValues = Utils::transform<std::vector>(sourceIds, [](SourceId sourceId) {
+ auto sourceIdValues = Utils::transform<std::vector>(package.sourceIds, [](SourceId sourceId) {
return &sourceId;
});
std::sort(sourceIdValues.begin(), sourceIdValues.end());
- synchronizeFileStatuses(fileStatuses, sourceIdValues);
- synchronizeModules(modules, typeIdsToBeDeleted, sourceIdValues);
- synchronizeImports(imports, sourceIdValues);
- synchronizeTypes(types,
+ synchronizeFileStatuses(package.fileStatuses, sourceIdValues);
+ synchronizeImports(package.imports, sourceIdValues);
+ synchronizeTypes(package.types,
updatedTypeIds,
insertedAliasPropertyDeclarations,
- updatedAliasPropertyDeclarations);
-
- deleteNotUpdatedTypes(updatedTypeIds, sourceIdValues, typeIdsToBeDeleted);
+ updatedAliasPropertyDeclarations,
+ relinkableAliasPropertyDeclarations,
+ relinkablePropertyDeclarations,
+ relinkablePrototypes,
+ sourceIdValues);
+
+ deleteNotUpdatedTypes(updatedTypeIds,
+ sourceIdValues,
+ typeIdsToBeDeleted,
+ relinkableAliasPropertyDeclarations,
+ relinkablePropertyDeclarations,
+ relinkablePrototypes,
+ deletedTypeIds);
+
+ relink(relinkableAliasPropertyDeclarations,
+ relinkablePropertyDeclarations,
+ relinkablePrototypes,
+ deletedTypeIds);
linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations);
transaction.commit();
}
- ModuleId fetchModuleId(Utils::SmallStringView moduleName)
+ ModuleId moduleId(Utils::SmallStringView moduleName) override
{
- Sqlite::DeferredTransaction transaction{database};
-
- ModuleId moduleId = fetchModuleIdUnguarded(moduleName);
-
- transaction.commit();
-
- return moduleId;
+ return moduleCache.id(moduleName);
}
- ModuleIds fetchModuleIds(const Storage::Modules &modules)
+ Utils::SmallString moduleName(ModuleId moduleId)
{
- Sqlite::DeferredTransaction transaction{database};
-
- ModuleIds moduleIds = fetchModuleIdsUnguarded(modules);
-
- transaction.commit();
+ if (!moduleId)
+ throw ModuleDoesNotExists{};
- return moduleIds;
+ return moduleCache.value(moduleId);
}
PropertyDeclarationId fetchPropertyDeclarationByTypeIdAndName(TypeId typeId,
@@ -132,9 +143,9 @@ public:
static_cast<void *>(moduleIds.data()), static_cast<long long>(moduleIds.size()), name);
}
- TypeId fetchTypeIdByName(ModuleId moduleId, Utils::SmallStringView name)
+ TypeId fetchTypeIdByName(SourceId sourceId, Utils::SmallStringView name)
{
- return selectTypeIdByModuleIdAndNameStatement.template valueWithTransaction<TypeId>(&moduleId,
+ return selectTypeIdBySourceIdAndNameStatement.template valueWithTransaction<TypeId>(&sourceId,
name);
}
@@ -283,11 +294,6 @@ public:
return writeSourceId(sourceContextId, sourceName);
}
- auto fetchAllModules() const
- {
- return selectAllModulesStatement.template valuesWithTransaction<Storage::Module>(128);
- }
-
auto fetchAllFileStatuses() const
{
return selectAllFileStatusesStatement.template rangeWithTransaction<FileStatus>();
@@ -299,9 +305,79 @@ public:
&sourceId);
}
- SourceIds fetchSourceDependencieIds(SourceId sourceId) const override { return {}; }
+ Storage::ProjectDatas fetchProjectDatas(SourceId sourceId) const override
+ {
+ return Storage::ProjectDatas{};
+ }
private:
+ class ModuleStorageAdapter
+ {
+ public:
+ auto fetchId(const Utils::SmallStringView name) { return storage.fetchModuleId(name); }
+
+ auto fetchValue(ModuleId id) { return storage.fetchModuleName(id); }
+
+ auto fetchAll() { return storage.fetchAllModules(); }
+
+ ProjectStorage &storage;
+ };
+
+ class Module : public StorageCacheEntry<Utils::PathString, Utils::SmallStringView, ModuleId>
+ {
+ using Base = StorageCacheEntry<Utils::PathString, Utils::SmallStringView, ModuleId>;
+
+ public:
+ using Base::Base;
+
+ friend bool operator==(const Module &first, const Module &second)
+ {
+ return first.id == second.id && first.value == second.value;
+ }
+ };
+
+ friend ModuleStorageAdapter;
+
+ static bool moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept
+ {
+ return Utils::reverseCompare(first, second) < 0;
+ }
+
+ using ModuleCache = StorageCache<Utils::PathString,
+ Utils::SmallStringView,
+ ModuleId,
+ ModuleStorageAdapter,
+ NonLockingMutex,
+ moduleNameLess,
+ Module>;
+
+ ModuleId fetchModuleId(Utils::SmallStringView moduleName)
+ {
+ Sqlite::DeferredTransaction transaction{database};
+
+ ModuleId moduleId = fetchModuleIdUnguarded(moduleName);
+
+ transaction.commit();
+
+ return moduleId;
+ }
+
+ auto fetchModuleName(ModuleId id)
+ {
+ Sqlite::DeferredTransaction transaction{database};
+
+ auto moduleName = fetchModuleNameUnguarded(id);
+
+ transaction.commit();
+
+ return moduleName;
+ }
+
+ auto fetchAllModules() const
+ {
+ return selectAllModulesStatement.template valuesWithTransaction<Module>(128);
+ }
+
class AliasPropertyDeclaration
{
public:
@@ -318,6 +394,13 @@ private:
, aliasPropertyDeclarationId{aliasPropertyDeclarationId}
{}
+ friend bool operator<(const AliasPropertyDeclaration &first,
+ const AliasPropertyDeclaration &second)
+ {
+ return std::tie(first.typeId, first.propertyDeclarationId)
+ < std::tie(second.typeId, second.propertyDeclarationId);
+ }
+
public:
TypeId typeId;
PropertyDeclarationId propertyDeclarationId;
@@ -326,6 +409,8 @@ private:
PropertyDeclarationId aliasPropertyDeclarationId;
};
+ using AliasPropertyDeclarations = std::vector<AliasPropertyDeclaration>;
+
class PropertyDeclaration
{
public:
@@ -345,12 +430,20 @@ private:
, importedTypeNameId{importedTypeNameId}
{}
+ friend bool operator<(const PropertyDeclaration &first, const PropertyDeclaration &second)
+ {
+ return std::tie(first.typeId, first.propertyDeclarationId)
+ < std::tie(second.typeId, second.propertyDeclarationId);
+ }
+
public:
TypeId typeId;
PropertyDeclarationId propertyDeclarationId;
ImportedTypeNameId importedTypeNameId;
};
+ using PropertyDeclarations = std::vector<PropertyDeclaration>;
+
class Prototype
{
public:
@@ -359,15 +452,58 @@ private:
, prototypeNameId{std::move(prototypeNameId)}
{}
+ friend bool operator<(Prototype first, Prototype second)
+ {
+ return first.typeId < second.typeId;
+ }
+
public:
TypeId typeId;
ImportedTypeNameId prototypeNameId;
};
+ using Prototypes = std::vector<Prototype>;
+
+ template<typename Type>
+ struct TypeCompare
+ {
+ bool operator()(const Type &type, TypeId typeId) { return type.typeId < typeId; };
+
+ bool operator()(TypeId typeId, const Type &type) { return typeId < type.typeId; };
+
+ bool operator()(const Type &first, const Type &second)
+ {
+ return first.typeId < second.typeId;
+ };
+ };
+
+ template<typename Property>
+ struct PropertyCompare
+ {
+ bool operator()(const Property &property, PropertyDeclarationId id)
+ {
+ return property.propertyDeclarationId < id;
+ };
+
+ bool operator()(PropertyDeclarationId id, const Property &property)
+ {
+ return id < property.propertyDeclarationId;
+ };
+
+ bool operator()(const Property &first, const Property &second)
+ {
+ return first.propertyDeclarationId < second.propertyDeclarationId;
+ };
+ };
+
void synchronizeTypes(Storage::Types &types,
TypeIds &updatedTypeIds,
- std::vector<AliasPropertyDeclaration> &insertedAliasPropertyDeclarations,
- std::vector<AliasPropertyDeclaration> &updatedAliasPropertyDeclarations)
+ AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
+ AliasPropertyDeclarations &updatedAliasPropertyDeclarations,
+ AliasPropertyDeclarations &relinkableAliasPropertyDeclarations,
+ PropertyDeclarations &relinkablePropertyDeclarations,
+ Prototypes &relinkablePrototypes,
+ const std::vector<int> &sourceIdValues)
{
Storage::ExportedTypes exportedTypes;
exportedTypes.reserve(types.size() * 3);
@@ -381,16 +517,19 @@ private:
extractExportedTypes(typeId, type, exportedTypes);
}
- synchronizeExportedTypes(updatedTypeIds, exportedTypes);
+ synchronizeExportedTypes(sourceIdValues,
+ updatedTypeIds,
+ exportedTypes,
+ relinkableAliasPropertyDeclarations,
+ relinkablePropertyDeclarations,
+ relinkablePrototypes);
- for (auto &&type : types)
- syncPrototypes(type);
-
- for (auto &&type : types)
- resetRemovedAliasPropertyDeclarationsToNull(type.typeId, type.propertyDeclarations);
-
- for (auto &&type : types)
- syncDeclarations(type, insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations);
+ syncPrototypes(types, relinkablePrototypes);
+ resetRemovedAliasPropertyDeclarationsToNull(types, relinkableAliasPropertyDeclarations);
+ syncDeclarations(types,
+ insertedAliasPropertyDeclarations,
+ updatedAliasPropertyDeclarations,
+ relinkablePropertyDeclarations);
}
void synchronizeFileStatuses(FileStatuses &fileStatuses, const std::vector<int> &sourceIdValues)
@@ -428,43 +567,10 @@ private:
Sqlite::insertUpdateDelete(range, fileStatuses, compareKey, insert, update, remove);
}
- void synchronizeModules(Storage::Modules &modules,
- TypeIds &typeIdsToBeDeleted,
- const std::vector<int> &moduleIdValues)
- {
- auto compareKey = [](auto &&first, auto &&second) {
- return first.sourceId.id - second.sourceId.id;
- };
-
- std::sort(modules.begin(), modules.end(), [&](auto &&first, auto &&second) {
- return compareKey(first, second) < 0;
- });
-
- auto range = selectModulesForIdsStatement.template range<Storage::ModuleView>(
- Utils::span(moduleIdValues));
-
- auto insert = [&](Storage::Module &module) {
- insertModuleStatement.write(module.name, &module.sourceId);
- };
-
- auto update = [&](const Storage::ModuleView &moduleView, Storage::Module &module) {
- if (moduleView.name != module.name)
- updateModuleStatement.write(&moduleView.sourceId, module.name);
- };
-
- auto remove = [&](const Storage::ModuleView &moduleView) {
- deleteModuleStatement.write(&moduleView.sourceId);
- selectTypeIdsForModuleIdStatement.readTo(typeIdsToBeDeleted, &moduleView.sourceId);
- };
-
- Sqlite::insertUpdateDelete(range, modules, compareKey, insert, update, remove);
- }
-
void synchronizeImports(Storage::Imports &imports, std::vector<int> &sourceIdValues)
{
deleteDocumentImportsForDeletedDocuments(imports, sourceIdValues);
- addModuleIdToImports(imports);
synchronizeDocumentImports(imports, sourceIdValues);
}
@@ -487,50 +593,28 @@ private:
deleteDocumentImportsWithSourceIdsStatement.write(Utils::span{documentSourceIdsToBeDeleted});
}
- void synchronizeModulesAndUpdatesModuleIds(Storage::Modules &modules,
- TypeIds &typeIdsToBeDeleted,
- const std::vector<int> &moduleIds)
+ ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const
{
- auto compareKey = [](auto &&first, auto &&second) {
- return first.sourceId.id - second.sourceId.id;
- };
-
- std::sort(modules.begin(), modules.end(), [&](auto &&first, auto &&second) {
- return compareKey(first, second) < 0;
- });
-
- auto range = selectModulesForIdsStatement.template range<Storage::ModuleView>(
- Utils::span(moduleIds));
-
- auto insert = [&](Storage::Module &module) {
- insertModuleStatement.write(module.name, &module.sourceId);
- };
-
- auto update = [&](const Storage::ModuleView &moduleView, Storage::Module &module) {
- if (moduleView.name != module.name)
- updateModuleStatement.write(&moduleView.sourceId, module.name);
- };
+ auto moduleId = selectModuleIdByNameStatement.template value<ModuleId>(name);
- auto remove = [&](const Storage::ModuleView &moduleView) {
- deleteModuleStatement.write(&moduleView.sourceId);
- selectTypeIdsForModuleIdStatement.readTo(typeIdsToBeDeleted, &moduleView.sourceId);
- };
+ if (moduleId)
+ return moduleId;
- Sqlite::insertUpdateDelete(range, modules, compareKey, insert, update, remove);
+ return insertModuleNameStatement.template value<ModuleId>(name);
}
- ModuleId fetchModuleIdUnguarded(const Storage::Module &module) const
+ auto fetchModuleNameUnguarded(ModuleId id) const
{
- return fetchModuleIdUnguarded(module.name);
- }
+ auto moduleName = selectModuleNameStatement.template value<Utils::PathString>(&id);
- ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const
- {
- return selectModuleIdByNameStatement.template value<ModuleId>(name);
+ if (moduleName.empty())
+ throw ModuleDoesNotExists{};
+
+ return moduleName;
}
void handleAliasPropertyDeclarationsWithPropertyType(
- TypeId typeId, std::vector<AliasPropertyDeclaration> &relinkableAliasPropertyDeclarations)
+ TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations)
{
auto callback = [&](long long typeId,
long long propertyDeclarationId,
@@ -555,8 +639,7 @@ private:
}
void prepareLinkingOfAliasPropertiesDeclarationsWithAliasId(
- PropertyDeclarationId aliasId,
- std::vector<AliasPropertyDeclaration> &relinkableAliasPropertyDeclarations)
+ PropertyDeclarationId aliasId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations)
{
auto callback = [&](long long propertyDeclarationId,
long long propertyImportedTypeNameId,
@@ -578,14 +661,14 @@ private:
&aliasId);
}
- void handlePropertyDeclarationWithPropertyType(
- TypeId typeId, std::vector<PropertyDeclaration> &relinkablePropertyDeclarations)
+ void handlePropertyDeclarationWithPropertyType(TypeId typeId,
+ PropertyDeclarations &relinkablePropertyDeclarations)
{
updatesPropertyDeclarationPropertyTypeToNullStatement.readTo(relinkablePropertyDeclarations,
&typeId);
}
- void handlePrototypes(TypeId prototypeId, std::vector<Prototype> &relinkablePrototypes)
+ void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes)
{
auto callback = [&](long long typeId, long long prototypeNameId) {
relinkablePrototypes.emplace_back(TypeId{typeId}, ImportedTypeNameId{prototypeNameId});
@@ -597,9 +680,9 @@ private:
}
void deleteType(TypeId typeId,
- std::vector<AliasPropertyDeclaration> &relinkableAliasPropertyDeclarations,
- std::vector<PropertyDeclaration> &relinkablePropertyDeclarations,
- std::vector<Prototype> &relinkablePrototypes)
+ AliasPropertyDeclarations &relinkableAliasPropertyDeclarations,
+ PropertyDeclarations &relinkablePropertyDeclarations,
+ Prototypes &relinkablePrototypes)
{
handlePropertyDeclarationWithPropertyType(typeId, relinkablePropertyDeclarations);
handleAliasPropertyDeclarationsWithPropertyType(typeId, relinkableAliasPropertyDeclarations);
@@ -612,72 +695,85 @@ private:
deleteTypeStatement.write(&typeId);
}
- void relinkAliasPropertyDeclarations(
- const std::vector<AliasPropertyDeclaration> &aliasPropertyDeclarations,
- const TypeIds &deletedTypeIds)
+ void relinkAliasPropertyDeclarations(AliasPropertyDeclarations &aliasPropertyDeclarations,
+ const TypeIds &deletedTypeIds)
{
- for (const AliasPropertyDeclaration &alias : aliasPropertyDeclarations) {
- if (std::binary_search(deletedTypeIds.begin(), deletedTypeIds.end(), alias.typeId))
- continue;
+ std::sort(aliasPropertyDeclarations.begin(), aliasPropertyDeclarations.end());
- auto typeId = fetchTypeId(alias.aliasImportedTypeNameId);
+ Utils::set_greedy_difference(
+ aliasPropertyDeclarations.cbegin(),
+ aliasPropertyDeclarations.cend(),
+ deletedTypeIds.begin(),
+ deletedTypeIds.end(),
+ [&](const AliasPropertyDeclaration &alias) {
+ auto typeId = fetchTypeId(alias.aliasImportedTypeNameId);
- if (!typeId)
- throw TypeNameDoesNotExists{};
+ if (!typeId)
+ throw TypeNameDoesNotExists{};
- auto [propertyTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeIdAndNameUngarded(
- typeId, alias.aliasPropertyName);
+ auto [propertyTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeIdAndNameUngarded(
+ typeId, alias.aliasPropertyName);
- updatePropertyDeclarationWithAliasAndTypeStatement.write(&alias.propertyDeclarationId,
- &propertyTypeId,
- propertyTraits,
- &alias.aliasImportedTypeNameId,
- &aliasId);
- }
+ updatePropertyDeclarationWithAliasAndTypeStatement.write(&alias.propertyDeclarationId,
+ &propertyTypeId,
+ propertyTraits,
+ &alias.aliasImportedTypeNameId,
+ &aliasId);
+ },
+ TypeCompare<AliasPropertyDeclaration>{});
}
- void relinkPropertyDeclarations(const std::vector<PropertyDeclaration> &relinkablePropertyDeclaration,
+ void relinkPropertyDeclarations(PropertyDeclarations &relinkablePropertyDeclaration,
const TypeIds &deletedTypeIds)
{
- for (const PropertyDeclaration &property : relinkablePropertyDeclaration) {
- if (std::binary_search(deletedTypeIds.begin(), deletedTypeIds.end(), property.typeId))
- continue;
+ std::sort(relinkablePropertyDeclaration.begin(), relinkablePropertyDeclaration.end());
- TypeId propertyTypeId = fetchTypeId(property.importedTypeNameId);
+ Utils::set_greedy_difference(
+ relinkablePropertyDeclaration.cbegin(),
+ relinkablePropertyDeclaration.cend(),
+ deletedTypeIds.begin(),
+ deletedTypeIds.end(),
+ [&](const PropertyDeclaration &property) {
+ TypeId propertyTypeId = fetchTypeId(property.importedTypeNameId);
- if (!propertyTypeId)
- throw TypeNameDoesNotExists{};
+ if (!propertyTypeId)
+ throw TypeNameDoesNotExists{};
- updatePropertyDeclarationTypeStatement.write(&property.propertyDeclarationId,
- &propertyTypeId);
- }
+ updatePropertyDeclarationTypeStatement.write(&property.propertyDeclarationId,
+ &propertyTypeId);
+ },
+ TypeCompare<PropertyDeclaration>{});
}
- void relinkPrototypes(std::vector<Prototype> relinkablePrototypes, const TypeIds &deletedTypeIds)
+ void relinkPrototypes(Prototypes &relinkablePrototypes, const TypeIds &deletedTypeIds)
{
- for (const Prototype &prototype : relinkablePrototypes) {
- if (std::binary_search(deletedTypeIds.begin(), deletedTypeIds.end(), prototype.typeId))
- continue;
+ std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end());
- TypeId prototypeId = fetchTypeId(prototype.prototypeNameId);
+ Utils::set_greedy_difference(
+ relinkablePrototypes.cbegin(),
+ relinkablePrototypes.cend(),
+ deletedTypeIds.begin(),
+ deletedTypeIds.end(),
+ [&](const Prototype &prototype) {
+ TypeId prototypeId = fetchTypeId(prototype.prototypeNameId);
- if (!prototypeId)
- throw TypeNameDoesNotExists{};
+ if (!prototypeId)
+ throw TypeNameDoesNotExists{};
- updateTypePrototypeStatement.write(&prototype.typeId, &prototypeId);
- checkForPrototypeChainCycle(prototype.typeId);
- }
+ updateTypePrototypeStatement.write(&prototype.typeId, &prototypeId);
+ checkForPrototypeChainCycle(prototype.typeId);
+ },
+ TypeCompare<Prototype>{});
}
void deleteNotUpdatedTypes(const TypeIds &updatedTypeIds,
const std::vector<int> &sourceIdValues,
- const TypeIds &typeIdsToBeDeleted)
+ const TypeIds &typeIdsToBeDeleted,
+ AliasPropertyDeclarations &relinkableAliasPropertyDeclarations,
+ PropertyDeclarations &relinkablePropertyDeclarations,
+ Prototypes &relinkablePrototypes,
+ TypeIds &deletedTypeIds)
{
- std::vector<AliasPropertyDeclaration> relinkableAliasPropertyDeclarations;
- std::vector<PropertyDeclaration> relinkablePropertyDeclarations;
- std::vector<Prototype> relinkablePrototypes;
- TypeIds deletedTypeIds;
-
auto updatedTypeIdValues = Utils::transform<std::vector>(updatedTypeIds, [](TypeId typeId) {
return &typeId;
});
@@ -696,7 +792,13 @@ private:
Utils::span(updatedTypeIdValues));
for (TypeId typeIdToBeDeleted : typeIdsToBeDeleted)
callback(&typeIdToBeDeleted);
+ }
+ void relink(AliasPropertyDeclarations &relinkableAliasPropertyDeclarations,
+ PropertyDeclarations &relinkablePropertyDeclarations,
+ Prototypes &relinkablePrototypes,
+ TypeIds &deletedTypeIds)
+ {
std::sort(deletedTypeIds.begin(), deletedTypeIds.end());
relinkPrototypes(relinkablePrototypes, deletedTypeIds);
@@ -704,7 +806,7 @@ private:
relinkAliasPropertyDeclarations(relinkableAliasPropertyDeclarations, deletedTypeIds);
}
- void linkAliasPropertyDeclarationAliasIds(const std::vector<AliasPropertyDeclaration> &aliasDeclarations)
+ void linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations)
{
for (const auto &aliasDeclaration : aliasDeclarations) {
auto aliasTypeId = fetchTypeId(aliasDeclaration.aliasImportedTypeNameId);
@@ -722,7 +824,7 @@ private:
}
}
- void updateAliasPropertyDeclarationValues(const std::vector<AliasPropertyDeclaration> &aliasDeclarations)
+ void updateAliasPropertyDeclarationValues(const AliasPropertyDeclarations &aliasDeclarations)
{
for (const auto &aliasDeclaration : aliasDeclarations) {
updatetPropertiesDeclarationValuesOfAliasStatement.write(
@@ -732,14 +834,14 @@ private:
}
}
- void checkAliasPropertyDeclarationCycles(const std::vector<AliasPropertyDeclaration> &aliasDeclarations)
+ void checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations)
{
for (const auto &aliasDeclaration : aliasDeclarations)
checkForAliasChainCycle(aliasDeclaration.propertyDeclarationId);
}
- void linkAliases(const std::vector<AliasPropertyDeclaration> &insertedAliasPropertyDeclarations,
- const std::vector<AliasPropertyDeclaration> &updatedAliasPropertyDeclarations)
+ void linkAliases(const AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
+ const AliasPropertyDeclarations &updatedAliasPropertyDeclarations)
{
linkAliasPropertyDeclarationAliasIds(insertedAliasPropertyDeclarations);
linkAliasPropertyDeclarationAliasIds(updatedAliasPropertyDeclarations);
@@ -751,16 +853,25 @@ private:
updateAliasPropertyDeclarationValues(updatedAliasPropertyDeclarations);
}
- void synchronizeExportedTypes(const TypeIds &typeIds, Storage::ExportedTypes &exportedTypes)
+ void synchronizeExportedTypes(const std::vector<int> &sourceIdValues,
+ const TypeIds &updatedTypeIds,
+ Storage::ExportedTypes &exportedTypes,
+ AliasPropertyDeclarations &relinkableAliasPropertyDeclarations,
+ PropertyDeclarations &relinkablePropertyDeclarations,
+ Prototypes &relinkablePrototypes)
{
std::sort(exportedTypes.begin(), exportedTypes.end(), [](auto &&first, auto &&second) {
return std::tie(first.moduleId, first.name, first.version)
< std::tie(second.moduleId, second.name, second.version);
});
- auto range = selectExportedTypesForTypeIdStatement.template range<Storage::ExportedTypeView>(
- const_cast<void *>(static_cast<const void *>(typeIds.data())),
- static_cast<long long>(typeIds.size()));
+ Utils::span typeIdValues{static_cast<const TypeIds::value_type::DatabaseType *>(
+ &updatedTypeIds.data()->id),
+ updatedTypeIds.size()};
+
+ auto range = selectExportedTypesForSourceIdsStatement
+ .template range<Storage::ExportedTypeView>(Utils::span{sourceIdValues},
+ typeIdValues);
auto compareKey = [](const Storage::ExportedTypeView &view,
const Storage::ExportedType &type) -> long long {
@@ -780,53 +891,55 @@ private:
};
auto insert = [&](const Storage::ExportedType &type) {
- if (type.version) {
- upsertExportedTypeNamesWithVersionStatement.write(&type.moduleId,
- type.name,
- static_cast<long long>(
- Storage::TypeNameKind::Exported),
- type.version.major.value,
- type.version.minor.value,
- &type.typeId);
-
- } else if (type.version.major) {
- upsertExportedTypeNamesWithMajorVersionStatement
- .write(&type.moduleId,
- type.name,
- static_cast<long long>(Storage::TypeNameKind::Exported),
- type.version.major.value,
- &type.typeId);
- } else {
- upsertExportedTypeNamesWithoutVersionStatement
- .write(&type.moduleId,
- type.name,
- static_cast<long long>(Storage::TypeNameKind::Exported),
- &type.typeId);
+ if (!type.moduleId)
+ throw QmlDesigner::ModuleDoesNotExists{};
+
+ try {
+ if (type.version) {
+ insertExportedTypeNamesWithVersionStatement.write(&type.moduleId,
+ type.name,
+ type.version.major.value,
+ type.version.minor.value,
+ &type.typeId);
+
+ } else if (type.version.major) {
+ insertExportedTypeNamesWithMajorVersionStatement.write(&type.moduleId,
+ type.name,
+ type.version.major.value,
+ &type.typeId);
+ } else {
+ insertExportedTypeNamesWithoutVersionStatement.write(&type.moduleId,
+ type.name,
+ &type.typeId);
+ }
+ } catch (const Sqlite::ConstraintPreventsModification &) {
+ throw QmlDesigner::ModuleDoesNotExists{};
}
};
auto update = [&](const Storage::ExportedTypeView &view, const Storage::ExportedType &type) {
- if (view.typeId != type.typeId)
+ if (view.typeId != type.typeId) {
+ handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations);
+ handleAliasPropertyDeclarationsWithPropertyType(view.typeId,
+ relinkableAliasPropertyDeclarations);
+ handlePrototypes(view.typeId, relinkablePrototypes);
updateExportedTypeNameTypeIdStatement.write(&view.exportedTypeNameId, &type.typeId);
+ }
};
auto remove = [&](const Storage::ExportedTypeView &view) {
+ handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations);
+ handleAliasPropertyDeclarationsWithPropertyType(view.typeId,
+ relinkableAliasPropertyDeclarations);
+ handlePrototypes(view.typeId, relinkablePrototypes);
deleteExportedTypeNameStatement.write(&view.exportedTypeNameId);
};
Sqlite::insertUpdateDelete(range, exportedTypes, compareKey, insert, update, remove);
}
- void upsertNativeType(ModuleId moduleId, Utils::SmallStringView name, TypeId typeId)
- {
- upsertExportedTypeNameStatement.write(&moduleId,
- name,
- static_cast<long long>(Storage::TypeNameKind::Native),
- &typeId);
- }
-
void synchronizePropertyDeclarationsInsertAlias(
- std::vector<AliasPropertyDeclaration> &insertedAliasPropertyDeclarations,
+ AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
const Storage::PropertyDeclaration &value,
SourceId sourceId,
TypeId typeId)
@@ -872,7 +985,7 @@ private:
}
void synchronizePropertyDeclarationsUpdateAlias(
- std::vector<AliasPropertyDeclaration> &updatedAliasPropertyDeclarations,
+ AliasPropertyDeclarations &updatedAliasPropertyDeclarations,
const Storage::PropertyDeclarationView &view,
const Storage::PropertyDeclaration &value,
SourceId sourceId)
@@ -887,7 +1000,8 @@ private:
void synchronizePropertyDeclarationsUpdateProperty(const Storage::PropertyDeclarationView &view,
const Storage::PropertyDeclaration &value,
- SourceId sourceId)
+ SourceId sourceId,
+ PropertyDeclarationIds &propertyDeclarationIds)
{
auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId);
@@ -906,14 +1020,15 @@ private:
&propertyImportedTypeNameId);
updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement
.write(&view.id, &propertyTypeId, static_cast<int>(value.traits));
+ propertyDeclarationIds.push_back(view.id);
}
- void synchronizePropertyDeclarations(
- TypeId typeId,
- Storage::PropertyDeclarations &propertyDeclarations,
- SourceId sourceId,
- std::vector<AliasPropertyDeclaration> &insertedAliasPropertyDeclarations,
- std::vector<AliasPropertyDeclaration> &updatedAliasPropertyDeclarations)
+ void synchronizePropertyDeclarations(TypeId typeId,
+ Storage::PropertyDeclarations &propertyDeclarations,
+ SourceId sourceId,
+ AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
+ AliasPropertyDeclarations &updatedAliasPropertyDeclarations,
+ PropertyDeclarationIds &propertyDeclarationIds)
{
std::sort(propertyDeclarations.begin(),
propertyDeclarations.end(),
@@ -947,8 +1062,12 @@ private:
view,
value,
sourceId);
+ propertyDeclarationIds.push_back(view.id);
} else {
- synchronizePropertyDeclarationsUpdateProperty(view, value, sourceId);
+ synchronizePropertyDeclarationsUpdateProperty(view,
+ value,
+ sourceId,
+ propertyDeclarationIds);
}
};
@@ -962,14 +1081,20 @@ private:
}
deletePropertyDeclarationStatement.write(&view.id);
+ propertyDeclarationIds.push_back(view.id);
};
Sqlite::insertUpdateDelete(range, propertyDeclarations, compareKey, insert, update, remove);
}
- void resetRemovedAliasPropertyDeclarationsToNull(TypeId typeId,
- Storage::PropertyDeclarations &aliasDeclarations)
+ void resetRemovedAliasPropertyDeclarationsToNull(Storage::Type &type,
+ PropertyDeclarationIds &propertyDeclarationIds)
{
+ if (type.changeLevel == Storage::ChangeLevel::Minimal)
+ return;
+
+ Storage::PropertyDeclarations &aliasDeclarations = type.propertyDeclarations;
+
class AliasPropertyDeclarationView
{
public:
@@ -992,7 +1117,7 @@ private:
});
auto range = selectPropertyDeclarationsWithAliasForTypeIdStatement
- .template range<AliasPropertyDeclarationView>(&typeId);
+ .template range<AliasPropertyDeclarationView>(&type.typeId);
auto compareKey = [](const AliasPropertyDeclarationView &view,
const Storage::PropertyDeclaration &value) {
@@ -1006,29 +1131,24 @@ private:
auto remove = [&](const AliasPropertyDeclarationView &view) {
updatePropertyDeclarationAliasIdToNullStatement.write(&view.id);
+ propertyDeclarationIds.push_back(view.id);
};
Sqlite::insertUpdateDelete(range, aliasDeclarations, compareKey, insert, update, remove);
}
- ModuleIds fetchModuleIdsUnguarded(const Storage::Modules &modules)
+ void resetRemovedAliasPropertyDeclarationsToNull(
+ Storage::Types &types, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations)
{
- ModuleIds moduleIds;
- moduleIds.reserve(moduleIds.size());
+ PropertyDeclarationIds propertyDeclarationIds;
+ propertyDeclarationIds.reserve(types.size());
- for (auto &&module : modules)
- moduleIds.push_back(fetchModuleIdUnguarded(module));
+ for (auto &&type : types)
+ resetRemovedAliasPropertyDeclarationsToNull(type, propertyDeclarationIds);
- return moduleIds;
- }
-
- void addModuleIdToImports(Storage::Imports &imports)
- {
- for (Storage::Import &import : imports) {
- import.moduleId = fetchModuleIdUnguarded(import.name);
- if (!import.moduleId)
- throw ModuleDoesNotExists{};
- }
+ removeRelinkableEntries(relinkableAliasPropertyDeclarations,
+ propertyDeclarationIds,
+ PropertyCompare<AliasPropertyDeclaration>{});
}
void synchronizeDocumentImports(Storage::Imports &imports, const std::vector<int> &sourceIdValues)
@@ -1096,7 +1216,7 @@ private:
json.append(parameter.name);
json.append("\",\"tn\":\"");
json.append(parameter.typeName);
- if (parameter.traits == Storage::PropertyDeclarationTraits::Non) {
+ if (parameter.traits == Storage::PropertyDeclarationTraits::None) {
json.append("\"}");
} else {
json.append("\",\"tr\":");
@@ -1257,60 +1377,88 @@ private:
Storage::ExportedTypes &exportedTypes)
{
for (const auto &exportedType : type.exportedTypes)
- exportedTypes.emplace_back(exportedType.name, exportedType.version, typeId, type.moduleId);
+ exportedTypes.emplace_back(exportedType.name,
+ exportedType.version,
+ typeId,
+ exportedType.moduleId);
}
- struct ModuleAndTypeId
- {
- ModuleAndTypeId() = default;
- ModuleAndTypeId(int moduleId, long long typeId)
- : moduleId{moduleId}
- , typeId{typeId}
- {}
-
- ModuleId moduleId;
- TypeId typeId;
- };
-
TypeId declareType(Storage::Type &type)
{
- if (!type.moduleId && type.typeName.isEmpty()) {
- auto [moduleId, typeId] = selectModuleAndTypeIdBySourceIdStatement
- .template value<ModuleAndTypeId>(&type.sourceId);
- type.typeId = typeId;
- type.moduleId = moduleId;
+ if (type.typeName.isEmpty()) {
+ type.typeId = selectTypeIdBySourceIdStatement.template value<TypeId>(&type.sourceId);
+
return type.typeId;
}
- if (!type.moduleId)
- throw ModuleDoesNotExists{};
-
- type.typeId = upsertTypeStatement.template value<TypeId>(&type.moduleId,
+ type.typeId = upsertTypeStatement.template value<TypeId>(&type.sourceId,
type.typeName,
- static_cast<int>(type.accessSemantics),
- &type.sourceId);
+ static_cast<int>(
+ type.accessSemantics));
if (!type.typeId)
- type.typeId = selectTypeIdByModuleIdAndNameStatement.template value<TypeId>(&type.moduleId,
+ type.typeId = selectTypeIdBySourceIdAndNameStatement.template value<TypeId>(&type.sourceId,
type.typeName);
- upsertNativeType(type.moduleId, type.typeName, type.typeId);
return type.typeId;
}
void syncDeclarations(Storage::Type &type,
- std::vector<AliasPropertyDeclaration> &insertedAliasPropertyDeclarations,
- std::vector<AliasPropertyDeclaration> &updatedAliasPropertyDeclarations)
+ AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
+ AliasPropertyDeclarations &updatedAliasPropertyDeclarations,
+ PropertyDeclarationIds &propertyDeclarationIds)
{
- auto typeId = type.typeId;
- synchronizePropertyDeclarations(typeId,
+ if (type.changeLevel == Storage::ChangeLevel::Minimal)
+ return;
+
+ synchronizePropertyDeclarations(type.typeId,
type.propertyDeclarations,
type.sourceId,
insertedAliasPropertyDeclarations,
- updatedAliasPropertyDeclarations);
- synchronizeFunctionDeclarations(typeId, type.functionDeclarations);
- synchronizeSignalDeclarations(typeId, type.signalDeclarations);
- synchronizeEnumerationDeclarations(typeId, type.enumerationDeclarations);
+ updatedAliasPropertyDeclarations,
+ propertyDeclarationIds);
+ synchronizeFunctionDeclarations(type.typeId, type.functionDeclarations);
+ synchronizeSignalDeclarations(type.typeId, type.signalDeclarations);
+ synchronizeEnumerationDeclarations(type.typeId, type.enumerationDeclarations);
+ }
+
+ template<typename Relinkable, typename Ids, typename Compare>
+ void removeRelinkableEntries(std::vector<Relinkable> &relinkables, Ids &ids, Compare compare)
+ {
+ std::vector<Relinkable> newRelinkables;
+ newRelinkables.reserve(relinkables.size());
+
+ std::sort(ids.begin(), ids.end());
+ std::sort(relinkables.begin(), relinkables.end(), compare);
+
+ Utils::set_greedy_difference(
+ relinkables.begin(),
+ relinkables.end(),
+ ids.cbegin(),
+ ids.cend(),
+ [&](Relinkable &entry) { newRelinkables.push_back(std::move(entry)); },
+ compare);
+
+ relinkables = std::move(newRelinkables);
+ }
+
+ void syncDeclarations(Storage::Types &types,
+ AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
+ AliasPropertyDeclarations &updatedAliasPropertyDeclarations,
+ PropertyDeclarations &relinkablePropertyDeclarations)
+ {
+ PropertyDeclarationIds propertyDeclarationIds;
+ propertyDeclarationIds.reserve(types.size() * 10);
+
+ for (auto &&type : types)
+ syncDeclarations(type,
+ insertedAliasPropertyDeclarations,
+ updatedAliasPropertyDeclarations,
+ propertyDeclarationIds);
+
+ removeRelinkableEntries(relinkablePropertyDeclarations,
+ propertyDeclarationIds,
+ PropertyCompare<PropertyDeclaration>{});
}
void checkForPrototypeChainCycle(TypeId typeId) const
@@ -1338,7 +1486,7 @@ private:
&propertyDeclarationId);
}
- void syncPrototypes(Storage::Type &type)
+ void syncPrototype(Storage::Type &type, TypeIds &typeIds)
{
if (type.changeLevel == Storage::ChangeLevel::Minimal)
return;
@@ -1358,22 +1506,35 @@ private:
updatePrototypeStatement.write(&type.typeId, &prototypeId, &prototypeTypeNameId);
checkForPrototypeChainCycle(type.typeId);
}
+
+ typeIds.push_back(type.typeId);
+ }
+
+ void syncPrototypes(Storage::Types &types, Prototypes &relinkablePrototypes)
+ {
+ TypeIds typeIds;
+ typeIds.reserve(types.size());
+
+ for (auto &type : types)
+ syncPrototype(type, typeIds);
+
+ removeRelinkableEntries(relinkablePrototypes, typeIds, TypeCompare<Prototype>{});
}
ImportId fetchImportId(SourceId sourceId, const Storage::Import &import) const
{
if (import.version) {
- return selectImportIdBySourceIdAndImportNameAndVersionStatement.template value<ImportId>(
- &sourceId, import.name, import.version.major.value, import.version.minor.value);
+ return selectImportIdBySourceIdAndModuleIdAndVersionStatement.template value<ImportId>(
+ &sourceId, &import.moduleId, import.version.major.value, import.version.minor.value);
}
if (import.version.major) {
- return selectImportIdBySourceIdAndImportNameAndMajorVersionStatement
- .template value<ImportId>(&sourceId, import.name, import.version.major.value);
+ return selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement
+ .template value<ImportId>(&sourceId, &import.moduleId, import.version.major.value);
}
- return selectImportIdBySourceIdAndImportNameStatement.template value<ImportId>(&sourceId,
- import.name);
+ return selectImportIdBySourceIdAndModuleIdStatement.template value<ImportId>(&sourceId,
+ &import.moduleId);
}
ImportedTypeNameId fetchImportedTypeNameId(const Storage::ImportedTypeName &name, SourceId sourceId)
@@ -1440,9 +1601,6 @@ private:
&typeNameId);
}
- if (kind == Storage::TypeNameKind::Native)
- return selectTypeIdForNativeTypeNameNamesStatement.template value<TypeId>(&typeNameId);
-
return selectTypeIdForImportedTypeNameNamesStatement.template value<TypeId>(&typeNameId);
}
@@ -1649,21 +1807,16 @@ private:
typesTable.setUseIfNotExists(true);
typesTable.setName("types");
typesTable.addColumn("typeId", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
- auto &moduleIdColumn = typesTable.addForeignKeyColumn("moduleId",
- foreignModuleIdColumn,
- Sqlite::ForeignKeyAction::NoAction,
- Sqlite::ForeignKeyAction::NoAction,
- Sqlite::Enforment::Deferred);
+ auto &sourceIdColumn = typesTable.addColumn("sourceId");
auto &typesNameColumn = typesTable.addColumn("name");
typesTable.addColumn("accessSemantics");
- typesTable.addColumn("sourceId");
typesTable.addForeignKeyColumn("prototypeId",
typesTable,
Sqlite::ForeignKeyAction::NoAction,
Sqlite::ForeignKeyAction::Restrict);
typesTable.addColumn("prototypeNameId");
- typesTable.addUniqueIndex({moduleIdColumn, typesNameColumn});
+ typesTable.addUniqueIndex({sourceIdColumn, typesNameColumn});
typesTable.initialize(database);
@@ -1706,23 +1859,20 @@ private:
auto &moduleIdColumn = table.addForeignKeyColumn("moduleId",
foreignModuleIdColumn,
Sqlite::ForeignKeyAction::NoAction,
- Sqlite::ForeignKeyAction::NoAction,
- Sqlite::Enforment::Deferred);
+ Sqlite::ForeignKeyAction::NoAction);
auto &nameColumn = table.addColumn("name");
- auto &kindColumn = table.addColumn("kind");
auto &typeIdColumn = table.addColumn("typeId");
auto &majorVersionColumn = table.addColumn("majorVersion");
auto &minorVersionColumn = table.addColumn("minorVersion");
- table.addUniqueIndex({moduleIdColumn, nameColumn, kindColumn},
+ table.addUniqueIndex({moduleIdColumn, nameColumn},
"majorVersion IS NULL AND minorVersion IS NULL");
- table.addUniqueIndex({moduleIdColumn, nameColumn, kindColumn, majorVersionColumn},
+ table.addUniqueIndex({moduleIdColumn, nameColumn, majorVersionColumn},
"majorVersion IS NOT NULL AND minorVersion IS NULL");
- table.addUniqueIndex(
- {moduleIdColumn, nameColumn, kindColumn, majorVersionColumn, minorVersionColumn},
- "majorVersion IS NOT NULL AND minorVersion IS NOT NULL");
+ table.addUniqueIndex({moduleIdColumn, nameColumn, majorVersionColumn, minorVersionColumn},
+ "majorVersion IS NOT NULL AND minorVersion IS NOT NULL");
- table.addIndex({typeIdColumn}, "kind=1");
+ table.addIndex({typeIdColumn});
table.initialize(database);
}
@@ -1823,6 +1973,7 @@ private:
Sqlite::Enforment::Deferred);
auto &majorVersionColumn = table.addColumn("majorVersion");
auto &minorVersionColumn = table.addColumn("minorVersion");
+ table.addColumn("kind");
table.addUniqueIndex({sourceIdColumn, moduleIdColumn},
"majorVersion IS NULL AND minorVersion IS NULL");
@@ -1856,18 +2007,18 @@ private:
public:
Database &database;
Initializer initializer;
+ ModuleCache moduleCache{ModuleStorageAdapter{*this}};
ReadWriteStatement<1> upsertTypeStatement{
- "INSERT INTO types(moduleId, name, accessSemantics, sourceId) VALUES(?1, ?2, "
- "?3, nullif(?4, -1)) ON CONFLICT DO UPDATE SET accessSemantics=excluded.accessSemantics, "
- "sourceId=excluded.sourceId WHERE accessSemantics IS NOT excluded.accessSemantics OR "
- "sourceId IS NOT excluded.sourceId RETURNING typeId",
+ "INSERT INTO types(sourceId, name, accessSemantics) VALUES(?1, ?2, ?3) ON CONFLICT DO "
+ "UPDATE SET accessSemantics=excluded.accessSemantics WHERE accessSemantics IS NOT "
+ "excluded.accessSemantics RETURNING typeId",
database};
WriteStatement updatePrototypeStatement{
"UPDATE types SET prototypeId=?2, prototypeNameId=?3 WHERE typeId=?1 AND (prototypeId IS "
"NOT ?2 OR prototypeNameId IS NOT ?3)",
database};
mutable ReadStatement<1> selectTypeIdByExportedNameStatement{
- "SELECT typeId FROM exportedTypeNames WHERE name=?1 AND kind=1", database};
+ "SELECT typeId FROM exportedTypeNames WHERE name=?1", database};
mutable ReadStatement<1> selectPrototypeIdStatement{
"WITH RECURSIVE "
" typeSelection(typeId) AS ("
@@ -1925,18 +2076,14 @@ public:
"INSERT INTO sources(sourceContextId, sourceName) VALUES (?,?)", database};
mutable ReadStatement<3> selectAllSourcesStatement{
"SELECT sourceName, sourceContextId, sourceId FROM sources", database};
- mutable ReadStatement<5> selectTypeByTypeIdStatement{
- "SELECT moduleId, name, (SELECT name FROM types WHERE typeId=outerTypes.prototypeId), "
- "accessSemantics, ifnull(sourceId, -1) FROM types AS outerTypes WHERE typeId=?",
+ mutable ReadStatement<4> selectTypeByTypeIdStatement{
+ "SELECT sourceId, name, prototypeId, accessSemantics FROM types WHERE typeId=?", database};
+ mutable ReadStatement<4> selectExportedTypesByTypeIdStatement{
+ "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1) FROM "
+ "exportedTypeNames WHERE typeId=?",
database};
- mutable ReadStatement<3> selectExportedTypesByTypeIdStatement{
- "SELECT name, ifnull(majorVersion, -1), ifnull(minorVersion, -1) FROM exportedTypeNames "
- "WHERE typeId=? AND kind=1",
- database};
- mutable ReadStatement<6> selectTypesStatement{
- "SELECT moduleId, name, typeId, (SELECT name FROM types WHERE "
- "typeId=t.prototypeId), accessSemantics, ifnull(sourceId, -1) FROM types AS "
- "t",
+ mutable ReadStatement<5> selectTypesStatement{
+ "SELECT sourceId, name, typeId, ifnull(prototypeId, -1), accessSemantics FROM types",
database};
ReadStatement<1> selectNotUpdatedTypesInSourcesStatement{
"SELECT typeId FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN carray(?2))",
@@ -1953,10 +2100,9 @@ public:
"DELETE FROM signalDeclarations WHERE typeId=?", database};
WriteStatement deleteTypeStatement{"DELETE FROM types WHERE typeId=?", database};
mutable ReadStatement<4> selectPropertyDeclarationsByTypeIdStatement{
- "SELECT name, (SELECT name FROM types WHERE typeId=pd.propertyTypeId), propertyTraits, "
- "(SELECT name FROM propertyDeclarations WHERE "
- "propertyDeclarationId=pd.aliasPropertyDeclarationId) FROM propertyDeclarations AS pd "
- "WHERE typeId=?",
+ "SELECT name, nullif(propertyTypeId, -1), propertyTraits, (SELECT name FROM "
+ "propertyDeclarations WHERE propertyDeclarationId=pd.aliasPropertyDeclarationId) FROM "
+ "propertyDeclarations AS pd WHERE typeId=?",
database};
ReadStatement<6> selectPropertyDeclarationsForTypeIdStatement{
"SELECT name, propertyTraits, propertyTypeId, propertyImportedTypeNameId, "
@@ -2081,25 +2227,18 @@ public:
database};
WriteStatement deleteEnumerationDeclarationStatement{
"DELETE FROM enumerationDeclarations WHERE enumerationDeclarationId=?", database};
- WriteStatement insertModuleStatement{"INSERT INTO modules(name, moduleId) VALUES(?1, ?2)",
- database};
- WriteStatement updateModuleStatement{"UPDATE modules SET name=?2 WHERE moduleId=?1", database};
- WriteStatement deleteModuleStatement{"DELETE FROM modules WHERE moduleId=?", database};
mutable ReadStatement<1> selectModuleIdByNameStatement{
"SELECT moduleId FROM modules WHERE name=? LIMIT 1", database};
- mutable ReadStatement<2> selectModulesForIdsStatement{
- "SELECT name, moduleId FROM modules WHERE moduleId IN carray(?1) ORDER BY "
- "moduleId",
- database};
- mutable ReadStatement<2> selectAllModulesStatement{
- "SELECT name, moduleId FROM modules ORDER BY moduleId", database};
- mutable ReadStatement<1> selectTypeIdsForModuleIdStatement{
- "SELECT typeId FROM types WHERE moduleId=?", database};
- mutable ReadStatement<1> selectTypeIdByModuleIdAndNameStatement{
- "SELECT typeId FROM types WHERE moduleId=?1 and name=?2", database};
+ mutable ReadWriteStatement<1> insertModuleNameStatement{
+ "INSERT INTO modules(name) VALUES(?1) RETURNING moduleId", database};
+ mutable ReadStatement<1> selectModuleNameStatement{
+ "SELECT name FROM modules WHERE moduleId =?1", database};
+ mutable ReadStatement<2> selectAllModulesStatement{"SELECT name, moduleId FROM modules", database};
+ mutable ReadStatement<1> selectTypeIdBySourceIdAndNameStatement{
+ "SELECT typeId FROM types WHERE sourceId=?1 and name=?2", database};
mutable ReadStatement<1> selectTypeIdByModuleIdsAndExportedNameStatement{
"SELECT typeId FROM exportedTypeNames WHERE moduleId IN carray(?1, ?2, 'int32') AND "
- "name=?3 AND kind=1",
+ "name=?3",
database};
mutable ReadStatement<5> selectDocumentImportForSourceIdStatement{
"SELECT importId, sourceId, moduleId, ifnull(majorVersion, -1), ifnull(minorVersion, -1) "
@@ -2241,8 +2380,8 @@ public:
WriteStatement deleteFileStatusStatement{"DELETE FROM fileStatuses WHERE sourceId=?1", database};
WriteStatement updateFileStatusStatement{
"UPDATE fileStatuses SET size=?2, lastModified=?3 WHERE sourceId=?1", database};
- ReadStatement<2> selectModuleAndTypeIdBySourceIdStatement{
- "SELECT moduleId, typeId FROM types WHERE sourceId=?", database};
+ ReadStatement<1> selectTypeIdBySourceIdStatement{"SELECT typeId FROM types WHERE sourceId=?",
+ database};
mutable ReadStatement<1> selectImportedTypeNameIdStatement{
"SELECT importedTypeNameId FROM importedTypeNames WHERE kind=?1 AND importOrSourceId=?2 "
"AND name=?3 LIMIT 1",
@@ -2251,24 +2390,24 @@ public:
"INSERT INTO importedTypeNames(kind, importOrSourceId, name) VALUES (?1, ?2, ?3) "
"RETURNING importedTypeNameId",
database};
- mutable ReadStatement<1> selectImportIdBySourceIdAndImportNameStatement{
- "SELECT importId FROM documentImports JOIN modules AS m USING(moduleId) WHERE sourceId=?1 "
- "AND m.name=?2 AND majorVersion IS NULL AND minorVersion IS NULL LIMIT 1",
+ mutable ReadStatement<1> selectImportIdBySourceIdAndModuleIdStatement{
+ "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND majorVersion "
+ "IS NULL AND minorVersion IS NULL LIMIT 1",
database};
- mutable ReadStatement<1> selectImportIdBySourceIdAndImportNameAndMajorVersionStatement{
- "SELECT importId FROM documentImports JOIN modules AS m USING(moduleId) WHERE sourceId=?1 "
- "AND m.name=?2 AND majorVersion=?3 AND minorVersion IS NULL LIMIT 1",
+ mutable ReadStatement<1> selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement{
+ "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND "
+ "majorVersion=?3 AND minorVersion IS NULL LIMIT 1",
database};
- mutable ReadStatement<1> selectImportIdBySourceIdAndImportNameAndVersionStatement{
- "SELECT importId FROM documentImports JOIN modules AS m USING(moduleId) WHERE sourceId=?1 "
- "AND m.name=?2 AND majorVersion=?3 AND minorVersion=?4 LIMIT 1",
+ mutable ReadStatement<1> selectImportIdBySourceIdAndModuleIdAndVersionStatement{
+ "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND "
+ "majorVersion=?3 AND minorVersion=?4 LIMIT 1",
database};
mutable ReadStatement<1> selectKindFromImportedTypeNamesStatement{
"SELECT kind FROM importedTypeNames WHERE importedTypeNameId=?1", database};
mutable ReadStatement<1> selectTypeIdForQualifiedImportedTypeNameNamesStatement{
"SELECT typeId FROM importedTypeNames AS itn JOIN documentImports AS di ON "
"importOrSourceId=importId JOIN exportedTypeNames AS etn USING(moduleId) WHERE "
- "itn.kind=2 AND importedTypeNameId=?1 AND itn.name=etn.name AND etn.kind=1 AND "
+ "itn.kind=2 AND importedTypeNameId=?1 AND itn.name=etn.name AND "
"(di.majorVersion IS NULL OR (di.majorVersion=etn.majorVersion AND (di.minorVersion IS "
"NULL OR di.minorVersion>=etn.minorVersion))) ORDER BY etn.majorVersion DESC NULLS FIRST, "
"etn.minorVersion DESC NULLS FIRST LIMIT 1",
@@ -2276,39 +2415,29 @@ public:
mutable ReadStatement<1> selectTypeIdForImportedTypeNameNamesStatement{
"SELECT typeId FROM importedTypeNames AS itn JOIN documentImports AS di ON "
"importOrSourceId=sourceId JOIN exportedTypeNames AS etn USING(moduleId) WHERE "
- "itn.kind=1 AND importedTypeNameId=?1 AND itn.name=etn.name AND etn.kind=1 AND "
+ "itn.kind=1 AND importedTypeNameId=?1 AND itn.name=etn.name AND "
"(di.majorVersion IS NULL OR (di.majorVersion=etn.majorVersion AND (di.minorVersion IS "
"NULL OR di.minorVersion>=etn.minorVersion))) ORDER BY etn.majorVersion DESC NULLS FIRST, "
"etn.minorVersion DESC NULLS FIRST LIMIT 1",
database};
- mutable ReadStatement<1> selectTypeIdForNativeTypeNameNamesStatement{
- "SELECT typeId FROM importedTypeNames AS itn JOIN documentImports AS di ON "
- "importOrSourceId=sourceId JOIN exportedTypeNames AS etn USING(moduleId) WHERE itn.kind=0 "
- "AND importedTypeNameId=?1 AND itn.name=etn.name AND etn.kind=0 LIMIT 1",
- database};
WriteStatement deleteAllSourcesStatement{"DELETE FROM sources", database};
WriteStatement deleteAllSourceContextsStatement{"DELETE FROM sourceContexts", database};
- mutable ReadStatement<6> selectExportedTypesForTypeIdStatement{
- "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1), typeId, "
- "exportedTypeNameId FROM exportedTypeNames WHERE typeId IN carray(?1, ?2, 'int64') AND "
- "kind=1 ORDER BY moduleId, name, majorVersion, minorVersion",
- database};
- WriteStatement upsertExportedTypeNamesWithVersionStatement{
- "INSERT INTO exportedTypeNames(moduleId, name, kind, majorVersion, minorVersion, typeId) "
- "VALUES(?1, ?2, ?3, ?4, ?5, ?6) ON CONFLICT DO UPDATE SET typeId=excluded.typeId",
- database};
- WriteStatement upsertExportedTypeNamesWithMajorVersionStatement{
- "INSERT INTO exportedTypeNames(moduleId, name, kind, majorVersion, typeId) "
- "VALUES(?1, ?2, ?3, ?4, ?5) ON CONFLICT DO UPDATE SET typeId=excluded.typeId",
- database};
- WriteStatement upsertExportedTypeNamesWithoutVersionStatement{
- "INSERT INTO exportedTypeNames(moduleId, name, kind, typeId) VALUES(?1, ?2, ?3, ?4) ON "
- "CONFLICT DO UPDATE SET typeId=excluded.typeId",
- database};
- WriteStatement upsertExportedTypeNameStatement{
- "INSERT INTO exportedTypeNames(moduleId, name, kind, typeId) VALUES(?1, ?2, ?3, ?4) ON "
- "CONFLICT DO UPDATE SET typeId=excluded.typeId WHERE typeId IS NOT excluded.typeId",
- database};
+ mutable ReadStatement<6> selectExportedTypesForSourceIdsStatement{
+ "SELECT moduleId, etn.name, ifnull(majorVersion, -1), ifnull(minorVersion, -1), typeId, "
+ "exportedTypeNameId FROM exportedTypeNames AS etn JOIN types USING(typeId) WHERE sourceId "
+ "IN carray(?1) OR typeId in carray(?2) ORDER BY moduleId, etn.name, majorVersion, "
+ "minorVersion",
+ database};
+ WriteStatement insertExportedTypeNamesWithVersionStatement{
+ "INSERT INTO exportedTypeNames(moduleId, name, majorVersion, minorVersion, typeId) "
+ "VALUES(?1, ?2, ?3, ?4, ?5)",
+ database};
+ WriteStatement insertExportedTypeNamesWithMajorVersionStatement{
+ "INSERT INTO exportedTypeNames(moduleId, name, majorVersion, typeId) "
+ "VALUES(?1, ?2, ?3, ?4)",
+ database};
+ WriteStatement insertExportedTypeNamesWithoutVersionStatement{
+ "INSERT INTO exportedTypeNames(moduleId, name, typeId) VALUES(?1, ?2, ?3)", database};
WriteStatement deleteExportedTypeNameStatement{
"DELETE FROM exportedTypeNames WHERE exportedTypeNameId=?", database};
WriteStatement updateExportedTypeNameTypeIdStatement{
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h
index 0d23e11605..42790bded6 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h
@@ -101,4 +101,10 @@ public:
const char *what() const noexcept override { return "There is a prototype chain cycle!"; }
};
+class CannotParseQmlTypesFile : std::exception
+{
+public:
+ const char *what() const noexcept override { return "Cannot parse qml types file!"; }
+};
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h
index 302a1801a2..7d27bd22f1 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h
@@ -33,15 +33,12 @@ namespace QmlDesigner {
class ProjectStorageInterface
{
public:
- virtual void synchronize(Storage::Modules modules,
- Storage::Imports imports,
- Storage::Types types,
- SourceIds sourceIds,
- FileStatuses fileStatuses)
- = 0;
+ virtual void synchronize(Storage::SynchronizationPackage package) = 0;
+
+ virtual ModuleId moduleId(Utils::SmallStringView name) = 0;
virtual FileStatus fetchFileStatus(SourceId sourceId) const = 0;
- virtual SourceIds fetchSourceDependencieIds(SourceId sourceId) const = 0;
+ virtual Storage::ProjectDatas fetchProjectDatas(SourceId sourceId) const = 0;
protected:
~ProjectStorageInterface() = default;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h
index 50db7c4853..ef05539076 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h
@@ -25,6 +25,7 @@
#pragma once
+#include "filestatus.h"
#include "projectstorageids.h"
#include <utils/smallstring.h>
@@ -35,10 +36,10 @@
namespace QmlDesigner::Storage {
-enum class TypeAccessSemantics : int { Invalid, Reference, Value, Sequence, IsEnum = 1 << 8 };
+enum class TypeAccessSemantics : int { None, Reference, Value, Sequence, IsEnum = 1 << 8 };
enum class PropertyDeclarationTraits : unsigned int {
- Non = 0,
+ None = 0,
IsReadOnly = 1 << 0,
IsPointer = 1 << 1,
IsList = 1 << 2
@@ -121,38 +122,6 @@ public:
VersionNumber minor;
};
-class Module
-{
-public:
- explicit Module() = default;
-
- explicit Module(Utils::SmallStringView name, SourceId sourceId = SourceId{})
- : name{name}
- , sourceId{sourceId}
- {}
-
- explicit Module(QStringView name, SourceId sourceId = SourceId{})
- : name{name}
- , sourceId{sourceId}
- {}
-
- explicit Module(Utils::SmallStringView name, int sourceId)
- : name{name}
- , sourceId{sourceId}
- {}
-
- friend bool operator==(const Module &first, const Module &second)
- {
- return first.name == second.name;
- }
-
-public:
- Utils::PathString name;
- SourceId sourceId;
-};
-
-using Modules = std::vector<Module>;
-
enum class IsQualified : int { No, Yes };
inline int operator-(IsQualified first, IsQualified second)
@@ -165,34 +134,36 @@ inline int operator<(IsQualified first, IsQualified second)
return static_cast<int>(first) < static_cast<int>(second);
}
+enum class ImportKind : char { Module, Directory, QmlTypesDependency };
+
class Import
{
public:
explicit Import() = default;
- explicit Import(Utils::SmallStringView name, Version version, SourceId sourceId)
- : name{name}
- , version{version}
+ explicit Import(ModuleId moduleId, Version version, SourceId sourceId)
+ : version{version}
+ , moduleId{moduleId}
, sourceId{sourceId}
{}
- explicit Import(Utils::SmallStringView name, int majorVersion, int minorVersion, int sourceId)
- : name{name}
+ explicit Import(int moduleId, int majorVersion, int minorVersion, int sourceId)
+ : moduleId{moduleId}
, version{majorVersion, minorVersion}
, sourceId{sourceId}
{}
friend bool operator==(const Import &first, const Import &second)
{
- return first.name == second.name && first.version == second.version
+ return first.moduleId == second.moduleId && first.version == second.version
&& first.sourceId == second.sourceId;
}
public:
- Utils::PathString name;
Version version;
ModuleId moduleId;
SourceId sourceId;
+ Utils::SmallString aliasName;
};
using Imports = std::vector<Import>;
@@ -269,6 +240,12 @@ public:
, version{version}
{}
+ explicit ExportedType(ModuleId moduleId, Utils::SmallStringView name, Version version = Version{})
+ : name{name}
+ , version{version}
+ , moduleId{moduleId}
+ {}
+
explicit ExportedType(Utils::SmallStringView name, Version version, TypeId typeId, ModuleId moduleId)
: name{name}
, version{version}
@@ -276,9 +253,10 @@ public:
, moduleId{moduleId}
{}
- explicit ExportedType(Utils::SmallStringView name, int majorVersion, int minorVersion)
+ explicit ExportedType(int moduleId, Utils::SmallStringView name, int majorVersion, int minorVersion)
: name{name}
, version{majorVersion, minorVersion}
+ , moduleId{moduleId}
{}
friend bool operator==(const ExportedType &first, const ExportedType &second)
@@ -299,6 +277,11 @@ class ExportedTypeView
{
public:
explicit ExportedTypeView() = default;
+ explicit ExportedTypeView(ModuleId moduleId, Utils::SmallStringView name, Storage::Version version)
+ : name{name}
+ , version{version}
+ , moduleId{moduleId}
+ {}
explicit ExportedTypeView(int moduleId,
Utils::SmallStringView name,
int majorVersion,
@@ -552,6 +535,15 @@ public:
{}
explicit PropertyDeclaration(Utils::SmallStringView name,
+ TypeId propertyTypeId,
+ PropertyDeclarationTraits traits)
+ : name{name}
+ , traits{traits}
+ , propertyTypeId{propertyTypeId}
+ , kind{PropertyKind::Property}
+ {}
+
+ explicit PropertyDeclaration(Utils::SmallStringView name,
ImportedTypeName typeName,
PropertyDeclarationTraits traits,
Utils::SmallStringView aliasPropertyName)
@@ -563,13 +555,24 @@ public:
{}
explicit PropertyDeclaration(Utils::SmallStringView name,
- Utils::SmallStringView typeName,
+ TypeId propetyTypeId,
+ PropertyDeclarationTraits traits,
+ Utils::SmallStringView aliasPropertyName)
+ : name{name}
+ , aliasPropertyName{aliasPropertyName}
+ , traits{traits}
+ , propertyTypeId{propertyTypeId}
+ , kind{PropertyKind::Property}
+ {}
+
+ explicit PropertyDeclaration(Utils::SmallStringView name,
+ long long propertyTypeId,
int traits,
Utils::SmallStringView aliasPropertyName)
: name{name}
- , typeName{NativeType{typeName}}
, aliasPropertyName{aliasPropertyName}
, traits{static_cast<PropertyDeclarationTraits>(traits)}
+ , propertyTypeId{propertyTypeId}
, kind{PropertyKind::Property}
{}
@@ -594,6 +597,7 @@ public:
ImportedTypeName typeName;
Utils::SmallString aliasPropertyName;
PropertyDeclarationTraits traits = {};
+ TypeId propertyTypeId;
TypeId typeId;
PropertyKind kind = PropertyKind::Property;
};
@@ -632,8 +636,7 @@ class Type
{
public:
explicit Type() = default;
- explicit Type(ModuleId moduleId,
- Utils::SmallStringView typeName,
+ explicit Type(Utils::SmallStringView typeName,
ImportedTypeName prototype,
TypeAccessSemantics accessSemantics,
SourceId sourceId,
@@ -650,37 +653,42 @@ public:
, functionDeclarations{std::move(functionDeclarations)}
, signalDeclarations{std::move(signalDeclarations)}
, enumerationDeclarations{std::move(enumerationDeclarations)}
- , moduleId{moduleId}
, accessSemantics{accessSemantics}
, sourceId{sourceId}
, changeLevel{changeLevel}
{}
- explicit Type(ModuleId moduleId,
- Utils::SmallStringView typeName,
+ explicit Type(Utils::SmallStringView typeName,
+ TypeId prototypeId,
+ TypeAccessSemantics accessSemantics,
+ SourceId sourceId)
+ : typeName{typeName}
+ , accessSemantics{accessSemantics}
+ , sourceId{sourceId}
+ , prototypeId{prototypeId}
+ {}
+
+ explicit Type(Utils::SmallStringView typeName,
Utils::SmallStringView prototype,
int accessSemantics,
int sourceId)
: typeName{typeName}
, prototype{NativeType{prototype}}
- , moduleId{moduleId}
, accessSemantics{static_cast<TypeAccessSemantics>(accessSemantics)}
, sourceId{sourceId}
{}
- explicit Type(int moduleId,
+ explicit Type(int sourceId,
Utils::SmallStringView typeName,
long long typeId,
- Utils::SmallStringView prototype,
- int accessSemantics,
- int sourceId)
+ long long prototypeId,
+ int accessSemantics)
: typeName{typeName}
- , prototype{NativeType{prototype}}
- , moduleId{moduleId}
, accessSemantics{static_cast<TypeAccessSemantics>(accessSemantics)}
, sourceId{sourceId}
, typeId{typeId}
+ , prototypeId{prototypeId}
{}
friend bool operator==(const Type &first, const Type &second) noexcept
@@ -690,7 +698,6 @@ public:
&& first.propertyDeclarations == second.propertyDeclarations
&& first.functionDeclarations == second.functionDeclarations
&& first.signalDeclarations == second.signalDeclarations
- && first.moduleId == second.moduleId && first.sourceId == second.sourceId
&& first.sourceId == second.sourceId;
}
@@ -702,31 +709,52 @@ public:
FunctionDeclarations functionDeclarations;
SignalDeclarations signalDeclarations;
EnumerationDeclarations enumerationDeclarations;
- TypeAccessSemantics accessSemantics = TypeAccessSemantics::Invalid;
+ TypeAccessSemantics accessSemantics = TypeAccessSemantics::None;
SourceId sourceId;
TypeId typeId;
- ModuleId moduleId;
+ TypeId prototypeId;
ChangeLevel changeLevel = ChangeLevel::Full;
};
using Types = std::vector<Type>;
-class ModuleView
+class ProjectData
{
public:
- explicit ModuleView(Utils::SmallStringView name, int sourceId)
- : name{name}
- , sourceId{sourceId}
+ ModuleId extraModuleId;
+ SourceId sourceId;
+};
+
+using ProjectDatas = std::vector<ProjectData>;
+
+class SynchronizationPackage
+{
+public:
+ SynchronizationPackage() = default;
+ SynchronizationPackage(Imports imports, Types types, SourceIds sourceIds)
+ : imports{std::move(imports)}
+ , types{std::move(types)}
+ , sourceIds(std::move(sourceIds))
{}
- friend bool operator==(const ModuleView &first, const ModuleView &second)
- {
- return first.name == second.name && first.sourceId == second.sourceId;
- }
+ SynchronizationPackage(Types types)
+ : types{std::move(types)}
+ {}
+
+ SynchronizationPackage(SourceIds sourceIds)
+ : sourceIds(std::move(sourceIds))
+ {}
+
+ SynchronizationPackage(SourceIds sourceIds, FileStatuses fileStatuses)
+ : sourceIds(std::move(sourceIds))
+ , fileStatuses(std::move(fileStatuses))
+ {}
public:
- Utils::SmallStringView name;
- SourceId sourceId;
+ Imports imports;
+ Types types;
+ SourceIds sourceIds;
+ FileStatuses fileStatuses;
};
} // namespace QmlDesigner::Storage
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp
index b5a7945181..cc91a06fae 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp
@@ -52,40 +52,34 @@ ComponentReferences createComponentReferences(const QMultiHash<QString, QmlDirPa
void ProjectUpdater::update()
{
- Storage::Modules modules;
- Storage::Imports imports;
- Storage::Types types;
- SourceIds sourceIds;
- FileStatuses fileStatuses;
+ Storage::SynchronizationPackage package;
for (const QString &qmldirPath : m_projectManager.qtQmlDirs()) {
SourcePath qmldirSourcePath{qmldirPath};
SourceId qmlDirSourceId = m_pathCache.sourceId(qmldirSourcePath);
- switch (fileState(qmlDirSourceId, fileStatuses)) {
+ switch (fileState(qmlDirSourceId, package.fileStatuses)) {
case FileState::Changed: {
QmlDirParser parser;
parser.parse(m_fileSystem.contentAsQString(qmldirPath));
- modules.emplace_back(parser.typeNamespace(), qmlDirSourceId);
-
- sourceIds.push_back(qmlDirSourceId);
+ package.sourceIds.push_back(qmlDirSourceId);
SourceContextId directoryId = m_pathCache.sourceContextId(qmlDirSourceId);
- parseTypeInfos(parser.typeInfos(), directoryId, imports, types, sourceIds, fileStatuses);
+ Utils::PathString moduleName{parser.typeNamespace()};
+ ModuleId moduleId = m_projectStorage.moduleId(moduleName);
+
+ parseTypeInfos(parser.typeInfos(), directoryId, package);
parseQmlComponents(createComponentReferences(parser.components()),
directoryId,
- ModuleId{&qmlDirSourceId},
- imports,
- types,
- sourceIds,
- fileStatuses);
+ moduleId,
+ package);
break;
}
case FileState::NotChanged: {
- SourceIds qmltypesSourceIds = m_projectStorage.fetchSourceDependencieIds(qmlDirSourceId);
- parseTypeInfos(qmltypesSourceIds, imports, types, sourceIds, fileStatuses);
+ auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(qmlDirSourceId);
+ parseTypeInfos(qmlProjectDatas, package);
break;
}
case FileState::NotExists: {
@@ -95,21 +89,14 @@ void ProjectUpdater::update()
}
}
- m_projectStorage.synchronize(std::move(modules),
- std::move(imports),
- std::move(types),
- std::move(sourceIds),
- std::move(fileStatuses));
+ m_projectStorage.synchronize(std::move(package));
}
void ProjectUpdater::pathsWithIdsChanged(const std::vector<IdPaths> &idPaths) {}
void ProjectUpdater::parseTypeInfos(const QStringList &typeInfos,
SourceContextId directoryId,
- Storage::Imports &imports,
- Storage::Types &types,
- SourceIds &sourceIds,
- FileStatuses &fileStatuses)
+ Storage::SynchronizationPackage &package)
{
QString directory{m_pathCache.sourceContextPath(directoryId)};
@@ -117,44 +104,37 @@ void ProjectUpdater::parseTypeInfos(const QStringList &typeInfos,
SourceId sourceId = m_pathCache.sourceId(directoryId, Utils::SmallString{typeInfo});
QString qmltypesPath = directory + "/" + typeInfo;
- parseTypeInfo(sourceId, qmltypesPath, imports, types, sourceIds, fileStatuses);
+ Storage::ProjectData projectData{ModuleId{}, sourceId};
+
+ parseTypeInfo(projectData, qmltypesPath, package);
}
}
-void ProjectUpdater::parseTypeInfos(const SourceIds &qmltypesSourceIds,
- Storage::Imports &imports,
- Storage::Types &types,
- SourceIds &sourceIds,
- FileStatuses &fileStatuses)
+void ProjectUpdater::parseTypeInfos(const Storage::ProjectDatas &projectDatas,
+ Storage::SynchronizationPackage &package)
{
- for (SourceId sourceId : qmltypesSourceIds) {
- QString qmltypesPath = m_pathCache.sourcePath(sourceId).toQString();
+ for (const Storage::ProjectData &projectData : projectDatas) {
+ QString qmltypesPath = m_pathCache.sourcePath(projectData.sourceId).toQString();
- parseTypeInfo(sourceId, qmltypesPath, imports, types, sourceIds, fileStatuses);
+ parseTypeInfo(projectData, qmltypesPath, package);
}
}
-void ProjectUpdater::parseTypeInfo(SourceId sourceId,
+void ProjectUpdater::parseTypeInfo(const Storage::ProjectData &projectData,
const QString &qmltypesPath,
- Storage::Imports &imports,
- Storage::Types &types,
- SourceIds &sourceIds,
- FileStatuses &fileStatuses)
+ Storage::SynchronizationPackage &package)
{
- if (fileState(sourceId, fileStatuses) == FileState::Changed) {
- sourceIds.push_back(sourceId);
+ if (fileState(projectData.sourceId, package.fileStatuses) == FileState::Changed) {
+ package.sourceIds.push_back(projectData.sourceId);
const auto content = m_fileSystem.contentAsQString(qmltypesPath);
- m_qmlTypesParser.parse(content, imports, types, sourceIds);
+ m_qmlTypesParser.parse(content, package.imports, package.types, projectData);
}
}
void ProjectUpdater::parseQmlComponents(ComponentReferences components,
SourceContextId directoryId,
ModuleId moduleId,
- Storage::Imports &imports,
- Storage::Types &types,
- SourceIds &sourceIds,
- FileStatuses &fileStatuses)
+ Storage::SynchronizationPackage &package)
{
std::sort(components.begin(), components.end(), [](auto &&first, auto &&second) {
return std::tie(first.get().typeName, first.get().majorVersion, first.get().minorVersion)
@@ -174,23 +154,23 @@ void ProjectUpdater::parseQmlComponents(ComponentReferences components,
Utils::SmallString fileName{component.fileName};
SourceId sourceId = m_pathCache.sourceId(directoryId, fileName);
- if (fileState(sourceId, fileStatuses) != FileState::Changed)
+ if (fileState(sourceId, package.fileStatuses) != FileState::Changed)
continue;
- sourceIds.push_back(sourceId);
+ package.sourceIds.push_back(sourceId);
const auto content = m_fileSystem.contentAsQString(directory + "/" + component.fileName);
- auto type = m_qmlDocumentParser.parse(content, imports);
+ auto type = m_qmlDocumentParser.parse(content, package.imports);
type.typeName = fileName;
- type.moduleId = moduleId;
type.accessSemantics = Storage::TypeAccessSemantics::Reference;
type.sourceId = sourceId;
type.exportedTypes.push_back(
- Storage::ExportedType{Utils::SmallString{component.typeName},
+ Storage::ExportedType{moduleId,
+ Utils::SmallString{component.typeName},
Storage::Version{component.majorVersion, component.minorVersion}});
- types.push_back(std::move(type));
+ package.types.push_back(std::move(type));
}
}
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h
index dfe8ce05c4..e3323d7609 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h
@@ -88,28 +88,16 @@ private:
void parseTypeInfos(const QStringList &typeInfos,
SourceContextId directoryId,
- Storage::Imports &imports,
- Storage::Types &types,
- SourceIds &sourceIds,
- FileStatuses &fileStatuses);
- void parseTypeInfos(const SourceIds &qmltypesSourceIds,
- Storage::Imports &imports,
- Storage::Types &types,
- SourceIds &sourceIds,
- FileStatuses &fileStatuses);
- void parseTypeInfo(SourceId sourceId,
+ Storage::SynchronizationPackage &package);
+ void parseTypeInfos(const Storage::ProjectDatas &projectDatas,
+ Storage::SynchronizationPackage &package);
+ void parseTypeInfo(const Storage::ProjectData &projectData,
const QString &qmltypesPath,
- Storage::Imports &imports,
- Storage::Types &types,
- SourceIds &sourceIds,
- FileStatuses &fileStatuses);
+ Storage::SynchronizationPackage &package);
void parseQmlComponents(ComponentReferences components,
SourceContextId directoryId,
ModuleId moduleId,
- Storage::Imports &imports,
- Storage::Types &types,
- SourceIds &sourceIds,
- FileStatuses &fileStatuses);
+ Storage::SynchronizationPackage &package);
FileState fileState(SourceId sourceId, FileStatuses &fileStatuses) const;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp
new file mode 100644
index 0000000000..c4a2bae617
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "qmldocumentparser.h"
+
+#include "projectstorage.h"
+#include "sourcepathcache.h"
+
+#include <sqlitedatabase.h>
+
+#include <qmldom/qqmldomtop_p.h>
+
+#include <QDateTime>
+
+namespace QmlDesigner {
+
+namespace QmlDom = QQmlJS::Dom;
+
+namespace {
+
+int convertVersionNumber(qint32 versionNumber)
+{
+ return versionNumber < 0 ? -1 : versionNumber;
+}
+
+Storage::Version convertVersion(QmlDom::Version version)
+{
+ return Storage::Version{convertVersionNumber(version.majorVersion),
+ convertVersionNumber(version.minorVersion)};
+}
+
+Utils::PathString convertUri(const QString &uri)
+{
+ Utils::PathString path{QStringView{uri.begin() + 7, uri.end()}};
+ if (path.endsWith("/."))
+ return path;
+ if (path.endsWith("/")) {
+ path += ".";
+ return path;
+ }
+
+ path += "/.";
+ return path;
+}
+
+void addImports(Storage::Imports &imports,
+ const QList<QmlDom::Import> &qmlImports,
+ SourceId sourceId,
+ SourceContextId sourceContextId,
+ QmlDocumentParser::PathCache &pathCache,
+ QmlDocumentParser::ProjectStorage &storage)
+{
+ for (const QmlDom::Import &qmlImport : qmlImports) {
+ if (qmlImport.uri == u"file://.") {
+ auto moduleId = storage.moduleId(pathCache.sourceContextPath(sourceContextId));
+ imports.emplace_back(moduleId, Storage::Version{}, sourceId);
+ } else if (qmlImport.uri.startsWith(u"file://")) {
+ auto moduleId = storage.moduleId(convertUri(qmlImport.uri));
+ imports.emplace_back(moduleId, Storage::Version{}, sourceId);
+ } else {
+ auto moduleId = storage.moduleId(Utils::SmallString{qmlImport.uri});
+ imports.emplace_back(moduleId, convertVersion(qmlImport.version), sourceId);
+ }
+ }
+}
+
+void addPropertyDeclarations(Storage::Type &type, const QmlDom::QmlObject &rootObject)
+{
+ for (const QmlDom::PropertyDefinition &propertyDeclaration : rootObject.propertyDefs()) {
+ type.propertyDeclarations.emplace_back(Utils::SmallString{propertyDeclaration.name},
+ Storage::ImportedType{
+ Utils::SmallString{propertyDeclaration.typeName}},
+ Storage::PropertyDeclarationTraits::None);
+ }
+}
+
+void addParameterDeclaration(Storage::ParameterDeclarations &parameterDeclarations,
+ const QList<QmlDom::MethodParameter> &parameters)
+{
+ for (const QmlDom::MethodParameter &parameter : parameters) {
+ parameterDeclarations.emplace_back(Utils::SmallString{parameter.name},
+ Utils::SmallString{parameter.typeName});
+ }
+}
+
+void addFunctionAndSignalDeclarations(Storage::Type &type, const QmlDom::QmlObject &rootObject)
+{
+ for (const QmlDom::MethodInfo &methodInfo : rootObject.methods()) {
+ if (methodInfo.methodType == QmlDom::MethodInfo::Method) {
+ auto &functionDeclaration = type.functionDeclarations.emplace_back(
+ Utils::SmallString{methodInfo.name}, "", Storage::ParameterDeclarations{});
+ addParameterDeclaration(functionDeclaration.parameters, methodInfo.parameters);
+ } else {
+ auto &signalDeclaration = type.signalDeclarations.emplace_back(
+ Utils::SmallString{methodInfo.name});
+ addParameterDeclaration(signalDeclaration.parameters, methodInfo.parameters);
+ }
+ }
+}
+
+Storage::EnumeratorDeclarations createEnumerators(const QmlDom::EnumDecl &enumeration)
+{
+ Storage::EnumeratorDeclarations enumeratorDeclarations;
+ for (const QmlDom::EnumItem &enumerator : enumeration.values()) {
+ enumeratorDeclarations.emplace_back(Utils::SmallString{enumerator.name()},
+ static_cast<long long>(enumerator.value()));
+ }
+ return enumeratorDeclarations;
+}
+
+void addEnumeraton(Storage::Type &type, const QmlDom::Component &component)
+{
+ for (const QmlDom::EnumDecl &enumeration : component.enumerations()) {
+ Storage::EnumeratorDeclarations enumeratorDeclarations = createEnumerators(enumeration);
+ type.enumerationDeclarations.emplace_back(Utils::SmallString{enumeration.name()},
+ std::move(enumeratorDeclarations));
+ }
+}
+
+} // namespace
+
+Storage::Type QmlDocumentParser::parse(const QString &sourceContent,
+ Storage::Imports &imports,
+ SourceId sourceId,
+ SourceContextId sourceContextId)
+{
+ Storage::Type type;
+
+ QmlDom::DomItem environment = QmlDom::DomEnvironment::create(
+ {},
+ QmlDom::DomEnvironment::Option::SingleThreaded
+ | QmlDom::DomEnvironment::Option::NoDependencies);
+
+ QmlDom::DomItem items;
+
+ environment.loadFile(
+ {},
+ {},
+ sourceContent,
+ QDateTime{},
+ [&](QmlDom::Path, const QmlDom::DomItem &, const QmlDom::DomItem &newItems) {
+ items = newItems;
+ },
+ QmlDom::LoadOption::DefaultLoad,
+ QmlDom::DomType::QmlFile);
+
+ environment.loadPendingDependencies();
+
+ QmlDom::DomItem file = items.field(QmlDom::Fields::currentItem);
+ const QmlDom::QmlFile *qmlFile = file.as<QmlDom::QmlFile>();
+ const auto &components = qmlFile->components();
+
+ if (components.empty())
+ return type;
+
+ const auto &component = components.first();
+ const auto &objects = component.objects();
+
+ if (objects.empty())
+ return type;
+
+ const QmlDom::QmlObject &qmlObject = objects.front();
+
+ type.prototype = Storage::ImportedType{Utils::SmallString{qmlObject.name()}};
+
+ addImports(imports, qmlFile->imports(), sourceId, sourceContextId, m_pathCache, m_storage);
+
+ addPropertyDeclarations(type, qmlObject);
+ addFunctionAndSignalDeclarations(type, qmlObject);
+ addEnumeraton(type, component);
+
+ return type;
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h
new file mode 100644
index 0000000000..e2dd243405
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "nonlockingmutex.h"
+#include "qmldocumentparserinterface.h"
+
+namespace Sqlite {
+class Database;
+}
+
+namespace QmlDesigner {
+
+template<typename Database>
+class ProjectStorage;
+
+template<typename ProjectStorage, typename Mutex>
+class SourcePathCache;
+
+class QmlDocumentParser
+{
+public:
+ using ProjectStorage = QmlDesigner::ProjectStorage<Sqlite::Database>;
+ using PathCache = QmlDesigner::SourcePathCache<ProjectStorage, NonLockingMutex>;
+
+ QmlDocumentParser(PathCache &pathCache, ProjectStorage &storage)
+ : m_pathCache{pathCache}
+ , m_storage{storage}
+ {}
+
+ virtual Storage::Type parse(const QString &sourceContent,
+ Storage::Imports &imports,
+ SourceId sourceId,
+ SourceContextId sourceContextId);
+
+private:
+ PathCache &m_pathCache;
+ ProjectStorage &m_storage;
+};
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp
new file mode 100644
index 0000000000..f5d09409aa
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp
@@ -0,0 +1,307 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "qmltypesparser.h"
+
+#include "projectstorage.h"
+#include "sourcepathcache.h"
+
+#include <sqlitedatabase.h>
+
+#include <qmlcompiler/qqmljstypedescriptionreader_p.h>
+#include <qmldom/qqmldomtop_p.h>
+
+#include <QDateTime>
+
+#include <algorithm>
+#include <tuple>
+
+namespace QmlDesigner {
+
+namespace QmlDom = QQmlJS::Dom;
+
+namespace {
+
+void appendImports(Storage::Imports &imports,
+ const QString &dependency,
+ SourceId sourceId,
+ QmlTypesParser::ProjectStorage &storage)
+{
+ auto spaceFound = std::find_if(dependency.begin(), dependency.end(), [](QChar c) {
+ return c.isSpace();
+ });
+
+ Utils::PathString moduleName{QStringView(dependency.begin(), spaceFound)};
+ moduleName.append("-cppnative");
+ ModuleId cppModuleId = storage.moduleId(moduleName);
+
+ auto majorVersionFound = std::find_if(spaceFound, dependency.end(), [](QChar c) {
+ return c.isDigit();
+ });
+ auto majorVersionEnd = std::find_if(majorVersionFound, dependency.end(), [](QChar c) {
+ return !c.isDigit();
+ });
+
+ Storage::Version version;
+
+ QStringView majorVersionString(majorVersionFound, majorVersionEnd);
+ if (!majorVersionString.isEmpty()) {
+ version.major.value = majorVersionString.toInt();
+
+ auto minorVersionFound = std::find_if(majorVersionEnd, dependency.end(), [](QChar c) {
+ return c.isDigit();
+ });
+ auto minorVersionEnd = std::find_if(minorVersionFound, dependency.end(), [](QChar c) {
+ return !c.isDigit();
+ });
+ QStringView minorVersionString(minorVersionFound, minorVersionEnd);
+ if (!minorVersionString.isEmpty())
+ version.minor.value = QStringView(minorVersionFound, minorVersionEnd).toInt();
+ }
+
+ imports.emplace_back(cppModuleId, version, sourceId);
+}
+
+void addImports(Storage::Imports &imports,
+ SourceId sourceId,
+ const QStringList &dependencies,
+ QmlTypesParser::ProjectStorage &storage)
+{
+ for (const QString &dependency : dependencies)
+ appendImports(imports, dependency, sourceId, storage);
+
+ imports.emplace_back(storage.moduleId("QML"), Storage::Version{}, sourceId);
+ imports.emplace_back(storage.moduleId("QtQml-cppnative"), Storage::Version{}, sourceId);
+}
+
+Storage::TypeAccessSemantics createTypeAccessSemantics(QQmlJSScope::AccessSemantics accessSematics)
+{
+ switch (accessSematics) {
+ case QQmlJSScope::AccessSemantics::Reference:
+ return Storage::TypeAccessSemantics::Reference;
+ case QQmlJSScope::AccessSemantics::Value:
+ return Storage::TypeAccessSemantics::Value;
+ case QQmlJSScope::AccessSemantics::None:
+ return Storage::TypeAccessSemantics::None;
+ case QQmlJSScope::AccessSemantics::Sequence:
+ return Storage::TypeAccessSemantics::Sequence;
+ }
+
+ return Storage::TypeAccessSemantics::None;
+}
+
+Storage::Version createVersion(QTypeRevision qmlVersion)
+{
+ return Storage::Version{qmlVersion.majorVersion(), qmlVersion.minorVersion()};
+}
+
+Storage::ExportedTypes createExports(const QList<QQmlJSScope::Export> &qmlExports,
+ const QQmlJSScope &component,
+ QmlTypesParser::ProjectStorage &storage,
+ ModuleId cppModuleId)
+{
+ Storage::ExportedTypes exportedTypes;
+ exportedTypes.reserve(Utils::usize(qmlExports));
+
+ for (const QQmlJSScope::Export &qmlExport : qmlExports) {
+ exportedTypes.emplace_back(storage.moduleId(Utils::SmallString{qmlExport.package()}),
+ Utils::SmallString{qmlExport.type()},
+ createVersion(qmlExport.version()));
+ }
+
+ exportedTypes.emplace_back(cppModuleId, Utils::SmallString{component.internalName()});
+
+ return exportedTypes;
+}
+
+Storage::PropertyDeclarationTraits createPropertyDeclarationTraits(const QQmlJSMetaProperty &qmlProperty)
+{
+ Storage::PropertyDeclarationTraits traits{};
+
+ if (qmlProperty.isList())
+ traits = traits | Storage::PropertyDeclarationTraits::IsList;
+
+ if (qmlProperty.isPointer())
+ traits = traits | Storage::PropertyDeclarationTraits::IsPointer;
+
+ if (!qmlProperty.isWritable())
+ traits = traits | Storage::PropertyDeclarationTraits::IsReadOnly;
+
+ return traits;
+}
+
+Storage::PropertyDeclarations createProperties(const QHash<QString, QQmlJSMetaProperty> &qmlProperties)
+{
+ Storage::PropertyDeclarations propertyDeclarations;
+ propertyDeclarations.reserve(Utils::usize(qmlProperties));
+
+ for (const QQmlJSMetaProperty &qmlProperty : qmlProperties) {
+ propertyDeclarations.emplace_back(Utils::SmallString{qmlProperty.propertyName()},
+ Storage::NativeType{
+ Utils::SmallString{qmlProperty.typeName()}},
+ createPropertyDeclarationTraits(qmlProperty));
+ }
+
+ return propertyDeclarations;
+}
+
+Storage::ParameterDeclarations createParameters(const QQmlJSMetaMethod &qmlMethod)
+{
+ Storage::ParameterDeclarations parameterDeclarations;
+
+ const QStringList &parameterNames = qmlMethod.parameterNames();
+ const QStringList &parameterTypeNames = qmlMethod.parameterTypeNames();
+ auto currentName = parameterNames.begin();
+ auto currentType = parameterTypeNames.begin();
+ auto nameEnd = parameterNames.end();
+ auto typeEnd = parameterTypeNames.end();
+
+ for (; currentName != nameEnd && currentType != typeEnd; ++currentName, ++currentType) {
+ parameterDeclarations.emplace_back(Utils::SmallString{*currentName},
+ Utils::SmallString{*currentType});
+ }
+
+ return parameterDeclarations;
+}
+
+std::tuple<Storage::FunctionDeclarations, Storage::SignalDeclarations> createFunctionAndSignals(
+ const QMultiHash<QString, QQmlJSMetaMethod> &qmlMethods)
+{
+ std::tuple<Storage::FunctionDeclarations, Storage::SignalDeclarations> functionAndSignalDeclarations;
+ Storage::FunctionDeclarations &functionsDeclarations{std::get<0>(functionAndSignalDeclarations)};
+ functionsDeclarations.reserve(Utils::usize(qmlMethods));
+ Storage::SignalDeclarations &signalDeclarations{std::get<1>(functionAndSignalDeclarations)};
+ signalDeclarations.reserve(Utils::usize(qmlMethods));
+
+ for (const QQmlJSMetaMethod &qmlMethod : qmlMethods) {
+ if (qmlMethod.methodType() != QQmlJSMetaMethod::Type::Signal) {
+ functionsDeclarations.emplace_back(Utils::SmallString{qmlMethod.methodName()},
+ Utils::SmallString{qmlMethod.returnTypeName()},
+ createParameters(qmlMethod));
+ } else {
+ signalDeclarations.emplace_back(Utils::SmallString{qmlMethod.methodName()},
+ createParameters(qmlMethod));
+ }
+ }
+
+ return functionAndSignalDeclarations;
+}
+
+Storage::EnumeratorDeclarations createEnumeratorsWithValues(const QQmlJSMetaEnum &qmlEnumeration)
+{
+ Storage::EnumeratorDeclarations enumeratorDeclarations;
+
+ const QStringList &keys = qmlEnumeration.keys();
+ const QList<int> &values = qmlEnumeration.values();
+ auto currentKey = keys.begin();
+ auto currentValue = values.begin();
+ auto keyEnd = keys.end();
+ auto valueEnd = values.end();
+
+ for (; currentKey != keyEnd && currentValue != valueEnd; ++currentKey, ++currentValue)
+ enumeratorDeclarations.emplace_back(Utils::SmallString{*currentKey}, *currentValue);
+
+ return enumeratorDeclarations;
+}
+
+Storage::EnumeratorDeclarations createEnumeratorsWithoutValues(const QQmlJSMetaEnum &qmlEnumeration)
+{
+ Storage::EnumeratorDeclarations enumeratorDeclarations;
+
+ for (const QString &key : qmlEnumeration.keys())
+ enumeratorDeclarations.emplace_back(Utils::SmallString{key});
+
+ return enumeratorDeclarations;
+}
+
+Storage::EnumeratorDeclarations createEnumerators(const QQmlJSMetaEnum &qmlEnumeration)
+{
+ if (qmlEnumeration.hasValues())
+ return createEnumeratorsWithValues(qmlEnumeration);
+
+ return createEnumeratorsWithoutValues(qmlEnumeration);
+}
+
+Storage::EnumerationDeclarations createEnumeration(const QHash<QString, QQmlJSMetaEnum> &qmlEnumerations)
+{
+ Storage::EnumerationDeclarations enumerationDeclarations;
+ enumerationDeclarations.reserve(Utils::usize(qmlEnumerations));
+
+ for (const QQmlJSMetaEnum &qmlEnumeration : qmlEnumerations) {
+ enumerationDeclarations.emplace_back(Utils::SmallString{qmlEnumeration.name()},
+ createEnumerators(qmlEnumeration));
+ }
+
+ return enumerationDeclarations;
+}
+
+void addType(Storage::Types &types,
+ SourceId sourceId,
+ ModuleId cppModuleId,
+ const QQmlJSScope &component,
+ QmlTypesParser::ProjectStorage &storage)
+{
+ auto [functionsDeclarations, signalDeclarations] = createFunctionAndSignals(component.ownMethods());
+ types.emplace_back(Utils::SmallString{component.internalName()},
+ Storage::NativeType{Utils::SmallString{component.baseTypeName()}},
+ createTypeAccessSemantics(component.accessSemantics()),
+ sourceId,
+ createExports(component.exports(), component, storage, cppModuleId),
+ createProperties(component.ownProperties()),
+ std::move(functionsDeclarations),
+ std::move(signalDeclarations),
+ createEnumeration(component.ownEnumerations()));
+}
+
+void addTypes(Storage::Types &types,
+ const Storage::ProjectData &projectData,
+ const QHash<QString, QQmlJSScope::Ptr> &objects,
+ QmlTypesParser::ProjectStorage &storage)
+{
+ types.reserve(Utils::usize(objects) + types.size());
+
+ for (const auto &object : objects)
+ addType(types, projectData.sourceId, projectData.extraModuleId, *object.get(), storage);
+}
+
+} // namespace
+
+void QmlTypesParser::parse(const QString &sourceContent,
+ Storage::Imports &imports,
+ Storage::Types &types,
+ const Storage::ProjectData &projectData)
+{
+ QQmlJSTypeDescriptionReader reader({}, sourceContent);
+ QHash<QString, QQmlJSScope::Ptr> components;
+ QStringList dependencies;
+ bool isValid = reader(&components, &dependencies);
+ if (!isValid)
+ throw CannotParseQmlTypesFile{};
+
+ addImports(imports, projectData.sourceId, dependencies, m_storage);
+ addTypes(types, projectData, components, m_storage);
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h
new file mode 100644
index 0000000000..40f88d240d
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "nonlockingmutex.h"
+#include "qmltypesparserinterface.h"
+
+namespace Sqlite {
+class Database;
+}
+
+namespace QmlDesigner {
+
+template<typename Database>
+class ProjectStorage;
+
+template<typename ProjectStorage, typename Mutex>
+class SourcePathCache;
+
+class QmlTypesParser : public QmlTypesParserInterface
+{
+public:
+ using ProjectStorage = QmlDesigner::ProjectStorage<Sqlite::Database>;
+ using PathCache = QmlDesigner::SourcePathCache<ProjectStorage, NonLockingMutex>;
+
+ QmlTypesParser(PathCache &pathCache, ProjectStorage &storage)
+ : m_pathCache{pathCache}
+ , m_storage{storage}
+ {}
+
+ void parse(const QString &sourceContent,
+ Storage::Imports &imports,
+ Storage::Types &types,
+ const Storage::ProjectData &projectData) override;
+
+private:
+ PathCache &m_pathCache;
+ ProjectStorage &m_storage;
+};
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h
index 40c9883835..3255c0b5aa 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h
@@ -37,7 +37,7 @@ public:
virtual void parse(const QString &sourceContent,
Storage::Imports &imports,
Storage::Types &types,
- SourceIds &sourceIds)
+ const Storage::ProjectData &projectData)
= 0;
protected:
diff --git a/src/plugins/qmldesigner/generatecmakelists.cpp b/src/plugins/qmldesigner/generatecmakelists.cpp
new file mode 100644
index 0000000000..8e96c0ec95
--- /dev/null
+++ b/src/plugins/qmldesigner/generatecmakelists.cpp
@@ -0,0 +1,272 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Design Tooling
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "generatecmakelists.h"
+
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/actionmanager/actioncontainer.h>
+
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/session.h>
+
+#include <qmlprojectmanager/qmlprojectmanagerconstants.h>
+
+#include <utils/fileutils.h>
+
+#include <QAction>
+#include <QRegularExpression>
+#include <QStringList>
+#include <QTextStream>
+
+using namespace Utils;
+
+namespace QmlDesigner {
+namespace GenerateCmakeLists {
+
+const QDir::Filters FILES_ONLY = QDir::Files;
+const QDir::Filters DIRS_ONLY = QDir::Dirs|QDir::Readable|QDir::NoDotAndDotDot;
+
+const char CMAKEFILENAME[] = "CMakeLists.txt";
+const char QMLDIRFILENAME[] = "qmldir";
+
+void generateMenuEntry()
+{
+ Core::ActionContainer *buildMenu =
+ Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT);
+ const Core::Context projectCntext(QmlProjectManager::Constants::QML_PROJECT_ID);
+ auto action = new QAction("Generate CMakeLists.txt files");
+ QObject::connect(action, &QAction::triggered, GenerateCmakeLists::onGenerateCmakeLists);
+ Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.CreateCMakeLists");
+ buildMenu->addAction(cmd, ProjectExplorer::Constants::G_BUILD_RUN);
+
+ action->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr);
+ QObject::connect(ProjectExplorer::SessionManager::instance(),
+ &ProjectExplorer::SessionManager::startupProjectChanged, [action]() {
+ action->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr);
+ });
+}
+
+void onGenerateCmakeLists()
+{
+ generateMainCmake(ProjectExplorer::SessionManager::startupProject()->projectDirectory());
+}
+
+QStringList processDirectory(const FilePath &dir)
+{
+ QStringList moduleNames;
+
+ FilePaths files = dir.dirEntries(FILES_ONLY);
+ for (FilePath &file : files) {
+ if (!file.fileName().compare(CMAKEFILENAME))
+ files.removeAll(file);
+ }
+
+ if (files.isEmpty()) {
+ generateSubdirCmake(dir);
+ FilePaths subDirs = dir.dirEntries(DIRS_ONLY);
+ for (FilePath &subDir : subDirs) {
+ QStringList subDirModules = processDirectory(subDir);
+ moduleNames.append(subDirModules);
+ }
+ }
+ else {
+ QString moduleName = generateModuleCmake(dir);
+ if (!moduleName.isEmpty()) {
+ moduleNames.append(moduleName);
+ }
+ }
+
+ return moduleNames;
+}
+
+const char MAINFILE_REQUIRED_VERSION[] = "cmake_minimum_required(VERSION 3.18)\n\n";
+const char MAINFILE_PROJECT[] = "project(%1 LANGUAGES CXX)\n\n";
+const char MAINFILE_CMAKE_OPTIONS[] = "set(CMAKE_INCLUDE_CURRENT_DIR ON)\nset(CMAKE_AUTOMOC ON)\n\n";
+const char MAINFILE_PACKAGES[] = "find_package(Qt6 COMPONENTS Gui Qml Quick)\n";
+const char MAINFILE_LIBRARIES[] = "set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)\n\n";
+const char MAINFILE_CPP[] = "add_executable(%1 main.cpp)\n\n";
+const char MAINFILE_MAINMODULE[] = "qt6_add_qml_module(%1\n\tURI \"Main\"\n\tVERSION 1.0\n\tNO_PLUGIN\n\tQML_FILES main.qml\n)\n\n";
+const char MAINFILE_LINK_LIBRARIES[] = "target_link_libraries(%1 PRIVATE\n\tQt${QT_VERSION_MAJOR}::Core\n\tQt${QT_VERSION_MAJOR}::Gui\n\tQt${QT_VERSION_MAJOR}::Quick\n\tQt${QT_VERSION_MAJOR}::Qml\n)\n\n";
+
+const char ADD_SUBDIR[] = "add_subdirectory(%1)\n";
+
+void generateMainCmake(const FilePath &rootDir)
+{
+ //TODO startupProject() may be a terrible way to try to get "current project". It's not necessarily the same thing at all.
+ QString projectName = ProjectExplorer::SessionManager::startupProject()->displayName();
+
+ FilePaths subDirs = rootDir.dirEntries(DIRS_ONLY);
+
+ QString fileContent;
+ fileContent.append(MAINFILE_REQUIRED_VERSION);
+ fileContent.append(QString(MAINFILE_PROJECT).arg(projectName));
+ fileContent.append(MAINFILE_CMAKE_OPTIONS);
+ fileContent.append(MAINFILE_PACKAGES);
+ fileContent.append(QString(MAINFILE_CPP).arg(projectName));
+ fileContent.append(QString(MAINFILE_MAINMODULE).arg(projectName));
+ fileContent.append(MAINFILE_LIBRARIES);
+
+ for (FilePath &subDir : subDirs) {
+ QStringList subDirModules = processDirectory(subDir);
+ if (!subDirModules.isEmpty())
+ fileContent.append(QString(ADD_SUBDIR).arg(subDir.fileName()));
+ }
+ fileContent.append("\n");
+
+ fileContent.append(QString(MAINFILE_LINK_LIBRARIES).arg(projectName));
+
+ createCmakeFile(rootDir, fileContent);
+}
+
+const char MODULEFILE_PROPERTY_SINGLETON[] = "QT_QML_SINGLETON_TYPE";
+const char MODULEFILE_PROPERTY_SET[] = "set_source_files_properties(%1\n\tPROPERTIES\n\t\t%2 %3\n)\n\n";
+const char MODULEFILE_CREATE_MODULE[] = "qt6_add_qml_module(%1\n\tURI \"%1\"\n\tVERSION 1.0\n%2)\n\n";
+
+
+QString generateModuleCmake(const FilePath &dir)
+{
+ QString fileContent;
+ const QStringList qmlFilesOnly("*.qml");
+ const QStringList qmldirFilesOnly(QMLDIRFILENAME);
+ ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+
+ FilePaths qmldirFileList = dir.dirEntries(qmldirFilesOnly, FILES_ONLY);
+ if (!qmldirFileList.isEmpty()) {
+ QStringList singletons = getSingletonsFromQmldirFile(qmldirFileList.first());
+ for (QString &singleton : singletons) {
+ fileContent.append(QString(MODULEFILE_PROPERTY_SET).arg(singleton).arg(MODULEFILE_PROPERTY_SINGLETON).arg("true"));
+ }
+ }
+
+ FilePaths qmlFileList = dir.dirEntries(qmlFilesOnly, FILES_ONLY);
+ QString qmlFiles;
+ for (FilePath &qmlFile : qmlFileList) {
+ if (project->isKnownFile(qmlFile))
+ qmlFiles.append(QString("\t\t%1\n").arg(qmlFile.fileName()));
+ }
+
+ QStringList resourceFileList = getDirectoryTreeResources(dir);
+ QString resourceFiles;
+ for (QString &resourceFile : resourceFileList) {
+ resourceFiles.append(QString("\t\t%1\n").arg(resourceFile));
+ }
+
+ QString moduleContent;
+ if (!qmlFiles.isEmpty()) {
+ moduleContent.append(QString("\tQML_FILES\n%1").arg(qmlFiles));
+ }
+ if (!resourceFiles.isEmpty()) {
+ moduleContent.append(QString("\tRESOURCES\n%1").arg(resourceFiles));
+ }
+
+ QString moduleName = dir.fileName();
+
+ fileContent.append(QString(MODULEFILE_CREATE_MODULE).arg(moduleName).arg(moduleContent));
+
+ createCmakeFile(dir, fileContent);
+
+ return moduleName;
+}
+
+void generateSubdirCmake(const FilePath &dir)
+{
+ QString fileContent;
+ FilePaths subDirs = dir.dirEntries(DIRS_ONLY);
+
+ for (FilePath &subDir : subDirs) {
+ fileContent.append(QString(ADD_SUBDIR).arg(subDir.fileName()));
+ }
+
+ createCmakeFile(dir, fileContent);
+}
+
+QStringList getSingletonsFromQmldirFile(const FilePath &filePath)
+{
+ QStringList singletons;
+ QFile f(filePath.toString());
+ f.open(QIODevice::ReadOnly);
+ QTextStream stream(&f);
+
+ while (!stream.atEnd()) {
+ QString line = stream.readLine();
+ if (line.startsWith("singleton", Qt::CaseInsensitive)) {
+ QStringList tokenizedLine = line.split(QRegularExpression("\\s+"));
+ QString fileName = tokenizedLine.last();
+ if (fileName.endsWith(".qml", Qt::CaseInsensitive)) {
+ singletons.append(fileName);
+ }
+ }
+ }
+
+ f.close();
+
+ return singletons;
+}
+
+QStringList getDirectoryTreeResources(const FilePath &dir)
+{
+ ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ QStringList resourceFileList;
+
+ FilePaths thisDirFiles = dir.dirEntries(FILES_ONLY);
+ for (FilePath &file : thisDirFiles) {
+ if (!isFileBlacklisted(file.fileName()) &&
+ !file.fileName().endsWith(".qml", Qt::CaseInsensitive) &&
+ project->isKnownFile(file)) {
+ resourceFileList.append(file.fileName());
+ }
+ }
+
+ FilePaths subDirsList = dir.dirEntries(DIRS_ONLY);
+ for (FilePath &subDir : subDirsList) {
+ QStringList subDirResources = getDirectoryTreeResources(subDir);
+ for (QString &resource : subDirResources) {
+ resourceFileList.append(subDir.fileName().append('/').append(resource));
+ }
+
+ }
+
+ return resourceFileList;
+}
+
+void createCmakeFile(const FilePath &dir, const QString &content)
+{
+ FilePath filePath = dir.pathAppended(CMAKEFILENAME);
+ QFile cmakeFile(filePath.toString());
+ cmakeFile.open(QIODevice::WriteOnly);
+ QTextStream stream(&cmakeFile);
+ stream << content;
+ cmakeFile.close();
+}
+
+bool isFileBlacklisted(const QString &fileName)
+{
+ return (!fileName.compare(QMLDIRFILENAME) ||
+ !fileName.compare(CMAKEFILENAME));
+}
+
+}
+}
diff --git a/src/plugins/qmldesigner/generatecmakelists.h b/src/plugins/qmldesigner/generatecmakelists.h
new file mode 100644
index 0000000000..55b0f6958d
--- /dev/null
+++ b/src/plugins/qmldesigner/generatecmakelists.h
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Design Tooling
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <projectexplorer/project.h>
+
+#include <utils/fileutils.h>
+
+namespace QmlDesigner {
+namespace GenerateCmakeLists {
+void generateMenuEntry();
+void onGenerateCmakeLists();
+void generateMainCmake(const Utils::FilePath &rootDir);
+void generateSubdirCmake(const Utils::FilePath &dir);
+QString generateModuleCmake(const Utils::FilePath &dir);
+QStringList processDirectory(const Utils::FilePath &dir);
+QStringList getSingletonsFromQmldirFile(const Utils::FilePath &filePath);
+QStringList getDirectoryTreeResources(const Utils::FilePath &dir);
+void createCmakeFile(const Utils::FilePath &filePath, const QString &content);
+bool isFileBlacklisted(const QString &fileName);
+}
+}
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp
index 77c4715f17..014c79b59d 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.cpp
+++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp
@@ -31,6 +31,7 @@
#include "designmodecontext.h"
#include "openuiqmlfiledialog.h"
#include "generateresource.h"
+#include "generatecmakelists.h"
#include "nodeinstanceview.h"
#include "gestures.h"
@@ -222,6 +223,8 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e
if (DesignerSettings::getValue(DesignerSettingsKey::STANDALONE_MODE).toBool())
GenerateResource::generateMenuEntry();
+ GenerateCmakeLists::generateMenuEntry();
+
const QString fontPath
= Core::ICore::resourcePath(
"qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf")
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.pri b/src/plugins/qmldesigner/qmldesignerplugin.pri
index 58fb55788f..4cf7edf4ba 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.pri
+++ b/src/plugins/qmldesigner/qmldesignerplugin.pri
@@ -5,6 +5,7 @@ HEADERS += $$PWD/qmldesignerconstants.h \
$$PWD/designersettings.h \
$$PWD/editorproxy.h \
$$PWD/generateresource.h \
+ $$PWD/generatecmakelists.h \
$$PWD/settingspage.h \
$$PWD/designmodecontext.h \
$$PWD/documentmanager.h \
@@ -20,6 +21,7 @@ SOURCES += $$PWD/qmldesignerplugin.cpp \
$$PWD/designersettings.cpp \
$$PWD/editorproxy.cpp \
$$PWD/generateresource.cpp \
+ $$PWD/generatecmakelists.cpp \
$$PWD/settingspage.cpp \
$$PWD/designmodecontext.cpp \
$$PWD/documentmanager.cpp \
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs
index a1cf95c0e8..a3b70ec970 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.qbs
+++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs
@@ -1007,6 +1007,8 @@ Project {
files: [
"generateresource.cpp",
"generateresource.h",
+ "generatecmakelists.cpp",
+ "generatecmakelists.h",
"designersettings.cpp",
"designersettings.h",
"designmodecontext.cpp",
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/component-icon.png b/src/plugins/qmldesigner/qtquickplugin/images/component-icon.png
new file mode 100644
index 0000000000..9c7df42bc7
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/component-icon.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/component-icon16.png b/src/plugins/qmldesigner/qtquickplugin/images/component-icon16.png
new file mode 100644
index 0000000000..99941541c6
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/component-icon16.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/component-icon@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/component-icon@2x.png
new file mode 100644
index 0000000000..f66349a63b
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/component-icon@2x.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/loader-icon.png b/src/plugins/qmldesigner/qtquickplugin/images/loader-icon.png
new file mode 100644
index 0000000000..29082eacf1
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/loader-icon.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/loader-icon16.png b/src/plugins/qmldesigner/qtquickplugin/images/loader-icon16.png
new file mode 100644
index 0000000000..4a2b093259
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/loader-icon16.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/loader-icon@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/loader-icon@2x.png
new file mode 100644
index 0000000000..750b13bd02
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/loader-icon@2x.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon.png b/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon.png
new file mode 100644
index 0000000000..efe3ca80b4
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon16.png b/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon16.png
new file mode 100644
index 0000000000..775a57a38c
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon16.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon@2x.png
new file mode 100644
index 0000000000..bb541b6711
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon@2x.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc
index be1770378b..8adfb84baf 100644
--- a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc
+++ b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc
@@ -72,5 +72,14 @@
<file>images/animated-image-icon.png</file>
<file>images/animated-image-icon@2x.png</file>
<file>images/animated-image-icon16.png</file>
+ <file>images/component-icon.png</file>
+ <file>images/component-icon@2x.png</file>
+ <file>images/component-icon16.png</file>
+ <file>images/repeater-icon.png</file>
+ <file>images/repeater-icon@2x.png</file>
+ <file>images/repeater-icon16.png</file>
+ <file>images/loader-icon.png</file>
+ <file>images/loader-icon@2x.png</file>
+ <file>images/loader-icon16.png</file>
</qresource>
</RCC>
diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
index 019be09973..851d457cdc 100644
--- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
+++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
@@ -434,7 +434,7 @@ MetaInfo {
Type {
name: "QtQml.Component"
- icon: ":/qtquickplugin/images/item-icon16.png"
+ icon: ":/qtquickplugin/images/component-icon16.png"
Hints {
canBeDroppedInNavigator: true
@@ -444,19 +444,19 @@ MetaInfo {
ItemLibraryEntry {
name: "Component"
category: "e.Qt Quick - Component"
- libraryIcon: ":/qtquickplugin/images/item-icon.png"
+ libraryIcon: ":/qtquickplugin/images/component-icon.png"
version: "2.0"
}
}
Type {
name: "QtQuick.Loader"
- icon: ":/qtquickplugin/images/item-icon16.png"
+ icon: ":/qtquickplugin/images/loader-icon16.png"
ItemLibraryEntry {
name: "Loader"
category: "e.Qt Quick - Component"
- libraryIcon: ":/qtquickplugin/images/item-icon.png"
+ libraryIcon: ":/qtquickplugin/images/loader-icon.png"
version: "2.0"
Property { name: "width"; type: "int"; value: 200; }
Property { name: "height"; type: "int"; value: 200; }
@@ -465,7 +465,7 @@ MetaInfo {
Type {
name: "QtQuick.Repeater"
- icon: ":/qtquickplugin/images/item-icon16.png"
+ icon: ":/qtquickplugin/images/repeater-icon16.png"
Hints {
canBeDroppedInFormEditor: false
@@ -475,7 +475,7 @@ MetaInfo {
ItemLibraryEntry {
name: "Repeater"
category: "e.Qt Quick - Component"
- libraryIcon: ":/qtquickplugin/images/item-icon.png"
+ libraryIcon: ":/qtquickplugin/images/repeater-icon.png"
version: "2.0"
}
}
diff --git a/src/plugins/qmlpreview/qmldebugtranslationclient.cpp b/src/plugins/qmlpreview/qmldebugtranslationclient.cpp
index e383784ef5..5d48877265 100644
--- a/src/plugins/qmlpreview/qmldebugtranslationclient.cpp
+++ b/src/plugins/qmlpreview/qmldebugtranslationclient.cpp
@@ -52,14 +52,6 @@ void QmlDebugTranslationClient::changeLanguage(const QUrl &url, const QString &l
}
-void QmlDebugTranslationClient::messageReceived(const QByteArray &data)
-{
- QmlDebug::QPacket packet(dataStreamVersion(), data);
- qint8 command;
- packet >> command;
- qDebug() << Q_FUNC_INFO << "invalid command" << command;
-}
-
void QmlDebugTranslationClient::stateChanged(QmlDebug::QmlDebugClient::State state)
{
if (state == Unavailable)
diff --git a/src/plugins/qmlpreview/qmldebugtranslationclient.h b/src/plugins/qmlpreview/qmldebugtranslationclient.h
index 0b9e3594b2..8a6bc1478f 100644
--- a/src/plugins/qmlpreview/qmldebugtranslationclient.h
+++ b/src/plugins/qmlpreview/qmldebugtranslationclient.h
@@ -37,8 +37,6 @@ public:
explicit QmlDebugTranslationClient(QmlDebug::QmlDebugConnection *connection);
void changeLanguage(const QUrl &url, const QString &localeIsoCode);
-
- void messageReceived(const QByteArray &message) override;
void stateChanged(State state) override;
signals:
diff --git a/src/plugins/qmlprofiler/qmlprofilertraceclient.cpp b/src/plugins/qmlprofiler/qmlprofilertraceclient.cpp
index 054cc31eae..8dcd41cbb4 100644
--- a/src/plugins/qmlprofiler/qmlprofilertraceclient.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilertraceclient.cpp
@@ -36,7 +36,7 @@
namespace QmlProfiler {
-inline uint qHash(const QmlEventType &type)
+inline auto qHash(const QmlEventType &type)
{
return qHash(type.location())
^ (((type.message() << 12) & 0xf000) // 4 bits of message
diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp b/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp
index 53c76263a2..962b403492 100644
--- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp
+++ b/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp
@@ -99,6 +99,14 @@ QmlProjectItem *QmlProjectFileFormat::parseProjectFile(const Utils::FilePath &fi
if (fileSelectorsProperty.isValid())
projectItem->setFileSelectors(fileSelectorsProperty.value.toStringList());
+ const auto languagesProperty = rootNode->property(QLatin1String("supportedLanguages"));
+ if (languagesProperty.isValid())
+ projectItem->setSupportedLanguages(languagesProperty.value.toStringList());
+
+ const auto primaryLanguageProperty = rootNode->property(QLatin1String("primaryLanguage"));
+ if (primaryLanguageProperty.isValid())
+ projectItem->setPrimaryLanguage(primaryLanguageProperty.value.toString());
+
const auto forceFreeTypeProperty = rootNode->property("forceFreeType");
if (forceFreeTypeProperty.isValid())
projectItem->setForceFreeType(forceFreeTypeProperty.value.toBool());
diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp
index b7aa7ec434..249474a224 100644
--- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp
+++ b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp
@@ -77,6 +77,18 @@ void QmlProjectItem::setFileSelectors(const QStringList &selectors)
m_fileSelectors = selectors;
}
+void QmlProjectItem::setSupportedLanguages(const QStringList &languages)
+{
+ if (m_supportedLanguages != languages)
+ m_supportedLanguages = languages;
+}
+
+void QmlProjectItem::setPrimaryLanguage(const QString &language)
+{
+ if (m_primaryLanguage != language)
+ m_primaryLanguage = language;
+}
+
/* Returns list of absolute paths */
QStringList QmlProjectItem::files() const
{
diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h
index 2f0f8786ef..90d17e4859 100644
--- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h
+++ b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h
@@ -63,6 +63,12 @@ public:
QStringList fileSelectors() const { return m_fileSelectors; }
void setFileSelectors(const QStringList &selectors);
+ QStringList supportedLanguages() const { return m_supportedLanguages; }
+ void setSupportedLanguages(const QStringList &languages);
+
+ QString primaryLanguage() const { return m_primaryLanguage; }
+ void setPrimaryLanguage(const QString &language);
+
QStringList files() const;
bool matchesFile(const QString &filePath) const;
@@ -85,6 +91,8 @@ protected:
QString m_targetDirectory;
QStringList m_importPaths;
QStringList m_fileSelectors;
+ QStringList m_supportedLanguages;
+ QString m_primaryLanguage;
QString m_mainFile;
Utils::EnvironmentItems m_environment;
QVector<QmlProjectContentItem *> m_content; // content property
diff --git a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp
index dc8940844b..662e904974 100644
--- a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp
+++ b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp
@@ -46,8 +46,7 @@ static bool isMultilanguagePresent()
static Utils::FilePath getMultilanguageDatabaseFilePath(ProjectExplorer::Target *target)
{
if (target) {
- auto filePath = target->project()->projectDirectory().pathAppended(
- "multilanguage-experimental-v6.db");
+ auto filePath = target->project()->projectDirectory().pathAppended("translations.db");
if (filePath.exists())
return filePath;
}
diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp
index c1b8c6dc43..876fa4e188 100644
--- a/src/plugins/qmlprojectmanager/qmlproject.cpp
+++ b/src/plugins/qmlprojectmanager/qmlproject.cpp
@@ -340,6 +340,20 @@ QStringList QmlBuildSystem::customFileSelectors() const
return {};
}
+QStringList QmlBuildSystem::supportedLanguages() const
+{
+ if (m_projectItem)
+ return m_projectItem.data()->supportedLanguages();
+ return {};
+}
+
+QString QmlBuildSystem::primaryLanguage() const
+{
+ if (m_projectItem)
+ return m_projectItem.data()->primaryLanguage();
+ return {};
+}
+
void QmlBuildSystem::refreshProjectFile()
{
refresh(QmlBuildSystem::ProjectFile | Files);
@@ -509,6 +523,10 @@ QVariant QmlBuildSystem::additionalData(Id id) const
{
if (id == Constants::customFileSelectorsData)
return customFileSelectors();
+ if (id == Constants::supportedLanguagesData)
+ return supportedLanguages();
+ if (id == Constants::primaryLanguageData)
+ return primaryLanguage();
if (id == Constants::customForceFreeTypeData)
return forceFreeType();
if (id == Constants::customQtForMCUs)
diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h
index 51028b0d54..64c0cc90ec 100644
--- a/src/plugins/qmlprojectmanager/qmlproject.h
+++ b/src/plugins/qmlprojectmanager/qmlproject.h
@@ -88,6 +88,8 @@ public:
Utils::EnvironmentItems environment() const;
QStringList customImportPaths() const;
QStringList customFileSelectors() const;
+ QStringList supportedLanguages() const;
+ QString primaryLanguage() const;
bool forceFreeType() const;
bool addFiles(const QStringList &filePaths);
diff --git a/src/plugins/qmlprojectmanager/qmlprojectconstants.h b/src/plugins/qmlprojectmanager/qmlprojectconstants.h
index 7a8df1f4f8..184469f243 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectconstants.h
+++ b/src/plugins/qmlprojectmanager/qmlprojectconstants.h
@@ -32,6 +32,8 @@ namespace Constants {
const char * const QMLPROJECT_MIMETYPE = QmlJSTools::Constants::QMLPROJECT_MIMETYPE;
const char customFileSelectorsData[] = "CustomFileSelectorsData";
+const char supportedLanguagesData[] = "SupportedLanguagesData";
+const char primaryLanguageData[] = "PrimaryLanguageData";
const char customForceFreeTypeData[] = "CustomForceFreeType";
const char customQtForMCUs[] = "CustomQtForMCUs";
const char customQt6Project[] = "CustomQt6Project";
diff --git a/src/plugins/remotelinux/deploymenttimeinfo.cpp b/src/plugins/remotelinux/deploymenttimeinfo.cpp
index 3130aadf20..81eb9417e3 100644
--- a/src/plugins/remotelinux/deploymenttimeinfo.cpp
+++ b/src/plugins/remotelinux/deploymenttimeinfo.cpp
@@ -61,7 +61,7 @@ public:
QString sysroot;
};
-uint qHash(const DeployParameters &p) {
+auto qHash(const DeployParameters &p) {
return qHash(qMakePair(qMakePair(p.file, p.host), p.sysroot));
}
diff --git a/src/plugins/resourceeditor/resourceeditorplugin.cpp b/src/plugins/resourceeditor/resourceeditorplugin.cpp
index 62a4d75245..36de99ca39 100644
--- a/src/plugins/resourceeditor/resourceeditorplugin.cpp
+++ b/src/plugins/resourceeditor/resourceeditorplugin.cpp
@@ -46,6 +46,7 @@
#include <utils/algorithm.h>
#include <utils/parameteraction.h>
#include <utils/qtcassert.h>
+#include <utils/threadutils.h>
#include <QCoreApplication>
#include <QAction>
@@ -247,21 +248,35 @@ ResourceEditorPluginPrivate::ResourceEditorPluginPrivate(ResourceEditorPlugin *q
void ResourceEditorPlugin::extensionsInitialized()
{
- ProjectTree::registerTreeManager([](FolderNode *folder) {
- QList<FileNode *> toReplace;
- folder->forEachNode([&toReplace](FileNode *fn) {
- if (fn->fileType() == FileType::Resource)
- toReplace.append(fn);
- });
-
- for (FileNode *file : qAsConst(toReplace)) {
- FolderNode *const pn = file->parentFolderNode();
- QTC_ASSERT(pn, continue);
- const Utils::FilePath path = file->filePath();
- auto topLevel = std::make_unique<ResourceTopLevelNode>(path, pn->filePath());
- topLevel->setEnabled(file->isEnabled());
- topLevel->setIsGenerated(file->isGenerated());
- pn->replaceSubtree(file, std::move(topLevel));
+ ProjectTree::registerTreeManager([](FolderNode *folder, ProjectTree::ConstructionPhase phase) {
+ switch (phase) {
+ case ProjectTree::AsyncPhase: {
+ QList<FileNode *> toReplace;
+ folder->forEachNode([&toReplace](FileNode *fn) {
+ if (fn->fileType() == FileType::Resource)
+ toReplace.append(fn);
+ }, {}, [](const FolderNode *fn) {
+ return dynamic_cast<const ResourceTopLevelNode *>(fn) == nullptr;
+ });
+ for (FileNode *file : qAsConst(toReplace)) {
+ FolderNode *const pn = file->parentFolderNode();
+ QTC_ASSERT(pn, continue);
+ const Utils::FilePath path = file->filePath();
+ auto topLevel = std::make_unique<ResourceTopLevelNode>(path, pn->filePath());
+ topLevel->setEnabled(file->isEnabled());
+ topLevel->setIsGenerated(file->isGenerated());
+ pn->replaceSubtree(file, std::move(topLevel));
+ }
+ break;
+ }
+ case ProjectTree::FinalPhase: {
+ folder->forEachNode({}, [](FolderNode *fn) {
+ auto *topLevel = dynamic_cast<ResourceTopLevelNode *>(fn);
+ if (topLevel)
+ topLevel->setupWatcherIfNeeded();
+ });
+ break;
+ }
}
});
}
diff --git a/src/plugins/resourceeditor/resourcenode.cpp b/src/plugins/resourceeditor/resourcenode.cpp
index 30be6b0c0a..75f35da4d8 100644
--- a/src/plugins/resourceeditor/resourcenode.cpp
+++ b/src/plugins/resourceeditor/resourcenode.cpp
@@ -36,6 +36,7 @@
#include <utils/fileutils.h>
#include <utils/mimetypes/mimedatabase.h>
#include <utils/qtcassert.h>
+#include <utils/threadutils.h>
#include <QCoreApplication>
#include <QDir>
@@ -251,10 +252,8 @@ ResourceTopLevelNode::ResourceTopLevelNode(const FilePath &filePath,
setShowWhenEmpty(true);
if (!filePath.isEmpty()) {
- if (filePath.isReadableFile()) {
- m_document = new ResourceFileWatcher(this);
- DocumentManager::addDocument(m_document);
- }
+ if (filePath.isReadableFile())
+ setupWatcherIfNeeded();
} else {
m_contents = contents;
}
@@ -267,6 +266,15 @@ ResourceTopLevelNode::ResourceTopLevelNode(const FilePath &filePath,
addInternalNodes();
}
+void ResourceTopLevelNode::setupWatcherIfNeeded()
+{
+ if (m_document || !isMainThread())
+ return;
+
+ m_document = new ResourceFileWatcher(this);
+ DocumentManager::addDocument(m_document);
+}
+
ResourceTopLevelNode::~ResourceTopLevelNode()
{
if (m_document)
diff --git a/src/plugins/resourceeditor/resourcenode.h b/src/plugins/resourceeditor/resourcenode.h
index a992e267dc..03ae8039f0 100644
--- a/src/plugins/resourceeditor/resourcenode.h
+++ b/src/plugins/resourceeditor/resourcenode.h
@@ -39,6 +39,7 @@ public:
const QString &contents = {});
~ResourceTopLevelNode() override;
+ void setupWatcherIfNeeded();
void addInternalNodes();
bool supportsAction(ProjectExplorer::ProjectAction action, const Node *node) const override;
diff --git a/src/plugins/scxmleditor/scxmleditordocument.cpp b/src/plugins/scxmleditor/scxmleditordocument.cpp
index 22cf828589..2ce6669e68 100644
--- a/src/plugins/scxmleditor/scxmleditordocument.cpp
+++ b/src/plugins/scxmleditor/scxmleditordocument.cpp
@@ -147,6 +147,11 @@ bool ScxmlEditorDocument::reload(QString *errorString, ReloadFlag flag, ChangeTy
return success;
}
+bool ScxmlEditorDocument::supportsCodec(const QTextCodec *codec) const
+{
+ return codec == QTextCodec::codecForName("UTF-8");
+}
+
QString ScxmlEditorDocument::designWidgetContents() const
{
return m_designWidget->contents();
diff --git a/src/plugins/scxmleditor/scxmleditordocument.h b/src/plugins/scxmleditor/scxmleditordocument.h
index 587343a70b..f46d03f0a2 100644
--- a/src/plugins/scxmleditor/scxmleditordocument.h
+++ b/src/plugins/scxmleditor/scxmleditordocument.h
@@ -57,6 +57,7 @@ public:
bool isSaveAsAllowed() const override;
bool isModified() const override;
bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override;
+ bool supportsCodec(const QTextCodec *codec) const override;
// Internal
Common::MainWidget *designWidget() const;
diff --git a/src/plugins/studiowelcome/CMakeLists.txt b/src/plugins/studiowelcome/CMakeLists.txt
index d22625e7c7..2ae3bdcc9a 100644
--- a/src/plugins/studiowelcome/CMakeLists.txt
+++ b/src/plugins/studiowelcome/CMakeLists.txt
@@ -1,4 +1,5 @@
add_qtc_plugin(StudioWelcome
+ CONDITION TARGET Qt5::QuickWidgets
DEPENDS Qt5::QuickWidgets
PLUGIN_DEPENDS Core ProjectExplorer QtSupport
DEFINES STUDIO_QML_PATH="${CMAKE_CURRENT_SOURCE_DIR}/qml/"
diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp
index 52957d7192..d119567562 100644
--- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp
+++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp
@@ -342,10 +342,6 @@ bool StudioWelcomePlugin::initialize(const QStringList &arguments, QString *erro
m_welcomeMode = new WelcomeMode;
- QFontDatabase::addApplicationFont(":/studiofonts/TitilliumWeb-Regular.ttf");
- QFont systemFont("Titillium Web", QApplication::font().pointSize());
- QApplication::setFont(systemFont);
-
m_removeSplashTimer.setSingleShot(true);
m_removeSplashTimer.setInterval(15000);
connect(&m_removeSplashTimer, &QTimer::timeout, this, [this] { closeSplashScreen(); });
diff --git a/src/plugins/texteditor/codeassist/genericproposalmodel.cpp b/src/plugins/texteditor/codeassist/genericproposalmodel.cpp
index 96492caadb..d0343b11bf 100644
--- a/src/plugins/texteditor/codeassist/genericproposalmodel.cpp
+++ b/src/plugins/texteditor/codeassist/genericproposalmodel.cpp
@@ -41,7 +41,7 @@
using namespace TextEditor;
QT_BEGIN_NAMESPACE
-uint qHash(const AssistProposalItem &item)
+auto qHash(const AssistProposalItem &item)
{
return qHash(item.text());
}
diff --git a/src/plugins/texteditor/fontsettings.cpp b/src/plugins/texteditor/fontsettings.cpp
index e35ed5c3b2..4c6df391ce 100644
--- a/src/plugins/texteditor/fontsettings.cpp
+++ b/src/plugins/texteditor/fontsettings.cpp
@@ -141,7 +141,7 @@ bool FontSettings::equals(const FontSettings &f) const
&& m_scheme == f.m_scheme;
}
-uint qHash(const TextStyle &textStyle)
+auto qHash(const TextStyle &textStyle)
{
return ::qHash(quint8(textStyle));
}
@@ -202,7 +202,7 @@ QTextCharFormat FontSettings::toTextCharFormat(TextStyle category) const
return tf;
}
-uint qHash(TextStyles textStyles)
+auto qHash(TextStyles textStyles)
{
return ::qHash(reinterpret_cast<quint64&>(textStyles));
}
diff --git a/src/plugins/texteditor/snippets/snippetoverlay.cpp b/src/plugins/texteditor/snippets/snippetoverlay.cpp
index 568470b90e..287d0d7531 100644
--- a/src/plugins/texteditor/snippets/snippetoverlay.cpp
+++ b/src/plugins/texteditor/snippets/snippetoverlay.cpp
@@ -114,10 +114,10 @@ QTextCursor SnippetOverlay::nextSelectionCursor(const QTextCursor &cursor) const
const SnippetSelection &currentSelection = selectionForCursor(cursor);
if (currentSelection.variableIndex >= 0) {
int nextVariableIndex = currentSelection.variableIndex + 1;
- if (nextVariableIndex >= m_variables.size()) {
+ if (!m_variables.contains(nextVariableIndex)) {
if (m_finalSelectionIndex >= 0)
return cursorForIndex(m_finalSelectionIndex);
- nextVariableIndex = 0;
+ nextVariableIndex = m_variables.firstKey();
}
for (int selectionIndex : m_variables[nextVariableIndex]) {
@@ -142,8 +142,8 @@ QTextCursor SnippetOverlay::previousSelectionCursor(const QTextCursor &cursor) c
const SnippetSelection &currentSelection = selectionForCursor(cursor);
if (currentSelection.variableIndex >= 0) {
int previousVariableIndex = currentSelection.variableIndex - 1;
- if (previousVariableIndex < 0)
- previousVariableIndex = m_variables.size() - 1;
+ if (!m_variables.contains(previousVariableIndex))
+ previousVariableIndex = m_variables.lastKey();
const QList<int> &equivalents = m_variables[previousVariableIndex];
for (int i = equivalents.size() - 1; i >= 0; --i) {
diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp
index d7643af772..90d141808a 100644
--- a/src/plugins/texteditor/texteditor.cpp
+++ b/src/plugins/texteditor/texteditor.cpp
@@ -1966,7 +1966,7 @@ void TextEditorWidget::indent()
void TextEditorWidget::unindent()
{
- setMultiTextCursor(textDocument()->indent(multiTextCursor()));
+ setMultiTextCursor(textDocument()->unindent(multiTextCursor()));
}
void TextEditorWidget::undo()
@@ -8255,7 +8255,7 @@ IEditor *BaseTextEditor::duplicate()
QT_BEGIN_NAMESPACE
-uint qHash(const QColor &color)
+Utils::QHashValueType qHash(const QColor &color)
{
return color.rgba();
}
diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h
index 38cbdd01d3..5811f466e4 100644
--- a/src/plugins/texteditor/texteditor.h
+++ b/src/plugins/texteditor/texteditor.h
@@ -41,6 +41,7 @@
#include <utils/elidinglabel.h>
#include <utils/link.h>
#include <utils/multitextcursor.h>
+#include <utils/porting.h>
#include <utils/uncommentselection.h>
#include <QPlainTextEdit>
@@ -685,6 +686,6 @@ private:
QT_BEGIN_NAMESPACE
-uint qHash(const QColor &color);
+Utils::QHashValueType qHash(const QColor &color);
QT_END_NAMESPACE
diff --git a/src/plugins/vcsbase/vcsbaseeditor.cpp b/src/plugins/vcsbase/vcsbaseeditor.cpp
index d24b96dd5f..c2f4d4a0c9 100644
--- a/src/plugins/vcsbase/vcsbaseeditor.cpp
+++ b/src/plugins/vcsbase/vcsbaseeditor.cpp
@@ -958,18 +958,19 @@ void VcsBaseEditorWidget::slotCursorPositionChanged()
// Adapt entries combo to new position
// if the cursor goes across a file line.
const int newCursorLine = textCursor().blockNumber();
- if (newCursorLine == d->m_cursorLine)
- return;
- // Which section does it belong to?
- d->m_cursorLine = newCursorLine;
- const int section = sectionOfLine(d->m_cursorLine, d->m_entrySections);
- if (section != -1) {
- QComboBox *entriesComboBox = d->entriesComboBox();
- if (entriesComboBox->currentIndex() != section) {
- QSignalBlocker blocker(entriesComboBox);
- entriesComboBox->setCurrentIndex(section);
+ if (newCursorLine != d->m_cursorLine) {
+ // Which section does it belong to?
+ d->m_cursorLine = newCursorLine;
+ const int section = sectionOfLine(d->m_cursorLine, d->m_entrySections);
+ if (section != -1) {
+ QComboBox *entriesComboBox = d->entriesComboBox();
+ if (entriesComboBox->currentIndex() != section) {
+ QSignalBlocker blocker(entriesComboBox);
+ entriesComboBox->setCurrentIndex(section);
+ }
}
}
+ TextEditorWidget::slotCursorPositionChanged();
}
void VcsBaseEditorWidget::contextMenuEvent(QContextMenuEvent *e)
@@ -983,8 +984,10 @@ void VcsBaseEditorWidget::contextMenuEvent(QContextMenuEvent *e)
handler->fillContextMenu(menu, d->m_parameters->type);
}
}
- if (!menu)
- menu = createStandardContextMenu();
+ if (!menu) {
+ menu = new QMenu;
+ appendStandardContextMenuActions(menu);
+ }
switch (d->m_parameters->type) {
case LogOutput: // log might have diff
case DiffOutput: {
diff --git a/src/plugins/webassembly/webassemblyrunconfiguration.cpp b/src/plugins/webassembly/webassemblyrunconfiguration.cpp
index 82d7f8d9a5..123887caf7 100644
--- a/src/plugins/webassembly/webassemblyrunconfiguration.cpp
+++ b/src/plugins/webassembly/webassemblyrunconfiguration.cpp
@@ -40,25 +40,32 @@ using namespace Utils;
namespace WebAssembly {
namespace Internal {
+static FilePath pythonInterpreter(const Environment &env)
+{
+ const QString emsdkPythonEnvVarKey("EMSDK_PYTHON");
+ if (env.hasKey(emsdkPythonEnvVarKey))
+ return FilePath::fromUserInput(env.value(emsdkPythonEnvVarKey));
+
+ // FIXME: Centralize addPythonsFromPath() from the Python plugin and use that
+ for (const char *interpreterCandidate : {"python3", "python", "python2"}) {
+ const FilePath interpereter = env.searchInPath(QLatin1String(interpreterCandidate));
+ if (interpereter.isExecutableFile())
+ return interpereter;
+ }
+ return {};
+}
+
static CommandLine emrunCommand(Target *target, const QString &browser, const QString &port)
{
if (BuildConfiguration *bc = target->activeBuildConfiguration()) {
- const QFileInfo emrun = bc->environment().searchInPath("emrun").toFileInfo();
- auto html = bc->buildDirectory().pathAppended(target->project()->displayName() + ".html");
-
- // On Windows, we need to use the python interpreter (it comes with the emsdk) to ensure
- // that the web server is killed when the application is stopped in Qt Creator.
- // On Non-windows, we prefer using the shell script, because that knows how to find the
- // right python (not part of emsdk). The shell script stays attached to the server process.
- const FilePath interpreter = HostOsInfo::isWindowsHost()
- ? FilePath::fromUserInput(bc->environment().value("EMSDK_PYTHON"))
- : bc->environment().searchInPath("sh");
- const QString emrunLaunchScript = HostOsInfo::isWindowsHost()
- ? emrun.absolutePath() + "/" + emrun.baseName() + ".py"
- : emrun.absoluteFilePath();
-
- return CommandLine(interpreter, {
- emrunLaunchScript,
+ const Environment env = bc->environment();
+ const FilePath emrun = env.searchInPath("emrun");
+ const FilePath emrunPy = emrun.absolutePath().pathAppended(emrun.baseName() + ".py");
+ const FilePath html =
+ bc->buildDirectory().pathAppended(target->project()->displayName() + ".html");
+
+ return CommandLine(pythonInterpreter(env), {
+ emrunPy.path(),
"--browser", browser,
"--port", port,
"--no_emrun_detect",