Delete lib/src/yaml_edit and use yaml_edit package instead (#3138)
diff --git a/lib/src/command/add.dart b/lib/src/command/add.dart
index faabf99..728f8ea 100644
--- a/lib/src/command/add.dart
+++ b/lib/src/command/add.dart
@@ -7,6 +7,7 @@
import 'package:path/path.dart' as p;
import 'package:pub_semver/pub_semver.dart';
import 'package:yaml/yaml.dart';
+import 'package:yaml_edit/yaml_edit.dart';
import '../command.dart';
import '../entrypoint.dart';
@@ -20,7 +21,6 @@
import '../solver.dart';
import '../source/path.dart';
import '../utils.dart';
-import '../yaml_edit/editor.dart';
/// Handles the `add` pub command. Adds a dependency to `pubspec.yaml` and gets
/// the package. The user may pass in a git constraint, host url, or path as
diff --git a/lib/src/command/remove.dart b/lib/src/command/remove.dart
index d1603d5..f505785 100644
--- a/lib/src/command/remove.dart
+++ b/lib/src/command/remove.dart
@@ -5,6 +5,7 @@
// @dart=2.10
import 'package:yaml/yaml.dart';
+import 'package:yaml_edit/yaml_edit.dart';
import '../command.dart';
import '../entrypoint.dart';
@@ -13,7 +14,6 @@
import '../package.dart';
import '../pubspec.dart';
import '../solver.dart';
-import '../yaml_edit/editor.dart';
/// Handles the `remove` pub command. Removes dependencies from `pubspec.yaml`,
/// and performs an operation similar to `pub get`. Unlike `pub add`, this
diff --git a/lib/src/command/upgrade.dart b/lib/src/command/upgrade.dart
index 05b62f7..39a42c7 100644
--- a/lib/src/command/upgrade.dart
+++ b/lib/src/command/upgrade.dart
@@ -8,6 +8,7 @@
import 'dart:io';
import 'package:pub_semver/pub_semver.dart';
+import 'package:yaml_edit/yaml_edit.dart';
import '../command.dart';
import '../command_runner.dart';
@@ -22,7 +23,6 @@
import '../pubspec_utils.dart';
import '../solver.dart';
import '../source/hosted.dart';
-import '../yaml_edit/editor.dart';
/// Handles the `upgrade` pub command.
class UpgradeCommand extends PubCommand {
diff --git a/lib/src/yaml_edit/editor.dart b/lib/src/yaml_edit/editor.dart
deleted file mode 100644
index e68cec1..0000000
--- a/lib/src/yaml_edit/editor.dart
+++ /dev/null
@@ -1,639 +0,0 @@
-// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// @dart=2.10
-
-import 'package:meta/meta.dart';
-import 'package:yaml/yaml.dart';
-
-import 'equality.dart';
-import 'errors.dart';
-import 'list_mutations.dart';
-import 'map_mutations.dart';
-import 'source_edit.dart';
-import 'strings.dart';
-import 'utils.dart';
-import 'wrap.dart';
-
-/// An interface for modififying [YAML][1] documents while preserving comments
-/// and whitespaces.
-///
-/// YAML parsing is supported by `package:yaml`, and modifications are performed
-/// as string operations. An error will be thrown if internal assertions fail -
-/// such a situation should be extremely rare, and should only occur with
-/// degenerate formatting.
-///
-/// Most modification methods require the user to pass in an [Iterable<Object>]
-/// path that holds the keys/indices to navigate to the element.
-///
-/// **Example:**
-/// ```yaml
-/// a: 1
-/// b: 2
-/// c:
-/// - 3
-/// - 4
-/// - {e: 5, f: [6, 7]}
-/// ```
-///
-/// To get to `7`, our path will be `['c', 2, 'f', 1]`. The path for the base
-/// object is the empty array `[]`. All modification methods will throw a
-/// [ArgumentError] if the path provided is invalid. Note also that that the
-/// order of elements in the path is important, and it should be arranged in
-/// order of calling, with the first element being the first key or index to be
-/// called.
-///
-/// In most modification methods, users are required to pass in a value to be
-/// used for updating the YAML tree. This value is only allowed to either be a
-/// valid scalar that is recognizable by YAML (i.e. `bool`, `String`, `List`,
-/// `Map`, `num`, `null`) or a [YamlNode]. Should the user want to specify
-/// the style to be applied to the value passed in, the user may wrap the value
-/// using [wrapAsYamlNode] while passing in the appropriate `scalarStyle` or
-/// `collectionStyle`. While we try to respect the style that is passed in,
-/// there will be instances where the formatting will not result in valid YAML,
-/// and as such we will fallback to a default formatting while preserving the
-/// content.
-///
-/// To dump the YAML after all the modifications have been completed, simply
-/// call [toString()].
-///
-/// [1]: https://yaml.org/
-@sealed
-class YamlEditor {
- final List<SourceEdit> _edits = [];
-
- /// List of [SourceEdit]s that have been applied to [_yaml] since the creation
- /// of this instance, in chronological order. Intended to be compatible with
- /// `package:analysis_server`.
- ///
- /// The [SourceEdit] objects can be serialized to JSON using the `toJSON`
- /// function, deserialized using [SourceEdit.fromJson], and applied to a
- /// string using the `apply` function. Multiple [SourceEdit]s can be applied
- /// to a string using [SourceEdit.applyAll].
- ///
- /// For more information, refer to the [SourceEdit] class.
- List<SourceEdit> get edits => [..._edits];
-
- /// Current YAML string.
- String _yaml;
-
- /// Root node of YAML AST.
- YamlNode _contents;
-
- /// Stores the list of nodes in [_contents] that are connected by aliases.
- ///
- /// When a node is anchored with an alias and subsequently referenced,
- /// the full content of the anchored node is thought to be copied in the
- /// following references.
- ///
- /// **Example:**
- /// ```dart
- /// a: &SS Sammy Sosa
- /// b: *SS
- /// ```
- ///
- /// is equivalent to
- ///
- /// ```dart
- /// a: Sammy Sosa
- /// b: Sammy Sosa
- /// ```
- ///
- /// As such, aliased nodes have to be treated with special caution when
- /// any modification is taking place.
- ///
- /// See 7.1 Alias Nodes: https://yaml.org/spec/1.2/spec.html#id2786196
- Set<YamlNode> _aliases = {};
-
- /// Returns the current YAML string.
- @override
- String toString() => _yaml;
-
- factory YamlEditor(String yaml) => YamlEditor._(yaml);
-
- YamlEditor._(this._yaml) {
- ArgumentError.checkNotNull(_yaml);
- _initialize();
- }
-
- /// Loads [_contents] from [_yaml], and traverses the YAML tree formed to
- /// detect alias nodes.
- void _initialize() {
- _contents = loadYamlNode(_yaml);
- _aliases = {};
-
- /// Performs a DFS on [_contents] to detect alias nodes.
- final visited = <YamlNode>{};
- void collectAliases(YamlNode node) {
- if (visited.add(node)) {
- if (node is YamlMap) {
- node.nodes.forEach((key, value) {
- collectAliases(key);
- collectAliases(value);
- });
- } else if (node is YamlList) {
- node.nodes.forEach(collectAliases);
- }
- } else {
- _aliases.add(node);
- }
- }
-
- collectAliases(_contents);
- }
-
- /// Parses the document to return [YamlNode] currently present at [path].
- ///
- /// If no [YamlNode]s exist at [path], the result of invoking the [orElse]
- /// function is returned.
- ///
- /// If [orElse] is omitted, it defaults to throwing a [ArgumentError].
- ///
- /// To get `null` when [path] does not point to a value in the [YamlNode]-tree,
- /// simply pass `orElse: () => null`.
- ///
- /// **Example:** (using orElse)
- /// ```dart
- /// final myYamlEditor('{"key": "value"}');
- /// final value = myYamlEditor.valueAt(['invalid', 'path'], orElse: () => null);
- /// print(value) // null
- /// ```
- ///
- /// **Example:** (common usage)
- /// ```dart
- /// final doc = YamlEditor('''
- /// a: 1
- /// b:
- /// d: 4
- /// e: [5, 6, 7]
- /// c: 3
- /// ''');
- /// print(doc.parseAt(['b', 'e', 2])); // 7
- /// ```
- /// The value returned by [parseAt] is invalidated when the documented is
- /// mutated, as illustrated below:
- ///
- /// **Example:** (old [parseAt] value is invalidated)
- /// ```dart
- /// final doc = YamlEditor("YAML: YAML Ain't Markup Language");
- /// final node = doc.parseAt(['YAML']);
- ///
- /// print(node.value); // Expected output: "YAML Ain't Markup Language"
- ///
- /// doc.update(['YAML'], 'YAML');
- ///
- /// final newNode = doc.parseAt(['YAML']);
- ///
- /// // Note that the value does not change
- /// print(newNode.value); // "YAML"
- /// print(node.value); // "YAML Ain't Markup Language"
- /// ```
- YamlNode parseAt(Iterable<Object> path, {YamlNode Function() orElse}) {
- ArgumentError.checkNotNull(path, 'path');
-
- return _traverse(path, orElse: orElse);
- }
-
- /// Sets [value] in the [path].
- ///
- /// There is a subtle difference between [update] and [remove] followed by
- /// an [insertIntoList], because [update] preserves comments at the same level.
- ///
- /// Throws a [ArgumentError] if [path] is invalid.
- ///
- /// **Example:** (using [update])
- /// ```dart
- /// final doc = YamlEditor('''
- /// - 0
- /// - 1 # comment
- /// - 2
- /// ''');
- /// doc.update([1], 'test');
- /// ```
- ///
- /// **Expected Output:**
- /// ```yaml
- /// - 0
- /// - test # comment
- /// - 2
- /// ```
- ///
- /// **Example:** (using [remove] and [insertIntoList])
- /// ```dart
- /// final doc2 = YamlEditor('''
- /// - 0
- /// - 1 # comment
- /// - 2
- /// ''');
- /// doc2.remove([1]);
- /// doc2.insertIntoList([], 1, 'test');
- /// ```
- ///
- /// **Expected Output:**
- /// ```yaml
- /// - 0
- /// - test
- /// - 2
- /// ```
- void update(Iterable<Object> path, Object value) {
- ArgumentError.checkNotNull(path, 'path');
-
- final valueNode = wrapAsYamlNode(value);
-
- if (path.isEmpty) {
- final start = _contents.span.start.offset;
- final end = getContentSensitiveEnd(_contents);
- final lineEnding = getLineEnding(_yaml);
- final edit = SourceEdit(
- start, end - start, yamlEncodeBlockString(valueNode, 0, lineEnding));
-
- return _performEdit(edit, path, valueNode);
- }
-
- final pathAsList = path.toList();
- final collectionPath = pathAsList.take(path.length - 1);
- final keyOrIndex = pathAsList.last;
- final parentNode = _traverse(collectionPath, checkAlias: true);
-
- if (parentNode is YamlList) {
- final expected = wrapAsYamlNode(
- [...parentNode.nodes]..[keyOrIndex] = valueNode,
- );
-
- return _performEdit(updateInList(this, parentNode, keyOrIndex, valueNode),
- collectionPath, expected);
- }
-
- if (parentNode is YamlMap) {
- final expectedMap =
- updatedYamlMap(parentNode, (nodes) => nodes[keyOrIndex] = valueNode);
- return _performEdit(updateInMap(this, parentNode, keyOrIndex, valueNode),
- collectionPath, expectedMap);
- }
-
- throw PathError.unexpected(
- path, 'Scalar $parentNode does not have key $keyOrIndex');
- }
-
- /// Appends [value] to the list at [path].
- ///
- /// Throws a [ArgumentError] if the element at the given path is not a
- /// [YamlList] or if the path is invalid.
- ///
- /// **Example:**
- /// ```dart
- /// final doc = YamlEditor('[0, 1]');
- /// doc.appendToList([], 2); // [0, 1, 2]
- /// ```
- void appendToList(Iterable<Object> path, Object value) {
- ArgumentError.checkNotNull(path, 'path');
- final yamlList = _traverseToList(path);
-
- insertIntoList(path, yamlList.length, value);
- }
-
- /// Prepends [value] to the list at [path].
- ///
- /// Throws a [ArgumentError] if the element at the given path is not a
- /// [YamlList] or if the path is invalid.
- ///
- /// **Example:**
- /// ```dart
- /// final doc = YamlEditor('[1, 2]');
- /// doc.prependToList([], 0); // [0, 1, 2]
- /// ```
- void prependToList(Iterable<Object> path, Object value) {
- ArgumentError.checkNotNull(path, 'path');
-
- insertIntoList(path, 0, value);
- }
-
- /// Inserts [value] into the list at [path].
- ///
- /// [index] must be non-negative and no greater than the list's length.
- ///
- /// Throws a [ArgumentError] if the element at the given path is not a
- /// [YamlList] or if the path is invalid.
- ///
- /// **Example:**
- /// ```dart
- /// final doc = YamlEditor('[0, 2]');
- /// doc.insertIntoList([], 1, 1); // [0, 1, 2]
- /// ```
- void insertIntoList(Iterable<Object> path, int index, Object value) {
- ArgumentError.checkNotNull(path, 'path');
- final valueNode = wrapAsYamlNode(value);
-
- final list = _traverseToList(path, checkAlias: true);
- RangeError.checkValueInInterval(index, 0, list.length);
-
- final edit = insertInList(this, list, index, valueNode);
- final expected = wrapAsYamlNode(
- [...list.nodes]..insert(index, valueNode),
- );
-
- _performEdit(edit, path, expected);
- }
-
- /// Changes the contents of the list at [path] by removing [deleteCount] items
- /// at [index], and inserting [values] in-place. Returns the elements that
- /// are deleted.
- ///
- /// [index] and [deleteCount] must be non-negative and [index] + [deleteCount]
- /// must be no greater than the list's length.
- ///
- /// Throws a [ArgumentError] if the element at the given path is not a
- /// [YamlList] or if the path is invalid.
- ///
- /// **Example:**
- /// ```dart
- /// final doc = YamlEditor('[Jan, March, April, June]');
- /// doc.spliceList([], 1, 0, ['Feb']); // [Jan, Feb, March, April, June]
- /// doc.spliceList([], 4, 1, ['May']); // [Jan, Feb, March, April, May]
- /// ```
- Iterable<YamlNode> spliceList(Iterable<Object> path, int index,
- int deleteCount, Iterable<Object> values) {
- ArgumentError.checkNotNull(path, 'path');
- ArgumentError.checkNotNull(index, 'index');
- ArgumentError.checkNotNull(deleteCount, 'deleteCount');
- ArgumentError.checkNotNull(values, 'values');
-
- final list = _traverseToList(path, checkAlias: true);
-
- RangeError.checkValueInInterval(index, 0, list.length);
- RangeError.checkValueInInterval(index + deleteCount, 0, list.length);
-
- final nodesToRemove = list.nodes.getRange(index, index + deleteCount);
-
- /// Perform addition of elements before removal to avoid scenarioes where
- /// a block list gets emptied out to {} to avoid changing collection styles
- /// where possible.
-
- /// Reverse [values] and insert them.
- final reversedValues = values.toList().reversed;
- for (final value in reversedValues) {
- insertIntoList(path, index, value);
- }
-
- for (var i = 0; i < deleteCount; i++) {
- remove([...path, index + values.length]);
- }
-
- return nodesToRemove;
- }
-
- /// Removes the node at [path]. Comments "belonging" to the node will be
- /// removed while surrounding comments will be left untouched.
- ///
- /// Throws a [ArgumentError] if [path] is invalid.
- ///
- /// **Example:**
- /// ```dart
- /// final doc = YamlEditor('''
- /// - 0 # comment 0
- /// # comment A
- /// - 1 # comment 1
- /// # comment B
- /// - 2 # comment 2
- /// ''');
- /// doc.remove([1]);
- /// ```
- ///
- /// **Expected Result:**
- /// ```dart
- /// '''
- /// - 0 # comment 0
- /// # comment A
- /// # comment B
- /// - 2 # comment 2
- /// '''
- /// ```
- YamlNode remove(Iterable<Object> path) {
- ArgumentError.checkNotNull(path, 'path');
-
- SourceEdit edit;
- YamlNode expectedNode;
- final nodeToRemove = _traverse(path, checkAlias: true);
-
- if (path.isEmpty) {
- edit = SourceEdit(0, _yaml.length, '');
-
- /// Parsing an empty YAML document returns `null`.
- _performEdit(edit, path, expectedNode);
- return nodeToRemove;
- }
-
- final pathAsList = path.toList();
- final collectionPath = pathAsList.take(path.length - 1);
- final keyOrIndex = pathAsList.last;
- final parentNode = _traverse(collectionPath);
-
- if (parentNode is YamlList) {
- edit = removeInList(this, parentNode, keyOrIndex);
- expectedNode = wrapAsYamlNode(
- [...parentNode.nodes]..removeAt(keyOrIndex),
- );
- } else if (parentNode is YamlMap) {
- edit = removeInMap(this, parentNode, keyOrIndex);
-
- expectedNode =
- updatedYamlMap(parentNode, (nodes) => nodes.remove(keyOrIndex));
- }
-
- _performEdit(edit, collectionPath, expectedNode);
-
- return nodeToRemove;
- }
-
- /// Traverses down [path] to return the [YamlNode] at [path] if successful.
- ///
- /// If no [YamlNode]s exist at [path], the result of invoking the [orElse]
- /// function is returned.
- ///
- /// If [orElse] is omitted, it defaults to throwing a [PathError].
- ///
- /// If [checkAlias] is `true`, throw [AliasError] if an aliased node is
- /// encountered.
- YamlNode _traverse(Iterable<Object> path,
- {bool checkAlias = false, YamlNode Function() orElse}) {
- ArgumentError.checkNotNull(path, 'path');
- ArgumentError.checkNotNull(checkAlias, 'checkAlias');
-
- if (path.isEmpty) return _contents;
-
- var currentNode = _contents;
- final pathList = path.toList();
-
- for (var i = 0; i < pathList.length; i++) {
- final keyOrIndex = pathList[i];
-
- if (checkAlias && _aliases.contains(currentNode)) {
- throw AliasError(path, currentNode);
- }
-
- if (currentNode is YamlList) {
- final list = currentNode as YamlList;
- if (!isValidIndex(keyOrIndex, list.length)) {
- return _pathErrorOrElse(path, path.take(i + 1), list, orElse);
- }
-
- currentNode = list.nodes[keyOrIndex];
- } else if (currentNode is YamlMap) {
- final map = currentNode as YamlMap;
-
- if (!containsKey(map, keyOrIndex)) {
- return _pathErrorOrElse(path, path.take(i + 1), map, orElse);
- }
- final keyNode = getKeyNode(map, keyOrIndex);
-
- if (checkAlias) {
- if (_aliases.contains(keyNode)) throw AliasError(path, keyNode);
- }
-
- currentNode = map.nodes[keyNode];
- } else {
- return _pathErrorOrElse(path, path.take(i + 1), currentNode, orElse);
- }
- }
-
- if (checkAlias) _assertNoChildAlias(path, currentNode);
-
- return currentNode;
- }
-
- /// Throws a [PathError] if [orElse] is not provided, returns the result
- /// of invoking the [orElse] function otherwise.
- YamlNode _pathErrorOrElse(Iterable<Object> path, Iterable<Object> subPath,
- YamlNode parent, YamlNode Function() orElse) {
- if (orElse == null) throw PathError(path, subPath, parent);
- return orElse();
- }
-
- /// Asserts that [node] and none its children are aliases
- void _assertNoChildAlias(Iterable<Object> path, [YamlNode node]) {
- ArgumentError.checkNotNull(path, 'path');
-
- if (node == null) return _assertNoChildAlias(path, _traverse(path));
- if (_aliases.contains(node)) throw AliasError(path, node);
-
- if (node is YamlScalar) return;
-
- if (node is YamlList) {
- for (var i = 0; i < node.length; i++) {
- final updatedPath = [...path, i];
- _assertNoChildAlias(updatedPath, node.nodes[i]);
- }
- }
-
- if (node is YamlMap) {
- final keyList = node.keys.toList();
- for (var i = 0; i < node.length; i++) {
- final updatedPath = [...path, keyList[i]];
- if (_aliases.contains(keyList[i])) throw AliasError(path, keyList[i]);
- _assertNoChildAlias(updatedPath, node.nodes[keyList[i]]);
- }
- }
- }
-
- /// Traverses down the provided [path] to return the [YamlList] at [path].
- ///
- /// Convenience function to ensure that a [YamlList] is returned.
- ///
- /// Throws [ArgumentError] if the element at the given path is not a
- /// [YamlList] or if the path is invalid. If [checkAlias] is `true`, and an
- /// aliased node is encountered along [path], an [AliasError] will be thrown.
- YamlList _traverseToList(Iterable<Object> path, {bool checkAlias = false}) {
- ArgumentError.checkNotNull(path, 'path');
- ArgumentError.checkNotNull(checkAlias, 'checkAlias');
-
- final possibleList = _traverse(path, checkAlias: true);
-
- if (possibleList is YamlList) {
- return possibleList;
- } else {
- throw PathError.unexpected(
- path, 'Path $path does not point to a YamlList!');
- }
- }
-
- /// Utility method to replace the substring of [_yaml] according to [edit].
- ///
- /// When [_yaml] is modified with this method, the resulting string is parsed
- /// and reloaded and traversed down [path] to ensure that the reloaded YAML
- /// tree is equal to our expectations by deep equality of values. Throws an
- /// [AssertionError] if the two trees do not match.
- void _performEdit(
- SourceEdit edit, Iterable<Object> path, YamlNode expectedNode) {
- ArgumentError.checkNotNull(edit, 'edit');
- ArgumentError.checkNotNull(path, 'path');
-
- final expectedTree = _deepModify(_contents, path, [], expectedNode);
- final initialYaml = _yaml;
- _yaml = edit.apply(_yaml);
-
- try {
- _initialize();
- } on YamlException {
- throw createAssertionError(
- 'Failed to produce valid YAML after modification.',
- initialYaml,
- _yaml);
- }
-
- final actualTree = loadYamlNode(_yaml);
- if (!deepEquals(actualTree, expectedTree)) {
- throw createAssertionError(
- 'Modification did not result in expected result.',
- initialYaml,
- _yaml);
- }
- _contents = actualTree;
- _edits.add(edit);
- }
-
- /// Utility method to produce an updated YAML tree equivalent to converting
- /// the [YamlNode] at [path] to be [expectedNode]. [subPath] holds the portion
- /// of [path] that has been traversed thus far.
- ///
- /// Throws a [PathError] if path is invalid.
- ///
- /// When called, it creates a new [YamlNode] of the same type as [tree], and
- /// copies its children over, except for the child that is on the path. Doing
- /// so allows us to "update" the immutable [YamlNode] without having to clone
- /// the whole tree.
- ///
- /// [SourceSpan]s in this new tree are not guaranteed to be accurate.
- YamlNode _deepModify(YamlNode tree, Iterable<Object> path,
- Iterable<Object> subPath, YamlNode expectedNode) {
- ArgumentError.checkNotNull(path, 'path');
- ArgumentError.checkNotNull(tree, 'tree');
- RangeError.checkValueInInterval(subPath.length, 0, path.length);
-
- if (path.length == subPath.length) return expectedNode;
-
- final keyOrIndex = path.elementAt(subPath.length);
-
- if (tree is YamlList) {
- if (!isValidIndex(keyOrIndex, tree.length)) {
- throw PathError(path, subPath, tree);
- }
-
- return wrapAsYamlNode([...tree.nodes]..[keyOrIndex] = _deepModify(
- tree.nodes[keyOrIndex],
- path,
- path.take(subPath.length + 1),
- expectedNode));
- }
-
- if (tree is YamlMap) {
- return updatedYamlMap(
- tree,
- (nodes) => nodes[keyOrIndex] = _deepModify(nodes[keyOrIndex], path,
- path.take(subPath.length + 1), expectedNode));
- }
-
- /// Should not ever reach here.
- throw PathError(path, subPath, tree);
- }
-}
diff --git a/lib/src/yaml_edit/equality.dart b/lib/src/yaml_edit/equality.dart
deleted file mode 100644
index e7cce40..0000000
--- a/lib/src/yaml_edit/equality.dart
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// @dart=2.10
-
-import 'dart:collection';
-
-import 'package:collection/collection.dart';
-import 'package:yaml/yaml.dart';
-
-/// Creates a map that uses our custom [deepEquals] and [deepHashCode] functions
-/// to determine equality.
-Map<K, V> deepEqualsMap<K, V>() =>
- LinkedHashMap(equals: deepEquals, hashCode: deepHashCode);
-
-/// Compares two [Object]s for deep equality. This implementation differs from
-/// `package:yaml`'s deep equality notation by allowing for comparison of
-/// non-scalar map keys.
-bool deepEquals(dynamic obj1, dynamic obj2) {
- if (obj1 is YamlNode) obj1 = obj1.value;
- if (obj2 is YamlNode) obj2 = obj2.value;
-
- if (obj1 is Map && obj2 is Map) {
- return mapDeepEquals(obj1, obj2);
- }
-
- if (obj1 is List && obj2 is List) {
- return listDeepEquals(obj1, obj2);
- }
-
- return obj1 == obj2;
-}
-
-/// Compares two [List]s for deep equality.
-bool listDeepEquals(List list1, List list2) {
- if (list1.length != list2.length) return false;
-
- if (list1 is YamlList) list1 = (list1 as YamlList).nodes;
- if (list2 is YamlList) list2 = (list2 as YamlList).nodes;
-
- for (var i = 0; i < list1.length; i++) {
- if (!deepEquals(list1[i], list2[i])) {
- return false;
- }
- }
-
- return true;
-}
-
-/// Compares two [Map]s for deep equality. Differs from `package:yaml`'s deep
-/// equality notation by allowing for comparison of non-scalar map keys.
-bool mapDeepEquals(Map map1, Map map2) {
- if (map1.length != map2.length) return false;
-
- if (map1 is YamlList) map1 = (map1 as YamlMap).nodes;
- if (map2 is YamlList) map2 = (map2 as YamlMap).nodes;
-
- return map1.keys.every((key) {
- if (!containsKey(map2, key)) return false;
-
- /// Because two keys may be equal by deep equality but using one key on the
- /// other map might not get a hit since they may not be both using our
- /// [deepEqualsMap].
- final key2 = getKey(map2, key);
-
- if (!deepEquals(map1[key], map2[key2])) {
- return false;
- }
-
- return true;
- });
-}
-
-/// Returns a hashcode for [value] such that structures that are equal by
-/// [deepEquals] will have the same hash code.
-int deepHashCode(Object value) {
- if (value is Map) {
- const equality = UnorderedIterableEquality();
- return equality.hash(value.keys.map(deepHashCode)) ^
- equality.hash(value.values.map(deepHashCode));
- } else if (value is Iterable) {
- return const IterableEquality().hash(value.map(deepHashCode));
- } else if (value is YamlScalar) {
- return value.value.hashCode;
- }
-
- return value.hashCode;
-}
-
-/// Returns the [YamlNode] corresponding to the provided [key].
-YamlNode getKeyNode(YamlMap map, Object key) {
- return map.nodes.keys.firstWhere((node) => deepEquals(node, key)) as YamlNode;
-}
-
-/// Returns the [YamlNode] after the [YamlNode] corresponding to the provided
-/// [key].
-YamlNode getNextKeyNode(YamlMap map, Object key) {
- final keyIterator = map.nodes.keys.iterator;
- while (keyIterator.moveNext()) {
- if (deepEquals(keyIterator.current, key) && keyIterator.moveNext()) {
- return keyIterator.current;
- }
- }
-
- return null;
-}
-
-/// Returns the key in [map] that is equal to the provided [key] by the notion
-/// of deep equality.
-Object getKey(Map map, Object key) {
- return map.keys.firstWhere((k) => deepEquals(k, key));
-}
-
-/// Checks if [map] has any keys equal to the provided [key] by deep equality.
-bool containsKey(Map map, Object key) {
- return map.keys.where((node) => deepEquals(node, key)).isNotEmpty;
-}
diff --git a/lib/src/yaml_edit/errors.dart b/lib/src/yaml_edit/errors.dart
deleted file mode 100644
index 7d490a9..0000000
--- a/lib/src/yaml_edit/errors.dart
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// @dart=2.10
-
-import 'package:meta/meta.dart';
-import 'package:yaml/yaml.dart';
-
-/// Error thrown when a function is passed an invalid path.
-@sealed
-class PathError extends ArgumentError {
- /// The full path that caused the error
- final Iterable<Object> path;
-
- /// The subpath that caused the error
- final Iterable<Object> subPath;
-
- /// The last element of [path] that could be traversed.
- YamlNode parent;
-
- PathError(this.path, this.subPath, this.parent, [String message])
- : super.value(subPath, 'path', message);
-
- PathError.unexpected(this.path, String message)
- : subPath = path,
- super(message);
-
- @override
- String toString() {
- if (message == null) {
- var errorMessage = 'Failed to traverse to subpath $subPath!';
-
- if (subPath.isNotEmpty) {
- errorMessage +=
- ' Parent $parent does not contain key or index ${subPath.last}';
- }
-
- return 'Invalid path: $path. $errorMessage.';
- }
-
- return 'Invalid path: $path. $message';
- }
-}
-
-/// Error thrown when the path contains an alias along the way.
-///
-/// When a path contains an aliased node, the behavior becomes less well-defined
-/// because we cannot be certain if the user wishes for the change to
-/// propagate throughout all the other aliased nodes, or if the user wishes
-/// for only that particular node to be modified. As such, [AliasError] reflects
-/// the detection that our change will impact an alias, and we do not intend
-/// on supporting such changes for the foreseeable future.
-@sealed
-class AliasError extends UnsupportedError {
- /// The path that caused the error
- final Iterable<Object> path;
-
- /// The anchor node of the alias
- final YamlNode anchor;
-
- AliasError(this.path, this.anchor)
- : super('Encountered an alias node along $path! '
- 'Alias nodes are nodes that refer to a previously serialized nodes, '
- 'and are denoted by either the "*" or the "&" indicators in the '
- 'original YAML. As the resulting behavior of mutations on these '
- 'nodes is not well-defined, the operation will not be supported '
- 'by this library.\n\n'
- '${anchor.span.message('The alias was first defined here.')}');
-}
-
-/// Error thrown when an assertion about the YAML fails. Extends
-/// [AssertionError] to override the [toString] method for pretty printing.
-class _YamlAssertionError extends AssertionError {
- _YamlAssertionError(message) : super(message);
-
- @override
- String toString() {
- if (message != null) {
- return 'Assertion failed: $message';
- }
- return 'Assertion failed';
- }
-}
-
-/// Throws an [AssertionError] with the given [message], and format
-/// [oldYaml] and [newYaml] for information.
-Error createAssertionError(String message, String oldYaml, String newYaml) {
- return _YamlAssertionError('''
-(package:yaml_edit) $message
-
-# YAML before edit:
-> ${oldYaml.replaceAll('\n', '\n> ')}
-
-# YAML after edit:
-> ${newYaml.replaceAll('\n', '\n> ')}
-
-Please file an issue at:
-'''
- 'https://github.com/google/dart-neats/issues/new?labels=pkg%3Ayaml_edit'
- '%2C+pending-triage&template=yaml_edit.md\n');
-}
diff --git a/lib/src/yaml_edit/list_mutations.dart b/lib/src/yaml_edit/list_mutations.dart
deleted file mode 100644
index 224b87c..0000000
--- a/lib/src/yaml_edit/list_mutations.dart
+++ /dev/null
@@ -1,337 +0,0 @@
-// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// @dart=2.10
-
-import 'package:yaml/yaml.dart';
-
-import 'editor.dart';
-import 'source_edit.dart';
-import 'strings.dart';
-import 'utils.dart';
-import 'wrap.dart';
-
-/// Returns a [SourceEdit] describing the change to be made on [yaml] to achieve
-/// the effect of setting the element at [index] to [newValue] when re-parsed.
-SourceEdit updateInList(
- YamlEditor yamlEdit, YamlList list, int index, YamlNode newValue) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(list, 'list');
- RangeError.checkValueInInterval(index, 0, list.length - 1);
-
- final currValue = list.nodes[index];
- 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 representation of a new node.
- if (list.style == CollectionStyle.BLOCK) {
- final listIndentation = getListIndentation(yaml, list);
- final indentation = listIndentation + getIndentation(yamlEdit);
- final lineEnding = getLineEnding(yaml);
- valueString = yamlEncodeBlockString(
- wrapAsYamlNode(newValue), indentation, lineEnding);
-
- /// We prefer the compact nested notation for collections.
- ///
- /// By virtue of [yamlEncodeBlockString], collections automatically
- /// have the necessary line endings.
- if ((newValue is List && (newValue as List).isNotEmpty) ||
- (newValue is Map && (newValue as Map).isNotEmpty)) {
- valueString = valueString.substring(indentation);
- } else if (isCollection(currValue) &&
- getStyle(currValue) == CollectionStyle.BLOCK) {
- valueString += lineEnding;
- }
-
- var end = getContentSensitiveEnd(currValue);
- if (end <= offset) {
- offset++;
- end = offset;
- valueString = ' ' + valueString;
- }
-
- return SourceEdit(offset, end - offset, valueString);
- } else {
- valueString = yamlEncodeFlowString(newValue);
- return SourceEdit(offset, currValue.span.length, valueString);
- }
-}
-
-/// Returns a [SourceEdit] describing the change to be made on [yaml] to achieve
-/// the effect of appending [item] to the list.
-SourceEdit appendIntoList(YamlEditor yamlEdit, YamlList list, YamlNode item) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(list, 'list');
-
- if (list.style == CollectionStyle.FLOW) {
- return _appendToFlowList(yamlEdit, list, item);
- } else {
- return _appendToBlockList(yamlEdit, list, item);
- }
-}
-
-/// Returns a [SourceEdit] describing the change to be made on [yaml] to achieve
-/// the effect of inserting [item] to the list at [index].
-SourceEdit insertInList(
- YamlEditor yamlEdit, YamlList list, int index, YamlNode item) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(list, 'list');
- RangeError.checkValueInInterval(index, 0, list.length);
-
- /// We call the append method if the user wants to append it to the end of the
- /// list because appending requires different techniques.
- if (index == list.length) {
- return appendIntoList(yamlEdit, list, item);
- } else {
- if (list.style == CollectionStyle.FLOW) {
- return _insertInFlowList(yamlEdit, list, index, item);
- } else {
- return _insertInBlockList(yamlEdit, list, index, item);
- }
- }
-}
-
-/// Returns a [SourceEdit] describing the change to be made on [yaml] to achieve
-/// the effect of removing the element at [index] when re-parsed.
-SourceEdit removeInList(YamlEditor yamlEdit, YamlList list, int index) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(list, 'list');
-
- final nodeToRemove = list.nodes[index];
-
- if (list.style == CollectionStyle.FLOW) {
- return _removeFromFlowList(yamlEdit, list, nodeToRemove, index);
- } else {
- return _removeFromBlockList(yamlEdit, list, nodeToRemove, index);
- }
-}
-
-/// Returns a [SourceEdit] describing the change to be made on [yaml] to achieve
-/// the effect of addition [item] into [nodes], noting that this is a flow list.
-SourceEdit _appendToFlowList(
- YamlEditor yamlEdit, YamlList list, YamlNode item) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(list, 'list');
-
- final valueString = _formatNewFlow(list, item, true);
- return SourceEdit(list.span.end.offset - 1, 0, valueString);
-}
-
-/// Returns a [SourceEdit] describing the change to be made on [yaml] to achieve
-/// the effect of addition [item] into [nodes], noting that this is a block list.
-SourceEdit _appendToBlockList(
- YamlEditor yamlEdit, YamlList list, YamlNode item) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(list, 'list');
-
- var formattedValue = _formatNewBlock(yamlEdit, list, item);
- final yaml = yamlEdit.toString();
- var offset = list.span.end.offset;
-
- // Adjusts offset to after the trailing newline of the last entry, if it exists
- if (list.isNotEmpty) {
- final lastValueSpanEnd = list.nodes.last.span.end.offset;
- final nextNewLineIndex = yaml.indexOf('\n', lastValueSpanEnd);
- if (nextNewLineIndex == -1) {
- formattedValue = getLineEnding(yaml) + formattedValue;
- } else {
- offset = nextNewLineIndex + 1;
- }
- }
-
- return SourceEdit(offset, 0, formattedValue);
-}
-
-/// Formats [item] into a new node for block lists.
-String _formatNewBlock(YamlEditor yamlEdit, YamlList list, YamlNode item) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(list, 'list');
-
- final yaml = yamlEdit.toString();
- final listIndentation = getListIndentation(yaml, list);
- final newIndentation = listIndentation + getIndentation(yamlEdit);
- final lineEnding = getLineEnding(yaml);
-
- var valueString = yamlEncodeBlockString(item, newIndentation, lineEnding);
- if (isCollection(item) && !isFlowYamlCollectionNode(item) && !isEmpty(item)) {
- valueString = valueString.substring(newIndentation);
- }
- final indentedHyphen = ' ' * listIndentation + '- ';
-
- return '$indentedHyphen$valueString$lineEnding';
-}
-
-/// Formats [item] into a new node for flow lists.
-String _formatNewFlow(YamlList list, YamlNode item, [bool isLast = false]) {
- ArgumentError.checkNotNull(list, 'list');
- ArgumentError.checkNotNull(isLast, 'isLast');
-
- var valueString = yamlEncodeFlowString(item);
- if (list.isNotEmpty) {
- if (isLast) {
- valueString = ', $valueString';
- } else {
- valueString += ', ';
- }
- }
-
- return valueString;
-}
-
-/// Returns a [SourceEdit] describing the change to be made on [yaml] to achieve
-/// the effect of inserting [item] into [nodes] at [index], noting that this is
-/// a block list.
-///
-/// [index] should be non-negative and less than or equal to [length].
-SourceEdit _insertInBlockList(
- YamlEditor yamlEdit, YamlList list, int index, YamlNode item) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(list, 'list');
- RangeError.checkValueInInterval(index, 0, list.length);
-
- if (index == list.length) return _appendToBlockList(yamlEdit, list, item);
-
- final formattedValue = _formatNewBlock(yamlEdit, list, item);
-
- final currNode = list.nodes[index];
- final currNodeStart = currNode.span.start.offset;
- final yaml = yamlEdit.toString();
- final start = yaml.lastIndexOf('\n', currNodeStart) + 1;
-
- return SourceEdit(start, 0, formattedValue);
-}
-
-/// Returns a [SourceEdit] describing the change to be made on [yaml] to achieve
-/// the effect of inserting [item] into [nodes] at [index], noting that this is
-/// a flow list.
-///
-/// [index] should be non-negative and less than or equal to [length].
-SourceEdit _insertInFlowList(
- YamlEditor yamlEdit, YamlList list, int index, YamlNode item) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(list, 'list');
- RangeError.checkValueInInterval(index, 0, list.length);
-
- if (index == list.length) return _appendToFlowList(yamlEdit, list, item);
-
- final formattedValue = _formatNewFlow(list, item);
-
- final yaml = yamlEdit.toString();
- final currNode = list.nodes[index];
- final currNodeStart = currNode.span.start.offset;
- var start = yaml.lastIndexOf(RegExp(r',|\['), currNodeStart - 1) + 1;
- if (yaml[start] == ' ') start++;
-
- return SourceEdit(start, 0, formattedValue);
-}
-
-/// Returns a [SourceEdit] describing the change to be made on [yaml] to achieve
-/// the effect of removing [nodeToRemove] from [nodes], noting that this is a
-/// block list.
-///
-/// [index] should be non-negative and less than or equal to [length].
-SourceEdit _removeFromBlockList(
- YamlEditor yamlEdit, YamlList list, YamlNode nodeToRemove, int index) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- 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;
-
- return SourceEdit(start, end - start, '[]');
- }
-
- final yaml = yamlEdit.toString();
- final span = nodeToRemove.span;
-
- /// 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);
-
- /// 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;
- }
- }
-
- return SourceEdit(start, end - start, '');
-}
-
-/// Returns a [SourceEdit] describing the change to be made on [yaml] to achieve
-/// the effect of removing [nodeToRemove] from [nodes], noting that this is a
-/// flow list.
-///
-/// [index] should be non-negative and less than or equal to [length].
-SourceEdit _removeFromFlowList(
- YamlEditor yamlEdit, YamlList list, YamlNode nodeToRemove, int index) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(list, 'list');
- RangeError.checkValueInInterval(index, 0, list.length - 1);
-
- final span = nodeToRemove.span;
- final yaml = yamlEdit.toString();
- var start = span.start.offset;
- var end = span.end.offset;
-
- if (index == 0) {
- start = yaml.lastIndexOf('[', start - 1) + 1;
- if (index == list.length - 1) {
- end = yaml.indexOf(']', end);
- } else {
- end = yaml.indexOf(',', end) + 1;
- }
- } else {
- start = yaml.lastIndexOf(',', start - 1);
- }
-
- return SourceEdit(start, end - start, '');
-}
diff --git a/lib/src/yaml_edit/map_mutations.dart b/lib/src/yaml_edit/map_mutations.dart
deleted file mode 100644
index 597c0ba..0000000
--- a/lib/src/yaml_edit/map_mutations.dart
+++ /dev/null
@@ -1,264 +0,0 @@
-// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// @dart=2.10
-
-import 'package:yaml/yaml.dart';
-
-import 'editor.dart';
-import 'equality.dart';
-import 'source_edit.dart';
-import 'strings.dart';
-import 'utils.dart';
-import 'wrap.dart';
-
-/// Performs the string operation on [yaml] to achieve the effect of setting
-/// the element at [key] to [newValue] when re-parsed.
-SourceEdit updateInMap(
- YamlEditor yamlEdit, YamlMap map, Object key, YamlNode newValue) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(map, 'map');
-
- if (!containsKey(map, key)) {
- final keyNode = wrapAsYamlNode(key);
-
- if (map.style == CollectionStyle.FLOW) {
- return _addToFlowMap(yamlEdit, map, keyNode, newValue);
- } else {
- return _addToBlockMap(yamlEdit, map, keyNode, newValue);
- }
- } else {
- if (map.style == CollectionStyle.FLOW) {
- return _replaceInFlowMap(yamlEdit, map, key, newValue);
- } else {
- return _replaceInBlockMap(yamlEdit, map, key, newValue);
- }
- }
-}
-
-/// Performs the string operation on [yaml] to achieve the effect of removing
-/// the element at [key] when re-parsed.
-SourceEdit removeInMap(YamlEditor yamlEdit, YamlMap map, Object key) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(map, 'map');
-
- if (!containsKey(map, key)) return null;
-
- final keyNode = getKeyNode(map, key);
- final valueNode = map.nodes[keyNode];
-
- if (map.style == CollectionStyle.FLOW) {
- return _removeFromFlowMap(yamlEdit, map, keyNode, valueNode);
- } else {
- return _removeFromBlockMap(yamlEdit, map, keyNode, valueNode);
- }
-}
-
-/// Performs the string operation on [yaml] to achieve the effect of adding
-/// the [key]:[newValue] pair when reparsed, bearing in mind that this is a
-/// block map.
-SourceEdit _addToBlockMap(
- YamlEditor yamlEdit, YamlMap map, Object key, YamlNode newValue) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(map, 'map');
-
- final yaml = yamlEdit.toString();
- final newIndentation =
- getMapIndentation(yaml, map) + getIndentation(yamlEdit);
- final keyString = yamlEncodeFlowString(wrapAsYamlNode(key));
- final lineEnding = getLineEnding(yaml);
-
- var valueString = yamlEncodeBlockString(newValue, newIndentation, lineEnding);
- if (isCollection(newValue) &&
- !isFlowYamlCollectionNode(newValue) &&
- !isEmpty(newValue)) {
- valueString = '$lineEnding$valueString';
- }
-
- var formattedValue = ' ' * getMapIndentation(yaml, map) + '$keyString: ';
- var offset = map.span.end.offset;
-
- final insertionIndex = getMapInsertionIndex(map, keyString);
-
- if (map.isNotEmpty) {
- /// Adjusts offset to after the trailing newline of the last entry, if it
- /// exists
- if (insertionIndex == map.length) {
- final lastValueSpanEnd = getContentSensitiveEnd(map.nodes.values.last);
- final nextNewLineIndex = yaml.indexOf('\n', lastValueSpanEnd);
-
- if (nextNewLineIndex != -1) {
- offset = nextNewLineIndex + 1;
- } else {
- formattedValue = lineEnding + formattedValue;
- }
- } else {
- final keyAtIndex = map.nodes.keys.toList()[insertionIndex] as YamlNode;
- final keySpanStart = keyAtIndex.span.start.offset;
- final prevNewLineIndex = yaml.lastIndexOf('\n', keySpanStart);
-
- offset = prevNewLineIndex + 1;
- }
- }
-
- formattedValue += valueString + lineEnding;
-
- return SourceEdit(offset, 0, formattedValue);
-}
-
-/// Performs the string operation on [yaml] to achieve the effect of adding
-/// the [key]:[newValue] pair when reparsed, bearing in mind that this is a flow
-/// map.
-SourceEdit _addToFlowMap(
- YamlEditor yamlEdit, YamlMap map, YamlNode keyNode, YamlNode newValue) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(map, 'map');
-
- final keyString = yamlEncodeFlowString(keyNode);
- final valueString = yamlEncodeFlowString(newValue);
-
- // The -1 accounts for the closing bracket.
- if (map.isEmpty) {
- return SourceEdit(map.span.end.offset - 1, 0, '$keyString: $valueString');
- }
-
- final insertionIndex = getMapInsertionIndex(map, keyString);
-
- if (insertionIndex == map.length) {
- return SourceEdit(map.span.end.offset - 1, 0, ', $keyString: $valueString');
- }
-
- final insertionOffset =
- (map.nodes.keys.toList()[insertionIndex] as YamlNode).span.start.offset;
-
- return SourceEdit(insertionOffset, 0, '$keyString: $valueString, ');
-}
-
-/// Performs the string operation on [yaml] to achieve the effect of replacing
-/// the value at [key] with [newValue] when reparsed, bearing in mind that this
-/// is a block map.
-SourceEdit _replaceInBlockMap(
- YamlEditor yamlEdit, YamlMap map, Object key, YamlNode newValue) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(map, 'map');
-
- final yaml = yamlEdit.toString();
- final lineEnding = getLineEnding(yaml);
- final newIndentation =
- getMapIndentation(yaml, map) + getIndentation(yamlEdit);
-
- final keyNode = getKeyNode(map, key);
- var valueAsString = yamlEncodeBlockString(
- wrapAsYamlNode(newValue), newIndentation, lineEnding);
- if (isCollection(newValue) &&
- !isFlowYamlCollectionNode(newValue) &&
- !isEmpty(newValue)) {
- valueAsString = lineEnding + valueAsString;
- }
-
- /// +1 accounts for the colon
- final start = keyNode.span.end.offset + 1;
- 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;
-
- return SourceEdit(start, end - start, ' ' + valueAsString);
-}
-
-/// Performs the string operation on [yaml] to achieve the effect of replacing
-/// the value at [key] with [newValue] when reparsed, bearing in mind that this
-/// is a flow map.
-SourceEdit _replaceInFlowMap(
- YamlEditor yamlEdit, YamlMap map, Object key, YamlNode newValue) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(map, 'map');
-
- final valueSpan = map.nodes[key].span;
- final valueString = yamlEncodeFlowString(newValue);
-
- return SourceEdit(valueSpan.start.offset, valueSpan.length, valueString);
-}
-
-/// Performs the string operation on [yaml] to achieve the effect of removing
-/// the [key] from the map, bearing in mind that this is a block map.
-SourceEdit _removeFromBlockMap(
- YamlEditor yamlEdit, YamlMap map, YamlNode keyNode, YamlNode valueNode) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(map, 'map');
- ArgumentError.checkNotNull(keyNode, 'keyNode');
- ArgumentError.checkNotNull(valueNode, 'valueNode');
-
- final keySpan = keyNode.span;
- var end = getContentSensitiveEnd(valueNode);
- final yaml = yamlEdit.toString();
-
- if (map.length == 1) {
- final start = map.span.start.offset;
- return SourceEdit(start, end - start, '{}');
- }
-
- 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 (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;
- }
- }
-
- return SourceEdit(start, end - start, '');
-}
-
-/// Performs the string operation on [yaml] to achieve the effect of removing
-/// the [key] from the map, bearing in mind that this is a flow map.
-SourceEdit _removeFromFlowMap(
- YamlEditor yamlEdit, YamlMap map, YamlNode keyNode, YamlNode valueNode) {
- ArgumentError.checkNotNull(yamlEdit, 'yamlEdit');
- ArgumentError.checkNotNull(map, 'map');
- ArgumentError.checkNotNull(keyNode, 'keyNode');
- ArgumentError.checkNotNull(valueNode, 'valueNode');
-
- var start = keyNode.span.start.offset;
- var end = valueNode.span.end.offset;
- final yaml = yamlEdit.toString();
-
- if (deepEquals(keyNode, map.keys.first)) {
- start = yaml.lastIndexOf('{', start - 1) + 1;
-
- if (deepEquals(keyNode, map.keys.last)) {
- end = yaml.indexOf('}', end);
- } else {
- end = yaml.indexOf(',', end) + 1;
- }
- } else {
- start = yaml.lastIndexOf(',', start - 1);
- }
-
- return SourceEdit(start, end - start, '');
-}
diff --git a/lib/src/yaml_edit/source_edit.dart b/lib/src/yaml_edit/source_edit.dart
deleted file mode 100644
index 1b6512a..0000000
--- a/lib/src/yaml_edit/source_edit.dart
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// @dart=2.10
-
-import 'package:meta/meta.dart';
-
-/// A class representing a change on a [String], intended to be compatible with
-/// `package:analysis_server`'s [SourceEdit].
-///
-/// For example, changing a string from
-/// ```
-/// foo: foobar
-/// ```
-/// to
-/// ```
-/// foo: barbar
-/// ```
-/// will be represented by `SourceEdit(offset: 4, length: 3, replacement: 'bar')`
-@sealed
-class SourceEdit {
- /// The offset from the start of the string where the modification begins.
- final int offset;
-
- /// The length of the substring to be replaced.
- final int length;
-
- /// The replacement string to be used.
- final String replacement;
-
- /// Creates a new [SourceEdit] instance. [offset], [length] and [replacement]
- /// must be non-null, and [offset] and [length] must be non-negative.
- factory SourceEdit(int offset, int length, String replacement) =>
- SourceEdit._(offset, length, replacement);
-
- SourceEdit._(this.offset, this.length, this.replacement) {
- ArgumentError.checkNotNull(offset, 'offset');
- ArgumentError.checkNotNull(length, 'length');
- ArgumentError.checkNotNull(replacement, 'replacement');
- RangeError.checkNotNegative(offset);
- RangeError.checkNotNegative(length);
- }
-
- @override
- bool operator ==(Object other) {
- if (other is SourceEdit) {
- return offset == other.offset &&
- length == other.length &&
- replacement == other.replacement;
- }
-
- return false;
- }
-
- @override
- int get hashCode => offset.hashCode ^ length.hashCode ^ replacement.hashCode;
-
- /// Constructs a SourceEdit from JSON.
- ///
- /// **Example:**
- /// ```dart
- /// final edit = {
- /// 'offset': 1,
- /// 'length': 2,
- /// 'replacement': 'replacement string'
- /// };
- ///
- /// final sourceEdit = SourceEdit.fromJson(edit);
- /// ```
- factory SourceEdit.fromJson(Map<String, dynamic> json) {
- ArgumentError.checkNotNull(json, 'json');
-
- if (json is Map) {
- final offset = json['offset'];
- final length = json['length'];
- final replacement = json['replacement'];
-
- if (offset is int && length is int && replacement is String) {
- return SourceEdit(offset, length, replacement);
- }
- }
- throw FormatException('Invalid JSON passed to SourceEdit');
- }
-
- /// Encodes this object as JSON-compatible structure.
- ///
- /// **Example:**
- /// ```dart
- /// import 'dart:convert' show jsonEncode;
- ///
- /// final edit = SourceEdit(offset, length, 'replacement string');
- /// final jsonString = jsonEncode(edit.toJson());
- /// print(jsonString);
- /// ```
- Map<String, dynamic> toJson() {
- return {'offset': offset, 'length': length, 'replacement': replacement};
- }
-
- @override
- String toString() => 'SourceEdit($offset, $length, "$replacement")';
-
- /// Applies a series of [SourceEdit]s to an original string, and return the
- /// final output.
- ///
- /// [edits] should be in order i.e. the first [SourceEdit] in [edits] should
- /// be the first edit applied to [original].
- ///
- /// **Example:**
- /// ```dart
- /// const original = 'YAML: YAML';
- /// final sourceEdits = [
- /// SourceEdit(6, 4, "YAML Ain't Markup Language"),
- /// SourceEdit(6, 4, "YAML Ain't Markup Language"),
- /// SourceEdit(0, 4, "YAML Ain't Markup Language")
- /// ];
- /// final result = SourceEdit.applyAll(original, sourceEdits);
- /// ```
- /// **Expected result:**
- /// ```dart
- /// "YAML Ain't Markup Language: YAML Ain't Markup Language Ain't Markup
- /// Language"
- /// ```
- static String applyAll(String original, Iterable<SourceEdit> edits) {
- ArgumentError.checkNotNull(original, 'original');
- ArgumentError.checkNotNull(edits, 'edits');
-
- return edits.fold(original, (current, edit) => edit.apply(current));
- }
-
- /// Applies one [SourceEdit]s to an original string, and return the final
- /// output.
- ///
- /// **Example:**
- /// ```dart
- /// final edit = SourceEdit(4, 3, 'bar');
- /// final originalString = 'foo: foobar';
- /// print(edit.apply(originalString)); // 'foo: barbar'
- /// ```
- String apply(String original) {
- ArgumentError.checkNotNull(original, 'original');
-
- return original.replaceRange(offset, offset + length, replacement);
- }
-}
diff --git a/lib/src/yaml_edit/strings.dart b/lib/src/yaml_edit/strings.dart
deleted file mode 100644
index 8a7fee5..0000000
--- a/lib/src/yaml_edit/strings.dart
+++ /dev/null
@@ -1,315 +0,0 @@
-// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// @dart=2.10
-
-import 'package:yaml/yaml.dart';
-import 'utils.dart';
-
-/// Given [value], tries to format it into a plain string recognizable by YAML.
-/// If it fails, it defaults to returning a double-quoted string.
-///
-/// Not all values can be formatted into a plain string. If the string contains
-/// an escape sequence, it can only be detected when in a double-quoted
-/// sequence. Plain strings may also be misinterpreted by the YAML parser (e.g.
-/// ' null').
-String _tryYamlEncodePlain(Object value) {
- if (value is YamlNode) {
- AssertionError(
- 'YamlNodes should not be passed directly into getSafeString!');
- }
-
- assertValidScalar(value);
-
- if (value is String) {
- /// If it contains a dangerous character we want to wrap the result with
- /// double quotes because the double quoted style allows for arbitrary
- /// strings with "\" escape sequences.
- ///
- /// See 7.3.1 Double-Quoted Style
- /// https://yaml.org/spec/1.2/spec.html#id2787109
- if (isDangerousString(value)) {
- return _yamlEncodeDoubleQuoted(value);
- }
-
- return value;
- }
-
- return value.toString();
-}
-
-/// Checks if [string] has unprintable characters according to
-/// [unprintableCharCodes].
-bool _hasUnprintableCharacters(String string) {
- ArgumentError.checkNotNull(string, 'string');
-
- final codeUnits = string.codeUnits;
-
- for (final key in unprintableCharCodes.keys) {
- if (codeUnits.contains(key)) return true;
- }
-
- return false;
-}
-
-/// Generates a YAML-safe double-quoted string based on [string], escaping the
-/// list of characters as defined by the YAML 1.2 spec.
-///
-/// See 5.7 Escaped Characters https://yaml.org/spec/1.2/spec.html#id2776092
-String _yamlEncodeDoubleQuoted(String string) {
- ArgumentError.checkNotNull(string, 'string');
-
- final buffer = StringBuffer();
- for (final codeUnit in string.codeUnits) {
- if (doubleQuoteEscapeChars[codeUnit] != null) {
- buffer.write(doubleQuoteEscapeChars[codeUnit]);
- } else {
- buffer.writeCharCode(codeUnit);
- }
- }
-
- return '"$buffer"';
-}
-
-/// Generates a YAML-safe single-quoted string. Automatically escapes
-/// single-quotes.
-///
-/// It is important that we ensure that [string] is free of unprintable
-/// characters by calling [assertValidScalar] before invoking this function.
-String _tryYamlEncodeSingleQuoted(String string) {
- ArgumentError.checkNotNull(string, 'string');
-
- final result = string.replaceAll('\'', '\'\'');
- return '\'$result\'';
-}
-
-/// Generates a YAML-safe folded string.
-///
-/// It is important that we ensure that [string] is free of unprintable
-/// characters by calling [assertValidScalar] before invoking this function.
-String _tryYamlEncodeFolded(String string, int indentation, String lineEnding) {
- ArgumentError.checkNotNull(string, 'string');
- ArgumentError.checkNotNull(indentation, 'indentation');
- ArgumentError.checkNotNull(lineEnding, 'lineEnding');
-
- String result;
-
- final trimmedString = string.trimRight();
- final removedPortion = string.substring(trimmedString.length);
-
- if (removedPortion.contains('\n')) {
- result = '>+\n' + ' ' * indentation;
- } else {
- result = '>-\n' + ' ' * indentation;
- }
-
- /// Duplicating the newline for folded strings preserves it in YAML.
- /// Assumes the user did not try to account for windows documents by using
- /// `\r\n` already
- return result +
- trimmedString.replaceAll('\n', lineEnding * 2 + ' ' * indentation) +
- removedPortion;
-}
-
-/// Generates a YAML-safe literal string.
-///
-/// It is important that we ensure that [string] is free of unprintable
-/// characters by calling [assertValidScalar] before invoking this function.
-String _tryYamlEncodeLiteral(
- String string, int indentation, String lineEnding) {
- ArgumentError.checkNotNull(string, 'string');
- ArgumentError.checkNotNull(indentation, 'indentation');
- ArgumentError.checkNotNull(lineEnding, 'lineEnding');
-
- final result = '|-\n$string';
-
- /// Assumes the user did not try to account for windows documents by using
- /// `\r\n` already
- return result.replaceAll('\n', lineEnding + ' ' * indentation);
-}
-
-/// Returns [value] with the necessary formatting applied in a flow context
-/// if possible.
-///
-/// If [value] is a [YamlScalar], we try to respect its [style] parameter where
-/// possible. Certain cases make this impossible (e.g. a plain string scalar that
-/// starts with '>'), in which case we will produce [value] with default styling
-/// options.
-String _yamlEncodeFlowScalar(YamlNode value) {
- if (value is YamlScalar) {
- assertValidScalar(value.value);
-
- if (value.value is String) {
- if (_hasUnprintableCharacters(value.value) ||
- value.style == ScalarStyle.DOUBLE_QUOTED) {
- return _yamlEncodeDoubleQuoted(value.value);
- }
-
- if (value.style == ScalarStyle.SINGLE_QUOTED) {
- return _tryYamlEncodeSingleQuoted(value.value);
- }
- }
-
- return _tryYamlEncodePlain(value.value);
- }
-
- assertValidScalar(value);
- return _tryYamlEncodePlain(value);
-}
-
-/// Returns [value] with the necessary formatting applied in a block context
-/// if possible.
-///
-/// If [value] is a [YamlScalar], we try to respect its [style] parameter where
-/// possible. Certain cases make this impossible (e.g. a folded string scalar
-/// 'null'), in which case we will produce [value] with default styling
-/// options.
-String yamlEncodeBlockScalar(
- YamlNode value, int indentation, String lineEnding) {
- ArgumentError.checkNotNull(indentation, 'indentation');
- ArgumentError.checkNotNull(lineEnding, 'lineEnding');
-
- if (value is YamlScalar) {
- assertValidScalar(value.value);
-
- if (value.value is String) {
- if (_hasUnprintableCharacters(value.value)) {
- return _yamlEncodeDoubleQuoted(value.value);
- }
-
- if (value.style == ScalarStyle.SINGLE_QUOTED) {
- return _tryYamlEncodeSingleQuoted(value.value);
- }
-
- // Strings with only white spaces will cause a misparsing
- if (value.value.trim().length == value.value.length &&
- value.value.length != 0) {
- if (value.style == ScalarStyle.FOLDED) {
- return _tryYamlEncodeFolded(value.value, indentation, lineEnding);
- }
-
- if (value.style == ScalarStyle.LITERAL) {
- return _tryYamlEncodeLiteral(value.value, indentation, lineEnding);
- }
- }
- }
-
- return _tryYamlEncodePlain(value.value);
- }
-
- assertValidScalar(value);
-
- /// The remainder of the possibilities are similar to how [getFlowScalar]
- /// treats [value].
- return _yamlEncodeFlowScalar(value);
-}
-
-/// Returns [value] with the necessary formatting applied in a flow context.
-///
-/// If [value] is a [YamlNode], we try to respect its [style] parameter where
-/// possible. Certain cases make this impossible (e.g. a plain string scalar
-/// that starts with '>', a child having a block style parameters), in which
-/// case we will produce [value] with default styling options.
-String yamlEncodeFlowString(YamlNode value) {
- if (value is YamlList) {
- final list = value.nodes;
-
- final safeValues = list.map(yamlEncodeFlowString);
- return '[' + safeValues.join(', ') + ']';
- } else if (value is YamlMap) {
- final safeEntries = value.nodes.entries.map((entry) {
- final safeKey = yamlEncodeFlowString(entry.key);
- final safeValue = yamlEncodeFlowString(entry.value);
- return '$safeKey: $safeValue';
- });
-
- return '{' + safeEntries.join(', ') + '}';
- }
-
- return _yamlEncodeFlowScalar(value);
-}
-
-/// Returns [value] with the necessary formatting applied in a block context.
-///
-/// If [value] is a [YamlNode], we respect its [style] parameter.
-String yamlEncodeBlockString(
- YamlNode value, int indentation, String lineEnding) {
- ArgumentError.checkNotNull(indentation, 'indentation');
- ArgumentError.checkNotNull(lineEnding, 'lineEnding');
-
- const additionalIndentation = 2;
-
- if (!isBlockNode(value)) return yamlEncodeFlowString(value);
-
- final newIndentation = indentation + additionalIndentation;
-
- if (value is YamlList) {
- if (value.isEmpty) return ' ' * indentation + '[]';
-
- Iterable<String> safeValues;
-
- final children = value.nodes;
-
- safeValues = children.map((child) {
- var valueString =
- yamlEncodeBlockString(child, newIndentation, lineEnding);
- if (isCollection(child) && !isFlowYamlCollectionNode(child)) {
- valueString = valueString.substring(newIndentation);
- }
-
- return ' ' * indentation + '- $valueString';
- });
-
- return safeValues.join(lineEnding);
- } else if (value is YamlMap) {
- if (value.isEmpty) return ' ' * indentation + '{}';
-
- return value.nodes.entries.map((entry) {
- final safeKey = yamlEncodeFlowString(entry.key);
- final formattedKey = ' ' * indentation + safeKey;
- final formattedValue =
- yamlEncodeBlockString(entry.value, newIndentation, lineEnding);
-
- /// Empty collections are always encoded in flow-style, so new-line must
- /// be avoided.
- if (isCollection(entry.value) && !isEmpty(entry.value)) {
- return formattedKey + ':\n' + formattedValue;
- }
-
- return formattedKey + ': ' + formattedValue;
- }).join(lineEnding);
- }
-
- return yamlEncodeBlockScalar(value, newIndentation, lineEnding);
-}
-
-/// List of unprintable characters.
-///
-/// See 5.7 Escape Characters https://yaml.org/spec/1.2/spec.html#id2776092
-final Map<int, String> unprintableCharCodes = {
- 0: '\\0', // Escaped ASCII null (#x0) character.
- 7: '\\a', // Escaped ASCII bell (#x7) character.
- 8: '\\b', // Escaped ASCII backspace (#x8) character.
- 11: '\\v', // Escaped ASCII vertical tab (#xB) character.
- 12: '\\f', // Escaped ASCII form feed (#xC) character.
- 13: '\\r', // Escaped ASCII carriage return (#xD) character. Line Break.
- 27: '\\e', // Escaped ASCII escape (#x1B) character.
- 133: '\\N', // Escaped Unicode next line (#x85) character.
- 160: '\\_', // Escaped Unicode non-breaking space (#xA0) character.
- 8232: '\\L', // Escaped Unicode line separator (#x2028) character.
- 8233: '\\P', // Escaped Unicode paragraph separator (#x2029) character.
-};
-
-/// List of escape characters. In particular, \x32 is not included because it
-/// can be processed normally.
-///
-/// See 5.7 Escape Characters https://yaml.org/spec/1.2/spec.html#id2776092
-final Map<int, String> doubleQuoteEscapeChars = {
- ...unprintableCharCodes,
- 9: '\\t', // Escaped ASCII horizontal tab (#x9) character. Printable
- 10: '\\n', // Escaped ASCII line feed (#xA) character. Line Break.
- 34: '\\"', // Escaped ASCII double quote (#x22).
- 47: '\\/', // Escaped ASCII slash (#x2F), for JSON compatibility.
- 92: '\\\\', // Escaped ASCII back slash (#x5C).
-};
diff --git a/lib/src/yaml_edit/utils.dart b/lib/src/yaml_edit/utils.dart
deleted file mode 100644
index 91ff530..0000000
--- a/lib/src/yaml_edit/utils.dart
+++ /dev/null
@@ -1,271 +0,0 @@
-// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// @dart=2.10
-
-import 'package:source_span/source_span.dart';
-import 'package:yaml/yaml.dart';
-
-import 'editor.dart';
-
-/// Determines if [string] is dangerous by checking if parsing the plain string can
-/// return a result different from [string].
-///
-/// This function is also capable of detecting if non-printable characters are in
-/// [string].
-bool isDangerousString(String string) {
- ArgumentError.checkNotNull(string, 'string');
-
- try {
- if (loadYamlNode(string).value != string) {
- return true;
- }
-
- /// [string] should also not contain the `[`, `]`, `,`, `{` and `}` indicator characters.
- return string.contains(RegExp(r'\{|\[|\]|\}|,'));
- } catch (e) {
- /// This catch statement catches [ArgumentError] in `loadYamlNode` when
- /// a string can be interpreted as a URI tag, but catches for other
- /// [YamlException]s
- return true;
- }
-}
-
-/// Asserts that [value] is a valid scalar according to YAML.
-///
-/// A valid scalar is a number, String, boolean, or null.
-void assertValidScalar(Object value) {
- if (value is num || value is String || value is bool || value == null) {
- return;
- }
-
- throw ArgumentError.value(value, 'value', 'Not a valid scalar type!');
-}
-
-/// Checks if [node] is a [YamlNode] with block styling.
-///
-/// [ScalarStyle.ANY] and [CollectionStyle.ANY] are considered to be block styling
-/// by default for maximum flexibility.
-bool isBlockNode(YamlNode node) {
- ArgumentError.checkNotNull(node, 'node');
-
- if (node is YamlScalar) {
- if (node.style == ScalarStyle.LITERAL ||
- node.style == ScalarStyle.FOLDED ||
- node.style == ScalarStyle.ANY) {
- return true;
- }
- }
-
- if (node is YamlList &&
- (node.style == CollectionStyle.BLOCK ||
- node.style == CollectionStyle.ANY)) return true;
- if (node is YamlMap &&
- (node.style == CollectionStyle.BLOCK ||
- node.style == CollectionStyle.ANY)) return true;
-
- return false;
-}
-
-/// Returns the content sensitive ending offset of [yamlNode] (i.e. where the last
-/// meaningful content happens)
-int getContentSensitiveEnd(YamlNode yamlNode) {
- ArgumentError.checkNotNull(yamlNode, 'yamlNode');
-
- if (yamlNode is YamlList) {
- if (yamlNode.style == CollectionStyle.FLOW) {
- return yamlNode.span.end.offset;
- } else {
- return getContentSensitiveEnd(yamlNode.nodes.last);
- }
- } else if (yamlNode is YamlMap) {
- if (yamlNode.style == CollectionStyle.FLOW) {
- return yamlNode.span.end.offset;
- } else {
- return getContentSensitiveEnd(yamlNode.nodes.values.last);
- }
- }
-
- return yamlNode.span.end.offset;
-}
-
-/// Checks if the item is a Map or a List
-bool isCollection(Object item) => item is Map || item is List;
-
-/// Checks if [index] is [int], >=0, < [length]
-bool isValidIndex(Object index, int length) {
- return index is int && index >= 0 && index < length;
-}
-
-/// Checks if the item is empty, if it is a List or a Map.
-///
-/// Returns `false` if [item] is not a List or Map.
-bool isEmpty(Object item) {
- if (item is Map) return item.isEmpty;
- if (item is List) return item.isEmpty;
-
- return false;
-}
-
-/// Creates a [SourceSpan] from [sourceUrl] with no meaningful location
-/// information.
-///
-/// Mainly used with [wrapAsYamlNode] to allow for a reasonable
-/// implementation of [SourceSpan.message].
-SourceSpan shellSpan(Object sourceUrl) {
- final shellSourceLocation = SourceLocation(0, sourceUrl: sourceUrl);
- return SourceSpanBase(shellSourceLocation, shellSourceLocation, '');
-}
-
-/// Returns if [value] is a [YamlList] or [YamlMap] with [CollectionStyle.FLOW].
-bool isFlowYamlCollectionNode(Object value) {
- if (value is YamlList || value is YamlMap) {
- return (value as dynamic).style == CollectionStyle.FLOW;
- }
-
- return false;
-}
-
-/// Determines the index where [newKey] will be inserted if the keys in [map] are in
-/// alphabetical order when converted to strings.
-///
-/// Returns the length of [map] if the keys in [map] are not in alphabetical order.
-int getMapInsertionIndex(YamlMap map, Object newKey) {
- ArgumentError.checkNotNull(map, 'map');
-
- final keys = map.nodes.keys.map((k) => k.toString()).toList();
-
- for (var i = 1; i < keys.length; i++) {
- if (keys[i].compareTo(keys[i - 1]) < 0) {
- return map.length;
- }
- }
-
- final insertionIndex = keys.indexWhere((key) => key.compareTo(newKey) > 0);
-
- if (insertionIndex != -1) return insertionIndex;
-
- return map.length;
-}
-
-/// Returns the [style] property of [target], if it is a [YamlNode]. Otherwise return null.
-Object getStyle(Object target) {
- if (target is YamlNode) {
- return (target as dynamic).style;
- }
-
- return null;
-}
-
-/// Returns the detected indentation step used in [yaml], or
-/// defaults to a value of `2` if no indentation step can be detected.
-///
-/// Indentation step is determined by the difference in indentation of the
-/// first block-styled yaml collection in the second level as compared to the
-/// top-level elements. In the case where there are multiple possible
-/// candidates, we choose the candidate closest to the start of [yaml].
-int getIndentation(YamlEditor editor) {
- final node = editor.parseAt([]);
- Iterable<YamlNode> children;
- var indentation = 2;
-
- if (node is YamlMap && node.style == CollectionStyle.BLOCK) {
- children = node.nodes.values;
- } else if (node is YamlList && node.style == CollectionStyle.BLOCK) {
- children = node.nodes;
- }
-
- if (children != null) {
- for (final child in children) {
- var indent = 0;
- if (child is YamlList) {
- indent = getListIndentation(editor.toString(), child);
- } else if (child is YamlMap) {
- indent = getMapIndentation(editor.toString(), child);
- }
-
- if (indent != 0) indentation = indent;
- }
- }
- return indentation;
-}
-
-/// Gets the indentation level of [list]. This is 0 if it is a flow list,
-/// but returns the number of spaces before the hyphen of elements for
-/// block lists.
-///
-/// Throws [UnsupportedError] if an empty block map is passed in.
-int getListIndentation(String yaml, YamlList list) {
- ArgumentError.checkNotNull(list, 'list');
-
- if (list.style == CollectionStyle.FLOW) return 0;
-
- /// An empty block map doesn't really exist.
- if (list.isEmpty) {
- throw UnsupportedError('Unable to get indentation for empty block list');
- }
-
- final lastSpanOffset = list.nodes.last.span.start.offset;
- final lastNewLine = yaml.lastIndexOf('\n', lastSpanOffset - 1);
- final lastHyphen = yaml.lastIndexOf('-', lastSpanOffset - 1);
-
- if (lastNewLine == -1) return lastHyphen;
-
- return lastHyphen - lastNewLine - 1;
-}
-
-/// Gets the indentation level of [map]. This is 0 if it is a flow map,
-/// but returns the number of spaces before the keys for block maps.
-int getMapIndentation(String yaml, YamlMap map) {
- ArgumentError.checkNotNull(map, 'map');
-
- if (map.style == CollectionStyle.FLOW) return 0;
-
- /// An empty block map doesn't really exist.
- if (map.isEmpty) {
- throw UnsupportedError('Unable to get indentation for empty block map');
- }
-
- /// Use the number of spaces between the last key and the newline as
- /// indentation.
- final lastKey = map.nodes.keys.last as YamlNode;
- final lastSpanOffset = lastKey.span.start.offset;
- final lastNewLine = yaml.lastIndexOf('\n', lastSpanOffset);
- final lastQuestionMark = yaml.lastIndexOf('?', lastSpanOffset);
-
- if (lastQuestionMark == -1) {
- if (lastNewLine == -1) return lastSpanOffset;
- return lastSpanOffset - lastNewLine - 1;
- }
-
- /// If there is a question mark, it might be a complex key. Check if it
- /// is on the same line as the key node to verify.
- if (lastNewLine == -1) return lastQuestionMark;
- if (lastQuestionMark > lastNewLine) {
- return lastQuestionMark - lastNewLine - 1;
- }
-
- return lastSpanOffset - lastNewLine - 1;
-}
-
-/// Returns the detected line ending used in [yaml], more specifically, whether
-/// [yaml] appears to use Windows `\r\n` or Unix `\n` line endings.
-///
-/// The heuristic used is to count all `\n` in the text and if stricly more
-/// than half of them are preceded by `\r` we report that windows line endings
-/// are used.
-String getLineEnding(String yaml) {
- var index = -1;
- var unixNewlines = 0;
- var windowsNewlines = 0;
- while ((index = yaml.indexOf('\n', index + 1)) != -1) {
- if (index != 0 && yaml[index - 1] == '\r') {
- windowsNewlines++;
- } else {
- unixNewlines++;
- }
- }
-
- return windowsNewlines > unixNewlines ? '\r\n' : '\n';
-}
diff --git a/lib/src/yaml_edit/wrap.dart b/lib/src/yaml_edit/wrap.dart
deleted file mode 100644
index a4e8975..0000000
--- a/lib/src/yaml_edit/wrap.dart
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// @dart=2.10
-
-import 'dart:collection' as collection;
-import 'package:collection/collection.dart';
-import 'package:source_span/source_span.dart';
-import 'package:yaml/yaml.dart';
-
-import 'equality.dart';
-import 'utils.dart';
-
-/// Returns a new [YamlMap] constructed by applying [update] onto the [nodes]
-/// of this [YamlMap].
-YamlMap updatedYamlMap(YamlMap map, Function(Map) update) {
- ArgumentError.checkNotNull(map, 'map');
-
- final dummyMap = deepEqualsMap();
- dummyMap.addAll(map.nodes);
-
- update(dummyMap);
-
- return wrapAsYamlNode(dummyMap);
-}
-
-/// Wraps [value] into a [YamlNode].
-///
-/// [Map]s, [List]s and Scalars will be wrapped as [YamlMap]s, [YamlList]s,
-/// and [YamlScalar]s respectively. If [collectionStyle]/[scalarStyle] is
-/// defined, and [value] is a collection or scalar, the wrapped [YamlNode] will
-/// have the respective style, otherwise it defaults to the ANY style.
-///
-/// If a [YamlNode] is passed in, no further wrapping will be done, and the
-/// [collectionStyle]/[scalarStyle] will not be applied.
-YamlNode wrapAsYamlNode(Object value,
- {CollectionStyle collectionStyle = CollectionStyle.ANY,
- ScalarStyle scalarStyle = ScalarStyle.ANY}) {
- if (value is YamlScalar) {
- assertValidScalar(value.value);
- return value;
- } else if (value is YamlList) {
- for (final item in value.nodes) {
- wrapAsYamlNode(item);
- }
-
- 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) {
- ArgumentError.checkNotNull(collectionStyle, 'collectionStyle');
- return YamlMapWrap(value, collectionStyle: collectionStyle);
- } else if (value is List) {
- ArgumentError.checkNotNull(collectionStyle, 'collectionStyle');
- return YamlListWrap(value, collectionStyle: collectionStyle);
- } else {
- assertValidScalar(value);
-
- ArgumentError.checkNotNull(scalarStyle, 'scalarStyle');
- return YamlScalarWrap(value, style: scalarStyle);
- }
-}
-
-/// Internal class that allows us to define a constructor on [YamlScalar]
-/// which takes in [style] as an argument.
-class YamlScalarWrap implements YamlScalar {
- /// The [ScalarStyle] to be used for the scalar.
- @override
- final ScalarStyle style;
-
- @override
- final SourceSpan span;
-
- @override
- final dynamic value;
-
- YamlScalarWrap(this.value, {this.style = ScalarStyle.ANY, Object sourceUrl})
- : span = shellSpan(sourceUrl) {
- ArgumentError.checkNotNull(style, 'scalarStyle');
- }
-
- @override
- String toString() => value.toString();
-}
-
-/// Internal class that allows us to define a constructor on [YamlMap]
-/// which takes in [style] as an argument.
-class YamlMapWrap
- with collection.MapMixin, UnmodifiableMapMixin
- implements YamlMap {
- /// The [CollectionStyle] to be used for the map.
- @override
- final CollectionStyle style;
-
- @override
- final Map<dynamic, YamlNode> nodes;
-
- @override
- final SourceSpan span;
-
- factory YamlMapWrap(Map dartMap,
- {CollectionStyle collectionStyle = CollectionStyle.ANY,
- Object sourceUrl}) {
- ArgumentError.checkNotNull(collectionStyle, 'collectionStyle');
-
- final wrappedMap = deepEqualsMap<dynamic, YamlNode>();
-
- for (final entry in dartMap.entries) {
- final wrappedKey = wrapAsYamlNode(entry.key);
- final wrappedValue = wrapAsYamlNode(entry.value);
- wrappedMap[wrappedKey] = wrappedValue;
- }
-
- return YamlMapWrap._(wrappedMap,
- style: collectionStyle, sourceUrl: sourceUrl);
- }
-
- YamlMapWrap._(this.nodes,
- {CollectionStyle style = CollectionStyle.ANY, Object sourceUrl})
- : span = shellSpan(sourceUrl),
- style = nodes.isEmpty ? CollectionStyle.FLOW : style;
-
- @override
- dynamic operator [](Object key) => nodes[key]?.value;
-
- @override
- Iterable get keys => nodes.keys.map((node) => node.value);
-
- @override
- Map get value => this;
-}
-
-/// Internal class that allows us to define a constructor on [YamlList]
-/// which takes in [style] as an argument.
-class YamlListWrap with collection.ListMixin implements YamlList {
- /// The [CollectionStyle] to be used for the list.
- @override
- final CollectionStyle style;
-
- @override
- final List<YamlNode> nodes;
-
- @override
- final SourceSpan span;
-
- @override
- int get length => nodes.length;
-
- @override
- set length(int index) {
- throw UnsupportedError('Cannot modify an unmodifiable List');
- }
-
- factory YamlListWrap(List dartList,
- {CollectionStyle collectionStyle = CollectionStyle.ANY,
- Object sourceUrl}) {
- ArgumentError.checkNotNull(collectionStyle, 'collectionStyle');
-
- final wrappedList = dartList.map(wrapAsYamlNode).toList();
- return YamlListWrap._(wrappedList,
- style: collectionStyle, sourceUrl: sourceUrl);
- }
-
- YamlListWrap._(this.nodes,
- {CollectionStyle style = CollectionStyle.ANY, Object sourceUrl})
- : span = shellSpan(sourceUrl),
- style = nodes.isEmpty ? CollectionStyle.FLOW : style;
-
- @override
- dynamic operator [](int index) => nodes[index].value;
-
- @override
- operator []=(int index, value) {
- throw UnsupportedError('Cannot modify an unmodifiable List');
- }
-
- @override
- List get value => this;
-}
diff --git a/pubspec.yaml b/pubspec.yaml
index 5902f9d..23fc0c8 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -25,6 +25,7 @@
source_span: ^1.8.1
stack_trace: ^1.10.0
yaml: ^3.1.0
+ yaml_edit: ^2.0.0
dev_dependencies:
shelf_test_handler: ^2.0.0