915 lines · cpp
1//===-- CommandCompletions.cpp --------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/ADT/STLExtras.h"10#include "llvm/ADT/SmallString.h"11#include "llvm/ADT/StringRef.h"12#include "llvm/ADT/StringSet.h"13 14#include "lldb/Breakpoint/Watchpoint.h"15#include "lldb/Core/Module.h"16#include "lldb/Core/PluginManager.h"17#include "lldb/DataFormatters/DataVisualization.h"18#include "lldb/Host/FileSystem.h"19#include "lldb/Interpreter/CommandCompletions.h"20#include "lldb/Interpreter/CommandInterpreter.h"21#include "lldb/Interpreter/CommandObject.h"22#include "lldb/Interpreter/CommandObjectMultiword.h"23#include "lldb/Interpreter/OptionValueProperties.h"24#include "lldb/Symbol/CompileUnit.h"25#include "lldb/Symbol/Variable.h"26#include "lldb/Target/Language.h"27#include "lldb/Target/Process.h"28#include "lldb/Target/RegisterContext.h"29#include "lldb/Target/Thread.h"30#include "lldb/Utility/FileSpec.h"31#include "lldb/Utility/FileSpecList.h"32#include "lldb/Utility/StreamString.h"33#include "lldb/Utility/TildeExpressionResolver.h"34 35#include "llvm/Support/FileSystem.h"36#include "llvm/Support/Path.h"37 38using namespace lldb_private;39 40// This is the command completion callback that is used to complete the41// argument of the option it is bound to (in the OptionDefinition table42// below).43typedef void (*CompletionCallback)(CommandInterpreter &interpreter,44 CompletionRequest &request,45 // A search filter to limit the search...46 lldb_private::SearchFilter *searcher);47 48struct CommonCompletionElement {49 uint64_t type;50 CompletionCallback callback;51};52 53bool CommandCompletions::InvokeCommonCompletionCallbacks(54 CommandInterpreter &interpreter, uint32_t completion_mask,55 CompletionRequest &request, SearchFilter *searcher) {56 bool handled = false;57 58 const CommonCompletionElement common_completions[] = {59 {lldb::eNoCompletion, nullptr},60 {lldb::eSourceFileCompletion, CommandCompletions::SourceFiles},61 {lldb::eDiskFileCompletion, CommandCompletions::DiskFiles},62 {lldb::eDiskDirectoryCompletion, CommandCompletions::DiskDirectories},63 {lldb::eSymbolCompletion, CommandCompletions::Symbols},64 {lldb::eModuleCompletion, CommandCompletions::Modules},65 {lldb::eModuleUUIDCompletion, CommandCompletions::ModuleUUIDs},66 {lldb::eSettingsNameCompletion, CommandCompletions::SettingsNames},67 {lldb::ePlatformPluginCompletion,68 CommandCompletions::PlatformPluginNames},69 {lldb::eArchitectureCompletion, CommandCompletions::ArchitectureNames},70 {lldb::eVariablePathCompletion, CommandCompletions::VariablePath},71 {lldb::eRegisterCompletion, CommandCompletions::Registers},72 {lldb::eBreakpointCompletion, CommandCompletions::Breakpoints},73 {lldb::eProcessPluginCompletion, CommandCompletions::ProcessPluginNames},74 {lldb::eDisassemblyFlavorCompletion,75 CommandCompletions::DisassemblyFlavors},76 {lldb::eTypeLanguageCompletion, CommandCompletions::TypeLanguages},77 {lldb::eFrameIndexCompletion, CommandCompletions::FrameIndexes},78 {lldb::eStopHookIDCompletion, CommandCompletions::StopHookIDs},79 {lldb::eThreadIndexCompletion, CommandCompletions::ThreadIndexes},80 {lldb::eWatchpointIDCompletion, CommandCompletions::WatchPointIDs},81 {lldb::eBreakpointNameCompletion, CommandCompletions::BreakpointNames},82 {lldb::eProcessIDCompletion, CommandCompletions::ProcessIDs},83 {lldb::eProcessNameCompletion, CommandCompletions::ProcessNames},84 {lldb::eRemoteDiskFileCompletion, CommandCompletions::RemoteDiskFiles},85 {lldb::eRemoteDiskDirectoryCompletion,86 CommandCompletions::RemoteDiskDirectories},87 {lldb::eTypeCategoryNameCompletion,88 CommandCompletions::TypeCategoryNames},89 {lldb::eThreadIDCompletion, CommandCompletions::ThreadIDs},90 {lldb::eManagedPluginCompletion, CommandCompletions::ManagedPlugins},91 {lldb::eTerminatorCompletion,92 nullptr} // This one has to be last in the list.93 };94 95 for (int i = 0; request.ShouldAddCompletions(); i++) {96 if (common_completions[i].type == lldb::eTerminatorCompletion)97 break;98 else if ((common_completions[i].type & completion_mask) ==99 common_completions[i].type &&100 common_completions[i].callback != nullptr) {101 handled = true;102 common_completions[i].callback(interpreter, request, searcher);103 }104 }105 return handled;106}107 108namespace {109// The Completer class is a convenient base class for building searchers that110// go along with the SearchFilter passed to the standard Completer functions.111class Completer : public Searcher {112public:113 Completer(CommandInterpreter &interpreter, CompletionRequest &request)114 : m_interpreter(interpreter), m_request(request) {}115 116 ~Completer() override = default;117 118 CallbackReturn SearchCallback(SearchFilter &filter, SymbolContext &context,119 Address *addr) override = 0;120 121 lldb::SearchDepth GetDepth() override = 0;122 123 virtual void DoCompletion(SearchFilter *filter) = 0;124 125protected:126 CommandInterpreter &m_interpreter;127 CompletionRequest &m_request;128 129private:130 Completer(const Completer &) = delete;131 const Completer &operator=(const Completer &) = delete;132};133} // namespace134 135// SourceFileCompleter implements the source file completer136namespace {137class SourceFileCompleter : public Completer {138public:139 SourceFileCompleter(CommandInterpreter &interpreter,140 CompletionRequest &request)141 : Completer(interpreter, request) {142 FileSpec partial_spec(m_request.GetCursorArgumentPrefix());143 m_file_name = partial_spec.GetFilename().GetCString();144 m_dir_name = partial_spec.GetDirectory().GetCString();145 }146 147 lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthCompUnit; }148 149 Searcher::CallbackReturn SearchCallback(SearchFilter &filter,150 SymbolContext &context,151 Address *addr) override {152 if (context.comp_unit != nullptr) {153 const char *cur_file_name =154 context.comp_unit->GetPrimaryFile().GetFilename().GetCString();155 const char *cur_dir_name =156 context.comp_unit->GetPrimaryFile().GetDirectory().GetCString();157 158 bool match = false;159 if (m_file_name && cur_file_name &&160 strstr(cur_file_name, m_file_name) == cur_file_name)161 match = true;162 163 if (match && m_dir_name && cur_dir_name &&164 strstr(cur_dir_name, m_dir_name) != cur_dir_name)165 match = false;166 167 if (match) {168 m_matching_files.AppendIfUnique(context.comp_unit->GetPrimaryFile());169 }170 }171 return m_matching_files.GetSize() >= m_request.GetMaxNumberOfCompletionsToAdd()172 ? Searcher::eCallbackReturnStop173 : Searcher::eCallbackReturnContinue;174 }175 176 void DoCompletion(SearchFilter *filter) override {177 filter->Search(*this);178 // Now convert the filelist to completions:179 for (size_t i = 0; i < m_matching_files.GetSize(); i++) {180 m_request.AddCompletion(181 m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString());182 }183 }184 185private:186 FileSpecList m_matching_files;187 const char *m_file_name;188 const char *m_dir_name;189 190 SourceFileCompleter(const SourceFileCompleter &) = delete;191 const SourceFileCompleter &operator=(const SourceFileCompleter &) = delete;192};193} // namespace194 195static bool regex_chars(const char comp) {196 return llvm::StringRef("[](){}+.*|^$\\?").contains(comp);197}198 199namespace {200class SymbolCompleter : public Completer {201 202public:203 SymbolCompleter(CommandInterpreter &interpreter, CompletionRequest &request)204 : Completer(interpreter, request) {205 std::string regex_str;206 if (!m_request.GetCursorArgumentPrefix().empty()) {207 regex_str.append("^");208 regex_str.append(std::string(m_request.GetCursorArgumentPrefix()));209 } else {210 // Match anything since the completion string is empty211 regex_str.append(".");212 }213 std::string::iterator pos =214 find_if(regex_str.begin() + 1, regex_str.end(), regex_chars);215 while (pos < regex_str.end()) {216 pos = regex_str.insert(pos, '\\');217 pos = find_if(pos + 2, regex_str.end(), regex_chars);218 }219 m_regex = RegularExpression(regex_str);220 }221 222 lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }223 224 Searcher::CallbackReturn SearchCallback(SearchFilter &filter,225 SymbolContext &context,226 Address *addr) override {227 if (context.module_sp) {228 SymbolContextList sc_list;229 ModuleFunctionSearchOptions function_options;230 function_options.include_symbols = true;231 function_options.include_inlines = true;232 context.module_sp->FindFunctions(m_regex, function_options, sc_list);233 234 // Now add the functions & symbols to the list - only add if unique:235 for (const SymbolContext &sc : sc_list) {236 if (m_match_set.size() >= m_request.GetMaxNumberOfCompletionsToAdd())237 break;238 239 ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled);240 // Ensure that the function name matches the regex. This is more than241 // a sanity check. It is possible that the demangled function name242 // does not start with the prefix, for example when it's in an243 // anonymous namespace.244 if (!func_name.IsEmpty() && m_regex.Execute(func_name.GetStringRef()))245 m_match_set.insert(func_name);246 }247 }248 return m_match_set.size() >= m_request.GetMaxNumberOfCompletionsToAdd()249 ? Searcher::eCallbackReturnStop250 : Searcher::eCallbackReturnContinue;251 }252 253 void DoCompletion(SearchFilter *filter) override {254 filter->Search(*this);255 collection::iterator pos = m_match_set.begin(), end = m_match_set.end();256 for (pos = m_match_set.begin(); pos != end; pos++)257 m_request.AddCompletion((*pos).GetCString());258 }259 260private:261 RegularExpression m_regex;262 typedef std::set<ConstString> collection;263 collection m_match_set;264 265 SymbolCompleter(const SymbolCompleter &) = delete;266 const SymbolCompleter &operator=(const SymbolCompleter &) = delete;267};268} // namespace269 270namespace {271class ModuleCompleter : public Completer {272public:273 ModuleCompleter(CommandInterpreter &interpreter, CompletionRequest &request)274 : Completer(interpreter, request) {275 llvm::StringRef request_str = m_request.GetCursorArgumentPrefix();276 // We can match the full path, or the file name only. The full match will be277 // attempted always, the file name match only if the request does not278 // contain a path separator.279 280 // Preserve both the path as spelled by the user (used for completion) and281 // the canonical version (used for matching).282 m_spelled_path = request_str;283 m_canonical_path = FileSpec(m_spelled_path).GetPath();284 if (!m_spelled_path.empty() &&285 llvm::sys::path::is_separator(m_spelled_path.back()) &&286 !llvm::StringRef(m_canonical_path).ends_with(m_spelled_path.back())) {287 m_canonical_path += m_spelled_path.back();288 }289 290 if (llvm::find_if(request_str, [](char c) {291 return llvm::sys::path::is_separator(c);292 }) == request_str.end())293 m_file_name = request_str;294 }295 296 lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }297 298 Searcher::CallbackReturn SearchCallback(SearchFilter &filter,299 SymbolContext &context,300 Address *addr) override {301 if (context.module_sp) {302 // Attempt a full path match.303 std::string cur_path = context.module_sp->GetFileSpec().GetPath();304 llvm::StringRef cur_path_view = cur_path;305 if (cur_path_view.consume_front(m_canonical_path))306 m_request.AddCompletion((m_spelled_path + cur_path_view).str());307 308 // And a file name match.309 if (m_file_name) {310 llvm::StringRef cur_file_name =311 context.module_sp->GetFileSpec().GetFilename().GetStringRef();312 if (cur_file_name.starts_with(*m_file_name))313 m_request.AddCompletion(cur_file_name);314 }315 }316 return m_request.ShouldAddCompletions() ? Searcher::eCallbackReturnContinue317 : Searcher::eCallbackReturnStop;318 }319 320 void DoCompletion(SearchFilter *filter) override { filter->Search(*this); }321 322private:323 std::optional<llvm::StringRef> m_file_name;324 llvm::StringRef m_spelled_path;325 std::string m_canonical_path;326 327 ModuleCompleter(const ModuleCompleter &) = delete;328 const ModuleCompleter &operator=(const ModuleCompleter &) = delete;329};330} // namespace331 332void CommandCompletions::SourceFiles(CommandInterpreter &interpreter,333 CompletionRequest &request,334 SearchFilter *searcher) {335 SourceFileCompleter completer(interpreter, request);336 337 if (searcher == nullptr) {338 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();339 SearchFilterForUnconstrainedSearches null_searcher(target_sp);340 completer.DoCompletion(&null_searcher);341 } else {342 completer.DoCompletion(searcher);343 }344}345 346static void DiskFilesOrDirectories(const llvm::Twine &partial_name,347 bool only_directories,348 CompletionRequest &request,349 TildeExpressionResolver &Resolver) {350 llvm::SmallString<256> CompletionBuffer;351 llvm::SmallString<256> Storage;352 partial_name.toVector(CompletionBuffer);353 354 if (CompletionBuffer.size() >= PATH_MAX)355 return;356 357 namespace path = llvm::sys::path;358 359 llvm::StringRef SearchDir;360 llvm::StringRef PartialItem;361 362 if (CompletionBuffer.starts_with("~")) {363 llvm::StringRef Buffer = CompletionBuffer;364 size_t FirstSep =365 Buffer.find_if([](char c) { return path::is_separator(c); });366 367 llvm::StringRef Username = Buffer.take_front(FirstSep);368 llvm::StringRef Remainder;369 if (FirstSep != llvm::StringRef::npos)370 Remainder = Buffer.drop_front(FirstSep + 1);371 372 llvm::SmallString<256> Resolved;373 if (!Resolver.ResolveExact(Username, Resolved)) {374 // We couldn't resolve it as a full username. If there were no slashes375 // then this might be a partial username. We try to resolve it as such376 // but after that, we're done regardless of any matches.377 if (FirstSep == llvm::StringRef::npos) {378 llvm::StringSet<> MatchSet;379 Resolver.ResolvePartial(Username, MatchSet);380 for (const auto &S : MatchSet) {381 Resolved = S.getKey();382 path::append(Resolved, path::get_separator());383 request.AddCompletion(Resolved, "", CompletionMode::Partial);384 }385 }386 return;387 }388 389 // If there was no trailing slash, then we're done as soon as we resolve390 // the expression to the correct directory. Otherwise we need to continue391 // looking for matches within that directory.392 if (FirstSep == llvm::StringRef::npos) {393 // Make sure it ends with a separator.394 path::append(CompletionBuffer, path::get_separator());395 request.AddCompletion(CompletionBuffer, "", CompletionMode::Partial);396 return;397 }398 399 // We want to keep the form the user typed, so we special case this to400 // search in the fully resolved directory, but CompletionBuffer keeps the401 // unmodified form that the user typed.402 Storage = Resolved;403 llvm::StringRef RemainderDir = path::parent_path(Remainder);404 if (!RemainderDir.empty()) {405 // Append the remaining path to the resolved directory.406 Storage.append(path::get_separator());407 Storage.append(RemainderDir);408 }409 SearchDir = Storage;410 } else if (CompletionBuffer == path::root_directory(CompletionBuffer)) {411 SearchDir = CompletionBuffer;412 } else {413 SearchDir = path::parent_path(CompletionBuffer);414 }415 416 size_t FullPrefixLen = CompletionBuffer.size();417 418 PartialItem = path::filename(CompletionBuffer);419 420 // path::filename() will return "." when the passed path ends with a421 // directory separator or the separator when passed the disk root directory.422 // We have to filter those out, but only when the "." doesn't come from the423 // completion request itself.424 if ((PartialItem == "." || PartialItem == path::get_separator()) &&425 path::is_separator(CompletionBuffer.back()))426 PartialItem = llvm::StringRef();427 428 if (SearchDir.empty()) {429 llvm::sys::fs::current_path(Storage);430 SearchDir = Storage;431 }432 assert(!PartialItem.contains(path::get_separator()));433 434 // SearchDir now contains the directory to search in, and Prefix contains the435 // text we want to match against items in that directory.436 437 FileSystem &fs = FileSystem::Instance();438 std::error_code EC;439 llvm::vfs::directory_iterator Iter = fs.DirBegin(SearchDir, EC);440 llvm::vfs::directory_iterator End;441 for (; Iter != End && !EC && request.ShouldAddCompletions();442 Iter.increment(EC)) {443 auto &Entry = *Iter;444 llvm::ErrorOr<llvm::vfs::Status> Status = fs.GetStatus(Entry.path());445 446 if (!Status)447 continue;448 449 auto Name = path::filename(Entry.path());450 451 // Omit ".", ".."452 if (Name == "." || Name == ".." || !Name.starts_with(PartialItem))453 continue;454 455 bool is_dir = Status->isDirectory();456 457 // If it's a symlink, then we treat it as a directory as long as the target458 // is a directory.459 if (Status->isSymlink()) {460 FileSpec symlink_filespec(Entry.path());461 FileSpec resolved_filespec;462 auto error = fs.ResolveSymbolicLink(symlink_filespec, resolved_filespec);463 if (error.Success())464 is_dir = fs.IsDirectory(symlink_filespec);465 }466 467 if (only_directories && !is_dir)468 continue;469 470 // Shrink it back down so that it just has the original prefix the user471 // typed and remove the part of the name which is common to the located472 // item and what the user typed.473 CompletionBuffer.resize(FullPrefixLen);474 Name = Name.drop_front(PartialItem.size());475 CompletionBuffer.append(Name);476 477 if (is_dir) {478 path::append(CompletionBuffer, path::get_separator());479 }480 481 CompletionMode mode =482 is_dir ? CompletionMode::Partial : CompletionMode::Normal;483 request.AddCompletion(CompletionBuffer, "", mode);484 }485}486 487static void DiskFilesOrDirectories(const llvm::Twine &partial_name,488 bool only_directories, StringList &matches,489 TildeExpressionResolver &Resolver) {490 CompletionResult result;491 std::string partial_name_str = partial_name.str();492 CompletionRequest request(partial_name_str, partial_name_str.size(), result);493 DiskFilesOrDirectories(partial_name, only_directories, request, Resolver);494 result.GetMatches(matches);495}496 497static void DiskFilesOrDirectories(CompletionRequest &request,498 bool only_directories) {499 StandardTildeExpressionResolver resolver;500 DiskFilesOrDirectories(request.GetCursorArgumentPrefix(), only_directories,501 request, resolver);502}503 504void CommandCompletions::DiskFiles(CommandInterpreter &interpreter,505 CompletionRequest &request,506 SearchFilter *searcher) {507 DiskFilesOrDirectories(request, /*only_dirs*/ false);508}509 510void CommandCompletions::DiskFiles(const llvm::Twine &partial_file_name,511 StringList &matches,512 TildeExpressionResolver &Resolver) {513 DiskFilesOrDirectories(partial_file_name, false, matches, Resolver);514}515 516void CommandCompletions::DiskDirectories(CommandInterpreter &interpreter,517 CompletionRequest &request,518 SearchFilter *searcher) {519 DiskFilesOrDirectories(request, /*only_dirs*/ true);520}521 522void CommandCompletions::DiskDirectories(const llvm::Twine &partial_file_name,523 StringList &matches,524 TildeExpressionResolver &Resolver) {525 DiskFilesOrDirectories(partial_file_name, true, matches, Resolver);526}527 528void CommandCompletions::RemoteDiskFiles(CommandInterpreter &interpreter,529 CompletionRequest &request,530 SearchFilter *searcher) {531 lldb::PlatformSP platform_sp =532 interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();533 if (platform_sp)534 platform_sp->AutoCompleteDiskFileOrDirectory(request, false);535}536 537void CommandCompletions::RemoteDiskDirectories(CommandInterpreter &interpreter,538 CompletionRequest &request,539 SearchFilter *searcher) {540 lldb::PlatformSP platform_sp =541 interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();542 if (platform_sp)543 platform_sp->AutoCompleteDiskFileOrDirectory(request, true);544}545 546void CommandCompletions::Modules(CommandInterpreter &interpreter,547 CompletionRequest &request,548 SearchFilter *searcher) {549 ModuleCompleter completer(interpreter, request);550 551 if (searcher == nullptr) {552 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();553 SearchFilterForUnconstrainedSearches null_searcher(target_sp);554 completer.DoCompletion(&null_searcher);555 } else {556 completer.DoCompletion(searcher);557 }558}559 560void CommandCompletions::ModuleUUIDs(CommandInterpreter &interpreter,561 CompletionRequest &request,562 SearchFilter *searcher) {563 const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();564 if (!exe_ctx.HasTargetScope())565 return;566 567 exe_ctx.GetTargetPtr()->GetImages().ForEach(568 [&request](const lldb::ModuleSP &module) {569 StreamString strm;570 module->GetDescription(strm.AsRawOstream(),571 lldb::eDescriptionLevelInitial);572 request.TryCompleteCurrentArg(module->GetUUID().GetAsString(),573 strm.GetString());574 return IterationAction::Continue;575 });576}577 578void CommandCompletions::Symbols(CommandInterpreter &interpreter,579 CompletionRequest &request,580 SearchFilter *searcher) {581 SymbolCompleter completer(interpreter, request);582 583 if (searcher == nullptr) {584 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();585 SearchFilterForUnconstrainedSearches null_searcher(target_sp);586 completer.DoCompletion(&null_searcher);587 } else {588 completer.DoCompletion(searcher);589 }590}591 592void CommandCompletions::SettingsNames(CommandInterpreter &interpreter,593 CompletionRequest &request,594 SearchFilter *searcher) {595 // Cache the full setting name list596 static StringList g_property_names;597 if (g_property_names.GetSize() == 0) {598 // Generate the full setting name list on demand599 lldb::OptionValuePropertiesSP properties_sp(600 interpreter.GetDebugger().GetValueProperties());601 if (properties_sp) {602 StreamString strm;603 properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName);604 const std::string &str = std::string(strm.GetString());605 g_property_names.SplitIntoLines(str.c_str(), str.size());606 }607 }608 609 for (const std::string &s : g_property_names)610 request.TryCompleteCurrentArg(s);611}612 613void CommandCompletions::PlatformPluginNames(CommandInterpreter &interpreter,614 CompletionRequest &request,615 SearchFilter *searcher) {616 PluginManager::AutoCompletePlatformName(request.GetCursorArgumentPrefix(),617 request);618}619 620void CommandCompletions::ArchitectureNames(CommandInterpreter &interpreter,621 CompletionRequest &request,622 SearchFilter *searcher) {623 ArchSpec::AutoComplete(request);624}625 626void CommandCompletions::VariablePath(CommandInterpreter &interpreter,627 CompletionRequest &request,628 SearchFilter *searcher) {629 Variable::AutoComplete(interpreter.GetExecutionContext(), request);630}631 632void CommandCompletions::Registers(CommandInterpreter &interpreter,633 CompletionRequest &request,634 SearchFilter *searcher) {635 std::string reg_prefix;636 if (request.GetCursorArgumentPrefix().starts_with("$"))637 reg_prefix = "$";638 639 RegisterContext *reg_ctx =640 interpreter.GetExecutionContext().GetRegisterContext();641 if (!reg_ctx)642 return;643 644 const size_t reg_num = reg_ctx->GetRegisterCount();645 for (size_t reg_idx = 0; reg_idx < reg_num; ++reg_idx) {646 const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_idx);647 request.TryCompleteCurrentArg(reg_prefix + reg_info->name,648 reg_info->alt_name);649 }650}651 652void CommandCompletions::Breakpoints(CommandInterpreter &interpreter,653 CompletionRequest &request,654 SearchFilter *searcher) {655 lldb::TargetSP target = interpreter.GetDebugger().GetSelectedTarget();656 if (!target)657 return;658 659 const BreakpointList &breakpoints = target->GetBreakpointList();660 661 std::unique_lock<std::recursive_mutex> lock;662 target->GetBreakpointList().GetListMutex(lock);663 664 size_t num_breakpoints = breakpoints.GetSize();665 if (num_breakpoints == 0)666 return;667 668 for (size_t i = 0; i < num_breakpoints; ++i) {669 lldb::BreakpointSP bp = breakpoints.GetBreakpointAtIndex(i);670 671 StreamString s;672 bp->GetDescription(&s, lldb::eDescriptionLevelBrief);673 llvm::StringRef bp_info = s.GetString();674 675 const size_t colon_pos = bp_info.find_first_of(':');676 if (colon_pos != llvm::StringRef::npos)677 bp_info = bp_info.drop_front(colon_pos + 2);678 679 request.TryCompleteCurrentArg(std::to_string(bp->GetID()), bp_info);680 }681}682 683void CommandCompletions::BreakpointNames(CommandInterpreter &interpreter,684 CompletionRequest &request,685 SearchFilter *searcher) {686 lldb::TargetSP target = interpreter.GetDebugger().GetSelectedTarget();687 if (!target)688 return;689 690 std::vector<std::string> name_list;691 target->GetBreakpointNames(name_list);692 693 for (const std::string &name : name_list)694 request.TryCompleteCurrentArg(name);695}696 697void CommandCompletions::ProcessPluginNames(CommandInterpreter &interpreter,698 CompletionRequest &request,699 SearchFilter *searcher) {700 PluginManager::AutoCompleteProcessName(request.GetCursorArgumentPrefix(),701 request);702}703void CommandCompletions::DisassemblyFlavors(CommandInterpreter &interpreter,704 CompletionRequest &request,705 SearchFilter *searcher) {706 // Currently the only valid options for disassemble -F are default, and for707 // Intel architectures, att and intel.708 static const char *flavors[] = {"default", "att", "intel"};709 for (const char *flavor : flavors) {710 request.TryCompleteCurrentArg(flavor);711 }712}713 714void CommandCompletions::ProcessIDs(CommandInterpreter &interpreter,715 CompletionRequest &request,716 SearchFilter *searcher) {717 lldb::PlatformSP platform_sp(interpreter.GetPlatform(true));718 if (!platform_sp)719 return;720 ProcessInstanceInfoList process_infos;721 ProcessInstanceInfoMatch match_info;722 platform_sp->FindProcesses(match_info, process_infos);723 for (const ProcessInstanceInfo &info : process_infos)724 request.TryCompleteCurrentArg(std::to_string(info.GetProcessID()),725 info.GetNameAsStringRef());726}727 728void CommandCompletions::ProcessNames(CommandInterpreter &interpreter,729 CompletionRequest &request,730 SearchFilter *searcher) {731 lldb::PlatformSP platform_sp(interpreter.GetPlatform(true));732 if (!platform_sp)733 return;734 ProcessInstanceInfoList process_infos;735 ProcessInstanceInfoMatch match_info;736 platform_sp->FindProcesses(match_info, process_infos);737 for (const ProcessInstanceInfo &info : process_infos)738 request.TryCompleteCurrentArg(info.GetNameAsStringRef());739}740 741void CommandCompletions::TypeLanguages(CommandInterpreter &interpreter,742 CompletionRequest &request,743 SearchFilter *searcher) {744 for (int bit :745 Language::GetLanguagesSupportingTypeSystems().bitvector.set_bits()) {746 request.TryCompleteCurrentArg(747 Language::GetNameForLanguageType(static_cast<lldb::LanguageType>(bit)));748 }749}750 751void CommandCompletions::FrameIndexes(CommandInterpreter &interpreter,752 CompletionRequest &request,753 SearchFilter *searcher) {754 const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();755 if (!exe_ctx.HasProcessScope())756 return;757 758 lldb::ThreadSP thread_sp = exe_ctx.GetThreadSP();759 Debugger &dbg = interpreter.GetDebugger();760 const uint32_t frame_num = thread_sp->GetStackFrameCount();761 for (uint32_t i = 0; i < frame_num; ++i) {762 lldb::StackFrameSP frame_sp = thread_sp->GetStackFrameAtIndex(i);763 StreamString strm;764 // Dumping frames can be slow, allow interruption.765 if (INTERRUPT_REQUESTED(dbg, "Interrupted in frame completion"))766 break;767 frame_sp->Dump(&strm, false, true);768 request.TryCompleteCurrentArg(std::to_string(i), strm.GetString());769 }770}771 772void CommandCompletions::StopHookIDs(CommandInterpreter &interpreter,773 CompletionRequest &request,774 SearchFilter *searcher) {775 const lldb::TargetSP target_sp =776 interpreter.GetExecutionContext().GetTargetSP();777 if (!target_sp)778 return;779 780 for (auto &stophook_sp : target_sp->GetStopHooks()) {781 StreamString strm;782 // The value 11 is an offset to make the completion description looks783 // neater.784 strm.SetIndentLevel(11);785 stophook_sp->GetDescription(strm, lldb::eDescriptionLevelInitial);786 request.TryCompleteCurrentArg(std::to_string(stophook_sp->GetID()),787 strm.GetString());788 }789}790 791void CommandCompletions::ThreadIndexes(CommandInterpreter &interpreter,792 CompletionRequest &request,793 SearchFilter *searcher) {794 const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();795 if (!exe_ctx.HasProcessScope())796 return;797 798 ThreadList &threads = exe_ctx.GetProcessPtr()->GetThreadList();799 lldb::ThreadSP thread_sp;800 for (uint32_t idx = 0; (thread_sp = threads.GetThreadAtIndex(idx)); ++idx) {801 StreamString strm;802 thread_sp->GetStatus(strm, 0, 1, 1, true, /*show_hidden*/ true);803 request.TryCompleteCurrentArg(std::to_string(thread_sp->GetIndexID()),804 strm.GetString());805 }806}807 808void CommandCompletions::WatchPointIDs(CommandInterpreter &interpreter,809 CompletionRequest &request,810 SearchFilter *searcher) {811 const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();812 if (!exe_ctx.HasTargetScope())813 return;814 815 const WatchpointList &wp_list = exe_ctx.GetTargetPtr()->GetWatchpointList();816 for (lldb::WatchpointSP wp_sp : wp_list.Watchpoints()) {817 StreamString strm;818 wp_sp->Dump(&strm);819 request.TryCompleteCurrentArg(std::to_string(wp_sp->GetID()),820 strm.GetString());821 }822}823 824void CommandCompletions::TypeCategoryNames(CommandInterpreter &interpreter,825 CompletionRequest &request,826 SearchFilter *searcher) {827 DataVisualization::Categories::ForEach(828 [&request](const lldb::TypeCategoryImplSP &category_sp) {829 request.TryCompleteCurrentArg(category_sp->GetName(),830 category_sp->GetDescription());831 return true;832 });833}834 835void CommandCompletions::ThreadIDs(CommandInterpreter &interpreter,836 CompletionRequest &request,837 SearchFilter *searcher) {838 const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();839 if (!exe_ctx.HasProcessScope())840 return;841 842 ThreadList &threads = exe_ctx.GetProcessPtr()->GetThreadList();843 lldb::ThreadSP thread_sp;844 for (uint32_t idx = 0; (thread_sp = threads.GetThreadAtIndex(idx)); ++idx) {845 StreamString strm;846 thread_sp->GetStatus(strm, 0, 1, 1, true, /*show_hidden*/ true);847 request.TryCompleteCurrentArg(std::to_string(thread_sp->GetID()),848 strm.GetString());849 }850}851 852void CommandCompletions::ManagedPlugins(CommandInterpreter &interpreter,853 CompletionRequest &request,854 SearchFilter *searcher) {855 PluginManager::AutoCompletePluginName(request.GetCursorArgumentPrefix(),856 request);857}858 859void CommandCompletions::CompleteModifiableCmdPathArgs(860 CommandInterpreter &interpreter, CompletionRequest &request,861 OptionElementVector &opt_element_vector) {862 // The only arguments constitute a command path, however, there might be863 // options interspersed among the arguments, and we need to skip those. Do that864 // by copying the args vector, and just dropping all the option bits:865 Args args = request.GetParsedLine();866 std::vector<size_t> to_delete;867 for (auto &elem : opt_element_vector) {868 to_delete.push_back(elem.opt_pos);869 if (elem.opt_arg_pos != 0)870 to_delete.push_back(elem.opt_arg_pos);871 }872 sort(to_delete.begin(), to_delete.end(), std::greater<size_t>());873 for (size_t idx : to_delete)874 args.DeleteArgumentAtIndex(idx);875 876 // At this point, we should only have args, so now lookup the command up to877 // the cursor element.878 879 // There's nothing here but options. It doesn't seem very useful here to880 // dump all the commands, so just return.881 size_t num_args = args.GetArgumentCount();882 if (num_args == 0)883 return;884 885 // There's just one argument, so we should complete its name:886 StringList matches;887 if (num_args == 1) {888 interpreter.GetUserCommandObject(args.GetArgumentAtIndex(0), &matches,889 nullptr);890 request.AddCompletions(matches);891 return;892 }893 894 // There was more than one path element, lets find the containing command:895 Status error;896 CommandObjectMultiword *mwc =897 interpreter.VerifyUserMultiwordCmdPath(args, true, error);898 899 // Something was wrong somewhere along the path, but I don't think there's900 // a good way to go back and fill in the missing elements:901 if (error.Fail())902 return;903 904 // This should never happen. We already handled the case of one argument905 // above, and we can only get Success & nullptr back if there's a one-word906 // leaf.907 assert(mwc != nullptr);908 909 mwc->GetSubcommandObject(args.GetArgumentAtIndex(num_args - 1), &matches);910 if (matches.GetSize() == 0)911 return;912 913 request.AddCompletions(matches);914}915