Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Side by Side Diff: tools/nixysa/third_party/gflags-1.0/src/gflags_completions.cc

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

Powered by Google App Engine
This is Rietveld 408576698