[lldb] Add tab-mode to autosuggestion feature (#206728)
This commit is contained in:
parent
9ca7d24df3
commit
2902b031f0
6 changed files with 129 additions and 8 deletions
|
|
@ -349,7 +349,7 @@ public:
|
|||
|
||||
llvm::StringRef GetDisabledAnsiSuffix() const;
|
||||
|
||||
bool GetUseAutosuggestion() const;
|
||||
AutosuggestionMode GetAutosuggestionMode() const;
|
||||
|
||||
llvm::StringRef GetAutosuggestionAnsiPrefix() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}">,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Reference in a new issue