1 // Copyright (c) 2008, Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
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
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.
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.
32 // Bash-style command line flag completion for C++ binaries
34 // This module implements bash-style completions. It achieves this
35 // goal in the following broad chunks:
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
50 #include "gflags_completions.h"
56 #include <string.h> // for strlen
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");
79 namespace GFLAGS_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;
89 // The entry point if flag completion is to be used.
90 static void PrintFlagCompletionInfo(void);
93 // 1) Examine search word
94 static void CanonicalizeCursorWordAndSearchOptions(
95 const string &cursor_word,
96 string *canonical_search_token,
97 CompletionOptions *options);
99 static bool RemoveTrailingChar(string *str, char c);
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);
110 static bool DoesSingleFlagMatch(
111 const CommandLineFlagInfo &flag,
112 const CompletionOptions &options,
113 const string &match_token);
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);
124 static void TryFindModuleAndPackageDir(
125 const vector<CommandLineFlagInfo> &all_flags,
127 string *package_dir);
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);
137 static void RetrieveUnusedFlags(
138 const set<const CommandLineFlagInfo *> &matching_flags,
139 const NotableFlags ¬able_flags,
140 set<const CommandLineFlagInfo *> *unused_flags);
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);
155 static string GetShortFlagLine(
156 const string &line_indentation,
157 const CommandLineFlagInfo &info);
159 static string GetLongFlagLine(
160 const string &line_indentation,
161 const CommandLineFlagInfo &info);
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;
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
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(
214 DVLOG(1) << "Identified canonical_token: '" << canonical_token << "'";
216 vector<CommandLineFlagInfo> all_flags;
217 set<const CommandLineFlagInfo *> matching_flags;
218 GetAllFlags(&all_flags);
219 DVLOG(2) << "Found " << all_flags.size() << " flags overall";
221 string longest_common_prefix;
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());
240 if (matching_flags.empty()) {
241 VLOG(1) << "There were no matching flags, returning nothing.";
247 TryFindModuleAndPackageDir(all_flags, &module, &package_dir);
248 DVLOG(1) << "Identified module: '" << module << "'";
249 DVLOG(1) << "Identified package_dir: '" << package_dir << "'";
251 NotableFlags notable_flags;
252 CategorizeAllMatchingFlags(
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();
265 vector<string> completions;
266 FinalizeCompletionOutput(
272 if (options.force_no_update)
273 completions.push_back("~");
275 DVLOG(1) << "Finalized with " << completions.size()
276 << " chosen completions";
278 for (vector<string>::const_iterator it = completions.begin();
279 it != completions.end();
281 DVLOG(9) << " Completion entry: '" << *it << "'";
282 fprintf(stdout, "%s\n", it->c_str());
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;
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);
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;
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;
314 if (found_question_marks < 3 &&
315 RemoveTrailingChar(canonical_search_token, '?')) {
316 ++found_question_marks;
319 if (found_plusses < 1 &&
320 RemoveTrailingChar(canonical_search_token, '+')) {
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;
333 options->return_all_matching_flags = (found_plusses > 0);
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);
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();
359 if (DoesSingleFlagMatch(*it, options, match_token)) {
360 all_matches->insert(&*it);
363 *longest_common_prefix = it->name;
365 if (longest_common_prefix->empty() || it->name.empty()) {
366 longest_common_prefix->clear();
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])
374 longest_common_prefix->erase(pos);
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;
391 // Is there a substring match if we want it?
392 if (options.flag_name_substring_search &&
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)
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)
410 // 3) Categorize matches (and helper method)
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();
426 for (set<const CommandLineFlagInfo *>::const_iterator it =
428 it != all_matches.end();
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(
439 pos + package_dir.size() + 1);
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";
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";
465 DVLOG(3) << "Result: not special match";
469 static void PushNameWithSuffix(vector<string>* suffixes, const char* suffix) {
471 StringPrintf("/%s%s", ProgramInvocationShortName(), suffix));
474 static void TryFindModuleAndPackageDir(
475 const vector<CommandLineFlagInfo> &all_flags,
477 string *package_dir) {
479 package_dir->clear();
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.");
495 for (vector<CommandLineFlagInfo>::const_iterator it = all_flags.begin();
496 it != all_flags.end();
498 for (vector<string>::const_iterator suffix = suffixes.begin();
499 suffix != suffixes.end();
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);
512 // Can't specialize template type on a locally defined type. Silly C++...
513 struct DisplayInfoGroup {
516 set<const CommandLineFlagInfo *> *group;
518 int SizeInLines() const {
519 int size_in_lines = static_cast<int>(group->size()) + 1;
520 if (strlen(header) > 0) {
523 if (strlen(footer) > 0) {
526 return size_in_lines;
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) {
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
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;
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 =
557 ¬able_flags->perfect_match_flag };
558 lines_so_far += group.SizeInLines();
559 output_groups.push_back(group);
561 if (lines_so_far < max_desired_lines &&
562 !notable_flags->module_flags.empty()) {
563 DisplayInfoGroup group = {
564 "-* Matching module flags *-",
565 "===========================",
566 ¬able_flags->module_flags };
567 lines_so_far += group.SizeInLines();
568 output_groups.push_back(group);
570 if (lines_so_far < max_desired_lines &&
571 !notable_flags->package_flags.empty()) {
572 DisplayInfoGroup group = {
573 "-* Matching package flags *-",
574 "============================",
575 ¬able_flags->package_flags };
576 lines_so_far += group.SizeInLines();
577 output_groups.push_back(group);
579 if (lines_so_far < max_desired_lines &&
580 !notable_flags->most_common_flags.empty()) {
581 DisplayInfoGroup group = {
582 "-* Commonly used flags *-",
583 "=========================",
584 ¬able_flags->most_common_flags };
585 lines_so_far += group.SizeInLines();
586 output_groups.push_back(group);
588 if (lines_so_far < max_desired_lines &&
589 !notable_flags->subpackage_flags.empty()) {
590 DisplayInfoGroup group = {
591 "-* Matching sub-package flags *-",
592 "================================",
593 ¬able_flags->subpackage_flags };
594 lines_so_far += group.SizeInLines();
595 output_groups.push_back(group);
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 = {
606 lines_so_far += group.SizeInLines();
607 output_groups.push_back(group);
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();
620 OutputSingleGroupWithLimit(
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;
632 if (completions_output != matching_flags.size()) {
633 options->force_no_update = false;
634 completions->push_back("~ (Remaining flags hidden) ~");
636 options->force_no_update = true;
640 static void RetrieveUnusedFlags(
641 const set<const CommandLineFlagInfo *> &matching_flags,
642 const NotableFlags ¬able_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();
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))
656 unused_flags->insert(*it);
660 // 5) Output matches (and helper methods)
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(), '-'));
678 for (set<const CommandLineFlagInfo *>::const_iterator it = group.begin();
679 it != group.end() && *remaining_line_limit > 0;
681 --*remaining_line_limit;
682 ++*completion_elements_output;
683 completions->push_back(
685 ? GetLongFlagLine(line_indentation, **it)
686 : GetShortFlagLine(line_indentation, **it)));
688 if (!footer.empty()) {
689 if (*remaining_line_limit < 1) return;
690 --*remaining_line_limit;
691 completions->push_back(line_indentation + footer);
695 static string GetShortFlagLine(
696 const string &line_indentation,
697 const CommandLineFlagInfo &info) {
699 bool is_string = (info.type == "string");
700 SStringPrintf(&prefix, "%s--%s [%s%s%s] ",
701 line_indentation.c_str(),
703 (is_string ? "'" : ""),
704 info.default_value.c_str(),
705 (is_string ? "'" : ""));
707 FLAGS_tab_completion_columns - static_cast<int>(prefix.size());
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;
717 static string GetLongFlagLine(
718 const string &line_indentation,
719 const CommandLineFlagInfo &info) {
721 string output = DescribeOneFlag(info);
723 // Replace '-' with '--', and remove trailing newline before appending
724 // the module definition location.
725 string old_flagname = "-" + info.name;
727 output.find(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"
737 line_indentation.c_str(),
740 info.filename.c_str());
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"));
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);
764 void HandleCommandLineCompletions(void) {
765 if (FLAGS_tab_completion_word.empty()) return;
766 PrintFlagCompletionInfo();
771 } // namespace GFLAGS_NAMESPACE