[Lldb-commits] [lldb] [lldb-dap] Move requests into their own object/file (PR #128262)

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Sat Feb 22 18:30:05 PST 2025


https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/128262

>From e6cbcc3ab7a278c3ef01646db22872589486eb91 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Fri, 21 Feb 2025 19:20:36 -0600
Subject: [PATCH 01/11] [lldb-dap] Move requests into their own object/file

---
 lldb/tools/lldb-dap/CMakeLists.txt            |   5 +
 lldb/tools/lldb-dap/DAP.cpp                   |  23 +-
 lldb/tools/lldb-dap/DAP.h                     |   8 +
 lldb/tools/lldb-dap/Request/AttachRequest.cpp | 211 ++++++++++++++++++
 lldb/tools/lldb-dap/Request/Request.cpp       |  92 ++++++++
 lldb/tools/lldb-dap/Request/Request.h         |  45 ++++
 lldb/tools/lldb-dap/lldb-dap.cpp              | 190 +---------------
 7 files changed, 380 insertions(+), 194 deletions(-)
 create mode 100644 lldb/tools/lldb-dap/Request/AttachRequest.cpp
 create mode 100644 lldb/tools/lldb-dap/Request/Request.cpp
 create mode 100644 lldb/tools/lldb-dap/Request/Request.h

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 43fc18873feb3..e97f7ecdbd31b 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -36,6 +36,9 @@ add_lldb_tool(lldb-dap
   SourceBreakpoint.cpp
   Watchpoint.cpp
 
+  Request/Request.cpp
+  Request/AttachRequest.cpp
+
   LINK_LIBS
     liblldb
     lldbHost
@@ -46,6 +49,8 @@ add_lldb_tool(lldb-dap
     Support
   )
 
+target_include_directories(lldb-dap PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+
 if(LLDB_DAP_WELCOME_MESSAGE)
   target_compile_definitions(lldb-dap
     PRIVATE
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index a67abe582abd4..80f083b1ab592 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -744,16 +744,25 @@ bool DAP::HandleObject(const llvm::json::Object &object) {
   const auto packet_type = GetString(object, "type");
   if (packet_type == "request") {
     const auto command = GetString(object, "command");
+
+    // Try the new request handler first.
+    auto new_handler_pos = new_request_handlers.find(command);
+    if (new_handler_pos != new_request_handlers.end()) {
+      (*new_handler_pos->second)(object);
+      return true; // Success
+    }
+
+    // FIXME: Remove request_handlers once everything has been migrated.
     auto handler_pos = request_handlers.find(command);
-    if (handler_pos == request_handlers.end()) {
-      if (log)
-        *log << "error: unhandled command \"" << command.data() << "\""
-             << std::endl;
-      return false; // Fail
+    if (handler_pos != request_handlers.end()) {
+      handler_pos->second(*this, object);
+      return true; // Success
     }
 
-    handler_pos->second(*this, object);
-    return true; // Success
+    if (log)
+      *log << "error: unhandled command \"" << command.data() << "\""
+           << std::endl;
+    return false; // Fail
   }
 
   if (packet_type == "response") {
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index b23be68ea002f..33507061e0750 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -16,6 +16,7 @@
 #include "InstructionBreakpoint.h"
 #include "OutputRedirector.h"
 #include "ProgressEvent.h"
+#include "Request/Request.h"
 #include "SourceBreakpoint.h"
 #include "lldb/API/SBBroadcaster.h"
 #include "lldb/API/SBCommandInterpreter.h"
@@ -37,6 +38,7 @@
 #include "llvm/Support/JSON.h"
 #include "llvm/Support/Threading.h"
 #include <map>
+#include <memory>
 #include <mutex>
 #include <optional>
 #include <thread>
@@ -184,6 +186,7 @@ struct DAP {
   lldb::pid_t restarting_process_id;
   bool configuration_done_sent;
   std::map<std::string, RequestCallback, std::less<>> request_handlers;
+  llvm::StringMap<std::unique_ptr<Request>> new_request_handlers;
   bool waiting_for_run_in_terminal;
   ProgressEventReporter progress_event_reporter;
   // Keep track of the last stop thread index IDs as threads won't go away
@@ -330,6 +333,11 @@ struct DAP {
   ///     IDE.
   void RegisterRequestCallback(std::string request, RequestCallback callback);
 
+  /// Registers a request handler.
+  template <typename Request> void RegisterRequest() {
+    new_request_handlers[Request::getName()] = std::make_unique<Request>(*this);
+  }
+
   /// Debuggee will continue from stopped state.
   void WillContinue() { variables.Clear(); }
 
diff --git a/lldb/tools/lldb-dap/Request/AttachRequest.cpp b/lldb/tools/lldb-dap/Request/AttachRequest.cpp
new file mode 100644
index 0000000000000..f1b3bfc878427
--- /dev/null
+++ b/lldb/tools/lldb-dap/Request/AttachRequest.cpp
@@ -0,0 +1,211 @@
+//===-- AttachRequest.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "JSONUtils.h"
+#include "Request.h"
+#include "lldb/API/SBListener.h"
+#include "llvm/Support/FileSystem.h"
+
+namespace lldb_dap {
+/// Prints a welcome message on the editor if the preprocessor variable
+/// LLDB_DAP_WELCOME_MESSAGE is defined.
+static void PrintWelcomeMessage(DAP &dap) {
+#ifdef LLDB_DAP_WELCOME_MESSAGE
+  dap.SendOutput(OutputType::Console, LLDB_DAP_WELCOME_MESSAGE);
+#endif
+}
+
+// "AttachRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Attach request; value of command field is 'attach'.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "attach" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/AttachRequestArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments" ]
+//   }]
+// },
+// "AttachRequestArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'attach' request.\nThe attach request has no
+//   standardized attributes."
+// },
+// "AttachResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'attach' request. This is just an
+//     acknowledgement, so no body field is required."
+//   }]
+// }
+
+void AttachRequest::operator()(const llvm::json::Object &request) {
+  dap.is_attach = true;
+  dap.last_launch_or_attach_request = request;
+  llvm::json::Object response;
+  lldb::SBError error;
+  FillResponse(request, response);
+  lldb::SBAttachInfo attach_info;
+  const int invalid_port = 0;
+  const auto *arguments = request.getObject("arguments");
+  const lldb::pid_t pid =
+      GetUnsigned(arguments, "pid", LLDB_INVALID_PROCESS_ID);
+  const auto gdb_remote_port =
+      GetUnsigned(arguments, "gdb-remote-port", invalid_port);
+  const auto gdb_remote_hostname =
+      GetString(arguments, "gdb-remote-hostname", "localhost");
+  if (pid != LLDB_INVALID_PROCESS_ID)
+    attach_info.SetProcessID(pid);
+  const auto wait_for = GetBoolean(arguments, "waitFor", false);
+  attach_info.SetWaitForLaunch(wait_for, false /*async*/);
+  dap.init_commands = GetStrings(arguments, "initCommands");
+  dap.pre_run_commands = GetStrings(arguments, "preRunCommands");
+  dap.stop_commands = GetStrings(arguments, "stopCommands");
+  dap.exit_commands = GetStrings(arguments, "exitCommands");
+  dap.terminate_commands = GetStrings(arguments, "terminateCommands");
+  auto attachCommands = GetStrings(arguments, "attachCommands");
+  llvm::StringRef core_file = GetString(arguments, "coreFile");
+  const uint64_t timeout_seconds = GetUnsigned(arguments, "timeout", 30);
+  dap.stop_at_entry =
+      core_file.empty() ? GetBoolean(arguments, "stopOnEntry", false) : true;
+  dap.post_run_commands = GetStrings(arguments, "postRunCommands");
+  const llvm::StringRef debuggerRoot = GetString(arguments, "debuggerRoot");
+  dap.enable_auto_variable_summaries =
+      GetBoolean(arguments, "enableAutoVariableSummaries", false);
+  dap.enable_synthetic_child_debugging =
+      GetBoolean(arguments, "enableSyntheticChildDebugging", false);
+  dap.display_extended_backtrace =
+      GetBoolean(arguments, "displayExtendedBacktrace", false);
+  dap.command_escape_prefix = GetString(arguments, "commandEscapePrefix", "`");
+  dap.SetFrameFormat(GetString(arguments, "customFrameFormat"));
+  dap.SetThreadFormat(GetString(arguments, "customThreadFormat"));
+
+  PrintWelcomeMessage(dap);
+
+  // This is a hack for loading DWARF in .o files on Mac where the .o files
+  // in the debug map of the main executable have relative paths which require
+  // the lldb-dap binary to have its working directory set to that relative
+  // root for the .o files in order to be able to load debug info.
+  if (!debuggerRoot.empty())
+    llvm::sys::fs::set_current_path(debuggerRoot);
+
+  // Run any initialize LLDB commands the user specified in the launch.json
+  if (llvm::Error err = dap.RunInitCommands()) {
+    response["success"] = false;
+    EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
+    dap.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+
+  SetSourceMapFromArguments(*arguments);
+
+  lldb::SBError status;
+  dap.SetTarget(dap.CreateTargetFromArguments(*arguments, status));
+  if (status.Fail()) {
+    response["success"] = llvm::json::Value(false);
+    EmplaceSafeString(response, "message", status.GetCString());
+    dap.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+
+  // Run any pre run LLDB commands the user specified in the launch.json
+  if (llvm::Error err = dap.RunPreRunCommands()) {
+    response["success"] = false;
+    EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
+    dap.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+
+  if ((pid == LLDB_INVALID_PROCESS_ID || gdb_remote_port == invalid_port) &&
+      wait_for) {
+    char attach_msg[256];
+    auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg),
+                                   "Waiting to attach to \"%s\"...",
+                                   dap.target.GetExecutable().GetFilename());
+    dap.SendOutput(OutputType::Console,
+                   llvm::StringRef(attach_msg, attach_msg_len));
+  }
+  if (attachCommands.empty()) {
+    // No "attachCommands", just attach normally.
+    // Disable async events so the attach will be successful when we return from
+    // the launch call and the launch will happen synchronously
+    dap.debugger.SetAsync(false);
+    if (core_file.empty()) {
+      if ((pid != LLDB_INVALID_PROCESS_ID) &&
+          (gdb_remote_port != invalid_port)) {
+        // If both pid and port numbers are specified.
+        error.SetErrorString("The user can't specify both pid and port");
+      } else if (gdb_remote_port != invalid_port) {
+        // If port is specified and pid is not.
+        lldb::SBListener listener = dap.debugger.GetListener();
+
+        // If the user hasn't provided the hostname property, default localhost
+        // being used.
+        std::string connect_url =
+            llvm::formatv("connect://{0}:", gdb_remote_hostname);
+        connect_url += std::to_string(gdb_remote_port);
+        dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
+                                 error);
+      } else {
+        // Attach by process name or id.
+        dap.target.Attach(attach_info, error);
+      }
+    } else
+      dap.target.LoadCore(core_file.data(), error);
+    // Reenable async events
+    dap.debugger.SetAsync(true);
+  } else {
+    // We have "attachCommands" that are a set of commands that are expected
+    // to execute the commands after which a process should be created. If there
+    // is no valid process after running these commands, we have failed.
+    if (llvm::Error err = dap.RunAttachCommands(attachCommands)) {
+      response["success"] = false;
+      EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
+      dap.SendJSON(llvm::json::Value(std::move(response)));
+      return;
+    }
+    // The custom commands might have created a new target so we should use the
+    // selected target after these commands are run.
+    dap.target = dap.debugger.GetSelectedTarget();
+
+    // Make sure the process is attached and stopped before proceeding as the
+    // the launch commands are not run using the synchronous mode.
+    error = dap.WaitForProcessToStop(timeout_seconds);
+  }
+
+  if (error.Success() && core_file.empty()) {
+    auto attached_pid = dap.target.GetProcess().GetProcessID();
+    if (attached_pid == LLDB_INVALID_PROCESS_ID) {
+      if (attachCommands.empty())
+        error.SetErrorString("failed to attach to a process");
+      else
+        error.SetErrorString("attachCommands failed to attach to a process");
+    }
+  }
+
+  if (error.Fail()) {
+    response["success"] = llvm::json::Value(false);
+    EmplaceSafeString(response, "message", std::string(error.GetCString()));
+  } else {
+    dap.RunPostRunCommands();
+  }
+
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+  if (error.Success()) {
+    SendProcessEvent(Attach);
+    dap.SendJSON(CreateEventObject("initialized"));
+  }
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Request/Request.cpp b/lldb/tools/lldb-dap/Request/Request.cpp
new file mode 100644
index 0000000000000..9fe2445be9d32
--- /dev/null
+++ b/lldb/tools/lldb-dap/Request/Request.cpp
@@ -0,0 +1,92 @@
+//===-- Request.cpp -------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Request.h"
+#include "DAP.h"
+#include "JSONUtils.h"
+#include "lldb/API/SBFileSpec.h"
+
+namespace lldb_dap {
+
+void Request::SendProcessEvent(Request::LaunchMethod launch_method) {
+  lldb::SBFileSpec exe_fspec = dap.target.GetExecutable();
+  char exe_path[PATH_MAX];
+  exe_fspec.GetPath(exe_path, sizeof(exe_path));
+  llvm::json::Object event(CreateEventObject("process"));
+  llvm::json::Object body;
+  EmplaceSafeString(body, "name", std::string(exe_path));
+  const auto pid = dap.target.GetProcess().GetProcessID();
+  body.try_emplace("systemProcessId", (int64_t)pid);
+  body.try_emplace("isLocalProcess", true);
+  const char *startMethod = nullptr;
+  switch (launch_method) {
+  case Launch:
+    startMethod = "launch";
+    break;
+  case Attach:
+    startMethod = "attach";
+    break;
+  case AttachForSuspendedLaunch:
+    startMethod = "attachForSuspendedLaunch";
+    break;
+  }
+  body.try_emplace("startMethod", startMethod);
+  event.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(event)));
+}
+
+// Both attach and launch take a either a sourcePath or sourceMap
+// argument (or neither), from which we need to set the target.source-map.
+void Request::SetSourceMapFromArguments(const llvm::json::Object &arguments) {
+  const char *sourceMapHelp =
+      "source must be be an array of two-element arrays, "
+      "each containing a source and replacement path string.\n";
+
+  std::string sourceMapCommand;
+  llvm::raw_string_ostream strm(sourceMapCommand);
+  strm << "settings set target.source-map ";
+  const auto sourcePath = GetString(arguments, "sourcePath");
+
+  // sourceMap is the new, more general form of sourcePath and overrides it.
+  constexpr llvm::StringRef sourceMapKey = "sourceMap";
+
+  if (const auto *sourceMapArray = arguments.getArray(sourceMapKey)) {
+    for (const auto &value : *sourceMapArray) {
+      const auto *mapping = value.getAsArray();
+      if (mapping == nullptr || mapping->size() != 2 ||
+          (*mapping)[0].kind() != llvm::json::Value::String ||
+          (*mapping)[1].kind() != llvm::json::Value::String) {
+        dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
+        return;
+      }
+      const auto mapFrom = GetAsString((*mapping)[0]);
+      const auto mapTo = GetAsString((*mapping)[1]);
+      strm << "\"" << mapFrom << "\" \"" << mapTo << "\" ";
+    }
+  } else if (const auto *sourceMapObj = arguments.getObject(sourceMapKey)) {
+    for (const auto &[key, value] : *sourceMapObj) {
+      if (value.kind() == llvm::json::Value::String) {
+        strm << "\"" << key.str() << "\" \"" << GetAsString(value) << "\" ";
+      }
+    }
+  } else {
+    if (ObjectContainsKey(arguments, sourceMapKey)) {
+      dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
+      return;
+    }
+    if (sourcePath.empty())
+      return;
+    // Do any source remapping needed before we create our targets
+    strm << "\".\" \"" << sourcePath << "\"";
+  }
+  if (!sourceMapCommand.empty()) {
+    dap.RunLLDBCommands("Setting source map:", {sourceMapCommand});
+  }
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Request/Request.h b/lldb/tools/lldb-dap/Request/Request.h
new file mode 100644
index 0000000000000..339c3b1fb323f
--- /dev/null
+++ b/lldb/tools/lldb-dap/Request/Request.h
@@ -0,0 +1,45 @@
+//===-- Request.h ---------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TOOLS_LLDB_DAP_REQUEST_REQUEST_H
+#define LLDB_TOOLS_LLDB_DAP_REQUEST_REQUEST_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/JSON.h"
+
+namespace lldb_dap {
+struct DAP;
+
+class Request {
+public:
+  Request(DAP &dap) : dap(dap) {}
+  virtual ~Request() = default;
+
+  virtual void operator()(const llvm::json::Object &request) = 0;
+
+  static llvm::StringLiteral getName() { return "invalid"; };
+
+  enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
+
+  void SendProcessEvent(LaunchMethod launch_method);
+  void SetSourceMapFromArguments(const llvm::json::Object &arguments);
+
+protected:
+  DAP &dap;
+};
+
+class AttachRequest : public Request {
+public:
+  using Request::Request;
+  static llvm::StringLiteral getName() { return "attach"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
+} // namespace lldb_dap
+
+#endif
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index e323990d8b6ed..997b654011b64 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -10,6 +10,7 @@
 #include "FifoFiles.h"
 #include "JSONUtils.h"
 #include "LLDBUtils.h"
+#include "Request/Request.h"
 #include "RunInTerminal.h"
 #include "Watchpoint.h"
 #include "lldb/API/SBDeclaration.h"
@@ -726,192 +727,6 @@ bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
   return reached_end_of_stack;
 }
 
-// "AttachRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Attach request; value of command field is 'attach'.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "attach" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/AttachRequestArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments" ]
-//   }]
-// },
-// "AttachRequestArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'attach' request.\nThe attach request has no
-//   standardized attributes."
-// },
-// "AttachResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'attach' request. This is just an
-//     acknowledgement, so no body field is required."
-//   }]
-// }
-void request_attach(DAP &dap, const llvm::json::Object &request) {
-  dap.is_attach = true;
-  dap.last_launch_or_attach_request = request;
-  llvm::json::Object response;
-  lldb::SBError error;
-  FillResponse(request, response);
-  lldb::SBAttachInfo attach_info;
-  const int invalid_port = 0;
-  const auto *arguments = request.getObject("arguments");
-  const lldb::pid_t pid =
-      GetUnsigned(arguments, "pid", LLDB_INVALID_PROCESS_ID);
-  const auto gdb_remote_port =
-      GetUnsigned(arguments, "gdb-remote-port", invalid_port);
-  const auto gdb_remote_hostname =
-      GetString(arguments, "gdb-remote-hostname", "localhost");
-  if (pid != LLDB_INVALID_PROCESS_ID)
-    attach_info.SetProcessID(pid);
-  const auto wait_for = GetBoolean(arguments, "waitFor", false);
-  attach_info.SetWaitForLaunch(wait_for, false /*async*/);
-  dap.init_commands = GetStrings(arguments, "initCommands");
-  dap.pre_run_commands = GetStrings(arguments, "preRunCommands");
-  dap.stop_commands = GetStrings(arguments, "stopCommands");
-  dap.exit_commands = GetStrings(arguments, "exitCommands");
-  dap.terminate_commands = GetStrings(arguments, "terminateCommands");
-  auto attachCommands = GetStrings(arguments, "attachCommands");
-  llvm::StringRef core_file = GetString(arguments, "coreFile");
-  const uint64_t timeout_seconds = GetUnsigned(arguments, "timeout", 30);
-  dap.stop_at_entry =
-      core_file.empty() ? GetBoolean(arguments, "stopOnEntry", false) : true;
-  dap.post_run_commands = GetStrings(arguments, "postRunCommands");
-  const llvm::StringRef debuggerRoot = GetString(arguments, "debuggerRoot");
-  dap.enable_auto_variable_summaries =
-      GetBoolean(arguments, "enableAutoVariableSummaries", false);
-  dap.enable_synthetic_child_debugging =
-      GetBoolean(arguments, "enableSyntheticChildDebugging", false);
-  dap.display_extended_backtrace =
-      GetBoolean(arguments, "displayExtendedBacktrace", false);
-  dap.command_escape_prefix = GetString(arguments, "commandEscapePrefix", "`");
-  dap.SetFrameFormat(GetString(arguments, "customFrameFormat"));
-  dap.SetThreadFormat(GetString(arguments, "customThreadFormat"));
-
-  PrintWelcomeMessage(dap);
-
-  // This is a hack for loading DWARF in .o files on Mac where the .o files
-  // in the debug map of the main executable have relative paths which require
-  // the lldb-dap binary to have its working directory set to that relative
-  // root for the .o files in order to be able to load debug info.
-  if (!debuggerRoot.empty())
-    llvm::sys::fs::set_current_path(debuggerRoot);
-
-  // Run any initialize LLDB commands the user specified in the launch.json
-  if (llvm::Error err = dap.RunInitCommands()) {
-    response["success"] = false;
-    EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
-    dap.SendJSON(llvm::json::Value(std::move(response)));
-    return;
-  }
-
-  SetSourceMapFromArguments(dap, *arguments);
-
-  lldb::SBError status;
-  dap.SetTarget(dap.CreateTargetFromArguments(*arguments, status));
-  if (status.Fail()) {
-    response["success"] = llvm::json::Value(false);
-    EmplaceSafeString(response, "message", status.GetCString());
-    dap.SendJSON(llvm::json::Value(std::move(response)));
-    return;
-  }
-
-  // Run any pre run LLDB commands the user specified in the launch.json
-  if (llvm::Error err = dap.RunPreRunCommands()) {
-    response["success"] = false;
-    EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
-    dap.SendJSON(llvm::json::Value(std::move(response)));
-    return;
-  }
-
-  if ((pid == LLDB_INVALID_PROCESS_ID || gdb_remote_port == invalid_port) &&
-      wait_for) {
-    char attach_msg[256];
-    auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg),
-                                   "Waiting to attach to \"%s\"...",
-                                   dap.target.GetExecutable().GetFilename());
-    dap.SendOutput(OutputType::Console,
-                   llvm::StringRef(attach_msg, attach_msg_len));
-  }
-  if (attachCommands.empty()) {
-    // No "attachCommands", just attach normally.
-    // Disable async events so the attach will be successful when we return from
-    // the launch call and the launch will happen synchronously
-    dap.debugger.SetAsync(false);
-    if (core_file.empty()) {
-      if ((pid != LLDB_INVALID_PROCESS_ID) &&
-          (gdb_remote_port != invalid_port)) {
-        // If both pid and port numbers are specified.
-        error.SetErrorString("The user can't specify both pid and port");
-      } else if (gdb_remote_port != invalid_port) {
-        // If port is specified and pid is not.
-        lldb::SBListener listener = dap.debugger.GetListener();
-
-        // If the user hasn't provided the hostname property, default localhost
-        // being used.
-        std::string connect_url =
-            llvm::formatv("connect://{0}:", gdb_remote_hostname);
-        connect_url += std::to_string(gdb_remote_port);
-        dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
-                                 error);
-      } else {
-        // Attach by process name or id.
-        dap.target.Attach(attach_info, error);
-      }
-    } else
-      dap.target.LoadCore(core_file.data(), error);
-    // Reenable async events
-    dap.debugger.SetAsync(true);
-  } else {
-    // We have "attachCommands" that are a set of commands that are expected
-    // to execute the commands after which a process should be created. If there
-    // is no valid process after running these commands, we have failed.
-    if (llvm::Error err = dap.RunAttachCommands(attachCommands)) {
-      response["success"] = false;
-      EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
-      dap.SendJSON(llvm::json::Value(std::move(response)));
-      return;
-    }
-    // The custom commands might have created a new target so we should use the
-    // selected target after these commands are run.
-    dap.target = dap.debugger.GetSelectedTarget();
-
-    // Make sure the process is attached and stopped before proceeding as the
-    // the launch commands are not run using the synchronous mode.
-    error = dap.WaitForProcessToStop(timeout_seconds);
-  }
-
-  if (error.Success() && core_file.empty()) {
-    auto attached_pid = dap.target.GetProcess().GetProcessID();
-    if (attached_pid == LLDB_INVALID_PROCESS_ID) {
-      if (attachCommands.empty())
-        error.SetErrorString("failed to attach to a process");
-      else
-        error.SetErrorString("attachCommands failed to attach to a process");
-    }
-  }
-
-  if (error.Fail()) {
-    response["success"] = llvm::json::Value(false);
-    EmplaceSafeString(response, "message", std::string(error.GetCString()));
-  } else {
-    dap.RunPostRunCommands();
-  }
-
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-  if (error.Success()) {
-    SendProcessEvent(dap, Attach);
-    dap.SendJSON(CreateEventObject("initialized"));
-  }
-}
-
 // "BreakpointLocationsRequest": {
 //   "allOf": [ { "$ref": "#/definitions/Request" }, {
 //     "type": "object",
@@ -4998,7 +4813,8 @@ void request_setInstructionBreakpoints(DAP &dap,
 }
 
 void RegisterRequestCallbacks(DAP &dap) {
-  dap.RegisterRequestCallback("attach", request_attach);
+  dap.RegisterRequest<AttachRequest>();
+
   dap.RegisterRequestCallback("breakpointLocations",
                               request_breakpointLocations);
   dap.RegisterRequestCallback("completions", request_completions);

>From 92322aee828df65ae4ca7523d17c60162e3802b3 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Sat, 22 Feb 2025 12:10:00 -0600
Subject: [PATCH 02/11] Address review feedback

---
 lldb/tools/lldb-dap/CMakeLists.txt            |  4 ++--
 lldb/tools/lldb-dap/DAP.h                     |  9 ++++----
 .../AttachHandler.cpp}                        |  6 ++---
 .../RequestHandler.cpp}                       | 10 ++++----
 .../Request.h => Handler/RequestHandler.h}    | 23 ++++++++++---------
 lldb/tools/lldb-dap/lldb-dap.cpp              |  4 ++--
 6 files changed, 30 insertions(+), 26 deletions(-)
 rename lldb/tools/lldb-dap/{Request/AttachRequest.cpp => Handler/AttachHandler.cpp} (98%)
 rename lldb/tools/lldb-dap/{Request/Request.cpp => Handler/RequestHandler.cpp} (92%)
 rename lldb/tools/lldb-dap/{Request/Request.h => Handler/RequestHandler.h} (65%)

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index e97f7ecdbd31b..a3c605bf72391 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -36,8 +36,8 @@ add_lldb_tool(lldb-dap
   SourceBreakpoint.cpp
   Watchpoint.cpp
 
-  Request/Request.cpp
-  Request/AttachRequest.cpp
+  Handler/RequestHandler.cpp
+  Handler/AttachHandler.cpp
 
   LINK_LIBS
     liblldb
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 33507061e0750..5abd48d6363cf 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -12,11 +12,11 @@
 #include "DAPForward.h"
 #include "ExceptionBreakpoint.h"
 #include "FunctionBreakpoint.h"
+#include "Handler/RequestHandler.h"
 #include "IOStream.h"
 #include "InstructionBreakpoint.h"
 #include "OutputRedirector.h"
 #include "ProgressEvent.h"
-#include "Request/Request.h"
 #include "SourceBreakpoint.h"
 #include "lldb/API/SBBroadcaster.h"
 #include "lldb/API/SBCommandInterpreter.h"
@@ -186,7 +186,7 @@ struct DAP {
   lldb::pid_t restarting_process_id;
   bool configuration_done_sent;
   std::map<std::string, RequestCallback, std::less<>> request_handlers;
-  llvm::StringMap<std::unique_ptr<Request>> new_request_handlers;
+  llvm::StringMap<std::unique_ptr<RequestHandler>> new_request_handlers;
   bool waiting_for_run_in_terminal;
   ProgressEventReporter progress_event_reporter;
   // Keep track of the last stop thread index IDs as threads won't go away
@@ -334,8 +334,9 @@ struct DAP {
   void RegisterRequestCallback(std::string request, RequestCallback callback);
 
   /// Registers a request handler.
-  template <typename Request> void RegisterRequest() {
-    new_request_handlers[Request::getName()] = std::make_unique<Request>(*this);
+  template <typename Handler> void RegisterRequest() {
+    new_request_handlers[Handler::getCommand()] =
+        std::make_unique<Handler>(*this);
   }
 
   /// Debuggee will continue from stopped state.
diff --git a/lldb/tools/lldb-dap/Request/AttachRequest.cpp b/lldb/tools/lldb-dap/Handler/AttachHandler.cpp
similarity index 98%
rename from lldb/tools/lldb-dap/Request/AttachRequest.cpp
rename to lldb/tools/lldb-dap/Handler/AttachHandler.cpp
index f1b3bfc878427..3dcde2144246e 100644
--- a/lldb/tools/lldb-dap/Request/AttachRequest.cpp
+++ b/lldb/tools/lldb-dap/Handler/AttachHandler.cpp
@@ -1,4 +1,4 @@
-//===-- AttachRequest.cpp -------------------------------------------------===//
+//===-- AttachHandler.cpp -------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -8,7 +8,7 @@
 
 #include "DAP.h"
 #include "JSONUtils.h"
-#include "Request.h"
+#include "RequestHandler.h"
 #include "lldb/API/SBListener.h"
 #include "llvm/Support/FileSystem.h"
 
@@ -50,7 +50,7 @@ static void PrintWelcomeMessage(DAP &dap) {
 //   }]
 // }
 
-void AttachRequest::operator()(const llvm::json::Object &request) {
+void AttachRequestHandler::operator()(const llvm::json::Object &request) {
   dap.is_attach = true;
   dap.last_launch_or_attach_request = request;
   llvm::json::Object response;
diff --git a/lldb/tools/lldb-dap/Request/Request.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
similarity index 92%
rename from lldb/tools/lldb-dap/Request/Request.cpp
rename to lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index 9fe2445be9d32..ef091a805902d 100644
--- a/lldb/tools/lldb-dap/Request/Request.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -1,4 +1,4 @@
-//===-- Request.cpp -------------------------------------------------------===//
+//===-- Handler.cpp -------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,14 +6,15 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "Request.h"
+#include "RequestHandler.h"
 #include "DAP.h"
 #include "JSONUtils.h"
 #include "lldb/API/SBFileSpec.h"
 
 namespace lldb_dap {
 
-void Request::SendProcessEvent(Request::LaunchMethod launch_method) {
+void RequestHandler::SendProcessEvent(
+    RequestHandler::LaunchMethod launch_method) {
   lldb::SBFileSpec exe_fspec = dap.target.GetExecutable();
   char exe_path[PATH_MAX];
   exe_fspec.GetPath(exe_path, sizeof(exe_path));
@@ -42,7 +43,8 @@ void Request::SendProcessEvent(Request::LaunchMethod launch_method) {
 
 // Both attach and launch take a either a sourcePath or sourceMap
 // argument (or neither), from which we need to set the target.source-map.
-void Request::SetSourceMapFromArguments(const llvm::json::Object &arguments) {
+void RequestHandler::SetSourceMapFromArguments(
+    const llvm::json::Object &arguments) {
   const char *sourceMapHelp =
       "source must be be an array of two-element arrays, "
       "each containing a source and replacement path string.\n";
diff --git a/lldb/tools/lldb-dap/Request/Request.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
similarity index 65%
rename from lldb/tools/lldb-dap/Request/Request.h
rename to lldb/tools/lldb-dap/Handler/RequestHandler.h
index 339c3b1fb323f..4a60a7f3d1142 100644
--- a/lldb/tools/lldb-dap/Request/Request.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLDB_TOOLS_LLDB_DAP_REQUEST_REQUEST_H
-#define LLDB_TOOLS_LLDB_DAP_REQUEST_REQUEST_H
+#ifndef LLDB_TOOLS_LLDB_DAP_HANDLER_HANDLER_H
+#define LLDB_TOOLS_LLDB_DAP_HANDLER_HANDLER_H
 
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/JSON.h"
@@ -15,28 +15,29 @@
 namespace lldb_dap {
 struct DAP;
 
-class Request {
+class RequestHandler {
 public:
-  Request(DAP &dap) : dap(dap) {}
-  virtual ~Request() = default;
+  RequestHandler(DAP &dap) : dap(dap) {}
+  virtual ~RequestHandler() = default;
 
   virtual void operator()(const llvm::json::Object &request) = 0;
 
-  static llvm::StringLiteral getName() { return "invalid"; };
-
+  /// Helpers used by multiple request handlers.
+  /// FIXME: Move these into the DAP class.
+  /// @{
   enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
-
   void SendProcessEvent(LaunchMethod launch_method);
   void SetSourceMapFromArguments(const llvm::json::Object &arguments);
+  /// @}
 
 protected:
   DAP &dap;
 };
 
-class AttachRequest : public Request {
+class AttachRequestHandler : public RequestHandler {
 public:
-  using Request::Request;
-  static llvm::StringLiteral getName() { return "attach"; }
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "attach"; }
   void operator()(const llvm::json::Object &request) override;
 };
 
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 997b654011b64..c84eb5d7c7419 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -8,9 +8,9 @@
 
 #include "DAP.h"
 #include "FifoFiles.h"
+#include "Handler/RequestHandler.h"
 #include "JSONUtils.h"
 #include "LLDBUtils.h"
-#include "Request/Request.h"
 #include "RunInTerminal.h"
 #include "Watchpoint.h"
 #include "lldb/API/SBDeclaration.h"
@@ -4813,7 +4813,7 @@ void request_setInstructionBreakpoints(DAP &dap,
 }
 
 void RegisterRequestCallbacks(DAP &dap) {
-  dap.RegisterRequest<AttachRequest>();
+  dap.RegisterRequest<AttachRequestHandler>();
 
   dap.RegisterRequestCallback("breakpointLocations",
                               request_breakpointLocations);

>From 86302e50898db397d8ed731921e0cab861c5f648 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Sat, 22 Feb 2025 13:54:22 -0600
Subject: [PATCH 03/11] Address review feedback

---
 lldb/tools/lldb-dap/Handler/RequestHandler.h | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 4a60a7f3d1142..94acc1241243a 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -18,6 +18,13 @@ struct DAP;
 class RequestHandler {
 public:
   RequestHandler(DAP &dap) : dap(dap) {}
+
+  /// RequestHandler are not copyable.
+  /// @{
+  RequestHandler(const RequestHandler &) = delete;
+  RequestHandler &operator=(const RequestHandler &) = delete;
+  /// @}
+
   virtual ~RequestHandler() = default;
 
   virtual void operator()(const llvm::json::Object &request) = 0;

>From 9235950649593bdd47a32b1966fefe83d985e478 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Sat, 22 Feb 2025 19:21:57 -0600
Subject: [PATCH 04/11] Adopt BreakpointLocationsHandler

---
 lldb/tools/lldb-dap/CMakeLists.txt            |   3 +-
 .../Handler/BreakpointLocationsHandler.cpp    | 206 ++++++++++++++++++
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |   7 +
 lldb/tools/lldb-dap/lldb-dap.cpp              | 193 +---------------
 4 files changed, 216 insertions(+), 193 deletions(-)
 create mode 100644 lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index a3c605bf72391..b0208578e0566 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -36,8 +36,9 @@ add_lldb_tool(lldb-dap
   SourceBreakpoint.cpp
   Watchpoint.cpp
 
-  Handler/RequestHandler.cpp
   Handler/AttachHandler.cpp
+  Handler/BreakpointLocationsHandler.cpp
+  Handler/RequestHandler.cpp
 
   LINK_LIBS
     liblldb
diff --git a/lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp b/lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp
new file mode 100644
index 0000000000000..a324e59999ab1
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp
@@ -0,0 +1,206 @@
+//===-- BreakpointLocationsHandler..cpp -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "BreakpointLocationsRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "The `breakpointLocations` request returns all possible
+//     locations for source breakpoints in a given range.\nClients should only
+//     call this request if the corresponding capability
+//     `supportsBreakpointLocationsRequest` is true.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "breakpointLocations" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/BreakpointLocationsArguments"
+//       }
+//     },
+//     "required": [ "command" ]
+//   }]
+// },
+// "BreakpointLocationsArguments": {
+//   "type": "object",
+//   "description": "Arguments for `breakpointLocations` request.",
+//   "properties": {
+//     "source": {
+//       "$ref": "#/definitions/Source",
+//       "description": "The source location of the breakpoints; either
+//       `source.path` or `source.sourceReference` must be specified."
+//     },
+//     "line": {
+//       "type": "integer",
+//       "description": "Start line of range to search possible breakpoint
+//       locations in. If only the line is specified, the request returns all
+//       possible locations in that line."
+//     },
+//     "column": {
+//       "type": "integer",
+//       "description": "Start position within `line` to search possible
+//       breakpoint locations in. It is measured in UTF-16 code units and the
+//       client capability `columnsStartAt1` determines whether it is 0- or
+//       1-based. If no column is given, the first position in the start line is
+//       assumed."
+//     },
+//     "endLine": {
+//       "type": "integer",
+//       "description": "End line of range to search possible breakpoint
+//       locations in. If no end line is given, then the end line is assumed to
+//       be the start line."
+//     },
+//     "endColumn": {
+//       "type": "integer",
+//       "description": "End position within `endLine` to search possible
+//       breakpoint locations in. It is measured in UTF-16 code units and the
+//       client capability `columnsStartAt1` determines whether it is 0- or
+//       1-based. If no end column is given, the last position in the end line
+//       is assumed."
+//     }
+//   },
+//   "required": [ "source", "line" ]
+// },
+// "BreakpointLocationsResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to `breakpointLocations` request.\nContains
+//     possible locations for source breakpoints.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "breakpoints": {
+//             "type": "array",
+//             "items": {
+//               "$ref": "#/definitions/BreakpointLocation"
+//             },
+//             "description": "Sorted set of possible breakpoint locations."
+//           }
+//         },
+//         "required": [ "breakpoints" ]
+//       }
+//     },
+//     "required": [ "body" ]
+//   }]
+// },
+// "BreakpointLocation": {
+//   "type": "object",
+//   "description": "Properties of a breakpoint location returned from the
+//   `breakpointLocations` request.",
+//   "properties": {
+//     "line": {
+//       "type": "integer",
+//       "description": "Start line of breakpoint location."
+//     },
+//     "column": {
+//       "type": "integer",
+//       "description": "The start position of a breakpoint location. Position
+//       is measured in UTF-16 code units and the client capability
+//       `columnsStartAt1` determines whether it is 0- or 1-based."
+//     },
+//     "endLine": {
+//       "type": "integer",
+//       "description": "The end line of breakpoint location if the location
+//       covers a range."
+//     },
+//     "endColumn": {
+//       "type": "integer",
+//       "description": "The end position of a breakpoint location (if the
+//       location covers a range). Position is measured in UTF-16 code units and
+//       the client capability `columnsStartAt1` determines whether it is 0- or
+//       1-based."
+//     }
+//   },
+//   "required": [ "line" ]
+// },
+void BreakpointLocationsRequestHandler::operator()(
+    const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  auto *arguments = request.getObject("arguments");
+  auto *source = arguments->getObject("source");
+  std::string path = GetString(source, "path").str();
+  uint64_t start_line = GetUnsigned(arguments, "line", 0);
+  uint64_t start_column = GetUnsigned(arguments, "column", 0);
+  uint64_t end_line = GetUnsigned(arguments, "endLine", start_line);
+  uint64_t end_column =
+      GetUnsigned(arguments, "endColumn", std::numeric_limits<uint64_t>::max());
+
+  lldb::SBFileSpec file_spec(path.c_str(), true);
+  lldb::SBSymbolContextList compile_units =
+      dap.target.FindCompileUnits(file_spec);
+
+  // Find all relevant lines & columns
+  llvm::SmallVector<std::pair<uint32_t, uint32_t>, 8> locations;
+  for (uint32_t c_idx = 0, c_limit = compile_units.GetSize(); c_idx < c_limit;
+       ++c_idx) {
+    const lldb::SBCompileUnit &compile_unit =
+        compile_units.GetContextAtIndex(c_idx).GetCompileUnit();
+    if (!compile_unit.IsValid())
+      continue;
+    lldb::SBFileSpec primary_file_spec = compile_unit.GetFileSpec();
+
+    // Go through the line table and find all matching lines / columns
+    for (uint32_t l_idx = 0, l_limit = compile_unit.GetNumLineEntries();
+         l_idx < l_limit; ++l_idx) {
+      lldb::SBLineEntry line_entry = compile_unit.GetLineEntryAtIndex(l_idx);
+
+      // Filter by line / column
+      uint32_t line = line_entry.GetLine();
+      if (line < start_line || line > end_line)
+        continue;
+      uint32_t column = line_entry.GetColumn();
+      if (column == LLDB_INVALID_COLUMN_NUMBER)
+        continue;
+      if (line == start_line && column < start_column)
+        continue;
+      if (line == end_line && column > end_column)
+        continue;
+
+      // Make sure we are in the right file.
+      // We might have a match on line & column range and still
+      // be in the wrong file, e.g. for included files.
+      // Given that the involved pointers point into LLDB's string pool,
+      // we can directly compare the `const char*` pointers.
+      if (line_entry.GetFileSpec().GetFilename() !=
+              primary_file_spec.GetFilename() ||
+          line_entry.GetFileSpec().GetDirectory() !=
+              primary_file_spec.GetDirectory())
+        continue;
+
+      locations.emplace_back(line, column);
+    }
+  }
+
+  // The line entries are sorted by addresses, but we must return the list
+  // ordered by line / column position.
+  std::sort(locations.begin(), locations.end());
+  locations.erase(std::unique(locations.begin(), locations.end()),
+                  locations.end());
+
+  llvm::json::Array locations_json;
+  for (auto &l : locations) {
+    llvm::json::Object location;
+    location.try_emplace("line", l.first);
+    location.try_emplace("column", l.second);
+    locations_json.emplace_back(std::move(location));
+  }
+
+  llvm::json::Object body;
+  body.try_emplace("breakpoints", std::move(locations_json));
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 94acc1241243a..854fb88c36b86 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -48,6 +48,13 @@ class AttachRequestHandler : public RequestHandler {
   void operator()(const llvm::json::Object &request) override;
 };
 
+class BreakpointLocationsRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "breakpointLocations"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
 } // namespace lldb_dap
 
 #endif
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index c84eb5d7c7419..fe91f38609ff0 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -727,196 +727,6 @@ bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
   return reached_end_of_stack;
 }
 
-// "BreakpointLocationsRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "The `breakpointLocations` request returns all possible
-//     locations for source breakpoints in a given range.\nClients should only
-//     call this request if the corresponding capability
-//     `supportsBreakpointLocationsRequest` is true.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "breakpointLocations" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/BreakpointLocationsArguments"
-//       }
-//     },
-//     "required": [ "command" ]
-//   }]
-// },
-// "BreakpointLocationsArguments": {
-//   "type": "object",
-//   "description": "Arguments for `breakpointLocations` request.",
-//   "properties": {
-//     "source": {
-//       "$ref": "#/definitions/Source",
-//       "description": "The source location of the breakpoints; either
-//       `source.path` or `source.sourceReference` must be specified."
-//     },
-//     "line": {
-//       "type": "integer",
-//       "description": "Start line of range to search possible breakpoint
-//       locations in. If only the line is specified, the request returns all
-//       possible locations in that line."
-//     },
-//     "column": {
-//       "type": "integer",
-//       "description": "Start position within `line` to search possible
-//       breakpoint locations in. It is measured in UTF-16 code units and the
-//       client capability `columnsStartAt1` determines whether it is 0- or
-//       1-based. If no column is given, the first position in the start line is
-//       assumed."
-//     },
-//     "endLine": {
-//       "type": "integer",
-//       "description": "End line of range to search possible breakpoint
-//       locations in. If no end line is given, then the end line is assumed to
-//       be the start line."
-//     },
-//     "endColumn": {
-//       "type": "integer",
-//       "description": "End position within `endLine` to search possible
-//       breakpoint locations in. It is measured in UTF-16 code units and the
-//       client capability `columnsStartAt1` determines whether it is 0- or
-//       1-based. If no end column is given, the last position in the end line
-//       is assumed."
-//     }
-//   },
-//   "required": [ "source", "line" ]
-// },
-// "BreakpointLocationsResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to `breakpointLocations` request.\nContains
-//     possible locations for source breakpoints.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "breakpoints": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/BreakpointLocation"
-//             },
-//             "description": "Sorted set of possible breakpoint locations."
-//           }
-//         },
-//         "required": [ "breakpoints" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// },
-// "BreakpointLocation": {
-//   "type": "object",
-//   "description": "Properties of a breakpoint location returned from the
-//   `breakpointLocations` request.",
-//   "properties": {
-//     "line": {
-//       "type": "integer",
-//       "description": "Start line of breakpoint location."
-//     },
-//     "column": {
-//       "type": "integer",
-//       "description": "The start position of a breakpoint location. Position
-//       is measured in UTF-16 code units and the client capability
-//       `columnsStartAt1` determines whether it is 0- or 1-based."
-//     },
-//     "endLine": {
-//       "type": "integer",
-//       "description": "The end line of breakpoint location if the location
-//       covers a range."
-//     },
-//     "endColumn": {
-//       "type": "integer",
-//       "description": "The end position of a breakpoint location (if the
-//       location covers a range). Position is measured in UTF-16 code units and
-//       the client capability `columnsStartAt1` determines whether it is 0- or
-//       1-based."
-//     }
-//   },
-//   "required": [ "line" ]
-// },
-void request_breakpointLocations(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  auto *arguments = request.getObject("arguments");
-  auto *source = arguments->getObject("source");
-  std::string path = GetString(source, "path").str();
-  uint64_t start_line = GetUnsigned(arguments, "line", 0);
-  uint64_t start_column = GetUnsigned(arguments, "column", 0);
-  uint64_t end_line = GetUnsigned(arguments, "endLine", start_line);
-  uint64_t end_column =
-      GetUnsigned(arguments, "endColumn", std::numeric_limits<uint64_t>::max());
-
-  lldb::SBFileSpec file_spec(path.c_str(), true);
-  lldb::SBSymbolContextList compile_units =
-      dap.target.FindCompileUnits(file_spec);
-
-  // Find all relevant lines & columns
-  llvm::SmallVector<std::pair<uint32_t, uint32_t>, 8> locations;
-  for (uint32_t c_idx = 0, c_limit = compile_units.GetSize(); c_idx < c_limit;
-       ++c_idx) {
-    const lldb::SBCompileUnit &compile_unit =
-        compile_units.GetContextAtIndex(c_idx).GetCompileUnit();
-    if (!compile_unit.IsValid())
-      continue;
-    lldb::SBFileSpec primary_file_spec = compile_unit.GetFileSpec();
-
-    // Go through the line table and find all matching lines / columns
-    for (uint32_t l_idx = 0, l_limit = compile_unit.GetNumLineEntries();
-         l_idx < l_limit; ++l_idx) {
-      lldb::SBLineEntry line_entry = compile_unit.GetLineEntryAtIndex(l_idx);
-
-      // Filter by line / column
-      uint32_t line = line_entry.GetLine();
-      if (line < start_line || line > end_line)
-        continue;
-      uint32_t column = line_entry.GetColumn();
-      if (column == LLDB_INVALID_COLUMN_NUMBER)
-        continue;
-      if (line == start_line && column < start_column)
-        continue;
-      if (line == end_line && column > end_column)
-        continue;
-
-      // Make sure we are in the right file.
-      // We might have a match on line & column range and still
-      // be in the wrong file, e.g. for included files.
-      // Given that the involved pointers point into LLDB's string pool,
-      // we can directly compare the `const char*` pointers.
-      if (line_entry.GetFileSpec().GetFilename() !=
-              primary_file_spec.GetFilename() ||
-          line_entry.GetFileSpec().GetDirectory() !=
-              primary_file_spec.GetDirectory())
-        continue;
-
-      locations.emplace_back(line, column);
-    }
-  }
-
-  // The line entries are sorted by addresses, but we must return the list
-  // ordered by line / column position.
-  std::sort(locations.begin(), locations.end());
-  locations.erase(std::unique(locations.begin(), locations.end()),
-                  locations.end());
-
-  llvm::json::Array locations_json;
-  for (auto &l : locations) {
-    llvm::json::Object location;
-    location.try_emplace("line", l.first);
-    location.try_emplace("column", l.second);
-    locations_json.emplace_back(std::move(location));
-  }
-
-  llvm::json::Object body;
-  body.try_emplace("breakpoints", std::move(locations_json));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
 // "ContinueRequest": {
 //   "allOf": [ { "$ref": "#/definitions/Request" }, {
 //     "type": "object",
@@ -4814,9 +4624,8 @@ void request_setInstructionBreakpoints(DAP &dap,
 
 void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<AttachRequestHandler>();
+  dap.RegisterRequest<BreakpointLocationsRequestHandler>();
 
-  dap.RegisterRequestCallback("breakpointLocations",
-                              request_breakpointLocations);
   dap.RegisterRequestCallback("completions", request_completions);
   dap.RegisterRequestCallback("continue", request_continue);
   dap.RegisterRequestCallback("configurationDone", request_configurationDone);

>From 65576580f595428e65f07533e0ed380c4bb0b452 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Sat, 22 Feb 2025 19:27:26 -0600
Subject: [PATCH 05/11] Adopt CompletionsHandler

---
 lldb/tools/lldb-dap/CMakeLists.txt            |   1 +
 .../lldb-dap/Handler/CompletionsHandler.cpp   | 221 ++++++++++++++++++
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |   7 +
 lldb/tools/lldb-dap/lldb-dap.cpp              | 207 +---------------
 4 files changed, 230 insertions(+), 206 deletions(-)
 create mode 100644 lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index b0208578e0566..d41baae1ef5d9 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -38,6 +38,7 @@ add_lldb_tool(lldb-dap
 
   Handler/AttachHandler.cpp
   Handler/BreakpointLocationsHandler.cpp
+  Handler/CompletionsHandler.cpp
   Handler/RequestHandler.cpp
 
   LINK_LIBS
diff --git a/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp b/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp
new file mode 100644
index 0000000000000..b7cafb3c47ac6
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp
@@ -0,0 +1,221 @@
+//===-- CompletionsHandler.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+#include "lldb/API/SBStringList.h"
+
+namespace lldb_dap {
+
+// "CompletionsRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Returns a list of possible completions for a given caret
+//     position and text.\nThe CompletionsRequest may only be called if the
+//     'supportsCompletionsRequest' capability exists and is true.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "completions" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/CompletionsArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "CompletionsArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'completions' request.",
+//   "properties": {
+//     "frameId": {
+//       "type": "integer",
+//       "description": "Returns completions in the scope of this stack frame.
+//       If not specified, the completions are returned for the global scope."
+//     },
+//     "text": {
+//       "type": "string",
+//       "description": "One or more source lines. Typically this is the text a
+//       user has typed into the debug console before he asked for completion."
+//     },
+//     "column": {
+//       "type": "integer",
+//       "description": "The character position for which to determine the
+//       completion proposals."
+//     },
+//     "line": {
+//       "type": "integer",
+//       "description": "An optional line for which to determine the completion
+//       proposals. If missing the first line of the text is assumed."
+//     }
+//   },
+//   "required": [ "text", "column" ]
+// },
+// "CompletionsResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'completions' request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "targets": {
+//             "type": "array",
+//             "items": {
+//               "$ref": "#/definitions/CompletionItem"
+//             },
+//             "description": "The possible completions for ."
+//           }
+//         },
+//         "required": [ "targets" ]
+//       }
+//     },
+//     "required": [ "body" ]
+//   }]
+// },
+// "CompletionItem": {
+//   "type": "object",
+//   "description": "CompletionItems are the suggestions returned from the
+//   CompletionsRequest.", "properties": {
+//     "label": {
+//       "type": "string",
+//       "description": "The label of this completion item. By default this is
+//       also the text that is inserted when selecting this completion."
+//     },
+//     "text": {
+//       "type": "string",
+//       "description": "If text is not falsy then it is inserted instead of the
+//       label."
+//     },
+//     "sortText": {
+//       "type": "string",
+//       "description": "A string that should be used when comparing this item
+//       with other items. When `falsy` the label is used."
+//     },
+//     "type": {
+//       "$ref": "#/definitions/CompletionItemType",
+//       "description": "The item's type. Typically the client uses this
+//       information to render the item in the UI with an icon."
+//     },
+//     "start": {
+//       "type": "integer",
+//       "description": "This value determines the location (in the
+//       CompletionsRequest's 'text' attribute) where the completion text is
+//       added.\nIf missing the text is added at the location specified by the
+//       CompletionsRequest's 'column' attribute."
+//     },
+//     "length": {
+//       "type": "integer",
+//       "description": "This value determines how many characters are
+//       overwritten by the completion text.\nIf missing the value 0 is assumed
+//       which results in the completion text being inserted."
+//     }
+//   },
+//   "required": [ "label" ]
+// },
+// "CompletionItemType": {
+//   "type": "string",
+//   "description": "Some predefined types for the CompletionItem. Please note
+//   that not all clients have specific icons for all of them.", "enum": [
+//   "method", "function", "constructor", "field", "variable", "class",
+//   "interface", "module", "property", "unit", "value", "enum", "keyword",
+//   "snippet", "text", "color", "file", "reference", "customcolor" ]
+// }
+void CompletionsRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  llvm::json::Object body;
+  const auto *arguments = request.getObject("arguments");
+
+  // If we have a frame, try to set the context for variable completions.
+  lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
+  if (frame.IsValid()) {
+    frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread());
+    frame.GetThread().SetSelectedFrame(frame.GetFrameID());
+  }
+
+  std::string text = GetString(arguments, "text").str();
+  auto original_column = GetSigned(arguments, "column", text.size());
+  auto original_line = GetSigned(arguments, "line", 1);
+  auto offset = original_column - 1;
+  if (original_line > 1) {
+    llvm::SmallVector<::llvm::StringRef, 2> lines;
+    llvm::StringRef(text).split(lines, '\n');
+    for (int i = 0; i < original_line - 1; i++) {
+      offset += lines[i].size();
+    }
+  }
+  llvm::json::Array targets;
+
+  bool had_escape_prefix =
+      llvm::StringRef(text).starts_with(dap.command_escape_prefix);
+  ReplMode completion_mode = dap.DetectReplMode(frame, text, true);
+
+  // Handle the offset change introduced by stripping out the
+  // `command_escape_prefix`.
+  if (had_escape_prefix) {
+    if (offset < static_cast<int64_t>(dap.command_escape_prefix.size())) {
+      body.try_emplace("targets", std::move(targets));
+      response.try_emplace("body", std::move(body));
+      dap.SendJSON(llvm::json::Value(std::move(response)));
+      return;
+    }
+    offset -= dap.command_escape_prefix.size();
+  }
+
+  // While the user is typing then we likely have an incomplete input and cannot
+  // reliably determine the precise intent (command vs variable), try completing
+  // the text as both a command and variable expression, if applicable.
+  const std::string expr_prefix = "expression -- ";
+  std::array<std::tuple<ReplMode, std::string, uint64_t>, 2> exprs = {
+      {std::make_tuple(ReplMode::Command, text, offset),
+       std::make_tuple(ReplMode::Variable, expr_prefix + text,
+                       offset + expr_prefix.size())}};
+  for (const auto &[mode, line, cursor] : exprs) {
+    if (completion_mode != ReplMode::Auto && completion_mode != mode)
+      continue;
+
+    lldb::SBStringList matches;
+    lldb::SBStringList descriptions;
+    if (!dap.debugger.GetCommandInterpreter().HandleCompletionWithDescriptions(
+            line.c_str(), cursor, 0, 100, matches, descriptions))
+      continue;
+
+    // The first element is the common substring after the cursor position for
+    // all the matches. The rest of the elements are the matches so ignore the
+    // first result.
+    for (size_t i = 1; i < matches.GetSize(); i++) {
+      std::string match = matches.GetStringAtIndex(i);
+      std::string description = descriptions.GetStringAtIndex(i);
+
+      llvm::json::Object item;
+      llvm::StringRef match_ref = match;
+      for (llvm::StringRef commit_point : {".", "->"}) {
+        if (match_ref.contains(commit_point)) {
+          match_ref = match_ref.rsplit(commit_point).second;
+        }
+      }
+      EmplaceSafeString(item, "text", match_ref);
+
+      if (description.empty())
+        EmplaceSafeString(item, "label", match);
+      else
+        EmplaceSafeString(item, "label", match + " -- " + description);
+
+      targets.emplace_back(std::move(item));
+    }
+  }
+
+  body.try_emplace("targets", std::move(targets));
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 854fb88c36b86..76130ade76f32 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -55,6 +55,13 @@ class BreakpointLocationsRequestHandler : public RequestHandler {
   void operator()(const llvm::json::Object &request) override;
 };
 
+class CompletionsRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "completions"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
 } // namespace lldb_dap
 
 #endif
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index fe91f38609ff0..e20b1c5272336 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -19,7 +19,6 @@
 #include "lldb/API/SBListener.h"
 #include "lldb/API/SBMemoryRegionInfo.h"
 #include "lldb/API/SBStream.h"
-#include "lldb/API/SBStringList.h"
 #include "lldb/Host/Config.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
@@ -1080,210 +1079,6 @@ void request_exceptionInfo(DAP &dap, const llvm::json::Object &request) {
   dap.SendJSON(llvm::json::Value(std::move(response)));
 }
 
-// "CompletionsRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Returns a list of possible completions for a given caret
-//     position and text.\nThe CompletionsRequest may only be called if the
-//     'supportsCompletionsRequest' capability exists and is true.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "completions" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/CompletionsArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "CompletionsArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'completions' request.",
-//   "properties": {
-//     "frameId": {
-//       "type": "integer",
-//       "description": "Returns completions in the scope of this stack frame.
-//       If not specified, the completions are returned for the global scope."
-//     },
-//     "text": {
-//       "type": "string",
-//       "description": "One or more source lines. Typically this is the text a
-//       user has typed into the debug console before he asked for completion."
-//     },
-//     "column": {
-//       "type": "integer",
-//       "description": "The character position for which to determine the
-//       completion proposals."
-//     },
-//     "line": {
-//       "type": "integer",
-//       "description": "An optional line for which to determine the completion
-//       proposals. If missing the first line of the text is assumed."
-//     }
-//   },
-//   "required": [ "text", "column" ]
-// },
-// "CompletionsResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'completions' request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "targets": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/CompletionItem"
-//             },
-//             "description": "The possible completions for ."
-//           }
-//         },
-//         "required": [ "targets" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// },
-// "CompletionItem": {
-//   "type": "object",
-//   "description": "CompletionItems are the suggestions returned from the
-//   CompletionsRequest.", "properties": {
-//     "label": {
-//       "type": "string",
-//       "description": "The label of this completion item. By default this is
-//       also the text that is inserted when selecting this completion."
-//     },
-//     "text": {
-//       "type": "string",
-//       "description": "If text is not falsy then it is inserted instead of the
-//       label."
-//     },
-//     "sortText": {
-//       "type": "string",
-//       "description": "A string that should be used when comparing this item
-//       with other items. When `falsy` the label is used."
-//     },
-//     "type": {
-//       "$ref": "#/definitions/CompletionItemType",
-//       "description": "The item's type. Typically the client uses this
-//       information to render the item in the UI with an icon."
-//     },
-//     "start": {
-//       "type": "integer",
-//       "description": "This value determines the location (in the
-//       CompletionsRequest's 'text' attribute) where the completion text is
-//       added.\nIf missing the text is added at the location specified by the
-//       CompletionsRequest's 'column' attribute."
-//     },
-//     "length": {
-//       "type": "integer",
-//       "description": "This value determines how many characters are
-//       overwritten by the completion text.\nIf missing the value 0 is assumed
-//       which results in the completion text being inserted."
-//     }
-//   },
-//   "required": [ "label" ]
-// },
-// "CompletionItemType": {
-//   "type": "string",
-//   "description": "Some predefined types for the CompletionItem. Please note
-//   that not all clients have specific icons for all of them.", "enum": [
-//   "method", "function", "constructor", "field", "variable", "class",
-//   "interface", "module", "property", "unit", "value", "enum", "keyword",
-//   "snippet", "text", "color", "file", "reference", "customcolor" ]
-// }
-void request_completions(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  llvm::json::Object body;
-  const auto *arguments = request.getObject("arguments");
-
-  // If we have a frame, try to set the context for variable completions.
-  lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
-  if (frame.IsValid()) {
-    frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread());
-    frame.GetThread().SetSelectedFrame(frame.GetFrameID());
-  }
-
-  std::string text = GetString(arguments, "text").str();
-  auto original_column = GetSigned(arguments, "column", text.size());
-  auto original_line = GetSigned(arguments, "line", 1);
-  auto offset = original_column - 1;
-  if (original_line > 1) {
-    llvm::SmallVector<::llvm::StringRef, 2> lines;
-    llvm::StringRef(text).split(lines, '\n');
-    for (int i = 0; i < original_line - 1; i++) {
-      offset += lines[i].size();
-    }
-  }
-  llvm::json::Array targets;
-
-  bool had_escape_prefix =
-      llvm::StringRef(text).starts_with(dap.command_escape_prefix);
-  ReplMode completion_mode = dap.DetectReplMode(frame, text, true);
-
-  // Handle the offset change introduced by stripping out the
-  // `command_escape_prefix`.
-  if (had_escape_prefix) {
-    if (offset < static_cast<int64_t>(dap.command_escape_prefix.size())) {
-      body.try_emplace("targets", std::move(targets));
-      response.try_emplace("body", std::move(body));
-      dap.SendJSON(llvm::json::Value(std::move(response)));
-      return;
-    }
-    offset -= dap.command_escape_prefix.size();
-  }
-
-  // While the user is typing then we likely have an incomplete input and cannot
-  // reliably determine the precise intent (command vs variable), try completing
-  // the text as both a command and variable expression, if applicable.
-  const std::string expr_prefix = "expression -- ";
-  std::array<std::tuple<ReplMode, std::string, uint64_t>, 2> exprs = {
-      {std::make_tuple(ReplMode::Command, text, offset),
-       std::make_tuple(ReplMode::Variable, expr_prefix + text,
-                       offset + expr_prefix.size())}};
-  for (const auto &[mode, line, cursor] : exprs) {
-    if (completion_mode != ReplMode::Auto && completion_mode != mode)
-      continue;
-
-    lldb::SBStringList matches;
-    lldb::SBStringList descriptions;
-    if (!dap.debugger.GetCommandInterpreter().HandleCompletionWithDescriptions(
-            line.c_str(), cursor, 0, 100, matches, descriptions))
-      continue;
-
-    // The first element is the common substring after the cursor position for
-    // all the matches. The rest of the elements are the matches so ignore the
-    // first result.
-    for (size_t i = 1; i < matches.GetSize(); i++) {
-      std::string match = matches.GetStringAtIndex(i);
-      std::string description = descriptions.GetStringAtIndex(i);
-
-      llvm::json::Object item;
-      llvm::StringRef match_ref = match;
-      for (llvm::StringRef commit_point : {".", "->"}) {
-        if (match_ref.contains(commit_point)) {
-          match_ref = match_ref.rsplit(commit_point).second;
-        }
-      }
-      EmplaceSafeString(item, "text", match_ref);
-
-      if (description.empty())
-        EmplaceSafeString(item, "label", match);
-      else
-        EmplaceSafeString(item, "label", match + " -- " + description);
-
-      targets.emplace_back(std::move(item));
-    }
-  }
-
-  body.try_emplace("targets", std::move(targets));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
 
 //  "EvaluateRequest": {
 //    "allOf": [ { "$ref": "#/definitions/Request" }, {
@@ -4625,8 +4420,8 @@ void request_setInstructionBreakpoints(DAP &dap,
 void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<AttachRequestHandler>();
   dap.RegisterRequest<BreakpointLocationsRequestHandler>();
+  dap.RegisterRequest<CompletionsRequestHandler>();
 
-  dap.RegisterRequestCallback("completions", request_completions);
   dap.RegisterRequestCallback("continue", request_continue);
   dap.RegisterRequestCallback("configurationDone", request_configurationDone);
   dap.RegisterRequestCallback("disconnect", request_disconnect);

>From bf4e686f7ec263ea757e3142a9d09d655f7f6d29 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Sat, 22 Feb 2025 19:30:19 -0600
Subject: [PATCH 06/11] Adopt ContinueRequestHandler

---
 lldb/tools/lldb-dap/CMakeLists.txt            |  1 +
 .../Handler/ContinueRequestHandler.cpp        | 79 +++++++++++++++++++
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |  7 ++
 lldb/tools/lldb-dap/lldb-dap.cpp              | 67 +---------------
 4 files changed, 88 insertions(+), 66 deletions(-)
 create mode 100644 lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index d41baae1ef5d9..56a234b990729 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -39,6 +39,7 @@ add_lldb_tool(lldb-dap
   Handler/AttachHandler.cpp
   Handler/BreakpointLocationsHandler.cpp
   Handler/CompletionsHandler.cpp
+  Handler/ContinueRequestHandler.cpp
   Handler/RequestHandler.cpp
 
   LINK_LIBS
diff --git a/lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp
new file mode 100644
index 0000000000000..743858f87901f
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp
@@ -0,0 +1,79 @@
+//===-- CompletionsHandler.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "ContinueRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Continue request; value of command field is 'continue'.
+//                     The request starts the debuggee to run again.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "continue" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/ContinueArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "ContinueArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'continue' request.",
+//   "properties": {
+//     "threadId": {
+//       "type": "integer",
+//       "description": "Continue execution for the specified thread (if
+//                       possible). If the backend cannot continue on a single
+//                       thread but will continue on all threads, it should
+//                       set the allThreadsContinued attribute in the response
+//                       to true."
+//     }
+//   },
+//   "required": [ "threadId" ]
+// },
+// "ContinueResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'continue' request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "allThreadsContinued": {
+//             "type": "boolean",
+//             "description": "If true, the continue request has ignored the
+//                             specified thread and continued all threads
+//                             instead. If this attribute is missing a value
+//                             of 'true' is assumed for backward
+//                             compatibility."
+//           }
+//         }
+//       }
+//     },
+//     "required": [ "body" ]
+//   }]
+// }
+void ContinueRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  lldb::SBProcess process = dap.target.GetProcess();
+  lldb::SBError error = process.Continue();
+  llvm::json::Object body;
+  body.try_emplace("allThreadsContinued", true);
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 76130ade76f32..5b0d9071a73ac 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -62,6 +62,13 @@ class CompletionsRequestHandler : public RequestHandler {
   void operator()(const llvm::json::Object &request) override;
 };
 
+class ContinueRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "continue"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
 } // namespace lldb_dap
 
 #endif
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index e20b1c5272336..cf03efacf5735 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -726,71 +726,6 @@ bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
   return reached_end_of_stack;
 }
 
-// "ContinueRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Continue request; value of command field is 'continue'.
-//                     The request starts the debuggee to run again.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "continue" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/ContinueArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "ContinueArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'continue' request.",
-//   "properties": {
-//     "threadId": {
-//       "type": "integer",
-//       "description": "Continue execution for the specified thread (if
-//                       possible). If the backend cannot continue on a single
-//                       thread but will continue on all threads, it should
-//                       set the allThreadsContinued attribute in the response
-//                       to true."
-//     }
-//   },
-//   "required": [ "threadId" ]
-// },
-// "ContinueResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'continue' request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "allThreadsContinued": {
-//             "type": "boolean",
-//             "description": "If true, the continue request has ignored the
-//                             specified thread and continued all threads
-//                             instead. If this attribute is missing a value
-//                             of 'true' is assumed for backward
-//                             compatibility."
-//           }
-//         }
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void request_continue(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  lldb::SBProcess process = dap.target.GetProcess();
-  lldb::SBError error = process.Continue();
-  llvm::json::Object body;
-  body.try_emplace("allThreadsContinued", true);
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
 // "ConfigurationDoneRequest": {
 //   "allOf": [ { "$ref": "#/definitions/Request" }, {
 //             "type": "object",
@@ -4421,8 +4356,8 @@ void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<AttachRequestHandler>();
   dap.RegisterRequest<BreakpointLocationsRequestHandler>();
   dap.RegisterRequest<CompletionsRequestHandler>();
+  dap.RegisterRequest<ContinueRequestHandler>();
 
-  dap.RegisterRequestCallback("continue", request_continue);
   dap.RegisterRequestCallback("configurationDone", request_configurationDone);
   dap.RegisterRequestCallback("disconnect", request_disconnect);
   dap.RegisterRequestCallback("evaluate", request_evaluate);

>From 25504a81a23b2a7c264546b3914560ea1bed3003 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Sat, 22 Feb 2025 19:44:22 -0600
Subject: [PATCH 07/11] Adopt ConfigurationDoneRequestHandler

---
 lldb/tools/lldb-dap/CMakeLists.txt            |   2 +
 lldb/tools/lldb-dap/EventHelper.cpp           | 184 +++++++++++++++
 lldb/tools/lldb-dap/EventHelper.h             |  23 ++
 lldb/tools/lldb-dap/Handler/AttachHandler.cpp |   3 +-
 .../ConfigurationDoneRequestHandler.cpp       |  59 +++++
 .../tools/lldb-dap/Handler/RequestHandler.cpp |  28 ---
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |  11 +-
 lldb/tools/lldb-dap/lldb-dap.cpp              | 215 +-----------------
 8 files changed, 280 insertions(+), 245 deletions(-)
 create mode 100644 lldb/tools/lldb-dap/EventHelper.cpp
 create mode 100644 lldb/tools/lldb-dap/EventHelper.h
 create mode 100644 lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 56a234b990729..eda34792b66cb 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -23,6 +23,7 @@ add_lldb_tool(lldb-dap
   Breakpoint.cpp
   BreakpointBase.cpp
   DAP.cpp
+  EventHelper.cpp
   ExceptionBreakpoint.cpp
   FifoFiles.cpp
   FunctionBreakpoint.cpp
@@ -39,6 +40,7 @@ add_lldb_tool(lldb-dap
   Handler/AttachHandler.cpp
   Handler/BreakpointLocationsHandler.cpp
   Handler/CompletionsHandler.cpp
+  Handler/ConfigurationDoneRequestHandler.cpp
   Handler/ContinueRequestHandler.cpp
   Handler/RequestHandler.cpp
 
diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp
new file mode 100644
index 0000000000000..d1ce8901a6a67
--- /dev/null
+++ b/lldb/tools/lldb-dap/EventHelper.cpp
@@ -0,0 +1,184 @@
+//===-- EventHelper.h -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "EventHelper.h"
+#include "DAP.h"
+#include "JSONUtils.h"
+#include "LLDBUtils.h"
+#include "lldb/API/SBFileSpec.h"
+
+namespace lldb_dap {
+
+static void SendThreadExitedEvent(DAP &dap, lldb::tid_t tid) {
+  llvm::json::Object event(CreateEventObject("thread"));
+  llvm::json::Object body;
+  body.try_emplace("reason", "exited");
+  body.try_emplace("threadId", (int64_t)tid);
+  event.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(event)));
+}
+
+// "ProcessEvent": {
+//   "allOf": [
+//     { "$ref": "#/definitions/Event" },
+//     {
+//       "type": "object",
+//       "description": "Event message for 'process' event type. The event
+//                       indicates that the debugger has begun debugging a
+//                       new process. Either one that it has launched, or one
+//                       that it has attached to.",
+//       "properties": {
+//         "event": {
+//           "type": "string",
+//           "enum": [ "process" ]
+//         },
+//         "body": {
+//           "type": "object",
+//           "properties": {
+//             "name": {
+//               "type": "string",
+//               "description": "The logical name of the process. This is
+//                               usually the full path to process's executable
+//                               file. Example: /home/myproj/program.js."
+//             },
+//             "systemProcessId": {
+//               "type": "integer",
+//               "description": "The system process id of the debugged process.
+//                               This property will be missing for non-system
+//                               processes."
+//             },
+//             "isLocalProcess": {
+//               "type": "boolean",
+//               "description": "If true, the process is running on the same
+//                               computer as the debug adapter."
+//             },
+//             "startMethod": {
+//               "type": "string",
+//               "enum": [ "launch", "attach", "attachForSuspendedLaunch" ],
+//               "description": "Describes how the debug engine started
+//                               debugging this process.",
+//               "enumDescriptions": [
+//                 "Process was launched under the debugger.",
+//                 "Debugger attached to an existing process.",
+//                 "A project launcher component has launched a new process in
+//                  a suspended state and then asked the debugger to attach."
+//               ]
+//             }
+//           },
+//           "required": [ "name" ]
+//         }
+//       },
+//       "required": [ "event", "body" ]
+//     }
+//   ]
+// }
+void SendProcessEvent(DAP &dap, LaunchMethod launch_method) {
+  lldb::SBFileSpec exe_fspec = dap.target.GetExecutable();
+  char exe_path[PATH_MAX];
+  exe_fspec.GetPath(exe_path, sizeof(exe_path));
+  llvm::json::Object event(CreateEventObject("process"));
+  llvm::json::Object body;
+  EmplaceSafeString(body, "name", std::string(exe_path));
+  const auto pid = dap.target.GetProcess().GetProcessID();
+  body.try_emplace("systemProcessId", (int64_t)pid);
+  body.try_emplace("isLocalProcess", true);
+  const char *startMethod = nullptr;
+  switch (launch_method) {
+  case Launch:
+    startMethod = "launch";
+    break;
+  case Attach:
+    startMethod = "attach";
+    break;
+  case AttachForSuspendedLaunch:
+    startMethod = "attachForSuspendedLaunch";
+    break;
+  }
+  body.try_emplace("startMethod", startMethod);
+  event.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(event)));
+}
+
+// Send a thread stopped event for all threads as long as the process
+// is stopped.
+void SendThreadStoppedEvent(DAP &dap) {
+  lldb::SBProcess process = dap.target.GetProcess();
+  if (process.IsValid()) {
+    auto state = process.GetState();
+    if (state == lldb::eStateStopped) {
+      llvm::DenseSet<lldb::tid_t> old_thread_ids;
+      old_thread_ids.swap(dap.thread_ids);
+      uint32_t stop_id = process.GetStopID();
+      const uint32_t num_threads = process.GetNumThreads();
+
+      // First make a pass through the threads to see if the focused thread
+      // has a stop reason. In case the focus thread doesn't have a stop
+      // reason, remember the first thread that has a stop reason so we can
+      // set it as the focus thread if below if needed.
+      lldb::tid_t first_tid_with_reason = LLDB_INVALID_THREAD_ID;
+      uint32_t num_threads_with_reason = 0;
+      bool focus_thread_exists = false;
+      for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
+        lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
+        const lldb::tid_t tid = thread.GetThreadID();
+        const bool has_reason = ThreadHasStopReason(thread);
+        // If the focus thread doesn't have a stop reason, clear the thread ID
+        if (tid == dap.focus_tid) {
+          focus_thread_exists = true;
+          if (!has_reason)
+            dap.focus_tid = LLDB_INVALID_THREAD_ID;
+        }
+        if (has_reason) {
+          ++num_threads_with_reason;
+          if (first_tid_with_reason == LLDB_INVALID_THREAD_ID)
+            first_tid_with_reason = tid;
+        }
+      }
+
+      // We will have cleared dap.focus_tid if the focus thread doesn't have
+      // a stop reason, so if it was cleared, or wasn't set, or doesn't exist,
+      // then set the focus thread to the first thread with a stop reason.
+      if (!focus_thread_exists || dap.focus_tid == LLDB_INVALID_THREAD_ID)
+        dap.focus_tid = first_tid_with_reason;
+
+      // If no threads stopped with a reason, then report the first one so
+      // we at least let the UI know we stopped.
+      if (num_threads_with_reason == 0) {
+        lldb::SBThread thread = process.GetThreadAtIndex(0);
+        dap.focus_tid = thread.GetThreadID();
+        dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));
+      } else {
+        for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
+          lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
+          dap.thread_ids.insert(thread.GetThreadID());
+          if (ThreadHasStopReason(thread)) {
+            dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));
+          }
+        }
+      }
+
+      for (auto tid : old_thread_ids) {
+        auto end = dap.thread_ids.end();
+        auto pos = dap.thread_ids.find(tid);
+        if (pos == end)
+          SendThreadExitedEvent(dap, tid);
+      }
+    } else {
+      if (dap.log)
+        *dap.log << "error: SendThreadStoppedEvent() when process"
+                    " isn't stopped ("
+                 << lldb::SBDebugger::StateAsCString(state) << ')' << std::endl;
+    }
+  } else {
+    if (dap.log)
+      *dap.log << "error: SendThreadStoppedEvent() invalid process"
+               << std::endl;
+  }
+  dap.RunStopCommands();
+}
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/EventHelper.h b/lldb/tools/lldb-dap/EventHelper.h
new file mode 100644
index 0000000000000..dee36b4ad8554
--- /dev/null
+++ b/lldb/tools/lldb-dap/EventHelper.h
@@ -0,0 +1,23 @@
+//===-- EventHelper.h -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TOOLS_LLDB_DAP_EVENTHELPER_H
+#define LLDB_TOOLS_LLDB_DAP_EVENTHELPER_H
+
+namespace lldb_dap {
+struct DAP;
+
+enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
+
+void SendProcessEvent(DAP &dap, LaunchMethod launch_method);
+
+void SendThreadStoppedEvent(DAP &dap);
+
+} // namespace lldb_dap
+
+#endif
diff --git a/lldb/tools/lldb-dap/Handler/AttachHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachHandler.cpp
index 3dcde2144246e..872146cc45d69 100644
--- a/lldb/tools/lldb-dap/Handler/AttachHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/AttachHandler.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "DAP.h"
+#include "EventHelper.h"
 #include "JSONUtils.h"
 #include "RequestHandler.h"
 #include "lldb/API/SBListener.h"
@@ -203,7 +204,7 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) {
 
   dap.SendJSON(llvm::json::Value(std::move(response)));
   if (error.Success()) {
-    SendProcessEvent(Attach);
+    SendProcessEvent(dap, Attach);
     dap.SendJSON(CreateEventObject("initialized"));
   }
 }
diff --git a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp
new file mode 100644
index 0000000000000..429bcd21eab30
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp
@@ -0,0 +1,59 @@
+//===-- ConfigurationDoneRequestHandler..cpp ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "ConfigurationDoneRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//             "type": "object",
+//             "description": "ConfigurationDone request; value of command field
+//             is 'configurationDone'.\nThe client of the debug protocol must
+//             send this request at the end of the sequence of configuration
+//             requests (which was started by the InitializedEvent).",
+//             "properties": {
+//             "command": {
+//             "type": "string",
+//             "enum": [ "configurationDone" ]
+//             },
+//             "arguments": {
+//             "$ref": "#/definitions/ConfigurationDoneArguments"
+//             }
+//             },
+//             "required": [ "command" ]
+//             }]
+// },
+// "ConfigurationDoneArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'configurationDone' request.\nThe
+//   configurationDone request has no standardized attributes."
+// },
+// "ConfigurationDoneResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//             "type": "object",
+//             "description": "Response to 'configurationDone' request. This is
+//             just an acknowledgement, so no body field is required."
+//             }]
+// },
+void ConfigurationDoneRequestHandler::operator()(
+    const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+  dap.configuration_done_sent = true;
+  if (dap.stop_at_entry)
+    SendThreadStoppedEvent(dap);
+  else
+    dap.target.GetProcess().Continue();
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index ef091a805902d..bf2b82f518713 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -13,34 +13,6 @@
 
 namespace lldb_dap {
 
-void RequestHandler::SendProcessEvent(
-    RequestHandler::LaunchMethod launch_method) {
-  lldb::SBFileSpec exe_fspec = dap.target.GetExecutable();
-  char exe_path[PATH_MAX];
-  exe_fspec.GetPath(exe_path, sizeof(exe_path));
-  llvm::json::Object event(CreateEventObject("process"));
-  llvm::json::Object body;
-  EmplaceSafeString(body, "name", std::string(exe_path));
-  const auto pid = dap.target.GetProcess().GetProcessID();
-  body.try_emplace("systemProcessId", (int64_t)pid);
-  body.try_emplace("isLocalProcess", true);
-  const char *startMethod = nullptr;
-  switch (launch_method) {
-  case Launch:
-    startMethod = "launch";
-    break;
-  case Attach:
-    startMethod = "attach";
-    break;
-  case AttachForSuspendedLaunch:
-    startMethod = "attachForSuspendedLaunch";
-    break;
-  }
-  body.try_emplace("startMethod", startMethod);
-  event.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(event)));
-}
-
 // Both attach and launch take a either a sourcePath or sourceMap
 // argument (or neither), from which we need to set the target.source-map.
 void RequestHandler::SetSourceMapFromArguments(
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 5b0d9071a73ac..51bd834011b24 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -30,10 +30,8 @@ class RequestHandler {
   virtual void operator()(const llvm::json::Object &request) = 0;
 
   /// Helpers used by multiple request handlers.
-  /// FIXME: Move these into the DAP class.
+  /// FIXME: Move these into the DAP class?
   /// @{
-  enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
-  void SendProcessEvent(LaunchMethod launch_method);
   void SetSourceMapFromArguments(const llvm::json::Object &arguments);
   /// @}
 
@@ -69,6 +67,13 @@ class ContinueRequestHandler : public RequestHandler {
   void operator()(const llvm::json::Object &request) override;
 };
 
+class ConfigurationDoneRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "configurationDone"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
 } // namespace lldb_dap
 
 #endif
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index cf03efacf5735..9b214d98adb99 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "DAP.h"
+#include "EventHelper.h"
 #include "FifoFiles.h"
 #include "Handler/RequestHandler.h"
 #include "JSONUtils.h"
@@ -116,8 +117,6 @@ class LLDBDAPOptTable : public llvm::opt::GenericOptTable {
 
 typedef void (*RequestCallback)(const llvm::json::Object &command);
 
-enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
-
 /// Page size used for reporting addtional frames in the 'stackTrace' request.
 constexpr int StackPageSize = 20;
 
@@ -200,15 +199,6 @@ void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process) {
   dap.SendJSON(llvm::json::Value(std::move(event)));
 }
 
-void SendThreadExitedEvent(DAP &dap, lldb::tid_t tid) {
-  llvm::json::Object event(CreateEventObject("thread"));
-  llvm::json::Object body;
-  body.try_emplace("reason", "exited");
-  body.try_emplace("threadId", (int64_t)tid);
-  event.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(event)));
-}
-
 // Send a "continued" event to indicate the process is in the running state.
 void SendContinuedEvent(DAP &dap) {
   lldb::SBProcess process = dap.target.GetProcess();
@@ -242,165 +232,6 @@ void SendTerminatedEvent(DAP &dap) {
   });
 }
 
-// Send a thread stopped event for all threads as long as the process
-// is stopped.
-void SendThreadStoppedEvent(DAP &dap) {
-  lldb::SBProcess process = dap.target.GetProcess();
-  if (process.IsValid()) {
-    auto state = process.GetState();
-    if (state == lldb::eStateStopped) {
-      llvm::DenseSet<lldb::tid_t> old_thread_ids;
-      old_thread_ids.swap(dap.thread_ids);
-      uint32_t stop_id = process.GetStopID();
-      const uint32_t num_threads = process.GetNumThreads();
-
-      // First make a pass through the threads to see if the focused thread
-      // has a stop reason. In case the focus thread doesn't have a stop
-      // reason, remember the first thread that has a stop reason so we can
-      // set it as the focus thread if below if needed.
-      lldb::tid_t first_tid_with_reason = LLDB_INVALID_THREAD_ID;
-      uint32_t num_threads_with_reason = 0;
-      bool focus_thread_exists = false;
-      for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
-        lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
-        const lldb::tid_t tid = thread.GetThreadID();
-        const bool has_reason = ThreadHasStopReason(thread);
-        // If the focus thread doesn't have a stop reason, clear the thread ID
-        if (tid == dap.focus_tid) {
-          focus_thread_exists = true;
-          if (!has_reason)
-            dap.focus_tid = LLDB_INVALID_THREAD_ID;
-        }
-        if (has_reason) {
-          ++num_threads_with_reason;
-          if (first_tid_with_reason == LLDB_INVALID_THREAD_ID)
-            first_tid_with_reason = tid;
-        }
-      }
-
-      // We will have cleared dap.focus_tid if the focus thread doesn't have
-      // a stop reason, so if it was cleared, or wasn't set, or doesn't exist,
-      // then set the focus thread to the first thread with a stop reason.
-      if (!focus_thread_exists || dap.focus_tid == LLDB_INVALID_THREAD_ID)
-        dap.focus_tid = first_tid_with_reason;
-
-      // If no threads stopped with a reason, then report the first one so
-      // we at least let the UI know we stopped.
-      if (num_threads_with_reason == 0) {
-        lldb::SBThread thread = process.GetThreadAtIndex(0);
-        dap.focus_tid = thread.GetThreadID();
-        dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));
-      } else {
-        for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
-          lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
-          dap.thread_ids.insert(thread.GetThreadID());
-          if (ThreadHasStopReason(thread)) {
-            dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));
-          }
-        }
-      }
-
-      for (auto tid : old_thread_ids) {
-        auto end = dap.thread_ids.end();
-        auto pos = dap.thread_ids.find(tid);
-        if (pos == end)
-          SendThreadExitedEvent(dap, tid);
-      }
-    } else {
-      if (dap.log)
-        *dap.log << "error: SendThreadStoppedEvent() when process"
-                    " isn't stopped ("
-                 << lldb::SBDebugger::StateAsCString(state) << ')' << std::endl;
-    }
-  } else {
-    if (dap.log)
-      *dap.log << "error: SendThreadStoppedEvent() invalid process"
-               << std::endl;
-  }
-  dap.RunStopCommands();
-}
-
-// "ProcessEvent": {
-//   "allOf": [
-//     { "$ref": "#/definitions/Event" },
-//     {
-//       "type": "object",
-//       "description": "Event message for 'process' event type. The event
-//                       indicates that the debugger has begun debugging a
-//                       new process. Either one that it has launched, or one
-//                       that it has attached to.",
-//       "properties": {
-//         "event": {
-//           "type": "string",
-//           "enum": [ "process" ]
-//         },
-//         "body": {
-//           "type": "object",
-//           "properties": {
-//             "name": {
-//               "type": "string",
-//               "description": "The logical name of the process. This is
-//                               usually the full path to process's executable
-//                               file. Example: /home/myproj/program.js."
-//             },
-//             "systemProcessId": {
-//               "type": "integer",
-//               "description": "The system process id of the debugged process.
-//                               This property will be missing for non-system
-//                               processes."
-//             },
-//             "isLocalProcess": {
-//               "type": "boolean",
-//               "description": "If true, the process is running on the same
-//                               computer as the debug adapter."
-//             },
-//             "startMethod": {
-//               "type": "string",
-//               "enum": [ "launch", "attach", "attachForSuspendedLaunch" ],
-//               "description": "Describes how the debug engine started
-//                               debugging this process.",
-//               "enumDescriptions": [
-//                 "Process was launched under the debugger.",
-//                 "Debugger attached to an existing process.",
-//                 "A project launcher component has launched a new process in
-//                  a suspended state and then asked the debugger to attach."
-//               ]
-//             }
-//           },
-//           "required": [ "name" ]
-//         }
-//       },
-//       "required": [ "event", "body" ]
-//     }
-//   ]
-// }
-void SendProcessEvent(DAP &dap, LaunchMethod launch_method) {
-  lldb::SBFileSpec exe_fspec = dap.target.GetExecutable();
-  char exe_path[PATH_MAX];
-  exe_fspec.GetPath(exe_path, sizeof(exe_path));
-  llvm::json::Object event(CreateEventObject("process"));
-  llvm::json::Object body;
-  EmplaceSafeString(body, "name", std::string(exe_path));
-  const auto pid = dap.target.GetProcess().GetProcessID();
-  body.try_emplace("systemProcessId", (int64_t)pid);
-  body.try_emplace("isLocalProcess", true);
-  const char *startMethod = nullptr;
-  switch (launch_method) {
-  case Launch:
-    startMethod = "launch";
-    break;
-  case Attach:
-    startMethod = "attach";
-    break;
-  case AttachForSuspendedLaunch:
-    startMethod = "attachForSuspendedLaunch";
-    break;
-  }
-  body.try_emplace("startMethod", startMethod);
-  event.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(event)));
-}
-
 // Grab any STDOUT and STDERR from the process and send it up to VS Code
 // via an "output" event to the "stdout" and "stderr" categories.
 void SendStdOutStdErr(DAP &dap, lldb::SBProcess &process) {
@@ -726,48 +557,6 @@ bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
   return reached_end_of_stack;
 }
 
-// "ConfigurationDoneRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//             "type": "object",
-//             "description": "ConfigurationDone request; value of command field
-//             is 'configurationDone'.\nThe client of the debug protocol must
-//             send this request at the end of the sequence of configuration
-//             requests (which was started by the InitializedEvent).",
-//             "properties": {
-//             "command": {
-//             "type": "string",
-//             "enum": [ "configurationDone" ]
-//             },
-//             "arguments": {
-//             "$ref": "#/definitions/ConfigurationDoneArguments"
-//             }
-//             },
-//             "required": [ "command" ]
-//             }]
-// },
-// "ConfigurationDoneArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'configurationDone' request.\nThe
-//   configurationDone request has no standardized attributes."
-// },
-// "ConfigurationDoneResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//             "type": "object",
-//             "description": "Response to 'configurationDone' request. This is
-//             just an acknowledgement, so no body field is required."
-//             }]
-// },
-void request_configurationDone(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-  dap.configuration_done_sent = true;
-  if (dap.stop_at_entry)
-    SendThreadStoppedEvent(dap);
-  else
-    dap.target.GetProcess().Continue();
-}
-
 // "DisconnectRequest": {
 //   "allOf": [ { "$ref": "#/definitions/Request" }, {
 //     "type": "object",
@@ -4357,8 +4146,8 @@ void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<BreakpointLocationsRequestHandler>();
   dap.RegisterRequest<CompletionsRequestHandler>();
   dap.RegisterRequest<ContinueRequestHandler>();
+  dap.RegisterRequest<ConfigurationDoneRequestHandler>();
 
-  dap.RegisterRequestCallback("configurationDone", request_configurationDone);
   dap.RegisterRequestCallback("disconnect", request_disconnect);
   dap.RegisterRequestCallback("evaluate", request_evaluate);
   dap.RegisterRequestCallback("exceptionInfo", request_exceptionInfo);

>From 670701d3171389d79533c37b326965a060c10e00 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Sat, 22 Feb 2025 19:49:28 -0600
Subject: [PATCH 08/11] Adopt DisconnectRequestHandler

---
 lldb/tools/lldb-dap/CMakeLists.txt            |   1 +
 lldb/tools/lldb-dap/EventHelper.cpp           |  13 +++
 lldb/tools/lldb-dap/EventHelper.h             |   2 +
 .../Handler/DisconnectRequestHandler.cpp      | 104 ++++++++++++++++++
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |   7 ++
 lldb/tools/lldb-dap/lldb-dap.cpp              | 103 +----------------
 6 files changed, 128 insertions(+), 102 deletions(-)
 create mode 100644 lldb/tools/lldb-dap/Handler/DisconnectRequestHandler.cpp

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index eda34792b66cb..bd3aae6a03db3 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -42,6 +42,7 @@ add_lldb_tool(lldb-dap
   Handler/CompletionsHandler.cpp
   Handler/ConfigurationDoneRequestHandler.cpp
   Handler/ContinueRequestHandler.cpp
+  Handler/DisconnectRequestHandler.cpp
   Handler/RequestHandler.cpp
 
   LINK_LIBS
diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp
index d1ce8901a6a67..f59c73a6593c1 100644
--- a/lldb/tools/lldb-dap/EventHelper.cpp
+++ b/lldb/tools/lldb-dap/EventHelper.cpp
@@ -181,4 +181,17 @@ void SendThreadStoppedEvent(DAP &dap) {
   }
   dap.RunStopCommands();
 }
+
+// Send a "terminated" event to indicate the process is done being
+// debugged.
+void SendTerminatedEvent(DAP &dap) {
+  // Prevent races if the process exits while we're being asked to disconnect.
+  llvm::call_once(dap.terminated_event_flag, [&] {
+    dap.RunTerminateCommands();
+    // Send a "terminated" event
+    llvm::json::Object event(CreateTerminatedEventObject(dap.target));
+    dap.SendJSON(llvm::json::Value(std::move(event)));
+  });
+}
+
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/EventHelper.h b/lldb/tools/lldb-dap/EventHelper.h
index dee36b4ad8554..0c5978e1db49b 100644
--- a/lldb/tools/lldb-dap/EventHelper.h
+++ b/lldb/tools/lldb-dap/EventHelper.h
@@ -18,6 +18,8 @@ void SendProcessEvent(DAP &dap, LaunchMethod launch_method);
 
 void SendThreadStoppedEvent(DAP &dap);
 
+void SendTerminatedEvent(DAP &dap);
+
 } // namespace lldb_dap
 
 #endif
diff --git a/lldb/tools/lldb-dap/Handler/DisconnectRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DisconnectRequestHandler.cpp
new file mode 100644
index 0000000000000..ade337dac0390
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/DisconnectRequestHandler.cpp
@@ -0,0 +1,104 @@
+//===-- DisconnectRequestHandler.cpp --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+// "DisconnectRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Disconnect request; value of command field is
+//                     'disconnect'.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "disconnect" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/DisconnectArguments"
+//       }
+//     },
+//     "required": [ "command" ]
+//   }]
+// },
+// "DisconnectArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'disconnect' request.",
+//   "properties": {
+//     "terminateDebuggee": {
+//       "type": "boolean",
+//       "description": "Indicates whether the debuggee should be terminated
+//                       when the debugger is disconnected. If unspecified,
+//                       the debug adapter is free to do whatever it thinks
+//                       is best. A client can only rely on this attribute
+//                       being properly honored if a debug adapter returns
+//                       true for the 'supportTerminateDebuggee' capability."
+//     },
+//     "restart": {
+//       "type": "boolean",
+//       "description": "Indicates whether the debuggee should be restart
+//                       the process."
+//     }
+//   }
+// },
+// "DisconnectResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'disconnect' request. This is just an
+//                     acknowledgement, so no body field is required."
+//   }]
+// }
+void DisconnectRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  const auto *arguments = request.getObject("arguments");
+
+  bool defaultTerminateDebuggee = dap.is_attach ? false : true;
+  bool terminateDebuggee =
+      GetBoolean(arguments, "terminateDebuggee", defaultTerminateDebuggee);
+  lldb::SBProcess process = dap.target.GetProcess();
+  auto state = process.GetState();
+  switch (state) {
+  case lldb::eStateInvalid:
+  case lldb::eStateUnloaded:
+  case lldb::eStateDetached:
+  case lldb::eStateExited:
+    break;
+  case lldb::eStateConnected:
+  case lldb::eStateAttaching:
+  case lldb::eStateLaunching:
+  case lldb::eStateStepping:
+  case lldb::eStateCrashed:
+  case lldb::eStateSuspended:
+  case lldb::eStateStopped:
+  case lldb::eStateRunning:
+    dap.debugger.SetAsync(false);
+    lldb::SBError error = terminateDebuggee ? process.Kill() : process.Detach();
+    if (!error.Success())
+      EmplaceSafeString(response, "error", error.GetCString());
+    dap.debugger.SetAsync(true);
+    break;
+  }
+  SendTerminatedEvent(dap);
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+  if (dap.event_thread.joinable()) {
+    dap.broadcaster.BroadcastEventByType(eBroadcastBitStopEventThread);
+    dap.event_thread.join();
+  }
+  if (dap.progress_event_thread.joinable()) {
+    dap.broadcaster.BroadcastEventByType(eBroadcastBitStopProgressThread);
+    dap.progress_event_thread.join();
+  }
+  dap.StopIO();
+  dap.disconnecting = true;
+}
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 51bd834011b24..e4e5c0ec5af41 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -74,6 +74,13 @@ class ConfigurationDoneRequestHandler : public RequestHandler {
   void operator()(const llvm::json::Object &request) override;
 };
 
+class DisconnectRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "disconnect"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
 } // namespace lldb_dap
 
 #endif
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 9b214d98adb99..efec6b0786691 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -220,18 +220,6 @@ void SendContinuedEvent(DAP &dap) {
   dap.SendJSON(llvm::json::Value(std::move(event)));
 }
 
-// Send a "terminated" event to indicate the process is done being
-// debugged.
-void SendTerminatedEvent(DAP &dap) {
-  // Prevent races if the process exits while we're being asked to disconnect.
-  llvm::call_once(dap.terminated_event_flag, [&] {
-    dap.RunTerminateCommands();
-    // Send a "terminated" event
-    llvm::json::Object event(CreateTerminatedEventObject(dap.target));
-    dap.SendJSON(llvm::json::Value(std::move(event)));
-  });
-}
-
 // Grab any STDOUT and STDERR from the process and send it up to VS Code
 // via an "output" event to the "stdout" and "stderr" categories.
 void SendStdOutStdErr(DAP &dap, lldb::SBProcess &process) {
@@ -557,95 +545,6 @@ bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
   return reached_end_of_stack;
 }
 
-// "DisconnectRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Disconnect request; value of command field is
-//                     'disconnect'.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "disconnect" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/DisconnectArguments"
-//       }
-//     },
-//     "required": [ "command" ]
-//   }]
-// },
-// "DisconnectArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'disconnect' request.",
-//   "properties": {
-//     "terminateDebuggee": {
-//       "type": "boolean",
-//       "description": "Indicates whether the debuggee should be terminated
-//                       when the debugger is disconnected. If unspecified,
-//                       the debug adapter is free to do whatever it thinks
-//                       is best. A client can only rely on this attribute
-//                       being properly honored if a debug adapter returns
-//                       true for the 'supportTerminateDebuggee' capability."
-//     },
-//     "restart": {
-//       "type": "boolean",
-//       "description": "Indicates whether the debuggee should be restart
-//                       the process."
-//     }
-//   }
-// },
-// "DisconnectResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'disconnect' request. This is just an
-//                     acknowledgement, so no body field is required."
-//   }]
-// }
-void request_disconnect(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-
-  bool defaultTerminateDebuggee = dap.is_attach ? false : true;
-  bool terminateDebuggee =
-      GetBoolean(arguments, "terminateDebuggee", defaultTerminateDebuggee);
-  lldb::SBProcess process = dap.target.GetProcess();
-  auto state = process.GetState();
-  switch (state) {
-  case lldb::eStateInvalid:
-  case lldb::eStateUnloaded:
-  case lldb::eStateDetached:
-  case lldb::eStateExited:
-    break;
-  case lldb::eStateConnected:
-  case lldb::eStateAttaching:
-  case lldb::eStateLaunching:
-  case lldb::eStateStepping:
-  case lldb::eStateCrashed:
-  case lldb::eStateSuspended:
-  case lldb::eStateStopped:
-  case lldb::eStateRunning:
-    dap.debugger.SetAsync(false);
-    lldb::SBError error = terminateDebuggee ? process.Kill() : process.Detach();
-    if (!error.Success())
-      EmplaceSafeString(response, "error", error.GetCString());
-    dap.debugger.SetAsync(true);
-    break;
-  }
-  SendTerminatedEvent(dap);
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-  if (dap.event_thread.joinable()) {
-    dap.broadcaster.BroadcastEventByType(eBroadcastBitStopEventThread);
-    dap.event_thread.join();
-  }
-  if (dap.progress_event_thread.joinable()) {
-    dap.broadcaster.BroadcastEventByType(eBroadcastBitStopProgressThread);
-    dap.progress_event_thread.join();
-  }
-  dap.StopIO();
-  dap.disconnecting = true;
-}
-
 // "ExceptionInfoRequest": {
 //   "allOf": [ { "$ref": "#/definitions/Request" }, {
 //     "type": "object",
@@ -4147,8 +4046,8 @@ void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<CompletionsRequestHandler>();
   dap.RegisterRequest<ContinueRequestHandler>();
   dap.RegisterRequest<ConfigurationDoneRequestHandler>();
+  dap.RegisterRequest<DisconnectRequestHandler>();
 
-  dap.RegisterRequestCallback("disconnect", request_disconnect);
   dap.RegisterRequestCallback("evaluate", request_evaluate);
   dap.RegisterRequestCallback("exceptionInfo", request_exceptionInfo);
   dap.RegisterRequestCallback("initialize", request_initialize);

>From dfb3b0c8903fda5c02f7c808cb171d0d95d5fa0e Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Sat, 22 Feb 2025 19:52:51 -0600
Subject: [PATCH 09/11] Adopt EvaluateRequestHandler

---
 lldb/tools/lldb-dap/CMakeLists.txt            |   1 +
 .../Handler/EvaluateRequestHandler.cpp        | 227 ++++++++++++++++++
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |   7 +
 lldb/tools/lldb-dap/lldb-dap.cpp              | 212 +---------------
 4 files changed, 236 insertions(+), 211 deletions(-)
 create mode 100644 lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index bd3aae6a03db3..8942051c9b234 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -43,6 +43,7 @@ add_lldb_tool(lldb-dap
   Handler/ConfigurationDoneRequestHandler.cpp
   Handler/ContinueRequestHandler.cpp
   Handler/DisconnectRequestHandler.cpp
+  Handler/EvaluateRequestHandler.cpp
   Handler/RequestHandler.cpp
 
   LINK_LIBS
diff --git a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
new file mode 100644
index 0000000000000..ed4a13a66c488
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
@@ -0,0 +1,227 @@
+//===-- EvaluateRequestHandler.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "LLDBUtils.h"
+#include "RequestHandler.h"
+
+namespace lldb_dap {
+
+//  "EvaluateRequest": {
+//    "allOf": [ { "$ref": "#/definitions/Request" }, {
+//      "type": "object",
+//      "description": "Evaluate request; value of command field is 'evaluate'.
+//                      Evaluates the given expression in the context of the
+//                      top most stack frame. The expression has access to any
+//                      variables and arguments that are in scope.",
+//      "properties": {
+//        "command": {
+//          "type": "string",
+//          "enum": [ "evaluate" ]
+//        },
+//        "arguments": {
+//          "$ref": "#/definitions/EvaluateArguments"
+//        }
+//      },
+//      "required": [ "command", "arguments"  ]
+//    }]
+//  },
+//  "EvaluateArguments": {
+//    "type": "object",
+//    "description": "Arguments for 'evaluate' request.",
+//    "properties": {
+//      "expression": {
+//        "type": "string",
+//        "description": "The expression to evaluate."
+//      },
+//      "frameId": {
+//        "type": "integer",
+//        "description": "Evaluate the expression in the scope of this stack
+//                        frame. If not specified, the expression is evaluated
+//                        in the global scope."
+//      },
+//      "context": {
+//        "type": "string",
+//        "_enum": [ "watch", "repl", "hover" ],
+//        "enumDescriptions": [
+//          "evaluate is run in a watch.",
+//          "evaluate is run from REPL console.",
+//          "evaluate is run from a data hover."
+//        ],
+//        "description": "The context in which the evaluate request is run."
+//      },
+//      "format": {
+//        "$ref": "#/definitions/ValueFormat",
+//        "description": "Specifies details on how to format the Evaluate
+//                        result."
+//      }
+//    },
+//    "required": [ "expression" ]
+//  },
+//  "EvaluateResponse": {
+//    "allOf": [ { "$ref": "#/definitions/Response" }, {
+//      "type": "object",
+//      "description": "Response to 'evaluate' request.",
+//      "properties": {
+//        "body": {
+//          "type": "object",
+//          "properties": {
+//            "result": {
+//              "type": "string",
+//              "description": "The result of the evaluate request."
+//            },
+//            "type": {
+//              "type": "string",
+//              "description": "The optional type of the evaluate result."
+//            },
+//            "presentationHint": {
+//              "$ref": "#/definitions/VariablePresentationHint",
+//              "description": "Properties of a evaluate result that can be
+//                              used to determine how to render the result in
+//                              the UI."
+//            },
+//            "variablesReference": {
+//              "type": "number",
+//              "description": "If variablesReference is > 0, the evaluate
+//                              result is structured and its children can be
+//                              retrieved by passing variablesReference to the
+//                              VariablesRequest."
+//            },
+//            "namedVariables": {
+//              "type": "number",
+//              "description": "The number of named child variables. The
+//                              client can use this optional information to
+//                              present the variables in a paged UI and fetch
+//                              them in chunks."
+//            },
+//            "indexedVariables": {
+//              "type": "number",
+//              "description": "The number of indexed child variables. The
+//                              client can use this optional information to
+//                              present the variables in a paged UI and fetch
+//                              them in chunks."
+//            },
+//            "valueLocationReference": {
+//              "type": "integer",
+//              "description": "A reference that allows the client to request
+//                              the location where the returned value is
+//                              declared. For example, if a function pointer is
+//                              returned, the adapter may be able to look up the
+//                              function's location. This should be present only
+//                              if the adapter is likely to be able to resolve
+//                              the location.\n\nThis reference shares the same
+//                              lifetime as the `variablesReference`. See
+//                              'Lifetime of Object References' in the
+//              Overview section for details."
+//            }
+//            "memoryReference": {
+//               "type": "string",
+//                "description": "A memory reference to a location appropriate
+//                                for this result. For pointer type eval
+//                                results, this is generally a reference to the
+//                                memory address contained in the pointer. This
+//                                attribute may be returned by a debug adapter
+//                                if corresponding capability
+//                                `supportsMemoryReferences` is true."
+//             },
+//          },
+//          "required": [ "result", "variablesReference" ]
+//        }
+//      },
+//      "required": [ "body" ]
+//    }]
+//  }
+void EvaluateRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  llvm::json::Object body;
+  const auto *arguments = request.getObject("arguments");
+  lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
+  std::string expression = GetString(arguments, "expression").str();
+  llvm::StringRef context = GetString(arguments, "context");
+  bool repeat_last_command =
+      expression.empty() && dap.last_nonempty_var_expression.empty();
+
+  if (context == "repl" &&
+      (repeat_last_command ||
+       (!expression.empty() &&
+        dap.DetectReplMode(frame, expression, false) == ReplMode::Command))) {
+    // Since the current expression is not for a variable, clear the
+    // last_nonempty_var_expression field.
+    dap.last_nonempty_var_expression.clear();
+    // If we're evaluating a command relative to the current frame, set the
+    // focus_tid to the current frame for any thread related events.
+    if (frame.IsValid()) {
+      dap.focus_tid = frame.GetThread().GetThreadID();
+    }
+    auto result = RunLLDBCommandsVerbatim(dap.debugger, llvm::StringRef(),
+                                          {std::string(expression)});
+    EmplaceSafeString(body, "result", result);
+    body.try_emplace("variablesReference", (int64_t)0);
+  } else {
+    if (context == "repl") {
+      // If the expression is empty and the last expression was for a
+      // variable, set the expression to the previous expression (repeat the
+      // evaluation); otherwise save the current non-empty expression for the
+      // next (possibly empty) variable expression.
+      if (expression.empty())
+        expression = dap.last_nonempty_var_expression;
+      else
+        dap.last_nonempty_var_expression = expression;
+    }
+    // Always try to get the answer from the local variables if possible. If
+    // this fails, then if the context is not "hover", actually evaluate an
+    // expression using the expression parser.
+    //
+    // "frame variable" is more reliable than the expression parser in
+    // many cases and it is faster.
+    lldb::SBValue value = frame.GetValueForVariablePath(
+        expression.data(), lldb::eDynamicDontRunTarget);
+
+    // Freeze dry the value in case users expand it later in the debug console
+    if (value.GetError().Success() && context == "repl")
+      value = value.Persist();
+
+    if (value.GetError().Fail() && context != "hover")
+      value = frame.EvaluateExpression(expression.data());
+
+    if (value.GetError().Fail()) {
+      response["success"] = llvm::json::Value(false);
+      // This error object must live until we're done with the pointer returned
+      // by GetCString().
+      lldb::SBError error = value.GetError();
+      const char *error_cstr = error.GetCString();
+      if (error_cstr && error_cstr[0])
+        EmplaceSafeString(response, "message", std::string(error_cstr));
+      else
+        EmplaceSafeString(response, "message", "evaluate failed");
+    } else {
+      VariableDescription desc(value, dap.enable_auto_variable_summaries);
+      EmplaceSafeString(body, "result", desc.GetResult(context));
+      EmplaceSafeString(body, "type", desc.display_type_name);
+      int64_t var_ref = 0;
+      if (value.MightHaveChildren() || ValuePointsToCode(value))
+        var_ref = dap.variables.InsertVariable(
+            value, /*is_permanent=*/context == "repl");
+      if (value.MightHaveChildren())
+        body.try_emplace("variablesReference", var_ref);
+      else
+        body.try_emplace("variablesReference", (int64_t)0);
+      if (lldb::addr_t addr = value.GetLoadAddress();
+          addr != LLDB_INVALID_ADDRESS)
+        body.try_emplace("memoryReference", EncodeMemoryReference(addr));
+      if (ValuePointsToCode(value))
+        body.try_emplace("valueLocationReference", var_ref);
+    }
+  }
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index e4e5c0ec5af41..43f6aac8608dd 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -81,6 +81,13 @@ class DisconnectRequestHandler : public RequestHandler {
   void operator()(const llvm::json::Object &request) override;
 };
 
+class EvaluateRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "evaluate"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
 } // namespace lldb_dap
 
 #endif
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index efec6b0786691..184c6755e145d 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -703,216 +703,6 @@ void request_exceptionInfo(DAP &dap, const llvm::json::Object &request) {
 }
 
 
-//  "EvaluateRequest": {
-//    "allOf": [ { "$ref": "#/definitions/Request" }, {
-//      "type": "object",
-//      "description": "Evaluate request; value of command field is 'evaluate'.
-//                      Evaluates the given expression in the context of the
-//                      top most stack frame. The expression has access to any
-//                      variables and arguments that are in scope.",
-//      "properties": {
-//        "command": {
-//          "type": "string",
-//          "enum": [ "evaluate" ]
-//        },
-//        "arguments": {
-//          "$ref": "#/definitions/EvaluateArguments"
-//        }
-//      },
-//      "required": [ "command", "arguments"  ]
-//    }]
-//  },
-//  "EvaluateArguments": {
-//    "type": "object",
-//    "description": "Arguments for 'evaluate' request.",
-//    "properties": {
-//      "expression": {
-//        "type": "string",
-//        "description": "The expression to evaluate."
-//      },
-//      "frameId": {
-//        "type": "integer",
-//        "description": "Evaluate the expression in the scope of this stack
-//                        frame. If not specified, the expression is evaluated
-//                        in the global scope."
-//      },
-//      "context": {
-//        "type": "string",
-//        "_enum": [ "watch", "repl", "hover" ],
-//        "enumDescriptions": [
-//          "evaluate is run in a watch.",
-//          "evaluate is run from REPL console.",
-//          "evaluate is run from a data hover."
-//        ],
-//        "description": "The context in which the evaluate request is run."
-//      },
-//      "format": {
-//        "$ref": "#/definitions/ValueFormat",
-//        "description": "Specifies details on how to format the Evaluate
-//                        result."
-//      }
-//    },
-//    "required": [ "expression" ]
-//  },
-//  "EvaluateResponse": {
-//    "allOf": [ { "$ref": "#/definitions/Response" }, {
-//      "type": "object",
-//      "description": "Response to 'evaluate' request.",
-//      "properties": {
-//        "body": {
-//          "type": "object",
-//          "properties": {
-//            "result": {
-//              "type": "string",
-//              "description": "The result of the evaluate request."
-//            },
-//            "type": {
-//              "type": "string",
-//              "description": "The optional type of the evaluate result."
-//            },
-//            "presentationHint": {
-//              "$ref": "#/definitions/VariablePresentationHint",
-//              "description": "Properties of a evaluate result that can be
-//                              used to determine how to render the result in
-//                              the UI."
-//            },
-//            "variablesReference": {
-//              "type": "number",
-//              "description": "If variablesReference is > 0, the evaluate
-//                              result is structured and its children can be
-//                              retrieved by passing variablesReference to the
-//                              VariablesRequest."
-//            },
-//            "namedVariables": {
-//              "type": "number",
-//              "description": "The number of named child variables. The
-//                              client can use this optional information to
-//                              present the variables in a paged UI and fetch
-//                              them in chunks."
-//            },
-//            "indexedVariables": {
-//              "type": "number",
-//              "description": "The number of indexed child variables. The
-//                              client can use this optional information to
-//                              present the variables in a paged UI and fetch
-//                              them in chunks."
-//            },
-//            "valueLocationReference": {
-//              "type": "integer",
-//              "description": "A reference that allows the client to request
-//                              the location where the returned value is
-//                              declared. For example, if a function pointer is
-//                              returned, the adapter may be able to look up the
-//                              function's location. This should be present only
-//                              if the adapter is likely to be able to resolve
-//                              the location.\n\nThis reference shares the same
-//                              lifetime as the `variablesReference`. See
-//                              'Lifetime of Object References' in the
-//              Overview section for details."
-//            }
-//            "memoryReference": {
-//               "type": "string",
-//                "description": "A memory reference to a location appropriate
-//                                for this result. For pointer type eval
-//                                results, this is generally a reference to the
-//                                memory address contained in the pointer. This
-//                                attribute may be returned by a debug adapter
-//                                if corresponding capability
-//                                `supportsMemoryReferences` is true."
-//             },
-//          },
-//          "required": [ "result", "variablesReference" ]
-//        }
-//      },
-//      "required": [ "body" ]
-//    }]
-//  }
-void request_evaluate(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  llvm::json::Object body;
-  const auto *arguments = request.getObject("arguments");
-  lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
-  std::string expression = GetString(arguments, "expression").str();
-  llvm::StringRef context = GetString(arguments, "context");
-  bool repeat_last_command =
-      expression.empty() && dap.last_nonempty_var_expression.empty();
-
-  if (context == "repl" &&
-      (repeat_last_command ||
-       (!expression.empty() &&
-        dap.DetectReplMode(frame, expression, false) == ReplMode::Command))) {
-    // Since the current expression is not for a variable, clear the
-    // last_nonempty_var_expression field.
-    dap.last_nonempty_var_expression.clear();
-    // If we're evaluating a command relative to the current frame, set the
-    // focus_tid to the current frame for any thread related events.
-    if (frame.IsValid()) {
-      dap.focus_tid = frame.GetThread().GetThreadID();
-    }
-    auto result = RunLLDBCommandsVerbatim(dap.debugger, llvm::StringRef(),
-                                          {std::string(expression)});
-    EmplaceSafeString(body, "result", result);
-    body.try_emplace("variablesReference", (int64_t)0);
-  } else {
-    if (context == "repl") {
-      // If the expression is empty and the last expression was for a
-      // variable, set the expression to the previous expression (repeat the
-      // evaluation); otherwise save the current non-empty expression for the
-      // next (possibly empty) variable expression.
-      if (expression.empty())
-        expression = dap.last_nonempty_var_expression;
-      else
-        dap.last_nonempty_var_expression = expression;
-    }
-    // Always try to get the answer from the local variables if possible. If
-    // this fails, then if the context is not "hover", actually evaluate an
-    // expression using the expression parser.
-    //
-    // "frame variable" is more reliable than the expression parser in
-    // many cases and it is faster.
-    lldb::SBValue value = frame.GetValueForVariablePath(
-        expression.data(), lldb::eDynamicDontRunTarget);
-
-    // Freeze dry the value in case users expand it later in the debug console
-    if (value.GetError().Success() && context == "repl")
-      value = value.Persist();
-
-    if (value.GetError().Fail() && context != "hover")
-      value = frame.EvaluateExpression(expression.data());
-
-    if (value.GetError().Fail()) {
-      response["success"] = llvm::json::Value(false);
-      // This error object must live until we're done with the pointer returned
-      // by GetCString().
-      lldb::SBError error = value.GetError();
-      const char *error_cstr = error.GetCString();
-      if (error_cstr && error_cstr[0])
-        EmplaceSafeString(response, "message", std::string(error_cstr));
-      else
-        EmplaceSafeString(response, "message", "evaluate failed");
-    } else {
-      VariableDescription desc(value, dap.enable_auto_variable_summaries);
-      EmplaceSafeString(body, "result", desc.GetResult(context));
-      EmplaceSafeString(body, "type", desc.display_type_name);
-      int64_t var_ref = 0;
-      if (value.MightHaveChildren() || ValuePointsToCode(value))
-        var_ref = dap.variables.InsertVariable(
-            value, /*is_permanent=*/context == "repl");
-      if (value.MightHaveChildren())
-        body.try_emplace("variablesReference", var_ref);
-      else
-        body.try_emplace("variablesReference", (int64_t)0);
-      if (lldb::addr_t addr = value.GetLoadAddress();
-          addr != LLDB_INVALID_ADDRESS)
-        body.try_emplace("memoryReference", EncodeMemoryReference(addr));
-      if (ValuePointsToCode(value))
-        body.try_emplace("valueLocationReference", var_ref);
-    }
-  }
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
 
 // "compileUnitsRequest": {
 //   "allOf": [ { "$ref": "#/definitions/Request" }, {
@@ -4047,8 +3837,8 @@ void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<ContinueRequestHandler>();
   dap.RegisterRequest<ConfigurationDoneRequestHandler>();
   dap.RegisterRequest<DisconnectRequestHandler>();
+  dap.RegisterRequest<EvaluateRequestHandler>();
 
-  dap.RegisterRequestCallback("evaluate", request_evaluate);
   dap.RegisterRequestCallback("exceptionInfo", request_exceptionInfo);
   dap.RegisterRequestCallback("initialize", request_initialize);
   dap.RegisterRequestCallback("launch", request_launch);

>From a8d212db406b898c87c0c11d5d0588169bf6a049 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Sat, 22 Feb 2025 19:56:33 -0600
Subject: [PATCH 10/11] Adopt ExceptionInfoRequestHandler

---
 lldb/tools/lldb-dap/CMakeLists.txt            |   1 +
 .../Handler/ExceptionInfoRequestHandler.cpp   | 174 ++++++++++++++++++
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |   7 +
 lldb/tools/lldb-dap/lldb-dap.cpp              | 161 +---------------
 4 files changed, 183 insertions(+), 160 deletions(-)
 create mode 100644 lldb/tools/lldb-dap/Handler/ExceptionInfoRequestHandler.cpp

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 8942051c9b234..8357da56ab5ff 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -44,6 +44,7 @@ add_lldb_tool(lldb-dap
   Handler/ContinueRequestHandler.cpp
   Handler/DisconnectRequestHandler.cpp
   Handler/EvaluateRequestHandler.cpp
+  Handler/ExceptionInfoRequestHandler.cpp
   Handler/RequestHandler.cpp
 
   LINK_LIBS
diff --git a/lldb/tools/lldb-dap/Handler/ExceptionInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ExceptionInfoRequestHandler.cpp
new file mode 100644
index 0000000000000..ca12e505e0630
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/ExceptionInfoRequestHandler.cpp
@@ -0,0 +1,174 @@
+//===-- ExceptionInfoRequestHandler.cpp -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+#include "lldb/API/SBStream.h"
+
+namespace lldb_dap {
+
+// "ExceptionInfoRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Retrieves the details of the exception that
+//     caused this event to be raised. Clients should only call this request if
+//     the corresponding capability `supportsExceptionInfoRequest` is true.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "exceptionInfo" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/ExceptionInfoArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// },
+// "ExceptionInfoArguments": {
+//   "type": "object",
+//   "description": "Arguments for `exceptionInfo` request.",
+//   "properties": {
+//     "threadId": {
+//       "type": "integer",
+//       "description": "Thread for which exception information should be
+//       retrieved."
+//     }
+//   },
+//   "required": [ "threadId" ]
+// },
+// "ExceptionInfoResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to `exceptionInfo` request.",
+//     "properties": {
+//       "body": {
+//         "type": "object",
+//         "properties": {
+//           "exceptionId": {
+//             "type": "string",
+//             "description": "ID of the exception that was thrown."
+//           },
+//           "description": {
+//             "type": "string",
+//             "description": "Descriptive text for the exception."
+//           },
+//           "breakMode": {
+//          "$ref": "#/definitions/ExceptionBreakMode",
+//            "description": "Mode that caused the exception notification to
+//            be raised."
+//           },
+//           "details": {
+//             "$ref": "#/definitions/ExceptionDetails",
+//            "description": "Detailed information about the exception."
+//           }
+//         },
+//         "required": [ "exceptionId", "breakMode" ]
+//       }
+//     },
+//     "required": [ "body" ]
+//   }]
+// }
+// "ExceptionDetails": {
+//   "type": "object",
+//   "description": "Detailed information about an exception that has
+//   occurred.", "properties": {
+//     "message": {
+//       "type": "string",
+//       "description": "Message contained in the exception."
+//     },
+//     "typeName": {
+//       "type": "string",
+//       "description": "Short type name of the exception object."
+//     },
+//     "fullTypeName": {
+//       "type": "string",
+//       "description": "Fully-qualified type name of the exception object."
+//     },
+//     "evaluateName": {
+//       "type": "string",
+//       "description": "An expression that can be evaluated in the current
+//       scope to obtain the exception object."
+//     },
+//     "stackTrace": {
+//       "type": "string",
+//       "description": "Stack trace at the time the exception was thrown."
+//     },
+//     "innerException": {
+//       "type": "array",
+//       "items": {
+//         "$ref": "#/definitions/ExceptionDetails"
+//       },
+//       "description": "Details of the exception contained by this exception,
+//       if any."
+//     }
+//   }
+// },
+void ExceptionInfoRequestHandler::operator()(
+    const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  const auto *arguments = request.getObject("arguments");
+  llvm::json::Object body;
+  lldb::SBThread thread = dap.GetLLDBThread(*arguments);
+  if (thread.IsValid()) {
+    auto stopReason = thread.GetStopReason();
+    if (stopReason == lldb::eStopReasonSignal)
+      body.try_emplace("exceptionId", "signal");
+    else if (stopReason == lldb::eStopReasonBreakpoint) {
+      ExceptionBreakpoint *exc_bp = dap.GetExceptionBPFromStopReason(thread);
+      if (exc_bp) {
+        EmplaceSafeString(body, "exceptionId", exc_bp->filter);
+        EmplaceSafeString(body, "description", exc_bp->label);
+      } else {
+        body.try_emplace("exceptionId", "exception");
+      }
+    } else {
+      body.try_emplace("exceptionId", "exception");
+    }
+    if (!ObjectContainsKey(body, "description")) {
+      char description[1024];
+      if (thread.GetStopDescription(description, sizeof(description))) {
+        EmplaceSafeString(body, "description", std::string(description));
+      }
+    }
+    body.try_emplace("breakMode", "always");
+    auto exception = thread.GetCurrentException();
+    if (exception.IsValid()) {
+      llvm::json::Object details;
+      lldb::SBStream stream;
+      if (exception.GetDescription(stream)) {
+        EmplaceSafeString(details, "message", stream.GetData());
+      }
+
+      auto exceptionBacktrace = thread.GetCurrentExceptionBacktrace();
+      if (exceptionBacktrace.IsValid()) {
+        lldb::SBStream stream;
+        exceptionBacktrace.GetDescription(stream);
+        for (uint32_t i = 0; i < exceptionBacktrace.GetNumFrames(); i++) {
+          lldb::SBFrame frame = exceptionBacktrace.GetFrameAtIndex(i);
+          frame.GetDescription(stream);
+        }
+        EmplaceSafeString(details, "stackTrace", stream.GetData());
+      }
+
+      body.try_emplace("details", std::move(details));
+    }
+    // auto excInfoCount = thread.GetStopReasonDataCount();
+    // for (auto i=0; i<excInfoCount; ++i) {
+    //   uint64_t exc_data = thread.GetStopReasonDataAtIndex(i);
+    // }
+  } else {
+    response["success"] = llvm::json::Value(false);
+  }
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 43f6aac8608dd..a151fc19d7fbb 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -88,6 +88,13 @@ class EvaluateRequestHandler : public RequestHandler {
   void operator()(const llvm::json::Object &request) override;
 };
 
+class ExceptionInfoRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "exceptionInfo"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
 } // namespace lldb_dap
 
 #endif
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 184c6755e145d..ab904bbee52be 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -545,165 +545,6 @@ bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
   return reached_end_of_stack;
 }
 
-// "ExceptionInfoRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Retrieves the details of the exception that
-//     caused this event to be raised. Clients should only call this request if
-//     the corresponding capability `supportsExceptionInfoRequest` is true.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "exceptionInfo" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/ExceptionInfoArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "ExceptionInfoArguments": {
-//   "type": "object",
-//   "description": "Arguments for `exceptionInfo` request.",
-//   "properties": {
-//     "threadId": {
-//       "type": "integer",
-//       "description": "Thread for which exception information should be
-//       retrieved."
-//     }
-//   },
-//   "required": [ "threadId" ]
-// },
-// "ExceptionInfoResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to `exceptionInfo` request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "exceptionId": {
-//             "type": "string",
-//             "description": "ID of the exception that was thrown."
-//           },
-//           "description": {
-//             "type": "string",
-//             "description": "Descriptive text for the exception."
-//           },
-//           "breakMode": {
-//          "$ref": "#/definitions/ExceptionBreakMode",
-//            "description": "Mode that caused the exception notification to
-//            be raised."
-//           },
-//           "details": {
-//             "$ref": "#/definitions/ExceptionDetails",
-//            "description": "Detailed information about the exception."
-//           }
-//         },
-//         "required": [ "exceptionId", "breakMode" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-// "ExceptionDetails": {
-//   "type": "object",
-//   "description": "Detailed information about an exception that has
-//   occurred.", "properties": {
-//     "message": {
-//       "type": "string",
-//       "description": "Message contained in the exception."
-//     },
-//     "typeName": {
-//       "type": "string",
-//       "description": "Short type name of the exception object."
-//     },
-//     "fullTypeName": {
-//       "type": "string",
-//       "description": "Fully-qualified type name of the exception object."
-//     },
-//     "evaluateName": {
-//       "type": "string",
-//       "description": "An expression that can be evaluated in the current
-//       scope to obtain the exception object."
-//     },
-//     "stackTrace": {
-//       "type": "string",
-//       "description": "Stack trace at the time the exception was thrown."
-//     },
-//     "innerException": {
-//       "type": "array",
-//       "items": {
-//         "$ref": "#/definitions/ExceptionDetails"
-//       },
-//       "description": "Details of the exception contained by this exception,
-//       if any."
-//     }
-//   }
-// },
-void request_exceptionInfo(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-  llvm::json::Object body;
-  lldb::SBThread thread = dap.GetLLDBThread(*arguments);
-  if (thread.IsValid()) {
-    auto stopReason = thread.GetStopReason();
-    if (stopReason == lldb::eStopReasonSignal)
-      body.try_emplace("exceptionId", "signal");
-    else if (stopReason == lldb::eStopReasonBreakpoint) {
-      ExceptionBreakpoint *exc_bp = dap.GetExceptionBPFromStopReason(thread);
-      if (exc_bp) {
-        EmplaceSafeString(body, "exceptionId", exc_bp->filter);
-        EmplaceSafeString(body, "description", exc_bp->label);
-      } else {
-        body.try_emplace("exceptionId", "exception");
-      }
-    } else {
-      body.try_emplace("exceptionId", "exception");
-    }
-    if (!ObjectContainsKey(body, "description")) {
-      char description[1024];
-      if (thread.GetStopDescription(description, sizeof(description))) {
-        EmplaceSafeString(body, "description", std::string(description));
-      }
-    }
-    body.try_emplace("breakMode", "always");
-    auto exception = thread.GetCurrentException();
-    if (exception.IsValid()) {
-      llvm::json::Object details;
-      lldb::SBStream stream;
-      if (exception.GetDescription(stream)) {
-        EmplaceSafeString(details, "message", stream.GetData());
-      }
-
-      auto exceptionBacktrace = thread.GetCurrentExceptionBacktrace();
-      if (exceptionBacktrace.IsValid()) {
-        lldb::SBStream stream;
-        exceptionBacktrace.GetDescription(stream);
-        for (uint32_t i = 0; i < exceptionBacktrace.GetNumFrames(); i++) {
-          lldb::SBFrame frame = exceptionBacktrace.GetFrameAtIndex(i);
-          frame.GetDescription(stream);
-        }
-        EmplaceSafeString(details, "stackTrace", stream.GetData());
-      }
-
-      body.try_emplace("details", std::move(details));
-    }
-    // auto excInfoCount = thread.GetStopReasonDataCount();
-    // for (auto i=0; i<excInfoCount; ++i) {
-    //   uint64_t exc_data = thread.GetStopReasonDataAtIndex(i);
-    // }
-  } else {
-    response["success"] = llvm::json::Value(false);
-  }
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-
-
 // "compileUnitsRequest": {
 //   "allOf": [ { "$ref": "#/definitions/Request" }, {
 //     "type": "object",
@@ -3838,8 +3679,8 @@ void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<ConfigurationDoneRequestHandler>();
   dap.RegisterRequest<DisconnectRequestHandler>();
   dap.RegisterRequest<EvaluateRequestHandler>();
+  dap.RegisterRequest<ExceptionInfoRequestHandler>();
 
-  dap.RegisterRequestCallback("exceptionInfo", request_exceptionInfo);
   dap.RegisterRequestCallback("initialize", request_initialize);
   dap.RegisterRequestCallback("launch", request_launch);
   dap.RegisterRequestCallback("next", request_next);

>From feae281935860be7d10258dca017e850f9cb79ec Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Sat, 22 Feb 2025 20:08:04 -0600
Subject: [PATCH 11/11] Adopt InitializeRequestHandler

---
 lldb/tools/lldb-dap/CMakeLists.txt            |   1 +
 lldb/tools/lldb-dap/EventHelper.cpp           |  41 ++
 lldb/tools/lldb-dap/EventHelper.h             |   8 +
 .../Handler/InitializeRequestHandler.cpp      | 412 +++++++++++++++++
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |   7 +
 lldb/tools/lldb-dap/lldb-dap.cpp              | 434 +-----------------
 6 files changed, 470 insertions(+), 433 deletions(-)
 create mode 100644 lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 8357da56ab5ff..141c555eb4ae0 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -43,6 +43,7 @@ add_lldb_tool(lldb-dap
   Handler/ConfigurationDoneRequestHandler.cpp
   Handler/ContinueRequestHandler.cpp
   Handler/DisconnectRequestHandler.cpp
+  Handler/InitializeRequestHandler.cpp
   Handler/EvaluateRequestHandler.cpp
   Handler/ExceptionInfoRequestHandler.cpp
   Handler/RequestHandler.cpp
diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp
index f59c73a6593c1..20845fbc5b916 100644
--- a/lldb/tools/lldb-dap/EventHelper.cpp
+++ b/lldb/tools/lldb-dap/EventHelper.cpp
@@ -194,4 +194,45 @@ void SendTerminatedEvent(DAP &dap) {
   });
 }
 
+// Grab any STDOUT and STDERR from the process and send it up to VS Code
+// via an "output" event to the "stdout" and "stderr" categories.
+void SendStdOutStdErr(DAP &dap, lldb::SBProcess &process) {
+  char buffer[OutputBufferSize];
+  size_t count;
+  while ((count = process.GetSTDOUT(buffer, sizeof(buffer))) > 0)
+    dap.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count));
+  while ((count = process.GetSTDERR(buffer, sizeof(buffer))) > 0)
+    dap.SendOutput(OutputType::Stderr, llvm::StringRef(buffer, count));
+}
+
+// Send a "continued" event to indicate the process is in the running state.
+void SendContinuedEvent(DAP &dap) {
+  lldb::SBProcess process = dap.target.GetProcess();
+  if (!process.IsValid()) {
+    return;
+  }
+
+  // If the focus thread is not set then we haven't reported any thread status
+  // to the client, so nothing to report.
+  if (!dap.configuration_done_sent || dap.focus_tid == LLDB_INVALID_THREAD_ID) {
+    return;
+  }
+
+  llvm::json::Object event(CreateEventObject("continued"));
+  llvm::json::Object body;
+  body.try_emplace("threadId", (int64_t)dap.focus_tid);
+  body.try_emplace("allThreadsContinued", true);
+  event.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(event)));
+}
+
+// Send a "exited" event to indicate the process has exited.
+void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process) {
+  llvm::json::Object event(CreateEventObject("exited"));
+  llvm::json::Object body;
+  body.try_emplace("exitCode", (int64_t)process.GetExitStatus());
+  event.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(event)));
+}
+
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/EventHelper.h b/lldb/tools/lldb-dap/EventHelper.h
index 0c5978e1db49b..90b009c73089e 100644
--- a/lldb/tools/lldb-dap/EventHelper.h
+++ b/lldb/tools/lldb-dap/EventHelper.h
@@ -9,6 +9,8 @@
 #ifndef LLDB_TOOLS_LLDB_DAP_EVENTHELPER_H
 #define LLDB_TOOLS_LLDB_DAP_EVENTHELPER_H
 
+#include "DAPForward.h"
+
 namespace lldb_dap {
 struct DAP;
 
@@ -20,6 +22,12 @@ void SendThreadStoppedEvent(DAP &dap);
 
 void SendTerminatedEvent(DAP &dap);
 
+void SendStdOutStdErr(DAP &dap, lldb::SBProcess &process);
+
+void SendContinuedEvent(DAP &dap);
+
+void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process);
+
 } // namespace lldb_dap
 
 #endif
diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
new file mode 100644
index 0000000000000..9823678b77f18
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
@@ -0,0 +1,412 @@
+//===-- InitializeRequestHandler.cpp --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBListener.h"
+#include "lldb/API/SBStream.h"
+
+using namespace lldb;
+
+namespace lldb_dap {
+
+static void ProgressEventThreadFunction(DAP &dap) {
+  lldb::SBListener listener("lldb-dap.progress.listener");
+  dap.debugger.GetBroadcaster().AddListener(
+      listener, lldb::SBDebugger::eBroadcastBitProgress |
+                    lldb::SBDebugger::eBroadcastBitExternalProgress);
+  dap.broadcaster.AddListener(listener, eBroadcastBitStopProgressThread);
+  lldb::SBEvent event;
+  bool done = false;
+  while (!done) {
+    if (listener.WaitForEvent(1, event)) {
+      const auto event_mask = event.GetType();
+      if (event.BroadcasterMatchesRef(dap.broadcaster)) {
+        if (event_mask & eBroadcastBitStopProgressThread) {
+          done = true;
+        }
+      } else {
+        uint64_t progress_id = 0;
+        uint64_t completed = 0;
+        uint64_t total = 0;
+        bool is_debugger_specific = false;
+        const char *message = lldb::SBDebugger::GetProgressFromEvent(
+            event, progress_id, completed, total, is_debugger_specific);
+        if (message)
+          dap.SendProgressEvent(progress_id, message, completed, total);
+      }
+    }
+  }
+}
+
+// All events from the debugger, target, process, thread and frames are
+// received in this function that runs in its own thread. We are using a
+// "FILE *" to output packets back to VS Code and they have mutexes in them
+// them prevent multiple threads from writing simultaneously so no locking
+// is required.
+static void EventThreadFunction(DAP &dap) {
+  lldb::SBEvent event;
+  lldb::SBListener listener = dap.debugger.GetListener();
+  bool done = false;
+  while (!done) {
+    if (listener.WaitForEvent(1, event)) {
+      const auto event_mask = event.GetType();
+      if (lldb::SBProcess::EventIsProcessEvent(event)) {
+        lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event);
+        if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) {
+          auto state = lldb::SBProcess::GetStateFromEvent(event);
+          switch (state) {
+          case lldb::eStateInvalid:
+            // Not a state event
+            break;
+          case lldb::eStateUnloaded:
+            break;
+          case lldb::eStateConnected:
+            break;
+          case lldb::eStateAttaching:
+            break;
+          case lldb::eStateLaunching:
+            break;
+          case lldb::eStateStepping:
+            break;
+          case lldb::eStateCrashed:
+            break;
+          case lldb::eStateDetached:
+            break;
+          case lldb::eStateSuspended:
+            break;
+          case lldb::eStateStopped:
+            // We launch and attach in synchronous mode then the first stop
+            // event will not be delivered. If we use "launchCommands" during a
+            // launch or "attachCommands" during an attach we might some process
+            // stop events which we do not want to send an event for. We will
+            // manually send a stopped event in request_configurationDone(...)
+            // so don't send any before then.
+            if (dap.configuration_done_sent) {
+              // Only report a stopped event if the process was not
+              // automatically restarted.
+              if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
+                SendStdOutStdErr(dap, process);
+                SendThreadStoppedEvent(dap);
+              }
+            }
+            break;
+          case lldb::eStateRunning:
+            dap.WillContinue();
+            SendContinuedEvent(dap);
+            break;
+          case lldb::eStateExited:
+            lldb::SBStream stream;
+            process.GetStatus(stream);
+            dap.SendOutput(OutputType::Console, stream.GetData());
+
+            // When restarting, we can get an "exited" event for the process we
+            // just killed with the old PID, or even with no PID. In that case
+            // we don't have to terminate the session.
+            if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID ||
+                process.GetProcessID() == dap.restarting_process_id) {
+              dap.restarting_process_id = LLDB_INVALID_PROCESS_ID;
+            } else {
+              // Run any exit LLDB commands the user specified in the
+              // launch.json
+              dap.RunExitCommands();
+              SendProcessExitedEvent(dap, process);
+              SendTerminatedEvent(dap);
+              done = true;
+            }
+            break;
+          }
+        } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) ||
+                   (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) {
+          SendStdOutStdErr(dap, process);
+        }
+      } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) {
+        if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) {
+          auto event_type =
+              lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event);
+          auto bp = Breakpoint(
+              dap, lldb::SBBreakpoint::GetBreakpointFromEvent(event));
+          // If the breakpoint was originated from the IDE, it will have the
+          // BreakpointBase::GetBreakpointLabel() label attached. Regardless
+          // of wether the locations were added or removed, the breakpoint
+          // ins't going away, so we the reason is always "changed".
+          if ((event_type & lldb::eBreakpointEventTypeLocationsAdded ||
+               event_type & lldb::eBreakpointEventTypeLocationsRemoved) &&
+              bp.MatchesName(BreakpointBase::GetBreakpointLabel())) {
+            auto bp_event = CreateEventObject("breakpoint");
+            llvm::json::Object body;
+            // As VSCode already knows the path of this breakpoint, we don't
+            // need to send it back as part of a "changed" event. This
+            // prevent us from sending to VSCode paths that should be source
+            // mapped. Note that CreateBreakpoint doesn't apply source mapping.
+            // Besides, the current implementation of VSCode ignores the
+            // "source" element of breakpoint events.
+            llvm::json::Value source_bp = CreateBreakpoint(&bp);
+            source_bp.getAsObject()->erase("source");
+
+            body.try_emplace("breakpoint", source_bp);
+            body.try_emplace("reason", "changed");
+            bp_event.try_emplace("body", std::move(body));
+            dap.SendJSON(llvm::json::Value(std::move(bp_event)));
+          }
+        }
+      } else if (event.BroadcasterMatchesRef(dap.broadcaster)) {
+        if (event_mask & eBroadcastBitStopEventThread) {
+          done = true;
+        }
+      }
+    }
+  }
+}
+
+// "InitializeRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Initialize request; value of command field is
+//                     'initialize'.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "initialize" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/InitializeRequestArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments" ]
+//   }]
+// },
+// "InitializeRequestArguments": {
+//   "type": "object",
+//   "description": "Arguments for 'initialize' request.",
+//   "properties": {
+//     "clientID": {
+//       "type": "string",
+//       "description": "The ID of the (frontend) client using this adapter."
+//     },
+//     "adapterID": {
+//       "type": "string",
+//       "description": "The ID of the debug adapter."
+//     },
+//     "locale": {
+//       "type": "string",
+//       "description": "The ISO-639 locale of the (frontend) client using
+//                       this adapter, e.g. en-US or de-CH."
+//     },
+//     "linesStartAt1": {
+//       "type": "boolean",
+//       "description": "If true all line numbers are 1-based (default)."
+//     },
+//     "columnsStartAt1": {
+//       "type": "boolean",
+//       "description": "If true all column numbers are 1-based (default)."
+//     },
+//     "pathFormat": {
+//       "type": "string",
+//       "_enum": [ "path", "uri" ],
+//       "description": "Determines in what format paths are specified. The
+//                       default is 'path', which is the native format."
+//     },
+//     "supportsVariableType": {
+//       "type": "boolean",
+//       "description": "Client supports the optional type attribute for
+//                       variables."
+//     },
+//     "supportsVariablePaging": {
+//       "type": "boolean",
+//       "description": "Client supports the paging of variables."
+//     },
+//     "supportsRunInTerminalRequest": {
+//       "type": "boolean",
+//       "description": "Client supports the runInTerminal request."
+//     }
+//   },
+//   "required": [ "adapterID" ]
+// },
+// "InitializeResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to 'initialize' request.",
+//     "properties": {
+//       "body": {
+//         "$ref": "#/definitions/Capabilities",
+//         "description": "The capabilities of this debug adapter."
+//       }
+//     }
+//   }]
+// }
+void InitializeRequestHandler::operator()(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  llvm::json::Object body;
+
+  const auto *arguments = request.getObject("arguments");
+  // sourceInitFile option is not from formal DAP specification. It is only
+  // used by unit tests to prevent sourcing .lldbinit files from environment
+  // which may affect the outcome of tests.
+  bool source_init_file = GetBoolean(arguments, "sourceInitFile", true);
+
+  // Do not source init files until in/out/err are configured.
+  dap.debugger = lldb::SBDebugger::Create(false);
+  dap.debugger.SetInputFile(dap.in);
+  auto out_fd = dap.out.GetWriteFileDescriptor();
+  if (llvm::Error err = out_fd.takeError()) {
+    response["success"] = false;
+    EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
+    dap.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+  dap.debugger.SetOutputFile(lldb::SBFile(*out_fd, "w", false));
+  auto err_fd = dap.err.GetWriteFileDescriptor();
+  if (llvm::Error err = err_fd.takeError()) {
+    response["success"] = false;
+    EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
+    dap.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+  dap.debugger.SetErrorFile(lldb::SBFile(*err_fd, "w", false));
+
+  auto interp = dap.debugger.GetCommandInterpreter();
+
+  if (source_init_file) {
+    dap.debugger.SkipLLDBInitFiles(false);
+    dap.debugger.SkipAppInitFiles(false);
+    lldb::SBCommandReturnObject init;
+    interp.SourceInitFileInGlobalDirectory(init);
+    interp.SourceInitFileInHomeDirectory(init);
+  }
+
+  if (llvm::Error err = dap.RunPreInitCommands()) {
+    response["success"] = false;
+    EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
+    dap.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+
+  dap.PopulateExceptionBreakpoints();
+  auto cmd = dap.debugger.GetCommandInterpreter().AddMultiwordCommand(
+      "lldb-dap", "Commands for managing lldb-dap.");
+  if (GetBoolean(arguments, "supportsStartDebuggingRequest", false)) {
+    cmd.AddCommand(
+        "start-debugging", new StartDebuggingRequestHandler(dap),
+        "Sends a startDebugging request from the debug adapter to the client "
+        "to start a child debug session of the same type as the caller.");
+  }
+  cmd.AddCommand(
+      "repl-mode", new ReplModeRequestHandler(dap),
+      "Get or set the repl behavior of lldb-dap evaluation requests.");
+  cmd.AddCommand("send-event", new SendEventRequestHandler(dap),
+                 "Sends an DAP event to the client.");
+
+  dap.progress_event_thread =
+      std::thread(ProgressEventThreadFunction, std::ref(dap));
+
+  // Start our event thread so we can receive events from the debugger, target,
+  // process and more.
+  dap.event_thread = std::thread(EventThreadFunction, std::ref(dap));
+
+  // The debug adapter supports the configurationDoneRequest.
+  body.try_emplace("supportsConfigurationDoneRequest", true);
+  // The debug adapter supports function breakpoints.
+  body.try_emplace("supportsFunctionBreakpoints", true);
+  // The debug adapter supports conditional breakpoints.
+  body.try_emplace("supportsConditionalBreakpoints", true);
+  // The debug adapter supports breakpoints that break execution after a
+  // specified number of hits.
+  body.try_emplace("supportsHitConditionalBreakpoints", true);
+  // The debug adapter supports a (side effect free) evaluate request for
+  // data hovers.
+  body.try_emplace("supportsEvaluateForHovers", true);
+  // Available filters or options for the setExceptionBreakpoints request.
+  llvm::json::Array filters;
+  for (const auto &exc_bp : *dap.exception_breakpoints) {
+    filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp));
+  }
+  body.try_emplace("exceptionBreakpointFilters", std::move(filters));
+  // The debug adapter supports launching a debugee in intergrated VSCode
+  // terminal.
+  body.try_emplace("supportsRunInTerminalRequest", true);
+  // The debug adapter supports stepping back via the stepBack and
+  // reverseContinue requests.
+  body.try_emplace("supportsStepBack", false);
+  // The debug adapter supports setting a variable to a value.
+  body.try_emplace("supportsSetVariable", true);
+  // The debug adapter supports restarting a frame.
+  body.try_emplace("supportsRestartFrame", false);
+  // The debug adapter supports the gotoTargetsRequest.
+  body.try_emplace("supportsGotoTargetsRequest", false);
+  // The debug adapter supports the stepInTargetsRequest.
+  body.try_emplace("supportsStepInTargetsRequest", true);
+  // The debug adapter supports the completions request.
+  body.try_emplace("supportsCompletionsRequest", true);
+  // The debug adapter supports the disassembly request.
+  body.try_emplace("supportsDisassembleRequest", true);
+  // The debug adapter supports the `breakpointLocations` request.
+  body.try_emplace("supportsBreakpointLocationsRequest", true);
+  // The debug adapter supports stepping granularities (argument `granularity`)
+  // for the stepping requests.
+  body.try_emplace("supportsSteppingGranularity", true);
+  // The debug adapter support for instruction breakpoint.
+  body.try_emplace("supportsInstructionBreakpoints", true);
+
+  llvm::json::Array completion_characters;
+  completion_characters.emplace_back(".");
+  completion_characters.emplace_back(" ");
+  completion_characters.emplace_back("\t");
+  body.try_emplace("completionTriggerCharacters",
+                   std::move(completion_characters));
+
+  // The debug adapter supports the modules request.
+  body.try_emplace("supportsModulesRequest", true);
+  // The set of additional module information exposed by the debug adapter.
+  //   body.try_emplace("additionalModuleColumns"] = ColumnDescriptor
+  // Checksum algorithms supported by the debug adapter.
+  //   body.try_emplace("supportedChecksumAlgorithms"] = ChecksumAlgorithm
+  // The debug adapter supports the RestartRequest. In this case a client
+  // should not implement 'restart' by terminating and relaunching the adapter
+  // but by calling the RestartRequest.
+  body.try_emplace("supportsRestartRequest", true);
+  // The debug adapter supports 'exceptionOptions' on the
+  // setExceptionBreakpoints request.
+  body.try_emplace("supportsExceptionOptions", true);
+  // The debug adapter supports a 'format' attribute on the stackTraceRequest,
+  // variablesRequest, and evaluateRequest.
+  body.try_emplace("supportsValueFormattingOptions", true);
+  // The debug adapter supports the exceptionInfo request.
+  body.try_emplace("supportsExceptionInfoRequest", true);
+  // The debug adapter supports the 'terminateDebuggee' attribute on the
+  // 'disconnect' request.
+  body.try_emplace("supportTerminateDebuggee", true);
+  // The debug adapter supports the delayed loading of parts of the stack,
+  // which requires that both the 'startFrame' and 'levels' arguments and the
+  // 'totalFrames' result of the 'StackTrace' request are supported.
+  body.try_emplace("supportsDelayedStackTraceLoading", true);
+  // The debug adapter supports the 'loadedSources' request.
+  body.try_emplace("supportsLoadedSourcesRequest", false);
+  // The debug adapter supports sending progress reporting events.
+  body.try_emplace("supportsProgressReporting", true);
+  // The debug adapter supports 'logMessage' in breakpoint.
+  body.try_emplace("supportsLogPoints", true);
+  // The debug adapter supports data watchpoints.
+  body.try_emplace("supportsDataBreakpoints", true);
+  // The debug adapter supports the `readMemory` request.
+  body.try_emplace("supportsReadMemoryRequest", true);
+
+  // Put in non-DAP specification lldb specific information.
+  llvm::json::Object lldb_json;
+  lldb_json.try_emplace("version", dap.debugger.GetVersionString());
+  body.try_emplace("__lldb", std::move(lldb_json));
+
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index a151fc19d7fbb..af0fb63606759 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -95,6 +95,13 @@ class ExceptionInfoRequestHandler : public RequestHandler {
   void operator()(const llvm::json::Object &request) override;
 };
 
+class InitializeRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "initialize"; }
+  void operator()(const llvm::json::Object &request) override;
+};
+
 } // namespace lldb_dap
 
 #endif
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index ab904bbee52be..eb5a23fe155e7 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -190,196 +190,6 @@ std::vector<const char *> MakeArgv(const llvm::ArrayRef<std::string> &strs) {
   return argv;
 }
 
-// Send a "exited" event to indicate the process has exited.
-void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process) {
-  llvm::json::Object event(CreateEventObject("exited"));
-  llvm::json::Object body;
-  body.try_emplace("exitCode", (int64_t)process.GetExitStatus());
-  event.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(event)));
-}
-
-// Send a "continued" event to indicate the process is in the running state.
-void SendContinuedEvent(DAP &dap) {
-  lldb::SBProcess process = dap.target.GetProcess();
-  if (!process.IsValid()) {
-    return;
-  }
-
-  // If the focus thread is not set then we haven't reported any thread status
-  // to the client, so nothing to report.
-  if (!dap.configuration_done_sent || dap.focus_tid == LLDB_INVALID_THREAD_ID) {
-    return;
-  }
-
-  llvm::json::Object event(CreateEventObject("continued"));
-  llvm::json::Object body;
-  body.try_emplace("threadId", (int64_t)dap.focus_tid);
-  body.try_emplace("allThreadsContinued", true);
-  event.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(event)));
-}
-
-// Grab any STDOUT and STDERR from the process and send it up to VS Code
-// via an "output" event to the "stdout" and "stderr" categories.
-void SendStdOutStdErr(DAP &dap, lldb::SBProcess &process) {
-  char buffer[OutputBufferSize];
-  size_t count;
-  while ((count = process.GetSTDOUT(buffer, sizeof(buffer))) > 0)
-    dap.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count));
-  while ((count = process.GetSTDERR(buffer, sizeof(buffer))) > 0)
-    dap.SendOutput(OutputType::Stderr, llvm::StringRef(buffer, count));
-}
-
-void ProgressEventThreadFunction(DAP &dap) {
-  lldb::SBListener listener("lldb-dap.progress.listener");
-  dap.debugger.GetBroadcaster().AddListener(
-      listener, lldb::SBDebugger::eBroadcastBitProgress |
-                    lldb::SBDebugger::eBroadcastBitExternalProgress);
-  dap.broadcaster.AddListener(listener, eBroadcastBitStopProgressThread);
-  lldb::SBEvent event;
-  bool done = false;
-  while (!done) {
-    if (listener.WaitForEvent(1, event)) {
-      const auto event_mask = event.GetType();
-      if (event.BroadcasterMatchesRef(dap.broadcaster)) {
-        if (event_mask & eBroadcastBitStopProgressThread) {
-          done = true;
-        }
-      } else {
-        uint64_t progress_id = 0;
-        uint64_t completed = 0;
-        uint64_t total = 0;
-        bool is_debugger_specific = false;
-        const char *message = lldb::SBDebugger::GetProgressFromEvent(
-            event, progress_id, completed, total, is_debugger_specific);
-        if (message)
-          dap.SendProgressEvent(progress_id, message, completed, total);
-      }
-    }
-  }
-}
-
-// All events from the debugger, target, process, thread and frames are
-// received in this function that runs in its own thread. We are using a
-// "FILE *" to output packets back to VS Code and they have mutexes in them
-// them prevent multiple threads from writing simultaneously so no locking
-// is required.
-void EventThreadFunction(DAP &dap) {
-  lldb::SBEvent event;
-  lldb::SBListener listener = dap.debugger.GetListener();
-  bool done = false;
-  while (!done) {
-    if (listener.WaitForEvent(1, event)) {
-      const auto event_mask = event.GetType();
-      if (lldb::SBProcess::EventIsProcessEvent(event)) {
-        lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event);
-        if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) {
-          auto state = lldb::SBProcess::GetStateFromEvent(event);
-          switch (state) {
-          case lldb::eStateInvalid:
-            // Not a state event
-            break;
-          case lldb::eStateUnloaded:
-            break;
-          case lldb::eStateConnected:
-            break;
-          case lldb::eStateAttaching:
-            break;
-          case lldb::eStateLaunching:
-            break;
-          case lldb::eStateStepping:
-            break;
-          case lldb::eStateCrashed:
-            break;
-          case lldb::eStateDetached:
-            break;
-          case lldb::eStateSuspended:
-            break;
-          case lldb::eStateStopped:
-            // We launch and attach in synchronous mode then the first stop
-            // event will not be delivered. If we use "launchCommands" during a
-            // launch or "attachCommands" during an attach we might some process
-            // stop events which we do not want to send an event for. We will
-            // manually send a stopped event in request_configurationDone(...)
-            // so don't send any before then.
-            if (dap.configuration_done_sent) {
-              // Only report a stopped event if the process was not
-              // automatically restarted.
-              if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
-                SendStdOutStdErr(dap, process);
-                SendThreadStoppedEvent(dap);
-              }
-            }
-            break;
-          case lldb::eStateRunning:
-            dap.WillContinue();
-            SendContinuedEvent(dap);
-            break;
-          case lldb::eStateExited:
-            lldb::SBStream stream;
-            process.GetStatus(stream);
-            dap.SendOutput(OutputType::Console, stream.GetData());
-
-            // When restarting, we can get an "exited" event for the process we
-            // just killed with the old PID, or even with no PID. In that case
-            // we don't have to terminate the session.
-            if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID ||
-                process.GetProcessID() == dap.restarting_process_id) {
-              dap.restarting_process_id = LLDB_INVALID_PROCESS_ID;
-            } else {
-              // Run any exit LLDB commands the user specified in the
-              // launch.json
-              dap.RunExitCommands();
-              SendProcessExitedEvent(dap, process);
-              SendTerminatedEvent(dap);
-              done = true;
-            }
-            break;
-          }
-        } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) ||
-                   (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) {
-          SendStdOutStdErr(dap, process);
-        }
-      } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) {
-        if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) {
-          auto event_type =
-              lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event);
-          auto bp = Breakpoint(
-              dap, lldb::SBBreakpoint::GetBreakpointFromEvent(event));
-          // If the breakpoint was originated from the IDE, it will have the
-          // BreakpointBase::GetBreakpointLabel() label attached. Regardless
-          // of wether the locations were added or removed, the breakpoint
-          // ins't going away, so we the reason is always "changed".
-          if ((event_type & lldb::eBreakpointEventTypeLocationsAdded ||
-               event_type & lldb::eBreakpointEventTypeLocationsRemoved) &&
-              bp.MatchesName(BreakpointBase::GetBreakpointLabel())) {
-            auto bp_event = CreateEventObject("breakpoint");
-            llvm::json::Object body;
-            // As VSCode already knows the path of this breakpoint, we don't
-            // need to send it back as part of a "changed" event. This
-            // prevent us from sending to VSCode paths that should be source
-            // mapped. Note that CreateBreakpoint doesn't apply source mapping.
-            // Besides, the current implementation of VSCode ignores the
-            // "source" element of breakpoint events.
-            llvm::json::Value source_bp = CreateBreakpoint(&bp);
-            source_bp.getAsObject()->erase("source");
-
-            body.try_emplace("breakpoint", source_bp);
-            body.try_emplace("reason", "changed");
-            bp_event.try_emplace("body", std::move(body));
-            dap.SendJSON(llvm::json::Value(std::move(bp_event)));
-          }
-        }
-      } else if (event.BroadcasterMatchesRef(dap.broadcaster)) {
-        if (event_mask & eBroadcastBitStopEventThread) {
-          done = true;
-        }
-      }
-    }
-  }
-}
-
 lldb::SBValue FindVariable(DAP &dap, uint64_t variablesReference,
                            llvm::StringRef name) {
   lldb::SBValue variable;
@@ -651,248 +461,6 @@ void request_modules(DAP &dap, const llvm::json::Object &request) {
   dap.SendJSON(llvm::json::Value(std::move(response)));
 }
 
-// "InitializeRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Initialize request; value of command field is
-//                     'initialize'.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "initialize" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/InitializeRequestArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments" ]
-//   }]
-// },
-// "InitializeRequestArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'initialize' request.",
-//   "properties": {
-//     "clientID": {
-//       "type": "string",
-//       "description": "The ID of the (frontend) client using this adapter."
-//     },
-//     "adapterID": {
-//       "type": "string",
-//       "description": "The ID of the debug adapter."
-//     },
-//     "locale": {
-//       "type": "string",
-//       "description": "The ISO-639 locale of the (frontend) client using
-//                       this adapter, e.g. en-US or de-CH."
-//     },
-//     "linesStartAt1": {
-//       "type": "boolean",
-//       "description": "If true all line numbers are 1-based (default)."
-//     },
-//     "columnsStartAt1": {
-//       "type": "boolean",
-//       "description": "If true all column numbers are 1-based (default)."
-//     },
-//     "pathFormat": {
-//       "type": "string",
-//       "_enum": [ "path", "uri" ],
-//       "description": "Determines in what format paths are specified. The
-//                       default is 'path', which is the native format."
-//     },
-//     "supportsVariableType": {
-//       "type": "boolean",
-//       "description": "Client supports the optional type attribute for
-//                       variables."
-//     },
-//     "supportsVariablePaging": {
-//       "type": "boolean",
-//       "description": "Client supports the paging of variables."
-//     },
-//     "supportsRunInTerminalRequest": {
-//       "type": "boolean",
-//       "description": "Client supports the runInTerminal request."
-//     }
-//   },
-//   "required": [ "adapterID" ]
-// },
-// "InitializeResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'initialize' request.",
-//     "properties": {
-//       "body": {
-//         "$ref": "#/definitions/Capabilities",
-//         "description": "The capabilities of this debug adapter."
-//       }
-//     }
-//   }]
-// }
-void request_initialize(DAP &dap, const llvm::json::Object &request) {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  llvm::json::Object body;
-
-  const auto *arguments = request.getObject("arguments");
-  // sourceInitFile option is not from formal DAP specification. It is only
-  // used by unit tests to prevent sourcing .lldbinit files from environment
-  // which may affect the outcome of tests.
-  bool source_init_file = GetBoolean(arguments, "sourceInitFile", true);
-
-  // Do not source init files until in/out/err are configured.
-  dap.debugger = lldb::SBDebugger::Create(false);
-  dap.debugger.SetInputFile(dap.in);
-  auto out_fd = dap.out.GetWriteFileDescriptor();
-  if (llvm::Error err = out_fd.takeError()) {
-    response["success"] = false;
-    EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
-    dap.SendJSON(llvm::json::Value(std::move(response)));
-    return;
-  }
-  dap.debugger.SetOutputFile(lldb::SBFile(*out_fd, "w", false));
-  auto err_fd = dap.err.GetWriteFileDescriptor();
-  if (llvm::Error err = err_fd.takeError()) {
-    response["success"] = false;
-    EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
-    dap.SendJSON(llvm::json::Value(std::move(response)));
-    return;
-  }
-  dap.debugger.SetErrorFile(lldb::SBFile(*err_fd, "w", false));
-
-  auto interp = dap.debugger.GetCommandInterpreter();
-
-  if (source_init_file) {
-    dap.debugger.SkipLLDBInitFiles(false);
-    dap.debugger.SkipAppInitFiles(false);
-    lldb::SBCommandReturnObject init;
-    interp.SourceInitFileInGlobalDirectory(init);
-    interp.SourceInitFileInHomeDirectory(init);
-  }
-
-  if (llvm::Error err = dap.RunPreInitCommands()) {
-    response["success"] = false;
-    EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
-    dap.SendJSON(llvm::json::Value(std::move(response)));
-    return;
-  }
-
-  dap.PopulateExceptionBreakpoints();
-  auto cmd = dap.debugger.GetCommandInterpreter().AddMultiwordCommand(
-      "lldb-dap", "Commands for managing lldb-dap.");
-  if (GetBoolean(arguments, "supportsStartDebuggingRequest", false)) {
-    cmd.AddCommand(
-        "start-debugging", new StartDebuggingRequestHandler(dap),
-        "Sends a startDebugging request from the debug adapter to the client "
-        "to start a child debug session of the same type as the caller.");
-  }
-  cmd.AddCommand(
-      "repl-mode", new ReplModeRequestHandler(dap),
-      "Get or set the repl behavior of lldb-dap evaluation requests.");
-  cmd.AddCommand("send-event", new SendEventRequestHandler(dap),
-                 "Sends an DAP event to the client.");
-
-  dap.progress_event_thread =
-      std::thread(ProgressEventThreadFunction, std::ref(dap));
-
-  // Start our event thread so we can receive events from the debugger, target,
-  // process and more.
-  dap.event_thread = std::thread(EventThreadFunction, std::ref(dap));
-
-  // The debug adapter supports the configurationDoneRequest.
-  body.try_emplace("supportsConfigurationDoneRequest", true);
-  // The debug adapter supports function breakpoints.
-  body.try_emplace("supportsFunctionBreakpoints", true);
-  // The debug adapter supports conditional breakpoints.
-  body.try_emplace("supportsConditionalBreakpoints", true);
-  // The debug adapter supports breakpoints that break execution after a
-  // specified number of hits.
-  body.try_emplace("supportsHitConditionalBreakpoints", true);
-  // The debug adapter supports a (side effect free) evaluate request for
-  // data hovers.
-  body.try_emplace("supportsEvaluateForHovers", true);
-  // Available filters or options for the setExceptionBreakpoints request.
-  llvm::json::Array filters;
-  for (const auto &exc_bp : *dap.exception_breakpoints) {
-    filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp));
-  }
-  body.try_emplace("exceptionBreakpointFilters", std::move(filters));
-  // The debug adapter supports launching a debugee in intergrated VSCode
-  // terminal.
-  body.try_emplace("supportsRunInTerminalRequest", true);
-  // The debug adapter supports stepping back via the stepBack and
-  // reverseContinue requests.
-  body.try_emplace("supportsStepBack", false);
-  // The debug adapter supports setting a variable to a value.
-  body.try_emplace("supportsSetVariable", true);
-  // The debug adapter supports restarting a frame.
-  body.try_emplace("supportsRestartFrame", false);
-  // The debug adapter supports the gotoTargetsRequest.
-  body.try_emplace("supportsGotoTargetsRequest", false);
-  // The debug adapter supports the stepInTargetsRequest.
-  body.try_emplace("supportsStepInTargetsRequest", true);
-  // The debug adapter supports the completions request.
-  body.try_emplace("supportsCompletionsRequest", true);
-  // The debug adapter supports the disassembly request.
-  body.try_emplace("supportsDisassembleRequest", true);
-  // The debug adapter supports the `breakpointLocations` request.
-  body.try_emplace("supportsBreakpointLocationsRequest", true);
-  // The debug adapter supports stepping granularities (argument `granularity`)
-  // for the stepping requests.
-  body.try_emplace("supportsSteppingGranularity", true);
-  // The debug adapter support for instruction breakpoint.
-  body.try_emplace("supportsInstructionBreakpoints", true);
-
-  llvm::json::Array completion_characters;
-  completion_characters.emplace_back(".");
-  completion_characters.emplace_back(" ");
-  completion_characters.emplace_back("\t");
-  body.try_emplace("completionTriggerCharacters",
-                   std::move(completion_characters));
-
-  // The debug adapter supports the modules request.
-  body.try_emplace("supportsModulesRequest", true);
-  // The set of additional module information exposed by the debug adapter.
-  //   body.try_emplace("additionalModuleColumns"] = ColumnDescriptor
-  // Checksum algorithms supported by the debug adapter.
-  //   body.try_emplace("supportedChecksumAlgorithms"] = ChecksumAlgorithm
-  // The debug adapter supports the RestartRequest. In this case a client
-  // should not implement 'restart' by terminating and relaunching the adapter
-  // but by calling the RestartRequest.
-  body.try_emplace("supportsRestartRequest", true);
-  // The debug adapter supports 'exceptionOptions' on the
-  // setExceptionBreakpoints request.
-  body.try_emplace("supportsExceptionOptions", true);
-  // The debug adapter supports a 'format' attribute on the stackTraceRequest,
-  // variablesRequest, and evaluateRequest.
-  body.try_emplace("supportsValueFormattingOptions", true);
-  // The debug adapter supports the exceptionInfo request.
-  body.try_emplace("supportsExceptionInfoRequest", true);
-  // The debug adapter supports the 'terminateDebuggee' attribute on the
-  // 'disconnect' request.
-  body.try_emplace("supportTerminateDebuggee", true);
-  // The debug adapter supports the delayed loading of parts of the stack,
-  // which requires that both the 'startFrame' and 'levels' arguments and the
-  // 'totalFrames' result of the 'StackTrace' request are supported.
-  body.try_emplace("supportsDelayedStackTraceLoading", true);
-  // The debug adapter supports the 'loadedSources' request.
-  body.try_emplace("supportsLoadedSourcesRequest", false);
-  // The debug adapter supports sending progress reporting events.
-  body.try_emplace("supportsProgressReporting", true);
-  // The debug adapter supports 'logMessage' in breakpoint.
-  body.try_emplace("supportsLogPoints", true);
-  // The debug adapter supports data watchpoints.
-  body.try_emplace("supportsDataBreakpoints", true);
-  // The debug adapter supports the `readMemory` request.
-  body.try_emplace("supportsReadMemoryRequest", true);
-
-  // Put in non-DAP specification lldb specific information.
-  llvm::json::Object lldb_json;
-  lldb_json.try_emplace("version", dap.debugger.GetVersionString());
-  body.try_emplace("__lldb", std::move(lldb_json));
-
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
 llvm::Error request_runInTerminal(DAP &dap,
                                   const llvm::json::Object &launch_request,
                                   const uint64_t timeout_seconds) {
@@ -3680,8 +3248,8 @@ void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<DisconnectRequestHandler>();
   dap.RegisterRequest<EvaluateRequestHandler>();
   dap.RegisterRequest<ExceptionInfoRequestHandler>();
+  dap.RegisterRequest<InitializeRequestHandler>();
 
-  dap.RegisterRequestCallback("initialize", request_initialize);
   dap.RegisterRequestCallback("launch", request_launch);
   dap.RegisterRequestCallback("next", request_next);
   dap.RegisterRequestCallback("pause", request_pause);



More information about the lldb-commits mailing list