[Lldb-commits] [lldb] [lldb-vscode] Allow specifying a custom escape character for LLDB commands (PR #69238)

Walter Erquinigo via lldb-commits lldb-commits at lists.llvm.org
Mon Oct 16 12:11:57 PDT 2023


https://github.com/walter-erquinigo created https://github.com/llvm/llvm-project/pull/69238

We've been using the backtick as our escape character, however that leads to a weird experience on VS Code, because on most hosts, as soon as you type the backtick on VS Code, the IDE will introduce another backtick. As changing the default escape character might be out of question because other plugins might rely on it, we can instead introduce an option to change this variable upon lldb-vscode initialization.
FWIW, my users will be using : instead ot the backtick.


>From 5b2da88825a14d3a0825b4372e6ca7ffe4255c5e Mon Sep 17 00:00:00 2001
From: walter erquinigo <walter at modular.com>
Date: Mon, 16 Oct 2023 15:08:20 -0400
Subject: [PATCH] [lldb-vscode] Allow specifying a custom escape character for
 LLDB commands

We've been using the backtick as our escape character, however that leads to a weird experience on VS Code, because on most hosts, as soon as you type the backtick on VS Code, the IDE will introduce another backtick. As changing the default escape character might be out of question because other plugins might rely on it, we can instead introduce an option to change this variable upon lldb-vscode initialization.
FWIW, my users will be using : instead ot the backtick.
---
 .../tools/lldb-vscode/lldbvscode_testcase.py  | 10 +++++----
 .../test/tools/lldb-vscode/vscode.py          |  4 ++--
 .../lldb-vscode/console/TestVSCode_console.py | 21 +++++++++++++++----
 lldb/tools/lldb-vscode/Options.td             |  6 ++++++
 lldb/tools/lldb-vscode/VSCode.cpp             |  8 ++++---
 lldb/tools/lldb-vscode/VSCode.h               |  1 +
 lldb/tools/lldb-vscode/lldb-vscode.cpp        | 10 +++++++++
 7 files changed, 47 insertions(+), 13 deletions(-)

diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py
index 8cd4e8454c89099..1869a5579f0a12e 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py
@@ -8,7 +8,7 @@
 class VSCodeTestCaseBase(TestBase):
     NO_DEBUG_INFO_TESTCASE = True
 
-    def create_debug_adaptor(self, lldbVSCodeEnv=None):
+    def create_debug_adaptor(self, lldbVSCodeEnv=None, commandEscapeChar='`'):
         """Create the Visual Studio Code debug adaptor"""
         self.assertTrue(
             is_exe(self.lldbVSCodeExec), "lldb-vscode must exist and be executable"
@@ -16,14 +16,15 @@ def create_debug_adaptor(self, lldbVSCodeEnv=None):
         log_file_path = self.getBuildArtifact("vscode.txt")
         self.vscode = vscode.DebugAdaptor(
             executable=self.lldbVSCodeExec,
+            args=["--command-escape-character", commandEscapeChar],
             init_commands=self.setUpCommands(),
             log_file=log_file_path,
             env=lldbVSCodeEnv,
         )
 
-    def build_and_create_debug_adaptor(self, lldbVSCodeEnv=None):
+    def build_and_create_debug_adaptor(self, lldbVSCodeEnv=None, commandEscapeChar='`'):
         self.build()
-        self.create_debug_adaptor(lldbVSCodeEnv)
+        self.create_debug_adaptor(lldbVSCodeEnv, commandEscapeChar)
 
     def set_source_breakpoints(self, source_path, lines, data=None):
         """Sets source breakpoints and returns an array of strings containing
@@ -425,11 +426,12 @@ def build_and_launch(
         lldbVSCodeEnv=None,
         enableAutoVariableSummaries=False,
         enableSyntheticChildDebugging=False,
+        commandEscapeChar='`',
     ):
         """Build the default Makefile target, create the VSCode debug adaptor,
         and launch the process.
         """
-        self.build_and_create_debug_adaptor(lldbVSCodeEnv)
+        self.build_and_create_debug_adaptor(lldbVSCodeEnv, commandEscapeChar=commandEscapeChar)
         self.assertTrue(os.path.exists(program), "executable must exist")
 
         return self.launch(
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
index 5ee0800b27a5699..f4b393e91c10c6e 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
@@ -1015,7 +1015,7 @@ def terminate(self):
 
 class DebugAdaptor(DebugCommunication):
     def __init__(
-        self, executable=None, port=None, init_commands=[], log_file=None, env=None
+        self, executable=None, args=[], port=None, init_commands=[], log_file=None, env=None
     ):
         self.process = None
         if executable is not None:
@@ -1026,7 +1026,7 @@ def __init__(
             if log_file:
                 adaptor_env["LLDBVSCODE_LOG"] = log_file
             self.process = subprocess.Popen(
-                [executable],
+                [executable, *args],
                 stdin=subprocess.PIPE,
                 stdout=subprocess.PIPE,
                 stderr=subprocess.PIPE,
diff --git a/lldb/test/API/tools/lldb-vscode/console/TestVSCode_console.py b/lldb/test/API/tools/lldb-vscode/console/TestVSCode_console.py
index d28e98b37c589dd..960b6c355fefc1d 100644
--- a/lldb/test/API/tools/lldb-vscode/console/TestVSCode_console.py
+++ b/lldb/test/API/tools/lldb-vscode/console/TestVSCode_console.py
@@ -2,16 +2,16 @@
 Test lldb-vscode setBreakpoints request
 """
 
+import lldbvscode_testcase
 import vscode
+from lldbsuite.test import lldbutil
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
-from lldbsuite.test import lldbutil
-import lldbvscode_testcase
 
 
 class TestVSCode_console(lldbvscode_testcase.VSCodeTestCaseBase):
-    def check_lldb_command(self, lldb_command, contains_string, assert_msg):
-        response = self.vscode.request_evaluate("`%s" % (lldb_command), context="repl")
+    def check_lldb_command(self, lldb_command, contains_string, assert_msg, command_escape_char='`'):
+        response = self.vscode.request_evaluate(f"{command_escape_char}{lldb_command}", context="repl")
         output = response["body"]["result"]
         self.assertIn(
             contains_string,
@@ -66,3 +66,16 @@ def test_scopes_variables_setVariable_evaluate(self):
         # currently selected frame.
 
         self.check_lldb_command("frame select", "frame #1", "frame 1 is selected")
+
+
+    @skipIfWindows
+    @skipIfRemote
+    def test_custom_escape_char(self):
+        program = self.getBuildArtifact("a.out")
+        self.build_and_launch(program, commandEscapeChar=":")
+        source = "main.cpp"
+        breakpoint1_line = line_number(source, "// breakpoint 1")
+        breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line])
+        self.continue_to_breakpoints(breakpoint_ids)
+
+        self.check_lldb_command("help", "For more information on any command", "Help can be invoked", command_escape_char=':')
diff --git a/lldb/tools/lldb-vscode/Options.td b/lldb/tools/lldb-vscode/Options.td
index a6ba0a318f388a1..7e885abeb615326 100644
--- a/lldb/tools/lldb-vscode/Options.td
+++ b/lldb/tools/lldb-vscode/Options.td
@@ -43,3 +43,9 @@ def debugger_pid: S<"debugger-pid">,
 def repl_mode: S<"repl-mode">,
   MetaVarName<"<mode>">,
   HelpText<"The mode for handling repl evaluation requests, supported modes: variable, command, auto.">;
+
+def command_escape_character: S<"command-escape-character">,
+  MetaVarName<"<character>">,
+  HelpText<"The escape prefix character to use for executing regular LLDB "
+           "commands in the Debug Console, instead of printing variables. "
+           "Defaults to a back-tick (`).">;
diff --git a/lldb/tools/lldb-vscode/VSCode.cpp b/lldb/tools/lldb-vscode/VSCode.cpp
index 1384604c48371b7..7ce5932f8691e80 100644
--- a/lldb/tools/lldb-vscode/VSCode.cpp
+++ b/lldb/tools/lldb-vscode/VSCode.cpp
@@ -384,8 +384,8 @@ llvm::json::Value VSCode::CreateTopLevelScopes() {
 
 ExpressionContext VSCode::DetectExpressionContext(lldb::SBFrame &frame,
                                                   std::string &text) {
-  // Include ` as an escape hatch.
-  if (!text.empty() && text[0] == '`') {
+  // Include the escape hatch prefix.
+  if (!text.empty() && text[0] == g_vsc.command_escape_character) {
     text = text.substr(1);
     return ExpressionContext::Command;
   }
@@ -418,7 +418,9 @@ ExpressionContext VSCode::DetectExpressionContext(lldb::SBFrame &frame,
         if (!auto_repl_mode_collision_warning) {
           llvm::errs() << "Variable expression '" << text
                        << "' is hiding an lldb command, prefix an expression "
-                          "with ` to ensure it runs as a lldb command.\n";
+                          "with "
+                       << g_vsc.command_escape_character
+                       << " to ensure it runs as a lldb command.\n";
           auto_repl_mode_collision_warning = true;
         }
         return ExpressionContext::Variable;
diff --git a/lldb/tools/lldb-vscode/VSCode.h b/lldb/tools/lldb-vscode/VSCode.h
index 59bb11c71e67203..1802eeb821fd30d 100644
--- a/lldb/tools/lldb-vscode/VSCode.h
+++ b/lldb/tools/lldb-vscode/VSCode.h
@@ -188,6 +188,7 @@ struct VSCode {
   ReplModeRequestHandler repl_mode_request_handler;
   ReplMode repl_mode;
   bool auto_repl_mode_collision_warning;
+  char command_escape_character = '`';
 
   VSCode();
   ~VSCode();
diff --git a/lldb/tools/lldb-vscode/lldb-vscode.cpp b/lldb/tools/lldb-vscode/lldb-vscode.cpp
index 3904d430c49b4cd..06c7724fb66d25a 100644
--- a/lldb/tools/lldb-vscode/lldb-vscode.cpp
+++ b/lldb/tools/lldb-vscode/lldb-vscode.cpp
@@ -3806,6 +3806,16 @@ int main(int argc, char *argv[]) {
     g_vsc.output.descriptor = StreamDescriptor::from_file(new_stdout_fd, false);
   }
 
+  if (llvm::opt::Arg *arg =
+          input_args.getLastArg(OPT_command_escape_character)) {
+    if (const char *escape_char = arg->getValue()) {
+      g_vsc.command_escape_character = *escape_char;
+    } else {
+      llvm::errs() << "Missing command escape character.\n";
+      return EXIT_FAILURE;
+    }
+  }
+
   bool CleanExit = true;
   if (auto Err = g_vsc.Loop()) {
     if (g_vsc.log)



More information about the lldb-commits mailing list