diff options
Diffstat (limited to 'src/plugins')
167 files changed, 4520 insertions, 2062 deletions
diff --git a/src/plugins/appmanager/project/appmanagerproject.cpp b/src/plugins/appmanager/project/appmanagerproject.cpp index d0ea2824d4..d67ad4731f 100644 --- a/src/plugins/appmanager/project/appmanagerproject.cpp +++ b/src/plugins/appmanager/project/appmanagerproject.cpp @@ -156,7 +156,7 @@ void AppManagerProject::addNodes(const QSet<QString> &nodes) path = QDir(projectDirectory().toString()).relativeFilePath(node).split(QDir::separator()); path.pop_back(); FolderNode *folder = findFolderFor(path); - auto fileNode = new FileNode(FileName::fromString(node), SourceType, false); + auto fileNode = new FileNode(FileName::fromString(node), FileType::Source, false); folder->addFileNodes({fileNode}); } } @@ -205,7 +205,7 @@ FolderNode *AppManagerProject::findFolderFor(const QStringList &path) // Folder not found. Add it QString newFolderPath = QDir::cleanPath(currentPath + QDir::separator() + part); auto newFolder = new FolderNode(FileName::fromString(newFolderPath), - FolderNodeType, + NodeType::Folder, part); folder->addFolderNodes({newFolder}); folder = newFolder; diff --git a/src/plugins/appmanager/project/appmanagerprojectnode.cpp b/src/plugins/appmanager/project/appmanagerprojectnode.cpp index 8c4bafe074..8955188b75 100644 --- a/src/plugins/appmanager/project/appmanagerprojectnode.cpp +++ b/src/plugins/appmanager/project/appmanagerprojectnode.cpp @@ -47,10 +47,10 @@ QList<ProjectAction> AppManagerProjectNode::supportedActions(Node *node) const ProjectAction::RemoveFile }; switch (node->nodeType()) { - case FileNodeType: + case NodeType::File: return fileActions; - case FolderNodeType: - case ProjectNodeType: + case NodeType::Folder: + case NodeType::Project: return folderActions; default: return ProjectNode::supportedActions(node); diff --git a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp index d07d355234..c37d144438 100644 --- a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp +++ b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp @@ -222,8 +222,9 @@ void AutotoolsProject::makefileParsingFinished() QList<FileNode *> fileNodes = Utils::transform(files, [dir](const QString &f) { const Utils::FileName path = Utils::FileName::fromString(dir.absoluteFilePath(f)); return new FileNode(path, - (f == QLatin1String("Makefile.am") || f == QLatin1String("configure.ac")) - ? ProjectFileType : ResourceType, false); + (f == QLatin1String("Makefile.am") || + f == QLatin1String("configure.ac")) ? FileType::Project : FileType::Resource, + false); }); rootProjectNode()->buildTree(fileNodes); diff --git a/src/plugins/bazaar/bazaarclient.cpp b/src/plugins/bazaar/bazaarclient.cpp index 991f994741..b2bdac3288 100644 --- a/src/plugins/bazaar/bazaarclient.cpp +++ b/src/plugins/bazaar/bazaarclient.cpp @@ -30,7 +30,9 @@ #include <vcsbase/vcsbaseplugin.h> #include <vcsbase/vcsoutputwindow.h> -#include <vcsbase/vcsbaseeditorparameterwidget.h> +#include <vcsbase/vcsbaseeditorconfig.h> + +#include <utils/hostosinfo.h> #include <QDir> #include <QFileInfo> @@ -44,12 +46,12 @@ namespace Bazaar { namespace Internal { // Parameter widget controlling whitespace diff mode, associated with a parameter -class BazaarDiffParameterWidget : public VcsBaseEditorParameterWidget +class BazaarDiffConfig : public VcsBaseEditorConfig { Q_OBJECT public: - BazaarDiffParameterWidget(VcsBaseClientSettings &settings, QWidget *parent = 0) : - VcsBaseEditorParameterWidget(parent) + BazaarDiffConfig(VcsBaseClientSettings &settings, QToolBar *toolBar) : + VcsBaseEditorConfig(toolBar) { mapSetting(addToggleButton(QLatin1String("-w"), tr("Ignore Whitespace")), settings.boolPointer(BazaarSettings::diffIgnoreWhiteSpaceKey)); @@ -61,7 +63,7 @@ public: { QStringList args; // Bazaar wants "--diff-options=-w -B.." - const QStringList formatArguments = VcsBaseEditorParameterWidget::arguments(); + const QStringList formatArguments = VcsBaseEditorConfig::arguments(); if (!formatArguments.isEmpty()) { const QString a = QLatin1String("--diff-options=") + formatArguments.join(QString(QLatin1Char(' '))); @@ -71,12 +73,12 @@ public: } }; -class BazaarLogParameterWidget : public VcsBaseEditorParameterWidget +class BazaarLogConfig : public VcsBaseEditorConfig { Q_OBJECT public: - BazaarLogParameterWidget(VcsBaseClientSettings &settings, QWidget *parent = 0) : - VcsBaseEditorParameterWidget(parent) + BazaarLogConfig(VcsBaseClientSettings &settings, QToolBar *toolBar) : + VcsBaseEditorConfig(toolBar) { mapSetting(addToggleButton(QLatin1String("--verbose"), tr("Verbose"), tr("Show files changed in each revision.")), @@ -100,8 +102,12 @@ public: BazaarClient::BazaarClient() : VcsBaseClient(new BazaarSettings) { - setDiffParameterWidgetCreator([this] { return new BazaarDiffParameterWidget(settings()); }); - setLogParameterWidgetCreator([this] { return new BazaarLogParameterWidget(settings()); }); + setDiffConfigCreator([this](QToolBar *toolBar) { + return new BazaarDiffConfig(settings(), toolBar); + }); + setLogConfigCreator([this](QToolBar *toolBar) { + return new BazaarLogConfig(settings(), toolBar); + }); } bool BazaarClient::synchronousSetUserId() @@ -171,6 +177,12 @@ VcsBaseEditorWidget *BazaarClient::annotate( QStringList(extraOptions) << QLatin1String("--long")); } +bool BazaarClient::isVcsDirectory(const FileName &fileName) const +{ + return fileName.toFileInfo().isDir() + && !fileName.fileName().compare(Constants::BAZAARREPO, HostOsInfo::fileNameCaseSensitivity()); +} + QString BazaarClient::findTopLevelForFile(const QFileInfo &file) const { const QString repositoryCheckFile = diff --git a/src/plugins/bazaar/bazaarclient.h b/src/plugins/bazaar/bazaarclient.h index c4293366a2..96f6a97a80 100644 --- a/src/plugins/bazaar/bazaarclient.h +++ b/src/plugins/bazaar/bazaarclient.h @@ -52,6 +52,7 @@ public: VcsBase::VcsBaseEditorWidget *annotate( const QString &workingDir, const QString &file, const QString &revision = QString(), int lineNumber = -1, const QStringList &extraOptions = QStringList()); + bool isVcsDirectory(const Utils::FileName &fileName) const; QString findTopLevelForFile(const QFileInfo &file) const; bool managesFile(const QString &workingDirectory, const QString &fileName) const; diff --git a/src/plugins/bazaar/bazaarcontrol.cpp b/src/plugins/bazaar/bazaarcontrol.cpp index 35269f622c..3db806cf0c 100644 --- a/src/plugins/bazaar/bazaarcontrol.cpp +++ b/src/plugins/bazaar/bazaarcontrol.cpp @@ -52,6 +52,11 @@ Core::Id BazaarControl::id() const return Core::Id(VcsBase::Constants::VCS_ID_BAZAAR); } +bool BazaarControl::isVcsFileOrDirectory(const Utils::FileName &fileName) const +{ + return m_bazaarClient->isVcsDirectory(fileName); +} + bool BazaarControl::managesDirectory(const QString &directory, QString *topLevel) const { QFileInfo dir(directory); diff --git a/src/plugins/bazaar/bazaarcontrol.h b/src/plugins/bazaar/bazaarcontrol.h index 3b0be48097..020e15d37b 100644 --- a/src/plugins/bazaar/bazaarcontrol.h +++ b/src/plugins/bazaar/bazaarcontrol.h @@ -45,24 +45,26 @@ class BazaarControl: public Core::IVersionControl public: explicit BazaarControl(BazaarClient *bazaarClient); - QString displayName() const override; - Core::Id id() const override; + QString displayName() const final; + Core::Id id() const final; - bool managesDirectory(const QString &filename, QString *topLevel = 0) const override; - bool managesFile(const QString &workingDirectory, const QString &fileName) const override; - bool isConfigured() const override; - bool supportsOperation(Operation operation) const override; - bool vcsOpen(const QString &fileName) override; - bool vcsAdd(const QString &filename) override; - bool vcsDelete(const QString &filename) override; - bool vcsMove(const QString &from, const QString &to) override; - bool vcsCreateRepository(const QString &directory) override; - bool vcsAnnotate(const QString &file, int line) override; + bool isVcsFileOrDirectory(const Utils::FileName &fileName) const final; + + bool managesDirectory(const QString &filename, QString *topLevel = 0) const final; + bool managesFile(const QString &workingDirectory, const QString &fileName) const final; + bool isConfigured() const final; + bool supportsOperation(Operation operation) const final; + bool vcsOpen(const QString &fileName) final; + bool vcsAdd(const QString &filename) final; + bool vcsDelete(const QString &filename) final; + bool vcsMove(const QString &from, const QString &to) final; + bool vcsCreateRepository(const QString &directory) final; + bool vcsAnnotate(const QString &file, int line) final; Core::ShellCommand *createInitialCheckoutCommand(const QString &url, const Utils::FileName &baseDirectory, const QString &localName, - const QStringList &extraArgs) override; + const QStringList &extraArgs) final; // To be connected to the VCSTask's success signal to emit the repository/ // files changed signals according to the variant's type: diff --git a/src/plugins/clangcodemodel/clangcodemodel.pro b/src/plugins/clangcodemodel/clangcodemodel.pro index 0d9b5ee96b..3ab1502d46 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.pro +++ b/src/plugins/clangcodemodel/clangcodemodel.pro @@ -1,10 +1,7 @@ include(../../qtcreatorplugin.pri) include(../../shared/clang/clang_installation.pri) -# The following defines are used to determine the clang include path for intrinsics. -DEFINES += CLANG_VERSION=\\\"$${LLVM_VERSION}\\\" -CLANG_RESOURCE_DIR=$$clean_path($${LLVM_LIBDIR}/clang/$${LLVM_VERSION}/include) -DEFINES += "\"CLANG_RESOURCE_DIR=\\\"$${CLANG_RESOURCE_DIR}\\\"\"" +include(../../shared/clang/clang_defines.pri) SOURCES += \ clangactivationsequencecontextprocessor.cpp \ diff --git a/src/plugins/clangrefactoring/clangrefactoring.pro b/src/plugins/clangrefactoring/clangrefactoring.pro index 3716c86b93..b811f181a7 100644 --- a/src/plugins/clangrefactoring/clangrefactoring.pro +++ b/src/plugins/clangrefactoring/clangrefactoring.pro @@ -2,8 +2,7 @@ include(../../qtcreatorplugin.pri) include(clangrefactoring-source.pri) include(../../shared/clang/clang_installation.pri) -DEFINES += CLANG_VERSION=\\\"$${LLVM_VERSION}\\\" -DEFINES += "\"CLANG_RESOURCE_DIR=\\\"$${LLVM_LIBDIR}/clang/$${LLVM_VERSION}/include\\\"\"" +include(../../shared/clang/clang_defines.pri) HEADERS += \ $$PWD/clangrefactoringplugin.h diff --git a/src/plugins/classview/classviewparser.cpp b/src/plugins/classview/classviewparser.cpp index 4ab5d83750..7e4e4de283 100644 --- a/src/plugins/classview/classviewparser.cpp +++ b/src/plugins/classview/classviewparser.cpp @@ -718,7 +718,7 @@ QStringList Parser::projectNodeFileList(const FolderNode *node) const return list; QList<FileNode *> fileNodes = node->fileNodes(); - QList<FolderNode *> subFolderNodes = node->subFolderNodes(); + QList<FolderNode *> subFolderNodes = node->folderNodes(); foreach (const FileNode *file, fileNodes) { if (file->isGenerated()) @@ -728,7 +728,7 @@ QStringList Parser::projectNodeFileList(const FolderNode *node) const } foreach (const FolderNode *folder, subFolderNodes) { - if (folder->nodeType() != FolderNodeType && folder->nodeType() != VirtualFolderNodeType) + if (folder->nodeType() != NodeType::Folder && folder->nodeType() != NodeType::VirtualFolder) continue; list << projectNodeFileList(folder); } @@ -768,7 +768,7 @@ QStringList Parser::addProjectNode(const ParserTreeItem::Ptr &item, const Projec } // subnodes - QList<ProjectNode *> projectNodes = node->subProjectNodes(); + QList<ProjectNode *> projectNodes = node->projectNodes(); foreach (const ProjectNode *project, projectNodes) { ParserTreeItem::Ptr itemPrj(new ParserTreeItem()); @@ -804,7 +804,7 @@ QStringList Parser::getAllFiles(const ProjectNode *node) d->cachedPrjFileLists[nodePath] = fileList; } // subnodes - QList<ProjectNode *> projectNodes = node->subProjectNodes(); + QList<ProjectNode *> projectNodes = node->projectNodes(); foreach (const ProjectNode *project, projectNodes) fileList += getAllFiles(project); diff --git a/src/plugins/clearcase/clearcasecontrol.cpp b/src/plugins/clearcase/clearcasecontrol.cpp index 5c358082df..ef18e6d62d 100644 --- a/src/plugins/clearcase/clearcasecontrol.cpp +++ b/src/plugins/clearcase/clearcasecontrol.cpp @@ -49,6 +49,12 @@ Core::Id ClearCaseControl::id() const return Constants::VCS_ID_CLEARCASE; } +bool ClearCaseControl::isVcsFileOrDirectory(const Utils::FileName &fileName) const +{ + Q_UNUSED(fileName); + return false; // ClearCase has no files/directories littering the sources +} + bool ClearCaseControl::isConfigured() const { #ifdef WITH_TESTS diff --git a/src/plugins/clearcase/clearcasecontrol.h b/src/plugins/clearcase/clearcasecontrol.h index fbcee6b88c..07648d4836 100644 --- a/src/plugins/clearcase/clearcasecontrol.h +++ b/src/plugins/clearcase/clearcasecontrol.h @@ -39,28 +39,30 @@ class ClearCaseControl : public Core::IVersionControl Q_OBJECT public: explicit ClearCaseControl(ClearCasePlugin *plugin); - QString displayName() const override; - Core::Id id() const override; + QString displayName() const final; + Core::Id id() const final; - bool managesDirectory(const QString &directory, QString *topLevel = 0) const override; - bool managesFile(const QString &workingDirectory, const QString &fileName) const override; + bool isVcsFileOrDirectory(const Utils::FileName &fileName) const final; - bool isConfigured() const override; + bool managesDirectory(const QString &directory, QString *topLevel = 0) const final; + bool managesFile(const QString &workingDirectory, const QString &fileName) const final; - bool supportsOperation(Operation operation) const override; - OpenSupportMode openSupportMode(const QString &fileName) const override; - bool vcsOpen(const QString &fileName) override; - SettingsFlags settingsFlags() const override; - bool vcsAdd(const QString &fileName) override; - bool vcsDelete(const QString &filename) override; - bool vcsMove(const QString &from, const QString &to) override; - bool vcsCreateRepository(const QString &directory) override; + bool isConfigured() const final; - bool vcsAnnotate(const QString &file, int line) override; + bool supportsOperation(Operation operation) const final; + OpenSupportMode openSupportMode(const QString &fileName) const final; + bool vcsOpen(const QString &fileName) final; + SettingsFlags settingsFlags() const final; + bool vcsAdd(const QString &fileName) final; + bool vcsDelete(const QString &filename) final; + bool vcsMove(const QString &from, const QString &to) final; + bool vcsCreateRepository(const QString &directory) final; - QString vcsOpenText() const override; - QString vcsMakeWritableText() const override; - QString vcsTopic(const QString &directory) override; + bool vcsAnnotate(const QString &file, int line) final; + + QString vcsOpenText() const final; + QString vcsMakeWritableText() const final; + QString vcsTopic(const QString &directory) final; void emitRepositoryChanged(const QString &); void emitFilesChanged(const QStringList &); diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp index 2351e6bbb2..ee1f1779bb 100644 --- a/src/plugins/clearcase/clearcaseplugin.cpp +++ b/src/plugins/clearcase/clearcaseplugin.cpp @@ -64,7 +64,6 @@ #include <vcsbase/basevcseditorfactory.h> #include <vcsbase/basevcssubmiteditorfactory.h> #include <vcsbase/vcsbaseeditor.h> -#include <vcsbase/vcsbaseeditorparameterwidget.h> #include <vcsbase/vcsoutputwindow.h> #include <vcsbase/vcsbasesubmiteditor.h> diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.cpp b/src/plugins/cmakeprojectmanager/builddirmanager.cpp index 3bc0ab684e..5c5314b31d 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.cpp +++ b/src/plugins/cmakeprojectmanager/builddirmanager.cpp @@ -27,6 +27,7 @@ #include "cmakebuildconfiguration.h" #include "cmakekitinformation.h" #include "cmakeparser.h" +#include "cmakeprojectconstants.h" #include "cmakeprojectmanager.h" #include "cmakeprojectnodes.h" #include "cmaketool.h" @@ -36,6 +37,7 @@ #include <coreplugin/messagemanager.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/progressmanager/progressmanager.h> +#include <cpptools/cpptoolsconstants.h> #include <cpptools/projectpartbuilder.h> #include <projectexplorer/headerpath.h> #include <projectexplorer/kit.h> @@ -49,8 +51,10 @@ #include <utils/algorithm.h> #include <utils/fileutils.h> +#include <utils/mimetypes/mimedatabase.h> #include <utils/qtcassert.h> #include <utils/qtcprocess.h> +#include <utils/runextensions.h> #include <utils/synchronousprocess.h> #include <QDateTime> @@ -63,43 +67,9 @@ using namespace ProjectExplorer; -// -------------------------------------------------------------------- -// Helper: -// -------------------------------------------------------------------- - namespace CMakeProjectManager { namespace Internal { -static QStringList toArguments(const CMakeConfig &config, const Kit *k) { - return Utils::transform(config, [k](const CMakeConfigItem &i) -> QString { - QString a = QString::fromLatin1("-D"); - a.append(QString::fromUtf8(i.key)); - switch (i.type) { - case CMakeConfigItem::FILEPATH: - a.append(QLatin1String(":FILEPATH=")); - break; - case CMakeConfigItem::PATH: - a.append(QLatin1String(":PATH=")); - break; - case CMakeConfigItem::BOOL: - a.append(QLatin1String(":BOOL=")); - break; - case CMakeConfigItem::STRING: - a.append(QLatin1String(":STRING=")); - break; - case CMakeConfigItem::INTERNAL: - a.append(QLatin1String(":INTERNAL=")); - break; - case CMakeConfigItem::STATIC: - a.append(QLatin1String(":STATIC=")); - break; - } - a.append(i.expandedValue(k)); - - return a; - }); -} - // -------------------------------------------------------------------- // BuildDirManager: // -------------------------------------------------------------------- @@ -108,65 +78,158 @@ BuildDirManager::BuildDirManager(CMakeBuildConfiguration *bc) : m_buildConfiguration(bc) { QTC_ASSERT(bc, return); - m_projectName = sourceDirectory().fileName(); m_reparseTimer.setSingleShot(true); connect(&m_reparseTimer, &QTimer::timeout, this, &BuildDirManager::parse); - connect(Core::EditorManager::instance(), &Core::EditorManager::aboutToSave, - this, &BuildDirManager::handleDocumentSaves); + + connect(&m_futureWatcher, &QFutureWatcher<QList<FileNode *>>::finished, + this, &BuildDirManager::emitDataAvailable); } -BuildDirManager::~BuildDirManager() +BuildDirManager::~BuildDirManager() = default; + +const Utils::FileName BuildDirManager::workDirectory() const { - stopProcess(); - resetData(); - qDeleteAll(m_watchedFiles); - delete m_tempDir; + const Utils::FileName bdir = m_buildConfiguration->buildDirectory(); + if (bdir.exists()) + return bdir; + if (!m_tempDir) { + const QString path = QDir::tempPath() + QLatin1String("/qtc-cmake-XXXXXX"); + m_tempDir.reset(new QTemporaryDir(path)); + if (!m_tempDir->isValid()) + emit errorOccured(tr("Failed to create temporary directory using template \"%1\".").arg(path)); + } + return Utils::FileName::fromString(m_tempDir->path()); } -const Kit *BuildDirManager::kit() const +void BuildDirManager::emitDataAvailable() { - return m_buildConfiguration->target()->kit(); + if (!isParsing() && m_futureWatcher.isFinished()) + emit dataAvailable(); } -const Utils::FileName BuildDirManager::buildDirectory() const +void BuildDirManager::updateReaderType(std::function<void()> todo) { - return m_buildConfiguration->buildDirectory(); + BuildDirReader::Parameters p(m_buildConfiguration); + p.buildDirectory = workDirectory(); + + if (!m_reader || !m_reader->isCompatible(p)) { + m_reader.reset(BuildDirReader::createReader(p)); + connect(m_reader.get(), &BuildDirReader::configurationStarted, + this, &BuildDirManager::configurationStarted); + connect(m_reader.get(), &BuildDirReader::dataAvailable, + this, &BuildDirManager::emitDataAvailable); + connect(m_reader.get(), &BuildDirReader::errorOccured, + this, &BuildDirManager::errorOccured); + connect(m_reader.get(), &BuildDirReader::dirty, this, &BuildDirManager::becameDirty); + } + m_reader->setParameters(p); + + if (m_reader->isReady()) + todo(); + else + connect(m_reader.get(), &BuildDirReader::isReadyNow, this, todo); } -const Utils::FileName BuildDirManager::workDirectory() const +void BuildDirManager::updateReaderData() { - const Utils::FileName bdir = buildDirectory(); - if (bdir.exists()) - return bdir; - if (m_tempDir) - return Utils::FileName::fromString(m_tempDir->path()); - return bdir; + BuildDirReader::Parameters p(m_buildConfiguration); + p.buildDirectory = workDirectory(); + + m_reader->setParameters(p); } -const Utils::FileName BuildDirManager::sourceDirectory() const +void BuildDirManager::parseOnceReaderReady(bool force) { - return m_buildConfiguration->target()->project()->projectDirectory(); + m_futureInterface.reset(new QFutureInterface<QList<ProjectExplorer::FileNode *>>()); + m_futureWatcher.setFuture(m_futureInterface->future()); + + Core::ProgressManager::addTask(m_futureInterface->future(), "Scan CMake project tree", "CMake.Scan.Tree"); + Utils::runAsync([this]() { BuildDirManager::asyncScanForFiles(*m_futureInterface); }); + + checkConfiguration(); + m_reader->stop(); + m_reader->parse(force); } -const CMakeConfig BuildDirManager::intendedConfiguration() const +void BuildDirManager::maybeForceReparseOnceReaderReady() { - return m_buildConfiguration->cmakeConfiguration(); + checkConfiguration(); + + const QByteArray GENERATOR_KEY = "CMAKE_GENERATOR"; + const QByteArray EXTRA_GENERATOR_KEY = "CMAKE_EXTRA_GENERATOR"; + const QByteArray CMAKE_COMMAND_KEY = "CMAKE_COMMAND"; + + const QByteArrayList criticalKeys + = QByteArrayList() << GENERATOR_KEY << CMAKE_COMMAND_KEY; + + const CMakeConfig currentConfig = parsedConfiguration(); + + Kit *k = m_buildConfiguration->target()->kit(); + const CMakeTool *tool = CMakeKitInformation::cmakeTool(k); + QTC_ASSERT(tool, return); // No cmake... we should not have ended up here in the first place + const QString extraKitGenerator = CMakeGeneratorKitInformation::extraGenerator(k); + const QString mainKitGenerator = CMakeGeneratorKitInformation::generator(k); + CMakeConfig targetConfig = m_buildConfiguration->cmakeConfiguration(); + targetConfig.append(CMakeConfigItem(GENERATOR_KEY, CMakeConfigItem::INTERNAL, + QByteArray(), mainKitGenerator.toUtf8())); + if (!extraKitGenerator.isEmpty()) + targetConfig.append(CMakeConfigItem(EXTRA_GENERATOR_KEY, CMakeConfigItem::INTERNAL, + QByteArray(), extraKitGenerator.toUtf8())); + targetConfig.append(CMakeConfigItem(CMAKE_COMMAND_KEY, CMakeConfigItem::INTERNAL, + QByteArray(), tool->cmakeExecutable().toUserOutput().toUtf8())); + Utils::sort(targetConfig, CMakeConfigItem::sortOperator()); + + bool mustReparse = false; + auto ccit = currentConfig.constBegin(); + auto kcit = targetConfig.constBegin(); + + while (ccit != currentConfig.constEnd() && kcit != targetConfig.constEnd()) { + if (ccit->key == kcit->key) { + if (ccit->value != kcit->value) { + if (criticalKeys.contains(kcit->key)) { + clearCache(); + return; + } + mustReparse = true; + } + ++ccit; + ++kcit; + } else { + if (ccit->key < kcit->key) { + ++ccit; + } else { + ++kcit; + mustReparse = true; + } + } + } + + // If we have keys that do not exist yet, then reparse. + // + // The critical keys *must* be set in cmake configuration, so those were already + // handled above. + if (mustReparse || kcit != targetConfig.constEnd()) + parseOnceReaderReady(true); } bool BuildDirManager::isParsing() const { - if (m_cmakeProcess) - return m_cmakeProcess->state() != QProcess::NotRunning; - return false; + return m_reader && m_reader->isParsing(); } -void BuildDirManager::cmakeFilesChanged() +void BuildDirManager::becameDirty() { if (isParsing()) return; + Target *t = m_buildConfiguration->target()->project()->activeTarget(); + BuildConfiguration *bc = t ? t->activeBuildConfiguration() : nullptr; + + if (bc != m_buildConfiguration) + return; + const CMakeTool *tool = CMakeKitInformation::cmakeTool(m_buildConfiguration->target()->kit()); if (!tool->isAutoRun()) return; @@ -174,28 +237,70 @@ void BuildDirManager::cmakeFilesChanged() m_reparseTimer.start(1000); } +void BuildDirManager::asyncScanForFiles(QFutureInterface<QList<FileNode *> > &fi) +{ + fi.reportStarted(); + Utils::MimeDatabase mdb; + + QList<FileNode *> nodes + = FileNode::scanForFiles(m_buildConfiguration->target()->project()->projectDirectory(), + [&mdb](const Utils::FileName &fn) -> FileNode * { + QTC_ASSERT(!fn.isEmpty(), return nullptr); + const Utils::MimeType mimeType = mdb.mimeTypeForFile(fn.toString()); + FileType type = FileType::Unknown; + if (mimeType.isValid()) { + const QString mt = mimeType.name(); + if (mt == CppTools::Constants::C_SOURCE_MIMETYPE + || mt == CppTools::Constants::CPP_SOURCE_MIMETYPE + || mt == CppTools::Constants::OBJECTIVE_C_SOURCE_MIMETYPE + || mt == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE + || mt == CppTools::Constants::QDOC_MIMETYPE + || mt == CppTools::Constants::MOC_MIMETYPE) + type = FileType::Source; + else if (mt == CppTools::Constants::C_HEADER_MIMETYPE + || mt == CppTools::Constants::CPP_HEADER_MIMETYPE) + type = FileType::Header; + else if (mt == ProjectExplorer::Constants::FORM_MIMETYPE) + type = FileType::Form; + else if (mt == ProjectExplorer::Constants::RESOURCE_MIMETYPE) + type = FileType::Resource; + else if (mt == ProjectExplorer::Constants::SCXML_MIMETYPE) + type = FileType::StateChart; + else if (mt == CMakeProjectManager::Constants::CMAKEPROJECTMIMETYPE + || mt == CMakeProjectManager::Constants::CMAKEMIMETYPE) + type = FileType::Project; + else if (mt == ProjectExplorer::Constants::QML_MIMETYPE) + type = FileType::QML; + } + return new FileNode(fn, type, false); + }, + &fi); + fi.setProgressValue(m_futureInterface->progressMaximum()); + fi.reportResult(nodes); + fi.reportFinished(); +} + void BuildDirManager::forceReparse() { if (m_buildConfiguration->target()->activeBuildConfiguration() != m_buildConfiguration) return; - stopProcess(); - - CMakeTool *tool = CMakeKitInformation::cmakeTool(kit()); + CMakeTool *tool = CMakeKitInformation::cmakeTool(m_buildConfiguration->target()->kit()); QTC_ASSERT(tool, return); - startCMake(tool, CMakeGeneratorKitInformation::generatorArguments(kit()), intendedConfiguration()); + m_reader.reset(); // Force reparse by forcing in a new reader + updateReaderType([this]() { parseOnceReaderReady(true); }); } void BuildDirManager::resetData() { - m_hasData = false; + if (m_reader) + m_reader->resetData(); m_cmakeCache.clear(); - m_projectName.clear(); - m_buildTargets.clear(); - qDeleteAll(m_files); - m_files.clear(); + m_futureInterface.reset(); + + m_reader.reset(nullptr); } bool BuildDirManager::updateCMakeStateBeforeBuild() @@ -208,115 +313,54 @@ bool BuildDirManager::persistCMakeState() if (!m_tempDir) return false; - QDir dir(buildDirectory().toString()); - dir.mkpath(buildDirectory().toString()); + const QString buildDir = m_buildConfiguration->buildDirectory().toString(); + QDir dir(buildDir); + dir.mkpath(buildDir); - delete m_tempDir; - m_tempDir = nullptr; + m_tempDir.reset(nullptr); - resetData(); QTimer::singleShot(0, this, &BuildDirManager::parse); // make sure signals only happen afterwards! return true; } void BuildDirManager::generateProjectTree(CMakeProjectNode *root) { - root->setDisplayName(m_projectName); - - // Delete no longer necessary file watcher: - const QSet<Utils::FileName> currentWatched - = Utils::transform(m_watchedFiles, [](CMakeFile *cmf) { return cmf->filePath(); }); - const QSet<Utils::FileName> toWatch = m_cmakeFiles; - QSet<Utils::FileName> toDelete = currentWatched; - toDelete.subtract(toWatch); - m_watchedFiles = Utils::filtered(m_watchedFiles, [&toDelete](Internal::CMakeFile *cmf) { - if (toDelete.contains(cmf->filePath())) { - delete cmf; - return false; - } - return true; - }); - - // Add new file watchers: - QSet<Utils::FileName> toAdd = toWatch; - toAdd.subtract(currentWatched); - foreach (const Utils::FileName &fn, toAdd) { - CMakeFile *cm = new CMakeFile(this, fn); - Core::DocumentManager::addDocument(cm); - m_watchedFiles.insert(cm); - } + QTC_ASSERT(m_reader, return); + QTC_ASSERT(m_futureInterface, return); + + const Utils::FileName projectFile = m_buildConfiguration->target()->project()->projectFilePath(); + QList<FileNode *> tmp = Utils::filtered(m_futureInterface->future().result(), + [projectFile](const FileNode *fn) -> bool { + const Utils::FileName &path = fn->filePath(); + return path != projectFile && !path.toString().startsWith(projectFile.toString() + ".user"); + }); + Utils::sort(tmp, ProjectExplorer::Node::sortByPath); + + m_futureInterface.reset(); // Make sure to flush the stale results + + const QList<FileNode *> allFiles = tmp; + m_reader->generateProjectTree(root, allFiles); + QSet<FileNode *> usedNodes; + foreach (FileNode *fn, root->recursiveFileNodes()) + usedNodes.insert(fn); - QList<FileNode *> fileNodes = m_files; - root->buildTree(fileNodes); - m_files.clear(); // Some of the FileNodes in files() were deleted! + QList<FileNode *> leftOvers = Utils::filtered(allFiles, [&usedNodes](FileNode *fn) { + return !usedNodes.contains(fn); + }); + qDeleteAll(leftOvers); } QSet<Core::Id> BuildDirManager::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder) { - QSet<Core::Id> languages; - ToolChain *tc = ToolChainKitInformation::toolChain(kit(), ToolChain::Language::Cxx); - const Utils::FileName sysroot = SysRootKitInformation::sysRoot(kit()); - - QHash<QString, QStringList> targetDataCache; - foreach (const CMakeBuildTarget &cbt, buildTargets()) { - if (cbt.targetType == UtilityType) - continue; - - // CMake shuffles the include paths that it reports via the CodeBlocks generator - // So remove the toolchain include paths, so that at least those end up in the correct - // place. - QStringList cxxflags = getCXXFlagsFor(cbt, targetDataCache); - QSet<QString> tcIncludes; - foreach (const HeaderPath &hp, tc->systemHeaderPaths(cxxflags, sysroot)) - tcIncludes.insert(hp.path()); - QStringList includePaths; - foreach (const QString &i, cbt.includeFiles) { - if (!tcIncludes.contains(i)) - includePaths.append(i); - } - includePaths += buildDirectory().toString(); - ppBuilder.setIncludePaths(includePaths); - ppBuilder.setCFlags(cxxflags); - ppBuilder.setCxxFlags(cxxflags); - ppBuilder.setDefines(cbt.defines); - ppBuilder.setDisplayName(cbt.title); - - const QSet<Core::Id> partLanguages - = QSet<Core::Id>::fromList(ppBuilder.createProjectPartsForFiles(cbt.files)); - - languages.unite(partLanguages); - } - return languages; + QTC_ASSERT(m_reader, return QSet<Core::Id>()); + return m_reader->updateCodeModel(ppBuilder); } void BuildDirManager::parse() { - checkConfiguration(); - - CMakeTool *tool = CMakeKitInformation::cmakeTool(kit()); - const QStringList generatorArgs = CMakeGeneratorKitInformation::generatorArguments(kit()); - - QTC_ASSERT(tool, return); - - const QString cbpFile = CMakeManager::findCbpFile(QDir(workDirectory().toString())); - const QFileInfo cbpFileFi = cbpFile.isEmpty() ? QFileInfo() : QFileInfo(cbpFile); - if (!cbpFileFi.exists()) { - // Initial create: - startCMake(tool, generatorArgs, intendedConfiguration()); - return; - } + TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); - const bool mustUpdate = m_cmakeFiles.isEmpty() - || Utils::anyOf(m_cmakeFiles, [&cbpFileFi](const Utils::FileName &f) { - return f.toFileInfo().lastModified() > cbpFileFi.lastModified(); - }); - if (mustUpdate) { - startCMake(tool, generatorArgs, CMakeConfig()); - } else { - extractData(); - m_hasData = true; - emit dataAvailable(); - } + updateReaderType([this]() { parseOnceReaderReady(false); }); } void BuildDirManager::clearCache() @@ -336,367 +380,34 @@ void BuildDirManager::clearCache() QList<CMakeBuildTarget> BuildDirManager::buildTargets() const { - return m_buildTargets; + if (!m_reader) + return QList<CMakeBuildTarget>(); + return m_reader->buildTargets(); } CMakeConfig BuildDirManager::parsedConfiguration() const { - if (m_cmakeCache.isEmpty()) { - Utils::FileName cacheFile = workDirectory(); - cacheFile.appendPath(QLatin1String("CMakeCache.txt")); - if (!cacheFile.exists()) - return m_cmakeCache; - QString errorMessage; - m_cmakeCache = parseConfiguration(cacheFile, &errorMessage); - if (!errorMessage.isEmpty()) - emit errorOccured(errorMessage); - const Utils::FileName sourceOfBuildDir - = Utils::FileName::fromUtf8(CMakeConfigItem::valueOf("CMAKE_HOME_DIRECTORY", m_cmakeCache)); - const Utils::FileName canonicalSourceOfBuildDir = Utils::FileUtils::canonicalPath(sourceOfBuildDir); - const Utils::FileName canonicalSourceDirectory = Utils::FileUtils::canonicalPath(sourceDirectory()); - if (canonicalSourceOfBuildDir != canonicalSourceDirectory) // Uses case-insensitive compare where appropriate - emit errorOccured(tr("The build directory is not for %1 but for %2") - .arg(canonicalSourceOfBuildDir.toUserOutput(), - canonicalSourceDirectory.toUserOutput())); - } + if (!m_reader) + return m_cmakeCache; + if (m_cmakeCache.isEmpty()) + m_cmakeCache = m_reader->parsedConfiguration(); return m_cmakeCache; } -void BuildDirManager::stopProcess() -{ - if (!m_cmakeProcess) - return; - - m_cmakeProcess->disconnect(); - - if (m_cmakeProcess->state() == QProcess::Running) { - m_cmakeProcess->terminate(); - if (!m_cmakeProcess->waitForFinished(500) && m_cmakeProcess->state() == QProcess::Running) - m_cmakeProcess->kill(); - } - - cleanUpProcess(); - - if (!m_future) - return; - m_future->reportCanceled(); - m_future->reportFinished(); - delete m_future; - m_future = nullptr; -} - -void BuildDirManager::cleanUpProcess() -{ - if (!m_cmakeProcess) - return; - - QTC_ASSERT(m_cmakeProcess->state() == QProcess::NotRunning, return); - - m_cmakeProcess->disconnect(); - - if (m_cmakeProcess->state() == QProcess::Running) { - m_cmakeProcess->terminate(); - if (!m_cmakeProcess->waitForFinished(500) && m_cmakeProcess->state() == QProcess::Running) - m_cmakeProcess->kill(); - } - m_cmakeProcess->waitForFinished(); - delete m_cmakeProcess; - m_cmakeProcess = nullptr; - - // Delete issue parser: - m_parser->flush(); - delete m_parser; - m_parser = nullptr; -} - -void BuildDirManager::extractData() -{ - const Utils::FileName topCMake - = Utils::FileName::fromString(sourceDirectory().toString() + QLatin1String("/CMakeLists.txt")); - - resetData(); - - m_projectName = sourceDirectory().fileName(); - m_files.append(new FileNode(topCMake, ProjectFileType, false)); - // Do not insert topCMake into m_cmakeFiles: The project already watches that! - - // Find cbp file - QString cbpFile = CMakeManager::findCbpFile(workDirectory().toString()); - if (cbpFile.isEmpty()) - return; - m_cmakeFiles.insert(Utils::FileName::fromString(cbpFile)); - - // Add CMakeCache.txt file: - Utils::FileName cacheFile = workDirectory(); - cacheFile.appendPath(QLatin1String("CMakeCache.txt")); - if (cacheFile.toFileInfo().exists()) - m_cmakeFiles.insert(cacheFile); - - // setFolderName - CMakeCbpParser cbpparser; - // Parsing - if (!cbpparser.parseCbpFile(kit(), cbpFile, sourceDirectory().toString())) - return; - - m_projectName = cbpparser.projectName(); - - m_files = cbpparser.fileList(); - if (cbpparser.hasCMakeFiles()) { - m_files.append(cbpparser.cmakeFileList()); - foreach (const FileNode *node, cbpparser.cmakeFileList()) - m_cmakeFiles.insert(node->filePath()); - } - - // Make sure the top cmakelists.txt file is always listed: - if (!Utils::contains(m_files, [topCMake](FileNode *fn) { return fn->filePath() == topCMake; })) { - m_files.append(new FileNode(topCMake, ProjectFileType, false)); - } - - m_buildTargets = cbpparser.buildTargets(); -} - -void BuildDirManager::startCMake(CMakeTool *tool, const QStringList &generatorArgs, - const CMakeConfig &config) -{ - QTC_ASSERT(tool && tool->isValid(), return); - - QTC_ASSERT(!m_cmakeProcess, return); - QTC_ASSERT(!m_parser, return); - QTC_ASSERT(!m_future, return); - - // Find a directory to set up into: - if (!buildDirectory().exists()) { - if (!m_tempDir) - m_tempDir = new QTemporaryDir(QDir::tempPath() + QLatin1String("/qtc-cmake-XXXXXX")); - QTC_ASSERT(m_tempDir->isValid(), return); - } - - // Make sure work directory exists: - QTC_ASSERT(workDirectory().exists(), return); - - m_parser = new CMakeParser; - QDir source = QDir(sourceDirectory().toString()); - connect(m_parser, &IOutputParser::addTask, m_parser, - [source](const Task &task) { - if (task.file.isEmpty() || task.file.toFileInfo().isAbsolute()) { - TaskHub::addTask(task); - } else { - Task t = task; - t.file = Utils::FileName::fromString(source.absoluteFilePath(task.file.toString())); - TaskHub::addTask(t); - } - }); - - // Always use the sourceDir: If we are triggered because the build directory is getting deleted - // then we are racing against CMakeCache.txt also getting deleted. - const QString srcDir = sourceDirectory().toString(); - - m_cmakeProcess = new Utils::QtcProcess(this); - m_cmakeProcess->setWorkingDirectory(workDirectory().toString()); - m_cmakeProcess->setEnvironment(m_buildConfiguration->environment()); - - connect(m_cmakeProcess, &QProcess::readyReadStandardOutput, - this, &BuildDirManager::processCMakeOutput); - connect(m_cmakeProcess, &QProcess::readyReadStandardError, - this, &BuildDirManager::processCMakeError); - connect(m_cmakeProcess, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), - this, &BuildDirManager::cmakeFinished); - - QString args; - Utils::QtcProcess::addArg(&args, srcDir); - Utils::QtcProcess::addArgs(&args, generatorArgs); - Utils::QtcProcess::addArgs(&args, toArguments(config, kit())); - - TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); - - Core::MessageManager::write(tr("Running \"%1 %2\" in %3.") - .arg(tool->cmakeExecutable().toUserOutput()) - .arg(args) - .arg(workDirectory().toUserOutput())); - - m_future = new QFutureInterface<void>(); - m_future->setProgressRange(0, 1); - Core::ProgressManager::addTask(m_future->future(), - tr("Configuring \"%1\"").arg(m_buildConfiguration->target()->project()->displayName()), - "CMake.Configure"); - - m_cmakeProcess->setCommand(tool->cmakeExecutable().toString(), args); - m_cmakeProcess->start(); - emit configurationStarted(); -} - -void BuildDirManager::cmakeFinished(int code, QProcess::ExitStatus status) -{ - QTC_ASSERT(m_cmakeProcess, return); - - // process rest of the output: - processCMakeOutput(); - processCMakeError(); - - cleanUpProcess(); - - extractData(); // try even if cmake failed... - - QString msg; - if (status != QProcess::NormalExit) - msg = tr("*** cmake process crashed."); - else if (code != 0) - msg = tr("*** cmake process exited with exit code %1.").arg(code); - - if (!msg.isEmpty()) { - Core::MessageManager::write(msg); - TaskHub::addTask(Task::Error, msg, ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); - m_future->reportCanceled(); - } else { - m_future->setProgressValue(1); - } - - m_future->reportFinished(); - delete m_future; - m_future = nullptr; - - m_hasData = true; - emit dataAvailable(); -} - -static QString lineSplit(const QString &rest, const QByteArray &array, std::function<void(const QString &)> f) -{ - QString tmp = rest + Utils::SynchronousProcess::normalizeNewlines(QString::fromLocal8Bit(array)); - int start = 0; - int end = tmp.indexOf(QLatin1Char('\n'), start); - while (end >= 0) { - f(tmp.mid(start, end - start)); - start = end + 1; - end = tmp.indexOf(QLatin1Char('\n'), start); - } - return tmp.mid(start); -} - -void BuildDirManager::processCMakeOutput() -{ - static QString rest; - rest = lineSplit(rest, m_cmakeProcess->readAllStandardOutput(), [this](const QString &s) { Core::MessageManager::write(s); }); -} - -void BuildDirManager::processCMakeError() -{ - static QString rest; - rest = lineSplit(rest, m_cmakeProcess->readAllStandardError(), [this](const QString &s) { - m_parser->stdError(s); - Core::MessageManager::write(s); - }); -} - -QStringList BuildDirManager::getCXXFlagsFor(const CMakeBuildTarget &buildTarget, - QHash<QString, QStringList> &cache) -{ - // check cache: - auto it = cache.constFind(buildTarget.title); - if (it != cache.constEnd()) - return *it; - - if (extractCXXFlagsFromMake(buildTarget, cache)) - return cache.value(buildTarget.title); - - if (extractCXXFlagsFromNinja(buildTarget, cache)) - return cache.value(buildTarget.title); - - cache.insert(buildTarget.title, QStringList()); - return QStringList(); -} - -bool BuildDirManager::extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget, - QHash<QString, QStringList> &cache) -{ - QString makeCommand = QDir::fromNativeSeparators(buildTarget.makeCommand); - int startIndex = makeCommand.indexOf('\"'); - int endIndex = makeCommand.indexOf('\"', startIndex + 1); - if (startIndex != -1 && endIndex != -1) { - startIndex += 1; - QString makefile = makeCommand.mid(startIndex, endIndex - startIndex); - int slashIndex = makefile.lastIndexOf('/'); - makefile.truncate(slashIndex); - makefile.append("/CMakeFiles/" + buildTarget.title + ".dir/flags.make"); - QFile file(makefile); - if (file.exists()) { - file.open(QIODevice::ReadOnly | QIODevice::Text); - QTextStream stream(&file); - while (!stream.atEnd()) { - QString line = stream.readLine().trimmed(); - if (line.startsWith("CXX_FLAGS =")) { - // Skip past = - cache.insert(buildTarget.title, - line.mid(11).trimmed().split(' ', QString::SkipEmptyParts)); - return true; - } - } - } - } - return false; -} - -bool BuildDirManager::extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget, - QHash<QString, QStringList> &cache) -{ - Q_UNUSED(buildTarget) - if (!cache.isEmpty()) // We fill the cache in one go! - return false; - - // Attempt to find build.ninja file and obtain FLAGS (CXX_FLAGS) from there if no suitable flags.make were - // found - // Get "all" target's working directory - QByteArray ninjaFile; - QString buildNinjaFile = QDir::fromNativeSeparators(buildTargets().at(0).workingDirectory); - buildNinjaFile += "/build.ninja"; - QFile buildNinja(buildNinjaFile); - if (buildNinja.exists()) { - buildNinja.open(QIODevice::ReadOnly | QIODevice::Text); - ninjaFile = buildNinja.readAll(); - buildNinja.close(); - } - - if (ninjaFile.isEmpty()) - return false; - - QTextStream stream(ninjaFile); - bool cxxFound = false; - const QString targetSignature = "# Object build statements for "; - QString currentTarget; - - while (!stream.atEnd()) { - // 1. Look for a block that refers to the current target - // 2. Look for a build rule which invokes CXX_COMPILER - // 3. Return the FLAGS definition - QString line = stream.readLine().trimmed(); - if (line.startsWith('#')) { - if (line.startsWith(targetSignature)) { - int pos = line.lastIndexOf(' '); - currentTarget = line.mid(pos + 1); - } - } else if (!currentTarget.isEmpty() && line.startsWith("build")) { - cxxFound = line.indexOf("CXX_COMPILER") != -1; - } else if (cxxFound && line.startsWith("FLAGS =")) { - // Skip past = - cache.insert(currentTarget, line.mid(7).trimmed().split(' ', QString::SkipEmptyParts)); - } - } - return !cache.isEmpty(); -} - void BuildDirManager::checkConfiguration() { if (m_tempDir) // always throw away changes in the tmpdir! return; Kit *k = m_buildConfiguration->target()->kit(); - const CMakeConfig cache = parsedConfiguration(); + const CMakeConfig cache = m_reader->parsedConfiguration(); if (cache.isEmpty()) return; // No cache file yet. CMakeConfig newConfig; QSet<QString> changedKeys; QSet<QString> removedKeys; - foreach (const CMakeConfigItem &iBc, intendedConfiguration()) { + foreach (const CMakeConfigItem &iBc, m_buildConfiguration->cmakeConfiguration()) { const CMakeConfigItem &iCache = Utils::findOrDefault(cache, [&iBc](const CMakeConfigItem &i) { return i.key == iBc.key; }); if (iCache.isNull()) { @@ -729,194 +440,26 @@ void BuildDirManager::checkConfiguration() QPointer<QMessageBox> box = new QMessageBox(Core::ICore::mainWindow()); box->setText(tr("CMake configuration has changed on disk.")); box->setInformativeText(tr("The CMakeCache.txt file has changed: %1").arg(table)); - box->setStandardButtons(QMessageBox::Discard | QMessageBox::Apply); - box->setDefaultButton(QMessageBox::Discard); + auto *defaultButton = box->addButton(tr("Overwrite Changes in CMake"), QMessageBox::RejectRole); + box->addButton(tr("Apply Changes to Project"), QMessageBox::AcceptRole); + box->setDefaultButton(defaultButton); int ret = box->exec(); - if (ret == QMessageBox::Apply) + if (ret == QMessageBox::Apply) { m_buildConfiguration->setCMakeConfiguration(newConfig); - } -} - -void BuildDirManager::handleDocumentSaves(Core::IDocument *document) -{ - CMakeTool *tool = CMakeKitInformation::cmakeTool(m_buildConfiguration->target()->kit()); - if (!m_cmakeFiles.contains(document->filePath()) || !tool || !tool->isAutoRun()) - return; - - m_reparseTimer.start(100); -} - -static QByteArray trimCMakeCacheLine(const QByteArray &in) { - int start = 0; - while (start < in.count() && (in.at(start) == ' ' || in.at(start) == '\t')) - ++start; - - return in.mid(start, in.count() - start - 1); -} - -static QByteArrayList splitCMakeCacheLine(const QByteArray &line) { - const int colonPos = line.indexOf(':'); - if (colonPos < 0) - return QByteArrayList(); - - const int equalPos = line.indexOf('=', colonPos + 1); - if (equalPos < colonPos) - return QByteArrayList(); - - return QByteArrayList() << line.mid(0, colonPos) - << line.mid(colonPos + 1, equalPos - colonPos - 1) - << line.mid(equalPos + 1); -} - -static CMakeConfigItem::Type fromByteArray(const QByteArray &type) { - if (type == "BOOL") - return CMakeConfigItem::BOOL; - if (type == "STRING") - return CMakeConfigItem::STRING; - if (type == "FILEPATH") - return CMakeConfigItem::FILEPATH; - if (type == "PATH") - return CMakeConfigItem::PATH; - QTC_CHECK(type == "INTERNAL" || type == "STATIC"); - - return CMakeConfigItem::INTERNAL; -} - -CMakeConfig BuildDirManager::parseConfiguration(const Utils::FileName &cacheFile, - QString *errorMessage) -{ - CMakeConfig result; - QFile cache(cacheFile.toString()); - if (!cache.open(QIODevice::ReadOnly | QIODevice::Text)) { - if (errorMessage) - *errorMessage = tr("Failed to open %1 for reading.").arg(cacheFile.toUserOutput()); - return CMakeConfig(); - } - - QSet<QByteArray> advancedSet; - QMap<QByteArray, QByteArray> valuesMap; - QByteArray documentation; - while (!cache.atEnd()) { - const QByteArray line = trimCMakeCacheLine(cache.readLine()); - - if (line.isEmpty() || line.startsWith('#')) - continue; - - if (line.startsWith("//")) { - documentation = line.mid(2); - continue; - } - - const QByteArrayList pieces = splitCMakeCacheLine(line); - if (pieces.isEmpty()) - continue; - - QTC_ASSERT(pieces.count() == 3, continue); - const QByteArray key = pieces.at(0); - const QByteArray type = pieces.at(1); - const QByteArray value = pieces.at(2); - - if (key.endsWith("-ADVANCED") && value == "1") { - advancedSet.insert(key.left(key.count() - 9 /* "-ADVANCED" */)); - } else if (key.endsWith("-STRINGS") && fromByteArray(type) == CMakeConfigItem::INTERNAL) { - valuesMap[key.left(key.count() - 8) /* "-STRINGS" */] = value; - } else { - CMakeConfigItem::Type t = fromByteArray(type); - result << CMakeConfigItem(key, t, documentation, value); + updateReaderData(); // Apply changes to reader } } - - // Set advanced flags: - for (int i = 0; i < result.count(); ++i) { - CMakeConfigItem &item = result[i]; - item.isAdvanced = advancedSet.contains(item.key); - - if (valuesMap.contains(item.key)) { - item.values = CMakeConfigItem::cmakeSplitValue(QString::fromUtf8(valuesMap[item.key])); - } else if (item.key == "CMAKE_BUILD_TYPE") { - // WA for known options - item.values << "" << "Debug" << "Release" << "MinSizeRel" << "RelWithDebInfo"; - } - } - - Utils::sort(result, CMakeConfigItem::sortOperator()); - - return result; -} - -void BuildDirManager::handleCmakeFileChange() -{ - Target *t = m_buildConfiguration->target()->project()->activeTarget(); - BuildConfiguration *bc = t ? t->activeBuildConfiguration() : nullptr; - - if (m_buildConfiguration->target() == t && m_buildConfiguration == bc) - cmakeFilesChanged(); } void BuildDirManager::maybeForceReparse() { - checkConfiguration(); - - const QByteArray GENERATOR_KEY = "CMAKE_GENERATOR"; - const QByteArray EXTRA_GENERATOR_KEY = "CMAKE_EXTRA_GENERATOR"; - const QByteArray CMAKE_COMMAND_KEY = "CMAKE_COMMAND"; - - const QByteArrayList criticalKeys - = QByteArrayList() << GENERATOR_KEY << CMAKE_COMMAND_KEY; - - if (!m_hasData) { + if (!m_reader || !m_reader->hasData()) { forceReparse(); return; } - const CMakeConfig currentConfig = parsedConfiguration(); - - const CMakeTool *tool = CMakeKitInformation::cmakeTool(kit()); - QTC_ASSERT(tool, return); // No cmake... we should not have ended up here in the first place - const QString extraKitGenerator = CMakeGeneratorKitInformation::extraGenerator(kit()); - const QString mainKitGenerator = CMakeGeneratorKitInformation::generator(kit()); - CMakeConfig targetConfig = m_buildConfiguration->cmakeConfiguration(); - targetConfig.append(CMakeConfigItem(GENERATOR_KEY, CMakeConfigItem::INTERNAL, - QByteArray(), mainKitGenerator.toUtf8())); - if (!extraKitGenerator.isEmpty()) - targetConfig.append(CMakeConfigItem(EXTRA_GENERATOR_KEY, CMakeConfigItem::INTERNAL, - QByteArray(), extraKitGenerator.toUtf8())); - targetConfig.append(CMakeConfigItem(CMAKE_COMMAND_KEY, CMakeConfigItem::INTERNAL, - QByteArray(), tool->cmakeExecutable().toUserOutput().toUtf8())); - Utils::sort(targetConfig, CMakeConfigItem::sortOperator()); - - bool mustReparse = false; - auto ccit = currentConfig.constBegin(); - auto kcit = targetConfig.constBegin(); - - while (ccit != currentConfig.constEnd() && kcit != targetConfig.constEnd()) { - if (ccit->key == kcit->key) { - if (ccit->value != kcit->value) { - if (criticalKeys.contains(kcit->key)) { - clearCache(); - return; - } - mustReparse = true; - } - ++ccit; - ++kcit; - } else { - if (ccit->key < kcit->key) { - ++ccit; - } else { - ++kcit; - mustReparse = true; - } - } - } - - // If we have keys that do not exist yet, then reparse. - // - // The critical keys *must* be set in cmake configuration, so those were already - // handled above. - if (mustReparse || kcit != targetConfig.constEnd()) - forceReparse(); + updateReaderType([this]() { maybeForceReparseOnceReaderReady(); }); } } // namespace Internal diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.h b/src/plugins/cmakeprojectmanager/builddirmanager.h index ec26d0c3bb..3d3a98411b 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.h +++ b/src/plugins/cmakeprojectmanager/builddirmanager.h @@ -25,26 +25,19 @@ #pragma once -#include "cmakecbpparser.h" +#include "builddirreader.h" #include "cmakeconfigitem.h" -#include "cmakefile.h" -#include <projectexplorer/task.h> - -#include <utils/environment.h> -#include <utils/qtcprocess.h> #include <utils/fileutils.h> -#include <QByteArray> -#include <QFutureInterface> +#include <QFutureWatcher> #include <QObject> -#include <QSet> +#include <QTemporaryDir> #include <QTimer> -QT_FORWARD_DECLARE_CLASS(QTemporaryDir); -QT_FORWARD_DECLARE_CLASS(QFileSystemWatcher); +#include <functional> +#include <memory> -namespace Core { class IDocument; } namespace CppTools { class ProjectPartBuilder; } namespace ProjectExplorer { @@ -68,11 +61,10 @@ class BuildDirManager : public QObject public: BuildDirManager(CMakeBuildConfiguration *bc); - ~BuildDirManager() override; + ~BuildDirManager() final; bool isParsing() const; - void parse(); void clearCache(); void forceReparse(); void maybeForceReparse(); // Only reparse if the configuration has changed... @@ -86,11 +78,6 @@ public: QList<CMakeBuildTarget> buildTargets() const; CMakeConfig parsedConfiguration() const; - void checkConfiguration(); - - void handleDocumentSaves(Core::IDocument *document); - void handleCmakeFileChange(); - signals: void configurationStarted() const; void dataAvailable() const; @@ -100,48 +87,33 @@ protected: static CMakeConfig parseConfiguration(const Utils::FileName &cacheFile, QString *errorMessage); - const ProjectExplorer::Kit *kit() const; - const Utils::FileName buildDirectory() const; const Utils::FileName workDirectory() const; - const Utils::FileName sourceDirectory() const; - const CMakeConfig intendedConfiguration() const; private: - void cmakeFilesChanged(); + void emitDataAvailable(); + void checkConfiguration(); - void stopProcess(); - void cleanUpProcess(); - void extractData(); + void updateReaderType(std::function<void()> todo); + void updateReaderData(); - void startCMake(CMakeTool *tool, const QStringList &generatorArgs, const CMakeConfig &config); + void parseOnceReaderReady(bool force); + void maybeForceReparseOnceReaderReady(); - void cmakeFinished(int code, QProcess::ExitStatus status); - void processCMakeOutput(); - void processCMakeError(); + void parse(); - QStringList getCXXFlagsFor(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache); - bool extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache); - bool extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache); + void becameDirty(); - bool m_hasData = false; + void asyncScanForFiles(QFutureInterface<QList<ProjectExplorer::FileNode*>> &fi); CMakeBuildConfiguration *m_buildConfiguration = nullptr; - Utils::QtcProcess *m_cmakeProcess = nullptr; - QTemporaryDir *m_tempDir = nullptr; + mutable std::unique_ptr<QTemporaryDir> m_tempDir = nullptr; mutable CMakeConfig m_cmakeCache; - QSet<Utils::FileName> m_cmakeFiles; - QString m_projectName; - QList<CMakeBuildTarget> m_buildTargets; - QList<ProjectExplorer::FileNode *> m_files; - - // For error reporting: - ProjectExplorer::IOutputParser *m_parser = nullptr; - QFutureInterface<void> *m_future = nullptr; - QTimer m_reparseTimer; - QSet<Internal::CMakeFile *> m_watchedFiles; + std::unique_ptr<BuildDirReader> m_reader; + std::unique_ptr<QFutureInterface<QList<ProjectExplorer::FileNode*>>> m_futureInterface; + QFutureWatcher<QList<ProjectExplorer::FileNode*>> m_futureWatcher; }; } // namespace Internal diff --git a/src/plugins/cmakeprojectmanager/builddirreader.cpp b/src/plugins/cmakeprojectmanager/builddirreader.cpp new file mode 100644 index 0000000000..741ddc2da4 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/builddirreader.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "builddirreader.h" + +#include "cmakebuildconfiguration.h" +#include "cmakekitinformation.h" +#include "servermodereader.h" +#include "tealeafreader.h" + +#include <projectexplorer/kitinformation.h> +#include <projectexplorer/target.h> + +using namespace ProjectExplorer; + +namespace CMakeProjectManager { +namespace Internal { + +// -------------------------------------------------------------------- +// BuildDirReader: +// -------------------------------------------------------------------- + +BuildDirReader::Parameters::Parameters() = default; + +BuildDirReader::Parameters::Parameters(const CMakeBuildConfiguration *bc) +{ + const ProjectExplorer::Kit *k = bc->target()->kit(); + + projectName = bc->target()->project()->displayName(); + + sourceDirectory = bc->target()->project()->projectDirectory(); + buildDirectory = bc->buildDirectory(); + + environment = bc->environment(); + + CMakeTool *cmake = CMakeKitInformation::cmakeTool(k); + cmakeVersion = cmake->version(); + cmakeHasServerMode = cmake->hasServerMode(); + cmakeExecutable = cmake->cmakeExecutable(); + + pathMapper = cmake->pathMapper(); + isAutorun = cmake->isAutoRun(); + + auto tc = ProjectExplorer::ToolChainKitInformation::toolChain(k, ProjectExplorer::ToolChain::Language::Cxx); + if (tc) + toolChainId = tc->id(); + sysRoot = ProjectExplorer::SysRootKitInformation::sysRoot(k); + + expander = k->macroExpander(); + + configuration = bc->cmakeConfiguration(); + + generator = CMakeGeneratorKitInformation::generator(k); + extraGenerator = CMakeGeneratorKitInformation::extraGenerator(k); + platform = CMakeGeneratorKitInformation::platform(k); + toolset = CMakeGeneratorKitInformation::toolset(k); + generatorArguments = CMakeGeneratorKitInformation::generatorArguments(k); +} + +BuildDirReader::Parameters::Parameters(const BuildDirReader::Parameters &other) = default; + + +BuildDirReader *BuildDirReader::createReader(const BuildDirReader::Parameters &p) +{ + if (p.cmakeHasServerMode) + return new ServerModeReader; + return new TeaLeafReader; +} + +void BuildDirReader::setParameters(const BuildDirReader::Parameters &p) +{ + m_parameters = p; +} + +} // namespace Internal +} // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/builddirreader.h b/src/plugins/cmakeprojectmanager/builddirreader.h new file mode 100644 index 0000000000..db0a5bfc46 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/builddirreader.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "cmakeconfigitem.h" +#include "cmakeproject.h" +#include "cmaketool.h" + +#include <utils/environment.h> +#include <utils/fileutils.h> +#include <utils/macroexpander.h> + +#include <QFutureInterface> +#include <QObject> + +namespace CppTools { class ProjectPartBuilder; } + +namespace ProjectExplorer { +class IOutputParser; +class Kit; +} // ProjectExplorer + +namespace Utils { class QtcProcess; } + +namespace CMakeProjectManager { +namespace Internal { + +class CMakeBuildConfiguration; + +class BuildDirReader : public QObject +{ + Q_OBJECT + +public: + struct Parameters { + Parameters(); + Parameters(const CMakeBuildConfiguration *bc); + Parameters(const Parameters &other); + + QString projectName; + + Utils::FileName sourceDirectory; + Utils::FileName buildDirectory; + Utils::Environment environment; + Utils::FileName cmakeExecutable; + CMakeTool::Version cmakeVersion; + bool cmakeHasServerMode; + CMakeTool::PathMapper pathMapper; + + QByteArray toolChainId; + + Utils::FileName sysRoot; + + Utils::MacroExpander *expander = nullptr; + + CMakeConfig configuration; + + QString generator; + QString extraGenerator; + QString platform; + QString toolset; + QStringList generatorArguments; + bool isAutorun = false; + }; + + static BuildDirReader *createReader(const BuildDirReader::Parameters &p); + virtual void setParameters(const Parameters &p); + + virtual bool isCompatible(const Parameters &p) = 0; + virtual void resetData() = 0; + virtual void parse(bool force) = 0; + virtual void stop() = 0; + + virtual bool isReady() const { return true; } + virtual bool isParsing() const = 0; + virtual bool hasData() const = 0; + + virtual CMakeConfig parsedConfiguration() const = 0; + virtual QList<CMakeBuildTarget> buildTargets() const = 0; + virtual void generateProjectTree(CMakeProjectNode *root, + const QList<ProjectExplorer::FileNode *> &allFiles) = 0; + virtual QSet<Core::Id> updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder) = 0; + +signals: + void isReadyNow() const; + void configurationStarted() const; + void dataAvailable() const; + void dirty() const; + void errorOccured(const QString &message) const; + +protected: + Parameters m_parameters; +}; + +} // namespace Internal +} // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index c5d6bdafc4..34dac4e1d6 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -139,17 +139,17 @@ void CMakeBuildConfiguration::ctor() target()->kit(), displayName(), BuildConfiguration::Unknown)); - connect(m_buildDirManager, &BuildDirManager::dataAvailable, + connect(m_buildDirManager.get(), &BuildDirManager::dataAvailable, this, &CMakeBuildConfiguration::dataAvailable); - connect(m_buildDirManager, &BuildDirManager::errorOccured, + connect(m_buildDirManager.get(), &BuildDirManager::errorOccured, this, &CMakeBuildConfiguration::setError); - connect(m_buildDirManager, &BuildDirManager::configurationStarted, + connect(m_buildDirManager.get(), &BuildDirManager::configurationStarted, this, [this]() { m_completeConfigurationCache.clear(); emit parsingStarted(); }); connect(this, &CMakeBuildConfiguration::environmentChanged, - m_buildDirManager, &BuildDirManager::forceReparse); + m_buildDirManager.get(), &BuildDirManager::forceReparse); connect(this, &CMakeBuildConfiguration::buildDirectoryChanged, - m_buildDirManager, &BuildDirManager::forceReparse); + m_buildDirManager.get(), &BuildDirManager::forceReparse); connect(this, &CMakeBuildConfiguration::parsingStarted, project, &CMakeProject::handleParsingStarted); connect(this, &CMakeBuildConfiguration::dataAvailable, project, &CMakeProject::updateProjectData); @@ -185,7 +185,6 @@ void CMakeBuildConfiguration::runCMake() if (!m_buildDirManager || m_buildDirManager->isParsing()) return; - m_buildDirManager->checkConfiguration(); m_buildDirManager->forceReparse(); } diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h index 2fa82cb7d5..a4cf71d12e 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h @@ -32,6 +32,8 @@ #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/abi.h> +#include <memory> + namespace CppTools { class ProjectPartBuilder; } namespace ProjectExplorer { class ToolChain; } @@ -114,7 +116,7 @@ private: mutable QList<CMakeConfigItem> m_completeConfigurationCache; - BuildDirManager *const m_buildDirManager = nullptr; + std::unique_ptr<BuildDirManager> m_buildDirManager; friend class CMakeBuildSettingsWidget; friend class CMakeProjectManager::CMakeProject; diff --git a/src/plugins/cmakeprojectmanager/cmakecbpparser.cpp b/src/plugins/cmakeprojectmanager/cmakecbpparser.cpp index 9b8a5179ba..0895bd8999 100644 --- a/src/plugins/cmakeprojectmanager/cmakecbpparser.cpp +++ b/src/plugins/cmakeprojectmanager/cmakecbpparser.cpp @@ -45,13 +45,13 @@ namespace Internal { //// namespace { -int distance(const QString &targetDirectory, const FileName &fileName) +int distance(const FileName &targetDirectory, const FileName &fileName) { - const QString commonParent = Utils::commonPath(QStringList() << targetDirectory << fileName.toString()); - return targetDirectory.mid(commonParent.size()).count(QLatin1Char('/')) - + fileName.toString().mid(commonParent.size()).count(QLatin1Char('/')); -} + const QString commonParent = commonPath(QStringList({ targetDirectory.toString(), fileName.toString() })); + return targetDirectory.toString().mid(commonParent.size()).count('/') + + fileName.toString().mid(commonParent.size()).count('/'); } +} // namespace // called after everything is parsed // this function tries to figure out to which CMakeBuildTarget @@ -60,10 +60,9 @@ int distance(const QString &targetDirectory, const FileName &fileName) void CMakeCbpParser::sortFiles() { QLoggingCategory log("qtc.cmakeprojectmanager.filetargetmapping"); - FileNameList fileNames = Utils::transform(m_fileList, &FileNode::filePath); - - Utils::sort(fileNames); + FileNameList fileNames = transform(m_fileList, &FileNode::filePath); + sort(fileNames); CMakeBuildTarget *last = 0; FileName parentDirectory; @@ -101,9 +100,9 @@ void CMakeCbpParser::sortFiles() if (!unitTargets.isEmpty()) { // cmake >= 3.3: foreach (const QString &unitTarget, unitTargets) { - int index = Utils::indexOf(m_buildTargets, Utils::equal(&CMakeBuildTarget::title, unitTarget)); + int index = indexOf(m_buildTargets, equal(&CMakeBuildTarget::title, unitTarget)); if (index != -1) { - m_buildTargets[index].files.append(fileName.toString()); + m_buildTargets[index].files.append(fileName); qCDebug(log) << " into" << m_buildTargets[index].title << "(target attribute)"; continue; } @@ -114,7 +113,7 @@ void CMakeCbpParser::sortFiles() // fallback for cmake < 3.3: if (fileName.parentDir() == parentDirectory && last) { // easy case, same parent directory as last file - last->files.append(fileName.toString()); + last->files.append(fileName); qCDebug(log) << " into" << last->title << "(same parent)"; } else { int bestDistance = std::numeric_limits<int>::max(); @@ -142,7 +141,7 @@ void CMakeCbpParser::sortFiles() } if (bestIndex != -1) { - m_buildTargets[bestIndex].files.append(fileName.toString()); + m_buildTargets[bestIndex].files.append(fileName); last = &m_buildTargets[bestIndex]; parentDirectory = fileName.parentDir(); qCDebug(log) << " into" << last->title; @@ -157,19 +156,21 @@ void CMakeCbpParser::sortFiles() qCDebug(log) << target.title << target.sourceDirectory << target.includeFiles << target.defines << target.files << "\n"; } -bool CMakeCbpParser::parseCbpFile(const Kit *const kit, const QString &fileName, const QString &sourceDirectory) +bool CMakeCbpParser::parseCbpFile(CMakeTool::PathMapper mapper, const FileName &fileName, + const FileName &sourceDirectory) { - m_kit = kit; - m_buildDirectory = QFileInfo(fileName).absolutePath(); + + m_pathMapper = mapper; + m_buildDirectory = FileName::fromString(fileName.toFileInfo().absolutePath()); m_sourceDirectory = sourceDirectory; - QFile fi(fileName); + QFile fi(fileName.toString()); if (fi.exists() && fi.open(QFile::ReadOnly)) { setDevice(&fi); while (!atEnd()) { readNext(); - if (name() == QLatin1String("CodeBlocks_project_file")) + if (name() == "CodeBlocks_project_file") parseCodeBlocks_project_file(); else if (isStartElement()) parseUnknownElement(); @@ -181,7 +182,7 @@ bool CMakeCbpParser::parseCbpFile(const Kit *const kit, const QString &fileName, // There is always a clean target: CMakeBuildTarget cleanTarget; - cleanTarget.title = QLatin1String("clean"); + cleanTarget.title = "clean"; cleanTarget.targetType = UtilityType; cleanTarget.workingDirectory = m_buildDirectory; cleanTarget.sourceDirectory = m_sourceDirectory; @@ -199,7 +200,7 @@ void CMakeCbpParser::parseCodeBlocks_project_file() readNext(); if (isEndElement()) return; - else if (name() == QLatin1String("Project")) + else if (name() == "Project") parseProject(); else if (isStartElement()) parseUnknownElement(); @@ -212,11 +213,11 @@ void CMakeCbpParser::parseProject() readNext(); if (isEndElement()) return; - else if (name() == QLatin1String("Option")) + else if (name() == "Option") parseOption(); - else if (name() == QLatin1String("Unit")) + else if (name() == "Unit") parseUnit(); - else if (name() == QLatin1String("Build")) + else if (name() == "Build") parseBuild(); else if (isStartElement()) parseUnknownElement(); @@ -229,7 +230,7 @@ void CMakeCbpParser::parseBuild() readNext(); if (isEndElement()) return; - else if (name() == QLatin1String("Target")) + else if (name() == "Target") parseBuildTarget(); else if (isStartElement()) parseUnknownElement(); @@ -240,23 +241,23 @@ void CMakeCbpParser::parseBuildTarget() { m_buildTarget.clear(); - if (attributes().hasAttribute(QLatin1String("title"))) - m_buildTarget.title = attributes().value(QLatin1String("title")).toString(); + if (attributes().hasAttribute("title")) + m_buildTarget.title = attributes().value("title").toString(); while (!atEnd()) { readNext(); if (isEndElement()) { - if (!m_buildTarget.title.endsWith(QLatin1String("/fast")) - && !m_buildTarget.title.endsWith(QLatin1String("_automoc"))) { + if (!m_buildTarget.title.endsWith("/fast") + && !m_buildTarget.title.endsWith("_automoc")) { if (m_buildTarget.executable.isEmpty() && m_buildTarget.targetType == ExecutableType) m_buildTarget.targetType = UtilityType; m_buildTargets.append(m_buildTarget); } return; - } else if (name() == QLatin1String("Compiler")) { + } else if (name() == "Compiler") { parseCompiler(); - } else if (name() == QLatin1String("Option")) { + } else if (name() == "Option") { parseBuildTargetOption(); - } else if (name() == QLatin1String("MakeCommands")) { + } else if (name() == "MakeCommands") { parseMakeCommands(); } else if (isStartElement()) { parseUnknownElement(); @@ -266,13 +267,10 @@ void CMakeCbpParser::parseBuildTarget() void CMakeCbpParser::parseBuildTargetOption() { - if (attributes().hasAttribute(QLatin1String("output"))) { - m_buildTarget.executable = attributes().value(QLatin1String("output")).toString(); - CMakeTool *tool = CMakeKitInformation::cmakeTool(m_kit); - if (tool) - m_buildTarget.executable = tool->mapAllPaths(m_kit, m_buildTarget.executable); - } else if (attributes().hasAttribute(QLatin1String("type"))) { - const QStringRef value = attributes().value(QLatin1String("type")); + if (attributes().hasAttribute("output")) { + m_buildTarget.executable = m_pathMapper(FileName::fromString(attributes().value("output").toString())); + } else if (attributes().hasAttribute("type")) { + const QStringRef value = attributes().value("type"); if (value == "0" || value == "1") m_buildTarget.targetType = ExecutableType; else if (value == "2") @@ -281,10 +279,10 @@ void CMakeCbpParser::parseBuildTargetOption() m_buildTarget.targetType = DynamicLibraryType; else m_buildTarget.targetType = UtilityType; - } else if (attributes().hasAttribute(QLatin1String("working_dir"))) { - m_buildTarget.workingDirectory = attributes().value(QLatin1String("working_dir")).toString(); + } else if (attributes().hasAttribute("working_dir")) { + m_buildTarget.workingDirectory = FileName::fromUserInput(attributes().value("working_dir").toString()); - QFile cmakeSourceInfoFile(m_buildTarget.workingDirectory + QFile cmakeSourceInfoFile(m_buildTarget.workingDirectory.toString() + QStringLiteral("/CMakeFiles/CMakeDirectoryInformation.cmake")); if (cmakeSourceInfoFile.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream stream(&cmakeSourceInfoFile); @@ -292,18 +290,19 @@ void CMakeCbpParser::parseBuildTargetOption() while (!stream.atEnd()) { const QString lineTopSource = stream.readLine().trimmed(); if (lineTopSource.startsWith(searchSource, Qt::CaseInsensitive)) { - m_buildTarget.sourceDirectory = lineTopSource.mid(searchSource.size()); - m_buildTarget.sourceDirectory.chop(2); // cut off ") + QString src = lineTopSource.mid(searchSource.size()); + src.chop(2); + m_buildTarget.sourceDirectory = FileName::fromString(src); break; } } } if (m_buildTarget.sourceDirectory.isEmpty()) { - QDir dir(m_buildDirectory); - const QString relative = dir.relativeFilePath(m_buildTarget.workingDirectory); - m_buildTarget.sourceDirectory - = FileName::fromString(m_sourceDirectory).appendPath(relative).toString(); + QDir dir(m_buildDirectory.toString()); + const QString relative = dir.relativeFilePath(m_buildTarget.workingDirectory.toString()); + m_buildTarget.sourceDirectory = m_sourceDirectory; + m_buildTarget.sourceDirectory.appendPath(relative).toString(); } } while (!atEnd()) { @@ -322,11 +321,11 @@ QString CMakeCbpParser::projectName() const void CMakeCbpParser::parseOption() { - if (attributes().hasAttribute(QLatin1String("title"))) - m_projectName = attributes().value(QLatin1String("title")).toString(); + if (attributes().hasAttribute("title")) + m_projectName = attributes().value("title").toString(); - if (attributes().hasAttribute(QLatin1String("compiler"))) - m_compiler = attributes().value(QLatin1String("compiler")).toString(); + if (attributes().hasAttribute("compiler")) + m_compiler = attributes().value("compiler").toString(); while (!atEnd()) { readNext(); @@ -343,9 +342,9 @@ void CMakeCbpParser::parseMakeCommands() readNext(); if (isEndElement()) return; - else if (name() == QLatin1String("Build")) + else if (name() == "Build") parseBuildTargetBuild(); - else if (name() == QLatin1String("Clean")) + else if (name() == "Clean") parseBuildTargetClean(); else if (isStartElement()) parseUnknownElement(); @@ -354,13 +353,8 @@ void CMakeCbpParser::parseMakeCommands() void CMakeCbpParser::parseBuildTargetBuild() { - if (attributes().hasAttribute(QLatin1String("command"))) { - m_buildTarget.makeCommand = attributes().value(QLatin1String("command")).toString(); - - CMakeTool *tool = CMakeKitInformation::cmakeTool(m_kit); - if (tool) - m_buildTarget.makeCommand = tool->mapAllPaths(m_kit, m_buildTarget.makeCommand); - } + if (attributes().hasAttribute("command")) + m_buildTarget.makeCommand = m_pathMapper(FileName::fromUserInput(attributes().value("command").toString())); while (!atEnd()) { readNext(); if (isEndElement()) @@ -387,7 +381,7 @@ void CMakeCbpParser::parseCompiler() readNext(); if (isEndElement()) return; - else if (name() == QLatin1String("Add")) + else if (name() == "Add") parseAdd(); else if (isStartElement()) parseUnknownElement(); @@ -399,23 +393,20 @@ void CMakeCbpParser::parseAdd() // CMake only supports <Add option=\> and <Add directory=\> const QXmlStreamAttributes addAttributes = attributes(); - QString includeDirectory = addAttributes.value(QLatin1String("directory")).toString(); - - CMakeTool *tool = CMakeKitInformation::cmakeTool(m_kit); - if (tool) - includeDirectory = tool->mapAllPaths(m_kit, includeDirectory); + FileName includeDirectory + = m_pathMapper(FileName::fromString(addAttributes.value("directory").toString())); // allow adding multiple times because order happens if (!includeDirectory.isEmpty()) m_buildTarget.includeFiles.append(includeDirectory); - QString compilerOption = addAttributes.value(QLatin1String("option")).toString(); + QString compilerOption = addAttributes.value("option").toString(); // defining multiple times a macro to the same value makes no sense if (!compilerOption.isEmpty() && !m_buildTarget.compilerOptions.contains(compilerOption)) { m_buildTarget.compilerOptions.append(compilerOption); - int macroNameIndex = compilerOption.indexOf(QLatin1String("-D")) + 2; + int macroNameIndex = compilerOption.indexOf("-D") + 2; if (macroNameIndex != 1) { - int assignIndex = compilerOption.indexOf(QLatin1Char('='), macroNameIndex); + int assignIndex = compilerOption.indexOf('=', macroNameIndex); if (assignIndex != -1) compilerOption[assignIndex] = ' '; m_buildTarget.defines.append("#define "); @@ -435,43 +426,36 @@ void CMakeCbpParser::parseAdd() void CMakeCbpParser::parseUnit() { - //qDebug()<<stream.attributes().value("filename"); FileName fileName = - FileName::fromUserInput(attributes().value(QLatin1String("filename")).toString()); - - CMakeTool *tool = CMakeKitInformation::cmakeTool(m_kit); - if (tool) { - QString mappedFile = tool->mapAllPaths(m_kit, fileName.toString()); - fileName = FileName::fromUserInput(mappedFile); - } + m_pathMapper(FileName::fromUserInput(attributes().value("filename").toString())); m_parsingCMakeUnit = false; m_unitTargets.clear(); while (!atEnd()) { readNext(); if (isEndElement()) { - if (!fileName.endsWith(QLatin1String(".rule")) && !m_processedUnits.contains(fileName)) { + if (!fileName.endsWith(".rule") && !m_processedUnits.contains(fileName)) { // Now check whether we found a virtual element beneath if (m_parsingCMakeUnit) { - m_cmakeFileList.append( new ProjectExplorer::FileNode(fileName, ProjectExplorer::ProjectFileType, false)); + m_cmakeFileList.append( new FileNode(fileName, FileType::Project, false)); } else { bool generated = false; QString onlyFileName = fileName.fileName(); - if ( (onlyFileName.startsWith(QLatin1String("moc_")) && onlyFileName.endsWith(QLatin1String(".cxx"))) - || (onlyFileName.startsWith(QLatin1String("ui_")) && onlyFileName.endsWith(QLatin1String(".h"))) - || (onlyFileName.startsWith(QLatin1String("qrc_")) && onlyFileName.endsWith(QLatin1String(".cxx")))) + if ( (onlyFileName.startsWith("moc_") && onlyFileName.endsWith(".cxx")) + || (onlyFileName.startsWith("ui_") && onlyFileName.endsWith(".h")) + || (onlyFileName.startsWith("qrc_") && onlyFileName.endsWith(".cxx"))) generated = true; - if (fileName.endsWith(QLatin1String(".qrc"))) - m_fileList.append( new ProjectExplorer::FileNode(fileName, ProjectExplorer::ResourceType, generated)); + if (fileName.endsWith(".qrc")) + m_fileList.append( new FileNode(fileName, FileType::Resource, generated)); else - m_fileList.append( new ProjectExplorer::FileNode(fileName, ProjectExplorer::SourceType, generated)); + m_fileList.append( new FileNode(fileName, FileType::Source, generated)); } m_unitTargetMap.insert(fileName, m_unitTargets); m_processedUnits.insert(fileName); } return; - } else if (name() == QLatin1String("Option")) { + } else if (name() == "Option") { parseUnitOption(); } else if (isStartElement()) { parseUnknownElement(); @@ -482,8 +466,8 @@ void CMakeCbpParser::parseUnit() void CMakeCbpParser::parseUnitOption() { const QXmlStreamAttributes optionAttributes = attributes(); - m_parsingCMakeUnit = optionAttributes.hasAttribute(QLatin1String("virtualFolder")); - const QString target = optionAttributes.value(QLatin1String("target")).toString(); + m_parsingCMakeUnit = optionAttributes.hasAttribute("virtualFolder"); + const QString target = optionAttributes.value("target").toString(); if (!target.isEmpty()) m_unitTargets.append(target); @@ -513,12 +497,12 @@ void CMakeCbpParser::parseUnknownElement() } } -QList<ProjectExplorer::FileNode *> CMakeCbpParser::fileList() +QList<FileNode *> CMakeCbpParser::fileList() { return m_fileList; } -QList<ProjectExplorer::FileNode *> CMakeCbpParser::cmakeFileList() +QList<FileNode *> CMakeCbpParser::cmakeFileList() { return m_cmakeFileList; } @@ -538,6 +522,5 @@ QString CMakeCbpParser::compilerName() const return m_compiler; } - } // namespace Internal } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakecbpparser.h b/src/plugins/cmakeprojectmanager/cmakecbpparser.h index 286c0779f7..18905fa384 100644 --- a/src/plugins/cmakeprojectmanager/cmakecbpparser.h +++ b/src/plugins/cmakeprojectmanager/cmakecbpparser.h @@ -26,6 +26,7 @@ #pragma once #include "cmakeproject.h" +#include "cmaketool.h" #include <utils/fileutils.h> @@ -46,8 +47,8 @@ namespace Internal { class CMakeCbpParser : public QXmlStreamReader { public: - bool parseCbpFile(const ProjectExplorer::Kit *const kit, const QString &fileName, - const QString &sourceDirectory); + bool parseCbpFile(CMakeTool::PathMapper mapper, const Utils::FileName &fileName, + const Utils::FileName &sourceDirectory); QList<ProjectExplorer::FileNode *> fileList(); QList<ProjectExplorer::FileNode *> cmakeFileList(); QList<CMakeBuildTarget> buildTargets(); @@ -73,7 +74,7 @@ private: void sortFiles(); QMap<Utils::FileName, QStringList> m_unitTargetMap; - const ProjectExplorer::Kit *m_kit = 0; + CMakeTool::PathMapper m_pathMapper; QList<ProjectExplorer::FileNode *> m_fileList; QList<ProjectExplorer::FileNode *> m_cmakeFileList; QSet<Utils::FileName> m_processedUnits; @@ -83,8 +84,8 @@ private: QList<CMakeBuildTarget> m_buildTargets; QString m_projectName; QString m_compiler; - QString m_sourceDirectory; - QString m_buildDirectory; + Utils::FileName m_sourceDirectory; + Utils::FileName m_buildDirectory; QStringList m_unitTargets; }; diff --git a/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp b/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp index ff1011ef81..d2f21c90b1 100644 --- a/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp @@ -134,9 +134,31 @@ QStringList CMakeConfigItem::cmakeSplitValue(const QString &in, bool keepEmpty) return newArgs; } +CMakeConfigItem::Type CMakeConfigItem::typeStringToType(const QByteArray &type) +{ + if (type == "BOOL") + return CMakeConfigItem::BOOL; + if (type == "STRING") + return CMakeConfigItem::STRING; + if (type == "FILEPATH") + return CMakeConfigItem::FILEPATH; + if (type == "PATH") + return CMakeConfigItem::PATH; + if (type == "STATIC") + return CMakeConfigItem::STATIC; + + QTC_CHECK(type == "INTERNAL" || type == "UNINITIALIZED"); + return CMakeConfigItem::INTERNAL; +} + QString CMakeConfigItem::expandedValue(const ProjectExplorer::Kit *k) const { - return k->macroExpander()->expand(QString::fromUtf8(value)); + return expandedValue(k->macroExpander()); +} + +QString CMakeConfigItem::expandedValue(const Utils::MacroExpander *expander) const +{ + return expander->expand(QString::fromUtf8(value)); } std::function<bool (const CMakeConfigItem &a, const CMakeConfigItem &b)> CMakeConfigItem::sortOperator() @@ -190,26 +212,14 @@ CMakeConfigItem CMakeConfigItem::fromString(const QString &s) // Fill in item: CMakeConfigItem item; if (!key.isEmpty()) { - CMakeConfigItem::Type t = CMakeConfigItem::STRING; - if (type == QLatin1String("FILEPATH")) - t = CMakeConfigItem::FILEPATH; - else if (type == QLatin1String("PATH")) - t = CMakeConfigItem::PATH; - else if (type == QLatin1String("BOOL")) - t = CMakeConfigItem::BOOL; - else if (type == QLatin1String("INTERNAL")) - t = CMakeConfigItem::INTERNAL; - else if (type == QLatin1String("STATIC")) - t = CMakeConfigItem::STATIC; - item.key = key.toUtf8(); - item.type = t; + item.type = CMakeConfigItem::typeStringToType(type.toUtf8()); item.value = value.toUtf8(); } return item; } -QString CMakeConfigItem::toString() const +QString CMakeConfigItem::toString(const Utils::MacroExpander *expander) const { if (key.isEmpty() || type == CMakeProjectManager::CMakeConfigItem::STATIC) return QString(); @@ -235,7 +245,14 @@ QString CMakeConfigItem::toString() const break; } - return QString::fromUtf8(key) + QLatin1Char(':') + typeStr + QLatin1Char('=') + QString::fromUtf8(value); + const QString expandedValue + = expander ? expander->expand(QString::fromUtf8(value)) : QString::fromUtf8(value); + return QString::fromUtf8(key) + QLatin1Char(':') + typeStr + QLatin1Char('=') + expandedValue; +} + +QString CMakeConfigItem::toArgument(const Utils::MacroExpander *expander) const +{ + return "-D" + toString(expander); } bool CMakeConfigItem::operator==(const CMakeConfigItem &o) const diff --git a/src/plugins/cmakeprojectmanager/cmakeconfigitem.h b/src/plugins/cmakeprojectmanager/cmakeconfigitem.h index 1484cffda3..e52a3ee535 100644 --- a/src/plugins/cmakeprojectmanager/cmakeconfigitem.h +++ b/src/plugins/cmakeprojectmanager/cmakeconfigitem.h @@ -31,6 +31,7 @@ #include <functional> namespace ProjectExplorer { class Kit; } +namespace Utils { class MacroExpander; } namespace CMakeProjectManager { @@ -46,13 +47,16 @@ public: static QString expandedValueOf(const ProjectExplorer::Kit *k, const QByteArray &key, const QList<CMakeConfigItem> &input); static QStringList cmakeSplitValue(const QString &in, bool keepEmpty = false); + static Type typeStringToType(const QByteArray &typeString); bool isNull() const { return key.isEmpty(); } QString expandedValue(const ProjectExplorer::Kit *k) const; + QString expandedValue(const Utils::MacroExpander *expander) const; static std::function<bool(const CMakeConfigItem &a, const CMakeConfigItem &b)> sortOperator(); static CMakeConfigItem fromString(const QString &s); - QString toString() const; + QString toString(const Utils::MacroExpander *expander = nullptr) const; + QString toArgument(const Utils::MacroExpander *expander = nullptr) const; bool operator==(const CMakeConfigItem &o) const; diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp index 7375c7feb2..d38155c345 100644 --- a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp +++ b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp @@ -420,8 +420,8 @@ void CMakeGeneratorKitInformation::upgrade(Kit *k) } else { info.generator = fullName; } + setGeneratorInfo(k, info); } - setGeneratorInfo(k, info); } KitInformation::ItemList CMakeGeneratorKitInformation::toUserOutput(const Kit *k) const diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index 52905ad598..0c266fbf6b 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -395,7 +395,7 @@ void CMakeProject::updateTargetRunConfigurations(Target *t) auto btIt = buildTargetHash.constFind(cmakeRc->title()); cmakeRc->setEnabled(btIt != buildTargetHash.constEnd()); if (btIt != buildTargetHash.constEnd()) { - cmakeRc->setExecutable(btIt.value()->executable); + cmakeRc->setExecutable(btIt.value()->executable.toString()); cmakeRc->setBaseWorkingDirectory(btIt.value()->workingDirectory); } } @@ -436,12 +436,10 @@ void CMakeProject::updateApplicationAndDeploymentTargets() continue; if (ct.targetType == ExecutableType || ct.targetType == DynamicLibraryType) - deploymentData.addFile(ct.executable, deploymentPrefix + buildDir.relativeFilePath(QFileInfo(ct.executable).dir().path()), DeployableFile::TypeExecutable); + deploymentData.addFile(ct.executable.toString(), deploymentPrefix + buildDir.relativeFilePath(ct.executable.toFileInfo().dir().path()), DeployableFile::TypeExecutable); if (ct.targetType == ExecutableType) { // TODO: Put a path to corresponding .cbp file into projectFilePath? - appTargetList.list << BuildTargetInfo(ct.title, - FileName::fromString(ct.executable), - FileName::fromString(ct.executable)); + appTargetList.list << BuildTargetInfo(ct.title, ct.executable, ct.executable); } } @@ -500,6 +498,7 @@ void CMakeBuildTarget::clear() includeFiles.clear(); compilerOptions.clear(); defines.clear(); + files.clear(); } } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h index 8ede082cd7..0cb58b9494 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.h +++ b/src/plugins/cmakeprojectmanager/cmakeproject.h @@ -41,7 +41,6 @@ QT_END_NAMESPACE namespace CMakeProjectManager { namespace Internal { -class CMakeFile; class CMakeBuildSettingsWidget; class CMakeBuildConfiguration; class CMakeProjectNode; @@ -59,17 +58,17 @@ class CMAKE_EXPORT CMakeBuildTarget { public: QString title; - QString executable; // TODO: rename to output? + Utils::FileName executable; // TODO: rename to output? TargetType targetType = UtilityType; - QString workingDirectory; - QString sourceDirectory; - QString makeCommand; + Utils::FileName workingDirectory; + Utils::FileName sourceDirectory; + Utils::FileName makeCommand; // code model - QStringList includeFiles; + QList<Utils::FileName> includeFiles; QStringList compilerOptions; QByteArray defines; - QStringList files; + QList<Utils::FileName> files; void clear(); }; @@ -129,7 +128,6 @@ private: QList<ProjectExplorer::ExtraCompiler *> m_extraCompilers; friend class Internal::CMakeBuildConfiguration; - friend class Internal::CMakeFile; }; } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.qrc b/src/plugins/cmakeprojectmanager/cmakeproject.qrc index 4b5080980a..6926b1d41d 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.qrc +++ b/src/plugins/cmakeprojectmanager/cmakeproject.qrc @@ -1,5 +1,7 @@ <RCC> <qresource prefix="/cmakeproject"> <file>CMakeProjectManager.mimetypes.xml</file> + <file>images/fileoverlay_cmake.png</file> + <file>images/fileoverlay_cmake@2x.png</file> </qresource> </RCC> diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h b/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h index 1f6c33411e..94abe1c1fd 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h +++ b/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h @@ -54,5 +54,8 @@ const char CMAKE_SETTINGSPAGE_ID[] = "Z.CMake"; // Snippets const char CMAKE_SNIPPETS_GROUP_ID[] = "CMake"; +// Icons +const char FILEOVERLAY_CMAKE[] = ":/cmakeproject/images/fileoverlay_cmake.png"; + } // namespace Constants } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro index 82cd93df82..658564b177 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro @@ -2,6 +2,7 @@ DEFINES += CMAKEPROJECTMANAGER_LIBRARY include(../../qtcreatorplugin.pri) HEADERS = builddirmanager.h \ + builddirreader.h \ cmakebuildinfo.h \ cmakebuildstep.h \ cmakeconfigitem.h \ @@ -24,14 +25,17 @@ HEADERS = builddirmanager.h \ cmakekitinformation.h \ cmakekitconfigwidget.h \ cmakecbpparser.h \ - cmakefile.h \ cmakebuildsettingswidget.h \ cmakeindenter.h \ cmakeautocompleter.h \ configmodel.h \ - configmodelitemdelegate.h + configmodelitemdelegate.h \ + servermode.h \ + servermodereader.h \ + tealeafreader.h SOURCES = builddirmanager.cpp \ + builddirreader.cpp \ cmakebuildstep.cpp \ cmakeconfigitem.cpp \ cmakeproject.cpp \ @@ -51,11 +55,13 @@ SOURCES = builddirmanager.cpp \ cmakekitinformation.cpp \ cmakekitconfigwidget.cpp \ cmakecbpparser.cpp \ - cmakefile.cpp \ cmakebuildsettingswidget.cpp \ cmakeindenter.cpp \ cmakeautocompleter.cpp \ configmodel.cpp \ - configmodelitemdelegate.cpp + configmodelitemdelegate.cpp \ + servermode.cpp \ + servermodereader.cpp \ + tealeafreader.cpp RESOURCES += cmakeproject.qrc diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs index 99fe635b27..6bf3e624b7 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs @@ -20,6 +20,8 @@ QtcPlugin { files: [ "builddirmanager.cpp", "builddirmanager.h", + "builddirreader.cpp", + "builddirreader.h", "cmake_global.h", "cmakebuildconfiguration.cpp", "cmakebuildconfiguration.h", @@ -34,8 +36,6 @@ QtcPlugin { "cmakeconfigitem.h", "cmakeeditor.cpp", "cmakeeditor.h", - "cmakefile.cpp", - "cmakefile.h", "cmakefilecompletionassist.cpp", "cmakefilecompletionassist.h", "cmakekitconfigwidget.h", @@ -73,6 +73,12 @@ QtcPlugin { "configmodel.cpp", "configmodel.h", "configmodelitemdelegate.cpp", - "configmodelitemdelegate.h" + "configmodelitemdelegate.h", + "servermode.cpp", + "servermode.h", + "servermodereader.cpp", + "servermodereader.h", + "tealeafreader.cpp", + "tealeafreader.h" ] } diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp index dba81e9c27..cffcc282a6 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp @@ -37,9 +37,11 @@ #include "cmaketoolmanager.h" #include "cmakekitinformation.h" -#include <utils/mimetypes/mimedatabase.h> +#include <coreplugin/fileiconprovider.h> #include <projectexplorer/kitmanager.h> +#include <utils/mimetypes/mimedatabase.h> + using namespace CMakeProjectManager::Internal; bool CMakeProjectPlugin::initialize(const QStringList & /*arguments*/, QString *errorMessage) @@ -47,6 +49,9 @@ bool CMakeProjectPlugin::initialize(const QStringList & /*arguments*/, QString * Q_UNUSED(errorMessage) Utils::MimeDatabase::addMimeTypes(QLatin1String(":cmakeproject/CMakeProjectManager.mimetypes.xml")); + Core::FileIconProvider::registerIconOverlayForSuffix(Constants::FILEOVERLAY_CMAKE, "cmake"); + Core::FileIconProvider::registerIconOverlayForFilename(Constants::FILEOVERLAY_CMAKE, "CMakeLists.txt"); + addAutoReleasedObject(new Internal::CMakeSnippetProvider); addAutoReleasedObject(new CMakeSettingsPage); addAutoReleasedObject(new CMakeManager); diff --git a/src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp index f48209b35b..ea018e1126 100644 --- a/src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp @@ -60,7 +60,7 @@ const char TITLE_KEY[] = "CMakeProjectManager.CMakeRunConfiguation.Title"; } // namespace CMakeRunConfiguration::CMakeRunConfiguration(Target *parent, Core::Id id, const QString &target, - const QString &workingDirectory, const QString &title) : + const Utils::FileName &workingDirectory, const QString &title) : RunConfiguration(parent, id), m_buildTarget(target), m_title(title) @@ -70,7 +70,7 @@ CMakeRunConfiguration::CMakeRunConfiguration(Target *parent, Core::Id id, const addExtraAspect(new TerminalAspect(this, QStringLiteral("CMakeProjectManager.CMakeRunConfiguration.UseTerminal"))); auto wd = new WorkingDirectoryAspect(this, QStringLiteral("CMakeProjectManager.CMakeRunConfiguration.UserWorkingDirectory")); - wd->setDefaultWorkingDirectory(Utils::FileName::fromString(workingDirectory)); + wd->setDefaultWorkingDirectory(workingDirectory); addExtraAspect(wd); ctor(); @@ -119,10 +119,9 @@ void CMakeRunConfiguration::setExecutable(const QString &executable) m_buildTarget = executable; } -void CMakeRunConfiguration::setBaseWorkingDirectory(const QString &wd) +void CMakeRunConfiguration::setBaseWorkingDirectory(const Utils::FileName &wd) { - extraAspect<WorkingDirectoryAspect>() - ->setDefaultWorkingDirectory(Utils::FileName::fromString(wd)); + extraAspect<WorkingDirectoryAspect>()->setDefaultWorkingDirectory(wd); } QVariantMap CMakeRunConfiguration::toMap() const @@ -246,7 +245,7 @@ RunConfiguration *CMakeRunConfigurationFactory::doCreate(Target *parent, Core::I CMakeProject *project = static_cast<CMakeProject *>(parent->project()); const QString title(buildTargetFromId(id)); const CMakeBuildTarget &ct = project->buildTargetForTitle(title); - return new CMakeRunConfiguration(parent, id, ct.executable, ct.workingDirectory, ct.title); + return new CMakeRunConfiguration(parent, id, ct.executable.toString(), ct.workingDirectory, ct.title); } bool CMakeRunConfigurationFactory::canClone(Target *parent, RunConfiguration *source) const @@ -273,7 +272,7 @@ bool CMakeRunConfigurationFactory::canRestore(Target *parent, const QVariantMap RunConfiguration *CMakeRunConfigurationFactory::doRestore(Target *parent, const QVariantMap &map) { - return new CMakeRunConfiguration(parent, idFromMap(map), QString(), QString(), QString()); + return new CMakeRunConfiguration(parent, idFromMap(map), QString(), Utils::FileName(), QString()); } QString CMakeRunConfigurationFactory::buildTargetFromId(Core::Id id) diff --git a/src/plugins/cmakeprojectmanager/cmakerunconfiguration.h b/src/plugins/cmakeprojectmanager/cmakerunconfiguration.h index b694c1a7da..eb7ef71abb 100644 --- a/src/plugins/cmakeprojectmanager/cmakerunconfiguration.h +++ b/src/plugins/cmakeprojectmanager/cmakerunconfiguration.h @@ -39,13 +39,13 @@ class CMakeRunConfiguration : public ProjectExplorer::RunConfiguration public: CMakeRunConfiguration(ProjectExplorer::Target *parent, Core::Id id, const QString &target, - const QString &workingDirectory, const QString &title); + const Utils::FileName &workingDirectory, const QString &title); ProjectExplorer::Runnable runnable() const override; QWidget *createConfigurationWidget() override; void setExecutable(const QString &executable); - void setBaseWorkingDirectory(const QString &workingDirectory); + void setBaseWorkingDirectory(const Utils::FileName &workingDirectory); QString title() const; diff --git a/src/plugins/cmakeprojectmanager/cmaketool.cpp b/src/plugins/cmakeprojectmanager/cmaketool.cpp index 2cc0228656..5e917958f1 100644 --- a/src/plugins/cmakeprojectmanager/cmaketool.cpp +++ b/src/plugins/cmakeprojectmanager/cmaketool.cpp @@ -228,11 +228,11 @@ void CMakeTool::setPathMapper(const CMakeTool::PathMapper &pathMapper) m_pathMapper = pathMapper; } -QString CMakeTool::mapAllPaths(const ProjectExplorer::Kit *kit, const QString &in) const +CMakeTool::PathMapper CMakeTool::pathMapper() const { if (m_pathMapper) - return m_pathMapper(kit, in); - return in; + return m_pathMapper; + return [](const Utils::FileName &fn) { return fn; }; } void CMakeTool::readInformation(CMakeTool::QueryType type) const diff --git a/src/plugins/cmakeprojectmanager/cmaketool.h b/src/plugins/cmakeprojectmanager/cmaketool.h index 7e6ff51bc9..5851c75bfc 100644 --- a/src/plugins/cmakeprojectmanager/cmaketool.h +++ b/src/plugins/cmakeprojectmanager/cmaketool.h @@ -75,7 +75,7 @@ public: bool matches(const QString &n, const QString &ex) const; }; - typedef std::function<QString (const ProjectExplorer::Kit *, const QString &)> PathMapper; + typedef std::function<Utils::FileName (const Utils::FileName &)> PathMapper; explicit CMakeTool(Detection d, const Core::Id &id); explicit CMakeTool(const QVariantMap &map, bool fromSdk); @@ -103,7 +103,7 @@ public: void setDisplayName(const QString &displayName); void setPathMapper(const PathMapper &includePathMapper); - QString mapAllPaths(const ProjectExplorer::Kit *kit, const QString &in) const; + PathMapper pathMapper() const; private: enum class QueryType { diff --git a/src/plugins/cmakeprojectmanager/images/fileoverlay_cmake.png b/src/plugins/cmakeprojectmanager/images/fileoverlay_cmake.png Binary files differnew file mode 100644 index 0000000000..95104870c2 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/images/fileoverlay_cmake.png diff --git a/src/plugins/cmakeprojectmanager/images/fileoverlay_cmake@2x.png b/src/plugins/cmakeprojectmanager/images/fileoverlay_cmake@2x.png Binary files differnew file mode 100644 index 0000000000..805db2fabd --- /dev/null +++ b/src/plugins/cmakeprojectmanager/images/fileoverlay_cmake@2x.png diff --git a/src/plugins/cmakeprojectmanager/servermode.cpp b/src/plugins/cmakeprojectmanager/servermode.cpp new file mode 100644 index 0000000000..bb35fef33b --- /dev/null +++ b/src/plugins/cmakeprojectmanager/servermode.cpp @@ -0,0 +1,456 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "servermode.h" + +#include <coreplugin/reaper.h> + +#include <utils/algorithm.h> +#include <utils/qtcassert.h> +#include <utils/qtcprocess.h> + +#include <QByteArray> +#include <QCryptographicHash> +#include <QJsonDocument> +#include <QJsonObject> +#include <QLocalSocket> +#include <QUuid> + +using namespace Utils; + +namespace CMakeProjectManager { +namespace Internal { + +const char COOKIE_KEY[] = "cookie"; +const char IN_REPLY_TO_KEY[] = "inReplyTo"; +const char NAME_KEY[] = "name"; +const char TYPE_KEY[] = "type"; + +const char ERROR_TYPE[] = "error"; +const char HANDSHAKE_TYPE[] = "handshake"; + +const char START_MAGIC[] = "\n[== \"CMake Server\" ==[\n"; +const char END_MAGIC[] = "\n]== \"CMake Server\" ==]\n"; + +// ---------------------------------------------------------------------- +// Helpers: +// ---------------------------------------------------------------------- + +QString socketName(const Utils::FileName &buildDirectory) +{ + if (HostOsInfo::isWindowsHost()) { + QUuid uuid = QUuid::createUuid(); + return "\\\\.\\pipe\\" + uuid.toString(); + } + return buildDirectory.toString() + "/socket"; +} + +// -------------------------------------------------------------------- +// ServerMode: +// -------------------------------------------------------------------- + + +ServerMode::ServerMode(const Environment &env, + const FileName &sourceDirectory, const FileName &buildDirectory, + const FileName &cmakeExecutable, + const QString &generator, const QString &extraGenerator, + const QString &platform, const QString &toolset, + bool experimental, int major, int minor, + QObject *parent) : + QObject(parent), + m_sourceDirectory(sourceDirectory), m_buildDirectory(buildDirectory), + m_cmakeExecutable(cmakeExecutable), + m_generator(generator), m_extraGenerator(extraGenerator), + m_platform(platform), m_toolset(toolset), + m_useExperimental(experimental), m_majorProtocol(major), m_minorProtocol(minor) +{ + QTC_ASSERT(!m_sourceDirectory.isEmpty() && m_sourceDirectory.exists(), return); + QTC_ASSERT(!m_buildDirectory.isEmpty() && m_buildDirectory.exists(), return); + + m_connectionTimer.setInterval(100); + connect(&m_connectionTimer, &QTimer::timeout, this, &ServerMode::connectToServer); + + m_cmakeProcess.reset(new QtcProcess); + + m_cmakeProcess->setEnvironment(env); + m_cmakeProcess->setWorkingDirectory(buildDirectory.toString()); + m_socketName = socketName(buildDirectory); + const QStringList args = QStringList({ "-E", "server", "--pipe=" + m_socketName }); + + connect(m_cmakeProcess.get(), &QtcProcess::started, this, [this]() { m_connectionTimer.start(); }); + connect(m_cmakeProcess.get(), + static_cast<void(QtcProcess::*)(int, QProcess::ExitStatus)>(&QtcProcess::finished), + this, &ServerMode::handleCMakeFinished); + + QString argumentString; + QtcProcess::addArgs(&argumentString, args); + if (m_useExperimental) + QtcProcess::addArg(&argumentString, "--experimental"); + + m_cmakeProcess->setCommand(cmakeExecutable.toString(), argumentString); + + // Delay start: + QTimer::singleShot(0, [argumentString, this] { + emit message(tr("Running \"%1 %2\" in %3.") + .arg(m_cmakeExecutable.toUserOutput()) + .arg(argumentString) + .arg(m_buildDirectory.toUserOutput())); + + m_cmakeProcess->start(); + }); +} + +ServerMode::~ServerMode() +{ + if (m_cmakeProcess) + m_cmakeProcess->disconnect(); + if (m_cmakeSocket) { + m_cmakeSocket->disconnect(); + m_cmakeSocket->abort(); + delete(m_cmakeSocket); + } + m_cmakeSocket = nullptr; + Core::Reaper::reap(m_cmakeProcess.release()); +} + +void ServerMode::sendRequest(const QString &type, const QVariantMap &extra, const QVariant &cookie) +{ + QTC_ASSERT(m_cmakeSocket, return); + ++m_requestCounter; + + QVariantMap data = extra; + data.insert(TYPE_KEY, type); + const QVariant realCookie = cookie.isNull() ? QVariant(m_requestCounter) : cookie; + data.insert(COOKIE_KEY, realCookie); + m_expectedReplies.push_back({ type, realCookie }); + + QJsonObject object = QJsonObject::fromVariantMap(data); + QJsonDocument document; + document.setObject(object); + + const QByteArray rawData = START_MAGIC + document.toJson() + END_MAGIC; + m_cmakeSocket->write(rawData); + m_cmakeSocket->flush(); +} + +bool ServerMode::isConnected() +{ + return m_cmakeSocket && m_isConnected; +} + +void ServerMode::connectToServer() +{ + QTC_ASSERT(m_cmakeProcess, return); + if (m_cmakeSocket) + return; // We connected in the meantime... + + static int counter = 0; + ++counter; + + if (counter > 50) { + counter = 0; + m_cmakeProcess->disconnect(); + reportError(tr("Running \"%1\" failed: Timeout waiting for pipe \"%2\".") + .arg(m_cmakeExecutable.toUserOutput()) + .arg(m_socketName)); + + Core::Reaper::reap(m_cmakeProcess.release()); + emit disconnected(); + return; + } + + QTC_ASSERT(!m_cmakeSocket, return); + + auto socket = new QLocalSocket(m_cmakeProcess.get()); + connect(socket, &QLocalSocket::readyRead, this, &ServerMode::handleRawCMakeServerData); + connect(socket, static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error), + this, [this, socket]() { + reportError(socket->errorString()); + m_cmakeSocket = nullptr; + socket->disconnect(); + socket->deleteLater(); + }); + connect(socket, &QLocalSocket::connected, this, [this, socket]() { m_cmakeSocket = socket; }); + connect(socket, &QLocalSocket::disconnected, this, [this, socket]() { + if (m_cmakeSocket) + emit disconnected(); + m_cmakeSocket = nullptr; + socket->disconnect(); + socket->deleteLater(); + }); + + socket->connectToServer(m_socketName); + m_connectionTimer.start(); +} + +void ServerMode::handleCMakeFinished(int code, QProcess::ExitStatus status) +{ + QString msg; + if (status != QProcess::NormalExit) + msg = tr("CMake process \"%1\" crashed.").arg(m_cmakeExecutable.toUserOutput()); + else if (code != 0) + msg = tr("CMake process \"%1\" quit with exit code %2.").arg(m_cmakeExecutable.toUserOutput()).arg(code); + + if (!msg.isEmpty()) { + reportError(msg); + } else { + emit message(tr("CMake process \"%1\" quit normally.").arg(m_cmakeExecutable.toUserOutput())); + } + + if (m_cmakeSocket) { + m_cmakeSocket->disconnect(); + delete m_cmakeSocket; + m_cmakeSocket = nullptr; + } + + if (!HostOsInfo::isWindowsHost()) + QFile::remove(m_socketName); + + emit disconnected(); +} + +void ServerMode::handleRawCMakeServerData() +{ + const static QByteArray startNeedle(START_MAGIC); + const static QByteArray endNeedle(END_MAGIC); + + if (!m_cmakeSocket) // might happen during shutdown + return; + + m_buffer.append(m_cmakeSocket->readAll()); + + while (true) { + const int startPos = m_buffer.indexOf(startNeedle); + if (startPos >= 0) { + const int afterStartNeedle = startPos + startNeedle.count(); + const int endPos = m_buffer.indexOf(endNeedle, afterStartNeedle); + if (endPos > afterStartNeedle) { + // Process JSON, remove junk and JSON-part, continue to loop with shorter buffer + parseBuffer(m_buffer.mid(afterStartNeedle, endPos - afterStartNeedle)); + m_buffer.remove(0, endPos + endNeedle.count()); + } else { + // Remove junk up to the start needle and break out of the loop + if (startPos > 0) + m_buffer.remove(0, startPos); + break; + } + } else { + // Keep at last startNeedle.count() characters (as that might be a + // partial startNeedle), break out of the loop + if (m_buffer.count() > startNeedle.count()) + m_buffer.remove(0, m_buffer.count() - startNeedle.count()); + break; + } + } +} + +void ServerMode::parseBuffer(const QByteArray &buffer) +{ + QJsonDocument document = QJsonDocument::fromJson(buffer); + if (document.isNull()) { + reportError(tr("Failed to parse JSON from CMake server.")); + return; + } + QJsonObject rootObject = document.object(); + if (rootObject.isEmpty()) { + reportError(tr("JSON data from CMake server was not a JSON object.")); + return; + } + + parseJson(rootObject.toVariantMap()); +} + +void ServerMode::parseJson(const QVariantMap &data) +{ + QString type = data.value(TYPE_KEY).toString(); + if (type == "hello") { + if (m_gotHello) { + reportError(tr("Unexpected hello received from CMake server.")); + return; + } else { + handleHello(data); + m_gotHello = true; + return; + } + } + if (!m_gotHello && type != ERROR_TYPE) { + reportError(tr("Unexpected type \"%1\" received while waiting for \"hello\".").arg(type)); + return; + } + + if (type == "reply") { + if (m_expectedReplies.empty()) { + reportError(tr("Received a reply even though no request is open.")); + return; + } + const QString replyTo = data.value(IN_REPLY_TO_KEY).toString(); + const QVariant cookie = data.value(COOKIE_KEY); + + const auto expected = m_expectedReplies.begin(); + if (expected->type != replyTo) { + reportError(tr("Received a reply to a request of type \"%1\", when a request of type \"%2\" was sent.") + .arg(replyTo).arg(expected->type)); + return; + } + if (expected->cookie != cookie) { + reportError(tr("Received a reply with cookie \"%1\", when \"%2\" was expected.") + .arg(cookie.toString()).arg(expected->cookie.toString())); + return; + } + + m_expectedReplies.erase(expected); + if (replyTo != HANDSHAKE_TYPE) + emit cmakeReply(data, replyTo, cookie); + else { + m_isConnected = true; + emit connected(); + } + return; + } + if (type == "error") { + if (m_expectedReplies.empty()) { + reportError(tr("An error was reported even though no request is open.")); + return; + } + const QString replyTo = data.value(IN_REPLY_TO_KEY).toString(); + const QVariant cookie = data.value(COOKIE_KEY); + + const auto expected = m_expectedReplies.begin(); + if (expected->type != replyTo) { + reportError(tr("Received an error in response to a request of type \"%1\", when a request of type \"%2\" was sent.") + .arg(replyTo).arg(expected->type)); + return; + } + if (expected->cookie != cookie) { + reportError(tr("Received an error with cookie \"%1\", when \"%2\" was expected.") + .arg(cookie.toString()).arg(expected->cookie.toString())); + return; + } + + m_expectedReplies.erase(expected); + + emit cmakeError(data.value("errorMessage").toString(), replyTo, cookie); + if (replyTo == HANDSHAKE_TYPE) { + Core::Reaper::reap(m_cmakeProcess.release()); + m_cmakeSocket->disconnect(); + m_cmakeSocket->disconnectFromServer(); + m_cmakeSocket = nullptr; + emit disconnected(); + } + return; + } + if (type == "message") { + const QString replyTo = data.value(IN_REPLY_TO_KEY).toString(); + const QVariant cookie = data.value(COOKIE_KEY); + + const auto expected = m_expectedReplies.begin(); + if (expected->type != replyTo) { + reportError(tr("Received a message in response to a request of type \"%1\", when a request of type \"%2\" was sent.") + .arg(replyTo).arg(expected->type)); + return; + } + if (expected->cookie != cookie) { + reportError(tr("Received a message with cookie \"%1\", when \"%2\" was expected.") + .arg(cookie.toString()).arg(expected->cookie.toString())); + return; + } + + emit cmakeMessage(data.value("message").toString(), replyTo, cookie); + return; + } + if (type == "progress") { + const QString replyTo = data.value(IN_REPLY_TO_KEY).toString(); + const QVariant cookie = data.value(COOKIE_KEY); + + const auto expected = m_expectedReplies.begin(); + if (expected->type != replyTo) { + reportError(tr("Received a progress report in response to a request of type \"%1\", when a request of type \"%2\" was sent.") + .arg(replyTo).arg(expected->type)); + return; + } + if (expected->cookie != cookie) { + reportError(tr("Received a progress report with cookie \"%1\", when \"%2\" was expected.") + .arg(cookie.toString()).arg(expected->cookie.toString())); + return; + } + + emit cmakeProgress(data.value("progressMinimum").toInt(), + data.value("progressCurrent").toInt(), + data.value("progressMaximum").toInt(), replyTo, cookie); + return; + } + if (type == "signal") { + const QString replyTo = data.value(IN_REPLY_TO_KEY).toString(); + const QVariant cookie = data.value(COOKIE_KEY); + const QString name = data.value(NAME_KEY).toString(); + + if (name.isEmpty()) { + reportError(tr("Received a signal without a name.")); + return; + } + if (!replyTo.isEmpty() || cookie.isValid()) { + reportError(tr("Received a signal in reply to a request.")); + return; + } + + emit cmakeSignal(name, data); + return; + } +} + +void ServerMode::handleHello(const QVariantMap &data) +{ + Q_UNUSED(data); + QVariantMap extra; + QVariantMap version; + version.insert("major", m_majorProtocol); + if (m_minorProtocol >= 0) + version.insert("minor", m_minorProtocol); + extra.insert("protocolVersion", version); + extra.insert("sourceDirectory", m_sourceDirectory.toString()); + extra.insert("buildDirectory", m_buildDirectory.toString()); + extra.insert("generator", m_generator); + if (!m_platform.isEmpty()) + extra.insert("platform", m_platform); + if (!m_toolset.isEmpty()) + extra.insert("toolset", m_toolset); + if (!m_extraGenerator.isEmpty()) + extra.insert("extraGenerator", m_extraGenerator); + if (!m_platform.isEmpty()) + extra.insert("platform", m_platform); + if (!m_toolset.isEmpty()) + extra.insert("toolset", m_toolset); + + sendRequest(HANDSHAKE_TYPE, extra); +} + +void ServerMode::reportError(const QString &msg) +{ + emit message(msg); + emit errorOccured(msg); +} + +} // namespace Internal +} // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/servermode.h b/src/plugins/cmakeprojectmanager/servermode.h new file mode 100644 index 0000000000..00d4393825 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/servermode.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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/qtcprocess.h> + +#include <QTemporaryDir> +#include <QTimer> +#include <QVariantMap> + +#include <memory> + +QT_FORWARD_DECLARE_CLASS(QLocalSocket); + +namespace Utils { class QtcProcess; } + +namespace CMakeProjectManager { +namespace Internal { + +class ServerMode : public QObject +{ + Q_OBJECT + +public: + ServerMode(const Utils::Environment &env, + const Utils::FileName &sourceDirectory, const Utils::FileName &buildDirectory, + const Utils::FileName &cmakeExecutable, + const QString &generator, const QString &extraGenerator, + const QString &platform, const QString &toolset, + bool experimental, int major, int minor = -1, + QObject *parent = nullptr); + ~ServerMode() final; + + void sendRequest(const QString &type, const QVariantMap &extra = QVariantMap(), + const QVariant &cookie = QVariant()); + + bool isConnected(); + +signals: + void connected(); + void disconnected(); + void message(const QString &msg); + void errorOccured(const QString &msg); + + // Forward stuff from the server + void cmakeReply(const QVariantMap &data, const QString &inResponseTo, const QVariant &cookie); + void cmakeError(const QString &errorMessage, const QString &inResponseTo, const QVariant &cookie); + void cmakeMessage(const QString &message, const QString &inResponseTo, const QVariant &cookie); + void cmakeProgress(int min, int cur, int max, const QString &inResponseTo, const QVariant &cookie); + void cmakeSignal(const QString &name, const QVariantMap &data); + +private: + void connectToServer(); + void handleCMakeFinished(int code, QProcess::ExitStatus status); + + void handleRawCMakeServerData(); + void parseBuffer(const QByteArray &buffer); + void parseJson(const QVariantMap &data); + + void handleHello(const QVariantMap &data); + + void reportError(const QString &msg); + + std::unique_ptr<Utils::QtcProcess> m_cmakeProcess; + QLocalSocket *m_cmakeSocket = nullptr; + QTimer m_connectionTimer; + + Utils::FileName m_sourceDirectory; + Utils::FileName m_buildDirectory; + Utils::FileName m_cmakeExecutable; + + QByteArray m_buffer; + + struct ExpectedReply { + QString type; + QVariant cookie; + }; + std::vector<ExpectedReply> m_expectedReplies; + + const QString m_generator; + const QString m_extraGenerator; + const QString m_platform; + const QString m_toolset; + QString m_socketName; + const bool m_useExperimental; + bool m_gotHello = false; + bool m_isConnected = false; + const int m_majorProtocol = -1; + const int m_minorProtocol = -1; + + int m_requestCounter = 0; +}; + +} // namespace Internal +} // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/servermodereader.cpp b/src/plugins/cmakeprojectmanager/servermodereader.cpp new file mode 100644 index 0000000000..5295a07ccd --- /dev/null +++ b/src/plugins/cmakeprojectmanager/servermodereader.cpp @@ -0,0 +1,452 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "servermodereader.h" + +#include "cmakebuildconfiguration.h" +#include "cmakeprojectconstants.h" +#include "cmakeprojectmanager.h" +#include "cmakeprojectnodes.h" +#include "servermode.h" + +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/messagemanager.h> +#include <coreplugin/progressmanager/progressmanager.h> +#include <cpptools/projectpartbuilder.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/toolchain.h> +#include <projectexplorer/task.h> +#include <projectexplorer/taskhub.h> + +#include <utils/algorithm.h> +#include <utils/qtcassert.h> +#include <utils/qtcprocess.h> + +using namespace ProjectExplorer; +using namespace Utils; + +namespace CMakeProjectManager { +namespace Internal { + +const char CACHE_TYPE[] = "cache"; +const char CODEMODEL_TYPE[] = "codemodel"; +const char CONFIGURE_TYPE[] = "configure"; +const char CMAKE_INPUTS_TYPE[] = "cmakeInputs"; +const char COMPUTE_TYPE[] = "compute"; + +const char NAME_KEY[] = "name"; +const char SOURCE_DIRECTORY_KEY[] = "sourceDirectory"; +const char SOURCES_KEY[] = "sources"; + +const int MAX_PROGRESS = 1400; + +// ---------------------------------------------------------------------- +// Helpers: +// ---------------------------------------------------------------------- + +QString socketName(const BuildDirReader::Parameters &p) +{ + return p.buildDirectory.toString() + "/socket"; +} + +// -------------------------------------------------------------------- +// ServerModeReader: +// -------------------------------------------------------------------- + +ServerModeReader::ServerModeReader() +{ + connect(Core::EditorManager::instance(), &Core::EditorManager::aboutToSave, + this, [this](const Core::IDocument *document) { + if (m_cmakeFiles.contains(document->filePath())) + emit dirty(); + }); +} + +ServerModeReader::~ServerModeReader() +{ + stop(); +} + +void ServerModeReader::setParameters(const BuildDirReader::Parameters &p) +{ + BuildDirReader::setParameters(p); + if (!m_cmakeServer) { + m_cmakeServer.reset(new ServerMode(p.environment, + p.sourceDirectory, p.buildDirectory, p.cmakeExecutable, + p.generator, p.extraGenerator, p.platform, p.toolset, + true, 1)); + connect(m_cmakeServer.get(), &ServerMode::errorOccured, + this, &ServerModeReader::errorOccured); + connect(m_cmakeServer.get(), &ServerMode::cmakeReply, + this, &ServerModeReader::handleReply); + connect(m_cmakeServer.get(), &ServerMode::cmakeError, + this, &ServerModeReader::handleError); + connect(m_cmakeServer.get(), &ServerMode::cmakeProgress, + this, &ServerModeReader::handleProgress); + connect(m_cmakeServer.get(), &ServerMode::cmakeMessage, + this, [this](const QString &m) { Core::MessageManager::write(m); }); + connect(m_cmakeServer.get(), &ServerMode::message, + this, [](const QString &m) { Core::MessageManager::write(m); }); + connect(m_cmakeServer.get(), &ServerMode::connected, + this, &ServerModeReader::isReadyNow, Qt::QueuedConnection); // Delay + connect(m_cmakeServer.get(), &ServerMode::disconnected, + this, [this]() { m_cmakeServer.reset(); }, Qt::QueuedConnection); // Delay + } +} + +bool ServerModeReader::isCompatible(const BuildDirReader::Parameters &p) +{ + // Server mode connection got lost, reset... + if (!m_parameters.cmakeExecutable.isEmpty() && !m_cmakeServer) + return false; + + return p.cmakeHasServerMode + && p.cmakeExecutable == m_parameters.cmakeExecutable + && p.environment == m_parameters.environment + && p.generator == m_parameters.generator + && p.extraGenerator == m_parameters.extraGenerator + && p.platform == m_parameters.platform + && p.toolset == m_parameters.toolset + && p.sourceDirectory == m_parameters.sourceDirectory + && p.buildDirectory == m_parameters.buildDirectory; +} + +void ServerModeReader::resetData() +{ + m_hasData = false; +} + +void ServerModeReader::parse(bool force) +{ + QTC_ASSERT(m_cmakeServer, return); + QVariantMap extra; + if (force) + extra.insert("cacheArguments", QVariant(transform(m_parameters.configuration, + [this](const CMakeConfigItem &i) { + return i.toArgument(m_parameters.expander); + }))); + + m_future.reset(new QFutureInterface<void>()); + m_future->setProgressRange(0, MAX_PROGRESS); + m_progressStepMinimum = 0; + m_progressStepMaximum = 1000; + Core::ProgressManager::addTask(m_future->future(), + tr("Configuring \"%1\"").arg(m_parameters.projectName), + "CMake.Configure"); + + m_cmakeServer->sendRequest(CONFIGURE_TYPE, extra); +} + +void ServerModeReader::stop() +{ + if (m_future) { + m_future->reportCanceled(); + m_future->reportFinished(); + m_future.reset(); + } +} + +bool ServerModeReader::isReady() const +{ + return m_cmakeServer->isConnected(); +} + +bool ServerModeReader::isParsing() const +{ + return static_cast<bool>(m_future); +} + +bool ServerModeReader::hasData() const +{ + return m_hasData; +} + +QList<CMakeBuildTarget> ServerModeReader::buildTargets() const +{ + return transform(m_targets, [](const Target *t) -> CMakeBuildTarget { + CMakeBuildTarget ct; + ct.title = t->name; + ct.executable = t->artifacts.isEmpty() ? FileName() : t->artifacts.at(0); + TargetType type = UtilityType; + if (t->type == "STATIC_LIBRARY") + type = StaticLibraryType; + else if (t->type == "MODULE_LIBRARY" + || t->type == "SHARED_LIBRARY" + || t->type == "INTERFACE_LIBRARY" + || t->type == "OBJECT_LIBRARY") + type = DynamicLibraryType; + else + type = UtilityType; + ct.targetType = type; + ct.workingDirectory = t->buildDirectory; + ct.sourceDirectory = t->sourceDirectory; + return ct; + }); +} + +CMakeConfig ServerModeReader::parsedConfiguration() const +{ + return m_cmakeCache; +} + +void ServerModeReader::generateProjectTree(CMakeProjectNode *root, const QList<FileNode *> &allFiles) +{ + Q_UNUSED(allFiles); + QSet<Utils::FileName> knownFiles; + for (auto it = m_cmakeInputsFileNodes.constBegin(); it != m_cmakeInputsFileNodes.constEnd(); ++it) + knownFiles.insert((*it)->filePath()); + + QList<FileNode *> fileGroupNodes = m_cmakeInputsFileNodes; + m_cmakeInputsFileNodes.clear(); // Clean out, they are not going to be used anymore! + foreach (const FileGroup *fg, m_fileGroups) { + for (const FileName &s : fg->sources) { + const int oldCount = knownFiles.count(); + knownFiles.insert(s); + if (oldCount != knownFiles.count()) + fileGroupNodes.append(new FileNode(s, FileType::Source, fg->isGenerated)); + } + } + root->buildTree(fileGroupNodes); +} + +QSet<Core::Id> ServerModeReader::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder) +{ + QSet<Core::Id> languages; + int counter = 0; + foreach (const FileGroup *fg, m_fileGroups) { + ++counter; + const QString defineArg + = transform(fg->defines, [](const QString &s) -> QString { return QString::fromLatin1("#define ") + s; }).join('\n'); + const QStringList flags = QtcProcess::splitArgs(fg->compileFlags); + const QStringList includes = transform(fg->includePaths, [](const IncludePath *ip) { return ip->path.toString(); }); + + ppBuilder.setProjectFile(fg->target->sourceDirectory.toString()); + ppBuilder.setDisplayName(fg->target->name + QString::number(counter)); + ppBuilder.setDefines(defineArg.toUtf8()); + ppBuilder.setIncludePaths(includes); + ppBuilder.setCFlags(flags); + ppBuilder.setCxxFlags(flags); + + + languages.unite(QSet<Core::Id>::fromList(ppBuilder.createProjectPartsForFiles(transform(fg->sources, &FileName::toString)))); + } + + qDeleteAll(m_projects); // Not used anymore! + m_projects.clear(); + m_targets.clear(); + m_fileGroups.clear(); + + return languages; +} + +void ServerModeReader::handleReply(const QVariantMap &data, const QString &inReplyTo) +{ + Q_UNUSED(data); + if (inReplyTo == CONFIGURE_TYPE) { + m_cmakeServer->sendRequest(COMPUTE_TYPE); + if (m_future) + m_future->setProgressValue(1000); + m_progressStepMinimum = m_progressStepMaximum; + m_progressStepMaximum = 1100; + } else if (inReplyTo == COMPUTE_TYPE) { + m_cmakeServer->sendRequest(CODEMODEL_TYPE); + if (m_future) + m_future->setProgressValue(1100); + m_progressStepMinimum = m_progressStepMaximum; + m_progressStepMaximum = 1200; + } else if (inReplyTo == CODEMODEL_TYPE) { + extractCodeModelData(data); + m_cmakeServer->sendRequest(CMAKE_INPUTS_TYPE); + if (m_future) + m_future->setProgressValue(1200); + m_progressStepMinimum = m_progressStepMaximum; + m_progressStepMaximum = 1300; + } else if (inReplyTo == CMAKE_INPUTS_TYPE) { + extractCMakeInputsData(data); + m_cmakeServer->sendRequest(CACHE_TYPE); + if (m_future) + m_future->setProgressValue(1300); + m_progressStepMinimum = m_progressStepMaximum; + m_progressStepMaximum = 1400; + } else if (inReplyTo == CACHE_TYPE) { + extractCacheData(data); + if (m_future) { + m_future->setProgressValue(MAX_PROGRESS); + m_future->reportFinished(); + m_future.reset(); + } + m_hasData = true; + emit dataAvailable(); + } +} + +void ServerModeReader::handleError(const QString &message) +{ + TaskHub::addTask(Task::Error, message, ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM, + Utils::FileName(), -1); + stop(); +} + +void ServerModeReader::handleProgress(int min, int cur, int max, const QString &inReplyTo) +{ + Q_UNUSED(inReplyTo); + + if (!m_future) + return; + int progress = m_progressStepMinimum + + (((max - min) / (cur - min)) * (m_progressStepMaximum - m_progressStepMinimum)); + m_future->setProgressValue(progress); +} + +void ServerModeReader::extractCodeModelData(const QVariantMap &data) +{ + const QVariantList configs = data.value("configurations").toList(); + QTC_CHECK(configs.count() == 1); // FIXME: Support several configurations! + for (const QVariant &c : configs) { + const QVariantMap &cData = c.toMap(); + extractConfigurationData(cData); + } +} + +void ServerModeReader::extractConfigurationData(const QVariantMap &data) +{ + const QString name = data.value(NAME_KEY).toString(); + Q_UNUSED(name); + const QVariantList projects = data.value("projects").toList(); + for (const QVariant &p : projects) { + const QVariantMap pData = p.toMap(); + m_projects.append(extractProjectData(pData)); + } +} + +ServerModeReader::Project *ServerModeReader::extractProjectData(const QVariantMap &data) +{ + auto project = new Project; + project->name = data.value(NAME_KEY).toString(); + project->sourceDirectory = FileName::fromString(data.value(SOURCE_DIRECTORY_KEY).toString()); + + const QVariantList targets = data.value("targets").toList(); + for (const QVariant &t : targets) { + const QVariantMap tData = t.toMap(); + project->targets.append(extractTargetData(tData, project)); + } + return project; +} + +ServerModeReader::Target *ServerModeReader::extractTargetData(const QVariantMap &data, Project *p) +{ + auto target = new Target; + target->project = p; + target->name = data.value(NAME_KEY).toString(); + target->sourceDirectory = FileName::fromString(data.value(SOURCE_DIRECTORY_KEY).toString()); + target->buildDirectory = FileName::fromString(data.value("buildDirectory").toString()); + + QDir srcDir(target->sourceDirectory.toString()); + + target->type = data.value("type").toString(); + const QStringList artifacts = data.value("artifacts").toStringList(); + target->artifacts = transform(artifacts, [&srcDir](const QString &a) { return FileName::fromString(srcDir.absoluteFilePath(a)); }); + + const QVariantList fileGroups = data.value("fileGroups").toList(); + for (const QVariant &fg : fileGroups) { + const QVariantMap fgData = fg.toMap(); + target->fileGroups.append(extractFileGroupData(fgData, srcDir, target)); + } + + m_targets.append(target); + return target; +} + +ServerModeReader::FileGroup *ServerModeReader::extractFileGroupData(const QVariantMap &data, + const QDir &srcDir, + Target *t) +{ + auto fileGroup = new FileGroup; + fileGroup->target = t; + fileGroup->compileFlags = data.value("compileFlags").toString(); + fileGroup->defines = data.value("defines").toStringList(); + fileGroup->includePaths = transform(data.value("includePath").toList(), + [](const QVariant &i) -> IncludePath* { + const QVariantMap iData = i.toMap(); + auto result = new IncludePath; + result->path = FileName::fromString(iData.value("path").toString()); + result->isSystem = iData.value("isSystem", false).toBool(); + return result; + }); + fileGroup->isGenerated = data.value("isGenerated", false).toBool(); + fileGroup->sources = transform(data.value(SOURCES_KEY).toStringList(), + [&srcDir](const QString &s) { + return FileName::fromString(srcDir.absoluteFilePath(s)); + }); + + m_fileGroups.append(fileGroup); + return fileGroup; +} + +void ServerModeReader::extractCMakeInputsData(const QVariantMap &data) +{ + const FileName src = FileName::fromString(data.value(SOURCE_DIRECTORY_KEY).toString()); + QTC_ASSERT(src == m_parameters.sourceDirectory, return); + QDir srcDir(src.toString()); + + const QVariantList buildFiles = data.value("buildFiles").toList(); + for (const QVariant &bf : buildFiles) { + const QVariantMap §ion = bf.toMap(); + const QStringList sources = section.value(SOURCES_KEY).toStringList(); + + const bool isTemporary = section.value("isTemporary").toBool(); + const bool isCMake = section.value("isCMake").toBool(); + + for (const QString &s : sources) { + const FileName sfn = FileName::fromString(srcDir.absoluteFilePath(s)); + const int oldCount = m_cmakeFiles.count(); + m_cmakeFiles.insert(sfn); + if (!isCMake && oldCount < m_cmakeFiles.count()) + m_cmakeInputsFileNodes.append(new FileNode(sfn, FileType::Project, isTemporary)); + } + } +} + +void ServerModeReader::extractCacheData(const QVariantMap &data) +{ + CMakeConfig config; + const QVariantList entries = data.value("cache").toList(); + for (const QVariant &e : entries) { + const QVariantMap eData = e.toMap(); + CMakeConfigItem item; + item.key = eData.value("key").toByteArray(); + item.value = eData.value("value").toByteArray(); + item.type = CMakeConfigItem::typeStringToType(eData.value("type").toByteArray()); + const QVariantMap properties = eData.value("properties").toMap(); + item.isAdvanced = properties.value("ADVANCED", false).toBool(); + item.documentation = properties.value("HELPSTRING").toByteArray(); + item.values = CMakeConfigItem::cmakeSplitValue(properties.value("STRINGS").toString(), true); + config.append(item); + } + m_cmakeCache = config; +} + +} // namespace Internal +} // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/servermodereader.h b/src/plugins/cmakeprojectmanager/servermodereader.h new file mode 100644 index 0000000000..933542e71d --- /dev/null +++ b/src/plugins/cmakeprojectmanager/servermodereader.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "builddirreader.h" +#include "servermode.h" + +#include <utils/qtcprocess.h> + +#include <QSet> +#include <QTemporaryDir> +#include <QTimer> + +#include <memory> + +QT_FORWARD_DECLARE_CLASS(QLocalSocket); + +namespace Utils { class QtcProcess; } + +namespace CMakeProjectManager { +namespace Internal { + +class ServerModeReader : public BuildDirReader +{ + Q_OBJECT + +public: + ServerModeReader(); + ~ServerModeReader() final; + + void setParameters(const Parameters &p) final; + + bool isCompatible(const Parameters &p) final; + void resetData() final; + void parse(bool force) final; + void stop() final; + + bool isReady() const final; + bool isParsing() const final; + bool hasData() const final; + + QList<CMakeBuildTarget> buildTargets() const final; + CMakeConfig parsedConfiguration() const final; + void generateProjectTree(CMakeProjectNode *root, const QList<ProjectExplorer::FileNode *> &allFiles) final; + QSet<Core::Id> updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder) final; + +private: + void handleReply(const QVariantMap &data, const QString &inReplyTo); + void handleError(const QString &message); + void handleProgress(int min, int cur, int max, const QString &inReplyTo); + + struct Target; + struct Project; + + struct IncludePath { + Utils::FileName path; + bool isSystem; + }; + + struct FileGroup { + ~FileGroup() { qDeleteAll(includePaths); includePaths.clear(); } + + Target *target = nullptr; + QString compileFlags; + QStringList defines; + QList<IncludePath *> includePaths; + QString language; + QList<Utils::FileName> sources; + bool isGenerated; + }; + + struct Target { + ~Target() { qDeleteAll(fileGroups); fileGroups.clear(); } + + Project *project = nullptr; + QString name; + QString type; + QList<Utils::FileName> artifacts; + Utils::FileName sourceDirectory; + Utils::FileName buildDirectory; + QList<FileGroup *> fileGroups; + }; + + struct Project { + ~Project() { qDeleteAll(targets); targets.clear(); } + QString name; + Utils::FileName sourceDirectory; + QList<Target *> targets; + }; + + void extractCodeModelData(const QVariantMap &data); + void extractConfigurationData(const QVariantMap &data); + Project *extractProjectData(const QVariantMap &data); + Target *extractTargetData(const QVariantMap &data, Project *p); + FileGroup *extractFileGroupData(const QVariantMap &data, const QDir &srcDir, Target *t); + void extractCMakeInputsData(const QVariantMap &data); + void extractCacheData(const QVariantMap &data); + + bool m_hasData = false; + + std::unique_ptr<ServerMode> m_cmakeServer; + std::unique_ptr<QFutureInterface<void>> m_future; + + int m_progressStepMinimum; + int m_progressStepMaximum; + + CMakeConfig m_cmakeCache; + + QSet<Utils::FileName> m_cmakeFiles; + QList<ProjectExplorer::FileNode *> m_cmakeInputsFileNodes; + + QList<Project *> m_projects; + QList<Target *> m_targets; + QList<FileGroup *> m_fileGroups; +}; + +} // namespace Internal +} // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/tealeafreader.cpp b/src/plugins/cmakeprojectmanager/tealeafreader.cpp new file mode 100644 index 0000000000..9ed8373f52 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/tealeafreader.cpp @@ -0,0 +1,716 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "tealeafreader.h" + +#include "cmakebuildconfiguration.h" +#include "cmakecbpparser.h" +#include "cmakekitinformation.h" +#include "cmakeparser.h" +#include "cmakeprojectconstants.h" +#include "cmakeprojectmanager.h" +#include "cmakeprojectnodes.h" + +#include <coreplugin/documentmanager.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/idocument.h> +#include <coreplugin/messagemanager.h> +#include <coreplugin/progressmanager/progressmanager.h> +#include <coreplugin/reaper.h> +#include <cpptools/projectpartbuilder.h> +#include <projectexplorer/headerpath.h> +#include <projectexplorer/ioutputparser.h> +#include <projectexplorer/kitinformation.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/target.h> +#include <projectexplorer/task.h> +#include <projectexplorer/taskhub.h> +#include <projectexplorer/toolchain.h> +#include <projectexplorer/toolchainmanager.h> + +#include <utils/algorithm.h> +#include <utils/qtcassert.h> +#include <utils/qtcprocess.h> + +#include <QDateTime> +#include <QFileInfo> + +using namespace Core; +using namespace ProjectExplorer; +using namespace Utils; + +// -------------------------------------------------------------------- +// Helper: +// -------------------------------------------------------------------- + +namespace CMakeProjectManager { +namespace Internal { + +class CMakeFile : public IDocument +{ +public: + CMakeFile(TeaLeafReader *r, const FileName &fileName); + + ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const override; + bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override; + +private: + TeaLeafReader *m_reader; +}; + +CMakeFile::CMakeFile(TeaLeafReader *r, const FileName &fileName) : m_reader(r) +{ + setId("Cmake.ProjectFile"); + setMimeType(Constants::CMAKEPROJECTMIMETYPE); + setFilePath(fileName); +} + +IDocument::ReloadBehavior CMakeFile::reloadBehavior(ChangeTrigger state, ChangeType type) const +{ + Q_UNUSED(state) + Q_UNUSED(type) + return BehaviorSilent; +} + +bool CMakeFile::reload(QString *errorString, IDocument::ReloadFlag flag, IDocument::ChangeType type) +{ + Q_UNUSED(errorString); + Q_UNUSED(flag); + + if (type != TypePermissions) + emit m_reader->dirty(); + return true; +} + +static QString lineSplit(const QString &rest, const QByteArray &array, std::function<void(const QString &)> f) +{ + QString tmp = rest + SynchronousProcess::normalizeNewlines(QString::fromLocal8Bit(array)); + int start = 0; + int end = tmp.indexOf(QLatin1Char('\n'), start); + while (end >= 0) { + f(tmp.mid(start, end - start)); + start = end + 1; + end = tmp.indexOf(QLatin1Char('\n'), start); + } + return tmp.mid(start); +} + +static QStringList toArguments(const CMakeConfig &config, const MacroExpander *expander) { + return transform(config, [expander](const CMakeConfigItem &i) -> QString { + return i.toArgument(expander); + }); +} + +static QByteArray trimCMakeCacheLine(const QByteArray &in) { + int start = 0; + while (start < in.count() && (in.at(start) == ' ' || in.at(start) == '\t')) + ++start; + + return in.mid(start, in.count() - start - 1); +} + +static QByteArrayList splitCMakeCacheLine(const QByteArray &line) { + const int colonPos = line.indexOf(':'); + if (colonPos < 0) + return QByteArrayList(); + + const int equalPos = line.indexOf('=', colonPos + 1); + if (equalPos < colonPos) + return QByteArrayList(); + + return QByteArrayList() << line.mid(0, colonPos) + << line.mid(colonPos + 1, equalPos - colonPos - 1) + << line.mid(equalPos + 1); +} + +// -------------------------------------------------------------------- +// TeaLeafReader: +// -------------------------------------------------------------------- + +TeaLeafReader::TeaLeafReader() +{ + connect(EditorManager::instance(), &EditorManager::aboutToSave, + this, [this](const IDocument *document) { + if (m_cmakeFiles.contains(document->filePath()) || !m_parameters.isAutorun) + emit dirty(); + }); +} + +TeaLeafReader::~TeaLeafReader() +{ + stop(); + resetData(); +} + +bool TeaLeafReader::isCompatible(const BuildDirReader::Parameters &p) +{ + return !p.cmakeHasServerMode; +} + +void TeaLeafReader::resetData() +{ + m_hasData = false; + + qDeleteAll(m_watchedFiles); + m_watchedFiles.clear(); + + m_cmakeCache.clear(); + m_projectName.clear(); + m_buildTargets.clear(); + qDeleteAll(m_files); + m_files.clear(); +} + +void TeaLeafReader::parse(bool force) +{ + const QString cbpFile = CMakeManager::findCbpFile(QDir(m_parameters.buildDirectory.toString())); + const QFileInfo cbpFileFi = cbpFile.isEmpty() ? QFileInfo() : QFileInfo(cbpFile); + if (!cbpFileFi.exists()) { + // Initial create: + startCMake(toArguments(m_parameters.configuration, m_parameters.expander)); + return; + } + + const bool mustUpdate = force + || m_cmakeFiles.isEmpty() + || anyOf(m_cmakeFiles, [&cbpFileFi](const FileName &f) { + return f.toFileInfo().lastModified() > cbpFileFi.lastModified(); + }); + if (mustUpdate) { + startCMake(QStringList()); + } else { + extractData(); + m_hasData = true; + emit dataAvailable(); + } +} + +void TeaLeafReader::stop() +{ + cleanUpProcess(); + + if (m_future) { + m_future->reportCanceled(); + m_future->reportFinished(); + delete m_future; + m_future = nullptr; + } +} + +bool TeaLeafReader::isParsing() const +{ + return m_cmakeProcess && m_cmakeProcess->state() != QProcess::NotRunning; +} + +bool TeaLeafReader::hasData() const +{ + return m_hasData; +} + +QList<CMakeBuildTarget> TeaLeafReader::buildTargets() const +{ + return m_buildTargets; +} + +CMakeConfig TeaLeafReader::parsedConfiguration() const +{ + CMakeConfig result; + FileName cacheFile = m_parameters.buildDirectory; + cacheFile.appendPath(QLatin1String("CMakeCache.txt")); + if (!cacheFile.exists()) + return result; + QString errorMessage; + m_cmakeCache = parseConfiguration(cacheFile, &errorMessage); + if (!errorMessage.isEmpty()) + emit errorOccured(errorMessage); + const FileName sourceOfBuildDir + = FileName::fromUtf8(CMakeConfigItem::valueOf("CMAKE_HOME_DIRECTORY", m_cmakeCache)); + const FileName canonicalSourceOfBuildDir = FileUtils::canonicalPath(sourceOfBuildDir); + const FileName canonicalSourceDirectory = FileUtils::canonicalPath(m_parameters.sourceDirectory); + if (canonicalSourceOfBuildDir != canonicalSourceDirectory) { // Uses case-insensitive compare where appropriate + emit errorOccured(tr("The build directory is not for %1 but for %2") + .arg(canonicalSourceOfBuildDir.toUserOutput(), + canonicalSourceDirectory.toUserOutput())); + } + return result; +} + +CMakeConfig TeaLeafReader::parseConfiguration(const FileName &cacheFile, QString *errorMessage) const +{ + CMakeConfig result; + QFile cache(cacheFile.toString()); + if (!cache.open(QIODevice::ReadOnly | QIODevice::Text)) { + if (errorMessage) + *errorMessage = tr("Failed to open %1 for reading.").arg(cacheFile.toUserOutput()); + return CMakeConfig(); + } + + QSet<QByteArray> advancedSet; + QMap<QByteArray, QByteArray> valuesMap; + QByteArray documentation; + while (!cache.atEnd()) { + const QByteArray line = trimCMakeCacheLine(cache.readLine()); + + if (line.isEmpty() || line.startsWith('#')) + continue; + + if (line.startsWith("//")) { + documentation = line.mid(2); + continue; + } + + const QByteArrayList pieces = splitCMakeCacheLine(line); + if (pieces.isEmpty()) + continue; + + QTC_ASSERT(pieces.count() == 3, continue); + const QByteArray key = pieces.at(0); + const QByteArray type = pieces.at(1); + const QByteArray value = pieces.at(2); + + if (key.endsWith("-ADVANCED") && value == "1") { + advancedSet.insert(key.left(key.count() - 9 /* "-ADVANCED" */)); + } else if (key.endsWith("-STRINGS") && CMakeConfigItem::typeStringToType(type) == CMakeConfigItem::INTERNAL) { + valuesMap[key.left(key.count() - 8) /* "-STRINGS" */] = value; + } else { + CMakeConfigItem::Type t = CMakeConfigItem::typeStringToType(type); + result << CMakeConfigItem(key, t, documentation, value); + } + } + + // Set advanced flags: + for (int i = 0; i < result.count(); ++i) { + CMakeConfigItem &item = result[i]; + item.isAdvanced = advancedSet.contains(item.key); + + if (valuesMap.contains(item.key)) { + item.values = CMakeConfigItem::cmakeSplitValue(QString::fromUtf8(valuesMap[item.key])); + } else if (item.key == "CMAKE_BUILD_TYPE") { + // WA for known options + item.values << "" << "Debug" << "Release" << "MinSizeRel" << "RelWithDebInfo"; + } + } + + sort(result, CMakeConfigItem::sortOperator()); + + return result; +} + +void TeaLeafReader::generateProjectTree(CMakeProjectNode *root, const QList<FileNode *> &allFiles) +{ + root->setDisplayName(m_projectName); + + // Delete no longer necessary file watcher based on m_cmakeFiles: + const QSet<FileName> currentWatched + = transform(m_watchedFiles, [](CMakeFile *cmf) { return cmf->filePath(); }); + const QSet<FileName> toWatch = m_cmakeFiles; + QSet<FileName> toDelete = currentWatched; + toDelete.subtract(toWatch); + m_watchedFiles = filtered(m_watchedFiles, [&toDelete](Internal::CMakeFile *cmf) { + if (toDelete.contains(cmf->filePath())) { + delete cmf; + return false; + } + return true; + }); + + // Add new file watchers: + QSet<FileName> toAdd = toWatch; + toAdd.subtract(currentWatched); + foreach (const FileName &fn, toAdd) { + CMakeFile *cm = new CMakeFile(this, fn); + DocumentManager::addDocument(cm); + m_watchedFiles.insert(cm); + } + + QList<FileNode *> added; + QList<FileNode *> deleted; + + ProjectExplorer::compareSortedLists(m_files, allFiles, deleted, added, Node::sortByPath); + + QSet<FileName> allIncludePathSet; + for (const CMakeBuildTarget &bt : m_buildTargets) { + const QList<Utils::FileName> targetIncludePaths + = Utils::filtered(bt.includeFiles, [root](const Utils::FileName &fn) { + return fn.isChildOf(root->filePath()); + }); + allIncludePathSet.unite(QSet<FileName>::fromList(targetIncludePaths)); + } + const QList<FileName> allIncludePaths = allIncludePathSet.toList(); + + QList<FileNode *> includedHeaderFiles; + QList<FileNode *> unusedFileNodes; + std::tie(includedHeaderFiles, unusedFileNodes) + = Utils::partition(allFiles, [&allIncludePaths](const FileNode *fn) -> bool { + if (fn->fileType() != FileType::Header) + return false; + + for (const FileName &inc : allIncludePaths) { + if (fn->filePath().isChildOf(inc)) + return true; + } + return false; + }); + + const auto knownFiles = QSet<FileName>::fromList(Utils::transform(m_files, [](const FileNode *fn) { return fn->filePath(); })); + QList<FileNode *> uniqueHeaders; + foreach (FileNode *ifn, includedHeaderFiles) { + if (!knownFiles.contains(ifn->filePath())) { + uniqueHeaders.append(ifn); + ifn->setEnabled(false); + } + } + + QList<FileNode *> fileNodes = m_files + uniqueHeaders; + + // Filter out duplicate nodes that e.g. the servermode reader introduces: + QSet<FileName> uniqueFileNames; + QSet<Node *> uniqueNodes; + foreach (FileNode *fn, root->recursiveFileNodes()) { + const int count = uniqueFileNames.count(); + uniqueFileNames.insert(fn->filePath()); + if (count != uniqueFileNames.count()) + uniqueNodes.insert(static_cast<Node *>(fn)); + } + root->trim(uniqueNodes); + root->removeProjectNodes(root->projectNodes()); // Remove all project nodes + + root->buildTree(fileNodes, m_parameters.sourceDirectory); + m_files.clear(); // Some of the FileNodes in files() were deleted! +} + +QSet<Id> TeaLeafReader::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder) +{ + QSet<Id> languages; + const ToolChain *tc = ToolChainManager::findToolChain(m_parameters.toolChainId); + const FileName sysroot = m_parameters.sysRoot; + + QHash<QString, QStringList> targetDataCache; + foreach (const CMakeBuildTarget &cbt, m_buildTargets) { + if (cbt.targetType == UtilityType) + continue; + + // CMake shuffles the include paths that it reports via the CodeBlocks generator + // So remove the toolchain include paths, so that at least those end up in the correct + // place. + const QStringList cxxflags = getCXXFlagsFor(cbt, targetDataCache); + QSet<FileName> tcIncludes; + QStringList includePaths; + if (tc) { + foreach (const HeaderPath &hp, tc->systemHeaderPaths(cxxflags, sysroot)) + tcIncludes.insert(FileName::fromString(hp.path())); + foreach (const FileName &i, cbt.includeFiles) { + if (!tcIncludes.contains(i)) + includePaths.append(i.toString()); + } + } else { + includePaths = transform(cbt.includeFiles, &FileName::toString); + } + includePaths += m_parameters.buildDirectory.toString(); + ppBuilder.setIncludePaths(includePaths); + ppBuilder.setCFlags(cxxflags); + ppBuilder.setCxxFlags(cxxflags); + ppBuilder.setDefines(cbt.defines); + ppBuilder.setDisplayName(cbt.title); + + const QSet<Id> partLanguages + = QSet<Id>::fromList(ppBuilder.createProjectPartsForFiles( + transform(cbt.files, [](const FileName &fn) { return fn.toString(); }))); + + languages.unite(partLanguages); + } + return languages; + +} + +void TeaLeafReader::cleanUpProcess() +{ + if (m_cmakeProcess) { + m_cmakeProcess->disconnect(); + Reaper::reap(m_cmakeProcess); + m_cmakeProcess = nullptr; + } + + // Delete issue parser: + if (m_parser) + m_parser->flush(); + delete m_parser; + m_parser = nullptr; +} + +void TeaLeafReader::extractData() +{ + const FileName srcDir = m_parameters.sourceDirectory; + const FileName bldDir = m_parameters.buildDirectory; + const FileName topCMake = Utils::FileName(srcDir).appendPath("CMakeLists.txt"); + + resetData(); + + m_projectName = m_parameters.projectName; + m_files.append(new FileNode(topCMake, FileType::Project, false)); + // Do not insert topCMake into m_cmakeFiles: The project already watches that! + + // Find cbp file + FileName cbpFile = FileName::fromString(CMakeManager::findCbpFile(bldDir.toString())); + if (cbpFile.isEmpty()) + return; + m_cmakeFiles.insert(cbpFile); + + // Add CMakeCache.txt file: + FileName cacheFile = m_parameters.buildDirectory; + cacheFile.appendPath(QLatin1String("CMakeCache.txt")); + if (cacheFile.toFileInfo().exists()) + m_cmakeFiles.insert(cacheFile); + + // setFolderName + CMakeCbpParser cbpparser; + // Parsing + if (!cbpparser.parseCbpFile(m_parameters.pathMapper, cbpFile, srcDir)) + return; + + m_projectName = cbpparser.projectName(); + + m_files = cbpparser.fileList(); + if (cbpparser.hasCMakeFiles()) { + m_files.append(cbpparser.cmakeFileList()); + foreach (const FileNode *node, cbpparser.cmakeFileList()) + m_cmakeFiles.insert(node->filePath()); + } + + // Make sure the top cmakelists.txt file is always listed: + if (!contains(m_files, [topCMake](FileNode *fn) { return fn->filePath() == topCMake; })) + m_files.append(new FileNode(topCMake, FileType::Project, false)); + + Utils::sort(m_files, &Node::sortByPath); + + m_buildTargets = cbpparser.buildTargets(); +} + +void TeaLeafReader::startCMake(const QStringList &configurationArguments) +{ + const FileName buildDirectory = m_parameters.buildDirectory; + QTC_ASSERT(!m_cmakeProcess, return); + QTC_ASSERT(!m_parser, return); + QTC_ASSERT(!m_future, return); + QTC_ASSERT(buildDirectory.exists(), return); + + const QString srcDir = m_parameters.sourceDirectory.toString(); + + m_parser = new CMakeParser; + QDir source = QDir(srcDir); + connect(m_parser, &IOutputParser::addTask, m_parser, + [source](const Task &task) { + if (task.file.isEmpty() || task.file.toFileInfo().isAbsolute()) { + TaskHub::addTask(task); + } else { + Task t = task; + t.file = FileName::fromString(source.absoluteFilePath(task.file.toString())); + TaskHub::addTask(t); + } + }); + + // Always use the sourceDir: If we are triggered because the build directory is getting deleted + // then we are racing against CMakeCache.txt also getting deleted. + + m_cmakeProcess = new QtcProcess; + m_cmakeProcess->setWorkingDirectory(buildDirectory.toString()); + m_cmakeProcess->setEnvironment(m_parameters.environment); + + connect(m_cmakeProcess, &QProcess::readyReadStandardOutput, + this, &TeaLeafReader::processCMakeOutput); + connect(m_cmakeProcess, &QProcess::readyReadStandardError, + this, &TeaLeafReader::processCMakeError); + connect(m_cmakeProcess, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), + this, &TeaLeafReader::cmakeFinished); + + QString args; + QtcProcess::addArg(&args, srcDir); + QtcProcess::addArgs(&args, m_parameters.generatorArguments); + QtcProcess::addArgs(&args, configurationArguments); + + TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); + + MessageManager::write(tr("Running \"%1 %2\" in %3.") + .arg(m_parameters.cmakeExecutable.toUserOutput()) + .arg(args) + .arg(buildDirectory.toUserOutput())); + + m_future = new QFutureInterface<void>(); + m_future->setProgressRange(0, 1); + ProgressManager::addTask(m_future->future(), + tr("Configuring \"%1\"").arg(m_parameters.projectName), + "CMake.Configure"); + + m_cmakeProcess->setCommand(m_parameters.cmakeExecutable.toString(), args); + m_cmakeProcess->start(); + emit configurationStarted(); +} + +void TeaLeafReader::cmakeFinished(int code, QProcess::ExitStatus status) +{ + QTC_ASSERT(m_cmakeProcess, return); + + // process rest of the output: + processCMakeOutput(); + processCMakeError(); + + m_cmakeProcess->disconnect(); + cleanUpProcess(); + + extractData(); // try even if cmake failed... + + QString msg; + if (status != QProcess::NormalExit) + msg = tr("*** cmake process crashed."); + else if (code != 0) + msg = tr("*** cmake process exited with exit code %1.").arg(code); + + if (!msg.isEmpty()) { + MessageManager::write(msg); + TaskHub::addTask(Task::Error, msg, ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); + m_future->reportCanceled(); + } else { + m_future->setProgressValue(1); + } + + m_future->reportFinished(); + delete m_future; + m_future = nullptr; + + m_hasData = true; + emit dataAvailable(); +} + +void TeaLeafReader::processCMakeOutput() +{ + static QString rest; + rest = lineSplit(rest, m_cmakeProcess->readAllStandardOutput(), + [this](const QString &s) { MessageManager::write(s); }); +} + +void TeaLeafReader::processCMakeError() +{ + static QString rest; + rest = lineSplit(rest, m_cmakeProcess->readAllStandardError(), [this](const QString &s) { + m_parser->stdError(s); + MessageManager::write(s); + }); +} + +QStringList TeaLeafReader::getCXXFlagsFor(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache) +{ + // check cache: + auto it = cache.constFind(buildTarget.title); + if (it != cache.constEnd()) + return *it; + + if (extractCXXFlagsFromMake(buildTarget, cache)) + return cache.value(buildTarget.title); + + if (extractCXXFlagsFromNinja(buildTarget, cache)) + return cache.value(buildTarget.title); + + cache.insert(buildTarget.title, QStringList()); + return QStringList(); +} + +bool TeaLeafReader::extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache) +{ + QString makeCommand = buildTarget.makeCommand.toString(); + int startIndex = makeCommand.indexOf('\"'); + int endIndex = makeCommand.indexOf('\"', startIndex + 1); + if (startIndex != -1 && endIndex != -1) { + startIndex += 1; + QString makefile = makeCommand.mid(startIndex, endIndex - startIndex); + int slashIndex = makefile.lastIndexOf('/'); + makefile.truncate(slashIndex); + makefile.append("/CMakeFiles/" + buildTarget.title + ".dir/flags.make"); + QFile file(makefile); + if (file.exists()) { + file.open(QIODevice::ReadOnly | QIODevice::Text); + QTextStream stream(&file); + while (!stream.atEnd()) { + QString line = stream.readLine().trimmed(); + if (line.startsWith("CXX_FLAGS =")) { + // Skip past = + cache.insert(buildTarget.title, + line.mid(11).trimmed().split(' ', QString::SkipEmptyParts)); + return true; + } + } + } + } + return false; +} + +bool TeaLeafReader::extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache) +{ + Q_UNUSED(buildTarget) + if (!cache.isEmpty()) // We fill the cache in one go! + return false; + + // Attempt to find build.ninja file and obtain FLAGS (CXX_FLAGS) from there if no suitable flags.make were + // found + // Get "all" target's working directory + QByteArray ninjaFile; + QString buildNinjaFile = buildTargets().at(0).workingDirectory.toString(); + buildNinjaFile += "/build.ninja"; + QFile buildNinja(buildNinjaFile); + if (buildNinja.exists()) { + buildNinja.open(QIODevice::ReadOnly | QIODevice::Text); + ninjaFile = buildNinja.readAll(); + buildNinja.close(); + } + + if (ninjaFile.isEmpty()) + return false; + + QTextStream stream(ninjaFile); + bool cxxFound = false; + const QString targetSignature = "# Object build statements for "; + QString currentTarget; + + while (!stream.atEnd()) { + // 1. Look for a block that refers to the current target + // 2. Look for a build rule which invokes CXX_COMPILER + // 3. Return the FLAGS definition + QString line = stream.readLine().trimmed(); + if (line.startsWith('#')) { + if (line.startsWith(targetSignature)) { + int pos = line.lastIndexOf(' '); + currentTarget = line.mid(pos + 1); + } + } else if (!currentTarget.isEmpty() && line.startsWith("build")) { + cxxFound = line.indexOf("CXX_COMPILER") != -1; + } else if (cxxFound && line.startsWith("FLAGS =")) { + // Skip past = + cache.insert(currentTarget, line.mid(7).trimmed().split(' ', QString::SkipEmptyParts)); + } + } + return !cache.isEmpty(); +} + +} // namespace Internal +} // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/tealeafreader.h b/src/plugins/cmakeprojectmanager/tealeafreader.h new file mode 100644 index 0000000000..beecfa6b76 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/tealeafreader.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "builddirreader.h" + +namespace Utils { class QtcProcess; } + +namespace CMakeProjectManager { +namespace Internal { + +class CMakeFile; + +class TeaLeafReader : public BuildDirReader +{ + Q_OBJECT + +public: + TeaLeafReader(); + ~TeaLeafReader() final; + + bool isCompatible(const Parameters &p) final; + void resetData() final; + void parse(bool force) final; + void stop() final; + + bool isParsing() const final; + bool hasData() const final; + + QList<CMakeBuildTarget> buildTargets() const final; + CMakeConfig parsedConfiguration() const final; + void generateProjectTree(CMakeProjectNode *root, + const QList<ProjectExplorer::FileNode *> &allFiles) final; + QSet<Core::Id> updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder) final; + +private: + void cleanUpProcess(); + void extractData(); + + void startCMake(const QStringList &configurationArguments); + + void cmakeFinished(int code, QProcess::ExitStatus status); + void processCMakeOutput(); + void processCMakeError(); + + QStringList getCXXFlagsFor(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache); + bool extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache); + bool extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache); + + CMakeConfig parseConfiguration(const Utils::FileName &cacheFile, QString *errorMessage) const; + + Utils::QtcProcess *m_cmakeProcess = nullptr; + + // For error reporting: + ProjectExplorer::IOutputParser *m_parser = nullptr; + QFutureInterface<void> *m_future = nullptr; + + bool m_hasData = false; + + mutable CMakeConfig m_cmakeCache; + QSet<Utils::FileName> m_cmakeFiles; + QString m_projectName; + QList<CMakeBuildTarget> m_buildTargets; + QList<ProjectExplorer::FileNode *> m_files; + QSet<Internal::CMakeFile *> m_watchedFiles; + + friend class CMakeFile; +}; + +} // namespace Internal +} // namespace CMakeProjectManager diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp index 888abfbb5c..9423fce3bc 100644 --- a/src/plugins/coreplugin/coreplugin.cpp +++ b/src/plugins/coreplugin/coreplugin.cpp @@ -32,6 +32,7 @@ #include "modemanager.h" #include "infobar.h" #include "iwizardfactory.h" +#include "reaper_p.h" #include "themechooser.h" #include <coreplugin/actionmanager/actionmanager.h> diff --git a/src/plugins/coreplugin/coreplugin.h b/src/plugins/coreplugin/coreplugin.h index 62a11fb2e9..8322ba93bd 100644 --- a/src/plugins/coreplugin/coreplugin.h +++ b/src/plugins/coreplugin/coreplugin.h @@ -25,6 +25,8 @@ #pragma once +#include "reaper_p.h" + #include <extensionsystem/iplugin.h> QT_BEGIN_NAMESPACE @@ -84,6 +86,7 @@ private: EditMode *m_editMode; DesignMode *m_designMode; Locator *m_locator; + ReaperPrivate m_reaper; }; } // namespace Internal diff --git a/src/plugins/coreplugin/coreplugin.pro b/src/plugins/coreplugin/coreplugin.pro index 6de7837acf..035316e5d0 100644 --- a/src/plugins/coreplugin/coreplugin.pro +++ b/src/plugins/coreplugin/coreplugin.pro @@ -56,6 +56,7 @@ SOURCES += corejsextensions.cpp \ progressmanager/progressview.cpp \ progressmanager/progressbar.cpp \ progressmanager/futureprogress.cpp \ + reaper.cpp \ statusbarwidget.cpp \ coreplugin.cpp \ modemanager.cpp \ @@ -160,6 +161,8 @@ HEADERS += corejsextensions.h \ progressmanager/progressbar.h \ progressmanager/futureprogress.h \ progressmanager/progressmanager.h \ + reaper.h \ + reaper_p.h \ icontext.h \ icore.h \ infobar.h \ diff --git a/src/plugins/coreplugin/coreplugin.qbs b/src/plugins/coreplugin/coreplugin.qbs index fe6b3cb75e..e6ea7f8d3c 100644 --- a/src/plugins/coreplugin/coreplugin.qbs +++ b/src/plugins/coreplugin/coreplugin.qbs @@ -88,6 +88,7 @@ Project { "outputwindow.cpp", "outputwindow.h", "patchtool.cpp", "patchtool.h", "plugindialog.cpp", "plugindialog.h", + "reaper.cpp", "reaper.h", "reaper_p.h", "removefiledialog.cpp", "removefiledialog.h", "removefiledialog.ui", "rightpane.cpp", "rightpane.h", "settingsdatabase.cpp", "settingsdatabase.h", diff --git a/src/plugins/coreplugin/fileiconprovider.cpp b/src/plugins/coreplugin/fileiconprovider.cpp index 1d89aeef9a..b216b41248 100644 --- a/src/plugins/coreplugin/fileiconprovider.cpp +++ b/src/plugins/coreplugin/fileiconprovider.cpp @@ -74,6 +74,14 @@ public: QIcon icon(const QFileInfo &info); using QFileIconProvider::icon; + void registerIconOverlayForFilename(const QIcon &icon, const QString &filename) + { + QTC_ASSERT(!icon.isNull() && !filename.isEmpty(), return); + + const QPixmap fileIconPixmap = FileIconProvider::overlayIcon(QStyle::SP_FileIcon, icon, QSize(16, 16)); + m_filenameCache.insert(filename, fileIconPixmap); + } + void registerIconOverlayForSuffix(const QIcon &icon, const QString &suffix) { if (debug) @@ -83,7 +91,7 @@ public: const QPixmap fileIconPixmap = FileIconProvider::overlayIcon(QStyle::SP_FileIcon, icon, QSize(16, 16)); // replace old icon, if it exists - m_cache.insert(suffix, fileIconPixmap); + m_suffixCache.insert(suffix, fileIconPixmap); } void registerIconOverlayForMimeType(const QIcon &icon, const Utils::MimeType &mimeType) @@ -93,7 +101,8 @@ public: } // Mapping of file suffix to icon. - QHash<QString, QIcon> m_cache; + QHash<QString, QIcon> m_suffixCache; + QHash<QString, QIcon> m_filenameCache; QIcon m_unknownFileIcon; }; @@ -115,19 +124,27 @@ QIcon FileIconProviderImplementation::icon(const QFileInfo &fileInfo) qDebug() << "FileIconProvider::icon" << fileInfo.absoluteFilePath(); // Check for cached overlay icons by file suffix. bool isDir = fileInfo.isDir(); - QString suffix = !isDir ? fileInfo.suffix() : QString(); - if (!m_cache.isEmpty() && !isDir && !suffix.isEmpty()) { - if (m_cache.contains(suffix)) - return m_cache.value(suffix); + const QString filename = !isDir ? fileInfo.fileName() : QString(); + if (!filename.isEmpty()) { + auto it = m_filenameCache.constFind(filename); + if (it != m_filenameCache.constEnd()) + return it.value(); } - // Get icon from OS. + const QString suffix = !isDir ? fileInfo.suffix() : QString(); + if (!suffix.isEmpty()) { + auto it = m_suffixCache.constFind(suffix); + if (it != m_suffixCache.constEnd()) + return it.value(); + } + + // Get icon from OS (and cache it based on suffix!) QIcon icon; if (HostOsInfo::isWindowsHost() || HostOsInfo::isMacHost()) icon = QFileIconProvider::icon(fileInfo); else // File icons are unknown on linux systems. icon = isDir ? QFileIconProvider::icon(fileInfo) : m_unknownFileIcon; if (!isDir && !suffix.isEmpty()) - m_cache.insert(suffix, icon); + m_suffixCache.insert(suffix, icon); return icon; } @@ -174,29 +191,32 @@ QPixmap overlayIcon(QStyle::StandardPixmap baseIcon, const QIcon &overlay, const Registers an icon for a given suffix, overlaying the system file icon. See platform note in class documentation about recommended usage. */ -void registerIconOverlayForSuffix(const char *path, const char *suffix) +void registerIconOverlayForSuffix(const QString &path, const QString &suffix) { - instance()->registerIconOverlayForSuffix(QIcon(QLatin1String(path)), QLatin1String(suffix)); + instance()->registerIconOverlayForSuffix(QIcon(path), suffix); } /*! Registers an icon for all the suffixes of a given mime type, overlaying the system file icon. */ -void registerIconOverlayForMimeType(const QIcon &icon, const char *mimeType) +void registerIconOverlayForMimeType(const QIcon &icon, const QString &mimeType) { Utils::MimeDatabase mdb; - instance()->registerIconOverlayForMimeType(icon, - mdb.mimeTypeForName(QString::fromLatin1(mimeType))); + instance()->registerIconOverlayForMimeType(icon, mdb.mimeTypeForName(mimeType)); } /*! * \overload */ -void registerIconOverlayForMimeType(const char *path, const char *mimeType) +void registerIconOverlayForMimeType(const QString &path, const QString &mimeType) { Utils::MimeDatabase mdb; - instance()->registerIconOverlayForMimeType(QIcon(QLatin1String(path)), - mdb.mimeTypeForName(QString::fromLatin1(mimeType))); + instance()->registerIconOverlayForMimeType(QIcon(path), mdb.mimeTypeForName(mimeType)); +} + +void registerIconOverlayForFilename(const QString &path, const QString &filename) +{ + instance()->registerIconOverlayForFilename(QIcon(path), filename); } } // namespace FileIconProvider diff --git a/src/plugins/coreplugin/fileiconprovider.h b/src/plugins/coreplugin/fileiconprovider.h index 5262bd26bc..dc7f57c299 100644 --- a/src/plugins/coreplugin/fileiconprovider.h +++ b/src/plugins/coreplugin/fileiconprovider.h @@ -44,9 +44,10 @@ CORE_EXPORT QIcon icon(QFileIconProvider::IconType type); // Register additional overlay icons CORE_EXPORT QPixmap overlayIcon(const QPixmap &baseIcon, const QIcon &overlayIcon); CORE_EXPORT QPixmap overlayIcon(QStyle::StandardPixmap baseIcon, const QIcon &overlayIcon, const QSize &size); -CORE_EXPORT void registerIconOverlayForSuffix(const char *path, const char *suffix); -CORE_EXPORT void registerIconOverlayForMimeType(const char *path, const char *mimeType); -CORE_EXPORT void registerIconOverlayForMimeType(const QIcon &icon, const char *mimeType); +CORE_EXPORT void registerIconOverlayForSuffix(const QString &path, const QString &suffix); +CORE_EXPORT void registerIconOverlayForFilename(const QString &path, const QString &filename); +CORE_EXPORT void registerIconOverlayForMimeType(const QString &path, const QString &mimeType); +CORE_EXPORT void registerIconOverlayForMimeType(const QIcon &icon, const QString &mimeType); } // namespace FileIconProvider } // namespace Core diff --git a/src/plugins/coreplugin/find/findplugin.cpp b/src/plugins/coreplugin/find/findplugin.cpp index 0b3eb6962b..1a690e6679 100644 --- a/src/plugins/coreplugin/find/findplugin.cpp +++ b/src/plugins/coreplugin/find/findplugin.cpp @@ -386,11 +386,6 @@ QStringListModel *Find::replaceCompletionModel() return &(d->m_replaceCompletionModel); } -QKeySequence IFindFilter::defaultShortcut() const -{ - return QKeySequence(); -} - // declared in textfindconstants.h QTextDocument::FindFlags textDocumentFlagsForFindFlags(FindFlags flags) { diff --git a/src/plugins/coreplugin/find/ifindfilter.cpp b/src/plugins/coreplugin/find/ifindfilter.cpp index db1c712190..e080e249f0 100644 --- a/src/plugins/coreplugin/find/ifindfilter.cpp +++ b/src/plugins/coreplugin/find/ifindfilter.cpp @@ -28,6 +28,7 @@ #include <coreplugin/coreicons.h> #include <QApplication> +#include <QKeySequence> #include <QPainter> #include <QPixmap> @@ -218,6 +219,11 @@ namespace Core { +QKeySequence IFindFilter::defaultShortcut() const +{ + return QKeySequence(); +} + FindFlags IFindFilter::supportedFindFlags() const { return FindCaseSensitively diff --git a/src/plugins/coreplugin/iversioncontrol.h b/src/plugins/coreplugin/iversioncontrol.h index a78775bae3..eb880e7c70 100644 --- a/src/plugins/coreplugin/iversioncontrol.h +++ b/src/plugins/coreplugin/iversioncontrol.h @@ -93,6 +93,17 @@ public: virtual Id id() const = 0; /*! + * \brief isVcsFileOrDirectory + * \param fileName + * \return True if filename is a file or directory that is maintained by the + * version control system. + * + * 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). + */ + virtual bool isVcsFileOrDirectory(const Utils::FileName &fileName) const = 0; + + /*! * Returns whether files in this directory should be managed with this * version control. * If \a topLevel is non-null, it should return the topmost directory, @@ -232,6 +243,9 @@ public: { } ~TestVersionControl(); + bool isVcsFileOrDirectory(const Utils::FileName &fileName) const final + { Q_UNUSED(fileName); return false; } + void setManagedDirectories(const QHash<QString, QString> &dirs); void setManagedFiles(const QSet<QString> &files); diff --git a/src/plugins/coreplugin/reaper.cpp b/src/plugins/coreplugin/reaper.cpp new file mode 100644 index 0000000000..1cc9d12c0c --- /dev/null +++ b/src/plugins/coreplugin/reaper.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "reaper.h" +#include "reaper_p.h" + +#include <utils/algorithm.h> +#include <utils/qtcassert.h> + +#include <QThread> + +namespace Core { +namespace Internal { + +static ReaperPrivate *d = nullptr; + +ProcessReaper::ProcessReaper(QProcess *p, int timeoutMs) : m_process(p) +{ + d->m_reapers.append(this); + + m_iterationTimer.setInterval(timeoutMs); + m_iterationTimer.setSingleShot(true); + connect(&m_iterationTimer, &QTimer::timeout, this, &ProcessReaper::nextIteration); + + QTimer::singleShot(0, this, &ProcessReaper::nextIteration); + m_futureInterface.reportStarted(); +} + +ProcessReaper::~ProcessReaper() +{ + d->m_reapers.removeOne(this); +} + +int ProcessReaper::timeoutMs() const +{ + const int remaining = m_iterationTimer.remainingTime(); + if (remaining < 0) + return m_iterationTimer.interval(); + m_iterationTimer.stop(); + return remaining; +} + +bool ProcessReaper::isFinished() const +{ + return !m_process; +} + +void ProcessReaper::nextIteration() +{ + QProcess::ProcessState state = m_process ? m_process->state() : QProcess::NotRunning; + if (state == QProcess::NotRunning || m_emergencyCounter > 5) { + delete m_process; + m_process = nullptr; + m_futureInterface.reportFinished(); + return; + } + + if (state == QProcess::Starting) { + if (m_lastState == QProcess::Starting) + m_process->kill(); + } else if (state == QProcess::Running) { + if (m_lastState == QProcess::Running) + m_process->kill(); + else + m_process->terminate(); + } + + m_lastState = state; + m_iterationTimer.start(); + + ++m_emergencyCounter; +} + +ReaperPrivate::ReaperPrivate() +{ + d = this; +} + +ReaperPrivate::~ReaperPrivate() +{ + while (!m_reapers.isEmpty()) { + int alreadyWaited = 0; + QList<ProcessReaper *> toDelete; + + // push reapers along: + foreach (ProcessReaper *pr, m_reapers) { + const int timeoutMs = pr->timeoutMs(); + if (alreadyWaited < timeoutMs) { + const unsigned long toSleep = static_cast<unsigned long>(timeoutMs - alreadyWaited); + QThread::msleep(toSleep); + alreadyWaited += toSleep; + } + + pr->nextIteration(); + + if (pr->isFinished()) + toDelete.append(pr); + } + + // Clean out reapers that finished in the meantime + qDeleteAll(toDelete); + toDelete.clear(); + } + + d = nullptr; +} + +} // namespace Internal + +namespace Reaper { + +void reap(QProcess *process, int timeoutMs) +{ + if (!process) + return; + + QTC_ASSERT(Internal::d, return); + + new Internal::ProcessReaper(process, timeoutMs); +} + +} // namespace Reaper +} // namespace Core + diff --git a/src/plugins/cmakeprojectmanager/cmakefile.h b/src/plugins/coreplugin/reaper.h index 4f3376b640..7c2b2a356a 100644 --- a/src/plugins/cmakeprojectmanager/cmakefile.h +++ b/src/plugins/coreplugin/reaper.h @@ -25,24 +25,14 @@ #pragma once -#include <coreplugin/idocument.h> +#include "core_global.h" -namespace CMakeProjectManager { -namespace Internal { +QT_FORWARD_DECLARE_CLASS(QProcess); -class BuildDirManager; +namespace Core { +namespace Reaper { -class CMakeFile : public Core::IDocument -{ -public: - CMakeFile(BuildDirManager *bdm, const Utils::FileName &fileName); +CORE_EXPORT void reap(QProcess *p, int timeoutMs = 500); - ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const override; - bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override; - -private: - BuildDirManager *m_buildDirManager; -}; - -} // namespace Internal -} // namespace CMakeProjectManager +} // namespace Reaper +} // namespace Core diff --git a/src/plugins/cmakeprojectmanager/cmakefile.cpp b/src/plugins/coreplugin/reaper_p.h index 612e48ec8d..474dbc6e0c 100644 --- a/src/plugins/cmakeprojectmanager/cmakefile.cpp +++ b/src/plugins/coreplugin/reaper_p.h @@ -23,43 +23,49 @@ ** ****************************************************************************/ -#include "cmakefile.h" +#pragma once -#include "builddirmanager.h" -#include "cmakeprojectconstants.h" +#include <QObject> +#include <QFutureInterface> +#include <QProcess> +#include <QTimer> -#include <projectexplorer/target.h> +namespace Core { +namespace Internal { -#include <utils/fileutils.h> +class CorePlugin; -using namespace Utils; +class ProcessReaper : public QObject +{ + Q_OBJECT -namespace CMakeProjectManager { -namespace Internal { +public: + ProcessReaper(QProcess *p, int timeoutMs); + ~ProcessReaper(); -CMakeFile::CMakeFile(BuildDirManager *bdm, const FileName &fileName) : m_buildDirManager(bdm) -{ - setId("Cmake.ProjectFile"); - setMimeType(QLatin1String(Constants::CMAKEPROJECTMIMETYPE)); - setFilePath(fileName); -} + int timeoutMs() const; + bool isFinished() const; + void nextIteration(); -Core::IDocument::ReloadBehavior CMakeFile::reloadBehavior(ChangeTrigger state, ChangeType type) const -{ - Q_UNUSED(state) - Q_UNUSED(type) - return BehaviorSilent; -} +private: + mutable QTimer m_iterationTimer; + QFutureInterface<void> m_futureInterface; + QProcess *m_process; + int m_emergencyCounter = 0; + QProcess::ProcessState m_lastState = QProcess::NotRunning; +}; -bool CMakeFile::reload(QString *errorString, Core::IDocument::ReloadFlag flag, Core::IDocument::ChangeType type) -{ - Q_UNUSED(errorString); - Q_UNUSED(flag); +class ReaperPrivate { +public: + ~ReaperPrivate(); + + QList<ProcessReaper *> m_reapers; + +private: + ReaperPrivate(); - if (type != TypePermissions) - m_buildDirManager->handleCmakeFileChange(); - return true; -} + friend class CorePlugin; +}; } // namespace Internal -} // namespace CMakeProjectManager +} // namespace Core diff --git a/src/plugins/cvs/cvsclient.cpp b/src/plugins/cvs/cvsclient.cpp index 5f85889092..bd6052a4ee 100644 --- a/src/plugins/cvs/cvsclient.cpp +++ b/src/plugins/cvs/cvsclient.cpp @@ -29,7 +29,7 @@ #include <vcsbase/vcsbaseplugin.h> #include <vcsbase/vcsbaseeditor.h> #include <vcsbase/vcsbaseconstants.h> -#include <vcsbase/vcsbaseeditorparameterwidget.h> +#include <vcsbase/vcsbaseeditorconfig.h> #include <QDir> #include <QFileInfo> @@ -43,20 +43,19 @@ namespace Cvs { namespace Internal { // Parameter widget controlling whitespace diff mode, associated with a parameter -class CvsDiffParameterWidget : public VcsBaseEditorParameterWidget +class CvsDiffConfig : public VcsBaseEditorConfig { Q_OBJECT public: - explicit CvsDiffParameterWidget(VcsBaseClientSettings &settings, QWidget *parent = 0); + CvsDiffConfig(VcsBaseClientSettings &settings, QToolBar *toolBar); QStringList arguments() const; private: VcsBaseClientSettings &m_settings; }; -CvsDiffParameterWidget::CvsDiffParameterWidget(VcsBaseClientSettings &settings, - QWidget *parent) : - VcsBaseEditorParameterWidget(parent), +CvsDiffConfig::CvsDiffConfig(VcsBaseClientSettings &settings, QToolBar *toolBar) : + VcsBaseEditorConfig(toolBar), m_settings(settings) { mapSetting(addToggleButton(QLatin1String("-w"), tr("Ignore Whitespace")), @@ -65,18 +64,20 @@ CvsDiffParameterWidget::CvsDiffParameterWidget(VcsBaseClientSettings &settings, settings.boolPointer(CvsSettings::diffIgnoreBlankLinesKey)); } -QStringList CvsDiffParameterWidget::arguments() const +QStringList CvsDiffConfig::arguments() const { QStringList args; args = m_settings.stringValue(CvsSettings::diffOptionsKey).split(QLatin1Char(' '), QString::SkipEmptyParts); - args += VcsBaseEditorParameterWidget::arguments(); + args += VcsBaseEditorConfig::arguments(); return args; } CvsClient::CvsClient() : VcsBaseClient(new CvsSettings) { - setDiffParameterWidgetCreator([this] { return new CvsDiffParameterWidget(settings()); }); + setDiffConfigCreator([this](QToolBar *toolBar) { + return new CvsDiffConfig(settings(), toolBar); + }); } CvsSettings &CvsClient::settings() const diff --git a/src/plugins/cvs/cvscontrol.cpp b/src/plugins/cvs/cvscontrol.cpp index c277557d16..d9bf55a86e 100644 --- a/src/plugins/cvs/cvscontrol.cpp +++ b/src/plugins/cvs/cvscontrol.cpp @@ -33,6 +33,7 @@ #include <vcsbase/vcscommand.h> #include <utils/fileutils.h> +#include <utils/hostosinfo.h> #include <utils/qtcassert.h> #include <QFileInfo> @@ -54,6 +55,12 @@ Core::Id CvsControl::id() const return Core::Id(VcsBase::Constants::VCS_ID_CVS); } +bool CvsControl::isVcsFileOrDirectory(const Utils::FileName &fileName) const +{ + return fileName.toFileInfo().isDir() + && !fileName.fileName().compare("CVS", Utils::HostOsInfo::fileNameCaseSensitivity()); +} + bool CvsControl::isConfigured() const { const Utils::FileName binary = m_plugin->client()->vcsBinary(); diff --git a/src/plugins/cvs/cvscontrol.h b/src/plugins/cvs/cvscontrol.h index ea9d94ccf7..1bc093b788 100644 --- a/src/plugins/cvs/cvscontrol.h +++ b/src/plugins/cvs/cvscontrol.h @@ -39,28 +39,30 @@ class CvsControl : public Core::IVersionControl public: explicit CvsControl(CvsPlugin *plugin); - QString displayName() const override; - Core::Id id() const override; + QString displayName() const final; + Core::Id id() const final; - bool managesDirectory(const QString &directory, QString *topLevel = 0) const override; - bool managesFile(const QString &workingDirectory, const QString &fileName) const override; + bool isVcsFileOrDirectory(const Utils::FileName &fileName) const final; - bool isConfigured() const override; - bool supportsOperation(Operation operation) const override; - OpenSupportMode openSupportMode(const QString &fileName) const override; - bool vcsOpen(const QString &fileName) override; - bool vcsAdd(const QString &fileName) override; - bool vcsDelete(const QString &filename) override; - bool vcsMove(const QString &from, const QString &to) override; - bool vcsCreateRepository(const QString &directory) override; - bool vcsAnnotate(const QString &file, int line) override; + bool managesDirectory(const QString &directory, QString *topLevel = 0) const final; + bool managesFile(const QString &workingDirectory, const QString &fileName) const final; - QString vcsOpenText() const override; + bool isConfigured() const final; + bool supportsOperation(Operation operation) const final; + OpenSupportMode openSupportMode(const QString &fileName) const final; + bool vcsOpen(const QString &fileName) final; + bool vcsAdd(const QString &fileName) final; + bool vcsDelete(const QString &filename) final; + bool vcsMove(const QString &from, const QString &to) final; + bool vcsCreateRepository(const QString &directory) final; + bool vcsAnnotate(const QString &file, int line) final; + + QString vcsOpenText() const final; Core::ShellCommand *createInitialCheckoutCommand(const QString &url, const Utils::FileName &baseDirectory, const QString &localName, - const QStringList &extraArgs) override; + const QStringList &extraArgs) final; void emitRepositoryChanged(const QString &s); void emitFilesChanged(const QStringList &l); diff --git a/src/plugins/cvs/cvsplugin.cpp b/src/plugins/cvs/cvsplugin.cpp index 336a45f6a8..06c25a4270 100644 --- a/src/plugins/cvs/cvsplugin.cpp +++ b/src/plugins/cvs/cvsplugin.cpp @@ -34,7 +34,6 @@ #include <vcsbase/basevcssubmiteditorfactory.h> #include <vcsbase/vcsbaseconstants.h> #include <vcsbase/vcsbaseeditor.h> -#include <vcsbase/vcsbaseeditorparameterwidget.h> #include <vcsbase/vcscommand.h> #include <vcsbase/vcsoutputwindow.h> diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 49c2f0633a..f23da12034 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -1880,7 +1880,6 @@ void DebuggerEngine::validateExecutable(DebuggerRunParameters *sp) switch (sp->toolChainAbi.binaryFormat()) { case Abi::PEFormat: { if (sp->masterEngineType != CdbEngineType) { - warnOnInappropriateDebugger = true; detailedWarning = tr( "The inferior is in the Portable Executable format.\n" "Selecting CDB as debugger would improve the debugging " @@ -1904,7 +1903,6 @@ void DebuggerEngine::validateExecutable(DebuggerRunParameters *sp) } case Abi::ElfFormat: { if (sp->masterEngineType == CdbEngineType) { - warnOnInappropriateDebugger = true; detailedWarning = tr( "The inferior is in the ELF format.\n" "Selecting GDB or LLDB as debugger would improve the debugging " diff --git a/src/plugins/designer/resourcehandler.cpp b/src/plugins/designer/resourcehandler.cpp index 8f8d791969..32e86796cc 100644 --- a/src/plugins/designer/resourcehandler.cpp +++ b/src/plugins/designer/resourcehandler.cpp @@ -68,7 +68,7 @@ void QrcFilesVisitor::visitProjectNode(ProjectNode *projectNode) void QrcFilesVisitor::visitFolderNode(FolderNode *folderNode) { foreach (const FileNode *fileNode, folderNode->fileNodes()) { - if (fileNode->fileType() == ResourceType) + if (fileNode->fileType() == FileType::Resource) m_qrcFiles.append(fileNode->filePath().toString()); } if (dynamic_cast<ResourceEditor::ResourceTopLevelNode *>(folderNode)) diff --git a/src/plugins/genericprojectmanager/genericproject.cpp b/src/plugins/genericprojectmanager/genericproject.cpp index e33d20dd94..264b2e8b24 100644 --- a/src/plugins/genericprojectmanager/genericproject.cpp +++ b/src/plugins/genericprojectmanager/genericproject.cpp @@ -92,15 +92,15 @@ GenericProject::GenericProject(Manager *manager, const QString &fileName) DocumentManager::addDocument(m_configIDocument); FileNode *projectFilesNode = new FileNode(Utils::FileName::fromString(m_filesFileName), - ProjectFileType, + FileType::Project, /* generated = */ false); FileNode *projectIncludesNode = new FileNode(Utils::FileName::fromString(m_includesFileName), - ProjectFileType, + FileType::Project, /* generated = */ false); FileNode *projectConfigNode = new FileNode(Utils::FileName::fromString(m_configFileName), - ProjectFileType, + FileType::Project, /* generated = */ false); rootProjectNode()->addFileNodes(QList<FileNode *>() << projectFilesNode @@ -277,9 +277,9 @@ void GenericProject::refresh(RefreshOptions options) if (options & Files) { QList<FileNode *> fileNodes = Utils::transform(files(), [](const QString &f) { - FileType fileType = SourceType; // ### FIXME + FileType fileType = FileType::Source; // ### FIXME if (f.endsWith(QLatin1String(".qrc"))) - fileType = ResourceType; + fileType = FileType::Resource; return new FileNode(Utils::FileName::fromString(f), fileType, false); }); rootProjectNode()->buildTree(fileNodes); diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index 41078392a7..3ab1733a05 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -54,7 +54,7 @@ #include <vcsbase/submitfilemodel.h> #include <vcsbase/vcsbaseeditor.h> -#include <vcsbase/vcsbaseeditorparameterwidget.h> +#include <vcsbase/vcsbaseeditorconfig.h> #include <vcsbase/vcsbaseplugin.h> #include <vcsbase/vcscommand.h> #include <vcsbase/vcsoutputwindow.h> @@ -389,13 +389,13 @@ void ShowController::reloadFinished(bool success) /////////////////////////////// -class BaseGitDiffArgumentsWidget : public VcsBaseEditorParameterWidget +class BaseGitDiffArgumentsWidget : public VcsBaseEditorConfig { Q_OBJECT public: - BaseGitDiffArgumentsWidget(VcsBaseClientSettings &settings, QWidget *parent = nullptr) : - VcsBaseEditorParameterWidget(parent) + BaseGitDiffArgumentsWidget(VcsBaseClientSettings &settings, QToolBar *toolBar) : + VcsBaseEditorConfig(toolBar) { m_patienceButton = addToggleButton("--patience", tr("Patience"), @@ -408,17 +408,17 @@ public: } protected: - QToolButton *m_patienceButton; - QToolButton *m_ignoreWSButton; + QAction *m_patienceButton; + QAction *m_ignoreWSButton; }; -class GitBlameArgumentsWidget : public VcsBaseEditorParameterWidget +class GitBlameArgumentsWidget : public VcsBaseEditorConfig { Q_OBJECT public: - GitBlameArgumentsWidget(VcsBaseClientSettings &settings, QWidget *parent = nullptr) : - VcsBaseEditorParameterWidget(parent) + GitBlameArgumentsWidget(VcsBaseClientSettings &settings, QToolBar *toolBar) : + VcsBaseEditorConfig(toolBar) { mapSetting(addToggleButton(QString(), tr("Omit Date"), tr("Hide the date of a change from the output.")), @@ -434,22 +434,27 @@ class GitLogArgumentsWidget : public BaseGitDiffArgumentsWidget Q_OBJECT public: - GitLogArgumentsWidget(VcsBaseClientSettings &settings, QWidget *parent = nullptr) : - BaseGitDiffArgumentsWidget(settings, parent) + GitLogArgumentsWidget(VcsBaseClientSettings &settings, QToolBar *toolBar = nullptr) : + BaseGitDiffArgumentsWidget(settings, toolBar) { - QToolButton *diffButton = addToggleButton("--patch", tr("Show Diff"), + QAction *diffButton = addToggleButton("--patch", tr("Show Diff"), tr("Show difference.")); mapSetting(diffButton, settings.boolPointer(GitSettings::logDiffKey)); - connect(diffButton, &QToolButton::toggled, m_patienceButton, &QToolButton::setVisible); - connect(diffButton, &QToolButton::toggled, m_ignoreWSButton, &QToolButton::setVisible); + connect(diffButton, &QAction::toggled, m_patienceButton, &QAction::setVisible); + connect(diffButton, &QAction::toggled, m_ignoreWSButton, &QAction::setVisible); m_patienceButton->setVisible(diffButton->isChecked()); m_ignoreWSButton->setVisible(diffButton->isChecked()); + QAction *firstParentButton = + addToggleButton({ "-m", "--first-parent" }, + tr("First Parent"), + tr("Follow only the first parent on merge commits.")); + mapSetting(firstParentButton, settings.boolPointer(GitSettings::firstParentKey)); const QStringList graphArguments = { "--graph", "--oneline", "--topo-order", QLatin1String("--pretty=format:") + graphLogFormatC }; - QToolButton *graphButton = addToggleButton(graphArguments, tr("Graph"), - tr("Show textual graph log.")); + QAction *graphButton = addToggleButton(graphArguments, tr("Graph"), + tr("Show textual graph log.")); mapSetting(graphButton, settings.boolPointer(GitSettings::graphLogKey)); } }; @@ -813,7 +818,7 @@ void GitClient::log(const QString &workingDirectory, const QString &fileName, QString msgArg; if (!fileName.isEmpty()) msgArg = fileName; - else if (!args.isEmpty()) + else if (!args.isEmpty() && !args.first().startsWith('-')) msgArg = args.first(); else msgArg = workingDirectory; @@ -824,11 +829,14 @@ void GitClient::log(const QString &workingDirectory, const QString &fileName, const QString sourceFile = VcsBaseEditor::getSource(workingDir, fileName); VcsBaseEditorWidget *editor = createVcsEditor(editorId, title, sourceFile, codecFor(CodecLogOutput), "logTitle", msgArg); - if (!editor->configurationWidget()) { - auto *argWidget = new GitLogArgumentsWidget(settings()); - connect(argWidget, &VcsBaseEditorParameterWidget::commandExecutionRequested, - [=]() { this->log(workingDir, fileName, enableAnnotationContextMenu, args); }); - editor->setConfigurationWidget(argWidget); + QStringList effectiveArgs = args; + if (!editor->configurationAdded()) { + auto *argWidget = new GitLogArgumentsWidget(settings(), editor->toolBar()); + argWidget->setBaseArguments(args); + connect(argWidget, &VcsBaseEditorConfig::commandExecutionRequested, + [=]() { this->log(workingDir, fileName, enableAnnotationContextMenu, argWidget->arguments()); }); + effectiveArgs = argWidget->arguments(); + editor->setConfigurationAdded(); } editor->setFileLogAnnotateEnabled(enableAnnotationContextMenu); editor->setWorkingDirectory(workingDir); @@ -838,11 +846,7 @@ void GitClient::log(const QString &workingDirectory, const QString &fileName, if (logCount > 0) arguments << "-n" << QString::number(logCount); - auto *argWidget = editor->configurationWidget(); - argWidget->setBaseArguments(args); - QStringList userArgs = argWidget->arguments(); - - arguments.append(userArgs); + arguments.append(effectiveArgs); if (!fileName.isEmpty()) arguments << "--follow" << "--" << fileName; @@ -911,21 +915,22 @@ VcsBaseEditorWidget *GitClient::annotate( VcsBaseEditorWidget *editor = createVcsEditor(editorId, title, sourceFile, codecFor(CodecSource, sourceFile), "blameFileName", id); - if (!editor->configurationWidget()) { - auto *argWidget = new GitBlameArgumentsWidget(settings()); + QStringList effectiveArgs = extraOptions; + if (!editor->configurationAdded()) { + auto *argWidget = new GitBlameArgumentsWidget(settings(), editor->toolBar()); argWidget->setBaseArguments(extraOptions); - connect(argWidget, &VcsBaseEditorParameterWidget::commandExecutionRequested, + connect(argWidget, &VcsBaseEditorConfig::commandExecutionRequested, [=] { const int line = VcsBaseEditor::lineNumberOfCurrentEditor(); - annotate(workingDir, file, revision, line, extraOptions); + annotate(workingDir, file, revision, line, argWidget->arguments()); } ); - editor->setConfigurationWidget(argWidget); + effectiveArgs = argWidget->arguments(); + editor->setConfigurationAdded(); } editor->setWorkingDirectory(workingDir); QStringList arguments = { "blame", "--root" }; - arguments << editor->configurationWidget()->arguments(); - arguments << "--" << file; + arguments << effectiveArgs << "--" << file; if (!revision.isEmpty()) arguments << revision; vcsExec(workingDir, arguments, editor, false, 0, lineNumber); diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index 869ed36622..5261d5afb8 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -50,7 +50,6 @@ namespace VcsBase { class VcsCommand; class SubmitFileModel; class VcsBaseEditorWidget; - class VcsBaseEditorParameterWidget; } namespace DiffEditor { diff --git a/src/plugins/git/gitsettings.cpp b/src/plugins/git/gitsettings.cpp index 337db12627..ce9bb5589b 100644 --- a/src/plugins/git/gitsettings.cpp +++ b/src/plugins/git/gitsettings.cpp @@ -44,6 +44,7 @@ const QLatin1String GitSettings::gitkOptionsKey("GitKOptions"); const QLatin1String GitSettings::logDiffKey("LogDiff"); const QLatin1String GitSettings::repositoryBrowserCmd("RepositoryBrowserCmd"); const QLatin1String GitSettings::graphLogKey("GraphLog"); +const QLatin1String GitSettings::firstParentKey("FirstParent"); const QLatin1String GitSettings::lastResetIndexKey("LastResetIndex"); GitSettings::GitSettings() @@ -64,6 +65,7 @@ GitSettings::GitSettings() declareKey(logDiffKey, false); declareKey(repositoryBrowserCmd, QString()); declareKey(graphLogKey, false); + declareKey(firstParentKey, false); declareKey(lastResetIndexKey, 0); } diff --git a/src/plugins/git/gitsettings.h b/src/plugins/git/gitsettings.h index c2f2281721..ec31d2ed94 100644 --- a/src/plugins/git/gitsettings.h +++ b/src/plugins/git/gitsettings.h @@ -55,6 +55,7 @@ public: static const QLatin1String logDiffKey; static const QLatin1String repositoryBrowserCmd; static const QLatin1String graphLogKey; + static const QLatin1String firstParentKey; static const QLatin1String lastResetIndexKey; Utils::FileName gitExecutable(bool *ok = 0, QString *errorMessage = 0) const; diff --git a/src/plugins/git/gitversioncontrol.cpp b/src/plugins/git/gitversioncontrol.cpp index b1ba613911..564c32d3b9 100644 --- a/src/plugins/git/gitversioncontrol.cpp +++ b/src/plugins/git/gitversioncontrol.cpp @@ -30,6 +30,8 @@ #include <vcsbase/vcsbaseconstants.h> #include <vcsbase/vcscommand.h> +#include <utils/hostosinfo.h> + #include <QFileInfo> #include <QProcessEnvironment> @@ -74,6 +76,12 @@ Core::Id GitVersionControl::id() const return Core::Id(VcsBase::Constants::VCS_ID_GIT); } +bool GitVersionControl::isVcsFileOrDirectory(const Utils::FileName &fileName) const +{ + return fileName.toFileInfo().isDir() + && !fileName.fileName().compare(".git", Utils::HostOsInfo::fileNameCaseSensitivity()); +} + bool GitVersionControl::isConfigured() const { return !m_client->vcsBinary().isEmpty(); diff --git a/src/plugins/git/gitversioncontrol.h b/src/plugins/git/gitversioncontrol.h index b46ee90484..2df3c7c856 100644 --- a/src/plugins/git/gitversioncontrol.h +++ b/src/plugins/git/gitversioncontrol.h @@ -39,29 +39,31 @@ class GitVersionControl : public Core::IVersionControl public: explicit GitVersionControl(GitClient *client); - QString displayName() const override; - Core::Id id() const override; + QString displayName() const final; + Core::Id id() const final; - bool managesDirectory(const QString &directory, QString *topLevel) const override; - bool managesFile(const QString &workingDirectory, const QString &fileName) const override; + bool isVcsFileOrDirectory(const Utils::FileName &fileName) const final; - bool isConfigured() const override; - bool supportsOperation(Operation operation) const override; - bool vcsOpen(const QString &fileName) override; - bool vcsAdd(const QString &fileName) override; - bool vcsDelete(const QString &filename) override; - bool vcsMove(const QString &from, const QString &to) override; - bool vcsCreateRepository(const QString &directory) override; + bool managesDirectory(const QString &directory, QString *topLevel) const final; + bool managesFile(const QString &workingDirectory, const QString &fileName) const final; - bool vcsAnnotate(const QString &file, int line) override; - QString vcsTopic(const QString &directory) override; + bool isConfigured() const final; + bool supportsOperation(Operation operation) const final; + bool vcsOpen(const QString &fileName) final; + bool vcsAdd(const QString &fileName) final; + bool vcsDelete(const QString &filename) final; + bool vcsMove(const QString &from, const QString &to) final; + bool vcsCreateRepository(const QString &directory) final; + + bool vcsAnnotate(const QString &file, int line) final; + QString vcsTopic(const QString &directory) final; Core::ShellCommand *createInitialCheckoutCommand(const QString &url, const Utils::FileName &baseDirectory, const QString &localName, - const QStringList &extraArgs) override; + const QStringList &extraArgs) final; - QStringList additionalToolsPath() const override; + QStringList additionalToolsPath() const final; void emitFilesChanged(const QStringList &); void emitRepositoryChanged(const QString &); diff --git a/src/plugins/mercurial/mercurialclient.cpp b/src/plugins/mercurial/mercurialclient.cpp index 21149bce98..3efc8a3352 100644 --- a/src/plugins/mercurial/mercurialclient.cpp +++ b/src/plugins/mercurial/mercurialclient.cpp @@ -30,9 +30,10 @@ #include <vcsbase/vcsoutputwindow.h> #include <vcsbase/vcsbaseplugin.h> #include <vcsbase/vcsbaseeditor.h> -#include <vcsbase/vcsbaseeditorparameterwidget.h> +#include <vcsbase/vcsbaseeditorconfig.h> #include <utils/synchronousprocess.h> #include <utils/fileutils.h> +#include <utils/hostosinfo.h> #include <utils/qtcassert.h> #include <QDateTime> @@ -49,12 +50,12 @@ namespace Mercurial { namespace Internal { // Parameter widget controlling whitespace diff mode, associated with a parameter -class MercurialDiffParameterWidget : public VcsBaseEditorParameterWidget +class MercurialDiffConfig : public VcsBaseEditorConfig { Q_OBJECT public: - MercurialDiffParameterWidget(VcsBaseClientSettings &settings, QWidget *parent = 0) : - VcsBaseEditorParameterWidget(parent) + MercurialDiffConfig(VcsBaseClientSettings &settings, QToolBar *toolBar) : + VcsBaseEditorConfig(toolBar) { mapSetting(addToggleButton(QLatin1String("-w"), tr("Ignore Whitespace")), settings.boolPointer(MercurialSettings::diffIgnoreWhiteSpaceKey)); @@ -65,7 +66,9 @@ public: MercurialClient::MercurialClient() : VcsBaseClient(new MercurialSettings) { - setDiffParameterWidgetCreator([this] { return new MercurialDiffParameterWidget(settings()); }); + setDiffConfigCreator([this](QToolBar *toolBar) { + return new MercurialDiffConfig(settings(), toolBar); + }); } bool MercurialClient::manifestSync(const QString &repository, const QString &relativeFilename) @@ -333,6 +336,12 @@ void MercurialClient::revertAll(const QString &workingDir, const QString &revisi QStringList(extraOptions) << QLatin1String("--all")); } +bool MercurialClient::isVcsDirectory(const FileName &fileName) const +{ + return fileName.toFileInfo().isDir() + && !fileName.fileName().compare(Constants::MERCURIALREPO, HostOsInfo::fileNameCaseSensitivity()); +} + void MercurialClient::view(const QString &source, const QString &id, const QStringList &extraOptions) { diff --git a/src/plugins/mercurial/mercurialclient.h b/src/plugins/mercurial/mercurialclient.h index f1dfce97ae..2692b4bfd6 100644 --- a/src/plugins/mercurial/mercurialclient.h +++ b/src/plugins/mercurial/mercurialclient.h @@ -70,6 +70,7 @@ public: void revertAll(const QString &workingDir, const QString &revision = QString(), const QStringList &extraOptions = QStringList()) override; + bool isVcsDirectory(const Utils::FileName &fileName) const; QString findTopLevelForFile(const QFileInfo &file) const override; public slots: diff --git a/src/plugins/mercurial/mercurialcontrol.cpp b/src/plugins/mercurial/mercurialcontrol.cpp index 3c3a42bccf..b60fd3e419 100644 --- a/src/plugins/mercurial/mercurialcontrol.cpp +++ b/src/plugins/mercurial/mercurialcontrol.cpp @@ -78,6 +78,11 @@ Core::Id MercurialControl::id() const return Core::Id(VcsBase::Constants::VCS_ID_MERCURIAL); } +bool MercurialControl::isVcsFileOrDirectory(const Utils::FileName &fileName) const +{ + return mercurialClient->isVcsDirectory(fileName); +} + bool MercurialControl::managesDirectory(const QString &directory, QString *topLevel) const { QFileInfo dir(directory); diff --git a/src/plugins/mercurial/mercurialcontrol.h b/src/plugins/mercurial/mercurialcontrol.h index f9cf123fe2..39917063ac 100644 --- a/src/plugins/mercurial/mercurialcontrol.h +++ b/src/plugins/mercurial/mercurialcontrol.h @@ -45,23 +45,25 @@ class MercurialControl : public Core::IVersionControl public: explicit MercurialControl(MercurialClient *mercurialClient); - QString displayName() const override; - Core::Id id() const override; - bool managesDirectory(const QString &filename, QString *topLevel = 0) const override; - bool managesFile(const QString &workingDirectory, const QString &fileName) const override; - bool isConfigured() const override; - bool supportsOperation(Operation operation) const override; - bool vcsOpen(const QString &fileName) override; - bool vcsAdd(const QString &filename) override; - bool vcsDelete(const QString &filename) override; - bool vcsMove(const QString &from, const QString &to) override; - bool vcsCreateRepository(const QString &directory) override; - bool vcsAnnotate(const QString &file, int line) override; + QString displayName() const final; + Core::Id id() const final; + bool isVcsFileOrDirectory(const Utils::FileName &fileName) const final; + + bool managesDirectory(const QString &filename, QString *topLevel = 0) const final; + bool managesFile(const QString &workingDirectory, const QString &fileName) const final; + bool isConfigured() const final; + bool supportsOperation(Operation operation) const final; + bool vcsOpen(const QString &fileName) final; + bool vcsAdd(const QString &filename) final; + bool vcsDelete(const QString &filename) final; + bool vcsMove(const QString &from, const QString &to) final; + bool vcsCreateRepository(const QString &directory) final; + bool vcsAnnotate(const QString &file, int line) final; Core::ShellCommand *createInitialCheckoutCommand(const QString &url, const Utils::FileName &baseDirectory, const QString &localName, - const QStringList &extraArgs) override; + const QStringList &extraArgs) final; bool sccManaged(const QString &filename); diff --git a/src/plugins/modeleditor/componentviewcontroller.cpp b/src/plugins/modeleditor/componentviewcontroller.cpp index f7bdd58670..349b4ed7aa 100644 --- a/src/plugins/modeleditor/componentviewcontroller.cpp +++ b/src/plugins/modeleditor/componentviewcontroller.cpp @@ -296,7 +296,7 @@ void UpdateIncludeDependenciesVisitor::collectElementPaths(const ProjectExplorer QStringList elementsPath = qmt::NameController::buildElementsPath(nodePath, false); filePathsMap->insertMulti(elementName, Node(fileNode->filePath().toString(), elementsPath)); } - foreach (const ProjectExplorer::FolderNode *subNode, folderNode->subFolderNodes()) + foreach (const ProjectExplorer::FolderNode *subNode, folderNode->folderNodes()) collectElementPaths(subNode, filePathsMap); } @@ -419,7 +419,7 @@ void ComponentViewController::createComponentModel(const ProjectExplorer::Folder } } - foreach (const ProjectExplorer::FolderNode *subNode, folderNode->subFolderNodes()) + foreach (const ProjectExplorer::FolderNode *subNode, folderNode->folderNodes()) createComponentModel(subNode, diagram, anchorFolder); } diff --git a/src/plugins/modeleditor/modelindexer.cpp b/src/plugins/modeleditor/modelindexer.cpp index 7dd932d3a1..eac685f433 100644 --- a/src/plugins/modeleditor/modelindexer.cpp +++ b/src/plugins/modeleditor/modelindexer.cpp @@ -456,7 +456,7 @@ QString ModelIndexer::findFirstModel(ProjectExplorer::FolderNode *folderNode) if (mimeType.name() == QLatin1String(Constants::MIME_TYPE_MODEL)) return fileNode->filePath().toString(); } - foreach (ProjectExplorer::FolderNode *subFolderNode, folderNode->subFolderNodes()) { + foreach (ProjectExplorer::FolderNode *subFolderNode, folderNode->folderNodes()) { QString modelFileName = findFirstModel(subFolderNode); if (!modelFileName.isEmpty()) return modelFileName; diff --git a/src/plugins/modeleditor/pxnodecontroller.cpp b/src/plugins/modeleditor/pxnodecontroller.cpp index ecac98854c..7fa03a4d75 100644 --- a/src/plugins/modeleditor/pxnodecontroller.cpp +++ b/src/plugins/modeleditor/pxnodecontroller.cpp @@ -137,7 +137,7 @@ void PxNodeController::addExplorerNode(const ProjectExplorer::Node *node, node->filePath().toString()); switch (node->nodeType()) { - case ProjectExplorer::FileNodeType: + case ProjectExplorer::NodeType::File: { QStringList classNames = d->classViewController->findClassDeclarations( node->filePath().toString()).toList(); @@ -162,16 +162,16 @@ void PxNodeController::addExplorerNode(const ProjectExplorer::Node *node, menu->popup(QCursor::pos()); break; } - case ProjectExplorer::FolderNodeType: - case ProjectExplorer::VirtualFolderNodeType: - case ProjectExplorer::ProjectNodeType: + case ProjectExplorer::NodeType::Folder: + case ProjectExplorer::NodeType::VirtualFolder: + case ProjectExplorer::NodeType::Project: { QString stereotype; switch (node->nodeType()) { - case ProjectExplorer::VirtualFolderNodeType: + case ProjectExplorer::NodeType::VirtualFolder: stereotype = QStringLiteral("virtual folder"); break; - case ProjectExplorer::ProjectNodeType: + case ProjectExplorer::NodeType::Project: stereotype = QStringLiteral("project"); break; default: @@ -198,7 +198,7 @@ void PxNodeController::addExplorerNode(const ProjectExplorer::Node *node, menu->popup(QCursor::pos()); break; } - case ProjectExplorer::SessionNodeType: + case ProjectExplorer::NodeType::Session: break; } } diff --git a/src/plugins/modeleditor/pxnodeutilities.cpp b/src/plugins/modeleditor/pxnodeutilities.cpp index c3b7d79bb1..6df0e93a97 100644 --- a/src/plugins/modeleditor/pxnodeutilities.cpp +++ b/src/plugins/modeleditor/pxnodeutilities.cpp @@ -69,18 +69,18 @@ QString PxNodeUtilities::calcRelativePath(const ProjectExplorer::Node *node, QString nodePath; switch (node->nodeType()) { - case ProjectExplorer::FileNodeType: + case ProjectExplorer::NodeType::File: { QFileInfo fileInfo = node->filePath().toFileInfo(); nodePath = fileInfo.path(); break; } - case ProjectExplorer::FolderNodeType: - case ProjectExplorer::VirtualFolderNodeType: - case ProjectExplorer::ProjectNodeType: + case ProjectExplorer::NodeType::Folder: + case ProjectExplorer::NodeType::VirtualFolder: + case ProjectExplorer::NodeType::Project: nodePath = node->filePath().toString(); break; - case ProjectExplorer::SessionNodeType: + case ProjectExplorer::NodeType::Session: QTC_ASSERT(false, return QString()); break; } diff --git a/src/plugins/nim/project/nimbuildconfigurationfactory.cpp b/src/plugins/nim/project/nimbuildconfigurationfactory.cpp index 1d2a548d15..107d0e7fe1 100644 --- a/src/plugins/nim/project/nimbuildconfigurationfactory.cpp +++ b/src/plugins/nim/project/nimbuildconfigurationfactory.cpp @@ -108,7 +108,9 @@ BuildConfiguration *NimBuildConfigurationFactory::create(Target *parent, const B break; } nimCompilerBuildStep->setDefaultCompilerOptions(defaultOption); - nimCompilerBuildStep->setTargetNimFile(project->nimFiles().first()); + Utils::FileNameList nimFiles = project->nimFiles(); + if (!nimFiles.isEmpty()) + nimCompilerBuildStep->setTargetNimFile(nimFiles.first()); buildSteps->appendStep(nimCompilerBuildStep); } diff --git a/src/plugins/nim/project/nimcompilerbuildstep.cpp b/src/plugins/nim/project/nimcompilerbuildstep.cpp index 10c400c228..6b13968c5d 100644 --- a/src/plugins/nim/project/nimcompilerbuildstep.cpp +++ b/src/plugins/nim/project/nimcompilerbuildstep.cpp @@ -26,6 +26,7 @@ #include "nimcompilerbuildstep.h" #include "nimbuildconfiguration.h" #include "nimcompilerbuildstepconfigwidget.h" +#include "nimproject.h" #include "../nimconstants.h" @@ -50,6 +51,8 @@ NimCompilerBuildStep::NimCompilerBuildStep(BuildStepList *parentList) this, &NimCompilerBuildStep::updateProcessParameters); connect(this, &NimCompilerBuildStep::outFilePathChanged, bc, &NimBuildConfiguration::outFilePathChanged); + connect(bc->target()->project(), &ProjectExplorer::Project::fileListChanged, + this, &NimCompilerBuildStep::updateTargetNimFile); updateProcessParameters(); } @@ -202,5 +205,13 @@ void NimCompilerBuildStep::updateEnvironment() processParameters()->setEnvironment(bc->environment()); } +void NimCompilerBuildStep::updateTargetNimFile() +{ + if (!m_targetNimFile.isEmpty()) + return; + const Utils::FileNameList nimFiles = static_cast<NimProject *>(project())->nimFiles(); + if (!nimFiles.isEmpty()) + setTargetNimFile(nimFiles.at(0)); } +} // namespace Nim diff --git a/src/plugins/nim/project/nimcompilerbuildstep.h b/src/plugins/nim/project/nimcompilerbuildstep.h index 1c22df2707..50fecb4248 100644 --- a/src/plugins/nim/project/nimcompilerbuildstep.h +++ b/src/plugins/nim/project/nimcompilerbuildstep.h @@ -74,6 +74,8 @@ private: void updateArguments(); void updateEnvironment(); + void updateTargetNimFile(); + DefaultBuildOptions m_defaultOptions; QStringList m_userCompilerOptions; Utils::FileName m_targetNimFile; diff --git a/src/plugins/nim/project/nimproject.cpp b/src/plugins/nim/project/nimproject.cpp index eb0829e7a6..794336f88d 100644 --- a/src/plugins/nim/project/nimproject.cpp +++ b/src/plugins/nim/project/nimproject.cpp @@ -30,6 +30,7 @@ #include "../nimconstants.h" +#include <coreplugin/progressmanager/progressmanager.h> #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/kit.h> #include <projectexplorer/projectexplorerconstants.h> @@ -37,6 +38,8 @@ #include <texteditor/textdocument.h> #include <utils/algorithm.h> +#include <utils/qtcassert.h> +#include <utils/runextensions.h> #include <QFileInfo> #include <QQueue> @@ -60,9 +63,12 @@ NimProject::NimProject(NimProjectManager *projectManager, const QString &fileNam rootProjectNode()->setDisplayName(dir.dirName()); m_projectScanTimer.setSingleShot(true); - connect(&m_projectScanTimer, &QTimer::timeout, this, &NimProject::populateProject); + connect(&m_projectScanTimer, &QTimer::timeout, this, &NimProject::collectProjectFiles); - populateProject(); + m_futureWatcher.setFuture(m_futureInterface.future()); + connect(&m_futureWatcher, &QFutureWatcher<QList<FileNode *>>::finished, this, &NimProject::updateProject); + + collectProjectFiles(); connect(&m_fsWatcher, &QFileSystemWatcher::directoryChanged, this, &NimProject::scheduleProjectScan); } @@ -74,7 +80,7 @@ QString NimProject::displayName() const QStringList NimProject::files(FilesMode) const { - return QStringList(m_files.toList()); + return m_files; } bool NimProject::needsConfiguration() const @@ -91,24 +97,46 @@ void NimProject::scheduleProjectScan() m_projectScanTimer.start(); } } else { - populateProject(); + collectProjectFiles(); } } -void NimProject::populateProject() +void NimProject::collectProjectFiles() { m_lastProjectScan.start(); + QTC_ASSERT(!m_futureInterface.isRunning(), return); + + runAsync([this]() { + m_futureInterface.reportStarted(); + QList<FileNode *> nodes + = FileNode::scanForFiles(projectDirectory(), + [](const FileName &fn) { return new FileNode(fn, FileType::Source, false); }, + &m_futureInterface); + m_futureInterface.setProgressValue(m_futureInterface.progressMaximum()); + m_futureInterface.reportResult(nodes); + m_futureInterface.reportFinished(); + }); + Core::ProgressManager::addTask(m_futureInterface.future(), tr("Scanning for Nim files"), "Nim.Project.Scan"); +} - QSet<QString> oldFiles = m_files; +void NimProject::updateProject() +{ + QStringList oldFiles = m_files; m_files.clear(); - recursiveScanDirectory(QDir(projectDirectory().toString()), m_files); - if (m_files == oldFiles) + QList<FileNode *> fileNodes = Utils::filtered(m_futureInterface.future().result(), + [](const FileNode *fn) { + const QString fileName = fn->filePath().fileName(); + return !fileName.endsWith(".nimproject", HostOsInfo::fileNameCaseSensitivity()) + && !fileName.contains(".nimproject.user", HostOsInfo::fileNameCaseSensitivity()); + }); + + m_files = Utils::transform(fileNodes, [](const FileNode *fn) { return fn->filePath().toString(); }); + Utils::sort(m_files, [](const QString &a, const QString &b) { return a < b; }); + + if (oldFiles == m_files) return; - QList<FileNode *> fileNodes = Utils::transform(m_files.toList(), [](const QString &f) { - return new FileNode(FileName::fromString(f), SourceType, false); - }); rootProjectNode()->buildTree(fileNodes); emit fileListChanged(); @@ -116,22 +144,6 @@ void NimProject::populateProject() emit parsingFinished(); } -void NimProject::recursiveScanDirectory(const QDir &dir, QSet<QString> &container) -{ - static const QRegExp projectFilePattern(QLatin1String(".*\\.nimproject(?:\\.user)?$")); - for (const QFileInfo &info : dir.entryInfoList(QDir::AllDirs | - QDir::Files | - QDir::NoDotAndDotDot | - QDir::NoSymLinks | - QDir::CaseSensitive)) { - if (info.isDir()) - recursiveScanDirectory(QDir(info.filePath()), container); - else if (projectFilePattern.indexIn(info.fileName()) == -1) - container << info.filePath(); - } - m_fsWatcher.addPath(dir.absolutePath()); -} - bool NimProject::supportsKit(Kit *k, QString *) const { return k->isValid(); @@ -150,7 +162,7 @@ FileNameList NimProject::nimFiles() const if (file->displayName().endsWith(QLatin1String(".nim"))) result.append(file->filePath()); } - folders.append(folder->subFolderNodes()); + folders.append(folder->folderNodes()); } return result; diff --git a/src/plugins/nim/project/nimproject.h b/src/plugins/nim/project/nimproject.h index 7c308bc17c..8e7f87f4b0 100644 --- a/src/plugins/nim/project/nimproject.h +++ b/src/plugins/nim/project/nimproject.h @@ -28,8 +28,9 @@ #include <projectexplorer/project.h> #include <projectexplorer/projectnodes.h> -#include <QFileSystemWatcher> #include <QElapsedTimer> +#include <QFileSystemWatcher> +#include <QFutureWatcher> #include <QTimer> namespace TextEditor { class TextDocument; } @@ -54,11 +55,13 @@ public: private: void scheduleProjectScan(); - void populateProject(); - void recursiveScanDirectory(const QDir &dir, QSet<QString> &container); + void collectProjectFiles(); + void updateProject(); - QSet<QString> m_files; + QStringList m_files; QFileSystemWatcher m_fsWatcher; + QFutureInterface<QList<ProjectExplorer::FileNode *>> m_futureInterface; + QFutureWatcher<QList<ProjectExplorer::FileNode *>> m_futureWatcher; QElapsedTimer m_lastProjectScan; QTimer m_projectScanTimer; diff --git a/src/plugins/nim/project/nimprojectnode.cpp b/src/plugins/nim/project/nimprojectnode.cpp index 8d77072a67..88a53227f4 100644 --- a/src/plugins/nim/project/nimprojectnode.cpp +++ b/src/plugins/nim/project/nimprojectnode.cpp @@ -44,10 +44,10 @@ QList<ProjectAction> NimProjectNode::supportedActions(Node *node) const ProjectAction::RemoveFile }; switch (node->nodeType()) { - case FileNodeType: + case NodeType::File: return fileActions; - case FolderNodeType: - case ProjectNodeType: + case NodeType::Folder: + case NodeType::Project: return folderActions; default: return ProjectNode::supportedActions(node); diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp index a351014293..53f6284280 100644 --- a/src/plugins/perforce/perforceplugin.cpp +++ b/src/plugins/perforce/perforceplugin.cpp @@ -53,7 +53,7 @@ #include <vcsbase/basevcssubmiteditorfactory.h> #include <vcsbase/vcsbaseeditor.h> #include <vcsbase/vcsoutputwindow.h> -#include <vcsbase/vcsbaseeditorparameterwidget.h> +#include <vcsbase/vcsbaseeditorconfig.h> #include <QAction> #include <QDebug> @@ -1195,11 +1195,11 @@ struct PerforceDiffParameters }; // Parameter widget controlling whitespace diff mode, associated with a parameter -class PerforceDiffParameterWidget : public VcsBaseEditorParameterWidget +class PerforceDiffConfig : public VcsBaseEditorConfig { Q_OBJECT public: - explicit PerforceDiffParameterWidget(const PerforceDiffParameters &p, QWidget *parent = 0); + explicit PerforceDiffConfig(const PerforceDiffParameters &p, QToolBar *toolBar); void triggerReRun(); signals: @@ -1209,15 +1209,15 @@ private: const PerforceDiffParameters m_parameters; }; -PerforceDiffParameterWidget::PerforceDiffParameterWidget(const PerforceDiffParameters &p, QWidget *parent) : - VcsBaseEditorParameterWidget(parent), m_parameters(p) +PerforceDiffConfig::PerforceDiffConfig(const PerforceDiffParameters &p, QToolBar *toolBar) : + VcsBaseEditorConfig(toolBar), m_parameters(p) { setBaseArguments(p.diffArguments); addToggleButton(QLatin1String("w"), tr("Ignore Whitespace")); - connect(this, &VcsBaseEditorParameterWidget::argumentsChanged, this, &PerforceDiffParameterWidget::triggerReRun); + connect(this, &VcsBaseEditorConfig::argumentsChanged, this, &PerforceDiffConfig::triggerReRun); } -void PerforceDiffParameterWidget::triggerReRun() +void PerforceDiffConfig::triggerReRun() { PerforceDiffParameters effectiveParameters = m_parameters; effectiveParameters.diffArguments = arguments(); @@ -1269,12 +1269,12 @@ void PerforcePlugin::p4Diff(const PerforceDiffParameters &p) auto diffEditorWidget = qobject_cast<VcsBaseEditorWidget *>(editor->widget()); // Wire up the parameter widget to trigger a re-run on // parameter change and 'revert' from inside the diff editor. - auto pw = new PerforceDiffParameterWidget(p); - connect(pw, &PerforceDiffParameterWidget::reRunDiff, + auto pw = new PerforceDiffConfig(p, diffEditorWidget->toolBar()); + connect(pw, &PerforceDiffConfig::reRunDiff, this, [this](const PerforceDiffParameters &p) { p4Diff(p); }); connect(diffEditorWidget, &VcsBaseEditorWidget::diffChunkReverted, - pw, &PerforceDiffParameterWidget::triggerReRun); - diffEditorWidget->setConfigurationWidget(pw); + pw, &PerforceDiffConfig::triggerReRun); + diffEditorWidget->setConfigurationAdded(); } void PerforcePlugin::describe(const QString & source, const QString &n) diff --git a/src/plugins/perforce/perforceversioncontrol.cpp b/src/plugins/perforce/perforceversioncontrol.cpp index d436991b0f..e027a8ef26 100644 --- a/src/plugins/perforce/perforceversioncontrol.cpp +++ b/src/plugins/perforce/perforceversioncontrol.cpp @@ -49,6 +49,12 @@ Core::Id PerforceVersionControl::id() const return Core::Id(VcsBase::Constants::VCS_ID_PERFORCE); } +bool PerforceVersionControl::isVcsFileOrDirectory(const Utils::FileName &fileName) const +{ + Q_UNUSED(fileName); + return false; // Perforce does not seem to litter its files into the source tree. +} + bool PerforceVersionControl::isConfigured() const { const QString binary = m_plugin->settings().p4BinaryPath(); diff --git a/src/plugins/perforce/perforceversioncontrol.h b/src/plugins/perforce/perforceversioncontrol.h index 5ca6c7b20e..6f1a2c2bc3 100644 --- a/src/plugins/perforce/perforceversioncontrol.h +++ b/src/plugins/perforce/perforceversioncontrol.h @@ -38,24 +38,25 @@ class PerforceVersionControl : public Core::IVersionControl public: explicit PerforceVersionControl(PerforcePlugin *plugin); - QString displayName() const override; - Core::Id id() const override; + QString displayName() const final; + Core::Id id() const final; - bool managesDirectory(const QString &directory, QString *topLevel = 0) const override; - bool managesFile(const QString &workingDirectory, const QString &fileName) const override; + bool isVcsFileOrDirectory(const Utils::FileName &fileName) const final; + bool managesDirectory(const QString &directory, QString *topLevel = 0) const final; + bool managesFile(const QString &workingDirectory, const QString &fileName) const final; - bool isConfigured() const override; - bool supportsOperation(Operation operation) const override; - OpenSupportMode openSupportMode(const QString &fileName) const override; - bool vcsOpen(const QString &fileName) override; - SettingsFlags settingsFlags() const override; - bool vcsAdd(const QString &fileName) override; - bool vcsDelete(const QString &filename) override; - bool vcsMove(const QString &from, const QString &to) override; - bool vcsCreateRepository(const QString &directory) override; - bool vcsAnnotate(const QString &file, int line) override; - QString vcsOpenText() const override; - QString vcsMakeWritableText() const override; + bool isConfigured() const final; + bool supportsOperation(Operation operation) const final; + OpenSupportMode openSupportMode(const QString &fileName) const final; + bool vcsOpen(const QString &fileName) final; + SettingsFlags settingsFlags() const final; + bool vcsAdd(const QString &fileName) final; + bool vcsDelete(const QString &filename) final; + bool vcsMove(const QString &from, const QString &to) final; + bool vcsCreateRepository(const QString &directory) final; + bool vcsAnnotate(const QString &file, int line) final; + QString vcsOpenText() const final; + QString vcsMakeWritableText() const final; void emitRepositoryChanged(const QString &s); void emitFilesChanged(const QStringList &l); diff --git a/src/plugins/projectexplorer/kitmanager.cpp b/src/plugins/projectexplorer/kitmanager.cpp index 36f94b65cc..8d5e6efb33 100644 --- a/src/plugins/projectexplorer/kitmanager.cpp +++ b/src/plugins/projectexplorer/kitmanager.cpp @@ -253,7 +253,7 @@ void KitManager::saveKits() d->m_writer->save(data, ICore::mainWindow()); } -static bool isLoaded() +bool KitManager::isLoaded() { return d->m_initialized; } diff --git a/src/plugins/projectexplorer/kitmanager.h b/src/plugins/projectexplorer/kitmanager.h index e47acd6e41..cde9075657 100644 --- a/src/plugins/projectexplorer/kitmanager.h +++ b/src/plugins/projectexplorer/kitmanager.h @@ -155,6 +155,8 @@ public: static void saveKits(); + static bool isLoaded(); + signals: void kitAdded(ProjectExplorer::Kit *); // Kit is still valid when this call happens! diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h index 7f636f8ad8..5eda1ceb29 100644 --- a/src/plugins/projectexplorer/project.h +++ b/src/plugins/projectexplorer/project.h @@ -168,7 +168,6 @@ signals: void projectContextUpdated(); void projectLanguagesUpdated(); -signals: // for tests only void parsingFinished(); protected: diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index fb4f32c01d..4e53fc1087 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -277,6 +277,8 @@ public: void runConfigurationConfigurationFinished(); + QList<QPair<QString, QString> > recentProjects() const; + public: QMenu *m_sessionMenu; QMenu *m_openWithMenu; @@ -1066,6 +1068,10 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er if (!dd->m_shuttingDown && !SessionManager::loadingSession()) SessionManager::save(); }); + connect(qApp, &QApplication::applicationStateChanged, this, [](Qt::ApplicationState state) { + if (state == Qt::ApplicationActive) + dd->updateWelcomePage(); + }); addAutoReleasedObject(new ProjectTreeWidgetFactory); addAutoReleasedObject(new FolderNavigationWidgetFactory); @@ -1078,8 +1084,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er s->value(QLatin1String("ProjectExplorer/RecentProjects/DisplayNames")).toStringList(); if (fileNames.size() == displayNames.size()) { for (int i = 0; i < fileNames.size(); ++i) { - if (QFileInfo(fileNames.at(i)).isFile()) - dd->m_recentProjects.append(qMakePair(fileNames.at(i), displayNames.at(i))); + dd->m_recentProjects.append(qMakePair(fileNames.at(i), displayNames.at(i))); } } @@ -1983,20 +1988,27 @@ void ProjectExplorerPluginPrivate::runConfigurationConfigurationFinished() executeRunConfiguration(rc, runMode); } +QList<QPair<QString, QString> > ProjectExplorerPluginPrivate::recentProjects() const +{ + return Utils::filtered(dd->m_recentProjects, [](const QPair<QString, QString> &p) { + return QFileInfo(p.first).isFile(); + }); +} + static QString pathOrDirectoryFor(Node *node, bool dir) { Utils::FileName path = node->filePath(); QString location; FolderNode *folder = node->asFolderNode(); - if (node->nodeType() == VirtualFolderNodeType && folder) { + if (node->nodeType() == NodeType::VirtualFolder && folder) { // Virtual Folder case // If there are files directly below or no subfolders, take the folder path - if (!folder->fileNodes().isEmpty() || folder->subFolderNodes().isEmpty()) { + if (!folder->fileNodes().isEmpty() || folder->folderNodes().isEmpty()) { location = path.toString(); } else { // Otherwise we figure out a commonPath from the subfolders QStringList list; - foreach (FolderNode *f, folder->subFolderNodes()) + foreach (FolderNode *f, folder->folderNodes()) list << f->filePath().toString() + QLatin1Char('/'); location = Utils::commonPath(list); } @@ -2844,11 +2856,11 @@ void ProjectExplorerPluginPrivate::updateRecentProjectMenu() QMenu *menu = aci->menu(); menu->clear(); - bool hasRecentProjects = false; int acceleratorKey = 1; + auto projects = recentProjects(); //projects (ignore sessions, they used to be in this list) - const StringPairListConstIterator end = dd->m_recentProjects.constEnd(); - for (StringPairListConstIterator it = dd->m_recentProjects.constBegin(); it != end; ++it, ++acceleratorKey) { + const StringPairListConstIterator end = projects.constEnd(); + for (StringPairListConstIterator it = projects.constBegin(); it != end; ++it, ++acceleratorKey) { const QString fileName = it->first; if (fileName.endsWith(QLatin1String(".qws"))) continue; @@ -2859,8 +2871,8 @@ void ProjectExplorerPluginPrivate::updateRecentProjectMenu() connect(action, &QAction::triggered, this, [this, fileName] { openRecentProject(fileName); }); - hasRecentProjects = true; } + const bool hasRecentProjects = !projects.empty(); menu->setEnabled(hasRecentProjects); // add the Clear Menu item @@ -2929,7 +2941,7 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions() Node *currentNode = ProjectTree::currentNode(); - if (currentNode && currentNode->projectNode()) { + if (currentNode && currentNode->managingProject()) { QList<ProjectAction> actions = currentNode->supportedActions(currentNode); if (ProjectNode *pn = currentNode->asProjectNode()) { @@ -2957,10 +2969,10 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions() // Also handles ProjectNode m_addNewFileAction->setEnabled(actions.contains(AddNewFile) && !ICore::isNewItemDialogRunning()); - m_addNewSubprojectAction->setEnabled(currentNode->nodeType() == ProjectNodeType + m_addNewSubprojectAction->setEnabled(currentNode->nodeType() == NodeType::Project && actions.contains(AddSubProject) && !ICore::isNewItemDialogRunning()); - m_removeProjectAction->setEnabled(currentNode->nodeType() == ProjectNodeType + m_removeProjectAction->setEnabled(currentNode->nodeType() == NodeType::Project && actions.contains(RemoveSubProject)); m_addExistingFilesAction->setEnabled(actions.contains(AddExistingFile)); m_addExistingDirectoryAction->setEnabled(actions.contains(AddExistingDirectory)); @@ -3033,7 +3045,7 @@ void ProjectExplorerPluginPrivate::addNewSubproject() Node *currentNode = ProjectTree::currentNode(); QString location = directoryFor(currentNode); - if (currentNode->nodeType() == ProjectNodeType + if (currentNode->nodeType() == NodeType::Project && currentNode->supportedActions( currentNode).contains(AddSubProject)) { QVariantMap map; @@ -3097,7 +3109,7 @@ void ProjectExplorerPlugin::addExistingFiles(FolderNode *folderNode, const QStri if (!notAdded.isEmpty()) { const QString message = tr("Could not add following files to project %1:") - .arg(folderNode->projectNode()->displayName()) + QLatin1Char('\n'); + .arg(folderNode->managingProject()->displayName()) + QLatin1Char('\n'); const QStringList nativeFiles = Utils::transform(notAdded, [](const QString &f) { return QDir::toNativeSeparators(f); }); @@ -3115,10 +3127,10 @@ void ProjectExplorerPluginPrivate::removeProject() Node *node = ProjectTree::currentNode(); if (!node) return; - ProjectNode *subProjectNode = node->projectNode(); + ProjectNode *subProjectNode = node->managingProject(); if (!subProjectNode) return; - ProjectNode *projectNode = subProjectNode->parentFolderNode()->asProjectNode(); + ProjectNode *projectNode = subProjectNode->managingProject(); if (projectNode) { RemoveFileDialog removeFileDialog(subProjectNode->filePath().toString(), ICore::mainWindow()); removeFileDialog.setDeleteFileVisible(false); @@ -3154,7 +3166,7 @@ void ProjectExplorerPluginPrivate::openTerminalHere() void ProjectExplorerPluginPrivate::removeFile() { Node *currentNode = ProjectTree::currentNode(); - QTC_ASSERT(currentNode && currentNode->nodeType() == FileNodeType, return); + QTC_ASSERT(currentNode && currentNode->nodeType() == NodeType::File, return); FileNode *fileNode = currentNode->asFileNode(); @@ -3172,7 +3184,7 @@ void ProjectExplorerPluginPrivate::removeFile() QMessageBox::warning(ICore::mainWindow(), tr("Removing File Failed"), tr("Could not remove file %1 from project %2.") .arg(QDir::toNativeSeparators(filePath)) - .arg(folderNode->projectNode()->displayName())); + .arg(folderNode->managingProject()->displayName())); if (!deleteFile) return; } @@ -3185,7 +3197,7 @@ void ProjectExplorerPluginPrivate::removeFile() void ProjectExplorerPluginPrivate::duplicateFile() { Node *currentNode = ProjectTree::currentNode(); - QTC_ASSERT(currentNode && currentNode->nodeType() == FileNodeType, return); + QTC_ASSERT(currentNode && currentNode->nodeType() == NodeType::File, return); FileNode *fileNode = currentNode->asFileNode(); QString filePath = currentNode->filePath().toString(); @@ -3216,7 +3228,7 @@ void ProjectExplorerPluginPrivate::duplicateFile() void ProjectExplorerPluginPrivate::deleteFile() { Node *currentNode = ProjectTree::currentNode(); - QTC_ASSERT(currentNode && currentNode->nodeType() == FileNodeType, return); + QTC_ASSERT(currentNode && currentNode->nodeType() == NodeType::File, return); FileNode *fileNode = currentNode->asFileNode(); @@ -3266,7 +3278,7 @@ void ProjectExplorerPlugin::renameFile(Node *node, const QString &newFilePath) { const QString oldFilePath = node->filePath().toFileInfo().absoluteFilePath(); FolderNode *folderNode = node->parentFolderNode(); - const QString projectFileName = folderNode->projectNode()->filePath().fileName(); + const QString projectFileName = folderNode->managingProject()->filePath().fileName(); if (oldFilePath == newFilePath) return; @@ -3386,7 +3398,7 @@ void ProjectExplorerPlugin::openOpenProjectDialog() QList<QPair<QString, QString> > ProjectExplorerPlugin::recentProjects() { - return dd->m_recentProjects; + return dd->recentProjects(); } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp index dcaf3c1932..e597e23bf7 100644 --- a/src/plugins/projectexplorer/projectmodels.cpp +++ b/src/plugins/projectexplorer/projectmodels.cpp @@ -50,122 +50,20 @@ namespace { bool sortNodes(Node *n1, Node *n2) { - // Ordering is: project files, project, folder, file - - const NodeType n1Type = n1->nodeType(); - const NodeType n2Type = n2->nodeType(); - - // project files - FileNode *file1 = n1->asFileNode(); - FileNode *file2 = n2->asFileNode(); - if (file1 && file1->fileType() == ProjectFileType) { - if (file2 && file2->fileType() == ProjectFileType) { - const QString fileName1 = file1->filePath().fileName(); - const QString fileName2 = file2->filePath().fileName(); - - int result = caseFriendlyCompare(fileName1, fileName2); - if (result != 0) - return result < 0; - else - return file1 < file2; - } else { - return true; // project file is before everything else - } - } else { - if (file2 && file2->fileType() == ProjectFileType) - return false; - } - - // projects - if (n1Type == ProjectNodeType) { - if (n2Type == ProjectNodeType) { - auto project1 = static_cast<ProjectNode*>(n1); - auto project2 = static_cast<ProjectNode*>(n2); - - int result = caseFriendlyCompare(project1->displayName(), project2->displayName()); - if (result != 0) - return result < 0; - - result = caseFriendlyCompare(project1->filePath().toString(), - project2->filePath().toString()); - if (result != 0) - return result < 0; - return project1 < project2; // sort by pointer value - } else { - return true; // project is before folder & file - } - } - if (n2Type == ProjectNodeType) - return false; - - if (n1Type == VirtualFolderNodeType) { - if (n2Type == VirtualFolderNodeType) { - auto folder1 = static_cast<VirtualFolderNode *>(n1); - auto folder2 = static_cast<VirtualFolderNode *>(n2); - - if (folder1->priority() > folder2->priority()) - return true; - if (folder1->priority() < folder2->priority()) - return false; - int result = caseFriendlyCompare(folder1->filePath().toString(), - folder2->filePath().toString()); - if (result != 0) - return result < 0; - else - return folder1 < folder2; - } else { - return true; // virtual folder is before folder - } - } - - if (n2Type == VirtualFolderNodeType) + if (n1->priority() > n2->priority()) + return true; + if (n1->priority() < n2->priority()) return false; + const int displayNameResult = caseFriendlyCompare(n1->displayName(), n2->displayName()); + if (displayNameResult != 0) + return displayNameResult < 0; - if (n1Type == FolderNodeType) { - if (n2Type == FolderNodeType) { - auto folder1 = static_cast<FolderNode*>(n1); - auto folder2 = static_cast<FolderNode*>(n2); - - int result = caseFriendlyCompare(folder1->filePath().toString(), - folder2->filePath().toString()); - if (result != 0) - return result < 0; - else - return folder1 < folder2; - } else { - return true; // folder is before file - } - } - if (n2Type == FolderNodeType) - return false; - - // must be file nodes - { - int result = caseFriendlyCompare(n1->displayName(), n2->displayName()); - if (result != 0) - return result < 0; - - const QString filePath1 = n1->filePath().toString(); - const QString filePath2 = n2->filePath().toString(); - - const QString fileName1 = n1->filePath().fileName(); - const QString fileName2 = n2->filePath().fileName(); - - result = caseFriendlyCompare(fileName1, fileName2); - if (result != 0) { - return result < 0; // sort by filename - } else { - result = caseFriendlyCompare(filePath1, filePath2); - if (result != 0) - return result < 0; // sort by filepath - - if (n1->line() != n2->line()) - return n1->line() < n2->line(); // sort by line numbers - return n1 < n2; // sort by pointer value - } - } - return false; + const int filePathResult = caseFriendlyCompare(n1->filePath().toString(), + n2->filePath().toString()); + if (filePathResult != 0) + return filePathResult < 0; + return n1 < n2; // sort by pointer value } } // namespace anon @@ -252,9 +150,9 @@ QVariant FlatModel::data(const QModelIndex &index, int role) const case Qt::DisplayRole: { QString name = node->displayName(); - if (node->nodeType() == ProjectNodeType + if (node->nodeType() == NodeType::Project && node->parentFolderNode() - && node->parentFolderNode()->nodeType() == SessionNodeType) { + && node->parentFolderNode()->nodeType() == NodeType::Session) { const QString vcsTopic = static_cast<ProjectNode *>(node)->vcsTopic(); if (!vcsTopic.isEmpty()) @@ -386,7 +284,7 @@ bool FlatModel::canFetchMore(const QModelIndex & parent) const void FlatModel::recursiveAddFolderNodes(FolderNode *startNode, QList<Node *> *list, const QSet<Node *> &blackList) const { - foreach (FolderNode *folderNode, startNode->subFolderNodes()) { + foreach (FolderNode *folderNode, startNode->folderNodes()) { if (folderNode && !blackList.contains(folderNode)) recursiveAddFolderNodesImpl(folderNode, list, blackList); } @@ -398,7 +296,7 @@ void FlatModel::recursiveAddFolderNodesImpl(FolderNode *startNode, QList<Node *> if (!blackList.contains(startNode)) list->append(startNode); } else { - foreach (FolderNode *folderNode, startNode->subFolderNodes()) { + foreach (FolderNode *folderNode, startNode->folderNodes()) { if (folderNode && !blackList.contains(folderNode)) recursiveAddFolderNodesImpl(folderNode, list, blackList); } @@ -407,7 +305,7 @@ void FlatModel::recursiveAddFolderNodesImpl(FolderNode *startNode, QList<Node *> void FlatModel::recursiveAddFileNodes(FolderNode *startNode, QList<Node *> *list, const QSet<Node *> &blackList) const { - foreach (FolderNode *subFolderNode, startNode->subFolderNodes()) { + foreach (FolderNode *subFolderNode, startNode->folderNodes()) { if (!blackList.contains(subFolderNode)) recursiveAddFileNodes(subFolderNode, list, blackList); } @@ -422,7 +320,7 @@ QList<Node*> FlatModel::childNodes(FolderNode *parentNode, const QSet<Node*> &bl qCDebug(logger()) << " FlatModel::childNodes for " << parentNode->filePath(); QList<Node*> nodeList; - if (parentNode->nodeType() == SessionNodeType) { + if (parentNode->nodeType() == NodeType::Session) { auto sessionNode = static_cast<SessionNode*>(parentNode); QList<ProjectNode*> projectList = sessionNode->projectNodes(); for (int i = 0; i < projectList.size(); ++i) { @@ -855,7 +753,7 @@ void FlatModel::foldersAboutToBeRemoved(FolderNode *parentFolder, const QList<Fo void FlatModel::removeFromCache(QList<FolderNode *> list) { foreach (FolderNode *fn, list) { - removeFromCache(fn->subFolderNodes()); + removeFromCache(fn->folderNodes()); m_childNodes.remove(fn); } } diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp index 9d97dff941..24b3451c12 100644 --- a/src/plugins/projectexplorer/projectnodes.cpp +++ b/src/plugins/projectexplorer/projectnodes.cpp @@ -60,13 +60,13 @@ namespace ProjectExplorer { */ Node::Node(NodeType nodeType, const Utils::FileName &filePath, int line) : - m_nodeType(nodeType), - m_line(line), - m_filePath(filePath) + m_filePath(filePath), m_line(line), m_nodeType(nodeType) { } -Node::~Node() -{ } +void Node::setPriority(int p) +{ + m_priority = p; +} void Node::emitNodeSortKeyAboutToChange() { @@ -97,13 +97,23 @@ NodeType Node::nodeType() const return m_nodeType; } +int Node::priority() const +{ + return m_priority; +} + /*! The project that owns and manages the node. It is the first project in the list of ancestors. */ -ProjectNode *Node::projectNode() const +ProjectNode *Node::parentProjectNode() const { - return m_projectNode; + if (!m_parentFolderNode) + return nullptr; + auto pn = m_parentFolderNode->asProjectNode(); + if (pn) + return pn; + return m_parentFolderNode->parentProjectNode(); } /*! @@ -111,7 +121,20 @@ ProjectNode *Node::projectNode() const */ FolderNode *Node::parentFolderNode() const { - return m_folderNode; + return m_parentFolderNode; +} + +ProjectNode *Node::managingProject() +{ + if (asSessionNode()) + return nullptr; + ProjectNode *pn = parentProjectNode(); + return pn ? pn : asProjectNode(); // projects manage themselves... +} + +const ProjectNode *Node::managingProject() const +{ + return const_cast<Node *>(this)->managingProject(); } /*! @@ -139,7 +162,10 @@ QString Node::tooltip() const bool Node::isEnabled() const { - return parentFolderNode()->isEnabled(); + if (!m_isEnabled) + return false; + FolderNode *parent = parentFolderNode(); + return parent ? parent->isEnabled() : true; } QList<ProjectAction> Node::supportedActions(Node *node) const @@ -149,9 +175,12 @@ QList<ProjectAction> Node::supportedActions(Node *node) const return list; } -void Node::setProjectNode(ProjectNode *project) +void Node::setEnabled(bool enabled) { - m_projectNode = project; + if (m_isEnabled == enabled) + return; + m_isEnabled = enabled; + emitNodeUpdated(); } void Node::emitNodeUpdated() @@ -160,29 +189,19 @@ void Node::emitNodeUpdated() ProjectTree::instance()->emitNodeUpdated(this); } -FileNode *Node::asFileNode() -{ - return nullptr; -} - -FolderNode *Node::asFolderNode() +Node *Node::trim(const QSet<Node *> &keepers) { - return nullptr; + return keepers.contains(this) ? nullptr : this; } -ProjectNode *Node::asProjectNode() +bool Node::sortByPath(Node *a, Node *b) { - return nullptr; -} - -SessionNode *Node::asSessionNode() -{ - return nullptr; + return a->filePath() < b->filePath(); } void Node::setParentFolderNode(FolderNode *parentFolder) { - m_folderNode = parentFolder; + m_parentFolderNode = parentFolder; } /*! @@ -197,10 +216,15 @@ void Node::setParentFolderNode(FolderNode *parentFolder) FileNode::FileNode(const Utils::FileName &filePath, const FileType fileType, - bool generated, int line) : Node(FileNodeType, filePath, line), + bool generated, int line) : Node(NodeType::File, filePath, line), m_fileType(fileType), m_generated(generated) -{ } +{ + if (fileType == FileType::Project) + setPriority(DefaultProjectFilePriority); + else + setPriority(DefaultFilePriority); +} FileType FileNode::fileType() const { @@ -215,9 +239,59 @@ bool FileNode::isGenerated() const return m_generated; } -FileNode *FileNode::asFileNode() +static QList<FileNode *> scanForFilesRecursively(const Utils::FileName &directory, + const std::function<FileNode *(const Utils::FileName &)> factory, + QSet<QString> &visited, QFutureInterface<QList<FileNode*>> *future, + double progressStart, double progressRange) +{ + QList<FileNode *> result; + + const QDir baseDir = QDir(directory.toString()); + + // Do not follow directory loops: + const int visitedCount = visited.count(); + visited.insert(baseDir.canonicalPath()); + if (visitedCount == visited.count()) + return result; + + const Core::IVersionControl *vcsControl + = Core::VcsManager::findVersionControlForDirectory(baseDir.absolutePath(), nullptr); + const QList<QFileInfo> entries = baseDir.entryInfoList(QStringList(), QDir::AllEntries|QDir::NoDotAndDotDot); + double progress = 0; + const double progressIncrement = progressRange / static_cast<double>(entries.count()); + int lastIntProgress = 0; + for (const QFileInfo &entry : entries) { + if (future && future->isCanceled()) + return result; + + const Utils::FileName entryName = Utils::FileName::fromString(entry.absoluteFilePath()); + if (!vcsControl || !vcsControl->isVcsFileOrDirectory(entryName)) { + if (entry.isDir()) + result.append(scanForFilesRecursively(entryName, factory, visited, future, progress, progressIncrement)); + else + result.append(factory(entryName)); + } + if (future) { + progress += progressIncrement; + const int intProgress = std::min(static_cast<int>(progressStart + progress), future->progressMaximum()); + if (lastIntProgress < intProgress) { + future->setProgressValue(intProgress); + lastIntProgress = intProgress; + } + } + } + if (future) + future->setProgressValue(std::min(static_cast<int>(progressStart + progressRange), future->progressMaximum())); + return result; +} + +QList<FileNode *> FileNode::scanForFiles(const Utils::FileName &directory, + const std::function<FileNode *(const Utils::FileName &)> factory, + QFutureInterface<QList<FileNode*>> *future) { - return this; + QSet<QString> visited; + future->setProgressRange(0, 1000000); + return scanForFilesRecursively(directory, factory, visited, future, 0.0, 1000000.0); } /*! @@ -228,16 +302,17 @@ FileNode *FileNode::asFileNode() \sa ProjectExplorer::FileNode, ProjectExplorer::ProjectNode */ FolderNode::FolderNode(const Utils::FileName &folderPath, NodeType nodeType, const QString &displayName) : - Node(nodeType, folderPath), + Node(nodeType, folderPath, -1), m_displayName(displayName) { + setPriority(DefaultFolderPriority); if (m_displayName.isEmpty()) m_displayName = folderPath.toUserOutput(); } FolderNode::~FolderNode() { - qDeleteAll(m_subFolderNodes); + qDeleteAll(m_folderNodes); qDeleteAll(m_fileNodes); } @@ -264,74 +339,125 @@ QIcon FolderNode::icon() const return m_icon; } +Node *FolderNode::trim(const QSet<Node *> &keepers) +{ + if (keepers.contains(this)) + return nullptr; + + bool keepThis = false; + QList<Node *> toTrim = Utils::transform(m_fileNodes, [&keepers](Node *n) { return n->trim(keepers); }); + int count = toTrim.count(); + toTrim = Utils::filtered(toTrim, [](const Node *n) { return n; }); + keepThis = (count != toTrim.count()); + removeFileNodes(Utils::transform(toTrim, [](Node *n) { return static_cast<FileNode *>(n); })); + + toTrim = Utils::transform(m_folderNodes, [&keepers](Node *n) { return n->trim(keepers); }); + count = toTrim.count(); + toTrim = Utils::filtered(toTrim, [](const Node *n) { return n; }); + keepThis = keepThis || (count != toTrim.count()); + removeFolderNodes(Utils::transform(toTrim, [](Node *n) { return static_cast<FolderNode *>(n); })); + return keepThis ? nullptr : this; +} + QList<FileNode*> FolderNode::fileNodes() const { return m_fileNodes; } +FileNode *FolderNode::fileNode(const Utils::FileName &file) const +{ + return Utils::findOrDefault(m_fileNodes, [&file](const FileNode *fn) { + return fn->filePath() == file; + }); +} + +FileNode *FolderNode::recursiveFileNode(const Utils::FileName &file) const +{ + Utils::FileName dir = file.parentDir(); + + const QDir thisDir(filePath().toString()); + QString relativePath = thisDir.relativeFilePath(dir.toString()); + if (relativePath == ".") + relativePath.clear(); + QStringList parts = relativePath.split('/', QString::SkipEmptyParts); + const ProjectExplorer::FolderNode *parent = this; + foreach (const QString &part, parts) { + dir.appendPath(part); + // Find folder in subFolders + parent = Utils::findOrDefault(parent->folderNodes(), [&dir](const FolderNode *fn) { + return fn->filePath() == dir; + }); + if (!parent) + return nullptr; + } + return parent->fileNode(file); +} + QList<FileNode *> FolderNode::recursiveFileNodes() const { QList<FileNode *> result = fileNodes(); - foreach (ProjectExplorer::FolderNode *folder, subFolderNodes()) + foreach (ProjectExplorer::FolderNode *folder, folderNodes()) result.append(folder->recursiveFileNodes()); return result; } -QList<FolderNode*> FolderNode::subFolderNodes() const +QList<FolderNode*> FolderNode::folderNodes() const { - return m_subFolderNodes; + return m_folderNodes; } -FolderNode *FolderNode::findOrCreateSubFolderNode(const QString &directory) +FolderNode *FolderNode::folderNode(const Utils::FileName &directory) const { - Utils::FileName path = filePath(); - QDir parentDir(path.toString()); - QString relativePath = parentDir.relativeFilePath(directory); - if (relativePath == ".") - relativePath.clear(); - QStringList parts = relativePath.split('/', QString::SkipEmptyParts); + return Utils::findOrDefault(m_folderNodes, [&directory](const FolderNode *fn) { + return fn->filePath() == directory; + }); +} + +FolderNode *FolderNode::recursiveFindOrCreateFolderNode(const QString &directory, + const Utils::FileName &overrideBaseDir) +{ + Utils::FileName path = overrideBaseDir.isEmpty() ? filePath() : overrideBaseDir; + QString workPath; + if (path.isEmpty() || path.toFileInfo().isRoot()) { + workPath = directory; + } else { + QDir parentDir(path.toString()); + workPath = parentDir.relativeFilePath(directory); + if (workPath == ".") + workPath.clear(); + } + const QStringList parts = workPath.split('/', QString::SkipEmptyParts); + ProjectExplorer::FolderNode *parent = this; foreach (const QString &part, parts) { path.appendPath(part); // Find folder in subFolders - bool found = false; - foreach (ProjectExplorer::FolderNode *folder, parent->subFolderNodes()) { - if (folder->filePath() == path) { - // yeah found something :) - parent = folder; - found = true; - break; - } - } - if (!found) { + FolderNode *next = parent->folderNode(path); + if (!next) { // No FolderNode yet, so create it auto tmp = new ProjectExplorer::FolderNode(path); tmp->setDisplayName(part); parent->addFolderNodes(QList<ProjectExplorer::FolderNode *>({ tmp })); - parent = tmp; + next = tmp; } + parent = next; } return parent; } -static bool sortNodesByPath(Node *a, Node *b) -{ - return a->filePath() < b->filePath(); -} - -void FolderNode::buildTree(QList<FileNode *> &files) +void FolderNode::buildTree(QList<FileNode *> &files, const Utils::FileName &overrideBaseDir) { // Gather old list QList<ProjectExplorer::FileNode *> oldFiles = recursiveFileNodes(); - Utils::sort(oldFiles, sortNodesByPath); - Utils::sort(files, sortNodesByPath); + Utils::sort(oldFiles, Node::sortByPath); + Utils::sort(files, Node::sortByPath); QList<ProjectExplorer::FileNode *> added; QList<ProjectExplorer::FileNode *> deleted; - ProjectExplorer::compareSortedLists(oldFiles, files, deleted, added, sortNodesByPath); + ProjectExplorer::compareSortedLists(oldFiles, files, deleted, added, Node::sortByPath); - qDeleteAll(ProjectExplorer::subtractSortedList(files, added, sortNodesByPath)); + qDeleteAll(ProjectExplorer::subtractSortedList(files, added, Node::sortByPath)); QHash<ProjectExplorer::FolderNode *, QList<ProjectExplorer::FileNode *> > addedFolderMapping; QHash<ProjectExplorer::FolderNode *, QList<ProjectExplorer::FileNode *> > deletedFolderMapping; @@ -340,7 +466,8 @@ void FolderNode::buildTree(QList<FileNode *> &files) foreach (ProjectExplorer::FileNode *fn, added) { // Get relative path to rootNode QString parentDir = fn->filePath().toFileInfo().absolutePath(); - ProjectExplorer::FolderNode *folder = findOrCreateSubFolderNode(parentDir); + ProjectExplorer::FolderNode *folder + = recursiveFindOrCreateFolderNode(parentDir, overrideBaseDir); addedFolderMapping[folder] << fn; } @@ -354,8 +481,12 @@ void FolderNode::buildTree(QList<FileNode *> &files) for (auto i = deletedFolderMapping.constBegin(); i != deletedFolderMapping.constEnd(); ++i) { ProjectExplorer::FolderNode *parent = i.key(); parent->removeFileNodes(i.value()); + + if (parent == this) // Never delete this node! + continue; + // Check for empty parent - while (parent->subFolderNodes().isEmpty() && parent->fileNodes().isEmpty()) { + while (parent->folderNodes().isEmpty() && parent->fileNodes().isEmpty()) { ProjectExplorer::FolderNode *grandparent = parent->parentFolderNode(); grandparent->removeFolderNodes(QList<ProjectExplorer::FolderNode *>() << parent); parent = grandparent; @@ -368,7 +499,7 @@ void FolderNode::buildTree(QList<FileNode *> &files) void FolderNode::accept(NodesVisitor *visitor) { visitor->visitFolderNode(this); - foreach (FolderNode *subFolder, m_subFolderNodes) + foreach (FolderNode *subFolder, m_folderNodes) subFolder->accept(visitor); } @@ -394,36 +525,41 @@ QString FolderNode::addFileFilter() const bool FolderNode::addFiles(const QStringList &filePaths, QStringList *notAdded) { - if (projectNode()) - return projectNode()->addFiles(filePaths, notAdded); + ProjectNode *pn = managingProject(); + if (pn) + return pn->addFiles(filePaths, notAdded); return false; } bool FolderNode::removeFiles(const QStringList &filePaths, QStringList *notRemoved) { - if (projectNode()) - return projectNode()->removeFiles(filePaths, notRemoved); + ProjectNode *pn = managingProject(); + if (pn) + return pn->removeFiles(filePaths, notRemoved); return false; } bool FolderNode::deleteFiles(const QStringList &filePaths) { - if (projectNode()) - return projectNode()->deleteFiles(filePaths); + ProjectNode *pn = managingProject(); + if (pn) + return pn->deleteFiles(filePaths); return false; } bool FolderNode::canRenameFile(const QString &filePath, const QString &newFilePath) { - if (projectNode()) - return projectNode()->canRenameFile(filePath, newFilePath); + ProjectNode *pn = managingProject(); + if (pn) + return pn->canRenameFile(filePath, newFilePath); return false; } bool FolderNode::renameFile(const QString &filePath, const QString &newFilePath) { - if (projectNode()) - return projectNode()->renameFile(filePath, newFilePath); + ProjectNode *pn = managingProject(); + if (pn) + return pn->renameFile(filePath, newFilePath); return false; } @@ -443,7 +579,8 @@ FolderNode::AddNewInformation FolderNode::addNewInformation(const QStringList &f void FolderNode::addFileNodes(const QList<FileNode *> &files) { - Q_ASSERT(projectNode()); + Q_ASSERT(managingProject()); + if (files.isEmpty()) return; @@ -454,7 +591,6 @@ void FolderNode::addFileNodes(const QList<FileNode *> &files) qDebug("File node has already a parent folder")); file->setParentFolderNode(this); - file->setProjectNode(projectNode()); // Now find the correct place to insert file if (m_fileNodes.count() == 0 || m_fileNodes.last() < file) { @@ -479,7 +615,7 @@ void FolderNode::addFileNodes(const QList<FileNode *> &files) void FolderNode::removeFileNodes(const QList<FileNode *> &files) { - Q_ASSERT(projectNode()); + Q_ASSERT(managingProject()); if (files.isEmpty()) return; @@ -510,7 +646,7 @@ void FolderNode::removeFileNodes(const QList<FileNode *> &files) */ void FolderNode::addFolderNodes(const QList<FolderNode*> &subFolders) { - Q_ASSERT(projectNode()); + Q_ASSERT(managingProject()); if (subFolders.isEmpty()) return; @@ -520,21 +656,20 @@ void FolderNode::addFolderNodes(const QList<FolderNode*> &subFolders) QTC_ASSERT(!folder->parentFolderNode(), qDebug("Project node has already a parent folder")); folder->setParentFolderNode(this); - folder->setProjectNode(projectNode()); // Find the correct place to insert - if (m_subFolderNodes.count() == 0 - || m_subFolderNodes.last() < folder) { + if (m_folderNodes.count() == 0 + || m_folderNodes.last() < folder) { // empty list or greater then last node - m_subFolderNodes.append(folder); + m_folderNodes.append(folder); } else { // Binary Search for insertion point - auto it = std::lower_bound(m_subFolderNodes.begin(), m_subFolderNodes.end(), folder); - m_subFolderNodes.insert(it, folder); + auto it = std::lower_bound(m_folderNodes.begin(), m_folderNodes.end(), folder); + m_folderNodes.insert(it, folder); } // project nodes have to be added via addProjectNodes - QTC_ASSERT(folder->nodeType() != ProjectNodeType, + QTC_ASSERT(folder->nodeType() != NodeType::Project, qDebug("project nodes have to be added via addProjectNodes")); } @@ -549,7 +684,7 @@ void FolderNode::addFolderNodes(const QList<FolderNode*> &subFolders) */ void FolderNode::removeFolderNodes(const QList<FolderNode*> &subFolders) { - Q_ASSERT(projectNode()); + Q_ASSERT(managingProject()); if (subFolders.isEmpty()) return; @@ -560,27 +695,22 @@ void FolderNode::removeFolderNodes(const QList<FolderNode*> &subFolders) ProjectTree::instance()->emitFoldersAboutToBeRemoved(this, toRemove); auto toRemoveIter = toRemove.constBegin(); - auto folderIter = m_subFolderNodes.begin(); + auto folderIter = m_folderNodes.begin(); for (; toRemoveIter != toRemove.constEnd(); ++toRemoveIter) { - QTC_ASSERT((*toRemoveIter)->nodeType() != ProjectNodeType, + QTC_ASSERT((*toRemoveIter)->nodeType() != NodeType::Project, qDebug("project nodes have to be removed via removeProjectNodes")); while (*folderIter != *toRemoveIter) { ++folderIter; - QTC_ASSERT(folderIter != m_subFolderNodes.end(), + QTC_ASSERT(folderIter != m_folderNodes.end(), qDebug("Folder to remove is not part of specified folder!")); } delete *folderIter; - folderIter = m_subFolderNodes.erase(folderIter); + folderIter = m_folderNodes.erase(folderIter); } ProjectTree::instance()->emitFoldersRemoved(this); } -FolderNode *FolderNode::asFolderNode() -{ - return this; -} - bool FolderNode::showInSimpleTree() const { return false; @@ -598,13 +728,9 @@ bool FolderNode::showInSimpleTree() const \sa ProjectExplorer::FileNode, ProjectExplorer::ProjectNode */ VirtualFolderNode::VirtualFolderNode(const Utils::FileName &folderPath, int priority) : - FolderNode(folderPath, VirtualFolderNodeType), - m_priority(priority) -{ } - -int VirtualFolderNode::priority() const + FolderNode(folderPath, NodeType::VirtualFolder, QString()) { - return m_priority; + setPriority(priority); } /*! @@ -621,10 +747,9 @@ int VirtualFolderNode::priority() const Creates an uninitialized project node object. */ ProjectNode::ProjectNode(const Utils::FileName &projectFilePath) : - FolderNode(projectFilePath, ProjectNodeType) + FolderNode(projectFilePath, NodeType::Project) { - // project node "manages" itself - setProjectNode(this); + setPriority(DefaultProjectPriority); setDisplayName(projectFilePath.fileName()); } @@ -639,11 +764,6 @@ QString ProjectNode::vcsTopic() const return QString(); } -QList<ProjectNode*> ProjectNode::subProjectNodes() const -{ - return m_subProjectNodes; -} - bool ProjectNode::canAddSubProject(const QString &proFilePath) const { Q_UNUSED(proFilePath) @@ -716,10 +836,22 @@ void ProjectNode::accept(NodesVisitor *visitor) { visitor->visitProjectNode(this); - foreach (FolderNode *folder, m_subFolderNodes) + foreach (FolderNode *folder, m_folderNodes) folder->accept(visitor); } +ProjectNode *ProjectNode::projectNode(const Utils::FileName &file) const +{ + return Utils::findOrDefault(m_projectNodes, [&file](const ProjectNode *fn) { + return fn->filePath() == file; + }); +} + +QList<ProjectNode*> ProjectNode::projectNodes() const +{ + return m_projectNodes; +} + /*! Adds project nodes specified by \a subProjects to the node hierarchy and emits the corresponding signals. @@ -737,11 +869,11 @@ void ProjectNode::addProjectNodes(const QList<ProjectNode*> &subProjects) QTC_ASSERT(!project->parentFolderNode() || project->parentFolderNode() == this, qDebug("Project node has already a parent")); project->setParentFolderNode(this); - m_subFolderNodes.append(project); - m_subProjectNodes.append(project); + m_folderNodes.append(project); + m_projectNodes.append(project); } - Utils::sort(m_subFolderNodes); - Utils::sort(m_subProjectNodes); + Utils::sort(m_folderNodes); + Utils::sort(m_projectNodes); ProjectTree::instance()->emitFoldersAdded(this); } @@ -765,40 +897,51 @@ void ProjectNode::removeProjectNodes(const QList<ProjectNode*> &subProjects) ProjectTree::instance()->emitFoldersAboutToBeRemoved(this, toRemove); auto toRemoveIter = toRemove.constBegin(); - auto folderIter = m_subFolderNodes.begin(); - auto projectIter = m_subProjectNodes.begin(); + auto folderIter = m_folderNodes.begin(); + auto projectIter = m_projectNodes.begin(); for (; toRemoveIter != toRemove.constEnd(); ++toRemoveIter) { while (*projectIter != *toRemoveIter) { ++projectIter; - QTC_ASSERT(projectIter != m_subProjectNodes.end(), + QTC_ASSERT(projectIter != m_projectNodes.end(), qDebug("Project to remove is not part of specified folder!")); } while (*folderIter != *toRemoveIter) { ++folderIter; - QTC_ASSERT(folderIter != m_subFolderNodes.end(), + QTC_ASSERT(folderIter != m_folderNodes.end(), qDebug("Project to remove is not part of specified folder!")); } delete *projectIter; - projectIter = m_subProjectNodes.erase(projectIter); - folderIter = m_subFolderNodes.erase(folderIter); + projectIter = m_projectNodes.erase(projectIter); + folderIter = m_folderNodes.erase(folderIter); } ProjectTree::instance()->emitFoldersRemoved(this); } } -ProjectNode *ProjectNode::asProjectNode() +Node *ProjectNode::trim(const QSet<Node *> &keepers) { - return this; -} + if (keepers.contains(this)) + return nullptr; + QList<Node *> toTrim + = Utils::transform(m_projectNodes, [&keepers](Node *n) { return n->trim(keepers); }); + int count = toTrim.count(); + toTrim = Utils::filtered(toTrim, [](Node *n) { return n; }); + removeProjectNodes(Utils::transform(toTrim, [](Node *n) { return static_cast<ProjectNode *>(n); })); + + if (!FolderNode::trim(keepers)) + return nullptr; + + return (toTrim.count() != count) ? nullptr : this; +} /*! \class ProjectExplorer::SessionNode */ SessionNode::SessionNode() : - FolderNode(Utils::FileName::fromString("session"), SessionNodeType) + FolderNode(Utils::FileName::fromString("session"), NodeType::Session) { } QList<ProjectAction> SessionNode::supportedActions(Node *node) const @@ -826,11 +969,6 @@ void SessionNode::projectDisplayNameChanged(Node *node) ProjectTree::instance()->emitNodeSortKeyChanged(node); } -SessionNode *SessionNode::asSessionNode() -{ - return this; -} - QList<ProjectNode*> SessionNode::projectNodes() const { return m_projectNodes; @@ -854,11 +992,11 @@ void SessionNode::addProjectNodes(const QList<ProjectNode*> &projectNodes) QTC_ASSERT(!project->parentFolderNode(), qDebug("Project node has already a parent folder")); project->setParentFolderNode(this); - m_subFolderNodes.append(project); + m_folderNodes.append(project); m_projectNodes.append(project); } - Utils::sort(m_subFolderNodes); + Utils::sort(m_folderNodes); Utils::sort(m_projectNodes); ProjectTree::instance()->emitFoldersAdded(this); @@ -877,7 +1015,7 @@ void SessionNode::removeProjectNodes(const QList<ProjectNode*> &projectNodes) ProjectTree::instance()->emitFoldersAboutToBeRemoved(this, toRemove); auto toRemoveIter = toRemove.constBegin(); - auto folderIter = m_subFolderNodes.begin(); + auto folderIter = m_folderNodes.begin(); auto projectIter = m_projectNodes.begin(); for (; toRemoveIter != toRemove.constEnd(); ++toRemoveIter) { while (*projectIter != *toRemoveIter) { @@ -887,11 +1025,11 @@ void SessionNode::removeProjectNodes(const QList<ProjectNode*> &projectNodes) } while (*folderIter != *toRemoveIter) { ++folderIter; - QTC_ASSERT(folderIter != m_subFolderNodes.end(), + QTC_ASSERT(folderIter != m_folderNodes.end(), qDebug("Project to remove is not part of specified folder!")); } projectIter = m_projectNodes.erase(projectIter); - folderIter = m_subFolderNodes.erase(folderIter); + folderIter = m_folderNodes.erase(folderIter); } ProjectTree::instance()->emitFoldersRemoved(this); diff --git a/src/plugins/projectexplorer/projectnodes.h b/src/plugins/projectexplorer/projectnodes.h index a4b4ea4765..3de04ecf13 100644 --- a/src/plugins/projectexplorer/projectnodes.h +++ b/src/plugins/projectexplorer/projectnodes.h @@ -29,12 +29,15 @@ #include <utils/fileutils.h> +#include <QFutureInterface> #include <QIcon> #include <QObject> #include <QStringList> #include <QDebug> +#include <functional> + QT_BEGIN_NAMESPACE class QFileInfo; QT_END_NAMESPACE @@ -42,24 +45,24 @@ QT_END_NAMESPACE namespace ProjectExplorer { class RunConfiguration; -enum NodeType { - FileNodeType = 1, - FolderNodeType, - VirtualFolderNodeType, - ProjectNodeType, - SessionNodeType +enum class NodeType : quint16 { + File = 1, + Folder, + VirtualFolder, + Project, + Session }; // File types common for qt projects -enum FileType { - UnknownFileType = 0, - HeaderType, - SourceType, - FormType, - StateChartType, - ResourceType, - QMLType, - ProjectFileType, +enum class FileType : quint16 { + Unknown = 0, + Header, + Source, + Form, + StateChart, + Resource, + QML, + Project, FileTypeSize }; @@ -104,42 +107,70 @@ class SessionManager; class PROJECTEXPLORER_EXPORT Node { public: - virtual ~Node(); + enum PriorityLevel { + DefaultPriority = 0, + DefaultFilePriority = 100000, + DefaultFolderPriority = 200000, + DefaultVirtualFolderPriority = 300000, + DefaultProjectPriority = 400000, + DefaultProjectFilePriority = 500000 + }; + + virtual ~Node() = default; NodeType nodeType() const; - ProjectNode *projectNode() const; // managing project + int priority() const; + + ProjectNode *parentProjectNode() const; // parent project, will be nullptr for the top-level project FolderNode *parentFolderNode() const; // parent folder or project + + ProjectNode *managingProject(); // project managing this node. + // result is nullptr if node is the SessionNode + // or node if node is a ProjectNode directly below SessionNode + // or node->parentProjectNode() for all other cases. + const ProjectNode *managingProject() const; // see above. + const Utils::FileName &filePath() const; // file system path int line() const; virtual QString displayName() const; virtual QString tooltip() const; - virtual bool isEnabled() const; + bool isEnabled() const; virtual QList<ProjectAction> supportedActions(Node *node) const; + void setEnabled(bool enabled); void setAbsoluteFilePathAndLine(const Utils::FileName &filePath, int line); void emitNodeUpdated(); - virtual FileNode *asFileNode(); - virtual FolderNode *asFolderNode(); - virtual ProjectNode *asProjectNode(); - virtual SessionNode *asSessionNode(); + virtual Node *trim(const QSet<Node *> &keepers); + + virtual FileNode *asFileNode() { return nullptr; } + virtual const FileNode *asFileNode() const { return nullptr; } + virtual FolderNode *asFolderNode() { return nullptr; } + virtual const FolderNode *asFolderNode() const { return nullptr; } + virtual ProjectNode *asProjectNode() { return nullptr; } + virtual const ProjectNode *asProjectNode() const { return nullptr; } + virtual SessionNode *asSessionNode() { return nullptr; } + virtual const SessionNode *asSessionNode() const { return nullptr; } + + static bool sortByPath(Node *a, Node *b); protected: Node(NodeType nodeType, const Utils::FileName &filePath, int line = -1); - void setProjectNode(ProjectNode *project); + void setPriority(int priority); void setParentFolderNode(FolderNode *parentFolder); void emitNodeSortKeyAboutToChange(); void emitNodeSortKeyChanged(); private: - NodeType m_nodeType; - int m_line; - ProjectNode *m_projectNode = nullptr; - FolderNode *m_folderNode = nullptr; + FolderNode *m_parentFolderNode = nullptr; Utils::FileName m_filePath; + int m_line = -1; + int m_priority = DefaultPriority; + const NodeType m_nodeType; + bool m_isEnabled = true; }; class PROJECTEXPLORER_EXPORT FileNode : public Node @@ -150,7 +181,12 @@ public: FileType fileType() const; bool isGenerated() const; - FileNode *asFileNode() override; + FileNode *asFileNode() final { return this; } + const FileNode *asFileNode() const final { return this; } + + static QList<FileNode *> scanForFiles(const Utils::FileName &directory, + const std::function<FileNode *(const Utils::FileName &fileName)> factory, + QFutureInterface<QList<FileNode *>> *future = nullptr); private: // managed by ProjectNode @@ -165,18 +201,24 @@ private: class PROJECTEXPLORER_EXPORT FolderNode : public Node { public: - explicit FolderNode(const Utils::FileName &folderPath, NodeType nodeType = FolderNodeType, + explicit FolderNode(const Utils::FileName &folderPath, NodeType nodeType = NodeType::Folder, const QString &displayName = QString()); ~FolderNode() override; QString displayName() const override; QIcon icon() const; + Node *trim(const QSet<Node *> &keepers) override; + QList<FileNode *> fileNodes() const; + FileNode *fileNode(const Utils::FileName &file) const; + FileNode *recursiveFileNode(const Utils::FileName &file) const; QList<FileNode *> recursiveFileNodes() const; - QList<FolderNode *> subFolderNodes() const; - FolderNode *findOrCreateSubFolderNode(const QString &directory); - void buildTree(QList<FileNode *> &files); + QList<FolderNode *> folderNodes() const; + FolderNode *folderNode(const Utils::FileName &directory) const; + FolderNode *recursiveFindOrCreateFolderNode(const QString &directory, + const Utils::FileName &overrideBaseDir = Utils::FileName()); + void buildTree(QList<FileNode *> &files, const Utils::FileName &overrideBaseDir = Utils::FileName()); virtual void accept(NodesVisitor *visitor); @@ -213,28 +255,25 @@ public: void addFolderNodes(const QList<FolderNode*> &subFolders); void removeFolderNodes(const QList<FolderNode*> &subFolders); - FolderNode *asFolderNode() override; + FolderNode *asFolderNode() final { return this; } + const FolderNode *asFolderNode() const final { return this; } protected: - QList<FolderNode*> m_subFolderNodes; + QList<FolderNode*> m_folderNodes; QList<FileNode*> m_fileNodes; private: - // managed by ProjectNode - friend class ProjectNode; QString m_displayName; mutable QIcon m_icon; + + // managed by ProjectNode + friend class ProjectNode; }; class PROJECTEXPLORER_EXPORT VirtualFolderNode : public FolderNode { public: explicit VirtualFolderNode(const Utils::FileName &folderPath, int priority); - - int priority() const; - -private: - int m_priority; }; // Documentation inside. @@ -243,9 +282,6 @@ class PROJECTEXPLORER_EXPORT ProjectNode : public FolderNode public: QString vcsTopic() const; - // all subFolders that are projects - QList<ProjectNode*> subProjectNodes() const; - virtual bool canAddSubProject(const QString &proFilePath) const; virtual bool addSubProjects(const QStringList &proFilePaths); virtual bool removeSubProjects(const QStringList &proFilePaths); @@ -263,14 +299,16 @@ public: void accept(NodesVisitor *visitor) override; - bool isEnabled() const override { return true; } - - // to be called in implementation of - // the corresponding public functions + ProjectNode *projectNode(const Utils::FileName &file) const; + // all subFolders that are projects + QList<ProjectNode*> projectNodes() const; void addProjectNodes(const QList<ProjectNode*> &subProjects); void removeProjectNodes(const QList<ProjectNode*> &subProjects); - ProjectNode *asProjectNode() override; + ProjectNode *asProjectNode() final { return this; } + const ProjectNode *asProjectNode() const final { return this; } + + Node *trim(const QSet<Node *> &keepers) override; protected: // this is just the in-memory representation, a subclass @@ -278,7 +316,7 @@ protected: explicit ProjectNode(const Utils::FileName &projectFilePath); private: - QList<ProjectNode*> m_subProjectNodes; + QList<ProjectNode*> m_projectNodes; // let SessionNode call setParentFolderNode friend class SessionNode; @@ -299,12 +337,12 @@ public: void accept(NodesVisitor *visitor) override; - bool isEnabled() const override { return true; } - bool showInSimpleTree() const override; void projectDisplayNameChanged(Node *node); - SessionNode *asSessionNode() override; + SessionNode *asSessionNode() final { return this; } + const SessionNode *asSessionNode() const final { return this; } + protected: void addProjectNodes(const QList<ProjectNode*> &projectNodes); void removeProjectNodes(const QList<ProjectNode*> &projectNodes); diff --git a/src/plugins/projectexplorer/projecttree.cpp b/src/plugins/projectexplorer/projecttree.cpp index 3e89539aac..1ec856c651 100644 --- a/src/plugins/projectexplorer/projecttree.cpp +++ b/src/plugins/projectexplorer/projecttree.cpp @@ -341,10 +341,10 @@ void ProjectTree::emitFoldersAboutToBeRemoved(FolderNode *parentFolder, const QL while (n) { if (FolderNode *fn = n->asFolderNode()) { if (staleFolders.contains(fn)) { - ProjectNode *pn = n->projectNode(); + ProjectNode *pn = n->parentProjectNode(); // Make sure the node we are switching too isn't going to be removed also while (staleFolders.contains(pn)) - pn = pn->parentFolderNode()->projectNode(); + pn = pn->parentFolderNode()->parentProjectNode(); m_resetCurrentNodeFolder = true; break; } @@ -492,22 +492,22 @@ void ProjectTree::showContextMenu(ProjectTreeWidget *focus, const QPoint &global if (!node) node = SessionManager::sessionNode(); - if (node->nodeType() != SessionNodeType) { + if (node->nodeType() != NodeType::Session) { Project *project = SessionManager::projectForNode(node); emit s_instance->aboutToShowContextMenu(project, node); switch (node->nodeType()) { - case ProjectNodeType: + case NodeType::Project: if (node->parentFolderNode() == SessionManager::sessionNode()) contextMenu = Core::ActionManager::actionContainer(Constants::M_PROJECTCONTEXT)->menu(); else contextMenu = Core::ActionManager::actionContainer(Constants::M_SUBPROJECTCONTEXT)->menu(); break; - case VirtualFolderNodeType: - case FolderNodeType: + case NodeType::VirtualFolder: + case NodeType::Folder: contextMenu = Core::ActionManager::actionContainer(Constants::M_FOLDERCONTEXT)->menu(); break; - case FileNodeType: + case NodeType::File: contextMenu = Core::ActionManager::actionContainer(Constants::M_FILECONTEXT)->menu(); break; default: diff --git a/src/plugins/projectexplorer/projecttreewidget.cpp b/src/plugins/projectexplorer/projecttreewidget.cpp index 233b0c95ca..ba2f304c7a 100644 --- a/src/plugins/projectexplorer/projecttreewidget.cpp +++ b/src/plugins/projectexplorer/projecttreewidget.cpp @@ -533,7 +533,7 @@ void ProjectTreeWidget::initView() void ProjectTreeWidget::openItem(const QModelIndex &mainIndex) { Node *node = m_model->nodeForIndex(mainIndex); - if (node->nodeType() != FileNodeType) + if (node->nodeType() != NodeType::File) return; IEditor *editor = EditorManager::openEditor(node->filePath().toString()); if (editor && node->line() >= 0) diff --git a/src/plugins/projectexplorer/projectwizardpage.cpp b/src/plugins/projectexplorer/projectwizardpage.cpp index 7d7980a965..24e1d79d9d 100644 --- a/src/plugins/projectexplorer/projectwizardpage.cpp +++ b/src/plugins/projectexplorer/projectwizardpage.cpp @@ -170,7 +170,7 @@ BestNodeSelector::BestNodeSelector(const QString &commonDirectory, const QString void BestNodeSelector::inspect(AddNewTree *tree, bool isContextNode) { FolderNode *node = tree->node(); - if (node->nodeType() == ProjectNodeType) { + if (node->nodeType() == NodeType::Project) { if (static_cast<ProjectNode *>(node)->deploysFolder(m_commonDirectory)) { m_deploys = true; m_deployText += tree->displayName() + QLatin1Char('\n'); @@ -232,7 +232,7 @@ static inline AddNewTree *createNoneNode(BestNodeSelector *selector) static inline AddNewTree *buildAddProjectTree(ProjectNode *root, const QString &projectPath, Node *contextNode, BestNodeSelector *selector) { QList<AddNewTree *> children; - foreach (ProjectNode *pn, root->subProjectNodes()) { + foreach (ProjectNode *pn, root->projectNodes()) { AddNewTree *child = buildAddProjectTree(pn, projectPath, contextNode, selector); if (child) children.append(child); @@ -269,7 +269,7 @@ static inline AddNewTree *buildAddFilesTree(FolderNode *root, const QStringList Node *contextNode, BestNodeSelector *selector) { QList<AddNewTree *> children; - foreach (FolderNode *fn, root->subFolderNodes()) { + foreach (FolderNode *fn, root->folderNodes()) { AddNewTree *child = buildAddFilesTree(fn, files, contextNode, selector); if (child) children.append(child); diff --git a/src/plugins/projectexplorer/session.cpp b/src/plugins/projectexplorer/session.cpp index ee33bc79cc..a470814c56 100644 --- a/src/plugins/projectexplorer/session.cpp +++ b/src/plugins/projectexplorer/session.cpp @@ -610,7 +610,7 @@ Node *SessionManager::nodeForFile(const Utils::FileName &fileName) Node *node = nullptr; foreach (Node *n, nodesForFile(fileName)) { // prefer file nodes - if (!node || (node->nodeType() != FileNodeType && n->nodeType() == FileNodeType)) + if (!node || (node->nodeType() != NodeType::File && n->nodeType() == NodeType::File)) node = n; } return node; diff --git a/src/plugins/pythoneditor/pythoneditorplugin.cpp b/src/plugins/pythoneditor/pythoneditorplugin.cpp index 2aab5cf231..60ed1a9d7f 100644 --- a/src/plugins/pythoneditor/pythoneditorplugin.cpp +++ b/src/plugins/pythoneditor/pythoneditorplugin.cpp @@ -598,7 +598,7 @@ class PythonFileNode : public FileNode { public: PythonFileNode(const Utils::FileName &filePath, const QString &nodeDisplayName) - : FileNode(filePath, SourceType, false) + : FileNode(filePath, FileType::Source, false) , m_displayName(nodeDisplayName) {} diff --git a/src/plugins/qbsprojectmanager/qbsnodes.cpp b/src/plugins/qbsprojectmanager/qbsnodes.cpp index dd847f5bdb..27796ed6d4 100644 --- a/src/plugins/qbsprojectmanager/qbsnodes.cpp +++ b/src/plugins/qbsprojectmanager/qbsnodes.cpp @@ -87,7 +87,7 @@ QIcon QbsProductNode::m_productIcon; static QbsProjectNode *parentQbsProjectNode(ProjectExplorer::Node *node) { - for (ProjectExplorer::FolderNode *pn = node->projectNode(); pn; pn = pn->parentFolderNode()) { + for (ProjectExplorer::FolderNode *pn = node->managingProject(); pn; pn = pn->parentProjectNode()) { QbsProjectNode *prjNode = dynamic_cast<QbsProjectNode *>(pn); if (prjNode) return prjNode; @@ -252,7 +252,7 @@ static QList<ProjectExplorer::ProjectAction> supportedNodeActions(ProjectExplore return actions; if (managesFiles) actions << ProjectExplorer::AddNewFile << ProjectExplorer::AddExistingFile; - if (node->nodeType() == ProjectExplorer::FileNodeType + if (node->nodeType() == ProjectExplorer::NodeType::File && !project->qbsProject().buildSystemFiles().contains(node->filePath().toString())) { actions << ProjectExplorer::RemoveFile << ProjectExplorer::Rename; } @@ -323,21 +323,13 @@ QbsGroupNode::QbsGroupNode(const qbs::GroupData &grp, const QString &productPath setIcon(m_groupIcon); QbsFileNode *idx = new QbsFileNode(Utils::FileName::fromString(grp.location().filePath()), - ProjectExplorer::ProjectFileType, false, + ProjectExplorer::FileType::Project, false, grp.location().line()); addFileNodes(QList<ProjectExplorer::FileNode *>() << idx); updateQbsGroupData(grp, productPath, true, true); } -bool QbsGroupNode::isEnabled() const -{ - if (!parentFolderNode() || !m_qbsGroupData.isValid()) - return false; - return static_cast<QbsProductNode *>(parentFolderNode())->isEnabled() - && m_qbsGroupData.isEnabled(); -} - QList<ProjectExplorer::ProjectAction> QbsGroupNode::supportedActions(ProjectExplorer::Node *node) const { return supportedNodeActions(node, true); @@ -413,6 +405,8 @@ void QbsGroupNode::updateQbsGroupData(const qbs::GroupData &grp, const QString & bool groupIsEnabled = productIsEnabled && grp.isEnabled(); bool updateExisting = groupWasEnabled != groupIsEnabled; + setEnabled(groupIsEnabled); + m_productPath = productPath; m_qbsGroupData = grp; @@ -481,8 +475,8 @@ void QbsGroupNode::setupFolder(ProjectExplorer::FolderNode *root, QList<ProjectExplorer::FileNode *> filesToAdd; QList<ProjectExplorer::FolderNode *> foldersToRemove; - foreach (ProjectExplorer::FolderNode *fn, root->subFolderNodes()) { - if (fn->nodeType() == ProjectExplorer::ProjectNodeType) + foreach (ProjectExplorer::FolderNode *fn, root->folderNodes()) { + if (fn->nodeType() == ProjectExplorer::NodeType::Project) continue; // Skip ProjectNodes mixed into the folders... const auto * const qbsFolder = dynamic_cast<QbsFolderNode *>(fn); if (qbsFolder && qbsFolder->isGeneratedFilesFolder()) @@ -493,8 +487,8 @@ void QbsGroupNode::setupFolder(ProjectExplorer::FolderNode *root, foreach (FileTreeNode *c, fileTree->children) { Utils::FileName path = Utils::FileName::fromString(c->path()); const ProjectExplorer::FileType newFileType = - fileTypeHash.value(c->path(), ProjectExplorer::UnknownFileType); - const bool isQrcFile = newFileType == ProjectExplorer::ResourceType; + fileTypeHash.value(c->path(), ProjectExplorer::FileType::Unknown); + const bool isQrcFile = newFileType == ProjectExplorer::FileType::Resource; // Handle files: if (c->isFile() && !isQrcFile) { @@ -517,7 +511,7 @@ void QbsGroupNode::setupFolder(ProjectExplorer::FolderNode *root, continue; } else { ProjectExplorer::FolderNode *fn = 0; - foreach (ProjectExplorer::FolderNode *f, root->subFolderNodes()) { + foreach (ProjectExplorer::FolderNode *f, root->folderNodes()) { // There can be one match only here! if (f->filePath() != path) continue; @@ -530,7 +524,7 @@ void QbsGroupNode::setupFolder(ProjectExplorer::FolderNode *root, fn = new ResourceTopLevelNode(Utils::FileName::fromString(c->path()), QString(), root); } else { fn = new QbsFolderNode(Utils::FileName::fromString(c->path()), - ProjectExplorer::FolderNodeType, + ProjectExplorer::NodeType::Folder, displayNameFromPath(c->path(), baseDir), false); } root->addFolderNodes(QList<FolderNode *>() << fn); @@ -555,23 +549,23 @@ void QbsGroupNode::setupFolder(ProjectExplorer::FolderNode *root, ProjectExplorer::FileType QbsGroupNode::fileType(const qbs::ArtifactData &artifact) { - QTC_ASSERT(artifact.isValid(), return ProjectExplorer::UnknownFileType); + QTC_ASSERT(artifact.isValid(), return ProjectExplorer::FileType::Unknown); if (artifact.fileTags().contains(QLatin1String("c")) || artifact.fileTags().contains(QLatin1String("cpp")) || artifact.fileTags().contains(QLatin1String("objc")) || artifact.fileTags().contains(QLatin1String("objcpp"))) { - return ProjectExplorer::SourceType; + return ProjectExplorer::FileType::Source; } if (artifact.fileTags().contains(QLatin1String("hpp"))) - return ProjectExplorer::HeaderType; + return ProjectExplorer::FileType::Header; if (artifact.fileTags().contains(QLatin1String("qrc"))) - return ProjectExplorer::ResourceType; + return ProjectExplorer::FileType::Resource; if (artifact.fileTags().contains(QLatin1String("ui"))) - return ProjectExplorer::FormType; + return ProjectExplorer::FileType::Form; if (artifact.fileTags().contains(QLatin1String("scxml"))) - return ProjectExplorer::StateChartType; - return ProjectExplorer::UnknownFileType; + return ProjectExplorer::FileType::StateChart; + return ProjectExplorer::FileType::Unknown; } // -------------------------------------------------------------------- @@ -581,7 +575,7 @@ ProjectExplorer::FileType QbsGroupNode::fileType(const qbs::ArtifactData &artifa QbsProductNode::QbsProductNode(const qbs::Project &project, const qbs::ProductData &prd) : QbsBaseProjectNode(Utils::FileName::fromString(prd.location().filePath())), m_generatedFilesNode(new QbsFolderNode(Utils::FileName::fromString(prd.buildDirectory()), - ProjectExplorer::FolderNodeType, + ProjectExplorer::NodeType::Folder, QCoreApplication::translate("QbsProductNode", "Generated files"), true)) { if (m_productIcon.isNull()) @@ -591,18 +585,13 @@ QbsProductNode::QbsProductNode(const qbs::Project &project, const qbs::ProductDa addFolderNodes(QList<ProjectExplorer::FolderNode *>() << m_generatedFilesNode); auto idx = new QbsFileNode(Utils::FileName::fromString(prd.location().filePath()), - ProjectExplorer::ProjectFileType, false, + ProjectExplorer::FileType::Project, false, prd.location().line()); addFileNodes(QList<ProjectExplorer::FileNode *>() << idx); setQbsProductData(project, prd); } -bool QbsProductNode::isEnabled() const -{ - return m_qbsProductData.isEnabled(); -} - bool QbsProductNode::showInSimpleTree() const { return true; @@ -675,6 +664,8 @@ void QbsProductNode::setQbsProductData(const qbs::Project &project, const qbs::P bool productIsEnabled = prd.isEnabled(); bool updateExisting = productWasEnabled != productIsEnabled; + setEnabled(prd.isEnabled()); + setDisplayName(QbsProject::productDisplayName(project, prd)); setAbsoluteFilePathAndLine(Utils::FileName::fromString(prd.location().filePath()), line()); const QString &productPath = QFileInfo(prd.location().filePath()).absolutePath(); @@ -691,7 +682,7 @@ void QbsProductNode::setQbsProductData(const qbs::Project &project, const qbs::P prd.location().line()); QList<ProjectExplorer::ProjectNode *> toAdd; - QList<ProjectExplorer::ProjectNode *> toRemove = subProjectNodes(); + QList<ProjectExplorer::ProjectNode *> toRemove = projectNodes(); foreach (const qbs::GroupData &grp, prd.groups()) { if (grp.name() == prd.name() && grp.location() == prd.location()) { @@ -728,7 +719,7 @@ void QbsProductNode::setQbsProductData(const qbs::Project &project, const qbs::P QList<ProjectExplorer::RunConfiguration *> QbsProductNode::runConfigurations() const { QList<ProjectExplorer::RunConfiguration *> result; - QbsProjectNode *pn = dynamic_cast<QbsProjectNode *>(projectNode()); + auto pn = dynamic_cast<const QbsProjectNode *>(managingProject()); if (!isEnabled() || !pn || m_qbsProductData.targetExecutable().isEmpty()) return result; @@ -745,7 +736,7 @@ QList<ProjectExplorer::RunConfiguration *> QbsProductNode::runConfigurations() c QbsGroupNode *QbsProductNode::findGroupNode(const QString &name) { - foreach (ProjectExplorer::ProjectNode *n, subProjectNodes()) { + foreach (ProjectExplorer::ProjectNode *n, projectNodes()) { QbsGroupNode *qn = static_cast<QbsGroupNode *>(n); if (qn->qbsGroupData().name() == name) return qn; @@ -771,7 +762,7 @@ QbsProjectNode::~QbsProjectNode() void QbsProjectNode::update(const qbs::Project &qbsProject, const qbs::ProjectData &prjData) { QList<ProjectExplorer::ProjectNode *> toAdd; - QList<ProjectExplorer::ProjectNode *> toRemove = subProjectNodes(); + QList<ProjectExplorer::ProjectNode *> toRemove = projectNodes(); foreach (const qbs::ProjectData &subData, prjData.subProjects()) { QbsProjectNode *qn = findProjectNode(subData.name()); @@ -828,12 +819,12 @@ void QbsProjectNode::ctor() setIcon(m_projectIcon); addFileNodes(QList<ProjectExplorer::FileNode *>() - << new ProjectExplorer::FileNode(filePath(), ProjectExplorer::ProjectFileType, false)); + << new ProjectExplorer::FileNode(filePath(), ProjectExplorer::FileType::Project, false)); } QbsProductNode *QbsProjectNode::findProductNode(const QString &uniqueName) { - foreach (ProjectExplorer::ProjectNode *n, subProjectNodes()) { + foreach (ProjectExplorer::ProjectNode *n, projectNodes()) { QbsProductNode *qn = dynamic_cast<QbsProductNode *>(n); if (qn && QbsProject::uniqueProductName(qn->qbsProductData()) == uniqueName) return qn; @@ -843,7 +834,7 @@ QbsProductNode *QbsProjectNode::findProductNode(const QString &uniqueName) QbsProjectNode *QbsProjectNode::findProjectNode(const QString &name) { - foreach (ProjectExplorer::ProjectNode *n, subProjectNodes()) { + foreach (ProjectExplorer::ProjectNode *n, projectNodes()) { QbsProjectNode *qn = dynamic_cast<QbsProjectNode *>(n); if (qn && qn->qbsProjectData().name() == name) return qn; @@ -859,7 +850,7 @@ QbsRootProjectNode::QbsRootProjectNode(QbsProject *project) : QbsProjectNode(project->projectFilePath()), m_project(project), m_buildSystemFiles(new ProjectExplorer::FolderNode(project->projectDirectory(), - ProjectExplorer::FolderNodeType, + ProjectExplorer::NodeType::Folder, QCoreApplication::translate("QbsRootProjectNode", "Qbs files"))) { addFolderNodes(QList<FolderNode *>() << m_buildSystemFiles); diff --git a/src/plugins/qbsprojectmanager/qbsnodes.h b/src/plugins/qbsprojectmanager/qbsnodes.h index cfc86deb89..846b93f8ff 100644 --- a/src/plugins/qbsprojectmanager/qbsnodes.h +++ b/src/plugins/qbsprojectmanager/qbsnodes.h @@ -92,7 +92,6 @@ class QbsGroupNode : public QbsBaseProjectNode public: QbsGroupNode(const qbs::GroupData &grp, const QString &productPath); - bool isEnabled() const override; QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const override; bool addFiles(const QStringList &filePaths, QStringList *notAdded = 0) override; bool removeFiles(const QStringList &filePaths, QStringList *notRemoved = 0) override; @@ -130,7 +129,6 @@ class QbsProductNode : public QbsBaseProjectNode public: explicit QbsProductNode(const qbs::Project &project, const qbs::ProductData &prd); - bool isEnabled() const override; bool showInSimpleTree() const override; QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const override; bool addFiles(const QStringList &filePaths, QStringList *notAdded = 0) override; diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp index 04cc9c25ae..b078fc6b7e 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp +++ b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp @@ -258,8 +258,10 @@ void QbsProjectManagerPlugin::updateContextActions() && m_selectedProject && !m_selectedProject->isParsing() && m_selectedNode && m_selectedNode->isEnabled(); - bool isFile = m_selectedProject && m_selectedNode && (m_selectedNode->nodeType() == FileNodeType); - bool isProduct = m_selectedProject && m_selectedNode && dynamic_cast<QbsProductNode *>(m_selectedNode->projectNode()); + bool isFile = m_selectedProject && m_selectedNode && (m_selectedNode->nodeType() == NodeType::File); + bool isProduct = m_selectedProject + && m_selectedNode + && dynamic_cast<QbsProductNode *>(m_selectedNode->parentProjectNode()); QbsProjectNode *subproject = dynamic_cast<QbsProjectNode *>(m_selectedNode); bool isSubproject = m_selectedProject && subproject && subproject != m_selectedProject->rootProjectNode(); @@ -293,10 +295,10 @@ void QbsProjectManagerPlugin::updateBuildActions() && !m_editorProject->isParsing(); fileName = m_editorNode->filePath().fileName(); - fileVisible = m_editorProject && m_editorNode && dynamic_cast<QbsBaseProjectNode *>(m_editorNode->projectNode()); + fileVisible = m_editorProject && m_editorNode && dynamic_cast<QbsBaseProjectNode *>(m_editorNode->parentProjectNode()); QbsProductNode *productNode - = dynamic_cast<QbsProductNode *>(m_editorNode ? m_editorNode->projectNode() : 0); + = dynamic_cast<QbsProductNode *>(m_editorNode ? m_editorNode->parentProjectNode() : 0); if (productNode) { productVisible = true; productName = productNode->displayName(); @@ -393,7 +395,7 @@ void QbsProjectManagerPlugin::buildProduct() if (!m_editorProject || !m_editorNode) return; - QbsProductNode *product = dynamic_cast<QbsProductNode *>(m_editorNode->projectNode()); + QbsProductNode *product = dynamic_cast<QbsProductNode *>(m_editorNode->parentProjectNode()); if (!product) return; @@ -423,7 +425,7 @@ void QbsProjectManagerPlugin::buildSubproject() return; QbsProjectNode *subproject = 0; - QbsBaseProjectNode *start = dynamic_cast<QbsBaseProjectNode *>(m_editorNode->projectNode()); + QbsBaseProjectNode *start = dynamic_cast<QbsBaseProjectNode *>(m_editorNode->parentProjectNode()); while (start && start != m_editorProject->rootProjectNode()) { QbsProjectNode *tmp = dynamic_cast<QbsProjectNode *>(start); if (tmp) { diff --git a/src/plugins/qmakeprojectmanager/profileeditor.cpp b/src/plugins/qmakeprojectmanager/profileeditor.cpp index ac2b0f0e29..d9e1242359 100644 --- a/src/plugins/qmakeprojectmanager/profileeditor.cpp +++ b/src/plugins/qmakeprojectmanager/profileeditor.cpp @@ -195,11 +195,11 @@ ProFileEditorFactory::ProFileEditorFactory() const QString defaultOverlay = QLatin1String(ProjectExplorer::Constants::FILEOVERLAY_QT); Core::FileIconProvider::registerIconOverlayForSuffix( - creatorTheme()->imageFile(Theme::IconOverlayPro, defaultOverlay).toLatin1().data(), "pro"); + creatorTheme()->imageFile(Theme::IconOverlayPro, defaultOverlay), "pro"); Core::FileIconProvider::registerIconOverlayForSuffix( - creatorTheme()->imageFile(Theme::IconOverlayPri, defaultOverlay).toLatin1().data(), "pri"); + creatorTheme()->imageFile(Theme::IconOverlayPri, defaultOverlay), "pri"); Core::FileIconProvider::registerIconOverlayForSuffix( - creatorTheme()->imageFile(Theme::IconOverlayPrf, defaultOverlay).toLatin1().data(), "prf"); + creatorTheme()->imageFile(Theme::IconOverlayPrf, defaultOverlay), "prf"); } } // namespace Internal diff --git a/src/plugins/qmakeprojectmanager/qmakenodes.cpp b/src/plugins/qmakeprojectmanager/qmakenodes.cpp index e25359110e..0881b6313a 100644 --- a/src/plugins/qmakeprojectmanager/qmakenodes.cpp +++ b/src/plugins/qmakeprojectmanager/qmakenodes.cpp @@ -95,19 +95,19 @@ struct FileTypeDataStorage { }; static const FileTypeDataStorage fileTypeDataStorage[] = { - { HeaderType, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Headers"), + { FileType::Header, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Headers"), ProjectExplorer::Constants::FILEOVERLAY_H, "*.h; *.hh; *.hpp; *.hxx;"}, - { SourceType, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Sources"), + { FileType::Source, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Sources"), ProjectExplorer::Constants::FILEOVERLAY_CPP, "*.c; *.cc; *.cpp; *.cp; *.cxx; *.c++;" }, - { FormType, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Forms"), + { FileType::Form, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Forms"), Constants::FILEOVERLAY_UI, "*.ui;" }, - { StateChartType, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "State charts"), + { FileType::StateChart, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "State charts"), ProjectExplorer::Constants::FILEOVERLAY_SCXML, "*.scxml;" }, - { ResourceType, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Resources"), + { FileType::Resource, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Resources"), ProjectExplorer::Constants::FILEOVERLAY_QRC, "*.qrc;" }, - { QMLType, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "QML"), + { FileType::QML, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "QML"), ProjectExplorer::Constants::FILEOVERLAY_QML, "*.qml;" }, - { UnknownFileType, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Other files"), + { FileType::Unknown, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Other files"), ProjectExplorer::Constants::FILEOVERLAY_UNKNOWN, "*;" } }; @@ -129,7 +129,7 @@ class QmakeNodeStaticData { public: class FileTypeData { public: - FileTypeData(FileType t = UnknownFileType, + FileTypeData(FileType t = FileType::Unknown, const QString &tN = QString(), const QString &aff = QString(), const QIcon &i = QIcon()) : @@ -321,8 +321,8 @@ struct InternalNode QList<InternalNode *> virtualfolders; QMap<QString, InternalNode *> subnodes; FileNameList files; - FileType type = UnknownFileType; - int priority = 0; + FileType type = FileType::Unknown; + int priority = Node::DefaultVirtualFolderPriority; QString displayName; QString typeName; QString addFileFilter; @@ -426,7 +426,7 @@ struct InternalNode newNode = new FolderNode(FileName::fromString(node->fullPath)); } else { auto n = new ProVirtualFolderNode(FileName::fromString(node->fullPath), - node->priority, node->typeName); + node->priority, node->typeName); n->setAddFileFilter(node->addFileFilter); newNode = n; } @@ -440,15 +440,15 @@ struct InternalNode // Makes the projectNode's subtree below the given folder match this internal node's subtree void updateSubFolders(FolderNode *folder) { - if (type == ResourceType) + if (type == FileType::Resource) updateResourceFiles(folder); else updateFiles(folder, type); // updateFolders QMultiMap<QString, FolderNode *> existingFolderNodes; - foreach (FolderNode *node, folder->subFolderNodes()) - if (node->nodeType() != ProjectNodeType && !dynamic_cast<ResourceEditor::ResourceTopLevelNode *>(node)) + foreach (FolderNode *node, folder->folderNodes()) + if (node->nodeType() != NodeType::Project && !dynamic_cast<ResourceEditor::ResourceTopLevelNode *>(node)) existingFolderNodes.insert(node->filePath().toString(), node); QList<FolderNode *> foldersToRemove; @@ -466,7 +466,7 @@ struct InternalNode QMultiMap<QString, FolderNode *>::const_iterator oldit = existingFolderNodes.constFind(path); while (oldit != existingFolderNodes.constEnd() && oldit.key() == path) { - if (oldit.value()->nodeType() == VirtualFolderNodeType) { + if (oldit.value()->nodeType() == NodeType::VirtualFolder) { VirtualFolderNode *vfn = dynamic_cast<VirtualFolderNode *>(oldit.value()); if (vfn->priority() == (*it)->priority) { found = true; @@ -495,7 +495,7 @@ struct InternalNode QMultiMap<QString, FolderNode *>::const_iterator oldit = existingFolderNodes.constFind(path); while (oldit != existingFolderNodes.constEnd() && oldit.key() == path) { - if (oldit.value()->nodeType() == FolderNodeType) { + if (oldit.value()->nodeType() == NodeType::Folder) { found = true; break; } @@ -560,7 +560,7 @@ struct InternalNode void updateResourceFiles(FolderNode *folder) { QList<FolderNode *> existingResourceNodes; // for resource special handling - foreach (FolderNode *folderNode, folder->subFolderNodes()) { + foreach (FolderNode *folderNode, folder->folderNodes()) { if (ResourceEditor::ResourceTopLevelNode *rn = dynamic_cast<ResourceEditor::ResourceTopLevelNode *>(folderNode)) existingResourceNodes << rn; } @@ -578,7 +578,7 @@ struct InternalNode nodesToAdd.reserve(resourcesToAdd.size()); foreach (const FileName &file, resourcesToAdd) { - auto vfs = static_cast<QmakePriFileNode *>(folder->projectNode())->m_project->qmakeVfs(); + auto vfs = static_cast<QmakePriFileNode *>(folder->parentProjectNode())->m_project->qmakeVfs(); QString contents; // Prefer the cumulative file if it's non-empty, based on the assumption // that it contains more "stuff". @@ -715,7 +715,7 @@ void QmakePriFileNode::update(const Internal::PriFileEvalResult &result) { // add project file node if (m_fileNodes.isEmpty()) - addFileNodes(QList<FileNode *>() << new FileNode(m_projectFilePath, ProjectFileType, false)); + addFileNodes(QList<FileNode *>() << new FileNode(m_projectFilePath, FileType::Project, false)); m_recursiveEnumerateFiles = result.recursiveEnumerateFiles; watchFolders(result.folders.toSet()); @@ -739,7 +739,7 @@ void QmakePriFileNode::update(const Internal::PriFileEvalResult &result) subfolder->fullPath = m_projectDir; subfolder->typeName = fileTypes.at(i).typeName; subfolder->addFileFilter = fileTypes.at(i).addFileFilter; - subfolder->priority = -i; + subfolder->priority = Node::DefaultVirtualFolderPriority - i; subfolder->displayName = fileTypes.at(i).typeName; contents.virtualfolders.append(subfolder); // create the hierarchy with subdirectories @@ -816,7 +816,7 @@ bool QmakePriFileNode::folderChanged(const QString &changedFolder, const QSet<Fi subfolder->icon = fileTypes.at(i).icon; subfolder->fullPath = m_projectDir; subfolder->typeName = fileTypes.at(i).typeName; - subfolder->priority = -i; + subfolder->priority = Node::DefaultVirtualFolderPriority - i; subfolder->displayName = fileTypes.at(i).typeName; contents.virtualfolders.append(subfolder); // create the hierarchy with subdirectories @@ -855,7 +855,7 @@ QList<RunConfiguration *> QmakePriFileNode::runConfigurations() const QList<QmakePriFileNode *> QmakePriFileNode::subProjectNodesExact() const { QList<QmakePriFileNode *> nodes; - foreach (ProjectNode *node, subProjectNodes()) { + foreach (ProjectNode *node, projectNodes()) { QmakePriFileNode *n = dynamic_cast<QmakePriFileNode *>(node); if (n && n->includedInExactParse()) nodes << n; @@ -904,12 +904,12 @@ QList<ProjectAction> QmakePriFileNode::supportedActions(Node *node) const actions << RemoveFile; bool addExistingFiles = true; - if (node->nodeType() == VirtualFolderNodeType) { + if (node->nodeType() == NodeType::VirtualFolder) { // A virtual folder, we do what the projectexplorer does FolderNode *folder = node->asFolderNode(); if (folder) { QStringList list; - foreach (FolderNode *f, folder->subFolderNodes()) + foreach (FolderNode *f, folder->folderNodes()) list << f->filePath().toString() + QLatin1Char('/'); if (deploysFolder(Utils::commonPath(list))) addExistingFiles = false; @@ -931,7 +931,7 @@ QList<ProjectAction> QmakePriFileNode::supportedActions(Node *node) const } FileNode *fileNode = node->asFileNode(); - if ((fileNode && fileNode->fileType() != ProjectFileType) + if ((fileNode && fileNode->fileType() != FileType::Project) || dynamic_cast<ResourceEditor::ResourceTopLevelNode *>(node)) { actions << Rename; actions << DuplicateFile; @@ -1110,7 +1110,7 @@ bool QmakePriFileNode::renameFile(const QString &filePath, const QString &newFil FolderNode::AddNewInformation QmakePriFileNode::addNewInformation(const QStringList &files, Node *context) const { Q_UNUSED(files) - return FolderNode::AddNewInformation(filePath().fileName(), context && context->projectNode() == this ? 120 : 90); + return FolderNode::AddNewInformation(filePath().fileName(), context && context->parentProjectNode() == this ? 120 : 90); } bool QmakePriFileNode::priFileWritable(const QString &path) @@ -1345,11 +1345,11 @@ QStringList QmakePriFileNode::varNames(FileType type, QtSupport::ProFileReader * { QStringList vars; switch (type) { - case HeaderType: + case FileType::Header: vars << QLatin1String("HEADERS"); vars << QLatin1String("PRECOMPILED_HEADER"); break; - case SourceType: { + case FileType::Source: { vars << QLatin1String("SOURCES"); QStringList listOfExtraCompilers = readerExact->values(QLatin1String("QMAKE_EXTRA_COMPILERS")); foreach (const QString &var, listOfExtraCompilers) { @@ -1365,19 +1365,19 @@ QStringList QmakePriFileNode::varNames(FileType type, QtSupport::ProFileReader * } break; } - case ResourceType: + case FileType::Resource: vars << QLatin1String("RESOURCES"); break; - case FormType: + case FileType::Form: vars << QLatin1String("FORMS"); break; - case StateChartType: + case FileType::StateChart: vars << QLatin1String("STATECHARTS"); break; - case ProjectFileType: + case FileType::Project: vars << QLatin1String("SUBDIRS"); break; - case QMLType: + case FileType::QML: vars << QLatin1String("OTHER_FILES"); vars << QLatin1String("DISTFILES"); break; @@ -1454,10 +1454,10 @@ QStringList QmakePriFileNode::varNamesForRemoving() QSet<FileName> QmakePriFileNode::filterFilesProVariables(FileType fileType, const QSet<FileName> &files) { - if (fileType != QMLType && fileType != UnknownFileType) + if (fileType != FileType::QML && fileType != FileType::Unknown) return files; QSet<FileName> result; - if (fileType == QMLType) { + if (fileType == FileType::QML) { foreach (const FileName &file, files) if (file.toString().endsWith(QLatin1String(".qml"))) result << file; @@ -1472,9 +1472,9 @@ QSet<FileName> QmakePriFileNode::filterFilesProVariables(FileType fileType, cons QSet<FileName> QmakePriFileNode::filterFilesRecursiveEnumerata(FileType fileType, const QSet<FileName> &files) { QSet<FileName> result; - if (fileType != QMLType && fileType != UnknownFileType) + if (fileType != FileType::QML && fileType != FileType::Unknown) return result; - if (fileType == QMLType) { + if (fileType == FileType::QML) { foreach (const FileName &file, files) if (file.toString().endsWith(QLatin1String(".qml"))) result << file; @@ -1541,7 +1541,7 @@ QmakeProFileNode *QmakeProFileNode::findProFileFor(const FileName &fileName) con { if (fileName == filePath()) return const_cast<QmakeProFileNode *>(this); - foreach (ProjectNode *pn, subProjectNodes()) + foreach (ProjectNode *pn, projectNodes()) if (QmakeProFileNode *qmakeProFileNode = dynamic_cast<QmakeProFileNode *>(pn)) if (QmakeProFileNode *result = qmakeProFileNode->findProFileFor(fileName)) return result; @@ -1635,7 +1635,7 @@ bool QmakeProFileNode::showInSimpleTree() const FolderNode::AddNewInformation QmakeProFileNode::addNewInformation(const QStringList &files, Node *context) const { Q_UNUSED(files) - return AddNewInformation(filePath().fileName(), context && context->projectNode() == this ? 120 : 100); + return AddNewInformation(filePath().fileName(), context && context->parentProjectNode() == this ? 120 : 100); } bool QmakeProFileNode::showInSimpleTree(QmakeProjectType projectType) const @@ -1674,7 +1674,7 @@ QString QmakeProFileNode::singleVariableValue(const QmakeVariable var) const void QmakeProFileNode::setParseInProgressRecursive(bool b) { setParseInProgress(b); - foreach (ProjectNode *subNode, subProjectNodes()) { + foreach (ProjectNode *subNode, projectNodes()) { if (QmakeProFileNode *node = dynamic_cast<QmakeProFileNode *>(subNode)) node->setParseInProgressRecursive(b); } @@ -1691,7 +1691,7 @@ void QmakeProFileNode::setParseInProgress(bool b) void QmakeProFileNode::setValidParseRecursive(bool b) { setValidParse(b); - foreach (ProjectNode *subNode, subProjectNodes()) { + foreach (ProjectNode *subNode, projectNodes()) { if (QmakeProFileNode *node = dynamic_cast<QmakeProFileNode *>(subNode)) node->setValidParseRecursive(b); } @@ -2050,8 +2050,8 @@ void QmakeProFileNode::applyEvaluate(EvalResult *evalResult) // delete files && folders && projects removeFileNodes(fileNodes()); - removeProjectNodes(subProjectNodes()); - removeFolderNodes(subFolderNodes()); + removeProjectNodes(projectNodes()); + removeFolderNodes(folderNodes()); m_projectType = InvalidProject; } @@ -2064,7 +2064,7 @@ void QmakeProFileNode::applyEvaluate(EvalResult *evalResult) if (result->projectType != m_projectType) { // probably all subfiles/projects have changed anyway // delete files && folders && projects - foreach (ProjectNode *projectNode, subProjectNodes()) { + foreach (ProjectNode *projectNode, projectNodes()) { if (QmakeProFileNode *qmakeProFileNode = dynamic_cast<QmakeProFileNode *>(projectNode)) { qmakeProFileNode->setValidParseRecursive(false); qmakeProFileNode->setParseInProgressRecursive(false); @@ -2072,8 +2072,8 @@ void QmakeProFileNode::applyEvaluate(EvalResult *evalResult) } removeFileNodes(fileNodes()); - removeProjectNodes(subProjectNodes()); - removeFolderNodes(subFolderNodes()); + removeProjectNodes(projectNodes()); + removeFolderNodes(folderNodes()); bool changesShowInSimpleTree = showInSimpleTree() ^ showInSimpleTree(result->projectType); @@ -2102,7 +2102,7 @@ void QmakeProFileNode::applyEvaluate(EvalResult *evalResult) IncludedPriFile *tree = toCompare.first().second; toCompare.pop_front(); - QList<ProjectNode*> existingProjectNodes = pn->subProjectNodes(); + QList<ProjectNode*> existingProjectNodes = pn->projectNodes(); Utils::sort(existingProjectNodes, sortByPath); // result is already sorted @@ -2451,7 +2451,7 @@ QStringList QmakeProFileNode::generatedFiles(const QString &buildDir, // ui_*.h files into a special directory, or even change the .h suffix, we // cannot help doing this here. switch (sourceFile->fileType()) { - case FormType: { + case FileType::Form: { FileName location; auto it = m_varValues.constFind(UiDirVar); if (it != m_varValues.constEnd() && !it.value().isEmpty()) @@ -2465,7 +2465,7 @@ QStringList QmakeProFileNode::generatedFiles(const QString &buildDir, + singleVariableValue(HeaderExtensionVar)); return QStringList(QDir::cleanPath(location.toString())); } - case StateChartType: { + case FileType::StateChart: { if (buildDir.isEmpty()) return QStringList(); QString location = QDir::cleanPath(FileName::fromString(buildDir).appendPath( diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index 8be3ff5c5a..1f5a79d8dc 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -117,14 +117,14 @@ public: void clear(); bool equals(const QmakeProjectFiles &f) const; - QStringList files[FileTypeSize]; - QStringList generatedFiles[FileTypeSize]; + QStringList files[static_cast<int>(FileType::FileTypeSize)]; + QStringList generatedFiles[static_cast<int>(FileType::FileTypeSize)]; QStringList proFiles; }; void QmakeProjectFiles::clear() { - for (int i = 0; i < FileTypeSize; ++i) { + for (int i = 0; i < static_cast<int>(FileType::FileTypeSize); ++i) { files[i].clear(); generatedFiles[i].clear(); } @@ -133,7 +133,7 @@ void QmakeProjectFiles::clear() bool QmakeProjectFiles::equals(const QmakeProjectFiles &f) const { - for (int i = 0; i < FileTypeSize; ++i) + for (int i = 0; i < static_cast<int>(FileType::FileTypeSize); ++i) if (files[i] != f.files[i] || generatedFiles[i] != f.generatedFiles[i]) return false; if (proFiles != f.proFiles) @@ -151,7 +151,7 @@ QDebug operator<<(QDebug d, const QmakeProjectFiles &f) { QDebug nsp = d.nospace(); nsp << "QmakeProjectFiles: proFiles=" << f.proFiles << '\n'; - for (int i = 0; i < FileTypeSize; ++i) + for (int i = 0; i < static_cast<int>(FileType::FileTypeSize); ++i) nsp << "Type " << i << " files=" << f.files[i] << " generated=" << f.generatedFiles[i] << '\n'; return d; } @@ -190,7 +190,7 @@ void ProjectFilesVisitor::findProjectFiles(QmakeProFileNode *rootNode, QmakeProj files->clear(); ProjectFilesVisitor visitor(files); rootNode->accept(&visitor); - for (int i = 0; i < FileTypeSize; ++i) { + for (int i = 0; i < static_cast<int>(FileType::FileTypeSize); ++i) { Utils::sort(files->files[i]); unique(files->files[i]); Utils::sort(files->generatedFiles[i]); @@ -209,10 +209,10 @@ void ProjectFilesVisitor::visitProjectNode(ProjectNode *projectNode) void ProjectFilesVisitor::visitFolderNode(FolderNode *folderNode) { if (dynamic_cast<ResourceEditor::ResourceTopLevelNode *>(folderNode)) - m_files->files[ResourceType].push_back(folderNode->filePath().toString()); + m_files->files[static_cast<int>(FileType::Resource)].push_back(folderNode->filePath().toString()); foreach (FileNode *fileNode, folderNode->fileNodes()) { - const int type = fileNode->fileType(); + const int type = static_cast<int>(fileNode->fileType()); QStringList &targetList = fileNode->isGenerated() ? m_files->generatedFiles[type] : m_files->files[type]; targetList.push_back(fileNode->filePath().toString()); } @@ -806,7 +806,7 @@ QString QmakeProject::displayName() const QStringList QmakeProject::files(FilesMode fileMode) const { QStringList files; - for (int i = 0; i < FileTypeSize; ++i) { + for (int i = 0; i < static_cast<int>(FileType::FileTypeSize); ++i) { if (fileMode & SourceFiles) files += m_projectFiles->files[i]; if (fileMode & GeneratedFiles) @@ -824,7 +824,7 @@ static FolderNode *folderOf(FolderNode *in, const FileName &fileName) foreach (FileNode *fn, in->fileNodes()) if (fn->filePath() == fileName) return in; - foreach (FolderNode *folder, in->subFolderNodes()) + foreach (FolderNode *folder, in->folderNodes()) if (FolderNode *pn = folderOf(folder, fileName)) return pn; return 0; @@ -986,7 +986,7 @@ void QmakeProject::collectAllProFiles(QList<QmakeProFileNode *> &list, QmakeProF if (parse == ExactAndCumulativeParse || node->includedInExactParse()) if (projectTypes.isEmpty() || projectTypes.contains(node->projectType())) list.append(node); - foreach (ProjectNode *n, node->subProjectNodes()) { + foreach (ProjectNode *n, node->projectNodes()) { QmakeProFileNode *qmakeProFileNode = dynamic_cast<QmakeProFileNode *>(n); if (qmakeProFileNode) collectAllProFiles(list, qmakeProFileNode, parse, projectTypes); @@ -1070,7 +1070,7 @@ bool QmakeProject::hasSubNode(QmakePriFileNode *root, const FileName &path) { if (root->filePath() == path) return true; - foreach (FolderNode *fn, root->subFolderNodes()) { + foreach (FolderNode *fn, root->folderNodes()) { if (dynamic_cast<QmakeProFileNode *>(fn)) { // we aren't interested in pro file nodes } else if (QmakePriFileNode *qt4prifilenode = dynamic_cast<QmakePriFileNode *>(fn)) { @@ -1086,7 +1086,7 @@ void QmakeProject::findProFile(const FileName &fileName, QmakeProFileNode *root, if (hasSubNode(root, fileName)) list.append(root); - foreach (FolderNode *fn, root->subFolderNodes()) + foreach (FolderNode *fn, root->folderNodes()) if (QmakeProFileNode *qt4proFileNode = dynamic_cast<QmakeProFileNode *>(fn)) findProFile(fileName, qt4proFileNode, list); } diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectmanager.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectmanager.cpp index 2bdf16f76c..aa9f96d423 100644 --- a/src/plugins/qmakeprojectmanager/qmakeprojectmanager.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeprojectmanager.cpp @@ -221,7 +221,7 @@ void QmakeManager::buildFile() Project *project = SessionManager::projectForFile(file); if (project && node) - handleSubDirContextMenu(BUILD, true, project, node->projectNode(), node); + handleSubDirContextMenu(BUILD, true, project, node->parentProjectNode(), node); } } diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp index a1d269a188..30ba229d27 100644 --- a/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp @@ -323,12 +323,12 @@ void QmakeProjectManagerPlugin::updateContextActions(ProjectExplorer::Node *node auto qmakeProject = qobject_cast<QmakeProject *>(project); QmakeProFileNode *subProjectNode = nullptr; if (node) { - if (auto subPriFileNode = dynamic_cast<QmakePriFileNode *>(node->projectNode())) + if (auto subPriFileNode = dynamic_cast<QmakePriFileNode *>(node->parentProjectNode())) subProjectNode = subPriFileNode->proFileNode(); } ProjectExplorer::FileNode *fileNode = node ? node->asFileNode() : nullptr; bool buildFilePossible = subProjectNode && fileNode - && (fileNode->fileType() == ProjectExplorer::SourceType); + && (fileNode->fileType() == ProjectExplorer::FileType::Source); m_qmakeProjectManager->setContextNode(subProjectNode); m_qmakeProjectManager->setContextProject(qmakeProject); @@ -397,7 +397,7 @@ void QmakeProjectManagerPlugin::updateBuildFileAction() m_buildFileAction->setParameter(file.fileName()); visible = qobject_cast<QmakeProject *>(project) && node - && dynamic_cast<QmakePriFileNode *>(node->projectNode()); + && dynamic_cast<QmakePriFileNode *>(node->parentProjectNode()); enabled = !BuildManager::isBuilding(project); } diff --git a/src/plugins/qmldesigner/components/integration/componentaction.cpp b/src/plugins/qmldesigner/components/integration/componentaction.cpp index 01497f8c1c..d04f84da5f 100644 --- a/src/plugins/qmldesigner/components/integration/componentaction.cpp +++ b/src/plugins/qmldesigner/components/integration/componentaction.cpp @@ -46,7 +46,7 @@ void ComponentAction::setCurrentIndex(int index) dontEmitCurrentComponentChanged = false; } -QWidget *ComponentAction::createWidget(QWidget *parent) +QWidget *ComponentAction::createWidget(QWidget *parent) { QComboBox *comboBox = new QComboBox(parent); comboBox->setMinimumWidth(120); diff --git a/src/plugins/qmldesigner/components/integration/componentaction.h b/src/plugins/qmldesigner/components/integration/componentaction.h index 4d697cc7f4..f6cc02b303 100644 --- a/src/plugins/qmldesigner/components/integration/componentaction.h +++ b/src/plugins/qmldesigner/components/integration/componentaction.h @@ -47,7 +47,7 @@ public: protected: - QWidget *createWidget(QWidget *parent); + QWidget *createWidget(QWidget *parent); signals: void currentComponentChanged(const ModelNode &node); diff --git a/src/plugins/qmldesigner/components/integration/componentview.cpp b/src/plugins/qmldesigner/components/integration/componentview.cpp index 77d2bf78a8..f341c1e31e 100644 --- a/src/plugins/qmldesigner/components/integration/componentview.cpp +++ b/src/plugins/qmldesigner/components/integration/componentview.cpp @@ -83,7 +83,6 @@ void ComponentView::removeSingleNodeFromList(const ModelNode &node) } } - int ComponentView::indexForNode(const ModelNode &node) const { for (int row = 0; row < m_standardItemModel->rowCount(); row++) { diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp index d1aecf0ee1..1caa8c433d 100644 --- a/src/plugins/qmldesigner/documentmanager.cpp +++ b/src/plugins/qmldesigner/documentmanager.cpp @@ -381,23 +381,25 @@ void DocumentManager::findPathToIsoProFile(bool *iconResourceFileAlreadyExists, ProjectExplorer::Node *iconQrcFileNode = nullptr; while (node && !iconQrcFileNode) { - qCDebug(documentManagerLog) << "Checking" << node->displayName() << "(" << node << node->nodeType() << ")"; + qCDebug(documentManagerLog) << "Checking" << node->displayName() + << "(" << node << static_cast<int>(node->nodeType()) << ")"; - if (node->nodeType() == ProjectExplorer::VirtualFolderNodeType && node->displayName() == "Resources") { + if (node->nodeType() == ProjectExplorer::NodeType::VirtualFolder && node->displayName() == "Resources") { ProjectExplorer::VirtualFolderNode *virtualFolderNode = dynamic_cast<ProjectExplorer::VirtualFolderNode*>(node); - for (int subFolderIndex = 0; subFolderIndex < virtualFolderNode->subFolderNodes().size() && !iconQrcFileNode; ++subFolderIndex) { - ProjectExplorer::FolderNode *subFolderNode = virtualFolderNode->subFolderNodes().at(subFolderIndex); + for (int subFolderIndex = 0; subFolderIndex < virtualFolderNode->folderNodes().size() && !iconQrcFileNode; ++subFolderIndex) { + ProjectExplorer::FolderNode *subFolderNode = virtualFolderNode->folderNodes().at(subFolderIndex); qCDebug(documentManagerLog) << "Checking if" << subFolderNode->displayName() << "(" - << subFolderNode << subFolderNode->nodeType() << ") is" << isoIconsQrcFile; + << subFolderNode << static_cast<int>(subFolderNode->nodeType()) + << ") is" << isoIconsQrcFile; - if (subFolderNode->nodeType() == ProjectExplorer::FolderNodeType + if (subFolderNode->nodeType() == ProjectExplorer::NodeType::Folder && subFolderNode->displayName() == isoIconsQrcFile) { qCDebug(documentManagerLog) << "Found" << isoIconsQrcFile << "in" << virtualFolderNode->filePath(); iconQrcFileNode = subFolderNode; - *resourceFileProPath = iconQrcFileNode->projectNode()->filePath().toString(); + *resourceFileProPath = iconQrcFileNode->parentProjectNode()->filePath().toString(); } } } @@ -414,7 +416,7 @@ void DocumentManager::findPathToIsoProFile(bool *iconResourceFileAlreadyExists, *resourceFilePath = project->projectDirectory().toString() + "/" + isoIconsQrcFile; // We assume that the .pro containing the QML file is an acceptable place to add the .qrc file. - ProjectExplorer::ProjectNode *projectNode = ProjectExplorer::SessionManager::nodeForFile(qmlFileName)->projectNode(); + ProjectExplorer::ProjectNode *projectNode = ProjectExplorer::SessionManager::nodeForFile(qmlFileName)->parentProjectNode(); *resourceFileProPath = projectNode->filePath().toString(); } else { // We found the QRC file that we want. diff --git a/src/plugins/qmljseditor/qmljseditingsettingspage.cpp b/src/plugins/qmljseditor/qmljseditingsettingspage.cpp new file mode 100644 index 0000000000..1e772df1b3 --- /dev/null +++ b/src/plugins/qmljseditor/qmljseditingsettingspage.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "qmljseditingsettingspage.h" +#include "qmljseditorconstants.h" + +#include <qmljstools/qmljstoolsconstants.h> +#include <coreplugin/icore.h> + +#include <QSettings> +#include <QTextStream> +#include <QCheckBox> + + +using namespace QmlJSEditor; +using namespace QmlJSEditor::Internal; + +QmlJsEditingSettings::QmlJsEditingSettings() + : m_enableContextPane(false), + m_pinContextPane(false), + m_autoFormatOnSave(false), + m_autoFormatOnlyCurrentProject(false) +{} + +void QmlJsEditingSettings::set() +{ + if (get() != *this) + toSettings(Core::ICore::settings()); +} + +void QmlJsEditingSettings::fromSettings(QSettings *settings) +{ + settings->beginGroup(QLatin1String(QmlJSEditor::Constants::SETTINGS_CATEGORY_QML)); + m_enableContextPane = settings->value( + QLatin1String(QmlJSEditor::Constants::QML_CONTEXTPANE_KEY), + QVariant(false)).toBool(); + m_pinContextPane = settings->value( + QLatin1String(QmlJSEditor::Constants::QML_CONTEXTPANEPIN_KEY), + QVariant(false)).toBool(); + m_autoFormatOnSave = settings->value( + QLatin1String(QmlJSEditor::Constants::AUTO_FORMAT_ON_SAVE), + QVariant(false)).toBool(); + m_autoFormatOnlyCurrentProject = settings->value( + QLatin1String(QmlJSEditor::Constants::AUTO_FORMAT_ONLY_CURRENT_PROJECT), + QVariant(false)).toBool(); + settings->endGroup(); +} + +void QmlJsEditingSettings::toSettings(QSettings *settings) const +{ + settings->beginGroup(QLatin1String(QmlJSEditor::Constants::SETTINGS_CATEGORY_QML)); + settings->setValue(QLatin1String(QmlJSEditor::Constants::QML_CONTEXTPANE_KEY), + m_enableContextPane); + settings->setValue(QLatin1String(QmlJSEditor::Constants::QML_CONTEXTPANEPIN_KEY), + m_pinContextPane); + settings->setValue(QLatin1String(QmlJSEditor::Constants::AUTO_FORMAT_ON_SAVE), + m_autoFormatOnSave); + settings->setValue(QLatin1String(QmlJSEditor::Constants::AUTO_FORMAT_ONLY_CURRENT_PROJECT), + m_autoFormatOnlyCurrentProject); + settings->endGroup(); +} + +bool QmlJsEditingSettings::equals(const QmlJsEditingSettings &other) const +{ + return m_enableContextPane == other.m_enableContextPane + && m_pinContextPane == other.m_pinContextPane + && m_autoFormatOnSave == other.m_autoFormatOnSave + && m_autoFormatOnlyCurrentProject == other.m_autoFormatOnlyCurrentProject; +} + +bool QmlJsEditingSettings::enableContextPane() const +{ + return m_enableContextPane; +} + +void QmlJsEditingSettings::setEnableContextPane(const bool enableContextPane) +{ + m_enableContextPane = enableContextPane; +} + +bool QmlJsEditingSettings::pinContextPane() const +{ + return m_pinContextPane; +} + +void QmlJsEditingSettings::setPinContextPane(const bool pinContextPane) +{ + m_pinContextPane = pinContextPane; +} + +bool QmlJsEditingSettings::autoFormatOnSave() const +{ + return m_autoFormatOnSave; +} + +void QmlJsEditingSettings::setAutoFormatOnSave(const bool autoFormatOnSave) +{ + m_autoFormatOnSave = autoFormatOnSave; +} + +bool QmlJsEditingSettings::autoFormatOnlyCurrentProject() const +{ + return m_autoFormatOnlyCurrentProject; +} + +void QmlJsEditingSettings::setAutoFormatOnlyCurrentProject(const bool autoFormatOnlyCurrentProject) +{ + m_autoFormatOnlyCurrentProject = autoFormatOnlyCurrentProject; +} + +QmlJsEditingSettignsPageWidget::QmlJsEditingSettignsPageWidget(QWidget *parent) : + QWidget(parent) +{ + m_ui.setupUi(this); +} + +QmlJsEditingSettings QmlJsEditingSettignsPageWidget::settings() const +{ + QmlJsEditingSettings s; + s.setEnableContextPane(m_ui.textEditHelperCheckBox->isChecked()); + s.setPinContextPane(m_ui.textEditHelperCheckBoxPin->isChecked()); + s.setAutoFormatOnSave(m_ui.autoFormatOnSave->isChecked()); + s.setAutoFormatOnlyCurrentProject(m_ui.autoFormatOnlyCurrentProject->isChecked()); + return s; +} + +void QmlJsEditingSettignsPageWidget::setSettings(const QmlJsEditingSettings &s) +{ + m_ui.textEditHelperCheckBox->setChecked(s.enableContextPane()); + m_ui.textEditHelperCheckBoxPin->setChecked(s.pinContextPane()); + m_ui.autoFormatOnSave->setChecked(s.autoFormatOnSave()); + m_ui.autoFormatOnlyCurrentProject->setChecked(s.autoFormatOnlyCurrentProject()); +} + +QmlJsEditingSettings QmlJsEditingSettings::get() +{ + QmlJsEditingSettings settings; + settings.fromSettings(Core::ICore::settings()); + return settings; +} + +QmlJsEditingSettingsPage::QmlJsEditingSettingsPage() : + m_widget(0) +{ + setId("C.QmlJsEditing"); + setDisplayName(tr("QML/JS Editing")); + setCategory(Constants::SETTINGS_CATEGORY_QML); + setDisplayCategory(QCoreApplication::translate("QmlJSEditor", + QmlJSEditor::Constants::SETTINGS_TR_CATEGORY_QML)); + setCategoryIcon(Utils::Icon(QmlJSTools::Constants::SETTINGS_CATEGORY_QML_ICON)); +} + +QWidget *QmlJsEditingSettingsPage::widget() +{ + if (!m_widget) { + m_widget = new QmlJsEditingSettignsPageWidget; + m_widget->setSettings(QmlJsEditingSettings::get()); + } + return m_widget; +} + +void QmlJsEditingSettingsPage::apply() +{ + if (!m_widget) // page was never shown + return; + m_widget->settings().set(); +} + +void QmlJsEditingSettingsPage::finish() +{ + delete m_widget; +} diff --git a/src/plugins/qmljseditor/quicktoolbarsettingspage.h b/src/plugins/qmljseditor/qmljseditingsettingspage.h index db0c5ee99b..85687d6871 100644 --- a/src/plugins/qmljseditor/quicktoolbarsettingspage.h +++ b/src/plugins/qmljseditor/qmljseditingsettingspage.h @@ -25,7 +25,7 @@ #pragma once -#include "ui_quicktoolbarsettingspage.h" +#include "ui_qmljseditingsettingspage.h" #include <coreplugin/dialogs/ioptionspage.h> #include <QPointer> #include <QWidget> @@ -36,61 +36,77 @@ QT_END_NAMESPACE namespace QmlJSEditor { - class QuickToolBarSettings { + class QmlJsEditingSettings { public: - QuickToolBarSettings(); + QmlJsEditingSettings(); - static QuickToolBarSettings get(); + static QmlJsEditingSettings get(); void set(); void fromSettings(QSettings *); void toSettings(QSettings *) const; - bool equals(const QuickToolBarSettings &other) const; - bool enableContextPane; - bool pinContextPane; + bool equals(const QmlJsEditingSettings &other) const; + + bool enableContextPane() const; + void setEnableContextPane(const bool enableContextPane); + + bool pinContextPane() const; + void setPinContextPane(const bool pinContextPane); + + bool autoFormatOnSave() const; + void setAutoFormatOnSave(const bool autoFormatOnSave); + + bool autoFormatOnlyCurrentProject() const; + void setAutoFormatOnlyCurrentProject(const bool autoFormatOnlyCurrentProject); + + private: + bool m_enableContextPane; + bool m_pinContextPane; + bool m_autoFormatOnSave; + bool m_autoFormatOnlyCurrentProject; }; - inline bool operator==(const QuickToolBarSettings &s1, const QuickToolBarSettings &s2) + inline bool operator==(const QmlJsEditingSettings &s1, const QmlJsEditingSettings &s2) { return s1.equals(s2); } - inline bool operator!=(const QuickToolBarSettings &s1, const QuickToolBarSettings &s2) + inline bool operator!=(const QmlJsEditingSettings &s1, const QmlJsEditingSettings &s2) { return !s1.equals(s2); } -class QuickToolBarSettings; +class QmlJsEditingSettings; namespace Internal { -class QuickToolBarSettingsPageWidget : public QWidget +class QmlJsEditingSettignsPageWidget : public QWidget { Q_OBJECT public: - explicit QuickToolBarSettingsPageWidget(QWidget *parent = 0); + explicit QmlJsEditingSettignsPageWidget(QWidget *parent = 0); - QuickToolBarSettings settings() const; - void setSettings(const QuickToolBarSettings &); + QmlJsEditingSettings settings() const; + void setSettings(const QmlJsEditingSettings &); - static QuickToolBarSettings get(); + static QmlJsEditingSettings get(); private: - Ui::QuickToolBarSettingsPage m_ui; + Ui::QmlJsEditingSettingsPage m_ui; }; -class QuickToolBarSettingsPage : public Core::IOptionsPage +class QmlJsEditingSettingsPage : public Core::IOptionsPage { Q_OBJECT public: - QuickToolBarSettingsPage(); + QmlJsEditingSettingsPage(); QWidget *widget(); void apply(); void finish(); private: - QPointer<QuickToolBarSettingsPageWidget> m_widget; + QPointer<QmlJsEditingSettignsPageWidget> m_widget; }; } // namespace Internal diff --git a/src/plugins/qmljseditor/quicktoolbarsettingspage.ui b/src/plugins/qmljseditor/qmljseditingsettingspage.ui index fddcffef6b..c0c4c2d6b0 100644 --- a/src/plugins/qmljseditor/quicktoolbarsettingspage.ui +++ b/src/plugins/qmljseditor/qmljseditingsettingspage.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>QmlJSEditor::Internal::QuickToolBarSettingsPage</class> - <widget class="QWidget" name="QmlJSEditor::Internal::QuickToolBarSettingsPage"> + <class>QmlJSEditor::Internal::QmlJsEditingSettingsPage</class> + <widget class="QWidget" name="QmlJSEditor::Internal::QmlJsEditingSettingsPage"> <property name="geometry"> <rect> <x>0</x> @@ -14,8 +14,8 @@ <string>Form</string> </property> <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0"> - <widget class="QGroupBox" name="groupBox"> + <item row="1" column="0"> + <widget class="QGroupBox" name="groupBox_2"> <property name="title"> <string>Qt Quick Toolbars</string> </property> @@ -40,7 +40,7 @@ </layout> </widget> </item> - <item row="1" column="0"> + <item row="3" column="0"> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> @@ -48,13 +48,56 @@ <property name="sizeHint" stdset="0"> <size> <width>20</width> - <height>207</height> + <height>40</height> </size> </property> </spacer> </item> + <item row="0" column="0"> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Automatic Formatting on File Save</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QCheckBox" name="autoFormatOnSave"> + <property name="text"> + <string>Enable auto format on file save</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="autoFormatOnlyCurrentProject"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Restrict to files contained in the current project</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> </layout> </widget> <resources/> - <connections/> + <connections> + <connection> + <sender>autoFormatOnSave</sender> + <signal>toggled(bool)</signal> + <receiver>autoFormatOnlyCurrentProject</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>216</x> + <y>40</y> + </hint> + <hint type="destinationlabel"> + <x>216</x> + <y>63</y> + </hint> + </hints> + </connection> + </connections> </ui> diff --git a/src/plugins/qmljseditor/qmljseditor.pro b/src/plugins/qmljseditor/qmljseditor.pro index f5a0e42e92..978bc4cfe6 100644 --- a/src/plugins/qmljseditor/qmljseditor.pro +++ b/src/plugins/qmljseditor/qmljseditor.pro @@ -17,7 +17,7 @@ HEADERS += \ qmloutlinemodel.h \ qmltaskmanager.h \ qmljsoutlinetreeview.h \ - quicktoolbarsettingspage.h \ + qmljseditingsettingspage.h \ quicktoolbar.h \ qmljscomponentnamedialog.h \ qmljsfindreferences.h \ @@ -46,7 +46,7 @@ SOURCES += \ qmltaskmanager.cpp \ qmljsquickfixes.cpp \ qmljsoutlinetreeview.cpp \ - quicktoolbarsettingspage.cpp \ + qmljseditingsettingspage.cpp \ quicktoolbar.cpp \ qmljscomponentnamedialog.cpp \ qmljsfindreferences.cpp \ @@ -62,5 +62,5 @@ SOURCES += \ qmljseditordocument.cpp FORMS += \ - quicktoolbarsettingspage.ui \ + qmljseditingsettingspage.ui \ qmljscomponentnamedialog.ui diff --git a/src/plugins/qmljseditor/qmljseditor.qbs b/src/plugins/qmljseditor/qmljseditor.qbs index 0cba20a7a6..940f414c9b 100644 --- a/src/plugins/qmljseditor/qmljseditor.qbs +++ b/src/plugins/qmljseditor/qmljseditor.qbs @@ -26,6 +26,9 @@ QtcPlugin { "qmljscomponentnamedialog.cpp", "qmljscomponentnamedialog.h", "qmljscomponentnamedialog.ui", + "qmljseditingsettingspage.cpp", + "qmljseditingsettingspage.h", + "qmljseditingsettingspage.ui", "qmljseditor.cpp", "qmljseditor.h", "qmljseditor_global.h", @@ -68,9 +71,6 @@ QtcPlugin { "qmltaskmanager.h", "quicktoolbar.cpp", "quicktoolbar.h", - "quicktoolbarsettingspage.cpp", - "quicktoolbarsettingspage.h", - "quicktoolbarsettingspage.ui", ] Export { diff --git a/src/plugins/qmljseditor/qmljseditorconstants.h b/src/plugins/qmljseditor/qmljseditorconstants.h index 3f22b6e1ef..bfebc16d47 100644 --- a/src/plugins/qmljseditor/qmljseditorconstants.h +++ b/src/plugins/qmljseditor/qmljseditorconstants.h @@ -57,5 +57,8 @@ const char QML_CONTEXTPANEPIN_KEY[] = "QmlJSEditor.ContextPanePinned"; const char QML_UI_FILE_WARNING[] = "QmlJSEditor.QmlUiFileWarning"; +const char AUTO_FORMAT_ON_SAVE[] = "QmlJSEditor.AutoFormatOnSave"; +const char AUTO_FORMAT_ONLY_CURRENT_PROJECT[] = "QmlJSEditor.AutoFormatOnlyCurrentProject"; + } // namespace Constants } // namespace QmlJSEditor diff --git a/src/plugins/qmljseditor/qmljseditorplugin.cpp b/src/plugins/qmljseditor/qmljseditorplugin.cpp index 6bd676e7b8..9a427a1b08 100644 --- a/src/plugins/qmljseditor/qmljseditorplugin.cpp +++ b/src/plugins/qmljseditor/qmljseditorplugin.cpp @@ -23,18 +23,18 @@ ** ****************************************************************************/ -#include "qmljseditorplugin.h" -#include "qmljshighlighter.h" +#include "qmljseditingsettingspage.h" #include "qmljseditor.h" #include "qmljseditorconstants.h" #include "qmljseditordocument.h" +#include "qmljseditorplugin.h" +#include "qmljshighlighter.h" #include "qmljsoutline.h" #include "qmljspreviewrunner.h" +#include "qmljsquickfixassist.h" #include "qmljssnippetprovider.h" #include "qmltaskmanager.h" #include "quicktoolbar.h" -#include "quicktoolbarsettingspage.h" -#include "qmljsquickfixassist.h" #include <qmljs/qmljsicons.h> #include <qmljs/qmljsmodelmanagerinterface.h> @@ -50,6 +50,8 @@ #include <coreplugin/actionmanager/command.h> #include <coreplugin/editormanager/editormanager.h> #include <projectexplorer/taskhub.h> +#include <projectexplorer/project.h> +#include <projectexplorer/projecttree.h> #include <projectexplorer/projectexplorerconstants.h> #include <texteditor/texteditorconstants.h> #include <utils/qtcassert.h> @@ -193,11 +195,14 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e addAutoReleasedObject(new QmlJSOutlineWidgetFactory); addAutoReleasedObject(new QuickToolBar); - addAutoReleasedObject(new QuickToolBarSettingsPage); + addAutoReleasedObject(new QmlJsEditingSettingsPage); connect(EditorManager::instance(), &EditorManager::currentEditorChanged, this, &QmlJSEditorPlugin::currentEditorChanged); + connect(EditorManager::instance(), &Core::EditorManager::aboutToSave, + this, &QmlJSEditorPlugin::autoFormatOnSave); + return true; } @@ -234,9 +239,25 @@ void QmlJSEditorPlugin::renameUsages() void QmlJSEditorPlugin::reformatFile() { if (m_currentDocument) { - QTC_ASSERT(!m_currentDocument->isSemanticInfoOutdated(), return); + QmlJS::Document::Ptr document = m_currentDocument->semanticInfo().document; + QmlJS::Snapshot snapshot = QmlJS::ModelManagerInterface::instance()->snapshot(); + + if (m_currentDocument->isSemanticInfoOutdated()) { + QmlJS::Document::MutablePtr latestDocument; - const QString &newText = QmlJS::reformat(m_currentDocument->semanticInfo().document); + const QString fileName = m_currentDocument->filePath().toString(); + latestDocument = snapshot.documentFromSource(QString::fromUtf8(m_currentDocument->contents()), + fileName, + QmlJS::ModelManagerInterface::guessLanguageOfFile(fileName)); + latestDocument->parseQml(); + snapshot.insert(latestDocument); + document = latestDocument; + } + + if (!document->isParsedCorrectly()) + return; + + const QString &newText = QmlJS::reformat(document); QTextCursor tc(m_currentDocument->document()); tc.movePosition(QTextCursor::Start); tc.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); @@ -296,4 +317,25 @@ void QmlJSEditorPlugin::checkCurrentEditorSemanticInfoUpToDate() m_reformatFileAction->setEnabled(semanticInfoUpToDate); } +void QmlJSEditorPlugin::autoFormatOnSave(Core::IDocument *document) +{ + if (!QmlJsEditingSettings::get().autoFormatOnSave()) + return; + + // Check that we are dealing with a QML/JS editor + if (document->id() != Constants::C_QMLJSEDITOR_ID) + return; + + // Check if file is contained in the current project (if wished) + if (QmlJsEditingSettings::get().autoFormatOnlyCurrentProject()) { + const ProjectExplorer::Project *pro = ProjectExplorer::ProjectTree::currentProject(); + if (!pro || !pro->files(ProjectExplorer::Project::SourceFiles).contains( + document->filePath().toString())) { + return; + } + } + + reformatFile(); +} + } // namespace QmlJSEditor diff --git a/src/plugins/qmljseditor/qmljseditorplugin.h b/src/plugins/qmljseditor/qmljseditorplugin.h index a85f16b389..b176866d95 100644 --- a/src/plugins/qmljseditor/qmljseditorplugin.h +++ b/src/plugins/qmljseditor/qmljseditorplugin.h @@ -38,6 +38,7 @@ namespace Utils { class JsonSchemaManager; } namespace Core { class Command; class ActionContainer; +class IDocument; class IEditor; } @@ -82,6 +83,7 @@ private: void currentEditorChanged(Core::IEditor *editor); void runSemanticScan(); void checkCurrentEditorSemanticInfoUpToDate(); + void autoFormatOnSave(Core::IDocument *document); Core::Command *addToolAction(QAction *a, Core::Context &context, Core::Id id, Core::ActionContainer *c1, const QString &keySequence); diff --git a/src/plugins/qmljseditor/quicktoolbar.cpp b/src/plugins/qmljseditor/quicktoolbar.cpp index 863b778c17..9bc5b69855 100644 --- a/src/plugins/qmljseditor/quicktoolbar.cpp +++ b/src/plugins/qmljseditor/quicktoolbar.cpp @@ -24,7 +24,7 @@ ****************************************************************************/ #include "quicktoolbar.h" -#include "quicktoolbarsettingspage.h" +#include "qmljseditingsettingspage.h" #include <utils/changeset.h> #include <qmleditorwidgets/contextpanewidget.h> @@ -114,7 +114,7 @@ QuickToolBar::~QuickToolBar() void QuickToolBar::apply(TextEditor::TextEditorWidget *editorWidget, Document::Ptr document, const ScopeChain *scopeChain, Node *node, bool update, bool force) { - if (!QuickToolBarSettings::get().enableContextPane && !force && !update) { + if (!QmlJsEditingSettings::get().enableContextPane() && !force && !update) { contextWidget()->hide(); return; } @@ -219,10 +219,10 @@ void QuickToolBar::apply(TextEditor::TextEditorWidget *editorWidget, Document::P if (!update) contextWidget()->setType(m_prototypes); if (!update) - contextWidget()->activate(p3 , p1, p2, QuickToolBarSettings::get().pinContextPane); + contextWidget()->activate(p3 , p1, p2, QmlJsEditingSettings::get().pinContextPane()); else - contextWidget()->rePosition(p3 , p1, p2, QuickToolBarSettings::get().pinContextPane); - contextWidget()->setOptions(QuickToolBarSettings::get().enableContextPane, QuickToolBarSettings::get().pinContextPane); + contextWidget()->rePosition(p3 , p1, p2, QmlJsEditingSettings::get().pinContextPane()); + contextWidget()->setOptions(QmlJsEditingSettings::get().enableContextPane(), QmlJsEditingSettings::get().pinContextPane()); contextWidget()->setPath(document->path()); contextWidget()->setProperties(&propertyReader); m_doc = document; @@ -404,16 +404,16 @@ void QuickToolBar::onPropertyRemovedAndChange(const QString &remove, const QStri void QuickToolBar::onPinnedChanged(bool b) { - QuickToolBarSettings settings = QuickToolBarSettings::get(); - settings.pinContextPane = b; + QmlJsEditingSettings settings = QmlJsEditingSettings::get(); + settings.setPinContextPane(b); settings.set(); } void QuickToolBar::onEnabledChanged(bool b) { - QuickToolBarSettings settings = QuickToolBarSettings::get(); - settings.pinContextPane = b; - settings.enableContextPane = b; + QmlJsEditingSettings settings = QmlJsEditingSettings::get(); + settings.setPinContextPane(b); + settings.setEnableContextPane(b); settings.set(); } diff --git a/src/plugins/qmljseditor/quicktoolbarsettingspage.cpp b/src/plugins/qmljseditor/quicktoolbarsettingspage.cpp deleted file mode 100644 index c98124499e..0000000000 --- a/src/plugins/qmljseditor/quicktoolbarsettingspage.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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 "quicktoolbarsettingspage.h" -#include "qmljseditorconstants.h" - -#include <qmljstools/qmljstoolsconstants.h> -#include <coreplugin/icore.h> - -#include <QSettings> -#include <QTextStream> -#include <QCheckBox> - - -using namespace QmlJSEditor; -using namespace QmlJSEditor::Internal; - -QuickToolBarSettings::QuickToolBarSettings() - : enableContextPane(false), - pinContextPane(false) -{} - -void QuickToolBarSettings::set() -{ - if (get() != *this) - toSettings(Core::ICore::settings()); -} - -void QuickToolBarSettings::fromSettings(QSettings *settings) -{ - settings->beginGroup(QLatin1String(QmlJSEditor::Constants::SETTINGS_CATEGORY_QML)); - enableContextPane = settings->value( - QLatin1String(QmlJSEditor::Constants::QML_CONTEXTPANE_KEY), QVariant(false)).toBool(); - pinContextPane = settings->value( - QLatin1String(QmlJSEditor::Constants::QML_CONTEXTPANEPIN_KEY), QVariant(false)).toBool(); - settings->endGroup(); -} - -void QuickToolBarSettings::toSettings(QSettings *settings) const -{ - settings->beginGroup(QLatin1String(QmlJSEditor::Constants::SETTINGS_CATEGORY_QML)); - settings->setValue(QLatin1String(QmlJSEditor::Constants::QML_CONTEXTPANE_KEY), enableContextPane); - settings->setValue(QLatin1String(QmlJSEditor::Constants::QML_CONTEXTPANEPIN_KEY), pinContextPane); - settings->endGroup(); -} - -bool QuickToolBarSettings::equals(const QuickToolBarSettings &other) const -{ - return enableContextPane == other.enableContextPane - && pinContextPane == other.pinContextPane; -} - - -QuickToolBarSettingsPageWidget::QuickToolBarSettingsPageWidget(QWidget *parent) : - QWidget(parent) -{ - m_ui.setupUi(this); -} - -QuickToolBarSettings QuickToolBarSettingsPageWidget::settings() const -{ - QuickToolBarSettings ds; - ds.enableContextPane = m_ui.textEditHelperCheckBox->isChecked(); - ds.pinContextPane = m_ui.textEditHelperCheckBoxPin->isChecked(); - return ds; -} - -void QuickToolBarSettingsPageWidget::setSettings(const QuickToolBarSettings &s) -{ - m_ui.textEditHelperCheckBox->setChecked(s.enableContextPane); - m_ui.textEditHelperCheckBoxPin->setChecked(s.pinContextPane); -} - -QuickToolBarSettings QuickToolBarSettings::get() -{ - QuickToolBarSettings settings; - settings.fromSettings(Core::ICore::settings()); - return settings; -} - -QuickToolBarSettingsPage::QuickToolBarSettingsPage() : - m_widget(0) -{ - setId("C.QmlToolbar"); - setDisplayName(tr("Qt Quick ToolBar")); - setCategory(Constants::SETTINGS_CATEGORY_QML); - setDisplayCategory(QCoreApplication::translate("QmlJSEditor", - QmlJSEditor::Constants::SETTINGS_TR_CATEGORY_QML)); - setCategoryIcon(Utils::Icon(QmlJSTools::Constants::SETTINGS_CATEGORY_QML_ICON)); -} - -QWidget *QuickToolBarSettingsPage::widget() -{ - if (!m_widget) { - m_widget = new QuickToolBarSettingsPageWidget; - m_widget->setSettings(QuickToolBarSettings::get()); - } - return m_widget; -} - -void QuickToolBarSettingsPage::apply() -{ - if (!m_widget) // page was never shown - return; - m_widget->settings().set(); -} - -void QuickToolBarSettingsPage::finish() -{ - delete m_widget; -} diff --git a/src/plugins/qmljstools/qmljstools.pro b/src/plugins/qmljstools/qmljstools.pro index 662c0d950f..bea9367a1b 100644 --- a/src/plugins/qmljstools/qmljstools.pro +++ b/src/plugins/qmljstools/qmljstools.pro @@ -2,7 +2,7 @@ include(../../qtcreatorplugin.pri) DEFINES += QMLJSTOOLS_LIBRARY -!dll { +!contains(CONFIG, dll) { DEFINES += QMLJSTOOLS_STATIC } diff --git a/src/plugins/qmlprofiler/debugmessagesmodel.cpp b/src/plugins/qmlprofiler/debugmessagesmodel.cpp index d3df28d601..f9665bd054 100644 --- a/src/plugins/qmlprofiler/debugmessagesmodel.cpp +++ b/src/plugins/qmlprofiler/debugmessagesmodel.cpp @@ -39,7 +39,7 @@ int DebugMessagesModel::typeId(int index) const return m_data[index].typeId; } -QColor DebugMessagesModel::color(int index) const +QRgb DebugMessagesModel::color(int index) const { return colorBySelectionId(index); } diff --git a/src/plugins/qmlprofiler/debugmessagesmodel.h b/src/plugins/qmlprofiler/debugmessagesmodel.h index bbcfa387b6..4cb49e4e27 100644 --- a/src/plugins/qmlprofiler/debugmessagesmodel.h +++ b/src/plugins/qmlprofiler/debugmessagesmodel.h @@ -38,7 +38,7 @@ public: DebugMessagesModel(QmlProfilerModelManager *manager, QObject *parent = 0); int typeId(int index) const override; - QColor color(int index) const override; + QRgb color(int index) const override; QVariantList labels() const override; QVariantMap details(int index) const override; int expandedRow(int index) const override; diff --git a/src/plugins/qmlprofiler/inputeventsmodel.cpp b/src/plugins/qmlprofiler/inputeventsmodel.cpp index a7827ee3aa..f31d19d0ce 100644 --- a/src/plugins/qmlprofiler/inputeventsmodel.cpp +++ b/src/plugins/qmlprofiler/inputeventsmodel.cpp @@ -45,7 +45,7 @@ int InputEventsModel::typeId(int index) const return selectionId(index) == Mouse ? m_mouseTypeId : m_keyTypeId; } -QColor InputEventsModel::color(int index) const +QRgb InputEventsModel::color(int index) const { return colorBySelectionId(index); } diff --git a/src/plugins/qmlprofiler/inputeventsmodel.h b/src/plugins/qmlprofiler/inputeventsmodel.h index 23509c19e0..0e77893a17 100644 --- a/src/plugins/qmlprofiler/inputeventsmodel.h +++ b/src/plugins/qmlprofiler/inputeventsmodel.h @@ -50,7 +50,7 @@ public: void clear() override; int typeId(int index) const override; - QColor color(int index) const override; + QRgb color(int index) const override; QVariantList labels() const override; QVariantMap details(int index) const override; int expandedRow(int index) const override; diff --git a/src/plugins/qmlprofiler/memoryusagemodel.cpp b/src/plugins/qmlprofiler/memoryusagemodel.cpp index a380c8c304..d989b76df3 100644 --- a/src/plugins/qmlprofiler/memoryusagemodel.cpp +++ b/src/plugins/qmlprofiler/memoryusagemodel.cpp @@ -61,7 +61,7 @@ int MemoryUsageModel::typeId(int index) const return m_data[index].typeId; } -QColor MemoryUsageModel::color(int index) const +QRgb MemoryUsageModel::color(int index) const { return colorBySelectionId(index); } diff --git a/src/plugins/qmlprofiler/memoryusagemodel.h b/src/plugins/qmlprofiler/memoryusagemodel.h index dd978da788..b20f435f47 100644 --- a/src/plugins/qmlprofiler/memoryusagemodel.h +++ b/src/plugins/qmlprofiler/memoryusagemodel.h @@ -59,7 +59,7 @@ public: int expandedRow(int index) const override; int collapsedRow(int index) const override; int typeId(int index) const override; - QColor color(int index) const override; + QRgb color(int index) const override; float relativeHeight(int index) const override; QVariantMap location(int index) const override; diff --git a/src/plugins/qmlprofiler/pixmapcachemodel.cpp b/src/plugins/qmlprofiler/pixmapcachemodel.cpp index b38256c9a8..01770c579b 100644 --- a/src/plugins/qmlprofiler/pixmapcachemodel.cpp +++ b/src/plugins/qmlprofiler/pixmapcachemodel.cpp @@ -60,7 +60,7 @@ int PixmapCacheModel::typeId(int index) const return m_data[index].typeId; } -QColor PixmapCacheModel::color(int index) const +QRgb PixmapCacheModel::color(int index) const { if (m_data[index].pixmapEventType == PixmapCacheCountChanged) return colorByHue(s_pixmapCacheCountHue); diff --git a/src/plugins/qmlprofiler/pixmapcachemodel.h b/src/plugins/qmlprofiler/pixmapcachemodel.h index cdbfee49bb..9d899e6565 100644 --- a/src/plugins/qmlprofiler/pixmapcachemodel.h +++ b/src/plugins/qmlprofiler/pixmapcachemodel.h @@ -102,7 +102,7 @@ public: int expandedRow(int index) const override; int collapsedRow(int index) const override; int typeId(int index) const override; - QColor color(int index) const override; + QRgb color(int index) const override; float relativeHeight(int index) const override; QVariantList labels() const override; diff --git a/src/plugins/qmlprofiler/qmlevent.cpp b/src/plugins/qmlprofiler/qmlevent.cpp index 225186df25..2d06933719 100644 --- a/src/plugins/qmlprofiler/qmlevent.cpp +++ b/src/plugins/qmlprofiler/qmlevent.cpp @@ -59,14 +59,14 @@ enum SerializationTypeOffset { }; template<typename Number> -static void readNumbers(QDataStream &stream, Number *data, quint16 length) +static inline void readNumbers(QDataStream &stream, Number *data, quint16 length) { for (quint16 i = 0; i != length; ++i) stream >> data[i]; } template<typename Number> -static Number readNumber(QDataStream &stream, qint8 type) +static inline Number readNumber(QDataStream &stream, qint8 type) { switch (type) { case OneByte: { @@ -150,7 +150,7 @@ QDataStream &operator>>(QDataStream &stream, QmlEvent &event) return stream; } -static qint8 minimumType(const QmlEvent &event, quint16 length, quint16 origBitsPerNumber) +static inline qint8 minimumType(const QmlEvent &event, quint16 length, quint16 origBitsPerNumber) { qint8 type = OneByte; bool ok = true; @@ -182,7 +182,7 @@ static qint8 minimumType(const QmlEvent &event, quint16 length, quint16 origBits } template<typename Number> -static qint8 minimumType(Number number) +static inline qint8 minimumType(Number number) { if (static_cast<qint8>(number) == number) return OneByte; @@ -194,14 +194,14 @@ static qint8 minimumType(Number number) } template<typename Number> -static void writeNumbers(QDataStream &stream, const QmlEvent &event, quint16 length) +static inline void writeNumbers(QDataStream &stream, const QmlEvent &event, quint16 length) { for (quint16 i = 0; i != length; ++i) stream << event.number<Number>(i); } template<typename Number> -static void writeNumber(QDataStream &stream, Number number, qint8 type) +static inline void writeNumber(QDataStream &stream, Number number, qint8 type) { switch (type) { case OneByte: diff --git a/src/plugins/qmlprofiler/qmlprofileranimationsmodel.cpp b/src/plugins/qmlprofiler/qmlprofileranimationsmodel.cpp index 8e0bd30e84..97cee62797 100644 --- a/src/plugins/qmlprofiler/qmlprofileranimationsmodel.cpp +++ b/src/plugins/qmlprofiler/qmlprofileranimationsmodel.cpp @@ -137,7 +137,7 @@ int QmlProfilerAnimationsModel::collapsedRow(int index) const return rowFromThreadId(selectionId(index)); } -QColor QmlProfilerAnimationsModel::color(int index) const +QRgb QmlProfilerAnimationsModel::color(int index) const { double fpsFraction = m_data[index].framerate / 60.0; if (fpsFraction > 1.0) diff --git a/src/plugins/qmlprofiler/qmlprofileranimationsmodel.h b/src/plugins/qmlprofiler/qmlprofileranimationsmodel.h index 073383e47c..084851a667 100644 --- a/src/plugins/qmlprofiler/qmlprofileranimationsmodel.h +++ b/src/plugins/qmlprofiler/qmlprofileranimationsmodel.h @@ -58,7 +58,7 @@ public: Q_INVOKABLE int expandedRow(int index) const override; Q_INVOKABLE int collapsedRow(int index) const override; - QColor color(int index) const override; + QRgb color(int index) const override; float relativeHeight(int index) const override; QVariantList labels() const override; diff --git a/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp b/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp index 0390d493cd..52a17f7416 100644 --- a/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp @@ -154,6 +154,15 @@ void QmlProfilerDataModel::addEvent(const QmlEvent &event) d->eventStream << event; } +void QmlProfilerDataModel::addEvents(const QVector<QmlEvent> &events) +{ + Q_D(QmlProfilerDataModel); + for (const QmlEvent &event : events) { + d->modelManager->dispatch(event, d->eventTypes[event.typeIndex()]); + d->eventStream << event; + } +} + void QmlProfilerDataModel::clear() { Q_D(QmlProfilerDataModel); diff --git a/src/plugins/qmlprofiler/qmlprofilerdatamodel.h b/src/plugins/qmlprofiler/qmlprofilerdatamodel.h index 5bb97abea4..4698dac409 100644 --- a/src/plugins/qmlprofiler/qmlprofilerdatamodel.h +++ b/src/plugins/qmlprofiler/qmlprofilerdatamodel.h @@ -52,6 +52,7 @@ public: void clear(); bool isEmpty() const; void addEvent(const QmlEvent &event); + void addEvents(const QVector<QmlEvent> &events); void replayEvents(qint64 startTime, qint64 endTime, QmlProfilerModelManager::EventLoader loader) const; void finalize(); diff --git a/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp b/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp index 4c933040e6..6a236a12f9 100644 --- a/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp +++ b/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp @@ -378,8 +378,8 @@ void QmlProfilerModelManager::load(const QString &filename) connect(reader, &QmlProfilerFileReader::notesLoaded, d->notesModel, &QmlProfilerNotesModel::setNotes); - connect(reader, &QmlProfilerFileReader::qmlEventLoaded, - d->model, &QmlProfilerDataModel::addEvent); + connect(reader, &QmlProfilerFileReader::qmlEventsLoaded, + d->model, &QmlProfilerDataModel::addEvents); connect(reader, &QmlProfilerFileReader::success, this, [this, reader]() { d->traceTime->setTime(reader->traceStart(), reader->traceEnd()); diff --git a/src/plugins/qmlprofiler/qmlprofilerrangemodel.cpp b/src/plugins/qmlprofiler/qmlprofilerrangemodel.cpp index f52c46f7dc..a4c76f9d90 100644 --- a/src/plugins/qmlprofiler/qmlprofilerrangemodel.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerrangemodel.cpp @@ -180,7 +180,7 @@ int QmlProfilerRangeModel::bindingLoopDest(int index) const return m_data[index].bindingLoopHead; } -QColor QmlProfilerRangeModel::color(int index) const +QRgb QmlProfilerRangeModel::color(int index) const { return colorBySelectionId(index); } diff --git a/src/plugins/qmlprofiler/qmlprofilerrangemodel.h b/src/plugins/qmlprofiler/qmlprofilerrangemodel.h index 1b97833335..adc68ee836 100644 --- a/src/plugins/qmlprofiler/qmlprofilerrangemodel.h +++ b/src/plugins/qmlprofiler/qmlprofilerrangemodel.h @@ -62,7 +62,7 @@ public: Q_INVOKABLE int expandedRow(int index) const override; Q_INVOKABLE int collapsedRow(int index) const override; int bindingLoopDest(int index) const; - QColor color(int index) const override; + QRgb color(int index) const override; QVariantList labels() const override; QVariantMap details(int index) const override; diff --git a/src/plugins/qmlprofiler/qmlprofilertracefile.cpp b/src/plugins/qmlprofiler/qmlprofilertracefile.cpp index 67188e32fa..ec9195bbd0 100644 --- a/src/plugins/qmlprofiler/qmlprofilertracefile.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertracefile.cpp @@ -123,7 +123,7 @@ QmlProfilerFileReader::QmlProfilerFileReader(QObject *parent) : m_loadedFeatures(0) { static int meta[] = { - qRegisterMetaType<QmlEvent>(), + qRegisterMetaType<QVector<QmlEvent> >(), qRegisterMetaType<QVector<QmlEventType> >(), qRegisterMetaType<QVector<QmlNote> >() }; @@ -248,7 +248,9 @@ bool QmlProfilerFileReader::loadQzt(QIODevice *device) emit notesLoaded(m_notes); updateProgress(device); - QmlEvent event; + const int eventBufferLength = 1024; + QVector<QmlEvent> eventBuffer(eventBufferLength); + int eventBufferIndex = 0; while (!stream.atEnd()) { stream >> data; buffer.setData(qUncompress(data)); @@ -256,6 +258,7 @@ bool QmlProfilerFileReader::loadQzt(QIODevice *device) while (!buffer.atEnd()) { if (isCanceled()) return false; + QmlEvent &event = eventBuffer[eventBufferIndex]; bufferStream >> event; if (bufferStream.status() == QDataStream::Ok) { if (event.typeIndex() >= m_eventTypes.length()) { @@ -263,7 +266,6 @@ bool QmlProfilerFileReader::loadQzt(QIODevice *device) return false; } m_loadedFeatures |= (1ULL << m_eventTypes[event.typeIndex()].feature()); - emit qmlEventLoaded(event); } else if (bufferStream.status() == QDataStream::ReadPastEnd) { break; // Apparently EOF is a character so we end up here after the last event. } else if (bufferStream.status() == QDataStream::ReadCorruptData) { @@ -272,10 +274,16 @@ bool QmlProfilerFileReader::loadQzt(QIODevice *device) } else { Q_UNREACHABLE(); } + if (++eventBufferIndex == eventBufferLength) { + emit qmlEventsLoaded(eventBuffer); + eventBufferIndex = 0; + } } buffer.close(); updateProgress(device); } + eventBuffer.resize(eventBufferIndex); + emit qmlEventsLoaded(eventBuffer); emit success(); return true; } @@ -499,8 +507,7 @@ void QmlProfilerFileReader::loadEvents(QXmlStreamReader &stream) std::sort(events.begin(), events.end(), [](const QmlEvent &a, const QmlEvent &b) { return a.timestamp() < b.timestamp(); }); - foreach (const QmlEvent &event, events) - emit qmlEventLoaded(event); + emit qmlEventsLoaded(events); return; } break; diff --git a/src/plugins/qmlprofiler/qmlprofilertracefile.h b/src/plugins/qmlprofiler/qmlprofilertracefile.h index 937ec1de29..b705f07e68 100644 --- a/src/plugins/qmlprofiler/qmlprofilertracefile.h +++ b/src/plugins/qmlprofiler/qmlprofilertracefile.h @@ -62,7 +62,7 @@ public: signals: void typesLoaded(const QVector<QmlProfiler::QmlEventType> &types); void notesLoaded(const QVector<QmlProfiler::QmlNote> ¬es); - void qmlEventLoaded(const QmlProfiler::QmlEvent &event); + void qmlEventsLoaded(const QVector<QmlProfiler::QmlEvent> &event); void error(const QString &error); void success(); diff --git a/src/plugins/qmlprofiler/scenegraphtimelinemodel.cpp b/src/plugins/qmlprofiler/scenegraphtimelinemodel.cpp index d983630175..8ffb3f8290 100644 --- a/src/plugins/qmlprofiler/scenegraphtimelinemodel.cpp +++ b/src/plugins/qmlprofiler/scenegraphtimelinemodel.cpp @@ -94,7 +94,7 @@ int SceneGraphTimelineModel::typeId(int index) const return m_data[index].typeId; } -QColor SceneGraphTimelineModel::color(int index) const +QRgb SceneGraphTimelineModel::color(int index) const { return colorBySelectionId(index); } diff --git a/src/plugins/qmlprofiler/scenegraphtimelinemodel.h b/src/plugins/qmlprofiler/scenegraphtimelinemodel.h index baa06a83db..805b53b3e3 100644 --- a/src/plugins/qmlprofiler/scenegraphtimelinemodel.h +++ b/src/plugins/qmlprofiler/scenegraphtimelinemodel.h @@ -88,7 +88,7 @@ public: int expandedRow(int index) const override; int collapsedRow(int index) const override; int typeId(int index) const override; - QColor color(int index) const override; + QRgb color(int index) const override; QVariantList labels() const override; diff --git a/src/plugins/qmlprofiler/tests/debugmessagesmodel_test.cpp b/src/plugins/qmlprofiler/tests/debugmessagesmodel_test.cpp index c48d8a5219..7be5f04ff1 100644 --- a/src/plugins/qmlprofiler/tests/debugmessagesmodel_test.cpp +++ b/src/plugins/qmlprofiler/tests/debugmessagesmodel_test.cpp @@ -64,7 +64,7 @@ void DebugMessagesModelTest::testColor() // TimelineModel::colorBySelectionId ... for (int i = 0; i < 10; ++i) { QCOMPARE(model.color(i), - QColor::fromHsl((i % (QtMsgType::QtInfoMsg + 1) * 25) % 360, 150, 166)); + QColor::fromHsl((i % (QtMsgType::QtInfoMsg + 1) * 25) % 360, 150, 166).rgb()); } } diff --git a/src/plugins/qmlprofiler/tests/inputeventsmodel_test.cpp b/src/plugins/qmlprofiler/tests/inputeventsmodel_test.cpp index f0212b66a3..c67856bc03 100644 --- a/src/plugins/qmlprofiler/tests/inputeventsmodel_test.cpp +++ b/src/plugins/qmlprofiler/tests/inputeventsmodel_test.cpp @@ -74,15 +74,15 @@ void InputEventsModelTest::testTypeId() void InputEventsModelTest::testColor() { - QColor keyColor; - QColor mouseColor; + QRgb keyColor = 0; + QRgb mouseColor = 0; for (int i = 0; i < 10; ++i) { InputEventType type = static_cast<InputEventType>(i % MaximumInputEventType); int selectionId = (type <= InputKeyUnknown ? Key : Mouse); QCOMPARE(selectionId, model.selectionId(i)); - QColor &expectedColor = selectionId == Key ? keyColor : mouseColor; - if (expectedColor.isValid()) + QRgb &expectedColor = selectionId == Key ? keyColor : mouseColor; + if (expectedColor != 0) QCOMPARE(model.color(i), expectedColor); else expectedColor = model.color(i); diff --git a/src/plugins/qmlprofiler/tests/memoryusagemodel_test.cpp b/src/plugins/qmlprofiler/tests/memoryusagemodel_test.cpp index 274f3a9c01..b561074bf8 100644 --- a/src/plugins/qmlprofiler/tests/memoryusagemodel_test.cpp +++ b/src/plugins/qmlprofiler/tests/memoryusagemodel_test.cpp @@ -122,9 +122,9 @@ void MemoryUsageModelTest::testTypeId() void MemoryUsageModelTest::testColor() { - QColor heapPageColor = model.color(0); - QColor smallItemColor = model.color(1); - QColor largeItemColor = model.color(2); + QRgb heapPageColor = model.color(0); + QRgb smallItemColor = model.color(1); + QRgb largeItemColor = model.color(2); QVERIFY(smallItemColor != heapPageColor); QVERIFY(largeItemColor != heapPageColor); QVERIFY(smallItemColor != largeItemColor); diff --git a/src/plugins/qmlprofiler/tests/pixmapcachemodel_test.cpp b/src/plugins/qmlprofiler/tests/pixmapcachemodel_test.cpp index 8332904f87..069de0cca1 100644 --- a/src/plugins/qmlprofiler/tests/pixmapcachemodel_test.cpp +++ b/src/plugins/qmlprofiler/tests/pixmapcachemodel_test.cpp @@ -270,20 +270,20 @@ void PixmapCacheModelTest::testRowMaxValue() void PixmapCacheModelTest::testColor() { - QColor row1Color; - QColor dingsColor; - QColor blahColor; + QRgb row1Color = 0; + QRgb dingsColor = 0; + QRgb blahColor = 0; for (int i = 0; i < model.count(); ++i) { if (model.collapsedRow(i) == 1) { - if (!row1Color.isValid()) + if (row1Color == 0) row1Color = model.color(i); else QCOMPARE(model.color(i), row1Color); } else { const QmlEventType &type = manager.qmlModel()->eventTypes()[model.typeId(i)]; - QColor &pixmapColor = (type.location().filename() == QString("blah.png")) ? + QRgb &pixmapColor = (type.location().filename() == QString("blah.png")) ? blahColor : dingsColor; - if (!pixmapColor.isValid()) + if (pixmapColor == 0) pixmapColor = model.color(i); else QCOMPARE(model.color(i), pixmapColor); diff --git a/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp b/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp index c470c92f90..6062d117ab 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp @@ -60,14 +60,14 @@ void QmlProjectNode::refresh() using namespace ProjectExplorer; FileNode *projectFilesNode = new FileNode(m_project->filesFileName(), - ProjectFileType, + FileType::Project, /* generated = */ false); QStringList files = m_project->files(); files.removeAll(m_project->filesFileName().toString()); QList<FileNode *> fileNodes = Utils::transform(files, [](const QString &f) { - FileType fileType = SourceType; // ### FIXME + FileType fileType = FileType::Source; // ### FIXME return new FileNode(Utils::FileName::fromString(f), fileType, false); }); @@ -87,9 +87,9 @@ QList<ProjectExplorer::ProjectAction> QmlProjectNode::supportedActions(Node *nod QList<ProjectExplorer::ProjectAction> actions; actions.append(ProjectExplorer::AddNewFile); actions.append(ProjectExplorer::EraseFile); - if (node->nodeType() == ProjectExplorer::FileNodeType) { + if (node->nodeType() == ProjectExplorer::NodeType::File) { ProjectExplorer::FileNode *fileNode = static_cast<ProjectExplorer::FileNode *>(node); - if (fileNode->fileType() != ProjectExplorer::ProjectFileType) + if (fileNode->fileType() != ProjectExplorer::FileType::Project) actions.append(ProjectExplorer::Rename); } return actions; diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp index 459e07dd07..8978fff35a 100644 --- a/src/plugins/qtsupport/baseqtversion.cpp +++ b/src/plugins/qtsupport/baseqtversion.cpp @@ -1082,13 +1082,19 @@ FileName BaseQtVersion::mkspecPath() const bool BaseQtVersion::hasMkspec(const FileName &spec) const { - QFileInfo fi; - fi.setFile(QDir::fromNativeSeparators(qmakeProperty("QT_HOST_DATA")) - + QLatin1String("/mkspecs/") + spec.toString()); - if (fi.isDir()) + if (spec.isEmpty()) + return true; // default spec of a Qt version + + QDir mkspecDir = QDir(QDir::fromNativeSeparators(qmakeProperty("QT_HOST_DATA")) + + QLatin1String("/mkspecs/")); + const QString absSpec = mkspecDir.absoluteFilePath(spec.toString()); + if (QFileInfo(absSpec).isDir() && QFileInfo(absSpec + "/qmake.conf").isFile()) return true; - fi.setFile(sourcePath().toString() + QLatin1String("/mkspecs/") + spec.toString()); - return fi.isDir(); + mkspecDir.setPath(sourcePath().toString() + QLatin1String("/mkspecs/")); + const QString absSrcSpec = mkspecDir.absoluteFilePath(spec.toString()); + return absSrcSpec != absSpec + && QFileInfo(absSrcSpec).isDir() + && QFileInfo(absSrcSpec + "/qmake.conf").isFile(); } BaseQtVersion::QmakeBuildConfigs BaseQtVersion::defaultBuildConfig() const diff --git a/src/plugins/qtsupport/qscxmlcgenerator.cpp b/src/plugins/qtsupport/qscxmlcgenerator.cpp index 423bfc763b..e912e3d24b 100644 --- a/src/plugins/qtsupport/qscxmlcgenerator.cpp +++ b/src/plugins/qtsupport/qscxmlcgenerator.cpp @@ -136,7 +136,7 @@ Utils::FileName QScxmlcGenerator::tmpFile() const FileType QScxmlcGeneratorFactory::sourceType() const { - return StateChartType; + return FileType::StateChart; } QString QScxmlcGeneratorFactory::sourceTag() const diff --git a/src/plugins/qtsupport/uicgenerator.cpp b/src/plugins/qtsupport/uicgenerator.cpp index ff8c93e1bb..a88619e521 100644 --- a/src/plugins/qtsupport/uicgenerator.cpp +++ b/src/plugins/qtsupport/uicgenerator.cpp @@ -88,7 +88,7 @@ FileNameToContentsHash UicGenerator::handleProcessFinished(QProcess *process) FileType UicGeneratorFactory::sourceType() const { - return FormType; + return FileType::Form; } QString UicGeneratorFactory::sourceTag() const diff --git a/src/plugins/resourceeditor/resourcenode.cpp b/src/plugins/resourceeditor/resourcenode.cpp index f76bdd11de..1bb7b8c0ed 100644 --- a/src/plugins/resourceeditor/resourcenode.cpp +++ b/src/plugins/resourceeditor/resourcenode.cpp @@ -44,7 +44,7 @@ using namespace ResourceEditor; using namespace ResourceEditor::Internal; -static bool priority(const QStringList &files) +static bool hasPriority(const QStringList &files) { if (files.isEmpty()) return false; @@ -226,7 +226,7 @@ void ResourceTopLevelNode::update() } - QList<ProjectExplorer::FolderNode *> oldPrefixList = subFolderNodes(); + QList<ProjectExplorer::FolderNode *> oldPrefixList = folderNodes(); QList<ProjectExplorer::FolderNode *> prefixesToAdd; QList<ProjectExplorer::FolderNode *> prefixesToRemove; @@ -242,12 +242,12 @@ void ResourceTopLevelNode::update() // delete nodes that weren't added qDeleteAll(ProjectExplorer::subtractSortedList(newPrefixList, prefixesToAdd, sortByPrefixAndLang)); - foreach (FolderNode *sfn, subFolderNodes()) { + foreach (FolderNode *sfn, folderNodes()) { ResourceFolderNode *srn = static_cast<ResourceFolderNode *>(sfn); PrefixFolderLang folderId(srn->prefix(), QString(), srn->lang()); srn->updateFiles(filesToAdd[folderId]); srn->updateFolders(foldersToAddToPrefix[folderId]); - foreach (FolderNode* ssfn, sfn->subFolderNodes()) { + foreach (FolderNode* ssfn, sfn->folderNodes()) { SimpleResourceFolderNode *sssn = static_cast<SimpleResourceFolderNode *>(ssfn); sssn->addFilesAndSubfolders(filesToAdd, foldersToAddToFolders, srn->prefix(), srn->lang()); } @@ -341,11 +341,11 @@ ProjectExplorer::FolderNode::AddNewInformation ResourceTopLevelNode::addNewInfor .arg(QLatin1Char('/')); int p = -1; - if (priority(files)) { // images/* and qml/js mimetypes + if (hasPriority(files)) { // images/* and qml/js mimetypes p = 110; if (context == this) p = 120; - else if (projectNode() == context) + else if (parentProjectNode() == context) p = 150; // steal from our project node // The ResourceFolderNode '/' defers to us, as otherwise // two nodes would be responsible for '/' @@ -497,7 +497,7 @@ ProjectExplorer::FolderNode::AddNewInformation ResourceFolderNode::addNewInforma .arg(displayName()); int p = -1; // never the default - if (priority(files)) { // image/* and qml/js mimetypes + if (hasPriority(files)) { // image/* and qml/js mimetypes p = 105; // prefer against .pro and .pri files if (context == this) p = 120; @@ -552,7 +552,7 @@ void ResourceFolderNode::updateFiles(QList<ProjectExplorer::FileNode *> newList) void ResourceFolderNode::updateFolders(QList<ProjectExplorer::FolderNode *> newList) { - QList<ProjectExplorer::FolderNode *> oldList = subFolderNodes(); + QList<ProjectExplorer::FolderNode *> oldList = folderNodes(); QList<ProjectExplorer::FolderNode *> foldersToAdd; QList<ProjectExplorer::FolderNode *> foldersToRemove; @@ -593,7 +593,7 @@ bool ResourceFileWatcher::reload(QString *errorString, ReloadFlag flag, ChangeTy } ResourceFileNode::ResourceFileNode(const Utils::FileName &filePath, const QString &qrcPath, const QString &displayName) - : ProjectExplorer::FileNode(filePath, ProjectExplorer::UnknownFileType, false) + : ProjectExplorer::FileNode(filePath, ProjectExplorer::FileType::Unknown, false) , m_qrcPath(qrcPath) , m_displayName(displayName) { @@ -737,7 +737,7 @@ void SimpleResourceFolderNode::updateFiles(QList<ProjectExplorer::FileNode *> ne void SimpleResourceFolderNode::updateFolders(QList<ProjectExplorer::FolderNode *> newList) { - QList<ProjectExplorer::FolderNode *> oldList = subFolderNodes(); + QList<ProjectExplorer::FolderNode *> oldList = folderNodes(); QList<ProjectExplorer::FolderNode *> foldersToAdd; QList<ProjectExplorer::FolderNode *> foldersToRemove; @@ -761,7 +761,7 @@ void SimpleResourceFolderNode::addFilesAndSubfolders(QMap<PrefixFolderLang, { updateFiles(filesToAdd.value(PrefixFolderLang(prefix, m_folderName, lang))); updateFolders(foldersToAdd.value(PrefixFolderLang(prefix, m_folderName, lang))); - foreach (FolderNode* subNode, subFolderNodes()) { + foreach (FolderNode* subNode, folderNodes()) { SimpleResourceFolderNode* sn = static_cast<SimpleResourceFolderNode*>(subNode); sn->addFilesAndSubfolders(filesToAdd, foldersToAdd, prefix, lang); } diff --git a/src/plugins/subversion/subversionclient.cpp b/src/plugins/subversion/subversionclient.cpp index d17ba17625..73e69806e2 100644 --- a/src/plugins/subversion/subversionclient.cpp +++ b/src/plugins/subversion/subversionclient.cpp @@ -31,7 +31,7 @@ #include <vcsbase/vcscommand.h> #include <vcsbase/vcsbaseconstants.h> #include <vcsbase/vcsbaseeditor.h> -#include <vcsbase/vcsbaseeditorparameterwidget.h> +#include <vcsbase/vcsbaseeditorconfig.h> #include <vcsbase/vcsbaseplugin.h> #include <utils/qtcassert.h> #include <utils/synchronousprocess.h> @@ -55,12 +55,12 @@ using namespace VcsBase; namespace Subversion { namespace Internal { -class SubversionLogParameterWidget : public VcsBaseEditorParameterWidget +class SubversionLogConfig : public VcsBaseEditorConfig { Q_OBJECT public: - SubversionLogParameterWidget(VcsBaseClientSettings &settings, QWidget *parent = 0) : - VcsBaseEditorParameterWidget(parent) + SubversionLogConfig(VcsBaseClientSettings &settings, QToolBar *toolBar) : + VcsBaseEditorConfig(toolBar) { mapSetting(addToggleButton(QLatin1String("--verbose"), tr("Verbose"), tr("Show files changed in each revision")), @@ -70,7 +70,9 @@ public: SubversionClient::SubversionClient() : VcsBaseClient(new SubversionSettings) { - setLogParameterWidgetCreator([this] { return new SubversionLogParameterWidget(settings()); }); + setLogConfigCreator([this](QToolBar *toolBar) { + return new SubversionLogConfig(settings(), toolBar); + }); } VcsCommand *SubversionClient::createCommitCmd(const QString &repositoryRoot, diff --git a/src/plugins/subversion/subversioncontrol.cpp b/src/plugins/subversion/subversioncontrol.cpp index c8de2421b7..e7265534ea 100644 --- a/src/plugins/subversion/subversioncontrol.cpp +++ b/src/plugins/subversion/subversioncontrol.cpp @@ -77,6 +77,11 @@ Core::Id SubversionControl::id() const return Core::Id(VcsBase::Constants::VCS_ID_SUBVERSION); } +bool SubversionControl::isVcsFileOrDirectory(const Utils::FileName &fileName) const +{ + return m_plugin->isVcsDirectory(fileName); +} + bool SubversionControl::isConfigured() const { const Utils::FileName binary = m_plugin->client()->vcsBinary(); diff --git a/src/plugins/subversion/subversioncontrol.h b/src/plugins/subversion/subversioncontrol.h index 9f244fd529..8e63be6427 100644 --- a/src/plugins/subversion/subversioncontrol.h +++ b/src/plugins/subversion/subversioncontrol.h @@ -38,26 +38,27 @@ class SubversionControl : public Core::IVersionControl Q_OBJECT public: explicit SubversionControl(SubversionPlugin *plugin); - QString displayName() const override; - Core::Id id() const override; + QString displayName() const final; + Core::Id id() const final; + bool isVcsFileOrDirectory(const Utils::FileName &fileName) const final; - bool managesDirectory(const QString &directory, QString *topLevel = 0) const override; - bool managesFile(const QString &workingDirectory, const QString &fileName) const override; + bool managesDirectory(const QString &directory, QString *topLevel = 0) const final; + bool managesFile(const QString &workingDirectory, const QString &fileName) const final; - bool isConfigured() const override; - bool supportsOperation(Operation operation) const override; - bool vcsOpen(const QString &fileName) override; - bool vcsAdd(const QString &fileName) override; - bool vcsDelete(const QString &filename) override; - bool vcsMove(const QString &from, const QString &to) override; - bool vcsCreateRepository(const QString &directory) override; + bool isConfigured() const final; + bool supportsOperation(Operation operation) const final; + bool vcsOpen(const QString &fileName) final; + bool vcsAdd(const QString &fileName) final; + bool vcsDelete(const QString &filename) final; + bool vcsMove(const QString &from, const QString &to) final; + bool vcsCreateRepository(const QString &directory) final; - bool vcsAnnotate(const QString &file, int line) override; + bool vcsAnnotate(const QString &file, int line) final; Core::ShellCommand *createInitialCheckoutCommand(const QString &url, const Utils::FileName &baseDirectory, const QString &localName, - const QStringList &extraArgs) override; + const QStringList &extraArgs) final; void emitRepositoryChanged(const QString &); void emitFilesChanged(const QStringList &); diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp index 9c17f34f81..2abc041d12 100644 --- a/src/plugins/subversion/subversionplugin.cpp +++ b/src/plugins/subversion/subversionplugin.cpp @@ -39,7 +39,6 @@ #include <vcsbase/basevcssubmiteditorfactory.h> #include <vcsbase/vcsbaseconstants.h> #include <vcsbase/vcsoutputwindow.h> -#include <vcsbase/vcsbaseeditorparameterwidget.h> #include <texteditor/textdocument.h> @@ -54,6 +53,7 @@ #include <coreplugin/locator/commandlocator.h> #include <coreplugin/messagemanager.h> +#include <utils/algorithm.h> #include <utils/fileutils.h> #include <utils/hostosinfo.h> #include <utils/mimetypes/mimedatabase.h> @@ -408,6 +408,15 @@ bool SubversionPlugin::initialize(const QStringList & /*arguments */, QString *e return true; } +bool SubversionPlugin::isVcsDirectory(const FileName &fileName) +{ + const QString baseName = fileName.fileName(); + return fileName.toFileInfo().isDir() + && contains(m_svnDirectories, [baseName](const QString &s) { + return !baseName.compare(s, HostOsInfo::fileNameCaseSensitivity()); + }); +} + SubversionClient *SubversionPlugin::client() const { QTC_CHECK(m_client); diff --git a/src/plugins/subversion/subversionplugin.h b/src/plugins/subversion/subversionplugin.h index da05a072a5..3c614eeb7c 100644 --- a/src/plugins/subversion/subversionplugin.h +++ b/src/plugins/subversion/subversionplugin.h @@ -73,6 +73,8 @@ public: bool initialize(const QStringList &arguments, QString *errorMessage); + bool isVcsDirectory(const Utils::FileName &fileName); + SubversionClient *client() const; SubversionSubmitEditor *openSubversionSubmitEditor(const QString &fileName); diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 5b1f0f6d7a..21c097913f 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -256,6 +256,8 @@ public: void print(QPrinter *printer); void maybeSelectLine(); + void duplicateSelection(bool comment); + void duplicateBlockSelection(bool comment); void updateCannotDecodeInfo(); void collectToCircularClipboard(); @@ -6165,6 +6167,128 @@ void TextEditorWidget::copyLine() copy(); } +void TextEditorWidgetPrivate::duplicateSelection(bool comment) +{ + if (m_inBlockSelectionMode) { + duplicateBlockSelection(comment); + return; + } + + QTextCursor cursor = q->textCursor(); + + if (cursor.hasSelection()) { + + // Cannot "duplicate and comment" files without multi-line comment + if (comment && !m_commentDefinition.hasMultiLineStyle()) + return; + + QString dupText = cursor.selectedText().replace(QChar::ParagraphSeparator, QLatin1Char('\n')); + if (comment) + dupText = (m_commentDefinition.multiLineStart + dupText + m_commentDefinition.multiLineEnd); + const int selStart = cursor.selectionStart(); + const int selEnd = cursor.selectionEnd(); + const bool cursorAtStart = (cursor.position() == selStart); + cursor.setPosition(selEnd); + cursor.insertText(dupText); + cursor.setPosition(cursorAtStart ? selEnd : selStart); + cursor.setPosition(cursorAtStart ? selStart : selEnd, QTextCursor::KeepAnchor); + } else { + const int curPos = cursor.position(); + const QTextBlock &block = cursor.block(); + QString dupText = block.text() + QLatin1Char('\n'); + if (comment && m_commentDefinition.hasSingleLineStyle()) + dupText.append(m_commentDefinition.singleLine); + cursor.setPosition(block.position()); + cursor.insertText(dupText); + cursor.setPosition(curPos); + } + q->setTextCursor(cursor); +} + +void TextEditorWidgetPrivate::duplicateBlockSelection(bool comment) +{ + QTextCursor cursor = q->textCursor(); + + const TextBlockSelection curSel = m_blockSelection; + + if (curSel.positionColumn == curSel.anchorColumn) { + // No columns selected, duplicating multiple lines + + const bool isUp = curSel.positionBlock > curSel.anchorBlock; + const QString commentText = + (comment && m_commentDefinition.hasSingleLineStyle()) ? + m_commentDefinition.singleLine : QString(); + + QTextBlock block = cursor.block(); + QString dupText = commentText + block.text() + QLatin1Char('\n'); + + for (int b = curSel.firstBlockNumber(); b < curSel.lastBlockNumber(); ++b) { + if (isUp) { + block = block.previous(); + dupText.prepend(commentText + block.text() + QLatin1Char('\n')); + } else { + block = block.next(); + dupText.append(commentText + block.text() + QLatin1Char('\n')); + } + } + + if (isUp) + block = cursor.block(); + + cursor.setPosition(block.position() + block.length()); + cursor.insertText(dupText); + + } else { + // Duplicating full block selection with columns + + // Cannot "duplicate and comment" files without multi-line comment + if (comment && !m_commentDefinition.hasMultiLineStyle()) + return; + + const int fc = curSel.firstVisualColumn(); + const int lc = curSel.lastVisualColumn(); + const int l = lc - fc; + + cursor.beginEditBlock(); + for (int b = curSel.firstBlockNumber(); b <= curSel.lastBlockNumber(); ++b) { + const QTextBlock &block = m_document->document()->findBlockByNumber(b); + QString dupText = block.text(); + const int dupTextLength = dupText.length(); + + if (dupTextLength < lc) { + const QString addSpace(lc - dupTextLength, ' '); + cursor.setPosition(block.position() + dupTextLength); + cursor.insertText(addSpace); + dupText.append(addSpace); + } + + cursor.setPosition(block.position() + lc); + dupText = dupText.mid(fc, l); + + if (comment) + dupText = (m_commentDefinition.multiLineStart + dupText + m_commentDefinition.multiLineEnd); + cursor.insertText(dupText); + } + cursor.endEditBlock(); + } + + enableBlockSelection(curSel.positionBlock, curSel.positionColumn, + curSel.anchorBlock, curSel.anchorColumn); + + cursor = m_blockSelection.cursor(m_document.data()); + q->doSetTextCursor(cursor, m_blockSelection.hasSelection()); +} + +void TextEditorWidget::duplicateSelection() +{ + d->duplicateSelection(false); +} + +void TextEditorWidget::duplicateSelectionAndComment() +{ + d->duplicateSelection(true); +} + void TextEditorWidget::deleteLine() { d->maybeSelectLine(); @@ -7145,6 +7269,11 @@ void TextEditorWidget::setCursorPosition(int pos) setTextCursor(tc); } +QToolBar *TextEditorWidget::toolBar() +{ + return d->m_toolBar; +} + void BaseTextEditor::select(int toPos) { editorWidget()->setBlockSelection(false); diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h index 2cd60da380..592bd73e57 100644 --- a/src/plugins/texteditor/texteditor.h +++ b/src/plugins/texteditor/texteditor.h @@ -190,6 +190,7 @@ public: using QPlainTextEdit::cursorRect; QRect cursorRect(int pos) const; void setCursorPosition(int pos); + QToolBar *toolBar(); void print(QPrinter *); @@ -357,6 +358,8 @@ public: void cutLine(); void copyLine(); + void duplicateSelection(); + void duplicateSelectionAndComment(); void deleteLine(); void deleteEndOfWord(); void deleteEndOfWordCamelCase(); diff --git a/src/plugins/texteditor/texteditoractionhandler.cpp b/src/plugins/texteditor/texteditoractionhandler.cpp index 81472b6eea..bbd14f9efd 100644 --- a/src/plugins/texteditor/texteditoractionhandler.cpp +++ b/src/plugins/texteditor/texteditoractionhandler.cpp @@ -142,6 +142,8 @@ public: QAction *m_unfoldAction; QAction *m_cutLineAction; QAction *m_copyLineAction; + QAction *m_duplicateSelectionAction; + QAction *m_duplicateSelectionAndCommentAction; QAction *m_deleteLineAction; QAction *m_deleteEndOfWordAction; QAction *m_deleteEndOfWordCamelCaseAction; @@ -206,6 +208,8 @@ TextEditorActionHandlerPrivate::TextEditorActionHandlerPrivate m_unfoldAction(0), m_cutLineAction(0), m_copyLineAction(0), + m_duplicateSelectionAction(0), + m_duplicateSelectionAndCommentAction(0), m_deleteLineAction(0), m_deleteEndOfWordAction(0), m_deleteEndOfWordCamelCaseAction(0), @@ -403,6 +407,14 @@ void TextEditorActionHandlerPrivate::createActions() [this] (TextEditorWidget *w) { w->copyLine(); }, false, tr("Copy &Line"), QKeySequence(tr("Ctrl+Ins")), G_EDIT_TEXT, advancedEditMenu); + m_duplicateSelectionAction = registerAction(DUPLICATE_SELECTION, + [this] (TextEditorWidget *w) { w->duplicateSelection(); }, false, tr("&Duplicate Selection"), + QKeySequence(), + G_EDIT_TEXT, advancedEditMenu); + m_duplicateSelectionAndCommentAction = registerAction(DUPLICATE_SELECTION_AND_COMMENT, + [this] (TextEditorWidget *w) { w->duplicateSelectionAndComment(); }, false, tr("&Duplicate Selection and Comment"), + QKeySequence(), + G_EDIT_TEXT, advancedEditMenu); m_upperCaseSelectionAction = registerAction(UPPERCASE_SELECTION, [this] (TextEditorWidget *w) { w->uppercaseSelection(); }, true, tr("Uppercase Selection"), QKeySequence(Core::UseMacShortcuts ? tr("Meta+Shift+U") : tr("Alt+Shift+U")), diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h index 8dbaf93de4..9b392e3980 100644 --- a/src/plugins/texteditor/texteditorconstants.h +++ b/src/plugins/texteditor/texteditorconstants.h @@ -144,6 +144,8 @@ const char UPPERCASE_SELECTION[] = "TextEditor.UppercaseSelection"; const char LOWERCASE_SELECTION[] = "TextEditor.LowercaseSelection"; const char CUT_LINE[] = "TextEditor.CutLine"; const char COPY_LINE[] = "TextEditor.CopyLine"; +const char DUPLICATE_SELECTION[] = "TextEditor.DuplicateSelection"; +const char DUPLICATE_SELECTION_AND_COMMENT[] = "TextEditor.DuplicateSelectionAndComment"; const char DELETE_LINE[] = "TextEditor.DeleteLine"; const char DELETE_END_OF_WORD[] = "TextEditor.DeleteEndOfWord"; const char DELETE_END_OF_WORD_CAMEL_CASE[] = "TextEditor.DeleteEndOfWordCamelCase"; diff --git a/src/plugins/todo/todoitemsprovider.cpp b/src/plugins/todo/todoitemsprovider.cpp index 3bedefac05..858efa6731 100644 --- a/src/plugins/todo/todoitemsprovider.cpp +++ b/src/plugins/todo/todoitemsprovider.cpp @@ -146,7 +146,7 @@ void TodoItemsProvider::setItemsListWithinSubproject() // TODO prefer current editor as source of sub-project Node *node = ProjectTree::currentNode(); if (node) { - ProjectNode *projectNode = node->projectNode(); + ProjectNode *projectNode = node->parentProjectNode(); if (projectNode) { FindAllFilesVisitor filesVisitor; diff --git a/src/plugins/vcsbase/vcsbase.pro b/src/plugins/vcsbase/vcsbase.pro index 94616bd866..461dc152c0 100644 --- a/src/plugins/vcsbase/vcsbase.pro +++ b/src/plugins/vcsbase/vcsbase.pro @@ -25,7 +25,7 @@ HEADERS += vcsbase_global.h \ vcscommand.h \ vcsbaseclient.h \ vcsbaseclientsettings.h \ - vcsbaseeditorparameterwidget.h \ + vcsbaseeditorconfig.h \ submitfieldwidget.h \ submiteditorwidget.h @@ -52,7 +52,7 @@ SOURCES += vcsplugin.cpp \ vcscommand.cpp \ vcsbaseclient.cpp \ vcsbaseclientsettings.cpp \ - vcsbaseeditorparameterwidget.cpp \ + vcsbaseeditorconfig.cpp \ submitfieldwidget.cpp \ submiteditorwidget.cpp diff --git a/src/plugins/vcsbase/vcsbase.qbs b/src/plugins/vcsbase/vcsbase.qbs index a19269e857..deeb41a4c7 100644 --- a/src/plugins/vcsbase/vcsbase.qbs +++ b/src/plugins/vcsbase/vcsbase.qbs @@ -55,8 +55,8 @@ QtcPlugin { "vcsbaseconstants.h", "vcsbaseeditor.cpp", "vcsbaseeditor.h", - "vcsbaseeditorparameterwidget.cpp", - "vcsbaseeditorparameterwidget.h", + "vcsbaseeditorconfig.cpp", + "vcsbaseeditorconfig.h", "vcsbaseoptionspage.cpp", "vcsbaseoptionspage.h", "vcsbaseplugin.cpp", diff --git a/src/plugins/vcsbase/vcsbaseclient.cpp b/src/plugins/vcsbase/vcsbaseclient.cpp index 76e560597d..aa0204cc75 100644 --- a/src/plugins/vcsbase/vcsbaseclient.cpp +++ b/src/plugins/vcsbase/vcsbaseclient.cpp @@ -26,7 +26,7 @@ #include "vcsbaseclient.h" #include "vcscommand.h" #include "vcsbaseclientsettings.h" -#include "vcsbaseeditorparameterwidget.h" +#include "vcsbaseeditorconfig.h" #include <coreplugin/icore.h> #include <coreplugin/vcsmanager.h> @@ -278,21 +278,21 @@ void VcsBaseClientImpl::saveSettings() class VcsBaseClientPrivate { public: - VcsBaseEditorParameterWidget *createDiffEditor(); - VcsBaseEditorParameterWidget *createLogEditor(); + VcsBaseEditorConfig *createDiffEditor(VcsBaseEditorWidget *editor); + VcsBaseEditorConfig *createLogEditor(VcsBaseEditorWidget *editor); - VcsBaseClient::ParameterWidgetCreator m_diffParamWidgetCreator; - VcsBaseClient::ParameterWidgetCreator m_logParamWidgetCreator; + VcsBaseClient::ConfigCreator m_diffConfigCreator; + VcsBaseClient::ConfigCreator m_logConfigCreator; }; -VcsBaseEditorParameterWidget *VcsBaseClientPrivate::createDiffEditor() +VcsBaseEditorConfig *VcsBaseClientPrivate::createDiffEditor(VcsBaseEditorWidget *editor) { - return m_diffParamWidgetCreator ? m_diffParamWidgetCreator() : 0; + return m_diffConfigCreator ? m_diffConfigCreator(editor->toolBar()) : 0; } -VcsBaseEditorParameterWidget *VcsBaseClientPrivate::createLogEditor() +VcsBaseEditorConfig *VcsBaseClientPrivate::createLogEditor(VcsBaseEditorWidget *editor) { - return m_logParamWidgetCreator ? m_logParamWidgetCreator() : 0; + return m_logConfigCreator ? m_logConfigCreator(editor->toolBar()) : 0; } VcsBaseClient::StatusItem::StatusItem(const QString &s, const QString &f) : @@ -433,19 +433,21 @@ void VcsBaseClient::diff(const QString &workingDir, const QStringList &files, vcsCmdString.toLatin1().constData(), id); editor->setWorkingDirectory(workingDir); - VcsBaseEditorParameterWidget *paramWidget = editor->configurationWidget(); - if (!paramWidget && (paramWidget = d->createDiffEditor())) { - // editor has been just created, createVcsEditor() didn't set a configuration widget yet - connect(editor, &VcsBaseEditorWidget::diffChunkReverted, - paramWidget, &VcsBaseEditorParameterWidget::executeCommand); - connect(paramWidget, &VcsBaseEditorParameterWidget::commandExecutionRequested, - [=] { diff(workingDir, files, extraOptions); } ); - editor->setConfigurationWidget(paramWidget); + QStringList effectiveArgs = extraOptions; + if (!editor->configurationAdded()) { + if (VcsBaseEditorConfig *paramWidget = d->createDiffEditor(editor)) { + // editor has been just created, createVcsEditor() didn't set a configuration widget yet + connect(editor, &VcsBaseEditorWidget::diffChunkReverted, + paramWidget, &VcsBaseEditorConfig::executeCommand); + connect(paramWidget, &VcsBaseEditorConfig::commandExecutionRequested, + [=] { diff(workingDir, files, extraOptions + paramWidget->arguments()); } ); + effectiveArgs = paramWidget->arguments(); + editor->setConfigurationAdded(); + } } QStringList args; - const QStringList paramArgs = paramWidget != 0 ? paramWidget->arguments() : QStringList(); - args << vcsCmdString << extraOptions << paramArgs << files; + args << vcsCmdString << effectiveArgs << files; QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VcsBaseEditor::getCodec(source); VcsCommand *command = createCommand(workingDir, editor); command->setCodec(codec); @@ -466,17 +468,20 @@ void VcsBaseClient::log(const QString &workingDir, const QStringList &files, vcsCmdString.toLatin1().constData(), id); editor->setFileLogAnnotateEnabled(enableAnnotationContextMenu); - VcsBaseEditorParameterWidget *paramWidget = editor->configurationWidget(); - if (!paramWidget && (paramWidget = d->createLogEditor())) { - // editor has been just created, createVcsEditor() didn't set a configuration widget yet - connect(paramWidget, &VcsBaseEditorParameterWidget::commandExecutionRequested, - [=]() { this->log(workingDir, files, extraOptions, enableAnnotationContextMenu); } ); - editor->setConfigurationWidget(paramWidget); + QStringList effectiveArgs = extraOptions; + if (!editor->configurationAdded()) { + if (VcsBaseEditorConfig *paramWidget = d->createLogEditor(editor)) { + // editor has been just created, createVcsEditor() didn't set a configuration widget yet + connect(paramWidget, &VcsBaseEditorConfig::commandExecutionRequested, + [=]() { this->log(workingDir, files, extraOptions + paramWidget->arguments(), + enableAnnotationContextMenu); } ); + effectiveArgs = paramWidget->arguments(); + editor->setConfigurationAdded(); + } } QStringList args; - const QStringList paramArgs = paramWidget != 0 ? paramWidget->arguments() : QStringList(); - args << vcsCmdString << extraOptions << paramArgs << files; + args << vcsCmdString << effectiveArgs << files; enqueueJob(createCommand(workingDir, editor), args); } @@ -556,14 +561,14 @@ Utils::ExitCodeInterpreter VcsBaseClient::exitCodeInterpreter(VcsCommandTag cmd) return Utils::defaultExitCodeInterpreter; } -void VcsBaseClient::setDiffParameterWidgetCreator(ParameterWidgetCreator creator) +void VcsBaseClient::setDiffConfigCreator(ConfigCreator creator) { - d->m_diffParamWidgetCreator = std::move(creator); + d->m_diffConfigCreator = std::move(creator); } -void VcsBaseClient::setLogParameterWidgetCreator(ParameterWidgetCreator creator) +void VcsBaseClient::setLogConfigCreator(ConfigCreator creator) { - d->m_logParamWidgetCreator = std::move(creator); + d->m_logConfigCreator = std::move(creator); } void VcsBaseClient::import(const QString &repositoryRoot, const QStringList &files, diff --git a/src/plugins/vcsbase/vcsbaseclient.h b/src/plugins/vcsbase/vcsbaseclient.h index 2c97afb076..d693a4357e 100644 --- a/src/plugins/vcsbase/vcsbaseclient.h +++ b/src/plugins/vcsbase/vcsbaseclient.h @@ -39,6 +39,7 @@ QT_BEGIN_NAMESPACE class QFileInfo; class QProcessEnvironment; +class QToolBar; QT_END_NAMESPACE namespace Core { class Id; } @@ -51,7 +52,7 @@ class VcsBaseClientSettings; class VcsJob; class VcsBaseClientImplPrivate; class VcsBaseClientPrivate; -class VcsBaseEditorParameterWidget; +class VcsBaseEditorConfig; class VCSBASE_EXPORT VcsBaseClientImpl : public QObject { @@ -227,9 +228,9 @@ protected: virtual QStringList revisionSpec(const QString &revision) const = 0; - typedef std::function<VcsBaseEditorParameterWidget *()> ParameterWidgetCreator; - void setDiffParameterWidgetCreator(ParameterWidgetCreator creator); - void setLogParameterWidgetCreator(ParameterWidgetCreator creator); + typedef std::function<VcsBaseEditorConfig *(QToolBar *)> ConfigCreator; + void setDiffConfigCreator(ConfigCreator creator); + void setLogConfigCreator(ConfigCreator creator); virtual StatusItem parseStatusLine(const QString &line) const = 0; diff --git a/src/plugins/vcsbase/vcsbaseeditor.cpp b/src/plugins/vcsbase/vcsbaseeditor.cpp index 6d6678f6fc..b179923112 100644 --- a/src/plugins/vcsbase/vcsbaseeditor.cpp +++ b/src/plugins/vcsbase/vcsbaseeditor.cpp @@ -28,7 +28,7 @@ #include "baseannotationhighlighter.h" #include "basevcseditorfactory.h" #include "vcsbaseplugin.h" -#include "vcsbaseeditorparameterwidget.h" +#include "vcsbaseeditorconfig.h" #include "vcscommand.h" #include <coreplugin/icore.h> @@ -565,7 +565,7 @@ public: QString m_annotateRevisionTextFormat; QString m_annotatePreviousRevisionTextFormat; QString m_copyRevisionTextFormat; - VcsBaseEditorParameterWidget *m_configurationWidget = nullptr; + bool m_configurationAdded = false; QList<AbstractTextCursorHandler *> m_textCursorHandlers; QPointer<VcsCommand> m_command; QObject *m_describeReceiver = nullptr; @@ -1390,20 +1390,14 @@ QString VcsBaseEditor::getTitleId(const QString &workingDirectory, return rc; } -bool VcsBaseEditorWidget::setConfigurationWidget(VcsBaseEditorParameterWidget *w) +void VcsBaseEditorWidget::setConfigurationAdded() { - if (d->m_configurationWidget) - return false; - - d->m_configurationWidget = w; - insertExtraToolBarWidget(TextEditorWidget::Right, w); - - return true; + d->m_configurationAdded = true; } -VcsBaseEditorParameterWidget *VcsBaseEditorWidget::configurationWidget() const +bool VcsBaseEditorWidget::configurationAdded() const { - return d->m_configurationWidget; + return d->m_configurationAdded; } void VcsBaseEditorWidget::setCommand(VcsCommand *command) diff --git a/src/plugins/vcsbase/vcsbaseeditor.h b/src/plugins/vcsbase/vcsbaseeditor.h index d23eee46ff..4b765e1f53 100644 --- a/src/plugins/vcsbase/vcsbaseeditor.h +++ b/src/plugins/vcsbase/vcsbaseeditor.h @@ -46,7 +46,7 @@ class VcsBaseEditorWidgetPrivate; class BaseAnnotationHighlighter; class VcsBaseEditorWidget; -class VcsBaseEditorParameterWidget; +class VcsBaseEditorConfig; class VcsCommand; // Documentation inside @@ -209,8 +209,8 @@ public: EditorContentType contentType() const; - bool setConfigurationWidget(VcsBaseEditorParameterWidget *w); - VcsBaseEditorParameterWidget *configurationWidget() const; + void setConfigurationAdded(); + bool configurationAdded() const; void setCommand(VcsCommand *command); diff --git a/src/plugins/vcsbase/vcsbaseeditorparameterwidget.cpp b/src/plugins/vcsbase/vcsbaseeditorconfig.cpp index bd4aaf5f7e..8ef9f506f6 100644 --- a/src/plugins/vcsbase/vcsbaseeditorparameterwidget.cpp +++ b/src/plugins/vcsbase/vcsbaseeditorconfig.cpp @@ -23,10 +23,10 @@ ** ****************************************************************************/ -#include "vcsbaseeditorparameterwidget.h" +#include "vcsbaseeditorconfig.h" #include <QComboBox> -#include <QToolButton> +#include <QAction> #include <QHBoxLayout> #include <QStringList> @@ -74,66 +74,65 @@ private: Type m_type; }; -class VcsBaseEditorParameterWidgetPrivate +class VcsBaseEditorConfigPrivate { public: - VcsBaseEditorParameterWidgetPrivate() : - m_layout(0) - { } + VcsBaseEditorConfigPrivate(QToolBar *toolBar) : m_toolBar(toolBar) + { + if (!toolBar) + return; + toolBar->setContentsMargins(3, 0, 3, 0); + toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); + } QStringList m_baseArguments; - QHBoxLayout *m_layout; - QList<VcsBaseEditorParameterWidget::OptionMapping> m_optionMappings; - QHash<QWidget*, SettingMappingData> m_settingMapping; + QList<VcsBaseEditorConfig::OptionMapping> m_optionMappings; + QHash<QObject *, SettingMappingData> m_settingMapping; + QToolBar *m_toolBar; }; } // namespace Internal /*! - \class VcsBase::VcsBaseEditorParameterWidget + \class VcsBase::VcsBaseEditorConfig - \brief The VcsBaseEditorParameterWidget is a toolbar-like widget for use - with VcsBase::VcsBaseEditor::setConfigurationWidget() - influencing for example the generation of VCS diff output. + \brief The VcsBaseEditorConfig is a widget/action aggregator for use + with VcsBase::VcsBaseEditor, influencing for example the generation of VCS diff output. - The widget maintains a list of command line arguments (starting from baseArguments()) + The class maintains a list of command line arguments (starting from baseArguments()) which are set according to the state of the inside widgets. A change signal is provided that should trigger the rerun of the VCS operation. */ -VcsBaseEditorParameterWidget::ComboBoxItem::ComboBoxItem(const QString &text, - const QVariant &val) : +VcsBaseEditorConfig::ComboBoxItem::ComboBoxItem(const QString &text, const QVariant &val) : displayText(text), value(val) { } -VcsBaseEditorParameterWidget::VcsBaseEditorParameterWidget(QWidget *parent) : - QWidget(parent), d(new Internal::VcsBaseEditorParameterWidgetPrivate) +VcsBaseEditorConfig::VcsBaseEditorConfig(QToolBar *toolBar) : + QObject(toolBar), d(new Internal::VcsBaseEditorConfigPrivate(toolBar)) { - d->m_layout = new QHBoxLayout(this); - d->m_layout->setContentsMargins(3, 0, 3, 0); - d->m_layout->setSpacing(2); - connect(this, &VcsBaseEditorParameterWidget::argumentsChanged, - this, &VcsBaseEditorParameterWidget::handleArgumentsChanged); + connect(this, &VcsBaseEditorConfig::argumentsChanged, + this, &VcsBaseEditorConfig::handleArgumentsChanged); } -VcsBaseEditorParameterWidget::~VcsBaseEditorParameterWidget() +VcsBaseEditorConfig::~VcsBaseEditorConfig() { delete d; } -QStringList VcsBaseEditorParameterWidget::baseArguments() const +QStringList VcsBaseEditorConfig::baseArguments() const { return d->m_baseArguments; } -void VcsBaseEditorParameterWidget::setBaseArguments(const QStringList &b) +void VcsBaseEditorConfig::setBaseArguments(const QStringList &b) { d->m_baseArguments = b; } -QStringList VcsBaseEditorParameterWidget::arguments() const +QStringList VcsBaseEditorConfig::arguments() const { // Compile effective arguments QStringList args = baseArguments(); @@ -142,39 +141,42 @@ QStringList VcsBaseEditorParameterWidget::arguments() const return args; } -QToolButton *VcsBaseEditorParameterWidget::addToggleButton(const QString &option, - const QString &label, - const QString &tooltip) +QAction *VcsBaseEditorConfig::addToggleButton(const QString &option, + const QString &label, + const QString &tooltip) { return addToggleButton(option.isEmpty() ? QStringList() : QStringList(option), label, tooltip); } -QToolButton *VcsBaseEditorParameterWidget::addToggleButton(const QStringList &options, const QString &label, const QString &tooltip) +QAction *VcsBaseEditorConfig::addToggleButton(const QStringList &options, + const QString &label, + const QString &tooltip) { - auto tb = new QToolButton; - tb->setText(label); - tb->setToolTip(tooltip); - tb->setCheckable(true); - connect(tb, &QToolButton::toggled, this, &VcsBaseEditorParameterWidget::argumentsChanged); - d->m_layout->addWidget(tb); - d->m_optionMappings.append(OptionMapping(options, tb)); - return tb; + auto action = new QAction(label, d->m_toolBar); + action->setToolTip(tooltip); + action->setCheckable(true); + connect(action, &QAction::toggled, this, &VcsBaseEditorConfig::argumentsChanged); + const QList<QAction *> actions = d->m_toolBar->actions(); + // Insert the action before line/column and split actions. + d->m_toolBar->insertAction(actions.at(qMax(actions.count() - 2, 0)), action); + d->m_optionMappings.append(OptionMapping(options, action)); + return action; } -QComboBox *VcsBaseEditorParameterWidget::addComboBox(const QStringList &options, - const QList<ComboBoxItem> &items) +QComboBox *VcsBaseEditorConfig::addComboBox(const QStringList &options, + const QList<ComboBoxItem> &items) { auto cb = new QComboBox; foreach (const ComboBoxItem &item, items) cb->addItem(item.displayText, item.value); connect(cb, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), - this, &VcsBaseEditorParameterWidget::argumentsChanged); - d->m_layout->addWidget(cb); + this, &VcsBaseEditorConfig::argumentsChanged); + d->m_toolBar->addWidget(cb); d->m_optionMappings.append(OptionMapping(options, cb)); return cb; } -void VcsBaseEditorParameterWidget::mapSetting(QToolButton *button, bool *setting) +void VcsBaseEditorConfig::mapSetting(QAction *button, bool *setting) { if (!d->m_settingMapping.contains(button) && button) { d->m_settingMapping.insert(button, Internal::SettingMappingData(setting)); @@ -186,7 +188,7 @@ void VcsBaseEditorParameterWidget::mapSetting(QToolButton *button, bool *setting } } -void VcsBaseEditorParameterWidget::mapSetting(QComboBox *comboBox, QString *setting) +void VcsBaseEditorConfig::mapSetting(QComboBox *comboBox, QString *setting) { if (!d->m_settingMapping.contains(comboBox) && comboBox) { d->m_settingMapping.insert(comboBox, Internal::SettingMappingData(setting)); @@ -200,7 +202,7 @@ void VcsBaseEditorParameterWidget::mapSetting(QComboBox *comboBox, QString *sett } } -void VcsBaseEditorParameterWidget::mapSetting(QComboBox *comboBox, int *setting) +void VcsBaseEditorConfig::mapSetting(QComboBox *comboBox, int *setting) { if (d->m_settingMapping.contains(comboBox) || !comboBox) return; @@ -215,42 +217,42 @@ void VcsBaseEditorParameterWidget::mapSetting(QComboBox *comboBox, int *setting) comboBox->blockSignals(false); } -void VcsBaseEditorParameterWidget::handleArgumentsChanged() +void VcsBaseEditorConfig::handleArgumentsChanged() { updateMappedSettings(); executeCommand(); } -void VcsBaseEditorParameterWidget::executeCommand() +void VcsBaseEditorConfig::executeCommand() { emit commandExecutionRequested(); } -VcsBaseEditorParameterWidget::OptionMapping::OptionMapping(const QString &option, QWidget *w) : - widget(w) +VcsBaseEditorConfig::OptionMapping::OptionMapping(const QString &option, QObject *obj) : + object(obj) { if (!option.isEmpty()) options << option; } -VcsBaseEditorParameterWidget::OptionMapping::OptionMapping(const QStringList &optionList, QWidget *w) : +VcsBaseEditorConfig::OptionMapping::OptionMapping(const QStringList &optionList, QObject *obj) : options(optionList), - widget(w) + object(obj) { } -const QList<VcsBaseEditorParameterWidget::OptionMapping> &VcsBaseEditorParameterWidget::optionMappings() const +const QList<VcsBaseEditorConfig::OptionMapping> &VcsBaseEditorConfig::optionMappings() const { return d->m_optionMappings; } -QStringList VcsBaseEditorParameterWidget::argumentsForOption(const OptionMapping &mapping) const +QStringList VcsBaseEditorConfig::argumentsForOption(const OptionMapping &mapping) const { - const QToolButton *tb = qobject_cast<const QToolButton *>(mapping.widget); - if (tb && tb->isChecked()) + const QAction *action = qobject_cast<const QAction *>(mapping.object); + if (action && action->isChecked()) return mapping.options; - const QComboBox *cb = qobject_cast<const QComboBox *>(mapping.widget); + const QComboBox *cb = qobject_cast<const QComboBox *>(mapping.object); if (cb) { const QString value = cb->itemData(cb->currentIndex()).toString(); QStringList args; @@ -262,29 +264,28 @@ QStringList VcsBaseEditorParameterWidget::argumentsForOption(const OptionMapping return QStringList(); } -void VcsBaseEditorParameterWidget::updateMappedSettings() +void VcsBaseEditorConfig::updateMappedSettings() { foreach (const OptionMapping &optMapping, d->m_optionMappings) { - if (d->m_settingMapping.contains(optMapping.widget)) { - Internal::SettingMappingData& settingData = d->m_settingMapping[optMapping.widget]; + if (d->m_settingMapping.contains(optMapping.object)) { + Internal::SettingMappingData& settingData = d->m_settingMapping[optMapping.object]; switch (settingData.type()) { case Internal::SettingMappingData::Bool : { - const QToolButton *tb = qobject_cast<const QToolButton *>(optMapping.widget); - if (tb) - *settingData.boolSetting = tb->isChecked(); + if (auto action = qobject_cast<const QAction *>(optMapping.object)) + *settingData.boolSetting = action->isChecked(); break; } case Internal::SettingMappingData::String : { - const QComboBox *cb = qobject_cast<const QComboBox *>(optMapping.widget); + const QComboBox *cb = qobject_cast<const QComboBox *>(optMapping.object); if (cb && cb->currentIndex() != -1) *settingData.stringSetting = cb->itemData(cb->currentIndex()).toString(); break; } case Internal::SettingMappingData::Int: { - const QComboBox *cb = qobject_cast<const QComboBox *>(optMapping.widget); + const QComboBox *cb = qobject_cast<const QComboBox *>(optMapping.object); if (cb && cb->currentIndex() != -1) *settingData.intSetting = cb->currentIndex(); break; diff --git a/src/plugins/vcsbase/vcsbaseeditorparameterwidget.h b/src/plugins/vcsbase/vcsbaseeditorconfig.h index 2152f714b5..d019b79d47 100644 --- a/src/plugins/vcsbase/vcsbaseeditorparameterwidget.h +++ b/src/plugins/vcsbase/vcsbaseeditorconfig.h @@ -28,7 +28,7 @@ #include "vcsbase_global.h" #include <QVariant> -#include <QWidget> +#include <QToolBar> QT_BEGIN_NAMESPACE class QComboBox; @@ -38,16 +38,18 @@ QT_END_NAMESPACE namespace VcsBase { -namespace Internal { class VcsBaseEditorParameterWidgetPrivate; } +class VcsBaseEditorWidget; + +namespace Internal { class VcsBaseEditorConfigPrivate; } // Documentation->inside. -class VCSBASE_EXPORT VcsBaseEditorParameterWidget : public QWidget +class VCSBASE_EXPORT VcsBaseEditorConfig : public QObject { Q_OBJECT public: - explicit VcsBaseEditorParameterWidget(QWidget *parent = 0); - ~VcsBaseEditorParameterWidget() override; + explicit VcsBaseEditorConfig(QToolBar *toolBar); + ~VcsBaseEditorConfig() override; class VCSBASE_EXPORT ComboBoxItem { @@ -61,13 +63,13 @@ public: QStringList baseArguments() const; void setBaseArguments(const QStringList &); - QToolButton *addToggleButton(const QString &option, const QString &label, - const QString &tooltip = QString()); - QToolButton *addToggleButton(const QStringList &options, const QString &label, - const QString &tooltip = QString()); + QAction *addToggleButton(const QString &option, const QString &label, + const QString &tooltip = QString()); + QAction *addToggleButton(const QStringList &options, const QString &label, + const QString &tooltip = QString()); QComboBox *addComboBox(const QStringList &options, const QList<ComboBoxItem> &items); - void mapSetting(QToolButton *button, bool *setting); + void mapSetting(QAction *button, bool *setting); void mapSetting(QComboBox *comboBox, QString *setting); void mapSetting(QComboBox *comboBox, int *setting); @@ -89,10 +91,10 @@ protected: { public: OptionMapping() = default; - OptionMapping(const QString &option, QWidget *w); - OptionMapping(const QStringList &optionList, QWidget *w); + OptionMapping(const QString &option, QObject *obj); + OptionMapping(const QStringList &optionList, QObject *obj); QStringList options; - QWidget *widget = nullptr; + QObject *object = nullptr; }; const QList<OptionMapping> &optionMappings() const; @@ -100,8 +102,8 @@ protected: void updateMappedSettings(); private: - friend class Internal::VcsBaseEditorParameterWidgetPrivate; - Internal::VcsBaseEditorParameterWidgetPrivate *const d; + friend class Internal::VcsBaseEditorConfigPrivate; + Internal::VcsBaseEditorConfigPrivate *const d; }; } // namespace VcsBase |