https://github.com/DrSergei updated https://github.com/llvm/llvm-project/pull/146950
>From d0b98039440d4cca76db55e03914b1e4d452ebbb Mon Sep 17 00:00:00 2001 From: Druzhkov Sergei <serzhdruz...@gmail.com> Date: Wed, 2 Jul 2025 23:29:26 +0300 Subject: [PATCH] [lldb-dap] Add external terminal support --- .../test/tools/lldb-dap/dap_server.py | 2 +- .../tools/lldb-dap/launch/TestDAP_launch.py | 16 ++++++++- .../lldb-dap/Handler/LaunchRequestHandler.cpp | 3 +- .../tools/lldb-dap/Handler/RequestHandler.cpp | 5 +-- lldb/tools/lldb-dap/JSONUtils.cpp | 12 ++++--- lldb/tools/lldb-dap/JSONUtils.h | 6 +++- .../lldb-dap/Protocol/ProtocolRequests.cpp | 34 +++++++++++++++++-- .../lldb-dap/Protocol/ProtocolRequests.h | 4 ++- lldb/tools/lldb-dap/README.md | 1 + lldb/tools/lldb-dap/package.json | 24 +++++++++++-- 10 files changed, 91 insertions(+), 16 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index 0fe36cd4bc71f..ec5c6be1dcc8a 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -894,7 +894,7 @@ def request_launch( disableASLR=False, disableSTDIO=False, shellExpandArguments=False, - runInTerminal=False, + runInTerminal: Union[bool, str] =False, enableAutoVariableSummaries=False, displayExtendedBacktrace=False, enableSyntheticChildDebugging=False, diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py index ae8142ae4f484..4331be5b453c6 100644 --- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py +++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py @@ -46,7 +46,7 @@ def test_failing_launch_program(self): def test_failing_launch_commands_and_run_in_terminal(self): """ - Tests launching with an invalid program. + Tests launching with launch commands in an integrated terminal. """ program = self.getBuildArtifact("a.out") self.create_debug_adapter() @@ -60,6 +60,20 @@ def test_failing_launch_commands_and_run_in_terminal(self): self.get_dict_value(response, ["body", "error", "format"]), ) + def test_failing_run_in_terminal(self): + """ + Tests launching in terminal with an invalid terminal type. + """ + program = self.getBuildArtifact("a.out") + self.create_debug_adapter() + response = self.launch(program, runInTerminal="invalid", expectFailure=True) + self.assertFalse(response["success"]) + self.assertTrue(self.get_dict_value(response, ["body", "error", "showUser"])) + self.assertRegex( + response["body"]["error"]["format"], + r"unexpected value, expected 'console', 'integrated' or 'external' at arguments.runInTerminal", + ) + @skipIfWindows def test_termination(self): """ diff --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp index 1d7b4b7009462..566622dcef982 100644 --- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp @@ -23,7 +23,8 @@ namespace lldb_dap { /// Launch request; value of command field is 'launch'. Error LaunchRequestHandler::Run(const LaunchRequestArguments &arguments) const { // Validate that we have a well formed launch request. - if (!arguments.launchCommands.empty() && arguments.runInTerminal) + if (!arguments.launchCommands.empty() && + arguments.terminal != protocol::eConsole) return make_error<DAPError>( "'launchCommands' and 'runInTerminal' are mutually exclusive"); diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index 93bc80a38e29d..e2e97d0250de7 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -80,7 +80,8 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest( arguments.configuration.program, arguments.args, arguments.env, - arguments.cwd, comm_file.m_path, debugger_pid); + arguments.cwd, comm_file.m_path, debugger_pid, + arguments.terminal == protocol::eExternal); dap.SendReverseRequest<LogFailureResponseHandler>("runInTerminal", std::move(reverse_request)); @@ -192,7 +193,7 @@ llvm::Error BaseRequestHandler::LaunchProcess( // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - if (arguments.runInTerminal) { + if (arguments.terminal != protocol::eConsole) { if (llvm::Error err = RunInTerminal(dap, arguments)) return err; } else if (launchCommands.empty()) { diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp index 08e65ab835a57..08e3e504689e7 100644 --- a/lldb/tools/lldb-dap/JSONUtils.cpp +++ b/lldb/tools/lldb-dap/JSONUtils.cpp @@ -1168,11 +1168,15 @@ llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit) { llvm::json::Object CreateRunInTerminalReverseRequest( llvm::StringRef program, const std::vector<std::string> &args, const llvm::StringMap<std::string> &env, llvm::StringRef cwd, - llvm::StringRef comm_file, lldb::pid_t debugger_pid) { + llvm::StringRef comm_file, lldb::pid_t debugger_pid, bool external) { llvm::json::Object run_in_terminal_args; - // This indicates the IDE to open an embedded terminal, instead of opening - // the terminal in a new window. - run_in_terminal_args.try_emplace("kind", "integrated"); + if (external) + // This indicates the IDE to open an external terminal window. + run_in_terminal_args.try_emplace("kind", "external"); + else + // This indicates the IDE to open an embedded terminal, instead of opening + // the terminal in a new window. + run_in_terminal_args.try_emplace("kind", "integrated"); // The program path must be the first entry in the "args" field std::vector<std::string> req_args = {DAP::debug_adapter_path.str(), diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h index fd9a06931ebff..ab0942d57ab2c 100644 --- a/lldb/tools/lldb-dap/JSONUtils.h +++ b/lldb/tools/lldb-dap/JSONUtils.h @@ -441,13 +441,17 @@ llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit); /// launcher uses it on Linux tell the kernel that it should allow the /// debugger process to attach. /// +/// \param[in] external +/// If set to true, the program will run in an external console window +/// instead of IDE's integrated terminal. +/// /// \return /// A "runInTerminal" JSON object that follows the specification outlined by /// Microsoft. llvm::json::Object CreateRunInTerminalReverseRequest( llvm::StringRef program, const std::vector<std::string> &args, const llvm::StringMap<std::string> &env, llvm::StringRef cwd, - llvm::StringRef comm_file, lldb::pid_t debugger_pid); + llvm::StringRef comm_file, lldb::pid_t debugger_pid, bool external); /// Create a "Terminated" JSON object that contains statistics /// diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp index 9bd84a6c898f9..f55efd185f9f6 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp @@ -262,6 +262,37 @@ json::Value toJSON(const BreakpointLocationsResponseBody &BLRB) { return json::Object{{"breakpoints", BLRB.breakpoints}}; } +bool fromJSON(const json::Value &Params, Terminal &T, json::Path P) { + auto oldFormatTerminal = Params.getAsBoolean(); + if (oldFormatTerminal) { + if (*oldFormatTerminal) + T = eIntegrated; + else + T = eConsole; + return true; + } + auto newFormatTerminal = Params.getAsString(); + if (!newFormatTerminal) { + P.report("expected a string"); + return false; + } + + std::optional<Terminal> terminal = + StringSwitch<std::optional<Terminal>>(*newFormatTerminal) + .Case("console", eConsole) + .Case("integrated", eIntegrated) + .Case("external", eExternal) + .Default(std::nullopt); + if (!terminal) { + P.report( + "unexpected value, expected 'console', 'integrated' or 'external'"); + return false; + } + + T = *terminal; + return true; +} + bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA, json::Path P) { json::ObjectMapper O(Params, P); @@ -273,8 +304,7 @@ bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA, O.mapOptional("disableASLR", LRA.disableASLR) && O.mapOptional("disableSTDIO", LRA.disableSTDIO) && O.mapOptional("shellExpandArguments", LRA.shellExpandArguments) && - - O.mapOptional("runInTerminal", LRA.runInTerminal) && + O.mapOptional("runInTerminal", LRA.terminal) && parseEnv(Params, LRA.env, P); } diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index d4b816c72679b..2e72f50c63afe 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -242,6 +242,8 @@ struct Configuration { std::string platformName; }; +enum Terminal : unsigned { eConsole, eIntegrated, eExternal }; + /// lldb-dap specific launch arguments. struct LaunchRequestArguments { /// Common lldb-dap configuration values for launching/attaching operations. @@ -292,7 +294,7 @@ struct LaunchRequestArguments { /// Launch the program inside an integrated terminal in the IDE. Useful for /// debugging interactive command line programs. - bool runInTerminal = false; + Terminal terminal = eConsole; /// @} }; diff --git a/lldb/tools/lldb-dap/README.md b/lldb/tools/lldb-dap/README.md index 18bfa9d518b98..61d2cc06f99eb 100644 --- a/lldb/tools/lldb-dap/README.md +++ b/lldb/tools/lldb-dap/README.md @@ -236,6 +236,7 @@ contain the following key/value pairs: | **env** | dictionary | | Environment variables to set when launching the program. The format of each environment variable string is "VAR=VALUE" for environment variables with values or just "VAR" for environment variables with no values. | **stopOnEntry** | boolean | | Whether to stop program immediately after launching. | **runInTerminal** | boolean | | Launch the program inside an integrated terminal in the IDE. Useful for debugging interactive command line programs. +| **runInTerminal** | string | | Specifies where program should be launch: `integrated` for an integrated terminal in the IDE, `external` for external terminal window or `console` (default) for IDE debug console. | **launchCommands** | [string] | | LLDB commands executed to launch the program. For JSON configurations of `"type": "attach"`, the JSON configuration can contain diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json index b150dee792c34..cf5712261d051 100644 --- a/lldb/tools/lldb-dap/package.json +++ b/lldb/tools/lldb-dap/package.json @@ -526,9 +526,27 @@ "default": [] }, "runInTerminal": { - "type": "boolean", - "description": "Launch the program inside an integrated terminal in the IDE. Useful for debugging interactive command line programs", - "default": false + "anyOf": [ + { + "type": "boolean", + "description": "Launch the program inside an integrated terminal in the IDE. Useful for debugging interactive command line programs", + "default": false + }, + { + "type": "string", + "enum": [ + "console", + "integrated", + "external" + ], + "enumDescriptions": [ + "Launch the program inside an integrated terminal in the IDE.", + "Launch the program inside an external terminal window.", + "Use Debug Console for output (input is not supported)." + ], + "default": "console" + } + ] }, "timeout": { "type": "number", _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits