brintos

brintos / llvm-project-archived public Read only

0
0
Text · 35.2 KiB · c60d303 Raw
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