fromTomlString static method

TerminalConfig fromTomlString(
  1. String input
)

Parse an alacritty-style TOML document. Unknown keys are ignored; any missing or malformed field keeps its default. Pure — no file IO.

Implementation

static TerminalConfig fromTomlString(String input) {
  final map = TomlDocument.parse(input).toMap();
  final d = TerminalConfig.defaults();

  Map<String, dynamic> section(Map m, String key) =>
      (m[key] is Map) ? Map<String, dynamic>.from(m[key] as Map) : const {};

  int color(Map m, String key, int fallback) {
    final v = m[key];
    if (v is String) {
      final c = parseColor(v);
      if (c != null) return c;
    }
    return fallback;
  }

  final colorsM = section(map, 'colors');
  final primary = section(colorsM, 'primary');
  final normal = section(colorsM, 'normal');
  final bright = section(colorsM, 'bright');
  final selectionM = section(colorsM, 'selection');
  final searchM = section(colorsM, 'search');
  final hintsM = section(colorsM, 'hints');
  final hintStartM = section(hintsM, 'start');
  final matchesM = section(searchM, 'matches');
  final focusedM = section(searchM, 'focused_match');

  const names = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'];
  final ansi = List<int>.generate(16, (i) {
    final group = i < 8 ? normal : bright;
    return color(group, names[i % 8], d.colors.ansi[i]);
  });

  final fontM = section(map, 'font');
  List<String> fallbackList() {
    final v = fontM['fallback'];
    if (v is List) return v.map((e) => '$e').toList();
    return d.font.fallback;
  }

  double dbl(Map m, String key, double fallback) {
    final v = m[key];
    if (v is num) return v.toDouble();
    return fallback;
  }

  int integer(Map m, String key, int fallback) {
    final v = m[key];
    if (v is int) return v;
    if (v is num) return v.toInt();
    return fallback;
  }

  String str(Map m, String key, String fallback) {
    final v = m[key];
    return v is String ? v : fallback;
  }

  bool boolean(Map m, String key, bool fallback) {
    final v = m[key];
    return v is bool ? v : fallback;
  }

  final cursorM = section(map, 'cursor');
  final scrollM = section(map, 'scrolling');
  final mouseM = section(map, 'mouse');
  final bellM = section(map, 'bell');
  final imeM = section(map, 'ime');
  final shellM = section(map, 'shell');
  final windowM = section(map, 'window');
  final paddingM = section(windowM, 'padding');
  final selectionM2 = section(map, 'selection');
  final terminalM = section(map, 'terminal');
  final cursorStyleM = section(cursorM, 'style');
  final colorsCursorM = section(colorsM, 'cursor');

  List<String> strList(Map m, String key) {
    final v = m[key];
    if (v is List) return v.map((e) => '$e').toList();
    return const [];
  }

  Map<String, String> strMap(Map m, String key) {
    final v = m[key];
    if (v is Map) {
      return v.map((k, val) => MapEntry('$k', '$val'));
    }
    return const {};
  }

  String? strOrNull(Map m, String key) {
    final v = m[key];
    return v is String ? v : null;
  }

  int? colorOrNull(Map m, String key) {
    final v = m[key];
    if (v is String) return parseColor(v);
    return null;
  }

  FontStyleConfig? fontStyleFrom(Map m, String key) {
    final v = m[key];
    if (v is! Map) return null;
    return FontStyleConfig(
      family: strOrNull(v, 'family'),
      style: strOrNull(v, 'style'),
    );
  }

  final rawBindings = <RawKeyBinding>[];
  final kb = map['keyboard'];
  final rawList = (kb is Map && kb['bindings'] is List)
      ? kb['bindings'] as List
      : const [];
  for (final e in rawList) {
    if (e is! Map) continue;
    final keyName = e['key'];
    if (keyName is! String) continue;
    rawBindings.add(RawKeyBinding(
      key: keyName,
      mods: e['mods'] is String ? e['mods'] as String : '',
      mode: e['mode'] is String ? e['mode'] as String : '',
      action: e['action'] is String ? e['action'] as String : null,
      chars: e['chars'] is String ? e['chars'] as String : null,
    ));
  }

  return TerminalConfig(
    colors: TerminalColors(
      background: color(primary, 'background', d.colors.background),
      foreground: color(primary, 'foreground', d.colors.foreground),
      selection: color(selectionM, 'background', d.colors.selection),
      ansi: ansi,
      searchMatchBg: color(matchesM, 'background', d.colors.searchMatchBg),
      searchMatchFg: color(matchesM, 'foreground', d.colors.searchMatchFg),
      searchFocusedBg: color(focusedM, 'background', d.colors.searchFocusedBg),
      searchFocusedFg: color(focusedM, 'foreground', d.colors.searchFocusedFg),
      hintStartFg: color(hintStartM, 'foreground', d.colors.hintStartFg),
      hintStartBg: color(hintStartM, 'background', d.colors.hintStartBg),
      cursorText: colorOrNull(colorsCursorM, 'text') ?? d.colors.cursorText,
      cursorBody: colorOrNull(colorsCursorM, 'cursor') ?? d.colors.cursorBody,
    ),
    font: FontConfig(
      family: str(fontM, 'family', d.font.family),
      fallback: fallbackList(),
      size: dbl(fontM, 'size', d.font.size),
      lineHeight: dbl(fontM, 'line_height', d.font.lineHeight),
      bold: fontStyleFrom(fontM, 'bold') ?? d.font.bold,
      italic: fontStyleFrom(fontM, 'italic') ?? d.font.italic,
      boldItalic: fontStyleFrom(fontM, 'bold_italic') ?? d.font.boldItalic,
    ),
    cursor: CursorConfig(
      blinkInterval: integer(cursorM, 'blink_interval', d.cursor.blinkInterval),
      defaultShape: _cursorShapeFromName(str(cursorStyleM, 'shape', ''), d.cursor.defaultShape),
      defaultBlinking:
          str(cursorStyleM, 'blinking', '').toLowerCase() == 'on' ? true : d.cursor.defaultBlinking,
      blinkTimeout: integer(cursorM, 'blink_timeout', d.cursor.blinkTimeout),
    ),
    scrolling: ScrollConfig(
      history: integer(scrollM, 'history', d.scrolling.history),
      multiplier: integer(scrollM, 'multiplier', d.scrolling.multiplier),
    ),
    mouse: MouseConfig(
      doubleClickThreshold:
          integer(mouseM, 'double_click_threshold', d.mouse.doubleClickThreshold),
    ),
    bell: BellConfig(
      color: color(bellM, 'color', d.bell.color),
      duration: integer(bellM, 'duration', d.bell.duration),
      animation: str(bellM, 'animation', d.bell.animation),
    ),
    ime: ImeConfig(
      preeditBg: color(imeM, 'preedit_bg', d.ime.preeditBg),
      preeditFg: color(imeM, 'preedit_fg', d.ime.preeditFg),
      underline: boolean(imeM, 'underline', d.ime.underline),
    ),
    shell: ShellConfig(
      program: strOrNull(shellM, 'program'),
      args: strList(shellM, 'args'),
      workingDirectory: strOrNull(shellM, 'working_directory'),
      env: strMap(shellM, 'env'),
    ),
    keyboard: KeyboardConfig(bindings: rawBindings),
    window: WindowConfig(
      padding: WindowPadding(
        dbl(paddingM, 'x', d.window.padding.x),
        dbl(paddingM, 'y', d.window.padding.y),
      ),
      opacity: dbl(windowM, 'opacity', d.window.opacity),
      decorations: str(windowM, 'decorations', d.window.decorations),
    ),
    selection: SelectionConfig(
      semanticEscapeChars:
          str(selectionM2, 'semantic_escape_chars', d.selection.semanticEscapeChars),
    ),
    terminal: TerminalBehaviorConfig(
      osc52: osc52FromString(str(terminalM, 'osc52', ''), d.terminal.osc52),
    ),
  );
}