[Lldb-commits] [lldb] [lldb-dap] Add external terminal support (PR #146950)
Druzhkov Sergei via lldb-commits
lldb-commits at lists.llvm.org
Mon Jul 7 22:23:40 PDT 2025
https://github.com/DrSergei updated https://github.com/llvm/llvm-project/pull/146950
>From 155c6ee3d65d33de5640cfc961ebcb4cbb3ddfbc Mon Sep 17 00:00:00 2001
From: Druzhkov Sergei <serzhdruzhok at 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 | 5 ++-
.../tools/lldb-dap/launch/TestDAP_launch.py | 25 ++++++++++---
.../lldb-dap/Handler/LaunchRequestHandler.cpp | 5 +--
.../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 | 36 +++++++++++++++++--
.../lldb-dap/Protocol/ProtocolRequests.h | 12 +++++--
lldb/tools/lldb-dap/README.md | 3 +-
lldb/tools/lldb-dap/package.json | 18 +++++++++-
10 files changed, 105 insertions(+), 22 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..b15c1cb8d8440 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,8 @@ def request_launch(
disableASLR=False,
disableSTDIO=False,
shellExpandArguments=False,
- runInTerminal=False,
+ runInTerminal=False, # deprecated
+ console: Optional[str] = None,
enableAutoVariableSummaries=False,
displayExtendedBacktrace=False,
enableSyntheticChildDebugging=False,
@@ -946,6 +947,8 @@ def request_launch(
args_dict["sourceMap"] = sourceMap
if runInTerminal:
args_dict["runInTerminal"] = runInTerminal
+ if console:
+ args_dict["console"] = console
if postRunCommands:
args_dict["postRunCommands"] = postRunCommands
if customFrameFormat:
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..a611cc30c1897 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
@@ -44,22 +44,39 @@ def test_failing_launch_program(self):
"'{0}' does not exist".format(program), response["body"]["error"]["format"]
)
- def test_failing_launch_commands_and_run_in_terminal(self):
+ def test_failing_launch_commands_and_console(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()
response = self.launch(
- program, launchCommands=["a b c"], runInTerminal=True, expectFailure=True
+ program,
+ launchCommands=["a b c"],
+ console="integratedTerminal",
+ expectFailure=True,
)
self.assertFalse(response["success"])
self.assertTrue(self.get_dict_value(response, ["body", "error", "showUser"]))
self.assertEqual(
- "'launchCommands' and 'runInTerminal' are mutually exclusive",
+ "'launchCommands' and non-internal 'console' are mutually exclusive",
self.get_dict_value(response, ["body", "error", "format"]),
)
+ def test_failing_console(self):
+ """
+ Tests launching in console with an invalid terminal type.
+ """
+ program = self.getBuildArtifact("a.out")
+ self.create_debug_adapter()
+ response = self.launch(program, console="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 'internalConsole\', 'integratedTerminal\' or 'externalTerminal\' at arguments.console",
+ )
+
@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..553cbeaf849e2 100644
--- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp
@@ -23,9 +23,10 @@ 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.console != protocol::eConsoleInternal)
return make_error<DAPError>(
- "'launchCommands' and 'runInTerminal' are mutually exclusive");
+ "'launchCommands' and non-internal 'console' are mutually exclusive");
dap.SetConfiguration(arguments.configuration, /*is_attach=*/false);
dap.last_launch_request = arguments;
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index 93bc80a38e29d..4fadf1c22e0e3 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.console == protocol::eConsoleExternalTerminal);
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.console != protocol::eConsoleInternal) {
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..28a0e04462307 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 terminal 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..72ed891fc9405 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, Console &C, json::Path P) {
+ auto oldFormatConsole = Params.getAsBoolean();
+ if (oldFormatConsole) {
+ if (*oldFormatConsole)
+ C = eConsoleIntegratedTerminal;
+ else
+ C = eConsoleInternal;
+ return true;
+ }
+ auto newFormatConsole = Params.getAsString();
+ if (!newFormatConsole) {
+ P.report("expected a string");
+ return false;
+ }
+
+ std::optional<Console> console =
+ StringSwitch<std::optional<Console>>(*newFormatConsole)
+ .Case("internalConsole", eConsoleInternal)
+ .Case("integratedTerminal", eConsoleIntegratedTerminal)
+ .Case("externalTerminal", eConsoleExternalTerminal)
+ .Default(std::nullopt);
+ if (!console) {
+ P.report("unexpected value, expected 'internalConsole', "
+ "'integratedTerminal' or 'externalTerminal'");
+ return false;
+ }
+
+ C = *console;
+ return true;
+}
+
bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA,
json::Path P) {
json::ObjectMapper O(Params, P);
@@ -273,9 +304,8 @@ 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) &&
- parseEnv(Params, LRA.env, P);
+ O.mapOptional("runInTerminal", LRA.console) &&
+ O.mapOptional("console", LRA.console) && parseEnv(Params, LRA.env, P);
}
bool fromJSON(const json::Value &Params, AttachRequestArguments &ARA,
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index d4b816c72679b..dcfb584e689b9 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -242,6 +242,12 @@ struct Configuration {
std::string platformName;
};
+enum Console : unsigned {
+ eConsoleInternal,
+ eConsoleIntegratedTerminal,
+ eConsoleExternalTerminal
+};
+
/// lldb-dap specific launch arguments.
struct LaunchRequestArguments {
/// Common lldb-dap configuration values for launching/attaching operations.
@@ -290,9 +296,9 @@ struct LaunchRequestArguments {
/// Set whether to shell expand arguments to the process when launching.
bool shellExpandArguments = false;
- /// Launch the program inside an integrated terminal in the IDE. Useful for
- /// debugging interactive command line programs.
- bool runInTerminal = false;
+ /// Specify where to launch the program: internal console, integrated
+ /// terminal or external terminal.
+ Console console = eConsoleInternal;
/// @}
};
diff --git a/lldb/tools/lldb-dap/README.md b/lldb/tools/lldb-dap/README.md
index 18bfa9d518b98..cf2b12a8724ab 100644
--- a/lldb/tools/lldb-dap/README.md
+++ b/lldb/tools/lldb-dap/README.md
@@ -235,7 +235,8 @@ contain the following key/value pairs:
| **cwd** | string | | The program working directory.
| **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** (deprecated) | boolean | | Launch the program inside an integrated terminal in the IDE. Useful for debugging interactive command line programs.
+| **console** | string | | Specify where to launch the program: internal console (`internalConsole`), integrated terminal (`integratedTerminal`) or external terminal (`externalTerminal`).
| **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..801abe73edd7d 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -528,7 +528,23 @@
"runInTerminal": {
"type": "boolean",
"description": "Launch the program inside an integrated terminal in the IDE. Useful for debugging interactive command line programs",
- "default": false
+ "default": false,
+ "deprecationMessage": "Attribute 'runInTerminal' is deprecated, use 'console' instead."
+ },
+ "console": {
+ "type": "string",
+ "enum": [
+ "internalConsole",
+ "integratedTerminal",
+ "externalTerminal"
+ ],
+ "enumDescriptions": [
+ "Use Debug Console for output (input is not supported).",
+ "Launch the program inside an integrated terminal in the IDE.",
+ "Launch the program inside an external terminal window."
+ ],
+ "description": "Specify where to launch the program: internal console, integrated terminal or external terminal.",
+ "default": "internalConsole"
},
"timeout": {
"type": "number",
More information about the lldb-commits
mailing list