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 <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         |  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",

_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to