[lldb] Add tab-mode to autosuggestion feature (#206728)

This commit is contained in:
Raphael Isemann 2026-06-30 16:42:24 +01:00 committed by GitHub
parent 9ca7d24df3
commit 2902b031f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 129 additions and 8 deletions

View file

@ -349,7 +349,7 @@ public:
llvm::StringRef GetDisabledAnsiSuffix() const;
bool GetUseAutosuggestion() const;
AutosuggestionMode GetAutosuggestionMode() const;
llvm::StringRef GetAutosuggestionAnsiPrefix() const;

View file

@ -257,6 +257,17 @@ enum class IterationAction {
Stop,
};
/// Controls how the `show-autosuggestion` setting drives inline suggestions
/// in the interactive command line.
enum AutosuggestionMode {
/// Do not show any autosuggestion.
eAutosuggestionOff = 0,
/// Show a suggestion sourced from previously entered commands.
eAutosuggestionOn = 1,
/// Show the prefix that tab completion would insert for the current line.
eAutosuggestionTabMode = 2,
};
/// Specifies the type of PCs when creating a `HistoryThread`.
/// - `Returns` - Usually, when LLDB unwinds the stack or we retrieve a stack
/// trace via `backtrace()` we are collecting return addresses (except for the

View file

@ -260,10 +260,11 @@ let Definition = "debugger", Path = "" in {
Global,
DefaultStringValue<"frame #${frame.index}: {${ansi.fg.cyan}${frame.pc}${ansi.normal} }{${module.file.basename}{`}}{${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">,
Desc<"The default frame format string to use when displaying stack frame information for threads from thread backtrace unique.">;
def ShowAutosuggestion: Property<"show-autosuggestion", "Boolean">,
def ShowAutosuggestion: Property<"show-autosuggestion", "Enum">,
Global,
DefaultFalse,
Desc<"If true, LLDB will show suggestions to complete the command the user typed. Suggestions may be accepted using Ctrl-F.">;
DefaultEnumValue<"eAutosuggestionOff">,
EnumValues<"OptionEnumValues(g_show_autosuggestion_enum_values)">,
Desc<"Controls whether LLDB shows suggestions to complete the command the user typed. Suggestions may be accepted using Ctrl-F. Use 'true' to suggest from previously entered commands, 'tab-mode' to suggest the prefix that would be inserted by tab completion, or 'false' to disable suggestions.">;
def ShowAutosuggestionAnsiPrefix: Property<"show-autosuggestion-ansi-prefix", "String">,
Global,
DefaultStringValue<"${ansi.faint}">,

View file

@ -195,6 +195,25 @@ static constexpr OptionEnumValueElement s_stop_show_column_values[] = {
},
};
static constexpr OptionEnumValueElement g_show_autosuggestion_enum_values[] = {
{
eAutosuggestionOff,
"false",
"Do not show any autosuggestion.",
},
{
eAutosuggestionOn,
"true",
"Show a suggestion sourced from previously entered commands.",
},
{
eAutosuggestionTabMode,
"tab-mode",
"Show the prefix that tab completion would insert for the current "
"line.",
},
};
#define LLDB_PROPERTIES_debugger
#include "CoreProperties.inc"
@ -600,10 +619,11 @@ bool Debugger::SetSeparator(llvm::StringRef s) {
return ret;
}
bool Debugger::GetUseAutosuggestion() const {
AutosuggestionMode Debugger::GetAutosuggestionMode() const {
const uint32_t idx = ePropertyShowAutosuggestion;
return GetPropertyAtIndexAs<bool>(
idx, g_debugger_properties[idx].default_uint_value != 0);
return GetPropertyAtIndexAs<AutosuggestionMode>(
idx, static_cast<AutosuggestionMode>(
g_debugger_properties[idx].default_uint_value));
}
llvm::StringRef Debugger::GetAutosuggestionAnsiPrefix() const {

View file

@ -266,7 +266,7 @@ IOHandlerEditline::IOHandlerEditline(
});
m_editline_up->SetRedrawCallback([this]() { this->RedrawCallback(); });
if (debugger.GetUseAutosuggestion()) {
if (debugger.GetAutosuggestionMode() != eAutosuggestionOff) {
m_editline_up->SetSuggestionCallback([this](llvm::StringRef line) {
return this->SuggestionCallback(line);
});
@ -440,6 +440,20 @@ int IOHandlerEditline::FixIndentationCallback(Editline *editline,
std::optional<std::string>
IOHandlerEditline::SuggestionCallback(llvm::StringRef line) {
// In tab-mode, we just display what tab would complete if the user
// would tab.
if (m_debugger.GetAutosuggestionMode() == eAutosuggestionTabMode) {
CompletionResult result;
CompletionRequest request(line, line.size(), result);
m_delegate.IOHandlerComplete(*this, request);
StringList matches;
result.GetMatches(matches);
std::string longest_prefix = matches.LongestCommonPrefix();
llvm::StringRef cursor_arg_prefix = request.GetCursorArgumentPrefix();
if (longest_prefix.size() > cursor_arg_prefix.size())
return longest_prefix.substr(cursor_arg_prefix.size());
return std::nullopt;
}
return m_delegate.IOHandlerSuggestion(*this, line);
}

View file

@ -154,3 +154,78 @@ class TestCase(PExpectTest):
self.child.expect_exact(self.ANSI_RED + "ame variable" + self.ANSI_CYAN)
self.child.send("\n")
@skipIfAsan
@skipIfEditlineSupportMissing
def test_autosuggestion_tab_mode(self):
"""Test that show-autosuggestion=tab-mode suggests the prefix that
tab completion would insert, instead of consulting command history."""
self.launch(
use_colors=True,
extra_args=[
"-o",
"settings set show-autosuggestion tab-mode",
"-o",
"settings set use-color true",
],
)
ctrl_f = "\x06"
# Put 'help frame' into the session history. If tab-mode incorrectly
# consulted history (as the 'true' mode does), typing 'hel' would
# suggest 'p frame'. In tab-mode the suggestion should be just 'p',
# since the only tab completion of 'hel' is 'help'.
frame_output_needle = "Syntax: frame <subcommand>"
self.expect("help frame", substrs=[frame_output_needle])
self.child.send("hel")
# Note that the ANSI_RESET color prevents us from accepting a longer
# suggestion as a valid test outcome.
self.child.expect_exact(
cursor_horizontal_abs("(lldb) he")
+ "l"
+ self.ANSI_FAINT
+ "p"
+ self.ANSI_RESET
)
# Applying the suggestion with Ctrl-F should leave the line as 'help'
# (not 'help frame'). Running it lists all commands.
self.child.send(ctrl_f + "\n")
self.child.expect_exact("Debugger commands:")
# Tab completion must still work in tab-mode. Pressing tab on 'hel'
# should complete to 'help'; running it lists all commands.
self.child.send("hel\t\n")
self.child.expect_exact("Debugger commands:")
@skipIfAsan
@skipIfEditlineSupportMissing
def test_autosuggestion_tab_mode_no_common_prefix(self):
"""Test that show-autosuggestion=tab-mode shows no suggestion when
the tab completions don't share a prefix beyond what the user typed."""
self.launch(
use_colors=True,
extra_args=[
"-o",
"settings set show-autosuggestion tab-mode",
"-o",
"settings set use-color true",
],
)
ctrl_f = "\x06"
# 'se' matches several top-level commands (session, settings, ...)
# whose longest common prefix is 'se' itself, so tab-mode must not
# show any suggestion. Pressing Ctrl-F should therefore do nothing
# and running the line should yield the 'ambiguous command' error.
self.child.send("se" + ctrl_f + "\n")
self.child.expect_exact("ambiguous command 'se'")
# Tab completion must still work in tab-mode even when there is no
# common prefix to suggest: pressing tab on 'se' should list the
# available completions.
self.child.send("se\t")
self.child.expect_exact("session")
self.child.expect_exact("settings")