Skip to content

Commit

Permalink
[consoleui] Make it easier to extend ConsolePrompt (#1068)
Browse files Browse the repository at this point in the history
* Made `ConsolePrompt` easier to subclass

Being able to subclass `ConsolePrompt` and have protected access to its
implementation would make it easier for devs to extends its functionality.

* Made it easier to add support for new prompt elements to `ConsolePrompt`
  • Loading branch information
quintesse authored Sep 15, 2024
1 parent 3d4efe8 commit 8b63743
Showing 1 changed file with 89 additions and 76 deletions.
165 changes: 89 additions & 76 deletions console-ui/src/main/java/org/jline/consoleui/prompt/ConsolePrompt.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
* 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;
protected final LineReader reader;
protected final Terminal terminal;
protected final UiConfig config;

/**
*
Expand Down Expand Up @@ -78,6 +78,7 @@ public ConsolePrompt(LineReader reader, Terminal terminal, UiConfig config) {
public Map<String, PromptResultItemIF> prompt(List<PromptableElementIF> 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
Expand All @@ -102,76 +103,7 @@ public Map<String, PromptResultItemIF> prompt(

for (int i = 0; i < promptableElementList.size(); i++) {
PromptableElementIF pe = promptableElementList.get(i);
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(),
computePageSize(terminal, lc.getPageSize(), lc.getPageSizeType()),
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(), 10, config)
.execute();
}
} else if (pe instanceof Checkbox) {
Checkbox cb = (Checkbox) pe;
result = CheckboxPrompt.getPrompt(
terminal,
header,
message.toAttributedString(),
cb.getCheckboxItemList(),
computePageSize(terminal, cb.getPageSize(), cb.getPageSizeType()),
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");
}
PromptResultItemIF result = promptElement(header, pe);
if (result == null) {
// Prompt was cancelled by the user
if (i > 0) {
Expand Down Expand Up @@ -200,7 +132,7 @@ public Map<String, PromptResultItemIF> prompt(
resp = config.resourceBundle().getString("confirmation_no_answer");
}
}
message.style(config.style(".an")).append(resp);
AttributedStringBuilder message = createMessage(pe.getMessage(), resp);
header.add(message.toAttributedString());
resultMap.put(pe.getName(), result);
}
Expand All @@ -219,7 +151,88 @@ public Map<String, PromptResultItemIF> prompt(
}
}

private int computePageSize(Terminal terminal, int pageSize, PageSizeType sizeType) {
protected PromptResultItemIF promptElement(List<AttributedString> header, PromptableElementIF pe) {
AttributedStringBuilder message = createMessage(pe.getMessage(), null);
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(),
computePageSize(terminal, lc.getPageSize(), lc.getPageSizeType()),
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(), 10, config)
.execute();
}
} else if (pe instanceof Checkbox) {
Checkbox cb = (Checkbox) pe;
result = CheckboxPrompt.getPrompt(
terminal,
header,
message.toAttributedString(),
cb.getCheckboxItemList(),
computePageSize(terminal, cb.getPageSize(), cb.getPageSizeType()),
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");
}
return result;
}

protected AttributedStringBuilder createMessage(String message, String response) {
AttributedStringBuilder asb = new AttributedStringBuilder();
asb.style(config.style(".pr")).append("? ");
asb.style(config.style(".me")).append(message).append(" ");
if (response != null) {
asb.style(config.style(".an")).append(response);
}
return asb;
}

public static int computePageSize(Terminal terminal, int pageSize, PageSizeType sizeType) {
int rows = terminal.getHeight();
return sizeType == PageSizeType.ABSOLUTE ? Math.min(rows, pageSize) : (rows * pageSize) / 100;
}
Expand Down Expand Up @@ -311,7 +324,7 @@ public Map<LineReader.Option, Boolean> readerOptions() {
return readerOptions;
}

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

0 comments on commit 8b63743

Please sign in to comment.