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:source_span/source_span.dart';
  
  import '../../../util/span.dart';
  import '../../../visitor/interface/statement.dart';
  import '../argument_list.dart';
  import '../callable_invocation.dart';
  import '../reference.dart';
  import '../statement.dart';
  import 'content_block.dart';
  
  /// A mixin invocation.
  ///
  /// {@category AST}
  final class IncludeRule extends Statement
      implements CallableInvocation, SassReference {
    /// The namespace of the mixin being invoked, or `null` if it's invoked
    /// without a namespace.
    final String? namespace;
  
    /// The name of the mixin being invoked, with underscores converted to
    /// hyphens.
    final String name;
  
    /// The original name of the mixin being invoked, without underscores
    /// converted to hyphens.
    final String originalName;
  
    /// The arguments to pass to the mixin.
    final ArgumentList arguments;
  
    /// The block that will be invoked for [ContentRule]s in the mixin being
    /// invoked, or `null` if this doesn't pass a content block.
    final ContentBlock? content;
  
    final FileSpan span;
  
    /// Returns this include's span, without its content block (if it has one).
    FileSpan get spanWithoutContent => content == null
        ? span
        : span.file.span(span.start.offset, arguments.span.end.offset).trim();
  
    FileSpan get nameSpan {
      var startSpan = span.text.startsWith('+')
          ? span.subspan(1).trimLeft()
          : span.withoutInitialAtRule();
      if (namespace != null) startSpan = startSpan.withoutNamespace();
      return startSpan.initialIdentifier();
    }
  
    FileSpan? get namespaceSpan {
      if (namespace == null) return null;
      var startSpan = span.text.startsWith('+')
          ? span.subspan(1).trimLeft()
          : span.withoutInitialAtRule();
      return startSpan.initialIdentifier();
    }
  
    IncludeRule(
      this.originalName,
      this.arguments,
      this.span, {
      this.namespace,
      this.content,
    }) : name = originalName.replaceAll('_', '-');
  
    T accept<T>(StatementVisitor<T> visitor) => visitor.visitIncludeRule(this);
  
    String toString() {
      var buffer = StringBuffer("@include ");
      if (namespace != null) buffer.write("$namespace.");
      buffer.write(name);
      if (!arguments.isEmpty) buffer.write("($arguments)");
      buffer.write(content == null ? ";" : " $content");
      return buffer.toString();
    }
  }
  