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