logic: background indexing (issue #175)

* allow hiding of indexing dialogs to browse loaded project while indexing
* show indexing status in status bar while indexing
* use different dialog views for different use cases to avoid state collisions
* fixed error label in status bar jittering while indexing
* show hand cursors for clickable elements in status bar
* indexing is not interrupted on pressing ESC while in hidden
* disable error table and error label while indexing
* refactored indexing messages
* forbid indexing and changing project while indexing
* don't show indexing status messages in status bar when indexing in the background
* allow tasks to run in the background

fortune cookie message = A journey is waiting for you.
This commit is contained in:
Eberhard Graether
2018-07-10 11:42:58 +02:00
parent 6bbb44309d
commit ad325ac974
76 changed files with 875 additions and 448 deletions
+65 -46
View File
@@ -12,7 +12,6 @@
#include "data/TaskFinishParsing.h"
#include "data/TaskInjectStorage.h"
#include "data/TaskMergeStorages.h"
#include "data/TaskShowUnknownProgressDialog.h"
#include "project/RefreshInfoGenerator.h"
#include "project/SourceGroup.h"
#include "project/SourceGroupFactory.h"
@@ -23,7 +22,8 @@
#include "utility/file/FilePath.h"
#include "utility/file/FileSystem.h"
#include "utility/messaging/type/error/MessageErrorCountClear.h"
#include "utility/messaging/type/MessageFinishedParsing.h"
#include "utility/messaging/type/indexing/MessageIndexingFinished.h"
#include "utility/messaging/type/indexing/MessageIndexingStarted.h"
#include "utility/messaging/type/MessageRefresh.h"
#include "utility/messaging/type/MessageStatus.h"
#include "utility/scheduling/TaskDecoratorRepeat.h"
@@ -51,6 +51,7 @@ Project::Project(std::shared_ptr<ProjectSettings> settings, StorageCache* storag
: m_settings(settings)
, m_storageCache(storageCache)
, m_state(PROJECT_STATE_NOT_LOADED)
, m_isIndexing(false)
, m_hasGUI(hasGUI)
{
}
@@ -69,6 +70,11 @@ std::string Project::getDescription() const
return m_settings->getDescription();
}
bool Project::isIndexing() const
{
return m_isIndexing;
}
bool Project::settingsEqualExceptNameAndLocation(const ProjectSettings& otherSettings) const
{
return m_settings->equalsExceptNameAndLocation(otherSettings);
@@ -84,6 +90,12 @@ void Project::setStateOutdated()
void Project::load()
{
if (m_isIndexing)
{
MessageStatus(L"Cannot load another project while indexing.", true, false).dispatch();
return;
}
m_storageCache->clear();
m_storageCache->setSubject(nullptr);
@@ -101,9 +113,10 @@ void Project::load()
{
if (dbPath.exists())
{
if (Application::getInstance()->getDialogView()->confirm(
if (Application::getInstance()->getDialogView(DialogView::UseCase::GENERAL)->confirm(
"Sourcetrail has been closed unexpectedly while indexing this project. You can either choose to keep the data that has "
"already been indexed or discard that data and restore the state of your project before indexing?", { "Keep and Continue", "Discard and Restore" }) == 0)
"already been indexed or discard that data and restore the state of your project before indexing?",
{ "Keep and Continue", "Discard and Restore" }) == 0)
{
LOG_INFO("Switching to temporary indexing data on user's decision");
FileSystem::remove(dbPath);
@@ -124,7 +137,7 @@ void Project::load()
}
m_storage = std::make_shared<PersistentStorage>(
projectSettingsPath.replaceExtension(INDEX_DB_FILE_EXTENSION),
projectSettingsPath.replaceExtension(INDEX_DB_FILE_EXTENSION),
projectSettingsPath.replaceExtension(BOOKMARK_DB_FILE_EXTENSION)
);
@@ -181,7 +194,7 @@ void Project::load()
if (m_hasGUI)
{
MessageFinishedParsing().dispatch();
MessageIndexingFinished().dispatch();
}
MessageStatus(L"Finished Loading", false, false).dispatch();
}
@@ -214,6 +227,12 @@ void Project::load()
void Project::refresh(RefreshMode refreshMode, DialogView* dialogView)
{
if (m_isIndexing)
{
MessageStatus(L"Cannot refresh the project while indexing.", true, false).dispatch();
return;
}
if (m_state == PROJECT_STATE_NOT_LOADED)
{
return;
@@ -354,15 +373,21 @@ RefreshInfo Project::getRefreshInfo(RefreshMode mode) const
void Project::buildIndex(const RefreshInfo& info, DialogView* dialogView)
{
if (m_isIndexing)
{
MessageStatus(L"Cannot refresh project while indexing.", true, false).dispatch();
return;
}
if (info.mode != REFRESH_ALL_FILES && info.filesToClear.empty() && info.filesToIndex.empty())
{
if (m_hasGUI)
{
dialogView->hideDialogs();
dialogView->clearDialogs();
}
else
{
MessageFinishedParsing().dispatch();
MessageIndexingFinished().dispatch();
}
MessageStatus(L"Nothing to refresh, all files are up-to-date.").dispatch();
@@ -390,6 +415,10 @@ void Project::buildIndex(const RefreshInfo& info, DialogView* dialogView)
std::shared_ptr<TaskGroupSequence> taskSequential = std::make_shared<TaskGroupSequence>();
bool hideable = m_state == PROJECT_STATE_LOADED || m_state == PROJECT_STATE_OUTDATED;
dialogView->setDialogsHideable(hideable);
dialogView->setUpdateIndexingStatus(true);
if (info.mode != REFRESH_ALL_FILES && (info.filesToClear.size() || info.nonIndexedFilesToClear.size()))
{
taskSequential->addTask(std::make_shared<TaskCleanStorage>(
@@ -489,7 +518,9 @@ void Project::buildIndex(const RefreshInfo& info, DialogView* dialogView)
);
// add task that notifies the user of what's going on
taskSequential->addTask( // we don't need to hide this dialog again, because it's overridden by other dialogs later on.
std::make_shared<TaskShowUnknownProgressDialog>(L"Finish Indexing", L"Saving\nRemaining Data")
std::make_shared<TaskLambda>([dialogView]() {
dialogView->showUnknownProgressDialog(L"Finish Indexing", L"Saving\nRemaining Data");
})
);
// add task that injects the remaining intermediate storages into the persistent storage
@@ -509,7 +540,7 @@ void Project::buildIndex(const RefreshInfo& info, DialogView* dialogView)
taskSequential->addTask(std::make_shared<TaskGroupSelector>()->addChildTasks(
std::make_shared<TaskGroupSequence>()->addChildTasks(
std::make_shared<TaskFindValue>("keep_database"),
std::make_shared<TaskFindValue>("keep_database"),
std::make_shared<TaskLambda>([this]() {
Task::dispatch(std::make_shared<TaskLambda>([this]() {
swapToTempStorage();
@@ -521,37 +552,42 @@ void Project::buildIndex(const RefreshInfo& info, DialogView* dialogView)
std::make_shared<TaskFindValue>("discard_database"),
std::make_shared<TaskLambda>([this]() {
Task::dispatch(std::make_shared<TaskLambda>([this]() {
const FilePath tempIndexDbPath = m_storage->getIndexDbFilePath().replaceExtension(TEMP_INDEX_DB_FILE_EXTENSION);
if (tempIndexDbPath.exists())
{
LOG_INFO("Discarding temporary indexing data");
FileSystem::remove(tempIndexDbPath);
}
discardTempStorage();
}));
})
)
));
taskSequential->addTask(std::make_shared<TaskLambda>([]() {
MessageFinishedParsing().dispatch();
taskSequential->addTask(std::make_shared<TaskLambda>([dialogView, this]() {
m_isIndexing = false;
dialogView->setDialogsHideable(false);
dialogView->setUpdateIndexingStatus(false);
MessageIndexingFinished().dispatch();
}));
taskSequential->setIsBackgroundTask(true);
Task::dispatch(taskSequential);
m_isIndexing = true;
MessageIndexingStarted().dispatch();
}
void Project::swapToTempStorage()
{
LOG_INFO("Switching to temporary indexing data");
const FilePath indexDbFilePath = m_storage->getIndexDbFilePath();
const FilePath tempIndexDbFilePath = indexDbFilePath.replaceExtension(TEMP_INDEX_DB_FILE_EXTENSION);
const FilePath bookmarkDbFilePath = m_storage->getBookmarkDbFilePath();
m_storage.reset();
FileSystem::remove(indexDbFilePath);
FileSystem::rename(tempIndexDbFilePath, indexDbFilePath);
m_storage = std::make_shared<PersistentStorage>(indexDbFilePath, bookmarkDbFilePath);
m_storage->setup();
//std::shared_ptr<DialogView> dialogView = Application::getInstance()->getDialogView();
//std::shared_ptr<DialogView> dialogView = Application::getInstance()->getDialogView(DialogView::UseCase::INDEXING);
//dialogView->showUnknownProgressDialog(L"Finish Indexing", L"Building caches");
m_storage->buildCaches();
//dialogView->hideUnknownProgressDialog();
@@ -559,6 +595,16 @@ void Project::swapToTempStorage()
m_storageCache->setSubject(m_storage.get());
}
void Project::discardTempStorage()
{
const FilePath tempIndexDbPath = m_storage->getIndexDbFilePath().replaceExtension(TEMP_INDEX_DB_FILE_EXTENSION);
if (tempIndexDbPath.exists())
{
LOG_INFO("Discarding temporary indexing data");
FileSystem::remove(tempIndexDbPath);
}
}
bool Project::hasCxxSourceGroup() const
{
for (const std::shared_ptr<SourceGroup>& sourceGroup: m_sourceGroups)
@@ -573,30 +619,3 @@ bool Project::hasCxxSourceGroup() const
}
return false;
}
bool Project::didFileChange(const FileInfo& info) const
{
FileInfo diskFileInfo = FileSystem::getFileInfoForPath(info.path);
if (diskFileInfo.lastWriteTime > info.lastWriteTime)
{
std::shared_ptr<TextAccess> storedFileContent = m_storage->getFileContent(info.path);
std::shared_ptr<TextAccess> diskFileContent = TextAccess::createFromFile(diskFileInfo.path);
const std::vector<std::string>& diskFileLines = diskFileContent->getAllLines();
const std::vector<std::string>& storedFileLines = storedFileContent->getAllLines();
if (diskFileLines.size() == storedFileLines.size())
{
for (size_t i = 0; i < diskFileLines.size(); i++)
{
if (diskFileLines[i] != storedFileLines[i])
{
return true;
}
}
return false;
}
return true;
}
return false;
}