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