Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces configurable alias and anchor mutation behaviors (AliasBehavior) to package:yaml_edit, allowing developers to choose between disallowing, referencing, or copying-on-write when modifying aliased YAML nodes. It also includes out-of-scope changes to package:pub_semver to fix parsing of pre-release and build identifiers. Feedback highlights several critical issues: getAnchorTag can return trailing whitespace leading to invalid YAML formatting in map mutations, _AliasEntryKey violates the == and hashCode contract, and potential NullThrownErrors exist when handling YamlNode keys. Additionally, the public getAnchorTag method lacks required documentation, and the out-of-scope pub_semver changes should be moved to a separate pull request.
Adds AliasBehavior enum with reference, copyOnWrite, and disallow options to YamlEditor. Implements path redirection for alias references, Asymmetric Copy-On-Write inline materialization, and preserves anchor tags during anchor updates.
…references, and anchor removal
… conditions, and add example script
…ing, unwrap YamlNode keys in editor.dart, and fix _AliasEntryKey equality
babbc57 to
e34988d
Compare
- Add _IdentityPair coinductive cycle detection to deepEquals and recursion guard to deepHashCode in equality.dart. - Add Set<Object?>.identity() recursion guard to wrapAsYamlNode in wrap.dart. - Add Set<YamlNode>.identity() recursion guards to _hasActiveReferencesToAnchor, _collectSubAnchorTags, and _updateNodeAndAliases in editor.dart. - Guard getContentSensitiveEnd and getListIndentation against cycles and alias offsets in utils.dart. - Ensure AliasBehavior.disallow cleanly rejects mutations on cyclic structures with AliasException. - Ensure AliasBehavior.reference updates cyclic anchor definitions without hanging. - Add comprehensive unit tests in test/cyclic_anchors_test.dart.
- Differentiate intra-template anchors and aliases vs extra-template aliases during Copy-On-Write shallow unfolding. - Strip intra-template sub-anchor definition tags and expand intra-template alias references inline to their target value representation. - Preserve extra-template alias references intact to maintain Lazy COW. - Update getTrueContentSensitiveEnd to resolve content-sensitive ends across child collections with alias reference leaves. - Add comprehensive unit tests in test/alias_behavior_test.dart covering intra-template alias expansion, Lazy COW preservation, subsequent base template mutation, and clean base template removal without AliasException.
- Locate key origins in << via _findMergedOrigin, supporting single merges, chained merges, and multi-merges (<<: [*m1, *m2]) with precedence. - Update _traverse to resolve keys through << when not directly present in map.nodes. - Update _resolvePath to enforce AliasBehavior across merge keys: - AliasBehavior.disallow: throw AliasException on merged key update or removal, while allowing explicit keys and new keys. - AliasBehavior.reference: redirect merged key mutations and removals to the anchor definition in-place, preserving explicit local overrides. - AliasBehavior.copyOnWrite: insert explicit overrides into target map without mutating template, materialize intermediate sub-maps for nested paths, and restore inherited anchor values on explicit override removal. - Update _addToBlockMap and _removeFromBlockMap to respect alias spans and preserve anchor tags. - Propagate chained alias updates through _updateNodeAndAliases. - Add comprehensive unit tests in test/merge_keys_test.dart.
- Add YamlChar classification constants and predicates in char_codes.dart. - Introduce comment-aware flow delimiter scanning (findNextFlowDelimiter, findPreviousFlowDelimiter) in utils.dart. - Use positive lookahead for YAML delimiter boundaries in aliasReferencePattern, supporting anchor names containing special characters (., /, @, +) and avoiding prefix substring collisions. - Sort intra-template anchor keys by descending length before inline replacement. - Use true content-sensitive ending in _appendToBlockList and preserve trailing newlines of keep-chomping scalars (|+). - Locate colon dynamically after map key in _replaceInBlockMap to avoid deleting colons on quoted keys with whitespace. - Support empty values without trailing spaces in _replaceInFlowMap. - Use lineEnding in _tryYamlEncodeFolded and _tryYamlEncodeLiteral headers. - Fall back to list.span.start.column in getListIndentation when a list contains only alias references. - Add comprehensive robustness tests in test/robustness_test.dart.
Stacked on top of #2587.
This PR introduces configurable YAML alias handling semantics to
package:yaml_edit, allowing developers to opt into reference redirection or lazy Copy-On-Write (COW) materialization instead of throwing exceptions on alias mutations.Motivation
Previously, any mutation targeting an alias reference (
*anchor) or anchor definition (&anchor) inYamlEditorthrew anAliasException. This prevented automated YAML editing in real-world files (such as CI/CD workflows, Docker Compose, and Kubernetes manifests) where YAML anchors and aliases are widely used for shared configuration templates.Changes
AliasBehaviorEnum: Added anAliasBehaviorenum with three options:disallow(default): Any mutation touching an alias or anchor throws anAliasException(100% backwards-compatible).reference: Mutating a property through an alias reference redirects the mutation to the underlying anchor definition, updating the template and automatically propagating the change to all references.copyOnWrite(Lazy COW): Mutating a property through an alias reference unfolds the referenced collection inline at the reference location, decoupling it from the anchor template without modifying the original definition.*nested) until those subtrees are directly modified.&sub_anchor) and expands intra-template aliases (*sub_anchor) inline so decoupled copies are completely self-contained and free from dangling references upon anchor deletion.getAnchorTagto extract clean&anchorNametags from AST span text so that updating or replacing an anchor definition leaf preserves its anchor tag.isAnchorDefinitionto inspect whether a child node in a map or list is an anchor definition.Set.identity(),Map.identity()) to ensure AST node tracking respectspackage:yamlobject reuse.aliasReferencePatternusing positive lookahead(?=[\s,\[\]{}]|$)to match YAML delimiter boundaries, supporting anchor names with special characters (.,/,@,+).test/alias_behavior_test.dartcovering reference, copyOnWrite, disallow, intra-template sub-anchors, anchor name character sets, collection headers, and list appends.example/alias_behavior_example.dartdemonstrating usage.2.3.0-wipinpubspec.yamland documented changes inCHANGELOG.md.