Skip to content

Commit

Permalink
Debugger: Inspect variables WIP.
Browse files Browse the repository at this point in the history
  • Loading branch information
SpartanJ committed Jan 6, 2025
1 parent ad4824d commit 635cba6
Show file tree
Hide file tree
Showing 10 changed files with 294 additions and 23 deletions.
42 changes: 42 additions & 0 deletions bin/assets/colorschemes/colorschemes.conf
Original file line number Diff line number Diff line change
Expand Up @@ -654,3 +654,45 @@ operator = #91d7e3
function = #8aadf4,shadow
link = #8aadf4,shadow
link_hover = #91d7e3,transparent,shadow,underline

[geany]
background = #ffffff
text = #000000
caret = #000000
selection = #c0c0c0
line_highlight = #f0f0f0
line_number = #464646
line_number2 = #181818
gutter_background = #dfdfdf
whitespace = #c0c0c0
line_break_column = #54575b99
matching_bracket = #dbdbdb
matching_selection = #ffff00
matching_search = #0000f0
suggestion = #e1e1e6,#1d1f27
suggestion_selected = #ffffff,#222533
suggestion_scrollbar = #3daee9
error = #ff3030
warning = yellow
notice = #8abdff
selection_region = #c0c0c077
minimap_background = #FFFFFF1A
minimap_visible_area = #dfdfdfAA
minimap_current_line = #f0f0f00A
minimap_hover = #FFFFFF1A
minimap_selection = #8abdff80
minimap_highlight = #FFFF0080

