LogBox.fromText constructor

LogBox.fromText(
  1. Log log,
  2. LogTheme theme,
  3. String text, {
  4. required int? maxLines,
  5. String? verticalFiller,
  6. LogConstraints constraints = const LogConstraints.unlimited(),
  7. LogTextAlign textAlign = LogTextAlign.left,
  8. LogVerticalAlign verticalAlign = LogVerticalAlign.top,
  9. String? debugName,
})

Implementation

factory LogBox.fromText(
  Log log,
  LogTheme theme,
  String text, {
  required int? maxLines,
  String? verticalFiller,
  LogConstraints constraints = const LogConstraints.unlimited(),
  LogTextAlign textAlign = LogTextAlign.left,
  LogVerticalAlign verticalAlign = LogVerticalAlign.top,
  String? debugName,
}) {
  final lines = text.split('\n');
  final parsers = lines.map(ansi.Parser.new).toList(growable: false);
  var boxWidth = parsers.fold(0, (w, parser) => math.max(w, parser.length));
  boxWidth = constraints.apply(boxWidth);

  if (boxWidth == 0) {
    return LogBox.empty(debugName: debugName);
  }

  // Место под символ переноса нужно только строкам, которые реально
  // переносятся, — иначе односимвольное сообщение выродилось бы в пробел.
  final needsWrap = parsers.any((parser) => parser.length > boxWidth);
  final textWidth = boxWidth - theme.main.lineBreak.length;
  if (needsWrap && textWidth <= 0) {
    return LogBox.raw(
      boxWidth,
      [' ' * boxWidth],
      debugName: debugName,
    );
  }

  final boxLines = <String>[];
  for (final parser in parsers) {
    if (parser.length <= boxWidth) {
      boxLines.add(
        parser.applyConstraints(
          log,
          theme,
          LogConstraints.exact(boxWidth),
          textAlign: textAlign,
        ),
      );
      continue;
    }

    var start = 0;
    final end = parser.length;
    while (start < end) {
      final remaining = end - start;
      if (remaining <= textWidth) {
        boxLines.add(
          '${fixDanglingSurrogates(parser.substring(start)).applyConstraints(
            log,
            theme,
            LogConstraints.exact(textWidth),
            textAlign: textAlign,
          )}${' ' * theme.main.lineBreak.length}',
        );
        start = end;
      } else if (maxLines == null || maxLines > boxLines.length + 1) {
        boxLines.add(
          '${fixDanglingSurrogates(parser.substring(start, maxLength: textWidth))}'
          '${theme.styledLineBreak}',
        );
        start += textWidth;
      } else {
        // Последняя (обрезанная) строка не содержит символа переноса и
        // занимает полную ширину бокса — иначе была бы на колонку уже.
        boxLines.add(
          parser.terminatedSubstring(
            theme.main.ellipsis,
            theme.data.ellipsisStyle,
            start,
            maxLength: boxWidth,
          ),
        );
        start += boxWidth;
        break;
      }
    }
  }

  String? boxVerticalFiller;
  if (verticalFiller != null) {
    final parser = ansi.Parser(verticalFiller);
    boxVerticalFiller = parser.applyConstraints(
      log,
      theme,
      LogConstraints.exact(boxWidth),
      textAlign: textAlign,
      // См. комментарий в фабрике LogBox: без многоточия на филлерах.
      showEllipsis: false,
    );
  }

  return LogBox(
    log,
    theme,
    boxLines,
    constraints: constraints,
    textAlign: textAlign,
    verticalFiller: boxVerticalFiller,
    verticalAlign: verticalAlign,
    debugName: debugName,
  );
}