performReassemble method

  1. @override
Future<void> performReassemble()
override

Reassembles the application after a hot reload.

This method is called by the hot reload mechanism after successful code compilation. It forces a re-render by clearing cached view state, allowing the model to rebuild its view with the updated code.

For WidgetApp models, this triggers a full element tree rebuild. For raw TEA models, this re-executes model.view() to pick up any changes to the view code.

Subclasses or hosts that wrap Program can override this method to provide custom reassembly behavior (e.g., preserving additional state across reloads).

Implementation

@override
Future<void> performReassemble() async {
  dev.log(
    'performReassemble: start '
    '(model=${_model.runtimeType}, '
    'isReassemblable=${_model is ReassemblableModel}, '
    'renderer=${_renderer.runtimeType}, '
    'terminalReleased=$_terminalReleased, '
    'needsRender=$_needsRender)',
    name: 'HotReload',
  );

  // Invalidate Program-level view caches so that the identity-based
  // skip in _render() does not suppress the next frame.
  _lastView = null;
  _lastRenderedView = null;
  _lastRenderWidth = null;
  _lastRenderHeight = null;

  // Let the model invalidate its own caches (e.g., WidgetApp marks its
  // element tree dirty so that build() methods are re-executed).
  if (_model case ReassemblableModel reassemblable) {
    dev.log(
      'performReassemble: calling model.reassemble()',
      name: 'HotReload',
    );
    reassemblable.reassemble();
  }

  if (_renderer != null && !_terminalReleased) {
    // Invalidate the renderer's diff state so the next render produces
    // a full redraw. Unlike clear(), invalidate() does NOT perform any
    // terminal I/O (no flush, no screen erase), so it avoids the
    // _stdoutFlushInFlight race that caused previous approaches to leave
    // the screen stale until a keypress.
    _renderer!.invalidate();
    dev.log(
      'performReassemble: invalidated renderer, calling scheduleRender()',
      name: 'HotReload',
    );

    // Schedule a render through the normal pipeline: scheduleRender()
    // sets _needsRender and queues a microtask that calls _flushRender()
    // → _render() → UV render() → _flushInternal() + terminal.flush().
    // This is the exact same path used for keypress-triggered renders,
    // which are known to update the screen reliably.
    scheduleRender();
  } else {
    dev.log(
      'performReassemble: skipped render '
      '(renderer=${_renderer == null ? "null" : "present"}, '
      'terminalReleased=$_terminalReleased)',
      name: 'HotReload',
    );
  }
  dev.log('performReassemble: done', name: 'HotReload');
}