run method

Future<void> run()

Runs the IDE until the user quits, restoring the terminal afterwards.

Implementation

Future<void> run() async {
  _terminal.enter();
  final decoder = KeyDecoder();
  StreamSubscription<List<int>>? inputSub;
  StreamSubscription<void>? resizeSub;
  try {
    // Load the tree root and git state before the first paint.
    _repo = await GitRepo.discover(_workspace, rootPath);
    await _tree.init();
    await _refreshGit();
    _render();
    // Input is handled asynchronously (file ops await the workspace), so the
    // subscription is paused while a chunk is processed and resumed after, to
    // serialise key handling and keep ordering deterministic.
    inputSub = _terminal.input.listen(
      (bytes) async {
        inputSub!.pause();
        for (final key in decoder.decode(bytes)) {
          // Never let an unexpected error from a key handler crash or freeze
          // the TUI: surface it in the status bar and keep the loop alive.
          try {
            await _handleKey(key);
          } on Object catch (e) {
            _setMessage('Unexpected error: $e', isError: true);
          }
          if (_done.isCompleted) break;
        }
        if (!_done.isCompleted) _render();
        if (!_done.isCompleted) inputSub.resume();
      },
      onError: (_) => _finish(),
      onDone: _finish,
    );
    resizeSub = _terminal.resizeEvents.listen((_) {
      _terminal.invalidate();
      _render();
    });
    await _done.future;
  } finally {
    await inputSub?.cancel();
    await resizeSub?.cancel();
    await _terminalPanel?.dispose();
    if (_confirm != null && !_confirm!.completer.isCompleted) {
      _confirm!.completer.complete(false);
    }
    _agentBackend?.close();
    await _workspace.close();
    _terminal.leave();
  }
}