@Test public void testCreateDescriptionFirstLines() throws Exception { Method m = Help.class.getDeclaredMethod("createDescriptionFirstLines", Help.ColorScheme.class, Model.ArgSpec.class, String[].class, boolean[].class); m.setAccessible(true); String[][] input = new String[][] { new String[0], new String[] {""}, new String[] {"a", "b", "c"} }; Help.Ansi.Text[][] expectedOutput = new Help.Ansi.Text[][] { new Help.Ansi.Text[] {Help.Ansi.OFF.text("")}, new Help.Ansi.Text[] {Help.Ansi.OFF.text("")}, new Help.Ansi.Text[] {Help.Ansi.OFF.text("a"), Help.Ansi.OFF.text("b"), Help.Ansi.OFF.text("c")} }; for (int i = 0; i < input.length; i++) { String[] description = input[i]; Help.Ansi.Text[] result = (Help.Ansi.Text[]) m.invoke(null, new Help.ColorScheme(Help.Ansi.OFF), null, description, new boolean[3]); Help.Ansi.Text[] expected = expectedOutput[i]; for (int j = 0; j < result.length; j++) { assertEquals(expected[j], result[j]); } } }
optionText = optionText.concat(" ").concat(colorScheme.optionText(clusteredRequired.toString())); optionText = optionText.concat(" [").concat(colorScheme.optionText(clusteredOptional.toString())).concat("]"); Text name = colorScheme.optionText(option.shortestName()); Text param = parameterLabelRenderer().renderParameterLabel(option, colorScheme.ansi(), colorScheme.optionParamStyles); if (option.required()) { // e.g., -x=VAL optionText = optionText.concat(" ").concat(name).concat(param).concat("");
/** Generates a generic synopsis like {@code <command name> [OPTIONS] [PARAM1 [PARAM2]...]}, omitting parts * that don't apply to the command (e.g., does not show [OPTIONS] if the command has no options). * @return a generic synopsis */ public String abbreviatedSynopsis() { StringBuilder sb = new StringBuilder(); if (!commandSpec.optionsMap().isEmpty()) { // only show if annotated object actually has options sb.append(" [OPTIONS]"); } // sb.append(" [--] "); // implied for (PositionalParamSpec positionalParam : commandSpec.positionalParameters()) { if (!positionalParam.hidden()) { sb.append(' ').append(parameterLabelRenderer().renderParameterLabel(positionalParam, ansi(), colorScheme.parameterStyles)); } } // only show if object has subcommands if (!commandSpec.subcommands().isEmpty()) { sb.append(" [COMMAND]"); } return colorScheme.commandText(commandSpec.qualifiedName()).toString() + (sb.toString()) + System.getProperty("line.separator"); } /** Generates a detailed synopsis message showing all options and parameters. Follows the unix convention of
optionText = optionText.concat(" ").concat(colorScheme.optionText(clusteredRequired.toString())); optionText = optionText.concat(" [").concat(colorScheme.optionText(clusteredOptional.toString())).concat("]"); Text name = colorScheme.optionText(option.shortestName()); Text param = parameterLabelRenderer().renderParameterLabel(option, colorScheme.ansi(), colorScheme.optionParamStyles); if (option.required()) { // e.g., -x=VAL optionText = optionText.concat(" ").concat(name).concat(param).concat("");
@Test public void testCreateDescriptionFirstLines() throws Exception { Method m = Help.class.getDeclaredMethod("createDescriptionFirstLines", Help.ColorScheme.class, Model.ArgSpec.class, String[].class, boolean[].class); m.setAccessible(true); String[][] input = new String[][] { new String[0], new String[] {""}, new String[] {"a", "b", "c"} }; Help.Ansi.Text[][] expectedOutput = new Help.Ansi.Text[][] { new Help.Ansi.Text[] {Help.Ansi.OFF.text("")}, new Help.Ansi.Text[] {Help.Ansi.OFF.text("")}, new Help.Ansi.Text[] {Help.Ansi.OFF.text("a"), Help.Ansi.OFF.text("b"), Help.Ansi.OFF.text("c")} }; for (int i = 0; i < input.length; i++) { String[] description = input[i]; Help.Ansi.Text[] result = (Help.Ansi.Text[]) m.invoke(null, new Help.ColorScheme(Help.Ansi.OFF), null, description, new boolean[3]); Help.Ansi.Text[] expected = expectedOutput[i]; for (int j = 0; j < result.length; j++) { assertEquals(expected[j], result[j]); } } }
private Text[][] renderDescriptionLines(OptionSpec option, ColorScheme scheme, String requiredOption, String shortOption, Text longOptionText) { Text EMPTY = Ansi.EMPTY_TEXT; boolean[] showDefault = {option.internalShowDefaultValue(showDefaultValues)}; List<Text[]> result = new ArrayList<Text[]>(); String[] description = option.renderedDescription(); Text[] descriptionFirstLines = createDescriptionFirstLines(scheme, option, description, showDefault); result.add(new Text[] { scheme.optionText(requiredOption), scheme.optionText(shortOption), scheme.ansi().new Text(sep), longOptionText, descriptionFirstLines[0] }); for (int i = 1; i < descriptionFirstLines.length; i++) { result.add(new Text[] { EMPTY, EMPTY, EMPTY, EMPTY, descriptionFirstLines[i] }); } for (int i = 1; i < description.length; i++) { Text[] descriptionNextLines = scheme.ansi().new Text(description[i]).splitLines(); for (Text line : descriptionNextLines) { result.add(new Text[] { EMPTY, EMPTY, EMPTY, EMPTY, line }); } } if (showDefault[0]) { addTrailingDefaultLine(result, option, scheme); } return result.toArray(new Text[result.size()][]); } }
@Test public void testSystemPropertiesOverrideExplicitColorScheme() { @CommandLine.Command(separator = "=") class App { @CommandLine.Option(names = {"--verbose", "-v"}) boolean verbose; @CommandLine.Option(names = {"--count", "-c"}) int count; @CommandLine.Option(names = {"--help", "-h"}, hidden = true) boolean helpRequested; @CommandLine.Parameters(paramLabel = "FILE", arity = "1..*") File[] files; } Ansi ansi = Ansi.ON; CommandLine.Help.ColorScheme explicit = new CommandLine.Help.ColorScheme(ansi) .commands(Ansi.Style.faint, Ansi.Style.bg_magenta) .options(Ansi.Style.bg_red) .parameters(Ansi.Style.reverse) .optionParams(Ansi.Style.bg_green); // default color scheme assertEquals(ansi.new Text("@|faint,bg(magenta) <main class>|@ [@|bg(red) -v|@] [@|bg(red) -c|@=@|bg(green) <count>|@] @|reverse FILE|@..." + LINESEP), new CommandLine.Help(CommandLine.Model.CommandSpec.forAnnotatedObject(new App(), CommandLine.defaultFactory()), explicit).synopsis(0)); System.setProperty("picocli.color.commands", "blue"); System.setProperty("picocli.color.options", "blink"); System.setProperty("picocli.color.parameters", "red"); System.setProperty("picocli.color.optionParams", "magenta"); assertEquals(ansi.new Text("@|blue <main class>|@ [@|blink -v|@] [@|blink -c|@=@|magenta <count>|@] @|red FILE|@..." + LINESEP), new CommandLine.Help(CommandLine.Model.CommandSpec.forAnnotatedObject(new App(), CommandLine.defaultFactory()), explicit).synopsis(0)); } @Test
@Test public void testPrintHelpIfRequestedWithCustomColorScheme() { ColorScheme customColorScheme = new Help.ColorScheme(Help.Ansi.ON) .optionParams(Style.fg_magenta) .commands(Style.bg_cyan) .options(Style.fg_green) .parameters(Style.bg_white); @Command(mixinStandardHelpOptions = true) class App { @Option(names = { "-f" }, paramLabel = "ARCHIVE", description = "the archive file") File archive; @Parameters(paramLabel = "POSITIONAL", description = "positional arg") String arg; } List<CommandLine> list = new CommandLine(new App()).parse("--help"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); final PrintStream out = new PrintStream(baos); assertTrue(CommandLine.printHelpIfRequested(list, out, out, customColorScheme)); String expected = Help.Ansi.ON.string(String.format("" + "Usage: @|bg_cyan <main class>|@ [@|green -hV|@] [@|green -f|@=@|magenta ARCHIVE|@] @|bg_white POSITIONAL|@%n" + "@|bg_white |@ @|bg_white POSITIONAL|@ positional arg%n" + " @|green -f|@=@|magenta A|@@|magenta RCHIVE|@ the archive file%n" + " @|green -h|@, @|green --help|@ Show this help message and exit.%n" + " @|green -V|@, @|green --version|@ Print version information and exit.%n")); assertEquals(expected, baos.toString()); }
/** Generates a generic synopsis like {@code <command name> [OPTIONS] [PARAM1 [PARAM2]...]}, omitting parts * that don't apply to the command (e.g., does not show [OPTIONS] if the command has no options). * @return a generic synopsis */ public String abbreviatedSynopsis() { StringBuilder sb = new StringBuilder(); if (!commandSpec.optionsMap().isEmpty()) { // only show if annotated object actually has options sb.append(" [OPTIONS]"); } // sb.append(" [--] "); // implied for (PositionalParamSpec positionalParam : commandSpec.positionalParameters()) { if (!positionalParam.hidden()) { sb.append(' ').append(parameterLabelRenderer().renderParameterLabel(positionalParam, ansi(), colorScheme.parameterStyles)); } } // only show if object has subcommands if (!commandSpec.subcommands().isEmpty()) { sb.append(" [COMMAND]"); } return colorScheme.commandText(commandSpec.qualifiedName()).toString() + (sb.toString()) + System.getProperty("line.separator"); } /** Generates a detailed synopsis message showing all options and parameters. Follows the unix convention of
/** * Returns the detailed synopsis text by inserting the command name before the specified text with options and positional parameters details. * @param synopsisHeadingLength length of the synopsis heading string to be displayed on the same line as the first synopsis line. * For example, if the synopsis heading is {@code "Usage: "}, this value is 7. * @param optionsAndPositionalsAndCommandsDetails formatted string with options, positional parameters and subcommands. * Follows the unix convention of showing optional options and parameters in square brackets ({@code [ ]}). * @return the detailed synopsis text, in multiple lines if the length exceeds the usage width */ protected String insertSynopsisCommandName(int synopsisHeadingLength, Text optionsAndPositionalsAndCommandsDetails) { // Fix for #142: first line of synopsis overshoots max. characters String commandName = commandSpec.qualifiedName(); int firstColumnLength = commandName.length() + synopsisHeadingLength; // synopsis heading ("Usage: ") may be on the same line, so adjust column width TextTable textTable = TextTable.forColumnWidths(ansi(), firstColumnLength, width() - firstColumnLength); textTable.indentWrappedLines = 1; // don't worry about first line: options (2nd column) always start with a space // right-adjust the command name by length of synopsis heading Text PADDING = Ansi.OFF.new Text(stringOf('X', synopsisHeadingLength)); textTable.addRowValues(PADDING.concat(colorScheme.commandText(commandName)), optionsAndPositionalsAndCommandsDetails); return textTable.toString().substring(synopsisHeadingLength); // cut off leading synopsis heading spaces }
public Text[][] render(PositionalParamSpec param, IParamLabelRenderer paramLabelRenderer, ColorScheme scheme) { Text label = paramLabelRenderer.renderParameterLabel(param, scheme.ansi(), scheme.parameterStyles); Text requiredParameter = scheme.parameterText(param.arity().min > 0 ? requiredMarker : ""); Text EMPTY = Ansi.EMPTY_TEXT; boolean[] showDefault = {param.internalShowDefaultValue(showDefaultValues)}; List<Text[]> result = new ArrayList<Text[]>(); String[] description = param.renderedDescription(); Text[] descriptionFirstLines = createDescriptionFirstLines(scheme, param, description, showDefault); result.add(new Text[] { requiredParameter, EMPTY, EMPTY, label, descriptionFirstLines[0] }); for (int i = 1; i < descriptionFirstLines.length; i++) { result.add(new Text[] { EMPTY, EMPTY, EMPTY, EMPTY, descriptionFirstLines[i] }); } for (int i = 1; i < description.length; i++) { Text[] descriptionNextLines = scheme.ansi().new Text(description[i]).splitLines(); for (Text line : descriptionNextLines) { result.add(new Text[] { EMPTY, EMPTY, EMPTY, EMPTY, line }); } } if (showDefault[0]) { addTrailingDefaultLine(result, param, scheme); } return result.toArray(new Text[result.size()][]); } }
@Test public void testUsageWithCustomColorScheme() throws UnsupportedEncodingException { CommandLine.Help.ColorScheme scheme = new CommandLine.Help.ColorScheme(Ansi.ON) .options(Ansi.Style.bg_magenta).parameters(Ansi.Style.bg_cyan).optionParams(Ansi.Style.bg_yellow).commands(Ansi.Style.reverse); class Args { @CommandLine.Parameters(description = "param desc") String[] params; @CommandLine.Option(names = "-x", description = "option desc") String[] options; } ByteArrayOutputStream baos = new ByteArrayOutputStream(); CommandLine.usage(new Args(), new PrintStream(baos, true, "UTF8"), scheme); String actual = baos.toString("UTF8"); String expected = String.format("" + "Usage: @|reverse <main class>|@ [@|bg_magenta -x|@=@|bg_yellow <options>|@]... [@|bg_cyan <params>|@...]%n" + " [@|bg_cyan <params>|@...] param desc%n" + " @|bg_magenta -x|@=@|bg_yellow <|@@|bg_yellow options>|@ option desc%n"); assertEquals(Ansi.ON.new Text(expected).toString(), actual); }
/** Returns a Text object containing a partial detailed synopsis showing only the positional parameters, starting with a {@code " "} space. * Follows the unix convention of showing optional options and parameters in square brackets ({@code [ ]}). * @return the formatted positional parameters, starting with a {@code " "} space, or an empty Text if this command has no positional parameters * @since 3.9 */ protected Text createDetailedSynopsisPositionalsText() { Text positionalParamText = ansi().new Text(0); for (PositionalParamSpec positionalParam : commandSpec.positionalParameters()) { if (!positionalParam.hidden()) { positionalParamText = positionalParamText.concat(" "); Text label = parameterLabelRenderer().renderParameterLabel(positionalParam, colorScheme.ansi(), colorScheme.parameterStyles); positionalParamText = positionalParamText.concat(label); } } return positionalParamText; }
private Layout createLayout(int longOptionsColumnWidth) { return new Layout(colorScheme, TextTable.forDefaultColumns(colorScheme.ansi(), longOptionsColumnWidth, width()), createDefaultOptionRenderer(), createDefaultParameterRenderer()); }
private Text createLongOptionText(OptionSpec option, IParamLabelRenderer renderer, ColorScheme scheme, String longOption) { Text paramLabelText = renderer.renderParameterLabel(option, scheme.ansi(), scheme.optionParamStyles); // if no long option, fill in the space between the short option name and the param label value if (paramLabelText.length > 0 && longOption.length() == 0) { sep = renderer.separator(); // #181 paramLabelText may be =LABEL or [=LABEL...] int sepStart = paramLabelText.plainString().indexOf(sep); Text prefix = paramLabelText.substring(0, sepStart); paramLabelText = prefix.concat(paramLabelText.substring(sepStart + sep.length())); } Text longOptionText = scheme.optionText(longOption); longOptionText = longOptionText.concat(paramLabelText); return longOptionText; }
@Test public void testCommandAliasAnnotationSubcommandUsageHelp() { CommandLine commandLine = new CommandLine(new TopLevelCommand()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); commandLine.getSubcommands().get("sub").usage(new PrintStream(baos), CommandLine.Help.defaultColorScheme(Help.Ansi.ON).commands(Help.Ansi.Style.underline)); // add underline String expected = Help.Ansi.ON.new Text(String.format("" + "Usage: @|bold,underline top sub|@ [COMMAND]%n" + "I'm subcommand No. 1!%n" + "Commands:%n" + " @|bold,underline subsub|@, @|bold,underline ss|@, @|bold,underline sbsb|@ I'm like a 3rd rate command but great bang for your buck%n")).toString(); assertEquals(expected, baos.toString()); } }
@Test public void testCommandAliasAnnotationUsageHelp() { CommandLine commandLine = new CommandLine(new TopLevelCommand()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); commandLine.usage(new PrintStream(baos), CommandLine.Help.defaultColorScheme(Help.Ansi.ON).commands(Help.Ansi.Style.underline)); // add underline String expected = Help.Ansi.ON.new Text(String.format("" + "Usage: @|bold,underline top|@ [COMMAND]%n" + "top level command%n" + "Commands:%n" + " @|bold,underline sub|@, @|bold,underline s|@, @|bold,underline sb|@ I'm subcommand No. 1!%n" + " @|bold,underline sub2|@, @|bold,underline s2|@, @|bold,underline sb2|@ I'm subcommand 2 but pretty good still%n")).toString(); assertEquals(expected, baos.toString()); }
/** Returns a Text with all optionParam styles applied to the specified optionParam string. * @param optionParam the option parameter string to apply the registered option parameter styles to * @return a Text with all option parameter styles applied to the specified option parameter string */ public Ansi.Text optionParamText(String optionParam) { return ansi().apply(optionParam, optionParamStyles); }
private static void addTrailingDefaultLine(List<Text[]> result, ArgSpec arg, ColorScheme scheme) { Text EMPTY = Ansi.EMPTY_TEXT; result.add(new Text[]{EMPTY, EMPTY, EMPTY, EMPTY, scheme.ansi().new Text(" Default: " + arg.defaultValueString())}); }
@SuppressWarnings("deprecation") @Test public void testSynopsis() { Help help = new Help(CommandSpec.create(), new Help.ColorScheme(Help.Ansi.OFF)); String actual = help.synopsis(); assertEquals(String.format("<main class>%n"), actual); }