ui: Added show definition context menu action for graph nodes (issue #83)

* use 'show definition' context menu action or 'Ctrl/Cmd + Left Click' to show definition of node in code
This commit is contained in:
Eberhard Graether
2018-07-22 14:30:56 +02:00
parent 0b5ad33a8c
commit ad6f5c8727
16 changed files with 267 additions and 53 deletions
+2
View File
@@ -418,6 +418,8 @@ add_files(
utility/messaging/filter_types/MessageFilterFocusInOut.h
utility/messaging/filter_types/MessageFilterSearchAutocomplete.h
utility/messaging/type/code/MessageCodeShowDefinition.h
utility/messaging/type/error/MessageActivateErrors.h
utility/messaging/type/error/MessageErrorCountClear.h
utility/messaging/type/error/MessageErrorCountUpdate.h
+95 -16
View File
@@ -287,6 +287,91 @@ void CodeController::handleMessage(MessageChangeFileView* message)
}
}
void CodeController::handleMessage(MessageCodeShowDefinition* message)
{
TRACE("code show definition");
Id nodeId = message->nodeId;
std::shared_ptr<SourceLocationCollection> collection = m_storageAccess->getSourceLocationsForTokenIds({ nodeId });
if (!collection->getSourceLocationFileCount())
{
LOG_ERROR("MessageCodeShowDefinition did not contain a nodeId with location files.");
return;
}
size_t lineNumber = 1;
FilePath filePath;
// use first scope location for nodeId, otherwise first location
if (collection->getSourceLocationCount())
{
std::shared_ptr<SourceLocationCollection> filteredCollection = std::make_shared<SourceLocationCollection>();
bool addedLocation = false;
collection->forEachSourceLocation(
[&](SourceLocation* location)
{
if (addedLocation || !location->isStartLocation())
{
return;
}
if (location->isScopeLocation())
{
filteredCollection->addSourceLocationCopy(location);
filteredCollection->addSourceLocationCopy(location->getEndLocation());
filePath = location->getFilePath();
lineNumber = location->getLineNumber();
addedLocation = true;
return;
}
}
);
if (!addedLocation)
{
SourceLocation* location = collection->getSourceLocationFiles().begin()->second->getSourceLocations().begin()->get();
filteredCollection->addSourceLocationCopy(location);
filteredCollection->addSourceLocationCopy(location->getOtherLocation());
filePath = location->getFilePath();
lineNumber = location->getStartLocation()->getLineNumber();
}
collection = filteredCollection;
}
else // otherwise first file
{
filePath = collection->getSourceLocationFiles().begin()->second->getFilePath();
}
std::vector<CodeSnippetParams> snippets = getSnippetsForFile(collection->getSourceLocationFiles().begin()->second);
if (snippets.size() != 1)
{
LOG_ERROR("MessageCodeShowDefinition snippet count is not 1");
return;
}
snippets[0].insertSnippet = true;
m_collection->addSourceLocationCopies(collection.get());
saveOrRestoreViewMode(message);
CodeView* view = getView();
CodeView::ScrollParams scrollParams(CodeView::ScrollParams::SCROLL_TO_LINE);
scrollParams.filePath = filePath;
scrollParams.line = lineNumber;
view->scrollTo(scrollParams);
CodeView::CodeParams params;
params.showContents = !message->isReplayed();
addAllSourceLocations(&snippets);
getView()->showCodeSnippets(snippets, params);
}
void CodeController::handleMessage(MessageDeactivateEdge* message)
{
if (message->scrollToDefinition)
@@ -394,7 +479,7 @@ void CodeController::clear()
{
getView()->clear();
m_collection.reset();
m_collection = std::make_shared<SourceLocationCollection>();
}
std::vector<CodeSnippetParams> CodeController::getSnippetsForFileWithState(
@@ -402,20 +487,18 @@ std::vector<CodeSnippetParams> CodeController::getSnippetsForFileWithState(
{
TRACE();
std::shared_ptr<SourceLocationFile> file = m_collection->getSourceLocationFileByPath(filePath);
if (!file)
{
return {};
}
std::vector<CodeSnippetParams> snippets;
switch (state)
{
case CodeView::FILE_SNIPPETS:
{
std::shared_ptr<SourceLocationFile> file = m_collection->getSourceLocationFileByPath(filePath);
if (!file)
{
return snippets;
}
snippets = getSnippetsForFile(file);
}
snippets = getSnippetsForFile(file);
break;
case CodeView::FILE_MAXIMIZED:
@@ -431,12 +514,8 @@ std::vector<CodeSnippetParams> CodeController::getSnippetsForFileWithState(
// make a copy of SourceLocationFile so that isWhole flag is different for first snippet adding the file
// and second snippet adding the content
params.locationFile =
std::make_shared<SourceLocationFile>(*m_collection->getSourceLocationFileByPath(filePath).get());
if (params.locationFile)
{
params.locationFile->setIsWhole(true);
}
params.locationFile = std::make_shared<SourceLocationFile>(*file.get());
params.locationFile->setIsWhole(true);
snippets.push_back(params);
}
@@ -6,6 +6,7 @@
#include "utility/file/FilePath.h"
#include "utility/messaging/MessageListener.h"
#include "utility/messaging/type/code/MessageCodeShowDefinition.h"
#include "utility/messaging/type/error/MessageActivateErrors.h"
#include "utility/messaging/type/error/MessageErrorCountClear.h"
#include "utility/messaging/type/error/MessageShowError.h"
@@ -42,6 +43,7 @@ class CodeController
, public MessageListener<MessageActivateTokens>
, public MessageListener<MessageActivateTrailEdge>
, public MessageListener<MessageChangeFileView>
, public MessageListener<MessageCodeShowDefinition>
, public MessageListener<MessageDeactivateEdge>
, public MessageListener<MessageErrorCountClear>
, public MessageListener<MessageFlushUpdates>
@@ -66,6 +68,7 @@ private:
virtual void handleMessage(MessageActivateTokens* message);
virtual void handleMessage(MessageActivateTrailEdge* message);
virtual void handleMessage(MessageChangeFileView* message);
virtual void handleMessage(MessageCodeShowDefinition* message);
virtual void handleMessage(MessageDeactivateEdge* message);
virtual void handleMessage(MessageErrorCountClear* message);
virtual void handleMessage(MessageFlushUpdates* message);
@@ -139,6 +139,18 @@ void UndoRedoController::handleMessage(MessageChangeFileView* message)
processCommand(command);
}
void UndoRedoController::handleMessage(MessageCodeShowDefinition* message)
{
if (sameMessageTypeAsLast(message) &&
static_cast<MessageCodeShowDefinition*>(lastMessage())->nodeId == message->nodeId)
{
return;
}
Command command(std::make_shared<MessageCodeShowDefinition>(*message), Command::ORDER_ADAPT);
processCommand(command);
}
void UndoRedoController::handleMessage(MessageDeactivateEdge* message)
{
if (m_iterator == m_list.begin())
@@ -5,6 +5,7 @@
#include "utility/messaging/MessageBase.h"
#include "utility/messaging/MessageListener.h"
#include "utility/messaging/type/code/MessageCodeShowDefinition.h"
#include "utility/messaging/type/error/MessageActivateErrors.h"
#include "utility/messaging/type/error/MessageShowError.h"
#include "utility/messaging/type/history/MessageHistoryRedo.h"
@@ -44,6 +45,7 @@ class UndoRedoController
, public MessageListener<MessageActivateTrail>
, public MessageListener<MessageActivateTrailEdge>
, public MessageListener<MessageChangeFileView>
, public MessageListener<MessageCodeShowDefinition>
, public MessageListener<MessageDeactivateEdge>
, public MessageListener<MessageGraphNodeBundleSplit>
, public MessageListener<MessageGraphNodeExpand>
@@ -93,6 +95,7 @@ private:
virtual void handleMessage(MessageActivateTrail* message);
virtual void handleMessage(MessageActivateTrailEdge* message);
virtual void handleMessage(MessageChangeFileView* message);
virtual void handleMessage(MessageCodeShowDefinition* message);
virtual void handleMessage(MessageDeactivateEdge* message);
virtual void handleMessage(MessageGraphNodeBundleSplit* message);
virtual void handleMessage(MessageGraphNodeExpand* message);
@@ -78,9 +78,30 @@ SourceLocation* SourceLocationCollection::addSourceLocation(
return file->addSourceLocation(type, locationId, tokenIds, startLineNumber, startColumnNumber, endLineNumber, endColumnNumber);
}
SourceLocation* SourceLocationCollection::addSourceLocationCopy(SourceLocation* location)
SourceLocation* SourceLocationCollection::addSourceLocationCopy(const SourceLocation* location)
{
return createSourceLocationFile(location->getFilePath())->addSourceLocationCopy(location);
SourceLocationFile* other = location->getSourceLocationFile();
SourceLocationFile* file =
createSourceLocationFile(location->getFilePath(), other->isWhole(), other->isComplete(), other->isIndexed());
return file->addSourceLocationCopy(location);
}
void SourceLocationCollection::addSourceLocationCopies(const SourceLocationCollection* other)
{
other->forEachSourceLocationFile(
[this](std::shared_ptr<SourceLocationFile> otherFile)
{
SourceLocationFile* file = createSourceLocationFile(
otherFile->getFilePath(), otherFile->isWhole(), otherFile->isComplete(), otherFile->isIndexed());
otherFile->forEachSourceLocation(
[file](SourceLocation* otherLocation)
{
file->addSourceLocationCopy(otherLocation);
}
);
}
);
}
void SourceLocationCollection::addSourceLocationFile(std::shared_ptr<SourceLocationFile> file)
@@ -105,7 +126,8 @@ void SourceLocationCollection::forEachSourceLocation(std::function<void(SourceLo
}
}
SourceLocationFile* SourceLocationCollection::createSourceLocationFile(const FilePath& filePath)
SourceLocationFile* SourceLocationCollection::createSourceLocationFile(
const FilePath& filePath, bool isWhole, bool isComplete, bool isIndexed)
{
SourceLocationFile* file = getSourceLocationFileByPath(filePath).get();
if (file)
@@ -113,7 +135,8 @@ SourceLocationFile* SourceLocationCollection::createSourceLocationFile(const Fil
return file;
}
std::shared_ptr<SourceLocationFile> filePtr = std::make_shared<SourceLocationFile>(filePath, false, false, false);
std::shared_ptr<SourceLocationFile> filePtr =
std::make_shared<SourceLocationFile>(filePath, isWhole, isComplete, isIndexed);
m_files.emplace(filePath, filePtr);
return filePtr.get();
}
@@ -32,7 +32,9 @@ public:
LocationType type, Id locationId, std::vector<Id> tokenIds, const FilePath& filePath,
size_t startLineNumber, size_t startColumnNumber,
size_t endLineNumber, size_t endColumnNumber);
SourceLocation* addSourceLocationCopy(SourceLocation* location);
SourceLocation* addSourceLocationCopy(const SourceLocation* location);
void addSourceLocationCopies(const SourceLocationCollection* other);
void addSourceLocationFile(std::shared_ptr<SourceLocationFile> file);
@@ -40,7 +42,8 @@ public:
void forEachSourceLocation(std::function<void(SourceLocation*)> func) const;
private:
SourceLocationFile* createSourceLocationFile(const FilePath& filePath);
SourceLocationFile* createSourceLocationFile(
const FilePath& filePath, bool isWhole = false, bool isComplete = false, bool isIndexed = false);
std::map<FilePath, std::shared_ptr<SourceLocationFile>> m_files;
};
@@ -215,6 +215,21 @@ std::wostream& operator<<(std::wostream& ostream, const SourceLocationFile& file
{
ostream << L"file \"" << file.getFilePath().wstr() << L"\"";
if (file.isWhole())
{
ostream << L" whole";
}
if (file.isComplete())
{
ostream << L" complete";
}
if (file.isIndexed())
{
ostream << L" indexed";
}
size_t line = 0;
file.forEachSourceLocation(
[&ostream, &line](SourceLocation* location)
+3 -1
View File
@@ -1380,6 +1380,7 @@ std::shared_ptr<SourceLocationCollection> PersistentStorage::getSourceLocationsF
if (nonFileIds.size())
{
// FIXME: can we use get SqliteIndexStorage::getSourceLocationsForElementIds() here instead?
std::vector<Id> locationIds;
std::unordered_map<Id, Id> locationIdToElementIdMap;
for (const StorageOccurrence& occurrence: m_sqliteIndexStorage.getOccurrencesForElementIds(nonFileIds))
@@ -1403,6 +1404,7 @@ std::shared_ptr<SourceLocationCollection> PersistentStorage::getSourceLocationsF
}
FilePath path = getFileNodePath(sourceLocation.fileNodeId);
// FIXME: This shouldn't be necessary since all files are stored, even non-indexed
if (path.empty())
{
const StorageNode fileNode = m_sqliteIndexStorage.getNodeById(sourceLocation.fileNodeId);
@@ -1421,7 +1423,7 @@ std::shared_ptr<SourceLocationCollection> PersistentStorage::getSourceLocationsF
collection->addSourceLocation(
type,
sourceLocation.id,
std::vector<Id>(1, it->second),
{ it->second },
path,
sourceLocation.startLine,
sourceLocation.startCol,
@@ -0,0 +1,29 @@
#ifndef MESSAGE_CODE_SHOW_DEFINITION_H
#define MESSAGE_CODE_SHOW_DEFINITION_H
#include "utility/messaging/Message.h"
#include "utility/types.h"
class MessageCodeShowDefinition
: public Message<MessageCodeShowDefinition>
{
public:
static const std::string getStaticType()
{
return "MessageCodeShowDefinition";
}
MessageCodeShowDefinition(Id nodeId)
: nodeId(nodeId)
{
}
virtual void print(std::wostream& os) const
{
os << nodeId;
}
const Id nodeId;
};
#endif // MESSAGE_CODE_SHOW_DEFINITION_H
+19 -14
View File
@@ -120,44 +120,49 @@ QtCodeSnippet* QtCodeFile::addCodeSnippet(const CodeSnippetParams& params)
QtCodeSnippet* QtCodeFile::insertCodeSnippet(const CodeSnippetParams& params)
{
QtCodeSnippet* snippet = new QtCodeSnippet(params, m_navigator, this);
QtCodeSnippet* newSnippet = new QtCodeSnippet(params, m_navigator, this);
size_t i = 0;
while (i < m_snippets.size())
{
uint start = snippet->getStartLineNumber();
uint end = snippet->getEndLineNumber();
uint start = newSnippet->getStartLineNumber();
uint end = newSnippet->getEndLineNumber();
QtCodeSnippet* s = m_snippets[i];
QtCodeSnippet* oldSnippet = m_snippets[i];
if (s->getEndLineNumber() + 1 < start)
if (oldSnippet->getEndLineNumber() + 1 < start) // before
{
i++;
continue;
}
else if (s->getStartLineNumber() > end + 1)
else if (oldSnippet->getStartLineNumber() > end + 1) // after
{
break;
}
else if (s->getStartLineNumber() < start || s->getEndLineNumber() > end)
else if (oldSnippet->getStartLineNumber() <= start && oldSnippet->getEndLineNumber() >= end) // containing
{
newSnippet->deleteLater();
return oldSnippet;
}
else if (oldSnippet->getStartLineNumber() < start || oldSnippet->getEndLineNumber() > end) // overlaping
{
m_navigator->clearSnippetReferences();
snippet = QtCodeSnippet::merged(snippet, s, m_navigator, this);
newSnippet = QtCodeSnippet::merged(newSnippet, oldSnippet, m_navigator, this);
}
s->hide();
m_snippetLayout->removeWidget(s);
s->deleteLater();
oldSnippet->hide();
m_snippetLayout->removeWidget(oldSnippet);
oldSnippet->deleteLater();
m_snippets.erase(m_snippets.begin() + i);
}
m_snippetLayout->insertWidget(i, snippet);
m_snippets.insert(m_snippets.begin() + i, snippet);
m_snippetLayout->insertWidget(i, newSnippet);
m_snippets.insert(m_snippets.begin() + i, newSnippet);
setSnippets();
return snippet;
return newSnippet;
}
void QtCodeFile::updateCodeSnippet(const CodeSnippetParams& params)
+32 -16
View File
@@ -20,6 +20,7 @@
#include "qt/utility/QtFileDialog.h"
#include "qt/utility/utilityQt.h"
#include "settings/ApplicationSettings.h"
#include "utility/messaging/type/code/MessageCodeShowDefinition.h"
#include "utility/messaging/type/MessageDisplayBookmarkCreator.h"
#include "utility/messaging/type/MessageGraphNodeHide.h"
#include "utility/ResourcePaths.h"
@@ -51,29 +52,37 @@ QtGraphicsView::QtGraphicsView(QWidget* parent)
m_zoomLabelTimer = std::make_shared<QTimer>(this);
connect(m_zoomLabelTimer.get(), &QTimer::timeout, this, &QtGraphicsView::hideZoomLabel);
m_exportGraphAction = new QAction(tr("Save as Image"), this);
m_exportGraphAction->setStatusTip(tr("Save this graph as image file"));
m_exportGraphAction->setToolTip(tr("Save this graph as image file"));
m_exportGraphAction = new QAction("Save as Image", this);
m_exportGraphAction->setStatusTip("Save this graph as image file");
m_exportGraphAction->setToolTip("Save this graph as image file");
connect(m_exportGraphAction, &QAction::triggered, this, &QtGraphicsView::exportGraph);
m_copyNodeNameAction = new QAction(tr("Copy Name"), this);
m_copyNodeNameAction->setStatusTip(tr("Copies the name of this node to the clipboard"));
m_copyNodeNameAction->setToolTip(tr("Copies the name of this node to the clipboard"));
m_copyNodeNameAction = new QAction("Copy Name", this);
m_copyNodeNameAction->setStatusTip("Copies the name of this node to the clipboard");
m_copyNodeNameAction->setToolTip("Copies the name of this node to the clipboard");
connect(m_copyNodeNameAction, &QAction::triggered, this, &QtGraphicsView::copyNodeName);
m_hideNodeAction = new QAction(tr("Hide Node (Alt + Left Click)"), this);
m_hideNodeAction->setStatusTip(tr("Hide the node from this graph"));
m_hideNodeAction->setToolTip(tr("Hide the node from this graph"));
m_showDefinitionAction = new QAction("Show Definition (Ctrl + Left Click)", this);
#if defined(Q_OS_MAC)
m_showDefinitionAction->setText("Show Definition (Cmd + Left Click)");
#endif
m_showDefinitionAction->setStatusTip("Show definition of this symbol in the code");
m_showDefinitionAction->setToolTip("Show definition of this symbol in the code");
connect(m_showDefinitionAction, &QAction::triggered, this, &QtGraphicsView::showDefinition);
m_hideNodeAction = new QAction("Hide Node (Alt + Left Click)", this);
m_hideNodeAction->setStatusTip("Hide the node from this graph");
m_hideNodeAction->setToolTip("Hide the node from this graph");
connect(m_hideNodeAction, &QAction::triggered, this, &QtGraphicsView::hideNode);
m_hideEdgeAction = new QAction(tr("Hide Edge (Alt + Left Click)"), this);
m_hideEdgeAction->setStatusTip(tr("Hide the edge from this graph"));
m_hideEdgeAction->setToolTip(tr("Hide the edge from this graph"));
m_hideEdgeAction = new QAction("Hide Edge (Alt + Left Click)", this);
m_hideEdgeAction->setStatusTip("Hide the edge from this graph");
m_hideEdgeAction->setToolTip("Hide the edge from this graph");
connect(m_hideEdgeAction, &QAction::triggered, this, &QtGraphicsView::hideEdge);
m_bookmarkNodeAction = new QAction(tr("Bookmark Node"), this);
m_bookmarkNodeAction->setStatusTip(tr("Create a bookmark for this node"));
m_bookmarkNodeAction->setToolTip(tr("Create a bookmark for this node"));
m_bookmarkNodeAction = new QAction("Bookmark Node", this);
m_bookmarkNodeAction->setStatusTip("Create a bookmark for this node");
m_bookmarkNodeAction->setToolTip("Create a bookmark for this node");
connect(m_bookmarkNodeAction, &QAction::triggered, this, &QtGraphicsView::bookmarkNode);
m_zoomState = new QPushButton(this);
@@ -354,6 +363,7 @@ void QtGraphicsView::contextMenuEvent(QContextMenuEvent* event)
}
}
m_showDefinitionAction->setEnabled(m_hideNodeId);
m_hideNodeAction->setEnabled(m_hideNodeId);
m_hideEdgeAction->setEnabled(m_hideEdgeId);
m_bookmarkNodeAction->setEnabled(m_bookmarkNodeId);
@@ -366,8 +376,9 @@ void QtGraphicsView::contextMenuEvent(QContextMenuEvent* event)
menu.addAction(m_exportGraphAction);
menu.addSeparator();
menu.addAction(m_hideEdgeAction);
menu.addAction(m_showDefinitionAction);
menu.addAction(m_hideNodeAction);
menu.addAction(m_hideEdgeAction);
menu.addAction(m_bookmarkNodeAction);
menu.addSeparator();
@@ -521,6 +532,11 @@ void QtGraphicsView::copyNodeName()
QApplication::clipboard()->setText(QString::fromStdWString(m_clipboardNodeName));
}
void QtGraphicsView::showDefinition()
{
MessageCodeShowDefinition(m_hideNodeId).dispatch();
}
void QtGraphicsView::hideNode()
{
MessageGraphNodeHide(m_hideNodeId).dispatch();
+2
View File
@@ -57,6 +57,7 @@ private slots:
void exportGraph();
void copyNodeName();
void showDefinition();
void hideNode();
void hideEdge();
void bookmarkNode();
@@ -94,6 +95,7 @@ private:
QAction* m_exportGraphAction;
QAction* m_copyNodeNameAction;
QAction* m_showDefinitionAction;
QAction* m_hideNodeAction;
QAction* m_hideEdgeAction;
QAction* m_bookmarkNodeAction;
@@ -11,6 +11,7 @@
#include "qt/utility/utilityQt.h"
#include "qt/view/graphElements/nodeComponents/QtGraphNodeComponent.h"
#include "qt/view/graphElements/QtGraphEdge.h"
#include "utility/messaging/type/code/MessageCodeShowDefinition.h"
#include "utility/messaging/type/MessageGraphNodeHide.h"
#include "utility/messaging/type/MessageGraphNodeMove.h"
#include "utility/ResourcePaths.h"
@@ -400,6 +401,20 @@ void QtGraphNode::onHide()
}
}
void QtGraphNode::onShowDefinition()
{
Id tokenId = getTokenId();
if (tokenId)
{
MessageCodeShowDefinition(tokenId).dispatch();
}
else if (getParent())
{
getParent()->onShowDefinition();
}
}
void QtGraphNode::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
event->ignore();
@@ -98,6 +98,7 @@ public:
virtual void onClick();
virtual void onHide();
virtual void onShowDefinition();
virtual void moved(const Vec2i& oldPosition);
virtual void updateStyle() = 0;
@@ -39,6 +39,10 @@ void QtGraphNodeComponentClickable::nodeMouseReleaseEvent(QGraphicsSceneMouseEve
{
m_graphNode->onHide();
}
else if (event->modifiers() & Qt::ControlModifier)
{
m_graphNode->onShowDefinition();
}
else
{
m_graphNode->onClick();