From f9e4afda77c45b41db8103febc44aae336d91f53 Mon Sep 17 00:00:00 2001 From: Eberhard Graether Date: Sun, 3 Dec 2017 22:30:09 +0100 Subject: [PATCH] logic: Added indexing mode to reindex incomplete files (issue #493, #496) * added radio buttons to start indexing dialog * don't block task scheduler thread while start indexing dialog is visible * don't force whole project refresh on settings change * added help button to start indexing dialog explaining indexing modes * disable unavailable modes when project needs full refresh * improved label texts on indexing dialogs * added --incomplete flag to index command of commandline API * fixed deletion of window stack elements --- .../gui/indexing_dialog/indexing_dialog.css | 11 +- src/app/main.cpp | 6 +- src/lib/Application.cpp | 33 +- src/lib/Application.h | 2 +- src/lib/CMakeLists.txt | 1 + src/lib/component/view/DialogView.cpp | 5 +- src/lib/component/view/DialogView.h | 20 +- src/lib/data/storage/PersistentStorage.cpp | 67 ++- src/lib/data/storage/PersistentStorage.h | 6 +- src/lib/project/Project.cpp | 531 ++++++++++-------- src/lib/project/Project.h | 30 +- src/lib/project/RefreshInfo.h | 23 + src/lib/project/SourceGroup.cpp | 27 +- src/lib/project/SourceGroup.h | 10 +- .../utility/commandline/CommandLineParser.cpp | 13 +- .../utility/commandline/CommandLineParser.h | 8 +- .../commands/CommandlineCommandIndex.cpp | 7 +- .../messaging/type/MessageLoadProject.h | 14 +- src/lib_cxx/project/SourceGroupCxxCdb.cpp | 21 +- src/lib_cxx/project/SourceGroupCxxCdb.h | 5 +- src/lib_cxx/project/SourceGroupCxxEmpty.cpp | 7 +- src/lib_cxx/project/SourceGroupCxxEmpty.h | 3 +- src/lib_gui/qt/QtApplication.cpp | 2 +- src/lib_gui/qt/view/QtDialogView.cpp | 198 +++++-- src/lib_gui/qt/view/QtDialogView.h | 14 +- src/lib_gui/qt/window/QtIndexingDialog.cpp | 155 +++-- src/lib_gui/qt/window/QtIndexingDialog.h | 25 +- src/lib_gui/qt/window/QtMainWindow.cpp | 4 +- src/lib_gui/qt/window/QtStartScreen.cpp | 2 +- src/lib_gui/qt/window/QtWindowStack.cpp | 4 +- .../project_wizzard/QtProjectWizzard.cpp | 13 +- src/lib_java/project/SourceGroupJava.cpp | 7 +- src/lib_java/project/SourceGroupJava.h | 3 +- 33 files changed, 748 insertions(+), 529 deletions(-) create mode 100644 src/lib/project/RefreshInfo.h diff --git a/bin/app/data/gui/indexing_dialog/indexing_dialog.css b/bin/app/data/gui/indexing_dialog/indexing_dialog.css index 1f7abc92..ba38c4d7 100644 --- a/bin/app/data/gui/indexing_dialog/indexing_dialog.css +++ b/bin/app/data/gui/indexing_dialog/indexing_dialog.css @@ -1,4 +1,4 @@ -QLabel, QCheckBox { +QLabel, QCheckBox, QRadioButton { color: white; } @@ -24,6 +24,15 @@ QLabel, QCheckBox { font-weight: bold; } +#option { + font-size: 14px; + margin-left: 15px; +} + +#option:disabled { + color: #A0FFFFFF; +} + #filePath { font-size: 11px; } diff --git a/src/app/main.cpp b/src/app/main.cpp index 316c61f4..d0316df1 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -270,7 +270,8 @@ int main(int argc, char *argv[]) { MessageLoadProject( commandLineParser.getProjectFilePath(), - commandLineParser.getFullProjectRefresh() + false, + commandLineParser.getRefreshMode() ).dispatch(); } @@ -320,7 +321,8 @@ int main(int argc, char *argv[]) { MessageLoadProject( commandLineParser.getProjectFilePath(), - commandLineParser.getFullProjectRefresh() + false, + commandLineParser.getRefreshMode() ).dispatch(); } diff --git a/src/lib/Application.cpp b/src/lib/Application.cpp index d4e5e048..0b58dc45 100644 --- a/src/lib/Application.cpp +++ b/src/lib/Application.cpp @@ -189,7 +189,8 @@ void Application::createAndLoadProject(const FilePath& projectSettingsFilePath) m_storageCache->clear(); m_storageCache->setSubject(nullptr); - m_project = std::make_shared(std::make_shared(projectSettingsFilePath), m_storageCache.get()); + m_project = std::make_shared( + std::make_shared(projectSettingsFilePath), m_storageCache.get(), hasGUI()); if (m_project) { @@ -220,15 +221,11 @@ void Application::createAndLoadProject(const FilePath& projectSettingsFilePath) } } -void Application::refreshProject(bool force) +void Application::refreshProject(RefreshMode refreshMode) { if (m_project && checkSharedMemory()) { - bool indexing = m_project->refresh(force); - if (indexing) - { - m_storageCache->clear(); - } + m_project->refresh(getDialogView().get(), refreshMode); } } @@ -275,22 +272,20 @@ void Application::handleMessage(MessageLoadProject* message) if (m_project && projectSettingsFilePath == m_project->getProjectSettingsFilePath()) { - if (message->forceRefresh && m_hasGUI) + if (message->settingsChanged && m_hasGUI) { m_project->setStateSettingsUpdated(); - refreshProject(false); + refreshProject(REFRESH_ALL_FILES); } - return; } - createAndLoadProject(projectSettingsFilePath); + else + { + createAndLoadProject(projectSettingsFilePath); - if (message->forceRefresh) - { - refreshProject(true); - } - else if (!m_hasGUI) - { - refreshProject(false); + if (message->refreshMode != REFRESH_NONE) + { + refreshProject(message->refreshMode); + } } } @@ -311,7 +306,7 @@ void Application::handleMessage(MessageRefresh* message) if (!message->uiOnly) { - refreshProject(message->all); + refreshProject(message->all ? REFRESH_ALL_FILES : REFRESH_UPDATED_FILES); } } diff --git a/src/lib/Application.h b/src/lib/Application.h index 4a6f4d89..a67c05fd 100644 --- a/src/lib/Application.h +++ b/src/lib/Application.h @@ -48,7 +48,7 @@ public: const std::shared_ptr getCurrentProject(); void createAndLoadProject(const FilePath& projectSettingsFilePath); - void refreshProject(bool force); + void refreshProject(RefreshMode refreshMode); bool hasGUI(); int handleDialog(const std::string& message); diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 600df223..9318cdbd 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -283,6 +283,7 @@ add_files( project/Project.cpp project/Project.h + project/RefreshInfo.h project/SourceGroupFactory.cpp project/SourceGroupFactory.h project/SourceGroupFactoryModule.cpp diff --git a/src/lib/component/view/DialogView.cpp b/src/lib/component/view/DialogView.cpp index c7136923..44a39038 100644 --- a/src/lib/component/view/DialogView.cpp +++ b/src/lib/component/view/DialogView.cpp @@ -25,10 +25,9 @@ void DialogView::hideProgressDialog() { } -DialogView::IndexingOptions DialogView::startIndexingDialog( - size_t cleanFileCount, size_t indexFileCount, size_t totalFileCount, DialogView::IndexingOptions options) +void DialogView::startIndexingDialog( + Project* project, const std::vector& enabledModes, const RefreshInfo& info) { - return IndexingOptions(); } void DialogView::updateIndexingDialog( diff --git a/src/lib/component/view/DialogView.h b/src/lib/component/view/DialogView.h index 439894ff..a00afe53 100644 --- a/src/lib/component/view/DialogView.h +++ b/src/lib/component/view/DialogView.h @@ -5,26 +5,14 @@ #include #include "data/ErrorCountInfo.h" +#include "project/RefreshInfo.h" +class Project; class StorageAccess; class DialogView { public: - struct IndexingOptions - { - IndexingOptions() - : startIndexing(false) - , fullRefreshVisible(false) - , fullRefresh(false) - {} - - bool startIndexing; - - bool fullRefreshVisible; - bool fullRefresh; - }; - DialogView(StorageAccess* storageAccess); virtual ~DialogView(); @@ -34,8 +22,8 @@ public: virtual void showProgressDialog(const std::string& title, const std::string& message, int progress); virtual void hideProgressDialog(); - virtual IndexingOptions startIndexingDialog( - size_t cleanFileCount, size_t indexFileCount, size_t totalFileCount, IndexingOptions options); + virtual void startIndexingDialog( + Project* project, const std::vector& enabledModes, const RefreshInfo& info); virtual void updateIndexingDialog( size_t startedFileCount, size_t finishedFileCount, size_t totalFileCount, std::string sourcePath); virtual void finishedIndexingDialog( diff --git a/src/lib/data/storage/PersistentStorage.cpp b/src/lib/data/storage/PersistentStorage.cpp index a7eb906e..6faa3836 100644 --- a/src/lib/data/storage/PersistentStorage.cpp +++ b/src/lib/data/storage/PersistentStorage.cpp @@ -345,29 +345,46 @@ void PersistentStorage::clearFileElements(const std::vector& filePaths } } -std::vector PersistentStorage::getInfoOnAllFiles() const +std::vector PersistentStorage::getFileInfoForAllFiles() const { TRACE(); std::vector fileInfos; - - std::vector storageFiles = m_sqliteIndexStorage.getAll(); - for (size_t i = 0; i < storageFiles.size(); i++) + for (StorageFile file : m_sqliteIndexStorage.getAll()) { boost::posix_time::ptime modificationTime = boost::posix_time::not_a_date_time; - if (storageFiles[i].modificationTime != "not-a-date-time") + if (file.modificationTime != "not-a-date-time") { - modificationTime = boost::posix_time::time_from_string(storageFiles[i].modificationTime); + modificationTime = boost::posix_time::time_from_string(file.modificationTime); } - fileInfos.push_back(FileInfo( - FilePath(storageFiles[i].filePath), - modificationTime - )); + + fileInfos.push_back( + FileInfo( + FilePath(file.filePath), + modificationTime + ) + ); } return fileInfos; } +std::set PersistentStorage::getIncompleteFiles() const +{ + TRACE(); + + std::set incompleteFiles; + for (auto p : m_fileNodeComplete) + { + if (p.second == false) + { + incompleteFiles.insert(getFileNodePath(p.first)); + } + } + + return incompleteFiles; +} + void PersistentStorage::buildCaches() { TRACE(); @@ -1712,14 +1729,7 @@ TooltipInfo PersistentStorage::getTooltipInfoForTokenIds(const std::vector& if (type.getType() == NodeType::NODE_FILE && m_fileNodePaths.find(node.id) != m_fileNodePaths.end()) { - bool complete = false; - auto it = m_fileNodeComplete.find(node.id); - if (it != m_fileNodeComplete.end()) - { - complete = it->second; - } - - if (!complete) + if (!getFileNodeComplete(node.id)) { info.title = "incomplete " + info.title; } @@ -1960,16 +1970,23 @@ FilePath PersistentStorage::getFileNodePath(Id fileId) const return FilePath(); } -bool PersistentStorage::getFileNodeComplete(const FilePath& filePath) const +bool PersistentStorage::getFilePathComplete(const FilePath& filePath) const { auto it = m_fileNodeIds.find(filePath); if (it != m_fileNodeIds.end()) { - auto it2 = m_fileNodeComplete.find(it->second); - if (it2 != m_fileNodeComplete.end()) - { - return it2->second; - } + return getFileNodeComplete(it->second); + } + + return false; +} + +bool PersistentStorage::getFileNodeComplete(Id fileId) const +{ + auto it = m_fileNodeComplete.find(fileId); + if (it != m_fileNodeComplete.end()) + { + return it->second; } return false; @@ -2419,7 +2436,7 @@ void PersistentStorage::addCompleteFlagsToSourceLocationCollection(SourceLocatio collection->forEachSourceLocationFile( [this](std::shared_ptr file) { - file->setIsComplete(getFileNodeComplete(file->getFilePath())); + file->setIsComplete(getFilePathComplete(file->getFilePath())); } ); } diff --git a/src/lib/data/storage/PersistentStorage.h b/src/lib/data/storage/PersistentStorage.h index 2069eed8..901de018 100644 --- a/src/lib/data/storage/PersistentStorage.h +++ b/src/lib/data/storage/PersistentStorage.h @@ -63,7 +63,8 @@ public: void clearFileElements(const std::vector& filePaths, std::function updateStatusCallback); - std::vector getInfoOnAllFiles() const; + std::vector getFileInfoForAllFiles() const; + std::set getIncompleteFiles() const; void buildCaches(); @@ -148,7 +149,8 @@ private: std::vector getFileNodeIds(const std::vector& filePaths) const; std::set getFileNodeIds(const std::set& filePaths) const; FilePath getFileNodePath(Id fileId) const; - bool getFileNodeComplete(const FilePath& filePath) const; + bool getFilePathComplete(const FilePath& filePath) const; + bool getFileNodeComplete(Id fileId) const; std::unordered_map> getFileIdToIncludingFileIdMap() const; std::unordered_map> getFileIdToImportingFileIdMap() const; diff --git a/src/lib/project/Project.cpp b/src/lib/project/Project.cpp index 328bc80b..7aaf1891 100644 --- a/src/lib/project/Project.cpp +++ b/src/lib/project/Project.cpp @@ -1,19 +1,18 @@ #include "project/Project.h" -#include "Application.h" #include "component/view/DialogView.h" -#include "data/access/StorageAccessProxy.h" #include "data/indexer/IndexerCommand.h" #include "data/indexer/IndexerCommandList.h" #include "data/indexer/TaskBuildIndex.h" #include "data/parser/TaskParseWrapper.h" -#include "data/storage/StorageProvider.h" #include "data/storage/PersistentStorage.h" +#include "data/storage/StorageCache.h" +#include "data/storage/StorageProvider.h" #include "data/TaskCleanStorage.h" -#include "data/TaskMergeStorages.h" -#include "data/TaskShowStatusDialog.h" #include "data/TaskFinishParsing.h" #include "data/TaskInjectStorage.h" +#include "data/TaskMergeStorages.h" +#include "data/TaskShowStatusDialog.h" #include "project/SourceGroup.h" #include "project/SourceGroupFactory.h" #include "settings/ApplicationSettings.h" @@ -37,10 +36,11 @@ #include "utility/utilityApp.h" #include "utility/utilityString.h" -Project::Project(std::shared_ptr settings, StorageAccessProxy* storageAccessProxy) +Project::Project(std::shared_ptr settings, StorageCache* storageCache, bool hasGUI) : m_settings(settings) - , m_storageAccessProxy(storageAccessProxy) + , m_storageCache(storageCache) , m_state(PROJECT_STATE_NOT_LOADED) + , m_hasGUI(hasGUI) { } @@ -48,123 +48,6 @@ Project::~Project() { } -bool Project::refresh(bool forceRefresh) -{ - if (m_state == PROJECT_STATE_NOT_LOADED) - { - return false; - } - - bool needsFullRefresh = false; - std::string question; - - switch (m_state) - { - case PROJECT_STATE_EMPTY: - needsFullRefresh = true; - break; - - case PROJECT_STATE_LOADED: - break; - - case PROJECT_STATE_OUTDATED: - question = - "The project file was changed after the last indexing. The project needs to get fully reindexed to " - "reflect the current project state. Do you want to reindex the project?"; - needsFullRefresh = true; - break; - - case PROJECT_STATE_OUTVERSIONED: - question = - "This project was indexed with a different version of Sourcetrail. It needs to be fully reindexed to be used " - "with this version of Sourcetrail. Do you want to reindex the project?"; - needsFullRefresh = true; - break; - - case PROJECT_STATE_SETTINGS_UPDATED: - question = - "Some settings were changed, the project needs to be fully reindexed. " - "Do you want to reindex the project?"; - needsFullRefresh = true; - break; - - case PROJECT_STATE_NEEDS_MIGRATION: - question = - "This project was created with a different version of Sourcetrail. The project file needs to get updated and " - "the project fully reindexed. Do you want to update the project file and reindex the project?"; - needsFullRefresh = true; - - default: - break; - } - - std::shared_ptr dialogView = Application::getInstance()->getDialogView(); - - if ( - ApplicationSettings::getInstance()->getLoggingEnabled() && - ApplicationSettings::getInstance()->getVerboseIndexerLoggingEnabled() && - Application::getInstance()->hasGUI() - ) - { - std::vector options = { "Yes", "No" }; - int result = dialogView->confirm( - "Warning: You are about to index your project with the \"verbose indexer logging\" setting " - "enabled. This will cause a significant slowdown in indexing performance. Do you want to proceed?", - options - ); - - if (result == 1) - { - return false; - } - } - - if (!forceRefresh && needsFullRefresh && question.size() && Application::getInstance()->hasGUI()) - { - std::vector options = { "Yes", "No"}; - int result = dialogView->confirm(question, options); - - if (result == 1) - { - return false; - } - } - - dialogView->showUnknownProgressDialog("Preparing Project", "Processing Files"); - - ScopedFunctor dialogHider([&dialogView](){ - dialogView->hideUnknownProgressDialog(); - }); - - - if (m_state == PROJECT_STATE_NEEDS_MIGRATION) - { - m_settings->migrate(); - } - - m_settings->reload(); - - m_sourceGroups = SourceGroupFactory::getInstance()->createSourceGroups(m_settings->getAllSourceGroupSettings()); - for (const std::shared_ptr& sourceGroup: m_sourceGroups) - { - if (!sourceGroup->prepareRefresh()) - { - return false; - } - } - - if (requestIndex(forceRefresh, needsFullRefresh)) - { - m_storageAccessProxy->setSubject(m_storage.get()); - - m_state = PROJECT_STATE_LOADED; - - return true; - } - - return false; -} - FilePath Project::getProjectSettingsFilePath() const { return m_settings->getFilePath(); @@ -190,7 +73,7 @@ void Project::setStateSettingsUpdated() void Project::load() { - m_storageAccessProxy->setSubject(nullptr); + m_storageCache->setSubject(nullptr); bool loadedSettings = m_settings->reload(); @@ -246,9 +129,9 @@ void Project::load() { m_storage->setMode(SqliteStorage::STORAGE_MODE_READ); m_storage->buildCaches(); - m_storageAccessProxy->setSubject(m_storage.get()); + m_storageCache->setSubject(m_storage.get()); - if (Application::getInstance()->hasGUI()) + if (m_hasGUI) { MessageFinishedParsing().dispatch(); } @@ -259,171 +142,200 @@ void Project::load() MessageStatus("Project not loaded", false, false).dispatch(); } - if (m_state != PROJECT_STATE_LOADED && Application::getInstance()->hasGUI()) + if (m_state != PROJECT_STATE_LOADED && m_hasGUI) { MessageRefresh().dispatch(); } } -bool Project::requestIndex(bool forceRefresh, bool needsFullRefresh) +void Project::refresh(DialogView* dialogView, RefreshMode refreshMode) { - std::set allSourceFilePaths; - for (const std::shared_ptr& sourceGroup: m_sourceGroups) + if (m_state == PROJECT_STATE_NOT_LOADED) + { + return; + } + + bool needsFullRefresh = false; + bool fullRefresh = false; + std::string question; + + switch (m_state) + { + case PROJECT_STATE_EMPTY: + needsFullRefresh = true; + break; + + case PROJECT_STATE_LOADED: + break; + + case PROJECT_STATE_OUTDATED: + question = + "The project file was changed after the last indexing. The project needs to get fully reindexed to " + "reflect the current project state. Alternatively you can also choose to just reindex updated or " + "incomplete files. Do you want to reindex the project?"; + fullRefresh = true; + break; + + case PROJECT_STATE_OUTVERSIONED: + question = + "This project was indexed with a different version of Sourcetrail. It needs to be fully reindexed to be used " + "with this version of Sourcetrail. Do you want to reindex the project?"; + needsFullRefresh = true; + break; + + case PROJECT_STATE_SETTINGS_UPDATED: + question = + "Some settings were changed, the project should be fully reindexed. Alternatively you can also choose to " + "just reindex updated or incomplete files. " + "Do you want to reindex the project?"; + fullRefresh = true; + break; + + case PROJECT_STATE_NEEDS_MIGRATION: + question = + "This project was created with a different version of Sourcetrail. The project file needs to get updated and " + "the project fully reindexed. Do you want to update the project file and reindex the project?"; + needsFullRefresh = true; + + default: + break; + } + + if (question.size() && m_hasGUI) + { + std::vector options = { "Yes", "No" }; + int result = dialogView->confirm(question, options); + + if (result == 1) + { + return; + } + } + + if (ApplicationSettings::getInstance()->getLoggingEnabled() && + ApplicationSettings::getInstance()->getVerboseIndexerLoggingEnabled() && m_hasGUI) + { + std::vector options = { "Yes", "No" }; + int result = dialogView->confirm( + "Warning: You are about to index your project with the \"verbose indexer logging\" setting " + "enabled. This will cause a significant slowdown in indexing performance. Do you want to proceed?", + options + ); + + if (result == 1) + { + return; + } + } + + dialogView->showUnknownProgressDialog("Preparing Project", "Processing Files"); + ScopedFunctor dialogHider([&dialogView](){ + dialogView->hideUnknownProgressDialog(); + }); + + if (m_state == PROJECT_STATE_NEEDS_MIGRATION) + { + m_settings->migrate(); + } + + m_settings->reload(); + + m_sourceGroups = SourceGroupFactory::getInstance()->createSourceGroups(m_settings->getAllSourceGroupSettings()); + for (const std::shared_ptr& sourceGroup : m_sourceGroups) { if (!sourceGroup->prepareIndexing()) { - return false; + return; } + sourceGroup->fetchAllSourceFilePaths(); - utility::append(allSourceFilePaths, sourceGroup->getAllSourceFilePaths()); } - std::set filesToClean; - std::set filesToAdd; - if (!needsFullRefresh) + if (needsFullRefresh || fullRefresh) { - std::set unchangedFilePaths; - std::set changedFilePaths; - for (const FileInfo& info: m_storage->getInfoOnAllFiles()) - { - if (info.path.exists()) - { - if (didFileChange(info)) - { - changedFilePaths.insert(info.path); - } - else - { - unchangedFilePaths.insert(info.path); - } - } - else - { - // file has been removed - changedFilePaths.insert(info.path); - } - } - - filesToClean = changedFilePaths; - - // handle referencing paths - utility::append(filesToClean, m_storage->getReferencing(changedFilePaths)); - - // handle referenced paths - std::set staticSourceFiles = allSourceFilePaths; - for (const FilePath& path: changedFilePaths) - { - staticSourceFiles.erase(path); - } - - const std::set staticReferencedFilePaths = m_storage->getReferenced(staticSourceFiles); - const std::set dynamicReferencedFilePaths = m_storage->getReferenced(changedFilePaths); - - for (const FilePath& path : dynamicReferencedFilePaths) - { - if (staticReferencedFilePaths.find(path) == staticReferencedFilePaths.end() && - staticSourceFiles.find(path) == staticSourceFiles.end()) - { - // file may not be referenced anymore and will be reindexed if still needed - filesToClean.insert(path); - } - } - - for (const FilePath& path: unchangedFilePaths) - { - staticSourceFiles.erase(path); - } - filesToAdd = staticSourceFiles; + refreshMode = REFRESH_ALL_FILES; + } + else if (refreshMode == REFRESH_NONE) + { + refreshMode = REFRESH_UPDATED_FILES; } + RefreshInfo info = getRefreshInfo(refreshMode); - std::set staticSourceFilePaths; - for (const FilePath& path: allSourceFilePaths) + if (m_hasGUI) { - if (filesToClean.find(path) == filesToClean.end() && filesToAdd.find(path) == filesToAdd.end()) + std::vector enabledModes = { REFRESH_ALL_FILES }; + if (!needsFullRefresh) { - staticSourceFilePaths.insert(path); - } - } - - std::set filesToIndex; - for (const std::shared_ptr& sourceGroup: m_sourceGroups) - { - sourceGroup->fetchSourceFilePathsToIndex(staticSourceFilePaths); - utility::append(filesToIndex, sourceGroup->getSourceFilePathsToIndex()); - } - - bool fullRefresh = forceRefresh | needsFullRefresh; - - if (Application::getInstance()->hasGUI()) - { - DialogView::IndexingOptions options; - options.fullRefreshVisible = !needsFullRefresh; - options.fullRefresh = forceRefresh; - - Application::getInstance()->getDialogView()->hideUnknownProgressDialog(); - - options = Application::getInstance()->getDialogView()->startIndexingDialog( - filesToClean.size(), filesToIndex.size(), allSourceFilePaths.size(), options); - - if (!options.startIndexing) - { - return false; + enabledModes.insert(enabledModes.end(), { REFRESH_UPDATED_FILES, REFRESH_UPDATED_AND_INCOMPLETE_FILES }); } - fullRefresh = options.fullRefresh | needsFullRefresh; + dialogView->startIndexingDialog(this, enabledModes, info); } - - if (fullRefresh) + else { - filesToClean.clear(); - filesToIndex = allSourceFilePaths; + buildIndex(info); } +} - if (!filesToClean.size() && !filesToIndex.size()) +RefreshInfo Project::getRefreshInfo(RefreshMode mode) const +{ + switch (mode) { - if (!Application::getInstance()->hasGUI()) + case REFRESH_NONE: + return RefreshInfo(); + + case REFRESH_UPDATED_FILES: + return getRefreshInfoForUpdatedFiles(); + + case REFRESH_UPDATED_AND_INCOMPLETE_FILES: + return getRefreshInfoForIncompleteFiles(); + + case REFRESH_ALL_FILES: + return getRefreshInfoForAllFiles(); + } +} + +void Project::buildIndex(const RefreshInfo& info) +{ + if (info.mode == REFRESH_NONE || (!info.filesToClear.size() && !info.filesToIndex.size())) + { + if (!m_hasGUI) { MessageFinishedParsing().dispatch(); } MessageStatus("Nothing to refresh, all files are up-to-date.").dispatch(); - return false; + return; } - MessageStatus((fullRefresh ? "Reindexing Project" : "Refreshing Project"), false, true).dispatch(); - - buildIndex(filesToIndex, filesToClean, fullRefresh); - - return true; -} - -void Project::buildIndex( - const std::set& filesToIndex, const std::set& filesToClean, bool fullRefresh) -{ + MessageStatus("Preparing Indexing", false, true).dispatch(); MessageClearErrorCount().dispatch(); - if (fullRefresh) + + if (info.mode == REFRESH_ALL_FILES) { m_storage->clear(); } + m_storageCache->clear(); + m_storage->setProjectSettingsText(TextAccess::createFromFile(getProjectSettingsFilePath())->getText()); std::shared_ptr taskSequential = std::make_shared(); - // add task for cleaning the database - if (!filesToClean.empty()) + // add task for clearing the database + if (info.filesToClear.size()) { taskSequential->addTask(std::make_shared( m_storage.get(), - utility::toVector(filesToClean) + utility::toVector(info.filesToClear) )); } std::shared_ptr indexerCommandList = std::make_shared(); for (const std::shared_ptr& sourceGroup : m_sourceGroups) { - for (const std::shared_ptr& command : sourceGroup->getIndexerCommands(filesToIndex, fullRefresh)) + for (const std::shared_ptr& command : sourceGroup->getIndexerCommands(info.filesToIndex)) { indexerCommandList->addCommand(command); } @@ -515,9 +427,144 @@ void Project::buildIndex( ); } - taskSequential->addTask(std::make_shared(m_storage.get(), m_storageAccessProxy)); + taskSequential->addTask(std::make_shared(m_storage.get(), m_storageCache)); Task::dispatch(taskSequential); + + m_storageCache->setSubject(m_storage.get()); + m_state = PROJECT_STATE_LOADED; +} + +std::set Project::getAllSourceFilePaths() const +{ + std::set allSourceFilePaths; + + for (const std::shared_ptr& sourceGroup: m_sourceGroups) + { + utility::append(allSourceFilePaths, sourceGroup->getAllSourceFilePaths()); + } + + return allSourceFilePaths; +} + +RefreshInfo Project::getRefreshInfoForUpdatedFiles() const +{ + std::set unchangedFilePaths; + std::set changedFilePaths; + + for (const FileInfo& info: m_storage->getFileInfoForAllFiles()) + { + if (info.path.exists()) + { + if (didFileChange(info)) + { + changedFilePaths.insert(info.path); + } + else + { + unchangedFilePaths.insert(info.path); + } + } + else // file has been removed + { + changedFilePaths.insert(info.path); + } + } + + std::set filesToClear = changedFilePaths; + + // handle referencing paths + utility::append(filesToClear, m_storage->getReferencing(changedFilePaths)); + + // handle referenced paths + std::set allSourceFilePaths = getAllSourceFilePaths(); + std::set staticSourceFiles = allSourceFilePaths; + for (const FilePath& path: changedFilePaths) + { + staticSourceFiles.erase(path); + } + + const std::set staticReferencedFilePaths = m_storage->getReferenced(staticSourceFiles); + const std::set dynamicReferencedFilePaths = m_storage->getReferenced(changedFilePaths); + + for (const FilePath& path : dynamicReferencedFilePaths) + { + if (staticReferencedFilePaths.find(path) == staticReferencedFilePaths.end() && + staticSourceFiles.find(path) == staticSourceFiles.end()) + { + // file may not be referenced anymore and will be reindexed if still needed + filesToClear.insert(path); + } + } + + for (const FilePath& path: unchangedFilePaths) + { + staticSourceFiles.erase(path); + } + + std::set filesToAdd = staticSourceFiles; + + std::set staticSourceFilePaths; + for (const FilePath& path: allSourceFilePaths) + { + if (filesToClear.find(path) == filesToClear.end() && filesToAdd.find(path) == filesToAdd.end()) + { + staticSourceFilePaths.insert(path); + } + } + + RefreshInfo info; + info.mode = REFRESH_UPDATED_FILES; + info.filesToClear = filesToClear; + + for (const std::shared_ptr& sourceGroup: m_sourceGroups) + { + utility::append(info.filesToIndex, sourceGroup->getSourceFilePathsToIndex(staticSourceFilePaths)); + } + + return info; +} + +RefreshInfo Project::getRefreshInfoForIncompleteFiles() const +{ + RefreshInfo info = getRefreshInfoForUpdatedFiles(); + info.mode = REFRESH_UPDATED_AND_INCOMPLETE_FILES; + + std::set incompleteFiles; + for (const FilePath& path: m_storage->getIncompleteFiles()) + { + if (info.filesToClear.find(path) == info.filesToClear.end()) + { + incompleteFiles.insert(path); + } + } + + if (incompleteFiles.size()) + { + utility::append(incompleteFiles, m_storage->getReferencing(incompleteFiles)); + utility::append(info.filesToClear, incompleteFiles); + + std::set staticSourceFilePaths = getAllSourceFilePaths(); + for (const FilePath& path: incompleteFiles) + { + staticSourceFilePaths.erase(path); + } + + for (const std::shared_ptr& sourceGroup: m_sourceGroups) + { + utility::append(info.filesToIndex, sourceGroup->getSourceFilePathsToIndex(staticSourceFilePaths)); + } + } + + return info; +} + +RefreshInfo Project::getRefreshInfoForAllFiles() const +{ + RefreshInfo info; + info.mode = REFRESH_ALL_FILES; + info.filesToIndex = getAllSourceFilePaths(); + return info; } bool Project::hasCxxSourceGroup() const diff --git a/src/lib/project/Project.h b/src/lib/project/Project.h index 464e458d..d4f8cda5 100644 --- a/src/lib/project/Project.h +++ b/src/lib/project/Project.h @@ -6,28 +6,36 @@ #include #include +#include "project/RefreshInfo.h" #include "project/SourceGroup.h" struct FileInfo; +class DialogView; class FilePath; class PersistentStorage; class ProjectSettings; -class StorageAccessProxy; +class StorageCache; class Project { public: - Project(std::shared_ptr settings, StorageAccessProxy* storageAccessProxy); + Project(std::shared_ptr settings, StorageCache* storageCache, bool hasGUI); virtual ~Project(); - bool refresh(bool forceRefresh); - FilePath getProjectSettingsFilePath() const; std::string getDescription() const; bool settingsEqualExceptNameAndLocation(const ProjectSettings& otherSettings) const; void setStateSettingsUpdated(); + void load(); + + void refresh(DialogView* dialogView, RefreshMode refreshMode); + + RefreshInfo getRefreshInfo(RefreshMode mode) const; + + void buildIndex(const RefreshInfo& info); + private: enum ProjectStateType { @@ -42,23 +50,23 @@ private: Project(const Project&); -public: // todo: make private again - void load(); + std::set getAllSourceFilePaths() const; -private: - bool requestIndex(bool forceRefresh, bool needsFullRefresh); - - void buildIndex(const std::set& filesToIndex, const std::set& filesToClean, bool fullRefresh); + RefreshInfo getRefreshInfoForUpdatedFiles() const; + RefreshInfo getRefreshInfoForIncompleteFiles() const; + RefreshInfo getRefreshInfoForAllFiles() const; bool hasCxxSourceGroup() const; bool didFileChange(const FileInfo& info) const; std::shared_ptr m_settings; - StorageAccessProxy* const m_storageAccessProxy; + StorageCache* const m_storageCache; ProjectStateType m_state; std::shared_ptr m_storage; std::vector> m_sourceGroups; + + bool m_hasGUI; }; #endif // PROJECT_H diff --git a/src/lib/project/RefreshInfo.h b/src/lib/project/RefreshInfo.h new file mode 100644 index 00000000..f119673d --- /dev/null +++ b/src/lib/project/RefreshInfo.h @@ -0,0 +1,23 @@ +#ifndef REFRESH_INFO_H +#define REFRESH_INFO_H + +#include + +#include "utility/file/FilePath.h" + +enum RefreshMode +{ + REFRESH_NONE, + REFRESH_UPDATED_FILES, + REFRESH_UPDATED_AND_INCOMPLETE_FILES, + REFRESH_ALL_FILES +}; + +struct RefreshInfo +{ + std::set filesToIndex; + std::set filesToClear; + RefreshMode mode = REFRESH_NONE; +}; + +#endif // REFRESH_INFO_H diff --git a/src/lib/project/SourceGroup.cpp b/src/lib/project/SourceGroup.cpp index 4d7427cb..727d9f1d 100644 --- a/src/lib/project/SourceGroup.cpp +++ b/src/lib/project/SourceGroup.cpp @@ -14,11 +14,6 @@ LanguageType SourceGroup::getLanguage() const return getLanguageTypeForSourceGroupType(getType()); } -bool SourceGroup::prepareRefresh() -{ - return true; -} - bool SourceGroup::prepareIndexing() { return true; @@ -26,7 +21,6 @@ bool SourceGroup::prepareIndexing() void SourceGroup::fetchAllSourceFilePaths() { - m_sourceFilePathsToIndex.clear(); FileManager fileManager; fileManager.update( getAllSourcePaths(), @@ -36,15 +30,22 @@ void SourceGroup::fetchAllSourceFilePaths() m_allSourceFilePaths = fileManager.getAllSourceFilePaths(); } -void SourceGroup::fetchSourceFilePathsToIndex(const std::set& staticSourceFilePaths) +std::set SourceGroup::getAllSourceFilePaths() const { + return m_allSourceFilePaths; +} + +std::set SourceGroup::getSourceFilePathsToIndex(const std::set& staticSourceFilePaths) +{ + std::set sourceFilePathsToIndex; for (const FilePath& sourceFilePath: m_allSourceFilePaths) { if (staticSourceFilePaths.find(sourceFilePath) == staticSourceFilePaths.end()) { - m_sourceFilePathsToIndex.insert(sourceFilePath); + sourceFilePathsToIndex.insert(sourceFilePath); } } + return sourceFilePathsToIndex; } std::set SourceGroup::getIndexedPaths() @@ -57,16 +58,6 @@ std::set SourceGroup::getExcludedPaths() return findAndAddSymlinkedDirectories(getSourceGroupSettings()->getExcludePathsExpandedAndAbsolute()); } -std::set SourceGroup::getAllSourceFilePaths() const -{ - return m_allSourceFilePaths; -} - -std::set SourceGroup::getSourceFilePathsToIndex() const -{ - return m_sourceFilePathsToIndex; -} - std::set SourceGroup::findAndAddSymlinkedDirectories(const std::vector& paths) { std::set resultPaths; diff --git a/src/lib/project/SourceGroup.h b/src/lib/project/SourceGroup.h index 0dddae52..09a9a3cc 100644 --- a/src/lib/project/SourceGroup.h +++ b/src/lib/project/SourceGroup.h @@ -7,8 +7,8 @@ #include "settings/LanguageType.h" #include "settings/SourceGroupType.h" +#include "utility/file/FilePath.h" -class FilePath; class IndexerCommand; class SourceGroupSettings; @@ -20,24 +20,20 @@ public: virtual SourceGroupType getType() const = 0; LanguageType getLanguage() const; - virtual bool prepareRefresh(); virtual bool prepareIndexing(); void fetchAllSourceFilePaths(); - void fetchSourceFilePathsToIndex(const std::set& staticSourceFilePaths); std::set getAllSourceFilePaths() const; - std::set getSourceFilePathsToIndex() const; + std::set getSourceFilePathsToIndex(const std::set& staticSourceFilePaths); - virtual std::vector> getIndexerCommands( - const std::set& filesToIndex, bool fullRefresh) = 0; + virtual std::vector> getIndexerCommands(const std::set& filesToIndex) = 0; protected: std::set getIndexedPaths(); std::set getExcludedPaths(); std::set m_allSourceFilePaths; - std::set m_sourceFilePathsToIndex; private: virtual std::shared_ptr getSourceGroupSettings() = 0; diff --git a/src/lib/utility/commandline/CommandLineParser.cpp b/src/lib/utility/commandline/CommandLineParser.cpp index aa904552..14427acb 100644 --- a/src/lib/utility/commandline/CommandLineParser.cpp +++ b/src/lib/utility/commandline/CommandLineParser.cpp @@ -249,9 +249,14 @@ License CommandLineParser::getLicense() return m_license; } -void CommandLineParser::force() +void CommandLineParser::fullRefresh() { - m_force = true; + m_refreshMode = REFRESH_ALL_FILES; +} + +void CommandLineParser::incompleteRefresh() +{ + m_refreshMode = REFRESH_UPDATED_AND_INCOMPLETE_FILES; } const FilePath& CommandLineParser::getProjectFilePath() const @@ -259,9 +264,9 @@ const FilePath& CommandLineParser::getProjectFilePath() const return m_projectFile; } -bool CommandLineParser::getFullProjectRefresh() const +RefreshMode CommandLineParser::getRefreshMode() const { - return m_force; + return m_refreshMode; } } // namespace cmd diff --git a/src/lib/utility/commandline/CommandLineParser.h b/src/lib/utility/commandline/CommandLineParser.h index 67da5031..c15f7169 100644 --- a/src/lib/utility/commandline/CommandLineParser.h +++ b/src/lib/utility/commandline/CommandLineParser.h @@ -9,6 +9,7 @@ #include "boost/program_options.hpp" #include "License.h" +#include "project/RefreshInfo.h" #include "utility/file/FilePath.h" namespace po = boost::program_options; @@ -37,7 +38,8 @@ public: bool startedWithLicense(); bool hasError(); - void force(); + void fullRefresh(); + void incompleteRefresh(); std::string getError(); License getLicense(); @@ -45,7 +47,7 @@ public: const FilePath& getProjectFilePath() const; void setProjectFile(const FilePath& filepath); - bool getFullProjectRefresh() const; + RefreshMode getRefreshMode() const; private: void addCommand(std::unique_ptr command); @@ -57,7 +59,7 @@ private: std::vector> m_commands; const std::string m_version; - bool m_force{false}; + RefreshMode m_refreshMode = REFRESH_UPDATED_FILES; bool m_quit{false}; bool m_withoutGUI{false}; diff --git a/src/lib/utility/commandline/commands/CommandlineCommandIndex.cpp b/src/lib/utility/commandline/commands/CommandlineCommandIndex.cpp index 9693e731..3506bcd0 100644 --- a/src/lib/utility/commandline/commands/CommandlineCommandIndex.cpp +++ b/src/lib/utility/commandline/commands/CommandlineCommandIndex.cpp @@ -20,6 +20,7 @@ void CommandIndex::setup() po::options_description options("Config Options"); options.add_options() ("help,h", "Print this help message") + ("incomplete,i", "Also reindex incomplete files (files with errors)") ("full,f", "Index full project (omit to only index new/changed files)") ("project-file", po::value(), "Project file to index (.srctrlprj)") ; @@ -53,7 +54,11 @@ ReturnStatus CommandIndex::parse(std::vector& args) if (vm.count("full")) { - m_parser->force(); + m_parser->fullRefresh(); + } + else if (vm.count("incomplete")) + { + m_parser->incompleteRefresh(); } if (vm.count("project-file")) diff --git a/src/lib/utility/messaging/type/MessageLoadProject.h b/src/lib/utility/messaging/type/MessageLoadProject.h index 9b881ed2..36283017 100644 --- a/src/lib/utility/messaging/type/MessageLoadProject.h +++ b/src/lib/utility/messaging/type/MessageLoadProject.h @@ -1,6 +1,8 @@ #ifndef MESSAGE_LOAD_PROJECT_H #define MESSAGE_LOAD_PROJECT_H +#include "project/RefreshInfo.h" + #include "utility/file/FilePath.h" #include "utility/messaging/Message.h" @@ -8,9 +10,10 @@ class MessageLoadProject : public Message { public: - MessageLoadProject(const FilePath& filePath, bool forceRefresh) + MessageLoadProject(const FilePath& filePath, bool settingsChanged = false, RefreshMode refreshMode = REFRESH_NONE) : projectSettingsFilePath(filePath) - , forceRefresh(forceRefresh) + , settingsChanged(settingsChanged) + , refreshMode(refreshMode) { } @@ -21,11 +24,14 @@ public: virtual void print(std::ostream& os) const { - os << projectSettingsFilePath.str() << ", forceRefresh: " << std::boolalpha << forceRefresh; + os << projectSettingsFilePath.str(); + os << ", settingsChanged: " << std::boolalpha << settingsChanged; + os << ", refreshMode: " << refreshMode; } const FilePath projectSettingsFilePath; - const bool forceRefresh; + const bool settingsChanged; + const RefreshMode refreshMode; }; #endif // MESSAGE_LOAD_PROJECT_H diff --git a/src/lib_cxx/project/SourceGroupCxxCdb.cpp b/src/lib_cxx/project/SourceGroupCxxCdb.cpp index 66126c96..eafb8eaa 100644 --- a/src/lib_cxx/project/SourceGroupCxxCdb.cpp +++ b/src/lib_cxx/project/SourceGroupCxxCdb.cpp @@ -23,7 +23,7 @@ SourceGroupType SourceGroupCxxCdb::getType() const return SOURCE_GROUP_CXX_CDB; } -bool SourceGroupCxxCdb::prepareRefresh() +bool SourceGroupCxxCdb::prepareIndexing() { FilePath cdbPath = m_settings->getCompilationDatabasePathExpandedAndAbsolute(); if (!cdbPath.empty() && !cdbPath.exists()) @@ -47,8 +47,7 @@ bool SourceGroupCxxCdb::prepareRefresh() return true; } -std::vector> SourceGroupCxxCdb::getIndexerCommands( - const std::set& filesToIndex, bool fullRefresh) +std::vector> SourceGroupCxxCdb::getIndexerCommands(const std::set& filesToIndex) { std::shared_ptr appSettings = ApplicationSettings::getInstance(); @@ -75,16 +74,20 @@ std::vector> SourceGroupCxxCdb::getIndexerComman std::set indexedPaths = getIndexedPaths(); std::set excludedPaths = getExcludedPaths(); - const std::set& sourceFilePathsToIndex = (fullRefresh ? getAllSourceFilePaths() : getSourceFilePathsToIndex()); - std::vector> indexerCommands; FilePath cdbPath = m_settings->getCompilationDatabasePathExpandedAndAbsolute(); if (cdbPath.exists()) { std::string error; - std::shared_ptr cdb = std::shared_ptr - (clang::tooling::JSONCompilationDatabase::loadFromFile(cdbPath.str(), error, clang::tooling::JSONCommandLineSyntax::AutoDetect)); + std::shared_ptr cdb = + std::shared_ptr( + clang::tooling::JSONCompilationDatabase::loadFromFile(cdbPath.str(), + error, + clang::tooling::JSONCommandLineSyntax::AutoDetect + ) + ); + if (!error.empty()) { const std::string message = "Loading Clang compilation database failed with error: \"" + error + "\""; @@ -92,6 +95,8 @@ std::vector> SourceGroupCxxCdb::getIndexerComman MessageStatus(message, true).dispatch(); } + const std::set& sourceFilePaths = getAllSourceFilePaths(); + for (const clang::tooling::CompileCommand& command: cdb->getAllCompileCommands()) { FilePath sourcePath = FilePath(command.Filename).canonical(); @@ -101,7 +106,7 @@ std::vector> SourceGroupCxxCdb::getIndexerComman } if (filesToIndex.find(sourcePath) != filesToIndex.end() && - sourceFilePathsToIndex.find(sourcePath) != sourceFilePathsToIndex.end()) + sourceFilePaths.find(sourcePath) != sourceFilePaths.end()) { std::vector currentCompilerFlags = compilerFlags; currentCompilerFlags.insert(currentCompilerFlags.end(), command.CommandLine.begin(), command.CommandLine.end()); diff --git a/src/lib_cxx/project/SourceGroupCxxCdb.h b/src/lib_cxx/project/SourceGroupCxxCdb.h index d0b91920..4a1b2820 100644 --- a/src/lib_cxx/project/SourceGroupCxxCdb.h +++ b/src/lib_cxx/project/SourceGroupCxxCdb.h @@ -15,10 +15,9 @@ public: virtual SourceGroupType getType() const; - virtual bool prepareRefresh(); + virtual bool prepareIndexing(); - virtual std::vector> getIndexerCommands( - const std::set& filesToIndex, bool fullRefresh); + virtual std::vector> getIndexerCommands(const std::set& filesToIndex); private: virtual std::shared_ptr getSourceGroupSettingsCxx(); diff --git a/src/lib_cxx/project/SourceGroupCxxEmpty.cpp b/src/lib_cxx/project/SourceGroupCxxEmpty.cpp index 62916742..3a3518f8 100644 --- a/src/lib_cxx/project/SourceGroupCxxEmpty.cpp +++ b/src/lib_cxx/project/SourceGroupCxxEmpty.cpp @@ -18,8 +18,7 @@ SourceGroupType SourceGroupCxxEmpty::getType() const return m_settings->getType(); // may be either C or Cpp } -std::vector> SourceGroupCxxEmpty::getIndexerCommands( - const std::set& filesToIndex, bool fullRefresh) +std::vector> SourceGroupCxxEmpty::getIndexerCommands(const std::set& filesToIndex) { std::shared_ptr appSettings = ApplicationSettings::getInstance(); @@ -54,10 +53,8 @@ std::vector> SourceGroupCxxEmpty::getIndexerComm std::set indexedPaths = getIndexedPaths(); std::set excludedPaths = getExcludedPaths(); - const std::set& sourceFilePathsToIndex = (fullRefresh ? getAllSourceFilePaths() : getSourceFilePathsToIndex()); - std::vector> indexerCommands; - for (const FilePath& sourcePath: sourceFilePathsToIndex) + for (const FilePath& sourcePath: getAllSourceFilePaths()) { if (filesToIndex.find(sourcePath) != filesToIndex.end()) { diff --git a/src/lib_cxx/project/SourceGroupCxxEmpty.h b/src/lib_cxx/project/SourceGroupCxxEmpty.h index a05bb12a..4f0454a3 100644 --- a/src/lib_cxx/project/SourceGroupCxxEmpty.h +++ b/src/lib_cxx/project/SourceGroupCxxEmpty.h @@ -15,8 +15,7 @@ public: virtual SourceGroupType getType() const; - virtual std::vector> getIndexerCommands( - const std::set& filesToIndex, bool fullRefresh); + virtual std::vector> getIndexerCommands(const std::set& filesToIndex); private: virtual std::shared_ptr getSourceGroupSettingsCxx(); diff --git a/src/lib_gui/qt/QtApplication.cpp b/src/lib_gui/qt/QtApplication.cpp index 16465e0c..0a54fc2f 100644 --- a/src/lib_gui/qt/QtApplication.cpp +++ b/src/lib_gui/qt/QtApplication.cpp @@ -30,7 +30,7 @@ bool QtApplication::event(QEvent *event) if (path.exists() && (path.extension() == ".srctrlprj" || path.extension() == ".coatiproject")) { - MessageLoadProject(path, false).dispatch(); + MessageLoadProject(path).dispatch(); return true; } } diff --git a/src/lib_gui/qt/view/QtDialogView.cpp b/src/lib_gui/qt/view/QtDialogView.cpp index 2b859b3a..a16caa38 100644 --- a/src/lib_gui/qt/view/QtDialogView.cpp +++ b/src/lib_gui/qt/view/QtDialogView.cpp @@ -5,13 +5,16 @@ #include #include +#include #include "data/access/StorageAccess.h" #include "qt/window/QtIndexingDialog.h" #include "qt/window/QtMainWindow.h" #include "qt/window/QtWindow.h" #include "utility/messaging/type/MessageStatus.h" +#include "utility/scheduling/TaskLambda.h" #include "utility/utility.h" +#include "project/Project.h" QtDialogView::QtDialogView(QtMainWindow* mainWindow, StorageAccess* storageAccess) : DialogView(storageAccess) @@ -34,19 +37,7 @@ void QtDialogView::showUnknownProgressDialog(const std::string& title, const std m_onQtThread2( [=]() { - QtIndexingDialog* window = dynamic_cast(m_windowStack.getTopWindow()); - if (!window || window->getType() != QtIndexingDialog::DIALOG_UNKNOWN_PROGRESS) - { - m_windowStack.clearWindows(); - - window = createWindow(); - window->setupUnknownProgress(); - } - - window->updateTitle(title.c_str()); - window->updateMessage(message.c_str()); - - setUIBlocked(true); + showUnknownProgress(title, message, false); } ); } @@ -58,13 +49,7 @@ void QtDialogView::hideUnknownProgressDialog() m_onQtThread2( [=]() { - QtIndexingDialog* window = dynamic_cast(m_windowStack.getTopWindow()); - if (window && window->getType() == QtIndexingDialog::DIALOG_UNKNOWN_PROGRESS) - { - m_windowStack.popWindow(); - } - - setUIBlocked(false); + hideUnknownProgress(); } ); @@ -117,43 +102,91 @@ void QtDialogView::hideProgressDialog() } -DialogView::IndexingOptions QtDialogView::startIndexingDialog( - size_t cleanFileCount, size_t indexFileCount, size_t totalFileCount, DialogView::IndexingOptions options) +void QtDialogView::startIndexingDialog( + Project* project, const std::vector& enabledModes, const RefreshInfo& info) { - DialogView::IndexingOptions result; - m_resultReady = false; + m_refreshInfos.clear(); m_onQtThread( - [=, &result]() + [=]() { - QtIndexingDialog* window = createWindow(); - window->setupStart(cleanFileCount, indexFileCount, totalFileCount, options, - [&](DialogView::IndexingOptions o) - { - result = o; - m_resultReady = true; + m_windowStack.clearWindows(); - setUIBlocked(o.startIndexing); + QtIndexingDialog* window = createWindow(); + window->setupStart(enabledModes); + + m_refreshInfos.emplace(info.mode, info); + + connect(window, &QtIndexingDialog::setMode, + [=](RefreshMode refreshMode) + { + auto it = m_refreshInfos.find(refreshMode); + if (it != m_refreshInfos.end()) + { + window->updateRefreshInfo(it->second); + return; + } + + std::shared_ptr timer = std::make_shared(); + connect(timer.get(), &QTimer::timeout, + [=]() + { + showUnknownProgress("Preparing Index", "Processing Files", true); + } + ); + timer->start(200); + + Task::dispatch(std::make_shared( + [=]() + { + RefreshInfo info = project->getRefreshInfo(refreshMode); + + m_onQtThread2( + [=]() + { + m_refreshInfos.emplace(info.mode, info); + window->updateRefreshInfo(info); + + timer->stop(); + hideUnknownProgress(); + } + ); + } + )); } ); + connect(window, &QtIndexingDialog::startIndexing, + [=](RefreshMode refreshMode) + { + RefreshInfo info = m_refreshInfos.find(refreshMode)->second; + Task::dispatch(std::make_shared( + [=]() + { + project->buildIndex(info); + } + )); + + m_windowStack.clearWindows(); + } + ); + + connect(window, &QtWindow::canceled, + [=]() + { + setUIBlocked(false); + } + ); + + window->updateRefreshInfo(info); setUIBlocked(true); } ); - - while (!m_resultReady) - { - const int SLEEP_TIME_MS = 25; - std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME_MS)); - } - - return result; } void QtDialogView::updateIndexingDialog( size_t startedFileCount, size_t finishedFileCount, size_t totalFileCount, std::string sourcePath) { - m_onQtThread( [=]() { @@ -257,6 +290,68 @@ void QtDialogView::setParentWindow(QtWindow* window) ); } +void QtDialogView::showUnknownProgress(const std::string& title, const std::string& message, bool stacked) +{ + QtIndexingDialog* window = nullptr; + + if (!stacked) + { + window = dynamic_cast(m_windowStack.getTopWindow()); + + if (window && window->getType() != QtIndexingDialog::DIALOG_UNKNOWN_PROGRESS) + { + m_windowStack.clearWindows(); + window = nullptr; + } + } + + if (!window) + { + window = createWindow(); + window->setupUnknownProgress(); + } + + window->updateTitle(title.c_str()); + window->updateMessage(message.c_str()); + + setUIBlocked(true); +} + +void QtDialogView::hideUnknownProgress() +{ + QtIndexingDialog* window = dynamic_cast(m_windowStack.getTopWindow()); + if (window && window->getType() == QtIndexingDialog::DIALOG_UNKNOWN_PROGRESS) + { + m_windowStack.popWindow(); + } + + if (!m_windowStack.getWindowCount()) + { + setUIBlocked(false); + } +} + +void QtDialogView::setUIBlocked(bool blocked) +{ + if (m_parentWindow) + { + m_parentWindow->setEnabled(!blocked); + } + else + { + m_mainWindow->setContentEnabled(!blocked); + } + + if (blocked) + { + QWidget* window = m_windowStack.getTopWindow(); + if (window) + { + window->setEnabled(true); + } + } +} + void QtDialogView::handleMessage(MessageInterruptTasks* message) { m_onQtThread3( @@ -337,24 +432,3 @@ template return window; } - -void QtDialogView::setUIBlocked(bool blocked) -{ - if (m_parentWindow) - { - m_parentWindow->setEnabled(!blocked); - } - else - { - m_mainWindow->setContentEnabled(!blocked); - } - - if (blocked) - { - QWidget* window = m_windowStack.getTopWindow(); - if (window) - { - window->setEnabled(true); - } - } -} diff --git a/src/lib_gui/qt/view/QtDialogView.h b/src/lib_gui/qt/view/QtDialogView.h index 3a2cdb93..dd27341c 100644 --- a/src/lib_gui/qt/view/QtDialogView.h +++ b/src/lib_gui/qt/view/QtDialogView.h @@ -35,8 +35,8 @@ public: virtual void showProgressDialog(const std::string& title, const std::string& message, int progress) override; virtual void hideProgressDialog() override; - virtual DialogView::IndexingOptions startIndexingDialog( - size_t cleanFileCount, size_t indexFileCount, size_t totalFileCount, DialogView::IndexingOptions options) override; + virtual void startIndexingDialog( + Project* project, const std::vector& enabledModes, const RefreshInfo& info) override; virtual void updateIndexingDialog( size_t startedFileCount, size_t finishedFileCount, size_t totalFileCount, std::string sourcePath) override; virtual void finishedIndexingDialog( @@ -49,6 +49,12 @@ public: void setParentWindow(QtWindow* window); +private slots: + void showUnknownProgress(const std::string& title, const std::string& message, bool stacked); + void hideUnknownProgress(); + + void setUIBlocked(bool blocked); + private: void handleMessage(MessageInterruptTasks* message) override; void handleMessage(MessageNewErrors* message) override; @@ -60,8 +66,6 @@ private: template T* createWindow(); - void setUIBlocked(bool blocked); - QtMainWindow* m_mainWindow; QtWindow* m_parentWindow; @@ -71,6 +75,8 @@ private: QtThreadedLambdaFunctor m_onQtThread2; QtThreadedLambdaFunctor m_onQtThread3; + std::map m_refreshInfos; + bool m_resultReady; }; diff --git a/src/lib_gui/qt/window/QtIndexingDialog.cpp b/src/lib_gui/qt/window/QtIndexingDialog.cpp index bb849a14..1ab0dedc 100644 --- a/src/lib_gui/qt/window/QtIndexingDialog.cpp +++ b/src/lib_gui/qt/window/QtIndexingDialog.cpp @@ -2,8 +2,7 @@ #include #include -#include -#include +#include #include #include "qt/utility/utilityQt.h" @@ -24,9 +23,7 @@ QtIndexingDialog::QtIndexingDialog(QWidget* parent) , m_messageLabel(nullptr) , m_filePathLabel(nullptr) , m_errorWidget(nullptr) - , m_fullRefreshCheckBox(nullptr) , m_sizeHint(QSize(450, 450)) - , m_callback([](DialogView::IndexingOptions){}) { setSizeGripStyle(false); } @@ -41,69 +38,120 @@ QtIndexingDialog::DialogType QtIndexingDialog::getType() const return m_type; } -void QtIndexingDialog::setupStart( - size_t cleanFileCount, size_t indexFileCount, size_t totalFileCount, - DialogView::IndexingOptions options, std::function callback) +void QtIndexingDialog::setupStart(const std::vector& enabledModes) { + setType(DIALOG_START_INDEXING); + QBoxLayout* layout = createLayout(); addTitle("Start Indexing", layout); layout->addSpacing(5); - QLabel* clearLabel = createMessageLabel(layout); - QLabel* indexLabel = createMessageLabel(layout); - QLabel* fullLabel = createMessageLabel(layout); + m_clearLabel = createMessageLabel(layout); + m_indexLabel = createMessageLabel(layout); - clearLabel->setText("Clear: " + QString::number(cleanFileCount) + " File" + (cleanFileCount != 1 ? "s" : "")); - indexLabel->setText("Index: " + QString::number(indexFileCount) + " File" + (indexFileCount != 1 ? "s" : "")); - fullLabel->setText("Index: " + QString::number(totalFileCount) + " File" + (totalFileCount != 1 ? "s" : "")); + m_clearLabel->setVisible(false); + m_indexLabel->setVisible(false); layout->addStretch(); - if (options.fullRefreshVisible) - { - m_fullRefreshCheckBox = new QCheckBox("full refresh", this); - m_fullRefreshCheckBox->setObjectName("message"); + QHBoxLayout* subLayout = new QHBoxLayout(); + subLayout->addStretch(); - connect(m_fullRefreshCheckBox, &QCheckBox::toggled, - [=](bool checked = false) + QVBoxLayout* modeLayout = new QVBoxLayout(); + modeLayout->setSpacing(7); + + QHBoxLayout* modeTitleLayout = new QHBoxLayout(); + modeTitleLayout->setSpacing(7); + + QLabel* modeLabel = createMessageLabel(modeTitleLayout); + modeLabel->setText("Mode:"); + modeLabel->setAlignment(Qt::AlignLeft); + + QtHelpButton* helpButton = new QtHelpButton( + "Indexing Modes", + "Updated files: Reindexes all files that were modified since the last indexing, all files depending " + "on those and new files.

" + "Incomplete & updated files: Reindexes all files that had errors during last indexing, all files " + "depending on those and all updated files.

" + "All files: Deletes the previous index and reindexes all files.

" + ); + helpButton->setColor(Qt::white); + modeTitleLayout->addWidget(helpButton); + + modeTitleLayout->addStretch(); + + modeLayout->addLayout(modeTitleLayout); + modeLayout->addSpacing(5); + + m_refreshModeButtons.emplace(REFRESH_UPDATED_FILES, new QRadioButton("Updated files")); + m_refreshModeButtons.emplace(REFRESH_UPDATED_AND_INCOMPLETE_FILES, new QRadioButton("Incomplete && updated files")); + m_refreshModeButtons.emplace(REFRESH_ALL_FILES, new QRadioButton("All files")); + + std::function func = + [=](bool checked) + { + if (!checked) { - clearLabel->setVisible(!checked); - indexLabel->setVisible(!checked); - fullLabel->setVisible(checked); + return; } - ); - m_fullRefreshCheckBox->setChecked(!options.fullRefresh); - m_fullRefreshCheckBox->setChecked(options.fullRefresh); + for (auto p : m_refreshModeButtons) + { + if (p.second->isChecked()) + { + emit setMode(p.first); + return; + } + } + }; - QHBoxLayout* subLayout = new QHBoxLayout(); - subLayout->addStretch(); - subLayout->addWidget(m_fullRefreshCheckBox); - - layout->addLayout(subLayout); - } - else + for (auto p : m_refreshModeButtons) { - clearLabel->hide(); - indexLabel->hide(); + QRadioButton* button = p.second; + button->setObjectName("option"); + button->setEnabled(false); + modeLayout->addWidget(button); + connect(button, &QRadioButton::toggled, func); } - if (m_fullRefreshCheckBox) + for (RefreshMode mode : enabledModes) { - layout->addSpacing(20); + m_refreshModeButtons[mode]->setEnabled(true); } + subLayout->addLayout(modeLayout); + layout->addLayout(subLayout); + + layout->addSpacing(20); + addButtons(layout); updateNextButton("Start"); updateCloseButton("Cancel"); - m_sizeHint = QSize(350, 270); - m_callback = callback; + m_sizeHint = QSize(350, 310); finishSetup(); } +void QtIndexingDialog::updateRefreshInfo(const RefreshInfo& info) +{ + QRadioButton* button = m_refreshModeButtons.find(info.mode)->second; + if (!button->isChecked()) + { + button->setChecked(true); + } + + size_t clearCount = info.filesToClear.size(); + size_t indexCount = info.filesToIndex.size(); + + m_clearLabel->setText("Files to clear: " + QString::number(clearCount)); + m_indexLabel->setText("Source files to index: " + QString::number(indexCount)); + + m_clearLabel->setVisible(clearCount); + m_indexLabel->setVisible(true); +} + void QtIndexingDialog::setupIndexing() { setType(DIALOG_INDEXING); @@ -143,15 +191,15 @@ void QtIndexingDialog::setupReport( layout->addSpacing(5); createMessageLabel(layout)->setText( - "Source Files indexed: " + QString::number(indexedFileCount) + "/" + QString::number(totalIndexedFileCount) + "Source files indexed: " + QString::number(indexedFileCount) + "/" + QString::number(totalIndexedFileCount) ); createMessageLabel(layout)->setText( - "Files completed: " + QString::number(completedFileCount) + "/" + QString::number(totalFileCount) + "Total files completed: " + QString::number(completedFileCount) + "/" + QString::number(totalFileCount) ); layout->addSpacing(12); - createMessageLabel(layout)->setText("Total Time: " + QString::fromStdString(utility::timeToString(time))); + createMessageLabel(layout)->setText("Time: " + QString::fromStdString(utility::timeToString(time))); layout->addSpacing(12); addErrorWidget(layout); @@ -283,14 +331,19 @@ void QtIndexingDialog::resizeEvent(QResizeEvent* event) void QtIndexingDialog::handleNext() { - if (m_type == DIALOG_MESSAGE) + if (m_type == DIALOG_START_INDEXING) { - DialogView::IndexingOptions options; - options.startIndexing = true; - options.fullRefresh = m_fullRefreshCheckBox && m_fullRefreshCheckBox->isChecked(); - m_callback(options); + for (auto p : m_refreshModeButtons) + { + if (p.second->isChecked()) + { + emit startIndexing(p.first); + return; + } + } } - else if (m_type == DIALOG_REPORT) + + if (m_type == DIALOG_REPORT) { MessageShowErrorHelpMessage().dispatch(); } @@ -300,12 +353,6 @@ void QtIndexingDialog::handleNext() void QtIndexingDialog::handleClose() { - if (m_type == DIALOG_MESSAGE) - { - DialogView::IndexingOptions options; - m_callback(options); - } - if (m_type == DIALOG_INDEXING) { MessageInterruptTasks().dispatch(); @@ -368,7 +415,7 @@ void QtIndexingDialog::addTitle(QString title, QBoxLayout* layout) { m_title->show(); } - else + else if (layout) { layout->addWidget(m_title, 0, Qt::AlignRight); } diff --git a/src/lib_gui/qt/window/QtIndexingDialog.h b/src/lib_gui/qt/window/QtIndexingDialog.h index 5fba08a9..5388fa7c 100644 --- a/src/lib_gui/qt/window/QtIndexingDialog.h +++ b/src/lib_gui/qt/window/QtIndexingDialog.h @@ -1,13 +1,14 @@ -#ifndef QT_INDEXING_WIZARD_WINDOW_H -#define QT_INDEXING_WIZARD_WINDOW_H +#ifndef QT_INDEXING_DIALOG_H +#define QT_INDEXING_DIALOG_H #include -#include "component/view/DialogView.h" +#include "project/RefreshInfo.h" #include "qt/window/QtWindow.h" class QCheckBox; class QLabel; +class QRadioButton; class QtProgressBar; class QtIndexingDialog @@ -15,12 +16,17 @@ class QtIndexingDialog { Q_OBJECT +signals: + void setMode(RefreshMode mode); + void startIndexing(RefreshMode mode); + public: enum DialogType { DIALOG_MESSAGE, DIALOG_UNKNOWN_PROGRESS, DIALOG_PROGRESS, + DIALOG_START_INDEXING, DIALOG_INDEXING, DIALOG_REPORT }; @@ -30,8 +36,9 @@ public: DialogType getType() const; - void setupStart(size_t cleanFileCount, size_t indexFileCount, size_t totalFileCount, - DialogView::IndexingOptions options, std::function callback); + void setupStart(const std::vector& enabledModes); + void updateRefreshInfo(const RefreshInfo& info); + void setupIndexing(); void setupReport( size_t indexedFileCount, size_t totalIndexedFileCount, size_t completedFileCount, size_t totalFileCount, @@ -82,12 +89,12 @@ private: QWidget* m_errorWidget; // start indexing - QCheckBox* m_fullRefreshCheckBox; + QLabel* m_clearLabel; + QLabel* m_indexLabel; + std::map m_refreshModeButtons; QSize m_sizeHint; - - std::function m_callback; QString m_sourcePath; }; -#endif // QT_INDEXING_WIZARD_WINDOW_H +#endif // QT_INDEXING_DIALOG_H diff --git a/src/lib_gui/qt/window/QtMainWindow.cpp b/src/lib_gui/qt/window/QtMainWindow.cpp index 37425ec7..10ad6ef1 100644 --- a/src/lib_gui/qt/window/QtMainWindow.cpp +++ b/src/lib_gui/qt/window/QtMainWindow.cpp @@ -582,7 +582,7 @@ void QtMainWindow::openProject() if (!fileName.isEmpty()) { - MessageLoadProject(FilePath(fileName.toStdString()), false).dispatch(); + MessageLoadProject(FilePath(fileName.toStdString())).dispatch(); m_windowStack.clearWindows(); } } @@ -687,7 +687,7 @@ void QtMainWindow::openRecentProject() QAction *action = qobject_cast(sender()); if (action) { - MessageLoadProject(FilePath(action->data().toString().toStdString()), false).dispatch(); + MessageLoadProject(FilePath(action->data().toString().toStdString())).dispatch(); m_windowStack.clearWindows(); } } diff --git a/src/lib_gui/qt/window/QtStartScreen.cpp b/src/lib_gui/qt/window/QtStartScreen.cpp index 8cd1632a..83ff921d 100644 --- a/src/lib_gui/qt/window/QtStartScreen.cpp +++ b/src/lib_gui/qt/window/QtStartScreen.cpp @@ -49,7 +49,7 @@ void QtRecentProjectButton::handleButtonClick() { if (m_projectExists) { - MessageLoadProject(m_projectFilePath, false).dispatch(); + MessageLoadProject(m_projectFilePath).dispatch(); } else { diff --git a/src/lib_gui/qt/window/QtWindowStack.cpp b/src/lib_gui/qt/window/QtWindowStack.cpp index ab539b2a..d299337e 100644 --- a/src/lib_gui/qt/window/QtWindowStack.cpp +++ b/src/lib_gui/qt/window/QtWindowStack.cpp @@ -57,7 +57,7 @@ void QtWindowStack::popWindow() if (m_stack.size()) { m_stack.back()->hideWindow(); - delete m_stack.back(); + m_stack.back()->deleteLater(); m_stack.pop_back(); emit pop(); @@ -93,7 +93,7 @@ void QtWindowStack::clearWindows() for (QtWindowStackElement* window : m_stack) { window->hideWindow(); - delete window; + window->deleteLater(); } m_stack.clear(); diff --git a/src/lib_gui/qt/window/project_wizzard/QtProjectWizzard.cpp b/src/lib_gui/qt/window/project_wizzard/QtProjectWizzard.cpp index 76231652..eda012e0 100644 --- a/src/lib_gui/qt/window/project_wizzard/QtProjectWizzard.cpp +++ b/src/lib_gui/qt/window/project_wizzard/QtProjectWizzard.cpp @@ -919,30 +919,23 @@ void QtProjectWizzard::createProject() m_projectSettings->setAllSourceGroupSettings(m_allSourceGroupSettings); m_projectSettings->save(path); - bool forceRefreshProject = false; + bool settingsChanged = false; if (m_editing) { - bool settingsChanged = false; - Application* application = Application::getInstance().get(); if (application->getCurrentProject() != NULL) { settingsChanged = !(application->getCurrentProject()->settingsEqualExceptNameAndLocation(*(m_projectSettings.get()))); } - bool appSettingsChanged = !(m_appSettings == *ApplicationSettings::getInstance().get()); - - if (settingsChanged || appSettingsChanged) - { - forceRefreshProject = true; - } + settingsChanged |= !(m_appSettings == *ApplicationSettings::getInstance().get()); } else { MessageStatus("Created project: " + path.str()).dispatch(); } - MessageLoadProject(path, forceRefreshProject).dispatch(); + MessageLoadProject(path, settingsChanged).dispatch(); finishWizzard(); } diff --git a/src/lib_java/project/SourceGroupJava.cpp b/src/lib_java/project/SourceGroupJava.cpp index 463093d8..4686868c 100644 --- a/src/lib_java/project/SourceGroupJava.cpp +++ b/src/lib_java/project/SourceGroupJava.cpp @@ -30,8 +30,7 @@ bool SourceGroupJava::prepareIndexing() return true; } -std::vector> SourceGroupJava::getIndexerCommands( - const std::set& filesToIndex, bool fullRefresh) +std::vector> SourceGroupJava::getIndexerCommands(const std::set& filesToIndex) { const std::string languageStandard = getSourceGroupSettingsJava()->getStandard(); @@ -39,10 +38,8 @@ std::vector> SourceGroupJava::getIndexerCommands std::set indexedPaths = getIndexedPaths(); std::set excludedPaths = getExcludedPaths(); - const std::set& sourceFilePathsToIndex = (fullRefresh ? getAllSourceFilePaths() : getSourceFilePathsToIndex()); - std::vector> indexerCommands; - for (const FilePath& sourcePath: sourceFilePathsToIndex) + for (const FilePath& sourcePath: getAllSourceFilePaths()) { if (filesToIndex.find(sourcePath) != filesToIndex.end()) { diff --git a/src/lib_java/project/SourceGroupJava.h b/src/lib_java/project/SourceGroupJava.h index 0758c25f..df8ab139 100644 --- a/src/lib_java/project/SourceGroupJava.h +++ b/src/lib_java/project/SourceGroupJava.h @@ -17,8 +17,7 @@ public: virtual bool prepareIndexing(); - virtual std::vector> getIndexerCommands( - const std::set& filesToIndex, bool fullRefresh); + virtual std::vector> getIndexerCommands(const std::set& filesToIndex); protected: virtual std::vector doGetClassPath();