injectGlanceModifier function

String injectGlanceModifier(
  1. String code,
  2. String modifier, {
  3. GlanceSizeAxis? weightAxis,
})

Helper to parse a typical Compose call (e.g. Column { or Text(...)) and inject a modifier string (e.g. fillMaxSize()).

A size the chain already there carries along an axis modifier sizes is dropped, so the widget injecting its size wins over the one that asked for it further in. weightAxis is the axis a defaultWeight() in modifier sizes, which the call does not say on its own.

Implementation

String injectGlanceModifier(
  String code,
  String modifier, {
  GlanceSizeAxis? weightAxis,
}) {
  // We want to find the first Compose element like `Column(`, `Column {`, `Text(`.
  // If it already has arguments `Column(abc) {`, we inject `modifier = modifier, abc` or similar.

  final trimmed = code.trimLeft();
  final indentMatch = RegExp(r'^(\s*)').firstMatch(code);
  final indent = indentMatch?.group(1) ?? '';

  if (trimmed.startsWith('if (')) {
    final injected = _injectIntoIfBranches(code, modifier, weightAxis);
    if (injected != null) return injected;
  }

  if (trimmed.startsWith('when (')) {
    final injected = _injectIntoWhenBranches(code, modifier, weightAxis);
    if (injected != null) return injected;
  }

  final compMatch = RegExp(r'^[A-Z][a-zA-Z0-9_]*').firstMatch(trimmed);
  if (compMatch != null) {
    final compName = compMatch.group(0);
    final argRange = _argumentRange(trimmed, compMatch.end);
    final declared =
        argRange == null ? null : trimmed.substring(argRange.$1, argRange.$2);
    final callEnd = argRange == null ? compMatch.end : argRange.$2 + 1;

    final chainRange = declared == null ? null : _modifierChainRange(declared);
    final chain = chainRange == null
        ? null
        : declared!.substring(chainRange.$1, chainRange.$2);

    // One axis of a chain already filling both says nothing more.
    if (GlanceSizeAxis.values.any((axis) => axis.fill == modifier) &&
        chain != null &&
        _fillsBothAxes(chain)) {
      return code;
    }
    final args = chainRange == null
        ? declared ?? ''
        : declared!.replaceRange(
            chainRange.$1,
            chainRange.$2,
            _withoutSupersededSizes(chain!, modifier, weightAxis),
          );

    final hasBrace = trimmed.substring(callEnd).trimLeft().startsWith('{');

    String newArgs = '';
    if (args.isEmpty) {
      newArgs = 'modifier = GlanceModifier.$modifier';
    } else if (chainRange != null) {
      final head = args.substring(0, chainRange.$1);
      final tail = args.substring(chainRange.$1 + _modifierToken.length);
      newArgs = '$head$_modifierToken.$modifier$tail';
    } else {
      // Arguments spread over lines stay on theirs.
      final separator = args.startsWith('\n') ? ',' : ', ';
      newArgs = 'modifier = GlanceModifier.$modifier$separator$args';
    }

    final rest = trimmed.substring(
      callEnd + (hasBrace ? trimmed.substring(callEnd).indexOf('{') + 1 : 0),
    );

    if (hasBrace) {
      return '$indent$compName($newArgs) {$rest';
    } else {
      return '$indent$compName($newArgs)$rest';
    }
  }

  // Fallback: if we can't find a direct Compose element (e.g. it's an if-statement), wrap it in a Box
  final lines = code.split('\n');
  final indentedLines =
      lines.map((l) => l.trimRight().isEmpty ? '' : '    $l').join('\n');
  return '${indent}Box(modifier = GlanceModifier.$modifier) {\n$indentedLines\n$indent}';
}