From: Ben Soares Date: Tue, 30 May 2023 09:53:40 +0000 (+0100) Subject: JAL-629 Better formatting of Arg.Opt options in usage statement X-Git-Tag: Release_2_11_4_0~287 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=e134764b7eec841cb56a417250f2dd898680f985;p=jalview.git JAL-629 Better formatting of Arg.Opt options in usage statement --- diff --git a/src/jalview/bin/argparser/Arg.java b/src/jalview/bin/argparser/Arg.java index c502eec..6c97ebc 100644 --- a/src/jalview/bin/argparser/Arg.java +++ b/src/jalview/bin/argparser/Arg.java @@ -287,57 +287,138 @@ public enum Arg public static enum Opt { - BOOLEAN, // This Arg can be specified as --arg or --noarg to give true or - // false. A default can be given with setOptions(bool, Opt....). - // Use ArgParser.isSet(Arg) to see if this arg was not specified. - STRING, // This Arg can accept a value either through --arg=value or --arg - // value. - UNARY, // This Arg is a boolean value, true if present, false if not. Like - // BOOLEAN but without the --noarg option. - MULTI, // This Arg can be specified multiple times. Multiple values are - // stored in the ArgValuesMap (along with their positional index) for - // each linkedId. - LINKED, // This Arg can be linked to others through a --arg[linkedId] or - // --arg[linkedId]=value. If no linkedId is specified then the - // current default linkedId will be used. - NODUPLICATEVALUES, // This Arg can only have one value (per linkedId). The - // first value will be used and subsequent values ignored - // with a warning. - BOOTSTRAP, // This Arg value(s) can be determined at an earlier stage than - // non-BOOTSTRAP Args. Substitutions do not happen in BOOTSTRAP - // Args and they cannot be linked or contain SubVals. See - // jalview.bin.argparser.BootstrapArgs. - GLOB, // This Arg can expand wildcard filename "globs" (e.g. - // path/*/filename*). If the Arg value is given as --arg filename* - // then the shell will have expanded the glob already, but if - // specified as --arg=filename* then the Java glob expansion method - // will be used (see FileUtils.getFilenamesFromGlob()). Note that this - // might be different from the shell expansion rules. - NOACTION, // This Arg does not perform a data task, usually used to control - // flow in ArgParser.parse(args). - ALLOWSUBSTITUTIONS, // This Arg allows substitutions in its linkedId, - // SubVals and values. - PRIVATE, // This Arg is used internally, and cannot be specified by the - // user. - SECRET, // This Arg is used by development processes and although it can be - // set by the user, it is not displayed to the user. - ALLOWALL, // This Arg can use the '*' linkedId to apply to all known - // linkedIds - INCREMENTDEFAULTCOUNTER, // If an Arg has this option and the default - // linkedId is used, the defaultLinkedIdCounter is - // incremented *first*. - INPUT, // This Arg counts as an input for REQUIREINPUT - REQUIREINPUT, // This Arg can only be applied via --all if there is an - // input (i.e. --open or --append) - OUTPUTFILE, // This Arg provides an output filename. With Opt.ALLOWALL *.ext - // is - // shorthand for --all --output={basename}.ext - STORED, // This Arg resets and creates a new set of "opened" linkedIds - HELP, // This Arg is a --help type arg - PRIMARY, // This Arg is the main Arg for its type - HASTYPE, // This Arg can have an Arg.Type assigned to it (and no value) - FIRST, // Move this arg to the first in usage statement (within type) - LAST, // Move this arg to the end in usage statement (within type) + /* + * A BOOLEAN Arg can be specified as --arg or --noarg to give true or false. + * A default can be given with setOptions(bool, Opt....). + * Use ArgParser.isSet(Arg) to see if this arg was not specified. + */ + BOOLEAN("can be negated with " + ArgParser.DOUBLEDASH + + ArgParser.NEGATESTRING + "..."), + + /* + * A STRING Arg will take a value either through --arg=value or --arg value. + */ + STRING("expects a value"), + /* + * A UNARY Arg is a boolean value, true if present, false if not. + * Like BOOLEAN but without the --noarg option. + */ + UNARY(null), + /* + * A MULTI Arg can be specified multiple times. + * Multiple values are stored in the ArgValuesMap (along with their positional index) for each linkedId. + */ + MULTI("can be specified multiple times"), + /* + * A Linked Arg can be linked to others through a --arg[linkedId] or --arg[linkedId]=value. + * If no linkedId is specified then the current default linkedId will be used. + */ + LINKED("is linked to an alignment"), + /* + * A NODUPLICATES Arg can only have one value (per linkedId). + * The first value will be used and subsequent values ignored with a warning. + */ + NODUPLICATEVALUES("cannot have the same value more than once"), + /* + * A BOOTSTRAP Arg value(s) can be determined at an earlier stage than non-BOOTSTRAP Args. + * Substitutions do not happen in BOOTSTRAP Args and they cannot be linked or contain SubVals. + * See jalview.bin.argparser.BootstrapArgs. + */ + BOOTSTRAP("a configuration argument"), + /* + * A GLOB Arg can expand wildcard filename "globs" (e.g. path/* /filename*). + * If the Arg value is given as --arg filename* then the shell will have expanded the glob already, + * but if specified as --arg=filename* then the Java glob expansion method will be used + * (see FileUtils.getFilenamesFromGlob()). + * Note that this might be different from the shell expansion rules. + */ + GLOB("can take multiple filenames with wildcards"), + /* + * A NOACTION Arg does not perform a data task, + * usually used to control flow in ArgParser.parse(args). + */ + NOACTION(null), + /* + * An ALLOWSUBSTITUTIONS Arg allows substitutions in its linkedId, + * SubVals and values. + */ + ALLOWSUBSTITUTIONS("values can use substitutions"), + /* + * A PRIVATE Arg is used internally, and cannot be specified by the user. + */ + PRIVATE(null), + /* + * A SECRET Arg is used by development processes and although it can be set by the user, + * it is not displayed to the user. + */ + SECRET(null), + /* + * An ALLOWALL Arg can use the '*' linkedId to apply to all known linkedIds + */ + ALLOWALL("can be used with " + ArgParser.DOUBLEDASH + "all"), + /* + * If an Arg has the INCREMENTDEFAULTCOUNTER option and the default linkedId is used, + * the defaultLinkedIdCounter is incremented *first*. + */ + INCREMENTDEFAULTCOUNTER("starts a new default alignment"), + /* + * An INPUT Arg counts as an input for REQUIREINPUT + */ + INPUT(null), + /* + * A REQUIREINPUT Arg can only be applied via --all if there is an input + * (i.e. --open or --append) + */ + REQUIREINPUT(null), + /* + * An OUTPUTFILE Arg provides an output filename. With Opt.ALLOWALL *.ext is shorthand for + * --all --output={basename}.ext + */ + OUTPUTFILE("value is an output file"), + /* + * A STORED Arg resets and creates a new set of "opened" linkedIds + */ + STORED(null), + /* + * A HELP Arg is a --help type arg + */ + HELP("provides a help statement"), + /* + * A PRIMARY Arg is the main Arg for its type + */ + PRIMARY("is a primary argument for its type"), + /* + * A HASTYPE Arg can have an Arg.Type assigned to its ArgValue + */ + HASTYPE(null), + /* + * A FIRST arg gets moved to appear first in the usage statement (within type) + */ + FIRST(null), + /* + * A LAST arg gets moved to appear last in the usage statement (within type) + */ + LAST(null), + // + ; + + private String description; + + private Opt() + { + description = null; + } + + private Opt(String description) + { + this.description = description; + } + + public String description() + { + return description; + } + } public static enum Type @@ -627,7 +708,7 @@ public enum Arg } } - appendArgUsage(sb, a, maxArgLength); + appendArgUsage(sb, a, maxArgLength, Platform.consoleWidth()); if (argsI.hasNext()) { @@ -639,69 +720,71 @@ public enum Arg } private static void appendArgUsage(StringBuilder sb, Arg a, - int maxArgLength) + int maxArgLength, int maxWidth) { boolean first = appendArgAndDescription(sb, null, null, a, maxArgLength); List options = new ArrayList<>(); - if (a.hasOption(Opt.BOOLEAN)) - { - options.add("default " + (a.getDefaultBoolValue() ? a.argString() - : a.negateArgString())); - } - - if (a.hasOption(Opt.MULTI)) - { - options.add("multiple"); - } - - if (a.hasOption(Opt.LINKED)) - { - options.add("can be linked"); - } - - if (a.hasOption(Opt.GLOB)) + for (Opt o : EnumSet.allOf(Opt.class)) { - options.add("allows file globs"); - } - - if (a.hasOption(Opt.ALLOWSUBSTITUTIONS)) - { - options.add("allows substitutions"); - } - - if (a.hasOption(Opt.ALLOWALL)) - { - options.add("can be applied to all linked arguments"); - } - - if (a.hasOption(Opt.PRIVATE)) - { - options.add("for internal use only"); - } - - if (a.hasOption(Opt.SECRET)) - { - options.add("for development use only"); + if (a.hasOption(o) && o.description() != null) + { + options.add(o.description()); + } } + final String optDisplaySeparator = "; "; if (options.size() > 0) { + int linelength = 0; + String spacing = String.format("%-" + + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s", + ""); if (first) { sb.append(ARGDESCRIPTIONSEPARATOR); + linelength += maxArgLength + ARGDESCRIPTIONSEPARATOR.length(); } else { - sb.append(String.format("%-" - + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s", - "")); + sb.append(spacing); + linelength += spacing.length(); + } + if (options.size() > 0) + { + boolean optFirst = true; + Iterator optionsI = options.listIterator(); + while (optionsI.hasNext()) + { + String desc = optionsI.next(); + if (optFirst) + { + sb.append("("); + linelength += 1; + } + int descLength = desc.length() + + (optionsI.hasNext() ? optDisplaySeparator.length() : 0); + if (linelength + descLength > maxWidth) + { + sb.append(System.lineSeparator()); + linelength = 0; + sb.append(spacing); + linelength += spacing.length(); + } + // sb.append(linelength + "+" + desc.length() + " "); + sb.append(desc); + linelength += desc.length(); + if (optionsI.hasNext()) + { + sb.append(optDisplaySeparator); + linelength += optDisplaySeparator.length(); + } + optFirst = false; + } + sb.append(')'); + sb.append(System.lineSeparator()); } - sb.append("("); - sb.append(String.join("; ", options)); - sb.append(')'); - sb.append(System.lineSeparator()); } }