Skip to content

Commit 1066481

Browse files
[lldb-vscode] Allow specifying a custom escape prefix for LLDB commands (#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.
1 parent d72aa10 commit 1066481

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
@@ -731,6 +731,7 @@ def request_launch(
731731
postRunCommands=None,
732732
enableAutoVariableSummaries=False,
733733
enableSyntheticChildDebugging=False,
734+
commandEscapePrefix="`",
734735
):
735736
args_dict = {"program": program}
736737
if args:
@@ -774,6 +775,7 @@ def request_launch(
774775
args_dict["postRunCommands"] = postRunCommands
775776
args_dict["enableAutoVariableSummaries"] = enableAutoVariableSummaries
776777
args_dict["enableSyntheticChildDebugging"] = enableSyntheticChildDebugging
778+
args_dict["commandEscapePrefix"] = commandEscapePrefix
777779
command_dict = {"command": "launch", "type": "request", "arguments": args_dict}
778780
response = self.send_recv(command_dict)
779781

@@ -1015,7 +1017,12 @@ def terminate(self):
10151017

10161018
class DebugAdaptorServer(DebugCommunication):
10171019
def __init__(
1018-
self, executable=None, port=None, init_commands=[], log_file=None, env=None
1020+
self,
1021+
executable=None,
1022+
port=None,
1023+
init_commands=[],
1024+
log_file=None,
1025+
env=None,
10191026
):
10201027
self.process = None
10211028
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
@@ -382,9 +382,10 @@ llvm::json::Value DAP::CreateTopLevelScopes() {
382382

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

@@ -416,7 +417,9 @@ ExpressionContext DAP::DetectExpressionContext(lldb::SBFrame &frame,
416417
if (!auto_repl_mode_collision_warning) {
417418
llvm::errs() << "Variable expression '" << text
418419
<< "' is hiding an lldb command, prefix an expression "
419-
"with ` to ensure it runs as a lldb command.\n";
420+
"with '"
421+
<< g_dap.command_escape_prefix
422+
<< "' to ensure it runs as a lldb command.\n";
420423
auto_repl_mode_collision_warning = true;
421424
}
422425
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
@@ -651,6 +651,8 @@ void request_attach(const llvm::json::Object &request) {
651651
GetBoolean(arguments, "enableAutoVariableSummaries", false);
652652
g_dap.enable_synthetic_child_debugging =
653653
GetBoolean(arguments, "enableSyntheticChildDebugging", false);
654+
g_dap.command_escape_prefix =
655+
GetString(arguments, "commandEscapePrefix", "`");
654656

655657
// This is a hack for loading DWARF in .o files on Mac where the .o files
656658
// in the debug map of the main executable have relative paths which require
@@ -1801,6 +1803,8 @@ void request_launch(const llvm::json::Object &request) {
18011803
GetBoolean(arguments, "enableAutoVariableSummaries", false);
18021804
g_dap.enable_synthetic_child_debugging =
18031805
GetBoolean(arguments, "enableSyntheticChildDebugging", false);
1806+
g_dap.command_escape_prefix =
1807+
GetString(arguments, "commandEscapePrefix", "`");
18041808

18051809
// This is a hack for loading DWARF in .o files on Mac where the .o files
18061810
// 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
@@ -250,6 +250,11 @@
250250
"type": "boolean",
251251
"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.",
252252
"default": false
253+
},
254+
"commandEscapePrefix": {
255+
"type": "string",
256+
"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.",
257+
"default": "`"
253258
}
254259
}
255260
},
@@ -339,6 +344,11 @@
339344
"type": "boolean",
340345
"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.",
341346
"default": false
347+
},
348+
"commandEscapePrefix": {
349+
"type": "string",
350+
"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.",
351+
"default": "`"
342352
}
343353
}
344354
}

0 commit comments

Comments
 (0)