Skip to content

Commit d419df7

Browse files
authored
Add shared interfaces for various AST nodes (#1445)
Fixes #1401 and #1414. Adds `Dependency`, `SassDeclaration`, and `SassReference` interfaces, which expose some getters that multiple AST nodes have in common with a single type. These also add getters for common subspans (URL, name, and namespace) to the interfaces.
1 parent fd7eec9 commit d419df7

30 files changed

Lines changed: 425 additions & 119 deletions

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.38.1
2+
3+
* No user-visible changes
4+
15
## 1.38.0
26

37
* In expanded mode, emit characters in Unicode private-use areas as escape

lib/src/ast/sass.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ export 'sass/argument_invocation.dart';
88
export 'sass/at_root_query.dart';
99
export 'sass/callable_invocation.dart';
1010
export 'sass/configured_variable.dart';
11+
export 'sass/declaration.dart';
12+
export 'sass/dependency.dart';
1113
export 'sass/expression.dart';
1214
export 'sass/expression/binary_operation.dart';
1315
export 'sass/expression/boolean.dart';
1416
export 'sass/expression/color.dart';
1517
export 'sass/expression/function.dart';
1618
export 'sass/expression/if.dart';
19+
export 'sass/expression/interpolated_function.dart';
1720
export 'sass/expression/list.dart';
1821
export 'sass/expression/map.dart';
1922
export 'sass/expression/null.dart';
@@ -29,6 +32,7 @@ export 'sass/import/dynamic.dart';
2932
export 'sass/import/static.dart';
3033
export 'sass/interpolation.dart';
3134
export 'sass/node.dart';
35+
export 'sass/reference.dart';
3236
export 'sass/statement.dart';
3337
export 'sass/statement/at_root_rule.dart';
3438
export 'sass/statement/at_rule.dart';

lib/src/ast/sass/argument.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@ import 'package:meta/meta.dart';
66
import 'package:source_span/source_span.dart';
77

88
import '../../utils.dart';
9+
import '../../util/span.dart';
910
import 'expression.dart';
11+
import 'declaration.dart';
1012
import 'node.dart';
1113

1214
/// An argument declared as part of an [ArgumentDeclaration].
1315
///
1416
/// {@category AST}
1517
@sealed
16-
class Argument implements SassNode {
18+
class Argument implements SassNode, SassDeclaration {
1719
/// The argument name.
1820
final String name;
1921

@@ -30,6 +32,9 @@ class Argument implements SassNode {
3032
String get originalName =>
3133
defaultValue == null ? span.text : declarationName(span);
3234

35+
FileSpan get nameSpan =>
36+
defaultValue == null ? span : span.initialIdentifier(includeLeading: 1);
37+
3338
Argument(this.name, this.span, {this.defaultValue});
3439

3540
String toString() => defaultValue == null ? name : "$name: $defaultValue";

lib/src/ast/sass/argument_declaration.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import '../../logger.dart';
1010
import '../../parse/scss.dart';
1111
import '../../utils.dart';
1212
import '../../util/character.dart';
13+
import '../../util/span.dart';
1314
import 'argument.dart';
1415
import 'node.dart';
1516

lib/src/ast/sass/configured_variable.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
import 'package:meta/meta.dart';
66
import 'package:source_span/source_span.dart';
77

8+
import '../../util/span.dart';
89
import 'expression.dart';
10+
import 'declaration.dart';
911
import 'node.dart';
1012

1113
/// A variable configured by a `with` clause in a `@use` or `@forward` rule.
1214
///
1315
/// {@category AST}
1416
@sealed
15-
class ConfiguredVariable implements SassNode {
17+
class ConfiguredVariable implements SassNode, SassDeclaration {
1618
/// The name of the variable being configured.
1719
final String name;
1820

@@ -26,6 +28,8 @@ class ConfiguredVariable implements SassNode {
2628

2729
final FileSpan span;
2830

31+
FileSpan get nameSpan => span.initialIdentifier(includeLeading: 1);
32+
2933
ConfiguredVariable(this.name, this.expression, this.span,
3034
{bool guarded = false})
3135
: isGuarded = guarded;

lib/src/ast/sass/declaration.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2021 Google Inc. Use of this source code is governed by an
2+
// MIT-style license that can be found in the LICENSE file or at
3+
// https://opensource.org/licenses/MIT.
4+
5+
import 'package:meta/meta.dart';
6+
import 'package:source_span/source_span.dart';
7+
8+
import 'node.dart';
9+
10+
/// A common interface for any node that declares a Sass member.
11+
///
12+
/// {@category AST}
13+
@sealed
14+
abstract class SassDeclaration extends SassNode {
15+
/// The name of the declaration, with underscores converted to hyphens.
16+
///
17+
/// This does not include the `$` for variables.
18+
String get name;
19+
20+
/// The span containing this declaration's name.
21+
///
22+
/// This includes the `$` for variables.
23+
FileSpan get nameSpan;
24+
}

lib/src/ast/sass/dependency.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2021 Google Inc. Use of this source code is governed by an
2+
// MIT-style license that can be found in the LICENSE file or at
3+
// https://opensource.org/licenses/MIT.
4+
5+
import 'package:meta/meta.dart';
6+
import 'package:source_span/source_span.dart';
7+
8+
import 'node.dart';
9+
10+
/// A common interface for [UseRule]s, [ForwardRule]s, and [DynamicImport]s.
11+
///
12+
/// {@category AST}
13+
@sealed
14+
abstract class SassDependency extends SassNode {
15+
/// The URL of the dependency this rule loads.
16+
Uri get url;
17+
18+
/// The span of the URL for this dependency, including the quotes.
19+
FileSpan get urlSpan;
20+
}

lib/src/ast/sass/expression/function.dart

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,58 @@
55
import 'package:meta/meta.dart';
66
import 'package:source_span/source_span.dart';
77

8+
import '../../../util/span.dart';
89
import '../../../visitor/interface/expression.dart';
910
import '../expression.dart';
1011
import '../argument_invocation.dart';
1112
import '../callable_invocation.dart';
12-
import '../interpolation.dart';
13+
import '../reference.dart';
1314

1415
/// A function invocation.
1516
///
16-
/// This may be a plain CSS function or a Sass function.
17+
/// This may be a plain CSS function or a Sass function, but may not include
18+
/// interpolation.
1719
///
1820
/// {@category AST}
1921
@sealed
20-
class FunctionExpression implements Expression, CallableInvocation {
22+
class FunctionExpression
23+
implements Expression, CallableInvocation, SassReference {
2124
/// The namespace of the function being invoked, or `null` if it's invoked
2225
/// without a namespace.
2326
final String? namespace;
2427

25-
/// The name of the function being invoked.
26-
///
27-
/// If [namespace] is non-`null`, underscores are converted to hyphens in this name.
28-
/// If [namespace] is `null`, this could be a plain CSS function call, so underscores are kept unchanged.
29-
///
30-
/// If this is interpolated, the function will be interpreted as plain CSS,
31-
/// even if it has the same name as a Sass function.
32-
final Interpolation name;
28+
/// The name of the function being invoked, with underscores left as-is.
29+
final String originalName;
3330

3431
/// The arguments to pass to the function.
3532
final ArgumentInvocation arguments;
3633

3734
final FileSpan span;
3835

39-
FunctionExpression(this.name, this.arguments, this.span, {this.namespace});
36+
/// The name of the function being invoked, with underscores converted to
37+
/// hyphens.
38+
///
39+
/// If this function is a plain CSS function, use [originalName] instead.
40+
String get name => originalName.replaceAll('_', '-');
41+
42+
FileSpan get nameSpan {
43+
if (namespace == null) return span.initialIdentifier();
44+
return span.withoutNamespace().initialIdentifier();
45+
}
46+
47+
FileSpan? get namespaceSpan =>
48+
namespace == null ? null : span.initialIdentifier();
49+
50+
FunctionExpression(this.originalName, this.arguments, this.span,
51+
{this.namespace});
4052

4153
T accept<T>(ExpressionVisitor<T> visitor) =>
4254
visitor.visitFunctionExpression(this);
4355

4456
String toString() {
4557
var buffer = StringBuffer();
4658
if (namespace != null) buffer.write("$namespace.");
47-
buffer.write("$name$arguments");
59+
buffer.write("$originalName$arguments");
4860
return buffer.toString();
4961
}
5062
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2021 Google Inc. Use of this source code is governed by an
2+
// MIT-style license that can be found in the LICENSE file or at
3+
// https://opensource.org/licenses/MIT.
4+
5+
import 'package:meta/meta.dart';
6+
import 'package:source_span/source_span.dart';
7+
8+
import '../../../visitor/interface/expression.dart';
9+
import '../expression.dart';
10+
import '../argument_invocation.dart';
11+
import '../callable_invocation.dart';
12+
import '../interpolation.dart';
13+
14+
/// An interpolated function invocation.
15+
///
16+
/// This is always a plain CSS function.
17+
///
18+
/// {@category AST}
19+
@sealed
20+
class InterpolatedFunctionExpression implements Expression, CallableInvocation {
21+
/// The name of the function being invoked.
22+
final Interpolation name;
23+
24+
/// The arguments to pass to the function.
25+
final ArgumentInvocation arguments;
26+
27+
final FileSpan span;
28+
29+
InterpolatedFunctionExpression(this.name, this.arguments, this.span);
30+
31+
T accept<T>(ExpressionVisitor<T> visitor) =>
32+
visitor.visitInterpolatedFunctionExpression(this);
33+
34+
String toString() => '$name$arguments';
35+
}

lib/src/ast/sass/expression/variable.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
import 'package:meta/meta.dart';
66
import 'package:source_span/source_span.dart';
77

8+
import '../../../util/span.dart';
89
import '../../../visitor/interface/expression.dart';
910
import '../expression.dart';
11+
import '../reference.dart';
1012

1113
/// A Sass variable.
1214
///
1315
/// {@category AST}
1416
@sealed
15-
class VariableExpression implements Expression {
17+
class VariableExpression implements Expression, SassReference {
1618
/// The namespace of the variable being referenced, or `null` if it's
1719
/// referenced without a namespace.
1820
final String? namespace;
@@ -22,6 +24,14 @@ class VariableExpression implements Expression {
2224

2325
final FileSpan span;
2426

27+
FileSpan get nameSpan {
28+
if (namespace == null) return span;
29+
return span.withoutNamespace();
30+
}
31+
32+
FileSpan? get namespaceSpan =>
33+
namespace == null ? null : span.initialIdentifier();
34+
2535
VariableExpression(this.name, this.span, {this.namespace});
2636

2737
T accept<T>(ExpressionVisitor<T> visitor) =>

0 commit comments

Comments
 (0)