Skip to content

Commit 8ad5f6a

Browse files
walter-erquinigoadrian-prantl
authored andcommitted
[lldb-vscode] Allow specifying a custom escape prefix for LLDB commands (llvm#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. (cherry picked from commit 1066481)
1 parent 5fc0017 commit 8ad5f6a

File tree

9 files changed

+88
-16
lines changed

9 files changed

+88
-16
lines changed

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,7 @@ def request_launch(
729729
postRunCommands=None,
730730
enableAutoVariableSummaries=False,
731731
enableSyntheticChildDebugging=False,
732+
commandEscapePrefix="`",
732733
):
733734
args_dict = {"program": program}
734735
if args:
@@ -772,6 +773,7 @@ def request_launch(
772773
args_dict["postRunCommands"] = postRunCommands
773774
args_dict["enableAutoVariableSummaries"] = enableAutoVariableSummaries
774775
args_dict["enableSyntheticChildDebugging"] = enableSyntheticChildDebugging
776+
args_dict["commandEscapePrefix"] = commandEscapePrefix
775777
command_dict = {"command": "launch", "type": "request", "arguments": args_dict}
776778
response = self.send_recv(command_dict)
777779

@@ -1013,7 +1015,12 @@ def terminate(self):
10131015

10141016
class DebugAdaptorServer(DebugCommunication):
10151017
def __init__(
1016-
self, executable=None, port=None, init_commands=[], log_file=None, env=None
1018+
self,
1019+
executable=None,
1020+
port=None,
1021+
init_commands=[],
1022+
log_file=None,
1023+
env=None,
10171024
):
10181025
self.process = None
10191026
if executable is not None:

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ def launch(
351351
postRunCommands=None,
352352
enableAutoVariableSummaries=False,
353353
enableSyntheticChildDebugging=False,
354+
commandEscapePrefix="`",
354355
):
355356
"""Sending launch request to dap"""
356357

@@ -389,6 +390,7 @@ def cleanup():
389390
postRunCommands=postRunCommands,
390391
enableAutoVariableSummaries=enableAutoVariableSummaries,
391392
enableSyntheticChildDebugging=enableSyntheticChildDebugging,
393+
commandEscapePrefix=commandEscapePrefix,
392394
)
393395

394396
if expectFailure:
@@ -425,6 +427,7 @@ def build_and_launch(
425427
lldbDAPEnv=None,
426428
enableAutoVariableSummaries=False,
427429
enableSyntheticChildDebugging=False,
430+
commandEscapePrefix="`",
428431
):
429432
"""Build the default Makefile target, create the DAP debug adaptor,
430433
and launch the process.
@@ -455,4 +458,5 @@ def build_and_launch(
455458
postRunCommands=postRunCommands,
456459
enableAutoVariableSummaries=enableAutoVariableSummaries,
457460
enableSyntheticChildDebugging=enableSyntheticChildDebugging,
461+
commandEscapePrefix=commandEscapePrefix,
458462
)

lldb/test/API/tools/lldb-dap/console/TestDAP_console.py

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@
33
"""
44

55
import dap_server
6+
import lldbdap_testcase
7+
from lldbsuite.test import lldbutil
68
from lldbsuite.test.decorators import *
79
from lldbsuite.test.lldbtest import *
8-
from lldbsuite.test import lldbutil
9-
import lldbdap_testcase
1010

1111

1212
class TestDAP_console(lldbdap_testcase.DAPTestCaseBase):
13-
def check_lldb_command(self, lldb_command, contains_string, assert_msg):
13+
def check_lldb_command(
14+
self, lldb_command, contains_string, assert_msg, command_escape_prefix="`"
15+
):
1416
response = self.dap_server.request_evaluate(
15-
"`%s" % (lldb_command), context="repl"
17+
f"{command_escape_prefix}{lldb_command}", context="repl"
1618
)
1719
output = response["body"]["result"]
1820
self.assertIn(
@@ -68,3 +70,37 @@ def test_scopes_variables_setVariable_evaluate(self):
6870
# currently selected frame.
6971

7072
self.check_lldb_command("frame select", "frame #1", "frame 1 is selected")
73+
74+
@skipIfWindows
75+
@skipIfRemote
76+
def test_custom_escape_prefix(self):
77+
program = self.getBuildArtifact("a.out")
78+
self.build_and_launch(program, commandEscapePrefix="::")
79+
source = "main.cpp"
80+
breakpoint1_line = line_number(source, "// breakpoint 1")
81+
breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line])
82+
self.continue_to_breakpoints(breakpoint_ids)
83+
84+
self.check_lldb_command(
85+
"help",
86+
"For more information on any command",
87+
"Help can be invoked",
88+
command_escape_prefix="::",
89+
)
90+
91+
@skipIfWindows
92+
@skipIfRemote
93+
def test_empty_escape_prefix(self):
94+
program = self.getBuildArtifact("a.out")
95+
self.build_and_launch(program, commandEscapePrefix="")
96+
source = "main.cpp"
97+
breakpoint1_line = line_number(source, "// breakpoint 1")
98+
breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line])
99+
self.continue_to_breakpoints(breakpoint_ids)
100+
101+
self.check_lldb_command(
102+
"help",
103+
"For more information on any command",
104+
"Help can be invoked",
105+
command_escape_prefix="",
106+
)

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -384,9 +384,10 @@ llvm::json::Value DAP::CreateTopLevelScopes() {
384384

385385
ExpressionContext DAP::DetectExpressionContext(lldb::SBFrame &frame,
386386
std::string &text) {
387-
// Include ` as an escape hatch.
388-
if (!text.empty() && text[0] == '`') {
389-
text = text.substr(1);
387+
// Include the escape hatch prefix.
388+
if (!text.empty() &&
389+
llvm::StringRef(text).starts_with(g_dap.command_escape_prefix)) {
390+
text = text.substr(g_dap.command_escape_prefix.size());
390391
return ExpressionContext::Command;
391392
}
392393

@@ -418,7 +419,9 @@ ExpressionContext DAP::DetectExpressionContext(lldb::SBFrame &frame,
418419
if (!auto_repl_mode_collision_warning) {
419420
llvm::errs() << "Variable expression '" << text
420421
<< "' is hiding an lldb command, prefix an expression "
421-
"with ` to ensure it runs as a lldb command.\n";
422+
"with '"
423+
<< g_dap.command_escape_prefix
424+
<< "' to ensure it runs as a lldb command.\n";
422425
auto_repl_mode_collision_warning = true;
423426
}
424427
return ExpressionContext::Variable;

lldb/tools/lldb-dap/DAP.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ struct DAP {
188188
ReplModeRequestHandler repl_mode_request_handler;
189189
ReplMode repl_mode;
190190
bool auto_repl_mode_collision_warning;
191+
std::string command_escape_prefix = "`";
191192

192193
DAP();
193194
~DAP();

lldb/tools/lldb-dap/JSONUtils.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,17 @@ llvm::StringRef GetAsString(const llvm::json::Value &value) {
4646
}
4747

4848
// Gets a string from a JSON object using the key, or returns an empty string.
49-
llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key) {
49+
llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key,
50+
llvm::StringRef defaultValue) {
5051
if (std::optional<llvm::StringRef> value = obj.getString(key))
5152
return *value;
52-
return llvm::StringRef();
53+
return defaultValue;
5354
}
5455

55-
llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key) {
56+
llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key,
57+
llvm::StringRef defaultValue) {
5658
if (obj == nullptr)
57-
return llvm::StringRef();
59+
return defaultValue;
5860
return GetString(*obj, key);
5961
}
6062

lldb/tools/lldb-dap/JSONUtils.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,17 @@ llvm::StringRef GetAsString(const llvm::json::Value &value);
5252
/// \param[in] key
5353
/// The key to use when extracting the value
5454
///
55+
/// \param[in] defaultValue
56+
/// The default value to return if the key is not present
57+
///
5558
/// \return
5659
/// A llvm::StringRef that contains the string value for the
57-
/// specified \a key, or an empty string if there is no key that
60+
/// specified \a key, or the default value if there is no key that
5861
/// matches or if the value is not a string.
59-
llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key);
60-
llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key);
62+
llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key,
63+
llvm::StringRef defaultValue = {});
64+
llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key,
65+
llvm::StringRef defaultValue = {});
6166

