diff --git a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/SpringShellProperties.java b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/SpringShellProperties.java index f54ba15d9..1d62fa92e 100644 --- a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/SpringShellProperties.java +++ b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/SpringShellProperties.java @@ -126,6 +126,7 @@ public void setName(String name) { public static class HelpCommand { private boolean enabled = true; + private GroupingMode groupingMode = GroupingMode.GROUP; public boolean isEnabled() { return enabled; @@ -134,6 +135,19 @@ public boolean isEnabled() { public void setEnabled(boolean enabled) { this.enabled = enabled; } + + public GroupingMode getGroupingMode() { + return groupingMode; + } + + public void setGroupingMode(GroupingMode groupingMode) { + this.groupingMode = groupingMode; + } + + public enum GroupingMode { + GROUP, + FLAT + } } public static class ClearCommand { diff --git a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/StandardCommandsAutoConfiguration.java b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/StandardCommandsAutoConfiguration.java index d4cdfa8a9..42324789b 100644 --- a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/StandardCommandsAutoConfiguration.java +++ b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/StandardCommandsAutoConfiguration.java @@ -29,6 +29,7 @@ import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.shell.boot.SpringShellProperties.VersionCommand; +import org.springframework.shell.boot.SpringShellProperties.HelpCommand.GroupingMode; import org.springframework.shell.boot.condition.OnCompletionCommandCondition; import org.springframework.shell.result.ThrowableResultHandler; import org.springframework.shell.standard.commands.Clear; @@ -54,8 +55,12 @@ public class StandardCommandsAutoConfiguration { @Bean @ConditionalOnMissingBean(Help.Command.class) @ConditionalOnProperty(prefix = "spring.shell.command.help", value = "enabled", havingValue = "true", matchIfMissing = true) - public Help help() { - return new Help(); + public Help help(SpringShellProperties properties) { + Help help = new Help(); + if (properties.getCommand().getHelp().getGroupingMode() == GroupingMode.FLAT) { + help.setShowGroups(false); + } + return help; } @Bean diff --git a/spring-shell-autoconfigure/src/test/java/org/springframework/shell/boot/SpringShellPropertiesTests.java b/spring-shell-autoconfigure/src/test/java/org/springframework/shell/boot/SpringShellPropertiesTests.java index 9ef9577c2..b5f8b17ee 100644 --- a/spring-shell-autoconfigure/src/test/java/org/springframework/shell/boot/SpringShellPropertiesTests.java +++ b/spring-shell-autoconfigure/src/test/java/org/springframework/shell/boot/SpringShellPropertiesTests.java @@ -19,6 +19,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.shell.boot.SpringShellProperties.HelpCommand.GroupingMode; import static org.assertj.core.api.Assertions.assertThat; @@ -38,6 +39,7 @@ public void defaultNoPropertiesSet() { assertThat(properties.getTheme().getName()).isEqualTo("default"); assertThat(properties.getCommand().getClear().isEnabled()).isTrue(); assertThat(properties.getCommand().getHelp().isEnabled()).isTrue(); + assertThat(properties.getCommand().getHelp().getGroupingMode()).isEqualTo(GroupingMode.GROUP); assertThat(properties.getCommand().getHistory().isEnabled()).isTrue(); assertThat(properties.getCommand().getQuit().isEnabled()).isTrue(); assertThat(properties.getCommand().getScript().isEnabled()).isTrue(); @@ -67,6 +69,7 @@ public void setProperties() { .withPropertyValues("spring.shell.theme.name=fake") .withPropertyValues("spring.shell.command.clear.enabled=false") .withPropertyValues("spring.shell.command.help.enabled=false") + .withPropertyValues("spring.shell.command.help.grouping-mode=flat") .withPropertyValues("spring.shell.command.history.enabled=false") .withPropertyValues("spring.shell.command.quit.enabled=false") .withPropertyValues("spring.shell.command.script.enabled=false") @@ -93,6 +96,7 @@ public void setProperties() { assertThat(properties.getTheme().getName()).isEqualTo("fake"); assertThat(properties.getCommand().getClear().isEnabled()).isFalse(); assertThat(properties.getCommand().getHelp().isEnabled()).isFalse(); + assertThat(properties.getCommand().getHelp().getGroupingMode()).isEqualTo(GroupingMode.FLAT); assertThat(properties.getCommand().getHistory().isEnabled()).isFalse(); assertThat(properties.getCommand().getQuit().isEnabled()).isFalse(); assertThat(properties.getCommand().getScript().isEnabled()).isFalse(); diff --git a/spring-shell-autoconfigure/src/test/java/org/springframework/shell/boot/StandardCommandsAutoConfigurationTests.java b/spring-shell-autoconfigure/src/test/java/org/springframework/shell/boot/StandardCommandsAutoConfigurationTests.java index e812fc86b..f674b25e1 100644 --- a/spring-shell-autoconfigure/src/test/java/org/springframework/shell/boot/StandardCommandsAutoConfigurationTests.java +++ b/spring-shell-autoconfigure/src/test/java/org/springframework/shell/boot/StandardCommandsAutoConfigurationTests.java @@ -15,6 +15,7 @@ */ package org.springframework.shell.boot; +import java.lang.reflect.Field; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -22,6 +23,8 @@ import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.shell.standard.commands.Completion; +import org.springframework.shell.standard.commands.Help; +import org.springframework.util.ReflectionUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -48,6 +51,21 @@ public void testCompletionCommand() { }); } + @Test + public void testHelpCommand() { + this.contextRunner + .with(disableCommands("clear", "quit", "stacktrace", "script", "history", "completion")) + .withPropertyValues("spring.shell.command.help.grouping-mode=flat") + .run(context -> { + assertThat(context).hasSingleBean(Help.class); + Help help = context.getBean(Help.class); + Field showGroupsField = ReflectionUtils.findField(Help.class, "showGroups"); + ReflectionUtils.makeAccessible(showGroupsField); + ReflectionUtils.getField(showGroupsField, help); + assertThat(ReflectionUtils.getField(showGroupsField, help)).isEqualTo(false); + }); + } + private static Function disableCommands(String... commands) { return (cr) -> { for (String command : commands) { diff --git a/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Help.java b/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Help.java index 25e26a31a..818b77b9a 100644 --- a/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Help.java +++ b/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Help.java @@ -78,6 +78,7 @@ public interface Command { } private MessageInterpolator messageInterpolator = Utils.defaultValidatorFactory().getMessageInterpolator(); + private boolean showGroups = true; public Help() { } @@ -101,6 +102,16 @@ public CharSequence help( } + /** + * Sets if groups should be shown in a listing, defaults to true. If not enabled + * a simple list is shown without groups. + * + * @param showGroups the flag to show groups + */ + public void setShowGroups(boolean showGroups) { + this.showGroups = showGroups; + } + /** * Return a description of a specific command. Uses a layout inspired by *nix man pages. */ @@ -263,32 +274,34 @@ private String first(List keys) { private CharSequence listCommands() { Map commandsByName = getCommandRegistry().listCommands(); - SortedMap> commandsByGroupAndName = commandsByName.entrySet().stream() - .collect(groupingBy(e -> e.getValue().getGroup(), TreeMap::new, // group by and sort by command group - toMap(Entry::getKey, Entry::getValue))); - AttributedStringBuilder result = new AttributedStringBuilder(); result.append("AVAILABLE COMMANDS\n\n", AttributedStyle.BOLD); + SortedMap> commandsByGroupAndName = commandsByName.entrySet().stream() + .collect(groupingBy(e -> e.getValue().getGroup(), TreeMap::new, // group by and sort by command group + toMap(Entry::getKey, Entry::getValue))); // display groups, sorted alphabetically, "Default" first commandsByGroupAndName.forEach((group, commandsInGroup) -> { - result.append("".equals(group) ? "Default" : group, AttributedStyle.BOLD).append('\n'); - + if (showGroups) { + result.append("".equals(group) ? "Default" : group, AttributedStyle.BOLD).append('\n'); + } Map> commandNamesByMethod = commandsInGroup.entrySet().stream() .collect(groupingBy(Entry::getValue, // group by command method mapping(Entry::getKey, toCollection(TreeSet::new)))); // sort command names - // display commands, sorted alphabetically by their first alias commandNamesByMethod.entrySet().stream().sorted(sortByFirstCommandName()).forEach(e -> { + String prefix = showGroups ? " " : ""; + prefix = prefix + (isAvailable(e.getKey()) ? " " : " *"); result - .append(isAvailable(e.getKey()) ? " " : " * ") + .append(prefix) .append(String.join(", ", e.getValue()), AttributedStyle.BOLD) .append(": ") .append(e.getKey().getHelp()) .append('\n'); }); - - result.append('\n'); + if (showGroups) { + result.append('\n'); + } }); if (commandsByName.values().stream().distinct().anyMatch(m -> !isAvailable(m))) {