private static void addCandidatesForArgsFollowing(CommandSpec commandSpec, List<CharSequence> candidates) { if (commandSpec == null) { return; } for (Map.Entry<String, CommandLine> entry : commandSpec.subcommands().entrySet()) { candidates.add(entry.getKey()); candidates.addAll(Arrays.asList(entry.getValue().getCommandSpec().aliases())); } candidates.addAll(commandSpec.optionsMap().keySet()); for (PositionalParamSpec positional : commandSpec.positionalParameters()) { addCandidatesForArgsFollowing(positional, candidates); } } private static void addCandidatesForArgsFollowing(OptionSpec optionSpec, List<CharSequence> candidates) {
/** Returns suggested solutions if such solutions exist, otherwise returns an empty list. * @since 3.3.0 */ public List<String> getSuggestions() { if (unmatched == null || unmatched.isEmpty()) { return Collections.emptyList(); } String arg = unmatched.get(0); String stripped = CommandSpec.stripPrefix(arg); CommandSpec spec = getCommandLine().getCommandSpec(); if (spec.resemblesOption(arg, null)) { return spec.findOptionNamesWithPrefix(stripped.substring(0, Math.min(2, stripped.length()))); } else if (!spec.subcommands().isEmpty()) { List<String> mostSimilar = CosineSimilarity.mostSimilar(arg, spec.subcommands().keySet()); return mostSimilar.subList(0, Math.min(3, mostSimilar.size())); } return Collections.emptyList(); } private static boolean isUnknownOption(List<String> unmatch, CommandLine cmd) {
/** Returns a map with the subcommands {@linkplain #addSubcommand(String, Object) registered} on this instance. * @return a map with the registered subcommands * @since 0.9.7 */ public Map<String, CommandLine> getSubcommands() { return new LinkedHashMap<String, CommandLine>(getCommandSpec().subcommands()); } /**
@Test public void testCommandSpec_forAnnotatedObjectLenient_returnsEmptyCommandSpec() { CommandSpec spec = CommandSpec.forAnnotatedObjectLenient(new Object()); assertTrue(spec.optionsMap().isEmpty()); assertTrue(spec.posixOptionsMap().isEmpty()); assertTrue(spec.options().isEmpty()); assertTrue(spec.positionalParameters().isEmpty()); assertTrue(spec.unmatchedArgsBindings().isEmpty()); assertTrue(spec.subcommands().isEmpty()); assertTrue(spec.mixins().isEmpty()); assertTrue(spec.requiredArgs().isEmpty()); assertFalse(spec.mixinStandardHelpOptions()); assertFalse(spec.helpCommand()); assertEquals("<main class>", spec.name()); assertArrayEquals(new String[0], spec.version()); assertNull(spec.versionProvider()); }
/** Sets whether short options like {@code -x -v -f SomeFile} can be clustered together like {@code -xvfSomeFile}. The default is {@code true}. * <p>The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its * subcommands and nested sub-subcommands <em>at the moment this method is called</em>. Subcommands added * later will have the default setting. To ensure a setting is applied to all * subcommands, call the setter last, after adding subcommands.</p> * @param newValue the new setting * @return this {@code CommandLine} object, to allow method chaining * @since 3.0 */ public CommandLine setPosixClusteredShortOptionsAllowed(boolean newValue) { getCommandSpec().parser().posixClusteredShortOptionsAllowed(newValue); for (CommandLine command : getCommandSpec().subcommands().values()) { command.setPosixClusteredShortOptionsAllowed(newValue); } return this; }
/** Sets whether to use a simplified argument file format that is compatible with JCommander. * In this format, every line (except empty lines and comment lines) * is interpreted as a single argument. Arguments containing whitespace do not need to be quoted. * When system property {@code "picocli.useSimplifiedAtFiles"} is defined, the system property value overrides the programmatically set value. * @param simplifiedAtFiles whether to use a simplified argument file format. The default is {@code false}. * @return this {@code CommandLine} object, to allow method chaining * @since 3.9 */ public CommandLine setUseSimplifiedAtFiles(boolean simplifiedAtFiles) { getCommandSpec().parser().useSimplifiedAtFiles(simplifiedAtFiles); for (CommandLine command : getCommandSpec().subcommands().values()) { command.setUseSimplifiedAtFiles(simplifiedAtFiles); } return this; } private static boolean empty(String str) { return str == null || str.trim().length() == 0; }
/** Sets the String the parser uses to separate option names from option values to the specified value. * The separator may also be set declaratively with the {@link CommandLine.Command#separator()} annotation attribute. * <p>The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its * subcommands and nested sub-subcommands <em>at the moment this method is called</em>. Subcommands added * later will have the default setting. To ensure a setting is applied to all * subcommands, call the setter last, after adding subcommands.</p> * @param separator the String that separates option names from option values * @see ParserSpec#separator(String) * @return this {@code CommandLine} object, to allow method chaining */ public CommandLine setSeparator(String separator) { getCommandSpec().parser().separator(Assert.notNull(separator, "separator")); for (CommandLine command : getCommandSpec().subcommands().values()) { command.setSeparator(separator); } return this; }
/** Sets whether the value of boolean flag options should be "toggled" when the option is matched. The default is {@code true}. * <p>The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its * subcommands and nested sub-subcommands <em>at the moment this method is called</em>. Subcommands added * later will have the default setting. To ensure a setting is applied to all * subcommands, call the setter last, after adding subcommands.</p> * @param newValue the new setting * @return this {@code CommandLine} object, to allow method chaining * @since 3.0 */ public CommandLine setToggleBooleanFlags(boolean newValue) { getCommandSpec().parser().toggleBooleanFlags(newValue); for (CommandLine command : getCommandSpec().subcommands().values()) { command.setToggleBooleanFlags(newValue); } return this; }
/** Sets the maximum width of the usage help message. Longer lines are wrapped. * <p>The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its * subcommands and nested sub-subcommands <em>at the moment this method is called</em>. Subcommands added * later will have the default setting. To ensure a setting is applied to all * subcommands, call the setter last, after adding subcommands.</p> * @param width the maximum width of the usage help message * @see UsageMessageSpec#width(int) * @return this {@code CommandLine} object, to allow method chaining */ public CommandLine setUsageHelpWidth(int width) { getCommandSpec().usageMessage().width(width); for (CommandLine command : getCommandSpec().subcommands().values()) { command.setUsageHelpWidth(width); } return this; }
/** Sets the character that starts a single-line comment or {@code null} if all content of argument files should * be interpreted as arguments (without comments). * If specified, all characters from the comment character to the end of the line are ignored. * @param atFileCommentChar the character that starts a single-line comment or {@code null}. The default is {@code '#'}. * @return this {@code CommandLine} object, to allow method chaining * @since 3.5 */ public CommandLine setAtFileCommentChar(Character atFileCommentChar) { getCommandSpec().parser().atFileCommentChar(atFileCommentChar); for (CommandLine command : getCommandSpec().subcommands().values()) { command.setAtFileCommentChar(atFileCommentChar); } return this; }
/** Returns a Text object containing a partial detailed synopsis showing only the subcommands, starting with a {@code " "} space. * Follows the unix convention of showing optional elements in square brackets ({@code [ ]}). * @return this implementation returns a hard-coded string {@code " [COMMAND]"} if this command has subcommands, an empty Text otherwise * @since 3.9 */ protected Text createDetailedSynopsisCommandText() { Text commandText = ansi().new Text(0); if (!commandSpec.subcommands().isEmpty()){ commandText = commandText.concat(" [") .concat("COMMAND") .concat("]"); } return commandText; }
@Test public void testAddMixinMustBeValidCommand_SubCommandMethod() { @Command class ValidMixin { // valid command because it has @Command annotation } @Command class Receiver { @Command void sub(@Mixin ValidMixin mixin) { } } CommandLine commandLine = new CommandLine(new Receiver(), new InnerClassFactory(this)); CommandSpec commandSpec = commandLine.getCommandSpec().subcommands().get("sub").getCommandSpec().mixins().get("arg0"); assertEquals(ValidMixin.class, commandSpec.userObject().getClass()); commandLine.addMixin("valid", new ValidMixin()); // no exception }
public String render(Help help) { CommandSpec spec = help.commandSpec(); if (spec.subcommands().isEmpty()) { return ""; } // prepare layout: two columns // the left column overflows, the right column wraps if text is too long TextTable textTable = TextTable.forColumns(help.ansi(), new Column(15, 2, Overflow.SPAN), new Column(spec.usageMessage().width() - 15, 2, Overflow.WRAP)); for (CommandLine subcommand : spec.subcommands().values()) { addHierarchy(subcommand, textTable, ""); } return textTable.toString(); }
private static CommandSpec findCommandFor(PositionalParamSpec positional, CommandSpec commandSpec) { for (PositionalParamSpec defined : commandSpec.positionalParameters()) { if (defined == positional) { return commandSpec; } } for (CommandLine sub : commandSpec.subcommands().values()) { CommandSpec result = findCommandFor(positional, sub.getCommandSpec()); if (result != null) { return result; } } return null; } private static boolean isPicocliModelObject(Object obj) {
/** Constructs a new {@code Help} instance with the specified color scheme, initialized from annotatations * on the specified class and superclasses. * @param commandSpec the command model to create usage help for * @param colorScheme the color scheme to use */ public Help(CommandSpec commandSpec, ColorScheme colorScheme) { this.commandSpec = Assert.notNull(commandSpec, "commandSpec"); this.aliases = new ArrayList<String>(Arrays.asList(commandSpec.aliases())); this.aliases.add(0, commandSpec.name()); this.colorScheme = Assert.notNull(colorScheme, "colorScheme").applySystemProperties(); parameterLabelRenderer = createDefaultParamLabelRenderer(); // uses help separator this.addAllSubcommands(commandSpec.subcommands()); }
/** Returns whether the next argument can be assigned to a vararg option/positional parameter. * <p> * Usually, we stop if we encounter '--', a command, or another option. * However, if end-of-options has been reached, positional parameters may consume all remaining arguments. </p>*/ private boolean varargCanConsumeNextValue(ArgSpec argSpec, String nextValue) { if (endOfOptions && argSpec.isPositional()) { return true; } boolean isCommand = commandSpec.subcommands().containsKey(nextValue); return !isCommand && !isOption(nextValue); }
@Command public void explicit(@Option(names = "-v") boolean v) { CommandLine commandLine = spec.subcommands().get("explicit"); throw new CommandLine.ParameterException(commandLine, "Validation failed"); } }
/** Sets the end-of-options delimiter that signals that the remaining command line arguments should be treated as positional parameters. * @param delimiter the end-of-options delimiter; must not be {@code null}. The default is {@code "--"}. * @return this {@code CommandLine} object, to allow method chaining * @since 3.5 */ public CommandLine setEndOfOptionsDelimiter(String delimiter) { getCommandSpec().parser().endOfOptionsDelimiter(delimiter); for (CommandLine command : getCommandSpec().subcommands().values()) { command.setEndOfOptionsDelimiter(delimiter); } return this; }
private static CommandSpec findCommandFor(OptionSpec option, CommandSpec commandSpec) { for (OptionSpec defined : commandSpec.options()) { if (defined == option) { return commandSpec; } } for (CommandLine sub : commandSpec.subcommands().values()) { CommandSpec result = findCommandFor(option, sub.getCommandSpec()); if (result != null) { return result; } } return null; } private static CommandSpec findCommandFor(PositionalParamSpec positional, CommandSpec commandSpec) {
@Test public void testAddMethodSubcommands() { CommandSpec spec = CommandSpec.wrapWithoutInspection(new StaticMethodCommand(1)); assertEquals(0, spec.subcommands().size()); spec.addMethodSubcommands(); assertEquals(4, spec.subcommands().size()); }