Skip to content

Commit

Permalink
Replace AnyOsFileValueProvider by ExtendedFileValueProvider and add s…
Browse files Browse the repository at this point in the history
…upport to not add space while proposing directory
  • Loading branch information
fonimus committed Sep 4, 2020
1 parent 9edb197 commit 57cfe78
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 23 deletions.
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ ssh:
authorized-roles:
- ADMIN
display-banner: true
# to use AnyOsFileValueProvider instead of spring shell FileValueProvider for all File option parameters
# if set to false, it still can be used via '@ShellOption(valueProvider = AnyOsFileValueProvider.class) File file'
any-os-file-provider: true
# to use ExtendedFileValueProviderTest instead of spring shell FileValueProvider for all File option parameters
# if set to false, it still can be used via '@ShellOption(valueProvider = ExtendedFileValueProviderTest.class) File file'
extended-file-provider: true
history-file: <java.io.tmpdir>/sshShellHistory.log
# since 1.3.0, set to false to have one file per user (<history-directory>/sshShellHistory-<user>.log)
shared-history: true
Expand Down Expand Up @@ -284,7 +284,8 @@ Enumeration option parameters have auto completion by default.

### File

Thanks to [AnyOsFileValueProvider.java](./starter/src/main/java/com/github/fonimus/ssh/shell/providers/AnyOsFileValueProvider.java)
Thanks to [ExtendedFileValueProvider.java](./starter/src/main/java/com/github/fonimus/ssh/shell/providers
/ExtendedFileValueProvider.java)
(or FileValueProvider is deactivated), auto completion is available
for `java.io.File` option parameters.

Expand Down Expand Up @@ -653,6 +654,13 @@ public class ApplicationTest {}

## Release notes

### 1.4.1

* AnyOsFileValueProvider replaced by [ExtendedFileValueProvider.java](./starter/src/main/java/com/github/fonimus/ssh/shell/providers/ExtendedFileValueProvider.java)
* Do not add space after directory proposal (allows following directories directly)
* Supports Windows OS in addition to Unix
* Can be deactivated by `ssh.shell.extendedfile-provider`

### 1.4.0

* Bump to spring boot 2.3.3.RELEASE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import com.github.fonimus.ssh.shell.commands.SshShellComponent;
import com.github.fonimus.ssh.shell.interactive.Interactive;
import com.github.fonimus.ssh.shell.interactive.KeyBinding;
import com.github.fonimus.ssh.shell.providers.AnyOsFileValueProvider;
import com.github.fonimus.ssh.shell.providers.ExtendedFileValueProvider;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.session.ServerSession;
import org.jline.terminal.Size;
Expand Down Expand Up @@ -96,16 +96,16 @@ public void progress(int progress) {
/**
* File provider command example
*
* @param file file to get info from
* @param anyOs any os file to get info from
* @param file file to get info from
* @param extended extended provider file to get info from
*/
@ShellMethod("File command")
public void file(
@ShellOption(defaultValue = ShellOption.NULL) File file,
@ShellOption(valueProvider = AnyOsFileValueProvider.class, defaultValue = ShellOption.NULL) File anyOs) {
@ShellOption(valueProvider = ExtendedFileValueProvider.class, defaultValue = ShellOption.NULL) File extended) {

info(file);
info(anyOs);
info(extended);
}

private void info(File file) {
Expand Down
2 changes: 1 addition & 1 deletion samples/complete/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ ssh:
authentication: security
auth-provider-bean-name: customAuthManager
authorized-public-keys-file: samples/public-keys-sample
any-os-file-provider: false
extended-file-provider: false
commands:
actuator:
excludes:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright (c) 2020 François Onimus
*
* 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
*
* http://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 com.github.fonimus.ssh.shell;

import org.jline.reader.Candidate;
import org.jline.reader.LineReader;
import org.jline.reader.ParsedLine;
import org.springframework.shell.CompletingParsedLine;
import org.springframework.shell.CompletionContext;
import org.springframework.shell.CompletionProposal;
import org.springframework.shell.Shell;
import org.springframework.shell.jline.JLineShellAutoConfiguration;

import java.util.List;
import java.util.stream.Collectors;

/**
* Extended completer adapter to be able to set complete attribute of proposal
* <br>
* Based on @{@link JLineShellAutoConfiguration.CompleterAdapter}
*/
public class ExtendedCompleterAdapter extends JLineShellAutoConfiguration.CompleterAdapter {

private final Shell shell;

public ExtendedCompleterAdapter(Shell shell) {
this.shell = shell;
}

@Override
public void complete(LineReader reader, ParsedLine line, List<Candidate> candidates) {
CompletingParsedLine cpl = (line instanceof CompletingParsedLine) ? ((CompletingParsedLine) line) : t -> t;
CompletionContext context = new CompletionContext(sanitizeInput(line.words()), line.wordIndex(),
line.wordCursor());

shell.complete(context).stream().map(p -> new Candidate(
p.dontQuote() ? p.value() : cpl.emit(p.value()).toString(),
p.displayText(),
p.category(),
p.description(),
null,
null,
isComplete(p)
)).forEach(candidates::add);
}

private static boolean isComplete(CompletionProposal p) {
if (p instanceof ExtendedCompletionProposal) {
return ((ExtendedCompletionProposal) p).isComplete();
}
return true;
}

/**
* Sanitize the buffer input given the customizations applied to the JLine parser (<em>e.g.</em> support for
* line continuations, <em>etc.</em>)
* See @{@link org.springframework.shell.jline.JLineShellAutoConfiguration}
*/
static List<String> sanitizeInput(List<String> words) {
words = words.stream()
// CR at beginning/end of line introduced by backslash
.map(s -> s.replaceAll("^\\n+|\\n+$", ""))
// CR in middle of word introduced by return inside a quoted string
.map(s -> s.replaceAll("\\n+", " "))
.collect(Collectors.toList());
return words;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2020 François Onimus
*
* 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
*
* http://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 com.github.fonimus.ssh.shell;

import lombok.Data;
import org.springframework.shell.CompletionProposal;

/**
* Extended completion proposal to be able to set complete attribute of proposal
*/
@Data
public class ExtendedCompletionProposal extends CompletionProposal {

/**
* If should add space after proposed proposal
*/
private boolean complete;

/**
* Default constructor
*
* @param value string value
* @param complete true if should add space after proposed proposal (true is default value when not using
* extended completion proposal)
*/
public ExtendedCompletionProposal(String value, boolean complete) {
super(value);
this.complete = complete;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import com.github.fonimus.ssh.shell.postprocess.PostProcessor;
import com.github.fonimus.ssh.shell.postprocess.TypePostProcessorResultHandler;
import com.github.fonimus.ssh.shell.postprocess.provided.*;
import com.github.fonimus.ssh.shell.providers.AnyOsFileValueProvider;
import com.github.fonimus.ssh.shell.providers.ExtendedFileValueProvider;
import lombok.extern.slf4j.Slf4j;
import org.apache.sshd.server.SshServer;
import org.jline.reader.LineReader;
Expand Down Expand Up @@ -138,11 +138,17 @@ public Shell sshShell(@Qualifier("main") ResultHandler<Object> resultHandler, Li
return new ExtendedShell(new TypePostProcessorResultHandler(resultHandler, postProcessors), postProcessors);
}

@Bean
@Primary
public ExtendedCompleterAdapter sshCompleter(Shell shell) {
return new ExtendedCompleterAdapter(shell);
}

// value providers

@Bean
public ValueProvider anyOsFileValueProvider() {
return new AnyOsFileValueProvider(properties.isAnyOsFileProvider());
public ValueProvider extendedFileValueProvider() {
return new ExtendedFileValueProvider(properties.isExtendedFileProvider());
}

// post processors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public class SshShellProperties {

private boolean displayBanner = true;

private boolean anyOsFileProvider = true;
private boolean extendedFileProvider = true;

private AuthenticationType authentication = AuthenticationType.simple;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.github.fonimus.ssh.shell.providers;

import com.github.fonimus.ssh.shell.ExtendedCompletionProposal;
import org.springframework.core.MethodParameter;
import org.springframework.shell.CompletionContext;
import org.springframework.shell.CompletionProposal;
Expand All @@ -28,13 +29,13 @@
import java.util.stream.Collectors;

/**
* Fixed file value provider (mostly for windows)
* Fixed file value provider (mostly for windows) and allow to not put space after proposal when directory
*/
public class AnyOsFileValueProvider extends ValueProviderSupport {
public class ExtendedFileValueProvider extends ValueProviderSupport {

private boolean replaceAll;

public AnyOsFileValueProvider(boolean replaceAll) {
public ExtendedFileValueProvider(boolean replaceAll) {
this.replaceAll = replaceAll;
}

Expand All @@ -59,7 +60,12 @@ public List<CompletionProposal> complete(MethodParameter parameter, CompletionCo
return Collections.emptyList();
}
return Arrays.stream(files)
.map(f -> new CompletionProposal(f.getPath().replaceAll("\\\\", "/")))
.map(f -> new ExtendedCompletionProposal(path(f), f.isFile()))
.collect(Collectors.toList());
}

private static String path(File f) {
String path = f.getPath().replaceAll("\\\\", "/");
return f.isDirectory() ? path + "/" : path;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@

import static org.junit.jupiter.api.Assertions.*;

class AnyOsFileValueProviderTest {
class ExtendedFileValueProviderTest {

@Test
void complete() {
AnyOsFileValueProvider provider = new AnyOsFileValueProvider(true);
ExtendedFileValueProvider provider = new ExtendedFileValueProvider(true);
List<CompletionProposal> result = provider.complete(null,
new CompletionContext(Arrays.asList("--file", "src"), 1, 3), null);
assertNotEquals(0, result.size());
Expand All @@ -44,7 +44,7 @@ void complete() {

@Test
void testSupport() throws Exception {
AnyOsFileValueProvider providerForAll = new AnyOsFileValueProvider(true);
ExtendedFileValueProvider providerForAll = new ExtendedFileValueProvider(true);

Method method = TestCommand.class.getDeclaredMethod("test", File.class, File.class, String.class);

Expand All @@ -56,15 +56,15 @@ void testSupport() throws Exception {
assertTrue(providerForAll.supports(paramWithoutProvider, null));
assertFalse(providerForAll.supports(paramNotFile, null));

AnyOsFileValueProvider providerForDeclaredOnly = new AnyOsFileValueProvider(false);
ExtendedFileValueProvider providerForDeclaredOnly = new ExtendedFileValueProvider(false);
assertTrue(providerForDeclaredOnly.supports(paramFileWithProvider, null));
assertFalse(providerForDeclaredOnly.supports(paramWithoutProvider, null));
assertFalse(providerForDeclaredOnly.supports(paramNotFile, null));
}

private static class TestCommand {

private void test(@ShellOption(valueProvider = AnyOsFileValueProvider.class) File file, File otherFile,
private void test(@ShellOption(valueProvider = ExtendedFileValueProvider.class) File file, File otherFile,
String notAFile) {

}
Expand Down

0 comments on commit 57cfe78

Please sign in to comment.