readText function

Future<String?> readText(
  1. Console console, {
  2. required List<String> header,
})

Reads a line of free text below header, or null on Esc or Ctrl-C.

Implementation

Future<String?> readText(Console console, {required List<String> header}) async {
  final terminal = console.terminal;
  if (!terminal.interactive) {
    console.print(header);
    terminal.write('> ');
    return terminal.readLine();
  }

  final editor = LineEditor();
  void render() {
    final width = max(20, terminal.columns - 1);
    final lines = <String>[...header.expand((line) => wrapText(line, width))];
    final layout = layoutText(editor.chars, editor.cursor, width - 2);
    final firstRow = lines.length;
    for (var index = 0; index < layout.rows.length; index++) {
      lines.add('${index == 0 ? Style.accent('❯') : ' '} ${layout.rows[index]}');
    }
    lines
      ..add('')
      ..add(Style.dim('Enter to submit · Esc to cancel'));
    console.showLive(lines, cursorRow: firstRow + layout.row, cursorColumn: 2 + layout.column);
  }

  final result = Completer<String?>();
  render();
  final stopListening = terminal.listen((event) {
    if (result.isCompleted) {
      return true;
    }
    switch (event.key) {
      case Key.text || Key.paste:
        editor.insert(event.text.replaceAll(RegExp(r'\r\n?'), '\n'));
      case Key.newline:
        editor.insert('\n');
      case Key.enter:
        if (editor.text.trim().isNotEmpty) {
          result.complete(editor.text.trim());
          return true;
        }
      case Key.escape || Key.ctrlC || Key.ctrlD:
        result.complete(null);
        return true;
      case Key.backspace:
        editor.backspace();
      case Key.delete:
        editor.deleteForward();
      case Key.left:
        editor.left();
      case Key.right:
        editor.right();
      case Key.home || Key.ctrlA:
        editor.home();
      case Key.end || Key.ctrlE:
        editor.end();
      case Key.wordLeft:
        editor.wordLeft();
      case Key.wordRight:
        editor.wordRight();
      case Key.ctrlW:
        editor.deleteWordBefore();
      case Key.ctrlU:
        editor.killToStart();
      case Key.ctrlK:
        editor.killToEnd();
      default:
        break;
    }
    render();
    return true;
  });
  final text = await result.future;
  stopListening();
  console.live.clear();
  return text;
}