From 5533ab46f172d7464ce37fb673113690206825e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 19 Jan 2025 23:40:15 -0300 Subject: [PATCH] More improvements to debugger. Allow to pick running process to debug. Improve expressions inspection. --- .ecode/project_build.json | 4 +- bin/assets/plugins/debugger.json | 4 +- include/eepp/ui/uiwindow.hpp | 13 +- src/eepp/ui/uiwidgetcreator.cpp | 3 + src/eepp/ui/uiwindow.cpp | 18 ++- .../ecode/plugins/debugger/dap/messages.hpp | 1 + .../ecode/plugins/debugger/dap/protocol.cpp | 59 ++++---- .../debugger/debuggerclientlistener.cpp | 25 +++- .../ecode/plugins/debugger/debuggerplugin.cpp | 134 +++++++++++++++--- .../ecode/plugins/debugger/debuggerplugin.hpp | 2 +- .../debugger/models/processesmodel.cpp | 59 +++++++- .../debugger/models/processesmodel.hpp | 8 ++ .../debugger/models/variablesmodel.cpp | 38 ++++- .../debugger/models/variablesmodel.hpp | 6 +- .../ecode/plugins/lsp/lspclientplugin.cpp | 3 +- 15 files changed, 301 insertions(+), 76 deletions(-) diff --git a/.ecode/project_build.json b/.ecode/project_build.json index 0d2c8d619..7551ccdc1 100644 --- a/.ecode/project_build.json +++ b/.ecode/project_build.json @@ -217,14 +217,14 @@ }, { "args": "", - "command": "${project_root}/bin/eepp-unit-tests-debug", + "command": "${project_root}/bin/unit_tests/eepp-unit-tests-debug", "name": "eepp-unit_tests-debug", "run_in_terminal": true, "working_dir": "${project_root}/bin/unit_tests/" }, { "args": "", - "command": "${project_root}/bin/eepp-unit_tests", + "command": "${project_root}/bin/unit_tests/eepp-unit_tests", "name": "eepp-unit_tests", "run_in_terminal": true, "working_dir": "${project_root}/bin/unit_tests" diff --git a/bin/assets/plugins/debugger.json b/bin/assets/plugins/debugger.json index 5690a5921..306db88d2 100644 --- a/bin/assets/plugins/debugger.json +++ b/bin/assets/plugins/debugger.json @@ -53,7 +53,7 @@ "request": "attach", "arguments": { "program": "${file}", - "pid": "${pid}" + "pid": "${command:PickProcess}" } } ] @@ -114,7 +114,7 @@ "request": "attach", "arguments": { "program": "${file}", - "pid": "${pid}" + "pid": "${command:PickProcess}" } } ] diff --git a/include/eepp/ui/uiwindow.hpp b/include/eepp/ui/uiwindow.hpp index 846fd44c6..af926326a 100644 --- a/include/eepp/ui/uiwindow.hpp +++ b/include/eepp/ui/uiwindow.hpp @@ -53,13 +53,24 @@ class EE_API UIWindow : public UIWidget { bool BorderAutoSize = true; }; - enum WindowBaseContainerType { SIMPLE_LAYOUT, LINEAR_LAYOUT, RELATIVE_LAYOUT }; + enum WindowBaseContainerType { + SIMPLE_LAYOUT, + VERTICAL_LINEAR_LAYOUT, + HORIZONTAL_LINEAR_LAYOUT, + RELATIVE_LAYOUT + }; static UIWindow* NewOpt( WindowBaseContainerType type, const StyleConfig& windowStyleConfig = StyleConfig() ); static UIWindow* New(); + static UIWindow* NewVBox(); + + static UIWindow* NewHBox(); + + static UIWindow* NewRelLay(); + explicit UIWindow( WindowBaseContainerType type, const StyleConfig& windowStyleConfig ); explicit UIWindow( WindowBaseContainerType type = SIMPLE_LAYOUT ); diff --git a/src/eepp/ui/uiwidgetcreator.cpp b/src/eepp/ui/uiwidgetcreator.cpp index a871a7f79..e8f9b13cd 100644 --- a/src/eepp/ui/uiwidgetcreator.cpp +++ b/src/eepp/ui/uiwidgetcreator.cpp @@ -80,6 +80,9 @@ void UIWidgetCreator::createBaseWidgetList() { registeredWidget["loader"] = UILoader::New; registeredWidget["selectbutton"] = UISelectButton::New; registeredWidget["window"] = UIWindow::New; + registeredWidget["windowvbox"] = UIWindow::NewVBox; + registeredWidget["windowhbox"] = UIWindow::NewHBox; + registeredWidget["windowrellay"] = UIWindow::NewRelLay; registeredWidget["scrollview"] = UIScrollView::New; registeredWidget["textureregion"] = UITextureRegion::New; registeredWidget["touchdraggable"] = UITouchDraggableWidget::New; diff --git a/src/eepp/ui/uiwindow.cpp b/src/eepp/ui/uiwindow.cpp index bd91efca6..1c98bd69a 100644 --- a/src/eepp/ui/uiwindow.cpp +++ b/src/eepp/ui/uiwindow.cpp @@ -32,6 +32,18 @@ UIWindow* UIWindow::New() { return eeNew( UIWindow, ( SIMPLE_LAYOUT ) ); } +UIWindow* UIWindow::NewVBox() { + return eeNew( UIWindow, ( VERTICAL_LINEAR_LAYOUT ) ); +} + +UIWindow* UIWindow::NewHBox() { + return eeNew( UIWindow, ( HORIZONTAL_LINEAR_LAYOUT ) ); +} + +UIWindow* UIWindow::NewRelLay() { + return eeNew( UIWindow, ( RELATIVE_LAYOUT ) ); +} + UIWindow::UIWindow( UIWindow::WindowBaseContainerType type ) : UIWindow( type, StyleConfig() ) {} UIWindow::UIWindow( UIWindow::WindowBaseContainerType type, const StyleConfig& windowStyleConfig ) : @@ -65,9 +77,13 @@ UIWindow::UIWindow( UIWindow::WindowBaseContainerType type, const StyleConfig& w getUISceneNode()->windowAdd( this ); switch ( type ) { - case LINEAR_LAYOUT: + case VERTICAL_LINEAR_LAYOUT: mContainer = UILinearLayout::NewWithTag( "window::container", UIOrientation::Vertical ); break; + case HORIZONTAL_LINEAR_LAYOUT: + mContainer = + UILinearLayout::NewWithTag( "window::container", UIOrientation::Horizontal ); + break; case RELATIVE_LAYOUT: mContainer = UIRelativeLayout::NewWithTag( "window::container" ); break; diff --git a/src/tools/ecode/plugins/debugger/dap/messages.hpp b/src/tools/ecode/plugins/debugger/dap/messages.hpp index 2362cd2be..da604feea 100644 --- a/src/tools/ecode/plugins/debugger/dap/messages.hpp +++ b/src/tools/ecode/plugins/debugger/dap/messages.hpp @@ -60,6 +60,7 @@ static const auto DAP_OUTPUT = "output"sv; // fields static const auto DAP_NAME = "name"sv; +static const auto DAP_VALUE = "value"sv; static const auto DAP_SYSTEM_PROCESS_ID = "systemProcessId"sv; static const auto DAP_IS_LOCAL_PROCESS = "isLocalProcess"sv; static const auto DAP_POINTER_SIZE = "pointerSize"sv; diff --git a/src/tools/ecode/plugins/debugger/dap/protocol.cpp b/src/tools/ecode/plugins/debugger/dap/protocol.cpp index d221dc66e..32825ea47 100644 --- a/src/tools/ecode/plugins/debugger/dap/protocol.cpp +++ b/src/tools/ecode/plugins/debugger/dap/protocol.cpp @@ -86,8 +86,8 @@ template json toJsonArray( const std::vector& items ) { namespace ecode::dap { Message::Message( const json& body ) : - id( body[DAP_ID].get() ), - format( body["format"].get() ), + id( body.value( DAP_ID, 0 ) ), + format( body.value( "format", "" ) ), variables( parseOptionalStringMap( body, "variables" ) ), sendTelemetry( parseOptionalBool( body, "sendTelemetry" ) ), showUser( parseOptionalBool( body, "showUser" ) ), @@ -123,7 +123,7 @@ Output::Output( const json& body ) : column( parseOptionalInt( body, DAP_COLUMN ) ), data( body.contains( DAP_DATA ) ? body[DAP_DATA] : nlohmann::json{} ) { if ( body.contains( DAP_GROUP ) ) { - const auto value = body[DAP_GROUP].get(); + const auto value = body.value( DAP_GROUP, "" ); if ( DAP_START == value ) { group = Group::Start; } else if ( "startCollapsed" == value ) { @@ -133,7 +133,7 @@ Output::Output( const json& body ) : } } if ( body.contains( DAP_CATEGORY ) ) { - const auto value = body[DAP_CATEGORY].get(); + const auto value = body.value( DAP_CATEGORY, "" ); if ( "console" == value ) { category = Category::Console; } else if ( "important" == value ) { @@ -222,8 +222,7 @@ json Source::toJson() const { } Checksum::Checksum( const json& body ) : - checksum( body[DAP_CHECKSUM].get() ), - algorithm( body[DAP_ALGORITHM].get() ) {} + checksum( body.value( DAP_CHECKSUM, "" ) ), algorithm( body.value( DAP_ALGORITHM, "" ) ) {} json Checksum::toJson() const { json out; @@ -244,10 +243,10 @@ Capabilities::Capabilities( const json& body ) : supportsGotoTargetsRequest( body.value( "supportsGotoTargetsRequest", false ) ) {} ThreadEvent::ThreadEvent( const json& body ) : - reason( body[DAP_REASON].get() ), threadId( body[DAP_THREAD_ID].get() ) {} + reason( body.value( DAP_REASON, "" ) ), threadId( body.value( DAP_THREAD_ID, 0 ) ) {} StoppedEvent::StoppedEvent( const json& body ) : - reason( body[DAP_REASON].get() ), + reason( body.value( DAP_REASON, "" ) ), description( parseOptionalString( body, "description" ) ), threadId( body.value( DAP_THREAD_ID, 1 ) ), preserveFocusHint( parseOptionalBool( body, "preserveFocusHint" ) ), @@ -256,7 +255,7 @@ StoppedEvent::StoppedEvent( const json& body ) : hitBreakpointsIds( parseOptionalIntList( body, "hitBreakpointsIds" ) ) {} DapThread::DapThread( const json& body ) : - id( body[DAP_ID].get() ), name( body[DAP_NAME].get() ) {} + id( body.value( DAP_ID, 1 ) ), name( body.value( DAP_NAME, "" ) ) {} DapThread::DapThread( const int id ) : id( id ), name( std::string() ) {} @@ -265,11 +264,11 @@ std::vector DapThread::parseList( const json& threads ) { } StackFrame::StackFrame( const json& body ) : - id( body[DAP_ID].get() ), - name( body[DAP_NAME].get() ), + id( body.value( DAP_ID, 0 ) ), + name( body.value( DAP_NAME, "" ) ), source( parseOptionalObject( body, DAP_SOURCE ) ), - line( body[DAP_LINE].get() ), - column( body[DAP_COLUMN].get() ), + line( body.value( DAP_LINE, 0 ) ), + column( body.value( DAP_COLUMN, 0 ) ), endLine( parseOptionalInt( body, "endLine" ) ), canRestart( parseOptionalBool( body, "canRestart" ) ), instructionPointerReference( parseOptionalString( body, "instructionPointerReference" ) ), @@ -284,7 +283,7 @@ StackTraceInfo::StackTraceInfo( const json& body ) : Module::Module( const json& body ) : id_int( parseOptionalInt( body, DAP_ID ) ), id_str( parseOptionalString( body, DAP_ID ) ), - name( body[DAP_NAME].get() ), + name( body.value( DAP_NAME, "" ) ), path( parseOptionalString( body, DAP_PATH ) ), isOptimized( parseOptionalBool( body, "isOptimized" ) ), isUserCode( parseOptionalBool( body, "isUserCode" ) ), @@ -295,12 +294,12 @@ Module::Module( const json& body ) : addressRange( parseOptionalString( body, "addressRange" ) ) {} ModuleEvent::ModuleEvent( const json& body ) : - reason( body[DAP_REASON].get() ), module( Module( body["module"] ) ) {} + reason( body.value( DAP_REASON, "" ) ), module( Module( body["module"] ) ) {} Scope::Scope( const json& body ) : - name( body[DAP_NAME].get() ), + name( body.value( DAP_NAME, "" ) ), presentationHint( parseOptionalString( body, DAP_PRESENTATION_HINT ) ), - variablesReference( body[DAP_VARIABLES_REFERENCE].get() ), + variablesReference( body.value( DAP_VARIABLES_REFERENCE, 0 ) ), namedVariables( parseOptionalInt( body, "namedVariables" ) ), indexedVariables( parseOptionalInt( body, "indexedVariables" ) ), expensive( parseOptionalBool( body, "expensive" ) ), @@ -318,11 +317,11 @@ std::vector Scope::parseList( const json& scopes ) { } Variable::Variable( const json& body ) : - name( body[DAP_NAME].get() ), - value( body["value"].get() ), + name( body.value( DAP_NAME, "" ) ), + value( body.value( DAP_VALUE, "" ) ), type( parseOptionalString( body, DAP_TYPE ) ), evaluateName( parseOptionalString( body, "evaluateName" ) ), - variablesReference( body[DAP_VARIABLES_REFERENCE].get() ), + variablesReference( body.value( DAP_VARIABLES_REFERENCE, 0 ) ), namedVariables( parseOptionalInt( body, "namedVariables" ) ), indexedVariables( parseOptionalInt( body, "indexedVariables" ) ), memoryReference( parseOptionalString( body, "memoryReference" ) ) {} @@ -339,15 +338,14 @@ ModulesInfo::ModulesInfo( const json& body ) : totalModules( parseOptionalInt( body, "totalModules" ) ) {} ContinuedEvent::ContinuedEvent( const json& body ) : - threadId( body[DAP_THREAD_ID].get() ), + threadId( body.value( DAP_THREAD_ID, 1 ) ), allThreadsContinued( parseOptionalBool( body, DAP_ALL_THREADS_CONTINUED ) ) {} ContinuedEvent::ContinuedEvent( int threadId, bool allThreadsContinued ) : threadId( threadId ), allThreadsContinued( allThreadsContinued ) {} SourceContent::SourceContent( const json& body ) : - content( body["content"].get() ), - mimeType( parseOptionalString( body, "mimeType" ) ) {} + content( body.value( "content", "" ) ), mimeType( parseOptionalString( body, "mimeType" ) ) {} SourceContent::SourceContent( const std::string& path ) { const FileInfo file( path ); @@ -358,7 +356,7 @@ SourceContent::SourceContent( const std::string& path ) { } SourceBreakpoint::SourceBreakpoint( const json& body ) : - line( body[DAP_LINE].get() ), + line( body.value( DAP_LINE, 0 ) ), column( parseOptionalInt( body, DAP_COLUMN ) ), condition( parseOptionalString( body, DAP_CONDITION ) ), hitCondition( parseOptionalString( body, DAP_HIT_CONDITION ) ), @@ -399,21 +397,20 @@ Breakpoint::Breakpoint( const json& body ) : Breakpoint::Breakpoint( const int line ) : line( line ) {} BreakpointEvent::BreakpointEvent( const json& body ) : - reason( body[DAP_REASON].get() ), - breakpoint( Breakpoint( body[DAP_BREAKPOINT] ) ) {} + reason( body.value( DAP_REASON, "" ) ), breakpoint( Breakpoint( body[DAP_BREAKPOINT] ) ) {} EvaluateInfo::EvaluateInfo( const json& body ) : - result( body[DAP_RESULT].get() ), + result( body.value( DAP_RESULT, "" ) ), type( parseOptionalString( body, DAP_TYPE ) ), - variablesReference( body[DAP_VARIABLES_REFERENCE].get() ), + variablesReference( body.value( DAP_VARIABLES_REFERENCE, 0 ) ), namedVariables( parseOptionalInt( body, "namedVariables" ) ), indexedVariables( parseOptionalInt( body, "indexedVariables" ) ), memoryReference( parseOptionalString( body, "memoryReference" ) ) {} GotoTarget::GotoTarget( const json& body ) : - id( body[DAP_ID].get() ), - label( body["label"].get() ), - line( body[DAP_LINE].get() ), + id( body.value( DAP_ID, 0 ) ), + label( body.value( "label", "" ) ), + line( body.value( DAP_LINE, 0 ) ), column( parseOptionalInt( body, DAP_COLUMN ) ), endLine( parseOptionalInt( body, DAP_END_LINE ) ), endColumn( parseOptionalInt( body, DAP_END_COLUMN ) ), diff --git a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp index 9ebe8c18b..319f14a10 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp @@ -92,6 +92,8 @@ void DebuggerClientListener::initUI() { static_cast( modelEvent->getModelIndex().internalData() ); mClient->variables( node->var.variablesReference ); mVariablesHolder->saveExpandedState( modelEvent->getModelIndex() ); + } else if ( modelEvent->getModelEventType() == Abstract::ModelEventType::CloseTree ) { + mVariablesHolder->removeExpandedState( modelEvent->getModelIndex() ); } } ); @@ -157,6 +159,11 @@ void DebuggerClientListener::debuggeeExited( int /*exitCode*/ ) { void DebuggerClientListener::debuggeeStopped( const StoppedEvent& event ) { Log::debug( "DebuggerClientListener::debuggeeStopped: reason %s", event.reason ); + if ( "exception" == event.reason ) { + mPlugin->getPluginContext()->getNotificationCenter()->addNotification( + mPlugin->i18n( "debuggee_exception_triggered", "Debuggee triggered an exception" ) ); + } + mCurrentThreadId = mStoppedData->threadId ? *mStoppedData->threadId : 1; mStoppedData = event; @@ -215,14 +222,14 @@ void DebuggerClientListener::debuggingProcess( const ProcessInfo& ) {} void DebuggerClientListener::errorResponse( const std::string& command, const std::string& summary, const std::optional& /*message*/ ) { - if ( command != "evaluate" ) { - if ( command == "launch" ) { - failed(); - } + if ( command == "evaluate" ) + return; - mPlugin->getPluginContext()->getNotificationCenter()->addNotification( summary, - Seconds( 5 ) ); - } + if ( command == "launch" ) + failed(); + + mPlugin->getPluginContext()->getNotificationCenter()->addNotification( summary, Seconds( 5 ), + true ); } void DebuggerClientListener::threadChanged( const ThreadEvent& ) {} @@ -298,6 +305,10 @@ void DebuggerClientListener::stackTrace( const int threadId, StackTraceInfo&& st var.memoryReference = info->memoryReference; } mPlugin->mExpressionsHolder->upsertRootChild( std::move( var ) ); + ExpandedState::Location location{ mCurrentScopePos->first, mCurrentScopePos->second, + mCurrentFrameId }; + mPlugin->mExpressionsHolder->restoreExpandedState( + location, mClient, getStatusDebuggerController()->getUIExpressions(), true ); } ); } } diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp index d05e5aa63..bf20cb085 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp @@ -9,6 +9,7 @@ #include "bussocketprocess.hpp" #include "dap/debuggerclientdap.hpp" #include "models/breakpointsmodel.hpp" +#include "models/processesmodel.hpp" #include "models/variablesmodel.hpp" #include "statusdebuggercontroller.hpp" #include @@ -37,6 +38,8 @@ static constexpr auto REQUEST_TYPE_ATTACH = "attach"; namespace ecode { static constexpr auto INPUT_PATTERN = "%$%{input%:([%w_]+)%}"sv; +static constexpr auto COMMAND_PATTERN = "%$%{command%:([%w_]+)%}"sv; +static constexpr auto CMD_PICK_PROCESS = "${command:PickProcess}"sv; Plugin* DebuggerPlugin::New( PluginManager* pluginManager ) { return eeNew( DebuggerPlugin, ( pluginManager, false ) ); @@ -837,6 +840,10 @@ void DebuggerPlugin::buildStatusBar() { [this]( const int variablesReference, std::vector&& vars ) { mExpressionsHolder->addVariables( variablesReference, std::move( vars ) ); } ); + mExpressionsHolder->saveExpandedState( modelEvent->getModelIndex(), true ); + } else if ( mDebugger && mListener && + modelEvent->getModelEventType() == Abstract::ModelEventType::CloseTree ) { + mExpressionsHolder->removeExpandedState( modelEvent->getModelIndex(), true ); } } ); uiExpressions->removeEventsOfType( Event::MouseClick ); @@ -997,6 +1004,7 @@ void DebuggerPlugin::replaceKeysInJson( nlohmann::json& json, int randomPort, const std::unordered_map& solvedInputs ) { static LuaPattern inputPtrn( INPUT_PATTERN ); + static LuaPattern commandPtrn( COMMAND_PATTERN ); static constexpr auto KEY_FILE = "${file}"; static constexpr auto KEY_ARGS = "${args}"; static constexpr auto KEY_CWD = "${cwd}"; @@ -1009,23 +1017,40 @@ void DebuggerPlugin::replaceKeysInJson( auto runConfig = getPluginContext()->getProjectBuildManager()->getCurrentRunConfig(); auto buildConfig = getPluginContext()->getProjectBuildManager()->getCurrentBuild(); - const auto replaceVal = [this, &solvedInputs, &runConfig, randomPort]( std::string& val ) { + const auto replaceVal = [this, &solvedInputs, &runConfig, + randomPort]( nlohmann::json& j, std::string& val ) -> bool { if ( runConfig ) { String::replaceAll( val, KEY_FILE, runConfig->cmd ); String::replaceAll( val, KEY_CWD, runConfig->workingDir ); String::replaceAll( val, KEY_FILEDIRNAME, runConfig->workingDir ); } + String::replaceAll( val, KEY_WORKSPACEFOLDER, mProjectPath ); + if ( String::contains( val, KEY_RANDPORT ) ) String::replaceAll( val, KEY_RANDPORT, String::toString( randomPort ) ); - LuaPattern::Range matches[4]; + LuaPattern::Range matches[2]; if ( inputPtrn.matches( val, matches ) ) { std::string id( val.substr( matches[1].start, matches[1].end - matches[1].start ) ); auto solvedIdIt = solvedInputs.find( id ); if ( solvedIdIt != solvedInputs.end() ) String::replaceAll( val, String::format( "${input:%s}", id ), solvedIdIt->second ); } + + LuaPattern::Range matches2[2]; + if ( commandPtrn.matches( val, matches2 ) ) { + auto solvedIdIt = solvedInputs.find( "pid" ); + if ( solvedIdIt != solvedInputs.end() ) { + int pid = 0; + if ( String::fromString( pid, solvedIdIt->second ) ) { + j = pid; + return false; + } + } + } + + return true; }; for ( auto& j : json ) { @@ -1035,7 +1060,7 @@ void DebuggerPlugin::replaceKeysInJson( for ( auto& e : j ) { if ( e.is_string() ) { std::string val( e.get() ); - replaceVal( val ); + replaceVal( e, val ); e = std::move( val ); } } @@ -1046,7 +1071,7 @@ void DebuggerPlugin::replaceKeysInJson( auto argsArr = nlohmann::json::array(); auto args = Process::parseArgs( runConfig->args ); for ( auto arg : args ) { - replaceVal( arg ); + replaceVal( j, arg ); argsArr.push_back( arg ); } j = std::move( argsArr ); @@ -1073,8 +1098,8 @@ void DebuggerPlugin::replaceKeysInJson( } } } - replaceVal( val ); - j = std::move( val ); + if ( replaceVal( j, val ) ) + j = std::move( val ); } } } @@ -1094,9 +1119,9 @@ DebuggerPlugin::needsToResolveInputs( nlohmann::json& json ) { auto it = mDapInputs.find( id ); if ( it != mDapInputs.end() ) inputs[it->first] = it->second; - } else if ( String::contains( val, "${pid}" ) ) { - DapConfigurationInput dci{ "pid", "Process ID", "pid", "", {} }; - inputs["pid"] = dci; + } else if ( String::contains( val, CMD_PICK_PROCESS ) ) { + DapConfigurationInput dci{ "pid", "Process ID", "pickprocess", "", {} }; + inputs[std::string{ CMD_PICK_PROCESS }] = dci; } } }; @@ -1473,6 +1498,26 @@ void DebuggerPlugin::resolveInputsBeforeRun( DapConfig config, std::unordered_map solvedInputs ) { if ( !inputs.empty() ) { auto input = inputs.begin()->second; + if ( input.type == "pickprocess"sv ) { + UIWindow* win = processPicker(); + win->setTitle( i18n( "pick_process", "Pick Process" ) ); + win->center(); + win->showWhenReady(); + win->on( Event::OnConfirm, [inputs, win, debugger, config, solvedInputs, + this]( const Event* ) mutable { + UITableView* uiTableView = win->find( "processes_list" )->asType(); + auto model = static_cast( uiTableView->getModel() ); + std::string inputData( + model->data( uiTableView->getSelection().first(), ModelRole::Display ) + .toString() ); + std::string id( inputs.begin()->second.id ); + solvedInputs[id] = inputData; + inputs.erase( inputs.begin() ); + resolveInputsBeforeRun( inputs, debugger, config, solvedInputs ); + win->closeWindow(); + } ); + return; + } bool isPick = input.type == "pickstring"; UIMessageBox* msgBox = UIMessageBox::New( isPick ? UIMessageBox::DROPDOWNLIST : UIMessageBox::INPUT, input.description ); @@ -1534,17 +1579,17 @@ void DebuggerPlugin::prepareAndRun( DapTool debugger, DapConfig config, } ); } -UIWidget* DebuggerPlugin::processIdPicker() { +UIWindow* DebuggerPlugin::processPicker() { static constexpr auto PROCESS_PICKER_LAYOUT = R"html( - - + + - + - + @@ -1552,9 +1597,47 @@ UIWidget* DebuggerPlugin::processIdPicker() { )html"; - UIWidget* widget = getUISceneNode()->loadLayoutFromString( PROCESS_PICKER_LAYOUT ); - - return widget; + UIWindow* win = + getUISceneNode()->loadLayoutFromString( PROCESS_PICKER_LAYOUT )->asType(); + UITextInput* uiFilter = win->find( "processes_filter" )->asType(); + UITableView* uiTableView = win->find( "processes_list" )->asType(); + UIPushButton* uiPickBut = win->find( "pick_process" )->asType(); + UIPushButton* uiUpdateList = win->find( "update_process_list" )->asType(); + UIPushButton* uiCancel = win->find( "cancel_pick" )->asType(); + auto model = std::make_shared( std::vector>{}, + getUISceneNode() ); + uiTableView->setAutoColumnsWidth( true ); + uiTableView->setFitAllColumnsToWidget( true ); + uiTableView->setMainColumn( 1 ); + uiTableView->setModel( model ); + uiTableView->onModelEvent( [win]( const ModelEvent* event ) { + if ( event->getModelEventType() == ModelEventType::Open ) + win->sendCommonEvent( Event::OnConfirm ); + } ); + uiTableView->setOnSelectionChange( [uiTableView, uiPickBut]() { + uiPickBut->setEnabled( !uiTableView->getSelection().isEmpty() ); + } ); + uiFilter->on( Event::OnValueChange, [uiFilter, uiTableView, model]( auto ) { + model->setFilter( uiFilter->getText() ); + uiTableView->setSelection( model->index( 0 ) ); + } ); + uiFilter->on( Event::OnPressEnter, [uiTableView, win]( auto ) { + if ( !uiTableView->getSelection().isEmpty() ) + win->sendCommonEvent( Event::OnConfirm ); + } ); + uiUpdateList->onClick( [this, model]( auto ) { + mThreadPool->run( [model] { model->setProcesses( Sys::listProcesses() ); } ); + } ); + mThreadPool->run( [model, uiTableView] { + model->setProcesses( Sys::listProcesses() ); + uiTableView->scrollToBottom(); + } ); + uiCancel->onClick( [win]( auto ) { win->closeWindow(); } ); + uiPickBut->onClick( [win]( auto ) { win->sendCommonEvent( Event::OnConfirm ); } ); + win->on( Event::OnWindowReady, [uiFilter]( auto ) { uiFilter->setFocus(); } ); + win->setKeyBindingCommand( "close-window", [win]() { win->closeWindow(); } ); + win->addKeyBinding( { KEY_ESCAPE }, "close-window" ); + return win; } std::optional @@ -1621,7 +1704,8 @@ void DebuggerPlugin::run( const std::string& debugger, ProtocolSettings&& protoc if ( !cmdOpt && ( protocolSettings.launchCommand == REQUEST_TYPE_LAUNCH || ( protocolSettings.launchCommand == REQUEST_TYPE_ATTACH && - protocolSettings.launchRequest.value( "mode", "" ) == "local" ) ) ) { + protocolSettings.launchRequest.value( "mode", "" ) == "local" && + protocolSettings.launchRequest.contains( "program" ) ) ) ) { auto msg = String::format( i18n( "debugger_binary_not_found", "Debugger binary not found. Binary \"%s\" must be installed." ) @@ -1640,6 +1724,8 @@ void DebuggerPlugin::run( const std::string& debugger, ProtocolSettings&& protoc mDebugger = std::make_unique( protocolSettings, std::move( bus ) ); } else if ( protocolSettings.launchCommand == REQUEST_TYPE_ATTACH ) { auto mode = protocolSettings.launchRequest.value( "mode", "" ); + if ( mode.empty() ) + mode = "local"; Connection con; con.host = protocolSettings.launchRequest.value( "host", "localhost" ); @@ -1653,11 +1739,23 @@ void DebuggerPlugin::run( const std::string& debugger, ProtocolSettings&& protoc return; } + bool useProcessId = protocolSettings.launchRequest.contains( "pid" ) && + protocolSettings.launchRequest["pid"].is_number() && + protocolSettings.launchRequest.value( "pid", 0 ) != 0; + + bool useProgram = protocolSettings.launchRequest.contains( "program" ) && + protocolSettings.launchRequest["program"].is_string() && + !protocolSettings.launchRequest.value( "program", "" ).empty(); + if ( mode == "local" ) { if ( useSocket ) { auto bus = std::make_unique( cmd, con ); mDebugger = std::make_unique( protocolSettings, std::move( bus ) ); + } else if ( useProcessId || useProgram ) { + auto bus = std::make_unique( cmd ); + mDebugger = + std::make_unique( protocolSettings, std::move( bus ) ); } else { // Unsuported } diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp index ca48a38db..0d13caf92 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp @@ -237,7 +237,7 @@ class DebuggerPlugin : public PluginBase { void prepareAndRun( DapTool debugger, DapConfig config, std::unordered_map solvedInputs ); - UIWidget* processIdPicker(); + UIWindow* processPicker(); }; } // namespace ecode diff --git a/src/tools/ecode/plugins/debugger/models/processesmodel.cpp b/src/tools/ecode/plugins/debugger/models/processesmodel.cpp index ed174a799..d5b9651e1 100644 --- a/src/tools/ecode/plugins/debugger/models/processesmodel.cpp +++ b/src/tools/ecode/plugins/debugger/models/processesmodel.cpp @@ -8,7 +8,8 @@ ProcessesModel::ProcessesModel( std::vector>&& pr mProcesses( std::move( processes ) ), mSceneNode( sceneNode ) {} size_t ProcessesModel::rowCount( const ModelIndex& ) const { - return mProcesses.size(); + Lock l( mResourceLock ); + return mFilter.empty() ? mProcesses.size() : mProcessesFiltered.size(); } size_t ProcessesModel::columnCount( const ModelIndex& ) const { @@ -21,16 +22,66 @@ std::string ProcessesModel::columnName( const size_t& colIdx ) const { } Variant ProcessesModel::data( const ModelIndex& modelIndex, ModelRole role ) const { + Lock l( mResourceLock ); switch ( modelIndex.column() ) { case Columns::Name: - return Variant( mProcesses[modelIndex.row()].second ); + return Variant( mFilter.empty() ? mProcesses[modelIndex.row()].second.c_str() + : mProcessesFiltered[modelIndex.row()].second.data() ); case Columns::ID: default: return role == ModelRole::Display - ? Variant( String::toString( mProcesses[modelIndex.row()].first ) ) - : Variant( mProcesses[modelIndex.row()].first ); + ? Variant( String::toString( + mFilter.empty() ? mProcesses[modelIndex.row()].first + : mProcessesFiltered[modelIndex.row()].first ) ) + : Variant( mFilter.empty() ? mProcesses[modelIndex.row()].first + : mProcessesFiltered[modelIndex.row()].first ); } return {}; } +bool containsIgnoreCase( std::string_view str, std::string_view substr ) { + return std::search( str.begin(), str.end(), substr.begin(), substr.end(), + []( char ch1, char ch2 ) { + return std::tolower( ch1 ) == std::tolower( ch2 ); + } ) != str.end(); +} + +void ProcessesModel::setFilter( const std::string& filter ) { + if ( filter == mFilter ) + return; + Lock l( mResourceLock ); + mFilter = filter; + mProcessesFiltered.clear(); + for ( const auto& process : mProcesses ) { + if ( containsIgnoreCase( process.second, filter ) ) + mProcessesFiltered.emplace_back( process.first, process.second ); + } + invalidate(); +} + +void ProcessesModel::setProcesses( std::vector>&& processes ) { + { + Lock l( mResourceLock ); + mProcesses = std::move( processes ); + } + if ( !mFilter.empty() ) { + auto filter = mFilter; + mFilter.clear(); + setFilter( filter ); + } else { + invalidate(); + } +} + +std::pair ProcessesModel::getProcess( const ModelIndex& index ) const { + if ( !index.isValid() ) + return {}; + Lock l( mResourceLock ); + if ( mFilter.empty() ) { + return mProcesses[index.row()]; + } + return { mProcessesFiltered[index.row()].first, + std::string{ mProcessesFiltered[index.row()].second } }; +} + } // namespace ecode diff --git a/src/tools/ecode/plugins/debugger/models/processesmodel.hpp b/src/tools/ecode/plugins/debugger/models/processesmodel.hpp index aaf9d68fe..a91a9780a 100644 --- a/src/tools/ecode/plugins/debugger/models/processesmodel.hpp +++ b/src/tools/ecode/plugins/debugger/models/processesmodel.hpp @@ -27,8 +27,16 @@ class ProcessesModel : public Model { virtual Variant data( const ModelIndex& modelIndex, ModelRole role ) const; + void setFilter( const std::string& filter ); + + void setProcesses( std::vector>&& processes ); + + std::pair getProcess( const ModelIndex& index ) const; + protected: std::vector> mProcesses; + std::vector> mProcessesFiltered; + std::string mFilter; UISceneNode* mSceneNode{ nullptr }; }; diff --git a/src/tools/ecode/plugins/debugger/models/variablesmodel.cpp b/src/tools/ecode/plugins/debugger/models/variablesmodel.cpp index 9d955327b..f5836c031 100644 --- a/src/tools/ecode/plugins/debugger/models/variablesmodel.cpp +++ b/src/tools/ecode/plugins/debugger/models/variablesmodel.cpp @@ -234,7 +234,6 @@ void VariablesHolder::clear( bool all ) { mRootNode->clear(); if ( all ) { mNodeMap.clear(); - mExpandedStates.clear(); mCurrentLocation = {}; } } @@ -254,8 +253,8 @@ VariablePath VariablesHolder::buildVariablePath( ModelVariableNode* node ) const return path; } -void VariablesHolder::saveExpandedState( const ModelIndex& index ) { - if ( !mCurrentLocation ) +void VariablesHolder::saveExpandedState( const ModelIndex& index, bool uniqueLocation ) { + if ( !mCurrentLocation && !uniqueLocation ) return; ModelVariableNode* node = static_cast( index.internalData() ); @@ -263,7 +262,33 @@ void VariablesHolder::saveExpandedState( const ModelIndex& index ) { return; auto nodePath = buildVariablePath( node ); - mExpandedStates[*mCurrentLocation].insert( std::move( nodePath ) ); + + ExpandedState::Location location = + uniqueLocation ? ExpandedState::Location{} : *mCurrentLocation; + + mExpandedStates[location].insert( std::move( nodePath ) ); +} + +void VariablesHolder::removeExpandedState( const ModelIndex& index, bool uniqueLocation ) { + if ( !mCurrentLocation && !uniqueLocation ) + return; + + ExpandedState::Location location = + uniqueLocation ? ExpandedState::Location{} : *mCurrentLocation; + + auto locIt = mExpandedStates.find( location ); + if ( locIt == mExpandedStates.end() ) + return; + + ModelVariableNode* node = static_cast( index.internalData() ); + if ( !node ) + return; + + auto nodePath = buildVariablePath( node ); + + auto stateIt = locIt->second.find( nodePath ); + if ( stateIt != locIt->second.end() ) + locIt->second.erase( stateIt ); } bool VariablesHolder::resolvePath( std::vector path, DebuggerClient* client, @@ -315,10 +340,11 @@ static int getLocationDistance( const ExpandedState::Location& loc1, } bool VariablesHolder::restoreExpandedState( const ExpandedState::Location& location, - DebuggerClient* client, UITreeView* uiVariables ) { + DebuggerClient* client, UITreeView* uiVariables, + bool uniqueLocation ) { mCurrentLocation = location; - auto it = mExpandedStates.find( location ); + auto it = uniqueLocation ? mExpandedStates.begin() : mExpandedStates.find( location ); if ( it == mExpandedStates.end() ) { // Find the nearest expanded state const ExpandedState::Location* nearestLoc = nullptr; diff --git a/src/tools/ecode/plugins/debugger/models/variablesmodel.hpp b/src/tools/ecode/plugins/debugger/models/variablesmodel.hpp index f1a5f8005..70f4137d1 100644 --- a/src/tools/ecode/plugins/debugger/models/variablesmodel.hpp +++ b/src/tools/ecode/plugins/debugger/models/variablesmodel.hpp @@ -135,10 +135,12 @@ class VariablesHolder { void clear( bool all = false ); - void saveExpandedState( const ModelIndex& index ); + void saveExpandedState( const ModelIndex& index, bool uniqueLocation = false ); + + void removeExpandedState( const ModelIndex& index, bool uniqueLocation = false ); bool restoreExpandedState( const ExpandedState::Location& location, DebuggerClient* client, - UITreeView* uiVariables ); + UITreeView* uiVariables, bool uniqueLocation = false ); std::shared_ptr getModel() { return mModel; } diff --git a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp index 16685ddd9..0f55ee0bf 100644 --- a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp @@ -2070,7 +2070,8 @@ void LSPClientPlugin::showDocumentSymbols( UICodeEditor* editor ) { return; getUISceneNode()->getRoot()->setFocus(); - UIWindow* win = UIWindow::NewOpt( UIMessageBox::WindowBaseContainerType::LINEAR_LAYOUT ); + UIWindow* win = + UIWindow::NewOpt( UIMessageBox::WindowBaseContainerType::VERTICAL_LINEAR_LAYOUT ); win->setMinWindowSize( 400, getUISceneNode()->getSize().getHeight() * 0.7f ); win->setKeyBindingCommand( "closeWindow", [win, editor] { win->closeWindow();