All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jline.consoleui.prompt.ConsolePrompt Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2024, the original author(s).
 *
 * This software is distributable under the BSD license. See the terms of the
 * BSD license in the documentation provided with this software.
 *
 * https://opensource.org/licenses/BSD-3-Clause
 */
package org.jline.consoleui.prompt;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import org.jline.builtins.Styles;
import org.jline.consoleui.elements.*;
import org.jline.consoleui.elements.items.ConsoleUIItemIF;
import org.jline.consoleui.elements.items.impl.ChoiceItem;
import org.jline.consoleui.prompt.AbstractPrompt.*;
import org.jline.consoleui.prompt.builder.PromptBuilder;
import org.jline.reader.LineReader;
import org.jline.terminal.Attributes;
import org.jline.terminal.Terminal;
import org.jline.utils.*;

/**
 * ConsolePrompt encapsulates the prompting of a list of input questions for the user.
 */
public class ConsolePrompt {
    private final LineReader reader;
    private final Terminal terminal;
    private final UiConfig config;

    /**
     *
     * @param terminal the terminal.
     */
    public ConsolePrompt(Terminal terminal) {
        this(null, terminal, new UiConfig());
    }
    /**
     *
     * @param terminal the terminal.
     * @param config ConsolePrompt cursor pointer and checkbox configuration
     */
    public ConsolePrompt(Terminal terminal, UiConfig config) {
        this(null, terminal, config);
    }
    /**
     *
     * @param reader the lineReader.
     * @param terminal the terminal.
     * @param config ConsolePrompt cursor pointer and checkbox configuration
     */
    public ConsolePrompt(LineReader reader, Terminal terminal, UiConfig config) {
        this.terminal = terminal;
        this.config = config;
        this.reader = reader;
        if (reader != null) {
            Map options = new HashMap<>();
            for (LineReader.Option option : LineReader.Option.values()) {
                options.put(option, reader.isSet(option));
            }
            config.setReaderOptions(options);
        }
    }

    /**
     * Prompt a list of choices (questions). This method takes a list of promptable elements, typically
     * created with {@link PromptBuilder}. Each of the elements is processed and the user entries and
     * answers are filled in to the result map. The result map contains the key of each promptable element
     * and the user entry as an object implementing {@link PromptResultItemIF}.
     *
     * @param promptableElementList the list of questions / prompts to ask the user for.
     * @return a map containing a result for each element of promptableElementList
     * @throws IOException  may be thrown by terminal
     */
    public Map prompt(List promptableElementList) throws IOException {
        return prompt(new ArrayList<>(), promptableElementList);
    }
    /**
     * Prompt a list of choices (questions). This method takes a list of promptable elements, typically
     * created with {@link PromptBuilder}. Each of the elements is processed and the user entries and
     * answers are filled in to the result map. The result map contains the key of each promptable element
     * and the user entry as an object implementing {@link PromptResultItemIF}.
     *
     * @param header info to be displayed before first prompt.
     * @param promptableElementList the list of questions / prompts to ask the user for.
     * @return a map containing a result for each element of promptableElementList
     * @throws IOException  may be thrown by terminal
     */
    public Map prompt(
            List header, List promptableElementList) throws IOException {
        Attributes attributes = terminal.enterRawMode();
        try {
            terminal.puts(InfoCmp.Capability.enter_ca_mode);
            terminal.puts(InfoCmp.Capability.keypad_xmit);
            terminal.writer().flush();

            Map resultMap = new HashMap<>();

            for (PromptableElementIF pe : promptableElementList) {
                AttributedStringBuilder message = new AttributedStringBuilder();
                message.style(config.style(".pr")).append("? ");
                message.style(config.style(".me")).append(pe.getMessage()).append(" ");
                AttributedStringBuilder asb = new AttributedStringBuilder();
                asb.append(message);
                asb.style(AttributedStyle.DEFAULT);
                PromptResultItemIF result;
                if (pe instanceof ListChoice) {
                    ListChoice lc = (ListChoice) pe;
                    result = ListChoicePrompt.getPrompt(
                                    terminal, header, asb.toAttributedString(), lc.getListItemList(), config)
                            .execute();
                } else if (pe instanceof InputValue) {
                    InputValue ip = (InputValue) pe;
                    if (ip.getDefaultValue() != null) {
                        asb.append("(").append(ip.getDefaultValue()).append(") ");
                    }
                    result = InputValuePrompt.getPrompt(reader, terminal, header, asb.toAttributedString(), ip, config)
                            .execute();
                } else if (pe instanceof ExpandableChoice) {
                    ExpandableChoice ec = (ExpandableChoice) pe;
                    asb.append("(");
                    for (ConsoleUIItemIF item : ec.getChoiceItems()) {
                        if (item instanceof ChoiceItem) {
                            ChoiceItem ci = (ChoiceItem) item;
                            if (ci.isSelectable()) {
                                asb.append(ci.isDefaultChoice() ? Character.toUpperCase(ci.getKey()) : ci.getKey());
                            }
                        }
                    }
                    asb.append("h) ");
                    try {
                        result = ExpandableChoicePrompt.getPrompt(
                                        terminal, header, asb.toAttributedString(), ec, config)
                                .execute();
                    } catch (ExpandableChoiceException e) {
                        result = ListChoicePrompt.getPrompt(
                                        terminal, header, message.toAttributedString(), ec.getChoiceItems(), config)
                                .execute();
                    }
                } else if (pe instanceof Checkbox) {
                    Checkbox cb = (Checkbox) pe;
                    result = CheckboxPrompt.getPrompt(terminal, header, message.toAttributedString(), cb, config)
                            .execute();
                } else if (pe instanceof ConfirmChoice) {
                    ConfirmChoice cc = (ConfirmChoice) pe;
                    if (cc.getDefaultConfirmation() == null) {
                        asb.append(config.resourceBundle().getString("confirmation_without_default"));
                    } else if (cc.getDefaultConfirmation() == ConfirmChoice.ConfirmationValue.YES) {
                        asb.append(config.resourceBundle().getString("confirmation_yes_default"));
                    } else {
                        asb.append(config.resourceBundle().getString("confirmation_no_default"));
                    }
                    asb.append(" ");
                    result = ConfirmPrompt.getPrompt(terminal, header, asb.toAttributedString(), cc, config)
                            .execute();
                } else {
                    throw new IllegalArgumentException("wrong type of promptable element");
                }
                String resp = result.getResult();
                if (result instanceof ConfirmResult) {
                    ConfirmResult cr = (ConfirmResult) result;
                    if (cr.getConfirmed() == ConfirmChoice.ConfirmationValue.YES) {
                        resp = config.resourceBundle().getString("confirmation_yes_answer");
                    } else {
                        resp = config.resourceBundle().getString("confirmation_no_answer");
                    }
                }
                message.style(config.style(".an")).append(resp);
                header.add(message.toAttributedString());
                resultMap.put(pe.getName(), result);
            }
            return resultMap;
        } finally {
            terminal.setAttributes(attributes);
            terminal.puts(InfoCmp.Capability.exit_ca_mode);
            terminal.puts(InfoCmp.Capability.keypad_local);
            terminal.writer().flush();
            for (AttributedString as : header) {
                as.println(terminal);
            }
            terminal.writer().flush();
        }
    }

