diff --git a/bin/assets/plugins/debugger.json b/bin/assets/plugins/debugger.json index 0931a3029..acfd49de2 100644 --- a/bin/assets/plugins/debugger.json +++ b/bin/assets/plugins/debugger.json @@ -238,6 +238,44 @@ } } ] + }, + { + "name": "node", + "url": "https://github.com/microsoft/vscode-js-debug", + "type": "pwa-node", + "run": { + "command": "node", + "command_arguments": [ + "${env:VSCODE_JS_DEBUG_PATH}/src/dapDebugServer.js", + "${randPort}", + "127.0.0.1" + ] + }, + "languages": ["javascript", "typescript"], + "configurations": [ + { + "name": "Launch Node.js program", + "request": "launch", + "arguments": { + "type": "pwa-node", + "program": "${file}", + "args": "${args}", + "cwd": "${cwd}", + "env": "${env}", + "runtimeExecutable": "node", + "console": "externalConsole" + } + }, + { + "name": "Attach to Node.js process", + "request": "attach", + "arguments": { + "type": "pwa-node", + "processId": "${command:pickProcess}", + "cwd": "${cwd}" + } + } + ] } ] } diff --git a/projects/linux/ee.creator.user b/projects/linux/ee.creator.user index 9062831a8..c90be2ede 100644 --- a/projects/linux/ee.creator.user +++ b/projects/linux/ee.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -125,7 +125,7 @@ {388e5431-b31b-42b3-b9ad-9002d279d75d} 10 0 - 12 + 19 ../../make/linux @@ -1199,6 +1199,7 @@ ProjectExplorer.CustomExecutableRunConfiguration true + --text-shaper 0 false 1 diff --git a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp index fe8ca7045..58b214e03 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp @@ -193,10 +193,38 @@ void DebuggerClientListener::stateChanged( DebuggerClient::State state ) { void DebuggerClientListener::sendBreakpoints() { Lock l( mPlugin->mBreakpointsMutex ); for ( const auto& fileBps : mPlugin->mBreakpoints ) { - mClient->setBreakpoints( fileBps.first, fromSet( fileBps.second ) ); + if ( isRemote() && !mLocalRoot.empty() && !mRemoteRoot.empty() && + String::startsWith( fileBps.first, mLocalRoot ) ) { + auto remoteRoot = mRemoteRoot; + auto localRoot = mLocalRoot; + FileSystem::dirAddSlashAtEnd( localRoot ); + FileSystem::dirAddSlashAtEnd( remoteRoot ); + auto remotePath = fileBps.first; + FileSystem::filePathRemoveBasePath( mLocalRoot, remotePath ); + remotePath = remoteRoot + remotePath; + mClient->setBreakpoints( remotePath, fromSet( fileBps.second ) ); + } else { + mClient->setBreakpoints( fileBps.first, fromSet( fileBps.second ) ); + } } } +const std::string& DebuggerClientListener::localRoot() const { + return mLocalRoot; +} + +void DebuggerClientListener::setLocalRoot( const std::string& newLocalRoot ) { + mLocalRoot = newLocalRoot; +} + +const std::string& DebuggerClientListener::remoteRoot() const { + return mRemoteRoot; +} + +void DebuggerClientListener::setRemoteRoot( const std::string& newRemoteRoot ) { + mRemoteRoot = newRemoteRoot; +} + void DebuggerClientListener::initialized() { sendBreakpoints(); } diff --git a/src/tools/ecode/plugins/debugger/debuggerclientlistener.hpp b/src/tools/ecode/plugins/debugger/debuggerclientlistener.hpp index 589d58e40..a41d1552c 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclientlistener.hpp +++ b/src/tools/ecode/plugins/debugger/debuggerclientlistener.hpp @@ -73,6 +73,14 @@ class DebuggerClientListener : public DebuggerClient::Listener { const ProcessInfo& getProcessInfo() const { return mProcessInfo; } + const std::string& localRoot() const; + + void setLocalRoot( const std::string& newLocalRoot ); + + const std::string& remoteRoot() const; + + void setRemoteRoot( const std::string& newRemoteRoot ); + protected: DebuggerClient* mClient{ nullptr }; DebuggerPlugin* mPlugin{ nullptr }; @@ -87,6 +95,8 @@ class DebuggerClientListener : public DebuggerClient::Listener { std::shared_ptr mVariablesHolder; std::unordered_map mScopeRef; ProcessInfo mProcessInfo; + std::string mLocalRoot; + std::string mRemoteRoot; StatusDebuggerController* getStatusDebuggerController() const; diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp index c9650c21a..d4e54bd37 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp @@ -37,6 +37,7 @@ static constexpr auto REQUEST_TYPE_ATTACH = "attach"; namespace ecode { static constexpr auto INPUT_PATTERN = "%$%{input%:([%w_]+)%}"sv; +static constexpr auto ENV_PATTERN = "%$%{env%:([%w_]+)%}"sv; static constexpr auto COMMAND_PATTERN = "%$%{command%:([%w_]+)%}"sv; static constexpr auto CMD_PICK_PROCESS = "${command:pickProcess}"sv; static constexpr auto CMD_PROMPT_STRING = "${command:promptString}"sv; @@ -44,12 +45,14 @@ static constexpr auto CMD_PICK_FILE = "${command:pickFile}"sv; static LuaPattern inputPtrn( INPUT_PATTERN ); static LuaPattern commandPtrn( COMMAND_PATTERN ); +static LuaPattern envPtrn( ENV_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_WORKSPACEROOT = "${workspaceRoot}"; static constexpr auto KEY_FILEDIRNAME = "${fileDirname}"; static constexpr auto KEY_RANDPORT = "${randPort}"; static constexpr auto KEY_PID = "${pid}"; @@ -70,6 +73,46 @@ static constexpr auto KEY_DEFAULT_BUILD_TASK = "${defaultBuildTask}"; static constexpr auto KEY_PATH_SEPARATOR = "${pathSeparator}"; static constexpr auto KEY_PATH_SEPARATOR_ABBR = "${/}"; +static constexpr auto KEY_DEBUG_SERVER = "debugServer"; + +static void replaceExecutableArgs( std::string& arg ) { + LuaPattern ptrn( "%$%b()" ); + PatternMatcher::Range match[4]; + while ( ptrn.matches( arg, match ) ) { + auto cmd = arg.substr( match[0].start, match[0].end - match[0].start ); + if ( cmd.size() > 3 ) { + auto rcmd = cmd.substr( 2, cmd.size() - 3 ); + std::string out; + Process p; + if ( p.create( rcmd ) ) { + p.readAllStdOut( out ); + String::trimInPlace( out, '\n' ); + String::trimInPlace( out, '\t' ); + String::replaceAll( arg, cmd, out ); + } + } + }; +} + +static void replaceEnvVars( std::string& arg ) { + PatternMatcher::Range match[4]; + while ( envPtrn.matches( arg, match ) ) { + auto envCall = arg.substr( match[0].start, match[0].end - match[0].start ); + if ( envCall.size() > 6 ) { + auto envName = envCall.substr( 6, envCall.size() - 7 ); + const char* env = getenv( envName.c_str() ); + if ( env ) { + std::string out{ env }; + String::trimInPlace( out, '\n' ); + String::trimInPlace( out, '\t' ); + String::replaceAll( arg, envCall, out ); + } else { + arg = ""; + } + } + }; +} + // Mouse Hover Tooltip static Action::UniqueID getMouseMoveHash( UICodeEditor* editor ) { return hashCombine( String::hash( "DebuggerPlugin::onMouseMove-" ), @@ -1103,6 +1146,7 @@ void DebuggerPlugin::replaceInVal( std::string& val, } String::replaceAll( val, KEY_WORKSPACEFOLDER, mProjectPath ); + String::replaceAll( val, KEY_WORKSPACEROOT, mProjectPath ); String::replaceAll( val, KEY_USER_HOME, Sys::getUserDirectory() ); String::replaceAll( val, KEY_WORKSPACEFOLDER_BASENAME, FileSystem::fileNameFromPath( mProjectPath ) ); @@ -1122,6 +1166,8 @@ void DebuggerPlugin::replaceInVal( std::string& val, if ( String::contains( val, KEY_RANDPORT ) ) String::replaceAll( val, KEY_RANDPORT, String::toString( randomPort ) ); + + replaceEnvVars( val ); } std::vector DebuggerPlugin::replaceKeyInString( @@ -1828,7 +1874,7 @@ void DebuggerPlugin::prepareAndRun( DapTool debugger, DapConfig config, protocolSettings.runTarget = config.runTarget; auto args = config.args; replaceKeysInJson( args, randomPort, solvedInputs ); - protocolSettings.launchArgs = args; + protocolSettings.launchArgs = std::move( args ); protocolSettings.redirectStdout = debugger.redirectStdout; protocolSettings.redirectStderr = debugger.redirectStderr; protocolSettings.supportsSourceRequest = debugger.supportsSourceRequest; @@ -1838,7 +1884,7 @@ void DebuggerPlugin::prepareAndRun( DapTool debugger, DapConfig config, for ( auto& arg : debugger.run.args ) { auto res( replaceKeyInString( arg, randomPort, solvedInputs ) ); if ( res.size() == 1 && res[0] != arg ) { - if ( arg == KEY_RANDPORT ) + if ( String::contains( arg, KEY_RANDPORT ) ) usesPorts = true; arg = res[0]; } @@ -1853,6 +1899,14 @@ void DebuggerPlugin::prepareAndRun( DapTool debugger, DapConfig config, debugger.run.args.emplace_back( arg ); } + if ( protocolSettings.launchArgs.contains( KEY_DEBUG_SERVER ) && + ( protocolSettings.launchArgs[KEY_DEBUG_SERVER].is_number_integer() || + ( protocolSettings.launchArgs[KEY_DEBUG_SERVER].is_string() && + String::isNumber( + protocolSettings.launchArgs[KEY_DEBUG_SERVER].get() ) ) ) ) { + usesPorts = true; + } + mThreadPool->run( [this, protocolSettings = std::move( protocolSettings ), randomPort, debugger = std::move( debugger ), forceUseProgram, usesPorts]() mutable { @@ -2021,10 +2075,43 @@ void DebuggerPlugin::run( const std::string& debugger, ProtocolSettings&& protoc Command cmd = std::move( *cmdOpt ); bool isRemote = false; + bool runsDapServer = false; + + for ( auto& arg : cmd.arguments ) + replaceExecutableArgs( arg ); + + int port = randPort; + if ( protocolSettings.launchArgs.contains( KEY_DEBUG_SERVER ) && + ( protocolSettings.launchArgs[KEY_DEBUG_SERVER].is_number_integer() || + ( protocolSettings.launchArgs[KEY_DEBUG_SERVER].is_string() && + String::isNumber( + protocolSettings.launchArgs[KEY_DEBUG_SERVER].get() ) ) ) ) { + runsDapServer = true; + if ( protocolSettings.launchArgs[KEY_DEBUG_SERVER].is_string() ) { + String::fromString( port, + protocolSettings.launchArgs[KEY_DEBUG_SERVER].get() ); + } else if ( protocolSettings.launchArgs[KEY_DEBUG_SERVER].is_number_integer() ) { + port = protocolSettings.launchArgs.value( KEY_DEBUG_SERVER, randPort ); + } + } else if ( protocolSettings.launchArgs.contains( "port" ) && + protocolSettings.launchArgs["port"].is_number_integer() ) { + port = protocolSettings.launchArgs.value( "port", randPort ); + } + + std::string localRoot = mProjectPath; + std::string remoteRoot; if ( protocolSettings.launchRequestType == REQUEST_TYPE_LAUNCH ) { - auto bus = std::make_unique( cmd ); - mDebugger = std::make_unique( protocolSettings, std::move( bus ) ); + if ( usesPorts ) { + Connection con; + con.host = protocolSettings.launchArgs.value( "host", "localhost" ); + con.port = port; + auto bus = std::make_unique( cmd, con ); + mDebugger = std::make_unique( protocolSettings, std::move( bus ) ); + } else { + auto bus = std::make_unique( cmd ); + mDebugger = std::make_unique( protocolSettings, std::move( bus ) ); + } } else if ( protocolSettings.launchRequestType == REQUEST_TYPE_ATTACH ) { auto mode = protocolSettings.launchArgs.value( "mode", "" ); if ( mode.empty() ) @@ -2032,7 +2119,7 @@ void DebuggerPlugin::run( const std::string& debugger, ProtocolSettings&& protoc Connection con; con.host = protocolSettings.launchArgs.value( "host", "localhost" ); - con.port = protocolSettings.launchArgs.value( "port", 0 ); + con.port = port; bool useSocket = !con.host.empty() && con.port != 0; if ( ( protocolSettings.launchArgs.contains( "host" ) || protocolSettings.launchArgs.contains( "port" ) ) && @@ -2057,7 +2144,20 @@ void DebuggerPlugin::run( const std::string& debugger, ProtocolSettings&& protoc !protocolSettings.launchArgs.value( "program", "" ).empty() ); if ( mode == "local" ) { - if ( useSocket ) { + if ( runsDapServer ) { + if ( protocolSettings.launchArgs.contains( "remoteRoot" ) && + protocolSettings.launchArgs["remoteRoot"].is_string() ) { + isRemote = true; + remoteRoot = protocolSettings.launchArgs["remoteRoot"].get(); + } + if ( protocolSettings.launchArgs.contains( "localRoot" ) && + protocolSettings.launchArgs["localRoot"].is_string() ) { + localRoot = protocolSettings.launchArgs["localRoot"].get(); + } + auto bus = std::make_unique( cmd, con ); + mDebugger = + std::make_unique( protocolSettings, std::move( bus ) ); + } else if ( useSocket ) { auto bus = std::make_unique( cmd, con ); mDebugger = std::make_unique( protocolSettings, std::move( bus ) ); @@ -2089,6 +2189,12 @@ void DebuggerPlugin::run( const std::string& debugger, ProtocolSettings&& protoc mListener = std::make_unique( mDebugger.get(), this ); mListener->setIsRemote( isRemote ); + + if ( isRemote ) { + // mListener->setLocalRoot( localRoot ); + // mListener->setRemoteRoot( remoteRoot ); + } + mDebugger->addListener( mListener.get() ); mDebugger->setSilent( mSilence ); @@ -2105,6 +2211,7 @@ void DebuggerPlugin::run( const std::string& debugger, ProtocolSettings&& protoc UITerminal* term = getPluginContext()->getTerminalManager()->createTerminalInSplitter( cwd, cmd, args, false ); + term->getTerm()->setKeepAlive( false ); doneFn( term && term->getTerm() && term->getTerm()->getTerminal() && term->getTerm()->getTerminal()->getProcess()