aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libs/qmljs/qmljscheck.cpp1
-rw-r--r--src/libs/utils/environment.cpp11
-rw-r--r--src/libs/utils/pathlisteditor.cpp1
-rw-r--r--src/libs/utils/pathlisteditor.h3
-rw-r--r--src/plugins/android/androidavdmanager.cpp1
-rw-r--r--src/plugins/android/androidbuildapkstep.cpp5
-rw-r--r--src/plugins/android/androidconfigurations.cpp14
-rw-r--r--src/plugins/android/androidconstants.h19
-rw-r--r--src/plugins/android/androiddeployqtstep.cpp6
-rw-r--r--src/plugins/android/androidmanager.cpp2
-rw-r--r--src/plugins/android/androidpackageinstallationstep.cpp2
-rw-r--r--src/plugins/android/androidpotentialkit.cpp2
-rw-r--r--src/plugins/android/androidqtversion.cpp4
-rw-r--r--src/plugins/android/androidrunconfiguration.cpp6
-rw-r--r--src/plugins/android/androidrunnerworker.cpp2
-rw-r--r--src/plugins/clangcodemodel/clangdclient.cpp1127
-rw-r--r--src/plugins/clangformat/CMakeLists.txt1
-rw-r--r--src/plugins/clangformat/clangformat.pro2
-rw-r--r--src/plugins/clangformat/clangformat.qbs2
-rw-r--r--src/plugins/clangformat/clangformatconfigwidget.cpp97
-rw-r--r--src/plugins/clangformat/clangformatconfigwidget.h6
-rw-r--r--src/plugins/clangformat/clangformatfile.cpp116
-rw-r--r--src/plugins/clangformat/clangformatfile.h60
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp23
-rw-r--r--src/plugins/cmakeprojectmanager/cmaketool.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/fileapidataextractor.cpp32
-rw-r--r--src/plugins/coreplugin/dialogs/newdialogwidget.cpp1
-rw-r--r--src/plugins/coreplugin/locator/filesystemfilter.cpp5
-rw-r--r--src/plugins/coreplugin/manhattanstyle.cpp13
-rw-r--r--src/plugins/cppcheck/cppcheckoptions.cpp20
-rw-r--r--src/plugins/cppcheck/cppcheckoptions.h7
-rw-r--r--src/plugins/cppcheck/cppcheckrunner.cpp4
-rw-r--r--src/plugins/cppcheck/cppcheckrunner.h4
-rw-r--r--src/plugins/cppeditor/CppEditor.json.in2
-rw-r--r--src/plugins/cppeditor/semantichighlighter.cpp10
-rw-r--r--src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp19
-rw-r--r--src/plugins/debugger/shared/cdbsymbolpathlisteditor.h2
-rw-r--r--src/plugins/debugger/shared/symbolpathsdialog.cpp21
-rw-r--r--src/plugins/debugger/shared/symbolpathsdialog.h8
-rw-r--r--src/plugins/docker/dockerdevice.cpp112
-rw-r--r--src/plugins/help/litehtmlhelpviewer.cpp4
-rw-r--r--src/plugins/imageviewer/imageviewerfile.cpp10
-rw-r--r--src/plugins/mcusupport/mcusupportoptions.cpp104
-rw-r--r--src/plugins/mcusupport/mcusupportoptions.h14
-rw-r--r--src/plugins/mcusupport/mcusupportoptionspage.cpp2
-rw-r--r--src/plugins/mcusupport/mcusupportsdk.cpp129
-rw-r--r--src/plugins/projectexplorer/kitmanager.cpp15
-rw-r--r--src/plugins/projectexplorer/miniprojecttargetselector.cpp17
-rw-r--r--src/plugins/qbsprojectmanager/qbsnodes.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/profilecompletionassist.cpp13
-rw-r--r--src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/qmakenodes.cpp17
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp6
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeparsernodes.h6
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeproject.cpp3
-rw-r--r--src/plugins/qmakeprojectmanager/qmakestep.cpp5
-rw-r--r--src/plugins/qmldesigner/components/componentcore/componentcore_constants.h3
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp10
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanager.h2
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp3
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp28
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp14
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditoritem.h4
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorview.cpp43
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorview.h1
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp9
-rw-r--r--src/plugins/qmldesigner/components/formeditor/selectiontool.cpp20
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp23
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp43
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp4
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp2
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp12
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineutils.h5
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp11
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp5
-rw-r--r--src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h3
-rw-r--r--src/plugins/qmldesigner/designercore/include/nodehints.h1
-rw-r--r--src/plugins/qmldesigner/designercore/include/qmlitemnode.h1
-rw-r--r--src/plugins/qmldesigner/designercore/include/qmlobjectnode.h5
-rw-r--r--src/plugins/qmldesigner/designercore/include/qmlstate.h1
-rw-r--r--src/plugins/qmldesigner/designercore/include/qmltimelinekeyframegroup.h3
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp2
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp5
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp5
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp68
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlstate.cpp7
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp23
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp6
-rw-r--r--src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp46
-rw-r--r--src/plugins/qmldesigner/designercore/model/texttomodelmerger.h7
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/quick.metainfo47
-rw-r--r--src/plugins/qmljseditor/qmljseditorplugin.cpp3
-rw-r--r--src/plugins/texteditor/basefilefind.cpp2
-rw-r--r--src/plugins/valgrind/memcheckerrorview.cpp13
-rw-r--r--src/plugins/valgrind/memcheckerrorview.h8
-rw-r--r--src/plugins/valgrind/memchecktool.cpp26
-rw-r--r--src/plugins/valgrind/suppressiondialog.cpp35
-rw-r--r--src/plugins/valgrind/valgrindsettings.cpp14
-rw-r--r--src/plugins/valgrind/valgrindsettings.h6
-rw-r--r--src/shared/proparser/qmakebuiltins.cpp27
-rw-r--r--src/shared/proparser/qmakeglobals.cpp4
-rw-r--r--src/shared/proparser/qmakeglobals.h3
m---------src/shared/qbs0
-rw-r--r--src/shared/shared.pro5
-rw-r--r--src/tools/qml2puppet/qml2puppet.qbs1
106 files changed, 1693 insertions, 1052 deletions
diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp
index d63ae11611..3ce402589c 100644
--- a/src/libs/qmljs/qmljscheck.cpp
+++ b/src/libs/qmljs/qmljscheck.cpp
@@ -617,7 +617,6 @@ class UnsupportedTypesByQmlUi : public QStringList
{
public:
UnsupportedTypesByQmlUi() : QStringList({"ShaderEffect",
- "Component",
"Drawer"})
{
append(UnsupportedTypesByVisualDesigner());
diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp
index 2d3d6edf3b..76f32ccb98 100644
--- a/src/libs/utils/environment.cpp
+++ b/src/libs/utils/environment.cpp
@@ -136,8 +136,7 @@ void Environment::setupEnglishOutput()
set("LANGUAGE", "en_US:en");
}
-static FilePath searchInDirectory(const Environment &env,
- const QStringList &execs,
+static FilePath searchInDirectory(const QStringList &execs,
const FilePath &directory,
QSet<FilePath> &alreadyChecked)
{
@@ -226,7 +225,7 @@ static FilePath searchInDirectoriesHelper(const Environment &env,
QSet<FilePath> alreadyChecked;
for (const FilePath &dir : dirs) {
- FilePath tmp = searchInDirectory(env, execs, dir, alreadyChecked);
+ FilePath tmp = searchInDirectory(execs, dir, alreadyChecked);
if (!tmp.isEmpty() && (!func || func(tmp)))
return tmp;
}
@@ -236,7 +235,7 @@ static FilePath searchInDirectoriesHelper(const Environment &env,
return FilePath();
for (const FilePath &p : env.path()) {
- FilePath tmp = searchInDirectory(env, execs, p, alreadyChecked);
+ FilePath tmp = searchInDirectory(execs, p, alreadyChecked);
if (!tmp.isEmpty() && (!func || func(tmp)))
return tmp;
}
@@ -281,14 +280,14 @@ FilePaths Environment::findAllInPath(const QString &executable,
QSet<FilePath> result;
QSet<FilePath> alreadyChecked;
for (const FilePath &dir : additionalDirs) {
- FilePath tmp = searchInDirectory(*this, execs, dir, alreadyChecked);
+ FilePath tmp = searchInDirectory(execs, dir, alreadyChecked);
if (!tmp.isEmpty() && (!func || func(tmp)))
result << tmp;
}
if (!executable.contains('/')) {
for (const FilePath &p : path()) {
- FilePath tmp = searchInDirectory(*this, execs, p, alreadyChecked);
+ FilePath tmp = searchInDirectory(execs, p, alreadyChecked);
if (!tmp.isEmpty() && (!func || func(tmp)))
result << tmp;
}
diff --git a/src/libs/utils/pathlisteditor.cpp b/src/libs/utils/pathlisteditor.cpp
index 61d215fd83..d24be4995b 100644
--- a/src/libs/utils/pathlisteditor.cpp
+++ b/src/libs/utils/pathlisteditor.cpp
@@ -130,6 +130,7 @@ PathListEditor::PathListEditor(QWidget *parent) :
});
addButton(tr("Delete Line"), this, [this] { deletePathAtCursor(); });
addButton(tr("Clear"), this, [this] { d->edit->clear(); });
+ connect(d->edit, &QPlainTextEdit::textChanged, this, &PathListEditor::changed);
}
PathListEditor::~PathListEditor()
diff --git a/src/libs/utils/pathlisteditor.h b/src/libs/utils/pathlisteditor.h
index 07392fbe6e..edd70336c0 100644
--- a/src/libs/utils/pathlisteditor.h
+++ b/src/libs/utils/pathlisteditor.h
@@ -58,6 +58,9 @@ public:
void setPathList(const QString &pathString);
void setFileDialogTitle(const QString &l);
+signals:
+ void changed();
+
protected:
// Index after which to insert further "Add" buttons
static const int lastInsertButtonIndex;
diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp
index 27af7b76db..1b30803910 100644
--- a/src/plugins/android/androidavdmanager.cpp
+++ b/src/plugins/android/androidavdmanager.cpp
@@ -203,6 +203,7 @@ bool AndroidAvdManager::removeAvd(const QString &name) const
qCDebug(avdManagerLog) << "Running command (removeAvd):" << command.toUserOutput();
QtcProcess proc;
proc.setTimeoutS(5);
+ proc.setEnvironment(AndroidConfigurations::toolsEnvironment(m_config));
proc.setCommand(command);
proc.runBlocking();
return proc.result() == QtcProcess::FinishedWithSuccess;
diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp
index 4c47418c85..dc6b42754c 100644
--- a/src/plugins/android/androidbuildapkstep.cpp
+++ b/src/plugins/android/androidbuildapkstep.cpp
@@ -196,7 +196,7 @@ QWidget *AndroidBuildApkWidget::createApplicationGroup()
});
auto formLayout = new QFormLayout(group);
- formLayout->addRow(tr("Android build SDK:"), targetSDKComboBox);
+ formLayout->addRow(tr("Android build platform SDK:"), targetSDKComboBox);
auto createAndroidTemplatesButton = new QPushButton(tr("Create Templates"));
createAndroidTemplatesButton->setToolTip(
@@ -939,7 +939,8 @@ QVariant AndroidBuildApkStep::data(Utils::Id id) const
}
if (id == Constants::SdkLocation)
return QVariant::fromValue(AndroidConfigurations::currentConfig().sdkLocation());
- if (id == Constants::AndroidABIs)
+
+ if (id == Constants::AndroidMkSpecAbis)
return AndroidManager::applicationAbis(target());
return AbstractProcessStep::data(id);
diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp
index f1eb6379d6..c769a744d2 100644
--- a/src/plugins/android/androidconfigurations.cpp
+++ b/src/plugins/android/androidconfigurations.cpp
@@ -1127,10 +1127,9 @@ void AndroidConfigurations::removeOldToolChains()
void AndroidConfigurations::removeUnusedDebuggers()
{
- const QList<BaseQtVersion *> qtVersions
- = QtVersionManager::versions([](const BaseQtVersion *v) {
- return v->type() == Constants::ANDROIDQT;
- });
+ const QList<BaseQtVersion*> qtVersions = QtVersionManager::versions([](const BaseQtVersion *v) {
+ return v->type() == Constants::ANDROID_QT_TYPE;
+ });
QVector<FilePath> uniqueNdks;
for (const BaseQtVersion *qt : qtVersions) {
@@ -1267,8 +1266,8 @@ void AndroidConfigurations::updateAutomaticKitList()
removeUnusedDebuggers();
QHash<Abi, QList<const BaseQtVersion *> > qtVersionsForArch;
- const QList<BaseQtVersion *> qtVersions = QtVersionManager::versions([](const BaseQtVersion *v) {
- return v->type() == Constants::ANDROIDQT;
+ const QList<BaseQtVersion*> qtVersions = QtVersionManager::versions([](const BaseQtVersion *v) {
+ return v->type() == Constants::ANDROID_QT_TYPE;
});
for (const BaseQtVersion *qtVersion : qtVersions) {
const Abis qtAbis = qtVersion->qtAbis();
@@ -1322,9 +1321,8 @@ void AndroidConfigurations::updateAutomaticKitList()
QStringList abis = static_cast<const AndroidQtVersion *>(qt)->androidAbis();
Debugger::DebuggerKitAspect::setDebugger(k, findOrRegisterDebugger(tc, abis));
- k->setSticky(ToolChainKitAspect::id(), true);
+ BuildDeviceKitAspect::setDeviceId(k, DeviceManager::defaultDesktopDevice()->id());
k->setSticky(QtKitAspect::id(), true);
- k->setSticky(DeviceKitAspect::id(), true);
k->setMutable(DeviceKitAspect::id(), true);
k->setSticky(DeviceTypeKitAspect::id(), true);
diff --git a/src/plugins/android/androidconstants.h b/src/plugins/android/androidconstants.h
index df8c821f98..02bb50b348 100644
--- a/src/plugins/android/androidconstants.h
+++ b/src/plugins/android/androidconstants.h
@@ -42,9 +42,9 @@ namespace Internal {
namespace Constants {
const char ANDROID_SETTINGS_ID[] = "BB.Android Configurations";
const char ANDROID_TOOLCHAIN_TYPEID[] = "Qt4ProjectManager.ToolChain.Android";
-const char ANDROIDQT[] = "Qt4ProjectManager.QtVersion.Android";
+const char ANDROID_QT_TYPE[] = "Qt4ProjectManager.QtVersion.Android";
-const char ANDROID_AMSTARTARGS[] = "Android.AmStartArgs";
+const char ANDROID_AM_START_ARGS[] = "Android.AmStartArgs";
// Note: Can be set on RunConfiguration using an aspect and/or
// the AndroidRunnerWorker using recordData()
const char ANDROID_PRESTARTSHELLCMDLIST[] = "Android.PreStartShellCmdList";
@@ -67,32 +67,33 @@ const char ANDROID_ARCHITECTURE[] = "Android.Architecture";
const char ANDROID_PACKAGE_SOURCE_DIR[] = "ANDROID_PACKAGE_SOURCE_DIR";
const char ANDROID_EXTRA_LIBS[] = "ANDROID_EXTRA_LIBS";
const char ANDROID_ABI[] = "ANDROID_ABI";
+const char ANDROID_TARGET_ARCH[] = "ANDROID_TARGET_ARCH";
const char ANDROID_ABIS[] = "ANDROID_ABIS";
const char ANDROID_APPLICATION_ARGUMENTS[] = "ANDROID_APPLICATION_ARGUMENTS";
-const char QT_ANDROID_APPLICATION_ARGUMENTS[] = "QT_ANDROID_APPLICATION_ARGUMENTS";
const char ANDROID_DEPLOYMENT_SETTINGS_FILE[] = "ANDROID_DEPLOYMENT_SETTINGS_FILE";
const char ANDROID_SO_LIBS_PATHS[] = "ANDROID_SO_LIBS_PATHS";
-const char ANDROID_PACKAGENAME[] = "Android.PackageName";
-const char ANDROID_PACKAGE_INSTALLATION_STEP_ID[]
- = "Qt4ProjectManager.AndroidPackageInstallationStep";
+const char ANDROID_PACKAGE_INSTALL_STEP_ID[] = "Qt4ProjectManager.AndroidPackageInstallationStep";
const char ANDROID_BUILD_APK_ID[] = "QmakeProjectManager.AndroidBuildApkStep";
const char ANDROID_DEPLOY_QT_ID[] = "Qt4ProjectManager.AndroidDeployQtStep";
const char AndroidPackageSourceDir[] = "AndroidPackageSourceDir"; // QString
const char AndroidDeploySettingsFile[] = "AndroidDeploySettingsFile"; // QString
const char AndroidExtraLibs[] = "AndroidExtraLibs"; // QStringList
-// REMOVE ME
-const char AndroidArch[] = "AndroidArch"; // QString
+const char AndroidAbi[] = "AndroidAbi"; // QString
+const char AndroidAbis[] = "AndroidAbis"; // QStringList
+const char AndroidMkSpecAbis[] = "AndroidMkSpecAbis"; // QStringList
const char AndroidSoLibPath[] = "AndroidSoLibPath"; // QStringList
const char AndroidTargets[] = "AndroidTargets"; // QStringList
+const char AndroidApplicationArgs[] = "AndroidApplicationArgs"; // QString
+
+// For qbs support
const char AndroidApk[] = "Android.APK"; // QStringList
const char AndroidManifest[] = "Android.Manifest"; // QStringList
const char AndroidNdkPlatform[] = "AndroidNdkPlatform"; //QString
const char NdkLocation[] = "NdkLocation"; // FileName
const char SdkLocation[] = "SdkLocation"; // FileName
-const char AndroidABIs[] = "AndroidABIs"; // QString
// Android Device
const Utils::Id AndroidSerialNumber = "AndroidSerialNumber";
diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp
index 12d3b3e651..8c50c7a52c 100644
--- a/src/plugins/android/androiddeployqtstep.cpp
+++ b/src/plugins/android/androiddeployqtstep.cpp
@@ -149,14 +149,14 @@ bool AndroidDeployQtStep::init()
info = androidDeployQtStep->m_deviceInfo;
const BuildSystem *bs = buildSystem();
- auto selectedAbis = bs->property(Constants::ANDROID_ABIS).toStringList();
+ auto selectedAbis = bs->property(Constants::AndroidAbis).toStringList();
const QString buildKey = target()->activeBuildKey();
if (selectedAbis.isEmpty())
- selectedAbis = bs->extraData(buildKey, Constants::ANDROID_ABIS).toStringList();
+ selectedAbis = bs->extraData(buildKey, Constants::AndroidAbis).toStringList();
if (selectedAbis.isEmpty())
- selectedAbis.append(bs->extraData(buildKey, Constants::AndroidArch).toString());
+ selectedAbis.append(bs->extraData(buildKey, Constants::AndroidAbi).toString());
if (!info.isValid()) {
const IDevice *dev = DeviceKitAspect::device(kit()).data();
diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp
index 7d2a568303..e8944fba17 100644
--- a/src/plugins/android/androidmanager.cpp
+++ b/src/plugins/android/androidmanager.cpp
@@ -408,7 +408,7 @@ QString AndroidManager::apkDevicePreferredAbi(const Target *target)
auto libsPath = androidBuildDirectory(target).pathAppended("libs");
if (!libsPath.exists()) {
if (const ProjectNode *node = currentProjectNode(target))
- return preferredAbi(node->data(Android::Constants::ANDROID_ABIS).toStringList(),
+ return preferredAbi(node->data(Android::Constants::AndroidAbis).toStringList(),
target);
}
QStringList apkAbis;
diff --git a/src/plugins/android/androidpackageinstallationstep.cpp b/src/plugins/android/androidpackageinstallationstep.cpp
index cc951859ea..e1eb82e0cc 100644
--- a/src/plugins/android/androidpackageinstallationstep.cpp
+++ b/src/plugins/android/androidpackageinstallationstep.cpp
@@ -196,7 +196,7 @@ void AndroidPackageInstallationStep::doRun()
AndroidPackageInstallationFactory::AndroidPackageInstallationFactory()
{
- registerStep<AndroidPackageInstallationStep>(Constants::ANDROID_PACKAGE_INSTALLATION_STEP_ID);
+ registerStep<AndroidPackageInstallationStep>(Constants::ANDROID_PACKAGE_INSTALL_STEP_ID);
setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD);
setSupportedDeviceType(Android::Constants::ANDROID_DEVICE_TYPE);
setRepeatable(false);
diff --git a/src/plugins/android/androidpotentialkit.cpp b/src/plugins/android/androidpotentialkit.cpp
index 128c853c84..79a1afebec 100644
--- a/src/plugins/android/androidpotentialkit.cpp
+++ b/src/plugins/android/androidpotentialkit.cpp
@@ -74,7 +74,7 @@ bool AndroidPotentialKit::isEnabled() const
}
return QtSupport::QtVersionManager::version([](const QtSupport::BaseQtVersion *v) {
- return v->isValid() && v->type() == QString::fromLatin1(Constants::ANDROIDQT);
+ return v->isValid() && v->type() == QString::fromLatin1(Constants::ANDROID_QT_TYPE);
});
}
diff --git a/src/plugins/android/androidqtversion.cpp b/src/plugins/android/androidqtversion.cpp
index 3cc9b06ccd..b53dc9e8d0 100644
--- a/src/plugins/android/androidqtversion.cpp
+++ b/src/plugins/android/androidqtversion.cpp
@@ -197,7 +197,7 @@ void AndroidQtVersion::parseMkSpec(ProFileEvaluator *evaluator) const
{
m_androidAbis = evaluator->values("ALL_ANDROID_ABIS");
if (m_androidAbis.isEmpty())
- m_androidAbis = QStringList{evaluator->value("ANDROID_TARGET_ARCH")};
+ m_androidAbis = QStringList{evaluator->value(Constants::ANDROID_TARGET_ARCH)};
const QString androidPlatform = evaluator->value("ANDROID_PLATFORM");
if (!androidPlatform.isEmpty()) {
const QRegularExpression regex("android-(\\d+)");
@@ -232,7 +232,7 @@ QSet<Utils::Id> AndroidQtVersion::targetDeviceTypes() const
AndroidQtVersionFactory::AndroidQtVersionFactory()
{
setQtVersionCreator([] { return new AndroidQtVersion; });
- setSupportedType(Constants::ANDROIDQT);
+ setSupportedType(Constants::ANDROID_QT_TYPE);
setPriority(90);
setRestrictionChecker([](const SetupData &setup) {
diff --git a/src/plugins/android/androidrunconfiguration.cpp b/src/plugins/android/androidrunconfiguration.cpp
index 5b834fa673..e1477c2615 100644
--- a/src/plugins/android/androidrunconfiguration.cpp
+++ b/src/plugins/android/androidrunconfiguration.cpp
@@ -82,13 +82,13 @@ AndroidRunConfiguration::AndroidRunConfiguration(Target *target, Utils::Id id)
if (target->buildConfigurations().first()->buildType() == BuildConfiguration::BuildType::Release) {
const QString buildKey = target->activeBuildKey();
target->buildSystem()->setExtraData(buildKey,
- Android::Constants::ANDROID_APPLICATION_ARGUMENTS,
- extraAppArgsAspect->arguments(target->macroExpander()));
+ Android::Constants::AndroidApplicationArgs,
+ extraAppArgsAspect->arguments(target->macroExpander()));
}
});
auto amStartArgsAspect = addAspect<StringAspect>();
- amStartArgsAspect->setId(Constants::ANDROID_AMSTARTARGS);
+ amStartArgsAspect->setId(Constants::ANDROID_AM_START_ARGS);
amStartArgsAspect->setSettingsKey("Android.AmStartArgsKey");
amStartArgsAspect->setLabelText(tr("Activity manager start options:"));
amStartArgsAspect->setDisplayStyle(StringAspect::LineEditDisplay);
diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp
index db28291511..83c512a46a 100644
--- a/src/plugins/android/androidrunnerworker.cpp
+++ b/src/plugins/android/androidrunnerworker.cpp
@@ -279,7 +279,7 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa
m_extraAppParams = runControl->runnable().command.arguments();
}
- if (auto aspect = runControl->aspect(Constants::ANDROID_AMSTARTARGS)) {
+ if (auto aspect = runControl->aspect(Constants::ANDROID_AM_START_ARGS)) {
QTC_CHECK(aspect->value().type() == QVariant::String);
const QString startArgs = aspect->value().toString();
m_amStartExtraArgs = ProcessArgs::splitArgs(startArgs, OsTypeOtherUnix);
diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp
index fea5314001..5b3e76bee8 100644
--- a/src/plugins/clangcodemodel/clangdclient.cpp
+++ b/src/plugins/clangcodemodel/clangdclient.cpp
@@ -66,14 +66,18 @@
#include <utils/runextensions.h>
#include <QCheckBox>
+#include <QDateTime>
+#include <QElapsedTimer>
#include <QFile>
#include <QHash>
#include <QPair>
#include <QPointer>
#include <QRegularExpression>
+#include <QtConcurrent>
#include <set>
#include <unordered_map>
+#include <utility>
using namespace CPlusPlus;
using namespace Core;
@@ -89,6 +93,7 @@ static Q_LOGGING_CATEGORY(clangdLog, "qtc.clangcodemodel.clangd", QtWarningMsg);
static Q_LOGGING_CATEGORY(clangdLogServer, "qtc.clangcodemodel.clangd.server", QtWarningMsg);
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 QString indexingToken() { return "backgroundIndexProgress"; }
class AstNode : public JsonObject
@@ -171,6 +176,12 @@ public:
bool isNamespace() const { return role() == "declaration" && kind() == "Namespace"; }
+ bool isTemplateParameterDeclaration() const
+ {
+ return role() == "declaration" && (kind() == "TemplateTypeParm"
+ || kind() == "NonTypeTemplateParm");
+ };
+
QString type() const
{
const Utils::optional<QString> arcanaString = arcana();
@@ -305,14 +316,6 @@ static QList<AstNode> getAstPath(const AstNode &root, const Range &range)
return path;
}
-static AstNode getAstNode(const AstNode &root, const Range &range)
-{
- const QList<AstNode> path = getAstPath(root, range);
- if (!path.isEmpty())
- return path.last();
- return {};
-}
-
static Usage::Type getUsageType(const QList<AstNode> &path)
{
bool potentialWrite = false;
@@ -773,6 +776,109 @@ private:
std::unordered_map<DocType, VersionedDocData<DocType, DataType>> m_data;
};
+class TaskTimer
+{
+public:
+ TaskTimer(const QString &task) : m_task(task) {}
+
+ void stopTask()
+ {
+ // This can happen due to the RAII mechanism employed with SubtaskTimer.
+ // The subtask timers will expire immediately after, so this does not distort
+ // the timing data.
+ if (m_subtasks > 0) {
+ QTC_CHECK(m_timer.isValid());
+ m_elapsedMs += m_timer.elapsed();
+ m_timer.invalidate();
+ m_subtasks = 0;
+ }
+ m_started = false;
+ qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": took " << m_elapsedMs
+ << " ms in UI thread";
+ }
+ void startSubtask()
+ {
+ // We have some callbacks that are either synchronous or asynchronous, depending on
+ // dynamic conditions. In the sync case, we will have nested subtasks, in which case
+ // the inner ones must not collect timing data, as their code blocks are already covered.
+ if (++m_subtasks > 1)
+ return;
+ if (!m_started) {
+ QTC_ASSERT(m_elapsedMs == 0, m_elapsedMs = 0);
+ m_started = true;
+ m_finalized = false;
+ qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": starting";
+ }
+ qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": subtask started at "
+ << QDateTime::currentDateTime().toString();
+ QTC_CHECK(!m_timer.isValid());
+ m_timer.start();
+ }
+ void stopSubtask(bool isFinalizing)
+ {
+ if (m_subtasks == 0) // See stopTask().
+ return;
+ if (isFinalizing)
+ m_finalized = true;
+ if (--m_subtasks > 0) // See startSubtask().
+ return;
+ qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": subtask stopped at "
+ << QDateTime::currentDateTime().toString();
+ QTC_CHECK(m_timer.isValid());
+ m_elapsedMs += m_timer.elapsed();
+ m_timer.invalidate();
+ if (m_finalized)
+ stopTask();
+ }
+
+private:
+ const QString m_task;
+ QElapsedTimer m_timer;
+ qint64 m_elapsedMs = 0;
+ int m_subtasks = 0;
+ bool m_started = false;
+ bool m_finalized = false;
+};
+
+class SubtaskTimer
+{
+public:
+ SubtaskTimer(TaskTimer &timer) : m_timer(timer) { m_timer.startSubtask(); }
+ ~SubtaskTimer() { m_timer.stopSubtask(m_isFinalizing); }
+
+protected:
+ void makeFinalizing() { m_isFinalizing = true; }
+
+private:
+ TaskTimer &m_timer;
+ bool m_isFinalizing = false;
+};
+
+class FinalizingSubtaskTimer : public SubtaskTimer
+{
+public:
+ FinalizingSubtaskTimer(TaskTimer &timer) : SubtaskTimer(timer) { makeFinalizing(); }
+};
+
+class ThreadedSubtaskTimer
+{
+public:
+ ThreadedSubtaskTimer(const QString &task) : m_task(task)
+ {
+ qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": starting thread";
+ m_timer.start();
+ }
+
+ ~ThreadedSubtaskTimer()
+ {
+ qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": took " << m_timer.elapsed()
+ << " ms in dedicated thread";
+ }
+private:
+ const QString m_task;
+ QElapsedTimer m_timer;
+};
+
class ClangdClient::Private
{
public:
@@ -812,7 +918,7 @@ public:
using TextDocOrFile = const Utils::variant<const TextDocument *, Utils::FilePath>;
using AstHandler = const std::function<void(const AstNode &ast, const MessageId &)>;
MessageId getAndHandleAst(TextDocOrFile &doc, AstHandler &astHandler,
- AstCallbackMode callbackMode);
+ AstCallbackMode callbackMode, const Range &range = {});
ClangdClient * const q;
const CppEditor::ClangdSettings::Data settings;
@@ -821,9 +927,13 @@ public:
Utils::optional<SwitchDeclDefData> switchDeclDefData;
Utils::optional<LocalRefsData> localRefsData;
Utils::optional<QVersionNumber> versionNumber;
- std::unordered_map<TextDocument *, CppEditor::SemanticHighlighter> highlighters;
+
+ // The highlighters are owned by their respective documents.
+ std::unordered_map<TextDocument *, CppEditor::SemanticHighlighter *> highlighters;
+
VersionedDataCache<const TextDocument *, AstNode> astCache;
VersionedDataCache<Utils::FilePath, AstNode> externalAstCache;
+ TaskTimer highlightingTimer{"highlighting"};
quint64 nextJobId = 0;
bool isFullyIndexed = false;
bool isTesting = false;
@@ -1453,16 +1563,16 @@ void ClangdClient::followSymbol(TextDocument *document,
return;
}
- const auto astHandler = [this, id = d->followSymbolData->id, range = Range(cursor)]
+ const auto astHandler = [this, id = d->followSymbolData->id]
(const AstNode &ast, const MessageId &) {
qCDebug(clangdLog) << "received ast response for cursor";
if (!d->followSymbolData || d->followSymbolData->id != id)
return;
- d->followSymbolData->cursorNode = getAstNode(ast, range);
+ d->followSymbolData->cursorNode = ast;
if (d->followSymbolData->defLink.hasValidTarget())
d->handleGotoDefinitionResult();
};
- d->getAndHandleAst(document, astHandler, Private::AstCallbackMode::SyncIfPossible);
+ d->getAndHandleAst(document, astHandler, Private::AstCallbackMode::AlwaysAsync, Range(cursor));
}
void ClangdClient::switchDeclDef(TextDocument *document, const QTextCursor &cursor,
@@ -1912,18 +2022,19 @@ void ClangdClient::Private::handleGotoImplementationResult(
: TextDocOrFile(defLinkFilePath);
const Position defLinkPos(followSymbolData->defLink.targetLine - 1,
followSymbolData->defLink.targetColumn);
- const auto astHandler = [this, range = Range(defLinkPos, defLinkPos), id = followSymbolData->id]
+ const auto astHandler = [this, id = followSymbolData->id]
(const AstNode &ast, const MessageId &) {
qCDebug(clangdLog) << "received ast response for def link";
if (!followSymbolData || followSymbolData->id != id)
return;
- followSymbolData->defLinkNode = getAstNode(ast, range);
+ followSymbolData->defLinkNode = ast;
if (followSymbolData->pendingSymbolInfoRequests.isEmpty()
&& followSymbolData->pendingGotoDefRequests.isEmpty()) {
handleDocumentInfoResults();
}
};
- getAndHandleAst(defLinkDocVariant, astHandler, AstCallbackMode::SyncIfPossible);
+ getAndHandleAst(defLinkDocVariant, astHandler, AstCallbackMode::AlwaysAsync,
+ Range(defLinkPos, defLinkPos));
}
void ClangdClient::Private::handleDocumentInfoResults()
@@ -2013,446 +2124,37 @@ void ClangdClient::Private::setHelpItemForTooltip(const MessageId &token, const
q->hoverHandler()->setHelpItem(token, helpItem);
}
-static void collectExtraResults(QFutureInterface<HighlightingResult> &future,
- HighlightingResults &results, const AstNode &ast,
- QTextDocument *doc, const QString &docContent)
+class ExtraHighlightingResultsCollector
{
- if (!ast.isValid())
- return;
-
- static const auto lessThan = [](const HighlightingResult &r1,
- const HighlightingResult &r2) {
- return r1.line < r2.line || (r1.line == r2.line && r1.column < r2.column)
- || (r1.line == r2.line && r1.column == r2.column && r1.length < r2.length);
- };
- const auto insert = [&](const HighlightingResult &result) {
- if (!result.isValid()) // Some nodes don't have a range.
- return;
- const auto it = std::lower_bound(results.begin(), results.end(), result, lessThan);
- if (it == results.end() || *it != result) {
- qCDebug(clangdLogHighlight) << "adding additional highlighting result"
- << result.line << result.column << result.length;
- results.insert(it, result);
- return;
- }
-
- // This is for conversion operators, whose type part is only reported as a type by clangd.
- if ((it->textStyles.mainStyle == C_TYPE
- || it->textStyles.mainStyle == C_PRIMITIVE_TYPE)
- && !result.textStyles.mixinStyles.empty()
- && result.textStyles.mixinStyles.at(0) == C_OPERATOR) {
- it->textStyles.mixinStyles = result.textStyles.mixinStyles;
- }
- };
- const auto setFromRange = [doc](HighlightingResult &result, const Range &range) {
- if (!range.isValid())
- return;
- const Position startPos = range.start();
- const Position endPos = range.end();
- result.line = startPos.line() + 1;
- result.column = startPos.character() + 1;
- result.length = endPos.toPositionInDocument(doc) - startPos.toPositionInDocument(doc);
- };
- static const auto onlyIndexOf = [](const QStringView &view, const QStringView &s,
- int from = 0) {
- const int firstIndex = view.indexOf(s, from);
- if (firstIndex == -1)
- return -1;
- const int nextIndex = view.indexOf(s, firstIndex + 1);
-
- // The second condion deals with the off-by-one error in TemplateSpecialization nodes;
- // see below.
- return nextIndex == -1 || nextIndex == firstIndex + 1 ? firstIndex : -1;
- };
-
- QList<AstNode> nodes = {ast};
- while (!nodes.isEmpty()) {
- if (future.isCanceled())
- return;
- const AstNode node = nodes.takeFirst();
- const QList<AstNode> children = node.children().value_or(QList<AstNode>());
- nodes << children;
-
- if (node.kind().endsWith("Literal")) {
- HighlightingResult result;
- result.useTextSyles = true;
- const bool isStringLike = node.kind().startsWith("String")
- || node.kind().startsWith("Character");
- result.textStyles.mainStyle = isStringLike ? C_STRING : C_NUMBER;
- setFromRange(result, node.range());
- insert(result);
- continue;
- }
- if (node.role() == "type" && node.kind() == "Builtin") {
- HighlightingResult result;
- result.useTextSyles = true;
- result.textStyles.mainStyle = C_PRIMITIVE_TYPE;
- setFromRange(result, node.range());
- insert(result);
- continue;
- }
- if (node.role() == "attribute" && (node.kind() == "Override" || node.kind() == "Final")) {
- HighlightingResult result;
- result.useTextSyles = true;
- result.textStyles.mainStyle = C_KEYWORD;
- setFromRange(result, node.range());
- insert(result);
- continue;
- }
-
- const bool isExpression = node.role() == "expression";
- const bool isDeclaration = node.role() == "declaration";
-
- // Unfortunately, the exact position of a specific token is usually not
- // recorded in the AST, so if we need that, we have to search for it textually.
- // In corner cases, this might get sabotaged by e.g. comments, in which case we give up.
- const auto posForNodeStart = [doc](const AstNode &node) {
- return Utils::Text::positionInText(doc, node.range().start().line() + 1,
- node.range().start().character() + 1);
- };
- const auto posForNodeEnd = [doc](const AstNode &node) {
- return Utils::Text::positionInText(doc, node.range().end().line() + 1,
- node.range().end().character() + 1);
- };
- const int nodeStartPos = posForNodeStart(node);
- const int nodeEndPos = posForNodeEnd(node);
-
- // Match question mark and colon in ternary operators.
- if (isExpression && node.kind() == "ConditionalOperator") {
- if (children.size() != 3)
- continue;
-
- // The question mark is between sub-expressions 1 and 2, the colon is between
- // sub-expressions 2 and 3.
- const int searchStartPosQuestionMark = posForNodeEnd(children.first());
- const int searchEndPosQuestionMark = posForNodeStart(children.at(1));
- QStringView content = QStringView(docContent).mid(searchStartPosQuestionMark,
- searchEndPosQuestionMark - searchStartPosQuestionMark);
- const int questionMarkPos = onlyIndexOf(content, QStringView(QStringLiteral("?")));
- if (questionMarkPos == -1)
- continue;
- const int searchStartPosColon = posForNodeEnd(children.at(1));
- const int searchEndPosColon = posForNodeStart(children.at(2));
- content = QStringView(docContent).mid(searchStartPosColon,
- searchEndPosColon - searchStartPosColon);
- const int colonPos = onlyIndexOf(content, QStringView(QStringLiteral(":")));
- if (colonPos == -1)
- continue;
-
- const int absQuestionMarkPos = searchStartPosQuestionMark + questionMarkPos;
- const int absColonPos = searchStartPosColon + colonPos;
- if (absQuestionMarkPos > absColonPos)
- continue;
-
- HighlightingResult result;
- result.useTextSyles = true;
- result.textStyles.mainStyle = C_PUNCTUATION;
- result.textStyles.mixinStyles.push_back(C_OPERATOR);
- Utils::Text::convertPosition(doc, absQuestionMarkPos, &result.line, &result.column);
- result.length = 1;
- result.kind = CppEditor::SemanticHighlighter::TernaryIf;
- insert(result);
- Utils::Text::convertPosition(doc, absColonPos, &result.line, &result.column);
- result.kind = CppEditor::SemanticHighlighter::TernaryElse;
- insert(result);
- continue;
- }
-
- // The following functions are for matching the "<" and ">" brackets of template
- // declarations, specializations and instantiations.
- const auto insertAngleBracketInfo = [&docContent, doc, &insert](
- int searchStart1, int searchEnd1, int searchStart2, int searchEnd2) {
- const int openingAngleBracketPos = onlyIndexOf(
- QStringView(docContent).mid(searchStart1, searchEnd1 - searchStart1),
- QStringView(QStringLiteral("<")));
- if (openingAngleBracketPos == -1)
- return;
- const int absOpeningAngleBracketPos = searchStart1 + openingAngleBracketPos;
- if (absOpeningAngleBracketPos > searchStart2)
- searchStart2 = absOpeningAngleBracketPos + 1;
- if (searchStart2 >= searchEnd2)
- return;
- const int closingAngleBracketPos = onlyIndexOf(
- QStringView(docContent).mid(searchStart2, searchEnd2 - searchStart2),
- QStringView(QStringLiteral(">")));
- if (closingAngleBracketPos == -1)
- return;
-
- const int absClosingAngleBracketPos = searchStart2 + closingAngleBracketPos;
- if (absOpeningAngleBracketPos > absClosingAngleBracketPos)
- return;
-
- HighlightingResult result;
- result.useTextSyles = true;
- result.textStyles.mainStyle = C_PUNCTUATION;
- Utils::Text::convertPosition(doc, absOpeningAngleBracketPos,
- &result.line, &result.column);
- result.length = 1;
- result.kind = CppEditor::SemanticHighlighter::AngleBracketOpen;
- insert(result);
- Utils::Text::convertPosition(doc, absClosingAngleBracketPos,
- &result.line, &result.column);
- result.kind = CppEditor::SemanticHighlighter::AngleBracketClose;
- insert(result);
- };
-
- if (isDeclaration && (node.kind() == "FunctionTemplate"
- || node.kind() == "ClassTemplate")) {
- // The child nodes are the template parameters and and the function or class.
- // The opening angle bracket is before the first child node, the closing angle
- // bracket is before the function child node and after the last param node.
- const QString classOrFunctionKind = QLatin1String(node.kind() == "FunctionTemplate"
- ? "Function" : "CXXRecord");
- const auto functionOrClassIt = std::find_if(children.begin(), children.end(),
- [&classOrFunctionKind](const AstNode &n) {
- return n.role() == "declaration" && n.kind() == classOrFunctionKind;
- });
- if (functionOrClassIt == children.end() || functionOrClassIt == children.begin())
- continue;
- const int firstTemplateParamStartPos = posForNodeStart(children.first());
- const int lastTemplateParamEndPos = posForNodeEnd(*(functionOrClassIt - 1));
- const int functionOrClassStartPos = posForNodeStart(*functionOrClassIt);
- insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
- lastTemplateParamEndPos, functionOrClassStartPos);
- continue;
- }
-
- static const auto findTemplateParam = [](const AstNode &n) {
- return n.role() == "declaration" && (n.kind() == "TemplateTypeParm"
- || n.kind() == "NonTypeTemplateParm");
- };
-
- if (isDeclaration && node.kind() == "TypeAliasTemplate") {
- // Children are one node of type TypeAlias and the template parameters.
- // The opening angle bracket is before the first parameter and the closing
- // angle bracket is after the last parameter.
- // The TypeAlias node seems to appear first in the AST, even though lexically
- // is comes after the parameters. We don't rely on the order here.
- // Note that there is a second pair of angle brackets. That one is part of
- // a TemplateSpecialization, which is handled further below.
- const auto firstTemplateParam = std::find_if(children.begin(), children.end(),
- findTemplateParam);
- if (firstTemplateParam == children.end())
- continue;
- const auto lastTemplateParam = std::find_if(children.rbegin(), children.rend(),
- findTemplateParam);
- QTC_ASSERT(lastTemplateParam != children.rend(), continue);
- const auto typeAlias = std::find_if(children.begin(), children.end(),
- [](const AstNode &n) { return n.kind() == "TypeAlias"; });
- if (typeAlias == children.end())
- continue;
-
- const int firstTemplateParamStartPos = posForNodeStart(*firstTemplateParam);
- const int lastTemplateParamEndPos = posForNodeEnd(*lastTemplateParam);
- const int searchEndPos = posForNodeStart(*typeAlias);
- insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
- lastTemplateParamEndPos, searchEndPos);
- continue;
- }
-
- if (isDeclaration && node.kind() == "ClassTemplateSpecialization") {
- // There is one child of kind TemplateSpecialization. The first pair
- // of angle brackets comes before that.
- if (children.size() == 1) {
- const int childNodePos = posForNodeStart(children.first());
- insertAngleBracketInfo(nodeStartPos, childNodePos, nodeStartPos, childNodePos);
- }
- continue;
- }
-
- if (isDeclaration && node.kind() == "TemplateTemplateParm") {
- // The child nodes are template arguments and template parameters.
- // Arguments seem to appear before parameters in the AST, even though they
- // come after them in the source code. We don't rely on the order here.
- const auto firstTemplateParam = std::find_if(children.begin(), children.end(),
- findTemplateParam);
- if (firstTemplateParam == children.end())
- continue;
- const auto lastTemplateParam = std::find_if(children.rbegin(), children.rend(),
- findTemplateParam);
- QTC_ASSERT(lastTemplateParam != children.rend(), continue);
- const auto templateArg = std::find_if(children.begin(), children.end(),
- [](const AstNode &n) { return n.role() == "template argument"; });
-
- const int firstTemplateParamStartPos = posForNodeStart(*firstTemplateParam);
- const int lastTemplateParamEndPos = posForNodeEnd(*lastTemplateParam);
- const int searchEndPos = templateArg == children.end()
- ? nodeEndPos : posForNodeStart(*templateArg);
- insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
- lastTemplateParamEndPos, searchEndPos);
- continue;
- }
-
- // {static,dynamic,reinterpret}_cast<>().
- if (isExpression && node.kind().startsWith("CXX") && node.kind().endsWith("Cast")) {
- // First child is type, second child is expression.
- // The opening angle bracket is before the first child, the closing angle bracket
- // is between the two children.
- if (children.size() == 2) {
- insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.first()),
- posForNodeEnd(children.first()),
- posForNodeStart(children.last()));
- }
- continue;
- }
-
- if (node.kind() == "TemplateSpecialization") {
- // First comes the template type, then the template arguments.
- // The opening angle bracket is before the first template argument,
- // the closing angle bracket is after the last template argument.
- // The first child node has no range, so we start searching at the parent node.
- if (children.size() >= 2) {
- int searchStart2 = posForNodeEnd(children.last());
- int searchEnd2 = nodeEndPos;
-
- // There is a weird off-by-one error on the clang side: If there is a
- // nested template instantiation *and* there is no space between
- // the closing angle brackets, then the inner TemplateSpecialization node's range
- // will extend one character too far, covering the outer's closing angle bracket.
- // This is what we are correcting for here.
- // This issue is tracked at https://github.com/clangd/clangd/issues/871.
- if (searchStart2 == searchEnd2)
- --searchStart2;
- insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.at(1)),
- searchStart2, searchEnd2);
- }
- continue;
- }
-
- if (!isExpression && !isDeclaration)
- continue;
-
- // Operators, overloaded ones in particular.
- static const QString operatorPrefix = "operator";
- QString detail = node.detail().value_or(QString());
- const bool isCallToNew = node.kind() == "CXXNew";
- const bool isCallToDelete = node.kind() == "CXXDelete";
- if (!isCallToNew && !isCallToDelete
- && (!detail.startsWith(operatorPrefix) || detail == operatorPrefix)) {
- continue;
- }
-
- if (!isCallToNew && !isCallToDelete)
- detail.remove(0, operatorPrefix.length());
-
- HighlightingResult result;
- result.useTextSyles = true;
- const bool isConversionOp = node.kind() == "CXXConversion";
- const bool isOverloaded = !isConversionOp
- && (isDeclaration || ((!isCallToNew && !isCallToDelete)
- || node.arcanaContains("CXXMethod")));
- result.textStyles.mainStyle = isConversionOp
- ? C_PRIMITIVE_TYPE
- : isCallToNew || isCallToDelete || detail.at(0).isSpace()
- ? C_KEYWORD : C_PUNCTUATION;
- result.textStyles.mixinStyles.push_back(C_OPERATOR);
- if (isOverloaded)
- result.textStyles.mixinStyles.push_back(C_OVERLOADED_OPERATOR);
- if (isDeclaration)
- result.textStyles.mixinStyles.push_back(C_DECLARATION);
-
- const QStringView nodeText = QStringView(docContent)
- .mid(nodeStartPos, nodeEndPos - nodeStartPos);
-
- if (isCallToNew || isCallToDelete) {
- result.line = node.range().start().line() + 1;
- result.column = node.range().start().character() + 1;
- result.length = isCallToNew ? 3 : 6;
- insert(result);
- if (node.arcanaContains("array")) {
- const int openingBracketOffset = nodeText.indexOf('[');
- if (openingBracketOffset == -1)
- continue;
- const int closingBracketOffset = nodeText.lastIndexOf(']');
- if (closingBracketOffset == -1 || closingBracketOffset < openingBracketOffset)
- continue;
-
- result.textStyles.mainStyle = C_PUNCTUATION;
- result.length = 1;
- Utils::Text::convertPosition(doc,
- nodeStartPos + openingBracketOffset,
- &result.line, &result.column);
- insert(result);
- Utils::Text::convertPosition(doc,
- nodeStartPos + closingBracketOffset,
- &result.line, &result.column);
- insert(result);
- }
- continue;
- }
-
- if (isExpression && (detail == QLatin1String("()") || detail == QLatin1String("[]"))) {
- result.line = node.range().start().line() + 1;
- result.column = node.range().start().character() + 1;
- result.length = 1;
- insert(result);
- result.line = node.range().end().line() + 1;
- result.column = node.range().end().character();
- insert(result);
- continue;
- }
-
- const int opStringLen = detail.at(0).isSpace() ? detail.length() - 1 : detail.length();
-
- // The simple case: Call to operator+, +=, * etc.
- if (nodeEndPos - nodeStartPos == opStringLen) {
- setFromRange(result, node.range());
- insert(result);
- continue;
- }
-
- const int prefixOffset = nodeText.indexOf(operatorPrefix);
- if (prefixOffset == -1)
- continue;
-
- const bool isArray = detail == "[]";
- const bool isCall = detail == "()";
- const bool isArrayNew = detail == " new[]";
- const bool isArrayDelete = detail == " delete[]";
- const QStringView searchTerm = isArray || isCall
- ? QStringView(detail).chopped(1) : isArrayNew || isArrayDelete
- ? QStringView(detail).chopped(2) : detail;
- const int opStringOffset = nodeText.indexOf(searchTerm, prefixOffset
- + operatorPrefix.length());
- if (opStringOffset == -1 || nodeText.indexOf(operatorPrefix, opStringOffset) != -1)
- continue;
-
- const int opStringOffsetInDoc = nodeStartPos + opStringOffset
- + detail.length() - opStringLen;
- Utils::Text::convertPosition(doc, opStringOffsetInDoc, &result.line, &result.column);
- result.length = opStringLen;
- if (isArray || isCall)
- result.length = 1;
- else if (isArrayNew || isArrayDelete)
- result.length -= 2;
- if (!isArray && !isCall)
- insert(result);
- if (!isArray && !isCall && !isArrayNew && !isArrayDelete)
- continue;
+public:
+ ExtraHighlightingResultsCollector(QFutureInterface<HighlightingResult> &future,
+ HighlightingResults &results, const AstNode &ast,
+ const QTextDocument *doc, const QString &docContent);
- result.textStyles.mainStyle = C_PUNCTUATION;
- result.length = 1;
- const int openingParenOffset = nodeText.indexOf(
- isCall ? '(' : '[', prefixOffset + operatorPrefix.length());
- if (openingParenOffset == -1)
- continue;
- const int closingParenOffset = nodeText.indexOf(isCall ? ')' : ']', openingParenOffset + 1);
- if (closingParenOffset == -1 || closingParenOffset < openingParenOffset)
- continue;
- Utils::Text::convertPosition(doc, nodeStartPos + openingParenOffset,
- &result.line, &result.column);
- insert(result);
- Utils::Text::convertPosition(doc, nodeStartPos + closingParenOffset,
- &result.line, &result.column);
- insert(result);
- }
-}
+ void collect();
+private:
+ static bool lessThan(const HighlightingResult &r1, const HighlightingResult &r2);
+ static int onlyIndexOf(const QStringView &text, const QStringView &subString, int from = 0);
+ int posForNodeStart(const AstNode &node) const;
+ int posForNodeEnd(const AstNode &node) const;
+ void insertResult(const HighlightingResult &result);
+ void insertAngleBracketInfo(int searchStart1, int searchEnd1, int searchStart2, int searchEnd2);
+ void setResultPosFromRange(HighlightingResult &result, const Range &range);
+ void collectFromNode(const AstNode &node);
+ void visitNode(const AstNode&node);
+
+ QFutureInterface<HighlightingResult> &m_future;
+ HighlightingResults &m_results;
+ const AstNode &m_ast;
+ const QTextDocument * const m_doc;
+ const QString &m_docContent;
+};
// clangd reports also the #ifs, #elses and #endifs around the disabled code as disabled,
// and not even in a consistent manner. We don't want this, so we have to clean up here.
// But note that we require this behavior, as otherwise we would not be able to grey out
// e.g. empty lines after an #fdef, due to the lack of symbols.
-static QList<BlockRange> cleanupDisabledCode(HighlightingResults &results, QTextDocument *doc,
+static QList<BlockRange> cleanupDisabledCode(HighlightingResults &results, const QTextDocument *doc,
const QString &docContent)
{
QList<BlockRange> ifdefedOutRanges;
@@ -2513,12 +2215,13 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
const QPointer<TextEditorWidget> &widget,
int docRevision, const QVersionNumber &clangdVersion)
{
+ ThreadedSubtaskTimer t("highlighting");
if (future.isCanceled()) {
future.reportFinished();
return;
}
- QTextDocument doc(docContents);
+ const QTextDocument doc(docContents);
const auto isOutputParameter = [&ast](const ExpandedSemanticToken &token) {
if (token.modifiers.contains("usedAsMutableReference"))
return true;
@@ -2545,8 +2248,8 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
return false;
};
- const auto toResult = [&ast, &isOutputParameter, &clangdVersion]
- (const ExpandedSemanticToken &token) {
+ const std::function<HighlightingResult(const ExpandedSemanticToken &)> toResult
+ = [&ast, &isOutputParameter, &clangdVersion](const ExpandedSemanticToken &token) {
TextStyles styles;
if (token.type == "variable") {
if (token.modifiers.contains("functionScope")) {
@@ -2629,13 +2332,13 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
return HighlightingResult(token.line, token.column, token.length, styles);
};
- HighlightingResults results = Utils::transform(tokens, toResult);
+ auto results = QtConcurrent::blockingMapped<HighlightingResults>(tokens, toResult);
const QList<BlockRange> ifdefedOutBlocks = cleanupDisabledCode(results, &doc, docContents);
QMetaObject::invokeMethod(widget, [widget, ifdefedOutBlocks, docRevision] {
if (widget && widget->textDocument()->document()->revision() == docRevision)
widget->setIfdefedOutBlocks(ifdefedOutBlocks);
}, Qt::QueuedConnection);
- collectExtraResults(future, results, ast, &doc, docContents);
+ ExtraHighlightingResultsCollector(future, results, ast, &doc, docContents).collect();
if (!future.isCanceled()) {
qCDebug(clangdLog) << "reporting" << results.size() << "highlighting results";
future.reportResults(QVector<HighlightingResult>(results.cbegin(),
@@ -2659,12 +2362,14 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
void ClangdClient::Private::handleSemanticTokens(TextDocument *doc,
const QList<ExpandedSemanticToken> &tokens)
{
+ SubtaskTimer t(highlightingTimer);
qCDebug(clangdLog()) << "handling LSP tokens" << doc->filePath() << tokens.size();
for (const ExpandedSemanticToken &t : tokens)
qCDebug(clangdLogHighlight()) << '\t' << t.line << t.column << t.length << t.type
<< t.modifiers;
const auto astHandler = [this, tokens, doc](const AstNode &ast, const MessageId &) {
+ FinalizingSubtaskTimer t(highlightingTimer);
if (!q->documentOpen(doc))
return;
if (clangdLogAst().isDebugEnabled())
@@ -2692,12 +2397,13 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc,
auto it = highlighters.find(doc);
if (it == highlighters.end()) {
- it = highlighters.emplace(doc, doc).first;
+ it = highlighters.insert(std::make_pair(doc, new CppEditor::SemanticHighlighter(doc)))
+ .first;
} else {
- it->second.updateFormatMapFromFontSettings();
+ it->second->updateFormatMapFromFontSettings();
}
- it->second.setHighlightingRunner(runner);
- it->second.run();
+ it->second->setHighlightingRunner(runner);
+ it->second->run();
};
getAndHandleAst(doc, astHandler, AstCallbackMode::SyncIfPossible);
}
@@ -3014,26 +2720,30 @@ void ClangdCompletionItem::apply(TextDocumentManipulatorInterface &manipulator,
MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc,
const AstHandler &astHandler,
- AstCallbackMode callbackMode)
+ AstCallbackMode callbackMode, const Range &range)
{
const auto textDocPtr = Utils::get_if<const TextDocument *>(&doc);
const TextDocument * const textDoc = textDocPtr ? *textDocPtr : nullptr;
const Utils::FilePath filePath = textDoc ? textDoc->filePath()
: Utils::get<Utils::FilePath>(doc);
- // If the document's AST is in the cache and is up to date, call the handler.
- if (const auto ast = textDoc ? astCache.get(textDoc) : externalAstCache.get(filePath)) {
- qCDebug(clangdLog) << "using AST from cache";
- switch (callbackMode) {
- case AstCallbackMode::SyncIfPossible:
- astHandler(*ast, {});
- break;
- case AstCallbackMode::AlwaysAsync:
- QMetaObject::invokeMethod(q, [ast, astHandler] { astHandler(*ast, {}); },
+ // If the entire AST is requested and the document's AST is in the cache and it is up to date,
+ // call the handler.
+ const bool fullAstRequested = !range.isValid();
+ if (fullAstRequested) {
+ if (const auto ast = textDoc ? astCache.get(textDoc) : externalAstCache.get(filePath)) {
+ qCDebug(clangdLog) << "using AST from cache";
+ switch (callbackMode) {
+ case AstCallbackMode::SyncIfPossible:
+ astHandler(*ast, {});
+ break;
+ case AstCallbackMode::AlwaysAsync:
+ QMetaObject::invokeMethod(q, [ast, astHandler] { astHandler(*ast, {}); },
Qt::QueuedConnection);
- break;
+ break;
+ }
+ return {};
}
- return {};
}
// Otherwise retrieve the AST from clangd.
@@ -3041,7 +2751,6 @@ MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc,
class AstParams : public JsonObject
{
public:
- AstParams() {}
AstParams(const TextDocumentIdentifier &document, const Range &range = {})
{
setTextDocument(document);
@@ -3071,19 +2780,22 @@ MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc,
explicit AstRequest(const AstParams &params) : Request("textDocument/ast", params) {}
};
- AstRequest request(AstParams(TextDocumentIdentifier(DocumentUri::fromFilePath(filePath))));
+ AstRequest request(AstParams(TextDocumentIdentifier(DocumentUri::fromFilePath(filePath)),
+ range));
request.setResponseCallback([this, filePath, guardedTextDoc = QPointer(textDoc), astHandler,
- docRev = textDoc ? getRevision(textDoc) : -1,
+ fullAstRequested, docRev = textDoc ? getRevision(textDoc) : -1,
fileRev = getRevision(filePath), reqId = request.id()]
(AstRequest::Response response) {
qCDebug(clangdLog) << "retrieved AST from clangd";
const auto result = response.result();
const AstNode ast = result ? *result : AstNode();
- if (guardedTextDoc) {
- if (docRev == getRevision(guardedTextDoc))
- astCache.insert(guardedTextDoc, ast);
- } else if (fileRev == getRevision(filePath) && !q->documentForFilePath(filePath)) {
- externalAstCache.insert(filePath, ast);
+ if (fullAstRequested) {
+ if (guardedTextDoc) {
+ if (docRev == getRevision(guardedTextDoc))
+ astCache.insert(guardedTextDoc, ast);
+ } else if (fileRev == getRevision(filePath) && !q->documentForFilePath(filePath)) {
+ externalAstCache.insert(filePath, ast);
+ }
}
astHandler(ast, reqId);
});
@@ -3092,6 +2804,465 @@ MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc,
return request.id();
}
+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)
+{
+
+}
+
+void ExtraHighlightingResultsCollector::collect()
+{
+ if (!m_ast.isValid())
+ return;
+ visitNode(m_ast);
+}
+
+bool ExtraHighlightingResultsCollector::lessThan(const HighlightingResult &r1,
+ const HighlightingResult &r2)
+{
+ return r1.line < r2.line || (r1.line == r2.line && r1.column < r2.column)
+ || (r1.line == r2.line && r1.column == r2.column && r1.length < r2.length);
+}
+
+int ExtraHighlightingResultsCollector::onlyIndexOf(const QStringView &text,
+ const QStringView &subString, int from)
+{
+ const int firstIndex = text.indexOf(subString, from);
+ if (firstIndex == -1)
+ return -1;
+ const int nextIndex = text.indexOf(subString, firstIndex + 1);
+
+ // The second condion deals with the off-by-one error in TemplateSpecialization nodes;
+ // see collectFromNode().
+ return nextIndex == -1 || nextIndex == firstIndex + 1 ? firstIndex : -1;
+}
+
+// Unfortunately, the exact position of a specific token is usually not
+// recorded in the AST, so if we need that, we have to search for it textually.
+// In corner cases, this might get sabotaged by e.g. comments, in which case we give up.
+int ExtraHighlightingResultsCollector::posForNodeStart(const AstNode &node) const
+{
+ return Utils::Text::positionInText(m_doc, node.range().start().line() + 1,
+ node.range().start().character() + 1);
+}
+
+int ExtraHighlightingResultsCollector::posForNodeEnd(const AstNode &node) const
+{
+ return Utils::Text::positionInText(m_doc, node.range().end().line() + 1,
+ node.range().end().character() + 1);
+}
+
+void ExtraHighlightingResultsCollector::insertResult(const HighlightingResult &result)
+{
+ if (!result.isValid()) // Some nodes don't have a range.
+ return;
+ const auto it = std::lower_bound(m_results.begin(), m_results.end(), result, lessThan);
+ if (it == m_results.end() || *it != result) {
+ qCDebug(clangdLogHighlight) << "adding additional highlighting result"
+ << result.line << result.column << result.length;
+ m_results.insert(it, result);
+ return;
+ }
+
+ // This is for conversion operators, whose type part is only reported as a type by clangd.
+ if ((it->textStyles.mainStyle == C_TYPE
+ || it->textStyles.mainStyle == C_PRIMITIVE_TYPE)
+ && !result.textStyles.mixinStyles.empty()
+ && result.textStyles.mixinStyles.at(0) == C_OPERATOR) {
+ it->textStyles.mixinStyles = result.textStyles.mixinStyles;
+ }
+}
+
+// For matching the "<" and ">" brackets of template declarations, specializations
+// and instantiations.
+void ExtraHighlightingResultsCollector::insertAngleBracketInfo(int searchStart1, int searchEnd1,
+ int searchStart2, int searchEnd2)
+{
+ const int openingAngleBracketPos = onlyIndexOf(
+ QStringView(m_docContent).mid(searchStart1, searchEnd1 - searchStart1),
+ QStringView(QStringLiteral("<")));
+ if (openingAngleBracketPos == -1)
+ return;
+ const int absOpeningAngleBracketPos = searchStart1 + openingAngleBracketPos;
+ if (absOpeningAngleBracketPos > searchStart2)
+ searchStart2 = absOpeningAngleBracketPos + 1;
+ if (searchStart2 >= searchEnd2)
+ return;
+ const int closingAngleBracketPos = onlyIndexOf(
+ QStringView(m_docContent).mid(searchStart2, searchEnd2 - searchStart2),
+ QStringView(QStringLiteral(">")));
+ if (closingAngleBracketPos == -1)
+ return;
+
+ const int absClosingAngleBracketPos = searchStart2 + closingAngleBracketPos;
+ if (absOpeningAngleBracketPos > absClosingAngleBracketPos)
+ return;
+
+ HighlightingResult result;
+ result.useTextSyles = true;
+ result.textStyles.mainStyle = C_PUNCTUATION;
+ Utils::Text::convertPosition(m_doc, absOpeningAngleBracketPos, &result.line, &result.column);
+ result.length = 1;
+ result.kind = CppEditor::SemanticHighlighter::AngleBracketOpen;
+ insertResult(result);
+ Utils::Text::convertPosition(m_doc, absClosingAngleBracketPos, &result.line, &result.column);
+ result.kind = CppEditor::SemanticHighlighter::AngleBracketClose;
+ insertResult(result);
+}
+
+void ExtraHighlightingResultsCollector::setResultPosFromRange(HighlightingResult &result,
+ const Range &range)
+{
+ if (!range.isValid())
+ return;
+ const Position startPos = range.start();
+ const Position endPos = range.end();
+ result.line = startPos.line() + 1;
+ result.column = startPos.character() + 1;
+ result.length = endPos.toPositionInDocument(m_doc) - startPos.toPositionInDocument(m_doc);
+}
+
+void ExtraHighlightingResultsCollector::collectFromNode(const AstNode &node)
+{
+ if (node.kind().endsWith("Literal")) {
+ HighlightingResult result;
+ result.useTextSyles = true;
+ const bool isStringLike = node.kind().startsWith("String")
+ || node.kind().startsWith("Character");
+ result.textStyles.mainStyle = isStringLike ? C_STRING : C_NUMBER;
+ setResultPosFromRange(result, node.range());
+ insertResult(result);
+ return;
+ }
+ if (node.role() == "type" && node.kind() == "Builtin") {
+ HighlightingResult result;
+ result.useTextSyles = true;
+ result.textStyles.mainStyle = C_PRIMITIVE_TYPE;
+ setResultPosFromRange(result, node.range());
+ insertResult(result);
+ return;
+ }
+ if (node.role() == "attribute" && (node.kind() == "Override" || node.kind() == "Final")) {
+ HighlightingResult result;
+ result.useTextSyles = true;
+ result.textStyles.mainStyle = C_KEYWORD;
+ setResultPosFromRange(result, node.range());
+ insertResult(result);
+ return;
+ }
+
+ const bool isExpression = node.role() == "expression";
+ const bool isDeclaration = node.role() == "declaration";
+
+ const int nodeStartPos = posForNodeStart(node);
+ const int nodeEndPos = posForNodeEnd(node);
+ const QList<AstNode> children = node.children().value_or(QList<AstNode>());
+
+ // Match question mark and colon in ternary operators.
+ if (isExpression && node.kind() == "ConditionalOperator") {
+ if (children.size() != 3)
+ return;
+
+ // The question mark is between sub-expressions 1 and 2, the colon is between
+ // 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);
+ 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);
+ const int colonPos = onlyIndexOf(content, QStringView(QStringLiteral(":")));
+ if (colonPos == -1)
+ return;
+
+ const int absQuestionMarkPos = searchStartPosQuestionMark + questionMarkPos;
+ const int absColonPos = searchStartPosColon + colonPos;
+ if (absQuestionMarkPos > absColonPos)
+ return;
+
+ HighlightingResult result;
+ result.useTextSyles = true;
+ result.textStyles.mainStyle = C_PUNCTUATION;
+ result.textStyles.mixinStyles.push_back(C_OPERATOR);
+ Utils::Text::convertPosition(m_doc, absQuestionMarkPos, &result.line, &result.column);
+ result.length = 1;
+ result.kind = CppEditor::SemanticHighlighter::TernaryIf;
+ insertResult(result);
+ Utils::Text::convertPosition(m_doc, absColonPos, &result.line, &result.column);
+ result.kind = CppEditor::SemanticHighlighter::TernaryElse;
+ insertResult(result);
+ return;
+ }
+
+ if (isDeclaration && (node.kind() == "FunctionTemplate"
+ || node.kind() == "ClassTemplate")) {
+ // The child nodes are the template parameters and and the function or class.
+ // The opening angle bracket is before the first child node, the closing angle
+ // bracket is before the function child node and after the last param node.
+ const QString classOrFunctionKind = QLatin1String(node.kind() == "FunctionTemplate"
+ ? "Function" : "CXXRecord");
+ const auto functionOrClassIt = std::find_if(children.begin(), children.end(),
+ [&classOrFunctionKind](const AstNode &n) {
+ return n.role() == "declaration" && n.kind() == classOrFunctionKind;
+ });
+ if (functionOrClassIt == children.end() || functionOrClassIt == children.begin())
+ return;
+ const int firstTemplateParamStartPos = posForNodeStart(children.first());
+ const int lastTemplateParamEndPos = posForNodeEnd(*(functionOrClassIt - 1));
+ const int functionOrClassStartPos = posForNodeStart(*functionOrClassIt);
+ insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
+ lastTemplateParamEndPos, functionOrClassStartPos);
+ return;
+ }
+
+ const auto isTemplateParamDecl = [](const AstNode &node) {
+ return node.isTemplateParameterDeclaration();
+ };
+ if (isDeclaration && node.kind() == "TypeAliasTemplate") {
+ // Children are one node of type TypeAlias and the template parameters.
+ // The opening angle bracket is before the first parameter and the closing
+ // angle bracket is after the last parameter.
+ // The TypeAlias node seems to appear first in the AST, even though lexically
+ // is comes after the parameters. We don't rely on the order here.
+ // Note that there is a second pair of angle brackets. That one is part of
+ // a TemplateSpecialization, which is handled further below.
+ const auto firstTemplateParam = std::find_if(children.begin(), children.end(),
+ isTemplateParamDecl);
+ if (firstTemplateParam == children.end())
+ return;
+ const auto lastTemplateParam = std::find_if(children.rbegin(), children.rend(),
+ isTemplateParamDecl);
+ QTC_ASSERT(lastTemplateParam != children.rend(), return);
+ const auto typeAlias = std::find_if(children.begin(), children.end(),
+ [](const AstNode &n) { return n.kind() == "TypeAlias"; });
+ if (typeAlias == children.end())
+ return;
+
+ const int firstTemplateParamStartPos = posForNodeStart(*firstTemplateParam);
+ const int lastTemplateParamEndPos = posForNodeEnd(*lastTemplateParam);
+ const int searchEndPos = posForNodeStart(*typeAlias);
+ insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
+ lastTemplateParamEndPos, searchEndPos);
+ return;
+ }
+
+ if (isDeclaration && node.kind() == "ClassTemplateSpecialization") {
+ // There is one child of kind TemplateSpecialization. The first pair
+ // of angle brackets comes before that.
+ if (children.size() == 1) {
+ const int childNodePos = posForNodeStart(children.first());
+ insertAngleBracketInfo(nodeStartPos, childNodePos, nodeStartPos, childNodePos);
+ }
+ return;
+ }
+
+ if (isDeclaration && node.kind() == "TemplateTemplateParm") {
+ // The child nodes are template arguments and template parameters.
+ // Arguments seem to appear before parameters in the AST, even though they
+ // come after them in the source code. We don't rely on the order here.
+ const auto firstTemplateParam = std::find_if(children.begin(), children.end(),
+ isTemplateParamDecl);
+ if (firstTemplateParam == children.end())
+ return;
+ const auto lastTemplateParam = std::find_if(children.rbegin(), children.rend(),
+ isTemplateParamDecl);
+ QTC_ASSERT(lastTemplateParam != children.rend(), return);
+ const auto templateArg = std::find_if(children.begin(), children.end(),
+ [](const AstNode &n) { return n.role() == "template argument"; });
+
+ const int firstTemplateParamStartPos = posForNodeStart(*firstTemplateParam);
+ const int lastTemplateParamEndPos = posForNodeEnd(*lastTemplateParam);
+ const int searchEndPos = templateArg == children.end()
+ ? nodeEndPos : posForNodeStart(*templateArg);
+ insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
+ lastTemplateParamEndPos, searchEndPos);
+ return;
+ }
+
+ // {static,dynamic,reinterpret}_cast<>().
+ if (isExpression && node.kind().startsWith("CXX") && node.kind().endsWith("Cast")) {
+ // First child is type, second child is expression.
+ // The opening angle bracket is before the first child, the closing angle bracket
+ // is between the two children.
+ if (children.size() == 2) {
+ insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.first()),
+ posForNodeEnd(children.first()),
+ posForNodeStart(children.last()));
+ }
+ return;
+ }
+
+ if (node.kind() == "TemplateSpecialization") {
+ // First comes the template type, then the template arguments.
+ // The opening angle bracket is before the first template argument,
+ // the closing angle bracket is after the last template argument.
+ // The first child node has no range, so we start searching at the parent node.
+ if (children.size() >= 2) {
+ int searchStart2 = posForNodeEnd(children.last());
+ int searchEnd2 = nodeEndPos;
+
+ // There is a weird off-by-one error on the clang side: If there is a
+ // nested template instantiation *and* there is no space between
+ // the closing angle brackets, then the inner TemplateSpecialization node's range
+ // will extend one character too far, covering the outer's closing angle bracket.
+ // This is what we are correcting for here.
+ // This issue is tracked at https://github.com/clangd/clangd/issues/871.
+ if (searchStart2 == searchEnd2)
+ --searchStart2;
+ insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.at(1)),
+ searchStart2, searchEnd2);
+ }
+ return;
+ }
+
+ if (!isExpression && !isDeclaration)
+ return;
+
+ // Operators, overloaded ones in particular.
+ static const QString operatorPrefix = "operator";
+ QString detail = node.detail().value_or(QString());
+ const bool isCallToNew = node.kind() == "CXXNew";
+ const bool isCallToDelete = node.kind() == "CXXDelete";
+ if (!isCallToNew && !isCallToDelete
+ && (!detail.startsWith(operatorPrefix) || detail == operatorPrefix)) {
+ return;
+ }
+
+ if (!isCallToNew && !isCallToDelete)
+ detail.remove(0, operatorPrefix.length());
+
+ HighlightingResult result;
+ result.useTextSyles = true;
+ const bool isConversionOp = node.kind() == "CXXConversion";
+ const bool isOverloaded = !isConversionOp
+ && (isDeclaration || ((!isCallToNew && !isCallToDelete)
+ || node.arcanaContains("CXXMethod")));
+ result.textStyles.mainStyle = isConversionOp
+ ? C_PRIMITIVE_TYPE
+ : isCallToNew || isCallToDelete || detail.at(0).isSpace()
+ ? C_KEYWORD : C_PUNCTUATION;
+ result.textStyles.mixinStyles.push_back(C_OPERATOR);
+ if (isOverloaded)
+ result.textStyles.mixinStyles.push_back(C_OVERLOADED_OPERATOR);
+ if (isDeclaration)
+ result.textStyles.mixinStyles.push_back(C_DECLARATION);
+
+ const QStringView nodeText = QStringView(m_docContent)
+ .mid(nodeStartPos, nodeEndPos - nodeStartPos);
+
+ if (isCallToNew || isCallToDelete) {
+ result.line = node.range().start().line() + 1;
+ result.column = node.range().start().character() + 1;
+ result.length = isCallToNew ? 3 : 6;
+ insertResult(result);
+ if (node.arcanaContains("array")) {
+ const int openingBracketOffset = nodeText.indexOf('[');
+ if (openingBracketOffset == -1)
+ return;
+ const int closingBracketOffset = nodeText.lastIndexOf(']');
+ if (closingBracketOffset == -1 || closingBracketOffset < openingBracketOffset)
+ return;
+
+ result.textStyles.mainStyle = C_PUNCTUATION;
+ result.length = 1;
+ Utils::Text::convertPosition(m_doc,
+ nodeStartPos + openingBracketOffset,
+ &result.line, &result.column);
+ insertResult(result);
+ Utils::Text::convertPosition(m_doc,
+ nodeStartPos + closingBracketOffset,
+ &result.line, &result.column);
+ insertResult(result);
+ }
+ return;
+ }
+
+ if (isExpression && (detail == QLatin1String("()") || detail == QLatin1String("[]"))) {
+ result.line = node.range().start().line() + 1;
+ result.column = node.range().start().character() + 1;
+ result.length = 1;
+ insertResult(result);
+ result.line = node.range().end().line() + 1;
+ result.column = node.range().end().character();
+ insertResult(result);
+ return;
+ }
+
+ const int opStringLen = detail.at(0).isSpace() ? detail.length() - 1 : detail.length();
+
+ // The simple case: Call to operator+, +=, * etc.
+ if (nodeEndPos - nodeStartPos == opStringLen) {
+ setResultPosFromRange(result, node.range());
+ insertResult(result);
+ return;
+ }
+
+ const int prefixOffset = nodeText.indexOf(operatorPrefix);
+ if (prefixOffset == -1)
+ return;
+
+ const bool isArray = detail == "[]";
+ const bool isCall = detail == "()";
+ const bool isArrayNew = detail == " new[]";
+ const bool isArrayDelete = detail == " delete[]";
+ const QStringView searchTerm = isArray || isCall
+ ? QStringView(detail).chopped(1) : isArrayNew || isArrayDelete
+ ? QStringView(detail).chopped(2) : detail;
+ const int opStringOffset = nodeText.indexOf(searchTerm, prefixOffset
+ + operatorPrefix.length());
+ if (opStringOffset == -1 || nodeText.indexOf(operatorPrefix, opStringOffset) != -1)
+ return;
+
+ const int opStringOffsetInDoc = nodeStartPos + opStringOffset
+ + detail.length() - opStringLen;
+ Utils::Text::convertPosition(m_doc, opStringOffsetInDoc, &result.line, &result.column);
+ result.length = opStringLen;
+ if (isArray || isCall)
+ result.length = 1;
+ else if (isArrayNew || isArrayDelete)
+ result.length -= 2;
+ if (!isArray && !isCall)
+ insertResult(result);
+ if (!isArray && !isCall && !isArrayNew && !isArrayDelete)
+ return;
+
+ result.textStyles.mainStyle = C_PUNCTUATION;
+ result.length = 1;
+ const int openingParenOffset = nodeText.indexOf(
+ isCall ? '(' : '[', prefixOffset + operatorPrefix.length());
+ if (openingParenOffset == -1)
+ return;
+ const int closingParenOffset = nodeText.indexOf(isCall ? ')' : ']', openingParenOffset + 1);
+ if (closingParenOffset == -1 || closingParenOffset < openingParenOffset)
+ return;
+ Utils::Text::convertPosition(m_doc, nodeStartPos + openingParenOffset,
+ &result.line, &result.column);
+ insertResult(result);
+ Utils::Text::convertPosition(m_doc, nodeStartPos + closingParenOffset,
+ &result.line, &result.column);
+ insertResult(result);
+}
+
+void ExtraHighlightingResultsCollector::visitNode(const AstNode &node)
+{
+ if (m_future.isCanceled())
+ return;
+ collectFromNode(node);
+ const auto children = node.children();
+ if (!children)
+ return;
+ for (const AstNode &childNode : *children)
+ visitNode(childNode);
+}
+
} // namespace Internal
} // namespace ClangCodeModel
diff --git a/src/plugins/clangformat/CMakeLists.txt b/src/plugins/clangformat/CMakeLists.txt
index eb6e0687e6..5db5f8111a 100644
--- a/src/plugins/clangformat/CMakeLists.txt
+++ b/src/plugins/clangformat/CMakeLists.txt
@@ -8,6 +8,7 @@ add_qtc_plugin(ClangFormat
clangformatchecks.ui
clangformatconfigwidget.cpp clangformatconfigwidget.h clangformatconfigwidget.ui
clangformatconstants.h
+ clangformatfile.cpp clangformatfile.h
clangformatindenter.cpp clangformatindenter.h
clangformatplugin.cpp clangformatplugin.h
clangformatsettings.cpp clangformatsettings.h
diff --git a/src/plugins/clangformat/clangformat.pro b/src/plugins/clangformat/clangformat.pro
index 4d751db2ba..da7645613c 100644
--- a/src/plugins/clangformat/clangformat.pro
+++ b/src/plugins/clangformat/clangformat.pro
@@ -15,6 +15,7 @@ unix:!macos:QMAKE_LFLAGS += -Wl,--exclude-libs,ALL
SOURCES += \
clangformatconfigwidget.cpp \
+ clangformatfile.cpp \
clangformatindenter.cpp \
clangformatplugin.cpp \
clangformatsettings.cpp \
@@ -22,6 +23,7 @@ SOURCES += \
HEADERS += \
clangformatconfigwidget.h \
+ clangformatfile.h \
clangformatindenter.h \
clangformatplugin.h \
clangformatsettings.h \
diff --git a/src/plugins/clangformat/clangformat.qbs b/src/plugins/clangformat/clangformat.qbs
index ef8ae44781..fcd77b7c1b 100644
--- a/src/plugins/clangformat/clangformat.qbs
+++ b/src/plugins/clangformat/clangformat.qbs
@@ -33,6 +33,8 @@ QtcPlugin {
"clangformatconfigwidget.h",
"clangformatconfigwidget.ui",
"clangformatconstants.h",
+ "clangformatfile.cpp",
+ "clangformatfile.h",
"clangformatindenter.cpp",
"clangformatindenter.h",
"clangformatplugin.cpp",
diff --git a/src/plugins/clangformat/clangformatconfigwidget.cpp b/src/plugins/clangformat/clangformatconfigwidget.cpp
index f0cc5d9700..0dc6d4c54f 100644
--- a/src/plugins/clangformat/clangformatconfigwidget.cpp
+++ b/src/plugins/clangformat/clangformatconfigwidget.cpp
@@ -27,6 +27,7 @@
#include "clangformatconstants.h"
#include "clangformatindenter.h"
+#include "clangformatfile.h"
#include "clangformatsettings.h"
#include "clangformatutils.h"
#include "ui_clangformatchecks.h"
@@ -121,6 +122,12 @@ ClangFormatConfigWidget::ClangFormatConfigWidget(ProjectExplorer::Project *proje
{
m_ui->setupUi(this);
+ Utils::FilePath filePath = Core::ICore::userResourcePath();
+ if (m_project)
+ filePath = filePath / "clang-format/" / currentProjectUniqueId();
+ filePath = filePath / QLatin1String(Constants::SETTINGS_FILE_NAME);
+ m_config = std::make_unique<ClangFormatFile>(filePath);
+
initChecksAndPreview();
if (m_project) {
@@ -147,6 +154,8 @@ ClangFormatConfigWidget::ClangFormatConfigWidget(ProjectExplorer::Project *proje
connectChecks();
}
+ClangFormatConfigWidget::~ClangFormatConfigWidget() = default;
+
void ClangFormatConfigWidget::initChecksAndPreview()
{
m_checksScrollArea = new QScrollArea();
@@ -191,7 +200,7 @@ void ClangFormatConfigWidget::connectChecks()
continue;
}
- auto button = qobject_cast<QPushButton *>(child);
+ const auto button = qobject_cast<QPushButton *>(child);
if (button != nullptr)
connect(button, &QPushButton::clicked, this, &ClangFormatConfigWidget::onTableChanged);
}
@@ -202,15 +211,7 @@ void ClangFormatConfigWidget::onTableChanged()
if (m_disableTableUpdate)
return;
- const std::string newConfig = tableToString(sender());
- if (newConfig.empty())
- return;
- const std::string oldConfig = m_project ? currentProjectConfigText()
- : currentGlobalConfigText();
- saveConfig(newConfig);
- fillTable();
- updatePreview();
- saveConfig(oldConfig);
+ saveChanges(sender());
}
void ClangFormatConfigWidget::hideGlobalCheckboxes()
@@ -379,16 +380,17 @@ void ClangFormatConfigWidget::fillTable()
}
}
-std::string ClangFormatConfigWidget::tableToString(QObject *sender)
+void ClangFormatConfigWidget::saveChanges(QObject *sender)
{
- std::stringstream content;
- content << "---";
-
if (sender->objectName() == "BasedOnStyle") {
- auto *basedOnStyle = m_checksWidget->findChild<QComboBox *>("BasedOnStyle");
- content << "\nBasedOnStyle: " << basedOnStyle->currentText().toStdString() << '\n';
+ const auto *basedOnStyle = m_checksWidget->findChild<QComboBox *>("BasedOnStyle");
+ m_config->setBasedOnStyle(basedOnStyle->currentText());
} else {
+ QList<ClangFormatFile::Field> fields;
+
for (QObject *child : m_checksWidget->children()) {
+ if (child->objectName() == "BasedOnStyle")
+ continue;
auto *label = qobject_cast<QLabel *>(child);
if (!label)
continue;
@@ -396,7 +398,7 @@ std::string ClangFormatConfigWidget::tableToString(QObject *sender)
QWidget *valueWidget = m_checksWidget->findChild<QWidget *>(label->text().trimmed());
if (!valueWidget) {
// Currently BraceWrapping only.
- content << '\n' << label->text().toStdString() << ":";
+ fields.append({label->text(), ""});
continue;
}
@@ -410,46 +412,34 @@ std::string ClangFormatConfigWidget::tableToString(QObject *sender)
if (plainText->toPlainText().trimmed().isEmpty())
continue;
- content << '\n' << label->text().toStdString() << ":";
+
+ std::stringstream content;
QStringList list = plainText->toPlainText().split('\n');
for (const QString &line : list)
content << "\n " << line.toStdString();
+
+ fields.append({label->text(), QString::fromStdString(content.str())});
} else {
- auto *comboBox = qobject_cast<QComboBox *>(valueWidget);
- std::string text;
- if (comboBox) {
- text = comboBox->currentText().toStdString();
+ QString text;
+ if (auto *comboBox = qobject_cast<QComboBox *>(valueWidget)) {
+ text = comboBox->currentText();
} else {
auto *lineEdit = qobject_cast<QLineEdit *>(valueWidget);
QTC_ASSERT(lineEdit, continue;);
- text = lineEdit->text().toStdString();
+ text = lineEdit->text();
}
- if (!text.empty() && text != "Default")
- content << '\n' << label->text().toStdString() << ": " << text;
+ if (!text.isEmpty() && text != "Default")
+ fields.append({label->text(), text});
}
}
- content << '\n';
+ m_config->changeFields(fields);
}
- std::string text = content.str();
- clang::format::FormatStyle style;
- style.Language = clang::format::FormatStyle::LK_Cpp;
- const std::error_code error = clang::format::parseConfiguration(text, &style);
- if (error.value() != static_cast<int>(clang::format::ParseError::Success)) {
- QMessageBox::warning(this,
- tr("Error in ClangFormat configuration"),
- QString::fromStdString(error.message()));
- fillTable();
- updatePreview();
- return std::string();
- }
-
- return text;
+ fillTable();
+ updatePreview();
}
-ClangFormatConfigWidget::~ClangFormatConfigWidget() = default;
-
void ClangFormatConfigWidget::apply()
{
ClangFormatSettings &settings = ClangFormatSettings::instance();
@@ -466,28 +456,7 @@ void ClangFormatConfigWidget::apply()
if (!m_checksWidget->isVisible())
return;
- const std::string config = tableToString(this);
- if (config.empty())
- return;
-
- saveConfig(config);
- fillTable();
- updatePreview();
-}
-
-void ClangFormatConfigWidget::saveConfig(const std::string &text) const
-{
- QString filePath = Core::ICore::userResourcePath().toString();
- if (m_project)
- filePath += "/clang-format/" + currentProjectUniqueId();
- filePath += "/" + QLatin1String(Constants::SETTINGS_FILE_NAME);
-
- QFile file(filePath);
- if (!file.open(QFile::WriteOnly))
- return;
-
- file.write(text.c_str());
- file.close();
+ saveChanges(this);
}
} // namespace ClangFormat
diff --git a/src/plugins/clangformat/clangformatconfigwidget.h b/src/plugins/clangformat/clangformatconfigwidget.h
index 869ddeccb7..4c261f9992 100644
--- a/src/plugins/clangformat/clangformatconfigwidget.h
+++ b/src/plugins/clangformat/clangformatconfigwidget.h
@@ -44,6 +44,7 @@ namespace Ui {
class ClangFormatConfigWidget;
class ClangFormatChecksWidget;
}
+class ClangFormatFile;
class ClangFormatConfigWidget : public TextEditor::CodeStyleEditorWidget
{
@@ -66,18 +67,17 @@ private:
void connectChecks();
void fillTable();
- std::string tableToString(QObject *sender);
+ void saveChanges(QObject *sender);
void hideGlobalCheckboxes();
void showGlobalCheckboxes();
-
- void saveConfig(const std::string &text) const;
void updatePreview();
ProjectExplorer::Project *m_project;
QWidget *m_checksWidget;
QScrollArea *m_checksScrollArea;
TextEditor::SnippetEditorWidget *m_preview;
+ std::unique_ptr<ClangFormatFile> m_config;
std::unique_ptr<Ui::ClangFormatChecksWidget> m_checks;
std::unique_ptr<Ui::ClangFormatConfigWidget> m_ui;
bool m_disableTableUpdate = false;
diff --git a/src/plugins/clangformat/clangformatfile.cpp b/src/plugins/clangformat/clangformatfile.cpp
new file mode 100644
index 0000000000..8e532ea797
--- /dev/null
+++ b/src/plugins/clangformat/clangformatfile.cpp
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** 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 "clangformatfile.h"
+#include <sstream>
+
+using namespace ClangFormat;
+
+ClangFormatFile::ClangFormatFile(Utils::FilePath filePath)
+ : m_filePath(filePath)
+{
+ if (!m_filePath.exists()) {
+ resetStyleToLLVM();
+ return;
+ }
+
+ m_style.Language = clang::format::FormatStyle::LK_Cpp;
+ const std::error_code error
+ = clang::format::parseConfiguration(m_filePath.fileContents().toStdString(), &m_style);
+ if (error.value() != static_cast<int>(clang::format::ParseError::Success)) {
+ resetStyleToLLVM();
+ }
+}
+
+clang::format::FormatStyle ClangFormatFile::format() {
+ return m_style;
+}
+
+Utils::FilePath ClangFormatFile::filePath()
+{
+ return m_filePath;
+}
+
+void ClangFormatFile::setStyle(clang::format::FormatStyle style)
+{
+ m_style = style;
+ saveNewFormat();
+}
+
+void ClangFormatFile::resetStyleToLLVM()
+{
+ m_style = clang::format::getLLVMStyle();
+ saveNewFormat();
+}
+
+void ClangFormatFile::setBasedOnStyle(QString styleName)
+{
+ changeField({"BasedOnStyle", styleName});
+ saveNewFormat();
+}
+
+QString ClangFormatFile::setStyle(QString style)
+{
+ const std::error_code error = clang::format::parseConfiguration(style.toStdString(), &m_style);
+ if (error.value() != static_cast<int>(clang::format::ParseError::Success)) {
+ return QString::fromStdString(error.message());
+ }
+
+ saveNewFormat(style.toUtf8());
+ return "";
+}
+
+QString ClangFormatFile::changeField(Field field)
+{
+ return changeFields({field});
+}
+
+QString ClangFormatFile::changeFields(QList<Field> fields)
+{
+ std::stringstream content;
+ content << "---" << "\n";
+
+ for (const auto &field : fields) {
+ content << field.first.toStdString() << ": " << field.second.toStdString() << "\n";
+ }
+
+ return setStyle(QString::fromStdString(content.str()));
+}
+
+void ClangFormatFile::saveNewFormat()
+{
+ std::string style = clang::format::configurationAsText(m_style);
+
+ // workaround: configurationAsText() add comment "# " before BasedOnStyle line
+ const int pos = style.find("# BasedOnStyle");
+ if (pos < style.size())
+ style.erase(pos, 2);
+ m_filePath.writeFileContents(QByteArray::fromStdString(style));
+}
+
+void ClangFormatFile::saveNewFormat(QByteArray style)
+{
+ m_filePath.writeFileContents(style);
+}
diff --git a/src/plugins/clangformat/clangformatfile.h b/src/plugins/clangformat/clangformatfile.h
new file mode 100644
index 0000000000..3fe9010f44
--- /dev/null
+++ b/src/plugins/clangformat/clangformatfile.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** 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 "utils/filepath.h"
+#include <clang/Format/Format.h>
+
+namespace ClangFormat {
+
+class ClangFormatFile
+{
+public:
+ explicit ClangFormatFile(Utils::FilePath file);
+ clang::format::FormatStyle format();
+
+ Utils::FilePath filePath();
+ void resetStyleToLLVM();
+ void setBasedOnStyle(QString styleName);
+ void setStyle(clang::format::FormatStyle style);
+ QString setStyle(QString style);
+ void clearBasedOnStyle();
+
+ using Field = std::pair<QString, QString>;
+ QString changeFields(QList<Field> fields);
+ QString changeField(Field field);
+
+private:
+ void saveNewFormat();
+ void saveNewFormat(QByteArray style);
+
+private:
+ Utils::FilePath m_filePath;
+ clang::format::FormatStyle m_style;
+};
+
+
+} // namespace ClangFormat
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
index 5371aae25a..f43decd940 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
@@ -936,7 +936,7 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id)
initialArgs.append("-DCMAKE_TOOLCHAIN_FILE:PATH="
+ ndkLocation.pathAppended("build/cmake/android.toolchain.cmake").path());
- auto androidAbis = bs->data(Android::Constants::AndroidABIs).toStringList();
+ auto androidAbis = bs->data(Android::Constants::AndroidMkSpecAbis).toStringList();
QString preferredAbi;
if (androidAbis.contains(ProjectExplorer::Constants::ANDROID_ABI_ARMEABI_V7A)) {
preferredAbi = ProjectExplorer::Constants::ANDROID_ABI_ARMEABI_V7A;
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp
index 6c84e3d872..86d97babfe 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp
@@ -128,20 +128,26 @@ QVariant CMakeTargetNode::data(Utils::Id role) const
return {};
};
+ if (role == Android::Constants::AndroidAbi)
+ return value(Android::Constants::ANDROID_ABI);
+
+ if (role == Android::Constants::AndroidAbis)
+ return value(Android::Constants::ANDROID_ABIS);
+
+ // TODO: Concerns the variables below. Qt 6 uses target properties which cannot be read
+ // by the current mechanism, and the variables start with "Qt_" prefix.
+
if (role == Android::Constants::AndroidPackageSourceDir)
return value(Android::Constants::ANDROID_PACKAGE_SOURCE_DIR);
- if (role == Android::Constants::AndroidDeploySettingsFile)
- return value(Android::Constants::ANDROID_DEPLOYMENT_SETTINGS_FILE);
-
if (role == Android::Constants::AndroidExtraLibs)
return value(Android::Constants::ANDROID_EXTRA_LIBS);
- if (role == Android::Constants::ANDROID_APPLICATION_ARGUMENTS)
- return value(Android::Constants::QT_ANDROID_APPLICATION_ARGUMENTS);
+ if (role == Android::Constants::AndroidDeploySettingsFile)
+ return value(Android::Constants::ANDROID_DEPLOYMENT_SETTINGS_FILE);
- if (role == Android::Constants::AndroidArch)
- return value(Android::Constants::ANDROID_ABI);
+ if (role == Android::Constants::AndroidApplicationArgs)
+ return value(Android::Constants::ANDROID_APPLICATION_ARGUMENTS);
if (role == Android::Constants::ANDROID_ABIS)
return value(Android::Constants::ANDROID_ABIS);
@@ -152,6 +158,9 @@ QVariant CMakeTargetNode::data(Utils::Id role) const
if (role == Android::Constants::AndroidTargets)
return values("TARGETS_BUILD_PATH");
+ if (role == Android::Constants::AndroidApk)
+ return {};
+
if (role == Ios::Constants::IosTarget) {
// For some reason the artifact is e.g. "Debug/untitled.app/untitled" which is wrong.
// It actually is e.g. "Debug-iphonesimulator/untitled.app/untitled".
diff --git a/src/plugins/cmakeprojectmanager/cmaketool.cpp b/src/plugins/cmakeprojectmanager/cmaketool.cpp
index 51daa1bb5f..d407adeb66 100644
--- a/src/plugins/cmakeprojectmanager/cmaketool.cpp
+++ b/src/plugins/cmakeprojectmanager/cmaketool.cpp
@@ -360,7 +360,7 @@ Utils::optional<CMakeTool::ReaderType> CMakeTool::readerType() const
FilePath CMakeTool::searchQchFile(const FilePath &executable)
{
- if (executable.isEmpty())
+ if (executable.isEmpty() || executable.needsDevice()) // do not register docs from devices
return {};
FilePath prefixDir = executable.parentDir().parentDir();
diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
index d893370028..cabac0b3b9 100644
--- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
+++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
@@ -322,8 +322,14 @@ static QStringList splitFragments(const QStringList &fragments)
return result;
}
+bool isPchFile(const FilePath &buildDirectory, const FilePath &path)
+{
+ return path.isChildOf(buildDirectory) && path.fileName().startsWith("cmake_pch");
+}
+
RawProjectParts generateRawProjectParts(const PreprocessedData &input,
- const FilePath &sourceDirectory)
+ const FilePath &sourceDirectory,
+ const FilePath &buildDirectory)
{
RawProjectParts rpps;
@@ -331,8 +337,12 @@ RawProjectParts generateRawProjectParts(const PreprocessedData &input,
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>(t.sources, [&sourceDir](const SourceInfo &si) {
+ categorizer({}, transform<QList>(sourceFiles, [&sourceDir](const SourceInfo &si) {
return sourceDir.absoluteFilePath(si.path);
}));
@@ -531,6 +541,11 @@ void addCompileGroups(ProjectNode *targetRoot,
auto node = std::make_unique<FileNode>(sourcePath, Node::fileTypeForFileName(sourcePath));
node->setIsGenerated(si.isGenerated);
+ // CMake pch files are generated at configured time, but not marked as generated
+ // so that a "clean" step won't remove them and at a subsequent build they won't exist.
+ if (isPchFile(buildDirectory, sourcePath))
+ node->setIsGenerated(true);
+
// Where does the file node need to go?
if (sourcePath.isChildOf(buildDirectory) && !inSourceBuild) {
buildFileNodes.emplace_back(std::move(node));
@@ -638,9 +653,16 @@ std::unique_ptr<CMakeProjectNode> generateRootProjectNode(
void setupLocationInfoForTargets(CMakeProjectNode *rootNode, const QList<CMakeBuildTarget> &targets)
{
+ const QSet<QString> titles = Utils::transform<QSet>(targets, &CMakeBuildTarget::title);
+ QHash<QString, FolderNode *> buildKeyToNode;
+ rootNode->forEachGenericNode([&buildKeyToNode, &titles](Node *node) {
+ FolderNode *folderNode = node->asFolderNode();
+ const QString &buildKey = node->buildKey();
+ if (folderNode && titles.contains(buildKey))
+ buildKeyToNode.insert(buildKey, folderNode);
+ });
for (const CMakeBuildTarget &t : targets) {
- FolderNode *folderNode = static_cast<FolderNode *>(
- rootNode->findNode(Utils::equal(&Node::buildKey, t.title)));
+ FolderNode *folderNode = buildKeyToNode.value(t.title);
if (folderNode) {
QSet<std::pair<FilePath, int>> locations;
auto dedup = [&locations](const Backtrace &bt) {
@@ -705,7 +727,7 @@ FileApiQtcData extractData(FileApiData &input,
result.buildTargets = generateBuildTargets(data, sourceDirectory, buildDirectory, haveLibrariesRelativeToBuildDirectory);
result.cmakeFiles = std::move(data.cmakeFiles);
- result.projectParts = generateRawProjectParts(data, sourceDirectory);
+ result.projectParts = generateRawProjectParts(data, sourceDirectory, buildDirectory);
auto rootProjectNode = generateRootProjectNode(data, sourceDirectory, buildDirectory);
ProjectTree::applyTreeManager(rootProjectNode.get()); // QRC nodes
diff --git a/src/plugins/coreplugin/dialogs/newdialogwidget.cpp b/src/plugins/coreplugin/dialogs/newdialogwidget.cpp
index f8a77c4e8d..66569b85cf 100644
--- a/src/plugins/coreplugin/dialogs/newdialogwidget.cpp
+++ b/src/plugins/coreplugin/dialogs/newdialogwidget.cpp
@@ -32,7 +32,6 @@
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
-#include <QAbstractProxyModel>
#include <QDebug>
#include <QItemDelegate>
#include <QKeyEvent>
diff --git a/src/plugins/coreplugin/locator/filesystemfilter.cpp b/src/plugins/coreplugin/locator/filesystemfilter.cpp
index 95c9bf16ea..9e0751e570 100644
--- a/src/plugins/coreplugin/locator/filesystemfilter.cpp
+++ b/src/plugins/coreplugin/locator/filesystemfilter.cpp
@@ -176,8 +176,9 @@ void FileSystemFilter::accept(LocatorFilterEntry selection,
{
Q_UNUSED(selectionLength)
if (selection.filePath.isDir()) {
- const QString value = shortcutString() + ' '
- + selection.filePath.absoluteFilePath().toUserOutput() + '/';
+ const QString value
+ = shortcutString() + ' '
+ + selection.filePath.absoluteFilePath().cleanPath().pathAppended("/").toUserOutput();
*newText = value;
*selectionStart = value.length();
} else {
diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp
index 9a8934b989..50837d9f15 100644
--- a/src/plugins/coreplugin/manhattanstyle.cpp
+++ b/src/plugins/coreplugin/manhattanstyle.cpp
@@ -73,14 +73,20 @@ bool styleEnabled(const QWidget *widget)
return true;
}
+static bool isInDialogOrPopup(const QWidget *widget)
+{
+ // Do not style dialogs or explicitly ignored widgets
+ const Qt::WindowType windowType = widget->window()->windowType();
+ return (windowType == Qt::Dialog || windowType == Qt::Popup);
+}
+
// Consider making this a QStyle state
bool panelWidget(const QWidget *widget)
{
if (!widget)
return false;
- // Do not style dialogs or explicitly ignored widgets
- if ((widget->window()->windowFlags() & Qt::WindowType_Mask) == Qt::Dialog)
+ if (isInDialogOrPopup(widget))
return false;
if (qobject_cast<const FancyMainWindow *>(widget))
@@ -107,8 +113,7 @@ bool lightColored(const QWidget *widget)
if (!widget)
return false;
- // Don't style dialogs or explicitly ignored widgets
- if ((widget->window()->windowFlags() & Qt::WindowType_Mask) == Qt::Dialog)
+ if (isInDialogOrPopup(widget))
return false;
const QWidget *p = widget;
diff --git a/src/plugins/cppcheck/cppcheckoptions.cpp b/src/plugins/cppcheck/cppcheckoptions.cpp
index 735557daf2..d6c7b0196c 100644
--- a/src/plugins/cppcheck/cppcheckoptions.cpp
+++ b/src/plugins/cppcheck/cppcheckoptions.cpp
@@ -39,9 +39,10 @@
#include <debugger/analyzer/analyzericons.h>
#include <QCheckBox>
-#include <QDir>
#include <QFormLayout>
+using namespace Utils;
+
namespace Cppcheck {
namespace Internal {
@@ -102,7 +103,7 @@ OptionsWidget::OptionsWidget(QWidget *parent)
void OptionsWidget::load(const CppcheckOptions &options)
{
- m_binary->setPath(options.binary);
+ m_binary->setFilePath(options.binary);
m_customArguments->setText(options.customArguments);
m_ignorePatterns->setText(options.ignoredPatterns);
m_warning->setChecked(options.warning);
@@ -121,7 +122,7 @@ void OptionsWidget::load(const CppcheckOptions &options)
void OptionsWidget::save(CppcheckOptions &options) const
{
- options.binary = m_binary->filePath().toString();
+ options.binary = m_binary->filePath();
options.customArguments = m_customArguments->text();
options.ignoredPatterns = m_ignorePatterns->text();
options.warning = m_warning->isChecked();
@@ -149,14 +150,13 @@ CppcheckOptionsPage::CppcheckOptionsPage(CppcheckTool &tool, CppcheckTrigger &tr
setCategoryIconPath(Analyzer::Icons::SETTINGSCATEGORY_ANALYZER);
CppcheckOptions options;
- if (Utils::HostOsInfo::isAnyUnixHost()) {
+ if (HostOsInfo::isAnyUnixHost()) {
options.binary = "cppcheck";
} else {
- QString programFiles = QDir::fromNativeSeparators(
- QString::fromLocal8Bit(qgetenv("PROGRAMFILES")));
+ FilePath programFiles = FilePath::fromUserInput(qEnvironmentVariable("PROGRAMFILES"));
if (programFiles.isEmpty())
programFiles = "C:/Program Files";
- options.binary = programFiles + "/Cppcheck/cppcheck.exe";
+ options.binary = programFiles / "Cppcheck/cppcheck.exe";
}
load(options);
@@ -190,7 +190,7 @@ void CppcheckOptionsPage::save(const CppcheckOptions &options) const
QSettings *s = Core::ICore::settings();
QTC_ASSERT(s, return);
s->beginGroup(Constants::SETTINGS_ID);
- s->setValue(Constants::SETTINGS_BINARY, options.binary);
+ s->setValue(Constants::SETTINGS_BINARY, options.binary.toString());
s->setValue(Constants::SETTINGS_CUSTOM_ARGUMENTS, options.customArguments);
s->setValue(Constants::SETTINGS_IGNORE_PATTERNS, options.ignoredPatterns);
s->setValue(Constants::SETTINGS_WARNING, options.warning);
@@ -213,8 +213,8 @@ void CppcheckOptionsPage::load(CppcheckOptions &options) const
QSettings *s = Core::ICore::settings();
QTC_ASSERT(s, return);
s->beginGroup(Constants::SETTINGS_ID);
- options.binary = s->value(Constants::SETTINGS_BINARY,
- options.binary).toString();
+ options.binary = FilePath::fromString(s->value(Constants::SETTINGS_BINARY,
+ options.binary.toString()).toString());
options.customArguments = s->value(Constants::SETTINGS_CUSTOM_ARGUMENTS,
options.customArguments).toString();
options.ignoredPatterns = s->value(Constants::SETTINGS_IGNORE_PATTERNS,
diff --git a/src/plugins/cppcheck/cppcheckoptions.h b/src/plugins/cppcheck/cppcheckoptions.h
index 639ffca6c9..4d4ca83cb6 100644
--- a/src/plugins/cppcheck/cppcheckoptions.h
+++ b/src/plugins/cppcheck/cppcheckoptions.h
@@ -26,6 +26,7 @@
#pragma once
#include <coreplugin/dialogs/ioptionspage.h>
+#include <utils/filepath.h>
#include <QCoreApplication>
#include <QPointer>
@@ -36,9 +37,7 @@ class QLineEdit;
class QCheckBox;
QT_END_NAMESPACE
-namespace Utils {
-class PathChooser;
-}
+namespace Utils { class PathChooser; }
namespace Cppcheck {
namespace Internal {
@@ -50,7 +49,7 @@ class OptionsWidget;
class CppcheckOptions final
{
public:
- QString binary;
+ Utils::FilePath binary;
bool warning = true;
bool style = true;
diff --git a/src/plugins/cppcheck/cppcheckrunner.cpp b/src/plugins/cppcheck/cppcheckrunner.cpp
index 2f6cffb1eb..261ccb3b63 100644
--- a/src/plugins/cppcheck/cppcheckrunner.cpp
+++ b/src/plugins/cppcheck/cppcheckrunner.cpp
@@ -74,7 +74,7 @@ CppcheckRunner::~CppcheckRunner()
m_queueTimer.stop();
}
-void CppcheckRunner::reconfigure(const QString &binary, const QString &arguments)
+void CppcheckRunner::reconfigure(const FilePath &binary, const QString &arguments)
{
m_binary = binary;
m_arguments = arguments;
@@ -157,7 +157,7 @@ void CppcheckRunner::checkQueued()
else
m_queue.begin().value() = files;
- m_process->setCommand(CommandLine(FilePath::fromString(m_binary), arguments, CommandLine::Raw));
+ m_process->setCommand(CommandLine(m_binary, arguments, CommandLine::Raw));
m_process->start();
}
diff --git a/src/plugins/cppcheck/cppcheckrunner.h b/src/plugins/cppcheck/cppcheckrunner.h
index 83aca95192..d49a9016de 100644
--- a/src/plugins/cppcheck/cppcheckrunner.h
+++ b/src/plugins/cppcheck/cppcheckrunner.h
@@ -45,7 +45,7 @@ public:
explicit CppcheckRunner(CppcheckTool &tool);
~CppcheckRunner() override;
- void reconfigure(const QString &binary, const QString &arguments);
+ void reconfigure(const Utils::FilePath &binary, const QString &arguments);
void addToQueue(const Utils::FilePaths &files,
const QString &additionalArguments = {});
void removeFromQueue(const Utils::FilePaths &files);
@@ -63,7 +63,7 @@ private:
CppcheckTool &m_tool;
Utils::QtcProcess *m_process = nullptr;
- QString m_binary;
+ Utils::FilePath m_binary;
QString m_arguments;
QHash<QString, Utils::FilePaths> m_queue;
Utils::FilePaths m_currentFiles;
diff --git a/src/plugins/cppeditor/CppEditor.json.in b/src/plugins/cppeditor/CppEditor.json.in
index 437e85c35f..b3c0149291 100644
--- a/src/plugins/cppeditor/CppEditor.json.in
+++ b/src/plugins/cppeditor/CppEditor.json.in
@@ -61,7 +61,7 @@
\" <!-- Find include guards of header files without extension, for\",
\" example, STL ones like <string>. Those can have a big initial\",
\" comment exceeding 1000 chars, though. -->\",
- \" <magic priority=\'50\'>\",
+ \" <magic priority=\'40\'>\",
\" <match value=\'#ifndef \' type=\'string\' offset=\'0:2000\'/>\",
\" <match value=\'#if \' type=\'string\' offset=\'0:2000\'/>\",
\" <match value=\'#include \' type=\'string\' offset=\'0:2000\'/>\",
diff --git a/src/plugins/cppeditor/semantichighlighter.cpp b/src/plugins/cppeditor/semantichighlighter.cpp
index b4c1a6ff1f..f3112a725b 100644
--- a/src/plugins/cppeditor/semantichighlighter.cpp
+++ b/src/plugins/cppeditor/semantichighlighter.cpp
@@ -34,6 +34,7 @@
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
+#include <QElapsedTimer>
#include <QLoggingCategory>
#include <QTextDocument>
@@ -165,6 +166,8 @@ void SemanticHighlighter::onHighlighterResultAvailable(int from, int to)
return; // aborted
qCDebug(log) << "onHighlighterResultAvailable()" << from << to;
+ QElapsedTimer t;
+ t.start();
SyntaxHighlighter *highlighter = m_baseTextDocument->syntaxHighlighter();
QTC_ASSERT(highlighter, return);
@@ -234,11 +237,17 @@ void SemanticHighlighter::onHighlighterResultAvailable(int from, int to)
}
if (parentheses.first.isValid())
TextDocumentLayout::setParentheses(parentheses.first, parentheses.second);
+
+ qCDebug(log) << "onHighlighterResultAvailable() took" << t.elapsed() << "ms";
}
void SemanticHighlighter::onHighlighterFinished()
{
QTC_ASSERT(m_watcher, return);
+
+ QElapsedTimer t;
+ t.start();
+
if (!m_watcher->isCanceled() && documentRevision() == m_revision) {
SyntaxHighlighter *highlighter = m_baseTextDocument->syntaxHighlighter();
if (QTC_GUARD(highlighter)) {
@@ -273,6 +282,7 @@ void SemanticHighlighter::onHighlighterFinished()
}
m_watcher.reset();
+ qCDebug(log) << "onHighlighterFinished() took" << t.elapsed() << "ms";
}
void SemanticHighlighter::connectWatcher()
diff --git a/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp b/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp
index 2e67bf675e..18fcdce5dc 100644
--- a/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp
+++ b/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp
@@ -35,7 +35,6 @@
#include "symbolpathsdialog.h"
#include <QCheckBox>
-#include <QDir>
#include <QDebug>
#include <QAction>
#include <QFormLayout>
@@ -165,7 +164,7 @@ void CdbSymbolPathListEditor::addSymbolPath(CdbSymbolPathListEditor::SymbolPathM
{
FilePath cacheDir;
if (promptCacheDirectory(this, &cacheDir))
- insertPathAtCursor(CdbSymbolPathListEditor::symbolPath(cacheDir.path(), mode));
+ insertPathAtCursor(CdbSymbolPathListEditor::symbolPath(cacheDir, mode));
}
void CdbSymbolPathListEditor::setupSymbolPaths()
@@ -174,13 +173,13 @@ void CdbSymbolPathListEditor::setupSymbolPaths()
const int indexOfSymbolServer = indexOfSymbolPath(currentPaths, SymbolServerPath);
const int indexOfSymbolCache = indexOfSymbolPath(currentPaths, SymbolCachePath);
- QString path;
+ FilePath path;
if (indexOfSymbolServer != -1)
- path = currentPaths.at(indexOfSymbolServer);
+ path = FilePath::fromString(currentPaths.at(indexOfSymbolServer));
if (path.isEmpty() && indexOfSymbolCache != -1)
- path = currentPaths.at(indexOfSymbolCache);
+ path = FilePath::fromString(currentPaths.at(indexOfSymbolCache));
if (path.isEmpty())
- path = TemporaryDirectory::masterDirectoryPath() + "/symbolcache";
+ path = FilePath::fromString(TemporaryDirectory::masterDirectoryPath() + "/symbolcache");
bool useSymbolServer = true;
bool useSymbolCache = true;
@@ -193,20 +192,20 @@ void CdbSymbolPathListEditor::setupSymbolPaths()
if (useSymbolCache) {
insertPathAtCursor(CdbSymbolPathListEditor::symbolPath(path, SymbolCachePath));
if (useSymbolServer)
- insertPathAtCursor(CdbSymbolPathListEditor::symbolPath(QString(), SymbolServerPath));
+ insertPathAtCursor(CdbSymbolPathListEditor::symbolPath({}, SymbolServerPath));
} else if (useSymbolServer) {
insertPathAtCursor(CdbSymbolPathListEditor::symbolPath(path, SymbolServerPath));
}
}
-QString CdbSymbolPathListEditor::symbolPath(const QString &cacheDir,
+QString CdbSymbolPathListEditor::symbolPath(const FilePath &cacheDir,
CdbSymbolPathListEditor::SymbolPathMode mode)
{
if (mode == SymbolCachePath)
- return symbolCachePrefixC + QDir::toNativeSeparators(cacheDir);
+ return symbolCachePrefixC + cacheDir.toUserOutput();
QString s = QLatin1String(symbolServerPrefixC);
if (!cacheDir.isEmpty())
- s += QDir::toNativeSeparators(cacheDir) + '*';
+ s += cacheDir.toUserOutput() + '*';
s += QLatin1String(symbolServerPostfixC);
return s;
}
diff --git a/src/plugins/debugger/shared/cdbsymbolpathlisteditor.h b/src/plugins/debugger/shared/cdbsymbolpathlisteditor.h
index df64b35bfe..f7dffcdda0 100644
--- a/src/plugins/debugger/shared/cdbsymbolpathlisteditor.h
+++ b/src/plugins/debugger/shared/cdbsymbolpathlisteditor.h
@@ -53,7 +53,7 @@ public:
static bool promptCacheDirectory(QWidget *parent, Utils::FilePath *cacheDirectory);
// Format a symbol path specification
- static QString symbolPath(const QString &cacheDir, SymbolPathMode mode);
+ static QString symbolPath(const Utils::FilePath &cacheDir, SymbolPathMode mode);
// Check for a symbol server path and extract local cache directory
static bool isSymbolServerPath(const QString &path, QString *cacheDir = nullptr);
// Check for a symbol cache path and extract local cache directory
diff --git a/src/plugins/debugger/shared/symbolpathsdialog.cpp b/src/plugins/debugger/shared/symbolpathsdialog.cpp
index db0354cca4..be8ef21afe 100644
--- a/src/plugins/debugger/shared/symbolpathsdialog.cpp
+++ b/src/plugins/debugger/shared/symbolpathsdialog.cpp
@@ -26,10 +26,14 @@
#include "symbolpathsdialog.h"
#include "ui_symbolpathsdialog.h"
+#include <utils/filepath.h>
+
#include <QMessageBox>
-using namespace Debugger;
-using namespace Internal;
+using namespace Utils;
+
+namespace Debugger {
+namespace Internal {
SymbolPathsDialog::SymbolPathsDialog(QWidget *parent) :
QDialog(parent),
@@ -54,9 +58,9 @@ bool SymbolPathsDialog::useSymbolServer() const
return ui->useSymbolServer->isChecked();
}
-QString SymbolPathsDialog::path() const
+FilePath SymbolPathsDialog::path() const
{
- return ui->pathChooser->filePath().toString();
+ return ui->pathChooser->filePath();
}
void SymbolPathsDialog::setUseSymbolCache(bool useSymbolCache)
@@ -69,13 +73,13 @@ void SymbolPathsDialog::setUseSymbolServer(bool useSymbolServer)
ui->useSymbolServer->setChecked(useSymbolServer);
}
-void SymbolPathsDialog::setPath(const QString &path)
+void SymbolPathsDialog::setPath(const FilePath &path)
{
- ui->pathChooser->setPath(path);
+ ui->pathChooser->setFilePath(path);
}
bool SymbolPathsDialog::useCommonSymbolPaths(bool &useSymbolCache, bool &useSymbolServer,
- QString &path)
+ FilePath &path)
{
SymbolPathsDialog dialog;
dialog.setUseSymbolCache(useSymbolCache);
@@ -87,3 +91,6 @@ bool SymbolPathsDialog::useCommonSymbolPaths(bool &useSymbolCache, bool &useSymb
path = dialog.path();
return ret == QDialog::Accepted;
}
+
+} // Internal
+} // Debugger
diff --git a/src/plugins/debugger/shared/symbolpathsdialog.h b/src/plugins/debugger/shared/symbolpathsdialog.h
index c3534fbcf6..c77e112ea4 100644
--- a/src/plugins/debugger/shared/symbolpathsdialog.h
+++ b/src/plugins/debugger/shared/symbolpathsdialog.h
@@ -28,6 +28,8 @@
#include <QDialog>
#include <QString>
+namespace Utils { class FilePath; }
+
namespace Debugger {
namespace Internal {
@@ -43,15 +45,15 @@ public:
bool useSymbolCache() const;
bool useSymbolServer() const;
- QString path() const;
+ Utils::FilePath path() const;
bool doNotAskAgain() const;
void setUseSymbolCache(bool useSymbolCache);
void setUseSymbolServer(bool useSymbolServer);
- void setPath(const QString &path);
+ void setPath(const Utils::FilePath &path);
void setDoNotAskAgain(bool doNotAskAgain) const;
- static bool useCommonSymbolPaths(bool &useSymbolCache, bool &useSymbolServer, QString &path);
+ static bool useCommonSymbolPaths(bool &useSymbolCache, bool &useSymbolServer, Utils::FilePath &path);
private:
Ui::SymbolPathsDialog *ui;
diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp
index 624f3eab49..638fb91db0 100644
--- a/src/plugins/docker/dockerdevice.cpp
+++ b/src/plugins/docker/dockerdevice.cpp
@@ -49,17 +49,18 @@
#include <utils/algorithm.h>
#include <utils/basetreeview.h>
#include <utils/environment.h>
+#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/utilsicons.h>
#include <utils/layoutbuilder.h>
#include <utils/overridecursor.h>
+#include <utils/pathlisteditor.h>
#include <utils/port.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/temporaryfile.h>
#include <utils/treemodel.h>
-#include <utils/fileutils.h>
+#include <utils/utilsicons.h>
#include <QApplication>
#include <QCheckBox>
@@ -75,8 +76,8 @@
#include <QRandomGenerator>
#include <QRegularExpression>
#include <QTextBrowser>
-#include <QToolButton>
#include <QThread>
+#include <QToolButton>
#include <numeric>
@@ -394,15 +395,13 @@ public:
dockerDevice->tryCreateLocalFileAccess();
});
- m_pathsLineEdit = new QLineEdit;
- m_pathsLineEdit->setText(data.repo);
- m_pathsLineEdit->setToolTip(tr("Paths in this semi-colon separated list will be "
- "mapped one-to-one into the Docker container."));
- m_pathsLineEdit->setText(data.mounts.join(';'));
- m_pathsLineEdit->setPlaceholderText(tr("List project source directories here"));
+ m_pathsListEdit = new PathListEditor;
+ m_pathsListEdit->setToolTip(tr("Paths in this list will be mapped one-to-one into the "
+ "Docker container."));
+ m_pathsListEdit->setPathList(data.mounts);
- connect(m_pathsLineEdit, &QLineEdit::textChanged, this, [dockerDevice](const QString &text) {
- dockerDevice->setMounts(text.split(';', Qt::SkipEmptyParts));
+ connect(m_pathsListEdit, &PathListEditor::changed, this, [dockerDevice, this]() {
+ dockerDevice->setMounts(m_pathsListEdit->pathList());
});
auto logView = new QTextBrowser;
@@ -443,7 +442,10 @@ public:
daemonStateLabel, m_daemonReset, m_daemonState, Break(),
m_runAsOutsideUser, Break(),
m_usePathMapping, Break(),
- tr("Paths to mount:"), m_pathsLineEdit, Break(),
+ Column {
+ new QLabel(tr("Paths to mount:")),
+ m_pathsListEdit,
+ }, Break(),
Column {
Space(20),
Row { autoDetectButton, undoAutoDetectButton, listAutoDetectedButton, Stretch() },
@@ -463,7 +465,7 @@ private:
QLabel *m_daemonState;
QCheckBox *m_runAsOutsideUser;
QCheckBox *m_usePathMapping;
- QLineEdit *m_pathsLineEdit;
+ Utils::PathListEditor *m_pathsListEdit;
KitDetector m_kitItemDetector;
};
@@ -813,34 +815,51 @@ static QString getLocalIPv4Address()
void DockerDevicePrivate::startContainer()
{
- QString tempFileName;
-
- {
- TemporaryFile temp("qtc-docker-XXXXXX");
- temp.open();
- tempFileName = temp.fileName();
- }
-
const QString display = HostOsInfo::isWindowsHost() ? QString(getLocalIPv4Address() + ":0.0")
: QString(":0");
- CommandLine dockerRun{"docker", {"run", "-i", "--cidfile=" + tempFileName,
- "--rm",
- "-e", QString("DISPLAY=%1").arg(display),
- "-e", "XAUTHORITY=/.Xauthority",
- "--net", "host"}};
+ CommandLine dockerCreate{"docker", {"create",
+ "-i",
+ "--rm",
+ "-e", QString("DISPLAY=%1").arg(display),
+ "-e", "XAUTHORITY=/.Xauthority",
+ "--net", "host"}};
#ifdef Q_OS_UNIX
+ // no getuid() and getgid() on Windows.
if (m_data.useLocalUidGid)
- dockerRun.addArgs({"-u", QString("%1:%2").arg(getuid()).arg(getgid())});
+ dockerCreate.addArgs({"-u", QString("%1:%2").arg(getuid()).arg(getgid())});
#endif
- for (const QString &mount : qAsConst(m_data.mounts)) {
- if (!mount.isEmpty())
- dockerRun.addArgs({"-v", mount + ':' + mount});
+ 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();
+ }
+ dockerCreate.addArgs({"-v", mount + ':' + mount});
}
- dockerRun.addArgs({"--entrypoint", "/bin/sh", m_data.imageId});
+ dockerCreate.addArgs({"--entrypoint", "/bin/sh", m_data.imageId});
+
+ LOG("RUNNING: " << dockerCreate.toUserOutput());
+ QtcProcess createProcess;
+ createProcess.setCommand(dockerCreate);
+ createProcess.runBlocking();
+
+ if (createProcess.result() != QtcProcess::FinishedWithSuccess)
+ return;
+
+ m_container = createProcess.stdOut().trimmed();
+ if (m_container.isEmpty())
+ return;
+ LOG("Container via process: " << m_container);
+ CommandLine dockerRun{"docker", {"container" , "start", "-i", "-a", m_container}};
LOG("RUNNING: " << dockerRun.toUserOutput());
QPointer<QtcProcess> shell = new QtcProcess(ProcessMode::Writer);
connect(shell, &QtcProcess::finished, this, [this, shell] {
@@ -875,23 +894,6 @@ void DockerDevicePrivate::startContainer()
return;
}
- LOG("CHECKING: " << tempFileName);
- for (int i = 0; i <= 20; ++i) {
- QFile file(tempFileName);
- if (file.open(QIODevice::ReadOnly)) {
- m_container = QString::fromUtf8(file.readAll()).trimmed();
- if (!m_container.isEmpty()) {
- LOG("Container: " << m_container);
- break;
- }
- }
- if (i == 20 || !DockerPlugin::isDaemonRunning().value_or(true)) {
- qWarning("Docker cid file empty.");
- return; // No
- }
- qApp->processEvents(); // FIXME turn this for-loop into
- QThread::msleep(100);
- }
DockerPlugin::setGlobalDaemonState(true);
}
@@ -1288,12 +1290,8 @@ QDateTime DockerDevice::lastModified(const FilePath &filePath) const
return res;
}
- QtcProcess proc;
- proc.setCommand({"stat", {"-c", "%Y", filePath.path()}});
- runProcess(proc);
- proc.waitForFinished();
-
- const qint64 secs = proc.rawStdOut().toLongLong();
+ const QString output = d->outputForRunInShell({"stat", {"-c", "%Y", filePath.path()}});
+ qint64 secs = output.toLongLong();
const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC);
return dt;
}
@@ -1615,6 +1613,12 @@ void DockerDevice::aboutToBeRemoved() const
void DockerDevicePrivate::fetchSystemEnviroment()
{
+ if (m_shell) {
+ const QString remoteOutput = outputForRunInShell({"env", {}});
+ m_cachedEnviroment = Environment(remoteOutput.split('\n', Qt::SkipEmptyParts), q->osType());
+ return;
+ }
+
QtcProcess proc;
proc.setCommand({"env", {}});
diff --git a/src/plugins/help/litehtmlhelpviewer.cpp b/src/plugins/help/litehtmlhelpviewer.cpp
index c33ebf62ef..8d54e01b69 100644
--- a/src/plugins/help/litehtmlhelpviewer.cpp
+++ b/src/plugins/help/litehtmlhelpviewer.cpp
@@ -235,8 +235,10 @@ bool LiteHtmlHelpViewer::eventFilter(QObject *src, QEvent *e)
{
if (isScrollWheelZoomingEnabled() && e->type() == QEvent::Wheel) {
auto we = static_cast<QWheelEvent *>(e);
- if (we->modifiers() == Qt::ControlModifier)
+ if (we->modifiers() == Qt::ControlModifier) {
+ e->ignore();
return true;
+ }
}
return HelpViewer::eventFilter(src, e);
}
diff --git a/src/plugins/imageviewer/imageviewerfile.cpp b/src/plugins/imageviewer/imageviewerfile.cpp
index 2a6204e11d..3fe26890d0 100644
--- a/src/plugins/imageviewer/imageviewerfile.cpp
+++ b/src/plugins/imageviewer/imageviewerfile.cpp
@@ -126,7 +126,15 @@ Core::IDocument::OpenResult ImageViewerFile::openImpl(QString *errorString,
m_type = TypeMovie;
m_movie = new QMovie(fileName, QByteArray(), this);
m_movie->setCacheMode(QMovie::CacheAll);
- connect(m_movie, &QMovie::finished, m_movie, &QMovie::start);
+ connect(
+ m_movie,
+ &QMovie::finished,
+ m_movie,
+ [this] {
+ if (m_movie->isValid())
+ m_movie->start();
+ },
+ Qt::QueuedConnection);
connect(m_movie, &QMovie::resized, this, &ImageViewerFile::imageSizeChanged);
m_movie->start();
m_isPaused = false; // force update
diff --git a/src/plugins/mcusupport/mcusupportoptions.cpp b/src/plugins/mcusupport/mcusupportoptions.cpp
index 6fa37d5a83..ba6107d6fc 100644
--- a/src/plugins/mcusupport/mcusupportoptions.cpp
+++ b/src/plugins/mcusupport/mcusupportoptions.cpp
@@ -72,15 +72,15 @@ namespace Internal {
static const int KIT_VERSION = 8; // Bumps up whenever details in Kit creation change
-static QString packagePathFromSettings(const QString &settingsKey,
- QSettings::Scope scope = QSettings::UserScope,
- const QString &defaultPath = {})
+static FilePath packagePathFromSettings(const QString &settingsKey,
+ QSettings::Scope scope = QSettings::UserScope,
+ const FilePath &defaultPath = {})
{
QSettings *settings = Core::ICore::settings(scope);
const QString key = QLatin1String(Constants::SETTINGS_GROUP) + '/' +
QLatin1String(Constants::SETTINGS_KEY_PACKAGE_PREFIX) + settingsKey;
- const QString path = settings->value(key, defaultPath).toString();
- return FilePath::fromUserInput(path).toString();
+ const QString path = settings->value(key, defaultPath.toString()).toString();
+ return FilePath::fromUserInput(path);
}
static bool automaticKitCreationFromSettings(QSettings::Scope scope = QSettings::UserScope)
@@ -99,7 +99,7 @@ static bool kitNeedsQtVersion()
return !HostOsInfo::isWindowsHost();
}
-McuPackage::McuPackage(const QString &label, const QString &defaultPath,
+McuPackage::McuPackage(const QString &label, const FilePath &defaultPath,
const QString &detectionPath, const QString &settingsKey,
const McuPackageVersionDetector *versionDetector)
: m_label(label)
@@ -112,14 +112,14 @@ McuPackage::McuPackage(const QString &label, const QString &defaultPath,
m_automaticKitCreation = automaticKitCreationFromSettings(QSettings::UserScope);
}
-QString McuPackage::basePath() const
+FilePath McuPackage::basePath() const
{
- return m_fileChooser != nullptr ? m_fileChooser->filePath().toString() : m_path;
+ return m_fileChooser != nullptr ? m_fileChooser->filePath() : m_path;
}
-QString McuPackage::path() const
+FilePath McuPackage::path() const
{
- return QFileInfo(basePath() + m_relativePathModifier).absoluteFilePath();
+ return basePath().resolvePath(m_relativePathModifier).absoluteFilePath();
}
QString McuPackage::label() const
@@ -127,7 +127,7 @@ QString McuPackage::label() const
return m_label;
}
-QString McuPackage::defaultPath() const
+FilePath McuPackage::defaultPath() const
{
return m_defaultPath;
}
@@ -148,7 +148,7 @@ QWidget *McuPackage::widget()
Icons::RESET.icon());
m_fileChooser->lineEdit()->setButtonVisible(FancyLineEdit::Right, true);
connect(m_fileChooser->lineEdit(), &FancyLineEdit::rightButtonClicked, this, [&] {
- m_fileChooser->setPath(m_defaultPath);
+ m_fileChooser->setFilePath(m_defaultPath);
});
auto layout = new QGridLayout(m_widget);
@@ -168,7 +168,7 @@ QWidget *McuPackage::widget()
layout->addWidget(m_fileChooser, 0, 0, 1, 2);
layout->addWidget(m_infoLabel, 1, 0, 1, -1);
- m_fileChooser->setPath(m_path);
+ m_fileChooser->setFilePath(m_path);
QObject::connect(this, &McuPackage::statusChanged, this, [this] {
updateStatusUi();
@@ -228,7 +228,7 @@ void McuPackage::writeGeneralSettings() const
bool McuPackage::writeToSettings() const
{
- const QString savedPath = packagePathFromSettings(m_settingsKey, QSettings::UserScope, m_defaultPath);
+ const FilePath savedPath = packagePathFromSettings(m_settingsKey, QSettings::UserScope, m_defaultPath);
const QString key = QLatin1String(Constants::SETTINGS_GROUP) + '/' +
QLatin1String(Constants::SETTINGS_KEY_PACKAGE_PREFIX) + m_settingsKey;
Core::ICore::settings()->setValueWithDefault(key, m_path, m_defaultPath);
@@ -258,17 +258,18 @@ void McuPackage::setAutomaticKitCreationEnabled(const bool enabled)
void McuPackage::updatePath()
{
- m_path = m_fileChooser->rawPath();
+ m_path = m_fileChooser->rawFilePath();
m_fileChooser->lineEdit()->button(FancyLineEdit::Right)->setEnabled(m_path != m_defaultPath);
updateStatus();
}
void McuPackage::updateStatus()
{
- bool validPath = !m_path.isEmpty() && FilePath::fromString(m_path).exists();
- const FilePath detectionPath = FilePath::fromString(basePath() + "/" + m_detectionPath);
+ bool validPath = !m_path.isEmpty() && m_path.exists();
+ const FilePath detectionPath = basePath() / m_detectionPath;
const bool validPackage = m_detectionPath.isEmpty() || detectionPath.exists();
- m_detectedVersion = validPath && validPackage && m_versionDetector ? m_versionDetector->parseVersion(basePath()) : QString();
+ m_detectedVersion = validPath && validPackage && m_versionDetector
+ ? m_versionDetector->parseVersion(basePath().toString()) : QString();
const bool validVersion = m_detectedVersion.isEmpty() ||
m_versions.isEmpty() || m_versions.contains(m_detectedVersion);
@@ -293,7 +294,7 @@ void McuPackage::updateStatusUi()
QString McuPackage::statusText() const
{
- const QString displayPackagePath = FilePath::fromString(m_path).toUserOutput();
+ const QString displayPackagePath = m_path.toUserOutput();
const QString displayVersions = QStringList(m_versions.toList()).join(" or ");
const QString displayRequiredPath = QString("%1 %2").arg(
FilePath::fromString(m_detectionPath).toUserOutput(),
@@ -339,7 +340,7 @@ QString McuPackage::statusText() const
}
McuToolChainPackage::McuToolChainPackage(const QString &label,
- const QString &defaultPath,
+ const FilePath &defaultPath,
const QString &detectionPath,
const QString &settingsKey,
McuToolChainPackage::Type type,
@@ -447,11 +448,9 @@ ToolChain *McuToolChainPackage::toolChain(Id language) const
else {
const QLatin1String compilerName(
language == ProjectExplorer::Constants::C_LANGUAGE_ID ? "gcc" : "g++");
- const FilePath compiler = FilePath::fromUserInput(
- HostOsInfo::withExecutableSuffix(
- path() + (
- m_type == TypeArmGcc
- ? "/bin/arm-none-eabi-%1" : "/bar/foo-keil-%1")).arg(compilerName));
+ const QString comp = QLatin1String(m_type == TypeArmGcc ? "/bin/arm-none-eabi-%1" : "/bar/foo-keil-%1")
+ .arg(compilerName);
+ const FilePath compiler = path().pathAppended(comp).withExecutableSuffix();
tc = armGccToolChain(compiler, language);
}
@@ -479,11 +478,10 @@ QVariant McuToolChainPackage::debuggerId() const
{
using namespace Debugger;
- const FilePath command = FilePath::fromUserInput(
- HostOsInfo::withExecutableSuffix(path() + (
- m_type == TypeArmGcc
- ? "/bin/arm-none-eabi-gdb-py" : m_type == TypeIAR
- ? "../common/bin/CSpyBat" : "/bar/foo-keil-gdb")));
+ QString sub = QString::fromLatin1(m_type == TypeArmGcc ? "bin/arm-none-eabi-gdb-py"
+ : m_type == TypeIAR ? "../common/bin/CSpyBat" : "bar/foo-keil-gdb");
+
+ const FilePath command = path().pathAppended(sub).withExecutableSuffix();
const DebuggerItem *debugger = DebuggerItemManager::findByCommand(command);
QVariant debuggerId;
if (!debugger) {
@@ -591,7 +589,7 @@ McuSupportOptions::~McuSupportOptions()
void McuSupportOptions::populatePackagesAndTargets()
{
- setQulDir(FilePath::fromUserInput(qtForMCUsSdkPackage->path()));
+ setQulDir(qtForMCUsSdkPackage->path());
}
static FilePath qulDocsDir()
@@ -665,13 +663,12 @@ void McuSupportOptions::setQulDir(const FilePath &dir)
FilePath McuSupportOptions::qulDirFromSettings()
{
- return FilePath::fromUserInput(
- packagePathFromSettings(Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK,
- QSettings::UserScope));
+ return packagePathFromSettings(Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK,
+ QSettings::UserScope);
}
static void setKitProperties(const QString &kitName, Kit *k, const McuTarget *mcuTarget,
- const QString &sdkPath)
+ const FilePath &sdkPath)
{
using namespace Constants;
@@ -688,7 +685,7 @@ static void setKitProperties(const QString &kitName, Kit *k, const McuTarget *mc
if (mcuTarget->toolChainPackage()->isDesktopToolchain())
k->setDeviceTypeForIcon(DEVICE_TYPE);
k->setValue(QtSupport::SuppliesQtQuickImportPath::id(), true);
- k->setValue(QtSupport::KitQmlImportPath::id(), QVariant(sdkPath + "/include/qul"));
+ k->setValue(QtSupport::KitQmlImportPath::id(), sdkPath.pathAppended("include/qul").toVariant());
k->setValue(QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(), true);
QSet<Id> irrelevant = {
SysRootKitAspect::id(),
@@ -750,14 +747,13 @@ static void setKitEnvironment(Kit *k, const McuTarget *mcuTarget,
// feature of the run configuration. Otherwise, we just prepend the path, here.
if (mcuTarget->toolChainPackage()->isDesktopToolchain()
&& !CMakeProjectManager::CMakeToolManager::defaultCMakeTool()->hasFileApi())
- pathAdditions.append(QDir::toNativeSeparators(qtForMCUsSdkPackage->path() + "/bin"));
+ pathAdditions.append(qtForMCUsSdkPackage->path().pathAppended("bin").toUserOutput());
auto processPackage = [&pathAdditions, &changes](const McuPackage *package) {
if (package->addToPath())
- pathAdditions.append(QDir::toNativeSeparators(package->path()));
+ pathAdditions.append(package->path().toUserOutput());
if (!package->environmentVariableName().isEmpty())
- changes.append({package->environmentVariableName(),
- QDir::toNativeSeparators(package->path())});
+ changes.append({package->environmentVariableName(), package->path().toUserOutput()});
};
for (auto package : mcuTarget->packages())
processPackage(package);
@@ -808,7 +804,7 @@ static void updateKitEnvironment(Kit *k, const McuTarget *mcuTarget)
return item.name == varName;
});
const EnvironmentItem item = {package->environmentVariableName(),
- QDir::toNativeSeparators(package->path())};
+ package->path().toUserOutput()};
if (index != -1)
changes.replace(index, item);
else
@@ -819,7 +815,7 @@ static void updateKitEnvironment(Kit *k, const McuTarget *mcuTarget)
EnvironmentKitAspect::setEnvironmentChanges(k, changes);
}
-static void setKitCMakeOptions(Kit *k, const McuTarget* mcuTarget, const QString &qulDir)
+static void setKitCMakeOptions(Kit *k, const McuTarget* mcuTarget, const FilePath &qulDir)
{
using namespace CMakeProjectManager;
@@ -832,7 +828,7 @@ static void setKitCMakeOptions(Kit *k, const McuTarget* mcuTarget, const QString
}
if (!mcuTarget->toolChainPackage()->isDesktopToolchain()) {
- const FilePath cMakeToolchainFile = FilePath::fromString(qulDir + "/lib/cmake/Qul/toolchain/"
+ const FilePath cMakeToolchainFile = qulDir.pathAppended("lib/cmake/Qul/toolchain/"
+ mcuTarget->toolChainPackage()->cmakeToolChainFileName());
config.append(CMakeConfigItem(
@@ -844,7 +840,7 @@ static void setKitCMakeOptions(Kit *k, const McuTarget* mcuTarget, const QString
}
}
- const FilePath generatorsPath = FilePath::fromString(qulDir + "/lib/cmake/Qul/QulGenerators.cmake");
+ const FilePath generatorsPath = qulDir.pathAppended("/lib/cmake/Qul/QulGenerators.cmake");
config.append(CMakeConfigItem("QUL_GENERATORS",
generatorsPath.toString().toUtf8()));
if (!generatorsPath.exists()) {
@@ -947,7 +943,7 @@ QList<Kit *> McuSupportOptions::kitsWithMismatchedDependencies(const McuTarget *
EnvironmentKitAspect::environmentChanges(kit)));
return Utils::anyOf(mcuTarget->packages(), [&environment](const McuPackage *package) {
return !package->environmentVariableName().isEmpty() &&
- environment.value(package->environmentVariableName()) != QDir::toNativeSeparators(package->path());
+ environment.value(package->environmentVariableName()) != package->path().toUserOutput();
});
});
}
@@ -1003,13 +999,13 @@ QVersionNumber McuSupportOptions::kitQulVersion(const Kit *kit)
.toString());
}
-QString kitDependencyPath(const Kit *kit, const QString &variableName)
+static FilePath kitDependencyPath(const Kit *kit, const QString &variableName)
{
for (const NameValueItem &nameValueItem : EnvironmentKitAspect::environmentChanges(kit)) {
if (nameValueItem.name == variableName)
- return nameValueItem.value;
+ return FilePath::fromUserInput(nameValueItem.value);
}
- return QString();
+ return FilePath();
}
bool McuSupportOptions::kitUpToDate(const Kit *kit, const McuTarget *mcuTarget,
@@ -1052,13 +1048,13 @@ void McuSupportOptions::createAutomaticKits()
const QString displayPath = FilePath::fromString(qtForMCUsPackage->detectionPath())
.toUserOutput();
printMessage(tr("Path %1 exists, but does not contain %2.")
- .arg(qtForMCUsPackage->path(), displayPath),
+ .arg(qtForMCUsPackage->path().toUserOutput(), displayPath),
true);
break;
}
case McuPackage::InvalidPath: {
printMessage(tr("Path %1 does not exist. Add the path in Tools > Options > Devices > MCU.")
- .arg(qtForMCUsPackage->path()),
+ .arg(qtForMCUsPackage->path().toUserOutput()),
true);
break;
}
@@ -1079,7 +1075,7 @@ void McuSupportOptions::createAutomaticKits()
return;
}
- auto dir = FilePath::fromUserInput(qtForMCUsPackage->path());
+ FilePath dir = qtForMCUsPackage->path();
QVector<McuPackage*> packages;
QVector<McuTarget*> mcuTargets;
Sdk::targetsAndPackages(dir, &packages, &mcuTargets);
@@ -1131,7 +1127,7 @@ void McuSupportOptions::upgradeKits(UpgradeOption upgradeOption)
auto qtForMCUsPackage = Sdk::createQtForMCUsPackage();
- auto dir = FilePath::fromUserInput(qtForMCUsPackage->path());
+ auto dir = qtForMCUsPackage->path();
QVector<McuPackage*> packages;
QVector<McuTarget*> mcuTargets;
Sdk::targetsAndPackages(dir, &packages, &mcuTargets);
@@ -1169,7 +1165,7 @@ void McuSupportOptions::fixKitsDependencies()
{
auto qtForMCUsPackage = Sdk::createQtForMCUsPackage();
- auto dir = FilePath::fromUserInput(qtForMCUsPackage->path());
+ FilePath dir = qtForMCUsPackage->path();
QVector<McuPackage*> packages;
QVector<McuTarget*> mcuTargets;
Sdk::targetsAndPackages(dir, &packages, &mcuTargets);
@@ -1245,7 +1241,7 @@ void McuSupportOptions::fixExistingKits()
auto qtForMCUsPackage = Sdk::createQtForMCUsPackage();
qtForMCUsPackage->updateStatus();
if (qtForMCUsPackage->validStatus()) {
- auto dir = FilePath::fromUserInput(qtForMCUsPackage->path());
+ FilePath dir = qtForMCUsPackage->path();
QVector<McuPackage*> packages;
QVector<McuTarget*> mcuTargets;
Sdk::targetsAndPackages(dir, &packages, &mcuTargets);
diff --git a/src/plugins/mcusupport/mcusupportoptions.h b/src/plugins/mcusupport/mcusupportoptions.h
index f70722412d..346c2b9de5 100644
--- a/src/plugins/mcusupport/mcusupportoptions.h
+++ b/src/plugins/mcusupport/mcusupportoptions.h
@@ -66,15 +66,15 @@ public:
ValidPackage
};
- McuPackage(const QString &label, const QString &defaultPath,
+ McuPackage(const QString &label, const Utils::FilePath &defaultPath,
const QString &detectionPath, const QString &settingsKey,
const McuPackageVersionDetector *versionDetector = nullptr);
virtual ~McuPackage() = default;
- QString basePath() const;
- QString path() const;
+ Utils::FilePath basePath() const;
+ Utils::FilePath path() const;
QString label() const;
- QString defaultPath() const;
+ Utils::FilePath defaultPath() const;
QString detectionPath() const;
QString statusText() const;
void updateStatus();
@@ -110,12 +110,12 @@ private:
Utils::InfoLabel *m_infoLabel = nullptr;
const QString m_label;
- const QString m_defaultPath;
+ const Utils::FilePath m_defaultPath;
const QString m_detectionPath;
const QString m_settingsKey;
const McuPackageVersionDetector *m_versionDetector;
- QString m_path;
+ Utils::FilePath m_path;
QString m_relativePathModifier; // relative path to m_path to be returned by path()
QString m_detectedVersion;
QVector<QString> m_versions;
@@ -142,7 +142,7 @@ public:
};
McuToolChainPackage(const QString &label,
- const QString &defaultPath,
+ const Utils::FilePath &defaultPath,
const QString &detectionPath,
const QString &settingsKey,
Type type,
diff --git a/src/plugins/mcusupport/mcusupportoptionspage.cpp b/src/plugins/mcusupport/mcusupportoptionspage.cpp
index 9ccffd63e0..71eff41c24 100644
--- a/src/plugins/mcusupport/mcusupportoptionspage.cpp
+++ b/src/plugins/mcusupport/mcusupportoptionspage.cpp
@@ -193,7 +193,7 @@ void McuSupportOptionsWidget::updateStatus()
m_mcuTargetsInfoLabel->setVisible(valid && m_options.mcuTargets.isEmpty());
if (m_mcuTargetsInfoLabel->isVisible()) {
m_mcuTargetsInfoLabel->setType(Utils::InfoLabel::NotOk);
- const auto sdkPath = Utils::FilePath::fromString(m_options.qtForMCUsSdkPackage->basePath());
+ const Utils::FilePath sdkPath = m_options.qtForMCUsSdkPackage->basePath();
QString deprecationMessage;
if (Sdk::checkDeprecatedSdkError(sdkPath, deprecationMessage))
m_mcuTargetsInfoLabel->setText(deprecationMessage);
diff --git a/src/plugins/mcusupport/mcusupportsdk.cpp b/src/plugins/mcusupport/mcusupportsdk.cpp
index b805b4a91a..291f0647b2 100644
--- a/src/plugins/mcusupport/mcusupportsdk.cpp
+++ b/src/plugins/mcusupport/mcusupportsdk.cpp
@@ -49,15 +49,14 @@ namespace McuSupport {
namespace Internal {
namespace Sdk {
-static QString findInProgramFiles(const QString &folder)
+static FilePath findInProgramFiles(const QString &folder)
{
for (auto envVar : {"ProgramFiles", "ProgramFiles(x86)", "ProgramW6432"}) {
if (!qEnvironmentVariableIsSet(envVar))
continue;
- const Utils::FilePath dir =
- Utils::FilePath::fromUserInput(qEnvironmentVariable(envVar) + "/" + folder);
+ const FilePath dir = FilePath::fromUserInput(qEnvironmentVariable(envVar)) / folder;
if (dir.exists())
- return dir.toString();
+ return dir;
}
return {};
}
@@ -66,7 +65,7 @@ McuPackage *createQtForMCUsPackage()
{
auto result = new McuPackage(
McuPackage::tr("Qt for MCUs SDK"),
- QDir::homePath(),
+ FileUtils::homePath(),
FilePath("bin/qmltocpp").withExecutableSuffix().toString(),
Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK);
result->setEnvironmentVariableName("Qul_DIR");
@@ -92,22 +91,21 @@ static McuToolChainPackage *createArmGccPackage()
{
const char envVar[] = "ARMGCC_DIR";
- QString defaultPath;
+ FilePath defaultPath;
if (qEnvironmentVariableIsSet(envVar))
- defaultPath = qEnvironmentVariable(envVar);
- if (defaultPath.isEmpty() && Utils::HostOsInfo::isWindowsHost()) {
- const QDir installDir(findInProgramFiles("/GNU Tools ARM Embedded/"));
+ defaultPath = FilePath::fromUserInput(qEnvironmentVariable(envVar));
+ if (defaultPath.isEmpty() && HostOsInfo::isWindowsHost()) {
+ const FilePath installDir = findInProgramFiles("GNU Tools ARM Embedded");
if (installDir.exists()) {
// If GNU Tools installation dir has only one sub dir,
// select the sub dir, otherwise the installation dir.
- const QFileInfoList subDirs =
- installDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+ const FilePaths subDirs = installDir.dirEntries(QDir::Dirs | QDir::NoDotAndDotDot);
if (subDirs.count() == 1)
- defaultPath = subDirs.first().filePath() + '/';
+ defaultPath = subDirs.first();
}
}
if (defaultPath.isEmpty())
- defaultPath = QDir::homePath();
+ defaultPath = FileUtils::homePath();
const QString detectionPath = Utils::HostOsInfo::withExecutableSuffix("bin/arm-none-eabi-g++");
const auto versionDetector = new McuPackageExecutableVersionDetector(
@@ -131,8 +129,8 @@ static McuToolChainPackage *createGhsToolchainPackage()
{
const char envVar[] = "GHS_COMPILER_DIR";
- const QString defaultPath =
- qEnvironmentVariableIsSet(envVar) ? qEnvironmentVariable(envVar) : QDir::homePath();
+ const FilePath defaultPath = qEnvironmentVariableIsSet(envVar)
+ ? FilePath::fromUserInput(qEnvironmentVariable(envVar)) : FileUtils::homePath();
const auto versionDetector = new McuPackageExecutableVersionDetector(
Utils::HostOsInfo::withExecutableSuffix("as850"),
@@ -155,8 +153,8 @@ static McuToolChainPackage *createGhsArmToolchainPackage()
{
const char envVar[] = "GHS_ARM_COMPILER_DIR";
- const QString defaultPath =
- qEnvironmentVariableIsSet(envVar) ? qEnvironmentVariable(envVar) : QDir::homePath();
+ const FilePath defaultPath = qEnvironmentVariableIsSet(envVar)
+ ? FilePath::fromUserInput(qEnvironmentVariable(envVar)) : FileUtils::homePath();
const auto versionDetector = new McuPackageExecutableVersionDetector(
Utils::HostOsInfo::withExecutableSuffix("asarm"),
@@ -179,20 +177,20 @@ static McuToolChainPackage *createIarToolChainPackage()
{
const char envVar[] = "IAR_ARM_COMPILER_DIR";
- QString defaultPath;
+ FilePath defaultPath;
if (qEnvironmentVariableIsSet(envVar))
- defaultPath = qEnvironmentVariable(envVar);
+ defaultPath = FilePath::fromUserInput(qEnvironmentVariable(envVar));
else {
const ProjectExplorer::ToolChain *tc =
ProjectExplorer::ToolChainManager::toolChain([](const ProjectExplorer::ToolChain *t) {
return t->typeId() == BareMetal::Constants::IAREW_TOOLCHAIN_TYPEID;
});
if (tc) {
- const Utils::FilePath compilerExecPath = tc->compilerCommand();
- defaultPath = compilerExecPath.parentDir().parentDir().toString();
+ const FilePath compilerExecPath = tc->compilerCommand();
+ defaultPath = compilerExecPath.parentDir().parentDir();
}
else
- defaultPath = QDir::homePath();
+ defaultPath = FileUtils::homePath();
}
const QString detectionPath = Utils::HostOsInfo::withExecutableSuffix("bin/iccarm");
@@ -217,17 +215,17 @@ static McuPackage *createRGLPackage()
{
const char envVar[] = "RGL_DIR";
- QString defaultPath;
+ FilePath defaultPath;
if (qEnvironmentVariableIsSet(envVar)) {
- defaultPath = qEnvironmentVariable(envVar);
+ defaultPath = FilePath::fromUserInput(qEnvironmentVariable(envVar));
} else if (Utils::HostOsInfo::isWindowsHost()) {
- defaultPath = QDir::rootPath() + "Renesas_Electronics/D1x_RGL";
- if (QFileInfo::exists(defaultPath)) {
- const QFileInfoList subDirs =
- QDir(defaultPath).entryInfoList({QLatin1String("rgl_ghs_D1Mx_*")},
+ defaultPath = FilePath::fromUserInput(QDir::rootPath() + "Renesas_Electronics/D1x_RGL");
+ if (defaultPath.exists()) {
+ const FilePaths subDirs =
+ defaultPath.dirEntries({QLatin1String("rgl_ghs_D1Mx_*")},
QDir::Dirs | QDir::NoDotAndDotDot);
if (subDirs.count() == 1)
- defaultPath = subDirs.first().filePath() + '/';
+ defaultPath = subDirs.first();
}
}
@@ -242,15 +240,15 @@ static McuPackage *createRGLPackage()
static McuPackage *createStm32CubeProgrammerPackage()
{
- QString defaultPath = QDir::homePath();
- const QString cubePath = "/STMicroelectronics/STM32Cube/STM32CubeProgrammer/";
- if (Utils::HostOsInfo::isWindowsHost()) {
- const QString programPath = findInProgramFiles(cubePath);
+ FilePath defaultPath = FileUtils::homePath();
+ const QString cubePath = "STMicroelectronics/STM32Cube/STM32CubeProgrammer";
+ if (HostOsInfo::isWindowsHost()) {
+ const FilePath programPath = findInProgramFiles(cubePath);
if (!programPath.isEmpty())
defaultPath = programPath;
} else {
- const QString programPath = QDir::homePath() + cubePath;
- if (QFileInfo::exists(programPath))
+ const FilePath programPath = FileUtils::homePath() / cubePath;
+ if (programPath.exists())
defaultPath = programPath;
}
auto result = new McuPackage(
@@ -270,18 +268,18 @@ static McuPackage *createMcuXpressoIdePackage()
{
const char envVar[] = "MCUXpressoIDE_PATH";
- QString defaultPath;
+ FilePath defaultPath;
if (qEnvironmentVariableIsSet(envVar)) {
- defaultPath = qEnvironmentVariable(envVar);
- } else if (Utils::HostOsInfo::isWindowsHost()) {
- defaultPath = QDir::rootPath() + "nxp";
- if (QFileInfo::exists(defaultPath)) {
+ defaultPath = FilePath::fromUserInput(qEnvironmentVariable(envVar));
+ } else if (HostOsInfo::isWindowsHost()) {
+ defaultPath = FilePath::fromString(QDir::rootPath() + "nxp");
+ if (defaultPath.exists()) {
// If default dir has exactly one sub dir that could be the IDE path, pre-select that.
- const QFileInfoList subDirs =
- QDir(defaultPath).entryInfoList({QLatin1String("MCUXpressoIDE*")},
+ const FilePaths subDirs =
+ defaultPath.dirEntries({QLatin1String("MCUXpressoIDE*")},
QDir::Dirs | QDir::NoDotAndDotDot);
if (subDirs.count() == 1)
- defaultPath = subDirs.first().filePath() + '/';
+ defaultPath = subDirs.first();
}
} else {
defaultPath = "/usr/local/mcuxpressoide/";
@@ -301,21 +299,20 @@ static McuPackage *createCypressProgrammerPackage()
{
const char envVar[] = "CYPRESS_AUTO_FLASH_UTILITY_DIR";
- QString defaultPath;
+ FilePath defaultPath;
if (qEnvironmentVariableIsSet(envVar)) {
- defaultPath = qEnvironmentVariable(envVar);
- } else if (Utils::HostOsInfo::isWindowsHost()) {
- auto candidate = findInProgramFiles(QLatin1String("/Cypress/Cypress Auto Flash Utility 1.0/"));
- if (QFileInfo::exists(candidate)) {
+ defaultPath = FilePath::fromUserInput(qEnvironmentVariable(envVar));
+ } else if (HostOsInfo::isWindowsHost()) {
+ FilePath candidate = findInProgramFiles("Cypress/Cypress Auto Flash Utility 1.0");
+ if (candidate.exists()) {
defaultPath = candidate;
}
} else {
- defaultPath = QLatin1String("/usr");
+ defaultPath = "/usr";
}
- if (defaultPath.isEmpty()) {
- defaultPath = QDir::homePath();
- }
+ if (defaultPath.isEmpty())
+ defaultPath = FileUtils::homePath();
auto result = new McuPackage(
"Cypress Auto Flash Utility",
@@ -378,18 +375,16 @@ static McuPackage *createBoardSdkPackage(const McuTargetDescription& desc)
};
const QString sdkName = desc.boardSdkName.isEmpty() ? generateSdkName(desc.boardSdkEnvVar) : desc.boardSdkName;
- const QString defaultPath = [&] {
+ const FilePath defaultPath = [&] {
const auto envVar = desc.boardSdkEnvVar.toLatin1();
- if (qEnvironmentVariableIsSet(envVar)) {
- return qEnvironmentVariable(envVar);
- }
+ if (qEnvironmentVariableIsSet(envVar))
+ return FilePath::fromUserInput(qEnvironmentVariable(envVar));
if (!desc.boardSdkDefaultPath.isEmpty()) {
- QString defaultPath = QDir::rootPath() + desc.boardSdkDefaultPath;
- if (QFileInfo::exists(defaultPath)) {
+ FilePath defaultPath = FilePath::fromUserInput(QDir::rootPath() + desc.boardSdkDefaultPath);
+ if (defaultPath.exists())
return defaultPath;
- }
}
- return QDir::homePath();
+ return FileUtils::homePath();
}();
const auto versionDetector = generatePackageVersionDetector(desc.boardSdkEnvVar);
@@ -404,18 +399,18 @@ static McuPackage *createBoardSdkPackage(const McuTargetDescription& desc)
return result;
}
-static McuPackage *createFreeRTOSSourcesPackage(const QString &envVar, const QString &boardSdkDir,
+static McuPackage *createFreeRTOSSourcesPackage(const QString &envVar, const FilePath &boardSdkDir,
const QString &freeRTOSBoardSdkSubDir)
{
const QString envVarPrefix = envVar.chopped(int(strlen("_FREERTOS_DIR")));
- QString defaultPath;
+ FilePath defaultPath;
if (qEnvironmentVariableIsSet(envVar.toLatin1()))
- defaultPath = qEnvironmentVariable(envVar.toLatin1());
+ defaultPath = FilePath::fromUserInput(qEnvironmentVariable(envVar.toLatin1()));
else if (!boardSdkDir.isEmpty() && !freeRTOSBoardSdkSubDir.isEmpty())
- defaultPath = boardSdkDir + "/" + freeRTOSBoardSdkSubDir;
+ defaultPath = boardSdkDir / freeRTOSBoardSdkSubDir;
else
- defaultPath = QDir::homePath();
+ defaultPath = FileUtils::homePath();
auto result = new McuPackage(
QString::fromLatin1("FreeRTOS Sources (%1)").arg(envVarPrefix),
@@ -473,7 +468,7 @@ protected:
if (vendorPkgs.contains(desc.platformVendor))
required3rdPartyPkgs.push_back(vendorPkgs.value(desc.platformVendor));
- QString boardSdkDefaultPath;
+ FilePath boardSdkDefaultPath;
if (!desc.boardSdkEnvVar.isEmpty()) {
if (!boardSdkPkgs.contains(desc.boardSdkEnvVar)) {
auto boardSdkPkg = desc.boardSdkEnvVar != "RGL_DIR"
@@ -551,7 +546,7 @@ protected:
required3rdPartyPkgs.push_back(vendorPkgs.value(desc.platformVendor));
// Board SDK specific settings
- QString boardSdkDefaultPath;
+ FilePath boardSdkDefaultPath;
if (!desc.boardSdkEnvVar.isEmpty()) {
if (!boardSdkPkgs.contains(desc.boardSdkEnvVar)) {
auto boardSdkPkg = createBoardSdkPackage(desc);
diff --git a/src/plugins/projectexplorer/kitmanager.cpp b/src/plugins/projectexplorer/kitmanager.cpp
index 3f77fee414..1c338d730a 100644
--- a/src/plugins/projectexplorer/kitmanager.cpp
+++ b/src/plugins/projectexplorer/kitmanager.cpp
@@ -238,12 +238,15 @@ void KitManager::restoreKits()
kitsToCheck.clear();
// Remove replacement kits for which the original kit has turned up again.
- Utils::erase(resultList, [&resultList](const std::unique_ptr<Kit> &k) {
- return k->isReplacementKit()
- && contains(resultList, [&k](const std::unique_ptr<Kit> &other) {
- return other->id() == k->id() && other != k;
- });
- });
+ for (auto it = resultList.begin(); it != resultList.end();) {
+ const auto &k = *it;
+ if (k->isReplacementKit() && contains(resultList, [&k](const std::unique_ptr<Kit> &other) {
+ return other->id() == k->id() && other != k; })) {
+ it = resultList.erase(it);
+ } else {
+ ++it;
+ }
+ }
static const auto kitMatchesAbiList = [](const Kit *kit, const Abis &abis) {
const QList<ToolChain *> toolchains = ToolChainKitAspect::toolChains(kit);
diff --git a/src/plugins/projectexplorer/miniprojecttargetselector.cpp b/src/plugins/projectexplorer/miniprojecttargetselector.cpp
index b73d12c578..44fc5ae511 100644
--- a/src/plugins/projectexplorer/miniprojecttargetselector.cpp
+++ b/src/plugins/projectexplorer/miniprojecttargetselector.cpp
@@ -238,6 +238,18 @@ public:
protected:
void resetOptimalWidth()
{
+ if (m_resetScheduled)
+ return;
+ m_resetScheduled = true;
+ QMetaObject::invokeMethod(this, &SelectorView::doResetOptimalWidth, Qt::QueuedConnection);
+ }
+
+private:
+ void keyPressEvent(QKeyEvent *event) override;
+ void keyReleaseEvent(QKeyEvent *event) override;
+ void doResetOptimalWidth()
+ {
+ m_resetScheduled = false;
int width = 0;
QFontMetrics fn(font());
theModel()->forItemsAtLevel<1>([this, &width, &fn](const GenericItem *item) {
@@ -246,12 +258,9 @@ protected:
setOptimalWidth(width);
}
-private:
- void keyPressEvent(QKeyEvent *event) override;
- void keyReleaseEvent(QKeyEvent *event) override;
-
int m_maxCount = 0;
int m_optimalWidth = 0;
+ bool m_resetScheduled = false;
};
class ProjectListView : public SelectorView
diff --git a/src/plugins/qbsprojectmanager/qbsnodes.cpp b/src/plugins/qbsprojectmanager/qbsnodes.cpp
index 2a155d2128..2d6049b570 100644
--- a/src/plugins/qbsprojectmanager/qbsnodes.cpp
+++ b/src/plugins/qbsprojectmanager/qbsnodes.cpp
@@ -218,7 +218,7 @@ QVariant QbsProductNode::data(Id role) const
return m_productData.value("module-properties").toObject()
.value("Qt.core.enableKeywords").toBool();
- if (role == Android::Constants::ANDROID_ABIS) {
+ if (role == Android::Constants::AndroidAbis) {
// Try using qbs.architectures
QStringList qbsAbis;
QMap<QString, QString> archToAbi {
diff --git a/src/plugins/qmakeprojectmanager/profilecompletionassist.cpp b/src/plugins/qmakeprojectmanager/profilecompletionassist.cpp
index 419b4044de..ff08bb61b9 100644
--- a/src/plugins/qmakeprojectmanager/profilecompletionassist.cpp
+++ b/src/plugins/qmakeprojectmanager/profilecompletionassist.cpp
@@ -33,21 +33,22 @@ const TextEditor::Keywords &QmakeProjectManager::Internal::qmakeKeywords()
{
static TextEditor::Keywords keywords(
QStringList{ // variables
- "ANDROID_ABIS",
+ Android::Constants::ANDROID_ABI,
+ Android::Constants::ANDROID_ABIS,
"ANDROID_API_VERSION",
- QLatin1String(Android::Constants::ANDROID_APPLICATION_ARGUMENTS),
+ Android::Constants::ANDROID_APPLICATION_ARGUMENTS,
"ANDROID_BUNDLED_JAR_DEPENDENCIES",
"ANDROID_DEPLOYMENT_DEPENDENCIES",
- QLatin1String(Android::Constants::ANDROID_DEPLOYMENT_SETTINGS_FILE),
- QLatin1String(Android::Constants::ANDROID_EXTRA_LIBS),
+ Android::Constants::ANDROID_DEPLOYMENT_SETTINGS_FILE,
+ Android::Constants::ANDROID_EXTRA_LIBS,
"ANDROID_EXTRA_PLUGINS",
"ANDROID_FEATURES",
"ANDROID_LIB_DEPENDENCIES",
"ANDROID_MIN_SDK_VERSION",
- QLatin1String(Android::Constants::ANDROID_PACKAGE_SOURCE_DIR),
+ Android::Constants::ANDROID_PACKAGE_SOURCE_DIR,
"ANDROID_PERMISSIONS",
"ANDROID_TARGET_SDK_VERSION",
- "ANDROID_TARGET_ARCH",
+ Android::Constants::ANDROID_TARGET_ARCH,
"ANDROID_VERSION_CODE",
"ANDROID_VERSION_NAME",
"ARGC",
diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
index beef0a160d..6b28aa6b44 100644
--- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
@@ -157,7 +157,7 @@ QmakeBuildConfiguration::QmakeBuildConfiguration(Target *target, Utils::Id id)
if (DeviceTypeKitAspect::deviceTypeId(target->kit())
== Android::Constants::ANDROID_DEVICE_TYPE) {
- buildSteps()->appendStep(Android::Constants::ANDROID_PACKAGE_INSTALLATION_STEP_ID);
+ buildSteps()->appendStep(Android::Constants::ANDROID_PACKAGE_INSTALL_STEP_ID);
buildSteps()->appendStep(Android::Constants::ANDROID_BUILD_APK_ID);
}
diff --git a/src/plugins/qmakeprojectmanager/qmakenodes.cpp b/src/plugins/qmakeprojectmanager/qmakenodes.cpp
index 1214b79d64..46e95f0a68 100644
--- a/src/plugins/qmakeprojectmanager/qmakenodes.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakenodes.cpp
@@ -364,16 +364,16 @@ QStringList QmakeProFileNode::targetApplications() const
QVariant QmakeProFileNode::data(Utils::Id role) const
{
- if (role == Android::Constants::ANDROID_ABIS)
+ if (role == Android::Constants::AndroidAbis)
return variableValue(Variable::AndroidAbis);
+ if (role == Android::Constants::AndroidAbi)
+ return singleVariableValue(Variable::AndroidAbi);
+ if (role == Android::Constants::AndroidExtraLibs)
+ return variableValue(Variable::AndroidExtraLibs);
if (role == Android::Constants::AndroidPackageSourceDir)
return singleVariableValue(Variable::AndroidPackageSourceDir);
if (role == Android::Constants::AndroidDeploySettingsFile)
return singleVariableValue(Variable::AndroidDeploySettingsFile);
- if (role == Android::Constants::AndroidExtraLibs)
- return variableValue(Variable::AndroidExtraLibs);
- if (role == Android::Constants::AndroidArch)
- return singleVariableValue(Variable::AndroidArch);
if (role == Android::Constants::AndroidSoLibPath) {
TargetInformation info = targetInformation();
QStringList res = {info.buildDir.toString()};
@@ -431,8 +431,9 @@ bool QmakeProFileNode::setData(Utils::Id role, const QVariant &value) const
if (Target *target = m_buildSystem->target()) {
QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(target->kit());
if (version && !version->supportsMultipleQtAbis()) {
- const QString arch = pro->singleVariableValue(Variable::AndroidArch);
- scope = "contains(ANDROID_TARGET_ARCH," + arch + ')';
+ const QString arch = pro->singleVariableValue(Variable::AndroidAbi);
+ scope = QString("contains(%1,%2)").arg(Android::Constants::ANDROID_TARGET_ARCH)
+ .arg(arch);
flags |= QmakeProjectManager::Internal::ProWriter::MultiLine;
}
}
@@ -443,7 +444,7 @@ bool QmakeProFileNode::setData(Utils::Id role, const QVariant &value) const
if (role == Android::Constants::AndroidPackageSourceDir)
return pro->setProVariable(QLatin1String(Android::Constants::ANDROID_PACKAGE_SOURCE_DIR),
{value.toString()}, scope, flags);
- if (role == Android::Constants::ANDROID_APPLICATION_ARGUMENTS)
+ if (role == Android::Constants::AndroidApplicationArgs)
return pro->setProVariable(QLatin1String(Android::Constants::ANDROID_APPLICATION_ARGUMENTS),
{value.toString()}, scope, flags);
diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
index 6cc4634375..f64aecd296 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
@@ -1562,11 +1562,11 @@ QmakeEvalResult *QmakeProFile::evaluate(const QmakeEvalInput &input)
= exactReader->values(QLatin1String("TARGET_VERSION_EXT"));
result->newVarValues[Variable::StaticLibExtension] = exactReader->values(QLatin1String("QMAKE_EXTENSION_STATICLIB"));
result->newVarValues[Variable::ShLibExtension] = exactReader->values(QLatin1String("QMAKE_EXTENSION_SHLIB"));
- result->newVarValues[Variable::AndroidArch] = exactReader->values(QLatin1String("ANDROID_TARGET_ARCH"));
+ result->newVarValues[Variable::AndroidAbi] = exactReader->values(QLatin1String(Android::Constants::ANDROID_TARGET_ARCH));
result->newVarValues[Variable::AndroidDeploySettingsFile] = exactReader->values(QLatin1String(Android::Constants::ANDROID_DEPLOYMENT_SETTINGS_FILE));
result->newVarValues[Variable::AndroidPackageSourceDir] = exactReader->values(QLatin1String(Android::Constants::ANDROID_PACKAGE_SOURCE_DIR));
- result->newVarValues[Variable::AndroidAbis] = exactReader->values(QLatin1String("ANDROID_ABIS"));
- result->newVarValues[Variable::AndroidApplicationArguments] = exactReader->values(QLatin1String(Android::Constants::ANDROID_APPLICATION_ARGUMENTS));
+ result->newVarValues[Variable::AndroidAbis] = exactReader->values(QLatin1String(Android::Constants::ANDROID_ABIS));
+ result->newVarValues[Variable::AndroidApplicationArgs] = exactReader->values(QLatin1String(Android::Constants::ANDROID_APPLICATION_ARGUMENTS));
result->newVarValues[Variable::AndroidExtraLibs] = exactReader->values(QLatin1String(Android::Constants::ANDROID_EXTRA_LIBS));
result->newVarValues[Variable::AppmanPackageDir] = exactReader->values(QLatin1String("AM_PACKAGE_DIR"));
result->newVarValues[Variable::AppmanManifest] = exactReader->values(QLatin1String("AM_MANIFEST"));
diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
index fc90e0759d..cfa86b181f 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
+++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
@@ -96,12 +96,12 @@ enum class Variable {
TargetVersionExt,
StaticLibExtension,
ShLibExtension,
- AndroidArch,
- AndroidDeploySettingsFile,
+ AndroidAbi,
AndroidAbis,
+ AndroidDeploySettingsFile,
AndroidPackageSourceDir,
AndroidExtraLibs,
- AndroidApplicationArguments,
+ AndroidApplicationArgs,
AppmanPackageDir,
AppmanManifest,
IsoIcons,
diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp
index 793ec73ba8..54395bfc37 100644
--- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp
@@ -674,8 +674,9 @@ void QmakeBuildSystem::asyncUpdate()
connect(watcher, &QFutureWatcher<void>::canceled, this, [this, watcher] {
if (!m_qmakeGlobals)
return;
- watcher->disconnect();
m_qmakeGlobals->killProcesses();
+ watcher->disconnect();
+ watcher->deleteLater();
});
connect(watcher, &QFutureWatcher<void>::finished, this, [watcher] {
watcher->disconnect();
diff --git a/src/plugins/qmakeprojectmanager/qmakestep.cpp b/src/plugins/qmakeprojectmanager/qmakestep.cpp
index 7cc65532af..9e7c7f2230 100644
--- a/src/plugins/qmakeprojectmanager/qmakestep.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakestep.cpp
@@ -632,7 +632,7 @@ void QMakeStep::abisChanged()
if (BaseQtVersion *qtVersion = QtKitAspect::qtVersion(target()->kit())) {
if (qtVersion->hasAbi(Abi::LinuxOS, Abi::AndroidLinuxFlavor)) {
- const QString prefix = "ANDROID_ABIS=";
+ const QString prefix = QString("%1=").arg(Android::Constants::ANDROID_ABIS);
QStringList args = m_extraArgs;
for (auto it = args.begin(); it != args.end(); ++it) {
if (it->startsWith(prefix)) {
@@ -643,8 +643,7 @@ void QMakeStep::abisChanged()
if (!m_selectedAbis.isEmpty())
args << prefix + '"' + m_selectedAbis.join(' ') + '"';
setExtraArguments(args);
-
- buildSystem()->setProperty(Android::Constants::ANDROID_ABIS, m_selectedAbis);
+ buildSystem()->setProperty(Android::Constants::AndroidAbis, m_selectedAbis);
} else if (qtVersion->hasAbi(Abi::DarwinOS) && !isIos(target()->kit())) {
const QString prefix = "QMAKE_APPLE_DEVICE_ARCHS=";
QStringList args = m_extraArgs;
diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
index 4fd6fdf159..49505d2ca7 100644
--- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
+++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
@@ -222,6 +222,9 @@ const char addImagesDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources
const char addFontsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Font Files");
const char addSoundsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Sound Files");
const char addShadersDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Shader Files");
+const char add3DAssetsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "3D Assets");
+const char addQt3DSPresentationsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources",
+ "Qt 3D Studio Presentations");
const char addCustomEffectDialogDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Add Custom Effect");
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
index d452889363..8ca977e742 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
@@ -262,7 +262,7 @@ bool DesignerActionManager::externalDragHasSupportedAssets(const QMimeData *mime
return false;
}
-void DesignerActionManager::handleExternalAssetsDrop(const QMimeData *mimeData) const
+QHash<QString, QStringList> DesignerActionManager::handleExternalAssetsDrop(const QMimeData *mimeData) const
{
const QList<AddResourceHandler> handlers = addResourceHandler();
// create suffix to categry and category to operation hashes
@@ -283,13 +283,19 @@ void DesignerActionManager::handleExternalAssetsDrop(const QMimeData *mimeData)
categoryFiles[category].append(url.toLocalFile());
}
+ QHash<QString, QStringList> addedCategoryFiles;
+
// run operations
const QStringList categories = categoryFiles.keys();
for (const QString &category : categories) {
AddResourceOperation operation = categoryOperation.value(category);
QStringList files = categoryFiles.value(category);
- operation(files, {});
+ bool success = operation(files, {});
+ if (success)
+ addedCategoryFiles.insert(category, files);
}
+
+ return addedCategoryFiles;
}
class VisiblityModelNodeAction : public ModelNodeContextMenuAction
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
index e0fe93f601..60e2c7562f 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
@@ -137,7 +137,7 @@ public:
bool hasModelNodePreviewHandler(const ModelNode &node) const;
ModelNodePreviewImageOperation modelNodePreviewOperation(const ModelNode &node) const;
bool externalDragHasSupportedAssets(const QMimeData *data) const;
- void handleExternalAssetsDrop(const QMimeData *data) const;
+ QHash<QString, QStringList> handleExternalAssetsDrop(const QMimeData *data) const;
private:
void addTransitionEffectAction(const TypeName &typeName);
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
index 81066504de..ab2853354c 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
@@ -995,9 +995,8 @@ Utils::FilePath projectFilePath()
static bool addFilesToProject(const QStringList &fileNames, const QString &defaultDirectory)
{
QString directory = AddImagesDialog::getDirectory(fileNames, defaultDirectory);
-
if (directory.isEmpty())
- return true;
+ return false;
bool allSuccessful = true;
QList<QPair<QString, QString>> copyList;
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
index 025b622789..835ba07b5b 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
@@ -23,14 +23,15 @@
**
****************************************************************************/
-#include "edit3dwidget.h"
-#include "edit3dview.h"
-#include "edit3dcanvas.h"
-#include "edit3dactions.h"
-
-#include "qmldesignerplugin.h"
#include "designersettings.h"
+#include "edit3dactions.h"
+#include "edit3dcanvas.h"
+#include "edit3dview.h"
+#include "edit3dwidget.h"
+#include "metainfo.h"
#include "qmldesignerconstants.h"
+#include "qmldesignerplugin.h"
+#include "qmlvisualnode.h"
#include "viewmanager.h"
#include <coreplugin/actionmanager/actionmanager.h>
@@ -174,7 +175,20 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent)
{
const DesignerActionManager &actionManager = QmlDesignerPlugin::instance()
->viewManager().designerActionManager();
- actionManager.handleExternalAssetsDrop(dropEvent->mimeData());
+ QHash<QString, QStringList> addedAssets = actionManager.handleExternalAssetsDrop(dropEvent->mimeData());
+
+ // add 3D assets to 3d editor (QtQuick3D import will be added if missing)
+ ItemLibraryInfo *itemLibInfo = m_view->model()->metaInfo().itemLibraryInfo();
+
+ const QStringList added3DAssets = addedAssets.value(ComponentCoreConstants::add3DAssetsDisplayString);
+ for (const QString &assetPath : added3DAssets) {
+ QString fileName = QFileInfo(assetPath).baseName();
+ fileName = fileName.at(0).toUpper() + fileName.mid(1); // capitalize first letter
+ QString type = QString("Quick3DAssets.%1.%1").arg(fileName);
+ QList<ItemLibraryEntry> entriesForType = itemLibInfo->entriesForType(type.toLatin1());
+ if (!entriesForType.isEmpty()) // should always be true, but just in case
+ QmlVisualNode::createQml3DNode(view(), entriesForType.at(0), m_canvas->activeScene()).modelNode();
+ }
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
index 8200558d18..4f000e1295 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
@@ -99,8 +99,7 @@ FormEditorItem::FormEditorItem(const QmlItemNode &qmlItemNode, FormEditorScene*
m_borderWidth(1.0),
m_highlightBoundingRect(false),
m_blurContent(false),
- m_isContentVisible(true),
- m_isFormEditorVisible(true)
+ m_isContentVisible(true)
{
setCacheMode(QGraphicsItem::NoCache);
setup();
@@ -208,17 +207,6 @@ bool FormEditorItem::isContentVisible() const
return m_isContentVisible;
}
-
-bool FormEditorItem::isFormEditorVisible() const
-{
- return m_isFormEditorVisible;
-}
-void FormEditorItem::setFormEditorVisible(bool isVisible)
-{
- m_isFormEditorVisible = isVisible;
- setVisible(isVisible);
-}
-
QPointF FormEditorItem::center() const
{
return mapToScene(qmlItemNode().instanceBoundingRect().center());
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h
index 769488aa4a..2213f42a66 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h
+++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h
@@ -104,9 +104,6 @@ public:
void setContentVisible(bool visible);
bool isContentVisible() const;
- bool isFormEditorVisible() const;
- void setFormEditorVisible(bool isVisible);
-
QPointF center() const;
qreal selectionWeigth(const QPointF &point, int iteration);
@@ -152,7 +149,6 @@ private: // variables
bool m_highlightBoundingRect;
bool m_blurContent;
bool m_isContentVisible;
- bool m_isFormEditorVisible;
};
class FormEditorFlowItem : public FormEditorItem
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
index 0ee760b9a1..10e1373dde 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
@@ -91,6 +91,9 @@ void FormEditorView::modelAttached(Model *model)
//This function does the setup of the initial FormEditorItem tree in the scene
void FormEditorView::setupFormEditorItemTree(const QmlItemNode &qmlItemNode)
{
+ if (!qmlItemNode.hasFormEditorItem())
+ return;
+
if (qmlItemNode.isFlowTransition()) {
m_scene->addFormEditorItem(qmlItemNode, FormEditorScene::FlowTransition);
if (qmlItemNode.hasNodeParent())
@@ -199,16 +202,6 @@ void FormEditorView::removeNodeFromScene(const QmlItemNode &qmlItemNode)
m_currentTool->itemsAboutToRemoved(removedItemList);
}
-void FormEditorView::hideNodeFromScene(const QmlItemNode &qmlItemNode)
-{
- if (FormEditorItem *item = m_scene->itemForQmlItemNode(qmlItemNode)) {
- QList<FormEditorItem*> removedItems = scene()->itemsForQmlItemNodes(qmlItemNode.allSubModelNodes());
- removedItems.append(item);
- m_currentTool->itemsAboutToRemoved(removedItems);
- item->setFormEditorVisible(false);
- }
-}
-
void FormEditorView::createFormEditorWidget()
{
m_formEditorWidget = QPointer<FormEditorWidget>(new FormEditorWidget(this));
@@ -248,10 +241,7 @@ void FormEditorView::temporaryBlockView(int duration)
void FormEditorView::nodeCreated(const ModelNode &node)
{
- //If the node has source for components/custom parsers we ignore it.
- if (QmlItemNode::isValidQmlItemNode(node) && node.nodeSourceType() == ModelNode::NodeWithoutSource) //only setup QmlItems
- setupFormEditorItemTree(QmlItemNode(node));
- else if (QmlVisualNode::isFlowTransition(node))
+ if (QmlVisualNode::isFlowTransition(node))
setupFormEditorItemTree(QmlItemNode(node));
}
@@ -349,8 +339,26 @@ static inline bool hasNodeSourceParent(const ModelNode &node)
void FormEditorView::nodeReparented(const ModelNode &node, const NodeAbstractProperty &/*newPropertyParent*/, const NodeAbstractProperty &/*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/)
{
- if (hasNodeSourceParent(node))
- hideNodeFromScene(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);
+ 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(), {});
+ }
+ }
}
WidgetInfo FormEditorView::widgetInfo()
@@ -603,8 +611,7 @@ void FormEditorView::auxiliaryDataChanged(const ModelNode &node, const PropertyN
if (name == "invisible") {
if (FormEditorItem *item = scene()->itemForQmlItemNode(QmlItemNode(node))) {
bool isInvisible = data.toBool();
- if (item->isFormEditorVisible())
- item->setVisible(!isInvisible);
+ item->setVisible(!isInvisible);
ModelNode newNode(node);
if (isInvisible)
newNode.deselectNode();
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.h b/src/plugins/qmldesigner/components/formeditor/formeditorview.h
index ba2b79df99..04b7d1e83e 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorview.h
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.h
@@ -144,7 +144,6 @@ protected:
private:
void setupFormEditorItemTree(const QmlItemNode &qmlItemNode);
void removeNodeFromScene(const QmlItemNode &qmlItemNode);
- void hideNodeFromScene(const QmlItemNode &qmlItemNode);
void createFormEditorWidget();
void temporaryBlockView(int duration = 1000);
void resetNodeInstanceView();
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp
index 1f9fc4487b..6b81d1f92e 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp
@@ -597,7 +597,14 @@ void FormEditorWidget::dropEvent(QDropEvent *dropEvent)
{
const DesignerActionManager &actionManager = QmlDesignerPlugin::instance()
->viewManager().designerActionManager();
- actionManager.handleExternalAssetsDrop(dropEvent->mimeData());
+ QHash<QString, QStringList> addedAssets = actionManager.handleExternalAssetsDrop(dropEvent->mimeData());
+
+ // add image assets to Form Editor
+ const QStringList addedImages = addedAssets.value(ComponentCoreConstants::addImagesDisplayString);
+ for (const QString &imgPath : addedImages) {
+ QmlItemNode::createQmlItemNodeFromImage(m_formEditorView, imgPath, {},
+ m_formEditorView->scene()->rootFormEditorItem()->qmlItemNode());
+ }
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp b/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp
index 7230e97a1c..f240b32f76 100644
--- a/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp
@@ -34,6 +34,8 @@
#include <nodemetainfo.h>
+#include <utils/algorithm.h>
+
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
@@ -242,9 +244,23 @@ void SelectionTool::dragMoveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGr
{
}
-void SelectionTool::itemsAboutToRemoved(const QList<FormEditorItem*> &/*itemList*/)
+void SelectionTool::itemsAboutToRemoved(const QList<FormEditorItem*> &itemList)
{
-
+ const QList<FormEditorItem *> current = items();
+
+ QList<FormEditorItem *> remaining = Utils::filtered(current, [&itemList](FormEditorItem *item) {
+ return !itemList.contains(item);
+ });
+
+ if (!remaining.isEmpty()) {
+ m_selectionIndicator.setItems(remaining);
+ m_resizeIndicator.setItems(remaining);
+ m_rotationIndicator.setItems(remaining);
+ m_anchorIndicator.setItems(remaining);
+ m_bindingIndicator.setItems(remaining);
+ } else {
+ clear();
+ }
}
void SelectionTool::clear()
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
index 3bf7447a34..06b84d83f8 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
@@ -240,35 +240,38 @@ void ItemLibraryView::updateImport3DSupport(const QVariantMap &supportMap)
DesignerActionManager *actionManager =
&QmlDesignerPlugin::instance()->viewManager().designerActionManager();
- // All things importable by QSSGAssetImportManager are considered to be in the same category
- // so we don't get multiple separate import dialogs when different file types are imported.
- const QString category = tr("3D Assets");
-
if (!m_importableExtensions3DMap.isEmpty())
- actionManager->unregisterAddResourceHandlers(category);
+ actionManager->unregisterAddResourceHandlers(ComponentCoreConstants::add3DAssetsDisplayString);
m_importableExtensions3DMap = extMap;
- auto handle3DModel = [this](const QStringList &fileNames, const QString &defaultDir) -> bool {
+ auto import3DModelOperation = [this](const QStringList &fileNames, const QString &defaultDir) -> bool {
auto importDlg = new ItemLibraryAssetImportDialog(fileNames, defaultDir,
m_importableExtensions3DMap,
m_importOptions3DMap, {}, {},
Core::ICore::mainWindow());
- importDlg->show();
+ importDlg->exec();
return true;
};
- auto add3DHandler = [&](const QString &category, const QString &ext) {
+ auto add3DHandler = [&](const QString &group, const QString &ext) {
const QString filter = QStringLiteral("*.%1").arg(ext);
actionManager->registerAddResourceHandler(
- AddResourceHandler(category, filter, handle3DModel, 10));
+ AddResourceHandler(group, filter,
+ import3DModelOperation, 10));
+ };
+
+ const QHash<QString, QString> groupNames {
+ {"3D Scene", ComponentCoreConstants::add3DAssetsDisplayString},
+ {"Qt 3D Studio Presentation", ComponentCoreConstants::addQt3DSPresentationsDisplayString}
};
const auto groups = extMap.keys();
for (const auto &group : groups) {
const QStringList exts = extMap[group].toStringList();
+ const QString grp = groupNames.contains(group) ? groupNames.value(group) : group;
for (const auto &ext : exts)
- add3DHandler(category, ext);
+ add3DHandler(grp, ext);
}
}
diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
index 5abc38eba6..08dfc9fd03 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
@@ -732,17 +732,22 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in
ModelNode targetNode = targetProperty.parentModelNode();
NodeMetaInfo metaInfo = targetNode.metaInfo();
TypeName typeName = newModelNode.type();
- const PropertyNameList nameList = targetNode.metaInfo().directPropertyNames();
- for (const auto &propertyName : nameList) {
- auto testType = metaInfo.propertyTypeName(propertyName);
- if (testType == typeName || newModelNode.isSubclassOf(testType)) {
- ChooseFromPropertyListDialog *dialog = nullptr;
- dialog = new ChooseFromPropertyListDialog(targetNode, testType, Core::ICore::dialogParent());
- dialog->exec();
- if (dialog->result() == QDialog::Accepted)
- targetNode.bindingProperty(dialog->selectedProperty()).setExpression(newModelNode.validId());
- delete dialog;
- break;
+
+ // Empty components are not supported and having one as property value is generally
+ // unstable, so let's not offer user to put a fresh Component into a property
+ if (typeName != "QtQml.Component") {
+ const PropertyNameList nameList = targetNode.metaInfo().directPropertyNames();
+ for (const auto &propertyName : nameList) {
+ auto testType = metaInfo.propertyTypeName(propertyName);
+ if (testType == typeName || newModelNode.isSubclassOf(testType)) {
+ ChooseFromPropertyListDialog *dialog = nullptr;
+ dialog = new ChooseFromPropertyListDialog(targetNode, testType, Core::ICore::dialogParent());
+ dialog->exec();
+ if (dialog->result() == QDialog::Accepted)
+ targetNode.bindingProperty(dialog->selectedProperty()).setExpression(newModelNode.validId());
+ delete dialog;
+ break;
+ }
}
}
}
@@ -1085,10 +1090,24 @@ void NavigatorTreeModel::moveNodesInteractive(NodeAbstractProperty &parentProper
if (modelNode.isValid()
&& modelNode != parentProperty.parentModelNode()
&& !modelNode.isAncestorOf(parentProperty.parentModelNode())
- && (modelNode.metaInfo().isSubclassOf(propertyQmlType) || propertyQmlType == "alias")) {
+ && (modelNode.metaInfo().isSubclassOf(propertyQmlType)
+ || propertyQmlType == "alias"
+ || parentProperty.name() == "data"
+ || (parentProperty.parentModelNode().metaInfo().defaultPropertyName() == parentProperty.name()
+ && propertyQmlType == "<cpp>.QQmlComponent"))) {
//### todo: allowing alias is just a heuristic
//once the MetaInfo is part of instances we can do this right
+ // We assume above that "data" property in parent accepts all types.
+ // This is a workaround for Component parents to accept children, even though they
+ // do not have an actual "data" property or apparently any other default property.
+ // When the actual reparenting happens, model will create the "data" property if
+ // it is missing.
+
+ // We allow move even if target property type doesn't match, if the target property
+ // is the default property of the parent and is of Component type.
+ // In that case an implicit component will be created.
+
bool nodeCanBeMovedToParentProperty = removeModelNodeFromNodeProperty(parentProperty, modelNode);
if (nodeCanBeMovedToParentProperty) {
reparentModelNodeToNodeProperty(parentProperty, modelNode);
diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp
index aecab7c42c..2664f2c7a9 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp
@@ -861,6 +861,7 @@ void QmlAnchorBindingProxy::anchorVertical()
m_qmlItemNode.anchors().setAnchor(AnchorLineVerticalCenter, m_verticalTarget, AnchorLineVerticalCenter);
}
+ backupPropertyAndRemove(modelNode(), "y");
m_locked = false;
}
@@ -874,6 +875,7 @@ void QmlAnchorBindingProxy::anchorHorizontal()
} else if (m_relativeVerticalTarget == Center) {
m_qmlItemNode.anchors().setAnchor(AnchorLineHorizontalCenter, m_horizontalTarget, AnchorLineHorizontalCenter);
}
+ backupPropertyAndRemove(modelNode(), "x");
m_locked = false;
}
@@ -993,6 +995,7 @@ void QmlAnchorBindingProxy::setVerticalCentered(bool centered)
if (!centered) {
m_qmlItemNode.anchors().removeAnchor(AnchorLineVerticalCenter);
m_qmlItemNode.anchors().removeMargin(AnchorLineVerticalCenter);
+ restoreProperty(m_qmlItemNode, "y");
} else {
m_relativeVerticalTarget = Center;
@@ -1020,6 +1023,7 @@ void QmlAnchorBindingProxy::setHorizontalCentered(bool centered)
if (!centered) {
m_qmlItemNode.anchors().removeAnchor(AnchorLineHorizontalCenter);
m_qmlItemNode.anchors().removeMargin(AnchorLineHorizontalCenter);
+ restoreProperty(m_qmlItemNode, "x");
} else {
m_relativeHorizontalTarget = Center;
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp
index 12fff40c79..997852cfb2 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp
@@ -46,6 +46,7 @@
#include <qmlstate.h>
#include <annotationeditor/annotationeditor.h>
#include <utils/algorithm.h>
+#include <utils/qtcassert.h>
namespace QmlDesigner {
@@ -103,6 +104,7 @@ void StatesEditorView::removeState(int nodeId)
const auto propertyChanges = modelState.propertyChanges();
for (const QmlPropertyChanges &change : propertyChanges) {
const ModelNode target = change.target();
+ QTC_ASSERT(target.isValid(), continue);
if (target.locked())
lockedTargets.push_back(target.id());
}
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp
index 704d7c2b0d..ad2dfd88f6 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp
@@ -70,14 +70,6 @@
namespace QmlDesigner {
-static int deleteKey()
-{
- if (Utils::HostOsInfo::isMacHost())
- return Qt::Key_Backspace;
-
- return Qt::Key_Delete;
-}
-
QList<QmlTimelineKeyframeGroup> allTimelineFrames(const QmlTimeline &timeline)
{
QList<QmlTimelineKeyframeGroup> returnList;
@@ -674,7 +666,7 @@ void TimelineGraphicsScene::keyReleaseEvent(QKeyEvent *keyEvent)
return;
}
- if (deleteKey() == keyEvent->key())
+ if (TimelineUtils::isDeleteKey(keyEvent->key()))
handleKeyframeDeletion();
QGraphicsScene::keyReleaseEvent(keyEvent);
@@ -838,7 +830,7 @@ bool TimelineGraphicsScene::event(QEvent *event)
{
switch (event->type()) {
case QEvent::ShortcutOverride:
- if (static_cast<QKeyEvent *>(event)->key() == deleteKey()) {
+ if (TimelineUtils::isDeleteKey(static_cast<QKeyEvent *>(event)->key())) {
QGraphicsScene::keyPressEvent(static_cast<QKeyEvent *>(event));
event->accept();
return true;
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineutils.h b/src/plugins/qmldesigner/components/timelineeditor/timelineutils.h
index ec0f4cec5b..b31bd8f422 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineutils.h
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineutils.h
@@ -39,6 +39,11 @@ namespace TimelineUtils {
enum class Side { Top, Right, Bottom, Left };
+inline bool isDeleteKey(int key)
+{
+ return (key == Qt::Key_Backspace) || (key == Qt::Key_Delete);
+}
+
template<typename T>
inline T clamp(const T &value, const T &lo, const T &hi)
{
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp
index ab82744cc6..e2b9578356 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp
@@ -39,6 +39,7 @@
#include "timelineplaceholder.h"
#include "timelinepropertyitem.h"
#include "timelinesectionitem.h"
+#include "timelineutils.h"
#include <designdocumentview.h>
#include <exception.h>
@@ -73,14 +74,6 @@
namespace QmlDesigner {
-static int deleteKey()
-{
- if (Utils::HostOsInfo::isMacHost())
- return Qt::Key_Backspace;
-
- return Qt::Key_Delete;
-}
-
TransitionEditorGraphicsScene::TransitionEditorGraphicsScene(TransitionEditorWidget *parent)
: AbstractScrollGraphicsScene(parent)
, m_parent(parent)
@@ -459,7 +452,7 @@ bool TransitionEditorGraphicsScene::event(QEvent *event)
{
switch (event->type()) {
case QEvent::ShortcutOverride:
- if (static_cast<QKeyEvent *>(event)->key() == deleteKey()) {
+ if (TimelineUtils::isDeleteKey(static_cast<QKeyEvent *>(event)->key())) {
QGraphicsScene::keyPressEvent(static_cast<QKeyEvent *>(event));
event->accept();
return true;
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
index 97dc457f39..ccf84152b5 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
@@ -98,6 +98,10 @@ void TransitionEditorView::nodeRemoved(const ModelNode & removedNode,
{
if (parentProperty.name() == "transitions")
widget()->updateData(removedNode);
+
+ const ModelNode parent = parentProperty.parentModelNode();
+ if (parent.isValid() && parent.metaInfo().isSubclassOf("QtQuick.Transition"))
+ asyncUpdate(parent);
}
void TransitionEditorView::nodeReparented(const ModelNode &node,
@@ -110,7 +114,6 @@ void TransitionEditorView::nodeReparented(const ModelNode &node,
const ModelNode parent = newPropertyParent.parentModelNode();
- // qDebug() << Q_FUNC_INFO << parent;
if (parent.isValid() && parent.metaInfo().isValid()
&& parent.metaInfo().isSubclassOf("QtQuick.Transition")) {
asyncUpdate(parent);
diff --git a/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h b/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h
index c44467f002..8244fbb489 100644
--- a/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h
+++ b/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h
@@ -100,7 +100,8 @@ class QMLDESIGNERCORE_EXPORT ItemLibraryInfo : public QObject
public:
QList<ItemLibraryEntry> entries() const;
- QList<ItemLibraryEntry> entriesForType(const QByteArray &typeName, int majorVersion, int minorVersion) const;
+ QList<ItemLibraryEntry> entriesForType(const QByteArray &typeName, int majorVersion = 1,
+ int minorVersion = 0) const;
void addEntries(const QList<ItemLibraryEntry> &entries, bool overwriteDuplicate = false);
bool containsEntry(const ItemLibraryEntry &entry);
diff --git a/src/plugins/qmldesigner/designercore/include/nodehints.h b/src/plugins/qmldesigner/designercore/include/nodehints.h
index a22dd40f40..16fe18320c 100644
--- a/src/plugins/qmldesigner/designercore/include/nodehints.h
+++ b/src/plugins/qmldesigner/designercore/include/nodehints.h
@@ -62,6 +62,7 @@ public:
bool canBeDroppedInView3D() const;
bool isMovable() const;
bool isResizable() const;
+ bool hasFormEditorItem() const;
bool isStackedContainer() const;
bool canBeReparentedTo(const ModelNode &potenialParent);
QString indexPropertyForStackedContainer() const;
diff --git a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h
index 579a4ca7b8..9be07edfbf 100644
--- a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h
+++ b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h
@@ -105,6 +105,7 @@ public:
bool modelIsResizable() const;
bool modelIsRotatable() const;
bool modelIsInLayout() const;
+ bool hasFormEditorItem() const;
QRectF instanceBoundingRect() const;
QRectF instanceSceneBoundingRect() const;
diff --git a/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h b/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h
index 59f99bbe39..7b1cfbe86e 100644
--- a/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h
+++ b/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h
@@ -128,14 +128,13 @@ public:
virtual bool isBlocked(const PropertyName &propName) const;
friend auto qHash(const QmlObjectNode &node) { return qHash(node.modelNode()); }
+ QList<QmlModelState> allDefinedStates() const;
+ QList<QmlModelStateOperation> allInvalidStateOperations() const;
protected:
NodeInstance nodeInstance() const;
QmlObjectNode nodeForInstance(const NodeInstance &instance) const;
QmlItemNode itemForInstance(const NodeInstance &instance) const;
-
-protected:
- QList<QmlModelState> allDefinedStates() const;
};
QMLDESIGNERCORE_EXPORT QList<ModelNode> toModelNodeList(const QList<QmlObjectNode> &fxObjectNodeList);
diff --git a/src/plugins/qmldesigner/designercore/include/qmlstate.h b/src/plugins/qmldesigner/designercore/include/qmlstate.h
index bd0aad854b..ce9a8bf3f7 100644
--- a/src/plugins/qmldesigner/designercore/include/qmlstate.h
+++ b/src/plugins/qmldesigner/designercore/include/qmlstate.h
@@ -50,6 +50,7 @@ public:
QList<QmlModelStateOperation> stateOperations(const ModelNode &node) const;
QList<QmlPropertyChanges> propertyChanges() const;
QList<QmlModelStateOperation> stateOperations() const;
+ QList<QmlModelStateOperation> allInvalidStateOperations() const;
bool hasPropertyChanges(const ModelNode &node) const;
diff --git a/src/plugins/qmldesigner/designercore/include/qmltimelinekeyframegroup.h b/src/plugins/qmldesigner/designercore/include/qmltimelinekeyframegroup.h
index a20eeae55e..6aee5ea0d5 100644
--- a/src/plugins/qmldesigner/designercore/include/qmltimelinekeyframegroup.h
+++ b/src/plugins/qmldesigner/designercore/include/qmltimelinekeyframegroup.h
@@ -72,6 +72,7 @@ public:
static bool isValidKeyframe(const ModelNode &node);
static bool checkKeyframesType(const ModelNode &node);
static QmlTimelineKeyframeGroup keyframeGroupForKeyframe(const ModelNode &node);
+ static QList<QmlTimelineKeyframeGroup> allInvalidTimelineKeyframeGroups(AbstractView *view);
void moveAllKeyframes(qreal offset);
void scaleAllKeyframes(qreal factor);
@@ -84,6 +85,8 @@ public:
void toogleRecording(bool b) const;
QmlTimeline timeline() const;
+
+ bool isDangling() const;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
index 6ecde66a30..078a74a534 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
@@ -644,7 +644,7 @@ void NodeInstanceView::nodeSourceChanged(const ModelNode &node, const QString &
m_nodeInstanceServer->changeNodeSource(changeNodeSourceCommand);
// Puppet doesn't deal with node source changes properly, so just reset the puppet for now
- delayedRestartProcess(); // TODO: Remove this once the issue is properly fixed (QDS-4955)
+ resetPuppet(); // TODO: Remove this once the issue is properly fixed (QDS-4955)
}
}
diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp
index 26b06cdc42..84cc565074 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp
@@ -183,6 +183,11 @@ bool NodeHints::isResizable() const
return evaluateBooleanExpression("isResizable", true);
}
+bool NodeHints::hasFormEditorItem() const
+{
+ return evaluateBooleanExpression("hasFormEditorItem", true);
+}
+
bool NodeHints::isStackedContainer() const
{
if (!isValid())
diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp
index 03ae0ecd1d..02ecb64efd 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp
@@ -395,6 +395,11 @@ bool QmlItemNode::modelIsInLayout() const
return false;
}
+bool QmlItemNode::hasFormEditorItem() const
+{
+ return NodeHints::fromModelNode(modelNode()).hasFormEditorItem();
+}
+
QRectF QmlItemNode::instanceBoundingRect() const
{
return QRectF(QPointF(0, 0), nodeInstance().size());
diff --git a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp
index 556a7624ba..55305c72d1 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp
@@ -328,16 +328,58 @@ QmlPropertyChanges QmlObjectNode::propertyChangeForCurrentState() const
static void removeStateOperationsForChildren(const QmlObjectNode &node)
{
if (node.isValid()) {
- foreach (QmlModelStateOperation stateOperation, node.allAffectingStatesOperations()) {
+ for (QmlModelStateOperation stateOperation : node.allAffectingStatesOperations()) {
stateOperation.modelNode().destroy(); //remove of belonging StatesOperations
}
- foreach (const QmlObjectNode &childNode, node.modelNode().directSubModelNodes()) {
+ for (const QmlObjectNode &childNode : node.modelNode().directSubModelNodes()) {
removeStateOperationsForChildren(childNode);
}
}
}
+static void removeAnimationsFromAnimation(const ModelNode &animation)
+{
+ QTC_ASSERT(animation.isValid(), return);
+
+ const QList<ModelNode> propertyAnimations = animation.subModelNodesOfType(
+ "QtQuick.PropertyAnimation");
+
+ for (const ModelNode &child : propertyAnimations) {
+ if (!child.hasBindingProperty("target")) {
+ ModelNode nonConst = animation;
+ nonConst.destroy();
+ return;
+ }
+ }
+}
+
+static void removeAnimationsFromTransition(const ModelNode &transition, const QmlObjectNode &node)
+{
+ QTC_ASSERT(node.isValid(), return);
+ QTC_ASSERT(transition.isValid(), return);
+
+ const auto children = transition.directSubModelNodes();
+ for (const ModelNode &parallel : children)
+ removeAnimationsFromAnimation(parallel);
+}
+
+static void removeDanglingAnimationsFromTransitions(const QmlObjectNode &node)
+{
+ QTC_ASSERT(node.isValid(), return);
+
+ auto root = node.view()->rootModelNode();
+
+ if (root.isValid() && root.hasProperty("transitions")) {
+ NodeAbstractProperty transitions = root.nodeAbstractProperty("transitions");
+ if (transitions.isValid()) {
+ const auto transitionNodes = transitions.directSubNodes();
+ for (const auto &transition : transitionNodes)
+ removeAnimationsFromTransition(transition, node);
+ }
+ }
+}
+
static void removeAliasExports(const QmlObjectNode &node)
{
@@ -368,6 +410,14 @@ static void removeLayerEnabled(const ModelNode &node)
}
}
+static void deleteAllReferencesToNodeAndChildren(const ModelNode &node)
+{
+ BindingProperty::deleteAllReferencesTo(node);
+ const auto subNodes = node.allSubModelNodes();
+ for (const ModelNode &child : subNodes)
+ BindingProperty::deleteAllReferencesTo(child);
+}
+
/*!
Deletes this object's node and its dependencies from the model.
Everything that belongs to this Object, the ModelNode, and ChangeOperations
@@ -406,7 +456,9 @@ void QmlObjectNode::destroy()
}
removeStateOperationsForChildren(modelNode());
- BindingProperty::deleteAllReferencesTo(modelNode());
+ deleteAllReferencesToNodeAndChildren(modelNode());
+
+ removeDanglingAnimationsFromTransitions(modelNode());
QmlFlowViewNode root(view()->rootModelNode());
@@ -515,6 +567,16 @@ QList<QmlModelState> QmlObjectNode::allDefinedStates() const
return returnList;
}
+QList<QmlModelStateOperation> QmlObjectNode::allInvalidStateOperations() const
+{
+ QList<QmlModelStateOperation> result;
+
+ const auto allStates = allDefinedStates();
+ for (const auto &state : allStates)
+ result.append(state.allInvalidStateOperations());
+ return result;
+}
+
/*!
Removes a variant property of the object specified by \a name from the
diff --git a/src/plugins/qmldesigner/designercore/model/qmlstate.cpp b/src/plugins/qmldesigner/designercore/model/qmlstate.cpp
index ea15ba1d81..26fff118d2 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlstate.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlstate.cpp
@@ -34,6 +34,7 @@
#include "qmlitemnode.h"
#include "annotation.h"
+#include <utils/algorithm.h>
#include <utils/qtcassert.h>
namespace QmlDesigner {
@@ -136,6 +137,12 @@ QList<QmlModelStateOperation> QmlModelState::stateOperations() const
return returnList;
}
+QList<QmlModelStateOperation> QmlModelState::allInvalidStateOperations() const
+{
+ return Utils::filtered(stateOperations(), [](const QmlModelStateOperation &operation) {
+ return !operation.target().isValid();
+ });
+}
/*!
Adds a change set for \a node to this state, but only if it does not
diff --git a/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp b/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp
index ad0855ad95..85eb51de44 100644
--- a/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp
@@ -153,6 +153,13 @@ QmlTimeline QmlTimelineKeyframeGroup::timeline() const
return {};
}
+bool QmlTimelineKeyframeGroup::isDangling() const
+{
+ QTC_ASSERT(isValid(), return false);
+
+ return !target().isValid() || keyframes().isEmpty();
+}
+
void QmlTimelineKeyframeGroup::setValue(const QVariant &value, qreal currentFrame)
{
QTC_ASSERT(isValid(), return );
@@ -294,6 +301,22 @@ QmlTimelineKeyframeGroup QmlTimelineKeyframeGroup::keyframeGroupForKeyframe(cons
return QmlTimelineKeyframeGroup();
}
+QList<QmlTimelineKeyframeGroup> QmlTimelineKeyframeGroup::allInvalidTimelineKeyframeGroups(AbstractView *view)
+{
+ QList<QmlTimelineKeyframeGroup> ret;
+
+ QTC_ASSERT(view, return ret);
+ QTC_ASSERT(view->model(), return ret);
+ QTC_ASSERT(view->rootModelNode().isValid(), return ret);
+
+ const auto groups = view->rootModelNode().subModelNodesOfType("QtQuick.Timeline.KeyframeGroup");
+ for (const QmlTimelineKeyframeGroup &group : groups) {
+ if (group.isDangling())
+ ret.append(group);
+ }
+ return ret;
+}
+
void QmlTimelineKeyframeGroup::moveAllKeyframes(qreal offset)
{
for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) {
diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp
index 802740fc1d..544471b03c 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp
@@ -309,7 +309,11 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view,
}
}
- newQmlObjectNode = QmlObjectNode(view->createModelNode(itemLibraryEntry.typeName(), majorVersion, minorVersion, propertyPairList));
+ ModelNode::NodeSourceType nodeSourceType = ModelNode::NodeWithoutSource;
+ if (itemLibraryEntry.typeName() == "QtQml.Component")
+ nodeSourceType = ModelNode::NodeWithComponentSource;
+
+ newQmlObjectNode = QmlObjectNode(view->createModelNode(itemLibraryEntry.typeName(), majorVersion, minorVersion, propertyPairList, {}, {}, nodeSourceType));
} else {
newQmlObjectNode = createQmlObjectNodeFromSource(view, itemLibraryEntry.qmlSource(), position);
}
diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
index 0209c7a0a7..c678f3b06d 100644
--- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
+++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
@@ -1191,9 +1191,11 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
if (isComponentType(typeName) || isImplicitComponent)
setupComponentDelayed(modelNode, differenceHandler.isAmender());
-
- if (isCustomParserType(typeName))
+ else if (isCustomParserType(typeName))
setupCustomParserNodeDelayed(modelNode, differenceHandler.isAmender());
+ else if (!modelNode.nodeSource().isEmpty() || modelNode.nodeSourceType() != ModelNode::NodeWithoutSource)
+ clearImplicitComponentDelayed(modelNode, differenceHandler.isAmender());
+
context->enterScope(astNode);
@@ -2079,18 +2081,23 @@ void TextToModelMerger::setupComponent(const ModelNode &node)
QString componentText = m_rewriterView->extractText({node}).value(node);
- if (componentText.isEmpty())
+ if (componentText.isEmpty() && node.nodeSource().isEmpty())
return;
QString result = extractComponentFromQml(componentText);
- if (result.isEmpty())
+ if (result.isEmpty() && node.nodeSource().isEmpty())
return; //No object definition found
if (node.nodeSource() != result)
ModelNode(node).setNodeSource(result, ModelNode::NodeWithComponentSource);
}
+void TextToModelMerger::clearImplicitComponent(const ModelNode &node)
+{
+ ModelNode(node).setNodeSource({}, ModelNode::NodeWithoutSource);
+}
+
void TextToModelMerger::collectLinkErrors(QList<DocumentMessage> *errors, const ReadingContext &ctxt)
{
foreach (const QmlJS::DiagnosticMessage &diagnosticMessage, ctxt.diagnosticLinkMessages()) {
@@ -2237,9 +2244,9 @@ void TextToModelMerger::addIsoIconQrcMapping(const QUrl &fileUrl)
} while (dir.cdUp());
}
-void TextToModelMerger::setupComponentDelayed(const ModelNode &node, bool synchron)
+void TextToModelMerger::setupComponentDelayed(const ModelNode &node, bool synchronous)
{
- if (synchron) {
+ if (synchronous) {
setupComponent(node);
} else {
m_setupComponentList.insert(node);
@@ -2254,7 +2261,7 @@ void TextToModelMerger::setupCustomParserNode(const ModelNode &node)
QString modelText = m_rewriterView->extractText({node}).value(node);
- if (modelText.isEmpty())
+ if (modelText.isEmpty() && node.nodeSource().isEmpty())
return;
if (node.nodeSource() != modelText)
@@ -2262,11 +2269,11 @@ void TextToModelMerger::setupCustomParserNode(const ModelNode &node)
}
-void TextToModelMerger::setupCustomParserNodeDelayed(const ModelNode &node, bool synchron)
+void TextToModelMerger::setupCustomParserNodeDelayed(const ModelNode &node, bool synchronous)
{
Q_ASSERT(isCustomParserType(node.type()));
- if (synchron) {
+ if (synchronous) {
setupCustomParserNode(node);
} else {
m_setupCustomParserList.insert(node);
@@ -2274,15 +2281,32 @@ void TextToModelMerger::setupCustomParserNodeDelayed(const ModelNode &node, bool
}
}
+void TextToModelMerger::clearImplicitComponentDelayed(const ModelNode &node, bool synchronous)
+{
+ Q_ASSERT(!isComponentType(node.type()));
+
+ if (synchronous) {
+ clearImplicitComponent(node);
+ } else {
+ m_clearImplicitComponentList.insert(node);
+ m_setupTimer.start();
+ }
+}
+
void TextToModelMerger::delayedSetup()
{
- foreach (const ModelNode node, m_setupComponentList)
+ for (const ModelNode &node : std::as_const(m_setupComponentList))
setupComponent(node);
- foreach (const ModelNode node, m_setupCustomParserList)
+ for (const ModelNode &node : std::as_const(m_setupCustomParserList))
setupCustomParserNode(node);
+
+ for (const ModelNode &node : std::as_const(m_clearImplicitComponentList))
+ clearImplicitComponent(node);
+
m_setupCustomParserList.clear();
m_setupComponentList.clear();
+ m_clearImplicitComponentList.clear();
}
QSet<QPair<QString, QString> > TextToModelMerger::qrcMapping() const
diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h
index f2e308d02f..ed48d4ebc8 100644
--- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h
+++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h
@@ -128,8 +128,9 @@ public:
ReadingContext *context,
DifferenceHandler &differenceHandler);
- void setupComponentDelayed(const ModelNode &node, bool synchron);
- void setupCustomParserNodeDelayed(const ModelNode &node, bool synchron);
+ void setupComponentDelayed(const ModelNode &node, bool synchronous);
+ void setupCustomParserNodeDelayed(const ModelNode &node, bool synchronous);
+ void clearImplicitComponentDelayed(const ModelNode &node, bool synchronous);
void delayedSetup();
@@ -140,6 +141,7 @@ public:
private:
void setupCustomParserNode(const ModelNode &node);
void setupComponent(const ModelNode &node);
+ void clearImplicitComponent(const ModelNode &node);
void collectLinkErrors(QList<DocumentMessage> *errors, const ReadingContext &ctxt);
void collectImportErrors(QList<DocumentMessage> *errors);
void collectSemanticErrorsAndWarnings(QList<DocumentMessage> *errors,
@@ -163,6 +165,7 @@ private:
QTimer m_setupTimer;
QSet<ModelNode> m_setupComponentList;
QSet<ModelNode> m_setupCustomParserList;
+ QSet<ModelNode> m_clearImplicitComponentList;
QmlJS::ViewerContext m_vContext;
QSet<QPair<QString, QString> > m_qrcMapping;
QSet<QmlJS::ImportKey> m_possibleImportKeys;
diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
index bbd91d40a8..019be09973 100644
--- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
+++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
@@ -432,4 +432,51 @@ MetaInfo {
}
}
+ Type {
+ name: "QtQml.Component"
+ icon: ":/qtquickplugin/images/item-icon16.png"
+
+ Hints {
+ canBeDroppedInNavigator: true
+ canBeDroppedInFormEditor: false
+ }
+
+ ItemLibraryEntry {
+ name: "Component"
+ category: "e.Qt Quick - Component"
+ libraryIcon: ":/qtquickplugin/images/item-icon.png"
+ version: "2.0"
+ }
+ }
+
+ Type {
+ name: "QtQuick.Loader"
+ icon: ":/qtquickplugin/images/item-icon16.png"
+
+ ItemLibraryEntry {
+ name: "Loader"
+ category: "e.Qt Quick - Component"
+ libraryIcon: ":/qtquickplugin/images/item-icon.png"
+ version: "2.0"
+ Property { name: "width"; type: "int"; value: 200; }
+ Property { name: "height"; type: "int"; value: 200; }
+ }
+ }
+
+ Type {
+ name: "QtQuick.Repeater"
+ icon: ":/qtquickplugin/images/item-icon16.png"
+
+ Hints {
+ canBeDroppedInFormEditor: false
+ hasFormEditorItem: false
+ }
+
+ ItemLibraryEntry {
+ name: "Repeater"
+ category: "e.Qt Quick - Component"
+ libraryIcon: ":/qtquickplugin/images/item-icon.png"
+ version: "2.0"
+ }
+ }
}
diff --git a/src/plugins/qmljseditor/qmljseditorplugin.cpp b/src/plugins/qmljseditor/qmljseditorplugin.cpp
index e0d5703102..59cafdcfc1 100644
--- a/src/plugins/qmljseditor/qmljseditorplugin.cpp
+++ b/src/plugins/qmljseditor/qmljseditorplugin.cpp
@@ -110,6 +110,7 @@ QmlJSEditorPlugin::QmlJSEditorPlugin()
QmlJSEditorPlugin::~QmlJSEditorPlugin()
{
+ delete QmlJS::Icons::instance(); // delete object held by singleton
delete d;
d = nullptr;
m_instance = nullptr;
@@ -226,8 +227,6 @@ void QmlJSEditorPlugin::extensionsInitialized()
ExtensionSystem::IPlugin::ShutdownFlag QmlJSEditorPlugin::aboutToShutdown()
{
- delete QmlJS::Icons::instance(); // delete object held by singleton
-
return IPlugin::aboutToShutdown();
}
diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp
index abe8777b34..f0fe19cc34 100644
--- a/src/plugins/texteditor/basefilefind.cpp
+++ b/src/plugins/texteditor/basefilefind.cpp
@@ -236,7 +236,7 @@ static QString displayText(const QString &line)
QString result = line;
auto end = result.end();
for (auto it = result.begin(); it != end; ++it) {
- if (!it->isPrint())
+ if (!it->isSpace() && !it->isPrint())
*it = QChar('?');
}
return result;
diff --git a/src/plugins/valgrind/memcheckerrorview.cpp b/src/plugins/valgrind/memcheckerrorview.cpp
index 0e45b76d43..19e3199b72 100644
--- a/src/plugins/valgrind/memcheckerrorview.cpp
+++ b/src/plugins/valgrind/memcheckerrorview.cpp
@@ -46,6 +46,7 @@
#include <QAction>
+using namespace Utils;
using namespace Valgrind::XmlProtocol;
namespace Valgrind {
@@ -56,10 +57,10 @@ MemcheckErrorView::MemcheckErrorView(QWidget *parent)
{
m_suppressAction = new QAction(this);
m_suppressAction->setText(tr("Suppress Error"));
- const QIcon icon = Utils::Icon({
- {":/utils/images/eye_open.png", Utils::Theme::TextColorNormal},
- {":/valgrind/images/suppressoverlay.png", Utils::Theme::IconsErrorColor}},
- Utils::Icon::Tint | Utils::Icon::PunchEdges).icon();
+ const QIcon icon = Icon({
+ {":/utils/images/eye_open.png", Theme::TextColorNormal},
+ {":/valgrind/images/suppressoverlay.png", Theme::IconsErrorColor}},
+ Icon::Tint | Icon::PunchEdges).icon();
m_suppressAction->setIcon(icon);
m_suppressAction->setShortcuts({QKeySequence::Delete, QKeySequence::Backspace});
m_suppressAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
@@ -69,12 +70,12 @@ MemcheckErrorView::MemcheckErrorView(QWidget *parent)
MemcheckErrorView::~MemcheckErrorView() = default;
-void MemcheckErrorView::setDefaultSuppressionFile(const QString &suppFile)
+void MemcheckErrorView::setDefaultSuppressionFile(const FilePath &suppFile)
{
m_defaultSuppFile = suppFile;
}
-QString MemcheckErrorView::defaultSuppressionFile() const
+FilePath MemcheckErrorView::defaultSuppressionFile() const
{
return m_defaultSuppFile;
}
diff --git a/src/plugins/valgrind/memcheckerrorview.h b/src/plugins/valgrind/memcheckerrorview.h
index 6a22dbf907..888140fc45 100644
--- a/src/plugins/valgrind/memcheckerrorview.h
+++ b/src/plugins/valgrind/memcheckerrorview.h
@@ -28,6 +28,8 @@
#include <debugger/analyzer/detailederrorview.h>
+#include <utils/filepath.h>
+
#include <QListView>
namespace Valgrind {
@@ -43,8 +45,8 @@ public:
MemcheckErrorView(QWidget *parent = nullptr);
~MemcheckErrorView() override;
- void setDefaultSuppressionFile(const QString &suppFile);
- QString defaultSuppressionFile() const;
+ void setDefaultSuppressionFile(const Utils::FilePath &suppFile);
+ Utils::FilePath defaultSuppressionFile() const;
ValgrindBaseSettings *settings() const { return m_settings; }
void settingsChanged(ValgrindBaseSettings *settings);
@@ -53,7 +55,7 @@ private:
QList<QAction *> customActions() const override;
QAction *m_suppressAction;
- QString m_defaultSuppFile;
+ Utils::FilePath m_defaultSuppFile;
ValgrindBaseSettings *m_settings = nullptr;
};
diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp
index 95bfb7e756..ff97b153b6 100644
--- a/src/plugins/valgrind/memchecktool.cpp
+++ b/src/plugins/valgrind/memchecktool.cpp
@@ -128,7 +128,7 @@ public:
void start() override;
void stop() override;
- const QStringList suppressionFiles() const;
+ const Utils::FilePaths suppressionFiles() const;
signals:
void internalParserError(const QString &errorString);
@@ -212,8 +212,8 @@ QStringList MemcheckToolRunner::toolArguments() const
}
arguments << "--leak-check=" + leakCheckValue;
- for (const QString &file : m_settings.suppressions.value())
- arguments << QString("--suppressions=%1").arg(file);
+ for (const FilePath &file : m_settings.suppressions.value())
+ arguments << QString("--suppressions=%1").arg(file.path());
arguments << QString("--num-callers=%1").arg(m_settings.numCallers.value());
@@ -225,7 +225,7 @@ QStringList MemcheckToolRunner::toolArguments() const
return arguments;
}
-const QStringList MemcheckToolRunner::suppressionFiles() const
+const FilePaths MemcheckToolRunner::suppressionFiles() const
{
return m_settings.suppressions.value();
}
@@ -991,15 +991,15 @@ void MemcheckToolPrivate::setupRunner(MemcheckToolRunner *runTool)
clearErrorView();
m_loadExternalLogFile->setDisabled(true);
- QString dir = runControl->project()->projectDirectory().toString() + '/';
+ const FilePath dir = runControl->project()->projectDirectory();
const QString name = runTool->executable().fileName();
- m_errorView->setDefaultSuppressionFile(dir + name + ".supp");
+ m_errorView->setDefaultSuppressionFile(dir.pathAppended(name + ".supp"));
- const QStringList suppressionFiles = runTool->suppressionFiles();
- for (const QString &file : suppressionFiles) {
- QAction *action = m_filterMenu->addAction(FilePath::fromString(file).fileName());
- action->setToolTip(file);
+ const FilePaths suppressionFiles = runTool->suppressionFiles();
+ for (const FilePath &file : suppressionFiles) {
+ QAction *action = m_filterMenu->addAction(file.fileName());
+ action->setToolTip(file.toUserOutput());
connect(action, &QAction::triggered, this, [file] {
EditorManager::openEditorAt(file, 0);
});
@@ -1425,13 +1425,13 @@ void HeobDialog::updateProfile()
int leakRecording = settings->value(heobLeakRecordingC, 2).toInt();
bool attach = settings->value(heobAttachC, false).toBool();
const QString extraArgs = settings->value(heobExtraArgsC).toString();
- QString path = settings->value(heobPathC).toString();
+ FilePath path = FilePath::fromVariant(settings->value(heobPathC));
settings->endGroup();
if (path.isEmpty()) {
const QString heobPath = QStandardPaths::findExecutable("heob32.exe");
if (!heobPath.isEmpty())
- path = QFileInfo(heobPath).path();
+ path = FilePath::fromUserInput(heobPath);
}
m_xmlEdit->setText(xml);
@@ -1444,7 +1444,7 @@ void HeobDialog::updateProfile()
m_leakRecordingCombo->setCurrentIndex(leakRecording);
m_attachCheck->setChecked(attach);
m_extraArgsEdit->setText(extraArgs);
- m_pathChooser->setPath(path);
+ m_pathChooser->setFilePath(path);
}
void HeobDialog::updateEnabled()
diff --git a/src/plugins/valgrind/suppressiondialog.cpp b/src/plugins/valgrind/suppressiondialog.cpp
index 4f5b427f09..bde173edcc 100644
--- a/src/plugins/valgrind/suppressiondialog.cpp
+++ b/src/plugins/valgrind/suppressiondialog.cpp
@@ -51,6 +51,7 @@
#include <QPlainTextEdit>
#include <QPushButton>
+using namespace Utils;
using namespace Valgrind::XmlProtocol;
namespace Valgrind {
@@ -63,7 +64,7 @@ static QString suppressionText(const Error &error)
// workaround: https://bugs.kde.org/show_bug.cgi?id=255822
if (sup.frames().size() >= 24)
sup.setFrames(sup.frames().mid(0, 23));
- QTC_ASSERT(sup.frames().size() < 24, /**/);
+ QTC_CHECK(sup.frames().size() < 24);
// try to set some useful name automatically, instead of "insert_name_here"
// we take the last stack frame and append the suppression kind, e.g.:
@@ -117,7 +118,7 @@ SuppressionDialog::SuppressionDialog(MemcheckErrorView *view, const QList<Error>
m_settings(view->settings()),
m_cleanupIfCanceled(false),
m_errors(errors),
- m_fileChooser(new Utils::PathChooser(this)),
+ m_fileChooser(new PathChooser(this)),
m_suppressionEdit(new QPlainTextEdit(this))
{
setWindowTitle(tr("Save Suppression"));
@@ -140,27 +141,23 @@ SuppressionDialog::SuppressionDialog(MemcheckErrorView *view, const QList<Error>
formLayout->addRow(m_suppressionEdit);
formLayout->addRow(m_buttonBox);
- QFile defaultSuppFile(view->defaultSuppressionFile());
- if (!defaultSuppFile.exists()) {
- if (defaultSuppFile.open(QIODevice::WriteOnly)) {
- defaultSuppFile.close();
- m_cleanupIfCanceled = true;
- }
- }
+ const FilePath defaultSuppFile = view->defaultSuppressionFile();
+ if (!defaultSuppFile.exists() && defaultSuppFile.ensureExistingFile())
+ m_cleanupIfCanceled = true;
- m_fileChooser->setExpectedKind(Utils::PathChooser::File);
+ m_fileChooser->setExpectedKind(PathChooser::File);
m_fileChooser->setHistoryCompleter("Valgrind.Suppression.History");
m_fileChooser->setPath(defaultSuppFile.fileName());
m_fileChooser->setPromptDialogFilter("*.supp");
m_fileChooser->setPromptDialogTitle(tr("Select Suppression File"));
QString suppressions;
- foreach (const Error &error, m_errors)
+ for (const Error &error : qAsConst(m_errors))
suppressions += suppressionText(error);
m_suppressionEdit->setPlainText(suppressions);
- connect(m_fileChooser, &Utils::PathChooser::validChanged,
+ connect(m_fileChooser, &PathChooser::validChanged,
this, &SuppressionDialog::validate);
connect(m_suppressionEdit->document(), &QTextDocument::contentsChanged,
this, &SuppressionDialog::validate);
@@ -178,7 +175,7 @@ void SuppressionDialog::maybeShow(MemcheckErrorView *view)
indices.append(view->selectionModel()->currentIndex());
QList<XmlProtocol::Error> errors;
- foreach (const QModelIndex &index, indices) {
+ for (const QModelIndex &index : qAsConst(indices)) {
Error error = view->model()->data(index, ErrorListModel::ErrorRole).value<Error>();
if (!error.suppression().isNull())
errors.append(error);
@@ -193,11 +190,11 @@ void SuppressionDialog::maybeShow(MemcheckErrorView *view)
void SuppressionDialog::accept()
{
- const Utils::FilePath path = m_fileChooser->filePath();
+ const FilePath path = m_fileChooser->filePath();
QTC_ASSERT(!path.isEmpty(), return);
QTC_ASSERT(!m_suppressionEdit->toPlainText().trimmed().isEmpty(), return);
- Utils::FileSaver saver(path, QIODevice::Append);
+ FileSaver saver(path, QIODevice::Append);
if (!saver.hasError()) {
QTextStream stream(saver.file());
stream << m_suppressionEdit->toPlainText();
@@ -216,14 +213,14 @@ void SuppressionDialog::accept()
}
}
- m_settings->suppressions.addSuppressionFile(path.toString());
+ m_settings->suppressions.addSuppressionFile(path);
QModelIndexList indices = m_view->selectionModel()->selectedRows();
Utils::sort(indices, [](const QModelIndex &l, const QModelIndex &r) {
return l.row() > r.row();
});
QAbstractItemModel *model = m_view->model();
- foreach (const QModelIndex &index, indices) {
+ for (const QModelIndex &index : qAsConst(indices)) {
bool removed = model->removeRow(index.row());
QTC_ASSERT(removed, qt_noop());
Q_UNUSED(removed)
@@ -234,7 +231,7 @@ void SuppressionDialog::accept()
const Error rowError = model->data(
model->index(row, 0), ErrorListModel::ErrorRole).value<Error>();
- foreach (const Error &error, m_errors) {
+ for (const Error &error : qAsConst(m_errors)) {
if (equalSuppression(rowError, error)) {
bool removed = model->removeRow(row);
QTC_CHECK(removed);
@@ -254,7 +251,7 @@ void SuppressionDialog::accept()
void SuppressionDialog::reject()
{
if (m_cleanupIfCanceled)
- QFile::remove(m_view->defaultSuppressionFile());
+ m_view->defaultSuppressionFile().removeFile();
QDialog::reject();
}
diff --git a/src/plugins/valgrind/valgrindsettings.cpp b/src/plugins/valgrind/valgrindsettings.cpp
index 352ad060ef..21d056e3fd 100644
--- a/src/plugins/valgrind/valgrindsettings.cpp
+++ b/src/plugins/valgrind/valgrindsettings.cpp
@@ -73,9 +73,9 @@ public:
QStandardItemModel m_model; // The volatile value of this aspect.
};
-void SuppressionAspect::addSuppressionFile(const QString &suppression)
+void SuppressionAspect::addSuppressionFile(const FilePath &suppression)
{
- QStringList val = value();
+ FilePaths val = value();
val.append(suppression);
setValue(val);
}
@@ -141,14 +141,14 @@ SuppressionAspect::~SuppressionAspect()
delete d;
}
-QStringList SuppressionAspect::value() const
+FilePaths SuppressionAspect::value() const
{
- return BaseAspect::value().toStringList();
+ return Utils::transform(BaseAspect::value().toStringList(), &FilePath::fromString);
}
-void SuppressionAspect::setValue(const QStringList &val)
+void SuppressionAspect::setValue(const FilePaths &val)
{
- BaseAspect::setValue(val);
+ BaseAspect::setValue(Utils::transform<QStringList>(val, &FilePath::toString));
}
void SuppressionAspect::addToLayout(LayoutBuilder &builder)
@@ -180,7 +180,7 @@ void SuppressionAspect::addToLayout(LayoutBuilder &builder)
};
builder.addItem(Span { 2, group });
- setVolatileValue(value());
+ setVolatileValue(BaseAspect::value());
}
void SuppressionAspect::fromMap(const QVariantMap &map)
diff --git a/src/plugins/valgrind/valgrindsettings.h b/src/plugins/valgrind/valgrindsettings.h
index 79fc68136e..7d4f01f79e 100644
--- a/src/plugins/valgrind/valgrindsettings.h
+++ b/src/plugins/valgrind/valgrindsettings.h
@@ -46,8 +46,8 @@ public:
explicit SuppressionAspect(bool global);
~SuppressionAspect() final;
- QStringList value() const;
- void setValue(const QStringList &val);
+ Utils::FilePaths value() const;
+ void setValue(const Utils::FilePaths &val);
void addToLayout(Utils::LayoutBuilder &builder) final;
@@ -57,7 +57,7 @@ public:
QVariant volatileValue() const final;
void setVolatileValue(const QVariant &val) final;
- void addSuppressionFile(const QString &suppressionFile);
+ void addSuppressionFile(const Utils::FilePath &suppressionFile);
private:
friend class ValgrindBaseSettings;
diff --git a/src/shared/proparser/qmakebuiltins.cpp b/src/shared/proparser/qmakebuiltins.cpp
index a697133896..c47bc1b2e7 100644
--- a/src/shared/proparser/qmakebuiltins.cpp
+++ b/src/shared/proparser/qmakebuiltins.cpp
@@ -474,12 +474,8 @@ void QMakeEvaluator::runProcess(QProcess *proc, const QString &command) const
}
# endif
# ifdef PROEVALUATOR_THREAD_SAFE
- m_option->mutex.lock();
- if (m_option->canceled) {
- m_option->mutex.unlock();
+ if (m_option->canceled)
return;
- }
- m_option->runningProcs << proc;
# endif
# ifdef Q_OS_WIN
proc->setNativeArguments(QLatin1String("/v:off /s /c \"") + command + QLatin1Char('"'));
@@ -488,12 +484,21 @@ void QMakeEvaluator::runProcess(QProcess *proc, const QString &command) const
proc->start(QLatin1String("/bin/sh"), QStringList() << QLatin1String("-c") << command);
# endif
# ifdef PROEVALUATOR_THREAD_SAFE
- m_option->mutex.unlock();
-# endif
- proc->waitForFinished(-1);
-# ifdef PROEVALUATOR_THREAD_SAFE
- QMutexLocker(&m_option->mutex);
- m_option->runningProcs.removeOne(proc);
+ while (true) {
+ if (proc->waitForFinished(100))
+ break;
+ if (m_option->canceled) {
+ proc->terminate();
+ if (proc->waitForFinished(1000))
+ break;
+ proc->kill();
+ proc->waitForFinished(1000);
+ break;
+ }
+ }
+# else
+ proc->waitForFinished(-1); // If have have single thread we can't cancel it using
+ // synchronous API of QProcess
# endif
}
#endif
diff --git a/src/shared/proparser/qmakeglobals.cpp b/src/shared/proparser/qmakeglobals.cpp
index ea429145e3..ca22bdde8b 100644
--- a/src/shared/proparser/qmakeglobals.cpp
+++ b/src/shared/proparser/qmakeglobals.cpp
@@ -92,11 +92,7 @@ QMakeGlobals::~QMakeGlobals()
void QMakeGlobals::killProcesses()
{
#ifdef PROEVALUATOR_THREAD_SAFE
- QMutexLocker lock(&mutex);
canceled = true;
- for (QProcess * const proc : runningProcs)
- proc->kill();
- runningProcs.clear();
#endif
}
diff --git a/src/shared/proparser/qmakeglobals.h b/src/shared/proparser/qmakeglobals.h
index 2fa9a33213..2baeab4bf5 100644
--- a/src/shared/proparser/qmakeglobals.h
+++ b/src/shared/proparser/qmakeglobals.h
@@ -159,8 +159,7 @@ private:
#ifdef PROEVALUATOR_THREAD_SAFE
QMutex mutex;
- bool canceled = false;
- QList<QProcess *> runningProcs;
+ std::atomic_bool canceled = false;
#endif
QHash<QMakeBaseKey, QMakeBaseEnv *> baseEnvs;
diff --git a/src/shared/qbs b/src/shared/qbs
-Subproject 4592eff289852e3e5a81596d1cc6b0c2488e6bc
+Subproject a35ff56175cc8c993b54bb1d92ff71ba4532fc8
diff --git a/src/shared/shared.pro b/src/shared/shared.pro
index b4c64a02fb..4e4c860c82 100644
--- a/src/shared/shared.pro
+++ b/src/shared/shared.pro
@@ -6,9 +6,12 @@ QBS_DIRS = \
qbslibexec \
qbsmsbuildlib \
qbsplugins \
- qbsstatic
+ qbsstatic \
+ qbspkgconfig
+qbspkgconfig.subdir = qbs/src/lib/pkgconfig
qbscorelib.subdir = qbs/src/lib/corelib
+qbscorelib.depends = qbspkgconfig
qbsapps.subdir = qbs/src/app
qbsapps.depends = qbscorelib
qbslibexec.subdir = qbs/src/libexec
diff --git a/src/tools/qml2puppet/qml2puppet.qbs b/src/tools/qml2puppet/qml2puppet.qbs
index 7eff7ba02b..83acc77ba3 100644
--- a/src/tools/qml2puppet/qml2puppet.qbs
+++ b/src/tools/qml2puppet/qml2puppet.qbs
@@ -16,6 +16,7 @@ QtcTool {
]
}
Depends { name: "Qt.quick3d-private"; required: false }
+ Depends { name: "Qt.quick3dparticles-private"; required: false }
property bool useQuick3d: Utilities.versionCompare(Qt.core.version, "5.15") >= 0
&& Qt["quick3d-private"].present
property bool useParticle3d: Utilities.versionCompare(Qt.core.version, "6.2") >= 0