    /**
     * Creates a {@link PromptBuilder}.
     *
     * @return a new prompt builder object.
     */
    public PromptBuilder getPromptBuilder() {
        return new PromptBuilder();
    }

    /**
     * ConsoleUI configuration: colors, cursor pointer and selected/unselected/unavailable boxes.
     * ConsoleUI colors are configurable using UI_COLORS environment variable
     */
    public static class UiConfig {
        static final String DEFAULT_UI_COLORS = "cu=36:be=32:bd=37:pr=32:me=1:an=36:se=36:cb=100";
        static final String UI_COLORS = "UI_COLORS";
        private final AttributedString indicator;
        private final AttributedString uncheckedBox;
        private final AttributedString checkedBox;
        private final AttributedString unavailable;
        private final StyleResolver resolver;
        private final ResourceBundle resourceBundle;
        private Map readerOptions = new HashMap<>();

        public UiConfig() {
            this(null, null, null, null);
        }

        public UiConfig(String indicator, String uncheckedBox, String checkedBox, String unavailable) {
            String uc = System.getenv(UI_COLORS);
            String uiColors = uc != null && Styles.isStylePattern(uc) ? uc : DEFAULT_UI_COLORS;
            this.resolver = resolver(uiColors);
            this.indicator = toAttributedString(resolver, (indicator != null ? indicator : ">"), ".cu");
            this.uncheckedBox = toAttributedString(resolver, (uncheckedBox != null ? uncheckedBox : " "), ".be");
            this.checkedBox = toAttributedString(resolver, (checkedBox != null ? checkedBox : "x "), ".be");
            this.unavailable = toAttributedString(resolver, (unavailable != null ? unavailable : "- "), ".bd");
            this.resourceBundle = ResourceBundle.getBundle("consoleui_messages");
        }

        private static AttributedString toAttributedString(StyleResolver resolver, String string, String styleKey) {
            AttributedStringBuilder asb = new AttributedStringBuilder();
            asb.style(resolver.resolve(styleKey));
            asb.append(string);
            return asb.toAttributedString();
        }

        public AttributedString indicator() {
            return indicator;
        }

        public AttributedString uncheckedBox() {
            return uncheckedBox;
        }

        public AttributedString checkedBox() {
            return checkedBox;
        }

        public AttributedString unavailable() {
            return unavailable;
        }

        public AttributedStyle style(String key) {
            return resolver.resolve(key);
        }

        public ResourceBundle resourceBundle() {
            return resourceBundle;
        }

        protected void setReaderOptions(Map readerOptions) {
            this.readerOptions = readerOptions;
        }

        public Map readerOptions() {
            return readerOptions;
        }

        private static StyleResolver resolver(String style) {
            Map colors = Arrays.stream(style.split(":"))
                    .collect(Collectors.toMap(
                            s -> s.substring(0, s.indexOf('=')), s -> s.substring(s.indexOf('=') + 1)));
            return new StyleResolver(colors::get);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy