]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - source/Commands/CommandCompletions.cpp
Import LLDB as of upstream SVN 228549 (git 39760838)
[FreeBSD/FreeBSD.git] / source / Commands / CommandCompletions.cpp
1 //===-- CommandCompletions.cpp ----------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "lldb/lldb-python.h"
11
12 // C Includes
13 #include <sys/stat.h>
14 #if defined(__APPLE__) || defined(__linux__)
15 #include <pwd.h>
16 #endif
17
18 // C++ Includes
19 // Other libraries and framework includes
20 // Project includes
21 #include "lldb/Host/FileSpec.h"
22 #include "lldb/Core/FileSpecList.h"
23 #include "lldb/Core/PluginManager.h"
24 #include "lldb/Core/Module.h"
25 #include "lldb/Interpreter/Args.h"
26 #include "lldb/Interpreter/CommandCompletions.h"
27 #include "lldb/Interpreter/CommandInterpreter.h"
28 #include "lldb/Symbol/CompileUnit.h"
29 #include "lldb/Symbol/Variable.h"
30 #include "lldb/Target/Target.h"
31 #include "lldb/Utility/CleanUp.h"
32
33 #include "llvm/ADT/SmallString.h"
34
35 using namespace lldb_private;
36
37 CommandCompletions::CommonCompletionElement
38 CommandCompletions::g_common_completions[] =
39 {
40     {eCustomCompletion,          NULL},
41     {eSourceFileCompletion,      CommandCompletions::SourceFiles},
42     {eDiskFileCompletion,        CommandCompletions::DiskFiles},
43     {eDiskDirectoryCompletion,   CommandCompletions::DiskDirectories},
44     {eSymbolCompletion,          CommandCompletions::Symbols},
45     {eModuleCompletion,          CommandCompletions::Modules},
46     {eSettingsNameCompletion,    CommandCompletions::SettingsNames},
47     {ePlatformPluginCompletion,  CommandCompletions::PlatformPluginNames},
48     {eArchitectureCompletion,    CommandCompletions::ArchitectureNames},
49     {eVariablePathCompletion,    CommandCompletions::VariablePath},
50     {eNoCompletion,              NULL}      // This one has to be last in the list.
51 };
52
53 bool
54 CommandCompletions::InvokeCommonCompletionCallbacks 
55 (
56     CommandInterpreter &interpreter,
57     uint32_t completion_mask,
58     const char *completion_str,
59     int match_start_point,
60     int max_return_elements,
61     SearchFilter *searcher,
62     bool &word_complete,
63     StringList &matches
64 )
65 {
66     bool handled = false;
67
68     if (completion_mask & eCustomCompletion)
69         return false;
70
71     for (int i = 0; ; i++)
72     {
73         if (g_common_completions[i].type == eNoCompletion)
74             break;
75          else if ((g_common_completions[i].type & completion_mask) == g_common_completions[i].type
76                    && g_common_completions[i].callback != NULL)
77          {
78             handled = true;
79             g_common_completions[i].callback (interpreter,
80                                               completion_str,
81                                               match_start_point,
82                                               max_return_elements,
83                                               searcher,
84                                               word_complete,
85                                               matches);
86         }
87     }
88     return handled;
89 }
90
91 int
92 CommandCompletions::SourceFiles 
93 (
94     CommandInterpreter &interpreter,
95     const char *partial_file_name,
96     int match_start_point,
97     int max_return_elements,
98     SearchFilter *searcher,
99     bool &word_complete,
100     StringList &matches
101 )
102 {
103     word_complete = true;
104     // Find some way to switch "include support files..."
105     SourceFileCompleter completer (interpreter,
106                                    false, 
107                                    partial_file_name, 
108                                    match_start_point, 
109                                    max_return_elements,
110                                    matches);
111
112     if (searcher == NULL)
113     {
114         lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
115         SearchFilterForUnconstrainedSearches null_searcher (target_sp);
116         completer.DoCompletion (&null_searcher);
117     }
118     else
119     {
120         completer.DoCompletion (searcher);
121     }
122     return matches.GetSize();
123 }
124
125 typedef struct DiskFilesOrDirectoriesBaton
126 {
127     const char *remainder;
128     char *partial_name_copy;
129     bool only_directories;
130     bool *saw_directory;
131     StringList *matches;
132     char *end_ptr;
133     size_t baselen;
134 } DiskFilesOrDirectoriesBaton;
135
136 FileSpec::EnumerateDirectoryResult DiskFilesOrDirectoriesCallback(void *baton, FileSpec::FileType file_type, const FileSpec &spec)
137 {
138     const char *name = spec.GetFilename().AsCString();
139
140     const DiskFilesOrDirectoriesBaton *parameters = (DiskFilesOrDirectoriesBaton*)baton;
141     char *end_ptr = parameters->end_ptr;
142     char *partial_name_copy = parameters->partial_name_copy;
143     const char *remainder = parameters->remainder;
144
145     // Omit ".", ".." and any . files if the match string doesn't start with .
146     if (name[0] == '.')
147     {
148         if (name[1] == '\0')
149             return FileSpec::eEnumerateDirectoryResultNext;
150         else if (name[1] == '.' && name[2] == '\0')
151             return FileSpec::eEnumerateDirectoryResultNext;
152         else if (remainder[0] != '.')
153             return FileSpec::eEnumerateDirectoryResultNext;
154     }
155
156     // If we found a directory, we put a "/" at the end of the name.
157
158     if (remainder[0] == '\0' || strstr(name, remainder) == name)
159     {
160         if (strlen(name) + parameters->baselen >= PATH_MAX)
161             return FileSpec::eEnumerateDirectoryResultNext;
162
163         strcpy(end_ptr, name);
164
165         bool isa_directory = false;
166         if (file_type == FileSpec::eFileTypeDirectory)
167             isa_directory = true;
168         else if (file_type == FileSpec::eFileTypeSymbolicLink)
169         {
170             struct stat stat_buf;
171             if ((stat(partial_name_copy, &stat_buf) == 0) && S_ISDIR(stat_buf.st_mode))
172                 isa_directory = true;
173         }
174
175         if (isa_directory)
176         {
177             *parameters->saw_directory = true;
178             size_t len = strlen(parameters->partial_name_copy);
179             partial_name_copy[len] = '/';
180             partial_name_copy[len + 1] = '\0';
181         }
182         if (parameters->only_directories && !isa_directory)
183             return FileSpec::eEnumerateDirectoryResultNext;
184         parameters->matches->AppendString(partial_name_copy);
185     }
186
187     return FileSpec::eEnumerateDirectoryResultNext;
188 }
189
190 static int
191 DiskFilesOrDirectories 
192 (
193     const char *partial_file_name,
194     bool only_directories,
195     bool &saw_directory,
196     StringList &matches
197 )
198 {
199     // I'm going to  use the "glob" function with GLOB_TILDE for user directory expansion.  
200     // If it is not defined on your host system, you'll need to implement it yourself...
201     
202     size_t partial_name_len = strlen(partial_file_name);
203     
204     if (partial_name_len >= PATH_MAX)
205         return matches.GetSize();
206     
207     // This copy of the string will be cut up into the directory part, and the remainder.  end_ptr
208     // below will point to the place of the remainder in this string.  Then when we've resolved the
209     // containing directory, and opened it, we'll read the directory contents and overwrite the
210     // partial_name_copy starting from end_ptr with each of the matches.  Thus we will preserve
211     // the form the user originally typed.
212     
213     char partial_name_copy[PATH_MAX];
214     memcpy(partial_name_copy, partial_file_name, partial_name_len);
215     partial_name_copy[partial_name_len] = '\0';
216     
217     // We'll need to save a copy of the remainder for comparison, which we do here.
218     char remainder[PATH_MAX];
219     
220     // end_ptr will point past the last / in partial_name_copy, or if there is no slash to the beginning of the string.
221     char *end_ptr;
222     
223     end_ptr = strrchr(partial_name_copy, '/');
224     
225     // This will store the resolved form of the containing directory
226     llvm::SmallString<64> containing_part;
227     
228     if (end_ptr == NULL)
229     {
230         // There's no directory.  If the thing begins with a "~" then this is a bare
231         // user name.
232         if (*partial_name_copy == '~')
233         {
234             // Nothing here but the user name.  We could just put a slash on the end, 
235             // but for completeness sake we'll resolve the user name and only put a slash
236             // on the end if it exists.
237             llvm::SmallString<64> resolved_username(partial_name_copy);
238             FileSpec::ResolveUsername (resolved_username);
239                                                           
240            // Not sure how this would happen, a username longer than PATH_MAX?  Still...
241             if (resolved_username.size() == 0)
242             {
243                 // The user name didn't resolve, let's look in the password database for matches.
244                 // The user name database contains duplicates, and is not in alphabetical order, so
245                 // we'll use a set to manage that for us.
246                 FileSpec::ResolvePartialUsername (partial_name_copy, matches);
247                 if (matches.GetSize() > 0)
248                     saw_directory = true;
249                 return matches.GetSize();
250             } 
251             else
252             {   
253                 //The thing exists, put a '/' on the end, and return it...
254                 // FIXME: complete user names here:
255                 partial_name_copy[partial_name_len] = '/';
256                 partial_name_copy[partial_name_len+1] = '\0';
257                 matches.AppendString(partial_name_copy);
258                 saw_directory = true;
259                 return matches.GetSize();
260             }
261         }
262         else
263         {
264             // The containing part is the CWD, and the whole string is the remainder.
265             containing_part = ".";
266             strcpy(remainder, partial_name_copy);
267             end_ptr = partial_name_copy;
268         }
269     }
270     else
271     {
272         if (end_ptr == partial_name_copy)
273         {
274             // We're completing a file or directory in the root volume.
275             containing_part = "/";
276         }
277         else
278         {
279             containing_part.append(partial_name_copy, end_ptr);
280         }
281         // Push end_ptr past the final "/" and set remainder.
282         end_ptr++;
283         strcpy(remainder, end_ptr);
284     }
285     
286     // Look for a user name in the containing part, and if it's there, resolve it and stick the
287     // result back into the containing_part:
288
289     if (*partial_name_copy == '~')
290     {
291         FileSpec::ResolveUsername(containing_part);
292         // User name doesn't exist, we're not getting any further...
293         if (containing_part.empty())
294             return matches.GetSize();
295     }
296
297     // Okay, containing_part is now the directory we want to open and look for files:
298
299     size_t baselen = end_ptr - partial_name_copy;
300     
301     DiskFilesOrDirectoriesBaton parameters;
302     parameters.remainder = remainder;
303     parameters.partial_name_copy = partial_name_copy;
304     parameters.only_directories = only_directories;
305     parameters.saw_directory = &saw_directory;
306     parameters.matches = &matches;
307     parameters.end_ptr = end_ptr;
308     parameters.baselen = baselen;
309
310     FileSpec::EnumerateDirectory(containing_part.c_str(), true, true, true, DiskFilesOrDirectoriesCallback, &parameters);
311     
312     return matches.GetSize();
313 }
314
315 int
316 CommandCompletions::DiskFiles 
317 (
318     CommandInterpreter &interpreter,
319     const char *partial_file_name,
320     int match_start_point,
321     int max_return_elements,
322     SearchFilter *searcher,
323     bool &word_complete,
324     StringList &matches
325 )
326 {
327
328     int ret_val = DiskFilesOrDirectories (partial_file_name,
329                                           false,
330                                           word_complete,
331                                           matches);
332     word_complete = !word_complete;
333     return ret_val;
334 }
335
336 int
337 CommandCompletions::DiskDirectories 
338 (
339     CommandInterpreter &interpreter,
340     const char *partial_file_name,
341     int match_start_point,
342     int max_return_elements,
343     SearchFilter *searcher,
344     bool &word_complete,
345     StringList &matches
346 )
347 {
348     int ret_val =  DiskFilesOrDirectories (partial_file_name,
349                                            true,
350                                            word_complete,
351                                            matches); 
352     word_complete = false;
353     return ret_val;
354 }
355
356 int
357 CommandCompletions::Modules 
358 (
359     CommandInterpreter &interpreter,
360     const char *partial_file_name,
361     int match_start_point,
362     int max_return_elements,
363     SearchFilter *searcher,
364     bool &word_complete,
365     StringList &matches
366 )
367 {
368     word_complete = true;
369     ModuleCompleter completer (interpreter,
370                                partial_file_name, 
371                                match_start_point, 
372                                max_return_elements, 
373                                matches);
374     
375     if (searcher == NULL)
376     {
377         lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
378         SearchFilterForUnconstrainedSearches null_searcher (target_sp);
379         completer.DoCompletion (&null_searcher);
380     }
381     else
382     {
383         completer.DoCompletion (searcher);
384     }
385     return matches.GetSize();
386 }
387
388 int
389 CommandCompletions::Symbols 
390 (
391     CommandInterpreter &interpreter,
392     const char *partial_file_name,
393     int match_start_point,
394     int max_return_elements,
395     SearchFilter *searcher,
396     bool &word_complete,
397     StringList &matches)
398 {
399     word_complete = true;
400     SymbolCompleter completer (interpreter,
401                                partial_file_name, 
402                                match_start_point, 
403                                max_return_elements, 
404                                matches);
405
406     if (searcher == NULL)
407     {
408         lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
409         SearchFilterForUnconstrainedSearches null_searcher (target_sp);
410         completer.DoCompletion (&null_searcher);
411     }
412     else
413     {
414         completer.DoCompletion (searcher);
415     }
416     return matches.GetSize();
417 }
418
419 int
420 CommandCompletions::SettingsNames (CommandInterpreter &interpreter,
421                                    const char *partial_setting_name,
422                                    int match_start_point,
423                                    int max_return_elements,
424                                    SearchFilter *searcher,
425                                    bool &word_complete,
426                                    StringList &matches)
427 {
428     // Cache the full setting name list
429     static StringList g_property_names;
430     if (g_property_names.GetSize() == 0)
431     {
432         // Generate the full setting name list on demand
433         lldb::OptionValuePropertiesSP properties_sp (interpreter.GetDebugger().GetValueProperties());
434         if (properties_sp)
435         {
436             StreamString strm;
437             properties_sp->DumpValue(NULL, strm, OptionValue::eDumpOptionName);
438             const std::string &str = strm.GetString();
439             g_property_names.SplitIntoLines(str.c_str(), str.size());
440         }
441     }
442     
443     size_t exact_matches_idx = SIZE_MAX;
444     const size_t num_matches = g_property_names.AutoComplete (partial_setting_name, matches, exact_matches_idx);
445     word_complete = exact_matches_idx != SIZE_MAX;
446     return num_matches;
447 }
448
449
450 int
451 CommandCompletions::PlatformPluginNames (CommandInterpreter &interpreter,
452                                          const char *partial_name,
453                                          int match_start_point,
454                                          int max_return_elements,
455                                          SearchFilter *searcher,
456                                          bool &word_complete,
457                                          lldb_private::StringList &matches)
458 {
459     const uint32_t num_matches = PluginManager::AutoCompletePlatformName(partial_name, matches);
460     word_complete = num_matches == 1;
461     return num_matches;
462 }
463
464 int
465 CommandCompletions::ArchitectureNames (CommandInterpreter &interpreter,
466                                        const char *partial_name,
467                                        int match_start_point,
468                                        int max_return_elements,
469                                        SearchFilter *searcher,
470                                        bool &word_complete,
471                                        lldb_private::StringList &matches)
472 {
473     const uint32_t num_matches = ArchSpec::AutoComplete (partial_name, matches);
474     word_complete = num_matches == 1;
475     return num_matches;
476 }
477
478
479 int
480 CommandCompletions::VariablePath (CommandInterpreter &interpreter,
481                                   const char *partial_name,
482                                   int match_start_point,
483                                   int max_return_elements,
484                                   SearchFilter *searcher,
485                                   bool &word_complete,
486                                   lldb_private::StringList &matches)
487 {
488     return Variable::AutoComplete (interpreter.GetExecutionContext(), partial_name, matches, word_complete);
489 }
490
491
492 CommandCompletions::Completer::Completer 
493 (
494     CommandInterpreter &interpreter,
495     const char *completion_str,
496     int match_start_point,
497     int max_return_elements,
498     StringList &matches
499 ) :
500     m_interpreter (interpreter),
501     m_completion_str (completion_str),
502     m_match_start_point (match_start_point),
503     m_max_return_elements (max_return_elements),
504     m_matches (matches)
505 {
506 }
507
508 CommandCompletions::Completer::~Completer ()
509 {
510
511 }
512
513 //----------------------------------------------------------------------
514 // SourceFileCompleter
515 //----------------------------------------------------------------------
516
517 CommandCompletions::SourceFileCompleter::SourceFileCompleter 
518 (
519     CommandInterpreter &interpreter,
520     bool include_support_files,
521     const char *completion_str,
522     int match_start_point,
523     int max_return_elements,
524     StringList &matches
525 ) :
526     CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches),
527     m_include_support_files (include_support_files),
528     m_matching_files()
529 {
530     FileSpec partial_spec (m_completion_str.c_str(), false);
531     m_file_name = partial_spec.GetFilename().GetCString();
532     m_dir_name = partial_spec.GetDirectory().GetCString();
533 }
534
535 Searcher::Depth
536 CommandCompletions::SourceFileCompleter::GetDepth()
537 {
538     return eDepthCompUnit;
539 }
540
541 Searcher::CallbackReturn
542 CommandCompletions::SourceFileCompleter::SearchCallback (
543     SearchFilter &filter,
544     SymbolContext &context,
545     Address *addr,
546     bool complete
547 )
548 {
549     if (context.comp_unit != NULL)
550     {
551         if (m_include_support_files)
552         {
553             FileSpecList supporting_files = context.comp_unit->GetSupportFiles();
554             for (size_t sfiles = 0; sfiles < supporting_files.GetSize(); sfiles++)
555             {
556                 const FileSpec &sfile_spec = supporting_files.GetFileSpecAtIndex(sfiles);
557                 const char *sfile_file_name = sfile_spec.GetFilename().GetCString();
558                 const char *sfile_dir_name = sfile_spec.GetFilename().GetCString();
559                 bool match = false;
560                 if (m_file_name && sfile_file_name
561                     && strstr (sfile_file_name, m_file_name) == sfile_file_name)
562                     match = true;
563                 if (match && m_dir_name && sfile_dir_name
564                     && strstr (sfile_dir_name, m_dir_name) != sfile_dir_name)
565                     match = false;
566
567                 if (match)
568                 {
569                     m_matching_files.AppendIfUnique(sfile_spec);
570                 }
571             }
572
573         }
574         else
575         {
576             const char *cur_file_name = context.comp_unit->GetFilename().GetCString();
577             const char *cur_dir_name = context.comp_unit->GetDirectory().GetCString();
578
579             bool match = false;
580             if (m_file_name && cur_file_name
581                 && strstr (cur_file_name, m_file_name) == cur_file_name)
582                 match = true;
583
584             if (match && m_dir_name && cur_dir_name
585                 && strstr (cur_dir_name, m_dir_name) != cur_dir_name)
586                 match = false;
587
588             if (match)
589             {
590                 m_matching_files.AppendIfUnique(context.comp_unit);
591             }
592         }
593     }
594     return Searcher::eCallbackReturnContinue;
595 }
596
597 size_t
598 CommandCompletions::SourceFileCompleter::DoCompletion (SearchFilter *filter)
599 {
600     filter->Search (*this);
601     // Now convert the filelist to completions:
602     for (size_t i = 0; i < m_matching_files.GetSize(); i++)
603     {
604         m_matches.AppendString (m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString());
605     }
606     return m_matches.GetSize();
607
608 }
609
610 //----------------------------------------------------------------------
611 // SymbolCompleter
612 //----------------------------------------------------------------------
613
614 static bool
615 regex_chars (const char comp)
616 {
617     if (comp == '[' || comp == ']' ||
618         comp == '(' || comp == ')' ||
619         comp == '{' || comp == '}' ||
620         comp == '+' ||
621         comp == '.' ||
622         comp == '*' ||
623         comp == '|' ||
624         comp == '^' ||
625         comp == '$' ||
626         comp == '\\' ||
627         comp == '?')
628         return true;
629     else
630         return false;
631 }
632 CommandCompletions::SymbolCompleter::SymbolCompleter 
633 (
634     CommandInterpreter &interpreter,
635     const char *completion_str,
636     int match_start_point,
637     int max_return_elements,
638     StringList &matches
639 ) :
640     CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches)
641 {
642     std::string regex_str;
643     if (completion_str && completion_str[0])
644     {
645         regex_str.append("^");
646         regex_str.append(completion_str);
647     }
648     else
649     {
650         // Match anything since the completion string is empty
651         regex_str.append(".");
652     }
653     std::string::iterator pos = find_if(regex_str.begin() + 1, regex_str.end(), regex_chars);
654     while (pos < regex_str.end())
655     {
656         pos = regex_str.insert(pos, '\\');
657         pos = find_if(pos + 2, regex_str.end(), regex_chars);
658     }
659     m_regex.Compile(regex_str.c_str());
660 }
661
662 Searcher::Depth
663 CommandCompletions::SymbolCompleter::GetDepth()
664 {
665     return eDepthModule;
666 }
667
668 Searcher::CallbackReturn
669 CommandCompletions::SymbolCompleter::SearchCallback (
670     SearchFilter &filter,
671     SymbolContext &context,
672     Address *addr,
673     bool complete
674 )
675 {
676     if (context.module_sp)
677     {
678         SymbolContextList sc_list;        
679         const bool include_symbols = true;
680         const bool include_inlines = true;
681         const bool append = true;
682         context.module_sp->FindFunctions (m_regex, include_symbols, include_inlines, append, sc_list);
683
684         SymbolContext sc;
685         // Now add the functions & symbols to the list - only add if unique:
686         for (uint32_t i = 0; i < sc_list.GetSize(); i++)
687         {
688             if (sc_list.GetContextAtIndex(i, sc))
689             {
690                 ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled);
691                 if (!func_name.IsEmpty())
692                     m_match_set.insert (func_name);
693             }
694         }
695     }
696     return Searcher::eCallbackReturnContinue;
697 }
698
699 size_t
700 CommandCompletions::SymbolCompleter::DoCompletion (SearchFilter *filter)
701 {
702     filter->Search (*this);
703     collection::iterator pos = m_match_set.begin(), end = m_match_set.end();
704     for (pos = m_match_set.begin(); pos != end; pos++)
705         m_matches.AppendString((*pos).GetCString());
706
707     return m_matches.GetSize();
708 }
709
710 //----------------------------------------------------------------------
711 // ModuleCompleter
712 //----------------------------------------------------------------------
713 CommandCompletions::ModuleCompleter::ModuleCompleter 
714 (
715     CommandInterpreter &interpreter,
716     const char *completion_str,
717     int match_start_point,
718     int max_return_elements,
719     StringList &matches
720 ) :
721     CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches)
722 {
723     FileSpec partial_spec (m_completion_str.c_str(), false);
724     m_file_name = partial_spec.GetFilename().GetCString();
725     m_dir_name = partial_spec.GetDirectory().GetCString();
726 }
727
728 Searcher::Depth
729 CommandCompletions::ModuleCompleter::GetDepth()
730 {
731     return eDepthModule;
732 }
733
734 Searcher::CallbackReturn
735 CommandCompletions::ModuleCompleter::SearchCallback (
736     SearchFilter &filter,
737     SymbolContext &context,
738     Address *addr,
739     bool complete
740 )
741 {
742     if (context.module_sp)
743     {
744         const char *cur_file_name = context.module_sp->GetFileSpec().GetFilename().GetCString();
745         const char *cur_dir_name = context.module_sp->GetFileSpec().GetDirectory().GetCString();
746
747         bool match = false;
748         if (m_file_name && cur_file_name
749             && strstr (cur_file_name, m_file_name) == cur_file_name)
750             match = true;
751
752         if (match && m_dir_name && cur_dir_name
753             && strstr (cur_dir_name, m_dir_name) != cur_dir_name)
754             match = false;
755
756         if (match)
757         {
758             m_matches.AppendString (cur_file_name);
759         }
760     }
761     return Searcher::eCallbackReturnContinue;
762 }
763
764 size_t
765 CommandCompletions::ModuleCompleter::DoCompletion (SearchFilter *filter)
766 {
767     filter->Search (*this);
768     return m_matches.GetSize();
769 }