content: // Copyright 2019 Google Inc. Use of this source code is governed by an
  // MIT-style license that can be found in the LICENSE file or at
  // https://opensource.org/licenses/MIT.
  
  import '../exception.dart';
  import '../utils.dart';
  import 'extension.dart';
  
  /// An [Extension] created by merging two [Extension]s with the same extender
  /// and target.
  ///
  /// This is used when multiple mandatory extensions exist to ensure that both of
  /// them are marked as resolved.
  final class MergedExtension extends Extension {
    /// One of the merged extensions.
    final Extension left;
  
    /// The other merged extension.
    final Extension right;
  
    /// Returns an extension that combines [left] and [right].
    ///
    /// Throws a [SassException] if [left] and [right] have incompatible media
    /// contexts.
    ///
    /// Throws an [ArgumentError] if [left] and [right] don't have the same
    /// extender and target.
    static Extension merge(Extension left, Extension right) {
      if (left.extender.selector != right.extender.selector ||
          left.target != right.target) {
        throw ArgumentError("$left and $right aren't the same extension.");
      }
  
      if (left.mediaContext != null &&
          right.mediaContext != null &&
          !listEquals(left.mediaContext, right.mediaContext)) {
        throw SassException(
          "From ${left.span.message('')}\n"
          "You may not @extend the same selector from within different media "
          "queries.",
          right.span,
        );
      }
  
      // If one extension is optional and doesn't add a special media context, it
      // doesn't need to be merged.
      if (right.isOptional && right.mediaContext == null) return left;
      if (left.isOptional && left.mediaContext == null) return right;
  
      return MergedExtension._(left, right);
    }
  
    MergedExtension._(this.left, this.right)
        : super(
            left.extender.selector,
            left.target,
            left.span,
            mediaContext: left.mediaContext ?? right.mediaContext,
            optional: true,
          );
  
    /// Returns all leaf-node [Extension]s in the tree of [MergedExtension]s.
    Iterable<Extension> unmerge() sync* {
      var left = this.left;
      if (left is MergedExtension) {
        yield* left.unmerge();
      } else {
        yield left;
      }
  
      var right = this.right;
      if (right is MergedExtension) {
        yield* right.unmerge();
      } else {
        yield right;
      }
    }
  }
  