styleDiff function

String styleDiff(
  1. UvStyle? from,
  2. UvStyle? to
)

Returns the SGR diff needed to transition from from to to.

Implementation

String styleDiff(UvStyle? from, UvStyle? to) {
  if (from == null && to == null) return '';
  if (from != null && to != null && from == to) return '';
  if (from == null) return styleToSgr(to ?? const UvStyle());

  final simple = _simpleRgbStyleDiff(from, to);
  if (simple != null) return simple;

  if (to == null || to.isZero) {
    // Resetting all styles is cheaper than calculating diffs.
    return UvAnsi.resetStyle;
  }

  final sb = StringBuffer();
  var sep = false;
  void add(String s) {
    if (sep) sb.write(';');
    sb.write(s);
    sep = true;
  }

  if (from.fg != to.fg) {
    add(_colorDiffCode(to.fg, _ColorTarget.fg));
  }
  if (from.bg != to.bg) {
    add(_colorDiffCode(to.bg, _ColorTarget.bg));
  }
  if (from.underlineColor != to.underlineColor) {
    add(_colorDiffCode(to.underlineColor, _ColorTarget.underline));
  }

  final fromBold = (from.attrs & Attr.bold) != 0;
  final fromFaint = (from.attrs & Attr.faint) != 0;
  final fromItalic = (from.attrs & Attr.italic) != 0;
  final fromUnderline = from.underline != UnderlineStyle.none;
  final fromBlink = (from.attrs & Attr.blink) != 0;
  final fromRapidBlink = (from.attrs & Attr.rapidBlink) != 0;
  final fromReverse = (from.attrs & Attr.reverse) != 0;
  final fromConceal = (from.attrs & Attr.conceal) != 0;
  final fromStrikethrough = (from.attrs & Attr.strikethrough) != 0;

  final toBold = (to.attrs & Attr.bold) != 0;
  final toFaint = (to.attrs & Attr.faint) != 0;
  final toItalic = (to.attrs & Attr.italic) != 0;
  final toUnderline = to.underline != UnderlineStyle.none;
  final toBlink = (to.attrs & Attr.blink) != 0;
  final toRapidBlink = (to.attrs & Attr.rapidBlink) != 0;
  final toReverse = (to.attrs & Attr.reverse) != 0;
  final toConceal = (to.attrs & Attr.conceal) != 0;
  final toStrikethrough = (to.attrs & Attr.strikethrough) != 0;

  var boldChanged = fromBold != toBold;
  var faintChanged = fromFaint != toFaint;
  if (boldChanged || faintChanged) {
    if ((fromBold && !toBold) || (fromFaint && !toFaint)) {
      add('22');
      boldChanged = true;
      faintChanged = true;
    }
  }

  final italicChanged = fromItalic != toItalic;
  if (italicChanged && !toItalic) {
    add('23');
  }

  final underlineChanged =
      (fromUnderline != toUnderline) || (from.underline != to.underline);
  if (underlineChanged && !toUnderline) {
    add('24');
  }

  var blinkChanged = fromBlink != toBlink;
  var rapidBlinkChanged = fromRapidBlink != toRapidBlink;
  if (blinkChanged || rapidBlinkChanged) {
    if ((fromBlink && !toBlink) || (fromRapidBlink && !toRapidBlink)) {
      add('25');
      blinkChanged = true;
      rapidBlinkChanged = true;
    }
  }

  final reverseChanged = fromReverse != toReverse;
  if (reverseChanged && !toReverse) {
    add('27');
  }

  final concealChanged = fromConceal != toConceal;
  if (concealChanged && !toConceal) {
    add('28');
  }

  final strikethroughChanged = fromStrikethrough != toStrikethrough;
  if (strikethroughChanged && !toStrikethrough) {
    add('29');
  }

  if (boldChanged && toBold) add('1');
  if (faintChanged && toFaint) add('2');
  if (italicChanged && toItalic) add('3');
  if (underlineChanged &&
      toUnderline &&
      to.underline == UnderlineStyle.single) {
    add('4');
  }
  if (blinkChanged && toBlink) add('5');
  if (rapidBlinkChanged && toRapidBlink) add('6');
  if (reverseChanged && toReverse) add('7');
  if (concealChanged && toConceal) add('8');
  if (strikethroughChanged && toStrikethrough) add('9');

  if (underlineChanged &&
      toUnderline &&
      to.underline != UnderlineStyle.single) {
    add(switch (to.underline) {
      UnderlineStyle.none => '24',
      UnderlineStyle.single => '4',
      UnderlineStyle.double => '4:2',
      UnderlineStyle.curly => '4:3',
      UnderlineStyle.dotted => '4:4',
      UnderlineStyle.dashed => '4:5',
    });
  }

  if (!sep) return '';
  return '\x1b[${sb.toString()}m';
}