[Lldb-commits] [lldb] [lldb] Add tab-mode to autosuggestion feature (PR #206728)
Raphael Isemann via lldb-commits
lldb-commits at lists.llvm.org
Tue Jun 30 06:20:43 PDT 2026
https://github.com/Teemperor created https://github.com/llvm/llvm-project/pull/206728
LLDB has an autosuggestion feature that shows previous commands that start with the current command line invocation. These commands are shown in a faint color after the command line.
As an alternative to this mode, this patch adds a tab-mode to the autosuggestions. We no longer show previous commands, but instead just show what the tab-completion would complete if the user pressed tab.
This makes LLDB's command line a bit less frustrating to explore for users that don't want to enable the full autosuggestion feature.
>From af39117694c1f360f2cdffe1df21ad567972eb3d Mon Sep 17 00:00:00 2001
From: Raphael Isemann <rise at apple.com>
Date: Tue, 30 Jun 2026 13:32:52 +0100
Subject: [PATCH] [lldb] Add tab-mode to autosuggestion feature
LLDB has an autosuggestion feature that shows previous commands that
start with the current command line invocation. These commands are
shown in a faint color after the command line.
As an alternative to this mode, this patch adds a tab-mode to the
autosuggestions. We no longer show previous commands, but instead just
show what the tab-completion would complete if the user pressed tab.
This makes LLDB's command line a bit less frustrating to explore for
users that don't want to enable the full autosuggestion feature.
---
lldb/include/lldb/Core/Debugger.h | 2 +-
lldb/include/lldb/lldb-private-enumerations.h | 11 +++
lldb/source/Core/CoreProperties.td | 7 +-
lldb/source/Core/Debugger.cpp | 26 ++++++-
lldb/source/Core/IOHandler.cpp | 16 +++-
.../autosuggestion/TestAutosuggestion.py | 76 +++++++++++++++++++
6 files changed, 130 insertions(+), 8 deletions(-)
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index fff514fed156d..05b1bd83e32fd 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -348,7 +348,7 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
llvm::StringRef GetDisabledAnsiSuffix() const;
- bool GetUseAutosuggestion() const;
+ AutosuggestionMode GetAutosuggestionMode() const;
llvm::StringRef GetAutosuggestionAnsiPrefix() const;
diff --git a/lldb/include/lldb/lldb-private-enumerations.h b/lldb/include/lldb/lldb-private-enumerations.h
index a6965657f5bc9..f6278dbc74325 100644
--- a/lldb/include/lldb/lldb-private-enumerations.h
+++ b/lldb/include/lldb/lldb-private-enumerations.h
@@ -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
diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td
index 8cea0931868aa..97bfd4a361032 100644
--- a/lldb/source/Core/CoreProperties.td
+++ b/lldb/source/Core/CoreProperties.td
@@ -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}">,
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index b2e6fe66dc29e..413e74aa37292 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -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 {
diff --git a/lldb/source/Core/IOHandler.cpp b/lldb/source/Core/IOHandler.cpp
index 5cc15ad2ed6ff..f19fe3204ddf5 100644
--- a/lldb/source/Core/IOHandler.cpp
+++ b/lldb/source/Core/IOHandler.cpp
@@ -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);
}
diff --git a/lldb/test/API/iohandler/autosuggestion/TestAutosuggestion.py b/lldb/test/API/iohandler/autosuggestion/TestAutosuggestion.py
index 401b06f6c9b49..2c287eeeef676 100644
--- a/lldb/test/API/iohandler/autosuggestion/TestAutosuggestion.py
+++ b/lldb/test/API/iohandler/autosuggestion/TestAutosuggestion.py
@@ -154,3 +154,79 @@ def test_autosuggestion_custom_ansi_prefix_suffix(self):
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")
+
More information about the lldb-commits
mailing list