blob: 543b7a83f8b2db7d7a20a4f15f47843bab90dd09 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.common.options;
15
Damien Martin-Guillerez29728d42015-04-09 20:48:04 +000016import com.google.common.base.Joiner;
ccalvarinc69dbf82017-08-16 03:03:49 +020017import com.google.common.base.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010018import com.google.common.base.Splitter;
19import com.google.common.base.Strings;
Googler69faad02017-06-07 12:44:24 -040020import com.google.common.collect.ImmutableList;
Jingwen Chenbcad2212018-12-18 14:56:55 -080021import com.google.common.collect.Iterables;
Ulf Adams352211d2016-06-22 09:24:28 +000022import com.google.common.escape.Escaper;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010023import java.text.BreakIterator;
brandjond1b34d42017-04-18 15:52:57 +020024import java.util.ArrayList;
ccalvarin77e3a5c2017-09-26 18:11:53 -040025import java.util.Arrays;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010026import java.util.List;
ccalvarin77e3a5c2017-09-26 18:11:53 -040027import java.util.Locale;
28import java.util.stream.Collectors;
29import java.util.stream.Stream;
Jon Brandvein22d261c2017-03-21 23:15:28 +000030import javax.annotation.Nullable;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031
ccalvarinc69dbf82017-08-16 03:03:49 +020032/** A renderer for usage messages for any combination of options classes. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010033class OptionsUsage {
34
35 private static final Splitter NEWLINE_SPLITTER = Splitter.on('\n');
Damien Martin-Guillerez29728d42015-04-09 20:48:04 +000036 private static final Joiner COMMA_JOINER = Joiner.on(",");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010037
38 /**
Jon Brandvein22d261c2017-03-21 23:15:28 +000039 * Given an options class, render the usage string into the usage, which is passed in as an
40 * argument. This will not include information about expansions for options using expansion
41 * functions (it would be unsafe to report this as we cannot know what options from other {@link
42 * OptionsBase} subclasses they depend on until a complete parser is constructed).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010043 */
44 static void getUsage(Class<? extends OptionsBase> optionsClass, StringBuilder usage) {
brandjond1b34d42017-04-18 15:52:57 +020045 OptionsData data = OptionsParser.getOptionsDataInternal(optionsClass);
ccalvarine8aae032017-08-22 07:17:44 +020046 List<OptionDefinition> optionDefinitions =
ccalvarin987f09f2017-08-31 19:50:39 +020047 new ArrayList<>(OptionsData.getAllOptionDefinitionsForClass(optionsClass));
ccalvarine8aae032017-08-22 07:17:44 +020048 optionDefinitions.sort(OptionDefinition.BY_OPTION_NAME);
49 for (OptionDefinition optionDefinition : optionDefinitions) {
ccalvarin77e3a5c2017-09-26 18:11:53 -040050 getUsage(optionDefinition, usage, OptionsParser.HelpVerbosity.LONG, data, false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010051 }
52 }
53
54 /**
55 * Paragraph-fill the specified input text, indenting lines to 'indent' and
56 * wrapping lines at 'width'. Returns the formatted result.
57 */
58 static String paragraphFill(String in, int indent, int width) {
59 String indentString = Strings.repeat(" ", indent);
60 StringBuilder out = new StringBuilder();
61 String sep = "";
62 for (String paragraph : NEWLINE_SPLITTER.split(in)) {
ccalvarinc65147b2017-08-16 21:31:52 +020063 // TODO(ccalvarin) break iterators expect hyphenated words to be line-breakable, which looks
64 // funny for --flag
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010065 BreakIterator boundary = BreakIterator.getLineInstance(); // (factory)
66 boundary.setText(paragraph);
67 out.append(sep).append(indentString);
68 int cursor = indent;
69 for (int start = boundary.first(), end = boundary.next();
70 end != BreakIterator.DONE;
71 start = end, end = boundary.next()) {
72 String word =
73 paragraph.substring(start, end); // (may include trailing space)
74 if (word.length() + cursor > width) {
75 out.append('\n').append(indentString);
76 cursor = indent;
77 }
78 out.append(word);
79 cursor += word.length();
80 }
81 sep = "\n";
82 }
83 return out.toString();
84 }
85
86 /**
ccalvarinc69dbf82017-08-16 03:03:49 +020087 * Returns the expansion for an option, if any, regardless of if the expansion is from a function
88 * or is statically declared in the annotation.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010089 */
Googler69faad02017-06-07 12:44:24 -040090 private static @Nullable ImmutableList<String> getExpansionIfKnown(
ccalvarine8aae032017-08-22 07:17:44 +020091 OptionDefinition optionDefinition, OptionsData optionsData) {
92 Preconditions.checkNotNull(optionDefinition);
ccalvarin34a9fea2017-10-17 23:27:19 +020093 return optionsData.getEvaluatedExpansion(optionDefinition);
Jon Brandvein22d261c2017-03-21 23:15:28 +000094 }
95
ccalvarin77e3a5c2017-09-26 18:11:53 -040096 // Placeholder tag "UNKNOWN" is ignored.
97 private static boolean shouldEffectTagBeListed(OptionEffectTag effectTag) {
98 return !effectTag.equals(OptionEffectTag.UNKNOWN);
99 }
100
101 // Tags that only apply to undocumented options are excluded.
102 private static boolean shouldMetadataTagBeListed(OptionMetadataTag metadataTag) {
103 return !metadataTag.equals(OptionMetadataTag.HIDDEN)
104 && !metadataTag.equals(OptionMetadataTag.INTERNAL);
105 }
106
ccalvarinc69dbf82017-08-16 03:03:49 +0200107 /** Appends the usage message for a single option-field message to 'usage'. */
Jon Brandvein22d261c2017-03-21 23:15:28 +0000108 static void getUsage(
ccalvarine8aae032017-08-22 07:17:44 +0200109 OptionDefinition optionDefinition,
Jon Brandvein22d261c2017-03-21 23:15:28 +0000110 StringBuilder usage,
111 OptionsParser.HelpVerbosity helpVerbosity,
ccalvarin77e3a5c2017-09-26 18:11:53 -0400112 OptionsData optionsData,
113 boolean includeTags) {
ccalvarin00443492017-08-30 00:23:40 +0200114 String flagName = getFlagName(optionDefinition);
115 String typeDescription = getTypeDescription(optionDefinition);
Jonathan Bluett-Duncan0df3ddbd2017-08-09 11:13:54 +0200116 usage.append(" --").append(flagName);
ccalvarin77e3a5c2017-09-26 18:11:53 -0400117 if (helpVerbosity == OptionsParser.HelpVerbosity.SHORT) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100118 usage.append('\n');
119 return;
120 }
ccalvarin77e3a5c2017-09-26 18:11:53 -0400121
122 // Add the option's type and default information. Stop there for "medium" verbosity.
ccalvarine8aae032017-08-22 07:17:44 +0200123 if (optionDefinition.getAbbreviation() != '\0') {
124 usage.append(" [-").append(optionDefinition.getAbbreviation()).append(']');
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100125 }
126 if (!typeDescription.equals("")) {
Jonathan Bluett-Duncan0df3ddbd2017-08-09 11:13:54 +0200127 usage.append(" (").append(typeDescription).append("; ");
ccalvarine8aae032017-08-22 07:17:44 +0200128 if (optionDefinition.allowsMultiple()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100129 usage.append("may be used multiple times");
130 } else {
131 // Don't call the annotation directly (we must allow overrides to certain defaults)
ccalvarine8aae032017-08-22 07:17:44 +0200132 String defaultValueString = optionDefinition.getUnparsedDefaultValue();
133 if (optionDefinition.isSpecialNullDefault()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100134 usage.append("default: see description");
135 } else {
Jonathan Bluett-Duncan0df3ddbd2017-08-09 11:13:54 +0200136 usage.append("default: \"").append(defaultValueString).append("\"");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100137 }
138 }
139 usage.append(")");
140 }
141 usage.append("\n");
ccalvarin77e3a5c2017-09-26 18:11:53 -0400142 if (helpVerbosity == OptionsParser.HelpVerbosity.MEDIUM) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100143 return;
144 }
ccalvarin77e3a5c2017-09-26 18:11:53 -0400145
146 // For verbosity "long," add the full description and expansion, along with the tag
147 // information if requested.
ccalvarine8aae032017-08-22 07:17:44 +0200148 if (!optionDefinition.getHelpText().isEmpty()) {
149 usage.append(paragraphFill(optionDefinition.getHelpText(), /*indent=*/ 4, /*width=*/ 80));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100150 usage.append('\n');
151 }
ccalvarine8aae032017-08-22 07:17:44 +0200152 ImmutableList<String> expansion = getExpansionIfKnown(optionDefinition, optionsData);
Jon Brandvein22d261c2017-03-21 23:15:28 +0000153 if (expansion == null) {
ccalvarinc65147b2017-08-16 21:31:52 +0200154 usage.append(paragraphFill("Expands to unknown options.", /*indent=*/ 6, /*width=*/ 80));
155 usage.append('\n');
Googler69faad02017-06-07 12:44:24 -0400156 } else if (!expansion.isEmpty()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100157 StringBuilder expandsMsg = new StringBuilder("Expands to: ");
Jon Brandvein22d261c2017-03-21 23:15:28 +0000158 for (String exp : expansion) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100159 expandsMsg.append(exp).append(" ");
160 }
ccalvarinc65147b2017-08-16 21:31:52 +0200161 usage.append(paragraphFill(expandsMsg.toString(), /*indent=*/ 6, /*width=*/ 80));
162 usage.append('\n');
163 }
ccalvarin4acb36c2017-09-21 00:35:35 +0200164 if (optionDefinition.hasImplicitRequirements()) {
ccalvarinc65147b2017-08-16 21:31:52 +0200165 StringBuilder requiredMsg = new StringBuilder("Using this option will also add: ");
ccalvarine8aae032017-08-22 07:17:44 +0200166 for (String req : optionDefinition.getImplicitRequirements()) {
ccalvarinc65147b2017-08-16 21:31:52 +0200167 requiredMsg.append(req).append(" ");
168 }
ccalvarin77e3a5c2017-09-26 18:11:53 -0400169 usage.append(paragraphFill(requiredMsg.toString(), 6, 80)); // (indent, width)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100170 usage.append('\n');
171 }
ccalvarin77e3a5c2017-09-26 18:11:53 -0400172 if (!includeTags) {
173 return;
174 }
175
176 // If we are expected to include the tags, add them for high verbosity.
177 Stream<OptionEffectTag> effectTagStream =
178 Arrays.stream(optionDefinition.getOptionEffectTags())
179 .filter(OptionsUsage::shouldEffectTagBeListed);
180 Stream<OptionMetadataTag> metadataTagStream =
181 Arrays.stream(optionDefinition.getOptionMetadataTags())
182 .filter(OptionsUsage::shouldMetadataTagBeListed);
183 String tagList =
184 Stream.concat(effectTagStream, metadataTagStream)
185 .map(tag -> tag.toString().toLowerCase())
186 .collect(Collectors.joining(", "));
187 if (!tagList.isEmpty()) {
188 usage.append(paragraphFill("Tags: " + tagList, 6, 80)); // (indent, width)
189 usage.append("\n");
190 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100191 }
192
ccalvarinc69dbf82017-08-16 03:03:49 +0200193 /** Append the usage message for a single option-field message to 'usage'. */
Jon Brandvein22d261c2017-03-21 23:15:28 +0000194 static void getUsageHtml(
ccalvarine8aae032017-08-22 07:17:44 +0200195 OptionDefinition optionDefinition,
196 StringBuilder usage,
197 Escaper escaper,
ccalvarin77e3a5c2017-09-26 18:11:53 -0400198 OptionsData optionsData,
199 boolean includeTags) {
ccalvarine8aae032017-08-22 07:17:44 +0200200 String plainFlagName = optionDefinition.getOptionName();
ccalvarin00443492017-08-30 00:23:40 +0200201 String flagName = getFlagName(optionDefinition);
ccalvarine8aae032017-08-22 07:17:44 +0200202 String valueDescription = optionDefinition.getValueTypeHelpText();
ccalvarin00443492017-08-30 00:23:40 +0200203 String typeDescription = getTypeDescription(optionDefinition);
Jingwen Chenbcad2212018-12-18 14:56:55 -0800204
205 // String.format is a lot slower, sometimes up to 10x.
206 // https://stackoverflow.com/questions/925423/is-it-better-practice-to-use-string-format-over-string-concatenation-in-java
207 //
208 // Considering that this runs for every flag in the CLI reference, it's better to use regular
209 // appends here.
210 usage
211 // Add the id of the flag to point anchor hrefs to it
212 .append("<dt id=\"flag--")
213 .append(plainFlagName)
214 .append("\">")
215 // Add the href to the id hash
216 .append("<code><a href=\"#flag--")
217 .append(plainFlagName)
218 .append("\">")
219 .append("--")
220 .append(flagName)
221 .append("</a>");
222
ccalvarin5fe8e662017-09-14 15:56:43 +0200223 if (optionDefinition.usesBooleanValueSyntax() || optionDefinition.isVoidField()) {
Ulf Adams6f096662016-06-27 15:51:23 +0000224 // Nothing for boolean, tristate, boolean_or_enum, or void options.
225 } else if (!valueDescription.isEmpty()) {
226 usage.append("=").append(escaper.escape(valueDescription));
227 } else if (!typeDescription.isEmpty()) {
228 // Generic fallback, which isn't very good.
229 usage.append("=&lt;").append(escaper.escape(typeDescription)).append("&gt");
230 }
231 usage.append("</code>");
ccalvarine8aae032017-08-22 07:17:44 +0200232 if (optionDefinition.getAbbreviation() != '\0') {
233 usage.append(" [<code>-").append(optionDefinition.getAbbreviation()).append("</code>]");
Ulf Adams352211d2016-06-22 09:24:28 +0000234 }
ccalvarine8aae032017-08-22 07:17:44 +0200235 if (optionDefinition.allowsMultiple()) {
Ulf Adams6f096662016-06-27 15:51:23 +0000236 // Allow-multiple options can't have a default value.
237 usage.append(" multiple uses are accumulated");
238 } else {
239 // Don't call the annotation directly (we must allow overrides to certain defaults).
ccalvarine8aae032017-08-22 07:17:44 +0200240 String defaultValueString = optionDefinition.getUnparsedDefaultValue();
241 if (optionDefinition.isVoidField()) {
Ulf Adams6f096662016-06-27 15:51:23 +0000242 // Void options don't have a default.
ccalvarine8aae032017-08-22 07:17:44 +0200243 } else if (optionDefinition.isSpecialNullDefault()) {
Ulf Adams6f096662016-06-27 15:51:23 +0000244 usage.append(" default: see description");
Ulf Adams352211d2016-06-22 09:24:28 +0000245 } else {
Ulf Adams6f096662016-06-27 15:51:23 +0000246 usage.append(" default: \"").append(escaper.escape(defaultValueString)).append("\"");
Ulf Adams352211d2016-06-22 09:24:28 +0000247 }
Ulf Adams352211d2016-06-22 09:24:28 +0000248 }
249 usage.append("</dt>\n");
250 usage.append("<dd>\n");
ccalvarine8aae032017-08-22 07:17:44 +0200251 if (!optionDefinition.getHelpText().isEmpty()) {
252 usage.append(
253 paragraphFill(
254 escaper.escape(optionDefinition.getHelpText()), /*indent=*/ 0, /*width=*/ 80));
Ulf Adams352211d2016-06-22 09:24:28 +0000255 usage.append('\n');
256 }
ccalvarin62f72bc2017-08-17 17:59:05 +0200257
ccalvarin34a9fea2017-10-17 23:27:19 +0200258 if (!optionsData.getEvaluatedExpansion(optionDefinition).isEmpty()) {
ccalvarin62f72bc2017-08-17 17:59:05 +0200259 // If this is an expansion option, list the expansion if known, or at least specify that we
260 // don't know.
Ulf Adams352211d2016-06-22 09:24:28 +0000261 usage.append("<br/>\n");
ccalvarine8aae032017-08-22 07:17:44 +0200262 ImmutableList<String> expansion = getExpansionIfKnown(optionDefinition, optionsData);
ccalvarin62f72bc2017-08-17 17:59:05 +0200263 StringBuilder expandsMsg;
264 if (expansion == null) {
265 expandsMsg = new StringBuilder("Expands to unknown options.<br/>\n");
266 } else {
267 Preconditions.checkArgument(!expansion.isEmpty());
268 expandsMsg = new StringBuilder("Expands to:<br/>\n");
269 for (String exp : expansion) {
Jingwen Chenbcad2212018-12-18 14:56:55 -0800270 // TODO(jingwen): We link to the expanded flags here, but unfortunately we don't
ccalvarin77e3a5c2017-09-26 18:11:53 -0400271 // currently guarantee that all flags are only printed once. A flag in an OptionBase that
272 // is included by 2 different commands, but not inherited through a parent command, will
Jingwen Chenbcad2212018-12-18 14:56:55 -0800273 // be printed multiple times. Clicking on the flag will bring the user to its first
274 // definition.
ccalvarin62f72bc2017-08-17 17:59:05 +0200275 expandsMsg
Jingwen Chenbcad2212018-12-18 14:56:55 -0800276 .append("&nbsp;&nbsp;")
277 .append("<code><a href=\"#flag")
278 // Link to the '#flag--flag_name' hash.
279 // Some expansions are in the form of '--flag_name=value', so we drop everything from
280 // '=' onwards.
281 .append(Iterables.get(Splitter.on('=').split(escaper.escape(exp)), 0))
282 .append("\">")
ccalvarin62f72bc2017-08-17 17:59:05 +0200283 .append(escaper.escape(exp))
Jingwen Chenbcad2212018-12-18 14:56:55 -0800284 .append("</a></code><br/>\n");
ccalvarin62f72bc2017-08-17 17:59:05 +0200285 }
Ulf Adams352211d2016-06-22 09:24:28 +0000286 }
ccalvarinc65147b2017-08-16 21:31:52 +0200287 usage.append(expandsMsg.toString());
Ulf Adams352211d2016-06-22 09:24:28 +0000288 }
ccalvarin62f72bc2017-08-17 17:59:05 +0200289
ccalvarin77e3a5c2017-09-26 18:11:53 -0400290 // Add effect tags, if not UNKNOWN, and metadata tags, if not empty.
291 if (includeTags) {
292 Stream<OptionEffectTag> effectTagStream =
293 Arrays.stream(optionDefinition.getOptionEffectTags())
294 .filter(OptionsUsage::shouldEffectTagBeListed);
295 Stream<OptionMetadataTag> metadataTagStream =
296 Arrays.stream(optionDefinition.getOptionMetadataTags())
297 .filter(OptionsUsage::shouldMetadataTagBeListed);
298 String tagList =
299 Stream.concat(
300 effectTagStream.map(
301 tag ->
302 String.format(
303 "<a href=\"#effect_tag_%s\"><code>%s</code></a>",
304 tag, tag.name().toLowerCase())),
305 metadataTagStream.map(
306 tag ->
307 String.format(
308 "<a href=\"#metadata_tag_%s\"><code>%s</code></a>",
309 tag, tag.name().toLowerCase())))
310 .collect(Collectors.joining(", "));
311 if (!tagList.isEmpty()) {
312 usage.append("<br>Tags: \n").append(tagList);
313 }
314 }
315
Ulf Adams352211d2016-06-22 09:24:28 +0000316 usage.append("</dd>\n");
317 }
318
319 /**
Damien Martin-Guillerez29728d42015-04-09 20:48:04 +0000320 * Returns the available completion for the given option field. The completions are the exact
321 * command line option (with the prepending '--') that one should pass. It is suitable for
322 * completion script to use. If the option expect an argument, the kind of argument is given
323 * after the equals. If the kind is a enum, the various enum values are given inside an accolade
324 * in a comma separated list. For other special kind, the type is given as a name (e.g.,
325 * <code>label</code>, <code>float</ode>, <code>path</code>...). Example outputs of this
326 * function are for, respectively, a tristate flag <code>tristate_flag</code>, a enum
327 * flag <code>enum_flag</code> which can take <code>value1</code>, <code>value2</code> and
328 * <code>value3</code>, a path fragment flag <code>path_flag</code>, a string flag
329 * <code>string_flag</code> and a void flag <code>void_flag</code>:
330 * <pre>
331 * --tristate_flag={auto,yes,no}
332 * --notristate_flag
333 * --enum_flag={value1,value2,value3}
334 * --path_flag=path
335 * --string_flag=
336 * --void_flag
337 * </pre>
338 *
ccalvarine8aae032017-08-22 07:17:44 +0200339 * @param optionDefinition The field to return completion for
Damien Martin-Guillerez29728d42015-04-09 20:48:04 +0000340 * @param builder the string builder to store the completion values
341 */
ccalvarine8aae032017-08-22 07:17:44 +0200342 static void getCompletion(OptionDefinition optionDefinition, StringBuilder builder) {
Damien Martin-Guillerez29728d42015-04-09 20:48:04 +0000343 // Return the list of possible completions for this option
ccalvarine8aae032017-08-22 07:17:44 +0200344 String flagName = optionDefinition.getOptionName();
345 Class<?> fieldType = optionDefinition.getType();
Damien Martin-Guillerez29728d42015-04-09 20:48:04 +0000346 builder.append("--").append(flagName);
347 if (fieldType.equals(boolean.class)) {
348 builder.append("\n");
349 builder.append("--no").append(flagName).append("\n");
350 } else if (fieldType.equals(TriState.class)) {
351 builder.append("={auto,yes,no}\n");
352 builder.append("--no").append(flagName).append("\n");
353 } else if (fieldType.isEnum()) {
ccalvarin77e3a5c2017-09-26 18:11:53 -0400354 builder
355 .append("={")
356 .append(COMMA_JOINER.join(fieldType.getEnumConstants()).toLowerCase(Locale.ENGLISH))
357 .append("}\n");
Damien Martin-Guillerez29728d42015-04-09 20:48:04 +0000358 } else if (fieldType.getSimpleName().equals("Label")) {
359 // String comparison so we don't introduce a dependency to com.google.devtools.build.lib.
360 builder.append("=label\n");
361 } else if (fieldType.getSimpleName().equals("PathFragment")) {
362 builder.append("=path\n");
363 } else if (Void.class.isAssignableFrom(fieldType)) {
364 builder.append("\n");
365 } else {
366 // TODO(bazel-team): add more types. Maybe even move the completion type
367 // to the @Option annotation?
368 builder.append("=\n");
369 }
370 }
371
ccalvarin00443492017-08-30 00:23:40 +0200372 private static String getTypeDescription(OptionDefinition optionsDefinition) {
373 return optionsDefinition.getConverter().getTypeDescription();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100374 }
375
ccalvarin00443492017-08-30 00:23:40 +0200376 static String getFlagName(OptionDefinition optionDefinition) {
ccalvarine8aae032017-08-22 07:17:44 +0200377 String name = optionDefinition.getOptionName();
ccalvarin5fe8e662017-09-14 15:56:43 +0200378 return optionDefinition.usesBooleanValueSyntax() ? "[no]" + name : name;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100379 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100380}