Skip to content

Commit

Permalink
Added possibility of cancelling prompts
Browse files Browse the repository at this point in the history
Cancelling a prompt by hitting the ESC key makes it go back to the
previous prompt. If we're already at the fist prompt the result returned
will be `null`.

Fixes jline#1035
  • Loading branch information
quintesse committed Jul 24, 2024
1 parent 78fcff1 commit ab1dec6
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,19 @@ 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, ConsolePrompt.UiConfig cfg) {
this(terminal, header, message, new ArrayList<>(), 0, cfg);
Terminal terminal,
List<AttributedString> header,
AttributedString message,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
this(terminal, header, message, new ArrayList<>(), 0, cancellable, cfg);
}

public AbstractPrompt(
Expand All @@ -59,6 +66,7 @@ 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 @@ -70,6 +78,7 @@ public AbstractPrompt(
this.message = message;
this.items = items;
this.firstItemRow = this.header.size() + 1;
this.cancellable = cancellable;
this.config = cfg;
}

Expand Down Expand Up @@ -312,7 +321,8 @@ private List<AttributedString> displayLines(int cursorRow, AttributedString buff
protected static class ExpandableChoicePrompt extends AbstractPrompt<ListItemIF> {
private enum Operation {
INSERT,
EXIT
EXIT,
CANCEL
}

private final int startColumn;
Expand All @@ -324,8 +334,9 @@ private ExpandableChoicePrompt(
List<AttributedString> header,
AttributedString message,
ExpandableChoice expandableChoice,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
super(terminal, header, message, cfg);
super(terminal, header, message, cancellable, cfg);
startColumn = message.columnLength();
items = expandableChoice.getChoiceItems();
config = cfg;
Expand All @@ -336,15 +347,20 @@ public static ExpandableChoicePrompt getPrompt(
List<AttributedString> header,
AttributedString message,
ExpandableChoice expandableChoice,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
return new ExpandableChoicePrompt(terminal, header, message, expandableChoice, cfg);
return new ExpandableChoicePrompt(terminal, header, message, expandableChoice, cancellable, 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);
}
}

public ExpandableChoiceResult execute() {
Expand Down Expand Up @@ -396,6 +412,8 @@ public ExpandableChoiceResult execute() {
break;
}
return new ExpandableChoiceResult(selectedId);
case CANCEL:
return null;
}
}
}
Expand All @@ -408,7 +426,8 @@ protected static class ConfirmPrompt extends AbstractPrompt<ListItemIF> {
private enum Operation {
NO,
YES,
EXIT
EXIT,
CANCEL
}

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

private void bindKeys(KeyMap<Operation> map) {
Expand All @@ -442,6 +463,10 @@ 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);
}
}

public ConfirmResult execute() {
Expand Down Expand Up @@ -472,6 +497,8 @@ public ConfirmResult execute() {
break;
}
return new ConfirmResult(confirm);
case CANCEL:
return null;
}
}
}
Expand All @@ -487,13 +514,15 @@ private enum Operation {
BEGINNING_OF_LINE,
END_OF_LINE,
SELECT_CANDIDATE,
EXIT
EXIT,
CANCEL
}

private enum SelectOp {
FORWARD_ONE_LINE,
BACKWARD_ONE_LINE,
EXIT
EXIT,
CANCEL
}

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

private void bindKeys(KeyMap<Operation> map) {
Expand All @@ -543,12 +574,20 @@ 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);
}
}

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);
}
}

public InputResult execute() {
Expand Down Expand Up @@ -620,16 +659,20 @@ public InputResult execute() {
String selected =
selectCandidate(firstItemRow - 1, buffer.toString(), row + 1, startColumn, matches);
resetHeader();
buffer.delete(0, buffer.length());
buffer.append(selected);
column = startColumn + buffer.length();
if (selected != null) {
buffer.delete(0, buffer.length());
buffer.append(selected);
column = startColumn + buffer.length();
}
}
break;
case EXIT:
if (buffer.toString().isEmpty()) {
buffer.append(defaultValue);
}
return new InputResult(buffer.toString());
case CANCEL:
return null;
}
}
}
Expand Down Expand Up @@ -663,6 +706,8 @@ String selectCandidate(int buffRow, String buffer, int row, int column, List<Can
break;
case EXIT:
return selected;
case CANCEL:
return null;
}
}
}
Expand Down Expand Up @@ -756,7 +801,8 @@ private enum Operation {
FORWARD_ONE_LINE,
BACKWARD_ONE_LINE,
INSERT,
EXIT
EXIT,
CANCEL
}

private final List<T> items;
Expand All @@ -767,8 +813,9 @@ private ListChoicePrompt(
AttributedString message,
List<T> listItems,
int pageSize,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
super(terminal, header, message, listItems, pageSize, cfg);
super(terminal, header, message, listItems, pageSize, cancellable, cfg);
items = listItems;
}

Expand All @@ -778,8 +825,9 @@ 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, cfg);
return new ListChoicePrompt<>(terminal, header, message, listItems, pageSize, cancellable, cfg);
}

private void bindKeys(KeyMap<Operation> map) {
Expand All @@ -789,6 +837,10 @@ 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);
}
}

public ListResult execute() {
Expand Down Expand Up @@ -823,6 +875,8 @@ public ListResult execute() {
case EXIT:
T listItem = items.get(selectRow - firstItemRow);
return new ListResult(listItem.getName());
case CANCEL:
return null;
}
}
}
Expand All @@ -833,7 +887,8 @@ private enum Operation {
FORWARD_ONE_LINE,
BACKWARD_ONE_LINE,
TOGGLE,
EXIT
EXIT,
CANCEL
}

private final List<CheckboxItemIF> items;
Expand All @@ -844,8 +899,9 @@ private CheckboxPrompt(
AttributedString message,
List<CheckboxItemIF> checkboxItemList,
int pageSize,
boolean cancellable,
ConsolePrompt.UiConfig cfg) {
super(terminal, header, message, checkboxItemList, pageSize, cfg);
super(terminal, header, message, checkboxItemList, pageSize, cancellable, cfg);
items = checkboxItemList;
}

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

public CheckboxResult execute() {
Expand Down Expand Up @@ -895,6 +956,8 @@ public CheckboxResult execute() {
break;
case EXIT:
return new CheckboxResult(selected);
case CANCEL:
return null;
}
}
}
Expand Down
Loading

0 comments on commit ab1dec6

Please sign in to comment.