/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Commands/CommandCompletions.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- CommandCompletions.cpp --------------------------------------------===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | |
9 | | #include "llvm/ADT/SmallString.h" |
10 | | #include "llvm/ADT/StringSet.h" |
11 | | |
12 | | #include "lldb/Breakpoint/Watchpoint.h" |
13 | | #include "lldb/Core/Module.h" |
14 | | #include "lldb/Core/PluginManager.h" |
15 | | #include "lldb/DataFormatters/DataVisualization.h" |
16 | | #include "lldb/Host/FileSystem.h" |
17 | | #include "lldb/Interpreter/CommandCompletions.h" |
18 | | #include "lldb/Interpreter/CommandInterpreter.h" |
19 | | #include "lldb/Interpreter/CommandObject.h" |
20 | | #include "lldb/Interpreter/CommandObjectMultiword.h" |
21 | | #include "lldb/Interpreter/OptionValueProperties.h" |
22 | | #include "lldb/Symbol/CompileUnit.h" |
23 | | #include "lldb/Symbol/Variable.h" |
24 | | #include "lldb/Target/Language.h" |
25 | | #include "lldb/Target/Process.h" |
26 | | #include "lldb/Target/RegisterContext.h" |
27 | | #include "lldb/Target/Thread.h" |
28 | | #include "lldb/Utility/FileSpec.h" |
29 | | #include "lldb/Utility/FileSpecList.h" |
30 | | #include "lldb/Utility/StreamString.h" |
31 | | #include "lldb/Utility/TildeExpressionResolver.h" |
32 | | |
33 | | #include "llvm/Support/FileSystem.h" |
34 | | #include "llvm/Support/Path.h" |
35 | | |
36 | | using namespace lldb_private; |
37 | | |
38 | | // This is the command completion callback that is used to complete the |
39 | | // argument of the option it is bound to (in the OptionDefinition table |
40 | | // below). |
41 | | typedef void (*CompletionCallback)(CommandInterpreter &interpreter, |
42 | | CompletionRequest &request, |
43 | | // A search filter to limit the search... |
44 | | lldb_private::SearchFilter *searcher); |
45 | | |
46 | | struct CommonCompletionElement { |
47 | | uint32_t type; |
48 | | CompletionCallback callback; |
49 | | }; |
50 | | |
51 | | bool CommandCompletions::InvokeCommonCompletionCallbacks( |
52 | | CommandInterpreter &interpreter, uint32_t completion_mask, |
53 | 165 | CompletionRequest &request, SearchFilter *searcher) { |
54 | 165 | bool handled = false; |
55 | | |
56 | 165 | const CommonCompletionElement common_completions[] = { |
57 | 165 | {lldb::eSourceFileCompletion, CommandCompletions::SourceFiles}, |
58 | 165 | {lldb::eDiskFileCompletion, CommandCompletions::DiskFiles}, |
59 | 165 | {lldb::eDiskDirectoryCompletion, CommandCompletions::DiskDirectories}, |
60 | 165 | {lldb::eSymbolCompletion, CommandCompletions::Symbols}, |
61 | 165 | {lldb::eModuleCompletion, CommandCompletions::Modules}, |
62 | 165 | {lldb::eModuleUUIDCompletion, CommandCompletions::ModuleUUIDs}, |
63 | 165 | {lldb::eSettingsNameCompletion, CommandCompletions::SettingsNames}, |
64 | 165 | {lldb::ePlatformPluginCompletion, |
65 | 165 | CommandCompletions::PlatformPluginNames}, |
66 | 165 | {lldb::eArchitectureCompletion, CommandCompletions::ArchitectureNames}, |
67 | 165 | {lldb::eVariablePathCompletion, CommandCompletions::VariablePath}, |
68 | 165 | {lldb::eRegisterCompletion, CommandCompletions::Registers}, |
69 | 165 | {lldb::eBreakpointCompletion, CommandCompletions::Breakpoints}, |
70 | 165 | {lldb::eProcessPluginCompletion, CommandCompletions::ProcessPluginNames}, |
71 | 165 | {lldb::eDisassemblyFlavorCompletion, |
72 | 165 | CommandCompletions::DisassemblyFlavors}, |
73 | 165 | {lldb::eTypeLanguageCompletion, CommandCompletions::TypeLanguages}, |
74 | 165 | {lldb::eFrameIndexCompletion, CommandCompletions::FrameIndexes}, |
75 | 165 | {lldb::eStopHookIDCompletion, CommandCompletions::StopHookIDs}, |
76 | 165 | {lldb::eThreadIndexCompletion, CommandCompletions::ThreadIndexes}, |
77 | 165 | {lldb::eWatchpointIDCompletion, CommandCompletions::WatchPointIDs}, |
78 | 165 | {lldb::eBreakpointNameCompletion, CommandCompletions::BreakpointNames}, |
79 | 165 | {lldb::eProcessIDCompletion, CommandCompletions::ProcessIDs}, |
80 | 165 | {lldb::eProcessNameCompletion, CommandCompletions::ProcessNames}, |
81 | 165 | {lldb::eRemoteDiskFileCompletion, CommandCompletions::RemoteDiskFiles}, |
82 | 165 | {lldb::eRemoteDiskDirectoryCompletion, |
83 | 165 | CommandCompletions::RemoteDiskDirectories}, |
84 | 165 | {lldb::eTypeCategoryNameCompletion, |
85 | 165 | CommandCompletions::TypeCategoryNames}, |
86 | 165 | {lldb::CompletionType::eNoCompletion, |
87 | 165 | nullptr} // This one has to be last in the list. |
88 | 165 | }; |
89 | | |
90 | 4.29k | for (int i = 0;; i++4.12k ) { |
91 | 4.29k | if (common_completions[i].type == lldb::eNoCompletion) |
92 | 165 | break; |
93 | 4.12k | else if ((common_completions[i].type & completion_mask) == |
94 | 4.12k | common_completions[i].type && |
95 | 4.12k | common_completions[i].callback != nullptr156 ) { |
96 | 156 | handled = true; |
97 | 156 | common_completions[i].callback(interpreter, request, searcher); |
98 | 156 | } |
99 | 4.29k | } |
100 | 165 | return handled; |
101 | 165 | } |
102 | | |
103 | | namespace { |
104 | | // The Completer class is a convenient base class for building searchers that |
105 | | // go along with the SearchFilter passed to the standard Completer functions. |
106 | | class Completer : public Searcher { |
107 | | public: |
108 | | Completer(CommandInterpreter &interpreter, CompletionRequest &request) |
109 | 5 | : m_interpreter(interpreter), m_request(request) {} |
110 | | |
111 | 5 | ~Completer() override = default; |
112 | | |
113 | | CallbackReturn SearchCallback(SearchFilter &filter, SymbolContext &context, |
114 | | Address *addr) override = 0; |
115 | | |
116 | | lldb::SearchDepth GetDepth() override = 0; |
117 | | |
118 | | virtual void DoCompletion(SearchFilter *filter) = 0; |
119 | | |
120 | | protected: |
121 | | CommandInterpreter &m_interpreter; |
122 | | CompletionRequest &m_request; |
123 | | |
124 | | private: |
125 | | Completer(const Completer &) = delete; |
126 | | const Completer &operator=(const Completer &) = delete; |
127 | | }; |
128 | | } // namespace |
129 | | |
130 | | // SourceFileCompleter implements the source file completer |
131 | | namespace { |
132 | | class SourceFileCompleter : public Completer { |
133 | | public: |
134 | | SourceFileCompleter(CommandInterpreter &interpreter, |
135 | | CompletionRequest &request) |
136 | 1 | : Completer(interpreter, request) { |
137 | 1 | FileSpec partial_spec(m_request.GetCursorArgumentPrefix()); |
138 | 1 | m_file_name = partial_spec.GetFilename().GetCString(); |
139 | 1 | m_dir_name = partial_spec.GetDirectory().GetCString(); |
140 | 1 | } |
141 | | |
142 | 44 | lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthCompUnit; } |
143 | | |
144 | | Searcher::CallbackReturn SearchCallback(SearchFilter &filter, |
145 | | SymbolContext &context, |
146 | 1 | Address *addr) override { |
147 | 1 | if (context.comp_unit != nullptr) { |
148 | 1 | const char *cur_file_name = |
149 | 1 | context.comp_unit->GetPrimaryFile().GetFilename().GetCString(); |
150 | 1 | const char *cur_dir_name = |
151 | 1 | context.comp_unit->GetPrimaryFile().GetDirectory().GetCString(); |
152 | | |
153 | 1 | bool match = false; |
154 | 1 | if (m_file_name && cur_file_name && |
155 | 1 | strstr(cur_file_name, m_file_name) == cur_file_name) |
156 | 1 | match = true; |
157 | | |
158 | 1 | if (match && m_dir_name && cur_dir_name0 && |
159 | 1 | strstr(cur_dir_name, m_dir_name) != cur_dir_name0 ) |
160 | 0 | match = false; |
161 | | |
162 | 1 | if (match) { |
163 | 1 | m_matching_files.AppendIfUnique(context.comp_unit->GetPrimaryFile()); |
164 | 1 | } |
165 | 1 | } |
166 | 1 | return Searcher::eCallbackReturnContinue; |
167 | 1 | } |
168 | | |
169 | 1 | void DoCompletion(SearchFilter *filter) override { |
170 | 1 | filter->Search(*this); |
171 | | // Now convert the filelist to completions: |
172 | 2 | for (size_t i = 0; i < m_matching_files.GetSize(); i++1 ) { |
173 | 1 | m_request.AddCompletion( |
174 | 1 | m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString()); |
175 | 1 | } |
176 | 1 | } |
177 | | |
178 | | private: |
179 | | FileSpecList m_matching_files; |
180 | | const char *m_file_name; |
181 | | const char *m_dir_name; |
182 | | |
183 | | SourceFileCompleter(const SourceFileCompleter &) = delete; |
184 | | const SourceFileCompleter &operator=(const SourceFileCompleter &) = delete; |
185 | | }; |
186 | | } // namespace |
187 | | |
188 | 4 | static bool regex_chars(const char comp) { |
189 | 4 | return llvm::StringRef("[](){}+.*|^$\\?").contains(comp); |
190 | 4 | } |
191 | | |
192 | | namespace { |
193 | | class SymbolCompleter : public Completer { |
194 | | |
195 | | public: |
196 | | SymbolCompleter(CommandInterpreter &interpreter, CompletionRequest &request) |
197 | 2 | : Completer(interpreter, request) { |
198 | 2 | std::string regex_str; |
199 | 2 | if (!m_request.GetCursorArgumentPrefix().empty()) { |
200 | 2 | regex_str.append("^"); |
201 | 2 | regex_str.append(std::string(m_request.GetCursorArgumentPrefix())); |
202 | 2 | } else { |
203 | | // Match anything since the completion string is empty |
204 | 0 | regex_str.append("."); |
205 | 0 | } |
206 | 2 | std::string::iterator pos = |
207 | 2 | find_if(regex_str.begin() + 1, regex_str.end(), regex_chars); |
208 | 2 | while (pos < regex_str.end()) { |
209 | 0 | pos = regex_str.insert(pos, '\\'); |
210 | 0 | pos = find_if(pos + 2, regex_str.end(), regex_chars); |
211 | 0 | } |
212 | 2 | m_regex = RegularExpression(regex_str); |
213 | 2 | } |
214 | | |
215 | 86 | lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; } |
216 | | |
217 | | Searcher::CallbackReturn SearchCallback(SearchFilter &filter, |
218 | | SymbolContext &context, |
219 | 82 | Address *addr) override { |
220 | 82 | if (context.module_sp) { |
221 | 82 | SymbolContextList sc_list; |
222 | 82 | ModuleFunctionSearchOptions function_options; |
223 | 82 | function_options.include_symbols = true; |
224 | 82 | function_options.include_inlines = true; |
225 | 82 | context.module_sp->FindFunctions(m_regex, function_options, sc_list); |
226 | | |
227 | | // Now add the functions & symbols to the list - only add if unique: |
228 | 82 | for (const SymbolContext &sc : sc_list) { |
229 | 2 | ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled); |
230 | | // Ensure that the function name matches the regex. This is more than |
231 | | // a sanity check. It is possible that the demangled function name |
232 | | // does not start with the prefix, for example when it's in an |
233 | | // anonymous namespace. |
234 | 2 | if (!func_name.IsEmpty() && m_regex.Execute(func_name.GetStringRef())) |
235 | 1 | m_match_set.insert(func_name); |
236 | 2 | } |
237 | 82 | } |
238 | 82 | return Searcher::eCallbackReturnContinue; |
239 | 82 | } |
240 | | |
241 | 2 | void DoCompletion(SearchFilter *filter) override { |
242 | 2 | filter->Search(*this); |
243 | 2 | collection::iterator pos = m_match_set.begin(), end = m_match_set.end(); |
244 | 3 | for (pos = m_match_set.begin(); pos != end; pos++1 ) |
245 | 1 | m_request.AddCompletion((*pos).GetCString()); |
246 | 2 | } |
247 | | |
248 | | private: |
249 | | RegularExpression m_regex; |
250 | | typedef std::set<ConstString> collection; |
251 | | collection m_match_set; |
252 | | |
253 | | SymbolCompleter(const SymbolCompleter &) = delete; |
254 | | const SymbolCompleter &operator=(const SymbolCompleter &) = delete; |
255 | | }; |
256 | | } // namespace |
257 | | |
258 | | namespace { |
259 | | class ModuleCompleter : public Completer { |
260 | | public: |
261 | | ModuleCompleter(CommandInterpreter &interpreter, CompletionRequest &request) |
262 | 2 | : Completer(interpreter, request) { |
263 | 2 | FileSpec partial_spec(m_request.GetCursorArgumentPrefix()); |
264 | 2 | m_file_name = partial_spec.GetFilename().GetCString(); |
265 | 2 | m_dir_name = partial_spec.GetDirectory().GetCString(); |
266 | 2 | } |
267 | | |
268 | 43 | lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; } |
269 | | |
270 | | Searcher::CallbackReturn SearchCallback(SearchFilter &filter, |
271 | | SymbolContext &context, |
272 | 41 | Address *addr) override { |
273 | 41 | if (context.module_sp) { |
274 | 41 | const char *cur_file_name = |
275 | 41 | context.module_sp->GetFileSpec().GetFilename().GetCString(); |
276 | 41 | const char *cur_dir_name = |
277 | 41 | context.module_sp->GetFileSpec().GetDirectory().GetCString(); |
278 | | |
279 | 41 | bool match = false; |
280 | 41 | if (m_file_name && cur_file_name && |
281 | 41 | strstr(cur_file_name, m_file_name) == cur_file_name) |
282 | 1 | match = true; |
283 | | |
284 | 41 | if (match && m_dir_name1 && cur_dir_name0 && |
285 | 41 | strstr(cur_dir_name, m_dir_name) != cur_dir_name0 ) |
286 | 0 | match = false; |
287 | | |
288 | 41 | if (match) { |
289 | 1 | m_request.AddCompletion(cur_file_name); |
290 | 1 | } |
291 | 41 | } |
292 | 41 | return Searcher::eCallbackReturnContinue; |
293 | 41 | } |
294 | | |
295 | 2 | void DoCompletion(SearchFilter *filter) override { filter->Search(*this); } |
296 | | |
297 | | private: |
298 | | const char *m_file_name; |
299 | | const char *m_dir_name; |
300 | | |
301 | | ModuleCompleter(const ModuleCompleter &) = delete; |
302 | | const ModuleCompleter &operator=(const ModuleCompleter &) = delete; |
303 | | }; |
304 | | } // namespace |
305 | | |
306 | | void CommandCompletions::SourceFiles(CommandInterpreter &interpreter, |
307 | | CompletionRequest &request, |
308 | 1 | SearchFilter *searcher) { |
309 | 1 | SourceFileCompleter completer(interpreter, request); |
310 | | |
311 | 1 | if (searcher == nullptr) { |
312 | 1 | lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); |
313 | 1 | SearchFilterForUnconstrainedSearches null_searcher(target_sp); |
314 | 1 | completer.DoCompletion(&null_searcher); |
315 | 1 | } else { |
316 | 0 | completer.DoCompletion(searcher); |
317 | 0 | } |
318 | 1 | } |
319 | | |
320 | | static void DiskFilesOrDirectories(const llvm::Twine &partial_name, |
321 | | bool only_directories, |
322 | | CompletionRequest &request, |
323 | 25 | TildeExpressionResolver &Resolver) { |
324 | 25 | llvm::SmallString<256> CompletionBuffer; |
325 | 25 | llvm::SmallString<256> Storage; |
326 | 25 | partial_name.toVector(CompletionBuffer); |
327 | | |
328 | 25 | if (CompletionBuffer.size() >= PATH_MAX) |
329 | 0 | return; |
330 | | |
331 | 25 | namespace path = llvm::sys::path; |
332 | | |
333 | 25 | llvm::StringRef SearchDir; |
334 | 25 | llvm::StringRef PartialItem; |
335 | | |
336 | 25 | if (CompletionBuffer.startswith("~")) { |
337 | 7 | llvm::StringRef Buffer = CompletionBuffer; |
338 | 7 | size_t FirstSep = |
339 | 20 | Buffer.find_if([](char c) { return path::is_separator(c); }); |
340 | | |
341 | 7 | llvm::StringRef Username = Buffer.take_front(FirstSep); |
342 | 7 | llvm::StringRef Remainder; |
343 | 7 | if (FirstSep != llvm::StringRef::npos) |
344 | 3 | Remainder = Buffer.drop_front(FirstSep + 1); |
345 | | |
346 | 7 | llvm::SmallString<256> Resolved; |
347 | 7 | if (!Resolver.ResolveExact(Username, Resolved)) { |
348 | | // We couldn't resolve it as a full username. If there were no slashes |
349 | | // then this might be a partial username. We try to resolve it as such |
350 | | // but after that, we're done regardless of any matches. |
351 | 2 | if (FirstSep == llvm::StringRef::npos) { |
352 | 2 | llvm::StringSet<> MatchSet; |
353 | 2 | Resolver.ResolvePartial(Username, MatchSet); |
354 | 2 | for (const auto &S : MatchSet) { |
355 | 2 | Resolved = S.getKey(); |
356 | 2 | path::append(Resolved, path::get_separator()); |
357 | 2 | request.AddCompletion(Resolved, "", CompletionMode::Partial); |
358 | 2 | } |
359 | 2 | } |
360 | 2 | return; |
361 | 2 | } |
362 | | |
363 | | // If there was no trailing slash, then we're done as soon as we resolve |
364 | | // the expression to the correct directory. Otherwise we need to continue |
365 | | // looking for matches within that directory. |
366 | 5 | if (FirstSep == llvm::StringRef::npos) { |
367 | | // Make sure it ends with a separator. |
368 | 2 | path::append(CompletionBuffer, path::get_separator()); |
369 | 2 | request.AddCompletion(CompletionBuffer, "", CompletionMode::Partial); |
370 | 2 | return; |
371 | 2 | } |
372 | | |
373 | | // We want to keep the form the user typed, so we special case this to |
374 | | // search in the fully resolved directory, but CompletionBuffer keeps the |
375 | | // unmodified form that the user typed. |
376 | 3 | Storage = Resolved; |
377 | 3 | llvm::StringRef RemainderDir = path::parent_path(Remainder); |
378 | 3 | if (!RemainderDir.empty()) { |
379 | | // Append the remaining path to the resolved directory. |
380 | 2 | Storage.append(path::get_separator()); |
381 | 2 | Storage.append(RemainderDir); |
382 | 2 | } |
383 | 3 | SearchDir = Storage; |
384 | 18 | } else if (CompletionBuffer == path::root_directory(CompletionBuffer)) { |
385 | 2 | SearchDir = CompletionBuffer; |
386 | 16 | } else { |
387 | 16 | SearchDir = path::parent_path(CompletionBuffer); |
388 | 16 | } |
389 | | |
390 | 21 | size_t FullPrefixLen = CompletionBuffer.size(); |
391 | | |
392 | 21 | PartialItem = path::filename(CompletionBuffer); |
393 | | |
394 | | // path::filename() will return "." when the passed path ends with a |
395 | | // directory separator or the separator when passed the disk root directory. |
396 | | // We have to filter those out, but only when the "." doesn't come from the |
397 | | // completion request itself. |
398 | 21 | if ((PartialItem == "." || PartialItem == path::get_separator()15 ) && |
399 | 21 | path::is_separator(CompletionBuffer.back())7 ) |
400 | 6 | PartialItem = llvm::StringRef(); |
401 | | |
402 | 21 | if (SearchDir.empty()) { |
403 | 6 | llvm::sys::fs::current_path(Storage); |
404 | 6 | SearchDir = Storage; |
405 | 6 | } |
406 | 21 | assert(!PartialItem.contains(path::get_separator())); |
407 | | |
408 | | // SearchDir now contains the directory to search in, and Prefix contains the |
409 | | // text we want to match against items in that directory. |
410 | | |
411 | 21 | FileSystem &fs = FileSystem::Instance(); |
412 | 21 | std::error_code EC; |
413 | 21 | llvm::vfs::directory_iterator Iter = fs.DirBegin(SearchDir, EC); |
414 | 21 | llvm::vfs::directory_iterator End; |
415 | 347 | for (; Iter != End && !EC326 ; Iter.increment(EC)326 ) { |
416 | 326 | auto &Entry = *Iter; |
417 | 326 | llvm::ErrorOr<llvm::vfs::Status> Status = fs.GetStatus(Entry.path()); |
418 | | |
419 | 326 | if (!Status) |
420 | 1 | continue; |
421 | | |
422 | 325 | auto Name = path::filename(Entry.path()); |
423 | | |
424 | | // Omit ".", ".." |
425 | 325 | if (Name == "." || Name == ".." || !Name.startswith(PartialItem)) |
426 | 216 | continue; |
427 | | |
428 | 109 | bool is_dir = Status->isDirectory(); |
429 | | |
430 | | // If it's a symlink, then we treat it as a directory as long as the target |
431 | | // is a directory. |
432 | 109 | if (Status->isSymlink()) { |
433 | 0 | FileSpec symlink_filespec(Entry.path()); |
434 | 0 | FileSpec resolved_filespec; |
435 | 0 | auto error = fs.ResolveSymbolicLink(symlink_filespec, resolved_filespec); |
436 | 0 | if (error.Success()) |
437 | 0 | is_dir = fs.IsDirectory(symlink_filespec); |
438 | 0 | } |
439 | | |
440 | 109 | if (only_directories && !is_dir47 ) |
441 | 18 | continue; |
442 | | |
443 | | // Shrink it back down so that it just has the original prefix the user |
444 | | // typed and remove the part of the name which is common to the located |
445 | | // item and what the user typed. |
446 | 91 | CompletionBuffer.resize(FullPrefixLen); |
447 | 91 | Name = Name.drop_front(PartialItem.size()); |
448 | 91 | CompletionBuffer.append(Name); |
449 | | |
450 | 91 | if (is_dir) { |
451 | 60 | path::append(CompletionBuffer, path::get_separator()); |
452 | 60 | } |
453 | | |
454 | 91 | CompletionMode mode = |
455 | 91 | is_dir ? CompletionMode::Partial60 : CompletionMode::Normal31 ; |
456 | 91 | request.AddCompletion(CompletionBuffer, "", mode); |
457 | 91 | } |
458 | 21 | } |
459 | | |
460 | | static void DiskFilesOrDirectories(const llvm::Twine &partial_name, |
461 | | bool only_directories, StringList &matches, |
462 | 18 | TildeExpressionResolver &Resolver) { |
463 | 18 | CompletionResult result; |
464 | 18 | std::string partial_name_str = partial_name.str(); |
465 | 18 | CompletionRequest request(partial_name_str, partial_name_str.size(), result); |
466 | 18 | DiskFilesOrDirectories(partial_name, only_directories, request, Resolver); |
467 | 18 | result.GetMatches(matches); |
468 | 18 | } |
469 | | |
470 | | static void DiskFilesOrDirectories(CompletionRequest &request, |
471 | 7 | bool only_directories) { |
472 | 7 | StandardTildeExpressionResolver resolver; |
473 | 7 | DiskFilesOrDirectories(request.GetCursorArgumentPrefix(), only_directories, |
474 | 7 | request, resolver); |
475 | 7 | } |
476 | | |
477 | | void CommandCompletions::DiskFiles(CommandInterpreter &interpreter, |
478 | | CompletionRequest &request, |
479 | 7 | SearchFilter *searcher) { |
480 | 7 | DiskFilesOrDirectories(request, /*only_dirs*/ false); |
481 | 7 | } |
482 | | |
483 | | void CommandCompletions::DiskFiles(const llvm::Twine &partial_file_name, |
484 | | StringList &matches, |
485 | 4 | TildeExpressionResolver &Resolver) { |
486 | 4 | DiskFilesOrDirectories(partial_file_name, false, matches, Resolver); |
487 | 4 | } |
488 | | |
489 | | void CommandCompletions::DiskDirectories(CommandInterpreter &interpreter, |
490 | | CompletionRequest &request, |
491 | 0 | SearchFilter *searcher) { |
492 | 0 | DiskFilesOrDirectories(request, /*only_dirs*/ true); |
493 | 0 | } |
494 | | |
495 | | void CommandCompletions::DiskDirectories(const llvm::Twine &partial_file_name, |
496 | | StringList &matches, |
497 | 14 | TildeExpressionResolver &Resolver) { |
498 | 14 | DiskFilesOrDirectories(partial_file_name, true, matches, Resolver); |
499 | 14 | } |
500 | | |
501 | | void CommandCompletions::RemoteDiskFiles(CommandInterpreter &interpreter, |
502 | | CompletionRequest &request, |
503 | 4 | SearchFilter *searcher) { |
504 | 4 | lldb::PlatformSP platform_sp = |
505 | 4 | interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform(); |
506 | 4 | if (platform_sp) |
507 | 4 | platform_sp->AutoCompleteDiskFileOrDirectory(request, false); |
508 | 4 | } |
509 | | |
510 | | void CommandCompletions::RemoteDiskDirectories(CommandInterpreter &interpreter, |
511 | | CompletionRequest &request, |
512 | 1 | SearchFilter *searcher) { |
513 | 1 | lldb::PlatformSP platform_sp = |
514 | 1 | interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform(); |
515 | 1 | if (platform_sp) |
516 | 1 | platform_sp->AutoCompleteDiskFileOrDirectory(request, true); |
517 | 1 | } |
518 | | |
519 | | void CommandCompletions::Modules(CommandInterpreter &interpreter, |
520 | | CompletionRequest &request, |
521 | 2 | SearchFilter *searcher) { |
522 | 2 | ModuleCompleter completer(interpreter, request); |
523 | | |
524 | 2 | if (searcher == nullptr) { |
525 | 2 | lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); |
526 | 2 | SearchFilterForUnconstrainedSearches null_searcher(target_sp); |
527 | 2 | completer.DoCompletion(&null_searcher); |
528 | 2 | } else { |
529 | 0 | completer.DoCompletion(searcher); |
530 | 0 | } |
531 | 2 | } |
532 | | |
533 | | void CommandCompletions::ModuleUUIDs(CommandInterpreter &interpreter, |
534 | | CompletionRequest &request, |
535 | 1 | SearchFilter *searcher) { |
536 | 1 | const ExecutionContext &exe_ctx = interpreter.GetExecutionContext(); |
537 | 1 | if (!exe_ctx.HasTargetScope()) |
538 | 0 | return; |
539 | | |
540 | 1 | exe_ctx.GetTargetPtr()->GetImages().ForEach( |
541 | 42 | [&request](const lldb::ModuleSP &module) { |
542 | 42 | StreamString strm; |
543 | 42 | module->GetDescription(strm.AsRawOstream(), |
544 | 42 | lldb::eDescriptionLevelInitial); |
545 | 42 | request.TryCompleteCurrentArg(module->GetUUID().GetAsString(), |
546 | 42 | strm.GetString()); |
547 | 42 | return true; |
548 | 42 | }); |
549 | 1 | } |
550 | | |
551 | | void CommandCompletions::Symbols(CommandInterpreter &interpreter, |
552 | | CompletionRequest &request, |
553 | 2 | SearchFilter *searcher) { |
554 | 2 | SymbolCompleter completer(interpreter, request); |
555 | | |
556 | 2 | if (searcher == nullptr) { |
557 | 2 | lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); |
558 | 2 | SearchFilterForUnconstrainedSearches null_searcher(target_sp); |
559 | 2 | completer.DoCompletion(&null_searcher); |
560 | 2 | } else { |
561 | 0 | completer.DoCompletion(searcher); |
562 | 0 | } |
563 | 2 | } |
564 | | |
565 | | void CommandCompletions::SettingsNames(CommandInterpreter &interpreter, |
566 | | CompletionRequest &request, |
567 | 15 | SearchFilter *searcher) { |
568 | | // Cache the full setting name list |
569 | 15 | static StringList g_property_names; |
570 | 15 | if (g_property_names.GetSize() == 0) { |
571 | | // Generate the full setting name list on demand |
572 | 1 | lldb::OptionValuePropertiesSP properties_sp( |
573 | 1 | interpreter.GetDebugger().GetValueProperties()); |
574 | 1 | if (properties_sp) { |
575 | 1 | StreamString strm; |
576 | 1 | properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName); |
577 | 1 | const std::string &str = std::string(strm.GetString()); |
578 | 1 | g_property_names.SplitIntoLines(str.c_str(), str.size()); |
579 | 1 | } |
580 | 1 | } |
581 | | |
582 | 15 | for (const std::string &s : g_property_names) |
583 | 2.53k | request.TryCompleteCurrentArg(s); |
584 | 15 | } |
585 | | |
586 | | void CommandCompletions::PlatformPluginNames(CommandInterpreter &interpreter, |
587 | | CompletionRequest &request, |
588 | 0 | SearchFilter *searcher) { |
589 | 0 | PluginManager::AutoCompletePlatformName(request.GetCursorArgumentPrefix(), |
590 | 0 | request); |
591 | 0 | } |
592 | | |
593 | | void CommandCompletions::ArchitectureNames(CommandInterpreter &interpreter, |
594 | | CompletionRequest &request, |
595 | 2 | SearchFilter *searcher) { |
596 | 2 | ArchSpec::AutoComplete(request); |
597 | 2 | } |
598 | | |
599 | | void CommandCompletions::VariablePath(CommandInterpreter &interpreter, |
600 | | CompletionRequest &request, |
601 | 23 | SearchFilter *searcher) { |
602 | 23 | Variable::AutoComplete(interpreter.GetExecutionContext(), request); |
603 | 23 | } |
604 | | |
605 | | void CommandCompletions::Registers(CommandInterpreter &interpreter, |
606 | | CompletionRequest &request, |
607 | 15 | SearchFilter *searcher) { |
608 | 15 | std::string reg_prefix; |
609 | 15 | if (request.GetCursorArgumentPrefix().startswith("$")) |
610 | 3 | reg_prefix = "$"; |
611 | | |
612 | 15 | RegisterContext *reg_ctx = |
613 | 15 | interpreter.GetExecutionContext().GetRegisterContext(); |
614 | 15 | if (!reg_ctx) |
615 | 3 | return; |
616 | | |
617 | 12 | const size_t reg_num = reg_ctx->GetRegisterCount(); |
618 | 1.52k | for (size_t reg_idx = 0; reg_idx < reg_num; ++reg_idx1.51k ) { |
619 | 1.51k | const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_idx); |
620 | 1.51k | request.TryCompleteCurrentArg(reg_prefix + reg_info->name, |
621 | 1.51k | reg_info->alt_name); |
622 | 1.51k | } |
623 | 12 | } |
624 | | |
625 | | void CommandCompletions::Breakpoints(CommandInterpreter &interpreter, |
626 | | CompletionRequest &request, |
627 | 28 | SearchFilter *searcher) { |
628 | 28 | lldb::TargetSP target = interpreter.GetDebugger().GetSelectedTarget(); |
629 | 28 | if (!target) |
630 | 7 | return; |
631 | | |
632 | 21 | const BreakpointList &breakpoints = target->GetBreakpointList(); |
633 | | |
634 | 21 | std::unique_lock<std::recursive_mutex> lock; |
635 | 21 | target->GetBreakpointList().GetListMutex(lock); |
636 | | |
637 | 21 | size_t num_breakpoints = breakpoints.GetSize(); |
638 | 21 | if (num_breakpoints == 0) |
639 | 0 | return; |
640 | | |
641 | 56 | for (size_t i = 0; 21 i < num_breakpoints; ++i35 ) { |
642 | 35 | lldb::BreakpointSP bp = breakpoints.GetBreakpointAtIndex(i); |
643 | | |
644 | 35 | StreamString s; |
645 | 35 | bp->GetDescription(&s, lldb::eDescriptionLevelBrief); |
646 | 35 | llvm::StringRef bp_info = s.GetString(); |
647 | | |
648 | 35 | const size_t colon_pos = bp_info.find_first_of(':'); |
649 | 35 | if (colon_pos != llvm::StringRef::npos) |
650 | 35 | bp_info = bp_info.drop_front(colon_pos + 2); |
651 | | |
652 | 35 | request.TryCompleteCurrentArg(std::to_string(bp->GetID()), bp_info); |
653 | 35 | } |
654 | 21 | } |
655 | | |
656 | | void CommandCompletions::BreakpointNames(CommandInterpreter &interpreter, |
657 | | CompletionRequest &request, |
658 | 2 | SearchFilter *searcher) { |
659 | 2 | lldb::TargetSP target = interpreter.GetDebugger().GetSelectedTarget(); |
660 | 2 | if (!target) |
661 | 0 | return; |
662 | | |
663 | 2 | std::vector<std::string> name_list; |
664 | 2 | target->GetBreakpointNames(name_list); |
665 | | |
666 | 2 | for (const std::string &name : name_list) |
667 | 1 | request.TryCompleteCurrentArg(name); |
668 | 2 | } |
669 | | |
670 | | void CommandCompletions::ProcessPluginNames(CommandInterpreter &interpreter, |
671 | | CompletionRequest &request, |
672 | 3 | SearchFilter *searcher) { |
673 | 3 | PluginManager::AutoCompleteProcessName(request.GetCursorArgumentPrefix(), |
674 | 3 | request); |
675 | 3 | } |
676 | | void CommandCompletions::DisassemblyFlavors(CommandInterpreter &interpreter, |
677 | | CompletionRequest &request, |
678 | 1 | SearchFilter *searcher) { |
679 | | // Currently the only valid options for disassemble -F are default, and for |
680 | | // Intel architectures, att and intel. |
681 | 1 | static const char *flavors[] = {"default", "att", "intel"}; |
682 | 3 | for (const char *flavor : flavors) { |
683 | 3 | request.TryCompleteCurrentArg(flavor); |
684 | 3 | } |
685 | 1 | } |
686 | | |
687 | | void CommandCompletions::ProcessIDs(CommandInterpreter &interpreter, |
688 | | CompletionRequest &request, |
689 | 3 | SearchFilter *searcher) { |
690 | 3 | lldb::PlatformSP platform_sp(interpreter.GetPlatform(true)); |
691 | 3 | if (!platform_sp) |
692 | 0 | return; |
693 | 3 | ProcessInstanceInfoList process_infos; |
694 | 3 | ProcessInstanceInfoMatch match_info; |
695 | 3 | platform_sp->FindProcesses(match_info, process_infos); |
696 | 3 | for (const ProcessInstanceInfo &info : process_infos) |
697 | 252 | request.TryCompleteCurrentArg(std::to_string(info.GetProcessID()), |
698 | 252 | info.GetNameAsStringRef()); |
699 | 3 | } |
700 | | |
701 | | void CommandCompletions::ProcessNames(CommandInterpreter &interpreter, |
702 | | CompletionRequest &request, |
703 | 2 | SearchFilter *searcher) { |
704 | 2 | lldb::PlatformSP platform_sp(interpreter.GetPlatform(true)); |
705 | 2 | if (!platform_sp) |
706 | 0 | return; |
707 | 2 | ProcessInstanceInfoList process_infos; |
708 | 2 | ProcessInstanceInfoMatch match_info; |
709 | 2 | platform_sp->FindProcesses(match_info, process_infos); |
710 | 2 | for (const ProcessInstanceInfo &info : process_infos) |
711 | 168 | request.TryCompleteCurrentArg(info.GetNameAsStringRef()); |
712 | 2 | } |
713 | | |
714 | | void CommandCompletions::TypeLanguages(CommandInterpreter &interpreter, |
715 | | CompletionRequest &request, |
716 | 0 | SearchFilter *searcher) { |
717 | 0 | for (int bit : |
718 | 0 | Language::GetLanguagesSupportingTypeSystems().bitvector.set_bits()) { |
719 | 0 | request.TryCompleteCurrentArg( |
720 | 0 | Language::GetNameForLanguageType(static_cast<lldb::LanguageType>(bit))); |
721 | 0 | } |
722 | 0 | } |
723 | | |
724 | | void CommandCompletions::FrameIndexes(CommandInterpreter &interpreter, |
725 | | CompletionRequest &request, |
726 | 2 | SearchFilter *searcher) { |
727 | 2 | const ExecutionContext &exe_ctx = interpreter.GetExecutionContext(); |
728 | 2 | if (!exe_ctx.HasProcessScope()) |
729 | 0 | return; |
730 | | |
731 | 2 | lldb::ThreadSP thread_sp = exe_ctx.GetThreadSP(); |
732 | 2 | Debugger &dbg = interpreter.GetDebugger(); |
733 | 2 | const uint32_t frame_num = thread_sp->GetStackFrameCount(); |
734 | 8 | for (uint32_t i = 0; i < frame_num; ++i6 ) { |
735 | 6 | lldb::StackFrameSP frame_sp = thread_sp->GetStackFrameAtIndex(i); |
736 | 6 | StreamString strm; |
737 | | // Dumping frames can be slow, allow interruption. |
738 | 6 | if (INTERRUPT_REQUESTED(dbg, "Interrupted in frame completion")) |
739 | 0 | break; |
740 | 6 | frame_sp->Dump(&strm, false, true); |
741 | 6 | request.TryCompleteCurrentArg(std::to_string(i), strm.GetString()); |
742 | 6 | } |
743 | 2 | } |
744 | | |
745 | | void CommandCompletions::StopHookIDs(CommandInterpreter &interpreter, |
746 | | CompletionRequest &request, |
747 | 6 | SearchFilter *searcher) { |
748 | 6 | const lldb::TargetSP target_sp = |
749 | 6 | interpreter.GetExecutionContext().GetTargetSP(); |
750 | 6 | if (!target_sp) |
751 | 3 | return; |
752 | | |
753 | 3 | const size_t num = target_sp->GetNumStopHooks(); |
754 | 6 | for (size_t idx = 0; idx < num; ++idx3 ) { |
755 | 3 | StreamString strm; |
756 | | // The value 11 is an offset to make the completion description looks |
757 | | // neater. |
758 | 3 | strm.SetIndentLevel(11); |
759 | 3 | const Target::StopHookSP stophook_sp = target_sp->GetStopHookAtIndex(idx); |
760 | 3 | stophook_sp->GetDescription(strm, lldb::eDescriptionLevelInitial); |
761 | 3 | request.TryCompleteCurrentArg(std::to_string(stophook_sp->GetID()), |
762 | 3 | strm.GetString()); |
763 | 3 | } |
764 | 3 | } |
765 | | |
766 | | void CommandCompletions::ThreadIndexes(CommandInterpreter &interpreter, |
767 | | CompletionRequest &request, |
768 | 20 | SearchFilter *searcher) { |
769 | 20 | const ExecutionContext &exe_ctx = interpreter.GetExecutionContext(); |
770 | 20 | if (!exe_ctx.HasProcessScope()) |
771 | 10 | return; |
772 | | |
773 | 10 | ThreadList &threads = exe_ctx.GetProcessPtr()->GetThreadList(); |
774 | 10 | lldb::ThreadSP thread_sp; |
775 | 20 | for (uint32_t idx = 0; (thread_sp = threads.GetThreadAtIndex(idx)); ++idx10 ) { |
776 | 10 | StreamString strm; |
777 | 10 | thread_sp->GetStatus(strm, 0, 1, 1, true); |
778 | 10 | request.TryCompleteCurrentArg(std::to_string(thread_sp->GetIndexID()), |
779 | 10 | strm.GetString()); |
780 | 10 | } |
781 | 10 | } |
782 | | |
783 | | void CommandCompletions::WatchPointIDs(CommandInterpreter &interpreter, |
784 | | CompletionRequest &request, |
785 | 10 | SearchFilter *searcher) { |
786 | 10 | const ExecutionContext &exe_ctx = interpreter.GetExecutionContext(); |
787 | 10 | if (!exe_ctx.HasTargetScope()) |
788 | 5 | return; |
789 | | |
790 | 5 | const WatchpointList &wp_list = exe_ctx.GetTargetPtr()->GetWatchpointList(); |
791 | 5 | for (lldb::WatchpointSP wp_sp : wp_list.Watchpoints()) { |
792 | 5 | StreamString strm; |
793 | 5 | wp_sp->Dump(&strm); |
794 | 5 | request.TryCompleteCurrentArg(std::to_string(wp_sp->GetID()), |
795 | 5 | strm.GetString()); |
796 | 5 | } |
797 | 5 | } |
798 | | |
799 | | void CommandCompletions::TypeCategoryNames(CommandInterpreter &interpreter, |
800 | | CompletionRequest &request, |
801 | 6 | SearchFilter *searcher) { |
802 | 6 | DataVisualization::Categories::ForEach( |
803 | 30 | [&request](const lldb::TypeCategoryImplSP &category_sp) { |
804 | 30 | request.TryCompleteCurrentArg(category_sp->GetName(), |
805 | 30 | category_sp->GetDescription()); |
806 | 30 | return true; |
807 | 30 | }); |
808 | 6 | } |
809 | | |
810 | | void CommandCompletions::CompleteModifiableCmdPathArgs( |
811 | | CommandInterpreter &interpreter, CompletionRequest &request, |
812 | 1 | OptionElementVector &opt_element_vector) { |
813 | | // The only arguments constitute a command path, however, there might be |
814 | | // options interspersed among the arguments, and we need to skip those. Do that |
815 | | // by copying the args vector, and just dropping all the option bits: |
816 | 1 | Args args = request.GetParsedLine(); |
817 | 1 | std::vector<size_t> to_delete; |
818 | 1 | for (auto &elem : opt_element_vector) { |
819 | 0 | to_delete.push_back(elem.opt_pos); |
820 | 0 | if (elem.opt_arg_pos != 0) |
821 | 0 | to_delete.push_back(elem.opt_arg_pos); |
822 | 0 | } |
823 | 1 | sort(to_delete.begin(), to_delete.end(), std::greater<size_t>()); |
824 | 1 | for (size_t idx : to_delete) |
825 | 0 | args.DeleteArgumentAtIndex(idx); |
826 | | |
827 | | // At this point, we should only have args, so now lookup the command up to |
828 | | // the cursor element. |
829 | | |
830 | | // There's nothing here but options. It doesn't seem very useful here to |
831 | | // dump all the commands, so just return. |
832 | 1 | size_t num_args = args.GetArgumentCount(); |
833 | 1 | if (num_args == 0) |
834 | 0 | return; |
835 | | |
836 | | // There's just one argument, so we should complete its name: |
837 | 1 | StringList matches; |
838 | 1 | if (num_args == 1) { |
839 | 1 | interpreter.GetUserCommandObject(args.GetArgumentAtIndex(0), &matches, |
840 | 1 | nullptr); |
841 | 1 | request.AddCompletions(matches); |
842 | 1 | return; |
843 | 1 | } |
844 | | |
845 | | // There was more than one path element, lets find the containing command: |
846 | 0 | Status error; |
847 | 0 | CommandObjectMultiword *mwc = |
848 | 0 | interpreter.VerifyUserMultiwordCmdPath(args, true, error); |
849 | | |
850 | | // Something was wrong somewhere along the path, but I don't think there's |
851 | | // a good way to go back and fill in the missing elements: |
852 | 0 | if (error.Fail()) |
853 | 0 | return; |
854 | | |
855 | | // This should never happen. We already handled the case of one argument |
856 | | // above, and we can only get Success & nullptr back if there's a one-word |
857 | | // leaf. |
858 | 0 | assert(mwc != nullptr); |
859 | | |
860 | 0 | mwc->GetSubcommandObject(args.GetArgumentAtIndex(num_args - 1), &matches); |
861 | 0 | if (matches.GetSize() == 0) |
862 | 0 | return; |
863 | | |
864 | 0 | request.AddCompletions(matches); |
865 | 0 | } |