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"
21 #include "lldb/Core/FileSpecList.h"
22 #include "lldb/Core/Module.h"
23 #include "lldb/Core/PluginManager.h"
24 #include "lldb/Host/FileSpec.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"
35 #include "llvm/ADT/SmallString.h"
37 using namespace lldb_private;
39 CommandCompletions::CommonCompletionElement
40 CommandCompletions::g_common_completions[] = {
41 {eCustomCompletion, nullptr},
42 {eSourceFileCompletion, CommandCompletions::SourceFiles},
43 {eDiskFileCompletion, CommandCompletions::DiskFiles},
44 {eDiskDirectoryCompletion, CommandCompletions::DiskDirectories},
45 {eSymbolCompletion, CommandCompletions::Symbols},
46 {eModuleCompletion, CommandCompletions::Modules},
47 {eSettingsNameCompletion, CommandCompletions::SettingsNames},
48 {ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames},
49 {eArchitectureCompletion, CommandCompletions::ArchitectureNames},
50 {eVariablePathCompletion, CommandCompletions::VariablePath},
51 {eNoCompletion, nullptr} // This one has to be last in the list.
54 bool CommandCompletions::InvokeCommonCompletionCallbacks(
55 CommandInterpreter &interpreter, uint32_t completion_mask,
56 llvm::StringRef completion_str, int match_start_point,
57 int max_return_elements, SearchFilter *searcher, bool &word_complete,
58 StringList &matches) {
61 if (completion_mask & eCustomCompletion)
64 for (int i = 0;; i++) {
65 if (g_common_completions[i].type == eNoCompletion)
67 else if ((g_common_completions[i].type & completion_mask) ==
68 g_common_completions[i].type &&
69 g_common_completions[i].callback != nullptr) {
71 g_common_completions[i].callback(interpreter, completion_str,
72 match_start_point, max_return_elements,
73 searcher, word_complete, matches);
79 int CommandCompletions::SourceFiles(CommandInterpreter &interpreter,
80 llvm::StringRef partial_file_name,
81 int match_start_point,
82 int max_return_elements,
83 SearchFilter *searcher, bool &word_complete,
84 StringList &matches) {
86 // Find some way to switch "include support files..."
87 SourceFileCompleter completer(interpreter, false, partial_file_name,
88 match_start_point, max_return_elements,
91 if (searcher == nullptr) {
92 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
93 SearchFilterForUnconstrainedSearches null_searcher(target_sp);
94 completer.DoCompletion(&null_searcher);
96 completer.DoCompletion(searcher);
98 return matches.GetSize();
101 typedef struct DiskFilesOrDirectoriesBaton {
102 const char *remainder;
103 char *partial_name_copy;
104 bool only_directories;
109 } DiskFilesOrDirectoriesBaton;
111 FileSpec::EnumerateDirectoryResult
112 DiskFilesOrDirectoriesCallback(void *baton, FileSpec::FileType file_type,
113 const FileSpec &spec) {
114 const char *name = spec.GetFilename().AsCString();
116 const DiskFilesOrDirectoriesBaton *parameters =
117 (DiskFilesOrDirectoriesBaton *)baton;
118 char *end_ptr = parameters->end_ptr;
119 char *partial_name_copy = parameters->partial_name_copy;
120 const char *remainder = parameters->remainder;
122 // Omit ".", ".." and any . files if the match string doesn't start with .
123 if (name[0] == '.') {
125 return FileSpec::eEnumerateDirectoryResultNext;
126 else if (name[1] == '.' && name[2] == '\0')
127 return FileSpec::eEnumerateDirectoryResultNext;
128 else if (remainder[0] != '.')
129 return FileSpec::eEnumerateDirectoryResultNext;
132 // If we found a directory, we put a "/" at the end of the name.
134 if (remainder[0] == '\0' || strstr(name, remainder) == name) {
135 if (strlen(name) + parameters->baselen >= PATH_MAX)
136 return FileSpec::eEnumerateDirectoryResultNext;
138 strcpy(end_ptr, name);
140 bool isa_directory = false;
141 if (file_type == FileSpec::eFileTypeDirectory)
142 isa_directory = true;
143 else if (file_type == FileSpec::eFileTypeSymbolicLink) {
144 if (FileSpec(partial_name_copy, false).IsDirectory())
145 isa_directory = true;
149 *parameters->saw_directory = true;
150 size_t len = strlen(parameters->partial_name_copy);
151 partial_name_copy[len] = '/';
152 partial_name_copy[len + 1] = '\0';
154 if (parameters->only_directories && !isa_directory)
155 return FileSpec::eEnumerateDirectoryResultNext;
156 parameters->matches->AppendString(partial_name_copy);
159 return FileSpec::eEnumerateDirectoryResultNext;
162 static int DiskFilesOrDirectories(llvm::StringRef partial_file_name,
163 bool only_directories, bool &saw_directory,
164 StringList &matches) {
165 // I'm going to use the "glob" function with GLOB_TILDE for user directory
167 // If it is not defined on your host system, you'll need to implement it
170 size_t partial_name_len = partial_file_name.size();
172 if (partial_name_len >= PATH_MAX)
173 return matches.GetSize();
175 // This copy of the string will be cut up into the directory part, and the
176 // remainder. end_ptr below will point to the place of the remainder in this
177 // string. Then when we've resolved the containing directory, and opened it,
178 // we'll read the directory contents and overwrite the partial_name_copy
179 // starting from end_ptr with each of the matches. Thus we will preserve the
180 // form the user originally typed.
182 char partial_name_copy[PATH_MAX];
183 memcpy(partial_name_copy, partial_file_name.data(), partial_name_len);
184 partial_name_copy[partial_name_len] = '\0';
186 // We'll need to save a copy of the remainder for comparison, which we do
188 char remainder[PATH_MAX];
190 // end_ptr will point past the last / in partial_name_copy, or if there is no
191 // slash to the beginning of the string.
194 end_ptr = strrchr(partial_name_copy, '/');
196 // This will store the resolved form of the containing directory
197 llvm::SmallString<64> containing_part;
199 if (end_ptr == nullptr) {
200 // There's no directory. If the thing begins with a "~" then this is a bare
202 if (*partial_name_copy == '~') {
203 // Nothing here but the user name. We could just put a slash on the end,
204 // but for completeness sake we'll resolve the user name and only put a
206 // on the end if it exists.
207 llvm::SmallString<64> resolved_username(partial_name_copy);
208 FileSpec::ResolveUsername(resolved_username);
210 // Not sure how this would happen, a username longer than PATH_MAX?
212 if (resolved_username.size() == 0) {
213 // The user name didn't resolve, let's look in the password database for
215 // The user name database contains duplicates, and is not in
216 // alphabetical order, so
217 // we'll use a set to manage that for us.
218 FileSpec::ResolvePartialUsername(partial_name_copy, matches);
219 if (matches.GetSize() > 0)
220 saw_directory = true;
221 return matches.GetSize();
223 // The thing exists, put a '/' on the end, and return it...
224 // FIXME: complete user names here:
225 partial_name_copy[partial_name_len] = '/';
226 partial_name_copy[partial_name_len + 1] = '\0';
227 matches.AppendString(partial_name_copy);
228 saw_directory = true;
229 return matches.GetSize();
232 // The containing part is the CWD, and the whole string is the remainder.
233 containing_part = ".";
234 strcpy(remainder, partial_name_copy);
235 end_ptr = partial_name_copy;
238 if (end_ptr == partial_name_copy) {
239 // We're completing a file or directory in the root volume.
240 containing_part = "/";
242 containing_part.append(partial_name_copy, end_ptr);
244 // Push end_ptr past the final "/" and set remainder.
246 strcpy(remainder, end_ptr);
249 // Look for a user name in the containing part, and if it's there, resolve it
251 // result back into the containing_part:
253 if (*partial_name_copy == '~') {
254 FileSpec::ResolveUsername(containing_part);
255 // User name doesn't exist, we're not getting any further...
256 if (containing_part.empty())
257 return matches.GetSize();
260 // Okay, containing_part is now the directory we want to open and look for
263 size_t baselen = end_ptr - partial_name_copy;
265 DiskFilesOrDirectoriesBaton parameters;
266 parameters.remainder = remainder;
267 parameters.partial_name_copy = partial_name_copy;
268 parameters.only_directories = only_directories;
269 parameters.saw_directory = &saw_directory;
270 parameters.matches = &matches;
271 parameters.end_ptr = end_ptr;
272 parameters.baselen = baselen;
274 FileSpec::EnumerateDirectory(containing_part.c_str(), true, true, true,
275 DiskFilesOrDirectoriesCallback, ¶meters);
277 return matches.GetSize();
280 int CommandCompletions::DiskFiles(CommandInterpreter &interpreter,
281 llvm::StringRef partial_file_name,
282 int match_start_point,
283 int max_return_elements,
284 SearchFilter *searcher, bool &word_complete,
285 StringList &matches) {
287 DiskFilesOrDirectories(partial_file_name, false, word_complete, matches);
288 word_complete = !word_complete;
292 int CommandCompletions::DiskDirectories(
293 CommandInterpreter &interpreter, llvm::StringRef partial_file_name,
294 int match_start_point, int max_return_elements, SearchFilter *searcher,
295 bool &word_complete, StringList &matches) {
297 DiskFilesOrDirectories(partial_file_name, true, word_complete, matches);
298 word_complete = false;
302 int CommandCompletions::Modules(CommandInterpreter &interpreter,
303 llvm::StringRef partial_file_name,
304 int match_start_point, int max_return_elements,
305 SearchFilter *searcher, bool &word_complete,
306 StringList &matches) {
307 word_complete = true;
308 ModuleCompleter completer(interpreter, partial_file_name, match_start_point,
309 max_return_elements, matches);
311 if (searcher == nullptr) {
312 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
313 SearchFilterForUnconstrainedSearches null_searcher(target_sp);
314 completer.DoCompletion(&null_searcher);
316 completer.DoCompletion(searcher);
318 return matches.GetSize();
321 int CommandCompletions::Symbols(CommandInterpreter &interpreter,
322 llvm::StringRef partial_file_name,
323 int match_start_point, int max_return_elements,
324 SearchFilter *searcher, bool &word_complete,
325 StringList &matches) {
326 word_complete = true;
327 SymbolCompleter completer(interpreter, partial_file_name, match_start_point,
328 max_return_elements, matches);
330 if (searcher == nullptr) {
331 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
332 SearchFilterForUnconstrainedSearches null_searcher(target_sp);
333 completer.DoCompletion(&null_searcher);
335 completer.DoCompletion(searcher);
337 return matches.GetSize();
340 int CommandCompletions::SettingsNames(
341 CommandInterpreter &interpreter, llvm::StringRef partial_setting_name,
342 int match_start_point, int max_return_elements, SearchFilter *searcher,
343 bool &word_complete, StringList &matches) {
344 // Cache the full setting name list
345 static StringList g_property_names;
346 if (g_property_names.GetSize() == 0) {
347 // Generate the full setting name list on demand
348 lldb::OptionValuePropertiesSP properties_sp(
349 interpreter.GetDebugger().GetValueProperties());
352 properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName);
353 const std::string &str = strm.GetString();
354 g_property_names.SplitIntoLines(str.c_str(), str.size());
358 size_t exact_matches_idx = SIZE_MAX;
359 const size_t num_matches = g_property_names.AutoComplete(
360 partial_setting_name, matches, exact_matches_idx);
361 word_complete = exact_matches_idx != SIZE_MAX;
365 int CommandCompletions::PlatformPluginNames(
366 CommandInterpreter &interpreter, llvm::StringRef partial_name,
367 int match_start_point, int max_return_elements, SearchFilter *searcher,
368 bool &word_complete, lldb_private::StringList &matches) {
369 const uint32_t num_matches =
370 PluginManager::AutoCompletePlatformName(partial_name, matches);
371 word_complete = num_matches == 1;
375 int CommandCompletions::ArchitectureNames(
376 CommandInterpreter &interpreter, llvm::StringRef partial_name,
377 int match_start_point, int max_return_elements, SearchFilter *searcher,
378 bool &word_complete, lldb_private::StringList &matches) {
379 const uint32_t num_matches = ArchSpec::AutoComplete(partial_name, matches);
380 word_complete = num_matches == 1;
384 int CommandCompletions::VariablePath(
385 CommandInterpreter &interpreter, llvm::StringRef partial_name,
386 int match_start_point, int max_return_elements, SearchFilter *searcher,
387 bool &word_complete, lldb_private::StringList &matches) {
388 return Variable::AutoComplete(interpreter.GetExecutionContext(), partial_name,
389 matches, word_complete);
392 CommandCompletions::Completer::Completer(CommandInterpreter &interpreter,
393 llvm::StringRef completion_str,
394 int match_start_point,
395 int max_return_elements,
397 : m_interpreter(interpreter), m_completion_str(completion_str),
398 m_match_start_point(match_start_point),
399 m_max_return_elements(max_return_elements), m_matches(matches) {}
401 CommandCompletions::Completer::~Completer() = default;
403 //----------------------------------------------------------------------
404 // SourceFileCompleter
405 //----------------------------------------------------------------------
407 CommandCompletions::SourceFileCompleter::SourceFileCompleter(
408 CommandInterpreter &interpreter, bool include_support_files,
409 llvm::StringRef completion_str, int match_start_point,
410 int max_return_elements, StringList &matches)
411 : CommandCompletions::Completer(interpreter, completion_str,
412 match_start_point, max_return_elements,
414 m_include_support_files(include_support_files), m_matching_files() {
415 FileSpec partial_spec(m_completion_str, false);
416 m_file_name = partial_spec.GetFilename().GetCString();
417 m_dir_name = partial_spec.GetDirectory().GetCString();
420 Searcher::Depth CommandCompletions::SourceFileCompleter::GetDepth() {
421 return eDepthCompUnit;
424 Searcher::CallbackReturn
425 CommandCompletions::SourceFileCompleter::SearchCallback(SearchFilter &filter,
426 SymbolContext &context,
429 if (context.comp_unit != nullptr) {
430 if (m_include_support_files) {
431 FileSpecList supporting_files = context.comp_unit->GetSupportFiles();
432 for (size_t sfiles = 0; sfiles < supporting_files.GetSize(); sfiles++) {
433 const FileSpec &sfile_spec =
434 supporting_files.GetFileSpecAtIndex(sfiles);
435 const char *sfile_file_name = sfile_spec.GetFilename().GetCString();
436 const char *sfile_dir_name = sfile_spec.GetFilename().GetCString();
438 if (m_file_name && sfile_file_name &&
439 strstr(sfile_file_name, m_file_name) == sfile_file_name)
441 if (match && m_dir_name && sfile_dir_name &&
442 strstr(sfile_dir_name, m_dir_name) != sfile_dir_name)
446 m_matching_files.AppendIfUnique(sfile_spec);
450 const char *cur_file_name = context.comp_unit->GetFilename().GetCString();
451 const char *cur_dir_name = context.comp_unit->GetDirectory().GetCString();
454 if (m_file_name && cur_file_name &&
455 strstr(cur_file_name, m_file_name) == cur_file_name)
458 if (match && m_dir_name && cur_dir_name &&
459 strstr(cur_dir_name, m_dir_name) != cur_dir_name)
463 m_matching_files.AppendIfUnique(context.comp_unit);
467 return Searcher::eCallbackReturnContinue;
471 CommandCompletions::SourceFileCompleter::DoCompletion(SearchFilter *filter) {
472 filter->Search(*this);
473 // Now convert the filelist to completions:
474 for (size_t i = 0; i < m_matching_files.GetSize(); i++) {
475 m_matches.AppendString(
476 m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString());
478 return m_matches.GetSize();
481 //----------------------------------------------------------------------
483 //----------------------------------------------------------------------
485 static bool regex_chars(const char comp) {
486 return (comp == '[' || comp == ']' || comp == '(' || comp == ')' ||
487 comp == '{' || comp == '}' || comp == '+' || comp == '.' ||
488 comp == '*' || comp == '|' || comp == '^' || comp == '$' ||
489 comp == '\\' || comp == '?');
492 CommandCompletions::SymbolCompleter::SymbolCompleter(
493 CommandInterpreter &interpreter, llvm::StringRef completion_str,
494 int match_start_point, int max_return_elements, StringList &matches)
495 : CommandCompletions::Completer(interpreter, completion_str,
496 match_start_point, max_return_elements,
498 std::string regex_str;
499 if (!completion_str.empty()) {
500 regex_str.append("^");
501 regex_str.append(completion_str);
503 // Match anything since the completion string is empty
504 regex_str.append(".");
506 std::string::iterator pos =
507 find_if(regex_str.begin() + 1, regex_str.end(), regex_chars);
508 while (pos < regex_str.end()) {
509 pos = regex_str.insert(pos, '\\');
510 pos = find_if(pos + 2, regex_str.end(), regex_chars);
512 m_regex.Compile(regex_str);
515 Searcher::Depth CommandCompletions::SymbolCompleter::GetDepth() {
519 Searcher::CallbackReturn CommandCompletions::SymbolCompleter::SearchCallback(
520 SearchFilter &filter, SymbolContext &context, Address *addr,
522 if (context.module_sp) {
523 SymbolContextList sc_list;
524 const bool include_symbols = true;
525 const bool include_inlines = true;
526 const bool append = true;
527 context.module_sp->FindFunctions(m_regex, include_symbols, include_inlines,
531 // Now add the functions & symbols to the list - only add if unique:
532 for (uint32_t i = 0; i < sc_list.GetSize(); i++) {
533 if (sc_list.GetContextAtIndex(i, sc)) {
534 ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled);
535 if (!func_name.IsEmpty())
536 m_match_set.insert(func_name);
540 return Searcher::eCallbackReturnContinue;
543 size_t CommandCompletions::SymbolCompleter::DoCompletion(SearchFilter *filter) {
544 filter->Search(*this);
545 collection::iterator pos = m_match_set.begin(), end = m_match_set.end();
546 for (pos = m_match_set.begin(); pos != end; pos++)
547 m_matches.AppendString((*pos).GetCString());
549 return m_matches.GetSize();
552 //----------------------------------------------------------------------
554 //----------------------------------------------------------------------
555 CommandCompletions::ModuleCompleter::ModuleCompleter(
556 CommandInterpreter &interpreter, llvm::StringRef completion_str,
557 int match_start_point, int max_return_elements, StringList &matches)
558 : CommandCompletions::Completer(interpreter, completion_str,
559 match_start_point, max_return_elements,
561 FileSpec partial_spec(m_completion_str, false);
562 m_file_name = partial_spec.GetFilename().GetCString();
563 m_dir_name = partial_spec.GetDirectory().GetCString();
566 Searcher::Depth CommandCompletions::ModuleCompleter::GetDepth() {
570 Searcher::CallbackReturn CommandCompletions::ModuleCompleter::SearchCallback(
571 SearchFilter &filter, SymbolContext &context, Address *addr,
573 if (context.module_sp) {
574 const char *cur_file_name =
575 context.module_sp->GetFileSpec().GetFilename().GetCString();
576 const char *cur_dir_name =
577 context.module_sp->GetFileSpec().GetDirectory().GetCString();
580 if (m_file_name && cur_file_name &&
581 strstr(cur_file_name, m_file_name) == cur_file_name)
584 if (match && m_dir_name && cur_dir_name &&
585 strstr(cur_dir_name, m_dir_name) != cur_dir_name)
589 m_matches.AppendString(cur_file_name);
592 return Searcher::eCallbackReturnContinue;
595 size_t CommandCompletions::ModuleCompleter::DoCompletion(SearchFilter *filter) {
596 filter->Search(*this);
597 return m_matches.GetSize();