A few more API docs.

This commit is contained in:
Natalie Weizenbaum 2016-10-11 00:27:19 -07:00
parent 4c418603e0
commit 8136c16cab
4 changed files with 173 additions and 27 deletions

View File

@ -11,7 +11,6 @@ import 'stylesheet.dart';
/// A parser for the indented syntax.
class SassParser extends StylesheetParser {
/// The indentation level at the current scanner position.
int get currentIndentation => _currentIndentation;
var _currentIndentation = 0;
@ -87,6 +86,11 @@ class SassParser extends StylesheetParser {
return statements;
}
/// Consumes a child of the current statement.
///
/// This consumes children that are allowed at all levels of the document; the
/// [child] parameter is called to consume any children that are specifically
/// allowed in the caller's context.
Statement _child(Statement child()) {
switch (scanner.peekChar()) {
case $dollar:
@ -113,6 +117,7 @@ class SassParser extends StylesheetParser {
}
}
/// Consumes an indented-style silent comment.
Comment _silentComment() {
var start = scanner.state;
scanner.expect("//");
@ -141,6 +146,7 @@ class SassParser extends StylesheetParser {
silent: true);
}
/// Consumes an indented-style loud context.
Comment _loudComment() {
var start = scanner.state;
scanner.expect("/*");
@ -173,8 +179,9 @@ class SassParser extends StylesheetParser {
silent: false);
}
// Doesn't consume newlines, doesn't support comments.
void whitespace() {
// This overrides whitespace consumption so that it doesn't consume newlines
// or loud comments.
while (!scanner.isDone) {
var next = scanner.peekChar();
if (next != $tab && next != $space) break;
@ -186,6 +193,8 @@ class SassParser extends StylesheetParser {
}
}
/// As long as the scanner's position is indented beneath the starting line,
/// runs [body] to consume the next statement.
void _whileIndentedLower(void body()) {
var parentIndentation = currentIndentation;
int childIndentation;
@ -203,6 +212,8 @@ class SassParser extends StylesheetParser {
}
}
/// Consumes indentation whitespace and returns the indentation level of the
/// next line.
int _readIndentation() {
if (_nextIndentation == null) _peekIndentation();
_currentIndentation = _nextIndentation;
@ -212,6 +223,7 @@ class SassParser extends StylesheetParser {
return currentIndentation;
}
/// Returns the indentation level of the next line.
int _peekIndentation() {
if (_nextIndentation != null) return _nextIndentation;
@ -262,6 +274,10 @@ class SassParser extends StylesheetParser {
return _nextIndentation;
}
/// Ensures that the document uses consistent characters for indentation.
///
/// The [containsTab] and [containsSpace] parameters refer to a single line of
/// indentation that has just been parsed.
void _checkIndentationConsistency(bool containsTab, bool containsSpace) {
if (containsTab) {
if (containsSpace) {

View File

@ -8,6 +8,7 @@ import '../ast/sass.dart';
import '../util/character.dart';
import 'stylesheet.dart';
/// A parser for the CSS-compatible syntax.
class ScssParser extends StylesheetParser {
bool get indented => false;
int get currentIndentation => null;
@ -109,6 +110,7 @@ class ScssParser extends StylesheetParser {
return statements;
}
/// Consumes a statement-level silent comment block.
Comment _silentComment() {
var start = scanner.state;
scanner.expect("//");
@ -124,6 +126,7 @@ class ScssParser extends StylesheetParser {
silent: true);
}
/// Consumes a statement-level loud comment block.
Comment _loudComment() {
var start = scanner.state;
scanner.expect("/*");

View File

@ -9,13 +9,13 @@ import '../util/character.dart';
import '../utils.dart';
import 'parser.dart';
/// Pseudo-class selectors that take unadorned selectors as arguments.
final _selectorPseudoClasses = new Set.from(
["not", "matches", "current", "any", "has", "host", "host-context"]);
final _prefixedSelectorPseudoClasses =
new Set.from(["nth-child", "nth-last-child"]);
/// A parser for selectors.
class SelectorParser extends Parser {
/// Whether this parser allows the parent selector `&`.
final bool _allowParent;
SelectorParser(String contents, {url, bool allowParent: true})
@ -46,6 +46,7 @@ class SelectorParser extends Parser {
});
}
/// Consumes a selector list.
SelectorList _selectorList() {
var components = <ComplexSelector>[];
@ -65,6 +66,10 @@ class SelectorParser extends Parser {
return new SelectorList(components);
}
/// Consumes a complex selector.
///
/// If [lineBreak] is `true`, that indicates that there was a line break
/// before this selector.
ComplexSelector _complexSelector({bool lineBreak: false}) {
var components = <ComplexSelectorComponent>[];
@ -113,6 +118,7 @@ class SelectorParser extends Parser {
return new ComplexSelector(components, lineBreak: lineBreak);
}
/// Consumes a compound selector.
CompoundSelector _compoundSelector() {
var components = <SimpleSelector>[_simpleSelector()];
@ -124,6 +130,10 @@ class SelectorParser extends Parser {
return new CompoundSelector(components);
}
/// Consumes a simple selector.
///
/// If [allowParent] is passed, it controls whether the parent selector `&` is
/// allowed. Otherwise, it defaults to [_allowParent].
SimpleSelector _simpleSelector({bool allowParent}) {
allowParent ??= _allowParent;
switch (scanner.peekChar()) {
@ -146,6 +156,7 @@ class SelectorParser extends Parser {
}
}
/// Consumes an attribute selector.
AttributeSelector _attributeSelector() {
scanner.expectChar($lbracket);
whitespace();
@ -170,6 +181,7 @@ class SelectorParser extends Parser {
return new AttributeSelector.withOperator(name, operator, value);
}
/// Consumes a qualified name as part of an attribute selector.
QualifiedName _attributeName() {
if (scanner.scanChar($asterisk)) {
scanner.expectChar($pipe);
@ -185,6 +197,7 @@ class SelectorParser extends Parser {
return new QualifiedName(identifier(), namespace: nameOrNamespace);
}
/// Consumes an attribute selector's operator.
AttributeOperator _attributeOperator() {
var start = scanner.state;
switch (scanner.readChar()) {
@ -217,24 +230,28 @@ class SelectorParser extends Parser {
}
}
/// Consumes a class selector.
ClassSelector _classSelector() {
scanner.expectChar($dot);
var name = identifier();
return new ClassSelector(name);
}
/// Consumes an ID selector.
IDSelector _idSelector() {
scanner.expectChar($hash);
var name = identifier();
return new IDSelector(name);
}
/// Consumes a placeholder selector.
PlaceholderSelector _placeholderSelector() {
scanner.expectChar($percent);
var name = identifier();
return new PlaceholderSelector(name);
}
/// Consumes a parent selector.
ParentSelector _parentSelector() {
scanner.expectChar($ampersand);
var next = scanner.peekChar();
@ -244,6 +261,7 @@ class SelectorParser extends Parser {
return new ParentSelector(suffix: suffix);
}
/// Consumes a pseudo selector.
PseudoSelector _pseudoSelector() {
scanner.expectChar($colon);
var element = scanner.scanChar($colon);
@ -261,7 +279,7 @@ class SelectorParser extends Parser {
argument = declarationValue();
} else if (_selectorPseudoClasses.contains(unvendored)) {
selector = _selectorList();
} else if (_prefixedSelectorPseudoClasses.contains(unvendored)) {
} else if (unvendored == "nth-child" || unvendored == "nth-last-child") {
argument = rawText(_aNPlusB);
if (scanWhitespace()) {
expectIdentifier("of", ignoreCase: true);
@ -279,6 +297,9 @@ class SelectorParser extends Parser {
element: element, argument: argument, selector: selector);
}
/// Consumes an [`An+B` production][An+B].
///
/// [An+B]: https://drafts.csswg.org/css-syntax-3/#anb-microsyntax
void _aNPlusB() {
switch (scanner.peekChar()) {
case $e:
@ -321,6 +342,9 @@ class SelectorParser extends Parser {
}
}
/// Consumes a type selector or a universal selector.
///
/// These are combined because either one could start with `*`.
SimpleSelector _typeOrUniversalSelector() {
var first = scanner.peekChar();
if (first == $asterisk) {

View File

@ -29,20 +29,23 @@ import 'parser.dart';
/// private, except where they have to be public for subclasses to refer to
/// them.
abstract class StylesheetParser extends Parser {
/// Whether the parser is currently parsing the contents of a mixin
/// declaration.
var _inMixin = false;
var _inContentBlock = false;
var _inControlDirective = false;
/// Whether the current mixin contains at least one `@content` rule.
///
/// This is `null` unless [_inMixin] is `true`.
bool _mixinHasContent;
StylesheetParser(String contents, {url}) : super(contents, url: url);
/// Whether the parser is currently parsing a content block passed to a mixin.
var _inContentBlock = false;
// Conventions:
//
// * All statement functions consume through following whitespace, including
// comments. No other functions do so unless explicitly specified.
/// Whether the parser is currently parsing a control directive such as `@if`
/// or `@each`.
var _inControlDirective = false;
StylesheetParser(String contents, {url}) : super(contents, url: url);
// ## Statements
@ -63,11 +66,13 @@ abstract class StylesheetParser extends Parser {
});
}
/// Consumes a statement that's allowed at the top level of the stylesheet.
Statement _topLevelStatement() {
if (scanner.peekChar() == $at) return _atRule(_topLevelStatement);
return _styleRule();
}
/// Consumes a variable declaration.
VariableDeclaration variableDeclaration() {
var start = scanner.state;
var name = variableName();
@ -98,6 +103,7 @@ abstract class StylesheetParser extends Parser {
guarded: guarded, global: global);
}
/// Consumes a style rule.
StyleRule _styleRule() {
var start = scanner.state;
var selector = _almostAnyValue();
@ -105,21 +111,13 @@ abstract class StylesheetParser extends Parser {
return new StyleRule(selector, children, scanner.spanFrom(start));
}
/// Consumes a statement that's allowed within a style rule.
Statement _ruleChild() {
if (scanner.peekChar() == $at) return _atRule(_ruleChild);
return _declarationOrStyleRule();
}
Expression _declarationExpression() {
if (lookingAtChildren()) {
return new StringExpression(new Interpolation([], scanner.emptySpan),
quotes: true);
}
return _expression();
}
/// Parses a [Declaration] or a [StyleRule].
/// Consumes a [Declaration] or a [StyleRule].
///
/// When parsing the contents of a style rule, it can be difficult to tell
/// declarations apart from nested style rules. Since we don't thoroughly
@ -251,6 +249,11 @@ abstract class StylesheetParser extends Parser {
children: lookingAtChildren() ? children(_declarationChild) : null);
}
/// Consumes a property declaration.
///
/// This is only used in contexts where declarations are allowed but style
/// rules are not, such as nested declarations. Otherwise,
/// [_declarationOrStyleRule] is used instead.
Declaration _declaration() {
var start = scanner.state;
var name = _interpolatedIdentifier();
@ -269,6 +272,20 @@ abstract class StylesheetParser extends Parser {
children: lookingAtChildren() ? children(_declarationChild) : null);
}
/// Consumes an expression after a property declaration.
///
/// This parses an empty identifier expression if the declaration has no value
/// but has children.
Expression _declarationExpression() {
if (lookingAtChildren()) {
return new StringExpression(new Interpolation([], scanner.emptySpan),
quotes: true);
}
return _expression();
}
/// Consumes a statement that's allowed within a declaration.
Statement _declarationChild() {
if (scanner.peekChar() == $at) return _declarationAtRule();
return _declaration();
@ -276,6 +293,11 @@ abstract class StylesheetParser extends Parser {
// ## At Rules
/// Consumes an at-rule.
///
/// This consumes at-rules that are allowed at all levels of the document; the
/// [child] parameter is called to consume any at-rules that are specifically
/// allowed in the caller's context.
Statement _atRule(Statement child()) {
var start = scanner.state;
var name = _atRuleName();
@ -322,6 +344,7 @@ abstract class StylesheetParser extends Parser {
}
}
/// Consumes an at-rule allowed within a property declaration.
Statement _declarationAtRule() {
var start = scanner.state;
var name = _atRuleName();
@ -352,6 +375,7 @@ abstract class StylesheetParser extends Parser {
}
}
/// Consumes an at-rule allowed within a function.
Statement _functionAtRule() {
var start = scanner.state;
switch (_atRuleName()) {
@ -378,6 +402,7 @@ abstract class StylesheetParser extends Parser {
}
}
/// Consumes an at-rule's name.
String _atRuleName() {
scanner.expectChar($at);
var name = identifier();
@ -385,6 +410,9 @@ abstract class StylesheetParser extends Parser {
return name;
}
/// Consumes an `@at-root` rule.
///
/// [start] should point before the `@`.
AtRootRule _atRootRule(LineScannerState start) {
var next = scanner.peekChar();
var query = next == $hash || next == $lparen ? _queryExpression() : null;
@ -393,6 +421,9 @@ abstract class StylesheetParser extends Parser {
query: query);
}
/// Consumes a `@content` rule.
///
/// [start] should point before the `@`.
ContentRule _contentRule(LineScannerState start) {
if (_inMixin) {
_mixinHasContent = true;
@ -404,9 +435,16 @@ abstract class StylesheetParser extends Parser {
return null;
}
/// Consumes a `@debug` rule.
///
/// [start] should point before the `@`.
DebugRule _debugRule(LineScannerState start) =>
new DebugRule(_expression(), scanner.spanFrom(start));
/// Consumes an `@each` rule.
///
/// [start] should point before the `@`. [child] is called to consume any
/// children that are specifically allowed in the caller's context.
EachRule _eachRule(LineScannerState start, Statement child()) {
var wasInControlDirective = _inControlDirective;
_inControlDirective = true;
@ -429,9 +467,15 @@ abstract class StylesheetParser extends Parser {
return new EachRule(variables, list, children, scanner.spanFrom(start));
}
/// Consumes an `@error` rule.
///
/// [start] should point before the `@`.
ErrorRule _errorRule(LineScannerState start) =>
new ErrorRule(_expression(), scanner.spanFrom(start));
/// Consumes an `@extend` rule.
///
/// [start] should point before the `@`.
ExtendRule _extendRule(LineScannerState start) {
var value = _almostAnyValue();
var optional = scanner.scanChar($exclamation);
@ -439,6 +483,9 @@ abstract class StylesheetParser extends Parser {
return new ExtendRule(value, scanner.spanFrom(start), optional: optional);
}
/// Consumes a function declaration.
///
/// [start] should point before the `@`.
FunctionRule _functionRule(LineScannerState start) {
var name = identifier();
whitespace();
@ -457,6 +504,10 @@ abstract class StylesheetParser extends Parser {
return new FunctionRule(name, arguments, children, scanner.spanFrom(start));
}
/// Consumes a `@for` rule.
///
/// [start] should point before the `@`. [child] is called to consume any
/// children that are specifically allowed in the caller's context.
ForRule _forRule(LineScannerState start, Statement child()) {
var wasInControlDirective = _inControlDirective;
_inControlDirective = true;
@ -491,6 +542,10 @@ abstract class StylesheetParser extends Parser {
exclusive: exclusive);
}
/// Consumes an `@if` rule.
///
/// [start] should point before the `@`. [child] is called to consume any
/// children that are specifically allowed in the caller's context.
IfRule _ifRule(LineScannerState start, Statement child()) {
var ifIndentation = currentIndentation;
var wasInControlDirective = _inControlDirective;
@ -516,6 +571,9 @@ abstract class StylesheetParser extends Parser {
return new IfRule(clauses, scanner.spanFrom(start), lastClause: lastClause);
}
/// Consumes an `@import` rule.
///
/// [start] should point before the `@`.
Statement _importRule(LineScannerState start) {
if (_inControlDirective) {
_disallowedAtRule(start);
@ -547,6 +605,7 @@ abstract class StylesheetParser extends Parser {
}
}
/// Returns whether [url] indicates that an `@import` is a plain CSS import.
bool _isPlainImportUrl(String url) {
if (url.length < "//".length) return false;
@ -556,6 +615,9 @@ abstract class StylesheetParser extends Parser {
return url.startsWith("http://") || url.startsWith("https://");
}
/// Consumes an `@include` rule.
///
/// [start] should point before the `@`.
IncludeRule _includeRule(LineScannerState start) {
var name = identifier();
whitespace();
@ -575,9 +637,15 @@ abstract class StylesheetParser extends Parser {
children: children);
}
/// Consumes a `@media` rule.
///
/// [start] should point before the `@`.
MediaRule _mediaRule(LineScannerState start) => new MediaRule(
_mediaQueryList(), children(_ruleChild), scanner.spanFrom(start));
/// Consumes a mixin declaration.
///
/// [start] should point before the `@`.
MixinRule _mixinRule(LineScannerState start) {
var name = identifier();
whitespace();
@ -597,14 +665,21 @@ abstract class StylesheetParser extends Parser {
_mixinHasContent = false;
var children = this.children(_ruleChild);
_inMixin = false;
_mixinHasContent = null;
return new MixinRule(name, arguments, children, scanner.spanFrom(start),
hasContent: _mixinHasContent);
}
/// Consumes a `@return` rule.
///
/// [start] should point before the `@`.
ReturnRule _returnRule(LineScannerState start) =>
new ReturnRule(_expression(), scanner.spanFrom(start));
/// Consumes a `@supports` rule.
///
/// [start] should point before the `@`.
SupportsRule _supportsRule(LineScannerState start) {
var condition = _supportsCondition();
whitespace();
@ -612,9 +687,16 @@ abstract class StylesheetParser extends Parser {
condition, children(_ruleChild), scanner.spanFrom(start));
}
/// Consumes a `@warn` rule.
///
/// [start] should point before the `@`.
WarnRule _warnRule(LineScannerState start) =>
new WarnRule(_expression(), scanner.spanFrom(start));
/// Consumes a `@while` rule.
///
/// [start] should point before the `@`. [child] is called to consume any
/// children that are specifically allowed in the caller's context.
WhileRule _whileRule(LineScannerState start, Statement child()) {
var wasInControlDirective = _inControlDirective;
_inControlDirective = true;
@ -624,6 +706,9 @@ abstract class StylesheetParser extends Parser {
return new WhileRule(expression, children, scanner.spanFrom(start));
}
/// Consumes an at-rule that's not explicitly supported by Sass.
///
/// [start] should point before the `@`. [name] is the name of the at-rule.
AtRule _unknownAtRule(LineScannerState start, String name) {
Interpolation value;
var next = scanner.peekChar();
@ -634,7 +719,11 @@ abstract class StylesheetParser extends Parser {
children: lookingAtChildren() ? children(_ruleChild) : null);
}
// This returns [Statement] so that it can be returned within case statements.
/// Throws a [StringScannerException] indicating that the at-rule starting at
/// [start] is not allowed in the current context.
///
/// This declares a return type of [Statement] so that it can be returned
/// within case statements.
Statement _disallowedAtRule(LineScannerState start) {
_almostAnyValue();
scanner.error("This at-rule is not allowed here.",
@ -643,6 +732,7 @@ abstract class StylesheetParser extends Parser {
return null;
}
/// Consumes an argument declaration.
ArgumentDeclaration _argumentDeclaration() {
var start = scanner.state;
scanner.expectChar($lparen);
@ -684,6 +774,7 @@ abstract class StylesheetParser extends Parser {
// ## Expressions
/// Consumes an argument invocation.
ArgumentInvocation _argumentInvocation() {
var start = scanner.state;
scanner.expectChar($lparen);
@ -731,6 +822,11 @@ abstract class StylesheetParser extends Parser {
rest: rest, keywordRest: keywordRest);
}
/// Consumes an expression.
///
/// If [until] is passed, it's called each time the expression could end and
/// still be a valid expression. When it returns `true`, this returns the
/// expression.
Expression _expression({bool until()}) {
if (until != null && until()) scanner.error("Expected expression.");
@ -1001,10 +1097,11 @@ abstract class StylesheetParser extends Parser {
}
}
/// Consumes an expression until it reaches a top-level comma.
Expression _expressionUntilComma() =>
_expression(until: () => scanner.peekChar() == $comma);
// non-list expression
/// Consumes an expression that doesn't contain any top-level whitespace.
Expression _singleExpression() {
var first = scanner.peekChar();
switch (first) {
@ -1113,6 +1210,7 @@ abstract class StylesheetParser extends Parser {
}
}
/// Consumes a bracketed list.
ListExpression _bracketedList() {
var start = scanner.state;
scanner.expectChar($lbracket);
@ -1129,6 +1227,7 @@ abstract class StylesheetParser extends Parser {
brackets: true, span: scanner.spanFrom(start));
}
/// Parse a parenthesized expression.
Expression _parentheses() {
var start = scanner.state;
scanner.expectChar($lparen);
@ -1991,6 +2090,10 @@ abstract class StylesheetParser extends Parser {
bool get indented;
/// The indentation level at the current scanner position.
///
/// This value isn't used directly by [StylesheetParser]; it's just passed to
/// [scanElse].
int get currentIndentation;
bool atEndOfStatement();