6267
/// Extract the unsigned integer value for the specified key from
6368
/// the specified object.

lldb/tools/lldb-dap/lldb-dap.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,8 @@ void request_attach(const llvm::json::Object &request) {
649649
GetBoolean(arguments, "enableAutoVariableSummaries", false);
650650
g_dap.enable_synthetic_child_debugging =
651651
GetBoolean(arguments, "enableSyntheticChildDebugging", false);
652+
g_dap.command_escape_prefix =
653+
GetString(arguments, "commandEscapePrefix", "`");
652654

653655
// This is a hack for loading DWARF in .o files on Mac where the .o files
654656
// in the debug map of the main executable have relative paths which require
@@ -1799,6 +1801,8 @@ void request_launch(const llvm::json::Object &request) {
17991801
GetBoolean(arguments, "enableAutoVariableSummaries", false);
18001802
g_dap.enable_synthetic_child_debugging =
18011803
GetBoolean(arguments, "enableSyntheticChildDebugging", false);
1804+
g_dap.command_escape_prefix =
1805+
GetString(arguments, "commandEscapePrefix", "`");
18021806

18031807
// This is a hack for loading DWARF in .o files on Mac where the .o files
18041808
// in the debug map of the main executable have relative paths which require

lldb/tools/lldb-dap/package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,11 @@
246246
"type": "boolean",
247247
"description": "If a variable is displayed using a synthetic children, also display the actual contents of the variable at the end under a [raw] entry. This is useful when creating sythetic child plug-ins as it lets you see the actual contents of the variable.",
248248
"default": false
249+
},
250+
"commandEscapePrefix": {
251+
"type": "string",
252+
"description": "The escape prefix to use for executing regular LLDB commands in the Debug Console, instead of printing variables. Defaults to a back-tick (`). If it's an empty string, then all expression in the Debug Console are treated as regular LLDB commands.",
253+
"default": "`"
249254
}
250255
}
251256
},
@@ -335,6 +340,11 @@
335340
"type": "boolean",
336341
"description": "If a variable is displayed using a synthetic children, also display the actual contents of the variable at the end under a [raw] entry. This is useful when creating sythetic child plug-ins as it lets you see the actual contents of the variable.",
337342
"default": false
343+
},
344+
"commandEscapePrefix": {
345+
"type": "string",
346+
"description": "The escape prefix character to use for executing regular LLDB commands in the Debug Console, instead of printing variables. Defaults to a back-tick (`). If empty, then all expression in the Debug Console are treated as regular LLDB commands.",
347+
"default": "`"
338348
}
339349
}
340350
}

0 commit comments

Comments
 (0)