select from a small amount of options in spring shell

∥☆過路亽.° 提交于 2020-01-11 11:51:35

问题


I am currently trying to make an cli app with Spring Shell.

I want the user to able to fast pick one of 2-3 options. My current code works fine in eclipse but when I start it in Powershell, i have to hit enter multiple times (at least 3 times) in order to select the option. After pressing enter once, nothing happens.

My current Method:

@ShellMethod(key = { "setService", "select" }, value = "Choose a Speech to Text Service")

public void setService() {
    boolean success = false;
    do {
        this.console.write("Please select a speech recognition service. Type in the number and press enter:");
        this.console.write("1. Google Speech API");
        this.console.write("2. Bing Speech API");

        // Get Input
        Scanner s = new Scanner(System.in);
        String input = s.nextLine();

        // Select Service
        switch (input) {
        case "1":
            /*
             * do something...
             */
            console.write("Google Speech API selected");
            success = true;
            break;
        case "2":
            /*
             * do something...
             */
            console.write("Bing Speech API selected");
            success = true;
            break;
        default:
            this.console.error("Input not valid. Please type a number and press Enter to select a Service");
            break;
        }
    } while (!success);
}

How can i fix the issue with powershell or is there a more elegant way to perform this input?


回答1:


Facing a similar requirement, and not finding an example of such a thing anywhere in the JLine 3 or Spring-Shell 2 docs, I did some experimenting...

Basically -- these components are not designed to work that way. Trying to get additional user input while already in the context of a @ShellMethod is simply not a supported feature.

Any IO activity that you might do using System.out or System.in ends up being handled differently "under the hood" by JLine depending on what type of shell you first started from (hence your observation that your attempt behaves differently when the app is invoked from PowerShell vs being launched from within the IDE). I was able to get the desired behavior if I launched from GitBash on Windows, but not if I launched from CMD. In one case I just ended up crashing the application completely.

However, I did find one way to prompt for input that worked for both GitBash and CMD. This example will not work in plain JLine, but appears to work in Spring-Shell 2.0 as long as you are using Spring Boot autoconfiguration (which gives you access to the same 'LineReader' instance that Spring initialized for your shell application).

@Autowired
LineReader reader;

public String ask(String question) {
    return this.reader.readLine("\n" + question + " > ");
}

@ShellMethod(key = { "setService", "select" }, value = "Choose a Speech to Text Service")
public void setService() {
    boolean success = false;
    do {
        String question = "Please select a speech recognition service. Type in the number and press enter:"
        + "\n 1. Google Speech API"
        + "\n 2. Bing Speech API";

        // Get Input
        String input = this.ask(question);

        // Select Service
        switch (input) {
        case "1":
            /*
             * do something...
             */
            console.write("Google Speech API selected");
            success = true;
            break;
        case "2":
            /*
             * do something...
             */
            console.write("Bing Speech API selected");
            success = true;
            break;
        default:
            this.console.error("Input not valid. Please type a number and press Enter to select a Service");
            break;
        }
    } while (!success);
}



回答2:


Spring Shell 2 is based on JLine 3. For just a "small amount of options" I would recommend to use a feature like tab completion.

(It works on Powershell, but I never get any tab-completion support in Eclipse-Console).

It is quite easy:

import java.util.List;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.impl.completer.StringsCompleter;
import org.jline.terminal.Terminal;
import org.springframework.context.annotation.Lazy;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class CompleterInteractionCommand {

    private final List<String> OPTIONS = List.of("create", "update", "delete");
    private final Terminal terminal;

    public CompleterInteractionCommand(@Lazy final Terminal terminal) {
        this.terminal = terminal;
    }

    @ShellMethod
    public String completeSelect() {
        LineReader lineReader = LineReaderBuilder.builder()
                .terminal(this.terminal)
                .completer(new StringsCompleter(this.OPTIONS))
                .build();
        /* Important: This allows completion on an empty buffer, rather than inserting a tab! */
        lineReader.unsetOpt(LineReader.Option.INSERT_TAB);

        String desription = "select on of this options: " + OPTIONS + "\n"
                          + " use TAB (twice) to select them\n";        
        String input = lineReader.readLine(desription + "input: ").trim();
        return "you selected \"" + input + "\"";
    }
}



回答3:


I use TextIO for this to do something similar. More specifically, I use the read method on [IntInputReader][2] which is handy as according to its docs:

Reads a value of type T. It repeatedly prompts the users to enter the value, until they provide a valid input string.

As I've needed this functionality many times in my Spring Shell application (for users to select from different types of objects), I wrote a generic method which

Presents the user with a list of items of a given type {@code T} and prompts them to select one or more. Optionally, once selection is complete, the user is presented with a summary of their selection for their confirmation.

Unfortunately StackOverflow is playing havoc with my Javadoc, so I've created a Gist with the full documented method.

  public <T> List<T> cliObjectSelector(List<T> items, boolean allowMultiple, 
  boolean requireConfirmation, @Nullable String customPromptMessage,
  Function<T, String> f) throws OperationCancelledException {

if(items == null || items.isEmpty()) {
  textIO.getTextTerminal().print("No items found.  Cannot continue");
  throw new OperationCancelledException("The provided list of items is empty");
}

String userPrompt = (customPromptMessage != null) ? customPromptMessage + ":\n" : 
  String.format("Please select one of the %d items from the list:\n", items.size());

List<String> optionsList = items.stream()
    .map(item -> {
      return String.format("[%d] - %s", items.indexOf(item), f.apply(item));
    }).collect(Collectors.toList());

List<T> selectedItems = new ArrayList<>();

optionsList.add(0, userPrompt);

while(true) {
  textIO.getTextTerminal().println(optionsList);

  T selected = items.get(
      textIO.newIntInputReader()
      .withMinVal(0)
      .withMaxVal(items.size() - 1)
      .read("Option number"));

  selectedItems.add(selected);

  if(allowMultiple == false) {
    break;
  }

  if( ! askYesNo("Select another item?")) {
    break;
  }
}

if(requireConfirmation == false) {
  return selectedItems;
}

if(confirmSelection(selectedItems.stream()
    .map(item -> f.apply(item))
    .collect(Collectors.toList()))) {
  return selectedItems;
} else {
  throw new OperationCancelledException();
}
}

Sample usage:

  @ShellMethod(
  key = "select-something",
  value = "Select something")
public void select() {
    Instance selected = cliObjectSelector(instances, false, requireConfirmation,
        "Please select one of the " + instances.size() + " instances from the list",
        instance -> instance.getX().getName()).get(0);
  }


来源:https://stackoverflow.com/questions/49770449/select-from-a-small-amount-of-options-in-spring-shell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!