Skip to content

Commit

Permalink
Auto-expand previously expanded variables when repeating the same bre…
Browse files Browse the repository at this point in the history
…akpoint.
  • Loading branch information
SpartanJ committed Jan 14, 2025
1 parent 4913443 commit 820d352
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 109 deletions.
15 changes: 11 additions & 4 deletions src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ void DebuggerClientListener::initUI() {
} );

UITreeView* uiVariables = sdc->getUIVariables();
uiVariables->setModel( mVariablesHolder->model );
uiVariables->setModel( mVariablesHolder->getModel() );
uiVariables->removeEventsOfType( Event::OnModelEvent );
uiVariables->onModelEvent( [this]( const ModelEvent* modelEvent ) {
if ( modelEvent->getModelEventType() == Abstract::ModelEventType::OpenTree ) {
Expand Down Expand Up @@ -138,16 +138,18 @@ void DebuggerClientListener::debuggeeExited( int /*exitCode*/ ) {
void DebuggerClientListener::debuggeeStopped( const StoppedEvent& event ) {
Log::debug( "DebuggerClientListener::debuggeeStopped: reason %s", event.reason );

int threadId = mStoppedData->threadId ? *mStoppedData->threadId : 1;
mStoppedData = event;
changeThread( mStoppedData->threadId ? *mStoppedData->threadId : 1 );

if ( mPausedToRefreshBreakpoints ) {
mPlugin->sendPendingBreakpoints();
mClient->resume( 1 );
mClient->resume( threadId );
mPausedToRefreshBreakpoints = false;
return;
}

changeThread( mStoppedData->threadId ? *mStoppedData->threadId : 1 );

mClient->threads();
mClient->stackTrace( mCurrentThreadId );

Expand Down Expand Up @@ -302,7 +304,8 @@ void DebuggerClientListener::variables( const int variablesReference,
if ( mCurrentScopePos && scopeIt != mScopeRef.end() ) {
ExpandedState::Location location{ mCurrentScopePos->first, mCurrentScopePos->second,
mCurrentFrameId };
mVariablesHolder->restoreExpandedState( location, mClient );
mVariablesHolder->restoreExpandedState( location, mClient,
getStatusDebuggerController()->getUIVariables() );
}
}

Expand Down Expand Up @@ -333,6 +336,10 @@ std::optional<StoppedEvent> DebuggerClientListener::getStoppedData() const {
return mStoppedData;
}

void DebuggerClientListener::setPausedToRefreshBreakpoints() {
mPausedToRefreshBreakpoints = true;
}

int DebuggerClientListener::getCurrentThreadId() const {
return mCurrentThreadId;
}
Expand Down
3 changes: 1 addition & 2 deletions src/tools/ecode/plugins/debugger/debuggerclientlistener.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class DebuggerClientListener : public DebuggerClient::Listener {

std::optional<StoppedEvent> getStoppedData() const;

void setPausedToRefreshBreakpoints() { mPausedToRefreshBreakpoints = true; }
void setPausedToRefreshBreakpoints();

int getCurrentThreadId() const;

Expand Down Expand Up @@ -87,7 +87,6 @@ class DebuggerClientListener : public DebuggerClient::Listener {
void changeThread( int id );

void initUI();

};

} // namespace ecode
2 changes: 1 addition & 1 deletion src/tools/ecode/plugins/debugger/debuggerplugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,7 @@ void DebuggerPlugin::buildStatusBar() {

debuggerStatusElem->onWidgetCreated = [this]( StatusDebuggerController* sdc, UIWidget* ) {
UITreeView* uiExpressions = sdc->getUIExpressions();
uiExpressions->setModel( mExpressionsHolder->model );
uiExpressions->setModel( mExpressionsHolder->getModel() );
uiExpressions->removeEventsOfType( Event::OnModelEvent );
uiExpressions->onModelEvent( [this, uiExpressions]( const ModelEvent* modelEvent ) {
if ( modelEvent->getModelEventType() == Abstract::ModelEventType::OpenMenu ) {
Expand Down
139 changes: 62 additions & 77 deletions src/tools/ecode/plugins/debugger/models/variablesmodel.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "variablesmodel.hpp"
#include "../debuggerclient.hpp"
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uitreeview.hpp>

namespace ecode {

Expand Down Expand Up @@ -160,16 +161,16 @@ Variant VariablesModel::data( const ModelIndex& index, ModelRole role ) const {
}

VariablesHolder::VariablesHolder( UISceneNode* sceneNode ) :
rootNode( std::make_shared<ModelVariableNode>( "Root", 0 ) ),
model( std::make_shared<VariablesModel>( rootNode, sceneNode ) ) {
nodeMap[0] = rootNode;
mRootNode( std::make_shared<ModelVariableNode>( "Root", 0 ) ),
mModel( std::make_shared<VariablesModel>( mRootNode, sceneNode ) ) {
mNodeMap[0] = mRootNode;
}

void VariablesHolder::addVariables( const int variablesReference, std::vector<Variable>&& vars ) {
Lock l( mutex );
auto parentNode = getNodeByReference( variablesReference );
if ( !parentNode ) {
auto node = rootNode->getChildRecursive( variablesReference );
auto node = mRootNode->getChildRecursive( variablesReference );
if ( !node )
return;
parentNode = *node;
Expand All @@ -187,7 +188,7 @@ void VariablesHolder::addVariables( const int variablesReference, std::vector<Va
child->var = std::move( var );

if ( child->var.variablesReference != 0 )
nodeMap[child->var.variablesReference] = *found;
mNodeMap[child->var.variablesReference] = *found;

continue;
}
Expand All @@ -198,131 +199,115 @@ void VariablesHolder::addVariables( const int variablesReference, std::vector<Va
parentNode->addChild( child );

if ( child->var.variablesReference != 0 )
nodeMap[child->var.variablesReference] = child;
mNodeMap[child->var.variablesReference] = child;
}

model->invalidate( invalidateIndexes ? Model::UpdateFlag::InvalidateAllIndexes
: Model::UpdateFlag::DontInvalidateIndexes );
mModel->invalidate( invalidateIndexes ? Model::UpdateFlag::InvalidateAllIndexes
: Model::UpdateFlag::DontInvalidateIndexes );
}

void VariablesHolder::addChild( ModelVariableNode::NodePtr child ) {
Lock l( mutex );
rootNode->addChild( child );
nodeMap[child->var.variablesReference] = child;
model->invalidate( Model::UpdateFlag::InvalidateAllIndexes );
mRootNode->addChild( child );
mNodeMap[child->var.variablesReference] = child;
mModel->invalidate( Model::UpdateFlag::InvalidateAllIndexes );
}

void VariablesHolder::upsertRootChild( Variable&& var ) {
Lock l( mutex );
for ( size_t i = 0; i < rootNode->children.size(); i++ ) {
auto child = rootNode->children[i];
for ( size_t i = 0; i < mRootNode->children.size(); i++ ) {
auto child = mRootNode->children[i];
if ( child->getName() == var.name ) {
auto newChild = std::make_shared<ModelVariableNode>( std::move( var ), rootNode );
nodeMap[newChild->var.variablesReference] = newChild;
rootNode->children[i] = std::move( newChild );
model->invalidate( Model::UpdateFlag::DontInvalidateIndexes );
auto newChild = std::make_shared<ModelVariableNode>( std::move( var ), mRootNode );
mNodeMap[newChild->var.variablesReference] = newChild;
mRootNode->children[i] = std::move( newChild );
mModel->invalidate( Model::UpdateFlag::DontInvalidateIndexes );
return;
}
}
auto newChild = std::make_shared<ModelVariableNode>( std::move( var ), rootNode );
auto newChild = std::make_shared<ModelVariableNode>( std::move( var ), mRootNode );
addChild( newChild );
}

void VariablesHolder::clear( bool all ) {
Lock l( mutex );
rootNode->clear();
mRootNode->clear();
if ( all ) {
nodeMap.clear();
mNodeMap.clear();
mExpandedStates.clear();
mCurrentLocation = {};
}
}

ModelVariableNode::NodePtr VariablesHolder::getNodeByReference( int variablesReference ) {
auto it = nodeMap.find( variablesReference );
return ( it != nodeMap.end() ) ? it->second : nullptr;
auto it = mNodeMap.find( variablesReference );
return ( it != mNodeMap.end() ) ? it->second : nullptr;
}

VariablePath VariablesHolder::buildVariablePath( ModelVariableNode* node ) const {
VariablePath path;
std::vector<std::string> reversePath;

// Build the path from node to root
while ( node && node != rootNode.get() ) {
reversePath.push_back( node->getName() );
while ( node && node != mRootNode.get() ) {
path.push_back( node->getName() );
node = node->parent ? node->parent.get() : nullptr;
}

if ( reversePath.size() >= 2 ) {
path.scopeName = reversePath.back(); // The scope name is at the root
path.variableName = reversePath[reversePath.size() - 2]; // The variable name is next

// Add any remaining path components as child path
path.childPath.assign( reversePath.begin(), reversePath.end() - 2 );
}

std::reverse( path.begin(), path.end() );
return path;
}

void VariablesHolder::saveExpandedState( const ModelIndex& index ) {
if ( !currentLocation )
if ( !mCurrentLocation )
return;

ModelVariableNode* node = static_cast<ModelVariableNode*>( index.internalData() );
if ( !node )
return;

auto nodePath = buildVariablePath( node );
expandedStates[*currentLocation].insert( std::move( nodePath ) );
mExpandedStates[*mCurrentLocation].insert( std::move( nodePath ) );
}

void VariablesHolder::restoreExpandedState( const ExpandedState::Location& location,
DebuggerClient* client ) {
currentLocation = location;
void VariablesHolder::resolvePath( std::vector<std::string> path, DebuggerClient* client,
UITreeView* uiVariables, ModelVariableNode::NodePtr parentNode,
int pathPos ) {
if ( path.empty() || !parentNode )
return;

auto it = expandedStates.find( location );
if ( it == expandedStates.end() )
auto currentNodeOpt = parentNode->getChild( path[pathPos] );
if ( !currentNodeOpt )
return;

const auto& paths = it->second;
auto currentNode = *currentNodeOpt;

for ( const auto& path : paths ) {
// Find the scope node
auto scopeNode = [this, &path]() -> ModelVariableNode::NodePtr {
for ( const auto& child : rootNode->children ) {
if ( child->getName() == path.scopeName ) {
return child;
}
}
return nullptr;
}();
if ( currentNode->getVariablesReference() > 0 ) {
const auto onVariablesRecieved = [this, uiVariables, path, currentNode, pathPos,
client]( const int variablesReference,
std::vector<Variable>&& vars ) {
addVariables( variablesReference, std::move( vars ) );

if ( !scopeNode )
continue;
uiVariables->runOnMainThread(
[uiVariables, path] { uiVariables->selectRowWithPath( path ); } );

// Find the variable node
auto currentNode = [&path, scopeNode]() -> ModelVariableNode::NodePtr {
for ( const auto& child : scopeNode->children ) {
if ( child->getName() == path.variableName ) {
return child;
}
auto nextPos = pathPos + 1;
if ( nextPos < static_cast<Int64>( path.size() ) ) {
resolvePath( path, client, uiVariables, currentNode, nextPos );
}
return nullptr;
}();
};

if ( !currentNode )
continue;
client->variables( currentNode->getVariablesReference(), Variable::Type::Both,
onVariablesRecieved );
}
}

if ( currentNode->getVariablesReference() > 0 )
client->variables( currentNode->getVariablesReference() );
void VariablesHolder::restoreExpandedState( const ExpandedState::Location& location,
DebuggerClient* client, UITreeView* uiVariables ) {
mCurrentLocation = location;

for ( const auto& childName : path.childPath ) {
auto childOpt = currentNode->getChild( childName );
if ( !childOpt )
break;
auto it = mExpandedStates.find( location );
if ( it == mExpandedStates.end() )
return;

currentNode = *childOpt;
if ( currentNode->getVariablesReference() > 0 )
client->variables( currentNode->getVariablesReference() );
}
}
for ( const VariablePath& path : it->second )
resolvePath( path, client, uiVariables, mRootNode, 0 );
}

} // namespace ecode
49 changes: 24 additions & 25 deletions src/tools/ecode/plugins/debugger/models/variablesmodel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,25 @@ using namespace EE::UI::Models;

namespace EE::UI {
class UISceneNode;
}
class UITreeView;
} // namespace EE::UI

namespace ecode {

using namespace dap;

class DebuggerClient;

struct VariablePath {
std::string scopeName;
std::string variableName;
std::vector<std::string> childPath;

bool operator==( const VariablePath& other ) const {
return scopeName == other.scopeName && variableName == other.variableName &&
childPath == other.childPath;
}
};
using VariablePath = std::vector<std::string>;

} // namespace ecode

namespace std {
template <> struct hash<ecode::VariablePath> {
size_t operator()( const ecode::VariablePath& path ) const {
size_t h = std::hash<std::string>{}( path.scopeName );
h ^= std::hash<std::string>{}( path.variableName ) + 0x9e3779b9 + ( h << 6 ) + ( h >> 2 );
for ( const auto& child : path.childPath ) {
h ^= std::hash<std::string>{}( child ) + 0x9e3779b9 + ( h << 6 ) + ( h >> 2 );
}
size_t h = 0;
for ( const auto& child : path )
h = hashCombine( h, std::hash<std::string>{}( child ) );
return h;
}
};
Expand Down Expand Up @@ -133,7 +123,8 @@ class VariablesModel : public Model {
UISceneNode* mSceneNode;
};

struct VariablesHolder {
class VariablesHolder {
public:
VariablesHolder( UISceneNode* sceneNode );

void addVariables( const int variablesReference, std::vector<Variable>&& vars );
Expand All @@ -144,19 +135,27 @@ struct VariablesHolder {

void clear( bool all = false );

ModelVariableNode::NodePtr getNodeByReference( int variablesReference );
void saveExpandedState( const ModelIndex& index );

void restoreExpandedState( const ExpandedState::Location& location, DebuggerClient* client,
UITreeView* uiVariables );

std::shared_ptr<VariablesModel> getModel() { return mModel; }

protected:
Mutex mutex;
std::shared_ptr<ModelVariableNode> rootNode;
std::shared_ptr<VariablesModel> model;
std::unordered_map<int, ModelVariableNode::NodePtr> nodeMap;
std::shared_ptr<ModelVariableNode> mRootNode;
std::shared_ptr<VariablesModel> mModel;
std::unordered_map<int, ModelVariableNode::NodePtr> mNodeMap;
std::optional<ExpandedState::Location> mCurrentLocation;
std::unordered_map<ExpandedState::Location, std::unordered_set<VariablePath>> mExpandedStates;

std::unordered_map<ExpandedState::Location, std::unordered_set<VariablePath>> expandedStates;
std::optional<ExpandedState::Location> currentLocation;
ModelVariableNode::NodePtr getNodeByReference( int variablesReference );

VariablePath buildVariablePath( ModelVariableNode* node ) const;
void saveExpandedState( const ModelIndex& index );
void restoreExpandedState( const ExpandedState::Location& location, DebuggerClient* client );

void resolvePath( std::vector<std::string> path, DebuggerClient* client,
UITreeView* uiVariables, ModelVariableNode::NodePtr parentNode, int pathPos );
};

} // namespace ecode
1 change: 1 addition & 0 deletions src/tools/ecode/plugins/lsp/lspclientplugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2100,6 +2100,7 @@ void LSPClientPlugin::showDocumentSymbols( UICodeEditor* editor ) {
if ( symbolsIt != mDocCurrentSymbols.end() ) {
auto docSymbols = symbolsIt->second;
std::vector<std::string> path;
path.reserve( docSymbols.size() );
for ( const auto& sym : docSymbols )
path.emplace_back( sym.name );
tv->selectRowWithPath( path );
Expand Down

0 comments on commit 820d352

Please sign in to comment.