Skip to content

Commit

Permalink
[#1574][#1408][#964] Add annotation API to control whether synopsis s…
Browse files Browse the repository at this point in the history
…hould be sorted alphabetically or via `order`.

Closes #1408
Closes #1574
  • Loading branch information
remkop committed Feb 14, 2022
1 parent 2ba9f62 commit 84fa2c2
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 6 deletions.
4 changes: 3 additions & 1 deletion RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ class MyIntConverter implements ITypeConverter<Integer> {
* [#1380][#1505] API, bugfix: `requiredOptionMarker` should not be displayed on `ArgGroup` options. Thanks to [Ahmed El Khalifa](https://github.com/ahmede41) for the pull request.
* [#1563] API: Add constructor to `PicocliSpringFactory` to allow custom fallback `IFactory`. Thanks to [Andrew Holland](https://github.com/a1dutch) for raising this.
* [#1571] Enhancement: Variables in values from the default value provider should be interpolated. Thanks to [Bas Passon](https://github.com/bpasson) for raising this.
* [#964][#1080] Enhancement: ArgGroup synopsis should respect order (if specified). Thanks to [Enderaoe](https://github.com/Lyther) for the pull request with unit tests.
* [#1574] API: Add annotation API to control whether synopsis should be sorted alphabetically or by explicit `order`.
* [#1408] Enhancement: Synopsis should respect `order` if specified. Thanks to [Simon](https://github.com/sbernard31) for raising this.
* [#964][#1080] Enhancement: ArgGroup synopsis should respect `order` (if specified). Thanks to [Enderaoe](https://github.com/Lyther) for the pull request with unit tests.
* [#1572] Enhancement: Remove redundant braces in ArgGroup synopsis.
* [#1573] DEP: Bump JLine3 version to 3.21.0 from 3.19.0.

Expand Down
11 changes: 11 additions & 0 deletions docs/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5942,6 +5942,17 @@ When mixing `@Option` methods and `@Option` fields, options do not reliably appe
The `@Option(order = <int>)` attribute can be used to explicitly control the position in the usage help message at which the option should be shown.
Options with a lower number are shown before options with a higher number.

==== Unsorted Synopsis
By default the synopsis displays options in alphabetical order. Use the `sortSynopsis = false` attribute to let the synopsis display options in the order they are declared in your class,
or sorted by their `order` attribute.

//.Java / Kotlin
//[source,java,role="primary"]
[source,java]
----
@Command(sortSynopsis = false)
----

==== Required-Option Marker
Required options can be marked in the option list by the character specified with the `requiredOptionMarker` attribute. By default options are not marked because the synopsis shows users which options are required and which are optional. This feature may be useful in combination with the <<Abbreviated Synopsis,`abbreviateSynopsis`>> attribute. For example:

Expand Down
29 changes: 26 additions & 3 deletions src/main/java/picocli/CommandLine.java
Original file line number Diff line number Diff line change
Expand Up @@ -4621,10 +4621,17 @@ enum Target {
* @see Help#optionListHeading(Object...) */
String optionListHeading() default "";

/** Specify {@code false} to show Options in declaration order. The default is to sort alphabetically.
/** Specify {@code false} to show Options in declaration order (or sorted by their {@linkplain Option#order() order index}).
* The default is to sort alphabetically.
* @return whether options should be shown in alphabetic order. */
boolean sortOptions() default true;

/** Specify {@code false} to show the synopsis in declaration order (or sorted by their {@linkplain Option#order() order index}).
* The default is to sort alphabetically.
* @return whether options in the synopsis should be shown in alphabetic order.
* @since 4.7.0 */
boolean sortSynopsis() default true;

/** Prefix required options with this character in the options list. The default is no marker: the synopsis
* indicates which options and parameters are required.
* @return the character to show in the options list to mark required options */
Expand Down Expand Up @@ -7608,6 +7615,9 @@ public static class UsageMessageSpec {
/** Constant Boolean holding the default setting for whether to sort the options alphabetically: <code>{@value}</code>.*/
static final Boolean DEFAULT_SORT_OPTIONS = Boolean.TRUE;

/** Constant Boolean holding the default setting for whether to sort options in the synopsis alphabetically: <code>{@value}</code>.*/
static final Boolean DEFAULT_SORT_SYNOPSIS = Boolean.TRUE;

/** Constant Boolean holding the default setting for whether to show an entry for @-files in the usage help message.*/
static final Boolean DEFAULT_SHOW_AT_FILE = Boolean.FALSE;

Expand Down Expand Up @@ -7656,6 +7666,7 @@ public static class UsageMessageSpec {
private String[] footer;
private Boolean abbreviateSynopsis;
private Boolean sortOptions;
private Boolean sortSynopsis;
private Boolean showDefaultValues;
private Boolean showAtFileInUsageHelp;
private Boolean showEndOfOptionsDelimiterInUsageHelp;
Expand Down Expand Up @@ -8039,6 +8050,10 @@ private String[] arr(String[] localized, String[] value, String[] defaultValue)
/** Returns whether the options list in the usage help message should be sorted alphabetically. */
public boolean sortOptions() { return (sortOptions == null) ? DEFAULT_SORT_OPTIONS : sortOptions; }

/** Returns whether the options in the synopsis should be sorted alphabetically.
* @since 4.7.0 */
public boolean sortSynopsis() { return (sortSynopsis == null) ? DEFAULT_SORT_SYNOPSIS : sortSynopsis; }

/** Returns the character used to prefix required options in the options list. */
public char requiredOptionMarker() { return (requiredOptionMarker == null) ? DEFAULT_REQUIRED_OPTION_MARKER : requiredOptionMarker; }

Expand Down Expand Up @@ -8183,6 +8198,11 @@ public UsageMessageSpec synopsisAutoIndentThreshold(double newValue) {
* @return this UsageMessageSpec for method chaining */
public UsageMessageSpec sortOptions(boolean newValue) {sortOptions = newValue; return this;}

/** Sets whether the options in the synopsis should be sorted alphabetically.
* @return this UsageMessageSpec for method chaining
* @since 4.7.0 */
public UsageMessageSpec sortSynopsis(boolean newValue) {sortSynopsis = newValue; return this;}

/** Sets the character used to prefix required options in the options list.
* @return this UsageMessageSpec for method chaining */
public UsageMessageSpec requiredOptionMarker(char newValue) {requiredOptionMarker = newValue; return this;}
Expand Down Expand Up @@ -8288,6 +8308,7 @@ void updateFromCommand(Command cmd, CommandSpec commandSpec, boolean loadResourc
if (isNonDefault(cmd.showDefaultValues(), DEFAULT_SHOW_DEFAULT_VALUES)) {showDefaultValues = cmd.showDefaultValues();}
if (isNonDefault(cmd.showEndOfOptionsDelimiterInUsageHelp(), DEFAULT_SHOW_END_OF_OPTIONS)) {showEndOfOptionsDelimiterInUsageHelp = cmd.showEndOfOptionsDelimiterInUsageHelp();}
if (isNonDefault(cmd.sortOptions(), DEFAULT_SORT_OPTIONS)) {sortOptions = cmd.sortOptions();}
if (isNonDefault(cmd.sortSynopsis(), DEFAULT_SORT_SYNOPSIS)) {sortSynopsis = cmd.sortSynopsis();}
if (isNonDefault(cmd.synopsisHeading(), DEFAULT_SYNOPSIS_HEADING)) {synopsisHeading = cmd.synopsisHeading();}
if (isNonDefault(cmd.synopsisSubcommandLabel(), DEFAULT_SYNOPSIS_SUBCOMMANDS)){synopsisSubcommandLabel = cmd.synopsisSubcommandLabel();}
if (isNonDefault(cmd.usageHelpWidth(), DEFAULT_USAGE_WIDTH)) {width(cmd.usageHelpWidth());} // validate
Expand Down Expand Up @@ -8316,6 +8337,7 @@ void initFromMixin(UsageMessageSpec mixin, CommandSpec commandSpec) {
if (initializable(showDefaultValues, mixin.showDefaultValues(), DEFAULT_SHOW_DEFAULT_VALUES)) {showDefaultValues = mixin.showDefaultValues();}
if (initializable(showEndOfOptionsDelimiterInUsageHelp, mixin.showEndOfOptionsDelimiterInUsageHelp(), DEFAULT_SHOW_END_OF_OPTIONS)) {showEndOfOptionsDelimiterInUsageHelp = mixin.showEndOfOptionsDelimiterInUsageHelp();}
if (initializable(sortOptions, mixin.sortOptions(), DEFAULT_SORT_OPTIONS)) {sortOptions = mixin.sortOptions();}
if (initializable(sortSynopsis, mixin.sortSynopsis(), DEFAULT_SORT_SYNOPSIS)) {sortSynopsis = mixin.sortSynopsis();}
if (initializable(synopsisHeading, mixin.synopsisHeading(), DEFAULT_SYNOPSIS_HEADING)) {synopsisHeading = mixin.synopsisHeading();}
if (initializable(synopsisSubcommandLabel, mixin.synopsisSubcommandLabel(), DEFAULT_SYNOPSIS_SUBCOMMANDS)) {synopsisSubcommandLabel = mixin.synopsisSubcommandLabel();}
if (initializable(width, mixin.width(), DEFAULT_USAGE_WIDTH)) {width = mixin.width();}
Expand Down Expand Up @@ -8349,6 +8371,7 @@ void initFrom(UsageMessageSpec settings, CommandSpec commandSpec) {
showDefaultValues = settings.showDefaultValues;
showEndOfOptionsDelimiterInUsageHelp = settings.showEndOfOptionsDelimiterInUsageHelp;
sortOptions = settings.sortOptions;
sortSynopsis = settings.sortSynopsis;
synopsisAutoIndentThreshold = settings.synopsisAutoIndentThreshold;
synopsisHeading = settings.synopsisHeading;
synopsisIndent = settings.synopsisIndent;
Expand Down Expand Up @@ -10425,7 +10448,7 @@ private Text rawSynopsisUnitText(Help.ColorScheme colorScheme, Set<ArgSpec> outp
// if not sorted alphabetically then SortByOrder
boolean sortExplicitly = !args.isEmpty()
&& args.iterator().next().command() != null
&& !args.iterator().next().command().usageMessage().sortOptions();
&& !args.iterator().next().command().usageMessage().sortSynopsis();
if (sortExplicitly) {
List<IOrdered> sortableComponents = new ArrayList<IOrdered>();
List<PositionalParamSpec> remainder = new ArrayList<PositionalParamSpec>();
Expand Down Expand Up @@ -15340,7 +15363,7 @@ public String fullSynopsis() {
*/
public String synopsis(int synopsisHeadingLength) {
if (!empty(commandSpec.usageMessage().customSynopsis())) { return customSynopsis(); }
Comparator<OptionSpec> sortStrategy = true // #1574 TODO commandSpec.usageMessage().sortSynopsis()
Comparator<OptionSpec> sortStrategy = commandSpec.usageMessage().sortSynopsis()
? createShortOptionArityAndNameComparator() // alphabetic sort
: createOrderComparatorIfNecessary(commandSpec.options()); // explicit sort
return commandSpec.usageMessage().abbreviateSynopsis() ? abbreviatedSynopsis()
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/picocli/OrderedArgGroupSynopsisTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class OrderedArgGroupSynopsisTest {
@Rule
public final SystemErrRule systemErrRule = new SystemErrRule().enableLog().muteForSuccessfulTests();

@Command(sortOptions = false)
@Command(sortOptions = false, sortSynopsis = false)
class SynopsisOrder {
class GroupWithOneOption {
@Option(names = "--option1", required = true)
Expand Down
3 changes: 2 additions & 1 deletion src/test/java/picocli/OrderedSynopsisTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
// https://github.com/remkop/picocli/issues/1408
public class OrderedSynopsisTest {

@Ignore("#1408")
// @Ignore("#1408")
@Test
public void testIssue1408() {
@Command(name = "myCommand",
mixinStandardHelpOptions = true,
description = "A command with explicitly ordered options.",
sortSynopsis = false,
sortOptions = false)
class Example {
@Option(names = { "-d", "--option-d" }, order = -10, description = "Should be first")
Expand Down

0 comments on commit 84fa2c2

Please sign in to comment.