Adds support for shadow host selectors
Adds support for :host() and :host-context().
diff --git a/lib/parser.dart b/lib/parser.dart
index a268c55..a1a9823 100644
--- a/lib/parser.dart
+++ b/lib/parser.dart
@@ -1219,6 +1219,20 @@
return new Selector(simpleSequences, _makeSpan(start));
}
+ /// Same as [processSelector] but reports an error for each combinator.
+ ///
+ /// This is a quick fix for parsing <compound-selectors> until the parser
+ /// supports Selector Level 4 grammar:
+ /// https://drafts.csswg.org/selectors-4/#typedef-compound-selector
+ Selector processCompoundSelector() {
+ return processSelector()
+ ..simpleSelectorSequences.forEach((sequence) {
+ if (!sequence.isCombinatorNone) {
+ _error('compound selector can not contain combinator', sequence.span);
+ }
+ });
+ }
+
simpleSelectorSequence(bool forceCombinatorNone) {
var start = _peekToken.span;
var combinatorType = TokenKind.COMBINATOR_NONE;
@@ -1429,7 +1443,8 @@
// Functional pseudo?
if (_peekToken.kind == TokenKind.LPAREN) {
- if (!pseudoElement && pseudoName.name.toLowerCase() == 'not') {
+ var name = pseudoName.name.toLowerCase();
+ if (!pseudoElement && name == 'not') {
_eat(TokenKind.LPAREN);
// Negation : ':NOT(' S* negation_arg S* ')'
@@ -1437,6 +1452,12 @@
_eat(TokenKind.RPAREN);
return new NegationSelector(negArg, _makeSpan(start));
+ } else if (!pseudoElement && (name == 'host' || name == 'host-context')) {
+ _eat(TokenKind.LPAREN);
+ var selector = processCompoundSelector();
+ _eat(TokenKind.RPAREN);
+ var span = _makeSpan(start);
+ return new PseudoClassFunctionSelector(pseudoName, selector, span);
} else {
// Special parsing for expressions in pseudo functions. Minus is used
// as operator not identifier.
diff --git a/lib/src/css_printer.dart b/lib/src/css_printer.dart
index 035bc46..d3b62d0 100644
--- a/lib/src/css_printer.dart
+++ b/lib/src/css_printer.dart
@@ -324,7 +324,7 @@
void visitPseudoClassFunctionSelector(PseudoClassFunctionSelector node) {
emit(":${node.name}(");
- node.expression.visit(this);
+ node.argument.visit(this);
emit(')');
}
diff --git a/lib/src/tree.dart b/lib/src/tree.dart
index a6a64fc..63c7301 100644
--- a/lib/src/tree.dart
+++ b/lib/src/tree.dart
@@ -291,15 +291,19 @@
String toString() => "::$name";
}
-// :pseudoClassFunction(expression)
+// :pseudoClassFunction(argument)
class PseudoClassFunctionSelector extends PseudoClassSelector {
- final SelectorExpression expression;
+ final TreeNode _argument; // Selector, SelectorExpression
- PseudoClassFunctionSelector(Identifier name, this.expression, SourceSpan span)
+ PseudoClassFunctionSelector(Identifier name, this._argument, SourceSpan span)
: super(name, span);
PseudoClassFunctionSelector clone() =>
- new PseudoClassFunctionSelector(_name, expression, span);
+ new PseudoClassFunctionSelector(_name, _argument, span);
+
+ TreeNode get argument => _argument;
+ Selector get selector => _argument as Selector;
+ SelectorExpression get expression => _argument as SelectorExpression;
visit(VisitorBase visitor) => visitor.visitPseudoClassFunctionSelector(this);
}
diff --git a/lib/src/tree_printer.dart b/lib/src/tree_printer.dart
index 9b0a6c2..97c6acc 100644
--- a/lib/src/tree_printer.dart
+++ b/lib/src/tree_printer.dart
@@ -338,7 +338,7 @@
void visitPseudoClassFunctionSelector(PseudoClassFunctionSelector node) {
heading('Pseudo Class Function Selector', node);
output.depth++;
- visitSelectorExpression(node.expression);
+ node.argument.visit(this);
super.visitPseudoClassFunctionSelector(node);
output.depth--;
}
diff --git a/test/selector_test.dart b/test/selector_test.dart
index dff4415..87fb60a 100644
--- a/test/selector_test.dart
+++ b/test/selector_test.dart
@@ -46,6 +46,18 @@
selectorAst = selector('#_privateId', errors: errors..clear());
expect(errors.isEmpty, true, reason: errors.toString());
expect('#_privateId', compactOuptut(selectorAst));
+
+ selectorAst = selector(':host', errors: errors..clear());
+ expect(errors.isEmpty, true, reason: errors.toString());
+ expect(compactOuptut(selectorAst), ':host');
+
+ selectorAst = selector(':host(.foo)', errors: errors..clear());
+ expect(errors.isEmpty, true, reason: errors.toString());
+ expect(compactOuptut(selectorAst), ':host(.foo)');
+
+ selectorAst = selector(':host-context(.foo)', errors: errors..clear());
+ expect(errors.isEmpty, true, reason: errors.toString());
+ expect(compactOuptut(selectorAst), ':host-context(.foo)');
}
// TODO(terry): Move this failure case to a failure_test.dart when the analyzer