diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml index 1971d93c736..e3a77501192 100644 --- a/.github/workflows/build_cmake.yml +++ b/.github/workflows/build_cmake.yml @@ -4,7 +4,7 @@ on: [push, pull_request] env: QT_VERSION: 5.15.2 - CLANG_VERSION: 120 + CLANG_VERSION: 130 ELFUTILS_VERSION: 0.175 CMAKE_VERSION: 3.21.1 NINJA_VERSION: 1.10.2 @@ -286,9 +286,9 @@ jobs: set(libclang "libclang-release_${clang_version}-based-windows-vs2019_32.7z") endif() elseif ("${{ runner.os }}" STREQUAL "Linux") - set(libclang "libclang-release_${clang_version}-based-linux-Ubuntu18.04-gcc9.3-x86_64.7z") + set(libclang "libclang-release_${clang_version}-based-linux-Ubuntu20.04-gcc9.3-x86_64.7z") elseif ("${{ runner.os }}" STREQUAL "macOS") - set(libclang "libclang-release_${clang_version}-based-mac.7z") + set(libclang "libclang-release_${clang_version}-based-macos-universal.7z") endif() set(libclang_url "https://\${qt_mirror}/development_releases/prebuilt/libclang/${libclang}") diff --git a/cmake/FindQt5.cmake b/cmake/FindQt5.cmake index 4fde452733d..1931bf019c2 100644 --- a/cmake/FindQt5.cmake +++ b/cmake/FindQt5.cmake @@ -57,7 +57,8 @@ else() find_package(Qt6 CONFIG ${__arguments} ${Qt5_FIND_COMPONENTS}) endif() -foreach(comp IN LISTS Qt5_FIND_COMPONENTS) +set(__additional_imported_components ATSPI2_nolink) # Work around QTBUG-97023 +foreach(comp IN LISTS Qt5_FIND_COMPONENTS __additional_imported_components) if(TARGET Qt6::${comp}) if (NOT TARGET Qt5::${comp}) set_property(TARGET Qt6::${comp} PROPERTY IMPORTED_GLOBAL TRUE) diff --git a/coin/instructions/common_environment.yaml b/coin/instructions/common_environment.yaml index e8812895829..abd45f68d48 100644 --- a/coin/instructions/common_environment.yaml +++ b/coin/instructions/common_environment.yaml @@ -7,7 +7,7 @@ instructions: variableValue: "RelWithDebInfo" - type: EnvironmentVariable variableName: QTC_QT_BASE_URL - variableValue: "http://ci-files02-hki.intra.qt.io/packages/jenkins/archive/qt/6.2/6.2.0-rc-released/" + variableValue: "http://ci-files02-hki.intra.qt.io/packages/jenkins/archive/qt/6.2/6.2.0-final-released/" - type: EnvironmentVariable variableName: QTC_QT_MODULES variableValue: "qt5compat qtbase qtdeclarative qtimageformats qtquick3d qtquickcontrols2 qtquicktimeline qtserialport qtshadertools qtsvg qttools qttranslations" diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml index d07812f02fd..8006be8ffa1 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml @@ -1075,6 +1075,20 @@ SecondColumnLayout { Layout.fillWidth: true spacing: 0 + Connections { + target: ceMode + function onActivated() { + spinBox.readValue() + } + } + + Connections { + target: modelNodeBackend + function onSelectionChanged() { + spinBox.readValue() + } + } + GradientPropertySpinBox { id: spinBox implicitWidth: StudioTheme.Values.controlGap diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPropertySpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPropertySpinBox.qml index a1c3251335c..65b6c14c73d 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPropertySpinBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPropertySpinBox.qml @@ -45,6 +45,10 @@ Item { onFocusChanged: restoreCursor() + function readValue() { + spinBox.realValue = gradientLine.model.readGradientProperty(wrapper.propertyName) + } + StudioControls.RealSpinBox { id: spinBox @@ -56,9 +60,7 @@ Item { realStepSize: 1 decimals: 0 - Component.onCompleted: { - spinBox.realValue = gradientLine.model.readGradientProperty(wrapper.propertyName) - } + Component.onCompleted: wrapper.readValue() onCompressedRealValueModified: { gradientLine.model.setGradientProperty(wrapper.propertyName, spinBox.realValue) } diff --git a/src/app/main.cpp b/src/app/main.cpp index 8ab587a7131..b078786eb68 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -494,6 +494,11 @@ int main(int argc, char **argv) QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar); } + if (Utils::HostOsInfo::isRunningUnderRosetta()) { + // work around QTBUG-97085: QRegularExpression jitting is not reentrant under Rosetta + qputenv("QT_ENABLE_REGEXP_JIT", "0"); + } + Utils::TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath() + "/" + Core::Constants::IDE_CASED_ID + "-XXXXXX"); #ifdef Q_OS_MACOS diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index 030205f8ad0..d63ae116117 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -1747,7 +1747,7 @@ bool Check::visit(CallExpression *ast) static const QStringList colorFunctions = {"lighter", "darker", "rgba", "tint", "hsla", "hsva"}; static const QStringList qtFunction = {"point", "rect", "size", "vector2d", "vector3d", "vector4d", "quaternion" "matrix4x4", "formatDate", - "formatDateTime", "formatTime"}; + "formatDateTime", "formatTime", "resolvedUrl"}; const bool whiteListedFunction = translationFunctions.contains(name) || whiteListedFunctions.contains(name) || colorFunctions.contains(name) || qtFunction.contains(name); diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp index db178852676..2d3d6edf3b4 100644 --- a/src/libs/utils/environment.cpp +++ b/src/libs/utils/environment.cpp @@ -136,8 +136,10 @@ void Environment::setupEnglishOutput() set("LANGUAGE", "en_US:en"); } -FilePath Environment::searchInDirectory(const QStringList &execs, const FilePath &directory, - QSet &alreadyChecked) +static FilePath searchInDirectory(const Environment &env, + const QStringList &execs, + const FilePath &directory, + QSet &alreadyChecked) { const int checkedCount = alreadyChecked.count(); alreadyChecked.insert(directory); @@ -199,17 +201,19 @@ QString Environment::expandedValueForKey(const QString &key) const return expandVariables(value(key)); } -FilePath Environment::searchInPath(const QString &executable, - const FilePaths &additionalDirs, - const PathFilter &func) const +static FilePath searchInDirectoriesHelper(const Environment &env, + const QString &executable, + const FilePaths &dirs, + const Environment::PathFilter &func, + bool usePath) { if (executable.isEmpty()) return FilePath(); - const QString exec = QDir::cleanPath(expandVariables(executable)); + const QString exec = QDir::cleanPath(env.expandVariables(executable)); const QFileInfo fi(exec); - const QStringList execs = appendExeExtensions(exec); + const QStringList execs = env.appendExeExtensions(exec); if (fi.isAbsolute()) { for (const QString &path : execs) { @@ -221,23 +225,38 @@ FilePath Environment::searchInPath(const QString &executable, } QSet alreadyChecked; - for (const FilePath &dir : additionalDirs) { - FilePath tmp = searchInDirectory(execs, dir, alreadyChecked); + for (const FilePath &dir : dirs) { + FilePath tmp = searchInDirectory(env, execs, dir, alreadyChecked); if (!tmp.isEmpty() && (!func || func(tmp))) return tmp; } - if (executable.contains('/')) - return FilePath(); + if (usePath) { + if (executable.contains('/')) + return FilePath(); - for (const FilePath &p : path()) { - FilePath tmp = searchInDirectory(execs, p, alreadyChecked); - if (!tmp.isEmpty() && (!func || func(tmp))) - return tmp; + for (const FilePath &p : env.path()) { + FilePath tmp = searchInDirectory(env, execs, p, alreadyChecked); + if (!tmp.isEmpty() && (!func || func(tmp))) + return tmp; + } } return FilePath(); } +FilePath Environment::searchInDirectories(const QString &executable, + const FilePaths &dirs) const +{ + return searchInDirectoriesHelper(*this, executable, dirs, {}, false); +} + +FilePath Environment::searchInPath(const QString &executable, + const FilePaths &additionalDirs, + const PathFilter &func) const +{ + return searchInDirectoriesHelper(*this, executable, additionalDirs, func, true); +} + FilePaths Environment::findAllInPath(const QString &executable, const FilePaths &additionalDirs, const Environment::PathFilter &func) const @@ -262,14 +281,14 @@ FilePaths Environment::findAllInPath(const QString &executable, QSet result; QSet alreadyChecked; for (const FilePath &dir : additionalDirs) { - FilePath tmp = searchInDirectory(execs, dir, alreadyChecked); + FilePath tmp = searchInDirectory(*this, execs, dir, alreadyChecked); if (!tmp.isEmpty() && (!func || func(tmp))) result << tmp; } if (!executable.contains('/')) { for (const FilePath &p : path()) { - FilePath tmp = searchInDirectory(execs, p, alreadyChecked); + FilePath tmp = searchInDirectory(*this, execs, p, alreadyChecked); if (!tmp.isEmpty() && (!func || func(tmp))) result << tmp; } diff --git a/src/libs/utils/environment.h b/src/libs/utils/environment.h index c20ed67f7df..9cfa64faba4 100644 --- a/src/libs/utils/environment.h +++ b/src/libs/utils/environment.h @@ -63,6 +63,8 @@ public: FilePath searchInPath(const QString &executable, const FilePaths &additionalDirs = FilePaths(), const PathFilter &func = PathFilter()) const; + FilePath searchInDirectories(const QString &executable, + const FilePaths &dirs) const; FilePaths findAllInPath(const QString &executable, const FilePaths &additionalDirs = FilePaths(), const PathFilter &func = PathFilter()) const; @@ -80,10 +82,6 @@ public: static void modifySystemEnvironment(const EnvironmentItems &list); // use with care!!! static void setSystemEnvironment(const Environment &environment); // don't use at all!!! - -private: - static FilePath searchInDirectory(const QStringList &execs, const FilePath &directory, - QSet &alreadyChecked); }; class QTCREATOR_UTILS_EXPORT EnvironmentChange final diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 3602a6c304f..fd68683acee 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -1208,7 +1208,7 @@ FilePath FilePath::searchInDirectories(const FilePaths &dirs) const QTC_ASSERT(s_deviceHooks.searchInPath, return {}); return s_deviceHooks.searchInPath(*this, dirs); } - return Environment::systemEnvironment().searchInPath(path(), dirs); + return Environment::systemEnvironment().searchInDirectories(path(), dirs); } FilePath FilePath::searchInPath(const QList &additionalDirs) const diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp index 38d6e09ad8b..bba148dda86 100644 --- a/src/plugins/android/androiddevice.cpp +++ b/src/plugins/android/androiddevice.cpp @@ -69,8 +69,8 @@ public: void updateDeviceFromUi() final {} static QString dialogTitle(); - static bool criticalDialog(const QString &error); - static bool questionDialog(const QString &question); + static bool criticalDialog(const QString &error, QWidget *parent = nullptr); + static bool questionDialog(const QString &question, QWidget *parent = nullptr); }; AndroidDeviceWidget::AndroidDeviceWidget(const IDevice::Ptr &device) @@ -114,10 +114,10 @@ QString AndroidDeviceWidget::dialogTitle() return tr("Android Device Manager"); } -bool AndroidDeviceWidget::criticalDialog(const QString &error) +bool AndroidDeviceWidget::criticalDialog(const QString &error, QWidget *parent) { qCDebug(androidDeviceLog) << error; - QMessageBox box(Core::ICore::dialogParent()); + QMessageBox box(parent ? parent : Core::ICore::dialogParent()); box.QDialog::setWindowTitle(dialogTitle()); box.setText(error); box.setIcon(QMessageBox::Critical); @@ -125,9 +125,9 @@ bool AndroidDeviceWidget::criticalDialog(const QString &error) return box.exec(); } -bool AndroidDeviceWidget::questionDialog(const QString &question) +bool AndroidDeviceWidget::questionDialog(const QString &question, QWidget *parent) { - QMessageBox box(Core::ICore::dialogParent()); + QMessageBox box(parent ? parent : Core::ICore::dialogParent()); box.QDialog::setWindowTitle(dialogTitle()); box.setText(question); box.setIcon(QMessageBox::Question); @@ -155,19 +155,56 @@ AndroidDevice::AndroidDevice() AndroidDeviceManager::instance()->updateDevicesListOnce(); }}); - addDeviceAction({tr("Start AVD"), [](const IDevice::Ptr &device, QWidget *parent) { - if (device->machineType() == IDevice::Emulator) - AndroidDeviceManager::instance()->startAvd(device); - }}); + addEmulatorActionsIfNotFound(); +} - addDeviceAction({tr("Erase AVD"), [](const IDevice::Ptr &device, QWidget *parent) { - if (device->machineType() == IDevice::Emulator) - AndroidDeviceManager::instance()->eraseAvd(device); - }}); +void AndroidDevice::addEmulatorActionsIfNotFound() +{ + static const QString startAvdAction = tr("Start AVD"); + static const QString eraseAvdAction = tr("Erase AVD"); + static const QString avdArgumentsAction = tr("AVD Arguments"); - addDeviceAction({tr("AVD Arguments"), [](const IDevice::Ptr &device, QWidget *parent) { - AndroidDeviceManager::instance()->setEmulatorArguments(); - }}); + bool hasStartAction = false; + bool hasEraseAction = false; + bool hasAvdArgumentsAction = false; + + for (const DeviceAction &item : deviceActions()) { + if (item.display == startAvdAction) + hasStartAction = true; + else if (item.display == eraseAvdAction) + hasEraseAction = true; + else if (item.display == avdArgumentsAction) + hasAvdArgumentsAction = true; + } + + if (machineType() == Emulator) { + if (!hasStartAction) { + addDeviceAction({startAvdAction, [](const IDevice::Ptr &device, QWidget *parent) { + AndroidDeviceManager::instance()->startAvd(device, parent); + }}); + } + + if (!hasEraseAction) { + addDeviceAction({eraseAvdAction, [](const IDevice::Ptr &device, QWidget *parent) { + AndroidDeviceManager::instance()->eraseAvd(device, parent); + }}); + } + + if (!hasAvdArgumentsAction) { + addDeviceAction({avdArgumentsAction, [](const IDevice::Ptr &device, QWidget *parent) { + Q_UNUSED(device) + AndroidDeviceManager::instance()->setEmulatorArguments(parent); + }}); + } + } +} + +void AndroidDevice::fromMap(const QVariantMap &map) +{ + IDevice::fromMap(map); + // Add Actions for Emulator is not added already. + // This is needed because actions for Emulators and physical devices are not the same. + addEmulatorActionsIfNotFound(); } IDevice::Ptr AndroidDevice::create() @@ -407,8 +444,9 @@ void AndroidDeviceManager::updateDevicesListOnce() } } -void AndroidDeviceManager::startAvd(const ProjectExplorer::IDevice::Ptr &device) +void AndroidDeviceManager::startAvd(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent) { + Q_UNUSED(parent) const AndroidDevice *androidDev = static_cast(device.data()); const QString name = androidDev->avdName(); qCDebug(androidDeviceLog, "Starting Android AVD id \"%s\".", qPrintable(name)); @@ -422,7 +460,7 @@ void AndroidDeviceManager::startAvd(const ProjectExplorer::IDevice::Ptr &device) }); } -void AndroidDeviceManager::eraseAvd(const IDevice::Ptr &device) +void AndroidDeviceManager::eraseAvd(const IDevice::Ptr &device, QWidget *parent) { if (device.isNull()) return; @@ -432,7 +470,7 @@ void AndroidDeviceManager::eraseAvd(const IDevice::Ptr &device) const QString name = static_cast(device.data())->avdName(); const QString question = tr("Erase the Android AVD \"%1\"?\nThis cannot be undone.").arg(name); - if (!AndroidDeviceWidget::questionDialog(question)) + if (!AndroidDeviceWidget::questionDialog(question, parent)) return; qCDebug(androidDeviceLog) << QString("Erasing Android AVD \"%1\" from the system.").arg(name); @@ -460,12 +498,12 @@ void AndroidDeviceManager::handleAvdRemoved() } } -void AndroidDeviceManager::setEmulatorArguments() +void AndroidDeviceManager::setEmulatorArguments(QWidget *parent) { const QString helpUrl = "https://developer.android.com/studio/run/emulator-commandline#startup-options"; - QInputDialog dialog(Core::ICore::dialogParent()); + QInputDialog dialog(parent ? parent : Core::ICore::dialogParent()); dialog.setWindowTitle(tr("Emulator Command-line Startup Options")); dialog.setLabelText(tr("Emulator command-line startup options " "(Help Web Page):").arg(helpUrl)); diff --git a/src/plugins/android/androiddevice.h b/src/plugins/android/androiddevice.h index 5e6fabb43b6..31249e84808 100644 --- a/src/plugins/android/androiddevice.h +++ b/src/plugins/android/androiddevice.h @@ -46,6 +46,8 @@ class AndroidDevice final : public ProjectExplorer::IDevice public: AndroidDevice(); + void fromMap(const QVariantMap &map) override; + static IDevice::Ptr create(); static AndroidDeviceInfo androidDeviceInfoFromIDevice(const IDevice *dev); static void setAndroidDeviceInfoExtras(IDevice *dev, const AndroidDeviceInfo &info); @@ -74,6 +76,7 @@ public: AndroidConfig::OpenGl openGlStatus() const; private: + void addEmulatorActionsIfNotFound(); ProjectExplorer::IDevice::DeviceInfo deviceInformation() const override; ProjectExplorer::IDeviceWidget *createWidget() override; bool canAutoDetectPorts() const override; @@ -99,10 +102,10 @@ public: void updateDevicesList(); void updateDevicesListOnce(); - void startAvd(const ProjectExplorer::IDevice::Ptr &device); - void eraseAvd(const ProjectExplorer::IDevice::Ptr &device); + void startAvd(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent = nullptr); + void eraseAvd(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent = nullptr); - void setEmulatorArguments(); + void setEmulatorArguments(QWidget *parent = nullptr); private: AndroidDeviceManager(QObject *parent = nullptr); diff --git a/src/plugins/android/androidsdkmanagerwidget.cpp b/src/plugins/android/androidsdkmanagerwidget.cpp index c4598351536..c576571d32c 100644 --- a/src/plugins/android/androidsdkmanagerwidget.cpp +++ b/src/plugins/android/androidsdkmanagerwidget.cpp @@ -59,10 +59,12 @@ public: PackageFilterModel(AndroidSdkModel* sdkModel); void setAcceptedPackageState(AndroidSdkPackage::PackageState state); + void setAcceptedSearchPackage(const QString &text); bool filterAcceptsRow(int source_row, const QModelIndex &sourceParent) const override; private: AndroidSdkPackage::PackageState m_packageState = AndroidSdkPackage::AnyValidState; + QString m_searchText; }; AndroidSdkManagerWidget::AndroidSdkManagerWidget(AndroidConfig &config, @@ -129,6 +131,15 @@ AndroidSdkManagerWidget::AndroidSdkManagerWidget(AndroidConfig &config, } }); + m_ui->searchField->setPlaceholderText("Filter"); + connect(m_ui->searchField, &QLineEdit::textChanged, [this, proxyModel](const QString &text) { + const bool isExpanded = m_ui->expandCheck->isChecked(); + proxyModel->setAcceptedSearchPackage(text); + m_sdkModel->resetSelection(); + // It is more convenient to expand the view with the results + m_ui->expandCheck->setChecked(!text.isEmpty()); + }); + connect(m_ui->applySelectionButton, &QPushButton::clicked, this, &AndroidSdkManagerWidget::onApplyButton); connect(m_ui->cancelButton, &QPushButton::clicked, this, @@ -469,6 +480,12 @@ void PackageFilterModel::setAcceptedPackageState(AndroidSdkPackage::PackageState invalidateFilter(); } +void PackageFilterModel::setAcceptedSearchPackage(const QString &name) +{ + m_searchText = name; + invalidateFilter(); +} + bool PackageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { QModelIndex srcIndex = sourceModel()->index(sourceRow, 0, sourceParent); @@ -479,19 +496,24 @@ bool PackageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour return (AndroidSdkPackage::PackageState)i.data(AndroidSdkModel::PackageStateRole).toInt(); }; + auto packageFound = [this](const QModelIndex& i) { + return i.data(AndroidSdkModel::packageNameColumn).toString() + .contains(m_searchText, Qt::CaseInsensitive); + }; + bool showTopLevel = false; if (!sourceParent.isValid()) { // Top Level items for (int row = 0; row < sourceModel()->rowCount(srcIndex); ++row) { QModelIndex childIndex = sourceModel()->index(row, 0, srcIndex); - if (m_packageState & packageState(childIndex)) { + if ((m_packageState & packageState(childIndex) && packageFound(childIndex))) { showTopLevel = true; break; } } } - return showTopLevel || (packageState(srcIndex) & m_packageState); + return showTopLevel || (packageState(srcIndex) & m_packageState) && packageFound(srcIndex); } OptionsDialog::OptionsDialog(AndroidSdkManager *sdkManager, const QStringList &args, diff --git a/src/plugins/android/androidsdkmanagerwidget.ui b/src/plugins/android/androidsdkmanagerwidget.ui index e49f7a792ef..3baa968402a 100644 --- a/src/plugins/android/androidsdkmanagerwidget.ui +++ b/src/plugins/android/androidsdkmanagerwidget.ui @@ -51,27 +51,7 @@ 4 - - - - - - - 0 - 0 - - - - Expand All - - - true - - - - - - + 20 @@ -81,7 +61,7 @@ - + @@ -149,6 +129,16 @@ + + + + + + + Expand All + + + @@ -234,6 +224,13 @@ + + + Utils::FancyLineEdit + QLineEdit +
utils/fancylineedit.h
+
+
packagesView showAllRadio diff --git a/src/plugins/clangcodemodel/CMakeLists.txt b/src/plugins/clangcodemodel/CMakeLists.txt index 9336c29d830..ace1b99575c 100644 --- a/src/plugins/clangcodemodel/CMakeLists.txt +++ b/src/plugins/clangcodemodel/CMakeLists.txt @@ -36,7 +36,7 @@ add_qtc_plugin(ClangCodeModel clangfixitoperationsextractor.cpp clangfixitoperationsextractor.h clangfollowsymbol.cpp clangfollowsymbol.h clangfunctionhintmodel.cpp clangfunctionhintmodel.h - clanggloballocatorfilters.cpp clanggloballocatorfilters.h + clangdlocatorfilters.cpp clangdlocatorfilters.h clanghighlightingresultreporter.cpp clanghighlightingresultreporter.h clanghoverhandler.cpp clanghoverhandler.h clangisdiagnosticrelatedtolocation.h diff --git a/src/plugins/clangcodemodel/clangcodemodel.pro b/src/plugins/clangcodemodel/clangcodemodel.pro index 57eeb22d870..5675ea9d57e 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.pro +++ b/src/plugins/clangcodemodel/clangcodemodel.pro @@ -43,7 +43,7 @@ SOURCES += \ clangoverviewmodel.cpp \ clangdclient.cpp \ clangdquickfixfactory.cpp \ - clanggloballocatorfilters.cpp \ + clangdlocatorfilters.cpp \ HEADERS += \ clangactivationsequencecontextprocessor.h \ @@ -85,7 +85,7 @@ HEADERS += \ clangoverviewmodel.h \ clangdclient.h \ clangdquickfixfactory.h \ - clanggloballocatorfilters.h \ + clangdlocatorfilters.h \ FORMS += clangprojectsettingswidget.ui diff --git a/src/plugins/clangcodemodel/clangcodemodel.qbs b/src/plugins/clangcodemodel/clangcodemodel.qbs index 42c3e4df9d1..7b6689ad45f 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.qbs +++ b/src/plugins/clangcodemodel/clangcodemodel.qbs @@ -64,6 +64,8 @@ QtcPlugin { "clangdiagnosticmanager.h", "clangdiagnostictooltipwidget.cpp", "clangdiagnostictooltipwidget.h", + "clangdlocatorfilters.cpp", + "clangdlocatorfilters.h", "clangdquickfixfactory.cpp", "clangdquickfixfactory.h", "clangeditordocumentparser.cpp", @@ -78,8 +80,6 @@ QtcPlugin { "clangfollowsymbol.h", "clangfunctionhintmodel.cpp", "clangfunctionhintmodel.h", - "clanggloballocatorfilters.cpp", - "clanggloballocatorfilters.h", "clanghighlightingresultreporter.cpp", "clanghighlightingresultreporter.h", "clanghoverhandler.cpp", diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 7a6c1045ec3..fea5314001e 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -978,31 +978,7 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir) }; const auto hideDiagsHandler = []{ ClangDiagnosticManager::clearTaskHubIssues(); }; setDiagnosticsHandlers(textMarkCreator, hideDiagsHandler); - - static const auto symbolStringifier = [](SymbolKind kind, const QString &name, - const QString &detail) -> QString - { - switch (kind) { - case LanguageServerProtocol::SymbolKind::Constructor: - return name + detail; - case LanguageServerProtocol::SymbolKind::Method: - case LanguageServerProtocol::SymbolKind::Function: { - const int parenOffset = detail.indexOf(" ("); - if (parenOffset == -1) - return name; - return name + detail.mid(parenOffset + 1) + " -> " + detail.mid(0, parenOffset); - } - case LanguageServerProtocol::SymbolKind::Variable: - case LanguageServerProtocol::SymbolKind::Field: - case LanguageServerProtocol::SymbolKind::Constant: - if (detail.isEmpty()) - return name; - return name + " -> " + detail; - default: - return name; - } - }; - setSymbolStringifier(symbolStringifier); + setSymbolStringifier(displayNameFromDocumentSymbol); setSemanticTokensHandler([this](TextDocument *doc, const QList &tokens) { d->handleSemanticTokens(doc, tokens); }); @@ -1236,6 +1212,30 @@ bool ClangdClient::testingEnabled() const return d->isTesting; } +QString ClangdClient::displayNameFromDocumentSymbol(SymbolKind kind, const QString &name, + const QString &detail) +{ + switch (kind) { + case SymbolKind::Constructor: + return name + detail; + case SymbolKind::Method: + case LanguageServerProtocol::SymbolKind::Function: { + const int parenOffset = detail.indexOf(" ("); + if (parenOffset == -1) + return name; + return name + detail.mid(parenOffset + 1) + " -> " + detail.mid(0, parenOffset); + } + case SymbolKind::Variable: + case SymbolKind::Field: + case SymbolKind::Constant: + if (detail.isEmpty()) + return name; + return name + " -> " + detail; + default: + return name; + } +} + void ClangdClient::Private::handleFindUsagesResult(quint64 key, const QList &locations) { const auto refData = runningFindUsages.find(key); diff --git a/src/plugins/clangcodemodel/clangdclient.h b/src/plugins/clangcodemodel/clangdclient.h index bec77b9261e..03675516616 100644 --- a/src/plugins/clangcodemodel/clangdclient.h +++ b/src/plugins/clangcodemodel/clangdclient.h @@ -79,6 +79,9 @@ public: void enableTesting(); bool testingEnabled() const; + static QString displayNameFromDocumentSymbol(LanguageServerProtocol::SymbolKind kind, + const QString &name, const QString &detail); + signals: void indexingFinished(); void foundReferences(const QList &items); diff --git a/src/plugins/clangcodemodel/clanggloballocatorfilters.cpp b/src/plugins/clangcodemodel/clangdlocatorfilters.cpp similarity index 67% rename from src/plugins/clangcodemodel/clanggloballocatorfilters.cpp rename to src/plugins/clangcodemodel/clangdlocatorfilters.cpp index 4bc23a974a1..cafd7633d7d 100644 --- a/src/plugins/clangcodemodel/clanggloballocatorfilters.cpp +++ b/src/plugins/clangcodemodel/clangdlocatorfilters.cpp @@ -23,15 +23,17 @@ ** ****************************************************************************/ -#include "clanggloballocatorfilters.h" +#include "clangdlocatorfilters.h" #include "clangdclient.h" #include "clangmodelmanagersupport.h" +#include "clangcurrentdocumentfilter.h" #include #include #include #include +#include #include #include #include @@ -39,6 +41,8 @@ #include #include +using namespace LanguageServerProtocol; + namespace ClangCodeModel { namespace Internal { @@ -222,5 +226,104 @@ ClangFunctionsFilter::ClangFunctionsFilter() setDefaultIncludedByDefault(false); } +class CppCurrentDocumentFilter : public ClangCurrentDocumentFilter +{ +public: + CppCurrentDocumentFilter() + { + setId({}); + setDisplayName({}); + setDefaultShortcutString({}); + setEnabled(false); + setHidden(true); + } +}; + +class LspCurrentDocumentFilter : public LanguageClient::DocumentLocatorFilter +{ +public: + LspCurrentDocumentFilter() + { + setId({}); + setDisplayName({}); + setDefaultShortcutString({}); + setEnabled(false); + setHidden(true); + forceUse(); + } + +private: + Core::LocatorFilterEntry generateLocatorEntry(const DocumentSymbol &info, + const Core::LocatorFilterEntry &parent) override + { + Core::LocatorFilterEntry entry; + entry.filter = this; + entry.displayName = ClangdClient::displayNameFromDocumentSymbol( + static_cast(info.kind()), info.name(), + info.detail().value_or(QString())); + const Position &pos = info.range().start(); + entry.internalData = QVariant::fromValue(Utils::LineColumn(pos.line(), pos.character())); + entry.extraInfo = parent.extraInfo; + if (!entry.extraInfo.isEmpty()) + entry.extraInfo.append("::"); + entry.extraInfo.append(parent.displayName); + + // TODO: Can we extend clangd to send visibility information? + entry.displayIcon = LanguageClient::symbolIcon(info.kind()); + + return entry; + } +}; + +class ClangdCurrentDocumentFilter::Private +{ +public: + CppCurrentDocumentFilter cppFilter; + LspCurrentDocumentFilter lspFilter; + Core::ILocatorFilter *activeFilter = nullptr; +}; + + +ClangdCurrentDocumentFilter::ClangdCurrentDocumentFilter() : d(new Private) +{ + setId(CppEditor::Constants::CURRENT_DOCUMENT_FILTER_ID); + setDisplayName(CppEditor::Constants::CURRENT_DOCUMENT_FILTER_DISPLAY_NAME); + setDefaultShortcutString("."); + setPriority(High); + setDefaultIncludedByDefault(false); + setEnabled(false); + connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged, + this, [this](const Core::IEditor *editor) { setEnabled(editor); }); +} + +ClangdCurrentDocumentFilter::~ClangdCurrentDocumentFilter() { delete d; } + +void ClangdCurrentDocumentFilter::prepareSearch(const QString &entry) +{ + const auto doc = TextEditor::TextDocument::currentTextDocument(); + QTC_ASSERT(doc, return); + if (const ClangdClient * const client = ClangModelManagerSupport::instance() + ->clientForFile(doc->filePath()); client && client->reachable()) { + d->activeFilter = &d->lspFilter; + } else { + d->activeFilter = &d->cppFilter; + } + d->activeFilter->prepareSearch(entry); +} + +QList ClangdCurrentDocumentFilter::matchesFor( + QFutureInterface &future, const QString &entry) +{ + QTC_ASSERT(d->activeFilter, return {}); + return d->activeFilter->matchesFor(future, entry); +} + +void ClangdCurrentDocumentFilter::accept(Core::LocatorFilterEntry selection, QString *newText, + int *selectionStart, int *selectionLength) const +{ + QTC_ASSERT(d->activeFilter, return); + d->activeFilter->accept(selection, newText, selectionStart, selectionLength); +} + } // namespace Internal } // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clanggloballocatorfilters.h b/src/plugins/clangcodemodel/clangdlocatorfilters.h similarity index 79% rename from src/plugins/clangcodemodel/clanggloballocatorfilters.h rename to src/plugins/clangcodemodel/clangdlocatorfilters.h index e01ac4b3568..28341a6a573 100644 --- a/src/plugins/clangcodemodel/clanggloballocatorfilters.h +++ b/src/plugins/clangcodemodel/clangdlocatorfilters.h @@ -60,5 +60,22 @@ public: ClangFunctionsFilter(); }; +class ClangdCurrentDocumentFilter : public Core::ILocatorFilter +{ +public: + ClangdCurrentDocumentFilter(); + ~ClangdCurrentDocumentFilter() override; + +private: + void prepareSearch(const QString &entry) override; + QList matchesFor(QFutureInterface &future, + const QString &entry) override; + void accept(Core::LocatorFilterEntry selection, QString *newText, + int *selectionStart, int *selectionLength) const override; + + class Private; + Private * const d; +}; + } // namespace Internal } // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index a2dca2aab35..52be361305a 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -26,12 +26,11 @@ #include "clangmodelmanagersupport.h" #include "clangconstants.h" -#include "clangcurrentdocumentfilter.h" #include "clangdclient.h" #include "clangdquickfixfactory.h" #include "clangeditordocumentprocessor.h" #include "clangfollowsymbol.h" -#include "clanggloballocatorfilters.h" +#include "clangdlocatorfilters.h" #include "clanghoverhandler.h" #include "clangoverviewmodel.h" #include "clangprojectsettings.h" @@ -113,8 +112,7 @@ ClangModelManagerSupport::ClangModelManagerSupport() m_instance = this; watchForExternalChanges(); - CppEditor::CppModelManager::instance()->setCurrentDocumentFilter( - std::make_unique()); + cppModelManager()->setCurrentDocumentFilter(std::make_unique()); cppModelManager()->setLocatorFilter(std::make_unique()); cppModelManager()->setClassesFilter(std::make_unique()); cppModelManager()->setFunctionsFilter(std::make_unique()); diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp index 20938701157..ac7ec7e2b4f 100644 --- a/src/plugins/clangcodemodel/test/clangdtests.cpp +++ b/src/plugins/clangcodemodel/test/clangdtests.cpp @@ -393,7 +393,8 @@ void ClangdTestFollowSymbol::test() timer.stop(); QCOMPARE(actualLink.targetFilePath, filePath(targetFile)); - QEXPECT_FAIL("union member ref", "https://github.com/clangd/clangd/issues/877", Abort); + if (client()->versionNumber() < QVersionNumber(14)) + QEXPECT_FAIL("union member ref", "https://github.com/clangd/clangd/issues/877", Abort); QCOMPARE(actualLink.targetLine, targetLine); QCOMPARE(actualLink.targetColumn + 1, targetColumn); } diff --git a/src/plugins/clangtools/clangtool.cpp b/src/plugins/clangtools/clangtool.cpp index 2825c643e4d..c8de027bce6 100644 --- a/src/plugins/clangtools/clangtool.cpp +++ b/src/plugins/clangtools/clangtool.cpp @@ -340,9 +340,12 @@ static FileInfos sortedFileInfos(const QVector if (file.path == CppEditor::CppModelManager::configurationFileName()) continue; - if (file.active && CppEditor::ProjectFile::isSource(file.kind)) { + if (file.active + && (CppEditor::ProjectFile::isSource(file.kind) + || CppEditor::ProjectFile::isHeader(file.kind))) { + ProjectFile::Kind sourceKind = CppEditor::ProjectFile::sourceKind(file.kind); fileInfos.emplace_back(Utils::FilePath::fromString(file.path), - file.kind, + sourceKind, projectPart); } } diff --git a/src/plugins/clangtools/documentclangtoolrunner.cpp b/src/plugins/clangtools/documentclangtoolrunner.cpp index 5ec59339201..d62b882d4a2 100644 --- a/src/plugins/clangtools/documentclangtoolrunner.cpp +++ b/src/plugins/clangtools/documentclangtoolrunner.cpp @@ -160,11 +160,12 @@ static FileInfo getFileInfo(const FilePath &file, Project *project) if (!projectFile.active) continue; // found the best candidate, early return + ProjectFile::Kind sourceKind = ProjectFile::sourceKind(projectFile.kind); if (projectPart->buildTargetType != BuildTargetType::Unknown) - return FileInfo(projectFilePath, projectFile.kind, projectPart); + return FileInfo(projectFilePath, sourceKind, projectPart); // found something but keep looking for better candidates if (candidate.projectPart.isNull()) - candidate = FileInfo(projectFilePath, projectFile.kind, projectPart); + candidate = FileInfo(projectFilePath, sourceKind, projectPart); } } diff --git a/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp b/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp index ff4e6d331eb..acd9422fc74 100644 --- a/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp +++ b/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp @@ -213,7 +213,7 @@ void CMakeToolSettingsAccessor::saveCMakeTools(const QList &cmakeTo for (const CMakeTool *item : cmakeTools) { Utils::FilePath fi = item->cmakeExecutable(); - if (fi.isExecutableFile()) { + if (fi.needsDevice() || fi.isExecutableFile()) { // be graceful for device related stuff QVariantMap tmp = item->toMap(); if (tmp.isEmpty()) continue; diff --git a/src/plugins/cppeditor/cppprojectfile.cpp b/src/plugins/cppeditor/cppprojectfile.cpp index 382a0b7c056..fa3bf8c0238 100644 --- a/src/plugins/cppeditor/cppprojectfile.cpp +++ b/src/plugins/cppeditor/cppprojectfile.cpp @@ -87,6 +87,37 @@ bool ProjectFile::isAmbiguousHeader(const QString &filePath) return filePath.endsWith(".h"); } +ProjectFile::Kind ProjectFile::sourceForHeaderKind(ProjectFile::Kind kind) +{ + ProjectFile::Kind sourceKind; + switch (kind) { + case ProjectFile::CHeader: + sourceKind = ProjectFile::CSource; + break; + case ProjectFile::ObjCHeader: + sourceKind = ProjectFile::ObjCSource; + break; + case ProjectFile::ObjCXXHeader: + sourceKind = ProjectFile::ObjCXXSource; + break; + case ProjectFile::Unsupported: // no file extension, e.g. stl headers + case ProjectFile::AmbiguousHeader: + case ProjectFile::CXXHeader: + default: + sourceKind = ProjectFile::CXXSource; + } + + return sourceKind; +} + +ProjectFile::Kind ProjectFile::sourceKind(Kind kind) +{ + ProjectFile::Kind sourceKind = kind; + if (ProjectFile::isHeader(kind)) + sourceKind = ProjectFile::sourceForHeaderKind(kind); + return sourceKind; +} + bool ProjectFile::isHeader(ProjectFile::Kind kind) { switch (kind) { diff --git a/src/plugins/cppeditor/cppprojectfile.h b/src/plugins/cppeditor/cppprojectfile.h index f86c668d5ff..078b93cce9e 100644 --- a/src/plugins/cppeditor/cppprojectfile.h +++ b/src/plugins/cppeditor/cppprojectfile.h @@ -53,6 +53,9 @@ public: static Kind classifyByMimeType(const QString &mt); static Kind classify(const QString &filePath); + static Kind sourceForHeaderKind(Kind kind); + static Kind sourceKind(Kind kind); + static bool isSource(Kind kind); static bool isHeader(Kind kind); static bool isC(Kind kind); diff --git a/src/plugins/languageclient/languageclientutils.h b/src/plugins/languageclient/languageclientutils.h index d73e052590d..c8120766077 100644 --- a/src/plugins/languageclient/languageclientutils.h +++ b/src/plugins/languageclient/languageclientutils.h @@ -61,6 +61,6 @@ updateCodeActionRefactoringMarker(Client *client, const LanguageServerProtocol::CodeAction &action, const LanguageServerProtocol::DocumentUri &uri); void updateEditorToolBar(Core::IEditor *editor); -const QIcon symbolIcon(int type); +const QIcon LANGUAGECLIENT_EXPORT symbolIcon(int type); } // namespace LanguageClient diff --git a/src/plugins/languageclient/locatorfilter.cpp b/src/plugins/languageclient/locatorfilter.cpp index 5d2a583a27f..11588cc2ad2 100644 --- a/src/plugins/languageclient/locatorfilter.cpp +++ b/src/plugins/languageclient/locatorfilter.cpp @@ -63,8 +63,8 @@ void DocumentLocatorFilter::updateCurrentClient() TextEditor::TextDocument *document = TextEditor::TextDocument::currentTextDocument(); if (Client *client = LanguageClientManager::clientForDocument(document); - client && client->locatorsEnabled()) { - setEnabled(true); + client && (client->locatorsEnabled() || m_forced)) { + setEnabled(!m_forced); if (m_symbolCache != client->documentSymbolCache()) { disconnect(m_updateSymbolsConnection); m_symbolCache = client->documentSymbolCache(); @@ -98,8 +98,8 @@ void DocumentLocatorFilter::resetSymbols() m_currentSymbols.reset(); } -Core::LocatorFilterEntry generateLocatorEntry(const SymbolInformation &info, - Core::ILocatorFilter *filter) +static Core::LocatorFilterEntry generateLocatorEntry(const SymbolInformation &info, + Core::ILocatorFilter *filter) { Core::LocatorFilterEntry entry; entry.filter = filter; @@ -109,13 +109,31 @@ Core::LocatorFilterEntry generateLocatorEntry(const SymbolInformation &info, entry.displayIcon = symbolIcon(info.kind()); entry.internalData = QVariant::fromValue(info.location().toLink()); return entry; + } -Core::LocatorFilterEntry generateLocatorEntry(const DocumentSymbol &info, - Core::ILocatorFilter *filter) +Core::LocatorFilterEntry DocumentLocatorFilter::generateLocatorEntry(const SymbolInformation &info) { + return LanguageClient::generateLocatorEntry(info, this); +} + +QList DocumentLocatorFilter::generateLocatorEntries( + const SymbolInformation &info, const QRegularExpression ®exp, + const Core::LocatorFilterEntry &parent) +{ + Q_UNUSED(parent) + if (regexp.match(info.name()).hasMatch()) + return {generateLocatorEntry(info)}; + return {}; +} + +Core::LocatorFilterEntry DocumentLocatorFilter::generateLocatorEntry( + const DocumentSymbol &info, + const Core::LocatorFilterEntry &parent) +{ + Q_UNUSED(parent) Core::LocatorFilterEntry entry; - entry.filter = filter; + entry.filter = this; entry.displayName = info.name(); if (Utils::optional detail = info.detail()) entry.extraInfo = detail.value_or(QString()); @@ -125,6 +143,23 @@ Core::LocatorFilterEntry generateLocatorEntry(const DocumentSymbol &info, return entry; } +QList DocumentLocatorFilter::generateLocatorEntries( + const DocumentSymbol &info, const QRegularExpression ®exp, + const Core::LocatorFilterEntry &parent) +{ + QList entries; + const QList children = info.children().value_or(QList()); + const bool hasMatch = regexp.match(info.name()).hasMatch(); + Core::LocatorFilterEntry entry; + if (hasMatch || !children.isEmpty()) + entry = generateLocatorEntry(info, parent); + if (hasMatch) + entries << entry; + for (const DocumentSymbol &child : children) + entries << generateLocatorEntries(child, regexp, entry); + return entries; +} + template QList DocumentLocatorFilter::generateEntries(const QList &list, const QString &filter) @@ -138,11 +173,8 @@ QList DocumentLocatorFilter::generateEntries(const QLi if (!regexp.isValid()) return entries; - for (const T &item : list) { - QRegularExpressionMatch match = regexp.match(item.name()); - if (match.hasMatch()) - entries << generateLocatorEntry(item, this); - } + for (const T &item : list) + entries << generateLocatorEntries(item, regexp, {}); return entries; } diff --git a/src/plugins/languageclient/locatorfilter.h b/src/plugins/languageclient/locatorfilter.h index 6ef079ec45f..ebc35de57a7 100644 --- a/src/plugins/languageclient/locatorfilter.h +++ b/src/plugins/languageclient/locatorfilter.h @@ -26,6 +26,7 @@ #pragma once #include "client.h" +#include "languageclient_global.h" #include #include @@ -38,7 +39,7 @@ namespace Core { class IEditor; } namespace LanguageClient { -class DocumentLocatorFilter : public Core::ILocatorFilter +class LANGUAGECLIENT_EXPORT DocumentLocatorFilter : public Core::ILocatorFilter { Q_OBJECT public: @@ -57,6 +58,8 @@ signals: void symbolsUpToDate(QPrivateSignal); protected: + void forceUse() { m_forced = true; } + QPointer m_symbolCache; LanguageServerProtocol::DocumentUri m_currentUri; @@ -67,11 +70,25 @@ private: template QList generateEntries(const QList &list, const QString &filter); + QList generateLocatorEntries( + const LanguageServerProtocol::SymbolInformation &info, + const QRegularExpression ®exp, + const Core::LocatorFilterEntry &parent); + QList generateLocatorEntries( + const LanguageServerProtocol::DocumentSymbol &info, + const QRegularExpression ®exp, + const Core::LocatorFilterEntry &parent); + virtual Core::LocatorFilterEntry generateLocatorEntry( + const LanguageServerProtocol::DocumentSymbol &info, + const Core::LocatorFilterEntry &parent); + virtual Core::LocatorFilterEntry generateLocatorEntry( + const LanguageServerProtocol::SymbolInformation &info); QMutex m_mutex; QMetaObject::Connection m_updateSymbolsConnection; QMetaObject::Connection m_resetSymbolsConnection; Utils::optional m_currentSymbols; + bool m_forced = false; }; class LANGUAGECLIENT_EXPORT WorkspaceLocatorFilter : public Core::ILocatorFilter diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 852b5c27e87..f7fe67d213d 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -3313,7 +3313,7 @@ void ProjectExplorerPluginPrivate::openRecentProject(const QString &fileName) { if (!fileName.isEmpty()) { ProjectExplorerPlugin::OpenProjectResult result - = ProjectExplorerPlugin::openProject(FilePath::fromString(fileName)); + = ProjectExplorerPlugin::openProject(FilePath::fromUserInput(fileName)); if (!result) ProjectExplorerPlugin::showOpenProjectError(result); } diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 56a54a0071b..af066b81c23 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -448,7 +448,7 @@ void RewriterView::auxiliaryDataChanged(const ModelNode &node, const PropertyNam return; if (node.isRootNode()) { - if (name == "width" || name == "height" || name == "autoSize") + if (name == "width" || name == "height" || name == "autoSize" || name == "formeditorColor") return; } diff --git a/src/shared/proparser/qmakeparser.cpp b/src/shared/proparser/qmakeparser.cpp index f42d2181477..043faf8d00c 100644 --- a/src/shared/proparser/qmakeparser.cpp +++ b/src/shared/proparser/qmakeparser.cpp @@ -49,9 +49,9 @@ ProFileCache::ProFileCache() ProFileCache::~ProFileCache() { - foreach (const Entry &ent, parsed_files) - if (ent.pro) - ent.pro->deref(); + for (const auto &keyValuePair : parsed_files) + if (keyValuePair.second.pro) + keyValuePair.second.pro->deref(); QMakeVfs::deref(); } @@ -72,20 +72,37 @@ void ProFileCache::discardFile(int id) #endif auto it = parsed_files.find(id); if (it != parsed_files.end()) { + Entry &entry = it->second; #ifdef PROPARSER_THREAD_SAFE - if (it->locker) { - if (!it->locker->done) { - ++it->locker->waiters; - it->locker->cond.wait(&mutex); - if (!--it->locker->waiters) { - delete it->locker; - it->locker = 0; + Entry::Locker *locker = entry.locker; + if (locker) { // Either it's still being prepared or it's already done but someone + // else started waiting for it when it wasn't ready yet + // and is still waiting. + if (!locker->done) { + ++locker->waiters; + locker->cond.wait(&mutex); // Mutex is unlocked and relocked, + // everything may happen in this time window + it = parsed_files.find(id); // In meantime the iterator could have + // been invalidated, search for it again + Q_ASSERT(it != parsed_files.end()); // This would mean that some other + // waiter removed our entry, but only + // the last waiter should do it + if (!--locker->waiters) { + delete locker; + } else { + // There are still other waiters. Do nothing now. + locker->removeOnLastWaiter = true; + return; } + } else if (locker->waiters) { + // It's done but some waiters are still awaiting to be woken. + locker->removeOnLastWaiter = true; // Mark it for other waiters + return; } } #endif - if (it->pro) - it->pro->deref(); + if (entry.pro) + entry.pro->deref(); parsed_files.erase(it); } } @@ -95,26 +112,53 @@ void ProFileCache::discardFiles(const QString &prefix, QMakeVfs *vfs) #ifdef PROPARSER_THREAD_SAFE QMutexLocker lck(&mutex); #endif - auto it = parsed_files.begin(), end = parsed_files.end(); - while (it != end) { + auto it = parsed_files.begin(); + while (it != parsed_files.end()) { + const int id = it->first; // Note: this is empty for virtual files from other VFSes. - QString fn = vfs->fileNameForId(it.key()); + const QString fn = vfs->fileNameForId(id); if (fn.startsWith(prefix)) { + bool continueFromScratch = false; + Entry &entry = it->second; #ifdef PROPARSER_THREAD_SAFE - if (it->locker) { - if (!it->locker->done) { - ++it->locker->waiters; - it->locker->cond.wait(&mutex); - if (!--it->locker->waiters) { - delete it->locker; - it->locker = 0; + Entry::Locker *locker = entry.locker; + if (locker) { // Either it's still being prepared or it's already done but someone + // else started waiting for it when it wasn't ready yet + // and is still waiting. + if (!locker->done) { // It's still being prepared. + ++locker->waiters; + locker->cond.wait(&mutex); // Mutex is unlocked and relocked, + // everything may happen in this time window + it = parsed_files.find(id); // In meantime the iterator could have + // been invalidated, search for it again + Q_ASSERT(it != parsed_files.end()); // This would mean that some other + // waiter removed our entry, but only + // the last waiter should do it + if (!--locker->waiters) { + delete locker; // No more waiters, remove locker and entry + continueFromScratch = true; + } else { + // There are still other waiters. Do nothing now + locker->removeOnLastWaiter = true; + it = parsed_files.begin(); // Start from scratch, as new matching + // entries could have appeared. + // If we hit it again, we will skip it + // as the locker is already marked as done + continue; } + } else if (locker->waiters) { + // It's done but some waiters are still awaiting to be woken. + locker->removeOnLastWaiter = true; // Mark it for other waiters + ++it; // We skip it for now, other waiter will do the job + continue; } } #endif - if (it->pro) - it->pro->deref(); + if (entry.pro) + entry.pro->deref(); it = parsed_files.erase(it); + if (continueFromScratch) + it = parsed_files.begin(); } else { ++it; } @@ -187,20 +231,44 @@ ProFile *QMakeParser::parsedProFile(const QString &fileName, ParseFlags flags) if ((flags & ParseUseCache) && m_cache) { ProFileCache::Entry *ent; #ifdef PROPARSER_THREAD_SAFE - QMutexLocker locker(&m_cache->mutex); + QMutexLocker lck(&m_cache->mutex); #endif auto it = m_cache->parsed_files.find(id); if (it != m_cache->parsed_files.end()) { - ent = &*it; + ent = &it->second; #ifdef PROPARSER_THREAD_SAFE - if (ent->locker && !ent->locker->done) { - ++ent->locker->waiters; - QThreadPool::globalInstance()->releaseThread(); - ent->locker->cond.wait(locker.mutex()); - QThreadPool::globalInstance()->reserveThread(); - if (!--ent->locker->waiters) { - delete ent->locker; - ent->locker = 0; + ProFileCache::Entry::Locker *locker = ent->locker; + if (locker) { // Either it's still being prepared or it's already done but someone + // else started waiting for it when it wasn't ready yet + // and is still waiting. + if (!locker->done) { // It's still being prepared. + ++locker->waiters; + QThreadPool::globalInstance()->releaseThread(); + locker->cond.wait(lck.mutex()); // Mutex is unlocked and relocked, + // everything may happen in this time window + QThreadPool::globalInstance()->reserveThread(); + + it = m_cache->parsed_files.find(id); // In meantime the iterator could have + // been invalidated, search for it again + Q_ASSERT(it != m_cache->parsed_files.end()); // This would mean that some other + // waiter removed our entry, but only + // the last waiter should do it + const bool remove = locker->removeOnLastWaiter; // Some waiter marked this + // entry for removal. + if (!--locker->waiters) { // If we were the last waiter + delete locker; // we do it now + ent->locker = 0; + if (remove) { + if (ent->pro) + ent->pro->deref(); + m_cache->parsed_files.erase(it); + return nullptr; + } + } + if (remove) + return nullptr; + } else if (locker->removeOnLastWaiter) { + return nullptr; } } #endif @@ -210,7 +278,7 @@ ProFile *QMakeParser::parsedProFile(const QString &fileName, ParseFlags flags) ent = &m_cache->parsed_files[id]; #ifdef PROPARSER_THREAD_SAFE ent->locker = new ProFileCache::Entry::Locker; - locker.unlock(); + lck.unlock(); // We unlock the mutex and open a time window #endif QString contents; if (readFile(id, flags, &contents)) { @@ -222,11 +290,17 @@ ProFile *QMakeParser::parsedProFile(const QString &fileName, ParseFlags flags) } ent->pro = pro; #ifdef PROPARSER_THREAD_SAFE - locker.relock(); - if (ent->locker->waiters) { - ent->locker->done = true; + lck.relock(); // We relock it back and close the window. Someone could have added a new + // entry to the parsed_files list (or removed one). However, no one + // was awoken for our id in this time window. + it = m_cache->parsed_files.find(id); // In meantime the iterator could have + // been invalidated, search for it again + Q_ASSERT(it != m_cache->parsed_files.end()); // This would mean that our reference + // is also invalid + if (ent->locker->waiters) { // Someone runs getter or wants to remove it + ent->locker->done = true; // The structure is ready ent->locker->cond.wakeAll(); - } else { + } else { // No one was interested, remove locker as everything is ready now delete ent->locker; ent->locker = 0; } diff --git a/src/shared/proparser/qmakeparser.h b/src/shared/proparser/qmakeparser.h index 905b50608bd..f2bc076ce4a 100644 --- a/src/shared/proparser/qmakeparser.h +++ b/src/shared/proparser/qmakeparser.h @@ -29,13 +29,14 @@ #include "qmakevfs.h" #include "proitems.h" -#include #include #ifdef PROPARSER_THREAD_SAFE # include # include #endif +#include + QT_BEGIN_NAMESPACE class QMAKE_EXPORT QMakeParserHandler { @@ -208,16 +209,16 @@ private: ProFile *pro; #ifdef PROPARSER_THREAD_SAFE struct Locker { - Locker() : waiters(0), done(false) {} QWaitCondition cond; - int waiters; - bool done; + int waiters = 0; + bool done = false; + bool removeOnLastWaiter = false; }; Locker *locker; #endif }; - QHash parsed_files; + std::unordered_map parsed_files; #ifdef PROPARSER_THREAD_SAFE QMutex mutex; #endif