Updating package:yaml_edit to deal with empty nodes (#57)
* Fixed bug in empty values
* Restored example
* Restored example to an earlier version
* Removed unused dependency in example
* Removed printing of yaml
* Fixed bugs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76170e6..9ea9c51 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## v1.0.1
+
+- Updated behavior surrounding list and map removal.
+- Fixed bug in dealing with empty values.
+
## v1.0.0
- Initial release.
diff --git a/lib/src/list_mutations.dart b/lib/src/list_mutations.dart
index 3c8e361..5f5cfdc 100644
--- a/lib/src/list_mutations.dart
+++ b/lib/src/list_mutations.dart
@@ -29,13 +29,13 @@
RangeError.checkValueInInterval(index, 0, list.length - 1);
final currValue = list.nodes[index];
- final offset = currValue.span.start.offset;
+ var offset = currValue.span.start.offset;
final yaml = yamlEdit.toString();
String valueString;
/// We do not use [_formatNewBlock] since we want to only replace the contents
/// of this node while preserving comments/whitespace, while [_formatNewBlock]
- /// produces a string represnetation of a new node.
+ /// produces a string representation of a new node.
if (list.style == CollectionStyle.BLOCK) {
final listIndentation = getListIndentation(yaml, list);
final indentation = listIndentation + getIndentation(yamlEdit);
@@ -55,7 +55,12 @@
valueString += lineEnding;
}
- final end = getContentSensitiveEnd(currValue);
+ var end = getContentSensitiveEnd(currValue);
+ if (end <= offset) {
+ offset++;
+ end = offset;
+ valueString = ' ' + valueString;
+ }
return SourceEdit(offset, end - offset, valueString);
} else {
@@ -243,11 +248,12 @@
ArgumentError.checkNotNull(list, 'list');
RangeError.checkValueInInterval(index, 0, list.length - 1);
+ var end = getContentSensitiveEnd(nodeToRemove);
+
/// If we are removing the last element in a block list, convert it into a
/// flow empty list.
if (list.length == 1) {
final start = list.span.start.offset;
- final end = getContentSensitiveEnd(nodeToRemove);
return SourceEdit(start, end - start, '[]');
}
@@ -255,26 +261,53 @@
final yaml = yamlEdit.toString();
final span = nodeToRemove.span;
- /// The general removal strategy is to remove everything that starts from
- /// [nodeToRemove]'s dash to the next node's dash.
+ /// Adjust the end to clear the new line after the end too.
///
+ /// We do this because we suspect that our users will want the inline
+ /// comments to disappear too.
+ final nextNewLine = yaml.indexOf('\n', end);
+ if (nextNewLine != -1) {
+ end = nextNewLine + 1;
+ }
+
+ /// If the value is empty
+ if (span.length == 0) {
+ var start = span.start.offset;
+ return SourceEdit(start, end - start, '');
+ }
+
/// -1 accounts for the fact that the content can start with a dash
var start = yaml.lastIndexOf('-', span.start.offset - 1);
- var end = yaml.lastIndexOf('\n', list.span.end.offset) + 1;
- if (index < list.length - 1) {
- final nextNode = list.nodes[index + 1];
- end = yaml.lastIndexOf('-', nextNode.span.start.offset - 1);
- } else {
- /// If there is a possibility that there is a `-` or `\n` before the node
- if (start > 0) {
- final lastHyphen = yaml.lastIndexOf('-', start - 1);
- final lastNewLine = yaml.lastIndexOf('\n', start - 1);
- if (lastHyphen > lastNewLine) {
- start = lastHyphen + 2;
- } else if (lastNewLine > lastHyphen) {
- start = lastNewLine + 1;
+ /// Check if there is a `-` before the node
+ if (start > 0) {
+ final lastHyphen = yaml.lastIndexOf('-', start - 1);
+ final lastNewLine = yaml.lastIndexOf('\n', start - 1);
+ if (lastHyphen > lastNewLine) {
+ start = lastHyphen + 2;
+
+ /// If there is a `-` before the node, we need to check if we have
+ /// to update the indentation of the next node.
+ if (index < list.length - 1) {
+ /// Since [end] is currently set to the next new line after the current
+ /// node, check if we see a possible comment first, or a hyphen first.
+ /// Note that no actual content can appear here.
+ ///
+ /// We check this way because the start of a span in a block list is
+ /// the start of its value, and checking from the back leaves us
+ /// easily confused if there are comments that have dashes in them.
+ final nextHash = yaml.indexOf('#', end);
+ final nextHyphen = yaml.indexOf('-', end);
+ final nextNewLine = yaml.indexOf('\n', end);
+
+ /// If [end] is on the same line as the hyphen of the next node
+ if ((nextHash == -1 || nextHyphen < nextHash) &&
+ nextHyphen < nextNewLine) {
+ end = nextHyphen;
+ }
}
+ } else if (lastNewLine > lastHyphen) {
+ start = lastNewLine + 1;
}
}
diff --git a/lib/src/map_mutations.dart b/lib/src/map_mutations.dart
index b0dec1f..b3c3cf8 100644
--- a/lib/src/map_mutations.dart
+++ b/lib/src/map_mutations.dart
@@ -165,9 +165,14 @@
valueAsString = lineEnding + valueAsString;
}
- /// +2 accounts for the colon
+ /// +1 accounts for the colon
final start = keyNode.span.end.offset + 1;
- final end = getContentSensitiveEnd(map.nodes[key]);
+ var end = getContentSensitiveEnd(map.nodes[key]);
+
+ /// `package:yaml` parses empty nodes in a way where the start/end of the
+ /// empty value node is the end of the key node, so we have to adjust for
+ /// this.
+ if (end < start) end = start + 1;
return SourceEdit(start, end - start, ' ' + valueAsString);
}
@@ -206,24 +211,33 @@
var start = keySpan.start.offset;
+ /// Adjust the end to clear the new line after the end too.
+ ///
+ /// We do this because we suspect that our users will want the inline
+ /// comments to disappear too.
+ final nextNewLine = yaml.indexOf('\n', end);
+ if (nextNewLine != -1) {
+ end = nextNewLine + 1;
+ }
+
final nextNode = getNextKeyNode(map, keyNode);
- if (nextNode == null) {
- /// If there is a possibility that there is a `-` or `\n` before the node
- if (start > 0) {
- final lastHyphen = yaml.lastIndexOf('-', start - 1);
- final lastNewLine = yaml.lastIndexOf('\n', start - 1);
- if (lastHyphen > lastNewLine) {
- start = lastHyphen + 2;
- } else if (lastNewLine > lastHyphen) {
- start = lastNewLine + 1;
+
+ if (start > 0) {
+ final lastHyphen = yaml.lastIndexOf('-', start - 1);
+ final lastNewLine = yaml.lastIndexOf('\n', start - 1);
+ if (lastHyphen > lastNewLine) {
+ start = lastHyphen + 2;
+
+ /// If there is a `-` before the node, and the end is on the same line
+ /// as the next node, we need to add the necessary offset to the end to
+ /// make sure the next node has the correct indentation.
+ if (nextNode != null &&
+ nextNode.span.start.offset - end <= nextNode.span.start.column) {
+ end += nextNode.span.start.column;
}
+ } else if (lastNewLine > lastHyphen) {
+ start = lastNewLine + 1;
}
- final nextNewLine = yaml.indexOf('\n', end);
- if (nextNewLine != -1) {
- end = nextNewLine + 1;
- }
- } else {
- end = nextNode.span.start.offset;
}
return SourceEdit(start, end - start, '');
diff --git a/pubspec.yaml b/pubspec.yaml
index a83bea0..a33cc09 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: yaml_edit
-version: 1.0.0
+version: 1.0.1
description: A library for YAML manipulation with comment and whitespace preservation.
homepage: https://github.com/google/dart-neats/tree/master/yaml_edit
repository: https://github.com/google/dart-neats.git
@@ -15,5 +15,4 @@
test: ^1.14.4
path: ^1.6.2
environment:
- sdk: '>=2.4.0 <3.0.0'
-
\ No newline at end of file
+ sdk: ">=2.4.0 <3.0.0"
diff --git a/test/remove_test.dart b/test/remove_test.dart
index 7362d0d..21d9c41 100644
--- a/test/remove_test.dart
+++ b/test/remove_test.dart
@@ -72,6 +72,64 @@
'''));
});
+ test('empty value', () {
+ final doc = YamlEditor('''
+a: 1
+b:
+c: 3
+''');
+ doc.remove(['b']);
+ expect(doc.toString(), equals('''
+a: 1
+c: 3
+'''));
+ });
+
+ test('empty value (2)', () {
+ final doc = YamlEditor('''
+- a: 1
+ b:
+ c: 3
+''');
+ doc.remove([0, 'b']);
+ expect(doc.toString(), equals('''
+- a: 1
+ c: 3
+'''));
+ });
+
+ test('empty value (3)', () {
+ final doc = YamlEditor('''
+- a: 1
+ b:
+
+ c: 3
+''');
+ doc.remove([0, 'b']);
+ expect(doc.toString(), equals('''
+- a: 1
+
+ c: 3
+'''));
+ });
+
+ test('preserves comments', () {
+ final doc = YamlEditor('''
+a: 1 # preserved 1
+# preserved 2
+b: 2
+# preserved 3
+c: 3 # preserved 4
+''');
+ doc.remove(['b']);
+ expect(doc.toString(), equals('''
+a: 1 # preserved 1
+# preserved 2
+# preserved 3
+c: 3 # preserved 4
+'''));
+ });
+
test('final element in map', () {
final doc = YamlEditor('''
a: 1
@@ -138,46 +196,6 @@
});
});
- group('block list', () {
- test('last element should return flow empty list', () {
- final doc = YamlEditor('''
-- 0
-''');
- doc.remove([0]);
- expect(doc.toString(), equals('''
-[]
-'''));
- });
-
- test('last element should return flow empty list (2)', () {
- final doc = YamlEditor('''
-a: [1]
-b: [3]
-''');
- doc.remove(['a', 0]);
- expect(doc.toString(), equals('''
-a: []
-b: [3]
-'''));
- });
-
- test('last element should return flow empty list (3)', () {
- final doc = YamlEditor('''
-a:
- - 1
-b:
- - 3
-''');
- doc.remove(['a', 0]);
- expect(doc.toString(), equals('''
-a:
- []
-b:
- - 3
-'''));
- });
- });
-
group('flow map', () {
test('(1)', () {
final doc = YamlEditor('{a: 1, b: 2, c: 3}');
@@ -204,6 +222,12 @@
expect(doc.toString(), equals('{"{}[],": {"{}[],": 1, "}{[],": 3}}'));
});
+ test('empty value', () {
+ final doc = YamlEditor('{a: 1, b:, c: 3}');
+ doc.remove(['b']);
+ expect(doc.toString(), equals('{a: 1, c: 3}'));
+ });
+
test('nested flow map ', () {
final doc = YamlEditor('{a: 1, b: {d: 4, e: 5}, c: 3}');
doc.remove(['b', 'd']);
@@ -223,6 +247,59 @@
});
group('block list', () {
+ test('empty value', () {
+ final doc = YamlEditor('''
+- 0
+-
+- 2
+''');
+ doc.remove([1]);
+ expect(doc.toString(), equals('''
+- 0
+- 2
+'''));
+ });
+
+ test('last element should return flow empty list', () {
+ final doc = YamlEditor('''
+- 0
+''');
+ doc.remove([0]);
+ expect(doc.toString(), equals('''
+[]
+'''));
+ });
+
+ test('last element should return flow empty list (2)', () {
+ final doc = YamlEditor('''
+a:
+ - 1
+b: [3]
+''');
+ doc.remove(['a', 0]);
+ expect(doc.toString(), equals('''
+a:
+ []
+b: [3]
+'''));
+ });
+
+ test('last element should return flow empty list (3)', () {
+ final doc = YamlEditor('''
+a:
+ - 1
+b:
+ - 3
+''');
+ doc.remove(['a', 0]);
+ expect(doc.toString(), equals('''
+a:
+ []
+b:
+ - 3
+'''));
+ });
+
test('(1) ', () {
final doc = YamlEditor('''
- 0
@@ -285,21 +362,25 @@
test('with comments', () {
final doc = YamlEditor('''
-- 0
-- 1 # comments
-- 2
+- 0 # comment 0
+# comment 1
+- 1 # comment 2
+# comment 3
+- 2 # comment 4
- 3
''');
doc.remove([1]);
expect(doc.toString(), equals('''
-- 0
-- 2
+- 0 # comment 0
+# comment 1
+# comment 3
+- 2 # comment 4
- 3
'''));
expectYamlBuilderValue(doc, [0, 2, 3]);
});
- test('nested', () {
+ test('nested list', () {
final doc = YamlEditor('''
- - - 0
- 1
@@ -315,7 +396,7 @@
]);
});
- test('nested list', () {
+ test('nested list (2)', () {
final doc = YamlEditor('''
- - 0
- 1
@@ -328,7 +409,7 @@
expectYamlBuilderValue(doc, [2]);
});
- test('nested list (2)', () {
+ test('nested list (3)', () {
final doc = YamlEditor('''
- - 0
- 1
@@ -345,7 +426,7 @@
]);
});
- test('nested list (3)', () {
+ test('nested list (4)', () {
final doc = YamlEditor('''
-
- - 0
@@ -365,6 +446,41 @@
]
]);
});
+
+ test('nested list (5)', () {
+ final doc = YamlEditor('''
+- - 0
+ -
+ 1
+''');
+ doc.remove([0, 0]);
+ expect(doc.toString(), equals('''
+- -
+ 1
+'''));
+ expectYamlBuilderValue(doc, [
+ [1]
+ ]);
+ });
+
+ test('nested list (6)', () {
+ final doc = YamlEditor('''
+- - 0 # -
+ # -
+ -
+ 1
+''');
+ doc.remove([0, 0]);
+ expect(doc.toString(), equals('''
+- # -
+ -
+ 1
+'''));
+ expectYamlBuilderValue(doc, [
+ [1]
+ ]);
+ });
+
test('nested map', () {
final doc = YamlEditor('''
- - a: b
diff --git a/test/test_case.dart b/test/test_case.dart
index 849bdf8..5f2bd0b 100644
--- a/test/test_case.dart
+++ b/test/test_case.dart
@@ -196,20 +196,26 @@
}
}
-/// Converts a [YamlList] into a Dart list.
-List _getValueFromYamlList(YamlList node) {
- return node.value.map((n) {
+/// Converts [yamlList] into a Dart list.
+List _getValueFromYamlList(YamlList yamlList) {
+ return yamlList.value.map((n) {
if (n is YamlNode) return _getValueFromYamlNode(n);
return n;
}).toList();
}
-/// Converts a [YamlMap] into a Dart Map.
-Map _getValueFromYamlMap(YamlMap node) {
- final keys = node.keys;
+/// Converts [yamlMap] into a Dart Map.
+Map _getValueFromYamlMap(YamlMap yamlMap) {
+ final keys = yamlMap.keys;
final result = {};
for (final key in keys) {
- result[key.value] = result[key].value;
+ final value = yamlMap[key];
+
+ if (value is YamlNode) {
+ result[key] = _getValueFromYamlNode(value);
+ } else {
+ result[key] = value;
+ }
}
return result;
diff --git a/test/testdata/input/pubspec.test b/test/testdata/input/pubspec.test
index eafc6b4..5559e1d 100644
--- a/test/testdata/input/pubspec.test
+++ b/test/testdata/input/pubspec.test
@@ -15,13 +15,15 @@
dependencies:
meta: ^1.1.8 # To annotate
+ # quiver_hashcode
quiver_hashcode: ^2.0.0 # For hashcodes
+ # yaml
yaml: ^2.2.1 # For YAML
dev_dependencies:
- pedantic: ^1.9.0
- test: ^1.14.4
+
---
- [update, ['dependencies', 'yaml'], ^3.2.0]
- [update, ['dependencies', 'retry'], ^3.0.1]
-- [remove, ['dependencies', 'meta']]
\ No newline at end of file
+- [remove, ['dependencies', 'meta']]
+- [update, ['dev_dependencies'], {'test': '^1.14.4'}]
\ No newline at end of file
diff --git a/test/testdata/output/pubspec.golden b/test/testdata/output/pubspec.golden
index f058bb8..7cd5b25 100644
--- a/test/testdata/output/pubspec.golden
+++ b/test/testdata/output/pubspec.golden
@@ -13,12 +13,13 @@
dependencies:
meta: ^1.1.8 # To annotate
+ # quiver_hashcode
quiver_hashcode: ^2.0.0 # For hashcodes
+ # yaml
yaml: ^2.2.1 # For YAML
dev_dependencies:
- pedantic: ^1.9.0
- test: ^1.14.4
+
---
name: yaml_edit
description: A library for YAML manipulation with comment and whitespace preservation.
@@ -35,12 +36,13 @@
dependencies:
meta: ^1.1.8 # To annotate
+ # quiver_hashcode
quiver_hashcode: ^2.0.0 # For hashcodes
+ # yaml
yaml: ^3.2.0 # For YAML
dev_dependencies:
- pedantic: ^1.9.0
- test: ^1.14.4
+
---
name: yaml_edit
description: A library for YAML manipulation with comment and whitespace preservation.
@@ -57,13 +59,14 @@
dependencies:
meta: ^1.1.8 # To annotate
+ # quiver_hashcode
quiver_hashcode: ^2.0.0 # For hashcodes
+ # yaml
retry: ^3.0.1
yaml: ^3.2.0 # For YAML
dev_dependencies:
- pedantic: ^1.9.0
- test: ^1.14.4
+
---
name: yaml_edit
description: A library for YAML manipulation with comment and whitespace preservation.
@@ -79,10 +82,34 @@
sdk: ">=2.4.0 <3.0.0"
dependencies:
+ # quiver_hashcode
quiver_hashcode: ^2.0.0 # For hashcodes
+ # yaml
retry: ^3.0.1
yaml: ^3.2.0 # For YAML
dev_dependencies:
- pedantic: ^1.9.0
+
+---
+name: yaml_edit
+description: A library for YAML manipulation with comment and whitespace preservation.
+version: 1.0.0
+
+homepage: https://github.com/google/dart-neats/tree/master/yaml_edit
+
+repository: https://github.com/google/dart-neats.git
+
+issue_tracker: https://github.com/google/dart-neats/labels/pkg:yaml_edit
+
+environment:
+ sdk: ">=2.4.0 <3.0.0"
+
+dependencies:
+ # quiver_hashcode
+ quiver_hashcode: ^2.0.0 # For hashcodes
+ # yaml
+ retry: ^3.0.1
+ yaml: ^3.2.0 # For YAML
+
+dev_dependencies:
test: ^1.14.4
diff --git a/test/update_test.dart b/test/update_test.dart
index f8492a1..bee48b6 100644
--- a/test/update_test.dart
+++ b/test/update_test.dart
@@ -103,6 +103,22 @@
expectYamlBuilderValue(doc, {'test': []});
});
+ test('empty value', () {
+ final doc = YamlEditor('YAML: ');
+ doc.update(['YAML'], 'test');
+
+ expect(doc.toString(), equals('YAML: test'));
+ expectYamlBuilderValue(doc, {'YAML': 'test'});
+ });
+
+ test('empty value (2)', () {
+ final doc = YamlEditor('YAML : ');
+ doc.update(['YAML'], 'test');
+
+ expect(doc.toString(), equals('YAML : test'));
+ expectYamlBuilderValue(doc, {'YAML': 'test'});
+ });
+
test('with comment', () {
final doc = YamlEditor("YAML: YAML Ain't Markup Language # comment");
doc.update(['YAML'], 'test');
@@ -413,6 +429,22 @@
expectYamlBuilderValue(doc, {'YAML': 'd9]zH`FoYC/>]'});
});
+ test('empty value', () {
+ final doc = YamlEditor('{YAML: }');
+ doc.update(['YAML'], 'test');
+
+ expect(doc.toString(), equals('{YAML: test}'));
+ expectYamlBuilderValue(doc, {'YAML': 'test'});
+ });
+
+ test('empty value (2)', () {
+ final doc = YamlEditor('{YAML: , hi: bye}');
+ doc.update(['YAML'], 'test');
+
+ expect(doc.toString(), equals('{YAML: test, hi: bye}'));
+ expectYamlBuilderValue(doc, {'YAML': 'test', 'hi': 'bye'});
+ });
+
test('with spacing', () {
final doc = YamlEditor(
"{ YAML: YAML Ain't Markup Language , XML: Extensible Markup Language , HTML: Hypertext Markup Language }");
@@ -439,6 +471,22 @@
expectYamlBuilderValue(doc, ['test']);
});
+ test('(2)', () {
+ final doc = YamlEditor('''
+- 1
+-
+- 3
+''');
+ doc.update([1], 2);
+
+ expect(doc.toString(), equals('''
+- 1
+- 2
+- 3
+'''));
+ expectYamlBuilderValue(doc, [1, 2, 3]);
+ });
+
test('nested (1)', () {
final doc = YamlEditor("- YAML Ain't Markup Language");
doc.update([0], [1, 2]);
@@ -457,6 +505,22 @@
expectYamlBuilderValue(doc, ['test']);
});
+ test('with comment (2)', () {
+ final doc = YamlEditor('''
+- 1
+- # comment
+- 3
+''');
+ doc.update([1], 2);
+
+ expect(doc.toString(), equals('''
+- 1
+- 2 # comment
+- 3
+'''));
+ expectYamlBuilderValue(doc, [1, 2, 3]);
+ });
+
test('with comment and spaces', () {
final doc = YamlEditor("- YAML Ain't Markup Language # comment");
doc.update([0], 'test');
@@ -579,6 +643,8 @@
]);
});
+ /// We cannot have empty values in a flow list.
+
test('with spacing (1)', () {
final doc = YamlEditor('[ 0 , 1 , 2 , 3 ]');
doc.update([1], 4);