diff --git a/bin/assets/plugins/debugger.json b/bin/assets/plugins/debugger.json index 306db88d2..37ee3398e 100644 --- a/bin/assets/plugins/debugger.json +++ b/bin/assets/plugins/debugger.json @@ -17,8 +17,7 @@ "program": "${file}", "args": "${args}", "cwd": "${cwd}", - "env": "${env}", - "stopOnEntry": "${stopOnEntry}" + "env": "${env}" } }, { @@ -29,7 +28,6 @@ "args": "${args}", "cwd": "${cwd}", "env": "${env}", - "stopOnEntry": "${stopOnEntry}", "runInTerminal": true } }, @@ -52,8 +50,7 @@ "name": "Attach to PID", "request": "attach", "arguments": { - "program": "${file}", - "pid": "${command:PickProcess}" + "pid": "${command:pickProcess}" } } ] @@ -78,8 +75,7 @@ "program": "${file}", "args": "${args}", "cwd": "${cwd}", - "env": "${env}", - "stopOnEntry": "${stopOnEntry}" + "env": "${env}" } }, { @@ -90,7 +86,6 @@ "args": "${args}", "cwd": "${cwd}", "env": "${env}", - "stopOnEntry": "${stopOnEntry}", "runInTerminal": true } }, @@ -113,8 +108,7 @@ "name": "Attach to PID", "request": "attach", "arguments": { - "program": "${file}", - "pid": "${command:PickProcess}" + "pid": "${command:pickProcess}" } } ] @@ -158,6 +152,54 @@ "program": "${file}", "args": "${args}" } + }, + { + "name": "Attach to PID", + "request": "attach", + "arguments": { + "mode": "local", + "processId": "${command:pickProcess}" + } + } + ] + }, + { + "name": "debugpy", + "url": "https://github.com/microsoft/debugpy", + "type": "python", + "languages": [ "python" ], + "run": { + "command": "python3", + "command_arguments": ["-m", "debugpy", "--listen", "${randPort}"], + "supportsSourceRequest": false + }, + "configurations": [ + { + "name": "Launch", + "command_arguments": ["--wait-for-client", "${file}", "${args}"], + "request": "attach", + "arguments": { + "stopOnEntry": true, + "redirectOutput": true + } + }, + { + "name": "Launch module", + "command_arguments": ["--wait-for-client", "-m", "${command:pickString"], + "request": "attach", + "arguments": { + "stopOnEntry": true, + "redirectOutput": true + } + }, + { + "name": "Attach", + "command_arguments": ["--pid", "${command:pickProcess}"], + "request": "attach", + "arguments": { + "stopOnEntry": true, + "redirectOutput": true + } } ] } diff --git a/include/eepp/core/string.hpp b/include/eepp/core/string.hpp index 360d57a03..8282e3c3c 100644 --- a/include/eepp/core/string.hpp +++ b/include/eepp/core/string.hpp @@ -297,6 +297,18 @@ class EE_API String { */ static bool contains( const String& haystack, const String& needle ); + /** @return True if a string contains a substring. Case-insensitive check. + * @param haystack The string to search in. + * @param needle The searched string. + */ + static bool icontains( const std::string& haystack, const std::string& needle ); + + /** @return True if a string contains a substring. Case-insensitive check. + * @param haystack The string to search in. + * @param needle The searched string. + */ + static bool icontains( const String& haystack, const String& needle ); + static int fuzzyMatch( const std::string& string, const std::string& pattern, bool allowUneven = false, bool permissive = false ); diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 2563dac2e..69fdbeeb9 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -1581,10 +1581,13 @@ ../../src/tools/ecode/plugins/debugger/busprocess.hpp ../../src/tools/ecode/plugins/debugger/bussocket.cpp ../../src/tools/ecode/plugins/debugger/bussocket.hpp +../../src/tools/ecode/plugins/debugger/bussocketprocess.cpp +../../src/tools/ecode/plugins/debugger/bussocketprocess.hpp ../../src/tools/ecode/plugins/debugger/config.cpp ../../src/tools/ecode/plugins/debugger/config.hpp ../../src/tools/ecode/plugins/debugger/debuggerclient.cpp ../../src/tools/ecode/plugins/debugger/debuggerclient.hpp +../../src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp ../../src/tools/ecode/plugins/debugger/debuggerclientlistener.hpp ../../src/tools/ecode/plugins/debugger/debuggerplugin.cpp ../../src/tools/ecode/plugins/debugger/debuggerplugin.hpp diff --git a/src/eepp/core/string.cpp b/src/eepp/core/string.cpp index c3cbb8a65..2d1c1e29b 100644 --- a/src/eepp/core/string.cpp +++ b/src/eepp/core/string.cpp @@ -1186,6 +1186,20 @@ bool String::contains( const String& haystack, const String& needle ) { return haystack.find( needle ) != String::InvalidPos; } +bool String::icontains( const std::string& haystack, const std::string& needle ) { + return std::search( haystack.begin(), haystack.end(), needle.begin(), needle.end(), + []( char ch1, char ch2 ) { + return std::tolower( ch1 ) == std::tolower( ch2 ); + } ) != haystack.end(); +} + +bool String::icontains( const String& haystack, const String& needle ) { + return std::search( haystack.begin(), haystack.end(), needle.begin(), needle.end(), + []( String::StringBaseType ch1, String::StringBaseType ch2 ) { + return std::tolower( ch1 ) == std::tolower( ch2 ); + } ) != haystack.end(); +} + void String::replaceAll( std::string& target, const std::string& that, const std::string& with ) { std::string::size_type pos = 0; diff --git a/src/eepp/system/process.cpp b/src/eepp/system/process.cpp index 1051cf0b3..13cd16925 100644 --- a/src/eepp/system/process.cpp +++ b/src/eepp/system/process.cpp @@ -107,7 +107,7 @@ Process::~Process() { mStdOutThread.join(); if ( mStdErrThread.joinable() ) mStdErrThread.join(); - eeFree( mProcess ); + eeSAFE_FREE( mProcess ); } bool Process::create( const std::string& command, Uint32 options, diff --git a/src/tools/ecode/plugins/debugger/bus.hpp b/src/tools/ecode/plugins/debugger/bus.hpp index d9ecd021f..c68d35143 100644 --- a/src/tools/ecode/plugins/debugger/bus.hpp +++ b/src/tools/ecode/plugins/debugger/bus.hpp @@ -20,6 +20,8 @@ class Bus { virtual size_t write( const char* buffer, const size_t& size ) = 0; + virtual bool hasProcess() { return false; } + virtual ~Bus() {} protected: diff --git a/src/tools/ecode/plugins/debugger/busprocess.hpp b/src/tools/ecode/plugins/debugger/busprocess.hpp index 866a5b58a..f5cd6d918 100644 --- a/src/tools/ecode/plugins/debugger/busprocess.hpp +++ b/src/tools/ecode/plugins/debugger/busprocess.hpp @@ -20,6 +20,8 @@ class BusProcess : public Bus { size_t write( const char* buffer, const size_t& size ) override; + bool hasProcess() override { return true; } + protected: Command mCommand; Process mProcess; diff --git a/src/tools/ecode/plugins/debugger/bussocket.cpp b/src/tools/ecode/plugins/debugger/bussocket.cpp index acadf73d1..8a642b29a 100644 --- a/src/tools/ecode/plugins/debugger/bussocket.cpp +++ b/src/tools/ecode/plugins/debugger/bussocket.cpp @@ -1,5 +1,7 @@ #include "bussocket.hpp" #include +#include +#include namespace ecode { @@ -10,11 +12,18 @@ BusSocket::~BusSocket() { } bool BusSocket::start() { - bool res = - mSocket.connect( IpAddress( mConnection.host ), mConnection.port ) == Socket::Status::Done; - if ( res ) - setState( State::Running ); - return res; + Clock clock; + while ( clock.getElapsedTime() < Seconds( 3 ) ) { + bool res = mSocket.connect( IpAddress( mConnection.host ), mConnection.port, + Seconds( 1 ) ) == Socket::Status::Done; + if ( res ) { + setState( State::Running ); + return res; + } else { + Sys::sleep( Milliseconds( 50 ) ); + } + } + return false; } bool BusSocket::close() { diff --git a/src/tools/ecode/plugins/debugger/bussocket.hpp b/src/tools/ecode/plugins/debugger/bussocket.hpp index 23f7ea4d3..b789d133b 100644 --- a/src/tools/ecode/plugins/debugger/bussocket.hpp +++ b/src/tools/ecode/plugins/debugger/bussocket.hpp @@ -20,6 +20,8 @@ class BusSocket : public Bus { size_t write( const char* buffer, const size_t& size ) override; + bool hasProcess() override { return false; } + protected: Connection mConnection; TcpSocket mSocket; diff --git a/src/tools/ecode/plugins/debugger/bussocketprocess.hpp b/src/tools/ecode/plugins/debugger/bussocketprocess.hpp index a5735199e..424c4e61d 100644 --- a/src/tools/ecode/plugins/debugger/bussocketprocess.hpp +++ b/src/tools/ecode/plugins/debugger/bussocketprocess.hpp @@ -20,6 +20,8 @@ class BusSocketProcess : public Bus { size_t write( const char* buffer, const size_t& size ) override; + bool hasProcess() override { return true; } + protected: Command mCommand; Connection mConnection; diff --git a/src/tools/ecode/plugins/debugger/config.cpp b/src/tools/ecode/plugins/debugger/config.cpp index f33136ec4..ce11228de 100644 --- a/src/tools/ecode/plugins/debugger/config.cpp +++ b/src/tools/ecode/plugins/debugger/config.cpp @@ -29,7 +29,7 @@ ProtocolSettings::ProtocolSettings( const nlohmann::json& configuration ) : redirectStderr( configuration.value( REDIRECT_STDERR, false ) ), redirectStdout( configuration.value( REDIRECT_STDOUT, false ) ), supportsSourceRequest( configuration.value( "supportsSourceRequest", true ) ), - launchRequest( configuration[REQUEST] ), + launchArgs( configuration[REQUEST] ), locale( "en-US" ) {} } // namespace ecode diff --git a/src/tools/ecode/plugins/debugger/config.hpp b/src/tools/ecode/plugins/debugger/config.hpp index b2d94e26c..4ca08c3d1 100644 --- a/src/tools/ecode/plugins/debugger/config.hpp +++ b/src/tools/ecode/plugins/debugger/config.hpp @@ -48,8 +48,8 @@ struct ProtocolSettings { bool redirectStderr{ false }; bool redirectStdout{ false }; bool supportsSourceRequest{ true }; - std::string launchCommand{ "launch" }; - json launchRequest; + std::string launchRequestType{ "launch" }; + json launchArgs; std::string locale{ "en-US" }; ProtocolSettings() = default; diff --git a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp index a3b4c74a7..45dadba09 100644 --- a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp +++ b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp @@ -72,8 +72,7 @@ bool DebuggerClientDap::isServerConnected() const { } bool DebuggerClientDap::supportsTerminate() const { - return mAdapterCapabilities.supportsTerminateRequest && - ( mProtocol.launchRequest.value( DAP_COMMAND, "" ) == DAP_LAUNCH ); + return mAdapterCapabilities.supportsTerminateRequest; } bool DebuggerClientDap::start() { @@ -130,10 +129,10 @@ void DebuggerClientDap::requestLaunchCommand() { return; } - if ( mProtocol.launchCommand.empty() ) + if ( mProtocol.launchRequestType.empty() ) return; - makeRequest( mProtocol.launchCommand, mProtocol.launchRequest, + makeRequest( mProtocol.launchRequestType, mProtocol.launchArgs, [this]( const Response& response, const auto& ) { if ( response.success ) { mLaunched = true; diff --git a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp index 319f14a10..afe3b2239 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp @@ -1,6 +1,6 @@ -#include "debuggerclientlistener.hpp" #include "../../notificationcenter.hpp" #include "../../statusappoutputcontroller.hpp" +#include "debuggerclientlistener.hpp" #include "debuggerplugin.hpp" #include "models/stackmodel.hpp" #include "models/threadsmodel.hpp" @@ -160,8 +160,17 @@ 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" ) ); + if ( event.description ) { + mPlugin->getPluginContext()->getNotificationCenter()->addNotification( + String::format( mPlugin + ->i18n( "debuggee_exception_triggered_desc", + "Debuggee triggered an exception: %s" ) + .toUtf8(), + *event.description ) ); + } else { + mPlugin->getPluginContext()->getNotificationCenter()->addNotification( mPlugin->i18n( + "debuggee_exception_triggered", "Debuggee triggered an exception" ) ); + } } mCurrentThreadId = mStoppedData->threadId ? *mStoppedData->threadId : 1; diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp index bf20cb085..cf4fc3433 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp @@ -39,7 +39,20 @@ 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; +static constexpr auto CMD_PICK_PROCESS = "${command:pickProcess}"sv; +static constexpr auto CMD_PICK_STRING = "${command:pickString}"sv; + +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}"; +static constexpr auto KEY_ENV = "${env}"; +static constexpr auto KEY_STOPONENTRY = "${stopOnEntry}"; +static constexpr auto KEY_WORKSPACEFOLDER = "${workspaceFolder}"; +static constexpr auto KEY_FILEDIRNAME = "${fileDirname}"; +static constexpr auto KEY_RANDPORT = "${randPort}"; +static constexpr auto KEY_PID = "${pid}"; Plugin* DebuggerPlugin::New( PluginManager* pluginManager ) { return eeNew( DebuggerPlugin, ( pluginManager, false ) ); @@ -237,7 +250,7 @@ void DebuggerPlugin::resetExpressions() { } void DebuggerPlugin::closeProject() { - exitDebugger(); + exitDebugger( true ); mDapInputs.clear(); mExpressions.clear(); @@ -365,7 +378,7 @@ void DebuggerPlugin::loadDAPConfig( const std::string& path, bool updateConfigFi auto& args = run["command_arguments"]; dapTool.run.args.reserve( args.size() ); for ( auto& arg : args ) { - if ( args.is_string() ) + if ( arg.is_string() ) dapTool.run.args.emplace_back( arg.get() ); } } else if ( run["command_arguments"].is_string() ) { @@ -389,7 +402,7 @@ void DebuggerPlugin::loadDAPConfig( const std::string& path, bool updateConfigFi auto& args = config["command_arguments"]; dapConfig.cmdArgs.reserve( args.size() ); for ( auto& arg : args ) { - if ( args.is_string() ) + if ( arg.is_string() ) dapConfig.cmdArgs.emplace_back( arg.get() ); } } else if ( config["command_arguments"].is_string() ) { @@ -714,7 +727,7 @@ void DebuggerPlugin::buildSidePanelTab() { mRunButton->onClick( [this]( auto ) { if ( mDebugger && mDebugger->started() ) { - exitDebugger(); + exitDebugger( true ); } else { runCurrentConfig(); } @@ -1000,20 +1013,59 @@ void DebuggerPlugin::updateDebuggerConfigurationList() { } } +std::vector DebuggerPlugin::replaceKeyInString( + std::string val, int randomPort, + const std::unordered_map& solvedInputs ) { + auto runConfig = getPluginContext()->getProjectBuildManager()->getCurrentRunConfig(); + + const auto replaceVal = [this, &runConfig, &solvedInputs, randomPort]( std::string& val ) { + 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[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 ) ) { + std::string id( val.substr( matches2[1].start, matches2[1].end - matches2[1].start ) ); + String::toLowerInPlace( id ); + auto solvedIdIt = solvedInputs.find( id ); + if ( solvedIdIt != solvedInputs.end() ) + val = solvedIdIt->second; + } + }; + + if ( runConfig && val == KEY_ARGS ) { + std::vector argsArr; + auto args = Process::parseArgs( runConfig->args ); + for ( auto arg : args ) { + replaceVal( arg ); + argsArr.push_back( arg ); + } + return argsArr; + } + + replaceVal( val ); + + return { val }; +} + 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}"; - static constexpr auto KEY_ENV = "${env}"; - static constexpr auto KEY_STOPONENTRY = "${stopOnEntry}"; - static constexpr auto KEY_WORKSPACEFOLDER = "${workspaceFolder}"; - static constexpr auto KEY_FILEDIRNAME = "${fileDirname}"; - static constexpr auto KEY_RANDPORT = "${randPort}"; - static constexpr auto KEY_PID = "${pid}"; auto runConfig = getPluginContext()->getProjectBuildManager()->getCurrentRunConfig(); auto buildConfig = getPluginContext()->getProjectBuildManager()->getCurrentBuild(); @@ -1040,12 +1092,20 @@ void DebuggerPlugin::replaceKeysInJson( LuaPattern::Range matches2[2]; if ( commandPtrn.matches( val, matches2 ) ) { - auto solvedIdIt = solvedInputs.find( "pid" ); + std::string name( + val.substr( matches2[0].start, matches2[0].end - matches2[0].start ) ); + std::string id( val.substr( matches2[1].start, matches2[1].end - matches2[1].start ) ); + String::toLowerInPlace( id ); + auto solvedIdIt = solvedInputs.find( id ); if ( solvedIdIt != solvedInputs.end() ) { - int pid = 0; - if ( String::fromString( pid, solvedIdIt->second ) ) { - j = pid; - return false; + if ( id == "pickprocess" ) { + int pid = 0; + if ( String::fromString( pid, solvedIdIt->second ) ) { + j = pid; + return false; + } + } else { + String::replaceAll( val, name, solvedIdIt->second ); } } } @@ -1084,7 +1144,7 @@ void DebuggerPlugin::replaceKeysInJson( j = false; } else { bool containsPid = false; - if ( ( val == KEY_PID || ( containsPid = String::contains( val, KEY_PID ) ) ) && + if ( ( val == KEY_PID || ( containsPid = String::icontains( val, KEY_PID ) ) ) && json.contains( "program" ) && json["program"].is_string() ) { auto programName( FileSystem::fileNameFromPath( json["program"].get() ) ); @@ -1106,38 +1166,48 @@ void DebuggerPlugin::replaceKeysInJson( } std::unordered_map -DebuggerPlugin::needsToResolveInputs( nlohmann::json& json ) { +DebuggerPlugin::needsToResolveInputs( nlohmann::json& json, + const std::vector& cmdArgs ) { std::unordered_map inputs; + thread_local int recursionDepth = 0; - const auto matchString = [this, &inputs]( nlohmann::json& e ) { + const auto matchString = [this, &inputs]( const std::string& val ) { static LuaPattern ptrn( INPUT_PATTERN ); - if ( e.is_string() ) { - std::string val( e ); - PatternMatcher::Range matches[4]; - if ( ptrn.matches( val, matches ) ) { - std::string id( val.substr( matches[1].start, matches[1].end - matches[1].start ) ); - auto it = mDapInputs.find( id ); - if ( it != mDapInputs.end() ) - inputs[it->first] = it->second; - } else if ( String::contains( val, CMD_PICK_PROCESS ) ) { - DapConfigurationInput dci{ "pid", "Process ID", "pickprocess", "", {} }; - inputs[std::string{ CMD_PICK_PROCESS }] = dci; - } + PatternMatcher::Range matches[4]; + if ( ptrn.matches( val, matches ) ) { + std::string id( val.substr( matches[1].start, matches[1].end - matches[1].start ) ); + auto it = mDapInputs.find( id ); + if ( it != mDapInputs.end() ) + inputs[it->first] = it->second; + } else if ( String::icontains( val, CMD_PICK_PROCESS ) ) { + DapConfigurationInput dci{ "pickprocess", "Process ID", "pickprocess", "", {} }; + inputs[std::string{ CMD_PICK_PROCESS }] = dci; + } else if ( String::icontains( val, CMD_PICK_STRING ) ) { + DapConfigurationInput dci{ "pickstring", "Name", "pickstring", "", {} }; + inputs[std::string{ CMD_PICK_STRING }] = dci; } }; + recursionDepth++; for ( auto& j : json ) { if ( j.is_object() ) { - auto rinputs = needsToResolveInputs( j ); + auto rinputs = needsToResolveInputs( j, cmdArgs ); for ( auto& i : rinputs ) inputs[std::move( i.first )] = std::move( i.second ); } else if ( j.is_array() ) { for ( auto& e : j ) - matchString( e ); + if ( e.is_string() ) + matchString( e ); } else if ( j.is_string() ) { matchString( j ); } } + recursionDepth--; + + if ( recursionDepth == 0 ) { + for ( const auto& arg : cmdArgs ) + matchString( arg ); + } return inputs; } @@ -1179,7 +1249,7 @@ void DebuggerPlugin::onRegisterDocument( TextDocument* doc ) { doc->setCommand( "debugger-start", [this] { runCurrentConfig(); } ); - doc->setCommand( "debugger-stop", [this] { exitDebugger(); } ); + doc->setCommand( "debugger-stop", [this] { exitDebugger( true ); } ); doc->setCommand( "debugger-breakpoint-toggle", [doc, this] { if ( setBreakpoint( doc, doc->getSelection().start().line() + 1 ) ) @@ -1380,6 +1450,7 @@ bool DebuggerPlugin::breakpointSetEnabled( const std::string& doc, Uint32 lineNu if ( breakpointIt != breakpoints.end() ) { breakpointIt->enabled = enabled; mBreakpointsModel->enable( doc, lineNumber, breakpointIt->enabled ); + mThreadPool->run( [this, doc] { sendFileBreakpoints( doc ); } ); getUISceneNode()->getRoot()->invalidateDraw(); return true; } @@ -1484,7 +1555,7 @@ void DebuggerPlugin::runConfig( const std::string& debugger, const std::string& return; } - auto inputs = needsToResolveInputs( configIt->args ); + auto inputs = needsToResolveInputs( configIt->args, configIt->cmdArgs ); if ( !inputs.empty() ) { resolveInputsBeforeRun( inputs, *debuggerIt, *configIt ); return; @@ -1552,22 +1623,39 @@ void DebuggerPlugin::prepareAndRun( DapTool debugger, DapConfig config, std::unordered_map solvedInputs ) { int randomPort = Math::randi( 44000, 45000 ); ProtocolSettings protocolSettings; - protocolSettings.launchCommand = config.request; + protocolSettings.launchRequestType = config.request; auto args = config.args; replaceKeysInJson( args, randomPort, solvedInputs ); - protocolSettings.launchRequest = args; + protocolSettings.launchArgs = args; protocolSettings.redirectStdout = debugger.redirectStdout; protocolSettings.redirectStderr = debugger.redirectStderr; protocolSettings.supportsSourceRequest = debugger.supportsSourceRequest; + bool forceUseProgram = false; + bool usesPorts = false; - for ( const std::string& cmdArg : config.cmdArgs ) - debugger.run.args.emplace_back( cmdArg ); + for ( auto& arg : debugger.run.args ) { + auto res( replaceKeyInString( arg, randomPort, solvedInputs ) ); + if ( res.size() == 1 && res[0] != arg ) { + if ( arg == KEY_RANDPORT ) + usesPorts = true; + arg = res[0]; + } + } + + for ( const std::string& cmdArg : config.cmdArgs ) { + if ( cmdArg == KEY_FILE || cmdArg == KEY_ARGS || cmdArg == CMD_PICK_PROCESS || + cmdArg == CMD_PICK_STRING ) + forceUseProgram = true; + auto args = replaceKeyInString( cmdArg, randomPort, solvedInputs ); + for ( const auto& arg : args ) + debugger.run.args.emplace_back( arg ); + } mThreadPool->run( [this, protocolSettings = std::move( protocolSettings ), randomPort, - debugger = std::move( debugger )]() mutable { + debugger = std::move( debugger ), forceUseProgram, usesPorts]() mutable { run( debugger.name, std::move( protocolSettings ), std::move( debugger.run ), - randomPort ); + randomPort, forceUseProgram, usesPorts ); }, [this]( const Uint64& ) { if ( !mDebugger || !mDebugger->started() ) { @@ -1699,13 +1787,14 @@ void DebuggerPlugin::initStatusDebuggerController() { } void DebuggerPlugin::run( const std::string& debugger, ProtocolSettings&& protocolSettings, - DapRunConfig&& runConfig, int /*randPort*/ ) { + DapRunConfig&& runConfig, int randPort, bool forceUseProgram, + bool usesPorts ) { std::optional cmdOpt = debuggerBinaryExists( debugger, runConfig ); - if ( !cmdOpt && ( protocolSettings.launchCommand == REQUEST_TYPE_LAUNCH || - ( protocolSettings.launchCommand == REQUEST_TYPE_ATTACH && - protocolSettings.launchRequest.value( "mode", "" ) == "local" && - protocolSettings.launchRequest.contains( "program" ) ) ) ) { + if ( !cmdOpt && ( protocolSettings.launchRequestType == REQUEST_TYPE_LAUNCH || + ( protocolSettings.launchRequestType == REQUEST_TYPE_ATTACH && + protocolSettings.launchArgs.value( "mode", "" ) == "local" && + protocolSettings.launchArgs.contains( "program" ) ) ) ) { auto msg = String::format( i18n( "debugger_binary_not_found", "Debugger binary not found. Binary \"%s\" must be installed." ) @@ -1719,33 +1808,39 @@ void DebuggerPlugin::run( const std::string& debugger, ProtocolSettings&& protoc Command cmd = std::move( *cmdOpt ); bool isRemote = false; - if ( protocolSettings.launchCommand == REQUEST_TYPE_LAUNCH ) { + if ( protocolSettings.launchRequestType == REQUEST_TYPE_LAUNCH ) { auto bus = std::make_unique( cmd ); mDebugger = std::make_unique( protocolSettings, std::move( bus ) ); - } else if ( protocolSettings.launchCommand == REQUEST_TYPE_ATTACH ) { - auto mode = protocolSettings.launchRequest.value( "mode", "" ); + } else if ( protocolSettings.launchRequestType == REQUEST_TYPE_ATTACH ) { + auto mode = protocolSettings.launchArgs.value( "mode", "" ); if ( mode.empty() ) mode = "local"; Connection con; - con.host = protocolSettings.launchRequest.value( "host", "localhost" ); - con.port = protocolSettings.launchRequest.value( "port", 0 ); + con.host = protocolSettings.launchArgs.value( "host", "localhost" ); + con.port = protocolSettings.launchArgs.value( "port", 0 ); bool useSocket = !con.host.empty() && con.port != 0; - if ( ( protocolSettings.launchRequest.contains( "host" ) || - protocolSettings.launchRequest.contains( "port" ) ) && + if ( ( protocolSettings.launchArgs.contains( "host" ) || + protocolSettings.launchArgs.contains( "port" ) ) && !useSocket ) { getManager()->getPluginContext()->getNotificationCenter()->addNotification( i18n( "host_port_required", "No host or port has been specified." ) ); return; } - bool useProcessId = protocolSettings.launchRequest.contains( "pid" ) && - protocolSettings.launchRequest["pid"].is_number() && - protocolSettings.launchRequest.value( "pid", 0 ) != 0; + if ( !useSocket && forceUseProgram && usesPorts ) { + useSocket = true; + con.port = randPort; + } - bool useProgram = protocolSettings.launchRequest.contains( "program" ) && - protocolSettings.launchRequest["program"].is_string() && - !protocolSettings.launchRequest.value( "program", "" ).empty(); + bool useProcessId = protocolSettings.launchArgs.contains( "pid" ) && + protocolSettings.launchArgs["pid"].is_number() && + protocolSettings.launchArgs.value( "pid", 0 ) != 0; + + bool useProgram = + forceUseProgram || ( protocolSettings.launchArgs.contains( "program" ) && + protocolSettings.launchArgs["program"].is_string() && + !protocolSettings.launchArgs.value( "program", "" ).empty() ); if ( mode == "local" ) { if ( useSocket ) { @@ -1810,9 +1905,13 @@ void DebuggerPlugin::run( const std::string& debugger, ProtocolSettings&& protoc mDebugger->start(); } -void DebuggerPlugin::exitDebugger() { +void DebuggerPlugin::exitDebugger( bool requestDisconnect ) { if ( mDebugger && mListener ) mDebugger->removeListener( mListener.get() ); + + if ( requestDisconnect && mDebugger ) + mDebugger->disconnect( false ); + if ( mDebugger || mListener ) { mThreadPool->run( [this] { mDebugger.reset(); @@ -1863,6 +1962,8 @@ void DebuggerPlugin::updatePanelUIState( StatusDebuggerController::State state ) } void DebuggerPlugin::setUIDebuggingState( StatusDebuggerController::State state ) { + if ( isShuttingDown() ) + return; mDebuggingState = state; auto ctrl = getStatusDebuggerController(); @@ -1987,11 +2088,11 @@ bool DebuggerPlugin::onMouseMove( UICodeEditor* editor, const Vector2i& position LuaPattern::hasMatches( expression, "^[%p%s]+$" ) ) return; - mCurrentHover = range; - if ( !mDebugger ) return; + mCurrentHover = range; + mDebugger->evaluate( expression, "hover", mListener->getCurrentFrameId(), [this, editor]( const std::string&, const std::optional& info ) { diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp index 0d13caf92..db4196dfa 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp @@ -158,13 +158,17 @@ class DebuggerPlugin : public PluginBase { void runConfig( const std::string& debugger, const std::string& configuration ); void run( const std::string& debugger, ProtocolSettings&& protocolSettings, - DapRunConfig&& runConfig, int randPort ); + DapRunConfig&& runConfig, int randPort, bool forceUseProgram, bool usesPorts ); - void exitDebugger(); + void exitDebugger( bool requestDisconnect = false ); void replaceKeysInJson( nlohmann::json& json, int randomPort, const std::unordered_map& solvedInputs ); + std::vector + replaceKeyInString( std::string val, int randomPort, + const std::unordered_map& solvedInputs ); + void onRegisterEditor( UICodeEditor* ) override; void onUnregisterEditor( UICodeEditor* ) override; @@ -228,7 +232,7 @@ class DebuggerPlugin : public PluginBase { void closeProject(); std::unordered_map - needsToResolveInputs( nlohmann::json& json ); + needsToResolveInputs( nlohmann::json& json, const std::vector& cmdArgs ); void resolveInputsBeforeRun( std::unordered_map inputs, DapTool debugger, DapConfig config, diff --git a/src/tools/ecode/plugins/debugger/models/processesmodel.cpp b/src/tools/ecode/plugins/debugger/models/processesmodel.cpp index d5b9651e1..59059e373 100644 --- a/src/tools/ecode/plugins/debugger/models/processesmodel.cpp +++ b/src/tools/ecode/plugins/debugger/models/processesmodel.cpp @@ -39,13 +39,6 @@ Variant ProcessesModel::data( const ModelIndex& modelIndex, ModelRole role ) con 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; @@ -53,7 +46,7 @@ void ProcessesModel::setFilter( const std::string& filter ) { mFilter = filter; mProcessesFiltered.clear(); for ( const auto& process : mProcesses ) { - if ( containsIgnoreCase( process.second, filter ) ) + if ( String::icontains( process.second, filter ) ) mProcessesFiltered.emplace_back( process.first, process.second ); } invalidate();