read_line method
- bool cancel_on_break = false,
- bool cancel_on_escape = false,
- bool cancel_on_eof = false,
- void callback()?,
Reads a line of input, handling basic keyboard navigation commands.
The Dart stdin.readLineSync()
function reads a line from the input,
however it does not handle cursor navigation (e.g. arrow keys, home and
end keys), and has side-effects that may be unhelpful for certain console
applications. For example, Ctrl+C is processed as the break character,
which causes the application to immediately exit.
The implementation does not currently allow for multi-line input. It is best suited for short text fields that are not longer than the width of the current screen.
By default, readLine ignores break characters (e.g. Ctrl+C) and the Esc key, but if enabled, the function will exit and return a null string if those keys are pressed.
A callback function may be supplied, as a peek-ahead for what is being entered. This is intended for scenarios like auto-complete, where the text field is coupled with some other content.
Implementation
@override
String? read_line({
final bool cancel_on_break = false,
final bool cancel_on_escape = false,
final bool cancel_on_eof = false,
final void Function(String text, Key lastPressed)? callback,
}) {
String buffer = '';
// Cursor position relative to buffer, not screen.
int index = 0;
final current_cursor_position = cursor_position.get();
final screen_row = current_cursor_position!.row;
final screen_col_offset = current_cursor_position.col;
final buffer_max_length = dimensions.width - screen_col_offset - 3;
for (;;) {
final key = read_key();
key.match(
printable: (final key) {
if (buffer.length < buffer_max_length) {
if (index == buffer.length) {
buffer += key.char;
index++;
} else {
buffer = buffer.substring(0, index) +
key.char +
buffer.substring(index);
index++;
}
}
},
control: (final key) {
switch (key.control_char) {
case ControlCharacters.enter:
if (_scrollback_buffer != null) {
_scrollback_buffer!.add(buffer);
}
write_line();
return buffer;
case ControlCharacters.ctrlC:
if (cancel_on_break) {
return null;
}
break;
case ControlCharacters.escape:
if (cancel_on_escape) {
return null;
}
break;
case ControlCharacters.backspace:
case ControlCharacters.ctrlH:
if (index > 0) {
buffer =
buffer.substring(0, index - 1) + buffer.substring(index);
index--;
}
break;
case ControlCharacters.ctrlU:
buffer = buffer.substring(index, buffer.length);
index = 0;
break;
case ControlCharacters.delete:
case ControlCharacters.ctrlD:
if (index < buffer.length - 1) {
buffer =
buffer.substring(0, index) + buffer.substring(index + 1);
} else if (cancel_on_eof) {
return null;
}
break;
case ControlCharacters.ctrlK:
buffer = buffer.substring(0, index);
break;
case ControlCharacters.arrowLeft:
case ControlCharacters.ctrlB:
index = () {
if (index > 0) {
return index - 1;
} else {
return index;
}
}();
break;
case ControlCharacters.arrowUp:
if (_scrollback_buffer != null) {
buffer = _scrollback_buffer!.up(buffer);
index = buffer.length;
}
break;
case ControlCharacters.arrowDown:
if (_scrollback_buffer != null) {
final temp = _scrollback_buffer!.down();
if (temp != null) {
buffer = temp;
index = buffer.length;
}
}
break;
case ControlCharacters.arrowRight:
case ControlCharacters.ctrlF:
index = () {
if (index < buffer.length) {
return index + 1;
} else {
return index;
}
}();
break;
case ControlCharacters.wordLeft:
if (index > 0) {
final bufferLeftOfCursor = buffer.substring(0, index - 1);
final lastSpace = bufferLeftOfCursor.lastIndexOf(' ');
index = () {
if (lastSpace != -1) {
return lastSpace + 1;
} else {
return 0;
}
}();
}
break;
case ControlCharacters.wordRight:
if (index < buffer.length) {
final bufferRightOfCursor = buffer.substring(index + 1);
final nextSpace = bufferRightOfCursor.indexOf(' ');
if (nextSpace != -1) {
index = min(index + nextSpace + 2, buffer.length);
} else {
index = buffer.length;
}
}
break;
case ControlCharacters.home:
case ControlCharacters.ctrlA:
index = 0;
break;
case ControlCharacters.end:
case ControlCharacters.ctrlE:
index = buffer.length;
break;
case ControlCharacters.ctrlG:
case ControlCharacters.tab:
case ControlCharacters.ctrlJ:
case ControlCharacters.ctrlL:
case ControlCharacters.ctrlN:
case ControlCharacters.ctrlO:
case ControlCharacters.ctrlP:
case ControlCharacters.ctrlQ:
case ControlCharacters.ctrlR:
case ControlCharacters.ctrlS:
case ControlCharacters.ctrlT:
case ControlCharacters.ctrlV:
case ControlCharacters.ctrlW:
case ControlCharacters.ctrlX:
case ControlCharacters.ctrlY:
case ControlCharacters.ctrlZ:
case ControlCharacters.pageUp:
case ControlCharacters.pageDown:
case ControlCharacters.wordBackspace:
case ControlCharacters.F1:
case ControlCharacters.F2:
case ControlCharacters.F3:
case ControlCharacters.F4:
case ControlCharacters.unknown:
// Do nothing.
break;
}
},
);
cursor_position.update(
SneathCoordinateImpl(
row: screen_row,
col: screen_col_offset,
),
);
erase_cursor_to_end();
// Allow for backspace condition.
write(buffer);
cursor_position.update(
SneathCoordinateImpl(
row: screen_row,
col: screen_col_offset + index,
),
);
if (callback != null) {
callback(buffer, key);
}
}
}