content: // Copyright 2016 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 'package:meta/meta.dart';
  
  import '../../visitor/every_css.dart';
  import '../../visitor/interface/css.dart';
  import '../../visitor/serialize.dart';
  import '../node.dart';
  import 'at_rule.dart';
  import 'comment.dart';
  import 'style_rule.dart';
  
  /// A statement in a plain CSS syntax tree.
  @sealed
  abstract class CssNode implements AstNode {
    /// The node that contains this, or `null` for the root [CssStylesheet] node.
    @internal
    CssParentNode? get parent;
  
    /// Whether this was generated from the last node in a nested Sass tree that
    /// got flattened during evaluation.
    bool get isGroupEnd;
  
    /// Calls the appropriate visit method on [visitor].
    T accept<T>(CssVisitor<T> visitor);
  
    /// Whether this is invisible and won't be emitted to the compiled stylesheet.
    ///
    /// Note that this doesn't consider nodes that contain loud comments to be
    /// invisible even though they're omitted in compressed mode.
    @internal
    bool get isInvisible => accept(
          const _IsInvisibleVisitor(includeBogus: true, includeComments: false),
        );
  
    // Whether this node would be invisible even if style rule selectors within it
    // didn't have bogus combinators.
    ///
    /// Note that this doesn't consider nodes that contain loud comments to be
    /// invisible even though they're omitted in compressed mode.
    @internal
    bool get isInvisibleOtherThanBogusCombinators => accept(
          const _IsInvisibleVisitor(includeBogus: false, includeComments: false),
        );
  
    // Whether this node will be invisible when loud comments are stripped.
    @internal
    bool get isInvisibleHidingComments => accept(
          const _IsInvisibleVisitor(includeBogus: true, includeComments: true),
        );
  
    String toString() => serialize(this, inspect: true).$1;
  }
  
  // NOTE: New at-rule implementations should add themselves to [AtRootRule]'s
  // exclude logic.
  /// A [CssNode] that can have child statements.
  @sealed
  abstract class CssParentNode extends CssNode {
    /// The child statements of this node.
    List<CssNode> get children;
  
    /// Whether the rule has no children and should be emitted without curly
    /// braces.
    ///
    /// This implies `children.isEmpty`, but the reverse is not true—for a rule
    /// like `@foo {}`, [children] is empty but [isChildless] is `false`.
    bool get isChildless;
  }
  
  /// The visitor used to implement [CssNode.isInvisible]
  class _IsInvisibleVisitor with EveryCssVisitor {
    /// Whether to consider selectors with bogus combinators invisible.
    final bool includeBogus;
  
    /// Whether to consider comments invisible.
    final bool includeComments;
  
    const _IsInvisibleVisitor({
      required this.includeBogus,
      required this.includeComments,
    });
  
    // An unknown at-rule is never invisible. Because we don't know the semantics
    // of unknown rules, we can't guarantee that (for example) `@foo {}` isn't
    // meaningful.
    bool visitCssAtRule(CssAtRule rule) => false;
  
    bool visitCssComment(CssComment comment) =>
        includeComments && !comment.isPreserved;
  
    bool visitCssStyleRule(CssStyleRule rule) =>
        (includeBogus
            ? rule.selector.isInvisible
            : rule.selector.isInvisibleOtherThanBogusCombinators) ||
        super.visitCssStyleRule(rule);
  }
  