dart-lang / yaml_edit

A library for YAML manipulation with comment and whitespace preservation.
https://pub.dev/packages/yaml_edit
BSD 3-Clause "New" or "Revised" License
27 stars 16 forks source link

Proposed auxiliary method wrapAsCustomStyledYamlNode #26

Open jonasfj opened 1 year ago

jonasfj commented 1 year ago

This probably needs some test cases, and I'm sure the code can be improved further!

void main() {
  final doc = YamlEditor('');
  doc.update(
      [],
      wrapAsCustomStyledYamlNode({
        'title': 'Short string as title',
        'description': [
          'Multiple lines with losts of text',
          'that you really makes you want',
          'the YAML to be written with literal strings',
        ].join('\n'),
      }));
  print(doc.toString());
}

YamlNode wrapAsCustomStyledYamlNode(
  Object? value, {
  CollectionStyle Function(Map map, int depth) styleMap = _defaultMapStyle,
  CollectionStyle Function(List list, int depth) styleList = _defaultListStyle,
  ScalarStyle Function(String string, int depth) styleString =
      _defaultStringStyle,
}) {
  YamlNode wrap(Object? value, int depth) {
    if (value is YamlScalar) {
      wrapAsYamlNode(value); // assert valid scalar
      return value;
    } else if (value is YamlList) {
      for (final item in value.nodes) {
        wrapAsYamlNode(item); // assert valid scalar
      }
      return value;
    } else if (value is YamlMap) {
      /// Both [entry.key] and [entry.values] are guaranteed to be [YamlNode]s,
      /// so running this will just assert that they are valid scalars.
      for (final entry in value.nodes.entries) {
        wrapAsYamlNode(entry.key);
        wrapAsYamlNode(entry.value);
      }
      return value;
    } else if (value is Map) {
      return wrapAsYamlNode({
        for (final kv in value.entries)
          wrap(kv.key, depth + 1): wrap(kv.value, depth + 1),
      }, collectionStyle: styleMap(value, depth));
    } else if (value is List) {
      return wrapAsYamlNode({
        for (final v in value) wrap(v, depth + 1),
      }, collectionStyle: styleList(value, depth));
    } else if (value is String) {
      return wrapAsYamlNode(value, scalarStyle: styleString(value, depth));
    } else {
      return wrapAsYamlNode(value);
    }
  }

  return wrap(value, 0);
}

int _sizeOfScalar(dynamic value) => value == null ? 4 : '$value'.length;

CollectionStyle _defaultMapStyle(Map map, int depth) {
  if (map.values.any((value) => value is Map || value is List)) {
    return CollectionStyle.BLOCK;
  }
  final size = map.entries.fold<int>(
    0,
    (sum, entry) => sum + _sizeOfScalar(entry.key) + _sizeOfScalar(entry.value),
  );
  if (size < 80) {
    return CollectionStyle.FLOW;
  }
  return CollectionStyle.BLOCK;
}

CollectionStyle _defaultListStyle(List list, int depth) {
  if (list.any((value) => value is Map || value is List)) {
    return CollectionStyle.BLOCK;
  }
  final size = list.fold<int>(
    0,
    (sum, value) => sum + _sizeOfScalar(value),
  );
  if (size < 80) {
    return CollectionStyle.FLOW;
  }
  return CollectionStyle.BLOCK;
}

ScalarStyle _defaultStringStyle(String string, int depth) {
  if (string.contains('\n')) {
    return ScalarStyle.LITERAL;
  }
  if (string.length > 80) {
    return ScalarStyle.FOLDED;
  }
  if (!string.contains('\'')) {
    if (!string.contains('"')) {
      return ScalarStyle.PLAIN;
    }
    return ScalarStyle.SINGLE_QUOTED;
  }
  return ScalarStyle.DOUBLE_QUOTED;
}
jonasfj commented 1 year ago

Maybe, there needs to be some logic around depth... I suppose it would be nice with an argument that always goes into flow-mode for a depth higher than some threshold, a reasonable default might be 16 (at that point block-mode becomes pretty sketchy to read, I'm just guessing).

seifibrahim32 commented 1 year ago

I need to try this mr Jonas @jonasfj

jonasfj commented 1 year ago

@seifibrahim32, please do give it a try.

I think one of the important aspects is to have proper tests. Such that we know that the formatting works, and behaves reasonably.

Perhaps, it'd be a good idea to start with https://github.com/dart-lang/yaml_edit/issues/33 (I just filed it, hehe) But really, adding the functions above is probably not too hard. The hard part is making sure we have tests, so that we're reasonably confident that these things work.

seifibrahim32 commented 1 year ago

Sure i will try to take a look

seifibrahim32 commented 1 year ago

Let me take a try :))

seifibrahim32 commented 1 month ago

@jonasfj I will add some tests if there are \n as mentioned in #33

seifibrahim32 commented 1 month ago

This function is better since If we used the normal wrapAsYamlNode it wont handle spaces \n or either indentation for scalars.

seifibrahim32 commented 1 month ago

We need to handle some tests so it clears this method's cases.