1 //===-- CommandCompletions.cpp ----------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
12 #if defined(__APPLE__) || defined(__linux__)
17 // Other libraries and framework includes
18 #include "llvm/ADT/SmallString.h"
19 #include "llvm/ADT/StringSet.h"
22 #include "lldb/Core/FileSpecList.h"
23 #include "lldb/Core/Module.h"
24 #include "lldb/Core/PluginManager.h"
25 #include "lldb/Host/FileSystem.h"
26 #include "lldb/Interpreter/Args.h"
27 #include "lldb/Interpreter/CommandCompletions.h"
28 #include "lldb/Interpreter/CommandInterpreter.h"
29 #include "lldb/Interpreter/OptionValueProperties.h"
30 #include "lldb/Symbol/CompileUnit.h"
31 #include "lldb/Symbol/Variable.h"
32 #include "lldb/Target/Target.h"
33 #include "lldb/Utility/CleanUp.h"
34 #include "lldb/Utility/FileSpec.h"
35 #include "lldb/Utility/StreamString.h"
36 #include "lldb/Utility/TildeExpressionResolver.h"
38 #include "llvm/ADT/SmallString.h"
39 #include "llvm/Support/FileSystem.h"
40 #include "llvm/Support/Path.h"
42 using namespace lldb_private;
44 CommandCompletions::CommonCompletionElement
45 CommandCompletions::g_common_completions[] = {
46 {eCustomCompletion, nullptr},
47 {eSourceFileCompletion, CommandCompletions::SourceFiles},
48 {eDiskFileCompletion, CommandCompletions::DiskFiles},
49 {eDiskDirectoryCompletion, CommandCompletions::DiskDirectories},
50 {eSymbolCompletion, CommandCompletions::Symbols},
51 {eModuleCompletion, CommandCompletions::Modules},
52 {eSettingsNameCompletion, CommandCompletions::SettingsNames},
53 {ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames},
54 {eArchitectureCompletion, CommandCompletions::ArchitectureNames},
55 {eVariablePathCompletion, CommandCompletions::VariablePath},
56 {eNoCompletion, nullptr} // This one has to be last in the list.
59 bool CommandCompletions::InvokeCommonCompletionCallbacks(
60 CommandInterpreter &interpreter, uint32_t completion_mask,
61 llvm::StringRef completion_str, int match_start_point,
62 int max_return_elements, SearchFilter *searcher, bool &word_complete,
63 StringList &matches) {
66 if (completion_mask & eCustomCompletion)
69 for (int i = 0;; i++) {
70 if (g_common_completions[i].type == eNoCompletion)
72 else if ((g_common_completions[i].type & completion_mask) ==
73 g_common_completions[i].type &&
74 g_common_completions[i].callback != nullptr) {
76 g_common_completions[i].callback(interpreter, completion_str,
77 match_start_point, max_return_elements,
78 searcher, word_complete, matches);
84 int CommandCompletions::SourceFiles(CommandInterpreter &interpreter,
85 llvm::StringRef partial_file_name,
86 int match_start_point,
87 int max_return_elements,
88 SearchFilter *searcher, bool &word_complete,
89 StringList &matches) {
91 // Find some way to switch "include support files..."
92 SourceFileCompleter completer(interpreter, false, partial_file_name,
93 match_start_point, max_return_elements,
96 if (searcher == nullptr) {
97 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
98 SearchFilterForUnconstrainedSearches null_searcher(target_sp);
99 completer.DoCompletion(&null_searcher);
101 completer.DoCompletion(searcher);
103 return matches.GetSize();
106 static int DiskFilesOrDirectories(const llvm::Twine &partial_name,
107 bool only_directories, bool &saw_directory,
109 TildeExpressionResolver &Resolver) {
112 llvm::SmallString<256> CompletionBuffer;
113 llvm::SmallString<256> Storage;
114 partial_name.toVector(CompletionBuffer);
116 if (CompletionBuffer.size() >= PATH_MAX)
119 namespace fs = llvm::sys::fs;
120 namespace path = llvm::sys::path;
122 llvm::StringRef SearchDir;
123 llvm::StringRef PartialItem;
125 if (CompletionBuffer.startswith("~")) {
126 llvm::StringRef Buffer(CompletionBuffer);
128 Buffer.find_if([](char c) { return path::is_separator(c); });
130 llvm::StringRef Username = Buffer.take_front(FirstSep);
131 llvm::StringRef Remainder;
132 if (FirstSep != llvm::StringRef::npos)
133 Remainder = Buffer.drop_front(FirstSep + 1);
135 llvm::SmallString<PATH_MAX> Resolved;
136 if (!Resolver.ResolveExact(Username, Resolved)) {
137 // We couldn't resolve it as a full username. If there were no slashes
138 // then this might be a partial username. We try to resolve it as such
139 // but after that, we're done regardless of any matches.
140 if (FirstSep == llvm::StringRef::npos) {
141 llvm::StringSet<> MatchSet;
142 saw_directory = Resolver.ResolvePartial(Username, MatchSet);
143 for (const auto &S : MatchSet) {
144 Resolved = S.getKey();
145 path::append(Resolved, path::get_separator());
146 matches.AppendString(Resolved);
148 saw_directory = (matches.GetSize() > 0);
150 return matches.GetSize();
153 // If there was no trailing slash, then we're done as soon as we resolve the
154 // expression to the correct directory. Otherwise we need to continue
155 // looking for matches within that directory.
156 if (FirstSep == llvm::StringRef::npos) {
157 // Make sure it ends with a separator.
158 path::append(CompletionBuffer, path::get_separator());
159 saw_directory = true;
160 matches.AppendString(CompletionBuffer);
164 // We want to keep the form the user typed, so we special case this to
165 // search in the fully resolved directory, but CompletionBuffer keeps the
166 // unmodified form that the user typed.
168 SearchDir = Resolved;
170 SearchDir = path::parent_path(CompletionBuffer);
173 size_t FullPrefixLen = CompletionBuffer.size();
175 PartialItem = path::filename(CompletionBuffer);
176 if (PartialItem == ".")
177 PartialItem = llvm::StringRef();
179 if (SearchDir.empty()) {
180 llvm::sys::fs::current_path(Storage);
183 assert(!PartialItem.contains(path::get_separator()));
185 // SearchDir now contains the directory to search in, and Prefix contains the
186 // text we want to match against items in that directory.
189 fs::directory_iterator Iter(SearchDir, EC, false);
190 fs::directory_iterator End;
191 for (; Iter != End && !EC; Iter.increment(EC)) {
194 auto Name = path::filename(Entry.path());
197 if (Name == "." || Name == ".." || !Name.startswith(PartialItem))
202 llvm::ErrorOr<fs::basic_file_status> st = Entry.status();
206 // If it's a symlink, then we treat it as a directory as long as the target
208 bool is_dir = fs::is_directory(*st);
209 if (fs::is_symlink_file(*st)) {
210 fs::file_status target_st;
211 if (!fs::status(Entry.path(), target_st))
212 is_dir = fs::is_directory(target_st);
214 if (only_directories && !is_dir)
217 // Shrink it back down so that it just has the original prefix the user
218 // typed and remove the part of the name which is common to the located
219 // item and what the user typed.
220 CompletionBuffer.resize(FullPrefixLen);
221 Name = Name.drop_front(PartialItem.size());
222 CompletionBuffer.append(Name);
225 saw_directory = true;
226 path::append(CompletionBuffer, path::get_separator());
229 matches.AppendString(CompletionBuffer);
232 return matches.GetSize();
235 int CommandCompletions::DiskFiles(CommandInterpreter &interpreter,
236 llvm::StringRef partial_file_name,
237 int match_start_point,
238 int max_return_elements,
239 SearchFilter *searcher, bool &word_complete,
240 StringList &matches) {
241 word_complete = false;
242 StandardTildeExpressionResolver Resolver;
243 return DiskFiles(partial_file_name, matches, Resolver);
246 int CommandCompletions::DiskFiles(const llvm::Twine &partial_file_name,
248 TildeExpressionResolver &Resolver) {
250 int ret_val = DiskFilesOrDirectories(partial_file_name, false, word_complete,
255 int CommandCompletions::DiskDirectories(
256 CommandInterpreter &interpreter, llvm::StringRef partial_file_name,
257 int match_start_point, int max_return_elements, SearchFilter *searcher,
258 bool &word_complete, StringList &matches) {
259 word_complete = false;
260 StandardTildeExpressionResolver Resolver;
261 return DiskDirectories(partial_file_name, matches, Resolver);
264 int CommandCompletions::DiskDirectories(const llvm::Twine &partial_file_name,
266 TildeExpressionResolver &Resolver) {
268 int ret_val = DiskFilesOrDirectories(partial_file_name, true, word_complete,
273 int CommandCompletions::Modules(CommandInterpreter &interpreter,
274 llvm::StringRef partial_file_name,
275 int match_start_point, int max_return_elements,
276 SearchFilter *searcher, bool &word_complete,
277 StringList &matches) {
278 word_complete = true;
279 ModuleCompleter completer(interpreter, partial_file_name, match_start_point,
280 max_return_elements, matches);
282 if (searcher == nullptr) {
283 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
284 SearchFilterForUnconstrainedSearches null_searcher(target_sp);
285 completer.DoCompletion(&null_searcher);
287 completer.DoCompletion(searcher);
289 return matches.GetSize();
292 int CommandCompletions::Symbols(CommandInterpreter &interpreter,
293 llvm::StringRef partial_file_name,
294 int match_start_point, int max_return_elements,
295 SearchFilter *searcher, bool &word_complete,
296 StringList &matches) {
297 word_complete = true;
298 SymbolCompleter completer(interpreter, partial_file_name, match_start_point,
299 max_return_elements, matches);
301 if (searcher == nullptr) {
302 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
303 SearchFilterForUnconstrainedSearches null_searcher(target_sp);
304 completer.DoCompletion(&null_searcher);
306 completer.DoCompletion(searcher);
308 return matches.GetSize();
311 int CommandCompletions::SettingsNames(
312 CommandInterpreter &interpreter, llvm::StringRef partial_setting_name,
313 int match_start_point, int max_return_elements, SearchFilter *searcher,
314 bool &word_complete, StringList &matches) {
315 // Cache the full setting name list
316 static StringList g_property_names;
317 if (g_property_names.GetSize() == 0) {
318 // Generate the full setting name list on demand
319 lldb::OptionValuePropertiesSP properties_sp(
320 interpreter.GetDebugger().GetValueProperties());
323 properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName);
324 const std::string &str = strm.GetString();
325 g_property_names.SplitIntoLines(str.c_str(), str.size());
329 size_t exact_matches_idx = SIZE_MAX;
330 const size_t num_matches = g_property_names.AutoComplete(
331 partial_setting_name, matches, exact_matches_idx);
332 word_complete = exact_matches_idx != SIZE_MAX;
336 int CommandCompletions::PlatformPluginNames(
337 CommandInterpreter &interpreter, llvm::StringRef partial_name,
338 int match_start_point, int max_return_elements, SearchFilter *searcher,
339 bool &word_complete, lldb_private::StringList &matches) {
340 const uint32_t num_matches =
341 PluginManager::AutoCompletePlatformName(partial_name, matches);
342 word_complete = num_matches == 1;
346 int CommandCompletions::ArchitectureNames(
347 CommandInterpreter &interpreter, llvm::StringRef partial_name,
348 int match_start_point, int max_return_elements, SearchFilter *searcher,
349 bool &word_complete, lldb_private::StringList &matches) {
350 const uint32_t num_matches = ArchSpec::AutoComplete(partial_name, matches);
351 word_complete = num_matches == 1;
355 int CommandCompletions::VariablePath(
356 CommandInterpreter &interpreter, llvm::StringRef partial_name,
357 int match_start_point, int max_return_elements, SearchFilter *searcher,
358 bool &word_complete, lldb_private::StringList &matches) {
359 return Variable::AutoComplete(interpreter.GetExecutionContext(), partial_name,
360 matches, word_complete);
363 CommandCompletions::Completer::Completer(CommandInterpreter &interpreter,
364 llvm::StringRef completion_str,
365 int match_start_point,
366 int max_return_elements,
368 : m_interpreter(interpreter), m_completion_str(completion_str),
369 m_match_start_point(match_start_point),
370 m_max_return_elements(max_return_elements), m_matches(matches) {}
372 CommandCompletions::Completer::~Completer() = default;
374 //----------------------------------------------------------------------
375 // SourceFileCompleter
376 //----------------------------------------------------------------------
378 CommandCompletions::SourceFileCompleter::SourceFileCompleter(
379 CommandInterpreter &interpreter, bool include_support_files,
380 llvm::StringRef completion_str, int match_start_point,
381 int max_return_elements, StringList &matches)
382 : CommandCompletions::Completer(interpreter, completion_str,
383 match_start_point, max_return_elements,
385 m_include_support_files(include_support_files), m_matching_files() {
386 FileSpec partial_spec(m_completion_str, false);
387 m_file_name = partial_spec.GetFilename().GetCString();
388 m_dir_name = partial_spec.GetDirectory().GetCString();
391 Searcher::Depth CommandCompletions::SourceFileCompleter::GetDepth() {
392 return eDepthCompUnit;
395 Searcher::CallbackReturn
396 CommandCompletions::SourceFileCompleter::SearchCallback(SearchFilter &filter,
397 SymbolContext &context,
400 if (context.comp_unit != nullptr) {
401 if (m_include_support_files) {
402 FileSpecList supporting_files = context.comp_unit->GetSupportFiles();
403 for (size_t sfiles = 0; sfiles < supporting_files.GetSize(); sfiles++) {
404 const FileSpec &sfile_spec =
405 supporting_files.GetFileSpecAtIndex(sfiles);
406 const char *sfile_file_name = sfile_spec.GetFilename().GetCString();
407 const char *sfile_dir_name = sfile_spec.GetFilename().GetCString();
409 if (m_file_name && sfile_file_name &&
410 strstr(sfile_file_name, m_file_name) == sfile_file_name)
412 if (match && m_dir_name && sfile_dir_name &&
413 strstr(sfile_dir_name, m_dir_name) != sfile_dir_name)
417 m_matching_files.AppendIfUnique(sfile_spec);
421 const char *cur_file_name = context.comp_unit->GetFilename().GetCString();
422 const char *cur_dir_name = context.comp_unit->GetDirectory().GetCString();
425 if (m_file_name && cur_file_name &&
426 strstr(cur_file_name, m_file_name) == cur_file_name)
429 if (match && m_dir_name && cur_dir_name &&
430 strstr(cur_dir_name, m_dir_name) != cur_dir_name)
434 m_matching_files.AppendIfUnique(context.comp_unit);
438 return Searcher::eCallbackReturnContinue;
442 CommandCompletions::SourceFileCompleter::DoCompletion(SearchFilter *filter) {
443 filter->Search(*this);
444 // Now convert the filelist to completions:
445 for (size_t i = 0; i < m_matching_files.GetSize(); i++) {
446 m_matches.AppendString(
447 m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString());
449 return m_matches.GetSize();
452 //----------------------------------------------------------------------
454 //----------------------------------------------------------------------
456 static bool regex_chars(const char comp) {
457 return (comp == '[' || comp == ']' || comp == '(' || comp == ')' ||
458 comp == '{' || comp == '}' || comp == '+' || comp == '.' ||
459 comp == '*' || comp == '|' || comp == '^' || comp == '$' ||
460 comp == '\\' || comp == '?');
463 CommandCompletions::SymbolCompleter::SymbolCompleter(
464 CommandInterpreter &interpreter, llvm::StringRef completion_str,
465 int match_start_point, int max_return_elements, StringList &matches)
466 : CommandCompletions::Completer(interpreter, completion_str,
467 match_start_point, max_return_elements,
469 std::string regex_str;
470 if (!completion_str.empty()) {
471 regex_str.append("^");
472 regex_str.append(completion_str);
474 // Match anything since the completion string is empty
475 regex_str.append(".");
477 std::string::iterator pos =
478 find_if(regex_str.begin() + 1, regex_str.end(), regex_chars);
479 while (pos < regex_str.end()) {
480 pos = regex_str.insert(pos, '\\');
481 pos = find_if(pos + 2, regex_str.end(), regex_chars);
483 m_regex.Compile(regex_str);
486 Searcher::Depth CommandCompletions::SymbolCompleter::GetDepth() {
490 Searcher::CallbackReturn CommandCompletions::SymbolCompleter::SearchCallback(
491 SearchFilter &filter, SymbolContext &context, Address *addr,
493 if (context.module_sp) {
494 SymbolContextList sc_list;
495 const bool include_symbols = true;
496 const bool include_inlines = true;
497 const bool append = true;
498 context.module_sp->FindFunctions(m_regex, include_symbols, include_inlines,
502 // Now add the functions & symbols to the list - only add if unique:
503 for (uint32_t i = 0; i < sc_list.GetSize(); i++) {
504 if (sc_list.GetContextAtIndex(i, sc)) {
505 ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled);
506 if (!func_name.IsEmpty())
507 m_match_set.insert(func_name);
511 return Searcher::eCallbackReturnContinue;
514 size_t CommandCompletions::SymbolCompleter::DoCompletion(SearchFilter *filter) {
515 filter->Search(*this);
516 collection::iterator pos = m_match_set.begin(), end = m_match_set.end();
517 for (pos = m_match_set.begin(); pos != end; pos++)
518 m_matches.AppendString((*pos).GetCString());
520 return m_matches.GetSize();
523 //----------------------------------------------------------------------
525 //----------------------------------------------------------------------
526 CommandCompletions::ModuleCompleter::ModuleCompleter(
527 CommandInterpreter &interpreter, llvm::StringRef completion_str,
528 int match_start_point, int max_return_elements, StringList &matches)
529 : CommandCompletions::Completer(interpreter, completion_str,
530 match_start_point, max_return_elements,
532 FileSpec partial_spec(m_completion_str, false);
533 m_file_name = partial_spec.GetFilename().GetCString();
534 m_dir_name = partial_spec.GetDirectory().GetCString();
537 Searcher::Depth CommandCompletions::ModuleCompleter::GetDepth() {
541 Searcher::CallbackReturn CommandCompletions::ModuleCompleter::SearchCallback(
542 SearchFilter &filter, SymbolContext &context, Address *addr,
544 if (context.module_sp) {
545 const char *cur_file_name =
546 context.module_sp->GetFileSpec().GetFilename().GetCString();
547 const char *cur_dir_name =
548 context.module_sp->GetFileSpec().GetDirectory().GetCString();
551 if (m_file_name && cur_file_name &&
552 strstr(cur_file_name, m_file_name) == cur_file_name)
555 if (match && m_dir_name && cur_dir_name &&
556 strstr(cur_dir_name, m_dir_name) != cur_dir_name)
560 m_matches.AppendString(cur_file_name);
563 return Searcher::eCallbackReturnContinue;
566 size_t CommandCompletions::ModuleCompleter::DoCompletion(SearchFilter *filter) {
567 filter->Search(*this);
568 return m_matches.GetSize();