Skip to content

Commit

Permalink
Refactored prompt cancelling according to feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
quintesse committed Jul 29, 2024
1 parent ab1dec6 commit 4e653e3
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,14 @@ public abstract class AbstractPrompt<T extends ConsoleUIItemIF> {
protected int firstItemRow;
private final Size size = new Size();
protected final ConsolePrompt.UiConfig config;
protected boolean cancellable;
private Display display;
private ListRange range = null;

public static final long DEFAULT_TIMEOUT_WITH_ESC = 150L;

public AbstractPrompt(
Terminal terminal,
List<AttributedString> header,
AttributedString message,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
this(terminal, header, message, new ArrayList<>(), 0, cancellable, cfg);
Terminal terminal, List<AttributedString> header, AttributedString message, ConsolePrompt.UiConfig cfg) {
this(terminal, header, message, new ArrayList<>(), 0, cfg);
}

public AbstractPrompt(
Expand All @@ -66,7 +61,6 @@ public AbstractPrompt(
AttributedString message,
List<T> items,
int pageSize,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
this.terminal = terminal;
this.bindingReader = new BindingReader(terminal.reader());
Expand All @@ -78,7 +72,6 @@ public AbstractPrompt(
this.message = message;
this.items = items;
this.firstItemRow = this.header.size() + 1;
this.cancellable = cancellable;
this.config = cfg;
}

Expand Down Expand Up @@ -334,9 +327,8 @@ private ExpandableChoicePrompt(
List<AttributedString> header,
AttributedString message,
ExpandableChoice expandableChoice,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
super(terminal, header, message, cancellable, cfg);
super(terminal, header, message, cfg);
startColumn = message.columnLength();
items = expandableChoice.getChoiceItems();
config = cfg;
Expand All @@ -347,20 +339,17 @@ public static ExpandableChoicePrompt getPrompt(
List<AttributedString> header,
AttributedString message,
ExpandableChoice expandableChoice,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
return new ExpandableChoicePrompt(terminal, header, message, expandableChoice, cancellable, cfg);
return new ExpandableChoicePrompt(terminal, header, message, expandableChoice, cfg);
}

private void bindKeys(KeyMap<Operation> map) {
for (char i = 32; i < KEYMAP_LENGTH; i++) {
map.bind(Operation.INSERT, Character.toString(i));
}
map.bind(Operation.EXIT, "\r");
if (cancellable) {
map.bind(Operation.CANCEL, KeyMap.esc());
map.setAmbiguousTimeout(DEFAULT_TIMEOUT_WITH_ESC);
}
map.bind(Operation.CANCEL, KeyMap.esc());
map.setAmbiguousTimeout(DEFAULT_TIMEOUT_WITH_ESC);
}

public ExpandableChoiceResult execute() {
Expand Down Expand Up @@ -439,9 +428,8 @@ private ConfirmPrompt(
List<AttributedString> header,
AttributedString message,
ConfirmChoice confirmChoice,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
super(terminal, header, message, cancellable, cfg);
super(terminal, header, message, cfg);
startColumn = message.columnLength();
defaultValue = confirmChoice.getDefaultConfirmation();
config = cfg;
Expand All @@ -452,9 +440,8 @@ public static ConfirmPrompt getPrompt(
List<AttributedString> header,
AttributedString message,
ConfirmChoice confirmChoice,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
return new ConfirmPrompt(terminal, header, message, confirmChoice, cancellable, cfg);
return new ConfirmPrompt(terminal, header, message, confirmChoice, cfg);
}

private void bindKeys(KeyMap<Operation> map) {
Expand All @@ -463,10 +450,8 @@ private void bindKeys(KeyMap<Operation> map) {
map.bind(Operation.YES, yes, yes.toUpperCase());
map.bind(Operation.NO, no, no.toUpperCase());
map.bind(Operation.EXIT, "\r");
if (cancellable) {
map.bind(Operation.CANCEL, KeyMap.esc());
map.setAmbiguousTimeout(DEFAULT_TIMEOUT_WITH_ESC);
}
map.bind(Operation.CANCEL, KeyMap.esc());
map.setAmbiguousTimeout(DEFAULT_TIMEOUT_WITH_ESC);
}

public ConfirmResult execute() {
Expand Down Expand Up @@ -537,9 +522,8 @@ private InputValuePrompt(
List<AttributedString> header,
AttributedString message,
InputValue inputValue,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
super(terminal, header, message, cancellable, cfg);
super(terminal, header, message, cfg);
this.reader = reader;
defaultValue = inputValue.getDefaultValue();
startColumn = message.columnLength();
Expand All @@ -553,9 +537,8 @@ public static InputValuePrompt getPrompt(
List<AttributedString> header,
AttributedString message,
InputValue inputValue,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
return new InputValuePrompt(reader, terminal, header, message, inputValue, cancellable, cfg);
return new InputValuePrompt(reader, terminal, header, message, inputValue, cfg);
}

private void bindKeys(KeyMap<Operation> map) {
Expand All @@ -574,20 +557,16 @@ private void bindKeys(KeyMap<Operation> map) {
map.bind(Operation.RIGHT, ctrl('F'));
map.bind(Operation.LEFT, ctrl('B'));
map.bind(Operation.SELECT_CANDIDATE, "\t");
if (cancellable) {
map.bind(Operation.CANCEL, KeyMap.esc());
map.setAmbiguousTimeout(DEFAULT_TIMEOUT_WITH_ESC);
}
map.bind(Operation.CANCEL, KeyMap.esc());
map.setAmbiguousTimeout(DEFAULT_TIMEOUT_WITH_ESC);
}

private void bindSelectKeys(KeyMap<SelectOp> map) {
map.bind(SelectOp.FORWARD_ONE_LINE, "\t", "e", ctrl('E'), key(terminal, InfoCmp.Capability.key_down));
map.bind(SelectOp.BACKWARD_ONE_LINE, "y", ctrl('Y'), key(terminal, InfoCmp.Capability.key_up));
map.bind(SelectOp.EXIT, "\r");
if (cancellable) {
map.bind(SelectOp.CANCEL, KeyMap.esc());
map.setAmbiguousTimeout(DEFAULT_TIMEOUT_WITH_ESC);
}
map.bind(SelectOp.CANCEL, KeyMap.esc());
map.setAmbiguousTimeout(DEFAULT_TIMEOUT_WITH_ESC);
}

public InputResult execute() {
Expand Down Expand Up @@ -813,9 +792,8 @@ private ListChoicePrompt(
AttributedString message,
List<T> listItems,
int pageSize,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
super(terminal, header, message, listItems, pageSize, cancellable, cfg);
super(terminal, header, message, listItems, pageSize, cfg);
items = listItems;
}

Expand All @@ -825,9 +803,8 @@ public static <T extends ListItemIF> ListChoicePrompt<T> getPrompt(
AttributedString message,
List<T> listItems,
int pageSize,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
return new ListChoicePrompt<>(terminal, header, message, listItems, pageSize, cancellable, cfg);
return new ListChoicePrompt<>(terminal, header, message, listItems, pageSize, cfg);
}

private void bindKeys(KeyMap<Operation> map) {
Expand All @@ -837,10 +814,8 @@ private void bindKeys(KeyMap<Operation> map) {
map.bind(Operation.FORWARD_ONE_LINE, "e", ctrl('E'), key(terminal, InfoCmp.Capability.key_down));
map.bind(Operation.BACKWARD_ONE_LINE, "y", ctrl('Y'), key(terminal, InfoCmp.Capability.key_up));
map.bind(Operation.EXIT, "\r");
if (cancellable) {
map.bind(Operation.CANCEL, KeyMap.esc());
map.setAmbiguousTimeout(DEFAULT_TIMEOUT_WITH_ESC);
}
map.bind(Operation.CANCEL, KeyMap.esc());
map.setAmbiguousTimeout(DEFAULT_TIMEOUT_WITH_ESC);
}

public ListResult execute() {
Expand Down Expand Up @@ -899,9 +874,8 @@ private CheckboxPrompt(
AttributedString message,
List<CheckboxItemIF> checkboxItemList,
int pageSize,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
super(terminal, header, message, checkboxItemList, pageSize, cancellable, cfg);
super(terminal, header, message, checkboxItemList, pageSize, cfg);
items = checkboxItemList;
}

Expand All @@ -911,20 +885,17 @@ public static CheckboxPrompt getPrompt(
AttributedString message,
List<CheckboxItemIF> checkboxItemList,
int pageSize,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
return new CheckboxPrompt(terminal, header, message, checkboxItemList, pageSize, cancellable, cfg);
return new CheckboxPrompt(terminal, header, message, checkboxItemList, pageSize, cfg);
}

private void bindKeys(KeyMap<Operation> map) {
map.bind(Operation.FORWARD_ONE_LINE, "e", ctrl('E'), key(terminal, InfoCmp.Capability.key_down));
map.bind(Operation.BACKWARD_ONE_LINE, "y", ctrl('Y'), key(terminal, InfoCmp.Capability.key_up));
map.bind(Operation.TOGGLE, " ");
map.bind(Operation.EXIT, "\r");
if (cancellable) {
map.bind(Operation.CANCEL, KeyMap.esc());
map.setAmbiguousTimeout(DEFAULT_TIMEOUT_WITH_ESC);
}
map.bind(Operation.CANCEL, KeyMap.esc());
map.setAmbiguousTimeout(DEFAULT_TIMEOUT_WITH_ESC);
}

public CheckboxResult execute() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ public class ConsolePrompt {
private final LineReader reader;
private final Terminal terminal;
private final UiConfig config;
private boolean cancellable = false;

/**
*
Expand Down Expand Up @@ -66,11 +65,6 @@ public ConsolePrompt(LineReader reader, Terminal terminal, UiConfig config) {
}
}

public ConsolePrompt cancellable(boolean cancellable) {
this.cancellable = cancellable;
return this;
}

/**
* 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 Down Expand Up @@ -122,16 +116,14 @@ public Map<String, PromptResultItemIF> prompt(
asb.toAttributedString(),
lc.getListItemList(),
computePageSize(terminal, lc.getPageSize(), lc.getPageSizeType()),
cancellable,
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, cancellable, config)
result = InputValuePrompt.getPrompt(reader, terminal, header, asb.toAttributedString(), ip, config)
.execute();
} else if (pe instanceof ExpandableChoice) {
ExpandableChoice ec = (ExpandableChoice) pe;
Expand All @@ -147,17 +139,11 @@ public Map<String, PromptResultItemIF> prompt(
asb.append("h) ");
try {
result = ExpandableChoicePrompt.getPrompt(
terminal, header, asb.toAttributedString(), ec, cancellable, config)
terminal, header, asb.toAttributedString(), ec, config)
.execute();
} catch (ExpandableChoiceException e) {
result = ListChoicePrompt.getPrompt(
terminal,
header,
message.toAttributedString(),
ec.getChoiceItems(),
10,
cancellable,
config)
terminal, header, message.toAttributedString(), ec.getChoiceItems(), 10, config)
.execute();
}
} else if (pe instanceof Checkbox) {
Expand All @@ -168,7 +154,6 @@ public Map<String, PromptResultItemIF> prompt(
message.toAttributedString(),
cb.getCheckboxItemList(),
computePageSize(terminal, cb.getPageSize(), cb.getPageSizeType()),
cancellable,
config)
.execute();
} else if (pe instanceof ConfirmChoice) {
Expand All @@ -181,8 +166,7 @@ public Map<String, PromptResultItemIF> prompt(
asb.append(config.resourceBundle().getString("confirmation_no_default"));
}
asb.append(" ");
result = ConfirmPrompt.getPrompt(
terminal, header, asb.toAttributedString(), cc, cancellable, config)
result = ConfirmPrompt.getPrompt(terminal, header, asb.toAttributedString(), cc, config)
.execute();
} else {
throw new IllegalArgumentException("wrong type of promptable element");
Expand All @@ -196,7 +180,13 @@ public Map<String, PromptResultItemIF> prompt(
i -= 2;
continue;
} else {
return null;
if (config.cancellable()) {
return null;
} else {
// Repeat current prompt
i -= 1;
continue;
}
}
}
String resp = result.getResult();
Expand Down Expand Up @@ -253,12 +243,14 @@ public static class UiConfig {
private final StyleResolver resolver;
private final ResourceBundle resourceBundle;
private Map<LineReader.Option, Boolean> readerOptions = new HashMap<>();
private final boolean cancellable;

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

public UiConfig(String indicator, String uncheckedBox, String checkedBox, String unavailable) {
public UiConfig(
String indicator, String uncheckedBox, String checkedBox, String unavailable, boolean cancellable) {
String uc = System.getenv(UI_COLORS);
String uiColors = uc != null && Styles.isStylePattern(uc) ? uc : DEFAULT_UI_COLORS;
this.resolver = resolver(uiColors);
Expand All @@ -267,6 +259,7 @@ public UiConfig(String indicator, String uncheckedBox, String checkedBox, String
this.checkedBox = toAttributedString(resolver, (checkedBox != null ? checkedBox : "x "), ".be");
this.unavailable = toAttributedString(resolver, (unavailable != null ? unavailable : "- "), ".bd");
this.resourceBundle = ResourceBundle.getBundle("consoleui_messages");
this.cancellable = cancellable;
}

private static AttributedString toAttributedString(StyleResolver resolver, String string, String styleKey) {
Expand Down Expand Up @@ -300,6 +293,10 @@ public ResourceBundle resourceBundle() {
return resourceBundle;
}

public boolean cancellable() {
return cancellable;
}

protected void setReaderOptions(Map<LineReader.Option, Boolean> readerOptions) {
this.readerOptions = readerOptions;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ private static void test(Terminal terminal) throws IOException {
}

ConsolePrompt.UiConfig config;
config = new ConsolePrompt.UiConfig("\u276F", "\u25EF ", "\u25C9 ", "\u25EF ");
config = new ConsolePrompt.UiConfig("\u276F", "\u25EF ", "\u25C9 ", "\u25EF ", false);
//
// LineReader is needed only if you are adding JLine Completers in your prompts.
// If you are not using Completers you do not need to create LineReader.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,16 @@ public static void main(String[] args) {
throw new IllegalStateException("Dumb terminal detected.\nConsoleUi requires real terminal to work!\n"
+ "Note: On Windows Jansi or JNA library must be included in classpath.");
} else if (OSUtils.IS_WINDOWS) {
config = new ConsolePrompt.UiConfig(">", "( )", "(x)", "( )");
config = new ConsolePrompt.UiConfig(">", "( )", "(x)", "( )", true);
} else {
config = new ConsolePrompt.UiConfig("\u276F", "\u25EF ", "\u25C9 ", "\u25EF ");
config = new ConsolePrompt.UiConfig("\u276F", "\u25EF ", "\u25C9 ", "\u25EF ", true);
}
//
// LineReader is needed only if you are adding JLine Completers in your prompts.
// If you are not using Completers you do not need to create LineReader.
//
LineReader reader = LineReaderBuilder.builder().terminal(terminal).build();
ConsolePrompt prompt = new ConsolePrompt(reader, terminal, config).cancellable(true);
ConsolePrompt prompt = new ConsolePrompt(reader, terminal, config);
PromptBuilder promptBuilder = prompt.getPromptBuilder();

promptBuilder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public static void main(String[] args) {
header.add(new AttributedStringBuilder().append("in JavaScript.").toAttributedString());

try (Terminal terminal = TerminalBuilder.builder().build()) {
ConsolePrompt.UiConfig config = new ConsolePrompt.UiConfig(">", "( )", "(x)", "( )");
ConsolePrompt.UiConfig config = new ConsolePrompt.UiConfig(">", "( )", "(x)", "( )", false);
ConsolePrompt prompt = new ConsolePrompt(terminal, config);
PromptBuilder promptBuilder = prompt.getPromptBuilder();

Expand Down

0 comments on commit 4e653e3

Please sign in to comment.