normal = #000000
symbol = #000000
comment = #d00000
keyword = #00007f,bold
keyword2 = #007f7f,bold
keyword3 = #404080,bold
number = #007f00
literal = #000080,bold
string = #ff8000
operator = #000000
function = #000080,bold
link = #007f00
link_hover = #007f00,transparent,underline
2 changes: 2 additions & 0 deletions include/eepp/ui/keyboardshortcut.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ class EE_API KeyBindings {

bool existsKeybind( const Shortcut& keys );

bool hasCommand( const std::string& command );

void removeCommandKeybind( const std::string& command );

void removeCommandsKeybind( const std::vector<std::string>& command );
Expand Down
4 changes: 4 additions & 0 deletions src/eepp/ui/keyboardshortcut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ bool KeyBindings::existsKeybind( const KeyBindings::Shortcut& keys ) {
return mShortcuts.find( keys.toUint64() ) != mShortcuts.end();
}

bool KeyBindings::hasCommand( const std::string& command ) {
return mKeybindingsInvert.find( command ) != mKeybindingsInvert.end();
}

void KeyBindings::removeCommandKeybind( const std::string& command ) {
auto kbIt = mKeybindingsInvert.find( command );
if ( kbIt != mKeybindingsInvert.end() ) {
Expand Down
9 changes: 9 additions & 0 deletions src/tools/ecode/ecode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,15 @@ void App::runCommand( const std::string& command ) {
}
}

bool App::commandExists( const std::string& command ) const {
if ( mSplitter->getCurWidget() && mSplitter->getCurWidget()->isType( UI_TYPE_CODEEDITOR ) ) {
UICodeEditor* editor = mSplitter->getCurWidget()->asType<UICodeEditor>();
if ( editor->getDocument().hasCommand( command ) )
return true;
}
return mMainLayout->getKeyBindings().hasCommand( command );
}

void App::onPluginEnabled( Plugin* plugin ) {
if ( mSplitter ) {
mSplitter->forEachEditor(
Expand Down
2 changes: 2 additions & 0 deletions src/tools/ecode/ecode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {

void runCommand( const std::string& command );

bool commandExists( const std::string& command ) const;

bool loadConfig( const LogLevel& logLevel, const Sizeu& displaySize, bool sync, bool stdOutLogs,
bool disableFileLogs );

Expand Down
232 changes: 216 additions & 16 deletions src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,163 @@ DebuggerClientListener::fromSet( const EE::UnorderedSet<SourceBreakpointStateful

using i18nFn = std::function<String( const std::string&, const String& )>;

struct ModelVariableNode : public std::enable_shared_from_this<ModelVariableNode> {
using NodePtr = std::shared_ptr<ModelVariableNode>;

static std::unordered_map<int, NodePtr> nodeMap;

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

ModelVariableNode( Variable&& var, NodePtr parent ) :
parent( parent ), var( std::move( var ) ) {}

ModelVariableNode( const std::string& name, int variablesReference, NodePtr parent = nullptr ) :
parent( parent ) {
var.name = name;
var.variablesReference = variablesReference;
}

virtual ~ModelVariableNode() {}

const std::vector<NodePtr>& getChildren() const { return children; }

const std::string& getName() const { return var.name; }

int getVariablesReference() const { return var.variablesReference; }

void clear() { children.clear(); }

std::optional<NodePtr> getChild( const std::string& name ) {
auto found =
std::find_if( children.begin(), children.end(),
[&name]( const NodePtr& child ) { return child->var.name == name; } );
return ( found != children.end() ) ? *found : std::optional<NodePtr>{};
}

std::optional<NodePtr> getChild( int variablesReference ) {
auto found = std::find_if( children.begin(), children.end(),
[variablesReference]( const NodePtr& child ) {
return child->var.variablesReference == variablesReference;
} );
return ( found != children.end() ) ? *found : std::optional<NodePtr>{};
}

void addChild( NodePtr child ) { children.emplace_back( child ); }

NodePtr getParent() const { return parent; }

NodePtr parent{ nullptr };
Variable var;
std::vector<NodePtr> children;
};

std::unordered_map<int, ModelVariableNode::NodePtr> ModelVariableNode::nodeMap =
std::unordered_map<int, ModelVariableNode::NodePtr>();

class VariablesModel : public Model {
public:
VariablesModel( ModelVariableNode::NodePtr rootNode, i18nFn fn ) :
rootNode( rootNode ), mi18nFn( fn ) {}

ModelIndex index( int row, int column,
const ModelIndex& parent = ModelIndex() ) const override {
ModelVariableNode* parentNode =
parent.isValid() ? static_cast<ModelVariableNode*>( parent.internalData() )
: rootNode.get();

if ( row >= 0 && row < static_cast<int>( parentNode->getChildren().size() ) ) {
ModelVariableNode::NodePtr childNode = parentNode->getChildren()[row];
return createIndex( row, column, childNode.get() );
}

return ModelIndex();
}

ModelIndex parentIndex( const ModelIndex& index ) const override {
if ( !index.isValid() )
return ModelIndex();

ModelVariableNode* childNode = static_cast<ModelVariableNode*>( index.internalData() );
ModelVariableNode::NodePtr parentNode = childNode->getParent();

if ( parentNode == nullptr || parentNode == rootNode )
return ModelIndex();

ModelVariableNode::NodePtr grandParentNode = parentNode->getParent();
if ( grandParentNode == nullptr )
grandParentNode = rootNode;

int row = std::distance( grandParentNode->getChildren().begin(),
std::find_if( grandParentNode->getChildren().begin(),
grandParentNode->getChildren().end(),
[parentNode]( ModelVariableNode::NodePtr node ) {
return node.get() == parentNode.get();
} ) );

return createIndex( row, 0, parentNode.get() );
}

size_t rowCount( const ModelIndex& index = ModelIndex() ) const override {
ModelVariableNode* parentNode =
index.isValid() ? static_cast<ModelVariableNode*>( index.internalData() )
: rootNode.get();

return static_cast<int>( parentNode->getChildren().size() );
}

bool hasChilds( const ModelIndex& index = ModelIndex() ) const override {
if ( !index.isValid() )
return !rootNode->children.empty();
ModelVariableNode* node = static_cast<ModelVariableNode*>( index.internalData() );
return !node->children.empty() ||
( node->var.namedVariables ? *node->var.namedVariables : 0 ) +
( node->var.indexedVariables ? *node->var.indexedVariables : 0 ) >
0;
}

size_t columnCount( const ModelIndex& ) const override { return 3; }

std::string columnName( const size_t& colIdx ) const override {
switch ( colIdx ) {
case 0:
return mi18nFn( "variable_name", "Variable Name" );
case 1:
return mi18nFn( "value", "Value" );
case 2:
return mi18nFn( "type", "Type" );
}
return "";
}

Variant data( const ModelIndex& index, ModelRole role ) const override {
static const char* EMPTY = "";
if ( !index.isValid() )
return EMPTY;

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

if ( role == ModelRole::Display ) {
switch ( index.column() ) {
case 0:
return Variant( node->var.name.c_str() );
case 1:
return Variant( node->var.value.c_str() );
case 2:
return Variant( node->var.type ? node->var.type->c_str() : EMPTY );
}
}

return EMPTY;
}

protected:
ModelVariableNode::NodePtr rootNode;
i18nFn mi18nFn;
};

class ThreadsModel : public Model {
public:
ThreadsModel( const std::vector<Thread>& threads, i18nFn fn ) :
Expand Down Expand Up @@ -161,7 +318,10 @@ class StackModel : public Model {
};

DebuggerClientListener::DebuggerClientListener( DebuggerClient* client, DebuggerPlugin* plugin ) :
mClient( client ), mPlugin( plugin ) {
mClient( client ),
mPlugin( plugin ),
mVariablesRoot( std::make_shared<ModelVariableNode>( "Root", 0 ) ) {
ModelVariableNode::nodeMap[0] = mVariablesRoot;
eeASSERT( mClient && mPlugin );
}

Expand Down Expand Up @@ -203,6 +363,13 @@ void DebuggerClientListener::stateChanged( DebuggerClient::State state ) {
} );
}

if ( !mVariablesModel ) {
mVariablesModel = std::make_shared<VariablesModel>(
mVariablesRoot, [sceneNode]( const auto& key, const auto& val ) {
return sceneNode->i18n( key, val );
} );
}

UITableView* uiThreads = getStatusDebuggerController()->getUIThreads();
uiThreads->setModel( mThreadsModel );

Expand All @@ -225,6 +392,17 @@ void DebuggerClientListener::stateChanged( DebuggerClient::State state ) {
}
} );

UITreeView* uiVariables = getStatusDebuggerController()->getUIVariables();
uiVariables->setModel( mVariablesModel );
uiVariables->removeEventsOfType( Event::OnModelEvent );
uiVariables->onModelEvent( [this]( const ModelEvent* modelEvent ) {
if ( modelEvent->getModelEventType() == Abstract::ModelEventType::OpenTree ) {
ModelVariableNode* node = static_cast<ModelVariableNode*>(
modelEvent->getModelIndex().internalData() );
mClient->variables( node->var.variablesReference );
}
} );

mPlugin->setUIDebuggingState( StatusDebuggerController::State::Running );
} );
}
Expand Down Expand Up @@ -255,7 +433,7 @@ void DebuggerClientListener::resetState() {
mThreadsModel->resetThreads();
if ( mStackModel )
mStackModel->resetStack();
mScope.clear();
mVariablesRoot->clear();
}

void DebuggerClientListener::debuggeeExited( int /*exitCode*/ ) {
Expand Down Expand Up @@ -355,26 +533,48 @@ void DebuggerClientListener::stackTrace( const int threadId, StackTraceInfo&& st
}

void DebuggerClientListener::scopes( const int /*frameId*/, std::vector<Scope>&& scopes ) {
if ( !scopes.empty() ) {
for ( const auto& scope : scopes ) {
ModelScope mscope;
mscope.name = scope.name;
mscope.variablesReference = scope.variablesReference;
mScope.emplace_back( std::move( mscope ) );
mClient->variables( scope.variablesReference );
}
if ( scopes.empty() )
return;

mVariablesRoot->clear();

for ( const auto& scope : scopes ) {
auto child = std::make_shared<ModelVariableNode>( scope.name, scope.variablesReference );
mVariablesRoot->addChild( child );
ModelVariableNode::nodeMap[child->var.variablesReference] = child;

mClient->variables( scope.variablesReference );
}

if ( !getStatusDebuggerController() )
return;
auto uiVars = getStatusDebuggerController()->getUIVariables();
if ( uiVars )
uiVars->runOnMainThread( [uiVars] { uiVars->expandAll(); } );
}

void DebuggerClientListener::variables( const int variablesReference,
std::vector<Variable>&& vars ) {
auto scopeIt =
std::find_if( mScope.begin(), mScope.end(), [variablesReference]( const ModelScope& cur ) {
return cur.variablesReference == variablesReference;
} );
if ( scopeIt == mScope.end() )
auto parentNode = ModelVariableNode::getNodeByReference( variablesReference );
if ( !parentNode )
return;
scopeIt->variables = vars;

for ( auto& var : vars ) {
if ( var.name.empty() )
continue;

auto found = parentNode->getChild( var.name );
if ( found ) {
( *found )->var = std::move( var );
continue;
}

auto child = std::make_shared<ModelVariableNode>( std::move( var ), parentNode );
parentNode->addChild( child );

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

void DebuggerClientListener::modules( ModulesInfo&& ) {}
Expand Down
11 changes: 4 additions & 7 deletions src/tools/ecode/plugins/debugger/debuggerclientlistener.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,8 @@ namespace ecode {
class DebuggerPlugin;
class ThreadsModel;
class StackModel;

struct ModelScope {
std::string name;
int variablesReference;
std::vector<Variable> variables;
};
class VariablesModel;
struct ModelVariableNode;

class DebuggerClientListener : public DebuggerClient::Listener {
public:
Expand Down Expand Up @@ -70,11 +66,12 @@ class DebuggerClientListener : public DebuggerClient::Listener {
DebuggerPlugin* mPlugin{ nullptr };
std::optional<StoppedEvent> mStoppedData;
std::optional<std::pair<std::string, int>> mCurrentScopePos;
std::vector<ModelScope> mScope;
bool mPausedToRefreshBreakpoints{ false };
int mCurrentThreadId{ 1 };
std::shared_ptr<ThreadsModel> mThreadsModel;
std::shared_ptr<StackModel> mStackModel;
std::shared_ptr<VariablesModel> mVariablesModel;
std::shared_ptr<ModelVariableNode> mVariablesRoot;

StatusDebuggerController* getStatusDebuggerController() const;

Expand Down
Loading

0 comments on commit 635cba6

Please sign in to comment.