From fd73abde641db73488c805dbbeb21eceb18c408a Mon Sep 17 00:00:00 2001 From: malte_langkabel Date: Mon, 4 May 2015 15:00:18 +0200 Subject: [PATCH] ui: snippet sizes * added SnippetMerger class that conducts the merging and snapping of TokenLocations to create snippets. * added parameters for snippet snapping and merging to ApplicationSettings. * added forEachStartTokenLocation() and forEachEndTokenLocation() to TokenLocationLine and TokenLocationFile. * added < and > operators to TokenLocation that compare the location part (not the id). --- bin/app/data/ApplicationSettings_template.xml | 4 + src/lib/CMakeLists.txt | 3 + .../component/controller/CodeController.cpp | 81 +++++++++++-------- src/lib/component/controller/CodeController.h | 8 +- .../controller/helper/SnippetMerger.cpp | 74 +++++++++++++++++ .../controller/helper/SnippetMerger.h | 34 ++++++++ src/lib/data/Storage.cpp | 42 ++++++++++ src/lib/data/Storage.h | 2 + src/lib/data/access/StorageAccess.h | 2 + src/lib/data/access/StorageAccessProxy.cpp | 10 +++ src/lib/data/access/StorageAccessProxy.h | 2 + src/lib/data/location/TokenLocation.cpp | 44 ++++++++++ src/lib/data/location/TokenLocation.h | 6 ++ src/lib/data/location/TokenLocationFile.cpp | 16 ++++ src/lib/data/location/TokenLocationFile.h | 2 + src/lib/data/location/TokenLocationLine.cpp | 22 +++++ src/lib/data/location/TokenLocationLine.h | 2 + src/lib/settings/ApplicationSettings.cpp | 20 +++++ src/lib/settings/ApplicationSettings.h | 6 ++ 19 files changed, 345 insertions(+), 35 deletions(-) create mode 100644 src/lib/component/controller/helper/SnippetMerger.cpp create mode 100644 src/lib/component/controller/helper/SnippetMerger.h diff --git a/bin/app/data/ApplicationSettings_template.xml b/bin/app/data/ApplicationSettings_template.xml index 340f4ac2..741c5938 100644 --- a/bin/app/data/ApplicationSettings_template.xml +++ b/bin/app/data/ApplicationSettings_template.xml @@ -35,6 +35,10 @@ + + + + diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 92f6a7db..c404704f 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -35,6 +35,9 @@ add_files( add_files( LIB_FILES + component/controller/helper/SnippetMerger.cpp + component/controller/helper/SnippetMerger.h + component/controller/CodeController.cpp component/controller/CodeController.h component/controller/Controller.cpp diff --git a/src/lib/component/controller/CodeController.cpp b/src/lib/component/controller/CodeController.cpp index 537fca3a..5efb36ea 100644 --- a/src/lib/component/controller/CodeController.cpp +++ b/src/lib/component/controller/CodeController.cpp @@ -4,6 +4,7 @@ #include "data/location/TokenLocation.h" #include "data/location/TokenLocationCollection.h" #include "data/location/TokenLocationFile.h" +#include "settings/ApplicationSettings.h" #include "utility/text/TextAccess.h" CodeController::CodeController(StorageAccess* storageAccess) @@ -29,12 +30,13 @@ void CodeController::handleMessage(MessageActivateTokenLocation* message) void CodeController::handleMessage(MessageActivateTokens* message) { std::vector activeTokenIds = message->tokenIds; - Id declarationId = 0; + Id declarationId = 0; // 0 means that no token is found. if (activeTokenIds.size() == 1) { activeTokenIds = m_storageAccess->getActiveTokenIdsForId(activeTokenIds[0], &declarationId); } + // TODO: what about declarationId if more than 1 token is active? FIX THIS! CodeView* view = getView(); view->setActiveTokenIds(activeTokenIds); @@ -144,24 +146,37 @@ std::vector CodeController::getSnippetsForFile(cons { std::shared_ptr textAccess = TextAccess::createFromFile(file->getFilePath().str()); - std::vector> ranges; + std::deque ranges; if (file->isWholeCopy) { - ranges.push_back(std::make_pair(1, textAccess->getLineCount())); + ranges.push_back(SnippetMerger::Range( + SnippetMerger::Border(1, true), + SnippetMerger::Border(textAccess->getLineCount(), true) + )); } else { - ranges = getSnippetRangesForFile(file); + SnippetMerger fileScopedMerger(1, textAccess->getLineCount()); + std::map> mergers; + file->forEachStartTokenLocation( + [&](TokenLocation* location) + { + buildMergerHierarchy(location, fileScopedMerger, mergers); + } + ); + + ranges = fileScopedMerger.merge(); } + const int snippetExpandRange = ApplicationSettings::getInstance()->getCodeSnippetExpandRange(); std::vector snippets; - for (const std::pair& range: ranges) + for (const SnippetMerger::Range& range: ranges) { CodeView::CodeSnippetParams params; params.locationFile = *file; - params.startLineNumber = std::max(1, range.first - s_lineRadius); - params.endLineNumber = std::min(textAccess->getLineCount(), range.second + s_lineRadius); + params.startLineNumber = std::max(1, range.start.row - (range.start.strong ? 0 : snippetExpandRange)); + params.endLineNumber = std::min(textAccess->getLineCount(), range.end.row + (range.end.strong ? 0 : snippetExpandRange)); for (const std::string& line: textAccess->getLines(params.startLineNumber, params.endLineNumber)) { @@ -174,40 +189,38 @@ std::vector CodeController::getSnippetsForFile(cons return snippets; } -std::vector> CodeController::getSnippetRangesForFile(const TokenLocationFile* file) const +std::shared_ptr CodeController::buildMergerHierarchy( + TokenLocation* location, SnippetMerger& fileScopedMerger, std::map>& mergers) const { - std::vector> ranges; - uint start = 0; - uint end = 0; + const TokenLocation* currentLocation = location; + std::shared_ptr currentMerger = std::make_shared( + currentLocation->getStartTokenLocation()->getLineNumber(), + currentLocation->getEndTokenLocation()->getLineNumber() + ); - file->forEachTokenLocation( - [&](TokenLocation* location) -> void + std::shared_ptr locationFile = m_storageAccess->getTokenLocationOfParentScope(currentLocation); + if (locationFile->getTokenLocationLineCount() == 0) + { + fileScopedMerger.addChild(currentMerger); + return currentMerger; + } + + std::shared_ptr nextMerger; + locationFile->forEachStartTokenLocation( // contains just 1 start location + [&](TokenLocation* scopeLocation) { - uint lineNumber = location->getLineNumber(); - - if (location->isStartTokenLocation()) + std::map>::iterator it = mergers.find(scopeLocation->getId()); + if (it == mergers.end()) { - if (start && end && lineNumber > end + 2 * s_lineRadius + 1) - { - ranges.push_back(std::make_pair(uint(start), uint(end))); - start = end = 0; - } - - if (!start) - { - start = lineNumber; - } - - lineNumber = location->getEndTokenLocation()->getLineNumber(); + nextMerger = buildMergerHierarchy(scopeLocation, fileScopedMerger, mergers); + mergers[scopeLocation->getId()] = nextMerger; } - - if (lineNumber > end) + else { - end = lineNumber; + nextMerger = it->second; } } ); - - ranges.push_back(std::make_pair(uint(start), uint(end))); - return ranges; + nextMerger->addChild(currentMerger); + return currentMerger; } diff --git a/src/lib/component/controller/CodeController.h b/src/lib/component/controller/CodeController.h index 38dc4151..7ce8cb5d 100644 --- a/src/lib/component/controller/CodeController.h +++ b/src/lib/component/controller/CodeController.h @@ -2,6 +2,7 @@ #define CODE_CONTROLLER_H #include +#include #include "component/controller/Controller.h" #include "component/view/CodeView.h" @@ -13,6 +14,10 @@ #include "utility/messaging/type/MessageShowFile.h" #include "utility/types.h" + + +#include "component/controller/helper/SnippetMerger.h" + class StorageAccess; class TokenLocationFile; @@ -42,7 +47,8 @@ private: std::vector getSnippetsForActiveTokenIds( const std::vector& ids, Id declarationId) const; std::vector getSnippetsForFile(const TokenLocationFile* file) const; - std::vector> getSnippetRangesForFile(const TokenLocationFile* file) const; + std::shared_ptr buildMergerHierarchy( + TokenLocation* location, SnippetMerger& fileScopedMerger, std::map>& mergers) const; StorageAccess* m_storageAccess; }; diff --git a/src/lib/component/controller/helper/SnippetMerger.cpp b/src/lib/component/controller/helper/SnippetMerger.cpp new file mode 100644 index 00000000..da1d977f --- /dev/null +++ b/src/lib/component/controller/helper/SnippetMerger.cpp @@ -0,0 +1,74 @@ +#include "component/controller/helper/SnippetMerger.h" + +#include +#include "settings/ApplicationSettings.h" + +SnippetMerger::SnippetMerger(int startRow, int endRow) + : m_start(startRow) + , m_end(endRow) +{ +} + +void SnippetMerger::addChild(std::shared_ptr child) +{ + m_children.push_back(child); +} + +std::deque SnippetMerger::merge() const +{ + std::deque merged; + if (m_children.size() == 0) + { + merged.push_back(Range(Border(m_start, false), Border(m_end, false))); + } + else + { + for (size_t i = 0; i < m_children.size(); i++) + { + std::deque mergedFromChild = m_children[i]->merge(); + for (size_t j = 0; j < mergedFromChild.size(); j++) + { + merged.push_back(mergedFromChild[j]); + } + } + std::sort(merged.begin(), merged.end(), + [](const Range& a, const Range& b) + { + return a.start.row < b.start.row; + } + ); + + // merge children + const int snippetExpandRange = ApplicationSettings::getInstance()->getCodeSnippetExpandRange(); + const int snippetMergeRange = 2 * snippetExpandRange + 1; // +1 since snippets that end/start with consequtive + // lines should be merged as well. + for (size_t i = 0; i < merged.size() - 1; i++) + { + const Range first = merged[i]; + const Range second = merged[i + 1]; + if (first.end.row + snippetMergeRange >= second.start.row) + { + merged.erase(merged.begin() + i, merged.begin() + i + 2); + merged.insert(merged.begin() + i, Range( + first.start.row < second.start.row ? first.start : second.start, + first.end.row > second.end.row ? first.end : second.end + )); + i--; + } + } + + // snap to own borders + const int snippetSnapRange = ApplicationSettings::getInstance()->getCodeSnippetSnapRange(); + if (m_start + snippetSnapRange >= merged.front().start.row) + { + merged.front().start.row = m_start; + merged.front().start.strong = true; + } + if (m_end - snippetSnapRange <= merged.back().end.row) + { + merged.back().end.row = m_end; + merged.back().end.strong = true; + } + } + return merged; +} diff --git a/src/lib/component/controller/helper/SnippetMerger.h b/src/lib/component/controller/helper/SnippetMerger.h new file mode 100644 index 00000000..ff3dd944 --- /dev/null +++ b/src/lib/component/controller/helper/SnippetMerger.h @@ -0,0 +1,34 @@ +#ifndef SNIPPET_MERGER_H +#define SNIPPET_MERGER_H + +#include +#include +#include + +class SnippetMerger +{ +public: + struct Border + { + Border(int row, bool strong): row(row), strong(strong) {} + int row; + bool strong; + }; + struct Range + { + Range (Border start, Border end): start(start), end(end) {} + Border start; + Border end; + }; + + SnippetMerger(int startRow, int endRow); + void addChild(std::shared_ptr child); + std::deque merge() const; + +private: + const int m_start; + const int m_end; + std::vector> m_children; +}; + +#endif // SNIPPET_MERGER_H diff --git a/src/lib/data/Storage.cpp b/src/lib/data/Storage.cpp index 0413f4b4..42025232 100644 --- a/src/lib/data/Storage.cpp +++ b/src/lib/data/Storage.cpp @@ -1055,6 +1055,48 @@ TokenLocationCollection Storage::getErrorTokenLocations(std::vector return m_errorLocationCollection; } +std::shared_ptr Storage::getTokenLocationOfParentScope(const TokenLocation* child) const +{ + const TokenLocation* parent = child; + const FilePath filePath = child->getFilePath(); + const TokenLocationFile* locationFile = m_locationCollection.findTokenLocationFileByPath(child->getFilePath()); + locationFile->forEachTokenLocation( + [&](TokenLocation* tokenLocation) -> void + { + if (tokenLocation->isStartTokenLocation()) + { + TokenLocation::LocationType lt = tokenLocation->getType(); + int sln = tokenLocation->getLineNumber(); + int eln = tokenLocation->getEndTokenLocation()->getLineNumber(); + if (tokenLocation->getType() == TokenLocation::LOCATION_SCOPE && + tokenLocation->isStartTokenLocation() && + (*tokenLocation) < *(child->getStartTokenLocation()) && + (*tokenLocation->getEndTokenLocation()) > *(child->getEndTokenLocation())) + { + if (parent == child) + { + parent = tokenLocation; + } + else + { + if ((*tokenLocation) > *parent) // since tokenLocation is a start location the > location indiceates the scope that is closer to the child. + { + parent = tokenLocation; + } + } + } + } + } + ); + std::shared_ptr file = std::make_shared(filePath); + if (parent != child) + { + file->addTokenLocationAsPlainCopy(parent); + file->addTokenLocationAsPlainCopy(parent->getOtherTokenLocation()); + } + return file; +} + const Graph& Storage::getGraph() const { return m_graph; diff --git a/src/lib/data/Storage.h b/src/lib/data/Storage.h index d7489075..d1b812bc 100644 --- a/src/lib/data/Storage.h +++ b/src/lib/data/Storage.h @@ -126,6 +126,8 @@ public: virtual TokenLocationCollection getErrorTokenLocations(std::vector* errorMessages) const; + virtual std::shared_ptr getTokenLocationOfParentScope(const TokenLocation* child) const; + protected: const Graph& getGraph() const; const TokenLocationCollection& getTokenLocationCollection() const; diff --git a/src/lib/data/access/StorageAccess.h b/src/lib/data/access/StorageAccess.h index 7a36f92c..0836294f 100644 --- a/src/lib/data/access/StorageAccess.h +++ b/src/lib/data/access/StorageAccess.h @@ -11,6 +11,7 @@ #include "data/search/SearchMatch.h" class Graph; +class TokenLocation; class TokenLocationCollection; class TokenLocationFile; @@ -38,6 +39,7 @@ public: const std::string& filePath, uint firstLineNumber, uint lastLineNumber) const = 0; virtual TokenLocationCollection getErrorTokenLocations(std::vector* errorMessages) const = 0; + virtual std::shared_ptr getTokenLocationOfParentScope(const TokenLocation* child) const = 0; }; #endif // STORAGE_ACCESS_H diff --git a/src/lib/data/access/StorageAccessProxy.cpp b/src/lib/data/access/StorageAccessProxy.cpp index 764abdf1..d4357a45 100644 --- a/src/lib/data/access/StorageAccessProxy.cpp +++ b/src/lib/data/access/StorageAccessProxy.cpp @@ -153,3 +153,13 @@ TokenLocationCollection StorageAccessProxy::getErrorTokenLocations(std::vector StorageAccessProxy::getTokenLocationOfParentScope(const TokenLocation* child) const +{ + if (hasSubject()) + { + return m_subject->getTokenLocationOfParentScope(child); + } + + return std::make_shared(""); +} diff --git a/src/lib/data/access/StorageAccessProxy.h b/src/lib/data/access/StorageAccessProxy.h index c83ecacd..ca03ce79 100644 --- a/src/lib/data/access/StorageAccessProxy.h +++ b/src/lib/data/access/StorageAccessProxy.h @@ -34,6 +34,8 @@ public: virtual TokenLocationCollection getErrorTokenLocations(std::vector* errorMessages) const; + virtual std::shared_ptr getTokenLocationOfParentScope(const TokenLocation* child) const; + private: StorageAccess* m_subject; }; diff --git a/src/lib/data/location/TokenLocation.cpp b/src/lib/data/location/TokenLocation.cpp index 3c944ae9..9c4f8b2a 100644 --- a/src/lib/data/location/TokenLocation.cpp +++ b/src/lib/data/location/TokenLocation.cpp @@ -39,6 +39,26 @@ TokenLocation::~TokenLocation() { } +bool TokenLocation::operator<(const TokenLocation& rhs) const +{ + return ( + getLineNumber() < rhs.getLineNumber() || ( + getLineNumber() == rhs.getLineNumber() && + getColumnNumber() < rhs.getColumnNumber() + ) + ); +} + +bool TokenLocation::operator>(const TokenLocation& rhs) const +{ + return ( + getLineNumber() > rhs.getLineNumber() || ( + getLineNumber() == rhs.getLineNumber() && + getColumnNumber() > rhs.getColumnNumber() + ) + ); +} + Id TokenLocation::getId() const { return m_id; @@ -123,6 +143,30 @@ TokenLocation* TokenLocation::getEndTokenLocation() } } +const TokenLocation* TokenLocation::getStartTokenLocation() const +{ + if (m_isStart) + { + return this; + } + else + { + return m_other; + } +} + +const TokenLocation* TokenLocation::getEndTokenLocation() const +{ + if (!m_isStart) + { + return this; + } + else + { + return m_other; + } +} + bool TokenLocation::isStartTokenLocation() const { return m_isStart; diff --git a/src/lib/data/location/TokenLocation.h b/src/lib/data/location/TokenLocation.h index cc68150f..196c67fd 100644 --- a/src/lib/data/location/TokenLocation.h +++ b/src/lib/data/location/TokenLocation.h @@ -26,6 +26,9 @@ public: TokenLocation(const TokenLocation& other, TokenLocationLine* line); ~TokenLocation(); + bool operator<(const TokenLocation& rhs) const; + bool operator>(const TokenLocation& rhs) const; + Id getId() const; Id getTokenId() const; @@ -45,6 +48,9 @@ public: TokenLocation* getStartTokenLocation(); TokenLocation* getEndTokenLocation(); + const TokenLocation* getStartTokenLocation() const; + const TokenLocation* getEndTokenLocation() const; + bool isStartTokenLocation() const; bool isEndTokenLocation() const; diff --git a/src/lib/data/location/TokenLocationFile.cpp b/src/lib/data/location/TokenLocationFile.cpp index 35d1ae73..1d9dae6a 100644 --- a/src/lib/data/location/TokenLocationFile.cpp +++ b/src/lib/data/location/TokenLocationFile.cpp @@ -89,6 +89,22 @@ void TokenLocationFile::forEachTokenLocation(std::function } } +void TokenLocationFile::forEachStartTokenLocation(std::function func) const +{ + for (const TokenLocationLinePairType& line : m_lines) + { + line.second->forEachStartTokenLocation(func); + } +} + +void TokenLocationFile::forEachEndTokenLocation(std::function func) const +{ + for (const TokenLocationLinePairType& line : m_lines) + { + line.second->forEachEndTokenLocation(func); + } +} + TokenLocation* TokenLocationFile::addTokenLocationAsPlainCopy(const TokenLocation* location) { unsigned int lineNumber = location->getTokenLocationLine()->getLineNumber(); diff --git a/src/lib/data/location/TokenLocationFile.h b/src/lib/data/location/TokenLocationFile.h index df5eac77..fc652dda 100644 --- a/src/lib/data/location/TokenLocationFile.h +++ b/src/lib/data/location/TokenLocationFile.h @@ -37,6 +37,8 @@ public: void forEachTokenLocationLine(std::function func) const; void forEachTokenLocation(std::function func) const; + void forEachStartTokenLocation(std::function func) const; + void forEachEndTokenLocation(std::function func) const; TokenLocation* addTokenLocationAsPlainCopy(const TokenLocation* location); diff --git a/src/lib/data/location/TokenLocationLine.cpp b/src/lib/data/location/TokenLocationLine.cpp index bd9c748d..cce37ff6 100644 --- a/src/lib/data/location/TokenLocationLine.cpp +++ b/src/lib/data/location/TokenLocationLine.cpp @@ -91,6 +91,28 @@ void TokenLocationLine::forEachTokenLocation(std::function } } +void TokenLocationLine::forEachStartTokenLocation(std::function func) const +{ + for (const TokenLocationPairType& location : m_locations) + { + if (location.second->isStartTokenLocation()) + { + func(location.second.get()); + } + } +} + +void TokenLocationLine::forEachEndTokenLocation(std::function func) const +{ + for (const TokenLocationPairType& location : m_locations) + { + if (location.second->isEndTokenLocation()) + { + func(location.second.get()); + } + } +} + TokenLocation* TokenLocationLine::addTokenLocationAsPlainCopy(const TokenLocation* location) { std::shared_ptr locationPtr = std::make_shared(*location, this); diff --git a/src/lib/data/location/TokenLocationLine.h b/src/lib/data/location/TokenLocationLine.h index 65faa5c4..20fc41b3 100644 --- a/src/lib/data/location/TokenLocationLine.h +++ b/src/lib/data/location/TokenLocationLine.h @@ -37,6 +37,8 @@ public: TokenLocation* getTokenLocationById(Id id) const; void forEachTokenLocation(std::function func) const; + void forEachStartTokenLocation(std::function func) const; + void forEachEndTokenLocation(std::function func) const; TokenLocation* addTokenLocationAsPlainCopy(const TokenLocation* location); diff --git a/src/lib/settings/ApplicationSettings.cpp b/src/lib/settings/ApplicationSettings.cpp index 4488c44a..b151ba55 100644 --- a/src/lib/settings/ApplicationSettings.cpp +++ b/src/lib/settings/ApplicationSettings.cpp @@ -86,6 +86,26 @@ void ApplicationSettings::setCodeActiveLinkColor(Colori color) setValue("code/ActiveLinkColor", color.toString()); } +int ApplicationSettings::getCodeSnippetSnapRange() const +{ + return getValue("code/snippet/snap_range", 4); +} + +void ApplicationSettings::setCodeSnippetSnapRange(int range) +{ + setValue("code/snippet/snap_range", range); +} + +int ApplicationSettings::getCodeSnippetExpandRange() const +{ + return getValue("code/snippet/expand_range", 2); +} + +void ApplicationSettings::setCodeSnippetExpandRange(int range) +{ + setValue("code/snippet/expand_range", range); +} + std::string ApplicationSettings::getNodeTypeColor(Node::NodeType type, const std::string& state) const { std::string path = "colors/" + Node::getTypeString(type) + "/" + state; diff --git a/src/lib/settings/ApplicationSettings.h b/src/lib/settings/ApplicationSettings.h index 5859d185..81728014 100644 --- a/src/lib/settings/ApplicationSettings.h +++ b/src/lib/settings/ApplicationSettings.h @@ -38,6 +38,12 @@ public: Colori getCodeActiveLinkColor() const; void setCodeActiveLinkColor(Colori color); + int getCodeSnippetSnapRange() const; + void setCodeSnippetSnapRange(int range); + + int getCodeSnippetExpandRange() const; + void setCodeSnippetExpandRange(int range); + // colors std::string getNodeTypeColor(Node::NodeType type, const std::string& state = "normal") const; void setNodeTypeColor(Node::NodeType type, const std::string& color, const std::string& state = "normal");