Skip to content
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

Dynamic console-ui prompts #1051

Closed
quintesse opened this issue Aug 5, 2024 · 4 comments
Closed

Dynamic console-ui prompts #1051

quintesse opened this issue Aug 5, 2024 · 4 comments
Milestone

Comments

@quintesse
Copy link
Contributor

quintesse commented Aug 5, 2024

Right now the type and contents of each item in a prompt has to be defined before executing the prompt. This makes the prompt a very static thing that can't react to a user's input. It would be nice if a prompt could be more dynamic and change depending on external factors (most likely user input).

A possible example could be a question that asks to select a country and then follows up with a question to select a city. The answer to the first question obviously affects the possible option in the second one.

But one could also imagine prompts where the number and type of questions changes completely.

For example when asking for a payment method: a) credit card b) bank account c) paypal and then depending on the answer the follow up questions would either be [credit card number, expiration date, cvv], [iban] or [email].

@quintesse
Copy link
Contributor Author

quintesse commented Aug 6, 2024

In itself the idea wouldn't be too hard to implement but the current console-ui wasn't designed with dynamic prompts in mind so it's not immediately a great fit for this idea.

An ideal pseudo-code solution that I could imagine would be something like:

ConsolePrompt prompt = new ConsolePrompt(terminal);
prompt.start(); // Sets up prompt
var countryListPrompt = promptBuilder.createListPrompt()
    .name("country")
    .newItem("germany").add()
    .newItem("italy").add()
    .newItem("spain").add()
    .build();
String country= prompt.prompt(countryListPrompt).getResult();
var cityListPrompt = getCitiesListPrompt(promptBuilder, country);
String city= prompt.prompt(cityListPrompt).getResult();
prompt.end(); // resets terminal back to normal etc

The start() / end() is a bit cumbersome, but it's just an example. One could imagine using a Closeable and use a try-resources block. Imagine something like:

try (ConsolePrompt prompt = ConsolePrompt.execute(terminal)) {
    var countryListPrompt = ... ;
    String country= prompt.prompt(countryListPrompt).getResult();
    var cityListPrompt = ... ;
    String city= prompt.prompt(cityListPrompt).getResult();
}

Edit: After thinking about it some more I've realized that the above idea might be nice but isn't compatible with the new feature where you can cancel out of questions to go back to the previous question. At least not without making the code a whole lot uglier. Need to think about this some more.

@quintesse
Copy link
Contributor Author

NB: One issue that affects any kind of solution to this is that the various builder classes aren't clean builders, they not only build but also add the result to the PromptBuilder, which makes reusing them to build single items harder. So a first step would be to add a build() method (addPrompt() would then simply call that and add its result to the prompt builder).

@quintesse
Copy link
Contributor Author

quintesse commented Aug 7, 2024

One option would be that instead of passing a list of prompt elements to prompt() we'd pass a "provider", a function that returns the next prompt element to display. And to be able to make decisions depending on previous user input we'd pass the current state of the result map as an argument to that function. Imagine this new prompt() like this:

public Map<String, PromptResultItemIF> prompt(
        List<AttributedString> header,
        Function<Map<String, PromptResultItemIF>, PromptableElementIF> promptableElementProvider
) throws IOException {
    . . .
}

Usage would not be as nice as the first example I gave, but would look more like this:

void myShowPrompt(Terminal terminal) {
    ConsolePrompt prompt = new ConsolePrompt(terminal);
    var result = prompt.prompt(this::nextQuestion);
    // Do something with result here
}

PromptableElementIF nextQuestion(Map<String, PromptResultItemIF> results) {
    if ((!results.containsKey("country")) {
        // First question
        return ListPromptBuilder.create()
            .name("country")
            .newItem("germany").add()
            .newItem("italy").add()
            .newItem("spain").add()
            .build();
    } else if (!results.containsKey("city")) {
        // Second question
        String country = results.get("country").getResult();
        return getCitiesListPrompt(promptBuilder, country);
    } else if ( ... ) {
        // Any other questions
    }
    return null; // No further questions
}

@mattirn mattirn added this to the 3.27.2 milestone Nov 27, 2024
mattirn added a commit to mattirn/jline3 that referenced this issue Nov 29, 2024
gnodet pushed a commit to gnodet/jline3 that referenced this issue Dec 10, 2024
# Conflicts:
#	console-ui/src/main/java/org/jline/consoleui/prompt/ConsolePrompt.java
@gnodet gnodet closed this as completed in 257314a Dec 10, 2024
@quintesse
Copy link
Contributor Author

Ok, this is creepy, this is so close to what I had locally that I had to check three times to make sure I never actually pushed my code somewhere 🤣
But great, love this solution! 😉

quintesse added a commit to quintesse/jline3 that referenced this issue Dec 12, 2024
This adds an easier way to create complex dynamic prompts
quintesse added a commit to quintesse/jline3 that referenced this issue Dec 12, 2024
This adds an easier way to create complex dynamic prompts
quintesse added a commit to quintesse/jline3 that referenced this issue Dec 12, 2024
This adds an easier way to create complex dynamic prompts
quintesse added a commit to quintesse/jline3 that referenced this issue Dec 12, 2024
This adds an easier way to create complex dynamic prompts
mattirn pushed a commit that referenced this issue Dec 18, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
This adds an easier way to create complex dynamic prompts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants