Path: blob/main/contrib/llvm-project/lldb/source/Commands/CommandCompletions.cpp
39587 views
//===-- CommandCompletions.cpp --------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/ADT/STLExtras.h"9#include "llvm/ADT/SmallString.h"10#include "llvm/ADT/StringRef.h"11#include "llvm/ADT/StringSet.h"1213#include "lldb/Breakpoint/Watchpoint.h"14#include "lldb/Core/Module.h"15#include "lldb/Core/PluginManager.h"16#include "lldb/DataFormatters/DataVisualization.h"17#include "lldb/Host/FileSystem.h"18#include "lldb/Interpreter/CommandCompletions.h"19#include "lldb/Interpreter/CommandInterpreter.h"20#include "lldb/Interpreter/CommandObject.h"21#include "lldb/Interpreter/CommandObjectMultiword.h"22#include "lldb/Interpreter/OptionValueProperties.h"23#include "lldb/Symbol/CompileUnit.h"24#include "lldb/Symbol/Variable.h"25#include "lldb/Target/Language.h"26#include "lldb/Target/Process.h"27#include "lldb/Target/RegisterContext.h"28#include "lldb/Target/Thread.h"29#include "lldb/Utility/FileSpec.h"30#include "lldb/Utility/FileSpecList.h"31#include "lldb/Utility/StreamString.h"32#include "lldb/Utility/TildeExpressionResolver.h"3334#include "llvm/Support/FileSystem.h"35#include "llvm/Support/Path.h"3637using namespace lldb_private;3839// This is the command completion callback that is used to complete the40// argument of the option it is bound to (in the OptionDefinition table41// below).42typedef void (*CompletionCallback)(CommandInterpreter &interpreter,43CompletionRequest &request,44// A search filter to limit the search...45lldb_private::SearchFilter *searcher);4647struct CommonCompletionElement {48uint64_t type;49CompletionCallback callback;50};5152bool CommandCompletions::InvokeCommonCompletionCallbacks(53CommandInterpreter &interpreter, uint32_t completion_mask,54CompletionRequest &request, SearchFilter *searcher) {55bool handled = false;5657const CommonCompletionElement common_completions[] = {58{lldb::eNoCompletion, nullptr},59{lldb::eSourceFileCompletion, CommandCompletions::SourceFiles},60{lldb::eDiskFileCompletion, CommandCompletions::DiskFiles},61{lldb::eDiskDirectoryCompletion, CommandCompletions::DiskDirectories},62{lldb::eSymbolCompletion, CommandCompletions::Symbols},63{lldb::eModuleCompletion, CommandCompletions::Modules},64{lldb::eModuleUUIDCompletion, CommandCompletions::ModuleUUIDs},65{lldb::eSettingsNameCompletion, CommandCompletions::SettingsNames},66{lldb::ePlatformPluginCompletion,67CommandCompletions::PlatformPluginNames},68{lldb::eArchitectureCompletion, CommandCompletions::ArchitectureNames},69{lldb::eVariablePathCompletion, CommandCompletions::VariablePath},70{lldb::eRegisterCompletion, CommandCompletions::Registers},71{lldb::eBreakpointCompletion, CommandCompletions::Breakpoints},72{lldb::eProcessPluginCompletion, CommandCompletions::ProcessPluginNames},73{lldb::eDisassemblyFlavorCompletion,74CommandCompletions::DisassemblyFlavors},75{lldb::eTypeLanguageCompletion, CommandCompletions::TypeLanguages},76{lldb::eFrameIndexCompletion, CommandCompletions::FrameIndexes},77{lldb::eStopHookIDCompletion, CommandCompletions::StopHookIDs},78{lldb::eThreadIndexCompletion, CommandCompletions::ThreadIndexes},79{lldb::eWatchpointIDCompletion, CommandCompletions::WatchPointIDs},80{lldb::eBreakpointNameCompletion, CommandCompletions::BreakpointNames},81{lldb::eProcessIDCompletion, CommandCompletions::ProcessIDs},82{lldb::eProcessNameCompletion, CommandCompletions::ProcessNames},83{lldb::eRemoteDiskFileCompletion, CommandCompletions::RemoteDiskFiles},84{lldb::eRemoteDiskDirectoryCompletion,85CommandCompletions::RemoteDiskDirectories},86{lldb::eTypeCategoryNameCompletion,87CommandCompletions::TypeCategoryNames},88{lldb::eThreadIDCompletion, CommandCompletions::ThreadIDs},89{lldb::eTerminatorCompletion,90nullptr} // This one has to be last in the list.91};9293for (int i = 0;; i++) {94if (common_completions[i].type == lldb::eTerminatorCompletion)95break;96else if ((common_completions[i].type & completion_mask) ==97common_completions[i].type &&98common_completions[i].callback != nullptr) {99handled = true;100common_completions[i].callback(interpreter, request, searcher);101}102}103return handled;104}105106namespace {107// The Completer class is a convenient base class for building searchers that108// go along with the SearchFilter passed to the standard Completer functions.109class Completer : public Searcher {110public:111Completer(CommandInterpreter &interpreter, CompletionRequest &request)112: m_interpreter(interpreter), m_request(request) {}113114~Completer() override = default;115116CallbackReturn SearchCallback(SearchFilter &filter, SymbolContext &context,117Address *addr) override = 0;118119lldb::SearchDepth GetDepth() override = 0;120121virtual void DoCompletion(SearchFilter *filter) = 0;122123protected:124CommandInterpreter &m_interpreter;125CompletionRequest &m_request;126127private:128Completer(const Completer &) = delete;129const Completer &operator=(const Completer &) = delete;130};131} // namespace132133// SourceFileCompleter implements the source file completer134namespace {135class SourceFileCompleter : public Completer {136public:137SourceFileCompleter(CommandInterpreter &interpreter,138CompletionRequest &request)139: Completer(interpreter, request) {140FileSpec partial_spec(m_request.GetCursorArgumentPrefix());141m_file_name = partial_spec.GetFilename().GetCString();142m_dir_name = partial_spec.GetDirectory().GetCString();143}144145lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthCompUnit; }146147Searcher::CallbackReturn SearchCallback(SearchFilter &filter,148SymbolContext &context,149Address *addr) override {150if (context.comp_unit != nullptr) {151const char *cur_file_name =152context.comp_unit->GetPrimaryFile().GetFilename().GetCString();153const char *cur_dir_name =154context.comp_unit->GetPrimaryFile().GetDirectory().GetCString();155156bool match = false;157if (m_file_name && cur_file_name &&158strstr(cur_file_name, m_file_name) == cur_file_name)159match = true;160161if (match && m_dir_name && cur_dir_name &&162strstr(cur_dir_name, m_dir_name) != cur_dir_name)163match = false;164165if (match) {166m_matching_files.AppendIfUnique(context.comp_unit->GetPrimaryFile());167}168}169return Searcher::eCallbackReturnContinue;170}171172void DoCompletion(SearchFilter *filter) override {173filter->Search(*this);174// Now convert the filelist to completions:175for (size_t i = 0; i < m_matching_files.GetSize(); i++) {176m_request.AddCompletion(177m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString());178}179}180181private:182FileSpecList m_matching_files;183const char *m_file_name;184const char *m_dir_name;185186SourceFileCompleter(const SourceFileCompleter &) = delete;187const SourceFileCompleter &operator=(const SourceFileCompleter &) = delete;188};189} // namespace190191static bool regex_chars(const char comp) {192return llvm::StringRef("[](){}+.*|^$\\?").contains(comp);193}194195namespace {196class SymbolCompleter : public Completer {197198public:199SymbolCompleter(CommandInterpreter &interpreter, CompletionRequest &request)200: Completer(interpreter, request) {201std::string regex_str;202if (!m_request.GetCursorArgumentPrefix().empty()) {203regex_str.append("^");204regex_str.append(std::string(m_request.GetCursorArgumentPrefix()));205} else {206// Match anything since the completion string is empty207regex_str.append(".");208}209std::string::iterator pos =210find_if(regex_str.begin() + 1, regex_str.end(), regex_chars);211while (pos < regex_str.end()) {212pos = regex_str.insert(pos, '\\');213pos = find_if(pos + 2, regex_str.end(), regex_chars);214}215m_regex = RegularExpression(regex_str);216}217218lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }219220Searcher::CallbackReturn SearchCallback(SearchFilter &filter,221SymbolContext &context,222Address *addr) override {223if (context.module_sp) {224SymbolContextList sc_list;225ModuleFunctionSearchOptions function_options;226function_options.include_symbols = true;227function_options.include_inlines = true;228context.module_sp->FindFunctions(m_regex, function_options, sc_list);229230// Now add the functions & symbols to the list - only add if unique:231for (const SymbolContext &sc : sc_list) {232ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled);233// Ensure that the function name matches the regex. This is more than234// a sanity check. It is possible that the demangled function name235// does not start with the prefix, for example when it's in an236// anonymous namespace.237if (!func_name.IsEmpty() && m_regex.Execute(func_name.GetStringRef()))238m_match_set.insert(func_name);239}240}241return Searcher::eCallbackReturnContinue;242}243244void DoCompletion(SearchFilter *filter) override {245filter->Search(*this);246collection::iterator pos = m_match_set.begin(), end = m_match_set.end();247for (pos = m_match_set.begin(); pos != end; pos++)248m_request.AddCompletion((*pos).GetCString());249}250251private:252RegularExpression m_regex;253typedef std::set<ConstString> collection;254collection m_match_set;255256SymbolCompleter(const SymbolCompleter &) = delete;257const SymbolCompleter &operator=(const SymbolCompleter &) = delete;258};259} // namespace260261namespace {262class ModuleCompleter : public Completer {263public:264ModuleCompleter(CommandInterpreter &interpreter, CompletionRequest &request)265: Completer(interpreter, request) {266llvm::StringRef request_str = m_request.GetCursorArgumentPrefix();267// We can match the full path, or the file name only. The full match will be268// attempted always, the file name match only if the request does not269// contain a path separator.270271// Preserve both the path as spelled by the user (used for completion) and272// the canonical version (used for matching).273m_spelled_path = request_str;274m_canonical_path = FileSpec(m_spelled_path).GetPath();275if (!m_spelled_path.empty() &&276llvm::sys::path::is_separator(m_spelled_path.back()) &&277!llvm::StringRef(m_canonical_path).ends_with(m_spelled_path.back())) {278m_canonical_path += m_spelled_path.back();279}280281if (llvm::find_if(request_str, [](char c) {282return llvm::sys::path::is_separator(c);283}) == request_str.end())284m_file_name = request_str;285}286287lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }288289Searcher::CallbackReturn SearchCallback(SearchFilter &filter,290SymbolContext &context,291Address *addr) override {292if (context.module_sp) {293// Attempt a full path match.294std::string cur_path = context.module_sp->GetFileSpec().GetPath();295llvm::StringRef cur_path_view = cur_path;296if (cur_path_view.consume_front(m_canonical_path))297m_request.AddCompletion((m_spelled_path + cur_path_view).str());298299// And a file name match.300if (m_file_name) {301llvm::StringRef cur_file_name =302context.module_sp->GetFileSpec().GetFilename().GetStringRef();303if (cur_file_name.starts_with(*m_file_name))304m_request.AddCompletion(cur_file_name);305}306}307return Searcher::eCallbackReturnContinue;308}309310void DoCompletion(SearchFilter *filter) override { filter->Search(*this); }311312private:313std::optional<llvm::StringRef> m_file_name;314llvm::StringRef m_spelled_path;315std::string m_canonical_path;316317ModuleCompleter(const ModuleCompleter &) = delete;318const ModuleCompleter &operator=(const ModuleCompleter &) = delete;319};320} // namespace321322void CommandCompletions::SourceFiles(CommandInterpreter &interpreter,323CompletionRequest &request,324SearchFilter *searcher) {325SourceFileCompleter completer(interpreter, request);326327if (searcher == nullptr) {328lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();329SearchFilterForUnconstrainedSearches null_searcher(target_sp);330completer.DoCompletion(&null_searcher);331} else {332completer.DoCompletion(searcher);333}334}335336static void DiskFilesOrDirectories(const llvm::Twine &partial_name,337bool only_directories,338CompletionRequest &request,339TildeExpressionResolver &Resolver) {340llvm::SmallString<256> CompletionBuffer;341llvm::SmallString<256> Storage;342partial_name.toVector(CompletionBuffer);343344if (CompletionBuffer.size() >= PATH_MAX)345return;346347namespace path = llvm::sys::path;348349llvm::StringRef SearchDir;350llvm::StringRef PartialItem;351352if (CompletionBuffer.starts_with("~")) {353llvm::StringRef Buffer = CompletionBuffer;354size_t FirstSep =355Buffer.find_if([](char c) { return path::is_separator(c); });356357llvm::StringRef Username = Buffer.take_front(FirstSep);358llvm::StringRef Remainder;359if (FirstSep != llvm::StringRef::npos)360Remainder = Buffer.drop_front(FirstSep + 1);361362llvm::SmallString<256> Resolved;363if (!Resolver.ResolveExact(Username, Resolved)) {364// We couldn't resolve it as a full username. If there were no slashes365// then this might be a partial username. We try to resolve it as such366// but after that, we're done regardless of any matches.367if (FirstSep == llvm::StringRef::npos) {368llvm::StringSet<> MatchSet;369Resolver.ResolvePartial(Username, MatchSet);370for (const auto &S : MatchSet) {371Resolved = S.getKey();372path::append(Resolved, path::get_separator());373request.AddCompletion(Resolved, "", CompletionMode::Partial);374}375}376return;377}378379// If there was no trailing slash, then we're done as soon as we resolve380// the expression to the correct directory. Otherwise we need to continue381// looking for matches within that directory.382if (FirstSep == llvm::StringRef::npos) {383// Make sure it ends with a separator.384path::append(CompletionBuffer, path::get_separator());385request.AddCompletion(CompletionBuffer, "", CompletionMode::Partial);386return;387}388389// We want to keep the form the user typed, so we special case this to390// search in the fully resolved directory, but CompletionBuffer keeps the391// unmodified form that the user typed.392Storage = Resolved;393llvm::StringRef RemainderDir = path::parent_path(Remainder);394if (!RemainderDir.empty()) {395// Append the remaining path to the resolved directory.396Storage.append(path::get_separator());397Storage.append(RemainderDir);398}399SearchDir = Storage;400} else if (CompletionBuffer == path::root_directory(CompletionBuffer)) {401SearchDir = CompletionBuffer;402} else {403SearchDir = path::parent_path(CompletionBuffer);404}405406size_t FullPrefixLen = CompletionBuffer.size();407408PartialItem = path::filename(CompletionBuffer);409410// path::filename() will return "." when the passed path ends with a411// directory separator or the separator when passed the disk root directory.412// We have to filter those out, but only when the "." doesn't come from the413// completion request itself.414if ((PartialItem == "." || PartialItem == path::get_separator()) &&415path::is_separator(CompletionBuffer.back()))416PartialItem = llvm::StringRef();417418if (SearchDir.empty()) {419llvm::sys::fs::current_path(Storage);420SearchDir = Storage;421}422assert(!PartialItem.contains(path::get_separator()));423424// SearchDir now contains the directory to search in, and Prefix contains the425// text we want to match against items in that directory.426427FileSystem &fs = FileSystem::Instance();428std::error_code EC;429llvm::vfs::directory_iterator Iter = fs.DirBegin(SearchDir, EC);430llvm::vfs::directory_iterator End;431for (; Iter != End && !EC; Iter.increment(EC)) {432auto &Entry = *Iter;433llvm::ErrorOr<llvm::vfs::Status> Status = fs.GetStatus(Entry.path());434435if (!Status)436continue;437438auto Name = path::filename(Entry.path());439440// Omit ".", ".."441if (Name == "." || Name == ".." || !Name.starts_with(PartialItem))442continue;443444bool is_dir = Status->isDirectory();445446// If it's a symlink, then we treat it as a directory as long as the target447// is a directory.448if (Status->isSymlink()) {449FileSpec symlink_filespec(Entry.path());450FileSpec resolved_filespec;451auto error = fs.ResolveSymbolicLink(symlink_filespec, resolved_filespec);452if (error.Success())453is_dir = fs.IsDirectory(symlink_filespec);454}455456if (only_directories && !is_dir)457continue;458459// Shrink it back down so that it just has the original prefix the user460// typed and remove the part of the name which is common to the located461// item and what the user typed.462CompletionBuffer.resize(FullPrefixLen);463Name = Name.drop_front(PartialItem.size());464CompletionBuffer.append(Name);465466if (is_dir) {467path::append(CompletionBuffer, path::get_separator());468}469470CompletionMode mode =471is_dir ? CompletionMode::Partial : CompletionMode::Normal;472request.AddCompletion(CompletionBuffer, "", mode);473}474}475476static void DiskFilesOrDirectories(const llvm::Twine &partial_name,477bool only_directories, StringList &matches,478TildeExpressionResolver &Resolver) {479CompletionResult result;480std::string partial_name_str = partial_name.str();481CompletionRequest request(partial_name_str, partial_name_str.size(), result);482DiskFilesOrDirectories(partial_name, only_directories, request, Resolver);483result.GetMatches(matches);484}485486static void DiskFilesOrDirectories(CompletionRequest &request,487bool only_directories) {488StandardTildeExpressionResolver resolver;489DiskFilesOrDirectories(request.GetCursorArgumentPrefix(), only_directories,490request, resolver);491}492493void CommandCompletions::DiskFiles(CommandInterpreter &interpreter,494CompletionRequest &request,495SearchFilter *searcher) {496DiskFilesOrDirectories(request, /*only_dirs*/ false);497}498499void CommandCompletions::DiskFiles(const llvm::Twine &partial_file_name,500StringList &matches,501TildeExpressionResolver &Resolver) {502DiskFilesOrDirectories(partial_file_name, false, matches, Resolver);503}504505void CommandCompletions::DiskDirectories(CommandInterpreter &interpreter,506CompletionRequest &request,507SearchFilter *searcher) {508DiskFilesOrDirectories(request, /*only_dirs*/ true);509}510511void CommandCompletions::DiskDirectories(const llvm::Twine &partial_file_name,512StringList &matches,513TildeExpressionResolver &Resolver) {514DiskFilesOrDirectories(partial_file_name, true, matches, Resolver);515}516517void CommandCompletions::RemoteDiskFiles(CommandInterpreter &interpreter,518CompletionRequest &request,519SearchFilter *searcher) {520lldb::PlatformSP platform_sp =521interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();522if (platform_sp)523platform_sp->AutoCompleteDiskFileOrDirectory(request, false);524}525526void CommandCompletions::RemoteDiskDirectories(CommandInterpreter &interpreter,527CompletionRequest &request,528SearchFilter *searcher) {529lldb::PlatformSP platform_sp =530interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();531if (platform_sp)532platform_sp->AutoCompleteDiskFileOrDirectory(request, true);533}534535void CommandCompletions::Modules(CommandInterpreter &interpreter,536CompletionRequest &request,537SearchFilter *searcher) {538ModuleCompleter completer(interpreter, request);539540if (searcher == nullptr) {541lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();542SearchFilterForUnconstrainedSearches null_searcher(target_sp);543completer.DoCompletion(&null_searcher);544} else {545completer.DoCompletion(searcher);546}547}548549void CommandCompletions::ModuleUUIDs(CommandInterpreter &interpreter,550CompletionRequest &request,551SearchFilter *searcher) {552const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();553if (!exe_ctx.HasTargetScope())554return;555556exe_ctx.GetTargetPtr()->GetImages().ForEach(557[&request](const lldb::ModuleSP &module) {558StreamString strm;559module->GetDescription(strm.AsRawOstream(),560lldb::eDescriptionLevelInitial);561request.TryCompleteCurrentArg(module->GetUUID().GetAsString(),562strm.GetString());563return true;564});565}566567void CommandCompletions::Symbols(CommandInterpreter &interpreter,568CompletionRequest &request,569SearchFilter *searcher) {570SymbolCompleter completer(interpreter, request);571572if (searcher == nullptr) {573lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();574SearchFilterForUnconstrainedSearches null_searcher(target_sp);575completer.DoCompletion(&null_searcher);576} else {577completer.DoCompletion(searcher);578}579}580581void CommandCompletions::SettingsNames(CommandInterpreter &interpreter,582CompletionRequest &request,583SearchFilter *searcher) {584// Cache the full setting name list585static StringList g_property_names;586if (g_property_names.GetSize() == 0) {587// Generate the full setting name list on demand588lldb::OptionValuePropertiesSP properties_sp(589interpreter.GetDebugger().GetValueProperties());590if (properties_sp) {591StreamString strm;592properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName);593const std::string &str = std::string(strm.GetString());594g_property_names.SplitIntoLines(str.c_str(), str.size());595}596}597598for (const std::string &s : g_property_names)599request.TryCompleteCurrentArg(s);600}601602void CommandCompletions::PlatformPluginNames(CommandInterpreter &interpreter,603CompletionRequest &request,604SearchFilter *searcher) {605PluginManager::AutoCompletePlatformName(request.GetCursorArgumentPrefix(),606request);607}608609void CommandCompletions::ArchitectureNames(CommandInterpreter &interpreter,610CompletionRequest &request,611SearchFilter *searcher) {612ArchSpec::AutoComplete(request);613}614615void CommandCompletions::VariablePath(CommandInterpreter &interpreter,616CompletionRequest &request,617SearchFilter *searcher) {618Variable::AutoComplete(interpreter.GetExecutionContext(), request);619}620621void CommandCompletions::Registers(CommandInterpreter &interpreter,622CompletionRequest &request,623SearchFilter *searcher) {624std::string reg_prefix;625if (request.GetCursorArgumentPrefix().starts_with("$"))626reg_prefix = "$";627628RegisterContext *reg_ctx =629interpreter.GetExecutionContext().GetRegisterContext();630if (!reg_ctx)631return;632633const size_t reg_num = reg_ctx->GetRegisterCount();634for (size_t reg_idx = 0; reg_idx < reg_num; ++reg_idx) {635const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_idx);636request.TryCompleteCurrentArg(reg_prefix + reg_info->name,637reg_info->alt_name);638}639}640641void CommandCompletions::Breakpoints(CommandInterpreter &interpreter,642CompletionRequest &request,643SearchFilter *searcher) {644lldb::TargetSP target = interpreter.GetDebugger().GetSelectedTarget();645if (!target)646return;647648const BreakpointList &breakpoints = target->GetBreakpointList();649650std::unique_lock<std::recursive_mutex> lock;651target->GetBreakpointList().GetListMutex(lock);652653size_t num_breakpoints = breakpoints.GetSize();654if (num_breakpoints == 0)655return;656657for (size_t i = 0; i < num_breakpoints; ++i) {658lldb::BreakpointSP bp = breakpoints.GetBreakpointAtIndex(i);659660StreamString s;661bp->GetDescription(&s, lldb::eDescriptionLevelBrief);662llvm::StringRef bp_info = s.GetString();663664const size_t colon_pos = bp_info.find_first_of(':');665if (colon_pos != llvm::StringRef::npos)666bp_info = bp_info.drop_front(colon_pos + 2);667668request.TryCompleteCurrentArg(std::to_string(bp->GetID()), bp_info);669}670}671672void CommandCompletions::BreakpointNames(CommandInterpreter &interpreter,673CompletionRequest &request,674SearchFilter *searcher) {675lldb::TargetSP target = interpreter.GetDebugger().GetSelectedTarget();676if (!target)677return;678679std::vector<std::string> name_list;680target->GetBreakpointNames(name_list);681682for (const std::string &name : name_list)683request.TryCompleteCurrentArg(name);684}685686void CommandCompletions::ProcessPluginNames(CommandInterpreter &interpreter,687CompletionRequest &request,688SearchFilter *searcher) {689PluginManager::AutoCompleteProcessName(request.GetCursorArgumentPrefix(),690request);691}692void CommandCompletions::DisassemblyFlavors(CommandInterpreter &interpreter,693CompletionRequest &request,694SearchFilter *searcher) {695// Currently the only valid options for disassemble -F are default, and for696// Intel architectures, att and intel.697static const char *flavors[] = {"default", "att", "intel"};698for (const char *flavor : flavors) {699request.TryCompleteCurrentArg(flavor);700}701}702703void CommandCompletions::ProcessIDs(CommandInterpreter &interpreter,704CompletionRequest &request,705SearchFilter *searcher) {706lldb::PlatformSP platform_sp(interpreter.GetPlatform(true));707if (!platform_sp)708return;709ProcessInstanceInfoList process_infos;710ProcessInstanceInfoMatch match_info;711platform_sp->FindProcesses(match_info, process_infos);712for (const ProcessInstanceInfo &info : process_infos)713request.TryCompleteCurrentArg(std::to_string(info.GetProcessID()),714info.GetNameAsStringRef());715}716717void CommandCompletions::ProcessNames(CommandInterpreter &interpreter,718CompletionRequest &request,719SearchFilter *searcher) {720lldb::PlatformSP platform_sp(interpreter.GetPlatform(true));721if (!platform_sp)722return;723ProcessInstanceInfoList process_infos;724ProcessInstanceInfoMatch match_info;725platform_sp->FindProcesses(match_info, process_infos);726for (const ProcessInstanceInfo &info : process_infos)727request.TryCompleteCurrentArg(info.GetNameAsStringRef());728}729730void CommandCompletions::TypeLanguages(CommandInterpreter &interpreter,731CompletionRequest &request,732SearchFilter *searcher) {733for (int bit :734Language::GetLanguagesSupportingTypeSystems().bitvector.set_bits()) {735request.TryCompleteCurrentArg(736Language::GetNameForLanguageType(static_cast<lldb::LanguageType>(bit)));737}738}739740void CommandCompletions::FrameIndexes(CommandInterpreter &interpreter,741CompletionRequest &request,742SearchFilter *searcher) {743const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();744if (!exe_ctx.HasProcessScope())745return;746747lldb::ThreadSP thread_sp = exe_ctx.GetThreadSP();748Debugger &dbg = interpreter.GetDebugger();749const uint32_t frame_num = thread_sp->GetStackFrameCount();750for (uint32_t i = 0; i < frame_num; ++i) {751lldb::StackFrameSP frame_sp = thread_sp->GetStackFrameAtIndex(i);752StreamString strm;753// Dumping frames can be slow, allow interruption.754if (INTERRUPT_REQUESTED(dbg, "Interrupted in frame completion"))755break;756frame_sp->Dump(&strm, false, true);757request.TryCompleteCurrentArg(std::to_string(i), strm.GetString());758}759}760761void CommandCompletions::StopHookIDs(CommandInterpreter &interpreter,762CompletionRequest &request,763SearchFilter *searcher) {764const lldb::TargetSP target_sp =765interpreter.GetExecutionContext().GetTargetSP();766if (!target_sp)767return;768769const size_t num = target_sp->GetNumStopHooks();770for (size_t idx = 0; idx < num; ++idx) {771StreamString strm;772// The value 11 is an offset to make the completion description looks773// neater.774strm.SetIndentLevel(11);775const Target::StopHookSP stophook_sp = target_sp->GetStopHookAtIndex(idx);776stophook_sp->GetDescription(strm, lldb::eDescriptionLevelInitial);777request.TryCompleteCurrentArg(std::to_string(stophook_sp->GetID()),778strm.GetString());779}780}781782void CommandCompletions::ThreadIndexes(CommandInterpreter &interpreter,783CompletionRequest &request,784SearchFilter *searcher) {785const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();786if (!exe_ctx.HasProcessScope())787return;788789ThreadList &threads = exe_ctx.GetProcessPtr()->GetThreadList();790lldb::ThreadSP thread_sp;791for (uint32_t idx = 0; (thread_sp = threads.GetThreadAtIndex(idx)); ++idx) {792StreamString strm;793thread_sp->GetStatus(strm, 0, 1, 1, true);794request.TryCompleteCurrentArg(std::to_string(thread_sp->GetIndexID()),795strm.GetString());796}797}798799void CommandCompletions::WatchPointIDs(CommandInterpreter &interpreter,800CompletionRequest &request,801SearchFilter *searcher) {802const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();803if (!exe_ctx.HasTargetScope())804return;805806const WatchpointList &wp_list = exe_ctx.GetTargetPtr()->GetWatchpointList();807for (lldb::WatchpointSP wp_sp : wp_list.Watchpoints()) {808StreamString strm;809wp_sp->Dump(&strm);810request.TryCompleteCurrentArg(std::to_string(wp_sp->GetID()),811strm.GetString());812}813}814815void CommandCompletions::TypeCategoryNames(CommandInterpreter &interpreter,816CompletionRequest &request,817SearchFilter *searcher) {818DataVisualization::Categories::ForEach(819[&request](const lldb::TypeCategoryImplSP &category_sp) {820request.TryCompleteCurrentArg(category_sp->GetName(),821category_sp->GetDescription());822return true;823});824}825826void CommandCompletions::ThreadIDs(CommandInterpreter &interpreter,827CompletionRequest &request,828SearchFilter *searcher) {829const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();830if (!exe_ctx.HasProcessScope())831return;832833ThreadList &threads = exe_ctx.GetProcessPtr()->GetThreadList();834lldb::ThreadSP thread_sp;835for (uint32_t idx = 0; (thread_sp = threads.GetThreadAtIndex(idx)); ++idx) {836StreamString strm;837thread_sp->GetStatus(strm, 0, 1, 1, true);838request.TryCompleteCurrentArg(std::to_string(thread_sp->GetID()),839strm.GetString());840}841}842843void CommandCompletions::CompleteModifiableCmdPathArgs(844CommandInterpreter &interpreter, CompletionRequest &request,845OptionElementVector &opt_element_vector) {846// The only arguments constitute a command path, however, there might be847// options interspersed among the arguments, and we need to skip those. Do that848// by copying the args vector, and just dropping all the option bits:849Args args = request.GetParsedLine();850std::vector<size_t> to_delete;851for (auto &elem : opt_element_vector) {852to_delete.push_back(elem.opt_pos);853if (elem.opt_arg_pos != 0)854to_delete.push_back(elem.opt_arg_pos);855}856sort(to_delete.begin(), to_delete.end(), std::greater<size_t>());857for (size_t idx : to_delete)858args.DeleteArgumentAtIndex(idx);859860// At this point, we should only have args, so now lookup the command up to861// the cursor element.862863// There's nothing here but options. It doesn't seem very useful here to864// dump all the commands, so just return.865size_t num_args = args.GetArgumentCount();866if (num_args == 0)867return;868869// There's just one argument, so we should complete its name:870StringList matches;871if (num_args == 1) {872interpreter.GetUserCommandObject(args.GetArgumentAtIndex(0), &matches,873nullptr);874request.AddCompletions(matches);875return;876}877878// There was more than one path element, lets find the containing command:879Status error;880CommandObjectMultiword *mwc =881interpreter.VerifyUserMultiwordCmdPath(args, true, error);882883// Something was wrong somewhere along the path, but I don't think there's884// a good way to go back and fill in the missing elements:885if (error.Fail())886return;887888// This should never happen. We already handled the case of one argument889// above, and we can only get Success & nullptr back if there's a one-word890// leaf.891assert(mwc != nullptr);892893mwc->GetSubcommandObject(args.GetArgumentAtIndex(num_args - 1), &matches);894if (matches.GetSize() == 0)895return;896897request.AddCompletions(matches);898}899900901