From df0a58c34e9dcbed401ebda5bd0cff5afd86c0e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Thu, 26 Jun 2025 20:19:17 -0300 Subject: [PATCH] Some debugger improvements for node dap. Fixed a bug in TextDocument that created new history in the undo/redo stack when not doing any operation when trying to cut. --- bin/assets/plugins/debugger.json | 6 +- include/eepp/ui/doc/textdocument.hpp | 4 +- src/eepp/ui/doc/textdocument.cpp | 15 ++- src/tools/ecode/jsonhelper.hpp | 40 +++++++ .../ecode/plugins/debugger/debuggerplugin.cpp | 105 +++++++++++------- .../ecode/plugins/debugger/debuggerplugin.hpp | 4 +- 6 files changed, 123 insertions(+), 51 deletions(-) create mode 100644 src/tools/ecode/jsonhelper.hpp diff --git a/bin/assets/plugins/debugger.json b/bin/assets/plugins/debugger.json index b69f0331f..d25570927 100644 --- a/bin/assets/plugins/debugger.json +++ b/bin/assets/plugins/debugger.json @@ -245,11 +245,7 @@ "type": "pwa-node", "run": { "command": "node", - "command_arguments": [ - "${env:VSCODE_JS_DEBUG_PATH}/src/dapDebugServer.js", - "${randPort}", - "127.0.0.1" - ] + "command_arguments": [ "${env:VSCODE_JS_DEBUG_PATH}/src/dapDebugServer.js", "${randPort}", "127.0.0.1" ] }, "languages": ["javascript", "typescript"], "configurations": [ diff --git a/include/eepp/ui/doc/textdocument.hpp b/include/eepp/ui/doc/textdocument.hpp index 518227204..cb9ea0285 100644 --- a/include/eepp/ui/doc/textdocument.hpp +++ b/include/eepp/ui/doc/textdocument.hpp @@ -275,7 +275,7 @@ class EE_API TextDocument { void deleteTo( const size_t& cursorIdx, int offset ); - void deleteSelection(); + bool deleteSelection(); void selectTo( TextPosition position ); @@ -578,7 +578,7 @@ class EE_API TextDocument { void popSelection(); - void deleteSelection( const size_t& cursorIdx ); + bool deleteSelection( const size_t& cursorIdx ); String getSelectedText( const size_t& cursorIdx ) const; diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index 1a6df5671..191009fbd 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -1237,6 +1237,8 @@ TextPosition TextDocument::insert( const size_t& cursorIdx, TextPosition positio } size_t TextDocument::remove( const size_t& cursorIdx, TextRange range ) { + if ( !range.hasSelection() ) + return 0; size_t lineCount = mLines.size(); mUndoStack.clearRedoStack(); size_t linesRemoved = remove( cursorIdx, sanitizeRange( range.normalized() ), @@ -1249,7 +1251,7 @@ size_t TextDocument::remove( const size_t& cursorIdx, TextRange range ) { size_t TextDocument::remove( const size_t& cursorIdx, TextRange range, UndoStackContainer& undoStack, const Time& time, bool fromUndoRedo ) { - if ( !range.isValid() ) + if ( !range.hasSelection() ) return 0; mModificationId++; @@ -1601,16 +1603,21 @@ void TextDocument::deleteTo( const size_t& cursorIdx, int offset ) { setSelection( cursorIdx, cursorPos ); } -void TextDocument::deleteSelection( const size_t& cursorIdx ) { +bool TextDocument::deleteSelection( const size_t& cursorIdx ) { BoolScopedOpOptional op( !mDoingTextInput, mDoingTextInput, true ); + if ( !getSelectionIndex( cursorIdx ).hasSelection() ) + return false; TextPosition cursorPos = getSelectionIndex( cursorIdx ).normalized().start(); remove( cursorIdx, getSelectionIndex( cursorIdx ) ); setSelection( cursorIdx, cursorPos ); + return true; } -void TextDocument::deleteSelection() { +bool TextDocument::deleteSelection() { + bool somethingWasDeleted = false; for ( size_t i = 0; i < mSelection.size(); ++i ) - deleteSelection( i ); + somethingWasDeleted |= deleteSelection( i ); + return somethingWasDeleted; } void TextDocument::selectTo( TextPosition position ) { diff --git a/src/tools/ecode/jsonhelper.hpp b/src/tools/ecode/jsonhelper.hpp new file mode 100644 index 000000000..fb025da9f --- /dev/null +++ b/src/tools/ecode/jsonhelper.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +template static constexpr nlohmann::detail::value_t json_get_value_type() { + if constexpr ( std::is_same_v ) { + return nlohmann::detail::value_t::string; + } else if constexpr ( std::is_same_v ) { + return nlohmann::detail::value_t::boolean; + } else if constexpr ( std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v ) { + return nlohmann::detail::value_t::number_integer; + } else if constexpr ( std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v ) { + return nlohmann::detail::value_t::number_unsigned; + } else if constexpr ( std::is_same_v || std::is_same_v ) { + return nlohmann::detail::value_t::number_float; + } else if constexpr ( std::is_same_v ) { + return nlohmann::detail::value_t::object; + } else if constexpr ( std::is_same_v> ) { + return nlohmann::detail::value_t::binary; + } else { + return nlohmann::detail::value_t::null; + } +} + +template +static std::optional json_get_if( nlohmann::json& json, std::string_view key, T defVal ) { + if ( json.contains( key ) && json[key].type() == json_get_value_type() ) { + return json.value( key, defVal ); + } + return {}; +} + +template +static std::optional json_get_if( nlohmann::json& json, std::string_view key ) { + if ( json.contains( key ) && json[key].type() == json_get_value_type() ) + return json[key].get(); + return {}; +} diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp index d467cc47c..2d1055a9c 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp @@ -1,4 +1,5 @@ #include "debuggerplugin.hpp" +#include "../../jsonhelper.hpp" #include "../../notificationcenter.hpp" #include "../../terminalmanager.hpp" #include "../../uistatusbar.hpp" @@ -16,7 +17,6 @@ #include #include #include -#include using namespace EE::UI; using namespace EE::UI::Doc; @@ -97,23 +97,31 @@ static void replaceExecutableArgs( std::string& arg ) { }; } -static void replaceEnvVars( std::string& arg ) { +static bool replaceEnvVars( std::string& arg, PluginContextProvider* ctx ) { 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 ) { + if ( env && strlen( env ) != 0 ) { std::string out{ env }; String::trimInPlace( out, '\n' ); String::trimInPlace( out, '\t' ); String::replaceAll( arg, envCall, out ); } else { arg = ""; + ctx->getNotificationCenter()->addNotification( String::format( + ctx->i18n( "command_parameter_env_var_not_found", + "The environment variable \"%s\" was not found but it's needed in " + "order to run the debugger." ) + .toUtf8(), + envName ) ); + return false; } } }; + return true; } // Mouse Hover Tooltip @@ -1120,7 +1128,7 @@ void DebuggerPlugin::updateDebuggerConfigurationList() { } } -void DebuggerPlugin::replaceInVal( std::string& val, +bool DebuggerPlugin::replaceInVal( std::string& val, const std::optional& runConfig, ProjectBuild* buildConfig, int randomPort ) { @@ -1174,10 +1182,10 @@ void DebuggerPlugin::replaceInVal( std::string& val, if ( String::contains( val, KEY_RANDPORT ) ) String::replaceAll( val, KEY_RANDPORT, String::toString( randomPort ) ); - replaceEnvVars( val ); + return replaceEnvVars( val, this->getPluginContext() ); } -std::vector DebuggerPlugin::replaceKeyInString( +std::pair> DebuggerPlugin::replaceKeyInString( std::string val, int randomPort, const std::unordered_map& solvedInputs ) { auto pbm = getPluginContext()->getProjectBuildManager(); @@ -1185,8 +1193,9 @@ std::vector DebuggerPlugin::replaceKeyInString( auto buildConfig = pbm ? pbm->getCurrentBuild() : nullptr; const auto replaceVal = [this, &runConfig, &buildConfig, &solvedInputs, - randomPort]( std::string& val ) { - replaceInVal( val, runConfig, buildConfig, randomPort ); + randomPort]( std::string& val ) -> bool { + if ( !replaceInVal( val, runConfig, buildConfig, randomPort ) ) + return false; LuaPattern::Range matches[2]; if ( inputPtrn.matches( val, matches ) ) { @@ -1204,6 +1213,8 @@ std::vector DebuggerPlugin::replaceKeyInString( if ( solvedIdIt != solvedInputs.end() ) val = solvedIdIt->second; } + + return true; }; if ( runConfig && val == KEY_ARGS ) { @@ -1213,12 +1224,13 @@ std::vector DebuggerPlugin::replaceKeyInString( replaceVal( arg ); argsArr.push_back( arg ); } - return argsArr; + return { true, argsArr }; } - replaceVal( val ); + if ( !replaceVal( val ) ) + return { false, {} }; - return { val }; + return { true, { val } }; } void DebuggerPlugin::replaceKeysInJson( @@ -1873,6 +1885,26 @@ void DebuggerPlugin::resolveInputsBeforeRun( } } +std::optional getDebugServer( nlohmann::json& json ) { + auto debugServerInt = json_get_if( json, KEY_DEBUG_SERVER ); + + if ( debugServerInt ) + return Connection{ *debugServerInt, "localhost" }; + + auto debugServerStr = json_get_if( json, KEY_DEBUG_SERVER ); + if ( debugServerStr ) { + int port; + if ( String::isNumber( *debugServerStr ) && String::fromString( port, *debugServerStr ) ) + return Connection( port, "localhost" ); + + auto split = String::split( *debugServerStr, ':' ); + if ( split.size() == 2 && String::fromString( port, split[1] ) ) + return Connection( port, split[1] ); + } + + return {}; +} + void DebuggerPlugin::prepareAndRun( DapTool debugger, DapConfig config, std::unordered_map solvedInputs ) { int randomPort = Math::randi( 44000, 45000 ); @@ -1889,7 +1921,13 @@ void DebuggerPlugin::prepareAndRun( DapTool debugger, DapConfig config, bool usesPorts = false; for ( auto& arg : debugger.run.args ) { - auto res( replaceKeyInString( arg, randomPort, solvedInputs ) ); + auto ret( replaceKeyInString( arg, randomPort, solvedInputs ) ); + + // No success? Abort + if ( !ret.first ) + return; + + auto res( ret.second ); if ( res.size() == 1 && res[0] != arg ) { if ( String::contains( arg, KEY_RANDPORT ) ) usesPorts = true; @@ -1901,18 +1939,19 @@ void DebuggerPlugin::prepareAndRun( DapTool debugger, DapConfig config, if ( cmdArg == KEY_FILE || cmdArg == KEY_ARGS || cmdArg == CMD_PICK_PROCESS || cmdArg == CMD_PROMPT_STRING ) forceUseProgram = true; - auto args = replaceKeyInString( cmdArg, randomPort, solvedInputs ); + auto ret = replaceKeyInString( cmdArg, randomPort, solvedInputs ); + + // No success? Abort + if ( !ret.first ) + return; + + auto args = ret.second; for ( const auto& arg : args ) 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() ) ) ) ) { + if ( getDebugServer( protocolSettings.launchArgs ) ) usesPorts = true; - } mThreadPool->run( [this, protocolSettings = std::move( protocolSettings ), randomPort, @@ -2087,22 +2126,18 @@ void DebuggerPlugin::run( const std::string& debugger, ProtocolSettings&& protoc 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() ) ) ) ) { + Connection con; + con.host = protocolSettings.launchArgs.value( "host", "localhost" ); + con.port = randPort; + + auto debugServer = getDebugServer( protocolSettings.launchArgs ); + if ( debugServer ) { 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 ); - } + con.host = debugServer->host; + con.port = debugServer->port; } else if ( protocolSettings.launchArgs.contains( "port" ) && protocolSettings.launchArgs["port"].is_number_integer() ) { - port = protocolSettings.launchArgs.value( "port", randPort ); + con.port = protocolSettings.launchArgs.value( "port", randPort ); } std::string localRoot = mProjectPath; @@ -2110,9 +2145,6 @@ void DebuggerPlugin::run( const std::string& debugger, ProtocolSettings&& protoc if ( protocolSettings.launchRequestType == REQUEST_TYPE_LAUNCH ) { 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 { @@ -2124,9 +2156,6 @@ void DebuggerPlugin::run( const std::string& debugger, ProtocolSettings&& protoc if ( mode.empty() ) mode = "local"; - Connection con; - con.host = protocolSettings.launchArgs.value( "host", "localhost" ); - con.port = port; bool useSocket = !con.host.empty() && con.port != 0; if ( ( protocolSettings.launchArgs.contains( "host" ) || protocolSettings.launchArgs.contains( "port" ) ) && diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp index 72e703f8a..5a647eee5 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp @@ -198,7 +198,7 @@ class DebuggerPlugin : public PluginBase { void replaceKeysInJson( nlohmann::json& json, int randomPort, const std::unordered_map& solvedInputs ); - std::vector + std::pair> replaceKeyInString( std::string val, int randomPort, const std::unordered_map& solvedInputs ); @@ -285,7 +285,7 @@ class DebuggerPlugin : public PluginBase { void onDocumentLineMove( TextDocument* doc, const Int64& fromLine, const Int64& toLine, const Int64& numLines ); - void replaceInVal( std::string& val, const std::optional& runConfig, + bool replaceInVal( std::string& val, const std::optional& runConfig, ProjectBuild* buildConfig, int randomPort ); };