From ab9b346c8d6ff3df5a079030e5b5e96fe5ba266a Mon Sep 17 00:00:00 2001 From: Liu Zhangjian Date: Fri, 6 Dec 2024 13:05:54 +0800 Subject: [PATCH] refactor: [project] Optimize project tree icon loading and parsing Refactored project tree icon loading mechanism and parsing process to improve performance and maintainability: - Moved icon loading from individual items to ProjectModel data() method - Added ProjectItemRole enum for icon name and file path roles - Replaced direct icon setting with role-based data storage - Implemented stop mechanism for cmake async parsing - Moved cmake parser to separate thread for better responsiveness - Unified parsing state role usage across project types - Removed redundant icon loading code from various parsers - Added sorting capability to project tree Log: Optimize project tree icon loading and parsing mechanism --- .../cxx/cmake/project/cmakeasynparse.cpp | 76 ++++--- .../cxx/cmake/project/cmakeasynparse.h | 13 +- .../cmake/project/cmakeprojectgenerator.cpp | 189 ++++++++---------- .../cxx/cmake/project/cmakeprojectgenerator.h | 3 + .../java/gradle/project/gradleasynparse.cpp | 9 +- .../gradle/project/gradleprojectgenerator.cpp | 2 +- .../java/maven/project/mavenasynparse.cpp | 9 +- .../maven/project/mavenprojectgenerator.cpp | 2 +- .../project/mainframe/projectdelegate.cpp | 4 +- .../project/mainframe/projectmodel.cpp | 21 +- src/plugins/project/mainframe/projectmodel.h | 2 + src/plugins/project/mainframe/projecttree.cpp | 6 +- src/services/project/directoryasynparse.cpp | 9 +- src/services/project/directorygenerator.cpp | 2 +- src/services/project/projectservice.h | 7 +- 15 files changed, 185 insertions(+), 169 deletions(-) diff --git a/src/plugins/cxx/cmake/project/cmakeasynparse.cpp b/src/plugins/cxx/cmake/project/cmakeasynparse.cpp index 49e5a217c..3ba196691 100644 --- a/src/plugins/cxx/cmake/project/cmakeasynparse.cpp +++ b/src/plugins/cxx/cmake/project/cmakeasynparse.cpp @@ -25,15 +25,6 @@ enum_def(CDT_TARGETS_TYPE, QString) enum_exp Exe = "[exe]"; }; -QIcon cmakeFolderIcon() -{ - static QIcon cmakeFolderIcon; - if (cmakeFolderIcon.isNull()) { - cmakeFolderIcon = CustomIcons::icon(QFileIconProvider::Folder); - } - return cmakeFolderIcon; -} - } // namespace const QString kProjectFile = "CMakeLists.txt"; @@ -70,15 +61,14 @@ void sortParentItem(QStandardItem *parentItem) return item1->text().toLower().localeAwareCompare(item2->text().toLower()) < 0; }); - for ( auto item : cmakeFileList ) + for (auto item : cmakeFileList) parentItem->appendRow(item); - for ( auto item : directoryList ) + for (auto item : directoryList) parentItem->appendRow(item); - for ( auto item : fileList ) + for (auto item : fileList) parentItem->appendRow(item); - for ( auto item : others ) + for (auto item : others) parentItem->appendRow(item); - } CmakeAsynParse::CmakeAsynParse() @@ -89,6 +79,11 @@ CmakeAsynParse::~CmakeAsynParse() { } +void CmakeAsynParse::stop() +{ + isStop = true; +} + QString getTargetRootPath(const CMakeBuildTarget &target, const dpfservice::ProjectInfo &prjInfo) { auto srcFiles = target.srcfiles; @@ -101,7 +96,7 @@ QString getTargetRootPath(const CMakeBuildTarget &target, const dpfservice::Proj //get target root path by build-directory auto workingDirectory = target.workingDirectory; auto buildDirectory = prjInfo.buildFolder(); - if(workingDirectory.startsWith(buildDirectory)) { + if (workingDirectory.startsWith(buildDirectory)) { workingDirectory.remove(buildDirectory); return topPath + workingDirectory; } @@ -111,7 +106,7 @@ QString getTargetRootPath(const CMakeBuildTarget &target, const dpfservice::Proj QList> pathPartsList; for (const QString &filePath : srcFiles) { // remove outter file. - if ((!topPath.isEmpty() && !filePath.startsWith(topPath))/* || QFileInfo(filePath).suffix() == "h" || QFileInfo(filePath).suffix() == "hpp"*/) { + if ((!topPath.isEmpty() && !filePath.startsWith(topPath)) /* || QFileInfo(filePath).suffix() == "h" || QFileInfo(filePath).suffix() == "hpp"*/) { continue; } @@ -160,10 +155,11 @@ QString getTargetRootPath(const CMakeBuildTarget &target, const dpfservice::Proj return rootPath; } -QStandardItem *CmakeAsynParse::parseProject(QStandardItem *rootItem, const dpfservice::ProjectInfo &prjInfo) +void CmakeAsynParse::parseProject(QStandardItem *rootItem, const dpfservice::ProjectInfo &prjInfo) { + isStop = false; if (!rootItem) - return nullptr; + return; TargetsManager::instance()->readTargets(prjInfo.buildFolder(), prjInfo.workspaceFolder()); auto cbpParser = TargetsManager::instance()->cbpParser(); @@ -171,6 +167,9 @@ QStandardItem *CmakeAsynParse::parseProject(QStandardItem *rootItem, const dpfse auto cmakeList = cbpParser->getCmakeFileList(); QSet cmakeFiles {}; for (auto &cmakeFile : cmakeList) { + if (isStop) + return; + QString cmakeFilePath = cmakeFile.get()->getfilePath(); if (cmakeFilePath.endsWith("CMakeLists.txt")) cmakeFiles.insert(cmakeFilePath); @@ -187,15 +186,13 @@ QStandardItem *CmakeAsynParse::parseProject(QStandardItem *rootItem, const dpfse cmakeFileItem->setText(cmakeFileInfo.fileName()); cmakeFileItem->setToolTip(cmakeFileInfo.filePath()); cmakeParentItem->appendRow(cmakeFileItem); - QMetaObject::invokeMethod(this, [=](){ - cmakeFileItem->setIcon(CustomIcons::icon(cmakeFileInfo)); - }); + cmakeFileItem->setData(cmakeFileInfo.absoluteFilePath(), ProjectItemRole::FileIconRole); // monitor cmake file change to refresh project tree. if (cmakeParentItem == rootItem) { CmakeItemKeeper::instance()->addCmakeRootFile(rootItem, cmakeFilePath); } else { - CmakeItemKeeper::instance()->addCmakeSubFiles(rootItem, {cmakeFilePath}); + CmakeItemKeeper::instance()->addCmakeSubFiles(rootItem, { cmakeFilePath }); } } } @@ -203,6 +200,9 @@ QStandardItem *CmakeAsynParse::parseProject(QStandardItem *rootItem, const dpfse QSet commonFiles {}; const QList &targets = cbpParser->getBuildTargets(); for (auto target : targets) { + if (isStop) + return; + if (target.type == kUtility) { continue; } @@ -222,12 +222,10 @@ QStandardItem *CmakeAsynParse::parseProject(QStandardItem *rootItem, const dpfse } else if (target.type == kStaticLibrary || target.type == kDynamicLibrary) { prefix = CDT_TARGETS_TYPE::get()->Lib; } - QMetaObject::invokeMethod(this, [=](){ - if (target.type == kExecutable) - targetItem->setIcon(QIcon::fromTheme("project_executable")); - else if (target.type == kStaticLibrary || target.type == kDynamicLibrary) - targetItem->setIcon(QIcon::fromTheme("library")); - }); + if (target.type == kExecutable) + targetItem->setData("project_executable", ProjectItemRole::IconNameRole); + else if (target.type == kStaticLibrary || target.type == kDynamicLibrary) + targetItem->setData("library", ProjectItemRole::IconNameRole); QString title = prefix + target.title; targetItem->setText(title); targetItem->setToolTip(absolutePath); @@ -237,6 +235,9 @@ QStandardItem *CmakeAsynParse::parseProject(QStandardItem *rootItem, const dpfse targetRootItem->appendRow(targetItem); for (const auto &src : target.srcfiles) { + if (isStop) + return; + QFileInfo srcFileInfo(src); relativePath = QDir(targetRootPath).relativeFilePath(srcFileInfo.dir().path()); absolutePath = QDir(targetRootPath).absoluteFilePath(srcFileInfo.dir().path()); @@ -258,9 +259,9 @@ QStandardItem *CmakeAsynParse::parseProject(QStandardItem *rootItem, const dpfse QStandardItem *srcItem = new QStandardItem(); srcItem->setText(srcFileInfo.fileName()); srcItem->setToolTip(srcFileInfo.filePath()); - srcItem->setIcon(CustomIcons::icon(srcFileInfo)); + srcItem->setData(srcFileInfo.absoluteFilePath(), ProjectItemRole::FileIconRole); if (srcFileInfo.isDir()) - emit directoryCreated(srcFileInfo.filePath()); + emit directoryCreated(rootItem, srcFileInfo.filePath()); if (parentItem) parentItem->appendRow(srcItem); @@ -271,24 +272,21 @@ QStandardItem *CmakeAsynParse::parseProject(QStandardItem *rootItem, const dpfse ProjectInfo tempInfo = prjInfo; if (tempInfo.runProgram().isEmpty()) { - auto activeExecTarget = TargetsManager::instance()-> - getActivedTargetByTargetType(dpfservice::TargetType::kActiveExecTarget); + auto activeExecTarget = TargetsManager::instance()->getActivedTargetByTargetType(dpfservice::TargetType::kActiveExecTarget); tempInfo.setRunProgram(activeExecTarget.output); tempInfo.setRunWorkspaceDir(activeExecTarget.workingDir); } tempInfo.setSourceFiles(commonFiles + cmakeFiles); auto exePrograms = TargetsManager::instance()->getExeTargetNamesList(); - qSort(exePrograms.begin(), exePrograms.end(), [](const QString &s1, const QString &s2){ + qSort(exePrograms.begin(), exePrograms.end(), [](const QString &s1, const QString &s2) { return s1.toLower() < s2.toLower(); }); tempInfo.setExePrograms(exePrograms); tempInfo.setCurrentProgram(TargetsManager::instance()->getActivedTargetByTargetType(TargetType::kActiveExecTarget).name); ProjectInfo::set(rootItem, tempInfo); emit parseProjectEnd({ rootItem, true }); - rootItem->setData(ParsingState::Done, Parsing_State_Role); - - return rootItem; + rootItem->setData(ParsingState::Done, ProjectItemRole::ParsingStateRole); } QList CmakeAsynParse::parseActions(const QStandardItem *item) @@ -312,7 +310,7 @@ QList CmakeAsynParse::parseActions(const QStandardI build.buildTarget = QFileInfo(buildTarget.output).dir().path(); buildMenuList.push_back(build); - parseActionsEnd({ buildMenuList, true }); + emit parseActionsEnd({ buildMenuList, true }); return buildMenuList; } @@ -347,10 +345,10 @@ QStandardItem *CmakeAsynParse::createParentItem(QStandardItem *rootItem, const Q item = new QStandardItem(); item->setText(nameItem); item->setToolTip(basePath + relative); - item->setIcon(::cmakeFolderIcon()); + item->setData(basePath + relative, ProjectItemRole::FileIconRole); // append to parent. QStandardItem *parentItem = findParentItem(rootItem, relative); - emit directoryCreated(basePath+relative); + emit directoryCreated(rootItem, basePath + relative); parentItem->appendRow(item); sortParentItem(parentItem); } diff --git a/src/plugins/cxx/cmake/project/cmakeasynparse.h b/src/plugins/cxx/cmake/project/cmakeasynparse.h index e152380e5..b0b08c3ff 100644 --- a/src/plugins/cxx/cmake/project/cmakeasynparse.h +++ b/src/plugins/cxx/cmake/project/cmakeasynparse.h @@ -17,7 +17,7 @@ class CMakeCbpParser; namespace { enum_def(CDT_PROJECT_KIT, QString) { - enum_exp CBP_GENERATOR = "CodeBlocks - Unix Makefiles"; + enum_exp CBP_GENERATOR = "CodeBlocks - Unix Makefiles"; enum_exp CBP_FILE = ".cbp"; }; @@ -53,25 +53,30 @@ class CmakeAsynParse : public QObject QString stopOnError; QString useDefaultCommand; }; - typedef QList TargetBuilds; CmakeAsynParse(); virtual ~CmakeAsynParse(); + void stop(); + signals: void parseProjectEnd(const ParseInfo &info); void parseActionsEnd(const ParseInfo> &info); - void directoryCreated(const QString &path); + void directoryCreated(QStandardItem *rootItem, const QString &path); public slots: - QStandardItem *parseProject(QStandardItem *rootItem, const dpfservice::ProjectInfo &info); + void parseProject(QStandardItem *rootItem, const dpfservice::ProjectInfo &info); QList parseActions(const QStandardItem *item); private: QStandardItem *findParentItem(QStandardItem *rootItem, QString &name); QStandardItem *createParentItem(QStandardItem *rootItem, const QString &relativeName, const QString &absolutePath); QStandardItem *findItem(QStandardItem *rootItem, QString &name, QString &relativePath); + + QAtomicInteger isStop { false }; }; +Q_DECLARE_METATYPE(CmakeAsynParse::ParseInfo) +Q_DECLARE_METATYPE(CmakeAsynParse::ParseInfo>) #endif // CMAKEASYNPARSE_H diff --git a/src/plugins/cxx/cmake/project/cmakeprojectgenerator.cpp b/src/plugins/cxx/cmake/project/cmakeprojectgenerator.cpp index 5d83291ca..29b184efd 100644 --- a/src/plugins/cxx/cmake/project/cmakeprojectgenerator.cpp +++ b/src/plugins/cxx/cmake/project/cmakeprojectgenerator.cpp @@ -38,7 +38,6 @@ class CmakeProjectGeneratorPrivate RebuildProject, }; - QHash asynItemThreadPolls; QList reloadCmakeFileItems; dpfservice::ProjectInfo configureProjectInfo; QHash projectWatchers; @@ -47,11 +46,15 @@ class CmakeProjectGeneratorPrivate QMap cmakeItems; bool reConfigure = false; QSet toExpand; + + CmakeAsynParse *cmakeParser = nullptr; + QSharedPointer cmakeParserThread; }; CmakeProjectGenerator::CmakeProjectGenerator() : d(new CmakeProjectGeneratorPrivate()) { + initCMakeParser(); // when execute command end can create root Item QObject::connect(ProjectCmakeProxy::instance(), &ProjectCmakeProxy::buildExecuteEnd, @@ -59,29 +62,29 @@ CmakeProjectGenerator::CmakeProjectGenerator() QObject::connect(ProjectCmakeProxy::instance(), &ProjectCmakeProxy::fileDeleted, - this, [this](){ - runCMake(this->rootItem, {}); - }); + this, [this]() { + runCMake(this->rootItem, {}); + }); QObject::connect(ProjectCmakeProxy::instance(), &ProjectCmakeProxy::openProjectPropertys, - this, [this](const ProjectInfo &prjInfo){ - auto prjService = dpfGetService(ProjectService); - if (prjInfo.kitName() == toolKitName()) - actionProperties(prjInfo, prjService->getActiveProjectItem()); - }); + this, [this](const ProjectInfo &prjInfo) { + auto prjService = dpfGetService(ProjectService); + if (prjInfo.kitName() == toolKitName()) + actionProperties(prjInfo, prjService->getActiveProjectItem()); + }); QObject::connect(ProjectCmakeProxy::instance(), &ProjectCmakeProxy::nodeExpanded, - this, [this](const QString &filePath){ - d->toExpand.insert(filePath); - }); + this, [this](const QString &filePath) { + d->toExpand.insert(filePath); + }); QObject::connect(ProjectCmakeProxy::instance(), &ProjectCmakeProxy::nodeCollapsed, - this, [this](const QString &filePath){ - d->toExpand.remove(filePath); - }); + this, [this](const QString &filePath) { + d->toExpand.remove(filePath); + }); connect(TargetsManager::instance(), &TargetsManager::initialized, this, &CmakeProjectGenerator::targetInitialized); @@ -107,7 +110,7 @@ CmakeProjectGenerator::CmakeProjectGenerator() auto cmd = ActionManager::instance()->registerAction(runCMake, "Build.RunCMake"); mBuild->addAction(cmd); - QObject::connect(runCMake, &QAction::triggered, this, [this](){ + QObject::connect(runCMake, &QAction::triggered, this, [this]() { auto prjService = dpfGetService(ProjectService); auto activePrjInfo = prjService->getActiveProjectInfo(); for (auto item : d->cmakeItems.values()) { @@ -120,15 +123,15 @@ CmakeProjectGenerator::CmakeProjectGenerator() QObject::connect(config::ConfigUtil::instance(), &config::ConfigUtil::configureDone, [this](const dpfservice::ProjectInfo &info) { - configure(info); - }); + configure(info); + }); // add clear cmake menu item QAction *clearCMake = new QAction(tr("Clear CMake")); cmd = ActionManager::instance()->registerAction(clearCMake, "Build.ClearCMake"); mBuild->addAction(cmd); - QObject::connect(clearCMake, &QAction::triggered, this, [this](){ + QObject::connect(clearCMake, &QAction::triggered, this, [this]() { auto prjService = dpfGetService(ProjectService); auto activePrjInfo = prjService->getActiveProjectInfo(); for (auto item : d->cmakeItems.values()) { @@ -158,17 +161,13 @@ void CmakeProjectGenerator::clearCMake(QStandardItem *root) CmakeProjectGenerator::~CmakeProjectGenerator() { qInfo() << __FUNCTION__; - for (auto val : d->asynItemThreadPolls.keys()) { - auto threadPoll = d->asynItemThreadPolls[val]; - if (threadPoll) { - threadPoll->clear(); - while (threadPoll->activeThreadCount() != 0) {} - delete threadPoll; - } + if (d->cmakeParser) { + d->cmakeParser->stop(); + d->cmakeParserThread->quit(); + d->cmakeParserThread->wait(); + delete d->cmakeParser; } - d->asynItemThreadPolls.clear(); - if (d) delete d; } @@ -184,7 +183,7 @@ QStringList CmakeProjectGenerator::supportFileNames() } DWidget *CmakeProjectGenerator::configureWidget(const QString &language, - const QString &workspace) + const QString &workspace) { ProjectGenerator::configureWidget(language, workspace); disconnect(this); @@ -225,7 +224,7 @@ bool CmakeProjectGenerator::configure(const dpfservice::ProjectInfo &projInfo) commandInfo.arguments = projInfo.configCustomArgs(); commandInfo.workingDir = projInfo.workspaceFolder(); - bool isSuccess = builderService->runbuilderCommand({commandInfo}, false); + bool isSuccess = builderService->runbuilderCommand({ commandInfo }, false); if (isSuccess) { ProjectCmakeProxy::instance()->setBuildCommandUuid(commandInfo.uuid); // display root item before everything is done. @@ -236,7 +235,7 @@ bool CmakeProjectGenerator::configure(const dpfservice::ProjectInfo &projInfo) d->reConfigure = false; else { d->reConfigure = true; - rootItem->setData(ParsingState::Wait, Parsing_State_Role); + rootItem->setData(ParsingState::Wait, ProjectItemRole::ParsingStateRole); } d->cmakeItems.insert(newRoot, projInfo); @@ -258,51 +257,23 @@ QStandardItem *CmakeProjectGenerator::createRootItem(const dpfservice::ProjectIn { using namespace dpfservice; - d->asynItemThreadPolls[rootItem] = new QThreadPool; - - auto parse = new CmakeAsynParse; auto fileWatcher = new QFileSystemWatcher(this); d->projectWatchers.insert(rootItem, fileWatcher); auto thisProject = rootItem; d->projectsWaitingUpdate.append(thisProject); - // asyn free parse, that .project file parse - QObject::connect(parse, &CmakeAsynParse::parseProjectEnd, - [=](CmakeAsynParse::ParseInfo parseInfo) { - d->asynItemThreadPolls.remove(parseInfo.result); - // active after everything done. - project.activeProject(info.kitName(), info.language(), info.workspaceFolder()); - delete parse; - - d->projectsWaitingUpdate.removeOne(thisProject); - - if (!d->reConfigure) - return; - - QMetaObject::invokeMethod(this, [this]() { - auto prjService = dpfGetService(ProjectService); - prjService->expandItemByFile(d->toExpand.toList()); - }); - }); - - // asyn execute logic, that .project file parse - QtConcurrent::run(d->asynItemThreadPolls[rootItem], - parse, &CmakeAsynParse::parseProject, - rootItem, info); - - connect(parse, &CmakeAsynParse::directoryCreated, this, [=](const QString &path){ - if (!fileWatcher->directories().contains(path)) - fileWatcher->addPath(path); - }); + metaObject()->invokeMethod(d->cmakeParser, + std::bind(&CmakeAsynParse::parseProject, d->cmakeParser, rootItem, info)); - connect(fileWatcher, &QFileSystemWatcher::directoryChanged, this, [=](const QString &path){ - if (d->projectsWaitingUpdate.contains(thisProject)) - return; + connect(fileWatcher, &QFileSystemWatcher::directoryChanged, this, + [=](const QString &path) { + if (d->projectsWaitingUpdate.contains(thisProject)) + return; - auto windowService = dpfGetService(WindowService); - windowService->notify(0, "CMakeProject", tr("Files in project %1 have changed, needs to run cmake to update").arg(thisProject->text()), {}); - d->projectsWaitingUpdate.append(thisProject); - }); + auto windowService = dpfGetService(WindowService); + windowService->notify(0, "CMakeProject", tr("Files in project %1 have changed, needs to run cmake to update").arg(thisProject->text()), {}); + d->projectsWaitingUpdate.append(thisProject); + }); return rootItem; } @@ -311,15 +282,6 @@ void CmakeProjectGenerator::removeRootItem(QStandardItem *root) { // remove watcher from current root item CmakeItemKeeper::instance()->delCmakeFileNode(root); - - auto threadPoll = d->asynItemThreadPolls[root]; - if (threadPoll) { - threadPoll->clear(); - while(threadPoll->waitForDone()); - delete threadPoll; - d->asynItemThreadPolls.remove(root); - } - if (d->reloadCmakeFileItems.contains(root)) d->reloadCmakeFileItems.removeOne(root); @@ -337,23 +299,14 @@ QMenu *CmakeProjectGenerator::createItemMenu(const QStandardItem *item) if (item->parent()) return nullptr; - QMenu *menu = nullptr; - - // create parse - CmakeAsynParse *parse = new CmakeAsynParse(); - - // create item from syn - auto targetBuilds = parse->parseActions(item); - - // free parse from syn - delete parse; - auto root = ProjectGenerator::root(const_cast(item)); if (!root) - return menu; + return nullptr; + QMenu *menu = new QMenu(); + // create item from syn + auto targetBuilds = d->cmakeParser->parseActions(item); if (!targetBuilds.isEmpty()) { - menu = new QMenu(); for (auto val : targetBuilds) { QAction *action = new QAction(); action->setText(val.buildName); @@ -368,12 +321,7 @@ QMenu *CmakeProjectGenerator::createItemMenu(const QStandardItem *item) } } - if (!menu) { - menu = new QMenu(); - } - createBuildMenu(menu); - QAction *action = new QAction(tr("Properties")); menu->addAction(action); dpfservice::ProjectInfo info = dpfservice::ProjectInfo::get(item); @@ -410,7 +358,7 @@ void CmakeProjectGenerator::createDocument(const QStandardItem *item, const QStr file.close(); } - QObject::connect(dlg, &DDialog::buttonClicked, dlg, [=](){ + QObject::connect(dlg, &DDialog::buttonClicked, dlg, [=]() { QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(fileName); dlg->close(); @@ -455,15 +403,13 @@ void CmakeProjectGenerator::actionTriggered() commandInfo.program = program; commandInfo.arguments = args; commandInfo.workingDir = workDir; - builderService->runbuilderCommand({commandInfo}, false); + builderService->runbuilderCommand({ commandInfo }, false); } } } void CmakeProjectGenerator::setRootItemToView(QStandardItem *root) { - d->asynItemThreadPolls.remove(root); - using namespace dpfservice; auto &ctx = dpfInstance.serviceContext(); ProjectService *projectService = ctx.service(ProjectService::name()); @@ -566,7 +512,7 @@ void CmakeProjectGenerator::actionProperties(const dpfservice::ProjectInfo &info dlg.insertPropertyPanel(tr("Run"), runWidget); dlg.insertPropertyPanel(tr("Kit"), kitPage); - connect(buildWidget, &BuildPropertyPage::cacheFileUpdated, this, [=](){ + connect(buildWidget, &BuildPropertyPage::cacheFileUpdated, this, [=]() { runCMake(this->rootItem, {}); }); @@ -591,7 +537,7 @@ void CmakeProjectGenerator::recursionRemoveItem(QStandardItem *item) item = nullptr; } -void CmakeProjectGenerator::targetInitialized(const QString& workspace) +void CmakeProjectGenerator::targetInitialized(const QString &workspace) { ProjectConfigure *projectConfigure = ConfigUtil::instance()->getConfigureParamPointer(); auto tempType = projectConfigure->tempSelType; @@ -606,8 +552,7 @@ void CmakeProjectGenerator::targetInitialized(const QString& workspace) // update environment. for (auto targetRunConfigure : buildTypeConfigure.runConfigure.targetsRunConfigure) { if (targetRunConfigure.targetName == activeExecTarget.name) { - auto projectInfo = dpfGetService(ProjectService)-> - getProjectInfo(d->configureProjectInfo.kitName(), d->configureProjectInfo.workspaceFolder()); + auto projectInfo = dpfGetService(ProjectService)->getProjectInfo(d->configureProjectInfo.kitName(), d->configureProjectInfo.workspaceFolder()); projectInfo.setRunEnvironment(targetRunConfigure.env.toList()); dpfGetService(ProjectService)->updateProjectInfo(projectInfo); } @@ -617,6 +562,40 @@ void CmakeProjectGenerator::targetInitialized(const QString& workspace) ConfigUtil::instance()->saveConfig(ConfigUtil::instance()->getConfigPath(workspace), *projectConfigure); } +void CmakeProjectGenerator::projectParseFinished(const CmakeAsynParse::ParseInfo &info) +{ + const auto &projectInfo = ProjectInfo::get(info.result); + // active after everything done. + project.activeProject(projectInfo.kitName(), projectInfo.language(), projectInfo.workspaceFolder()); + d->projectsWaitingUpdate.removeOne(info.result); + if (!d->reConfigure) + return; + + auto prjService = dpfGetService(ProjectService); + prjService->expandItemByFile(d->toExpand.toList()); +} + +void CmakeProjectGenerator::initCMakeParser() +{ + qRegisterMetaType("QStandardItemPointer"); + qRegisterMetaType>("ParseInfo_QStandardItem"); + + d->cmakeParser = new CmakeAsynParse; + d->cmakeParserThread = QSharedPointer(new QThread); + d->cmakeParser->moveToThread(d->cmakeParserThread.data()); + + connect(d->cmakeParser, &CmakeAsynParse::directoryCreated, this, + [=](QStandardItem *rootItem, const QString &path) { + auto fileWatcher = d->projectWatchers.value(rootItem); + if (fileWatcher && !fileWatcher->directories().contains(path)) + fileWatcher->addPath(path); + }, + Qt::QueuedConnection); + connect(d->cmakeParser, &CmakeAsynParse::parseProjectEnd, this, + &CmakeProjectGenerator::projectParseFinished, Qt::QueuedConnection); + d->cmakeParserThread->start(); +} + void CmakeProjectGenerator::createTargetsRunConfigure(const QString &workDirectory, config::RunConfigure &runConfigure) { if (!runConfigure.targetsRunConfigure.isEmpty()) @@ -645,7 +624,7 @@ void CmakeProjectGenerator::createBuildMenu(QMenu *menu) return; menu->addSeparator(); - auto addBuildMenu = [&](const QString &actionID){ + auto addBuildMenu = [&](const QString &actionID) { auto command = ActionManager::instance()->command(actionID); if (command && command->action()) { menu->addAction(command->action()); diff --git a/src/plugins/cxx/cmake/project/cmakeprojectgenerator.h b/src/plugins/cxx/cmake/project/cmakeprojectgenerator.h index 363fa46ae..4d3395049 100644 --- a/src/plugins/cxx/cmake/project/cmakeprojectgenerator.h +++ b/src/plugins/cxx/cmake/project/cmakeprojectgenerator.h @@ -8,6 +8,7 @@ #include "services/project/projectservice.h" #include "services/builder/builderservice.h" #include "configutil.h" +#include "cmakeasynparse.h" #include #include @@ -43,8 +44,10 @@ private slots: void actionProperties(const dpfservice::ProjectInfo &info, QStandardItem *item); void recursionRemoveItem(QStandardItem *item); void targetInitialized(const QString& workspace); + void projectParseFinished(const CmakeAsynParse::ParseInfo &info); private: + void initCMakeParser(); void createTargetsRunConfigure(const QString &workDirectory, config::RunConfigure &runConfigure); void createBuildMenu(QMenu *menu); void clearCMake(QStandardItem *root); diff --git a/src/plugins/java/gradle/project/gradleasynparse.cpp b/src/plugins/java/gradle/project/gradleasynparse.cpp index 752975094..251b0aae5 100644 --- a/src/plugins/java/gradle/project/gradleasynparse.cpp +++ b/src/plugins/java/gradle/project/gradleasynparse.cpp @@ -6,6 +6,7 @@ #include "services/project/projectgenerator.h" #include "common/common.h" +#include "services/project/projectservice.h" #include #include @@ -92,8 +93,8 @@ void GradleAsynParse::createRows(const QString &path) QString childPath = dirItera.next().remove(0, rootPath.size()); QFileSystemWatcher::addPath(dirItera.filePath()); QStandardItem *item = findItem(childPath); - QIcon icon = CustomIcons::icon(dirItera.fileInfo()); - auto newItem = new QStandardItem(icon, dirItera.fileName()); + auto newItem = new QStandardItem(dirItera.fileName()); + newItem->setData(dirItera.filePath(), ProjectItemRole::FileIconRole); newItem->setToolTip(dirItera.filePath()); if (!item) { d->rows.append(newItem); @@ -111,8 +112,8 @@ void GradleAsynParse::createRows(const QString &path) while (fileItera.hasNext()) { QString childPath = fileItera.next().remove(0, rootPath.size()); QStandardItem *item = findItem(childPath); - QIcon icon = CustomIcons::icon(fileItera.fileInfo()); - auto newItem = new QStandardItem(icon, fileItera.fileName()); + auto newItem = new QStandardItem(fileItera.fileName()); + newItem->setData(fileItera.filePath(), ProjectItemRole::FileIconRole); newItem->setToolTip(fileItera.filePath()); if (!item) { d->rows.append(newItem); diff --git a/src/plugins/java/gradle/project/gradleprojectgenerator.cpp b/src/plugins/java/gradle/project/gradleprojectgenerator.cpp index cde62f981..ea31664ca 100644 --- a/src/plugins/java/gradle/project/gradleprojectgenerator.cpp +++ b/src/plugins/java/gradle/project/gradleprojectgenerator.cpp @@ -235,7 +235,7 @@ void GradleProjectGenerator::doProjectChildsModified(const QListappendRows(items); } - rootItem->setData(ParsingState::Done, Parsing_State_Role); + rootItem->setData(ParsingState::Done, ProjectItemRole::ParsingStateRole); } void GradleProjectGenerator::doGradleGeneratMenu(const QString &program, diff --git a/src/plugins/java/maven/project/mavenasynparse.cpp b/src/plugins/java/maven/project/mavenasynparse.cpp index 3fe523c82..bac0f9d88 100644 --- a/src/plugins/java/maven/project/mavenasynparse.cpp +++ b/src/plugins/java/maven/project/mavenasynparse.cpp @@ -5,6 +5,7 @@ #include "mavenasynparse.h" #include "services/project/projectgenerator.h" #include "services/option/optionmanager.h" +#include "services/project/projectservice.h" #include "common/common.h" @@ -136,8 +137,8 @@ void MavenAsynParse::createRows(const QString &path) QString childPath = dirItera.next().remove(0, rootPath.size()); QFileSystemWatcher::addPath(dirItera.filePath()); QStandardItem *item = findItem(childPath); - QIcon icon = CustomIcons::icon(dirItera.fileInfo()); - auto newItem = new QStandardItem(icon, dirItera.fileName()); + auto newItem = new QStandardItem(dirItera.fileName()); + newItem->setData(dirItera.filePath(), ProjectItemRole::FileIconRole); newItem->setToolTip(dirItera.filePath()); if (!item) { d->rows.append(newItem); @@ -155,8 +156,8 @@ void MavenAsynParse::createRows(const QString &path) while (fileItera.hasNext()) { QString childPath = fileItera.next().remove(0, rootPath.size()); QStandardItem *item = findItem(childPath); - QIcon icon = CustomIcons::icon(fileItera.fileInfo()); - auto newItem = new QStandardItem(icon, fileItera.fileName()); + auto newItem = new QStandardItem(fileItera.fileName()); + newItem->setData(fileItera.filePath(), ProjectItemRole::FileIconRole); newItem->setToolTip(fileItera.filePath()); if (!item) { d->rows.append(newItem); diff --git a/src/plugins/java/maven/project/mavenprojectgenerator.cpp b/src/plugins/java/maven/project/mavenprojectgenerator.cpp index c6f286655..b10289a3e 100644 --- a/src/plugins/java/maven/project/mavenprojectgenerator.cpp +++ b/src/plugins/java/maven/project/mavenprojectgenerator.cpp @@ -177,7 +177,7 @@ void MavenProjectGenerator::itemModified(const QList &items) if (parse) { auto root = d->projectParses.key(parse); emit itemChanged(root, items); - root->setData(ParsingState::Done, Parsing_State_Role); + root->setData(ParsingState::Done, ProjectItemRole::ParsingStateRole); } } diff --git a/src/plugins/project/mainframe/projectdelegate.cpp b/src/plugins/project/mainframe/projectdelegate.cpp index e55b61306..1cddc8e71 100644 --- a/src/plugins/project/mainframe/projectdelegate.cpp +++ b/src/plugins/project/mainframe/projectdelegate.cpp @@ -54,10 +54,10 @@ void ProjectDelegate::paint(QPainter *painter, iOption.font.setBold(true); d->spinner->move(option.rect.right() - 20, option.rect.top() + 4); - if (index.data(Parsing_State_Role).value() == ParsingState::Wait && !d->spinner->isVisible()) { + if (index.data(ProjectItemRole::ParsingStateRole).value() == ParsingState::Wait && !d->spinner->isVisible()) { d->spinner->show(); d->spinner->start(); - } else if (index.data(Parsing_State_Role).value() == ParsingState::Done) { + } else if (index.data(ProjectItemRole::ParsingStateRole).value() == ParsingState::Done) { d->spinner->hide(); d->spinner->stop(); } diff --git a/src/plugins/project/mainframe/projectmodel.cpp b/src/plugins/project/mainframe/projectmodel.cpp index 294c766ab..488321259 100644 --- a/src/plugins/project/mainframe/projectmodel.cpp +++ b/src/plugins/project/mainframe/projectmodel.cpp @@ -4,8 +4,27 @@ #include "projectmodel.h" +#include "common/util/customicons.h" +#include "services/project/projectservice.h" + +#include + ProjectModel::ProjectModel(QObject *parent) - : QStandardItemModel (parent) + : QStandardItemModel(parent) +{ +} + +QVariant ProjectModel::data(const QModelIndex &index, int role) const { + if (role == Qt::DecorationRole) { + auto name = index.data(ProjectItemRole::IconNameRole).toString(); + if (!name.isEmpty()) + return QIcon::fromTheme(name); + + name = index.data(ProjectItemRole::FileIconRole).toString(); + if (!name.isEmpty()) + return CustomIcons::icon(QFileInfo(name)); + } + return QStandardItemModel::data(index, role); } diff --git a/src/plugins/project/mainframe/projectmodel.h b/src/plugins/project/mainframe/projectmodel.h index e682c08bc..3214bf5fe 100644 --- a/src/plugins/project/mainframe/projectmodel.h +++ b/src/plugins/project/mainframe/projectmodel.h @@ -11,6 +11,8 @@ class ProjectModel : public QStandardItemModel Q_OBJECT public: explicit ProjectModel(QObject *parent = nullptr); + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; }; #endif // PROJECTMODEL_H diff --git a/src/plugins/project/mainframe/projecttree.cpp b/src/plugins/project/mainframe/projecttree.cpp index 226a06bbf..024e16d92 100644 --- a/src/plugins/project/mainframe/projecttree.cpp +++ b/src/plugins/project/mainframe/projecttree.cpp @@ -73,6 +73,8 @@ ProjectTree::ProjectTree(QWidget *parent) d->itemModel = new ProjectModel(this); setModel(d->itemModel); + setSortingEnabled(true); + sortByColumn(0); // 右键菜单创建 QObject::connect(this, &ProjectTree::itemMenuRequest, @@ -156,8 +158,8 @@ void ProjectTree::appendRootItem(QStandardItem *root) if (model) model->appendRow(root); - if (root->data(Parsing_State_Role).value() != ParsingState::Done) //avoid appent root item after complete parse - root->setData(ParsingState::Wait, Parsing_State_Role); + if (root->data(ProjectItemRole::ParsingStateRole).value() != ParsingState::Done) //avoid appent root item after complete parse + root->setData(ParsingState::Wait, ProjectItemRole::ParsingStateRole); // 发送工程节点已创建信号 SendEvents::projectCreated(info); diff --git a/src/services/project/directoryasynparse.cpp b/src/services/project/directoryasynparse.cpp index 347a2ac8b..1a7b78846 100644 --- a/src/services/project/directoryasynparse.cpp +++ b/src/services/project/directoryasynparse.cpp @@ -6,6 +6,7 @@ #include "projectgenerator.h" #include "common/common.h" +#include "services/project/projectservice.h" #include @@ -84,8 +85,8 @@ void DirectoryAsynParse::createRows(const QString &path) QString childPath = dirItera.next().remove(0, rootPath.size()); QFileSystemWatcher::addPath(dirItera.filePath()); QStandardItem *item = findItem(childPath); - QIcon icon = CustomIcons::icon(dirItera.fileInfo()); - auto newItem = new QStandardItem(icon, dirItera.fileName()); + auto newItem = new QStandardItem(dirItera.fileName()); + newItem->setData(dirItera.filePath(), ProjectItemRole::FileIconRole); newItem->setToolTip(dirItera.filePath()); if (!item) { d->rows.append(newItem); @@ -103,8 +104,8 @@ void DirectoryAsynParse::createRows(const QString &path) while (fileItera.hasNext()) { QString childPath = fileItera.next().remove(0, rootPath.size()); QStandardItem *item = findItem(childPath); - QIcon icon = CustomIcons::icon(fileItera.fileInfo()); - auto newItem = new QStandardItem(icon, fileItera.fileName()); + auto newItem = new QStandardItem(fileItera.fileName()); + newItem->setData(fileItera.filePath(), ProjectItemRole::FileIconRole); newItem->setToolTip(fileItera.filePath()); if (!item) { d->rows.append(newItem); diff --git a/src/services/project/directorygenerator.cpp b/src/services/project/directorygenerator.cpp index f58246afe..5b2848f19 100644 --- a/src/services/project/directorygenerator.cpp +++ b/src/services/project/directorygenerator.cpp @@ -113,5 +113,5 @@ void DirectoryGenerator::doProjectChildsModified(const QList &i rootItem->appendRows(info); } - rootItem->setData(ParsingState::Done, Parsing_State_Role); + rootItem->setData(ParsingState::Done, ProjectItemRole::ParsingStateRole); } diff --git a/src/services/project/projectservice.h b/src/services/project/projectservice.h index 2a9361037..ba57d97aa 100644 --- a/src/services/project/projectservice.h +++ b/src/services/project/projectservice.h @@ -10,13 +10,18 @@ #include -#define Parsing_State_Role Qt::UserRole + 100 enum ParsingState { Wait, Done }; Q_DECLARE_METATYPE(ParsingState); +enum ProjectItemRole { + ParsingStateRole = Qt::UserRole + 100, + IconNameRole, + FileIconRole +}; + namespace dpfservice { struct Target {