Add missing reference symbol to a function parameter.
[platform/upstream/gflags.git] / src / gflags_completions.cc
1 // Copyright (c) 2008, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 // ---
31
32 // Bash-style command line flag completion for C++ binaries
33 //
34 // This module implements bash-style completions.  It achieves this
35 // goal in the following broad chunks:
36 //
37 //  1) Take a to-be-completed word, and examine it for search hints
38 //  2) Identify all potentially matching flags
39 //     2a) If there are no matching flags, do nothing.
40 //     2b) If all matching flags share a common prefix longer than the
41 //         completion word, output just that matching prefix
42 //  3) Categorize those flags to produce a rough ordering of relevence.
43 //  4) Potentially trim the set of flags returned to a smaller number
44 //     that bash is happier with
45 //  5) Output the matching flags in groups ordered by relevence.
46 //     5a) Force bash to place most-relevent groups at the top of the list
47 //     5b) Trim most flag's descriptions to fit on a single terminal line
48
49
50 #include "gflags_completions.h"
51
52 #include "config.h"
53
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>   // for strlen
57
58 #include <set>
59 #include <string>
60 #include <utility>
61 #include <vector>
62
63 #include "gflags.h"
64 #include "util.h"
65
66 using std::set;
67 using std::string;
68 using std::vector;
69
70
71 DEFINE_string(tab_completion_word, "",
72               "If non-empty, HandleCommandLineCompletions() will hijack the "
73               "process and attempt to do bash-style command line flag "
74               "completion on this value.");
75 DEFINE_int32(tab_completion_columns, 80,
76              "Number of columns to use in output for tab completion");
77
78
79 namespace GFLAGS_NAMESPACE {
80
81
82 namespace {
83 // Function prototypes and Type forward declarations.  Code may be
84 // more easily understood if it is roughly ordered according to
85 // control flow, rather than by C's "declare before use" ordering
86 struct CompletionOptions;
87 struct NotableFlags;
88
89 // The entry point if flag completion is to be used.
90 static void PrintFlagCompletionInfo(void);
91
92
93 // 1) Examine search word
94 static void CanonicalizeCursorWordAndSearchOptions(
95     const string &cursor_word,
96     string *canonical_search_token,
97     CompletionOptions *options);
98
99 static bool RemoveTrailingChar(string *str, char c);
100
101
102 // 2) Find all matches
103 static void FindMatchingFlags(
104     const vector<CommandLineFlagInfo> &all_flags,
105     const CompletionOptions &options,
106     const string &match_token,
107     set<const CommandLineFlagInfo *> *all_matches,
108     string *longest_common_prefix);
109
110 static bool DoesSingleFlagMatch(
111     const CommandLineFlagInfo &flag,
112     const CompletionOptions &options,
113     const string &match_token);
114
115
116 // 3) Categorize matches
117 static void CategorizeAllMatchingFlags(
118     const set<const CommandLineFlagInfo *> &all_matches,
119     const string &search_token,
120     const string &module,
121     const string &package_dir,
122     NotableFlags *notable_flags);
123
124 static void TryFindModuleAndPackageDir(
125     const vector<CommandLineFlagInfo> &all_flags,
126     string *module,
127     string *package_dir);
128
129
130 // 4) Decide which flags to use
131 static void FinalizeCompletionOutput(
132     const set<const CommandLineFlagInfo *> &matching_flags,
133     CompletionOptions *options,
134     NotableFlags *notable_flags,
135     vector<string> *completions);
136
137 static void RetrieveUnusedFlags(
138     const set<const CommandLineFlagInfo *> &matching_flags,
139     const NotableFlags &notable_flags,
140     set<const CommandLineFlagInfo *> *unused_flags);
141
142
143 // 5) Output matches
144 static void OutputSingleGroupWithLimit(
145     const set<const CommandLineFlagInfo *> &group,
146     const string &line_indentation,
147     const string &header,
148     const string &footer,
149     bool long_output_format,
150     int *remaining_line_limit,
151     size_t *completion_elements_added,
152     vector<string> *completions);
153
154 // (helpers for #5)
155 static string GetShortFlagLine(
156     const string &line_indentation,
157     const CommandLineFlagInfo &info);
158
159 static string GetLongFlagLine(
160     const string &line_indentation,
161     const CommandLineFlagInfo &info);
162
163
164 //
165 // Useful types
166
167 // Try to deduce the intentions behind this completion attempt.  Return the
168 // canonical search term in 'canonical_search_token'.  Binary search options
169 // are returned in the various booleans, which should all have intuitive
170 // semantics, possibly except:
171 //  - return_all_matching_flags: Generally, we'll trim the number of
172 //    returned candidates to some small number, showing those that are
173 //    most likely to be useful first.  If this is set, however, the user
174 //    really does want us to return every single flag as an option.
175 //  - force_no_update: Any time we output lines, all of which share a
176 //    common prefix, bash will 'helpfully' not even bother to show the
177 //    output, instead changing the current word to be that common prefix.
178 //    If it's clear this shouldn't happen, we'll set this boolean
179 struct CompletionOptions {
180   bool flag_name_substring_search;
181   bool flag_location_substring_search;
182   bool flag_description_substring_search;
183   bool return_all_matching_flags;
184   bool force_no_update;
185 };
186
187 // Notable flags are flags that are special or preferred for some
188 // reason.  For example, flags that are defined in the binary's module
189 // are expected to be much more relevent than flags defined in some
190 // other random location.  These sets are specified roughly in precedence
191 // order.  Once a flag is placed in one of these 'higher' sets, it won't
192 // be placed in any of the 'lower' sets.
193 struct NotableFlags {
194   typedef set<const CommandLineFlagInfo *> FlagSet;
195   FlagSet perfect_match_flag;
196   FlagSet module_flags;       // Found in module file
197   FlagSet package_flags;      // Found in same directory as module file
198   FlagSet most_common_flags;  // One of the XXX most commonly supplied flags
199   FlagSet subpackage_flags;   // Found in subdirectories of package
200 };
201
202
203 //
204 // Tab completion implementation - entry point
205 static void PrintFlagCompletionInfo(void) {
206   string cursor_word = FLAGS_tab_completion_word;
207   string canonical_token;
208   CompletionOptions options = { };
209   CanonicalizeCursorWordAndSearchOptions(
210       cursor_word,
211       &canonical_token,
212       &options);
213
214   DVLOG(1) << "Identified canonical_token: '" << canonical_token << "'";
215
216   vector<CommandLineFlagInfo> all_flags;
217   set<const CommandLineFlagInfo *> matching_flags;
218   GetAllFlags(&all_flags);
219   DVLOG(2) << "Found " << all_flags.size() << " flags overall";
220
221   string longest_common_prefix;
222   FindMatchingFlags(
223       all_flags,
224       options,
225       canonical_token,
226       &matching_flags,
227       &longest_common_prefix);
228   DVLOG(1) << "Identified " << matching_flags.size() << " matching flags";
229   DVLOG(1) << "Identified " << longest_common_prefix
230           << " as longest common prefix.";
231   if (longest_common_prefix.size() > canonical_token.size()) {
232     // There's actually a shared common prefix to all matching flags,
233     // so may as well output that and quit quickly.
234     DVLOG(1) << "The common prefix '" << longest_common_prefix
235             << "' was longer than the token '" << canonical_token
236             << "'.  Returning just this prefix for completion.";
237     fprintf(stdout, "--%s", longest_common_prefix.c_str());
238     return;
239   }
240   if (matching_flags.empty()) {
241     VLOG(1) << "There were no matching flags, returning nothing.";
242     return;
243   }
244
245   string module;
246   string package_dir;
247   TryFindModuleAndPackageDir(all_flags, &module, &package_dir);
248   DVLOG(1) << "Identified module: '" << module << "'";
249   DVLOG(1) << "Identified package_dir: '" << package_dir << "'";
250
251   NotableFlags notable_flags;
252   CategorizeAllMatchingFlags(
253       matching_flags,
254       canonical_token,
255       module,
256       package_dir,
257       &notable_flags);
258   DVLOG(2) << "Categorized matching flags:";
259   DVLOG(2) << " perfect_match: " << notable_flags.perfect_match_flag.size();
260   DVLOG(2) << " module: " << notable_flags.module_flags.size();
261   DVLOG(2) << " package: " << notable_flags.package_flags.size();
262   DVLOG(2) << " most common: " << notable_flags.most_common_flags.size();
263   DVLOG(2) << " subpackage: " << notable_flags.subpackage_flags.size();
264
265   vector<string> completions;
266   FinalizeCompletionOutput(
267       matching_flags,
268       &options,
269       &notable_flags,
270       &completions);
271
272   if (options.force_no_update)
273     completions.push_back("~");
274
275   DVLOG(1) << "Finalized with " << completions.size()
276           << " chosen completions";
277
278   for (vector<string>::const_iterator it = completions.begin();
279       it != completions.end();
280       ++it) {
281     DVLOG(9) << "  Completion entry: '" << *it << "'";
282     fprintf(stdout, "%s\n", it->c_str());
283   }
284 }
285
286
287 // 1) Examine search word (and helper method)
288 static void CanonicalizeCursorWordAndSearchOptions(
289     const string &cursor_word,
290     string *canonical_search_token,
291     CompletionOptions *options) {
292   *canonical_search_token = cursor_word;
293   if (canonical_search_token->empty()) return;
294
295   // Get rid of leading quotes and dashes in the search term
296   if ((*canonical_search_token)[0] == '"')
297     *canonical_search_token = canonical_search_token->substr(1);
298   while ((*canonical_search_token)[0] == '-')
299     *canonical_search_token = canonical_search_token->substr(1);
300
301   options->flag_name_substring_search = false;
302   options->flag_location_substring_search = false;
303   options->flag_description_substring_search = false;
304   options->return_all_matching_flags = false;
305   options->force_no_update = false;
306
307   // Look for all search options we can deduce now.  Do this by walking
308   // backwards through the term, looking for up to three '?' and up to
309   // one '+' as suffixed characters.  Consume them if found, and remove
310   // them from the canonical search token.
311   int found_question_marks = 0;
312   int found_plusses = 0;
313   while (true) {
314     if (found_question_marks < 3 &&
315         RemoveTrailingChar(canonical_search_token, '?')) {
316       ++found_question_marks;
317       continue;
318     }
319     if (found_plusses < 1 &&
320         RemoveTrailingChar(canonical_search_token, '+')) {
321       ++found_plusses;
322       continue;
323     }
324     break;
325   }
326
327   switch (found_question_marks) {  // all fallthroughs
328     case 3: options->flag_description_substring_search = true;
329     case 2: options->flag_location_substring_search = true;
330     case 1: options->flag_name_substring_search = true;
331   };
332
333   options->return_all_matching_flags = (found_plusses > 0);
334 }
335
336 // Returns true if a char was removed
337 static bool RemoveTrailingChar(string *str, char c) {
338   if (str->empty()) return false;
339   if ((*str)[str->size() - 1] == c) {
340     *str = str->substr(0, str->size() - 1);
341     return true;
342   }
343   return false;
344 }
345
346
347 // 2) Find all matches (and helper methods)
348 static void FindMatchingFlags(
349     const vector<CommandLineFlagInfo> &all_flags,
350     const CompletionOptions &options,
351     const string &match_token,
352     set<const CommandLineFlagInfo *> *all_matches,
353     string *longest_common_prefix) {
354   all_matches->clear();
355   bool first_match = true;
356   for (vector<CommandLineFlagInfo>::const_iterator it = all_flags.begin();
357       it != all_flags.end();
358       ++it) {
359     if (DoesSingleFlagMatch(*it, options, match_token)) {
360       all_matches->insert(&*it);
361       if (first_match) {
362         first_match = false;
363         *longest_common_prefix = it->name;
364       } else {
365         if (longest_common_prefix->empty() || it->name.empty()) {
366           longest_common_prefix->clear();
367           continue;
368         }
369         string::size_type pos = 0;
370         while (pos < longest_common_prefix->size() &&
371             pos < it->name.size() &&
372             (*longest_common_prefix)[pos] == it->name[pos])
373           ++pos;
374         longest_common_prefix->erase(pos);
375       }
376     }
377   }
378 }
379
380 // Given the set of all flags, the parsed match options, and the
381 // canonical search token, produce the set of all candidate matching
382 // flags for subsequent analysis or filtering.
383 static bool DoesSingleFlagMatch(
384     const CommandLineFlagInfo &flag,
385     const CompletionOptions &options,
386     const string &match_token) {
387   // Is there a prefix match?
388   string::size_type pos = flag.name.find(match_token);
389   if (pos == 0) return true;
390
391   // Is there a substring match if we want it?
392   if (options.flag_name_substring_search &&
393       pos != string::npos)
394     return true;
395
396   // Is there a location match if we want it?
397   if (options.flag_location_substring_search &&
398       flag.filename.find(match_token) != string::npos)
399     return true;
400
401   // TODO(user): All searches should probably be case-insensitive
402   // (especially this one...)
403   if (options.flag_description_substring_search &&
404       flag.description.find(match_token) != string::npos)
405     return true;
406
407   return false;
408 }
409
410 // 3) Categorize matches (and helper method)
411
412 // Given a set of matching flags, categorize them by
413 // likely relevence to this specific binary
414 static void CategorizeAllMatchingFlags(
415     const set<const CommandLineFlagInfo *> &all_matches,
416     const string &search_token,
417     const string &module,  // empty if we couldn't find any
418     const string &package_dir,  // empty if we couldn't find any
419     NotableFlags *notable_flags) {
420   notable_flags->perfect_match_flag.clear();
421   notable_flags->module_flags.clear();
422   notable_flags->package_flags.clear();
423   notable_flags->most_common_flags.clear();
424   notable_flags->subpackage_flags.clear();
425
426   for (set<const CommandLineFlagInfo *>::const_iterator it =
427         all_matches.begin();
428       it != all_matches.end();
429       ++it) {
430     DVLOG(2) << "Examining match '" << (*it)->name << "'";
431     DVLOG(7) << "  filename: '" << (*it)->filename << "'";
432     string::size_type pos = string::npos;
433     if (!package_dir.empty())
434       pos = (*it)->filename.find(package_dir);
435     string::size_type slash = string::npos;
436     if (pos != string::npos)  // candidate for package or subpackage match
437       slash = (*it)->filename.find(
438           PATH_SEPARATOR,
439           pos + package_dir.size() + 1);
440
441     if ((*it)->name == search_token) {
442       // Exact match on some flag's name
443       notable_flags->perfect_match_flag.insert(*it);
444       DVLOG(3) << "Result: perfect match";
445     } else if (!module.empty() && (*it)->filename == module) {
446       // Exact match on module filename
447       notable_flags->module_flags.insert(*it);
448       DVLOG(3) << "Result: module match";
449     } else if (!package_dir.empty() &&
450         pos != string::npos && slash == string::npos) {
451       // In the package, since there was no slash after the package portion
452       notable_flags->package_flags.insert(*it);
453       DVLOG(3) << "Result: package match";
454     } else if (false) {
455       // In the list of the XXX most commonly supplied flags overall
456       // TODO(user): Compile this list.
457       DVLOG(3) << "Result: most-common match";
458     } else if (!package_dir.empty() &&
459         pos != string::npos && slash != string::npos) {
460       // In a subdirectory of the package
461       notable_flags->subpackage_flags.insert(*it);
462       DVLOG(3) << "Result: subpackage match";
463     }
464
465     DVLOG(3) << "Result: not special match";
466   }
467 }
468
469 static void PushNameWithSuffix(vector<string>* suffixes, const char* suffix) {
470   suffixes->push_back(
471       StringPrintf("/%s%s", ProgramInvocationShortName(), suffix));
472 }
473
474 static void TryFindModuleAndPackageDir(
475     const vector<CommandLineFlagInfo> &all_flags,
476     string *module,
477     string *package_dir) {
478   module->clear();
479   package_dir->clear();
480
481   vector<string> suffixes;
482   // TODO(user): There's some inherant ambiguity here - multiple directories
483   // could share the same trailing folder and file structure (and even worse,
484   // same file names), causing us to be unsure as to which of the two is the
485   // actual package for this binary.  In this case, we'll arbitrarily choose.
486   PushNameWithSuffix(&suffixes, ".");
487   PushNameWithSuffix(&suffixes, "-main.");
488   PushNameWithSuffix(&suffixes, "_main.");
489   // These four are new but probably merited?
490   PushNameWithSuffix(&suffixes, "-test.");
491   PushNameWithSuffix(&suffixes, "_test.");
492   PushNameWithSuffix(&suffixes, "-unittest.");
493   PushNameWithSuffix(&suffixes, "_unittest.");
494
495   for (vector<CommandLineFlagInfo>::const_iterator it = all_flags.begin();
496       it != all_flags.end();
497       ++it) {
498     for (vector<string>::const_iterator suffix = suffixes.begin();
499         suffix != suffixes.end();
500         ++suffix) {
501       // TODO(user): Make sure the match is near the end of the string
502       if (it->filename.find(*suffix) != string::npos) {
503         *module = it->filename;
504         string::size_type sep = it->filename.rfind(PATH_SEPARATOR);
505         *package_dir = it->filename.substr(0, (sep == string::npos) ? 0 : sep);
506         return;
507       }
508     }
509   }
510 }
511
512 // Can't specialize template type on a locally defined type.  Silly C++...
513 struct DisplayInfoGroup {
514   const char* header;
515   const char* footer;
516   set<const CommandLineFlagInfo *> *group;
517
518   int SizeInLines() const {
519     int size_in_lines = static_cast<int>(group->size()) + 1;
520     if (strlen(header) > 0) {
521       size_in_lines++;
522     }
523     if (strlen(footer) > 0) {
524       size_in_lines++;
525     }
526     return size_in_lines;
527   }
528 };
529
530 // 4) Finalize and trim output flag set
531 static void FinalizeCompletionOutput(
532     const set<const CommandLineFlagInfo *> &matching_flags,
533     CompletionOptions *options,
534     NotableFlags *notable_flags,
535     vector<string> *completions) {
536
537   // We want to output lines in groups.  Each group needs to be indented
538   // the same to keep its lines together.  Unless otherwise required,
539   // only 99 lines should be output to prevent bash from harassing the
540   // user.
541
542   // First, figure out which output groups we'll actually use.  For each
543   // nonempty group, there will be ~3 lines of header & footer, plus all
544   // output lines themselves.
545   int max_desired_lines =  // "999999 flags should be enough for anyone.  -dave"
546     (options->return_all_matching_flags ? 999999 : 98);
547   int lines_so_far = 0;
548
549   vector<DisplayInfoGroup> output_groups;
550   bool perfect_match_found = false;
551   if (lines_so_far < max_desired_lines &&
552       !notable_flags->perfect_match_flag.empty()) {
553     perfect_match_found = true;
554     DisplayInfoGroup group =
555         { "",
556           "==========",
557           &notable_flags->perfect_match_flag };
558     lines_so_far += group.SizeInLines();
559     output_groups.push_back(group);
560   }
561   if (lines_so_far < max_desired_lines &&
562       !notable_flags->module_flags.empty()) {
563     DisplayInfoGroup group = {
564         "-* Matching module flags *-",
565         "===========================",
566         &notable_flags->module_flags };
567     lines_so_far += group.SizeInLines();
568     output_groups.push_back(group);
569   }
570   if (lines_so_far < max_desired_lines &&
571       !notable_flags->package_flags.empty()) {
572     DisplayInfoGroup group = {
573         "-* Matching package flags *-",
574         "============================",
575         &notable_flags->package_flags };
576     lines_so_far += group.SizeInLines();
577     output_groups.push_back(group);
578   }
579   if (lines_so_far < max_desired_lines &&
580       !notable_flags->most_common_flags.empty()) {
581     DisplayInfoGroup group = {
582         "-* Commonly used flags *-",
583         "=========================",
584         &notable_flags->most_common_flags };
585     lines_so_far += group.SizeInLines();
586     output_groups.push_back(group);
587   }
588   if (lines_so_far < max_desired_lines &&
589       !notable_flags->subpackage_flags.empty()) {
590     DisplayInfoGroup group = {
591         "-* Matching sub-package flags *-",
592         "================================",
593         &notable_flags->subpackage_flags };
594     lines_so_far += group.SizeInLines();
595     output_groups.push_back(group);
596   }
597
598   set<const CommandLineFlagInfo *> obscure_flags;  // flags not notable
599   if (lines_so_far < max_desired_lines) {
600     RetrieveUnusedFlags(matching_flags, *notable_flags, &obscure_flags);
601     if (!obscure_flags.empty()) {
602       DisplayInfoGroup group = {
603           "-* Other flags *-",
604           "",
605           &obscure_flags };
606       lines_so_far += group.SizeInLines();
607       output_groups.push_back(group);
608     }
609   }
610
611   // Second, go through each of the chosen output groups and output
612   // as many of those flags as we can, while remaining below our limit
613   int remaining_lines = max_desired_lines;
614   size_t completions_output = 0;
615   int indent = static_cast<int>(output_groups.size()) - 1;
616   for (vector<DisplayInfoGroup>::const_iterator it =
617         output_groups.begin();
618       it != output_groups.end();
619       ++it, --indent) {
620     OutputSingleGroupWithLimit(
621         *it->group,  // group
622         string(indent, ' '),  // line indentation
623         string(it->header),  // header
624         string(it->footer),  // footer
625         perfect_match_found,  // long format
626         &remaining_lines,  // line limit - reduces this by number printed
627         &completions_output,  // completions (not lines) added
628         completions);  // produced completions
629     perfect_match_found = false;
630   }
631
632   if (completions_output != matching_flags.size()) {
633     options->force_no_update = false;
634     completions->push_back("~ (Remaining flags hidden) ~");
635   } else {
636     options->force_no_update = true;
637   }
638 }
639
640 static void RetrieveUnusedFlags(
641     const set<const CommandLineFlagInfo *> &matching_flags,
642     const NotableFlags &notable_flags,
643     set<const CommandLineFlagInfo *> *unused_flags) {
644   // Remove from 'matching_flags' set all members of the sets of
645   // flags we've already printed (specifically, those in notable_flags)
646   for (set<const CommandLineFlagInfo *>::const_iterator it =
647         matching_flags.begin();
648       it != matching_flags.end();
649       ++it) {
650     if (notable_flags.perfect_match_flag.count(*it) ||
651         notable_flags.module_flags.count(*it) ||
652         notable_flags.package_flags.count(*it) ||
653         notable_flags.most_common_flags.count(*it) ||
654         notable_flags.subpackage_flags.count(*it))
655       continue;
656     unused_flags->insert(*it);
657   }
658 }
659
660 // 5) Output matches (and helper methods)
661
662 static void OutputSingleGroupWithLimit(
663     const set<const CommandLineFlagInfo *> &group,
664     const string &line_indentation,
665     const string &header,
666     const string &footer,
667     bool long_output_format,
668     int *remaining_line_limit,
669     size_t *completion_elements_output,
670     vector<string> *completions) {
671   if (group.empty()) return;
672   if (!header.empty()) {
673     if (*remaining_line_limit < 2) return;
674     *remaining_line_limit -= 2;
675     completions->push_back(line_indentation + header);
676     completions->push_back(line_indentation + string(header.size(), '-'));
677   }
678   for (set<const CommandLineFlagInfo *>::const_iterator it = group.begin();
679       it != group.end() && *remaining_line_limit > 0;
680       ++it) {
681     --*remaining_line_limit;
682     ++*completion_elements_output;
683     completions->push_back(
684         (long_output_format
685           ? GetLongFlagLine(line_indentation, **it)
686           : GetShortFlagLine(line_indentation, **it)));
687   }
688   if (!footer.empty()) {
689     if (*remaining_line_limit < 1) return;
690     --*remaining_line_limit;
691     completions->push_back(line_indentation + footer);
692   }
693 }
694
695 static string GetShortFlagLine(
696     const string &line_indentation,
697     const CommandLineFlagInfo &info) {
698   string prefix;
699   bool is_string = (info.type == "string");
700   SStringPrintf(&prefix, "%s--%s [%s%s%s] ",
701                 line_indentation.c_str(),
702                 info.name.c_str(),
703                 (is_string ? "'" : ""),
704                 info.default_value.c_str(),
705                 (is_string ? "'" : ""));
706   int remainder =
707       FLAGS_tab_completion_columns - static_cast<int>(prefix.size());
708   string suffix;
709   if (remainder > 0)
710     suffix =
711         (static_cast<int>(info.description.size()) > remainder ?
712          (info.description.substr(0, remainder - 3) + "...").c_str() :
713          info.description.c_str());
714   return prefix + suffix;
715 }
716
717 static string GetLongFlagLine(
718     const string &line_indentation,
719     const CommandLineFlagInfo &info) {
720
721   string output = DescribeOneFlag(info);
722
723   // Replace '-' with '--', and remove trailing newline before appending
724   // the module definition location.
725   string old_flagname = "-" + info.name;
726   output.replace(
727       output.find(old_flagname),
728       old_flagname.size(),
729       "-" + old_flagname);
730   // Stick a newline and indentation in front of the type and default
731   // portions of DescribeOneFlag()s description
732   static const char kNewlineWithIndent[] = "\n    ";
733   output.replace(output.find(" type:"), 1, string(kNewlineWithIndent));
734   output.replace(output.find(" default:"), 1, string(kNewlineWithIndent));
735   output = StringPrintf("%s Details for '--%s':\n"
736                         "%s    defined: %s",
737                         line_indentation.c_str(),
738                         info.name.c_str(),
739                         output.c_str(),
740                         info.filename.c_str());
741
742   // Eliminate any doubled newlines that crept in.  Specifically, if
743   // DescribeOneFlag() decided to break the line just before "type"
744   // or "default", we don't want to introduce an extra blank line
745   static const string line_of_spaces(FLAGS_tab_completion_columns, ' ');
746   static const char kDoubledNewlines[] = "\n     \n";
747   for (string::size_type newlines = output.find(kDoubledNewlines);
748       newlines != string::npos;
749       newlines = output.find(kDoubledNewlines))
750     // Replace each 'doubled newline' with a single newline
751     output.replace(newlines, sizeof(kDoubledNewlines) - 1, string("\n"));
752
753   for (string::size_type newline = output.find('\n');
754       newline != string::npos;
755       newline = output.find('\n')) {
756     int newline_pos = static_cast<int>(newline) % FLAGS_tab_completion_columns;
757     int missing_spaces = FLAGS_tab_completion_columns - newline_pos;
758     output.replace(newline, 1, line_of_spaces, 1, missing_spaces);
759   }
760   return output;
761 }
762 }  // anonymous
763
764 void HandleCommandLineCompletions(void) {
765   if (FLAGS_tab_completion_word.empty()) return;
766   PrintFlagCompletionInfo();
767   gflags_exitfunc(0);
768 }
769
770
771 } // namespace GFLAGS_NAMESPACE