diff --git a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/CompleterAutoConfiguration.java b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/CompleterAutoConfiguration.java index 433ddb5b3..5b1a75365 100644 --- a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/CompleterAutoConfiguration.java +++ b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/CompleterAutoConfiguration.java @@ -63,7 +63,7 @@ public void complete(LineReader reader, ParsedLine line, List candida p.description(), null, null, - true) + p.complete()) ) .forEach(candidates::add); } diff --git a/spring-shell-core/src/main/java/org/springframework/shell/CompletionProposal.java b/spring-shell-core/src/main/java/org/springframework/shell/CompletionProposal.java index 449350800..6a13c0b91 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/CompletionProposal.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/CompletionProposal.java @@ -49,6 +49,12 @@ public class CompletionProposal { */ private boolean dontQuote = false; + /** + * Whether the proposal cant be completed further. By setting complete to false then it will not append an space + * making it easier to continue tab completion + */ + private boolean complete = true; + public CompletionProposal(String value) { this.value = this.displayText = value; } @@ -89,6 +95,16 @@ public CompletionProposal category(String category) { return this; } + public CompletionProposal complete(boolean complete) { + this.complete = complete; + return this; + } + + public boolean complete() { + return complete; + } + + public CompletionProposal dontQuote(boolean dontQuote) { this.dontQuote = dontQuote; return this; diff --git a/spring-shell-standard/src/main/java/org/springframework/shell/standard/FileValueProvider.java b/spring-shell-standard/src/main/java/org/springframework/shell/standard/FileValueProvider.java index 0f7093412..b1eb1d4b5 100644 --- a/spring-shell-standard/src/main/java/org/springframework/shell/standard/FileValueProvider.java +++ b/spring-shell-standard/src/main/java/org/springframework/shell/standard/FileValueProvider.java @@ -16,6 +16,9 @@ package org.springframework.shell.standard; +import org.springframework.shell.CompletionContext; +import org.springframework.shell.CompletionProposal; + import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; @@ -25,9 +28,6 @@ import java.util.List; import java.util.stream.Collectors; -import org.springframework.shell.CompletionContext; -import org.springframework.shell.CompletionProposal; - import static java.nio.file.FileVisitOption.FOLLOW_LINKS; /** @@ -39,21 +39,25 @@ */ public class FileValueProvider implements ValueProvider { - @Override - public List complete(CompletionContext completionContext) { + @Override + public List complete(CompletionContext completionContext) { String input = completionContext.currentWordUpToCursor(); int lastSlash = input.lastIndexOf(File.separatorChar); - Path dir = lastSlash > -1 ? Paths.get(input.substring(0, lastSlash+1)) : Paths.get(""); - String prefix = input.substring(lastSlash + 1, input.length()); + Path dir = lastSlash > -1 ? Paths.get(input.substring(0, lastSlash + 1)) : Paths.get(""); + String prefix = input.substring(lastSlash + 1); try { return Files - .find(dir, 1, (p, a) -> p.getFileName() != null && p.getFileName().toString().startsWith(prefix), - FOLLOW_LINKS) - .map(p -> new CompletionProposal(p.toString())) - .collect(Collectors.toList()); + .find(dir, 1, (p, a) -> p.getFileName() != null && p.getFileName().toString().startsWith(prefix), + FOLLOW_LINKS) + .map(p -> { + boolean directory = Files.isDirectory(p); + String value = p.toString() + (directory ? File.separatorChar : ""); + return new CompletionProposal(value).complete(!directory); + }) + .collect(Collectors.toList()); } catch (IOException e) { throw new UncheckedIOException(e); } - } + } }