mirror of
https://github.com/intel/llvm.git
synced 2026-01-16 05:32:28 +08:00
[lldb/Interpreter] Fix ambiguous partial command resolution (#101934)
This patch is a follow-up to #97263 that fix ambigous abbreviated command resolution. When multiple commands are resolved, instead of failing to pick a command to run, this patch changes to resolution logic to check if there is a single alias match and if so, it will run the alias instead of the other matches. This has as a side-effect that we don't need to make aliases for every substring of aliases to support abbrivated alias resolution. Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
This commit is contained in:
committed by
GitHub
parent
6a482972e0
commit
8334d2bfd3
@@ -168,6 +168,10 @@ is more convenient to make the basic commands unique down to a letter or two,
|
||||
and then learn these sequences than to fill the namespace with lots of aliases,
|
||||
and then have to type them all the way out.
|
||||
|
||||
If the alias abbreviation or the full alias command collides with another
|
||||
existing command, the command resolver will prefer to use the alias over any
|
||||
other command as far as there is only one alias command match.
|
||||
|
||||
However, users are free to customize LLDB's command set however they like, and
|
||||
since LLDB reads the file ``~/.lldbinit`` at startup, you can store all your
|
||||
aliases there and they will be generally available to you. Your aliases are
|
||||
|
||||
@@ -295,6 +295,10 @@ public:
|
||||
StringList *matches = nullptr,
|
||||
StringList *descriptions = nullptr) const;
|
||||
|
||||
CommandObject *
|
||||
GetAliasCommandObject(llvm::StringRef cmd, StringList *matches = nullptr,
|
||||
StringList *descriptions = nullptr) const;
|
||||
|
||||
/// Determine whether a root level, built-in command with this name exists.
|
||||
bool CommandExists(llvm::StringRef cmd) const;
|
||||
|
||||
|
||||
@@ -322,7 +322,13 @@ rather than using a positional placeholder:"
|
||||
|
||||
(lldb) command alias bl3 breakpoint set -f %1 -l 3
|
||||
|
||||
Always sets a breakpoint on line 3 of whatever file is indicated.)");
|
||||
Always sets a breakpoint on line 3 of whatever file is indicated.
|
||||
|
||||
)"
|
||||
|
||||
"If the alias abbreviation or the full alias command collides with another \
|
||||
existing command, the command resolver will prefer to use the alias over any \
|
||||
other command as far as there is only one alias command match.");
|
||||
|
||||
CommandArgumentEntry arg1;
|
||||
CommandArgumentEntry arg2;
|
||||
|
||||
@@ -520,10 +520,6 @@ void CommandInterpreter::Initialize() {
|
||||
|
||||
cmd_obj_sp = GetCommandSPExact("scripting run");
|
||||
if (cmd_obj_sp) {
|
||||
AddAlias("sc", cmd_obj_sp);
|
||||
AddAlias("scr", cmd_obj_sp);
|
||||
AddAlias("scri", cmd_obj_sp);
|
||||
AddAlias("scrip", cmd_obj_sp);
|
||||
AddAlias("script", cmd_obj_sp);
|
||||
}
|
||||
|
||||
@@ -1302,6 +1298,39 @@ CommandObject *CommandInterpreter::GetUserCommandObject(
|
||||
return {};
|
||||
}
|
||||
|
||||
CommandObject *CommandInterpreter::GetAliasCommandObject(
|
||||
llvm::StringRef cmd, StringList *matches, StringList *descriptions) const {
|
||||
auto find_exact =
|
||||
[&](const CommandObject::CommandMap &map) -> CommandObject * {
|
||||
auto found_elem = map.find(cmd.str());
|
||||
if (found_elem == map.end())
|
||||
return (CommandObject *)nullptr;
|
||||
CommandObject *exact_cmd = found_elem->second.get();
|
||||
if (!exact_cmd)
|
||||
return nullptr;
|
||||
|
||||
if (matches)
|
||||
matches->AppendString(exact_cmd->GetCommandName());
|
||||
|
||||
if (descriptions)
|
||||
descriptions->AppendString(exact_cmd->GetHelp());
|
||||
|
||||
return exact_cmd;
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
CommandObject *exact_cmd = find_exact(GetAliases());
|
||||
if (exact_cmd)
|
||||
return exact_cmd;
|
||||
|
||||
// We didn't have an exact command, so now look for partial matches.
|
||||
StringList tmp_list;
|
||||
StringList *matches_ptr = matches ? matches : &tmp_list;
|
||||
AddNamesMatchingPartialString(GetAliases(), cmd, *matches_ptr);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool CommandInterpreter::CommandExists(llvm::StringRef cmd) const {
|
||||
return m_command_dict.find(std::string(cmd)) != m_command_dict.end();
|
||||
}
|
||||
@@ -3421,6 +3450,19 @@ CommandInterpreter::ResolveCommandImpl(std::string &command_line,
|
||||
std::string next_word;
|
||||
StringList matches;
|
||||
bool done = false;
|
||||
|
||||
auto build_alias_cmd = [&](std::string &full_name) {
|
||||
revised_command_line.Clear();
|
||||
matches.Clear();
|
||||
std::string alias_result;
|
||||
cmd_obj =
|
||||
BuildAliasResult(full_name, scratch_command, alias_result, result);
|
||||
revised_command_line.Printf("%s", alias_result.c_str());
|
||||
if (cmd_obj) {
|
||||
wants_raw_input = cmd_obj->WantsRawCommandString();
|
||||
}
|
||||
};
|
||||
|
||||
while (!done) {
|
||||
char quote_char = '\0';
|
||||
std::string suffix;
|
||||
@@ -3432,14 +3474,7 @@ CommandInterpreter::ResolveCommandImpl(std::string &command_line,
|
||||
bool is_real_command =
|
||||
(!is_alias) || (cmd_obj != nullptr && !cmd_obj->IsAlias());
|
||||
if (!is_real_command) {
|
||||
matches.Clear();
|
||||
std::string alias_result;
|
||||
cmd_obj =
|
||||
BuildAliasResult(full_name, scratch_command, alias_result, result);
|
||||
revised_command_line.Printf("%s", alias_result.c_str());
|
||||
if (cmd_obj) {
|
||||
wants_raw_input = cmd_obj->WantsRawCommandString();
|
||||
}
|
||||
build_alias_cmd(full_name);
|
||||
} else {
|
||||
if (cmd_obj) {
|
||||
llvm::StringRef cmd_name = cmd_obj->GetCommandName();
|
||||
@@ -3486,21 +3521,32 @@ CommandInterpreter::ResolveCommandImpl(std::string &command_line,
|
||||
if (cmd_obj == nullptr) {
|
||||
const size_t num_matches = matches.GetSize();
|
||||
if (matches.GetSize() > 1) {
|
||||
StreamString error_msg;
|
||||
error_msg.Printf("Ambiguous command '%s'. Possible matches:\n",
|
||||
next_word.c_str());
|
||||
StringList alias_matches;
|
||||
GetAliasCommandObject(next_word, &alias_matches);
|
||||
|
||||
for (uint32_t i = 0; i < num_matches; ++i) {
|
||||
error_msg.Printf("\t%s\n", matches.GetStringAtIndex(i));
|
||||
if (alias_matches.GetSize() == 1) {
|
||||
std::string full_name;
|
||||
GetAliasFullName(alias_matches.GetStringAtIndex(0), full_name);
|
||||
build_alias_cmd(full_name);
|
||||
done = static_cast<bool>(cmd_obj);
|
||||
} else {
|
||||
StreamString error_msg;
|
||||
error_msg.Printf("Ambiguous command '%s'. Possible matches:\n",
|
||||
next_word.c_str());
|
||||
|
||||
for (uint32_t i = 0; i < num_matches; ++i) {
|
||||
error_msg.Printf("\t%s\n", matches.GetStringAtIndex(i));
|
||||
}
|
||||
result.AppendRawError(error_msg.GetString());
|
||||
}
|
||||
result.AppendRawError(error_msg.GetString());
|
||||
} else {
|
||||
// We didn't have only one match, otherwise we wouldn't get here.
|
||||
lldbassert(num_matches == 0);
|
||||
result.AppendErrorWithFormat("'%s' is not a valid command.\n",
|
||||
next_word.c_str());
|
||||
}
|
||||
return nullptr;
|
||||
if (!done)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (cmd_obj->IsMultiwordObject()) {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
Test how lldb reacts to ambiguous commands
|
||||
"""
|
||||
|
||||
import lldb
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test import lldbutil
|
||||
|
||||
|
||||
class AmbiguousCommandTestCase(TestBase):
|
||||
@no_debug_info_test
|
||||
def test_ambiguous_command_with_alias(self):
|
||||
command_interpreter = self.dbg.GetCommandInterpreter()
|
||||
self.assertTrue(command_interpreter, VALID_COMMAND_INTERPRETER)
|
||||
result = lldb.SBCommandReturnObject()
|
||||
|
||||
command_interpreter.HandleCommand(
|
||||
"command alias corefile target create -c %0", result
|
||||
)
|
||||
self.assertTrue(result.Succeeded())
|
||||
|
||||
command_interpreter.ResolveCommand("co", result)
|
||||
self.assertFalse(result.Succeeded())
|
||||
self.assertEqual(
|
||||
result.GetError(),
|
||||
"Ambiguous command 'co'. Possible matches:\n\tcommand\n\tcontinue\n\tcorefile\n",
|
||||
)
|
||||
|
||||
command_interpreter.HandleCommand("command unalias continue", result)
|
||||
self.assertTrue(result.Succeeded())
|
||||
|
||||
command_interpreter.ResolveCommand("co", result)
|
||||
self.assertTrue(result.Succeeded())
|
||||
self.assertEqual(result.GetOutput(), "target create -c %0")
|
||||
@@ -0,0 +1 @@
|
||||
cmdline
|
||||
Reference in New Issue
Block a user