Skip to content
This repository was archived by the owner on May 12, 2025. It is now read-only.

Commit c7a3335

Browse files
authored
⬆️ Bump spring-shell-starter from 2.1.0 to 2.1.1 (#233)
1 parent 7e8f883 commit c7a3335

File tree

5 files changed

+16
-150
lines changed

5 files changed

+16
-150
lines changed

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,6 @@ ssh:
151151
authorized-roles:
152152
- ADMIN
153153
display-banner: true
154-
# to use ExtendedFileValueProviderTest instead of spring shell FileValueProvider for all File option parameters
155-
# if set to false, it still can be used via '@ShellOption(valueProvider = ExtendedFileValueProviderTest.class) File file'
156-
extended-file-provider: true
157154
history-file: <java.io.tmpdir>/sshShellHistory.log
158155
# since 1.3.0, set to false to have one file per user (<history-directory>/sshShellHistory-<user>.log)
159156
shared-history: true
@@ -310,8 +307,8 @@ It also provides a ``setTaskScheduler()`` in case you want to specify custom one
310307
| ts1**, ** | |
311308
| ts2** in context | Local single-threaded (could not find name ** |
312309
| taskScheduler**) | |
313-
| Multiple ``TaskScheduler`` beans named **
314-
taskScheduler**, ** | |
310+
| Multiple ``TaskScheduler`` beans named ** | |
311+
| taskScheduler**, ** | |
315312
| ts2**, ** | |
316313
| ts3** in context | ** |
317314
| taskScheduler** bean | |
@@ -798,6 +795,11 @@ public class ApplicationTest {
798795

799796
## Release notes
800797

798+
### 2.0.1
799+
800+
* Bump spring-boot from 2.7.2 to 2.7.3
801+
* Bump spring shell from 2.1.0 to 2.1.1
802+
801803
### 2.0.0
802804

803805
* Bump spring-boot from 2.5.6 to 2.7.2
@@ -951,9 +953,8 @@ autoconfiguration will still create its own built in commands that you can deact
951953
### 1.2.0
952954

953955
* Bump to spring boot 2.2.0.RELEASE
954-
* Audit and Http Trace actuator commands will be disabled by default, because endpoint will be by spring boot by
955-
default
956-
(
956+
* `Audit` and `Http Trace` actuator commands will be disabled by default, because endpoint will be by spring boot by
957+
default (
957958
check [spring boot migration 2.2](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.2-Release-Notes#actuator-http-trace-and-auditing-are-disabled-by-default)
958959
for more info)
959960
* Fix hanging terminal when unexpected runtime exception occurs
@@ -971,7 +972,7 @@ autoconfiguration will still create its own built in commands that you can deact
971972

972973
### 1.1.4
973974

974-
* [AnyOsFileValueProvider.java](./starter/src/main/java/com/github/fonimus/ssh/shell/providers/ExtendedFileValueProvider.java)
975+
* [ExtendedFileValueProvider.java](./starter/src/main/java/com/github/fonimus/ssh/shell/providers/ExtendedFileValueProvider.java)
975976
replaces `FileValueProvider` (spring shell default) by default
976977
* Support Windows OS in addition to Unix
977978
* Can be deactivated by `ssh.shell.any-os-file-provider`

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
5757
<!--dependencies-->
5858
<spring-boot.version>2.7.3</spring-boot.version>
59-
<spring-shell.version>2.1.0</spring-shell.version>
59+
<spring-shell.version>2.1.1</spring-shell.version>
6060
<sshd.version>2.9.0</sshd.version>
6161
<junit-jupiter.version>5.9.0</junit-jupiter.version>
6262
<!--sonar-->

samples/complete/src/main/java/com/github/fonimus/ssh/shell/complete/CompleteCommands.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ public void progress(int progress) {
198198
*/
199199
@ShellMethod("File command")
200200
public void file(
201-
@ShellOption(defaultValue = ShellOption.NULL) File file,
201+
@ShellOption(valueProvider = FileValueProvider.class, defaultValue = ShellOption.NULL) File file,
202202
@ShellOption(valueProvider = ExtendedFileValueProvider.class, defaultValue = ShellOption.NULL) File extended
203203
) {
204204
info(file);

starter/src/main/java/com/github/fonimus/ssh/shell/ExtendedShell.java

Lines changed: 1 addition & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,15 @@
2424
import org.springframework.context.annotation.Primary;
2525
import org.springframework.shell.*;
2626
import org.springframework.shell.command.CommandCatalog;
27-
import org.springframework.shell.command.CommandOption;
28-
import org.springframework.shell.command.CommandRegistration;
29-
import org.springframework.shell.completion.CompletionResolver;
3027
import org.springframework.shell.context.ShellContext;
3128
import org.springframework.shell.exit.ExitCodeMappings;
3229
import org.springframework.stereotype.Component;
33-
import org.springframework.util.StringUtils;
3430

3531
import java.util.ArrayList;
3632
import java.util.Collections;
3733
import java.util.List;
38-
import java.util.function.Function;
3934
import java.util.stream.Collectors;
4035
import java.util.stream.IntStream;
41-
import java.util.stream.Stream;
4236

4337
import static com.github.fonimus.ssh.shell.ExtendedInput.*;
4438
import static com.github.fonimus.ssh.shell.SshShellCommandFactory.SSH_THREAD_CONTEXT;
@@ -52,7 +46,6 @@
5246
public class ExtendedShell extends Shell {
5347

5448
private final ResultHandlerService resultHandlerService;
55-
private final CommandCatalog commandRegistry;
5649
private final List<String> postProcessorNames = new ArrayList<>();
5750

5851
/**
@@ -72,7 +65,6 @@ public ExtendedShell(
7265
) {
7366
super(resultHandlerService, commandRegistry, terminal, shellContext, exitCodeMappings);
7467
this.resultHandlerService = resultHandlerService;
75-
this.commandRegistry = commandRegistry;
7668
if (postProcessors != null) {
7769
this.postProcessorNames.addAll(postProcessors.stream().map(PostProcessor::getName).collect(Collectors.toList()));
7870
}
@@ -158,53 +150,7 @@ public List<CompletionProposal> complete(CompletionContext context) {
158150
if (context.getWords().contains("|")) {
159151
return postProcessorNames.stream().map(CompletionProposal::new).collect(Collectors.toList());
160152
}
161-
162-
String prefix = context.upToCursor();
163-
164-
List<CompletionProposal> candidates = new ArrayList<>(duplicatedCommandsStartingWith(prefix));
165-
166-
String best = duplicatedFindLongestCommand(prefix);
167-
if (best != null) {
168-
context = context.drop(best.split(" ").length);
169-
CommandRegistration registration = commandRegistry.getRegistrations().get(best);
170-
CompletionContext argsContext = context.commandRegistration(registration);
171-
172-
final List<String> words = context.getWords().stream().filter(StringUtils::hasText).collect(Collectors.toList());
173-
String lastNotEmptyWord = words.isEmpty() ? null : words.get(words.size() - 1);
174-
175-
List<CommandOption> matchedArgOptions = new ArrayList<>();
176-
if (lastNotEmptyWord != null) {
177-
// last word used instead of first to check if matching args
178-
matchedArgOptions.addAll(duplicatedMatchOptions(registration.getOptions(), lastNotEmptyWord));
179-
}
180-
if (matchedArgOptions.isEmpty()) {
181-
// only add command options if last word did not match option
182-
for (CompletionResolver resolver : completionResolvers) {
183-
List<CompletionProposal> resolved = resolver.apply(argsContext);
184-
candidates.addAll(resolved.stream().filter(cp -> !words.contains(cp.value())).collect(Collectors.toList()));
185-
}
186-
// try to check if previous word before last word is an option and last word is not empty
187-
String lastOption = words.isEmpty() || words.size() < 2 ? null : words.get(words.size() - 2);
188-
String lastWord = context.getWords().isEmpty() ? null : context.getWords().get(context.getWords().size() - 1);
189-
if (lastOption != null && StringUtils.hasText(lastWord)) {
190-
matchedArgOptions.addAll(duplicatedMatchOptions(registration.getOptions(), lastOption));
191-
}
192-
}
193-
194-
List<CompletionProposal> argProposals = matchedArgOptions.stream()
195-
.flatMap(o -> {
196-
Function<CompletionContext, List<CompletionProposal>> completion = o.getCompletion();
197-
if (completion != null) {
198-
List<CompletionProposal> apply = completion.apply(argsContext.commandOption(o));
199-
return apply.stream();
200-
}
201-
return Stream.empty();
202-
})
203-
.collect(Collectors.toList());
204-
205-
candidates.addAll(argProposals);
206-
}
207-
return candidates;
153+
return super.complete(context);
208154
}
209155

210156
private static boolean isKeyCharInList(List<String> strList) {
@@ -216,87 +162,6 @@ private static boolean isKeyCharInList(List<String> strList) {
216162
return false;
217163
}
218164

219-
//---------------------------------
220-
// Private methods from Shell
221-
//---------------------------------
222-
223-
private List<CommandOption> duplicatedMatchOptions(List<CommandOption> options, String arg) {
224-
List<CommandOption> matched = new ArrayList<>();
225-
String trimmed = StringUtils.trimLeadingCharacter(arg, '-');
226-
int count = arg.length() - trimmed.length();
227-
if (count == 1) {
228-
if (trimmed.length() == 1) {
229-
Character trimmedChar = trimmed.charAt(0);
230-
options.stream()
231-
.filter(o -> {
232-
for (Character sn : o.getShortNames()) {
233-
if (trimmedChar.equals(sn)) {
234-
return true;
235-
}
236-
}
237-
return false;
238-
})
239-
.findFirst()
240-
.ifPresent(matched::add);
241-
} else if (trimmed.length() > 1) {
242-
trimmed.chars().mapToObj(i -> (char) i)
243-
.forEach(c -> options.forEach(o -> {
244-
for (Character sn : o.getShortNames()) {
245-
if (c.equals(sn)) {
246-
matched.add(o);
247-
}
248-
}
249-
}));
250-
}
251-
} else if (count == 2) {
252-
options.stream()
253-
.filter(o -> {
254-
for (String ln : o.getLongNames()) {
255-
if (trimmed.equals(ln)) {
256-
return true;
257-
}
258-
}
259-
return false;
260-
})
261-
.findFirst()
262-
.ifPresent(matched::add);
263-
}
264-
return matched;
265-
}
266-
267-
private List<CompletionProposal> duplicatedCommandsStartingWith(String prefix) {
268-
// Workaround for https://github.com/spring-projects/spring-shell/issues/150
269-
// (sadly, this ties this class to JLine somehow)
270-
int lastWordStart = prefix.lastIndexOf(' ') + 1;
271-
return commandRegistry.getRegistrations().entrySet().stream()
272-
.filter(e -> e.getKey().startsWith(prefix))
273-
.map(e -> {
274-
String c = e.getKey();
275-
c = c.substring(lastWordStart);
276-
return duplicatedToCommandProposal(c, e.getValue());
277-
})
278-
.collect(Collectors.toList());
279-
}
280-
281-
private CompletionProposal duplicatedToCommandProposal(String command, CommandRegistration registration) {
282-
return new CompletionProposal(command)
283-
.dontQuote(true)
284-
.category("Available commands")
285-
.description(registration.getDescription());
286-
}
287-
288-
/**
289-
* Returns the longest command that can be matched as first word(s) in the given buffer.
290-
*
291-
* @return a valid command name, or {@literal null} if none matched
292-
*/
293-
private String duplicatedFindLongestCommand(String prefix) {
294-
String result = commandRegistry.getRegistrations().keySet().stream()
295-
.filter(command -> prefix.equals(command) || prefix.startsWith(command + " "))
296-
.reduce("", (c1, c2) -> c1.length() > c2.length() ? c1 : c2);
297-
return "".equals(result) ? null : result;
298-
}
299-
300165
/**
301166
* Shell notifier interface
302167
*/

starter/src/test/java/com/github/fonimus/ssh/shell/ExtendedShellTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,10 @@ private static List<Arguments> completeSource() {
136136
Arguments.of("cmd --a", Arrays.asList("--ab", "--ac")),
137137
Arguments.of("cmd ab", Arrays.asList("--ab", "--ac")),
138138
Arguments.of("cmd ac", Arrays.asList("--ab", "--ac")),
139-
// nothing to complete for ab option
140-
Arguments.of("cmd --ab ", Collections.emptyList()),
139+
// nothing to complete for ab option -> giving others options
140+
Arguments.of("cmd --ab ", Collections.singletonList("--ac")),
141141
// used complete adapter
142-
Arguments.of("cmd --ac ", Arrays.asList("cp1", "cp2")),
142+
Arguments.of("cmd --ac ", Arrays.asList("--ab", "cp1", "cp2")),
143143
// propose only not used options
144144
Arguments.of("cmd --ac cp1 ", Collections.singletonList("--ab")),
145145
// after pipe, complete with post processors

0 commit comments

Comments
 (0)