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.
This commit is contained in:
Martín Lucas Golini
2025-06-26 20:19:17 -03:00
parent 03d2a133f4
commit df0a58c34e
6 changed files with 123 additions and 51 deletions

View File

@@ -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": [

View File

@@ -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;

View File

@@ -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 ) {

View File

@@ -0,0 +1,40 @@
#pragma once
#include <nlohmann/json.hpp>
template <typename T> static constexpr nlohmann::detail::value_t json_get_value_type() {
if constexpr ( std::is_same_v<T, std::string> ) {
return nlohmann::detail::value_t::string;
} else if constexpr ( std::is_same_v<T, bool> ) {
return nlohmann::detail::value_t::boolean;
} else if constexpr ( std::is_same_v<T, int64_t> || std::is_same_v<T, int32_t> ||
std::is_same_v<T, int16_t> || std::is_same_v<T, int8_t> ) {
return nlohmann::detail::value_t::number_integer;
} else if constexpr ( std::is_same_v<T, uint64_t> || std::is_same_v<T, uint32_t> ||
std::is_same_v<T, uint16_t> || std::is_same_v<T, uint8_t> ) {
return nlohmann::detail::value_t::number_unsigned;
} else if constexpr ( std::is_same_v<T, double> || std::is_same_v<T, float> ) {
return nlohmann::detail::value_t::number_float;
} else if constexpr ( std::is_same_v<T, nlohmann::json> ) {
return nlohmann::detail::value_t::object;
} else if constexpr ( std::is_same_v<T, std::vector<std::uint8_t>> ) {
return nlohmann::detail::value_t::binary;
} else {
return nlohmann::detail::value_t::null;
}
}
template <typename T>
static std::optional<T> json_get_if( nlohmann::json& json, std::string_view key, T defVal ) {
if ( json.contains( key ) && json[key].type() == json_get_value_type<T>() ) {
return json.value( key, defVal );
}
return {};
}
template <typename T>
static std::optional<T> json_get_if( nlohmann::json& json, std::string_view key ) {
if ( json.contains( key ) && json[key].type() == json_get_value_type<T>() )
return json[key].get<T>();
return {};
}

View File

@@ -1,4 +1,5 @@
#include "debuggerplugin.hpp"
#include "../../jsonhelper.hpp"
#include "../../notificationcenter.hpp"
#include "../../terminalmanager.hpp"
#include "../../uistatusbar.hpp"
@@ -16,7 +17,6 @@
#include <eepp/system/scopedop.hpp>
#include <eepp/ui/uidropdownlist.hpp>
#include <eepp/ui/uitooltip.hpp>
#include <nlohmann/json.hpp>
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<ProjectBuildStep>& 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<std::string> DebuggerPlugin::replaceKeyInString(
std::pair<bool, std::vector<std::string>> DebuggerPlugin::replaceKeyInString(
std::string val, int randomPort,
const std::unordered_map<std::string, std::string>& solvedInputs ) {
auto pbm = getPluginContext()->getProjectBuildManager();
@@ -1185,8 +1193,9 @@ std::vector<std::string> 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<std::string> DebuggerPlugin::replaceKeyInString(
if ( solvedIdIt != solvedInputs.end() )
val = solvedIdIt->second;
}
return true;
};
if ( runConfig && val == KEY_ARGS ) {
@@ -1213,12 +1224,13 @@ std::vector<std::string> 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<Connection> getDebugServer( nlohmann::json& json ) {
auto debugServerInt = json_get_if<int>( json, KEY_DEBUG_SERVER );
if ( debugServerInt )
return Connection{ *debugServerInt, "localhost" };
auto debugServerStr = json_get_if<std::string>( 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<std::string, std::string> 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<std::string>() ) ) ) ) {
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<std::string>() ) ) ) ) {
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<std::string>() );
} 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<BusSocketProcess>( cmd, con );
mDebugger = std::make_unique<DebuggerClientDap>( 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" ) ) &&

View File

@@ -198,7 +198,7 @@ class DebuggerPlugin : public PluginBase {
void replaceKeysInJson( nlohmann::json& json, int randomPort,
const std::unordered_map<std::string, std::string>& solvedInputs );
std::vector<std::string>
std::pair<bool, std::vector<std::string>>
replaceKeyInString( std::string val, int randomPort,
const std::unordered_map<std::string, std::string>& 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<ProjectBuildStep>& runConfig,
bool replaceInVal( std::string& val, const std::optional<ProjectBuildStep>& runConfig,
ProjectBuild* buildConfig, int randomPort );
};