180814287SRaphael Isemann //===-- CommandCompletions.cpp --------------------------------------------===// 2c9af18a5SGreg Clayton // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6c9af18a5SGreg Clayton // 7c9af18a5SGreg Clayton //===----------------------------------------------------------------------===// 8c9af18a5SGreg Clayton 9c8ecc2a9SEugene Zelenko #include "llvm/ADT/SmallString.h" 102cc5a18dSZachary Turner #include "llvm/ADT/StringSet.h" 11c8ecc2a9SEugene Zelenko 129b62fd2fSGreg Clayton #include "lldb/Core/FileSpecList.h" 131f746071SGreg Clayton #include "lldb/Core/Module.h" 14190fadcdSZachary Turner #include "lldb/Core/PluginManager.h" 15190fadcdSZachary Turner #include "lldb/Host/FileSystem.h" 169b62fd2fSGreg Clayton #include "lldb/Interpreter/CommandCompletions.h" 179b62fd2fSGreg Clayton #include "lldb/Interpreter/CommandInterpreter.h" 18633a29cfSZachary Turner #include "lldb/Interpreter/OptionValueProperties.h" 191f746071SGreg Clayton #include "lldb/Symbol/CompileUnit.h" 20f21feadcSGreg Clayton #include "lldb/Symbol/Variable.h" 215713a05bSZachary Turner #include "lldb/Utility/FileSpec.h" 222f3df613SZachary Turner #include "lldb/Utility/StreamString.h" 232cc5a18dSZachary Turner #include "lldb/Utility/TildeExpressionResolver.h" 24c9af18a5SGreg Clayton 257d86ee5aSZachary Turner #include "llvm/Support/FileSystem.h" 262cc5a18dSZachary Turner #include "llvm/Support/Path.h" 27190fadcdSZachary Turner 28c9af18a5SGreg Clayton using namespace lldb_private; 29c9af18a5SGreg Clayton 30*440460f1SRaphael Isemann // This is the command completion callback that is used to complete the 31*440460f1SRaphael Isemann // argument of the option it is bound to (in the OptionDefinition table 32*440460f1SRaphael Isemann // below). 33*440460f1SRaphael Isemann typedef void (*CompletionCallback)(CommandInterpreter &interpreter, 34*440460f1SRaphael Isemann CompletionRequest &request, 35*440460f1SRaphael Isemann // A search filter to limit the search... 36*440460f1SRaphael Isemann lldb_private::SearchFilter *searcher); 37*440460f1SRaphael Isemann 38*440460f1SRaphael Isemann struct CommonCompletionElement { 39*440460f1SRaphael Isemann uint32_t type; 40*440460f1SRaphael Isemann CompletionCallback callback; 41*440460f1SRaphael Isemann }; 42*440460f1SRaphael Isemann 43*440460f1SRaphael Isemann bool CommandCompletions::InvokeCommonCompletionCallbacks( 44*440460f1SRaphael Isemann CommandInterpreter &interpreter, uint32_t completion_mask, 45*440460f1SRaphael Isemann CompletionRequest &request, SearchFilter *searcher) { 46*440460f1SRaphael Isemann bool handled = false; 47*440460f1SRaphael Isemann 48*440460f1SRaphael Isemann const CommonCompletionElement common_completions[] = { 49c9af18a5SGreg Clayton {eSourceFileCompletion, CommandCompletions::SourceFiles}, 50558ce124SJim Ingham {eDiskFileCompletion, CommandCompletions::DiskFiles}, 51558ce124SJim Ingham {eDiskDirectoryCompletion, CommandCompletions::DiskDirectories}, 52c9af18a5SGreg Clayton {eSymbolCompletion, CommandCompletions::Symbols}, 53c9af18a5SGreg Clayton {eModuleCompletion, CommandCompletions::Modules}, 543df9a8dfSCaroline Tice {eSettingsNameCompletion, CommandCompletions::SettingsNames}, 55ab65b34fSGreg Clayton {ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames}, 56ab65b34fSGreg Clayton {eArchitectureCompletion, CommandCompletions::ArchitectureNames}, 57f21feadcSGreg Clayton {eVariablePathCompletion, CommandCompletions::VariablePath}, 58c8ecc2a9SEugene Zelenko {eNoCompletion, nullptr} // This one has to be last in the list. 59c9af18a5SGreg Clayton }; 60c9af18a5SGreg Clayton 61b9c1b51eSKate Stone for (int i = 0;; i++) { 62*440460f1SRaphael Isemann if (common_completions[i].type == eNoCompletion) 63c9af18a5SGreg Clayton break; 64*440460f1SRaphael Isemann else if ((common_completions[i].type & completion_mask) == 65*440460f1SRaphael Isemann common_completions[i].type && 66*440460f1SRaphael Isemann common_completions[i].callback != nullptr) { 67c9af18a5SGreg Clayton handled = true; 68*440460f1SRaphael Isemann common_completions[i].callback(interpreter, request, searcher); 69c9af18a5SGreg Clayton } 70c9af18a5SGreg Clayton } 71c9af18a5SGreg Clayton return handled; 72c9af18a5SGreg Clayton } 73c9af18a5SGreg Clayton 74572fc897SRaphael Isemann namespace { 75572fc897SRaphael Isemann // The Completer class is a convenient base class for building searchers that 76572fc897SRaphael Isemann // go along with the SearchFilter passed to the standard Completer functions. 77572fc897SRaphael Isemann class Completer : public Searcher { 78572fc897SRaphael Isemann public: 79572fc897SRaphael Isemann Completer(CommandInterpreter &interpreter, CompletionRequest &request) 80572fc897SRaphael Isemann : m_interpreter(interpreter), m_request(request) {} 81572fc897SRaphael Isemann 82572fc897SRaphael Isemann ~Completer() override = default; 83572fc897SRaphael Isemann 84572fc897SRaphael Isemann CallbackReturn SearchCallback(SearchFilter &filter, SymbolContext &context, 85572fc897SRaphael Isemann Address *addr) override = 0; 86572fc897SRaphael Isemann 87572fc897SRaphael Isemann lldb::SearchDepth GetDepth() override = 0; 88572fc897SRaphael Isemann 89572fc897SRaphael Isemann virtual void DoCompletion(SearchFilter *filter) = 0; 90572fc897SRaphael Isemann 91572fc897SRaphael Isemann protected: 92572fc897SRaphael Isemann CommandInterpreter &m_interpreter; 93572fc897SRaphael Isemann CompletionRequest &m_request; 94572fc897SRaphael Isemann 95572fc897SRaphael Isemann private: 96572fc897SRaphael Isemann DISALLOW_COPY_AND_ASSIGN(Completer); 97572fc897SRaphael Isemann }; 98572fc897SRaphael Isemann } // namespace 99572fc897SRaphael Isemann 100572fc897SRaphael Isemann // SourceFileCompleter implements the source file completer 101572fc897SRaphael Isemann namespace { 102572fc897SRaphael Isemann class SourceFileCompleter : public Completer { 103572fc897SRaphael Isemann public: 104572fc897SRaphael Isemann SourceFileCompleter(CommandInterpreter &interpreter, 105572fc897SRaphael Isemann CompletionRequest &request) 106572fc897SRaphael Isemann : Completer(interpreter, request), m_matching_files() { 107572fc897SRaphael Isemann FileSpec partial_spec(m_request.GetCursorArgumentPrefix()); 108572fc897SRaphael Isemann m_file_name = partial_spec.GetFilename().GetCString(); 109572fc897SRaphael Isemann m_dir_name = partial_spec.GetDirectory().GetCString(); 110572fc897SRaphael Isemann } 111572fc897SRaphael Isemann 112572fc897SRaphael Isemann lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthCompUnit; } 113572fc897SRaphael Isemann 114572fc897SRaphael Isemann Searcher::CallbackReturn SearchCallback(SearchFilter &filter, 115572fc897SRaphael Isemann SymbolContext &context, 116572fc897SRaphael Isemann Address *addr) override { 117572fc897SRaphael Isemann if (context.comp_unit != nullptr) { 118572fc897SRaphael Isemann const char *cur_file_name = 119572fc897SRaphael Isemann context.comp_unit->GetPrimaryFile().GetFilename().GetCString(); 120572fc897SRaphael Isemann const char *cur_dir_name = 121572fc897SRaphael Isemann context.comp_unit->GetPrimaryFile().GetDirectory().GetCString(); 122572fc897SRaphael Isemann 123572fc897SRaphael Isemann bool match = false; 124572fc897SRaphael Isemann if (m_file_name && cur_file_name && 125572fc897SRaphael Isemann strstr(cur_file_name, m_file_name) == cur_file_name) 126572fc897SRaphael Isemann match = true; 127572fc897SRaphael Isemann 128572fc897SRaphael Isemann if (match && m_dir_name && cur_dir_name && 129572fc897SRaphael Isemann strstr(cur_dir_name, m_dir_name) != cur_dir_name) 130572fc897SRaphael Isemann match = false; 131572fc897SRaphael Isemann 132572fc897SRaphael Isemann if (match) { 133572fc897SRaphael Isemann m_matching_files.AppendIfUnique(context.comp_unit->GetPrimaryFile()); 134572fc897SRaphael Isemann } 135572fc897SRaphael Isemann } 136572fc897SRaphael Isemann return Searcher::eCallbackReturnContinue; 137572fc897SRaphael Isemann } 138572fc897SRaphael Isemann 139572fc897SRaphael Isemann void DoCompletion(SearchFilter *filter) override { 140572fc897SRaphael Isemann filter->Search(*this); 141572fc897SRaphael Isemann // Now convert the filelist to completions: 142572fc897SRaphael Isemann for (size_t i = 0; i < m_matching_files.GetSize(); i++) { 143572fc897SRaphael Isemann m_request.AddCompletion( 144572fc897SRaphael Isemann m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString()); 145572fc897SRaphael Isemann } 146572fc897SRaphael Isemann } 147572fc897SRaphael Isemann 148572fc897SRaphael Isemann private: 149572fc897SRaphael Isemann FileSpecList m_matching_files; 150572fc897SRaphael Isemann const char *m_file_name; 151572fc897SRaphael Isemann const char *m_dir_name; 152572fc897SRaphael Isemann 153572fc897SRaphael Isemann DISALLOW_COPY_AND_ASSIGN(SourceFileCompleter); 154572fc897SRaphael Isemann }; 155572fc897SRaphael Isemann } // namespace 156572fc897SRaphael Isemann 157572fc897SRaphael Isemann static bool regex_chars(const char comp) { 158572fc897SRaphael Isemann return llvm::StringRef("[](){}+.*|^$\\?").contains(comp); 159572fc897SRaphael Isemann } 160572fc897SRaphael Isemann 161572fc897SRaphael Isemann namespace { 162572fc897SRaphael Isemann class SymbolCompleter : public Completer { 163572fc897SRaphael Isemann 164572fc897SRaphael Isemann public: 165572fc897SRaphael Isemann SymbolCompleter(CommandInterpreter &interpreter, CompletionRequest &request) 166572fc897SRaphael Isemann : Completer(interpreter, request) { 167572fc897SRaphael Isemann std::string regex_str; 168572fc897SRaphael Isemann if (!m_request.GetCursorArgumentPrefix().empty()) { 169572fc897SRaphael Isemann regex_str.append("^"); 170572fc897SRaphael Isemann regex_str.append(std::string(m_request.GetCursorArgumentPrefix())); 171572fc897SRaphael Isemann } else { 172572fc897SRaphael Isemann // Match anything since the completion string is empty 173572fc897SRaphael Isemann regex_str.append("."); 174572fc897SRaphael Isemann } 175572fc897SRaphael Isemann std::string::iterator pos = 176572fc897SRaphael Isemann find_if(regex_str.begin() + 1, regex_str.end(), regex_chars); 177572fc897SRaphael Isemann while (pos < regex_str.end()) { 178572fc897SRaphael Isemann pos = regex_str.insert(pos, '\\'); 179572fc897SRaphael Isemann pos = find_if(pos + 2, regex_str.end(), regex_chars); 180572fc897SRaphael Isemann } 181572fc897SRaphael Isemann m_regex = RegularExpression(regex_str); 182572fc897SRaphael Isemann } 183572fc897SRaphael Isemann 184572fc897SRaphael Isemann lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; } 185572fc897SRaphael Isemann 186572fc897SRaphael Isemann Searcher::CallbackReturn SearchCallback(SearchFilter &filter, 187572fc897SRaphael Isemann SymbolContext &context, 188572fc897SRaphael Isemann Address *addr) override { 189572fc897SRaphael Isemann if (context.module_sp) { 190572fc897SRaphael Isemann SymbolContextList sc_list; 191572fc897SRaphael Isemann const bool include_symbols = true; 192572fc897SRaphael Isemann const bool include_inlines = true; 193572fc897SRaphael Isemann context.module_sp->FindFunctions(m_regex, include_symbols, 194572fc897SRaphael Isemann include_inlines, sc_list); 195572fc897SRaphael Isemann 196572fc897SRaphael Isemann SymbolContext sc; 197572fc897SRaphael Isemann // Now add the functions & symbols to the list - only add if unique: 198572fc897SRaphael Isemann for (uint32_t i = 0; i < sc_list.GetSize(); i++) { 199572fc897SRaphael Isemann if (sc_list.GetContextAtIndex(i, sc)) { 200572fc897SRaphael Isemann ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled); 201572fc897SRaphael Isemann // Ensure that the function name matches the regex. This is more than 202572fc897SRaphael Isemann // a sanity check. It is possible that the demangled function name 203572fc897SRaphael Isemann // does not start with the prefix, for example when it's in an 204572fc897SRaphael Isemann // anonymous namespace. 205572fc897SRaphael Isemann if (!func_name.IsEmpty() && m_regex.Execute(func_name.GetStringRef())) 206572fc897SRaphael Isemann m_match_set.insert(func_name); 207572fc897SRaphael Isemann } 208572fc897SRaphael Isemann } 209572fc897SRaphael Isemann } 210572fc897SRaphael Isemann return Searcher::eCallbackReturnContinue; 211572fc897SRaphael Isemann } 212572fc897SRaphael Isemann 213572fc897SRaphael Isemann void DoCompletion(SearchFilter *filter) override { 214572fc897SRaphael Isemann filter->Search(*this); 215572fc897SRaphael Isemann collection::iterator pos = m_match_set.begin(), end = m_match_set.end(); 216572fc897SRaphael Isemann for (pos = m_match_set.begin(); pos != end; pos++) 217572fc897SRaphael Isemann m_request.AddCompletion((*pos).GetCString()); 218572fc897SRaphael Isemann } 219572fc897SRaphael Isemann 220572fc897SRaphael Isemann private: 221572fc897SRaphael Isemann RegularExpression m_regex; 222572fc897SRaphael Isemann typedef std::set<ConstString> collection; 223572fc897SRaphael Isemann collection m_match_set; 224572fc897SRaphael Isemann 225572fc897SRaphael Isemann DISALLOW_COPY_AND_ASSIGN(SymbolCompleter); 226572fc897SRaphael Isemann }; 227572fc897SRaphael Isemann } // namespace 228572fc897SRaphael Isemann 229572fc897SRaphael Isemann namespace { 230572fc897SRaphael Isemann class ModuleCompleter : public Completer { 231572fc897SRaphael Isemann public: 232572fc897SRaphael Isemann ModuleCompleter(CommandInterpreter &interpreter, CompletionRequest &request) 233572fc897SRaphael Isemann : Completer(interpreter, request) { 234572fc897SRaphael Isemann FileSpec partial_spec(m_request.GetCursorArgumentPrefix()); 235572fc897SRaphael Isemann m_file_name = partial_spec.GetFilename().GetCString(); 236572fc897SRaphael Isemann m_dir_name = partial_spec.GetDirectory().GetCString(); 237572fc897SRaphael Isemann } 238572fc897SRaphael Isemann 239572fc897SRaphael Isemann lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; } 240572fc897SRaphael Isemann 241572fc897SRaphael Isemann Searcher::CallbackReturn SearchCallback(SearchFilter &filter, 242572fc897SRaphael Isemann SymbolContext &context, 243572fc897SRaphael Isemann Address *addr) override { 244572fc897SRaphael Isemann if (context.module_sp) { 245572fc897SRaphael Isemann const char *cur_file_name = 246572fc897SRaphael Isemann context.module_sp->GetFileSpec().GetFilename().GetCString(); 247572fc897SRaphael Isemann const char *cur_dir_name = 248572fc897SRaphael Isemann context.module_sp->GetFileSpec().GetDirectory().GetCString(); 249572fc897SRaphael Isemann 250572fc897SRaphael Isemann bool match = false; 251572fc897SRaphael Isemann if (m_file_name && cur_file_name && 252572fc897SRaphael Isemann strstr(cur_file_name, m_file_name) == cur_file_name) 253572fc897SRaphael Isemann match = true; 254572fc897SRaphael Isemann 255572fc897SRaphael Isemann if (match && m_dir_name && cur_dir_name && 256572fc897SRaphael Isemann strstr(cur_dir_name, m_dir_name) != cur_dir_name) 257572fc897SRaphael Isemann match = false; 258572fc897SRaphael Isemann 259572fc897SRaphael Isemann if (match) { 260572fc897SRaphael Isemann m_request.AddCompletion(cur_file_name); 261572fc897SRaphael Isemann } 262572fc897SRaphael Isemann } 263572fc897SRaphael Isemann return Searcher::eCallbackReturnContinue; 264572fc897SRaphael Isemann } 265572fc897SRaphael Isemann 266572fc897SRaphael Isemann void DoCompletion(SearchFilter *filter) override { filter->Search(*this); } 267572fc897SRaphael Isemann 268572fc897SRaphael Isemann private: 269572fc897SRaphael Isemann const char *m_file_name; 270572fc897SRaphael Isemann const char *m_dir_name; 271572fc897SRaphael Isemann 272572fc897SRaphael Isemann DISALLOW_COPY_AND_ASSIGN(ModuleCompleter); 273572fc897SRaphael Isemann }; 274572fc897SRaphael Isemann } // namespace 275572fc897SRaphael Isemann 276ae34ed2cSRaphael Isemann void CommandCompletions::SourceFiles(CommandInterpreter &interpreter, 277a2e76c0bSRaphael Isemann CompletionRequest &request, 278a2e76c0bSRaphael Isemann SearchFilter *searcher) { 27962174682SRaphael Isemann SourceFileCompleter completer(interpreter, request); 280c9af18a5SGreg Clayton 281b9c1b51eSKate Stone if (searcher == nullptr) { 2822976d00aSJim Ingham lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); 28333df7cd3SJim Ingham SearchFilterForUnconstrainedSearches null_searcher(target_sp); 284c9af18a5SGreg Clayton completer.DoCompletion(&null_searcher); 285b9c1b51eSKate Stone } else { 286c9af18a5SGreg Clayton completer.DoCompletion(searcher); 287c9af18a5SGreg Clayton } 288c9af18a5SGreg Clayton } 289c9af18a5SGreg Clayton 290ae34ed2cSRaphael Isemann static void DiskFilesOrDirectories(const llvm::Twine &partial_name, 2915edee822SRaphael Isemann bool only_directories, 2925edee822SRaphael Isemann CompletionRequest &request, 2932cc5a18dSZachary Turner TildeExpressionResolver &Resolver) { 2942cc5a18dSZachary Turner llvm::SmallString<256> CompletionBuffer; 2952cc5a18dSZachary Turner llvm::SmallString<256> Storage; 2962cc5a18dSZachary Turner partial_name.toVector(CompletionBuffer); 297b2f1fb29SVirgile Bello 2982cc5a18dSZachary Turner if (CompletionBuffer.size() >= PATH_MAX) 299ae34ed2cSRaphael Isemann return; 300b2f1fb29SVirgile Bello 3012cc5a18dSZachary Turner namespace path = llvm::sys::path; 302b2f1fb29SVirgile Bello 3032cc5a18dSZachary Turner llvm::StringRef SearchDir; 3042cc5a18dSZachary Turner llvm::StringRef PartialItem; 3052cc5a18dSZachary Turner 3062cc5a18dSZachary Turner if (CompletionBuffer.startswith("~")) { 3072cc5a18dSZachary Turner llvm::StringRef Buffer(CompletionBuffer); 3085c5091fcSZachary Turner size_t FirstSep = 3095c5091fcSZachary Turner Buffer.find_if([](char c) { return path::is_separator(c); }); 3102cc5a18dSZachary Turner 3112cc5a18dSZachary Turner llvm::StringRef Username = Buffer.take_front(FirstSep); 3122cc5a18dSZachary Turner llvm::StringRef Remainder; 3132cc5a18dSZachary Turner if (FirstSep != llvm::StringRef::npos) 3142cc5a18dSZachary Turner Remainder = Buffer.drop_front(FirstSep + 1); 3152cc5a18dSZachary Turner 316b3f44ad9SStella Stamenova llvm::SmallString<256> Resolved; 3172cc5a18dSZachary Turner if (!Resolver.ResolveExact(Username, Resolved)) { 3182cc5a18dSZachary Turner // We couldn't resolve it as a full username. If there were no slashes 3192cc5a18dSZachary Turner // then this might be a partial username. We try to resolve it as such 3202cc5a18dSZachary Turner // but after that, we're done regardless of any matches. 3212cc5a18dSZachary Turner if (FirstSep == llvm::StringRef::npos) { 3222cc5a18dSZachary Turner llvm::StringSet<> MatchSet; 323a2e76c0bSRaphael Isemann Resolver.ResolvePartial(Username, MatchSet); 3242cc5a18dSZachary Turner for (const auto &S : MatchSet) { 3252cc5a18dSZachary Turner Resolved = S.getKey(); 3262cc5a18dSZachary Turner path::append(Resolved, path::get_separator()); 3275edee822SRaphael Isemann request.AddCompletion(Resolved, "", CompletionMode::Partial); 328b2f1fb29SVirgile Bello } 329b2f1fb29SVirgile Bello } 330ae34ed2cSRaphael Isemann return; 3312cc5a18dSZachary Turner } 3320734e6a5SZachary Turner 33305097246SAdrian Prantl // If there was no trailing slash, then we're done as soon as we resolve 33405097246SAdrian Prantl // the expression to the correct directory. Otherwise we need to continue 3352cc5a18dSZachary Turner // looking for matches within that directory. 3362cc5a18dSZachary Turner if (FirstSep == llvm::StringRef::npos) { 3372cc5a18dSZachary Turner // Make sure it ends with a separator. 3382cc5a18dSZachary Turner path::append(CompletionBuffer, path::get_separator()); 3395edee822SRaphael Isemann request.AddCompletion(CompletionBuffer, "", CompletionMode::Partial); 340ae34ed2cSRaphael Isemann return; 3412cc5a18dSZachary Turner } 3422cc5a18dSZachary Turner 3432cc5a18dSZachary Turner // We want to keep the form the user typed, so we special case this to 3442cc5a18dSZachary Turner // search in the fully resolved directory, but CompletionBuffer keeps the 3452cc5a18dSZachary Turner // unmodified form that the user typed. 3462cc5a18dSZachary Turner Storage = Resolved; 34788ec2e4eSPavel Labath llvm::StringRef RemainderDir = path::parent_path(Remainder); 3484621e0b0SRaphael Isemann if (!RemainderDir.empty()) { 3494621e0b0SRaphael Isemann // Append the remaining path to the resolved directory. 3504621e0b0SRaphael Isemann Storage.append(path::get_separator()); 3514621e0b0SRaphael Isemann Storage.append(RemainderDir); 3524621e0b0SRaphael Isemann } 35317843885SRaphael Isemann SearchDir = Storage; 3540734e6a5SZachary Turner } else { 3552cc5a18dSZachary Turner SearchDir = path::parent_path(CompletionBuffer); 3562cc5a18dSZachary Turner } 3572cc5a18dSZachary Turner 3582cc5a18dSZachary Turner size_t FullPrefixLen = CompletionBuffer.size(); 3592cc5a18dSZachary Turner 3602cc5a18dSZachary Turner PartialItem = path::filename(CompletionBuffer); 36178a10a7aSFrederic Riss 36278a10a7aSFrederic Riss // path::filename() will return "." when the passed path ends with a 36378a10a7aSFrederic Riss // directory separator. We have to filter those out, but only when the 36478a10a7aSFrederic Riss // "." doesn't come from the completion request itself. 36578a10a7aSFrederic Riss if (PartialItem == "." && path::is_separator(CompletionBuffer.back())) 3662cc5a18dSZachary Turner PartialItem = llvm::StringRef(); 3672cc5a18dSZachary Turner 368426d1379SZachary Turner if (SearchDir.empty()) { 369426d1379SZachary Turner llvm::sys::fs::current_path(Storage); 370426d1379SZachary Turner SearchDir = Storage; 371426d1379SZachary Turner } 3722cc5a18dSZachary Turner assert(!PartialItem.contains(path::get_separator())); 3732cc5a18dSZachary Turner 3742cc5a18dSZachary Turner // SearchDir now contains the directory to search in, and Prefix contains the 3752cc5a18dSZachary Turner // text we want to match against items in that directory. 3762cc5a18dSZachary Turner 377edaf2bccSJonas Devlieghere FileSystem &fs = FileSystem::Instance(); 3782cc5a18dSZachary Turner std::error_code EC; 379edaf2bccSJonas Devlieghere llvm::vfs::directory_iterator Iter = fs.DirBegin(SearchDir, EC); 380edaf2bccSJonas Devlieghere llvm::vfs::directory_iterator End; 3812cc5a18dSZachary Turner for (; Iter != End && !EC; Iter.increment(EC)) { 3822cc5a18dSZachary Turner auto &Entry = *Iter; 383edaf2bccSJonas Devlieghere llvm::ErrorOr<llvm::vfs::Status> Status = fs.GetStatus(Entry.path()); 384edaf2bccSJonas Devlieghere 385edaf2bccSJonas Devlieghere if (!Status) 386edaf2bccSJonas Devlieghere continue; 3872cc5a18dSZachary Turner 3882cc5a18dSZachary Turner auto Name = path::filename(Entry.path()); 3892cc5a18dSZachary Turner 3902cc5a18dSZachary Turner // Omit ".", ".." 3912cc5a18dSZachary Turner if (Name == "." || Name == ".." || !Name.startswith(PartialItem)) 3922cc5a18dSZachary Turner continue; 3932cc5a18dSZachary Turner 394edaf2bccSJonas Devlieghere bool is_dir = Status->isDirectory(); 3952cc5a18dSZachary Turner 3962cc5a18dSZachary Turner // If it's a symlink, then we treat it as a directory as long as the target 3972cc5a18dSZachary Turner // is a directory. 398edaf2bccSJonas Devlieghere if (Status->isSymlink()) { 399edaf2bccSJonas Devlieghere FileSpec symlink_filespec(Entry.path()); 400edaf2bccSJonas Devlieghere FileSpec resolved_filespec; 401edaf2bccSJonas Devlieghere auto error = fs.ResolveSymbolicLink(symlink_filespec, resolved_filespec); 402edaf2bccSJonas Devlieghere if (error.Success()) 403edaf2bccSJonas Devlieghere is_dir = fs.IsDirectory(symlink_filespec); 4042cc5a18dSZachary Turner } 405edaf2bccSJonas Devlieghere 4062cc5a18dSZachary Turner if (only_directories && !is_dir) 4072cc5a18dSZachary Turner continue; 4082cc5a18dSZachary Turner 4092cc5a18dSZachary Turner // Shrink it back down so that it just has the original prefix the user 4102cc5a18dSZachary Turner // typed and remove the part of the name which is common to the located 4112cc5a18dSZachary Turner // item and what the user typed. 4122cc5a18dSZachary Turner CompletionBuffer.resize(FullPrefixLen); 4132cc5a18dSZachary Turner Name = Name.drop_front(PartialItem.size()); 4142cc5a18dSZachary Turner CompletionBuffer.append(Name); 4152cc5a18dSZachary Turner 4162cc5a18dSZachary Turner if (is_dir) { 4172cc5a18dSZachary Turner path::append(CompletionBuffer, path::get_separator()); 4180734e6a5SZachary Turner } 4190734e6a5SZachary Turner 4205edee822SRaphael Isemann CompletionMode mode = 4215edee822SRaphael Isemann is_dir ? CompletionMode::Partial : CompletionMode::Normal; 4225edee822SRaphael Isemann request.AddCompletion(CompletionBuffer, "", mode); 423d5bd3a1eSZachary Turner } 424558ce124SJim Ingham } 425558ce124SJim Ingham 4265edee822SRaphael Isemann static void DiskFilesOrDirectories(const llvm::Twine &partial_name, 4275edee822SRaphael Isemann bool only_directories, StringList &matches, 4285edee822SRaphael Isemann TildeExpressionResolver &Resolver) { 4295edee822SRaphael Isemann CompletionResult result; 4305edee822SRaphael Isemann std::string partial_name_str = partial_name.str(); 4315edee822SRaphael Isemann CompletionRequest request(partial_name_str, partial_name_str.size(), result); 4325edee822SRaphael Isemann DiskFilesOrDirectories(partial_name, only_directories, request, Resolver); 4335edee822SRaphael Isemann result.GetMatches(matches); 4345edee822SRaphael Isemann } 4355edee822SRaphael Isemann 436ae34ed2cSRaphael Isemann static void DiskFilesOrDirectories(CompletionRequest &request, 4371a6d7ab5SRaphael Isemann bool only_directories) { 43872787ac6SJonas Devlieghere StandardTildeExpressionResolver resolver; 4391a6d7ab5SRaphael Isemann DiskFilesOrDirectories(request.GetCursorArgumentPrefix(), only_directories, 4405edee822SRaphael Isemann request, resolver); 4411a6d7ab5SRaphael Isemann } 4421a6d7ab5SRaphael Isemann 443ae34ed2cSRaphael Isemann void CommandCompletions::DiskFiles(CommandInterpreter &interpreter, 444a2e76c0bSRaphael Isemann CompletionRequest &request, 445a2e76c0bSRaphael Isemann SearchFilter *searcher) { 446ae34ed2cSRaphael Isemann DiskFilesOrDirectories(request, /*only_dirs*/ false); 4472cc5a18dSZachary Turner } 4482cc5a18dSZachary Turner 449ae34ed2cSRaphael Isemann void CommandCompletions::DiskFiles(const llvm::Twine &partial_file_name, 4502cc5a18dSZachary Turner StringList &matches, 4512cc5a18dSZachary Turner TildeExpressionResolver &Resolver) { 452ae34ed2cSRaphael Isemann DiskFilesOrDirectories(partial_file_name, false, matches, Resolver); 453558ce124SJim Ingham } 454558ce124SJim Ingham 455ae34ed2cSRaphael Isemann void CommandCompletions::DiskDirectories(CommandInterpreter &interpreter, 456a2e76c0bSRaphael Isemann CompletionRequest &request, 457a2e76c0bSRaphael Isemann SearchFilter *searcher) { 458ae34ed2cSRaphael Isemann DiskFilesOrDirectories(request, /*only_dirs*/ true); 4592cc5a18dSZachary Turner } 4602cc5a18dSZachary Turner 461ae34ed2cSRaphael Isemann void CommandCompletions::DiskDirectories(const llvm::Twine &partial_file_name, 4622cc5a18dSZachary Turner StringList &matches, 4632cc5a18dSZachary Turner TildeExpressionResolver &Resolver) { 464ae34ed2cSRaphael Isemann DiskFilesOrDirectories(partial_file_name, true, matches, Resolver); 465558ce124SJim Ingham } 466558ce124SJim Ingham 467ae34ed2cSRaphael Isemann void CommandCompletions::Modules(CommandInterpreter &interpreter, 468a2e76c0bSRaphael Isemann CompletionRequest &request, 469a2e76c0bSRaphael Isemann SearchFilter *searcher) { 470a2e76c0bSRaphael Isemann ModuleCompleter completer(interpreter, request); 471c9af18a5SGreg Clayton 472b9c1b51eSKate Stone if (searcher == nullptr) { 4732976d00aSJim Ingham lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); 47433df7cd3SJim Ingham SearchFilterForUnconstrainedSearches null_searcher(target_sp); 475c9af18a5SGreg Clayton completer.DoCompletion(&null_searcher); 476b9c1b51eSKate Stone } else { 477c9af18a5SGreg Clayton completer.DoCompletion(searcher); 478c9af18a5SGreg Clayton } 479c9af18a5SGreg Clayton } 480c9af18a5SGreg Clayton 481ae34ed2cSRaphael Isemann void CommandCompletions::Symbols(CommandInterpreter &interpreter, 482a2e76c0bSRaphael Isemann CompletionRequest &request, 483a2e76c0bSRaphael Isemann SearchFilter *searcher) { 484a2e76c0bSRaphael Isemann SymbolCompleter completer(interpreter, request); 485c9af18a5SGreg Clayton 486b9c1b51eSKate Stone if (searcher == nullptr) { 4872976d00aSJim Ingham lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); 48833df7cd3SJim Ingham SearchFilterForUnconstrainedSearches null_searcher(target_sp); 489c9af18a5SGreg Clayton completer.DoCompletion(&null_searcher); 490b9c1b51eSKate Stone } else { 491c9af18a5SGreg Clayton completer.DoCompletion(searcher); 492c9af18a5SGreg Clayton } 493c9af18a5SGreg Clayton } 494c9af18a5SGreg Clayton 495ae34ed2cSRaphael Isemann void CommandCompletions::SettingsNames(CommandInterpreter &interpreter, 496a2e76c0bSRaphael Isemann CompletionRequest &request, 497a2e76c0bSRaphael Isemann SearchFilter *searcher) { 49867cc0636SGreg Clayton // Cache the full setting name list 49967cc0636SGreg Clayton static StringList g_property_names; 500b9c1b51eSKate Stone if (g_property_names.GetSize() == 0) { 50167cc0636SGreg Clayton // Generate the full setting name list on demand 502b9c1b51eSKate Stone lldb::OptionValuePropertiesSP properties_sp( 503b9c1b51eSKate Stone interpreter.GetDebugger().GetValueProperties()); 504b9c1b51eSKate Stone if (properties_sp) { 50567cc0636SGreg Clayton StreamString strm; 506c8ecc2a9SEugene Zelenko properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName); 507adcd0268SBenjamin Kramer const std::string &str = std::string(strm.GetString()); 50867cc0636SGreg Clayton g_property_names.SplitIntoLines(str.c_str(), str.size()); 50967cc0636SGreg Clayton } 51067cc0636SGreg Clayton } 5113df9a8dfSCaroline Tice 51293ca36d7SRaphael Isemann for (const std::string &s : g_property_names) 51393ca36d7SRaphael Isemann request.TryCompleteCurrentArg(s); 514b8639f5cSRaphael Isemann } 515b8639f5cSRaphael Isemann 516ae34ed2cSRaphael Isemann void CommandCompletions::PlatformPluginNames(CommandInterpreter &interpreter, 517a2e76c0bSRaphael Isemann CompletionRequest &request, 518a2e76c0bSRaphael Isemann SearchFilter *searcher) { 519ae34ed2cSRaphael Isemann PluginManager::AutoCompletePlatformName(request.GetCursorArgumentPrefix(), 520ae34ed2cSRaphael Isemann request); 521ab65b34fSGreg Clayton } 522ab65b34fSGreg Clayton 523ae34ed2cSRaphael Isemann void CommandCompletions::ArchitectureNames(CommandInterpreter &interpreter, 524a2e76c0bSRaphael Isemann CompletionRequest &request, 525a2e76c0bSRaphael Isemann SearchFilter *searcher) { 526ae34ed2cSRaphael Isemann ArchSpec::AutoComplete(request); 527ab65b34fSGreg Clayton } 528ab65b34fSGreg Clayton 529ae34ed2cSRaphael Isemann void CommandCompletions::VariablePath(CommandInterpreter &interpreter, 530a2e76c0bSRaphael Isemann CompletionRequest &request, 531a2e76c0bSRaphael Isemann SearchFilter *searcher) { 532ae34ed2cSRaphael Isemann Variable::AutoComplete(interpreter.GetExecutionContext(), request); 533f21feadcSGreg Clayton } 534