-
Notifications
You must be signed in to change notification settings - Fork 347
/
simple.dart
117 lines (106 loc) · 3.76 KB
/
simple.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// 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 '../../exception.dart';
import '../../logger.dart';
import '../../parse/selector.dart';
import '../selector.dart';
/// Names of pseudo-classes that take selectors as arguments, and that are
/// subselectors of the union of their arguments.
///
/// For example, `.foo` is a superselector of `:matches(.foo)`.
final _subselectorPseudos = {
'is',
'matches',
'where',
'any',
'nth-child',
'nth-last-child'
};
/// An abstract superclass for simple selectors.
///
/// {@category AST}
/// {@category Parsing}
abstract class SimpleSelector extends Selector {
/// This selector's specificity.
///
/// Specificity is represented in base 1000. The spec says this should be
/// "sufficiently high"; it's extremely unlikely that any single selector
/// sequence will contain 1000 simple selectors.
int get specificity => 1000;
SimpleSelector();
/// Parses a simple selector from [contents].
///
/// If passed, [url] is the name of the file from which [contents] comes.
/// [allowParent] controls whether a [ParentSelector] is allowed in this
/// selector.
///
/// Throws a [SassFormatException] if parsing fails.
factory SimpleSelector.parse(String contents,
{Object? url, Logger? logger, bool allowParent = true}) =>
SelectorParser(contents,
url: url, logger: logger, allowParent: allowParent)
.parseSimpleSelector();
/// Returns a new [SimpleSelector] based on [this], as though it had been
/// written with [suffix] at the end.
///
/// Assumes [suffix] is a valid identifier suffix. If this wouldn't produce a
/// valid [SimpleSelector], throws a [SassScriptException].
///
/// @nodoc
@internal
SimpleSelector addSuffix(String suffix) =>
throw SassScriptException('Invalid parent selector "$this"');
/// Returns the components of a [CompoundSelector] that matches only elements
/// matched by both this and [compound].
///
/// By default, this just returns a copy of [compound] with this selector
/// added to the end, or returns the original array if this selector already
/// exists in it.
///
/// Returns `null` if unification is impossible—for example, if there are
/// multiple ID selectors.
///
/// @nodoc
@internal
List<SimpleSelector>? unify(List<SimpleSelector> compound) {
if (compound.length == 1) {
var other = compound.first;
if (other is UniversalSelector ||
(other is PseudoSelector && (other.isHost || other.isHostContext))) {
return other.unify([this]);
}
}
if (compound.contains(this)) return compound;
var result = <SimpleSelector>[];
var addedThis = false;
for (var simple in compound) {
// Make sure pseudo selectors always come last.
if (!addedThis && simple is PseudoSelector) {
result.add(this);
addedThis = true;
}
result.add(simple);
}
if (!addedThis) result.add(this);
return result;
}
/// Whether this is a superselector of [other].
///
/// That is, whether this matches every element that [other] matches, as well
/// as possibly additional elements.
bool isSuperselector(SimpleSelector other) {
if (this == other) return true;
if (other is PseudoSelector && other.isClass) {
var list = other.selector;
if (list != null && _subselectorPseudos.contains(other.normalizedName)) {
return list.components.every((complex) =>
complex.components.isNotEmpty &&
complex.components.last.selector.components
.any((simple) => isSuperselector(simple)));
}
}
return false;
}
}