diff options
Diffstat (limited to 'lldb/source/Commands/CommandCompletions.cpp')
-rw-r--r-- | lldb/source/Commands/CommandCompletions.cpp | 475 |
1 files changed, 282 insertions, 193 deletions
diff --git a/lldb/source/Commands/CommandCompletions.cpp b/lldb/source/Commands/CommandCompletions.cpp index d9bee66b442af..48df773572014 100644 --- a/lldb/source/Commands/CommandCompletions.cpp +++ b/lldb/source/Commands/CommandCompletions.cpp @@ -1,4 +1,4 @@ -//===-- CommandCompletions.cpp ----------------------------------*- C++ -*-===// +//===-- CommandCompletions.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -18,6 +18,7 @@ #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Variable.h" +#include "lldb/Target/RegisterContext.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/TildeExpressionResolver.h" @@ -27,19 +28,17 @@ using namespace lldb_private; -CommandCompletions::CommonCompletionElement - CommandCompletions::g_common_completions[] = { - {eCustomCompletion, nullptr}, - {eSourceFileCompletion, CommandCompletions::SourceFiles}, - {eDiskFileCompletion, CommandCompletions::DiskFiles}, - {eDiskDirectoryCompletion, CommandCompletions::DiskDirectories}, - {eSymbolCompletion, CommandCompletions::Symbols}, - {eModuleCompletion, CommandCompletions::Modules}, - {eSettingsNameCompletion, CommandCompletions::SettingsNames}, - {ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames}, - {eArchitectureCompletion, CommandCompletions::ArchitectureNames}, - {eVariablePathCompletion, CommandCompletions::VariablePath}, - {eNoCompletion, nullptr} // This one has to be last in the list. +// This is the command completion callback that is used to complete the +// argument of the option it is bound to (in the OptionDefinition table +// below). +typedef void (*CompletionCallback)(CommandInterpreter &interpreter, + CompletionRequest &request, + // A search filter to limit the search... + lldb_private::SearchFilter *searcher); + +struct CommonCompletionElement { + uint32_t type; + CompletionCallback callback; }; bool CommandCompletions::InvokeCommonCompletionCallbacks( @@ -47,27 +46,245 @@ bool CommandCompletions::InvokeCommonCompletionCallbacks( CompletionRequest &request, SearchFilter *searcher) { bool handled = false; - if (completion_mask & eCustomCompletion) - return false; + const CommonCompletionElement common_completions[] = { + {eSourceFileCompletion, CommandCompletions::SourceFiles}, + {eDiskFileCompletion, CommandCompletions::DiskFiles}, + {eDiskDirectoryCompletion, CommandCompletions::DiskDirectories}, + {eSymbolCompletion, CommandCompletions::Symbols}, + {eModuleCompletion, CommandCompletions::Modules}, + {eSettingsNameCompletion, CommandCompletions::SettingsNames}, + {ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames}, + {eArchitectureCompletion, CommandCompletions::ArchitectureNames}, + {eVariablePathCompletion, CommandCompletions::VariablePath}, + {eRegisterCompletion, CommandCompletions::Registers}, + {eBreakpointCompletion, CommandCompletions::Breakpoints}, + {eProcessPluginCompletion, CommandCompletions::ProcessPluginNames}, + {eNoCompletion, nullptr} // This one has to be last in the list. + }; for (int i = 0;; i++) { - if (g_common_completions[i].type == eNoCompletion) + if (common_completions[i].type == eNoCompletion) break; - else if ((g_common_completions[i].type & completion_mask) == - g_common_completions[i].type && - g_common_completions[i].callback != nullptr) { + else if ((common_completions[i].type & completion_mask) == + common_completions[i].type && + common_completions[i].callback != nullptr) { handled = true; - g_common_completions[i].callback(interpreter, request, searcher); + common_completions[i].callback(interpreter, request, searcher); } } return handled; } +namespace { +// The Completer class is a convenient base class for building searchers that +// go along with the SearchFilter passed to the standard Completer functions. +class Completer : public Searcher { +public: + Completer(CommandInterpreter &interpreter, CompletionRequest &request) + : m_interpreter(interpreter), m_request(request) {} + + ~Completer() override = default; + + CallbackReturn SearchCallback(SearchFilter &filter, SymbolContext &context, + Address *addr) override = 0; + + lldb::SearchDepth GetDepth() override = 0; + + virtual void DoCompletion(SearchFilter *filter) = 0; + +protected: + CommandInterpreter &m_interpreter; + CompletionRequest &m_request; + +private: + Completer(const Completer &) = delete; + const Completer &operator=(const Completer &) = delete; +}; +} // namespace + +// SourceFileCompleter implements the source file completer +namespace { +class SourceFileCompleter : public Completer { +public: + SourceFileCompleter(CommandInterpreter &interpreter, + CompletionRequest &request) + : Completer(interpreter, request), m_matching_files() { + FileSpec partial_spec(m_request.GetCursorArgumentPrefix()); + m_file_name = partial_spec.GetFilename().GetCString(); + m_dir_name = partial_spec.GetDirectory().GetCString(); + } + + lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthCompUnit; } + + Searcher::CallbackReturn SearchCallback(SearchFilter &filter, + SymbolContext &context, + Address *addr) override { + if (context.comp_unit != nullptr) { + const char *cur_file_name = + context.comp_unit->GetPrimaryFile().GetFilename().GetCString(); + const char *cur_dir_name = + context.comp_unit->GetPrimaryFile().GetDirectory().GetCString(); + + bool match = false; + if (m_file_name && cur_file_name && + strstr(cur_file_name, m_file_name) == cur_file_name) + match = true; + + if (match && m_dir_name && cur_dir_name && + strstr(cur_dir_name, m_dir_name) != cur_dir_name) + match = false; + + if (match) { + m_matching_files.AppendIfUnique(context.comp_unit->GetPrimaryFile()); + } + } + return Searcher::eCallbackReturnContinue; + } + + void DoCompletion(SearchFilter *filter) override { + filter->Search(*this); + // Now convert the filelist to completions: + for (size_t i = 0; i < m_matching_files.GetSize(); i++) { + m_request.AddCompletion( + m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString()); + } + } + +private: + FileSpecList m_matching_files; + const char *m_file_name; + const char *m_dir_name; + + SourceFileCompleter(const SourceFileCompleter &) = delete; + const SourceFileCompleter &operator=(const SourceFileCompleter &) = delete; +}; +} // namespace + +static bool regex_chars(const char comp) { + return llvm::StringRef("[](){}+.*|^$\\?").contains(comp); +} + +namespace { +class SymbolCompleter : public Completer { + +public: + SymbolCompleter(CommandInterpreter &interpreter, CompletionRequest &request) + : Completer(interpreter, request) { + std::string regex_str; + if (!m_request.GetCursorArgumentPrefix().empty()) { + regex_str.append("^"); + regex_str.append(std::string(m_request.GetCursorArgumentPrefix())); + } else { + // Match anything since the completion string is empty + regex_str.append("."); + } + std::string::iterator pos = + find_if(regex_str.begin() + 1, regex_str.end(), regex_chars); + while (pos < regex_str.end()) { + pos = regex_str.insert(pos, '\\'); + pos = find_if(pos + 2, regex_str.end(), regex_chars); + } + m_regex = RegularExpression(regex_str); + } + + lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; } + + Searcher::CallbackReturn SearchCallback(SearchFilter &filter, + SymbolContext &context, + Address *addr) override { + if (context.module_sp) { + SymbolContextList sc_list; + const bool include_symbols = true; + const bool include_inlines = true; + context.module_sp->FindFunctions(m_regex, include_symbols, + include_inlines, sc_list); + + SymbolContext sc; + // Now add the functions & symbols to the list - only add if unique: + for (uint32_t i = 0; i < sc_list.GetSize(); i++) { + if (sc_list.GetContextAtIndex(i, sc)) { + ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled); + // Ensure that the function name matches the regex. This is more than + // a sanity check. It is possible that the demangled function name + // does not start with the prefix, for example when it's in an + // anonymous namespace. + if (!func_name.IsEmpty() && m_regex.Execute(func_name.GetStringRef())) + m_match_set.insert(func_name); + } + } + } + return Searcher::eCallbackReturnContinue; + } + + void DoCompletion(SearchFilter *filter) override { + filter->Search(*this); + collection::iterator pos = m_match_set.begin(), end = m_match_set.end(); + for (pos = m_match_set.begin(); pos != end; pos++) + m_request.AddCompletion((*pos).GetCString()); + } + +private: + RegularExpression m_regex; + typedef std::set<ConstString> collection; + collection m_match_set; + + SymbolCompleter(const SymbolCompleter &) = delete; + const SymbolCompleter &operator=(const SymbolCompleter &) = delete; +}; +} // namespace + +namespace { +class ModuleCompleter : public Completer { +public: + ModuleCompleter(CommandInterpreter &interpreter, CompletionRequest &request) + : Completer(interpreter, request) { + FileSpec partial_spec(m_request.GetCursorArgumentPrefix()); + m_file_name = partial_spec.GetFilename().GetCString(); + m_dir_name = partial_spec.GetDirectory().GetCString(); + } + + lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; } + + Searcher::CallbackReturn SearchCallback(SearchFilter &filter, + SymbolContext &context, + Address *addr) override { + if (context.module_sp) { + const char *cur_file_name = + context.module_sp->GetFileSpec().GetFilename().GetCString(); + const char *cur_dir_name = + context.module_sp->GetFileSpec().GetDirectory().GetCString(); + + bool match = false; + if (m_file_name && cur_file_name && + strstr(cur_file_name, m_file_name) == cur_file_name) + match = true; + + if (match && m_dir_name && cur_dir_name && + strstr(cur_dir_name, m_dir_name) != cur_dir_name) + match = false; + + if (match) { + m_request.AddCompletion(cur_file_name); + } + } + return Searcher::eCallbackReturnContinue; + } + + void DoCompletion(SearchFilter *filter) override { filter->Search(*this); } + +private: + const char *m_file_name; + const char *m_dir_name; + + ModuleCompleter(const ModuleCompleter &) = delete; + const ModuleCompleter &operator=(const ModuleCompleter &) = delete; +}; +} // namespace + void CommandCompletions::SourceFiles(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher) { - // Find some way to switch "include support files..." - SourceFileCompleter completer(interpreter, false, request); + SourceFileCompleter completer(interpreter, request); if (searcher == nullptr) { lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); @@ -295,7 +512,7 @@ void CommandCompletions::SettingsNames(CommandInterpreter &interpreter, if (properties_sp) { StreamString strm; properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName); - const std::string &str = strm.GetString(); + const std::string &str = std::string(strm.GetString()); g_property_names.SplitIntoLines(str.c_str(), str.size()); } } @@ -323,185 +540,57 @@ void CommandCompletions::VariablePath(CommandInterpreter &interpreter, Variable::AutoComplete(interpreter.GetExecutionContext(), request); } -CommandCompletions::Completer::Completer(CommandInterpreter &interpreter, - CompletionRequest &request) - : m_interpreter(interpreter), m_request(request) {} - -CommandCompletions::Completer::~Completer() = default; - -// SourceFileCompleter - -CommandCompletions::SourceFileCompleter::SourceFileCompleter( - CommandInterpreter &interpreter, bool include_support_files, - CompletionRequest &request) - : CommandCompletions::Completer(interpreter, request), - m_include_support_files(include_support_files), m_matching_files() { - FileSpec partial_spec(m_request.GetCursorArgumentPrefix()); - m_file_name = partial_spec.GetFilename().GetCString(); - m_dir_name = partial_spec.GetDirectory().GetCString(); -} - -lldb::SearchDepth CommandCompletions::SourceFileCompleter::GetDepth() { - return lldb::eSearchDepthCompUnit; -} - -Searcher::CallbackReturn -CommandCompletions::SourceFileCompleter::SearchCallback(SearchFilter &filter, - SymbolContext &context, - Address *addr) { - if (context.comp_unit != nullptr) { - if (m_include_support_files) { - FileSpecList supporting_files = context.comp_unit->GetSupportFiles(); - for (size_t sfiles = 0; sfiles < supporting_files.GetSize(); sfiles++) { - const FileSpec &sfile_spec = - supporting_files.GetFileSpecAtIndex(sfiles); - const char *sfile_file_name = sfile_spec.GetFilename().GetCString(); - const char *sfile_dir_name = sfile_spec.GetFilename().GetCString(); - bool match = false; - if (m_file_name && sfile_file_name && - strstr(sfile_file_name, m_file_name) == sfile_file_name) - match = true; - if (match && m_dir_name && sfile_dir_name && - strstr(sfile_dir_name, m_dir_name) != sfile_dir_name) - match = false; - - if (match) { - m_matching_files.AppendIfUnique(sfile_spec); - } - } - } else { - const char *cur_file_name = - context.comp_unit->GetPrimaryFile().GetFilename().GetCString(); - const char *cur_dir_name = - context.comp_unit->GetPrimaryFile().GetDirectory().GetCString(); - - bool match = false; - if (m_file_name && cur_file_name && - strstr(cur_file_name, m_file_name) == cur_file_name) - match = true; - - if (match && m_dir_name && cur_dir_name && - strstr(cur_dir_name, m_dir_name) != cur_dir_name) - match = false; - - if (match) { - m_matching_files.AppendIfUnique(context.comp_unit->GetPrimaryFile()); - } - } - } - return Searcher::eCallbackReturnContinue; -} - -void CommandCompletions::SourceFileCompleter::DoCompletion( - SearchFilter *filter) { - filter->Search(*this); - // Now convert the filelist to completions: - for (size_t i = 0; i < m_matching_files.GetSize(); i++) { - m_request.AddCompletion( - m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString()); +void CommandCompletions::Registers(CommandInterpreter &interpreter, + CompletionRequest &request, + SearchFilter *searcher) { + std::string reg_prefix = ""; + if (request.GetCursorArgumentPrefix().startswith("$")) + reg_prefix = "$"; + + RegisterContext *reg_ctx = + interpreter.GetExecutionContext().GetRegisterContext(); + const size_t reg_num = reg_ctx->GetRegisterCount(); + for (size_t reg_idx = 0; reg_idx < reg_num; ++reg_idx) { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_idx); + request.TryCompleteCurrentArg(reg_prefix + reg_info->name, + reg_info->alt_name); } } -// SymbolCompleter - -static bool regex_chars(const char comp) { - return llvm::StringRef("[](){}+.*|^$\\?").contains(comp); -} +void CommandCompletions::Breakpoints(CommandInterpreter &interpreter, + CompletionRequest &request, + SearchFilter *searcher) { + lldb::TargetSP target = interpreter.GetDebugger().GetSelectedTarget(); + if (!target) + return; -CommandCompletions::SymbolCompleter::SymbolCompleter( - CommandInterpreter &interpreter, CompletionRequest &request) - : CommandCompletions::Completer(interpreter, request) { - std::string regex_str; - if (!m_request.GetCursorArgumentPrefix().empty()) { - regex_str.append("^"); - regex_str.append(m_request.GetCursorArgumentPrefix()); - } else { - // Match anything since the completion string is empty - regex_str.append("."); - } - std::string::iterator pos = - find_if(regex_str.begin() + 1, regex_str.end(), regex_chars); - while (pos < regex_str.end()) { - pos = regex_str.insert(pos, '\\'); - pos = find_if(pos + 2, regex_str.end(), regex_chars); - } - m_regex = RegularExpression(regex_str); -} + const BreakpointList &breakpoints = target->GetBreakpointList(); -lldb::SearchDepth CommandCompletions::SymbolCompleter::GetDepth() { - return lldb::eSearchDepthModule; -} + std::unique_lock<std::recursive_mutex> lock; + target->GetBreakpointList().GetListMutex(lock); -Searcher::CallbackReturn CommandCompletions::SymbolCompleter::SearchCallback( - SearchFilter &filter, SymbolContext &context, Address *addr) { - if (context.module_sp) { - SymbolContextList sc_list; - const bool include_symbols = true; - const bool include_inlines = true; - context.module_sp->FindFunctions(m_regex, include_symbols, include_inlines, - sc_list); - - SymbolContext sc; - // Now add the functions & symbols to the list - only add if unique: - for (uint32_t i = 0; i < sc_list.GetSize(); i++) { - if (sc_list.GetContextAtIndex(i, sc)) { - ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled); - // Ensure that the function name matches the regex. This is more than a - // sanity check. It is possible that the demangled function name does - // not start with the prefix, for example when it's in an anonymous - // namespace. - if (!func_name.IsEmpty() && m_regex.Execute(func_name.GetStringRef())) - m_match_set.insert(func_name); - } - } - } - return Searcher::eCallbackReturnContinue; -} + size_t num_breakpoints = breakpoints.GetSize(); + if (num_breakpoints == 0) + return; -void CommandCompletions::SymbolCompleter::DoCompletion(SearchFilter *filter) { - filter->Search(*this); - collection::iterator pos = m_match_set.begin(), end = m_match_set.end(); - for (pos = m_match_set.begin(); pos != end; pos++) - m_request.AddCompletion((*pos).GetCString()); -} + for (size_t i = 0; i < num_breakpoints; ++i) { + lldb::BreakpointSP bp = breakpoints.GetBreakpointAtIndex(i); -// ModuleCompleter -CommandCompletions::ModuleCompleter::ModuleCompleter( - CommandInterpreter &interpreter, CompletionRequest &request) - : CommandCompletions::Completer(interpreter, request) { - FileSpec partial_spec(m_request.GetCursorArgumentPrefix()); - m_file_name = partial_spec.GetFilename().GetCString(); - m_dir_name = partial_spec.GetDirectory().GetCString(); -} + StreamString s; + bp->GetDescription(&s, lldb::eDescriptionLevelBrief); + llvm::StringRef bp_info = s.GetString(); -lldb::SearchDepth CommandCompletions::ModuleCompleter::GetDepth() { - return lldb::eSearchDepthModule; -} + const size_t colon_pos = bp_info.find_first_of(':'); + if (colon_pos != llvm::StringRef::npos) + bp_info = bp_info.drop_front(colon_pos + 2); -Searcher::CallbackReturn CommandCompletions::ModuleCompleter::SearchCallback( - SearchFilter &filter, SymbolContext &context, Address *addr) { - if (context.module_sp) { - const char *cur_file_name = - context.module_sp->GetFileSpec().GetFilename().GetCString(); - const char *cur_dir_name = - context.module_sp->GetFileSpec().GetDirectory().GetCString(); - - bool match = false; - if (m_file_name && cur_file_name && - strstr(cur_file_name, m_file_name) == cur_file_name) - match = true; - - if (match && m_dir_name && cur_dir_name && - strstr(cur_dir_name, m_dir_name) != cur_dir_name) - match = false; - - if (match) { - m_request.AddCompletion(cur_file_name); - } + request.TryCompleteCurrentArg(std::to_string(bp->GetID()), bp_info); } - return Searcher::eCallbackReturnContinue; } -void CommandCompletions::ModuleCompleter::DoCompletion(SearchFilter *filter) { - filter->Search(*this); -} +void CommandCompletions::ProcessPluginNames(CommandInterpreter &interpreter, + CompletionRequest &request, + SearchFilter *searcher) { + PluginManager::AutoCompleteProcessName(request.GetCursorArgumentPrefix(), + request); +}
\ No newline at end of file |