diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/HelperWidgets/FontComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/HelperWidgets/FontComboBox.qml index ffc774d7cd0..6cca085da75 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/HelperWidgets/FontComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/HelperWidgets/FontComboBox.qml @@ -58,12 +58,28 @@ Controls.ComboBox { Layout.fillWidth: true - onCurrentTextChanged: { + onAccepted: { if (backendValue === undefined) return; - if (backendValue.value !== currentText) - backendValue.value = currentText; + if (editText === "") + return + + if (backendValue.value !== editText) + backendValue.value = editText; + } + + onActivated: { + if (backendValue === undefined) + return; + + if (editText === "") + return + + var indexText = comboBox.textAt(index) + + if (backendValue.value !== indexText) + backendValue.value = indexText; } ExtendedFunctionButton { @@ -73,6 +89,13 @@ Controls.ComboBox { visible: comboBox.enabled } + Connections { + target: modelNodeBackend + onSelectionChanged: { + comboBox.editText = backendValue.value + } + } + Component.onCompleted: { //Hack to style the text input for (var i = 0; i < comboBox.children.length; i++) { diff --git a/src/libs/utils/macroexpander.cpp b/src/libs/utils/macroexpander.cpp index 6aa965a942c..755f2e0a89c 100644 --- a/src/libs/utils/macroexpander.cpp +++ b/src/libs/utils/macroexpander.cpp @@ -53,16 +53,22 @@ public: : m_accumulating(false), m_aborted(false), m_lockDepth(0) {} - bool resolveMacro(const QString &name, QString *ret) + bool resolveMacro(const QString &name, QString *ret, QSet &seen) { + // Prevent loops: + const int count = seen.count(); + seen.insert(this); + if (seen.count() == count) + return false; + bool found; *ret = value(name.toUtf8(), &found); if (found) return true; - found = Utils::anyOf(m_subProviders, [name, ret] (const MacroExpanderProvider &p) -> bool { + found = Utils::anyOf(m_subProviders, [name, ret, &seen] (const MacroExpanderProvider &p) -> bool { MacroExpander *expander = p ? p() : 0; - return expander && expander->resolveMacro(name, ret); + return expander && expander->d->resolveMacro(name, ret, seen); }); if (found) @@ -75,7 +81,7 @@ public: if (found) return true; - return this == globalMacroExpander()->d ? false : globalMacroExpander()->d->resolveMacro(name, ret); + return this == globalMacroExpander()->d ? false : globalMacroExpander()->d->resolveMacro(name, ret, seen); } QString value(const QByteArray &variable, bool *found) @@ -243,7 +249,8 @@ MacroExpander::~MacroExpander() */ bool MacroExpander::resolveMacro(const QString &name, QString *ret) const { - return d->resolveMacro(name, ret); + QSet seen; + return d->resolveMacro(name, ret, seen); } /*! diff --git a/src/libs/utils/outputformatter.cpp b/src/libs/utils/outputformatter.cpp index 64ae5033425..aae6a28d168 100644 --- a/src/libs/utils/outputformatter.cpp +++ b/src/libs/utils/outputformatter.cpp @@ -68,8 +68,6 @@ QPlainTextEdit *OutputFormatter::plainTextEdit() const void OutputFormatter::setPlainTextEdit(QPlainTextEdit *plainText) { - plainText->setReadOnly(true); - plainText->setTextInteractionFlags(plainText->textInteractionFlags() | Qt::TextSelectableByKeyboard); d->plainTextEdit = plainText; d->cursor = plainText ? plainText->textCursor() : QTextCursor(); initFormats(); diff --git a/src/libs/utils/stringutils.cpp b/src/libs/utils/stringutils.cpp index 85813004dec..04df87fb5f2 100644 --- a/src/libs/utils/stringutils.cpp +++ b/src/libs/utils/stringutils.cpp @@ -31,6 +31,7 @@ #include #include +#include #include @@ -153,7 +154,8 @@ bool AbstractMacroExpander::expandNestedMacros(const QString &str, int *pos, QSt *pos = i; return true; } - if (resolveMacro(varName, ret)) { + QSet seen; + if (resolveMacro(varName, ret, seen)) { *pos = i; if (!pattern.isEmpty() && currArg == &replace) { const QRegularExpression regexp(pattern); diff --git a/src/libs/utils/stringutils.h b/src/libs/utils/stringutils.h index 8e0c78f028f..20f6063710c 100644 --- a/src/libs/utils/stringutils.h +++ b/src/libs/utils/stringutils.h @@ -65,7 +65,7 @@ public: //! \param name The name of the expando //! \param ret Replacement string on output //! \return True if the expando was found - virtual bool resolveMacro(const QString &name, QString *ret) = 0; + virtual bool resolveMacro(const QString &name, QString *ret, QSet &seen) = 0; private: bool expandNestedMacros(const QString &str, int *pos, QString *ret); }; diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.cpp b/src/plugins/cmakeprojectmanager/builddirmanager.cpp index ca2fd4bd6a9..71fccc5df8c 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.cpp +++ b/src/plugins/cmakeprojectmanager/builddirmanager.cpp @@ -277,10 +277,6 @@ void BuildDirManager::generateProjectTree(CMakeProjectNode *root, const QListtarget()->project()->projectFilePath(); m_reader->generateProjectTree(root, allFiles); - - // Make sure the top level CMakeLists.txt is always visible: - if (root->isEmpty()) - root->addNode(new FileNode(projectFile, FileType::Project, false)); } void BuildDirManager::updateCodeModel(CppTools::RawProjectParts &rpps) @@ -336,7 +332,12 @@ QList BuildDirManager::buildTargets() const m_buildTargets.append(utilityTarget(CMakeBuildStep::installTarget(), this)); m_buildTargets.append(utilityTarget(CMakeBuildStep::testTarget(), this)); - m_buildTargets.append(m_reader->buildTargets()); + m_buildTargets.append(Utils::filtered(m_reader->buildTargets(), [](const CMakeBuildTarget &bt) { + return bt.title != CMakeBuildStep::allTarget() + && bt.title != CMakeBuildStep::cleanTarget() + && bt.title != CMakeBuildStep::installTarget() + && bt.title != CMakeBuildStep::testTarget(); + })); } return m_buildTargets; } diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 8ae17f13d72..b4113c53fdc 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -221,6 +221,10 @@ CMakeBuildConfiguration::generateProjectTree(const QList &allFi auto root = new CMakeProjectNode(target()->project()->projectDirectory()); m_buildDirManager->generateProjectTree(root, allFiles); + if (root->isEmpty()) { + delete root; + return nullptr; + } return root; } @@ -280,7 +284,9 @@ QList CMakeBuildConfiguration::completeCMakeConfiguration j.values = i.values; j.inCMakeCache = i.inCMakeCache; - j.isAdvanced = i.isAdvanced || i.type == CMakeConfigItem::INTERNAL; + j.isAdvanced = i.isAdvanced; + j.isHidden = i.type == CMakeConfigItem::INTERNAL || i.type == CMakeConfigItem::STATIC; + switch (i.type) { case CMakeConfigItem::FILEPATH: j.type = ConfigModel::DataItem::FILE; diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp index d87a538f98b..4bc51dd42ec 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp @@ -267,7 +267,7 @@ void CMakeBuildStep::run(QFutureInterface &fi) m_runTrigger = connect(bc, &CMakeBuildConfiguration::dataAvailable, this, [this, &fi]() { runImpl(fi); }); m_errorTrigger = connect(bc, &CMakeBuildConfiguration::errorOccured, - this, [this, &fi]() { reportRunResult(fi, false); }); + this, [this, &fi](const QString& em) { handleCMakeError(fi, em); }); } else { runImpl(fi); } @@ -276,10 +276,21 @@ void CMakeBuildStep::run(QFutureInterface &fi) void CMakeBuildStep::runImpl(QFutureInterface &fi) { // Do the actual build: + disconnectTriggers(); + AbstractProcessStep::run(fi); +} + +void CMakeBuildStep::handleCMakeError(QFutureInterface &fi, const QString& errorMessage) +{ + disconnectTriggers(); + AbstractProcessStep::stdError(tr("Error parsing CMake: %1\n").arg(errorMessage)); + reportRunResult(fi, false); +} + +void CMakeBuildStep::disconnectTriggers() +{ disconnect(m_runTrigger); disconnect(m_errorTrigger); - - AbstractProcessStep::run(fi); } BuildStepConfigWidget *CMakeBuildStep::createConfigWidget() diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.h b/src/plugins/cmakeprojectmanager/cmakebuildstep.h index 70d89c87830..08cee9a01cb 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.h @@ -104,6 +104,8 @@ private: void ctor(ProjectExplorer::BuildStepList *bsl); void runImpl(QFutureInterface &fi); + void handleCMakeError(QFutureInterface &fi, const QString& errorMessage); + void disconnectTriggers(); void handleBuildTargetChanges(); CMakeRunConfiguration *targetsActiveRunConfiguration() const; diff --git a/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp b/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp index 2b09a3769c7..258803d45e7 100644 --- a/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp @@ -170,15 +170,20 @@ std::function CMakeCo CMakeConfigItem CMakeConfigItem::fromString(const QString &s) { - // Strip comments: + // Strip comments (only at start of line!): int commentStart = s.count(); - int pos = s.indexOf(QLatin1Char('#')); - if (pos >= 0) - commentStart = pos; - pos = s.indexOf(QLatin1String("//")); - if (pos >= 0 && pos < commentStart) - commentStart = pos; - + for (int i = 0; i < s.count(); ++i) { + const QChar c = s.at(i); + if (c == ' ' || c == '\t') + continue; + else if ((c == '#') + || (c == '/' && i < s.count() - 1 && s.at(i + 1) == '/')) { + commentStart = i; + break; + } else { + break; + } + } const QString line = s.mid(0, commentStart); // Split up line: diff --git a/src/plugins/cmakeprojectmanager/cmakeparser.cpp b/src/plugins/cmakeprojectmanager/cmakeparser.cpp index d31ee71daf2..a06a68f6840 100644 --- a/src/plugins/cmakeprojectmanager/cmakeparser.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeparser.cpp @@ -32,7 +32,7 @@ using namespace CMakeProjectManager; using namespace ProjectExplorer; -const char COMMON_ERROR_PATTERN[] = "^CMake Error at (.*):([0-9]*) \\((.*)\\):"; +const char COMMON_ERROR_PATTERN[] = "^CMake Error at (.*):([0-9]*)( \\((.*)\\))?:"; const char NEXT_SUBERROR_PATTERN[] = "^CMake Error in (.*):"; const char LOCATION_LINE_PATTERN[] = ":(\\d+):(?:(\\d+))?$"; @@ -250,6 +250,20 @@ void Internal::CMakeProjectPlugin::testCMakeParser_data() Utils::FileName(), -1, categoryBuild)) << QString(); + QTest::newRow("cmake error at") + << QString::fromLatin1("CMake Error at CMakeLists.txt:4:\n" + " Parse error. Expected \"(\", got newline with text \"\n" + "\n" + " \".\n") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Error, + QLatin1String("Parse error. Expected \"(\", got newline with text \" \"."), + Utils::FileName::fromUserInput(QLatin1String("CMakeLists.txt")), 4, + categoryBuild)) + << QString(); + QTest::newRow("cmake warning") << QString::fromLatin1("Syntax Warning in cmake code at\n" "/test/path/CMakeLists.txt:9:15\n" diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index 101649702cf..91aa189e64c 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -147,8 +147,8 @@ void CMakeProject::updateProjectData(CMakeBuildConfiguration *bc) auto newRoot = bc->generateProjectTree(m_allFiles); if (newRoot) { - setRootProjectNode(newRoot); setDisplayName(newRoot->displayName()); + setRootProjectNode(newRoot); } updateApplicationAndDeploymentTargets(); @@ -495,8 +495,13 @@ void CMakeProject::updateApplicationAndDeploymentTargets() if (ct.targetType == UtilityType) continue; - if (ct.targetType == ExecutableType || ct.targetType == DynamicLibraryType) - deploymentData.addFile(ct.executable.toString(), deploymentPrefix + buildDir.relativeFilePath(ct.executable.toFileInfo().dir().path()), DeployableFile::TypeExecutable); + if (ct.targetType == ExecutableType || ct.targetType == DynamicLibraryType) { + if (!ct.executable.isEmpty()) { + deploymentData.addFile(ct.executable.toString(), + deploymentPrefix + buildDir.relativeFilePath(ct.executable.toFileInfo().dir().path()), + DeployableFile::TypeExecutable); + } + } if (ct.targetType == ExecutableType) { FileName srcWithTrailingSlash = FileName::fromString(ct.sourceDirectory.toString()); srcWithTrailingSlash.appendString('/'); diff --git a/src/plugins/cmakeprojectmanager/configmodel.cpp b/src/plugins/cmakeprojectmanager/configmodel.cpp index 98d9cf05b7a..7336a2a7a2b 100644 --- a/src/plugins/cmakeprojectmanager/configmodel.cpp +++ b/src/plugins/cmakeprojectmanager/configmodel.cpp @@ -257,7 +257,9 @@ void ConfigModel::setConfiguration(const QList &config) QList result; while (newIt != newEndIt && oldIt != oldEndIt) { - if (newIt->key < oldIt->key) { + if (newIt->isHidden) { + ++newIt; + } else if (newIt->key < oldIt->key) { // Add new entry: result << InternalDataItem(*newIt); ++newIt; @@ -279,8 +281,11 @@ void ConfigModel::setConfiguration(const QList &config) } // Add remaining new entries: - for (; newIt != newEndIt; ++newIt) + for (; newIt != newEndIt; ++newIt) { + if (newIt->isHidden) + continue; result << InternalDataItem(*newIt); + } beginResetModel(); m_configuration = result; diff --git a/src/plugins/cmakeprojectmanager/configmodel.h b/src/plugins/cmakeprojectmanager/configmodel.h index 94fef0b6648..f9629f702fa 100644 --- a/src/plugins/cmakeprojectmanager/configmodel.h +++ b/src/plugins/cmakeprojectmanager/configmodel.h @@ -45,6 +45,7 @@ public: QString key; Type type = STRING; + bool isHidden = false; bool isAdvanced = false; bool inCMakeCache = false; QString value; diff --git a/src/plugins/cmakeprojectmanager/servermodereader.cpp b/src/plugins/cmakeprojectmanager/servermodereader.cpp index 84070ee98ce..c784997332b 100644 --- a/src/plugins/cmakeprojectmanager/servermodereader.cpp +++ b/src/plugins/cmakeprojectmanager/servermodereader.cpp @@ -73,6 +73,17 @@ ServerModeReader::ServerModeReader() if (m_cmakeFiles.contains(document->filePath())) emit dirty(); }); + + connect(&m_parser, &CMakeParser::addOutput, + this, [](const QString &m) { Core::MessageManager::write(m); }); + connect(&m_parser, &CMakeParser::addTask, this, [this](const Task &t) { + Task editable(t); + if (!editable.file.isEmpty()) { + QDir srcDir(m_parameters.sourceDirectory.toString()); + editable.file = FileName::fromString(srcDir.absoluteFilePath(editable.file.toString())); + } + TaskHub::addTask(editable); + }); } ServerModeReader::~ServerModeReader() @@ -98,8 +109,13 @@ void ServerModeReader::setParameters(const BuildDirReader::Parameters &p) this, &ServerModeReader::handleProgress); connect(m_cmakeServer.get(), &ServerMode::cmakeSignal, this, &ServerModeReader::handleSignal); - connect(m_cmakeServer.get(), &ServerMode::cmakeMessage, - this, [this](const QString &m) { Core::MessageManager::write(m); }); + connect(m_cmakeServer.get(), &ServerMode::cmakeMessage, [this](const QString &m) { + const QStringList lines = m.split('\n'); + for (const QString &l : lines) { + m_parser.stdError(l); + Core::MessageManager::write(l); + } + }); connect(m_cmakeServer.get(), &ServerMode::message, this, [](const QString &m) { Core::MessageManager::write(m); }); connect(m_cmakeServer.get(), &ServerMode::connected, @@ -170,6 +186,7 @@ void ServerModeReader::stop() m_future->reportFinished(); m_future.reset(); } + m_parser.flush(); } bool ServerModeReader::isReady() const @@ -732,6 +749,9 @@ void ServerModeReader::addFileGroups(ProjectNode *targetRoot, void ServerModeReader::addHeaderNodes(ProjectNode *root, const QList knownHeaders, const QList &allFiles) { + if (root->isEmpty()) + return; + auto headerNode = new VirtualFolderNode(root->filePath(), Node::DefaultPriority - 5); headerNode->setDisplayName(tr("")); diff --git a/src/plugins/cmakeprojectmanager/servermodereader.h b/src/plugins/cmakeprojectmanager/servermodereader.h index 0c3a3c2fd8c..4265d9627c3 100644 --- a/src/plugins/cmakeprojectmanager/servermodereader.h +++ b/src/plugins/cmakeprojectmanager/servermodereader.h @@ -27,6 +27,7 @@ #include "builddirreader.h" #include "servermode.h" +#include "cmakeparser.h" #include @@ -148,6 +149,8 @@ private: QList m_projects; mutable QList m_targets; QList m_fileGroups; + + CMakeParser m_parser; }; } // namespace Internal diff --git a/src/plugins/cmakeprojectmanager/tealeafreader.cpp b/src/plugins/cmakeprojectmanager/tealeafreader.cpp index 8c37220f262..e7ecd026dd4 100644 --- a/src/plugins/cmakeprojectmanager/tealeafreader.cpp +++ b/src/plugins/cmakeprojectmanager/tealeafreader.cpp @@ -243,6 +243,10 @@ CMakeConfig TeaLeafReader::takeParsedConfiguration() { FileName cacheFile = m_parameters.buildDirectory; cacheFile.appendPath(QLatin1String("CMakeCache.txt")); + + if (!cacheFile.exists()) + return { }; + QString errorMessage; CMakeConfig result = BuildDirManager::parseConfiguration(cacheFile, &errorMessage); @@ -266,6 +270,9 @@ CMakeConfig TeaLeafReader::takeParsedConfiguration() void TeaLeafReader::generateProjectTree(CMakeProjectNode *root, const QList &allFiles) { + if (m_files.isEmpty()) + return; + root->setDisplayName(m_projectName); // Delete no longer necessary file watcher based on m_cmakeFiles: diff --git a/src/plugins/cmakeprojectmanager/treescanner.cpp b/src/plugins/cmakeprojectmanager/treescanner.cpp index 7bb4ed259e3..877491a9de7 100644 --- a/src/plugins/cmakeprojectmanager/treescanner.cpp +++ b/src/plugins/cmakeprojectmanager/treescanner.cpp @@ -25,6 +25,8 @@ #include "treescanner.h" +#include +#include #include #include @@ -67,7 +69,10 @@ bool TreeScanner::asyncScanForFiles(const Utils::FileName &directory) m_scanFuture = fi->future(); m_futureWatcher.setFuture(m_scanFuture); - Utils::runAsync([this, fi, directory]() { TreeScanner::scanForFiles(fi, directory, m_filter, m_factory); }); + if (m_versionControls.isEmpty()) + m_versionControls = Core::VcsManager::versionControls(); + + Utils::runAsync([this, fi, directory]() { TreeScanner::scanForFiles(fi, directory, m_filter, m_factory, m_versionControls); }); return true; } @@ -144,13 +149,17 @@ FileType TreeScanner::genericFileType(const Utils::MimeType &mimeType, const Uti return Node::fileTypeForMimeType(mimeType); } -void TreeScanner::scanForFiles(FutureInterface *fi, const Utils::FileName& directory, const FileFilter &filter, const FileTypeFactory &factory) +void TreeScanner::scanForFiles(FutureInterface *fi, const Utils::FileName& directory, + const FileFilter &filter, const FileTypeFactory &factory, + QList &versionControls) { std::unique_ptr fip(fi); fip->reportStarted(); - Result nodes = FileNode::scanForFiles(directory, - [&filter, &factory](const Utils::FileName &fn) -> FileNode * { + Result nodes + = FileNode::scanForFilesWithVersionControls( + directory, + [&filter, &factory](const Utils::FileName &fn) -> FileNode * { const Utils::MimeType mimeType = Utils::mimeTypeForFile(fn.toString()); // Skip some files during scan. @@ -163,8 +172,7 @@ void TreeScanner::scanForFiles(FutureInterface *fi, const Utils::FileName& direc type = factory(mimeType, fn); return new FileNode(fn, type, false); - }, - fip.get()); + }, versionControls, fip.get()); Utils::sort(nodes, ProjectExplorer::Node::sortByPath); diff --git a/src/plugins/cmakeprojectmanager/treescanner.h b/src/plugins/cmakeprojectmanager/treescanner.h index 33b4522a204..146076f1f2a 100644 --- a/src/plugins/cmakeprojectmanager/treescanner.h +++ b/src/plugins/cmakeprojectmanager/treescanner.h @@ -36,6 +36,8 @@ #include +namespace Core { class IVersionControl; } + namespace CMakeProjectManager { namespace Internal { @@ -86,7 +88,8 @@ signals: private: static void scanForFiles(FutureInterface *fi, const Utils::FileName &directory, - const FileFilter &filter, const FileTypeFactory &factory); + const FileFilter &filter, const FileTypeFactory &factory, + QList &versionControls); private: FileFilter m_filter; @@ -94,6 +97,7 @@ private: FutureWatcher m_futureWatcher; Future m_scanFuture; + QList m_versionControls; }; } // namespace Internal diff --git a/src/plugins/coreplugin/iversioncontrol.h b/src/plugins/coreplugin/iversioncontrol.h index eb880e7c70f..eddcee4ff37 100644 --- a/src/plugins/coreplugin/iversioncontrol.h +++ b/src/plugins/coreplugin/iversioncontrol.h @@ -100,6 +100,8 @@ public: * * It will return true only for exact matches of the name, not for e.g. files in a * directory owned by the version control system (e.g. .git/control). + * + * This method needs to be thread safe! */ virtual bool isVcsFileOrDirectory(const Utils::FileName &fileName) const = 0; diff --git a/src/plugins/cpptools/compileroptionsbuilder.cpp b/src/plugins/cpptools/compileroptionsbuilder.cpp index 0d89c12f337..8f5fd397fb7 100644 --- a/src/plugins/cpptools/compileroptionsbuilder.cpp +++ b/src/plugins/cpptools/compileroptionsbuilder.cpp @@ -300,6 +300,7 @@ void CompilerOptionsBuilder::addDefineToAvoidIncludingGccOrMinGwIntrinsics() if (type == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID || type == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID) { addDefine("#define _X86INTRIN_H_INCLUDED"); + addDefine("#define BOOST_UUID_NO_SIMD"); } } diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index b91543b9765..0812e35bfef 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -2303,26 +2303,32 @@ void DebuggerPluginPrivate::requestContextMenu(TextEditorWidget *widget, } // Run to, jump to line below in stopped state. - if (currentEngine()->state() == InferiorStopOk && args.isValid()) { + DebuggerEngine *engine = currentEngine(); + QTC_ASSERT(engine, return); + if (engine->state() == InferiorStopOk && args.isValid()) { menu->addSeparator(); - if (currentEngine()->hasCapability(RunToLineCapability)) { + if (engine->hasCapability(RunToLineCapability)) { auto act = menu->addAction(args.address ? DebuggerEngine::tr("Run to Address 0x%1").arg(args.address, 0, 16) : DebuggerEngine::tr("Run to Line %1").arg(args.lineNumber)); connect(act, &QAction::triggered, [this, args] { - currentEngine()->executeRunToLine(args); + DebuggerEngine *engine = currentEngine(); + QTC_ASSERT(engine, return); + engine->executeRunToLine(args); }); } - if (currentEngine()->hasCapability(JumpToLineCapability)) { + if (engine->hasCapability(JumpToLineCapability)) { auto act = menu->addAction(args.address ? DebuggerEngine::tr("Jump to Address 0x%1").arg(args.address, 0, 16) : DebuggerEngine::tr("Jump to Line %1").arg(args.lineNumber)); connect(act, &QAction::triggered, [this, args] { - currentEngine()->executeJumpToLine(args); + DebuggerEngine *engine = currentEngine(); + QTC_ASSERT(engine, return); + engine->executeJumpToLine(args); }); } // Disassemble current function in stopped state. - if (currentEngine()->hasCapability(DisassemblerCapability)) { + if (engine->hasCapability(DisassemblerCapability)) { StackFrame frame; frame.function = cppFunctionAt(args.fileName, lineNumber, 1); frame.line = 42; // trick gdb into mixed mode. @@ -2331,7 +2337,9 @@ void DebuggerPluginPrivate::requestContextMenu(TextEditorWidget *widget, .arg(frame.function); auto act = new QAction(text, menu); connect(act, &QAction::triggered, [this, frame] { - currentEngine()->openDisassemblerView(Location(frame)); + DebuggerEngine *engine = currentEngine(); + QTC_ASSERT(engine, return); + engine->openDisassemblerView(Location(frame)); }); menu->addAction(act); } diff --git a/src/plugins/nim/project/nimproject.cpp b/src/plugins/nim/project/nimproject.cpp index 5db9fd554a3..8910bd42af6 100644 --- a/src/plugins/nim/project/nimproject.cpp +++ b/src/plugins/nim/project/nimproject.cpp @@ -31,6 +31,8 @@ #include "../nimconstants.h" #include +#include +#include #include #include #include @@ -113,8 +115,11 @@ void NimProject::collectProjectFiles() m_lastProjectScan.start(); QTC_ASSERT(!m_futureWatcher.future().isRunning(), return); FileName prjDir = projectDirectory(); - QFuture> future = Utils::runAsync([prjDir] { - return FileNode::scanForFiles(prjDir, [](const FileName &fn) { return new FileNode(fn, FileType::Source, false); }); + const QList versionControls = Core::VcsManager::versionControls(); + QFuture> future = Utils::runAsync([prjDir, versionControls] { + return FileNode::scanForFilesWithVersionControls( + prjDir, [](const FileName &fn) { return new FileNode(fn, FileType::Source, false); }, + versionControls); }); m_futureWatcher.setFuture(future); Core::ProgressManager::addTask(future, tr("Scanning for Nim files"), "Nim.Project.Scan"); diff --git a/src/plugins/projectexplorer/appoutputpane.cpp b/src/plugins/projectexplorer/appoutputpane.cpp index ce5511e9101..65a94202a70 100644 --- a/src/plugins/projectexplorer/appoutputpane.cpp +++ b/src/plugins/projectexplorer/appoutputpane.cpp @@ -149,7 +149,10 @@ void TabWidget::slotContextMenuRequested(const QPoint &pos) AppOutputPane::RunControlTab::RunControlTab(RunControl *rc, Core::OutputWindow *w) : runControl(rc), window(w) -{ } +{ + if (rc && w) + w->setFormatter(rc->outputFormatter()); +} AppOutputPane::AppOutputPane() : m_mainWidget(new QWidget), @@ -262,7 +265,7 @@ AppOutputPane::~AppOutputPane() qDebug() << "OutputPane::~OutputPane: Entries left" << m_runControlTabs.size(); foreach (const RunControlTab &rt, m_runControlTabs) { - rt.window->setFormatter(nullptr); + delete rt.window; delete rt.runControl; } delete m_mainWidget; @@ -416,13 +419,14 @@ void AppOutputPane::createNewOutputWindow(RunControl *rc) RunControlTab &tab = m_runControlTabs[tabIndex]; // Reuse this tab delete tab.runControl; - handleOldOutput(tab.window); tab.runControl = rc; + tab.window->setFormatter(rc ? rc->outputFormatter() : nullptr); + + handleOldOutput(tab.window); // Update the title. m_tabWidget->setTabText(tabIndex, rc->displayName()); - tab.window->setFormatter(nullptr); tab.window->scrollToBottom(); if (debug) qDebug() << "OutputPane::createNewOutputWindow: Reusing tab" << tabIndex << " for " << rc; @@ -435,7 +439,6 @@ void AppOutputPane::createNewOutputWindow(RunControl *rc) Core::OutputWindow *ow = new Core::OutputWindow(context, m_tabWidget); ow->setWindowTitle(tr("Application Output Window")); ow->setWindowIcon(Icons::WINDOW.icon()); - ow->setFormatter(nullptr); ow->setWordWrapEnabled(ProjectExplorerPlugin::projectExplorerSettings().wrapAppOutput); ow->setMaxLineCount(ProjectExplorerPlugin::projectExplorerSettings().maxAppOutputLines); ow->setWheelZoomEnabled(TextEditor::TextEditorSettings::behaviorSettings().m_scrollWheelZooming); @@ -590,9 +593,8 @@ bool AppOutputPane::closeTab(int tabIndex, CloseTabMode closeTabMode) } m_tabWidget->removeTab(tabIndex); - m_runControlTabs[index].window->setFormatter(nullptr); - delete m_runControlTabs[index].runControl; delete m_runControlTabs[index].window; + delete m_runControlTabs[index].runControl; m_runControlTabs.removeAt(index); updateCloseActions(); @@ -706,10 +708,6 @@ void AppOutputPane::contextMenuRequested(const QPoint &pos, int index) void AppOutputPane::slotRunControlStarted() { RunControl *current = currentRunControl(); - const int rcIndex = indexOf(current); - if (rcIndex >= 0 && m_runControlTabs.at(rcIndex).window) - m_runControlTabs.at(rcIndex).window->setFormatter(current->outputFormatter()); - if (current && current == sender()) enableButtons(current, true); // RunControl::isRunning() cannot be trusted in signal handler. } @@ -740,8 +738,6 @@ void AppOutputPane::slotRunControlFinished2(RunControl *sender) if (current && current == sender) enableButtons(current, false); // RunControl::isRunning() cannot be trusted in signal handler. - m_runControlTabs.at(senderIndex).window->setFormatter(nullptr); // Reset formater for this RC - ProjectExplorerPlugin::instance()->updateRunActions(); if (!isRunning()) diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp index aa619aa4263..741dc394e71 100644 --- a/src/plugins/projectexplorer/projectnodes.cpp +++ b/src/plugins/projectexplorer/projectnodes.cpp @@ -339,7 +339,8 @@ FileType FileNode::fileType() const static QList scanForFilesRecursively(const Utils::FileName &directory, const std::function factory, QSet &visited, QFutureInterface> *future, - double progressStart, double progressRange) + double progressStart, double progressRange, + const QList &versionControls) { QList result; @@ -351,8 +352,6 @@ static QList scanForFilesRecursively(const Utils::FileName &director if (visitedCount == visited.count()) return result; - const Core::IVersionControl *vcsControl - = Core::VcsManager::findVersionControlForDirectory(baseDir.absolutePath(), nullptr); const QList entries = baseDir.entryInfoList(QStringList(), QDir::AllEntries|QDir::NoDotAndDotDot); double progress = 0; const double progressIncrement = progressRange / static_cast(entries.count()); @@ -362,9 +361,11 @@ static QList scanForFilesRecursively(const Utils::FileName &director return result; const Utils::FileName entryName = Utils::FileName::fromString(entry.absoluteFilePath()); - if (!vcsControl || !vcsControl->isVcsFileOrDirectory(entryName)) { + if (!Utils::contains(versionControls, [&entryName](const Core::IVersionControl *vc) { + return vc->isVcsFileOrDirectory(entryName); + })) { if (entry.isDir()) - result.append(scanForFilesRecursively(entryName, factory, visited, future, progress, progressIncrement)); + result.append(scanForFilesRecursively(entryName, factory, visited, future, progress, progressIncrement, versionControls)); else if (FileNode *node = factory(entryName)) result.append(node); } @@ -382,14 +383,24 @@ static QList scanForFilesRecursively(const Utils::FileName &director return result; } + QList FileNode::scanForFiles(const Utils::FileName &directory, - const std::function factory, - QFutureInterface> *future) + const std::function factory, + QFutureInterface > *future) +{ + return FileNode::scanForFilesWithVersionControls(directory, factory, QList(), future); +} + +QList +FileNode::scanForFilesWithVersionControls(const Utils::FileName &directory, + const std::function factory, + const QList &versionControls, + QFutureInterface> *future) { QSet visited; if (future) future->setProgressRange(0, 1000000); - return scanForFilesRecursively(directory, factory, visited, future, 0.0, 1000000.0); + return scanForFilesRecursively(directory, factory, visited, future, 0.0, 1000000.0, versionControls); } bool FileNode::supportsAction(ProjectAction action, Node *node) const diff --git a/src/plugins/projectexplorer/projectnodes.h b/src/plugins/projectexplorer/projectnodes.h index 7c440b73559..746f7437b44 100644 --- a/src/plugins/projectexplorer/projectnodes.h +++ b/src/plugins/projectexplorer/projectnodes.h @@ -37,6 +37,7 @@ #include namespace Utils { class MimeType; } +namespace Core { class IVersionControl; } namespace ProjectExplorer { @@ -188,9 +189,16 @@ public: FileNode *asFileNode() final { return this; } const FileNode *asFileNode() const final { return this; } + // For ABI compatibility, remove in QtC 4.4: static QList scanForFiles(const Utils::FileName &directory, const std::function factory, QFutureInterface> *future = nullptr); + + static QList + scanForFilesWithVersionControls(const Utils::FileName &directory, + const std::function factory, + const QList &versionControls, + QFutureInterface> *future = nullptr); bool supportsAction(ProjectAction action, Node *node) const override; private: diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp index 6022602f517..817676bfeea 100644 --- a/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp @@ -330,7 +330,7 @@ void QmakeProjectManagerPlugin::updateContextActions() Project *project = ProjectTree::currentProject(); ContainerNode *containerNode = node ? node->asContainerNode() : nullptr; - ProjectNode *proFileNode = containerNode ? containerNode->rootProjectNode() : dynamic_cast(node); + QmakeProFileNode *proFileNode = dynamic_cast(containerNode ? containerNode->rootProjectNode() : node); m_addLibraryActionContextMenu->setEnabled(proFileNode); QmakeProject *qmakeProject = qobject_cast(QmakeManager::contextProject()); diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp index dbb100d8a18..c1aff9dabd7 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp @@ -26,6 +26,7 @@ #include "gradientmodel.h" #include "qmlanchorbindingproxy.h" +#include "propertyeditorview.h" #include #include @@ -35,7 +36,7 @@ #include GradientModel::GradientModel(QObject *parent) : - QAbstractListModel(parent), m_lock(false) + QAbstractListModel(parent), m_locked(false) { } @@ -93,7 +94,7 @@ QVariant GradientModel::data(const QModelIndex &index, int role) const int GradientModel::addStop(qreal position, const QColor &color) { - if (m_lock) + if (m_locked) return -1; if (!m_itemNode.isValid() || gradientPropertyName().isEmpty()) @@ -131,7 +132,7 @@ int GradientModel::addStop(qreal position, const QColor &color) void GradientModel::addGradient() { - if (m_lock) + if (m_locked) return; if (!m_itemNode.isValid() || gradientPropertyName().isEmpty()) @@ -174,7 +175,7 @@ void GradientModel::addGradient() void GradientModel::setColor(int index, const QColor &color) { - if (m_lock) + if (locked()) return; if (!m_itemNode.modelNode().isSelected()) @@ -191,7 +192,7 @@ void GradientModel::setColor(int index, const QColor &color) void GradientModel::setPosition(int index, qreal positition) { - if (m_lock) + if (locked()) return; if (index < rowCount()) { @@ -266,12 +267,12 @@ void GradientModel::deleteGradient() void GradientModel::lock() { - m_lock = true; + m_locked = true; } void GradientModel::unlock() { - m_lock = false; + m_locked = false; } void GradientModel::registerDeclarativeType() @@ -281,11 +282,11 @@ void GradientModel::registerDeclarativeType() void GradientModel::setupModel() { - m_lock = true; + m_locked = true; beginResetModel(); endResetModel(); - m_lock = false; + m_locked = false; } void GradientModel::setAnchorBackend(const QVariant &anchorBackend) @@ -300,12 +301,12 @@ void GradientModel::setAnchorBackend(const QVariant &anchorBackend) setupModel(); - m_lock = true; + m_locked = true; emit anchorBackendChanged(); emit hasGradientChanged(); - m_lock = false; + m_locked = false; } QString GradientModel::gradientPropertyName() const @@ -323,3 +324,16 @@ bool GradientModel::hasGradient() const return m_itemNode.isValid() && m_itemNode.modelNode().hasProperty(gradientPropertyName().toUtf8()); } + +bool GradientModel::locked() const +{ + if (m_locked) + return true; + + QmlDesigner::PropertyEditorView *view = qobject_cast(m_itemNode.view()); + + if (view && view->locked()) + return true; + + return false; +} diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.h b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.h index 39f1b576984..943d6ae22b3 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.h +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.h @@ -76,11 +76,12 @@ private: QString gradientPropertyName() const; void setGradientPropertyName(const QString &name); bool hasGradient() const; + bool locked() const; private: QmlDesigner::QmlItemNode m_itemNode; QString m_gradientPropertyName; - bool m_lock; + bool m_locked; }; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index ec65ddc0433..a38a6b93aff 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -135,7 +135,7 @@ void PropertyEditorView::changeValue(const QString &name) if (propertyName.isNull()) return; - if (m_locked) + if (locked()) return; if (propertyName == "className") @@ -231,7 +231,7 @@ void PropertyEditorView::changeExpression(const QString &propertyName) if (name.isNull()) return; - if (m_locked) + if (locked()) return; if (!m_selectedNode.isValid()) @@ -306,7 +306,7 @@ void PropertyEditorView::exportPopertyAsAlias(const QString &name) if (name.isNull()) return; - if (m_locked) + if (locked()) return; if (!m_selectedNode.isValid()) @@ -340,7 +340,7 @@ void PropertyEditorView::removeAliasExport(const QString &name) if (name.isNull()) return; - if (m_locked) + if (locked()) return; if (!m_selectedNode.isValid()) @@ -362,6 +362,11 @@ void PropertyEditorView::removeAliasExport(const QString &name) } } +bool PropertyEditorView::locked() const +{ + return m_locked; +} + void PropertyEditorView::updateSize() { if (!m_qmlBackEndForCurrentType) @@ -523,7 +528,6 @@ void PropertyEditorView::modelAttached(Model *model) m_locked = true; - resetView(); if (!m_setupCompleted) { m_singleShotTimer->setSingleShot(true); m_singleShotTimer->setInterval(100); @@ -532,6 +536,8 @@ void PropertyEditorView::modelAttached(Model *model) } m_locked = false; + + resetView(); } void PropertyEditorView::modelAboutToBeDetached(Model *model) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h index 07073976234..b9028aad3db 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h @@ -92,6 +92,8 @@ public: void exportPopertyAsAlias(const QString &name); void removeAliasExport(const QString &name); + bool locked() const; + protected: void timerEvent(QTimerEvent *event) override; void setupPane(const TypeName &typeName); diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp index 9f4d24c03f8..e8fbe3a71cf 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp @@ -344,7 +344,7 @@ QString NodeInstanceServerProxy::qrcMappingString() const foreach (const StringPair &pair, rewriterView->qrcMapping()) { if (!mappingString.isEmpty()) - mappingString.append(QLatin1String(",")); + mappingString.append(QLatin1String(";")); mappingString.append(pair.first); mappingString.append(QLatin1String("=")); mappingString.append(pair.second); diff --git a/tests/auto/qtcprocess/tst_qtcprocess.cpp b/tests/auto/qtcprocess/tst_qtcprocess.cpp index e789203fbcc..b4df6694419 100644 --- a/tests/auto/qtcprocess/tst_qtcprocess.cpp +++ b/tests/auto/qtcprocess/tst_qtcprocess.cpp @@ -34,8 +34,14 @@ using namespace Utils; class MacroMapExpander : public AbstractMacroExpander { public: - virtual bool resolveMacro(const QString &name, QString *ret) + virtual bool resolveMacro(const QString &name, QString *ret, QSet &seen) { + // loop prevention + const int count = seen.count(); + seen.insert(this); + if (seen.count() == count) + return false; + QHash::const_iterator it = m_map.constFind(name); if (it != m_map.constEnd()) { *ret = it.value(); diff --git a/tests/auto/utils/stringutils/tst_stringutils.cpp b/tests/auto/utils/stringutils/tst_stringutils.cpp index ae137b13e12..d9d47e18451 100644 --- a/tests/auto/utils/stringutils/tst_stringutils.cpp +++ b/tests/auto/utils/stringutils/tst_stringutils.cpp @@ -32,8 +32,14 @@ class TestMacroExpander : public Utils::AbstractMacroExpander { public: - virtual bool resolveMacro(const QString &name, QString *ret) + virtual bool resolveMacro(const QString &name, QString *ret, QSet &seen) { + // loop prevention + const int count = seen.count(); + seen.insert(this); + if (seen.count() == count) + return false; + if (name == QLatin1String("foo")) { *ret = QLatin1String("a"); return true;