Separate interactive and non-interactive commands

- Add new ShellContext concept which now is just a way
  to stash info about interaction mode where ShellRunner
  can update supported mode.
- ShellMethod has a new field interactionMode which user
  can use to define commands between interactive/non-interactive
  modes which then prevents CommandRegistry to show
  commands at runtime.
- Fixes #345
This commit is contained in:
Janne Valkealahti
2022-01-09 14:10:15 +00:00
parent 3cb3309d68
commit dbe8a8b408
21 changed files with 284 additions and 32 deletions

View File

@@ -21,14 +21,16 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.shell.CommandRegistry;
import org.springframework.shell.ConfigurableCommandRegistry;
import org.springframework.shell.MethodTargetRegistrar;
import org.springframework.shell.context.ShellContext;
@Configuration(proxyBeanMethods = false)
public class CommandRegistryAutoConfiguration {
@Bean
public CommandRegistry commandRegistry(
ObjectProvider<MethodTargetRegistrar> methodTargetRegistrars) {
ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry();
ObjectProvider<MethodTargetRegistrar> methodTargetRegistrars,
ShellContext shellContext) {
ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(shellContext);
methodTargetRegistrars.orderedStream().forEach(resolver -> {
resolver.register(registry);
});

View File

@@ -0,0 +1,15 @@
package org.springframework.shell.boot;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.shell.context.DefaultShellContext;
import org.springframework.shell.context.ShellContext;
@Configuration(proxyBeanMethods = false)
public class ShellContextAutoConfiguration {
@Bean
public ShellContext shellContext() {
return new DefaultShellContext();
}
}

View File

@@ -22,6 +22,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.shell.Shell;
import org.springframework.shell.context.ShellContext;
import org.springframework.shell.jline.InteractiveShellRunner;
import org.springframework.shell.jline.NonInteractiveShellRunner;
import org.springframework.shell.jline.PromptProvider;
@@ -34,24 +35,27 @@ public class ShellRunnerAutoConfiguration {
private PromptProvider promptProvider;
private LineReader lineReader;
private Parser parser;
private ShellContext shellContext;
public ShellRunnerAutoConfiguration(Shell shell, PromptProvider promptProvider, LineReader lineReader, Parser parser) {
public ShellRunnerAutoConfiguration(Shell shell, PromptProvider promptProvider, LineReader lineReader,
Parser parser, ShellContext shellContext) {
this.shell = shell;
this.promptProvider = promptProvider;
this.lineReader = lineReader;
this.parser = parser;
this.shellContext = shellContext;
}
@Bean
@ConditionalOnProperty(prefix = "spring.shell.interactive", value = "enabled", havingValue = "true", matchIfMissing = true)
public InteractiveShellRunner interactiveApplicationRunner() {
return new InteractiveShellRunner(lineReader, promptProvider, shell);
return new InteractiveShellRunner(lineReader, promptProvider, shell, shellContext);
}
@Bean
@ConditionalOnProperty(prefix = "spring.shell.noninteractive", value = "enabled", havingValue = "true", matchIfMissing = true)
public NonInteractiveShellRunner nonInteractiveApplicationRunner() {
return new NonInteractiveShellRunner(shell);
return new NonInteractiveShellRunner(shell, shellContext);
}
@Bean

View File

@@ -1,4 +1,5 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.shell.boot.ShellContextAutoConfiguration,\
org.springframework.shell.boot.SpringShellAutoConfiguration,\
org.springframework.shell.boot.ShellRunnerAutoConfiguration,\
org.springframework.shell.boot.ApplicationRunnerAutoConfiguration,\

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 the original author or authors.
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
import org.springframework.shell.context.InteractionMode;
import org.springframework.shell.context.ShellContext;
/**
* A {@link CommandRegistry} that supports registration of new commands.
@@ -29,11 +31,31 @@ import java.util.TreeMap;
*/
public class ConfigurableCommandRegistry implements CommandRegistry {
private final ShellContext shellContext;
private Map<String, MethodTarget> commands = new HashMap<>();
public ConfigurableCommandRegistry(ShellContext shellContext) {
this.shellContext = shellContext;
}
@Override
public Map<String, MethodTarget> listCommands() {
return new TreeMap<>(commands);
return commands.entrySet().stream()
.filter(e -> {
InteractionMode mim = e.getValue().getInteractionMode();
InteractionMode cim = shellContext.getInteractionMode();
if (mim == null || cim == null || mim == InteractionMode.ALL) {
return true;
}
else if (mim == InteractionMode.INTERACTIVE) {
return cim == InteractionMode.INTERACTIVE || cim == InteractionMode.ALL;
}
else if (mim == InteractionMode.NONINTERACTIVE) {
return cim == InteractionMode.NONINTERACTIVE || cim == InteractionMode.ALL;
}
return true;
})
.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
}
public void register(String name, MethodTarget target) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell;
import java.lang.reflect.Method;
@@ -21,6 +20,7 @@ import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;
import org.springframework.shell.context.InteractionMode;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
@@ -37,6 +37,8 @@ public class MethodTarget implements Command {
private final Help help;
private final InteractionMode interactionMode;
/**
* If not null, returns whether or not the command is currently available. Implementations must be idempotent.
*/
@@ -51,6 +53,10 @@ public class MethodTarget implements Command {
}
public MethodTarget(Method method, Object bean, Help help, Supplier<Availability> availabilityIndicator) {
this(method, bean, help, availabilityIndicator, null);
}
public MethodTarget(Method method, Object bean, Help help, Supplier<Availability> availabilityIndicator, InteractionMode interactionMode) {
Assert.notNull(method, "Method cannot be null");
Assert.notNull(bean, "Bean cannot be null");
Assert.hasText(help.getDescription(), String.format("Help cannot be blank when trying to define command based on '%s'", method));
@@ -59,6 +65,7 @@ public class MethodTarget implements Command {
this.bean = bean;
this.help = help;
this.availabilityIndicator = availabilityIndicator != null ? availabilityIndicator : () -> Availability.available();
this.interactionMode = interactionMode;
}
/**
@@ -103,6 +110,10 @@ public class MethodTarget implements Command {
return availabilityIndicator.get();
}
public InteractionMode getInteractionMode() {
return interactionMode;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.context;
import org.springframework.util.Assert;
/**
* Default implementation of a {@link ShellContext}.
*
* @author Janne Valkealahti
*/
public class DefaultShellContext implements ShellContext {
private InteractionMode interactionMode = InteractionMode.ALL;
@Override
public InteractionMode getInteractionMode() {
return interactionMode;
}
@Override
public void setInteractionMode(InteractionMode interactionMode) {
Assert.notNull(interactionMode, "mode cannot be null");
this.interactionMode = interactionMode;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.context;
/**
* Enumeration for modes shell is operating.
*
* @author Janne Valkealahti
*/
public enum InteractionMode {
/**
* All possible modes.
*/
ALL,
/**
* Non-interactive mode which is expected to exit and doesn't have any kind of
* running mode to keep shell alive.
*/
NONINTERACTIVE,
/**
* Interactive mode which is expected to not exit and do have a running mode to
* keep shell alive.
*/
INTERACTIVE
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.context;
/**
* Interface defining a contract for a context which allows to loosely connect
* different components together and keep things alive between commands.
*
* @author Janne Valkealahti
*/
public interface ShellContext {
/**
* Gets an interaction mode.
*
* @return a current interaction mode
*/
InteractionMode getInteractionMode();
/**
* Sets an interaction mode.
*
* @param interactionMode the interaction mode
*/
void setInteractionMode(InteractionMode interactionMode);
}

View File

@@ -27,6 +27,8 @@ import org.springframework.shell.Input;
import org.springframework.shell.InputProvider;
import org.springframework.shell.Shell;
import org.springframework.shell.ShellRunner;
import org.springframework.shell.context.InteractionMode;
import org.springframework.shell.context.ShellContext;
/**
* Default Boot runner that bootstraps the shell application in interactive
@@ -52,14 +54,19 @@ public class InteractiveShellRunner implements ShellRunner {
private final Shell shell;
public InteractiveShellRunner(LineReader lineReader, PromptProvider promptProvider, Shell shell) {
private final ShellContext shellContext;
public InteractiveShellRunner(LineReader lineReader, PromptProvider promptProvider, Shell shell,
ShellContext shellContext) {
this.lineReader = lineReader;
this.promptProvider = promptProvider;
this.shell = shell;
this.shellContext = shellContext;
}
@Override
public void run(ApplicationArguments args) throws Exception {
shellContext.setInteractionMode(InteractionMode.INTERACTIVE);
InputProvider inputProvider = new JLineInputProvider(lineReader, promptProvider);
shell.run(inputProvider);
}

View File

@@ -24,6 +24,8 @@ import org.springframework.shell.Input;
import org.springframework.shell.InputProvider;
import org.springframework.shell.Shell;
import org.springframework.shell.ShellRunner;
import org.springframework.shell.context.InteractionMode;
import org.springframework.shell.context.ShellContext;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@@ -37,9 +39,11 @@ import org.springframework.util.StringUtils;
public class NonInteractiveShellRunner implements ShellRunner {
private final Shell shell;
private final ShellContext shellContext;
public NonInteractiveShellRunner(Shell shell) {
public NonInteractiveShellRunner(Shell shell, ShellContext shellContext) {
this.shell = shell;
this.shellContext = shellContext;
}
@Override
@@ -50,6 +54,7 @@ public class NonInteractiveShellRunner implements ShellRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
shellContext.setInteractionMode(InteractionMode.NONINTERACTIVE);
List<String> argsToShellCommand = Arrays.asList(args.getSourceArgs());
InputProvider inputProvider = new StringInputProvider(argsToShellCommand);
shell.run(inputProvider);

View File

@@ -18,6 +18,8 @@ package org.springframework.shell;
import org.junit.jupiter.api.Test;
import org.springframework.shell.context.DefaultShellContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -30,14 +32,14 @@ public class ConfigurableCommandRegistryTest {
@Test
public void testRegistration() {
ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry();
ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(new DefaultShellContext());
registry.register("foo", MethodTarget.of("toString", this, new Command.Help("some command")));
assertThat(registry.listCommands()).containsKeys("foo");
}
@Test
public void testDoubleRegistration() {
ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry();
ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(new DefaultShellContext());
registry.register("foo", MethodTarget.of("toString", this, new Command.Help("some command")));
assertThatThrownBy(() -> {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2021 the original author or authors.
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package org.springframework.shell.standard.commands;
import org.jline.utils.InfoCmp;
import org.springframework.shell.context.InteractionMode;
import org.springframework.shell.standard.AbstractShellComponent;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
@@ -47,7 +48,7 @@ public class Clear extends AbstractShellComponent {
public Clear() {
}
@ShellMethod("Clear the shell screen.")
@ShellMethod(value = "Clear the shell screen.", interactionMode = InteractionMode.INTERACTIVE)
public void clear() {
getTerminal().puts(InfoCmp.Capability.clear_screen);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 the original author or authors.
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.shell.standard.commands;
import org.springframework.shell.ExitRequest;
import org.springframework.shell.context.InteractionMode;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
@@ -41,7 +42,7 @@ public class Quit {
*/
public interface Command {}
@ShellMethod(value = "Exit the shell.", key = {"quit", "exit"})
@ShellMethod(value = "Exit the shell.", key = {"quit", "exit"}, interactionMode = InteractionMode.INTERACTIVE)
public void quit() {
throw new ExitRequest();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2021 the original author or authors.
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.springframework.shell.standard.commands;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.shell.context.InteractionMode;
import org.springframework.shell.result.ThrowableResultHandler;
import org.springframework.shell.standard.AbstractShellComponent;
import org.springframework.shell.standard.ShellComponent;
@@ -49,7 +50,9 @@ public class Stacktrace extends AbstractShellComponent {
this.throwableResultHandler = throwableResultHandler;
}
@ShellMethod(key = ThrowableResultHandler.DETAILS_COMMAND_NAME, value = "Display the full stacktrace of the last error.")
@ShellMethod(key = ThrowableResultHandler.DETAILS_COMMAND_NAME,
value = "Display the full stacktrace of the last error.",
interactionMode = InteractionMode.INTERACTIVE)
public void stacktrace() {
if (throwableResultHandler.getIfAvailable().getLastError() != null) {
throwableResultHandler.getIfAvailable().getLastError().printStackTrace(getTerminal().writer());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.standard;
import java.lang.annotation.Documented;
@@ -22,6 +21,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.shell.context.InteractionMode;
/**
* Used to mark a method as invokable via Spring Shell.
*
@@ -68,4 +69,14 @@ public @interface ShellMethod {
*/
String group() default INHERITED;
/**
* Defines interaction mode for a command as a hint when command should be
* available. For example presense of some commands doesn't make sense if shell
* is running as non-interactive mode and vice versa.
*
* Defaults to {@link InteractionMode#ALL}
*
* @return interaction mode
*/
InteractionMode interactionMode() default InteractionMode.ALL;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2017 the original author or authors.
* Copyright 2015-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.standard;
import java.lang.reflect.Method;
@@ -76,7 +75,8 @@ public class StandardMethodTargetRegistrar implements MethodTargetRegistrar, App
String group = getOrInferGroup(method);
for (String key : keys) {
Supplier<Availability> availabilityIndicator = findAvailabilityIndicator(keys, bean, method);
MethodTarget target = new MethodTarget(method, bean, new Command.Help(shellMapping.value(), group), availabilityIndicator);
MethodTarget target = new MethodTarget(method, bean, new Command.Help(shellMapping.value(), group),
availabilityIndicator, shellMapping.interactionMode());
registry.register(key, target);
commands.put(key, target);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 the original author or authors.
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,8 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.shell.Availability;
import org.springframework.shell.ConfigurableCommandRegistry;
import org.springframework.shell.MethodTarget;
import org.springframework.shell.context.DefaultShellContext;
import org.springframework.shell.context.InteractionMode;
import org.springframework.shell.standard.test1.GroupOneCommands;
import org.springframework.shell.standard.test2.GroupThreeCommands;
import org.springframework.shell.standard.test2.GroupTwoCommands;
@@ -42,7 +44,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class StandardMethodTargetRegistrarTest {
private StandardMethodTargetRegistrar registrar = new StandardMethodTargetRegistrar();
private ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry();
private ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(new DefaultShellContext());
@Test
public void testRegistrations() {
@@ -256,4 +258,47 @@ public class StandardMethodTargetRegistrarTest {
Assertions.assertThat(commands.get("implicit3").getGroup()).isEqualTo("Explicit Group 3 Class Level");
}
@Test
public void testInteractionModeInteractive() {
DefaultShellContext shellContext = new DefaultShellContext();
shellContext.setInteractionMode(InteractionMode.INTERACTIVE);
registry = new ConfigurableCommandRegistry(shellContext);
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(InteractionModeCommands.class);
registrar.setApplicationContext(applicationContext);
registrar.register(registry);
assertThat(registry.listCommands().get("foo1")).isNotNull();
assertThat(registry.listCommands().get("foo2")).isNull();
assertThat(registry.listCommands().get("foo3")).isNotNull();
}
@Test
public void testInteractionModeNonInteractive() {
DefaultShellContext shellContext = new DefaultShellContext();
shellContext.setInteractionMode(InteractionMode.NONINTERACTIVE);
registry = new ConfigurableCommandRegistry(shellContext);
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(InteractionModeCommands.class);
registrar.setApplicationContext(applicationContext);
registrar.register(registry);
assertThat(registry.listCommands().get("foo1")).isNull();
assertThat(registry.listCommands().get("foo2")).isNotNull();
assertThat(registry.listCommands().get("foo3")).isNotNull();
}
@ShellComponent
public static class InteractionModeCommands {
@ShellMethod(value = "foo1", interactionMode = InteractionMode.INTERACTIVE)
public void foo1() {
}
@ShellMethod(value = "foo2", interactionMode = InteractionMode.NONINTERACTIVE)
public void foo2() {
}
@ShellMethod(value = "foo3")
public void foo3() {
}
}
}

View File

@@ -29,6 +29,7 @@ import org.springframework.shell.CommandRegistry;
import org.springframework.shell.ConfigurableCommandRegistry;
import org.springframework.shell.MethodTarget;
import org.springframework.shell.ParameterResolver;
import org.springframework.shell.context.DefaultShellContext;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.shell.standard.StandardParameterResolver;
@@ -42,7 +43,7 @@ public class AbstractCompletionsTests {
@Test
public void testBasicModelGeneration() {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry();
ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry(new DefaultShellContext());
List<ParameterResolver> parameterResolvers = new ArrayList<>();
StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(),
Collections.emptySet());
@@ -91,7 +92,7 @@ public class AbstractCompletionsTests {
@Test
public void testBuilder() {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry();
ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry(new DefaultShellContext());
List<ParameterResolver> parameterResolvers = new ArrayList<>();
TestCompletions completions = new TestCompletions(resourceLoader, commandRegistry, parameterResolvers);

View File

@@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.shell.ConfigurableCommandRegistry;
import org.springframework.shell.ParameterResolver;
import org.springframework.shell.context.DefaultShellContext;
import static org.assertj.core.api.Assertions.assertThat;
@@ -48,7 +49,7 @@ public class BashCompletionsTests {
@Test
public void testDoesNotError() {
ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry();
ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry(new DefaultShellContext());
List<ParameterResolver> parameterResolvers = new ArrayList<>();
BashCompletions completions = new BashCompletions(context, commandRegistry, parameterResolvers);
String bash = completions.generate("root-command");

View File

@@ -12,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.shell.ConfigurableCommandRegistry;
import org.springframework.shell.MethodTarget;
import org.springframework.shell.context.DefaultShellContext;
import org.springframework.shell.standard.StandardMethodTargetRegistrar;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -32,7 +33,7 @@ public class CalculatorCommandsTest extends BaseCalculatorTest {
private static final Class<CalculatorCommands> COMMAND_CLASS_UNDER_TEST = CalculatorCommands.class;
private final ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry();
private final ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(new DefaultShellContext());
@Autowired
private CalculatorState state;