extension PureYAML.Parsing.EventStreamComposer { func range(at mark: PureYAML.Parsing.Mark) -> PureYAML.Model.SourceRange { .init(at: .init(line: mark.line, column: mark.column)) } func range(start: PureYAML.Parsing.Mark, end: PureYAML.Parsing.Mark) -> PureYAML.Model.SourceRange { .init(start: .init(line: start.line, column: start.column), end: .init(line: end.line, column: end.column)) } /// The node's located projection, falling back to a synthesized one at the node's mark if /// the located field was somehow not populated (never expected while tracking locations). func locatedNode(_ node: PureYAML.Parsing.EventStreamComposedNode) -> PureYAML.Model.LocatedValue { node.located ?? .synthesized(from: node.value, at: range(at: node.mark)) } /// The located pairs a merge value (`<<`) contributes, mirroring ``mergeSources(from:)``. /// The value-side merge validation has already run, so the located shape is a mapping or a /// sequence of mappings; anything else contributes nothing. func locatedMergeSources(from node: PureYAML.Parsing.EventStreamComposedNode) -> [[PureYAML.Model.LocatedPair]] { switch locatedNode(node) { case let .mapping(pairs, _): [pairs] case let .sequence(items, _): items.compactMap { item in guard case let .mapping(pairs, _) = item else { return nil } return pairs } case .scalar: [] } } /// The final located pairs, mirroring ``mergedPairs(from:localPairs:)`` exactly (same dedup, /// same order) so the located mapping has the identical shape to the value mapping. func mergedLocatedPairs( from mergeSources: [[PureYAML.Model.LocatedPair]], localPairs: [PureYAML.Model.LocatedPair], ) -> [PureYAML.Model.LocatedPair] { guard !mergeSources.isEmpty else { return localPairs } let localKeys = Set(localPairs.map(\.keyNode)) var inheritedKeys = Set() var inheritedPairs: [PureYAML.Model.LocatedPair] = [] for source in mergeSources { var sourceKeys = Set() for pair in source { guard !localKeys.contains(pair.keyNode), !inheritedKeys.contains(pair.keyNode) else { continue } inheritedPairs.append(pair) sourceKeys.insert(pair.keyNode) } inheritedKeys.formUnion(sourceKeys) } return inheritedPairs + localPairs } }