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);