-
Notifications
You must be signed in to change notification settings - Fork 394
Add support for multiple non-interactive commands #372
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,12 +33,12 @@ public class ThemingAutoConfiguration { | |
public ThemeRegistry themeRegistry(ObjectProvider<Theme> themes) { | ||
ThemeRegistry registry = new ThemeRegistry(); | ||
registry.register(Theme.of("default", ThemeSettings.themeSettings())); | ||
themes.orderedStream().forEachOrdered(theme -> registry.register(theme)); | ||
themes.orderedStream().forEachOrdered(registry::register); | ||
return registry; | ||
} | ||
|
||
@Bean | ||
public ThemeResolver themeResolver(ThemeRegistry themeRegistry, SpringShellProperties properties) { | ||
public ThemeResolver shellThemeResolver(ThemeRegistry themeRegistry, SpringShellProperties properties) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was clashing w/ the non-shell Spring MVC theme resolver bean in SB. |
||
return new ThemeResolver(themeRegistry, properties.getTheme().getName()); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,19 +15,24 @@ | |
*/ | ||
package org.springframework.shell.jline; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.function.Function; | ||
import java.util.stream.Collectors; | ||
|
||
import org.jline.reader.ParsedLine; | ||
import org.jline.reader.Parser; | ||
import org.jline.reader.impl.DefaultParser; | ||
|
||
import org.springframework.boot.ApplicationArguments; | ||
import org.springframework.core.annotation.Order; | ||
import org.springframework.shell.Input; | ||
import org.springframework.shell.InputProvider; | ||
import org.springframework.shell.Shell; | ||
import org.springframework.shell.ShellRunner; | ||
import org.springframework.shell.Utils; | ||
import org.springframework.shell.context.InteractionMode; | ||
import org.springframework.shell.context.ShellContext; | ||
import org.springframework.util.StringUtils; | ||
|
||
/** | ||
* A {@link ShellRunner} that executes commands without entering interactive shell mode. | ||
|
@@ -38,66 +43,106 @@ | |
* @author Janne Valkealahti | ||
* @author Chris Bono | ||
*/ | ||
@Order(InteractiveShellRunner.PRECEDENCE - 50) | ||
@Order(NonInteractiveShellRunner.PRECEDENCE) | ||
public class NonInteractiveShellRunner implements ShellRunner { | ||
|
||
/** | ||
* The precedence at which this runner is ordered by the DefaultApplicationRunner - which also controls | ||
* the order it is consulted on the ability to handle the current shell. | ||
*/ | ||
public static final int PRECEDENCE = InteractiveShellRunner.PRECEDENCE - 50; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exposed the order so that extenders can easily put other runners in front of/behind of a particular runner w/o knowing the core relation runner order relations |
||
|
||
private final Shell shell; | ||
|
||
private final ShellContext shellContext; | ||
|
||
private Function<ApplicationArguments, List<String>> argsToShellCommand = (args) -> Arrays.asList(args.getSourceArgs()); | ||
private Parser lineParser; | ||
|
||
private Function<ApplicationArguments, List<String>> commandsFromInputArgs; | ||
|
||
public NonInteractiveShellRunner(Shell shell, ShellContext shellContext) { | ||
this.shell = shell; | ||
this.shellContext = shellContext; | ||
this.lineParser = new DefaultParser(); | ||
this.commandsFromInputArgs = (args) -> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Before this change we took ApplicationArguments and created a single command out of it where each word in the command is one of the source args. With this change the default is to take the ApplicationArguments and create a single command STRING out of it. It then in turn gets broken into words via the single ParseLineInput/Provider. This allows extenders to pass in list of string commands such as:
|
||
Collections.singletonList(String.join(" ", args.getSourceArgs())); | ||
} | ||
|
||
/** | ||
* Sets the function that creates the command() to run from the input application arguments. | ||
* | ||
* @param commandsFromInputArgs function that takes input application arguments and creates zero or more commands | ||
* where each command is a string that specifies the command and options | ||
* (eg. 'history --file myHistory.txt') | ||
*/ | ||
public void setCommandsFromInputArgs(Function<ApplicationArguments, List<String>> commandsFromInputArgs) { | ||
this.commandsFromInputArgs = commandsFromInputArgs; | ||
} | ||
|
||
public void setArgsToShellCommand(Function<ApplicationArguments, List<String>> argsToShellCommand) { | ||
this.argsToShellCommand = argsToShellCommand; | ||
/** | ||
* Sets the line parser used to parse commands. | ||
* | ||
* @param lineParser the line parser used to parse commands | ||
*/ | ||
public void setLineParser(Parser lineParser) { | ||
this.lineParser = lineParser; | ||
} | ||
|
||
@Override | ||
public boolean canRun(ApplicationArguments args) { | ||
return !argsToShellCommand.apply(args).isEmpty(); | ||
return !commandsFromInputArgs.apply(args).isEmpty(); | ||
} | ||
|
||
@Override | ||
public void run(ApplicationArguments args) throws Exception { | ||
shellContext.setInteractionMode(InteractionMode.NONINTERACTIVE); | ||
List<String> commands = this.argsToShellCommand.apply(args); | ||
InputProvider inputProvider = new StringInputProvider(commands); | ||
List<String> commands = this.commandsFromInputArgs.apply(args); | ||
List<ParsedLine> parsedLines = commands.stream() | ||
.map(rawCommandLine -> lineParser.parse(rawCommandLine, rawCommandLine.length() + 1)) | ||
.collect(Collectors.toList()); | ||
MultiParsedLineInputProvider inputProvider = new MultiParsedLineInputProvider(parsedLines); | ||
shell.run(inputProvider); | ||
} | ||
|
||
private class StringInputProvider implements InputProvider { | ||
|
||
private final List<String> commands; | ||
/** | ||
* An {@link InputProvider} that returns an input for each entry in a list of {@link ParsedLine parsed lines}. | ||
*/ | ||
static class MultiParsedLineInputProvider implements InputProvider { | ||
|
||
private boolean done; | ||
private final List<ParsedLineInput> parsedLineInputs; | ||
private int inputIdx; | ||
|
||
StringInputProvider(List<String> commands) { | ||
this.commands = commands; | ||
MultiParsedLineInputProvider(List<ParsedLine> parsedLines) { | ||
this.parsedLineInputs = parsedLines.stream() | ||
.map(ParsedLineInput::new) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
@Override | ||
public Input readInput() { | ||
if (!done) { | ||
done = true; | ||
return new Input() { | ||
@Override | ||
public List<String> words() { | ||
return commands; | ||
} | ||
|
||
@Override | ||
public String rawText() { | ||
return StringUtils.collectionToDelimitedString(commands, " "); | ||
} | ||
}; | ||
} | ||
else { | ||
if (inputIdx == parsedLineInputs.size()) { | ||
return null; | ||
} | ||
return parsedLineInputs.get(inputIdx++); | ||
} | ||
|
||
private static class ParsedLineInput implements Input { | ||
|
||
private final ParsedLine parsedLine; | ||
|
||
ParsedLineInput(ParsedLine parsedLine) { | ||
this.parsedLine = parsedLine; | ||
} | ||
|
||
@Override | ||
public String rawText() { | ||
return parsedLine.line(); | ||
} | ||
|
||
@Override | ||
public List<String> words() { | ||
return Utils.sanitizeInput(parsedLine.words()); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[UNRELATED] Suggestion from IDEA (I agreed w/ it 😸 )