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/CommandCompletions.h"
27 #include "lldb/Interpreter/CommandInterpreter.h"
28 #include "lldb/Interpreter/OptionValueProperties.h"
29 #include "lldb/Symbol/CompileUnit.h"
30 #include "lldb/Symbol/Variable.h"
31 #include "lldb/Target/Target.h"
32 #include "lldb/Utility/Args.h"
33 #include "lldb/Utility/FileSpec.h"
34 #include "lldb/Utility/StreamString.h"
35 #include "lldb/Utility/TildeExpressionResolver.h"
37 #include "llvm/ADT/SmallString.h"
38 #include "llvm/Support/FileSystem.h"
39 #include "llvm/Support/Path.h"
41 using namespace lldb_private;
43 CommandCompletions::CommonCompletionElement
44 CommandCompletions::g_common_completions[] = {
45 {eCustomCompletion, nullptr},
46 {eSourceFileCompletion, CommandCompletions::SourceFiles},
47 {eDiskFileCompletion, CommandCompletions::DiskFiles},
48 {eDiskDirectoryCompletion, CommandCompletions::DiskDirectories},
49 {eSymbolCompletion, CommandCompletions::Symbols},
50 {eModuleCompletion, CommandCompletions::Modules},
51 {eSettingsNameCompletion, CommandCompletions::SettingsNames},
52 {ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames},
53 {eArchitectureCompletion, CommandCompletions::ArchitectureNames},
54 {eVariablePathCompletion, CommandCompletions::VariablePath},
55 {eNoCompletion, nullptr} // This one has to be last in the list.
58 bool CommandCompletions::InvokeCommonCompletionCallbacks(
59 CommandInterpreter &interpreter, uint32_t completion_mask,
60 CompletionRequest &request, SearchFilter *searcher) {
63 if (completion_mask & eCustomCompletion)
66 for (int i = 0;; i++) {
67 if (g_common_completions[i].type == eNoCompletion)
69 else if ((g_common_completions[i].type & completion_mask) ==
70 g_common_completions[i].type &&
71 g_common_completions[i].callback != nullptr) {
73 g_common_completions[i].callback(interpreter, request, searcher);
79 int CommandCompletions::SourceFiles(CommandInterpreter &interpreter,
80 CompletionRequest &request,
81 SearchFilter *searcher) {
82 request.SetWordComplete(true);
83 // Find some way to switch "include support files..."
84 SourceFileCompleter completer(interpreter, false, request);
86 if (searcher == nullptr) {
87 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
88 SearchFilterForUnconstrainedSearches null_searcher(target_sp);
89 completer.DoCompletion(&null_searcher);
91 completer.DoCompletion(searcher);
93 return request.GetNumberOfMatches();
96 static int DiskFilesOrDirectories(const llvm::Twine &partial_name,
97 bool only_directories, StringList &matches,
98 TildeExpressionResolver &Resolver) {
101 llvm::SmallString<256> CompletionBuffer;
102 llvm::SmallString<256> Storage;
103 partial_name.toVector(CompletionBuffer);
105 if (CompletionBuffer.size() >= PATH_MAX)
106 return matches.GetSize();
108 namespace fs = llvm::sys::fs;
109 namespace path = llvm::sys::path;
111 llvm::StringRef SearchDir;
112 llvm::StringRef PartialItem;
114 if (CompletionBuffer.startswith("~")) {
115 llvm::StringRef Buffer(CompletionBuffer);
117 Buffer.find_if([](char c) { return path::is_separator(c); });
119 llvm::StringRef Username = Buffer.take_front(FirstSep);
120 llvm::StringRef Remainder;
121 if (FirstSep != llvm::StringRef::npos)
122 Remainder = Buffer.drop_front(FirstSep + 1);
124 llvm::SmallString<PATH_MAX> Resolved;
125 if (!Resolver.ResolveExact(Username, Resolved)) {
126 // We couldn't resolve it as a full username. If there were no slashes
127 // then this might be a partial username. We try to resolve it as such
128 // but after that, we're done regardless of any matches.
129 if (FirstSep == llvm::StringRef::npos) {
130 llvm::StringSet<> MatchSet;
131 Resolver.ResolvePartial(Username, MatchSet);
132 for (const auto &S : MatchSet) {
133 Resolved = S.getKey();
134 path::append(Resolved, path::get_separator());
135 matches.AppendString(Resolved);
138 return matches.GetSize();
141 // If there was no trailing slash, then we're done as soon as we resolve
142 // the expression to the correct directory. Otherwise we need to continue
143 // looking for matches within that directory.
144 if (FirstSep == llvm::StringRef::npos) {
145 // Make sure it ends with a separator.
146 path::append(CompletionBuffer, path::get_separator());
147 matches.AppendString(CompletionBuffer);
148 return matches.GetSize();
151 // We want to keep the form the user typed, so we special case this to
152 // search in the fully resolved directory, but CompletionBuffer keeps the
153 // unmodified form that the user typed.
155 llvm::StringRef RemainderDir = path::parent_path(Remainder);
156 if (!RemainderDir.empty()) {
157 // Append the remaining path to the resolved directory.
158 Storage.append(path::get_separator());
159 Storage.append(RemainderDir);
163 SearchDir = path::parent_path(CompletionBuffer);
166 size_t FullPrefixLen = CompletionBuffer.size();
168 PartialItem = path::filename(CompletionBuffer);
169 if (PartialItem == ".")
170 PartialItem = llvm::StringRef();
172 if (SearchDir.empty()) {
173 llvm::sys::fs::current_path(Storage);
176 assert(!PartialItem.contains(path::get_separator()));
178 // SearchDir now contains the directory to search in, and Prefix contains the
179 // text we want to match against items in that directory.
182 fs::directory_iterator Iter(SearchDir, EC, false);
183 fs::directory_iterator End;
184 for (; Iter != End && !EC; Iter.increment(EC)) {
187 auto Name = path::filename(Entry.path());
190 if (Name == "." || Name == ".." || !Name.startswith(PartialItem))
195 llvm::ErrorOr<fs::basic_file_status> st = Entry.status();
199 // If it's a symlink, then we treat it as a directory as long as the target
201 bool is_dir = fs::is_directory(*st);
202 if (fs::is_symlink_file(*st)) {
203 fs::file_status target_st;
204 if (!fs::status(Entry.path(), target_st))
205 is_dir = fs::is_directory(target_st);
207 if (only_directories && !is_dir)
210 // Shrink it back down so that it just has the original prefix the user
211 // typed and remove the part of the name which is common to the located
212 // item and what the user typed.
213 CompletionBuffer.resize(FullPrefixLen);
214 Name = Name.drop_front(PartialItem.size());
215 CompletionBuffer.append(Name);
218 path::append(CompletionBuffer, path::get_separator());
221 matches.AppendString(CompletionBuffer);
224 return matches.GetSize();
227 static int DiskFilesOrDirectories(CompletionRequest &request,
228 bool only_directories) {
229 request.SetWordComplete(false);
230 StandardTildeExpressionResolver resolver;
232 DiskFilesOrDirectories(request.GetCursorArgumentPrefix(), only_directories,
234 request.AddCompletions(matches);
235 return request.GetNumberOfMatches();
238 int CommandCompletions::DiskFiles(CommandInterpreter &interpreter,
239 CompletionRequest &request,
240 SearchFilter *searcher) {
241 return DiskFilesOrDirectories(request, /*only_dirs*/ false);
244 int CommandCompletions::DiskFiles(const llvm::Twine &partial_file_name,
246 TildeExpressionResolver &Resolver) {
247 return DiskFilesOrDirectories(partial_file_name, false, matches, Resolver);
250 int CommandCompletions::DiskDirectories(CommandInterpreter &interpreter,
251 CompletionRequest &request,
252 SearchFilter *searcher) {
253 return DiskFilesOrDirectories(request, /*only_dirs*/ true);
256 int CommandCompletions::DiskDirectories(const llvm::Twine &partial_file_name,
258 TildeExpressionResolver &Resolver) {
259 return DiskFilesOrDirectories(partial_file_name, true, matches, Resolver);
262 int CommandCompletions::Modules(CommandInterpreter &interpreter,
263 CompletionRequest &request,
264 SearchFilter *searcher) {
265 request.SetWordComplete(true);
266 ModuleCompleter completer(interpreter, request);
268 if (searcher == nullptr) {
269 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
270 SearchFilterForUnconstrainedSearches null_searcher(target_sp);
271 completer.DoCompletion(&null_searcher);
273 completer.DoCompletion(searcher);
275 return request.GetNumberOfMatches();
278 int CommandCompletions::Symbols(CommandInterpreter &interpreter,
279 CompletionRequest &request,
280 SearchFilter *searcher) {
281 request.SetWordComplete(true);
282 SymbolCompleter completer(interpreter, request);
284 if (searcher == nullptr) {
285 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
286 SearchFilterForUnconstrainedSearches null_searcher(target_sp);
287 completer.DoCompletion(&null_searcher);
289 completer.DoCompletion(searcher);
291 return request.GetNumberOfMatches();
294 int CommandCompletions::SettingsNames(CommandInterpreter &interpreter,
295 CompletionRequest &request,
296 SearchFilter *searcher) {
297 // Cache the full setting name list
298 static StringList g_property_names;
299 if (g_property_names.GetSize() == 0) {
300 // Generate the full setting name list on demand
301 lldb::OptionValuePropertiesSP properties_sp(
302 interpreter.GetDebugger().GetValueProperties());
305 properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName);
306 const std::string &str = strm.GetString();
307 g_property_names.SplitIntoLines(str.c_str(), str.size());
311 size_t exact_matches_idx = SIZE_MAX;
313 g_property_names.AutoComplete(request.GetCursorArgumentPrefix(), matches,
315 request.SetWordComplete(exact_matches_idx != SIZE_MAX);
316 request.AddCompletions(matches);
317 return request.GetNumberOfMatches();
320 int CommandCompletions::PlatformPluginNames(CommandInterpreter &interpreter,
321 CompletionRequest &request,
322 SearchFilter *searcher) {
323 StringList new_matches;
324 std::size_t num_matches = PluginManager::AutoCompletePlatformName(
325 request.GetCursorArgumentPrefix(), new_matches);
326 request.SetWordComplete(num_matches == 1);
327 request.AddCompletions(new_matches);
328 return request.GetNumberOfMatches();
331 int CommandCompletions::ArchitectureNames(CommandInterpreter &interpreter,
332 CompletionRequest &request,
333 SearchFilter *searcher) {
334 const uint32_t num_matches = ArchSpec::AutoComplete(request);
335 request.SetWordComplete(num_matches == 1);
339 int CommandCompletions::VariablePath(CommandInterpreter &interpreter,
340 CompletionRequest &request,
341 SearchFilter *searcher) {
342 return Variable::AutoComplete(interpreter.GetExecutionContext(), request);
345 CommandCompletions::Completer::Completer(CommandInterpreter &interpreter,
346 CompletionRequest &request)
347 : m_interpreter(interpreter), m_request(request) {}
349 CommandCompletions::Completer::~Completer() = default;
351 //----------------------------------------------------------------------
352 // SourceFileCompleter
353 //----------------------------------------------------------------------
355 CommandCompletions::SourceFileCompleter::SourceFileCompleter(
356 CommandInterpreter &interpreter, bool include_support_files,
357 CompletionRequest &request)
358 : CommandCompletions::Completer(interpreter, request),
359 m_include_support_files(include_support_files), m_matching_files() {
360 FileSpec partial_spec(m_request.GetCursorArgumentPrefix(), false);
361 m_file_name = partial_spec.GetFilename().GetCString();
362 m_dir_name = partial_spec.GetDirectory().GetCString();
365 Searcher::Depth CommandCompletions::SourceFileCompleter::GetDepth() {
366 return eDepthCompUnit;
369 Searcher::CallbackReturn
370 CommandCompletions::SourceFileCompleter::SearchCallback(SearchFilter &filter,
371 SymbolContext &context,
374 if (context.comp_unit != nullptr) {
375 if (m_include_support_files) {
376 FileSpecList supporting_files = context.comp_unit->GetSupportFiles();
377 for (size_t sfiles = 0; sfiles < supporting_files.GetSize(); sfiles++) {
378 const FileSpec &sfile_spec =
379 supporting_files.GetFileSpecAtIndex(sfiles);
380 const char *sfile_file_name = sfile_spec.GetFilename().GetCString();
381 const char *sfile_dir_name = sfile_spec.GetFilename().GetCString();
383 if (m_file_name && sfile_file_name &&
384 strstr(sfile_file_name, m_file_name) == sfile_file_name)
386 if (match && m_dir_name && sfile_dir_name &&
387 strstr(sfile_dir_name, m_dir_name) != sfile_dir_name)
391 m_matching_files.AppendIfUnique(sfile_spec);
395 const char *cur_file_name = context.comp_unit->GetFilename().GetCString();
396 const char *cur_dir_name = context.comp_unit->GetDirectory().GetCString();
399 if (m_file_name && cur_file_name &&
400 strstr(cur_file_name, m_file_name) == cur_file_name)
403 if (match && m_dir_name && cur_dir_name &&
404 strstr(cur_dir_name, m_dir_name) != cur_dir_name)
408 m_matching_files.AppendIfUnique(context.comp_unit);
412 return Searcher::eCallbackReturnContinue;
416 CommandCompletions::SourceFileCompleter::DoCompletion(SearchFilter *filter) {
417 filter->Search(*this);
418 // Now convert the filelist to completions:
419 for (size_t i = 0; i < m_matching_files.GetSize(); i++) {
420 m_request.AddCompletion(
421 m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString());
423 return m_request.GetNumberOfMatches();
426 //----------------------------------------------------------------------
428 //----------------------------------------------------------------------
430 static bool regex_chars(const char comp) {
431 return (comp == '[' || comp == ']' || comp == '(' || comp == ')' ||
432 comp == '{' || comp == '}' || comp == '+' || comp == '.' ||
433 comp == '*' || comp == '|' || comp == '^' || comp == '$' ||
434 comp == '\\' || comp == '?');
437 CommandCompletions::SymbolCompleter::SymbolCompleter(
438 CommandInterpreter &interpreter, CompletionRequest &request)
439 : CommandCompletions::Completer(interpreter, request) {
440 std::string regex_str;
441 if (!m_request.GetCursorArgumentPrefix().empty()) {
442 regex_str.append("^");
443 regex_str.append(m_request.GetCursorArgumentPrefix());
445 // Match anything since the completion string is empty
446 regex_str.append(".");
448 std::string::iterator pos =
449 find_if(regex_str.begin() + 1, regex_str.end(), regex_chars);
450 while (pos < regex_str.end()) {
451 pos = regex_str.insert(pos, '\\');
452 pos = find_if(pos + 2, regex_str.end(), regex_chars);
454 m_regex.Compile(regex_str);
457 Searcher::Depth CommandCompletions::SymbolCompleter::GetDepth() {
461 Searcher::CallbackReturn CommandCompletions::SymbolCompleter::SearchCallback(
462 SearchFilter &filter, SymbolContext &context, Address *addr,
464 if (context.module_sp) {
465 SymbolContextList sc_list;
466 const bool include_symbols = true;
467 const bool include_inlines = true;
468 const bool append = true;
469 context.module_sp->FindFunctions(m_regex, include_symbols, include_inlines,
473 // Now add the functions & symbols to the list - only add if unique:
474 for (uint32_t i = 0; i < sc_list.GetSize(); i++) {
475 if (sc_list.GetContextAtIndex(i, sc)) {
476 ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled);
477 if (!func_name.IsEmpty())
478 m_match_set.insert(func_name);
482 return Searcher::eCallbackReturnContinue;
485 size_t CommandCompletions::SymbolCompleter::DoCompletion(SearchFilter *filter) {
486 filter->Search(*this);
487 collection::iterator pos = m_match_set.begin(), end = m_match_set.end();
488 for (pos = m_match_set.begin(); pos != end; pos++)
489 m_request.AddCompletion((*pos).GetCString());
491 return m_request.GetNumberOfMatches();
494 //----------------------------------------------------------------------
496 //----------------------------------------------------------------------
497 CommandCompletions::ModuleCompleter::ModuleCompleter(
498 CommandInterpreter &interpreter, CompletionRequest &request)
499 : CommandCompletions::Completer(interpreter, request) {
500 FileSpec partial_spec(m_request.GetCursorArgumentPrefix(), false);
501 m_file_name = partial_spec.GetFilename().GetCString();
502 m_dir_name = partial_spec.GetDirectory().GetCString();
505 Searcher::Depth CommandCompletions::ModuleCompleter::GetDepth() {
509 Searcher::CallbackReturn CommandCompletions::ModuleCompleter::SearchCallback(
510 SearchFilter &filter, SymbolContext &context, Address *addr,
512 if (context.module_sp) {
513 const char *cur_file_name =
514 context.module_sp->GetFileSpec().GetFilename().GetCString();
515 const char *cur_dir_name =
516 context.module_sp->GetFileSpec().GetDirectory().GetCString();
519 if (m_file_name && cur_file_name &&
520 strstr(cur_file_name, m_file_name) == cur_file_name)
523 if (match && m_dir_name && cur_dir_name &&
524 strstr(cur_dir_name, m_dir_name) != cur_dir_name)
528 m_request.AddCompletion(cur_file_name);
531 return Searcher::eCallbackReturnContinue;
534 size_t CommandCompletions::ModuleCompleter::DoCompletion(SearchFilter *filter) {
535 filter->Search(*this);
536 return m_request.GetNumberOfMatches();