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()][]); } }
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()][]); } }
private static Text[] createDescriptionFirstLines(ColorScheme scheme, ArgSpec arg, String[] description, boolean[] showDefault) { Text[] result = scheme.ansi().new Text(str(description, 0)).splitLines(); if (result.length == 0 || (result.length == 1 && result[0].plain.length() == 0)) { if (showDefault[0]) { result = new Text[]{scheme.ansi().new Text(" Default: " + arg.defaultValueString())}; showDefault[0] = false; // don't show the default value twice } else { result = new Text[]{ Ansi.EMPTY_TEXT }; } } return result; }
@Test public void testTextSplitLinesStartEndIntermediate() { Ansi ansi = Ansi.ON; Ansi.Text[] all = { ansi.new Text("\n@|bold 012\n\n\n34|@").concat("5\n\n\nAA\n\n\n6").concat("@|underline 78\n90|@\n"), ansi.new Text("\r@|bold 012\r\r\r34|@").concat("5\r\r\rAA\r\r\r6").concat("@|underline 78\r90|@\r"), ansi.new Text("\r\n@|bold 012\r\n\r\n\r\n34|@").concat("5\r\n\r\n\r\nAA\r\n\r\n\r\n6").concat("@|underline 78\r\n90|@\r\n"), }; for (Ansi.Text text : all) { Ansi.Text[] lines = text.splitLines(); int i = 0; assertEquals(ansi.new Text(""), lines[i++]); assertEquals(ansi.new Text("@|bold 012|@"), lines[i++]); assertEquals(ansi.new Text(""), lines[i++]); assertEquals(ansi.new Text(""), lines[i++]); assertEquals(ansi.new Text("@|bold 34|@5"), lines[i++]); assertEquals(ansi.new Text(""), lines[i++]); assertEquals(ansi.new Text(""), lines[i++]); assertEquals(ansi.new Text("AA"), lines[i++]); assertEquals(ansi.new Text(""), lines[i++]); assertEquals(ansi.new Text(""), lines[i++]); assertEquals(ansi.new Text("6@|underline 78|@"), lines[i++]); assertEquals(ansi.new Text("@|underline 90|@"), lines[i++]); assertEquals(ansi.new Text(""), lines[i++]); } }
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 testTextSplitLinesStartEnd() { Ansi ansi = Ansi.ON; Ansi.Text[] all = { ansi.new Text("\n@|bold 012\n34|@").concat("5\nAA\n6").concat("@|underline 78\n90|@\n"), ansi.new Text("\r@|bold 012\r34|@").concat("5\rAA\r6").concat("@|underline 78\r90|@\r"), ansi.new Text("\r\n@|bold 012\r\n34|@").concat("5\r\nAA\r\n6").concat("@|underline 78\r\n90|@\r\n"), }; for (Ansi.Text text : all) { Ansi.Text[] lines = text.splitLines(); int i = 0; assertEquals(ansi.new Text(""), lines[i++]); assertEquals(ansi.new Text("@|bold 012|@"), lines[i++]); assertEquals(ansi.new Text("@|bold 34|@5"), lines[i++]); assertEquals(ansi.new Text("AA"), lines[i++]); assertEquals(ansi.new Text("6@|underline 78|@"), lines[i++]); assertEquals(ansi.new Text("@|underline 90|@"), lines[i++]); assertEquals(ansi.new Text(""), lines[i++]); } } @Test
/** Returns a 2-column list with command names and the first line of their header or (if absent) description. * @return a usage help section describing the added commands */ public String commandList() { if (subcommands().isEmpty()) { return ""; } int commandLength = maxLength(subcommands().keySet()); Help.TextTable textTable = Help.TextTable.forColumns(ansi(), new Help.Column(commandLength + 2, 2, Help.Column.Overflow.SPAN), new Help.Column(width() - (commandLength + 2), 2, Help.Column.Overflow.WRAP)); for (Map.Entry<String, Help> entry : subcommands().entrySet()) { Help help = entry.getValue(); UsageMessageSpec usage = help.commandSpec().usageMessage(); String header = !empty(usage.header()) ? usage.header()[0] : (!empty(usage.description()) ? usage.description()[0] : ""); Text[] lines = ansi().text(format(header)).splitLines(); for (int i = 0; i < lines.length; i++) { textTable.addRowValues(i == 0 ? help.commandNamesText(", ") : Ansi.EMPTY_TEXT, lines[i]); } } return textTable.toString(); } private static int maxLength(Collection<String> any) {
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()][]); } }
/** Formats each of the specified values and appends it to the specified StringBuilder. * @param ansi whether the result should contain ANSI escape codes or not * @param usageHelpWidth the width of the usage help message * @param values the values to format and append to the StringBuilder * @param sb the StringBuilder to collect the formatted strings * @param params the parameters to pass to the format method when formatting each value * @return the specified StringBuilder */ public static StringBuilder join(Ansi ansi, int usageHelpWidth, String[] values, StringBuilder sb, Object... params) { if (values != null) { TextTable table = TextTable.forColumnWidths(ansi, usageHelpWidth); table.indentWrappedLines = 0; for (String summaryLine : values) { Text[] lines = ansi.new Text(format(summaryLine, params)).splitLines(); for (Text line : lines) { table.addRowValues(line); } } table.toString(sb); } return sb; } private static String format(String formatString, Object... params) {
@Test public void testTextSplitLines() { Ansi ansi = Ansi.ON; Ansi.Text[] all = { ansi.new Text("@|bold 012\n34|@").concat("5\nAA\n6").concat("@|underline 78\n90|@"), ansi.new Text("@|bold 012\r34|@").concat("5\rAA\r6").concat("@|underline 78\r90|@"), ansi.new Text("@|bold 012\r\n34|@").concat("5\r\nAA\r\n6").concat("@|underline 78\r\n90|@"), }; for (Ansi.Text text : all) { Ansi.Text[] lines = text.splitLines(); int i = 0; assertEquals(ansi.new Text("@|bold 012|@"), lines[i++]); assertEquals(ansi.new Text("@|bold 34|@5"), lines[i++]); assertEquals(ansi.new Text("AA"), lines[i++]); assertEquals(ansi.new Text("6@|underline 78|@"), lines[i++]); assertEquals(ansi.new Text("@|underline 90|@"), lines[i++]); } } @Test
@Test public void testTextSplitLinesEmpty() { Ansi ansi = Ansi.ON; Ansi.Text text = ansi.new Text("abc\n\n\n"); Ansi.Text[] lines = text.splitLines(); assertEquals(4, lines.length); assertEquals(ansi.new Text("abc"), lines[0]); assertEquals(ansi.new Text(""), lines[1]); assertEquals(ansi.new Text(""), lines[2]); assertEquals(ansi.new Text(""), lines[3]); } @Test
private static Text[] createDescriptionFirstLines(ColorScheme scheme, ArgSpec arg, String[] description, boolean[] showDefault) { Text[] result = scheme.ansi().new Text(str(description, 0)).splitLines(); if (result.length == 0 || (result.length == 1 && result[0].plain.length() == 0)) { if (showDefault[0]) { result = new Text[]{scheme.ansi().new Text(" Default: " + arg.defaultValueString())}; showDefault[0] = false; // don't show the default value twice } else { result = new Text[]{ Ansi.EMPTY_TEXT }; } } return result; }
@Test public void testTextSplitLinesStartEndIntermediate() { Ansi ansi = Ansi.ON; Ansi.Text[] all = { ansi.new Text("\n@|bold 012\n\n\n34|@").concat("5\n\n\nAA\n\n\n6").concat("@|underline 78\n90|@\n"), ansi.new Text("\r@|bold 012\r\r\r34|@").concat("5\r\r\rAA\r\r\r6").concat("@|underline 78\r90|@\r"), ansi.new Text("\r\n@|bold 012\r\n\r\n\r\n34|@").concat("5\r\n\r\n\r\nAA\r\n\r\n\r\n6").concat("@|underline 78\r\n90|@\r\n"), }; for (Ansi.Text text : all) { Ansi.Text[] lines = text.splitLines(); int i = 0; assertEquals(ansi.new Text(""), lines[i++]); assertEquals(ansi.new Text("@|bold 012|@"), lines[i++]); assertEquals(ansi.new Text(""), lines[i++]); assertEquals(ansi.new Text(""), lines[i++]); assertEquals(ansi.new Text("@|bold 34|@5"), lines[i++]); assertEquals(ansi.new Text(""), lines[i++]); assertEquals(ansi.new Text(""), lines[i++]); assertEquals(ansi.new Text("AA"), lines[i++]); assertEquals(ansi.new Text(""), lines[i++]); assertEquals(ansi.new Text(""), lines[i++]); assertEquals(ansi.new Text("6@|underline 78|@"), lines[i++]); assertEquals(ansi.new Text("@|underline 90|@"), lines[i++]); assertEquals(ansi.new Text(""), lines[i++]); } }
/** Returns a 2-column list with command names and the first line of their header or (if absent) description. * @return a usage help section describing the added commands */ public String commandList() { if (subcommands().isEmpty()) { return ""; } int commandLength = maxLength(subcommands().keySet()); Help.TextTable textTable = Help.TextTable.forColumns(ansi(), new Help.Column(commandLength + 2, 2, Help.Column.Overflow.SPAN), new Help.Column(width() - (commandLength + 2), 2, Help.Column.Overflow.WRAP)); for (Map.Entry<String, Help> entry : subcommands().entrySet()) { Help help = entry.getValue(); UsageMessageSpec usage = help.commandSpec().usageMessage(); String header = !empty(usage.header()) ? usage.header()[0] : (!empty(usage.description()) ? usage.description()[0] : ""); Text[] lines = ansi().text(format(header)).splitLines(); for (int i = 0; i < lines.length; i++) { textTable.addRowValues(i == 0 ? help.commandNamesText(", ") : Ansi.EMPTY_TEXT, lines[i]); } } return textTable.toString(); } private static int maxLength(Collection<String> any) {
@Test public void testTextSplitLinesStartEnd() { Ansi ansi = Ansi.ON; Ansi.Text[] all = { ansi.new Text("\n@|bold 012\n34|@").concat("5\nAA\n6").concat("@|underline 78\n90|@\n"), ansi.new Text("\r@|bold 012\r34|@").concat("5\rAA\r6").concat("@|underline 78\r90|@\r"), ansi.new Text("\r\n@|bold 012\r\n34|@").concat("5\r\nAA\r\n6").concat("@|underline 78\r\n90|@\r\n"), }; for (Ansi.Text text : all) { Ansi.Text[] lines = text.splitLines(); int i = 0; assertEquals(ansi.new Text(""), lines[i++]); assertEquals(ansi.new Text("@|bold 012|@"), lines[i++]); assertEquals(ansi.new Text("@|bold 34|@5"), lines[i++]); assertEquals(ansi.new Text("AA"), lines[i++]); assertEquals(ansi.new Text("6@|underline 78|@"), lines[i++]); assertEquals(ansi.new Text("@|underline 90|@"), lines[i++]); assertEquals(ansi.new Text(""), lines[i++]); } } @Test
/** Formats each of the specified values and appends it to the specified StringBuilder. * @param ansi whether the result should contain ANSI escape codes or not * @param usageHelpWidth the width of the usage help message * @param values the values to format and append to the StringBuilder * @param sb the StringBuilder to collect the formatted strings * @param params the parameters to pass to the format method when formatting each value * @return the specified StringBuilder */ public static StringBuilder join(Ansi ansi, int usageHelpWidth, String[] values, StringBuilder sb, Object... params) { if (values != null) { TextTable table = TextTable.forColumnWidths(ansi, usageHelpWidth); table.indentWrappedLines = 0; for (String summaryLine : values) { Text[] lines = ansi.new Text(format(summaryLine, params)).splitLines(); for (Text line : lines) { table.addRowValues(line); } } table.toString(sb); } return sb; } private int width() { return commandSpec.usageMessage().width(); }
@Test public void testTextSplitLines() { Ansi ansi = Ansi.ON; Ansi.Text[] all = { ansi.new Text("@|bold 012\n34|@").concat("5\nAA\n6").concat("@|underline 78\n90|@"), ansi.new Text("@|bold 012\r34|@").concat("5\rAA\r6").concat("@|underline 78\r90|@"), ansi.new Text("@|bold 012\r\n34|@").concat("5\r\nAA\r\n6").concat("@|underline 78\r\n90|@"), }; for (Ansi.Text text : all) { Ansi.Text[] lines = text.splitLines(); int i = 0; assertEquals(ansi.new Text("@|bold 012|@"), lines[i++]); assertEquals(ansi.new Text("@|bold 34|@5"), lines[i++]); assertEquals(ansi.new Text("AA"), lines[i++]); assertEquals(ansi.new Text("6@|underline 78|@"), lines[i++]); assertEquals(ansi.new Text("@|underline 90|@"), lines[i++]); } } @Test
@Test public void testTextSplitLinesEmpty() { Ansi ansi = Ansi.ON; Ansi.Text text = ansi.new Text("abc\n\n\n"); Ansi.Text[] lines = text.splitLines(); assertEquals(4, lines.length); assertEquals(ansi.new Text("abc"), lines[0]); assertEquals(ansi.new Text(""), lines[1]); assertEquals(ansi.new Text(""), lines[2]); assertEquals(ansi.new Text(""), lines[3]); } @Test