From b2f9c20512f5ce45793e13e2a1c012282893a6a7 Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Wed, 18 Dec 2024 23:45:26 +0100 Subject: [PATCH 1/3] Make sure ConsolePrompt cancellable is properly reset --- .../jline/consoleui/prompt/ConsolePrompt.java | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/console-ui/src/main/java/org/jline/consoleui/prompt/ConsolePrompt.java b/console-ui/src/main/java/org/jline/consoleui/prompt/ConsolePrompt.java index ad0c7cc9..0ebcf07f 100644 --- a/console-ui/src/main/java/org/jline/consoleui/prompt/ConsolePrompt.java +++ b/console-ui/src/main/java/org/jline/consoleui/prompt/ConsolePrompt.java @@ -179,39 +179,41 @@ public Map prompt( Deque> prevLists = new ArrayDeque<>(); Deque> prevResults = new ArrayDeque<>(); boolean cancellable = config.cancellableFirstPrompt(); - // Get our first list of prompts - List peList = promptableElementLists.apply(new HashMap<>()); - Map peResult = new HashMap<>(); - while (peList != null) { - // Second and later prompts should always be cancellable - config.setCancellableFirstPrompt(!prevLists.isEmpty() || cancellable); - // Prompt the user - prompt(headerIn, peList, peResult); - if (peResult.isEmpty()) { - // The prompt was cancelled by the user, so let's go back to the - // previous list of prompts and its results (if any) - peList = prevLists.pollFirst(); - peResult = prevResults.pollFirst(); - if (peResult != null) { - // Remove the results of the previous prompt from the main result map - peResult.forEach((k, v) -> resultMap.remove(k)); - headerIn.remove(headerIn.size() - 1); + try { + // Get our first list of prompts + List peList = promptableElementLists.apply(new HashMap<>()); + Map peResult = new HashMap<>(); + while (peList != null) { + // Second and later prompts should always be cancellable + config.setCancellableFirstPrompt(!prevLists.isEmpty() || cancellable); + // Prompt the user + prompt(headerIn, peList, peResult); + if (peResult.isEmpty()) { + // The prompt was cancelled by the user, so let's go back to the + // previous list of prompts and its results (if any) + peList = prevLists.pollFirst(); + peResult = prevResults.pollFirst(); + if (peResult != null) { + // Remove the results of the previous prompt from the main result map + peResult.forEach((k, v) -> resultMap.remove(k)); + headerIn.remove(headerIn.size() - 1); + } + } else { + // We remember the list of prompts and their results + prevLists.push(peList); + prevResults.push(peResult); + // Add the results to the main result map + resultMap.putAll(peResult); + // And we get our next list of prompts (if any) + peList = promptableElementLists.apply(resultMap); + peResult = new HashMap<>(); } - } else { - // We remember the list of prompts and their results - prevLists.push(peList); - prevResults.push(peResult); - // Add the results to the main result map - resultMap.putAll(peResult); - // And we get our next list of prompts (if any) - peList = promptableElementLists.apply(resultMap); - peResult = new HashMap<>(); } + return resultMap; + } finally { + // Restore the original state of cancellable + config.setCancellableFirstPrompt(cancellable); } - // Restore the original state of cancellable - config.setCancellableFirstPrompt(cancellable); - - return resultMap; } /** From 129477c4eb487c0e96db6c5a1d7776593c02d80e Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Wed, 18 Dec 2024 21:38:22 +0100 Subject: [PATCH 2/3] Adds text-only prompt element for console-ui Fixes #1136 --- .../org/jline/consoleui/elements/Text.java | 29 ++++++++ .../consoleui/prompt/AbstractPrompt.java | 5 +- .../jline/consoleui/prompt/ConsolePrompt.java | 52 ++++++++++---- .../org/jline/consoleui/prompt/NoResult.java | 29 ++++++++ .../prompt/builder/PromptBuilder.java | 4 ++ .../consoleui/prompt/builder/TextBuilder.java | 71 +++++++++++++++++++ .../consoleui/examples/BasicDynamic.java | 14 +++- 7 files changed, 185 insertions(+), 19 deletions(-) create mode 100644 console-ui/src/main/java/org/jline/consoleui/elements/Text.java create mode 100644 console-ui/src/main/java/org/jline/consoleui/prompt/NoResult.java create mode 100644 console-ui/src/main/java/org/jline/consoleui/prompt/builder/TextBuilder.java diff --git a/console-ui/src/main/java/org/jline/consoleui/elements/Text.java b/console-ui/src/main/java/org/jline/consoleui/elements/Text.java new file mode 100644 index 00000000..8eebee6e --- /dev/null +++ b/console-ui/src/main/java/org/jline/consoleui/elements/Text.java @@ -0,0 +1,29 @@ +/* + * 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.elements; + +import java.util.List; + +import org.jline.utils.AttributedString; + +public class Text extends AbstractPromptableElement { + private final List lines; + + private static int num = 0; + + public Text(List lines) { + // We don't actually care about names, so we just generate a unique one + super("", "_text_" + ++num); + this.lines = lines; + } + + public List getLines() { + return lines; + } +} diff --git a/console-ui/src/main/java/org/jline/consoleui/prompt/AbstractPrompt.java b/console-ui/src/main/java/org/jline/consoleui/prompt/AbstractPrompt.java index da9e4853..91d46d9c 100644 --- a/console-ui/src/main/java/org/jline/consoleui/prompt/AbstractPrompt.java +++ b/console-ui/src/main/java/org/jline/consoleui/prompt/AbstractPrompt.java @@ -15,10 +15,7 @@ import org.jline.consoleui.elements.ConfirmChoice; import org.jline.consoleui.elements.ExpandableChoice; import org.jline.consoleui.elements.InputValue; -import org.jline.consoleui.elements.items.CheckboxItemIF; -import org.jline.consoleui.elements.items.ChoiceItemIF; -import org.jline.consoleui.elements.items.ConsoleUIItemIF; -import org.jline.consoleui.elements.items.ListItemIF; +import org.jline.consoleui.elements.items.*; import org.jline.consoleui.elements.items.impl.CheckboxItem; import org.jline.consoleui.elements.items.impl.ChoiceItem; import org.jline.consoleui.elements.items.impl.Separator; diff --git a/console-ui/src/main/java/org/jline/consoleui/prompt/ConsolePrompt.java b/console-ui/src/main/java/org/jline/consoleui/prompt/ConsolePrompt.java index 0ebcf07f..dace16a1 100644 --- a/console-ui/src/main/java/org/jline/consoleui/prompt/ConsolePrompt.java +++ b/console-ui/src/main/java/org/jline/consoleui/prompt/ConsolePrompt.java @@ -252,16 +252,21 @@ public void prompt( } this.header = headerIn; + boolean backward = false; for (int i = resultMap.isEmpty() ? 0 : resultMap.size() - 1; i < promptableElementList.size(); i++) { PromptableElementIF pe = promptableElementList.get(i); - PromptResultItemIF result = promptElement(header, pe); + if (backward) { + removePreviousResult(pe); + backward = false; + } + PromptResultItemIF oldResult = resultMap.get(pe.getName()); + PromptResultItemIF result = promptElement(header, pe, oldResult); if (result == null) { // Prompt was cancelled by the user if (i > 0) { - // Remove last result - header.remove(header.size() - 1); // Go back to previous prompt i -= 2; + backward = true; continue; } else { if (config.cancellableFirstPrompt()) { @@ -274,22 +279,40 @@ public void prompt( } } } - String resp = result.getDisplayResult(); - 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"); + AttributedStringBuilder message; + if (pe instanceof Text) { + Text te = (Text) pe; + header.addAll(te.getLines()); + } else { + String resp = result.getDisplayResult(); + 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 = createMessage(pe.getMessage(), resp); + header.add(message.toAttributedString()); } - AttributedStringBuilder message = createMessage(pe.getMessage(), resp); - header.add(message.toAttributedString()); resultMap.put(pe.getName(), result); } } - protected PromptResultItemIF promptElement(List header, PromptableElementIF pe) { + private void removePreviousResult(PromptableElementIF pe) { + if (pe instanceof Text) { + Text te = (Text) pe; + for (int i = 0; i < te.getLines().size(); i++) { + header.remove(header.size() - 1); + } + } else { + header.remove(header.size() - 1); + } + } + + protected PromptResultItemIF promptElement( + List header, PromptableElementIF pe, PromptResultItemIF oldResult) { AttributedStringBuilder message = createMessage(pe.getMessage(), null); AttributedStringBuilder asb = new AttributedStringBuilder(); asb.append(message); @@ -354,6 +377,9 @@ protected PromptResultItemIF promptElement(List header, Prompt asb.append(" "); result = ConfirmPrompt.getPrompt(terminal, header, asb.toAttributedString(), cc, config) .execute(); + } else if (pe instanceof Text) { + Text te = (Text) pe; + result = oldResult == null ? NoResult.INSTANCE : null; } else { throw new IllegalArgumentException("wrong type of promptable element"); } diff --git a/console-ui/src/main/java/org/jline/consoleui/prompt/NoResult.java b/console-ui/src/main/java/org/jline/consoleui/prompt/NoResult.java new file mode 100644 index 00000000..cbcc323d --- /dev/null +++ b/console-ui/src/main/java/org/jline/consoleui/prompt/NoResult.java @@ -0,0 +1,29 @@ +/* + * 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; + +public class NoResult implements PromptResultItemIF { + + public static final NoResult INSTANCE = new NoResult(); + + private NoResult() {} + + public String getDisplayResult() { + return ""; + } + + public String getResult() { + return ""; + } + + @Override + public String toString() { + return "NoResult{}"; + } +} diff --git a/console-ui/src/main/java/org/jline/consoleui/prompt/builder/PromptBuilder.java b/console-ui/src/main/java/org/jline/consoleui/prompt/builder/PromptBuilder.java index 4d3b2e55..6178f850 100644 --- a/console-ui/src/main/java/org/jline/consoleui/prompt/builder/PromptBuilder.java +++ b/console-ui/src/main/java/org/jline/consoleui/prompt/builder/PromptBuilder.java @@ -46,4 +46,8 @@ public CheckboxPromptBuilder createCheckboxPrompt() { public ConfirmPromptBuilder createConfirmPromp() { return new ConfirmPromptBuilder(this); } + + public TextBuilder createText() { + return new TextBuilder(this); + } } diff --git a/console-ui/src/main/java/org/jline/consoleui/prompt/builder/TextBuilder.java b/console-ui/src/main/java/org/jline/consoleui/prompt/builder/TextBuilder.java new file mode 100644 index 00000000..a1ff63d9 --- /dev/null +++ b/console-ui/src/main/java/org/jline/consoleui/prompt/builder/TextBuilder.java @@ -0,0 +1,71 @@ +/* + * 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.builder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.jline.consoleui.elements.Text; +import org.jline.utils.AttributedString; +import org.jline.utils.AttributedStringBuilder; +import org.jline.utils.AttributedStyle; + +public class TextBuilder { + private final PromptBuilder promptBuilder; + private final List lines = new ArrayList<>(); + + public TextBuilder(PromptBuilder promptBuilder) { + this.promptBuilder = promptBuilder; + } + + public TextBuilder addLine(AttributedString text) { + lines.add(text); + return this; + } + + public TextBuilder addLine(String line) { + lines.add(new AttributedString(line)); + return this; + } + + public TextBuilder addLine(AttributedStyle style, String line) { + AttributedStringBuilder asb = new AttributedStringBuilder(); + asb.append(line, style); + lines.add(asb.toAttributedString()); + return this; + } + + public TextBuilder addLines(AttributedString... lines) { + this.lines.addAll(Arrays.asList(lines)); + return this; + } + + public TextBuilder addLines(String... lines) { + for (String s : lines) { + this.lines.add(new AttributedString(s)); + } + return this; + } + + public TextBuilder addLines(AttributedStyle style, String... lines) { + for (String s : lines) { + AttributedStringBuilder asb = new AttributedStringBuilder(); + asb.append(s, style); + this.lines.add(asb.toAttributedString()); + } + return this; + } + + public PromptBuilder addPrompt() { + Text text = new Text(lines); + promptBuilder.addPrompt(text); + return promptBuilder; + } +} diff --git a/console-ui/src/test/java/org/jline/consoleui/examples/BasicDynamic.java b/console-ui/src/test/java/org/jline/consoleui/examples/BasicDynamic.java index 5d82ffbd..2c5ee861 100644 --- a/console-ui/src/test/java/org/jline/consoleui/examples/BasicDynamic.java +++ b/console-ui/src/test/java/org/jline/consoleui/examples/BasicDynamic.java @@ -30,6 +30,9 @@ import org.jline.utils.OSUtils; public class BasicDynamic { + private static final AttributedStyle ITALIC_GREEN = + AttributedStyle.DEFAULT.italic().foreground(2); + private static final AttributedStyle BOLD_RED = AttributedStyle.BOLD.foreground(1); private static void addInHeader(List header, String text) { addInHeader(header, AttributedStyle.DEFAULT, text); @@ -43,8 +46,7 @@ private static void addInHeader(List header, AttributedStyle s public static void main(String[] args) { List header = new ArrayList<>(); - AttributedStyle style = new AttributedStyle(); - addInHeader(header, style.italic().foreground(2), "Hello Dynamic World!"); + addInHeader(header, ITALIC_GREEN, "Hello Dynamic World!"); addInHeader( header, "This is a demonstration of ConsoleUI java library. It provides a simple console interface"); addInHeader( @@ -141,6 +143,7 @@ static List pizzaOrHamburgerPrompt(ConsolePrompt prompt) { static List pizzaPrompt(ConsolePrompt prompt) { PromptBuilder promptBuilder = prompt.getPromptBuilder(); + promptBuilder.createText().addLine(ITALIC_GREEN, "Pizza time!").addPrompt(); promptBuilder .createListPrompt() .name("pizzatype") @@ -197,6 +200,7 @@ static List pizzaPrompt(ConsolePrompt prompt) { static List hamburgerPrompt(ConsolePrompt prompt) { PromptBuilder promptBuilder = prompt.getPromptBuilder(); + promptBuilder.createText().addLine(ITALIC_GREEN, "Hamburger time!").addPrompt(); promptBuilder .createListPrompt() .name("hamburgertype") @@ -241,6 +245,12 @@ static List hamburgerPrompt(ConsolePrompt prompt) { static List finalPrompt(ConsolePrompt prompt) { PromptBuilder promptBuilder = prompt.getPromptBuilder(); + promptBuilder + .createText() + .addLine(BOLD_RED, "###################") + .addLine(ITALIC_GREEN, "Finalize your order") + .addLine(BOLD_RED, "###################") + .addPrompt(); promptBuilder .createChoicePrompt() .name("payment") From 8be6f3eb604aa098a6122d802e74b396b6874c51 Mon Sep 17 00:00:00 2001 From: mattirn Date: Fri, 20 Dec 2024 10:32:37 +0100 Subject: [PATCH 3/3] fix merge --- .../src/main/java/org/jline/consoleui/prompt/ConsolePrompt.java | 1 + 1 file changed, 1 insertion(+) diff --git a/console-ui/src/main/java/org/jline/consoleui/prompt/ConsolePrompt.java b/console-ui/src/main/java/org/jline/consoleui/prompt/ConsolePrompt.java index d8014595..a790da1c 100644 --- a/console-ui/src/main/java/org/jline/consoleui/prompt/ConsolePrompt.java +++ b/console-ui/src/main/java/org/jline/consoleui/prompt/ConsolePrompt.java @@ -270,6 +270,7 @@ public void prompt( } else { // Repeat current prompt i -= 1; + } } } AttributedStringBuilder message;