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.GetMatches().GetSize();
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)
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);
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 int CommandCompletions::DiskFiles(CommandInterpreter &interpreter,
228 CompletionRequest &request,
229 SearchFilter *searcher) {
230 request.SetWordComplete(false);
231 StandardTildeExpressionResolver Resolver;
232 return DiskFiles(request.GetCursorArgumentPrefix(), request.GetMatches(),
236 int CommandCompletions::DiskFiles(const llvm::Twine &partial_file_name,
238 TildeExpressionResolver &Resolver) {
239 return DiskFilesOrDirectories(partial_file_name, false, matches, Resolver);
242 int CommandCompletions::DiskDirectories(CommandInterpreter &interpreter,
243 CompletionRequest &request,
244 SearchFilter *searcher) {
245 request.SetWordComplete(false);
246 StandardTildeExpressionResolver Resolver;
247 return DiskDirectories(request.GetCursorArgumentPrefix(),
248 request.GetMatches(), Resolver);
251 int CommandCompletions::DiskDirectories(const llvm::Twine &partial_file_name,
253 TildeExpressionResolver &Resolver) {
254 return DiskFilesOrDirectories(partial_file_name, true, matches, Resolver);
257 int CommandCompletions::Modules(CommandInterpreter &interpreter,
258 CompletionRequest &request,
259 SearchFilter *searcher) {
260 request.SetWordComplete(true);
261 ModuleCompleter completer(interpreter, request);
263 if (searcher == nullptr) {
264 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
265 SearchFilterForUnconstrainedSearches null_searcher(target_sp);
266 completer.DoCompletion(&null_searcher);
268 completer.DoCompletion(searcher);
270 return request.GetMatches().GetSize();
273 int CommandCompletions::Symbols(CommandInterpreter &interpreter,
274 CompletionRequest &request,
275 SearchFilter *searcher) {
276 request.SetWordComplete(true);
277 SymbolCompleter completer(interpreter, request);
279 if (searcher == nullptr) {
280 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
281 SearchFilterForUnconstrainedSearches null_searcher(target_sp);
282 completer.DoCompletion(&null_searcher);
284 completer.DoCompletion(searcher);
286 return request.GetMatches().GetSize();
289 int CommandCompletions::SettingsNames(CommandInterpreter &interpreter,
290 CompletionRequest &request,
291 SearchFilter *searcher) {
292 // Cache the full setting name list
293 static StringList g_property_names;
294 if (g_property_names.GetSize() == 0) {
295 // Generate the full setting name list on demand
296 lldb::OptionValuePropertiesSP properties_sp(
297 interpreter.GetDebugger().GetValueProperties());
300 properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName);
301 const std::string &str = strm.GetString();
302 g_property_names.SplitIntoLines(str.c_str(), str.size());
306 size_t exact_matches_idx = SIZE_MAX;
307 const size_t num_matches =
308 g_property_names.AutoComplete(request.GetCursorArgumentPrefix(),
309 request.GetMatches(), exact_matches_idx);
310 request.SetWordComplete(exact_matches_idx != SIZE_MAX);
314 int CommandCompletions::PlatformPluginNames(CommandInterpreter &interpreter,
315 CompletionRequest &request,
316 SearchFilter *searcher) {
317 const uint32_t num_matches = PluginManager::AutoCompletePlatformName(
318 request.GetCursorArgumentPrefix(), request.GetMatches());
319 request.SetWordComplete(num_matches == 1);
323 int CommandCompletions::ArchitectureNames(CommandInterpreter &interpreter,
324 CompletionRequest &request,
325 SearchFilter *searcher) {
326 const uint32_t num_matches = ArchSpec::AutoComplete(request);
327 request.SetWordComplete(num_matches == 1);
331 int CommandCompletions::VariablePath(CommandInterpreter &interpreter,
332 CompletionRequest &request,
333 SearchFilter *searcher) {
334 return Variable::AutoComplete(interpreter.GetExecutionContext(), request);
337 CommandCompletions::Completer::Completer(CommandInterpreter &interpreter,
338 CompletionRequest &request)
339 : m_interpreter(interpreter), m_request(request) {}
341 CommandCompletions::Completer::~Completer() = default;
343 //----------------------------------------------------------------------
344 // SourceFileCompleter
345 //----------------------------------------------------------------------
347 CommandCompletions::SourceFileCompleter::SourceFileCompleter(
348 CommandInterpreter &interpreter, bool include_support_files,
349 CompletionRequest &request)
350 : CommandCompletions::Completer(interpreter, request),
351 m_include_support_files(include_support_files), m_matching_files() {
352 FileSpec partial_spec(m_request.GetCursorArgumentPrefix(), false);
353 m_file_name = partial_spec.GetFilename().GetCString();
354 m_dir_name = partial_spec.GetDirectory().GetCString();
357 Searcher::Depth CommandCompletions::SourceFileCompleter::GetDepth() {
358 return eDepthCompUnit;
361 Searcher::CallbackReturn
362 CommandCompletions::SourceFileCompleter::SearchCallback(SearchFilter &filter,
363 SymbolContext &context,
366 if (context.comp_unit != nullptr) {
367 if (m_include_support_files) {
368 FileSpecList supporting_files = context.comp_unit->GetSupportFiles();
369 for (size_t sfiles = 0; sfiles < supporting_files.GetSize(); sfiles++) {
370 const FileSpec &sfile_spec =
371 supporting_files.GetFileSpecAtIndex(sfiles);
372 const char *sfile_file_name = sfile_spec.GetFilename().GetCString();
373 const char *sfile_dir_name = sfile_spec.GetFilename().GetCString();
375 if (m_file_name && sfile_file_name &&
376 strstr(sfile_file_name, m_file_name) == sfile_file_name)
378 if (match && m_dir_name && sfile_dir_name &&
379 strstr(sfile_dir_name, m_dir_name) != sfile_dir_name)
383 m_matching_files.AppendIfUnique(sfile_spec);
387 const char *cur_file_name = context.comp_unit->GetFilename().GetCString();
388 const char *cur_dir_name = context.comp_unit->GetDirectory().GetCString();
391 if (m_file_name && cur_file_name &&
392 strstr(cur_file_name, m_file_name) == cur_file_name)
395 if (match && m_dir_name && cur_dir_name &&
396 strstr(cur_dir_name, m_dir_name) != cur_dir_name)
400 m_matching_files.AppendIfUnique(context.comp_unit);
404 return Searcher::eCallbackReturnContinue;
408 CommandCompletions::SourceFileCompleter::DoCompletion(SearchFilter *filter) {
409 filter->Search(*this);
410 // Now convert the filelist to completions:
411 for (size_t i = 0; i < m_matching_files.GetSize(); i++) {
412 m_request.GetMatches().AppendString(
413 m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString());
415 return m_request.GetMatches().GetSize();
418 //----------------------------------------------------------------------
420 //----------------------------------------------------------------------
422 static bool regex_chars(const char comp) {
423 return (comp == '[' || comp == ']' || comp == '(' || comp == ')' ||
424 comp == '{' || comp == '}' || comp == '+' || comp == '.' ||
425 comp == '*' || comp == '|' || comp == '^' || comp == '$' ||
426 comp == '\\' || comp == '?');
429 CommandCompletions::SymbolCompleter::SymbolCompleter(
430 CommandInterpreter &interpreter, CompletionRequest &request)
431 : CommandCompletions::Completer(interpreter, request) {
432 std::string regex_str;
433 if (!m_request.GetCursorArgumentPrefix().empty()) {
434 regex_str.append("^");
435 regex_str.append(m_request.GetCursorArgumentPrefix());
437 // Match anything since the completion string is empty
438 regex_str.append(".");
440 std::string::iterator pos =
441 find_if(regex_str.begin() + 1, regex_str.end(), regex_chars);
442 while (pos < regex_str.end()) {
443 pos = regex_str.insert(pos, '\\');
444 pos = find_if(pos + 2, regex_str.end(), regex_chars);
446 m_regex.Compile(regex_str);
449 Searcher::Depth CommandCompletions::SymbolCompleter::GetDepth() {
453 Searcher::CallbackReturn CommandCompletions::SymbolCompleter::SearchCallback(
454 SearchFilter &filter, SymbolContext &context, Address *addr,
456 if (context.module_sp) {
457 SymbolContextList sc_list;
458 const bool include_symbols = true;
459 const bool include_inlines = true;
460 const bool append = true;
461 context.module_sp->FindFunctions(m_regex, include_symbols, include_inlines,
465 // Now add the functions & symbols to the list - only add if unique:
466 for (uint32_t i = 0; i < sc_list.GetSize(); i++) {
467 if (sc_list.GetContextAtIndex(i, sc)) {
468 ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled);
469 if (!func_name.IsEmpty())
470 m_match_set.insert(func_name);
474 return Searcher::eCallbackReturnContinue;
477 size_t CommandCompletions::SymbolCompleter::DoCompletion(SearchFilter *filter) {
478 filter->Search(*this);
479 collection::iterator pos = m_match_set.begin(), end = m_match_set.end();
480 for (pos = m_match_set.begin(); pos != end; pos++)
481 m_request.GetMatches().AppendString((*pos).GetCString());
483 return m_request.GetMatches().GetSize();
486 //----------------------------------------------------------------------
488 //----------------------------------------------------------------------
489 CommandCompletions::ModuleCompleter::ModuleCompleter(
490 CommandInterpreter &interpreter, CompletionRequest &request)
491 : CommandCompletions::Completer(interpreter, request) {
492 FileSpec partial_spec(m_request.GetCursorArgumentPrefix(), false);
493 m_file_name = partial_spec.GetFilename().GetCString();
494 m_dir_name = partial_spec.GetDirectory().GetCString();
497 Searcher::Depth CommandCompletions::ModuleCompleter::GetDepth() {
501 Searcher::CallbackReturn CommandCompletions::ModuleCompleter::SearchCallback(
502 SearchFilter &filter, SymbolContext &context, Address *addr,
504 if (context.module_sp) {
505 const char *cur_file_name =
506 context.module_sp->GetFileSpec().GetFilename().GetCString();
507 const char *cur_dir_name =
508 context.module_sp->GetFileSpec().GetDirectory().GetCString();
511 if (m_file_name && cur_file_name &&
512 strstr(cur_file_name, m_file_name) == cur_file_name)
515 if (match && m_dir_name && cur_dir_name &&
516 strstr(cur_dir_name, m_dir_name) != cur_dir_name)
520 m_request.GetMatches().AppendString(cur_file_name);
523 return Searcher::eCallbackReturnContinue;
526 size_t CommandCompletions::ModuleCompleter::DoCompletion(SearchFilter *filter) {
527 filter->Search(*this);
528 return m_request.GetMatches().GetSize();