diff --git a/CMakeLists.txt b/CMakeLists.txt index 966a826c..08ad50b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,7 @@ set(Boost_USE_MULTITHREAD ON) set(Boost_USE_STATIC_LIBS OFF) set(Boost_USE_STATIC_RUNTIME OFF) set(BOOST_LIBRARYDIR $ENV{BOOST_155_DIR}) -find_package(Boost 1.55 COMPONENTS system filesystem REQUIRED) +find_package(Boost 1.55 COMPONENTS system filesystem date_time REQUIRED) # Eigen ------------------------------------------------------------------------- diff --git a/bin/app/data/gui/refresh_view/images/auto_refresh.png b/bin/app/data/gui/refresh_view/images/auto_refresh.png new file mode 100644 index 00000000..ae7bc7c4 Binary files /dev/null and b/bin/app/data/gui/refresh_view/images/auto_refresh.png differ diff --git a/bin/app/data/gui/refresh_view/images/refresh.png b/bin/app/data/gui/refresh_view/images/refresh.png new file mode 100755 index 00000000..f674bd4f Binary files /dev/null and b/bin/app/data/gui/refresh_view/images/refresh.png differ diff --git a/bin/app/data/gui/refresh_view/refresh_view.css b/bin/app/data/gui/refresh_view/refresh_view.css new file mode 100644 index 00000000..8c649a0d --- /dev/null +++ b/bin/app/data/gui/refresh_view/refresh_view.css @@ -0,0 +1,32 @@ +#refresh_bar { + background: white; +} + +QPushButton { + background: #E0E0E0; + border: none; + background-position: 15px 5px; + height: 30px; + width: 30px; +} + +QPushButton:hover, QPushButton:checked { + background: #B2B2B2; +} + +QPushButton#refresh_button { + border-bottom-left-radius: 12px; + border-top-left-radius: 12px; + margin-right: 2px; +} + +QPushButton#auto_refresh_button { + border-bottom-right-radius: 12px; + border-top-right-radius: 12px; +} + +QToolTip { + background-color: rgb(204, 204, 204); + border: 1px solid rgb(0, 0, 0); + color: rgb(0, 0, 0); +} diff --git a/bin/app/data/src/test/main.cpp b/bin/app/data/src/test/main.cpp index 83a6fc1a..0f8e17dd 100644 --- a/bin/app/data/src/test/main.cpp +++ b/bin/app/data/src/test/main.cpp @@ -15,7 +15,7 @@ int main() int b = diff(a, 3); int c = a * b; -// int x = y; + int x = 0; return 0; } diff --git a/bin/test/data/FileSystemTestSuite/update.c b/bin/test/data/FileSystemTestSuite/update.c new file mode 100644 index 00000000..77c430a5 --- /dev/null +++ b/bin/test/data/FileSystemTestSuite/update.c @@ -0,0 +1 @@ +update \ No newline at end of file diff --git a/bin/test/data/log/test_log.txt b/bin/test/data/log/test_log.txt index 607c563e..41b51725 100644 --- a/bin/test/data/log/test_log.txt +++ b/bin/test/data/log/test_log.txt @@ -1,4 +1,25 @@ ConfigManager.cpp ERROR: value path/to/nowhere is not present in config. +Token.cpp ERROR: Location Id was not referenced by this Token. +Node.cpp WARNING: Cannot change NodeType after it was already set from namespace to class +Edge.cpp ERROR: Nodes are not plain copies. +Edge.cpp ERROR: Edge usage can't go from Node undefined to Node undefined +Edge.cpp ERROR: Edge usage can't go from Node undefined to Node undefined +Edge.cpp ERROR: Edge usage can't go from Node undefined to Node undefined +Storage.cpp INFO: class: A +Storage.cpp INFO: method: A::A +Storage.cpp INFO: global usage: A::A -> A::count +Storage.cpp INFO: method: A::getCount +Storage.cpp INFO: global usage: A::getCount -> A::count +Storage.cpp INFO: method: A::process +Storage.cpp INFO: field: A::count +Storage.cpp INFO: class: B +Storage.cpp INFO: inheritance: B : A +Storage.cpp INFO: method: B::process +Storage.cpp INFO: type usage: B::process -> int +Storage.cpp INFO: function: main +Storage.cpp INFO: type usage: main -> B +Storage.cpp INFO: call: main -> B::B +Storage.cpp INFO: call: main -> A::getCount Storage.cpp INFO: class: A Storage.cpp INFO: method: A::A Storage.cpp INFO: global usage: A::A -> A::count @@ -31,24 +52,6 @@ SearchMatch.cpp INFO: 237 A::A ^^^^ -Storage.cpp INFO: class: A -Storage.cpp INFO: method: A::A -Storage.cpp INFO: global usage: A::A -> A::count -Storage.cpp INFO: method: A::getCount -Storage.cpp INFO: global usage: A::getCount -> A::count -Storage.cpp INFO: method: A::process -Storage.cpp INFO: field: A::count -Storage.cpp INFO: class: B -Storage.cpp INFO: inheritance: B : A -Storage.cpp INFO: method: B::process -Storage.cpp INFO: type usage: B::process -> int -Storage.cpp INFO: function: main -Storage.cpp INFO: type usage: main -> B -Storage.cpp INFO: call: main -> B::B -Storage.cpp INFO: call: main -> A::getCount -Token.cpp ERROR: Location Id was not referenced by this Token. -Node.cpp WARNING: Cannot change NodeType after it was already set from namespace to class -Edge.cpp ERROR: Nodes are not plain copies. Settings.cpp WARNING: File for Settings not found. ConfigManager.cpp ERROR: value Bool is not present in config. ConfigManager.cpp ERROR: value Int is not present in config. @@ -64,7 +67,6 @@ ConfigManager.cpp ERROR: value Int is not present in config. ConfigManager.cpp ERROR: value Float is not present in config. ConfigManager.cpp ERROR: value String is not present in config. ConfigManager.cpp ERROR: value NewBool is not present in config. -Graph.cpp ERROR: Can't remove member edge, without removing the child node. Storage.cpp INFO: typedef: type -> int Storage.cpp INFO: class: Class Storage.cpp INFO: struct: Struct @@ -107,6 +109,17 @@ Storage.cpp INFO: global usage: isTrue -> global Storage.cpp INFO: function: isTrue Storage.cpp INFO: struct: Struct Storage.cpp INFO: type usage: isTrue -> Struct +Storage.cpp INFO: function: isTrue +Storage.cpp INFO: function: isTrue +Storage.cpp INFO: function: main +Storage.cpp INFO: call: main -> isTrue +Storage.cpp INFO: function: isTrue +Storage.cpp INFO: function: main +Storage.cpp INFO: call: main -> isTrue +Storage.cpp INFO: function: isTrue +Storage.cpp INFO: function: main +Storage.cpp INFO: call: main -> isTrue +Graph.cpp ERROR: Can't remove member edge, without removing the child node. TextAccess.cpp WARNING: Index 'firstLine' has to be lower or equal index 'lastLine', is 3 > 2 TextAccess.cpp WARNING: Tried to access index 10. Maximum index is 8 TextAccess.cpp WARNING: Tried to access index 10. Maximum index is 8 diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 38dbee85..2574d379 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -15,6 +15,8 @@ add_files( qt/element/QtCodeSnippet.h qt/element/QtMainWindow.cpp qt/element/QtMainWindow.h + qt/element/QtRefreshBar.cpp + qt/element/QtRefreshBar.h qt/element/QtSearchBar.cpp qt/element/QtSearchBar.h qt/element/QtSmartSearchBox.cpp @@ -56,6 +58,8 @@ add_files( qt/view/QtGraphView.h qt/view/QtMainView.cpp qt/view/QtMainView.h + qt/view/QtRefreshView.cpp + qt/view/QtRefreshView.h qt/view/QtSearchView.cpp qt/view/QtSearchView.h qt/view/QtStatusBarView.cpp diff --git a/src/app/qt/element/QtMainWindow.cpp b/src/app/qt/element/QtMainWindow.cpp index cdc3c151..36548920 100644 --- a/src/app/qt/element/QtMainWindow.cpp +++ b/src/app/qt/element/QtMainWindow.cpp @@ -17,6 +17,7 @@ #include "utility/messaging/type/MessageRefresh.h" #include "utility/messaging/type/MessageSaveProject.h" #include "utility/messaging/type/MessageUndo.h" +#include "utility/messaging/type/MessageWindowFocus.h" QtMainWindow::QtMainWindow() { @@ -208,6 +209,16 @@ void QtMainWindow::saveLayout() } } +bool QtMainWindow::event(QEvent* event) +{ + if (event->type() == QEvent::WindowActivate) + { + MessageWindowFocus().dispatch(); + } + + return QMainWindow::event(event); +} + void QtMainWindow::setupProjectMenu() { QMenu *menu = new QMenu(tr("&Project"), this); diff --git a/src/app/qt/element/QtMainWindow.h b/src/app/qt/element/QtMainWindow.h index d5d41a68..bdf2099a 100644 --- a/src/app/qt/element/QtMainWindow.h +++ b/src/app/qt/element/QtMainWindow.h @@ -26,6 +26,9 @@ public: void loadLayout(); void saveLayout(); +protected: + bool event(QEvent* event); + public slots: void about(); void newProject(); diff --git a/src/app/qt/element/QtRefreshBar.cpp b/src/app/qt/element/QtRefreshBar.cpp new file mode 100644 index 00000000..7c3b86e1 --- /dev/null +++ b/src/app/qt/element/QtRefreshBar.cpp @@ -0,0 +1,56 @@ +#include "qt/element/QtRefreshBar.h" + +#include +#include + +#include "utility/messaging/type/MessageAutoRefreshChanged.h" +#include "utility/messaging/type/MessageRefresh.h" + +QtRefreshBar::QtRefreshBar() +{ + setObjectName("refresh_bar"); + + QBoxLayout* layout = new QHBoxLayout(); + layout->setSpacing(0); + layout->setAlignment(Qt::AlignTop); + setLayout(layout); + + m_refreshButton = new QPushButton(this); + m_refreshButton->setObjectName("refresh_button"); + m_refreshButton->setToolTip("refresh"); + m_refreshButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac + m_refreshButton->setIcon(QIcon("data/gui/refresh_view/images/refresh.png")); + m_refreshButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); + layout->addWidget(m_refreshButton); + + m_autoRefreshButton = new QPushButton(this); + m_autoRefreshButton->setObjectName("auto_refresh_button"); + m_autoRefreshButton->setCheckable(true); + m_autoRefreshButton->setToolTip("automatic refresh on window focus"); + m_autoRefreshButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac + m_autoRefreshButton->setIcon(QIcon("data/gui/refresh_view/images/auto_refresh.png")); + m_autoRefreshButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); + layout->addWidget(m_autoRefreshButton); + + connect(m_refreshButton, SIGNAL(clicked()), this, SLOT(refreshClicked())); + connect(m_autoRefreshButton, SIGNAL(clicked()), this, SLOT(autoRefreshClicked())); +} + +QtRefreshBar::~QtRefreshBar() +{ +} + +QSize QtRefreshBar::sizeHint() const +{ + return QSize(150, 100); +} + +void QtRefreshBar::refreshClicked() +{ + MessageRefresh().dispatch(); +} + +void QtRefreshBar::autoRefreshClicked() +{ + MessageAutoRefreshChanged(m_autoRefreshButton->isChecked()).dispatch(); +} diff --git a/src/app/qt/element/QtRefreshBar.h b/src/app/qt/element/QtRefreshBar.h new file mode 100644 index 00000000..2fb37c21 --- /dev/null +++ b/src/app/qt/element/QtRefreshBar.h @@ -0,0 +1,28 @@ +#ifndef QT_REFRESH_BAR_H +#define QT_REFRESH_BAR_H + +#include + +class QPushButton; + +class QtRefreshBar + : public QFrame +{ + Q_OBJECT + +public: + QtRefreshBar(); + virtual ~QtRefreshBar(); + + virtual QSize sizeHint() const; + +private slots: + void refreshClicked(); + void autoRefreshClicked(); + +private: + QPushButton* m_refreshButton; + QPushButton* m_autoRefreshButton; +}; + +#endif // QT_REFRESH_BAR_H diff --git a/src/app/qt/element/QtSearchBar.h b/src/app/qt/element/QtSearchBar.h index fdb3bfd9..2706cc7d 100644 --- a/src/app/qt/element/QtSearchBar.h +++ b/src/app/qt/element/QtSearchBar.h @@ -1,5 +1,5 @@ -#ifndef QT_SEARCH_BOX_H -#define QT_SEARCH_BOX_H +#ifndef QT_SEARCH_BAR_H +#define QT_SEARCH_BAR_H #include @@ -35,4 +35,4 @@ private: // QPushButton* m_caseSensitiveButton; }; -#endif // QT_SEARCH_BOX_H +#endif // QT_SEARCH_BAR_H diff --git a/src/app/qt/view/QtRefreshView.cpp b/src/app/qt/view/QtRefreshView.cpp new file mode 100644 index 00000000..5594fbe8 --- /dev/null +++ b/src/app/qt/view/QtRefreshView.cpp @@ -0,0 +1,43 @@ +#include "qt/view/QtRefreshView.h" + +#include "component/controller/RefreshController.h" +#include "qt/view/QtViewWidgetWrapper.h" +#include "utility/text/TextAccess.h" + +QtRefreshView::QtRefreshView(ViewLayout* viewLayout) + : RefreshView(viewLayout) + , m_refreshViewFunctor(std::bind(&QtRefreshView::doRefreshView, this)) +{ + m_widget = std::make_shared(); + setStyleSheet(); +} + +QtRefreshView::~QtRefreshView() +{ +} + +void QtRefreshView::createWidgetWrapper() +{ + setWidgetWrapper(std::make_shared(m_widget)); +} + +void QtRefreshView::initView() +{ +} + +void QtRefreshView::refreshView() +{ + m_refreshViewFunctor(); +} + +void QtRefreshView::doRefreshView() +{ + setStyleSheet(); +} + +void QtRefreshView::setStyleSheet() +{ + std::string css = TextAccess::createFromFile("data/gui/refresh_view/refresh_view.css")->getText(); + + m_widget->setStyleSheet(css.c_str()); +} diff --git a/src/app/qt/view/QtRefreshView.h b/src/app/qt/view/QtRefreshView.h new file mode 100644 index 00000000..16811347 --- /dev/null +++ b/src/app/qt/view/QtRefreshView.h @@ -0,0 +1,34 @@ +#ifndef QT_REFRESH_VIEW_H +#define QT_REFRESH_VIEW_H + +#include + +#include "component/view/RefreshView.h" +#include "qt/element/QtRefreshBar.h" +#include "qt/utility/QtThreadedFunctor.h" + +class QtRefreshView + : public RefreshView +{ +public: + QtRefreshView(ViewLayout* viewLayout); + ~QtRefreshView(); + + // View implementation + virtual void createWidgetWrapper(); + virtual void initView(); + virtual void refreshView(); + + // RefreshView implementation + +private: + void doRefreshView(); + + void setStyleSheet(); + + QtThreadedFunctor<> m_refreshViewFunctor; + + std::shared_ptr m_widget; +}; + +# endif // QT_REFRESH_VIEW_H diff --git a/src/app/qt/view/QtViewFactory.cpp b/src/app/qt/view/QtViewFactory.cpp index caec59e8..42413763 100644 --- a/src/app/qt/view/QtViewFactory.cpp +++ b/src/app/qt/view/QtViewFactory.cpp @@ -3,6 +3,7 @@ #include "qt/view/QtCodeView.h" #include "qt/view/QtGraphView.h" #include "qt/view/QtMainView.h" +#include "qt/view/QtRefreshView.h" #include "qt/view/QtSearchView.h" #include "qt/view/QtStatusBarView.h" #include "qt/view/QtUndoRedoView.h" @@ -30,6 +31,11 @@ std::shared_ptr QtViewFactory::createGraphView(ViewLayout* viewLayout return View::create(viewLayout); } +std::shared_ptr QtViewFactory::createRefreshView(ViewLayout* viewLayout) const +{ + return View::create(viewLayout); +} + std::shared_ptr QtViewFactory::createSearchView(ViewLayout* viewLayout) const { return View::create(viewLayout); diff --git a/src/app/qt/view/QtViewFactory.h b/src/app/qt/view/QtViewFactory.h index 73fe5481..62ca74f0 100644 --- a/src/app/qt/view/QtViewFactory.h +++ b/src/app/qt/view/QtViewFactory.h @@ -12,6 +12,7 @@ public: virtual std::shared_ptr createMainView() const; virtual std::shared_ptr createCodeView(ViewLayout* viewLayout) const; virtual std::shared_ptr createGraphView(ViewLayout* viewLayout) const; + virtual std::shared_ptr createRefreshView(ViewLayout* viewLayout) const; virtual std::shared_ptr createSearchView(ViewLayout* viewLayout) const; virtual std::shared_ptr createStatusBarView(ViewLayout* viewLayout) const; virtual std::shared_ptr createUndoRedoView(ViewLayout* viewLayout) const; diff --git a/src/lib/Application.cpp b/src/lib/Application.cpp index 49aaec60..b4a1d6df 100644 --- a/src/lib/Application.cpp +++ b/src/lib/Application.cpp @@ -46,7 +46,7 @@ void Application::loadProject(const std::string& projectSettingsFilePath) m_project = Project::create(m_graphAccessProxy.get(), m_locationAccessProxy.get()); m_project->loadProjectSettings(projectSettingsFilePath); - m_project->parseCode(); + m_project->parseCode(false); } void Application::loadSource(const std::string& sourceDirectoryPath) @@ -55,13 +55,12 @@ void Application::loadSource(const std::string& sourceDirectoryPath) m_project->clearProjectSettings(); m_project->setSourceDirectoryPath(sourceDirectoryPath); - m_project->parseCode(); + m_project->parseCode(false); } void Application::reloadProject() { - m_project->clearStorage(); - m_project->parseCode(); + m_project->parseCode(true); } void Application::saveProject(const std::string& projectSettingsFilePath) diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index e1293a66..70499397 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -32,6 +32,8 @@ add_files( component/controller/GraphController.h component/controller/GraphLayouter.cpp component/controller/GraphLayouter.h + component/controller/RefreshController.cpp + component/controller/RefreshController.h component/controller/SearchController.cpp component/controller/SearchController.h component/controller/StatusBarController.cpp @@ -50,6 +52,8 @@ add_files( component/view/GraphView.h component/view/MainView.cpp component/view/MainView.h + component/view/RefreshView.cpp + component/view/RefreshView.h component/view/SearchView.cpp component/view/SearchView.h component/view/StatusBarView.cpp @@ -204,6 +208,7 @@ add_files( utility/messaging/type/MessageActivateTokenLocation.h utility/messaging/type/MessageActivateTokens.h + utility/messaging/type/MessageAutoRefreshChanged.h utility/messaging/type/MessageError.h utility/messaging/type/MessageFind.h utility/messaging/type/MessageFinishedParsing.h @@ -219,6 +224,7 @@ add_files( utility/messaging/type/MessageStatus.h utility/messaging/type/MessageRedo.h utility/messaging/type/MessageUndo.h + utility/messaging/type/MessageWindowFocus.h utility/messaging/Message.h utility/messaging/MessageBase.h diff --git a/src/lib/Project.cpp b/src/lib/Project.cpp index 4b914e77..030ce032 100644 --- a/src/lib/Project.cpp +++ b/src/lib/Project.cpp @@ -77,7 +77,7 @@ void Project::clearStorage() Token::resetNextId(); } -void Project::parseCode() +void Project::parseCode(bool refresh) { std::string sourcePath = ProjectSettings::getInstance()->getSourcePath(); if (sourcePath.size()) @@ -92,11 +92,33 @@ void Project::parseCode() std::vector headerSearchPaths = ProjectSettings::getInstance()->getHeaderSearchPaths(); headerSearchPaths.push_back(sourcePath); - CxxParser parser(m_storage.get()); + std::vector filePaths; + if (refresh) + { + filePaths = FileSystem::getFileNamesFromDirectoryUpdatedAfter(sourcePath, extensions, m_lastParseTimeString); + } + else + { + filePaths = FileSystem::getFileNamesFromDirectory(sourcePath, extensions); + } + if (!filePaths.size()) + { + MessageFinishedParsing(0, 0, m_storage->getErrorCount()).dispatch(); + return; + } + + m_lastParseTimeString = FileSystem::getTimeStringNow(); + + if (refresh) + { + m_storage->clearFileData(filePaths); + } + + CxxParser parser(m_storage.get()); clock_t time = clock(); parser.parseFiles( - FileSystem::getSourceFilesFromDirectory(sourcePath, extensions), + filePaths, ApplicationSettings::getInstance()->getHeaderSearchPaths(), headerSearchPaths ); @@ -108,7 +130,7 @@ void Project::parseCode() double parseTime = (double)(time) / CLOCKS_PER_SEC; LOG_INFO_STREAM(<< "parse time: " << parseTime); - MessageFinishedParsing(parseTime, m_storage->getErrorCount()).dispatch(); + MessageFinishedParsing(filePaths.size(), parseTime, m_storage->getErrorCount()).dispatch(); } } diff --git a/src/lib/Project.h b/src/lib/Project.h index dfe2c666..edd1989a 100644 --- a/src/lib/Project.h +++ b/src/lib/Project.h @@ -23,7 +23,7 @@ public: bool setSourceDirectoryPath(const std::string& sourceDirectoryPath); void clearStorage(); - void parseCode(); + void parseCode(bool refresh); private: Project(GraphAccessProxy* graphAccessProxy, LocationAccessProxy* locationAccessProxy); @@ -36,6 +36,8 @@ private: LocationAccessProxy* const m_locationAccessProxy; std::shared_ptr m_storage; + + std::string m_lastParseTimeString; }; #endif // PROJECT_H diff --git a/src/lib/component/ComponentFactory.cpp b/src/lib/component/ComponentFactory.cpp index 0fd8f6bc..ee632b62 100644 --- a/src/lib/component/ComponentFactory.cpp +++ b/src/lib/component/ComponentFactory.cpp @@ -3,11 +3,13 @@ #include "component/Component.h" #include "component/controller/CodeController.h" #include "component/controller/GraphController.h" +#include "component/controller/RefreshController.h" #include "component/controller/SearchController.h" #include "component/controller/StatusBarController.h" #include "component/controller/UndoRedoController.h" #include "component/view/CodeView.h" #include "component/view/GraphView.h" +#include "component/view/RefreshView.h" #include "component/view/SearchView.h" #include "component/view/StatusBarView.h" #include "component/view/UndoRedoView.h" @@ -47,6 +49,15 @@ std::shared_ptr ComponentFactory::createGraphComponent() return component; } +std::shared_ptr ComponentFactory::createRefreshComponent() +{ + std::shared_ptr view = m_viewFactory->createRefreshView(m_viewLayout); + std::shared_ptr controller = std::make_shared(); + + std::shared_ptr component = std::make_shared(view, controller); + return component; +} + std::shared_ptr ComponentFactory::createSearchComponent() { std::shared_ptr view = m_viewFactory->createSearchView(m_viewLayout); diff --git a/src/lib/component/ComponentFactory.h b/src/lib/component/ComponentFactory.h index f72fe47a..61e81333 100644 --- a/src/lib/component/ComponentFactory.h +++ b/src/lib/component/ComponentFactory.h @@ -21,6 +21,7 @@ public: std::shared_ptr createCodeComponent(); std::shared_ptr createGraphComponent(); + std::shared_ptr createRefreshComponent(); std::shared_ptr createSearchComponent(); std::shared_ptr createStatusBarComponent(); std::shared_ptr createUndoRedoComponent(); diff --git a/src/lib/component/ComponentManager.cpp b/src/lib/component/ComponentManager.cpp index a809c146..42ad9c16 100644 --- a/src/lib/component/ComponentManager.cpp +++ b/src/lib/component/ComponentManager.cpp @@ -24,14 +24,17 @@ void ComponentManager::setup() std::shared_ptr codeComponent = m_componentFactory->createCodeComponent(); m_components.push_back(codeComponent); + std::shared_ptr undoRedoComponent = m_componentFactory->createUndoRedoComponent(); + m_components.push_back(undoRedoComponent); + + std::shared_ptr refreshComponent = m_componentFactory->createRefreshComponent(); + m_components.push_back(refreshComponent); + std::shared_ptr searchComponent = m_componentFactory->createSearchComponent(); m_components.push_back(searchComponent); std::shared_ptr statusBarComponent = m_componentFactory->createStatusBarComponent(); m_components.push_back(statusBarComponent); - - std::shared_ptr undoRedoComponent = m_componentFactory->createUndoRedoComponent(); - m_components.push_back(undoRedoComponent); } ComponentManager::ComponentManager() diff --git a/src/lib/component/controller/RefreshController.cpp b/src/lib/component/controller/RefreshController.cpp new file mode 100644 index 00000000..4112fa3b --- /dev/null +++ b/src/lib/component/controller/RefreshController.cpp @@ -0,0 +1,35 @@ +#include "component/controller/RefreshController.h" + +#include "component/view/RefreshView.h" + +RefreshController::RefreshController() + : m_autoRefreshEnabled(false) +{ +} + +RefreshController::~RefreshController() +{ +} + +void RefreshController::handleMessage(MessageAutoRefreshChanged* message) +{ + m_autoRefreshEnabled = message->enabled; +} + +void RefreshController::handleMessage(MessageRefresh* message) +{ + getView()->refreshView(); +} + +void RefreshController::handleMessage(MessageWindowFocus* message) +{ + if (m_autoRefreshEnabled) + { + MessageRefresh().dispatch(); + } +} + +RefreshView* RefreshController::getView() +{ + return Controller::getView(); +} diff --git a/src/lib/component/controller/RefreshController.h b/src/lib/component/controller/RefreshController.h new file mode 100644 index 00000000..54ae9d1c --- /dev/null +++ b/src/lib/component/controller/RefreshController.h @@ -0,0 +1,32 @@ +#ifndef REFRESH_CONTROLLER_H +#define REFRESH_CONTROLLER_H + +#include "component/controller/Controller.h" +#include "utility/messaging/MessageListener.h" +#include "utility/messaging/type/MessageAutoRefreshChanged.h" +#include "utility/messaging/type/MessageRefresh.h" +#include "utility/messaging/type/MessageWindowFocus.h" + +class RefreshView; + +class RefreshController + : public Controller + , public MessageListener + , public MessageListener + , public MessageListener +{ +public: + RefreshController(); + virtual ~RefreshController(); + +private: + virtual void handleMessage(MessageAutoRefreshChanged* message); + virtual void handleMessage(MessageRefresh* message); + virtual void handleMessage(MessageWindowFocus* message); + + RefreshView* getView(); + + bool m_autoRefreshEnabled; +}; + +#endif // REFRESH_CONTROLLER_H diff --git a/src/lib/component/controller/StatusBarController.cpp b/src/lib/component/controller/StatusBarController.cpp index 8b63bc37..847cef6f 100644 --- a/src/lib/component/controller/StatusBarController.cpp +++ b/src/lib/component/controller/StatusBarController.cpp @@ -28,6 +28,7 @@ void StatusBarController::handleMessage(MessageFinishedParsing* message) { std::stringstream ss; ss << "Parsing Finished: "; + ss << message->fileCount << " files, "; ss << std::setprecision(2) << message->parseTime << " seconds, "; ss << message->errorCount << " error(s)"; diff --git a/src/lib/component/view/RefreshView.cpp b/src/lib/component/view/RefreshView.cpp new file mode 100644 index 00000000..368eef47 --- /dev/null +++ b/src/lib/component/view/RefreshView.cpp @@ -0,0 +1,22 @@ +#include "component/view/RefreshView.h" + +#include "component/controller/RefreshController.h" + +RefreshView::RefreshView(ViewLayout* viewLayout) + : View(viewLayout, Vec2i(100, 100)) +{ +} + +RefreshView::~RefreshView() +{ +} + +std::string RefreshView::getName() const +{ + return "RefreshView"; +} + +RefreshController* RefreshView::getController() +{ + return View::getController(); +} diff --git a/src/lib/component/view/RefreshView.h b/src/lib/component/view/RefreshView.h new file mode 100644 index 00000000..3c70d834 --- /dev/null +++ b/src/lib/component/view/RefreshView.h @@ -0,0 +1,21 @@ +#ifndef REFRESH_VIEW_H +#define REFRESH_VIEW_H + +#include "component/view/View.h" + +class RefreshController; + +class RefreshView + : public View +{ +public: + RefreshView(ViewLayout* viewLayout); + virtual ~RefreshView(); + + virtual std::string getName() const; + +private: + RefreshController* getController(); +}; + +#endif // REFRESH_VIEW_H diff --git a/src/lib/component/view/ViewFactory.h b/src/lib/component/view/ViewFactory.h index 96938be9..880aa385 100644 --- a/src/lib/component/view/ViewFactory.h +++ b/src/lib/component/view/ViewFactory.h @@ -6,6 +6,7 @@ class CodeView; class GraphView; class MainView; +class RefreshView; class SearchView; class StatusBarView; class UndoRedoView; @@ -20,6 +21,7 @@ public: virtual std::shared_ptr createMainView() const = 0; virtual std::shared_ptr createCodeView(ViewLayout* viewLayout) const = 0; virtual std::shared_ptr createGraphView(ViewLayout* viewLayout) const = 0; + virtual std::shared_ptr createRefreshView(ViewLayout* viewLayout) const = 0; virtual std::shared_ptr createSearchView(ViewLayout* viewLayout) const = 0; virtual std::shared_ptr createStatusBarView(ViewLayout* viewLayout) const = 0; virtual std::shared_ptr createUndoRedoView(ViewLayout* viewLayout) const = 0; diff --git a/src/lib/data/Storage.cpp b/src/lib/data/Storage.cpp index 8e01c7e5..69395184 100644 --- a/src/lib/data/Storage.cpp +++ b/src/lib/data/Storage.cpp @@ -1,7 +1,5 @@ #include "data/Storage.h" -#include - #include "utility/logging/logging.h" #include "utility/utilityString.h" @@ -38,6 +36,66 @@ void Storage::clear() m_graph.clear(); m_locationCollection.clear(); m_tokenIndex.clear(); + + m_errorMessages.clear(); + m_errorLocationCollection.clear(); +} + +void Storage::clearFileData(const std::vector& filePaths) +{ + for (const std::string& filePath : filePaths) + { + TokenLocationFile* errorFile = m_errorLocationCollection.findTokenLocationFileByPath(filePath); + if (errorFile) + { + m_errorLocationCollection.removeTokenLocationFile(errorFile); + } + + TokenLocationFile* file = m_locationCollection.findTokenLocationFileByPath(filePath); + if (!file) + { + continue; + } + + file->forEachTokenLocation( + [&](TokenLocation* location) + { + if (location->isEndTokenLocation()) + { + return; + } + + Token* token = m_graph.getTokenById(location->getTokenId()); + if (!token) + { + return; + } + + token->removeLocationId(location->getId()); + if (token->getLocationIds().size()) + { + return; + } + + if (token->isEdge()) + { + Edge* edge = dynamic_cast(token); + Node* from = edge->getFrom(); + Node* to = edge->getTo(); + + m_graph.removeEdge(edge); + removeNodeIfUnreferenced(from); + removeNodeIfUnreferenced(to); + } + else + { + removeNodeIfUnreferenced(dynamic_cast(token)); + } + } + ); + + m_locationCollection.removeTokenLocationFile(file); + } } void Storage::logGraph() const @@ -52,7 +110,7 @@ void Storage::logLocations() const size_t Storage::getErrorCount() const { - return m_errorMessages.size(); + return m_errorLocationCollection.getTokenLocationCount(); } void Storage::onError(const ParseLocation& location, const std::string& message) @@ -64,15 +122,38 @@ void Storage::onError(const ParseLocation& location, const std::string& message) return; } - Id errorId = m_errorMessages.size(); + bool duplicate = false; + std::string filePath = location.filePath; + TokenLocationFile* file = m_errorLocationCollection.findTokenLocationFileByPath(filePath); - TokenLocation* loc = m_errorLocationCollection.addTokenLocation( - errorId, location.filePath, - location.startLineNumber, location.startColumnNumber, - location.endLineNumber, location.endColumnNumber - ); + if (file) + { + file->forEachTokenLocation( + [&](TokenLocation* loc) + { + if (loc->isStartTokenLocation() && + loc->getLineNumber() == location.startLineNumber && + loc->getColumnNumber() == location.startColumnNumber && + m_errorMessages[loc->getTokenId()] == message) + { + duplicate = true; + } + } + ); + } - m_errorMessages.push_back(message); + if (!duplicate) + { + Id errorId = m_errorMessages.size(); + + TokenLocation* loc = m_errorLocationCollection.addTokenLocation( + errorId, filePath, + location.startLineNumber, location.startColumnNumber, + location.endLineNumber, location.endColumnNumber + ); + + m_errorMessages.push_back(message); + } } Id Storage::onTypedefParsed( @@ -1030,6 +1111,20 @@ bool Storage::getSubQuerySearchResults( return true; } +void Storage::removeNodeIfUnreferenced(Node* node) +{ + Id tokenId = node->getId(); + SearchNode* searchNode = m_tokenIndex.getNode(node->getTokenComponentName()->getSearchNode()); + + bool removed = m_graph.removeNodeIfUnreferencedRecursive(node); + + if (removed && searchNode) + { + searchNode->removeTokenId(tokenId); + m_tokenIndex.removeNodeIfUnreferencedRecursive(searchNode); + } +} + void Storage::log(std::string type, std::string str, const ParseLocation& location) const { LOG_INFO_STREAM( diff --git a/src/lib/data/Storage.h b/src/lib/data/Storage.h index 9cfbad6c..7314c202 100644 --- a/src/lib/data/Storage.h +++ b/src/lib/data/Storage.h @@ -23,6 +23,7 @@ public: virtual ~Storage(); void clear(); + void clearFileData(const std::vector& filePaths); void logGraph() const; void logLocations() const; @@ -125,7 +126,6 @@ protected: const SearchIndex& getSearchIndex() const; private: - Node* addNodeHierarchy(Node::NodeType type, std::vector nameHierarchy); Node* addNodeHierarchyWithDistinctSignature(Node::NodeType type, const ParseFunction& function); @@ -141,6 +141,8 @@ private: bool getSubQuerySearchResults( const std::string& query, const std::string& word, SearchResults* results) const; + void removeNodeIfUnreferenced(Node* node); + void log(std::string type, std::string str, const ParseLocation& location) const; StorageGraph m_graph; diff --git a/src/lib/data/graph/Graph.cpp b/src/lib/data/graph/Graph.cpp index 213657d2..50971547 100644 --- a/src/lib/data/graph/Graph.cpp +++ b/src/lib/data/graph/Graph.cpp @@ -114,13 +114,16 @@ void Graph::removeNode(Node* node) return; } - node->forEachEdgeOfType(Edge::EDGE_MEMBER, [this, node](Edge* e) - { - if (node == e->getFrom()) + node->forEachEdgeOfType( + Edge::EDGE_MEMBER, + [this, node](Edge* e) { - this->removeNode(e->getTo()); + if (node == e->getFrom()) + { + this->removeNode(e->getTo()); + } } - }); + ); node->forEachEdge( [this](Edge* e) @@ -131,7 +134,7 @@ void Graph::removeNode(Node* node) if (node->getEdges().size()) { - LOG_ERROR("Node has still edges."); + LOG_ERROR("Node still has edges."); } m_nodes.erase(it); @@ -154,6 +157,25 @@ void Graph::removeEdge(Edge* edge) m_edges.erase(it); } +bool Graph::removeNodeIfUnreferencedRecursive(Node* node) +{ + if (!node->hasReferences()) + { + Node* parent = node->getParentNode(); + + removeNode(node); + + if (parent) + { + removeNodeIfUnreferencedRecursive(parent); + } + + return true; + } + + return false; +} + Node* Graph::findNode(std::function func) const { std::map>::const_iterator it = find_if(m_nodes.begin(), m_nodes.end(), diff --git a/src/lib/data/graph/Graph.h b/src/lib/data/graph/Graph.h index d9ec378a..62581f8a 100644 --- a/src/lib/data/graph/Graph.h +++ b/src/lib/data/graph/Graph.h @@ -40,6 +40,7 @@ public: void removeNode(Node* node); void removeEdge(Edge* edge); + bool removeNodeIfUnreferencedRecursive(Node* node); Node* findNode(std::function func) const; Edge* findEdge(std::function func) const; diff --git a/src/lib/data/graph/Node.cpp b/src/lib/data/graph/Node.cpp index cd14e6cc..d8c106c1 100644 --- a/src/lib/data/graph/Node.cpp +++ b/src/lib/data/graph/Node.cpp @@ -203,6 +203,37 @@ void Node::forEachChildNode(std::function func) const ); } +bool Node::hasReferences() const +{ + if (getLocationIds().size() > 0) + { + return true; + } + + bool hasChildrenWithReferences = false; + size_t childNodeCount = 0; + + forEachEdgeOfType( + Edge::EDGE_MEMBER, + [&](Edge* edge) + { + childNodeCount++; + + if (!hasChildrenWithReferences && edge->getTo() != this && edge->getTo()->hasReferences()) + { + hasChildrenWithReferences = true; + } + } + ); + + if (hasChildrenWithReferences || getEdges().size() > childNodeCount) + { + return true; + } + + return false; +} + bool Node::isNode() const { return true; diff --git a/src/lib/data/graph/Node.h b/src/lib/data/graph/Node.h index fb868061..7a5020c4 100644 --- a/src/lib/data/graph/Node.h +++ b/src/lib/data/graph/Node.h @@ -68,6 +68,8 @@ public: void forEachEdgeOfType(Edge::EdgeType type, std::function func) const; void forEachChildNode(std::function func) const; + bool hasReferences() const; + // Token implementation. virtual bool isNode() const; virtual bool isEdge() const; diff --git a/src/lib/data/location/TokenLocationCollection.cpp b/src/lib/data/location/TokenLocationCollection.cpp index df2de11f..6fc12f20 100644 --- a/src/lib/data/location/TokenLocationCollection.cpp +++ b/src/lib/data/location/TokenLocationCollection.cpp @@ -1,9 +1,13 @@ #include "data/location/TokenLocationCollection.h" +#include + +#include "utility/FileSystem.h" +#include "utility/logging/logging.h" + #include "data/location/TokenLocation.h" #include "data/location/TokenLocationFile.h" #include "data/location/TokenLocationLine.h" -#include "utility/logging/logging.h" TokenLocationCollection::TokenLocationCollection() { @@ -85,7 +89,13 @@ TokenLocation* TokenLocationCollection::findTokenLocationById(Id id) const TokenLocationFile* TokenLocationCollection::findTokenLocationFileByPath(const std::string& filePath) const { - std::map >::const_iterator it = m_files.find(filePath); + std::map>::const_iterator it = + find_if(m_files.begin(), m_files.end(), + [&](const std::pair>& p) + { + return FileSystem::equivalent(p.first, filePath); + } + ); if (it != m_files.end()) { @@ -119,6 +129,18 @@ void TokenLocationCollection::forEachTokenLocation(std::functionforEachTokenLocation( + [&](TokenLocation* location) + { + m_locations.erase(location->getId()); + } + ); + + m_files.erase(file->getFilePath()); +} + TokenLocation* TokenLocationCollection::addTokenLocationAsPlainCopy(const TokenLocation* location) { const std::string& filePath = location->getTokenLocationLine()->getTokenLocationFile()->getFilePath(); diff --git a/src/lib/data/location/TokenLocationCollection.h b/src/lib/data/location/TokenLocationCollection.h index a9daeedd..c8f8081c 100644 --- a/src/lib/data/location/TokenLocationCollection.h +++ b/src/lib/data/location/TokenLocationCollection.h @@ -41,6 +41,8 @@ public: void forEachTokenLocationLine(std::function func) const; void forEachTokenLocation(std::function func) const; + void removeTokenLocationFile(TokenLocationFile* file); + TokenLocation* addTokenLocationAsPlainCopy(const TokenLocation* location); void clear(); diff --git a/src/lib/data/search/SearchIndex.cpp b/src/lib/data/search/SearchIndex.cpp index 5fa7f4b4..8d79d2b7 100644 --- a/src/lib/data/search/SearchIndex.cpp +++ b/src/lib/data/search/SearchIndex.cpp @@ -3,6 +3,8 @@ #include #include +#include "utility/logging/logging.h" + #include "data/search/SearchMatch.h" std::vector SearchIndex::getMatches( @@ -34,6 +36,11 @@ void SearchIndex::clear() m_root.m_nodes.clear(); } +size_t SearchIndex::getNodeCount() const +{ + return m_root.getNodeCount() - 1; +} + Id SearchIndex::getWordId(const std::string& word) { return m_dictionary.getWordId(word); @@ -71,6 +78,50 @@ SearchNode* SearchIndex::getNode(const std::string& fullName) const return nullptr; } +SearchNode* SearchIndex::getNode(const SearchNode* searchNode) const +{ + std::deque nameIds = searchNode->getNameIdsRecursive(); + + if (nameIds.size()) + { + return m_root.getNodeRecursive(&nameIds).get(); + } + + return nullptr; +} + +void SearchIndex::removeNode(SearchNode* searchNode) +{ + SearchNode* parent = searchNode->getParent(); + + if (!parent) + { + LOG_ERROR_STREAM(<< "SearchNode to be removed has no parent: " << searchNode->getFullName()); + return; + } + + parent->removeSearchNode(searchNode); +} + +bool SearchIndex::removeNodeIfUnreferencedRecursive(SearchNode* searchNode) +{ + if (!searchNode->hasTokenIdsRecursive()) + { + SearchNode* parent = searchNode->getParent(); + + removeNode(searchNode); + + if (parent && parent != &m_root) + { + removeNodeIfUnreferencedRecursive(parent); + } + + return true; + } + + return false; +} + SearchResults SearchIndex::runFuzzySearch(const std::string& query) const { return m_root.runFuzzySearch(query); diff --git a/src/lib/data/search/SearchIndex.h b/src/lib/data/search/SearchIndex.h index 2e41300a..79526d9e 100644 --- a/src/lib/data/search/SearchIndex.h +++ b/src/lib/data/search/SearchIndex.h @@ -20,11 +20,17 @@ public: void clear(); + size_t getNodeCount() const; + Id getWordId(const std::string& word); const std::string& getWord(Id wordId) const; SearchNode* addNode(std::vector nameHierarchy); SearchNode* getNode(const std::string& fullName) const; + SearchNode* getNode(const SearchNode* searchNode) const; + + void removeNode(SearchNode* searchNode); + bool removeNodeIfUnreferencedRecursive(SearchNode* searchNode); SearchResults runFuzzySearch(const std::string& query) const; std::vector runFuzzySearchAndGetMatches(const std::string& query) const; diff --git a/src/lib/data/search/SearchNode.cpp b/src/lib/data/search/SearchNode.cpp index b5ccf14a..c5802f90 100644 --- a/src/lib/data/search/SearchNode.cpp +++ b/src/lib/data/search/SearchNode.cpp @@ -19,6 +19,18 @@ SearchNode::~SearchNode() { } +size_t SearchNode::getNodeCount() const +{ + size_t count = 1; + + for (std::shared_ptr n: m_nodes) + { + count += n->getNodeCount(); + } + + return count; +} + const std::string& SearchNode::getName() const { return m_name; @@ -28,7 +40,7 @@ std::vector SearchNode::getNameHierarchy() const { std::vector nameHierarchy; const SearchNode* parent = getParent(); - if (parent) + if (parent && parent->m_nameId) { nameHierarchy = parent->getNameHierarchy(); } @@ -53,6 +65,22 @@ Id SearchNode::getNameId() const return m_nameId; } +std::deque SearchNode::getNameIdsRecursive() const +{ + std::deque ids; + + ids.push_front(m_nameId); + + SearchNode* parent = m_parent; + while (parent && parent->m_nameId) + { + ids.push_front(parent->getNameId()); + parent = parent->getParent(); + } + + return ids; +} + Id SearchNode::getFirstTokenId() const { if (m_tokenIds.size()) @@ -68,14 +96,37 @@ const std::set& SearchNode::getTokenIds() const return m_tokenIds; } +bool SearchNode::hasTokenIdsRecursive() const +{ + if (m_tokenIds.size()) + { + return true; + } + + for (std::shared_ptr n: m_nodes) + { + if (n->hasTokenIdsRecursive()) + { + return true; + } + } + + return false; +} + void SearchNode::addTokenId(Id tokenId) { m_tokenIds.insert(tokenId); } +void SearchNode::removeTokenId(Id tokenId) +{ + m_tokenIds.erase(tokenId); +} + SearchNode* SearchNode::getParent() const { - if (m_parent && m_parent->m_nameId) + if (m_parent) { return m_parent; } @@ -183,6 +234,18 @@ std::shared_ptr SearchNode::getNodeRecursive(std::deque* nameIds return nullptr; } +void SearchNode::removeSearchNode(SearchNode* node) +{ + for (std::set>::iterator it = m_nodes.begin(); it != m_nodes.end(); it++) + { + if ((*it)->m_nameId == node->m_nameId) + { + m_nodes.erase(it); + return; + } + } +} + SearchMatch SearchNode::fuzzyMatchData(const std::string& query, const SearchNode* parent) const { SearchMatch data; diff --git a/src/lib/data/search/SearchNode.h b/src/lib/data/search/SearchNode.h index 1dad5d89..44b85df7 100644 --- a/src/lib/data/search/SearchNode.h +++ b/src/lib/data/search/SearchNode.h @@ -22,15 +22,21 @@ public: SearchNode(SearchNode* parent, const std::string& name, Id nameId); ~SearchNode(); + size_t getNodeCount() const; + const std::string& getName() const; std::vector getNameHierarchy() const; std::string getFullName() const; Id getNameId() const; + std::deque getNameIdsRecursive() const; Id getFirstTokenId() const; const std::set& getTokenIds() const; + bool hasTokenIdsRecursive() const; + void addTokenId(Id tokenId); + void removeTokenId(Id tokenId); SearchNode* getParent() const; std::deque getParentsWithoutTokenId(); @@ -46,11 +52,12 @@ private: typedef std::multimap FuzzyMap; typedef FuzzyMap::const_iterator FuzzyMapIterator; - // Accessed by SearchIndex std::shared_ptr addNodeRecursive(std::deque* nameIds, const Dictionary& dictionary); std::shared_ptr getNodeRecursive(std::deque* nameIds) const; + void removeSearchNode(SearchNode* node); + SearchMatch fuzzyMatchData(const std::string& query, const SearchNode* parent) const; friend class SearchIndex; diff --git a/src/lib/utility/FileSystem.cpp b/src/lib/utility/FileSystem.cpp index 9aee8a21..a9aebc7a 100644 --- a/src/lib/utility/FileSystem.cpp +++ b/src/lib/utility/FileSystem.cpp @@ -1,12 +1,12 @@ #include "utility/FileSystem.h" +#include "boost/date_time.hpp" #include "boost/filesystem.hpp" -#include "utility/logging/logging.h" - -std::vector FileSystem::getSourceFilesFromDirectory( +std::vector FileSystem::getFileNamesFromDirectory( const std::string& path, const std::vector& extensions -){ +) +{ std::vector files; if (boost::filesystem::is_directory(path)) @@ -25,25 +25,37 @@ std::vector FileSystem::getSourceFilesFromDirectory( return files; } -std::vector FileSystem::getFileNamesFromDirectory( - const std::string& path, const std::vector& extensions -) -{ - return getSourceFilesFromDirectory(path, extensions); -} +std::vector FileSystem::getFileNamesFromDirectoryUpdatedAfter( + const std::string& path, const std::vector& extensions, const std::string& timeString +){ + std::vector files; -bool FileSystem::isValidExtension(const std::string& filepath, const std::vector& extensions) -{ - boost::filesystem::path path(filepath); + const boost::posix_time::ptime time = boost::posix_time::from_iso_string(timeString); - for (std::string extension : extensions) + if (boost::filesystem::is_directory(path)) { - if (path.extension() == extension) + boost::filesystem::recursive_directory_iterator it(path); + boost::filesystem::recursive_directory_iterator endit; + while (it != endit) { - return true; + if (boost::filesystem::is_regular_file(*it) && isValidExtension(it->path().string(), extensions)) + { + std::time_t t = boost::filesystem::last_write_time(*it); + boost::posix_time::ptime lastWriteTime = boost::posix_time::from_time_t(t); + if (lastWriteTime >= time) + { + files.push_back(it->path().generic_string()); + } + } + ++it; } } - return false; + return files; +} + +std::string FileSystem::getTimeStringNow() +{ + return boost::posix_time::to_iso_string(boost::posix_time::second_clock::universal_time()); } bool FileSystem::exists(const std::string& path) @@ -64,4 +76,33 @@ std::string FileSystem::extension(const std::string& path) std::string FileSystem::filePathWithoutExtension(const std::string& path) { return boost::filesystem::path(path).replace_extension().generic_string(); -} \ No newline at end of file +} + +std::string FileSystem::absoluteFilePath(const std::string& path) +{ + return boost::filesystem::absolute(boost::filesystem::path(path)).generic_string(); +} + +bool FileSystem::equivalent(const std::string& pathA, const std::string& pathB) +{ + if (exists(pathA) && exists(pathB)) + { + return boost::filesystem::equivalent(boost::filesystem::path(pathA), boost::filesystem::path(pathB)); + } + + return boost::filesystem::path(pathA).compare(boost::filesystem::path(pathB)) == 0; +} + +bool FileSystem::isValidExtension(const std::string& filepath, const std::vector& extensions) +{ + boost::filesystem::path path(filepath); + + for (std::string extension : extensions) + { + if (path.extension() == extension) + { + return true; + } + } + return false; +} diff --git a/src/lib/utility/FileSystem.h b/src/lib/utility/FileSystem.h index d0d20d11..9d1cf468 100644 --- a/src/lib/utility/FileSystem.h +++ b/src/lib/utility/FileSystem.h @@ -7,18 +7,20 @@ class FileSystem { public: - static std::vector getSourceFilesFromDirectory( // TODO: Replace this with getFileNamesFromDirectory. - const std::string& path, const std::vector& extensions - ); - static std::vector getFileNamesFromDirectory( - const std::string& path, const std::vector& extensions - ); + const std::string& path, const std::vector& extensions); + static std::vector getFileNamesFromDirectoryUpdatedAfter( + const std::string& path, const std::vector& extensions, const std::string& timeString); + + static std::string getTimeStringNow(); static bool exists(const std::string& path); static std::string fileName(const std::string& path); static std::string extension(const std::string& path); static std::string filePathWithoutExtension(const std::string& path); + static std::string absoluteFilePath(const std::string& path); + + static bool equivalent(const std::string& pathA, const std::string& pathB); private: static bool isValidExtension(const std::string& filepath, const std::vector& extensions); diff --git a/src/lib/utility/messaging/type/MessageAutoRefreshChanged.h b/src/lib/utility/messaging/type/MessageAutoRefreshChanged.h new file mode 100644 index 00000000..004d728e --- /dev/null +++ b/src/lib/utility/messaging/type/MessageAutoRefreshChanged.h @@ -0,0 +1,22 @@ +#ifndef MESSAGE_AUTO_REFRESH_CHANGED_H +#define MESSAGE_AUTO_REFRESH_CHANGED_H + +#include "utility/messaging/Message.h" + +class MessageAutoRefreshChanged: public Message +{ +public: + MessageAutoRefreshChanged(bool enabled) + : enabled(enabled) + { + } + + static const std::string getStaticType() + { + return "MessageAutoRefreshChanged"; + } + + bool enabled; +}; + +#endif // MESSAGE_AUTO_REFRESH_CHANGED_H diff --git a/src/lib/utility/messaging/type/MessageFinishedParsing.h b/src/lib/utility/messaging/type/MessageFinishedParsing.h index 0eca55c3..a8f80c6d 100644 --- a/src/lib/utility/messaging/type/MessageFinishedParsing.h +++ b/src/lib/utility/messaging/type/MessageFinishedParsing.h @@ -6,8 +6,9 @@ class MessageFinishedParsing: public Message { public: - MessageFinishedParsing(float parseTime, size_t errorCount) - : parseTime(parseTime) + MessageFinishedParsing(size_t fileCount, float parseTime, size_t errorCount) + : fileCount(fileCount) + , parseTime(parseTime) , errorCount(errorCount) { } @@ -17,6 +18,7 @@ public: return "MessageFinishedParsing"; } + size_t fileCount; float parseTime; size_t errorCount; }; diff --git a/src/lib/utility/messaging/type/MessageRefresh.h b/src/lib/utility/messaging/type/MessageRefresh.h index 020bda4b..4946d8d5 100644 --- a/src/lib/utility/messaging/type/MessageRefresh.h +++ b/src/lib/utility/messaging/type/MessageRefresh.h @@ -2,7 +2,6 @@ #define MESSAGE_REFRESH_H #include "utility/messaging/Message.h" -#include "utility/types.h" class MessageRefresh: public Message { diff --git a/src/lib/utility/messaging/type/MessageWindowFocus.h b/src/lib/utility/messaging/type/MessageWindowFocus.h new file mode 100644 index 00000000..2f43c6c2 --- /dev/null +++ b/src/lib/utility/messaging/type/MessageWindowFocus.h @@ -0,0 +1,19 @@ +#ifndef MESSAGE_WINDOW_FOCUS_H +#define MESSAGE_WINDOW_FOCUS_H + +#include "utility/messaging/Message.h" + +class MessageWindowFocus: public Message +{ +public: + MessageWindowFocus() + { + } + + static const std::string getStaticType() + { + return "MessageWindowFocus"; + } +}; + +#endif // MESSAGE_WINDOW_FOCUS_H diff --git a/src/test/FileSystemTestSuite.h b/src/test/FileSystemTestSuite.h index 05c5fb6d..4fe22477 100644 --- a/src/test/FileSystemTestSuite.h +++ b/src/test/FileSystemTestSuite.h @@ -1,6 +1,7 @@ #include "cxxtest/TestSuite.h" #include +#include #include #include @@ -15,7 +16,7 @@ public: extensions.push_back(".cpp"); std::vector cppFiles = - FileSystem::getSourceFilesFromDirectory("data/FileSystemTestSuite", extensions); + FileSystem::getFileNamesFromDirectory("data/FileSystemTestSuite", extensions); TS_ASSERT_EQUALS(cppFiles.size(), 2); TS_ASSERT(isInVector(cppFiles, "data/FileSystemTestSuite/main.cpp")); @@ -28,7 +29,7 @@ public: extensions.push_back(".h"); std::vector headerFiles = - FileSystem::getSourceFilesFromDirectory("data/FileSystemTestSuite", extensions); + FileSystem::getFileNamesFromDirectory("data/FileSystemTestSuite", extensions); TS_ASSERT_EQUALS(headerFiles.size(), 2); TS_ASSERT(isInVector(headerFiles, "data/FileSystemTestSuite/tictactoe.h")); @@ -43,11 +44,33 @@ public: extensions.push_back(".cpp"); std::vector sourceFiles = - FileSystem::getSourceFilesFromDirectory("data/FileSystemTestSuite", extensions); + FileSystem::getFileNamesFromDirectory("data/FileSystemTestSuite", extensions); TS_ASSERT_EQUALS(sourceFiles.size(), 5); } + void test_find_updated_source_files() + { + std::string timeString = FileSystem::getTimeStringNow(); + + std::fstream fileStream; + fileStream.open("./data/FileSystemTestSuite/update.c"); + fileStream << "update"; + fileStream.close(); + + std::vector extensions; + extensions.push_back(".h"); + extensions.push_back(".c"); + extensions.push_back(".hpp"); + extensions.push_back(".cpp"); + + std::vector sourceFiles = + FileSystem::getFileNamesFromDirectoryUpdatedAfter("data/FileSystemTestSuite", extensions, timeString); + + TS_ASSERT_EQUALS(sourceFiles.size(), 1); + TS_ASSERT_EQUALS(sourceFiles[0], "data/FileSystemTestSuite/update.c"); + } + void test_filesystem_finds_existing_files() { TS_ASSERT(FileSystem::exists("data/FileSystemTestSuite")); diff --git a/src/test/GraphTestSuite.h b/src/test/GraphTestSuite.h index f4e79a72..9e964fe2 100644 --- a/src/test/GraphTestSuite.h +++ b/src/test/GraphTestSuite.h @@ -245,6 +245,21 @@ public: TS_ASSERT_EQUALS(children[1], &c); } + void test_node_has_references() + { + Node a(Node::NODE_UNDEFINED, std::make_shared(utility::splitToVector("A", "::"))); + Node b(Node::NODE_UNDEFINED, std::make_shared(utility::splitToVector("B", "::"))); + Node c(Node::NODE_UNDEFINED, std::make_shared(utility::splitToVector("C", "::"))); + Node d(Node::NODE_UNDEFINED, std::make_shared(utility::splitToVector("D", "::"))); + Edge e(Edge::EDGE_MEMBER, &a, &b); + Edge e2(Edge::EDGE_MEMBER, &a, &c); + Edge e3(Edge::EDGE_USAGE, &c, &d); + + TS_ASSERT(a.hasReferences()); + TS_ASSERT(!b.hasReferences()); + TS_ASSERT(c.hasReferences()); + } + void test_graph_saves_nodes() { Graph graph; @@ -294,6 +309,58 @@ public: TS_ASSERT_EQUALS(Edge::EDGE_CALL, graph.getEdgeById(e.getId())->getType()); } + void test_graph_removes_nodes() + { + Graph graph; + Node a(Node::NODE_UNDEFINED, std::make_shared(utility::splitToVector("A", "::"))); + Node b(Node::NODE_UNDEFINED, std::make_shared(utility::splitToVector("B", "::"))); + + graph.addNode(&a); + graph.addNode(&b); + + TS_ASSERT_EQUALS(2, graph.getNodeCount()); + TS_ASSERT_EQUALS(0, graph.getEdgeCount()); + + graph.removeNode(graph.getNodeById(a.getId())); + + TS_ASSERT_EQUALS(1, graph.getNodeCount()); + } + + void test_graph_removes_unreferenced_nodes() + { + Graph graph; + Node a(Node::NODE_UNDEFINED, std::make_shared(utility::splitToVector("A", "::"))); + Node b(Node::NODE_UNDEFINED, std::make_shared(utility::splitToVector("B", "::"))); + Node c(Node::NODE_UNDEFINED, std::make_shared(utility::splitToVector("C", "::"))); + Node d(Node::NODE_UNDEFINED, std::make_shared(utility::splitToVector("D", "::"))); + Node e(Node::NODE_UNDEFINED, std::make_shared(utility::splitToVector("E", "::"))); + + Edge e1(Edge::EDGE_MEMBER, &a, &b); + Edge e2(Edge::EDGE_MEMBER, &a, &c); + Edge e3(Edge::EDGE_USAGE, &c, &d); + Edge e4(Edge::EDGE_MEMBER, &b, &e); + + graph.addNode(&a); + graph.addNode(&b); + graph.addNode(&c); + graph.addNode(&d); + graph.addNode(&e); + + graph.addEdge(&e1); + graph.addEdge(&e2); + graph.addEdge(&e3); + graph.addEdge(&e4); + + TS_ASSERT_EQUALS(5, graph.getNodeCount()); + TS_ASSERT_EQUALS(4, graph.getEdgeCount()); + + TS_ASSERT(!graph.removeNodeIfUnreferencedRecursive(graph.getNodeById(a.getId()))); + TS_ASSERT(graph.removeNodeIfUnreferencedRecursive(graph.getNodeById(b.getId()))); + + TS_ASSERT_EQUALS(3, graph.getNodeCount()); + TS_ASSERT_EQUALS(2, graph.getEdgeCount()); + } + private: class TestToken: public Token { diff --git a/src/test/SearchIndexTestSuite.h b/src/test/SearchIndexTestSuite.h index d4582c50..a7da8400 100644 --- a/src/test/SearchIndexTestSuite.h +++ b/src/test/SearchIndexTestSuite.h @@ -17,7 +17,8 @@ public: TS_ASSERT(node->getNameId()); TS_ASSERT(!node->getFirstTokenId()); - TS_ASSERT(!node->getParent()); + TS_ASSERT(node->getParent()); + TS_ASSERT(!node->getParent()->getNameId()); } void test_get_node() @@ -32,7 +33,8 @@ public: TS_ASSERT(node->getNameId()); TS_ASSERT(!node->getFirstTokenId()); - TS_ASSERT(!node->getParent()); + TS_ASSERT(node->getParent()); + TS_ASSERT(!node->getParent()->getNameId()); node = index.getNode("math"); TS_ASSERT(!node); @@ -72,6 +74,55 @@ public: TS_ASSERT_EQUALS(node1->getParent(), node2->getParent()); } + void test_remove_nodes() + { + SearchIndex index; + index.addNode(utility::splitToVector("util::math::pow", "::")); + index.addNode(utility::splitToVector("util::math::floor", "::")); + + index.removeNode(index.getNode("util::math::pow")); + + TS_ASSERT(!index.getNode("util::math::pow")); + TS_ASSERT(index.getNode("util::math::floor")); + TS_ASSERT(index.getNode("util::math")); + + index.removeNode(index.getNode("util::math")); + + TS_ASSERT(!index.getNode("util::math::floor")); + TS_ASSERT(!index.getNode("util::math")); + TS_ASSERT(index.getNode("util")); + } + + void test_remove_unreferenced_nodes() + { + SearchIndex index; + SearchNode* node1 = index.addNode(utility::splitToVector("util::math::pow", "::")); + SearchNode* node2 = index.addNode(utility::splitToVector("util::math::floor", "::")); + + node1->addTokenId(1); + node2->addTokenId(2); + + TS_ASSERT(index.getNode("util")->hasTokenIdsRecursive()); + + TS_ASSERT(!index.removeNodeIfUnreferencedRecursive(index.getNode("util"))); + TS_ASSERT(!index.removeNodeIfUnreferencedRecursive(index.getNode("util::math"))); + TS_ASSERT(!index.removeNodeIfUnreferencedRecursive(index.getNode("util::math::pow"))); + + node1->removeTokenId(1); + + TS_ASSERT(index.removeNodeIfUnreferencedRecursive(index.getNode("util::math::pow"))); + TS_ASSERT(!index.getNode("util::math::pow")); + TS_ASSERT(index.getNode("util::math")); + TS_ASSERT(index.getNode("util")); + + node2->removeTokenId(2); + + TS_ASSERT(index.removeNodeIfUnreferencedRecursive(index.getNode("util::math"))); + + TS_ASSERT(!index.getNode("util::math")); + TS_ASSERT(!index.getNode("util")); + } + void test_clear() { SearchIndex index; @@ -83,6 +134,7 @@ public: index.clear(); TS_ASSERT(!index.getNode("math")); + TS_ASSERT(!index.getNode("string")); } void test_fuzzy_matching() diff --git a/src/test/StorageTestSuite.h b/src/test/StorageTestSuite.h index 09c79d3c..258bc4d7 100644 --- a/src/test/StorageTestSuite.h +++ b/src/test/StorageTestSuite.h @@ -15,6 +15,11 @@ class StorageTestSuite: public CxxTest::TestSuite { public: + void setUp() + { + m_filePath = "file.cpp"; + } + void test_storage_saves_typedef() { TestStorage storage; @@ -536,6 +541,117 @@ public: TS_ASSERT(isValidLocation(locations[0], 0)); } + void test_storage_clears_single_file_data_of_single_file_storage() + { + TestStorage storage; + storage.onFunctionParsed( + validLocation(), ParseFunction(typeUsage("bool"), utility::splitToVector("isTrue", "::"), + parameters("char")), validLocation() + ); + + TS_ASSERT_EQUALS(storage.graph().getNodeCount(), 3); + TS_ASSERT_EQUALS(storage.graph().getEdgeCount(), 2); + TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 4); + TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 3); + + storage.clearFileData(std::vector(1, m_filePath)); + + TS_ASSERT_EQUALS(storage.graph().getNodeCount(), 0); + TS_ASSERT_EQUALS(storage.graph().getEdgeCount(), 0); + TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 0); + TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 0); + } + + void test_storage_clears_unreferenced_single_file_data_of_multi_file_storage() + { + m_filePath = "file.h"; + + TestStorage storage; + + ParseFunction isTrue = ParseFunction(typeUsage("bool"), utility::splitToVector("isTrue", "::"), parameters("char")); + storage.onFunctionParsed(validLocation(), isTrue, validLocation()); + + m_filePath = "file.cpp"; + + ParseFunction main = ParseFunction(typeUsage("int"), utility::splitToVector("main", "::"), parameters("void")); + storage.onFunctionParsed(validLocation(), main, validLocation()); + + storage.onCallParsed(validLocation(), main, isTrue); + + TS_ASSERT_EQUALS(storage.graph().getNodeCount(), 6); + TS_ASSERT_EQUALS(storage.graph().getEdgeCount(), 5); + TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); + TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 6); + + storage.clearFileData(std::vector(1, "file.cpp")); + + TS_ASSERT_EQUALS(storage.graph().getNodeCount(), 3); + TS_ASSERT_EQUALS(storage.graph().getEdgeCount(), 2); + TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 4); + TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 3); + } + + void test_storage_clears_referenced_single_file_data_of_multi_file_storage() + { + m_filePath = "file.h"; + + TestStorage storage; + + ParseFunction isTrue = ParseFunction(typeUsage("bool"), utility::splitToVector("isTrue", "::"), parameters("void")); + storage.onFunctionParsed(validLocation(), isTrue, validLocation()); + + m_filePath = "file.cpp"; + + ParseFunction main = ParseFunction(typeUsage("int"), utility::splitToVector("main", "::"), parameters("void")); + storage.onFunctionParsed(validLocation(), main, validLocation()); + + storage.onCallParsed(validLocation(), main, isTrue); + + TS_ASSERT_EQUALS(storage.graph().getNodeCount(), 5); + TS_ASSERT_EQUALS(storage.graph().getEdgeCount(), 5); + TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); + TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 5); + + storage.clearFileData(std::vector(1, "file.h")); + + TS_ASSERT_EQUALS(storage.graph().getNodeCount(), 4); + TS_ASSERT_EQUALS(storage.graph().getEdgeCount(), 3); + TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 5); + TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 4); + } + + void test_storage_clears_multi_file_data_of_multi_file_storage() + { + m_filePath = "file.h"; + + TestStorage storage; + + ParseFunction isTrue = ParseFunction(typeUsage("bool"), utility::splitToVector("isTrue", "::"), parameters("void")); + storage.onFunctionParsed(validLocation(), isTrue, validLocation()); + + m_filePath = "file.cpp"; + + ParseFunction main = ParseFunction(typeUsage("int"), utility::splitToVector("main", "::"), parameters("void")); + storage.onFunctionParsed(validLocation(), main, validLocation()); + + storage.onCallParsed(validLocation(), main, isTrue); + + TS_ASSERT_EQUALS(storage.graph().getNodeCount(), 5); + TS_ASSERT_EQUALS(storage.graph().getEdgeCount(), 5); + TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); + TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 5); + + std::vector filePaths; + filePaths.push_back("file.cpp"); + filePaths.push_back("file.h"); + storage.clearFileData(filePaths); + + TS_ASSERT_EQUALS(storage.graph().getNodeCount(), 0); + TS_ASSERT_EQUALS(storage.graph().getEdgeCount(), 0); + TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 0); + TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 0); + } + private: class TestStorage : public Storage @@ -568,17 +684,32 @@ private: { return getSearchIndex().getWord(wordId); } + + const Graph& graph() const + { + return getGraph(); + } + + const TokenLocationCollection& tokenLocationCollection() const + { + return getTokenLocationCollection(); + } + + const SearchIndex& searchIndex() const + { + return getSearchIndex(); + } }; ParseLocation validLocation(Id locationId = 0) const { - return ParseLocation("file.cpp", 1, locationId, 1, locationId); + return ParseLocation(m_filePath, 1, locationId, 1, locationId); } bool isValidLocation(TokenLocation* location, Id locationId) const { return - location->getFilePath() == "file.cpp" && + location->getFilePath() == m_filePath && location->getLineNumber() == 1 && location->getColumnNumber() == locationId; } @@ -594,4 +725,6 @@ private: params.push_back(typeUsage(param)); return params; } + + std::string m_filePath; };