OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of csslib.parser; | 5 part of csslib.parser; |
6 | 6 |
7 | |
8 // TODO(terry): Add optimizing phase to remove duplicated selectors in the same | 7 // TODO(terry): Add optimizing phase to remove duplicated selectors in the same |
9 // selector group (e.g., .btn, .btn { color: red; }). Also, look | 8 // selector group (e.g., .btn, .btn { color: red; }). Also, look |
10 // at simplifying selectors expressions too (much harder). | 9 // at simplifying selectors expressions too (much harder). |
11 // TODO(terry): Detect invalid directive usage. All @imports must occur before | 10 // TODO(terry): Detect invalid directive usage. All @imports must occur before |
12 // all rules other than @charset directive. Any @import directive | 11 // all rules other than @charset directive. Any @import directive |
13 // after any non @charset or @import directive are ignored. e.g., | 12 // after any non @charset or @import directive are ignored. e.g., |
14 // @import "a.css"; | 13 // @import "a.css"; |
15 // div { color: red; } | 14 // div { color: red; } |
16 // @import "b.css"; | 15 // @import "b.css"; |
17 // becomes: | 16 // becomes: |
18 // @import "a.css"; | 17 // @import "a.css"; |
19 // div { color: red; } | 18 // div { color: red; } |
20 // <http://www.w3.org/TR/css3-syntax/#at-rules> | 19 // <http://www.w3.org/TR/css3-syntax/#at-rules> |
21 | 20 |
22 /** | 21 /** |
23 * Analysis phase will validate/fixup any new CSS feature or any SASS style | 22 * Analysis phase will validate/fixup any new CSS feature or any SASS style |
24 * feature. | 23 * feature. |
25 */ | 24 */ |
26 class Analyzer { | 25 class Analyzer { |
27 final List<StyleSheet> _styleSheets; | 26 final List<StyleSheet> _styleSheets; |
28 final Messages _messages; | 27 final Messages _messages; |
29 | 28 |
30 Analyzer(this._styleSheets, this._messages); | 29 Analyzer(this._styleSheets, this._messages); |
31 | 30 |
32 // TODO(terry): Currently each feature walks the AST each time. Once we have | 31 // TODO(terry): Currently each feature walks the AST each time. Once we have |
33 // our complete feature set consider benchmarking the cost and | 32 // our complete feature set consider benchmarking the cost and |
34 // possibly combine in one walk. | 33 // possibly combine in one walk. |
35 void run() { | 34 void run() { |
36 // Expand top-level @include. | 35 // Expand top-level @include. |
37 _styleSheets.forEach((styleSheet) => | 36 _styleSheets.forEach( |
38 TopLevelIncludes.expand(_messages, _styleSheets)); | 37 (styleSheet) => TopLevelIncludes.expand(_messages, _styleSheets)); |
39 | 38 |
40 // Expand @include in declarations. | 39 // Expand @include in declarations. |
41 _styleSheets.forEach((styleSheet) => | 40 _styleSheets.forEach( |
42 DeclarationIncludes.expand(_messages, _styleSheets)); | 41 (styleSheet) => DeclarationIncludes.expand(_messages, _styleSheets)); |
43 | 42 |
44 // Remove all @mixin and @include | 43 // Remove all @mixin and @include |
45 _styleSheets.forEach((styleSheet) => MixinsAndIncludes.remove(styleSheet)); | 44 _styleSheets.forEach((styleSheet) => MixinsAndIncludes.remove(styleSheet)); |
46 | 45 |
47 // Expand any nested selectors using selector desendant combinator to | 46 // Expand any nested selectors using selector desendant combinator to |
48 // signal CSS inheritance notation. | 47 // signal CSS inheritance notation. |
49 _styleSheets.forEach((styleSheet) => new ExpandNestedSelectors() | 48 _styleSheets.forEach((styleSheet) => new ExpandNestedSelectors() |
50 ..visitStyleSheet(styleSheet) | 49 ..visitStyleSheet(styleSheet) |
51 ..flatten(styleSheet)); | 50 ..flatten(styleSheet)); |
52 | 51 |
53 // Expand any @extend. | 52 // Expand any @extend. |
54 _styleSheets.forEach((styleSheet) { | 53 _styleSheets.forEach((styleSheet) { |
55 var allExtends = new AllExtends()..visitStyleSheet(styleSheet); | 54 var allExtends = new AllExtends()..visitStyleSheet(styleSheet); |
56 new InheritExtends(_messages, allExtends)..visitStyleSheet(styleSheet); | 55 new InheritExtends(_messages, allExtends)..visitStyleSheet(styleSheet); |
57 }); | 56 }); |
58 } | 57 } |
59 } | 58 } |
60 | 59 |
61 /** | 60 /** |
62 * Traverse all rulesets looking for nested ones. If a ruleset is in a | 61 * Traverse all rulesets looking for nested ones. If a ruleset is in a |
63 * declaration group (implies nested selector) then generate new ruleset(s) at | 62 * declaration group (implies nested selector) then generate new ruleset(s) at |
64 * level 0 of CSS using selector inheritance syntax (flattens the nesting). | 63 * level 0 of CSS using selector inheritance syntax (flattens the nesting). |
65 * | 64 * |
66 * How the AST works for a rule [RuleSet] and nested rules. First of all a | 65 * How the AST works for a rule [RuleSet] and nested rules. First of all a |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
208 _nestedSelectorGroup = _mergeToFlatten(node); | 207 _nestedSelectorGroup = _mergeToFlatten(node); |
209 } | 208 } |
210 | 209 |
211 _parentRuleSet = node; | 210 _parentRuleSet = node; |
212 | 211 |
213 super.visitRuleSet(node); | 212 super.visitRuleSet(node); |
214 | 213 |
215 _parentRuleSet = oldParent; | 214 _parentRuleSet = oldParent; |
216 | 215 |
217 // Remove nested rules; they're all flatten and in the _expandedRuleSets. | 216 // Remove nested rules; they're all flatten and in the _expandedRuleSets. |
218 node.declarationGroup.declarations.removeWhere((declaration) => | 217 node.declarationGroup.declarations |
219 declaration is RuleSet); | 218 .removeWhere((declaration) => declaration is RuleSet); |
220 | 219 |
221 _nestedSelectorGroup = oldNestedSelectorGroups; | 220 _nestedSelectorGroup = oldNestedSelectorGroups; |
222 | 221 |
223 // If any expandedRuleSets and we're back at the top-level rule set then | 222 // If any expandedRuleSets and we're back at the top-level rule set then |
224 // there were nested rule set(s). | 223 // there were nested rule set(s). |
225 if (_parentRuleSet == null) { | 224 if (_parentRuleSet == null) { |
226 if (!_expandedRuleSets.isEmpty) { | 225 if (!_expandedRuleSets.isEmpty) { |
227 // Remember ruleset to replace with these flattened rulesets. | 226 // Remember ruleset to replace with these flattened rulesets. |
228 _expansions[node] = _expandedRuleSets; | 227 _expansions[node] = _expandedRuleSets; |
229 _expandedRuleSets = []; | 228 _expandedRuleSets = []; |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
298 * descendant. Used for nested selectors when the parent selector needs to | 297 * descendant. Used for nested selectors when the parent selector needs to |
299 * be prefixed to a nested selector or to substitute the this (&) with the | 298 * be prefixed to a nested selector or to substitute the this (&) with the |
300 * parent selector. | 299 * parent selector. |
301 */ | 300 */ |
302 List<SimpleSelectorSequence> _convertToDescendentSequence( | 301 List<SimpleSelectorSequence> _convertToDescendentSequence( |
303 List<SimpleSelectorSequence> sequences) { | 302 List<SimpleSelectorSequence> sequences) { |
304 if (sequences.isEmpty) return sequences; | 303 if (sequences.isEmpty) return sequences; |
305 | 304 |
306 var newSequences = []; | 305 var newSequences = []; |
307 var first = sequences.first; | 306 var first = sequences.first; |
308 newSequences.add(new SimpleSelectorSequence(first.simpleSelector, | 307 newSequences.add(new SimpleSelectorSequence( |
309 first.span, TokenKind.COMBINATOR_DESCENDANT)); | 308 first.simpleSelector, first.span, TokenKind.COMBINATOR_DESCENDANT)); |
310 newSequences.addAll(sequences.skip(1)); | 309 newSequences.addAll(sequences.skip(1)); |
311 | 310 |
312 return newSequences; | 311 return newSequences; |
313 } | 312 } |
314 | 313 |
315 void visitDeclarationGroup(DeclarationGroup node) { | 314 void visitDeclarationGroup(DeclarationGroup node) { |
316 var span = node.span; | 315 var span = node.span; |
317 | 316 |
318 var currentGroup = new DeclarationGroup([], span); | 317 var currentGroup = new DeclarationGroup([], span); |
319 | 318 |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
400 | 399 |
401 class _MediaRulesReplacer extends Visitor { | 400 class _MediaRulesReplacer extends Visitor { |
402 RuleSet _ruleSet; | 401 RuleSet _ruleSet; |
403 List<RuleSet> _newRules; | 402 List<RuleSet> _newRules; |
404 bool _foundAndReplaced = false; | 403 bool _foundAndReplaced = false; |
405 | 404 |
406 /** | 405 /** |
407 * Look for the [ruleSet] inside of an @media directive; if found then replace | 406 * Look for the [ruleSet] inside of an @media directive; if found then replace |
408 * with the [newRules]. If [ruleSet] is found and replaced return true. | 407 * with the [newRules]. If [ruleSet] is found and replaced return true. |
409 */ | 408 */ |
410 static bool replace(StyleSheet styleSheet, RuleSet ruleSet, | 409 static bool replace( |
411 List<RuleSet>newRules) { | 410 StyleSheet styleSheet, RuleSet ruleSet, List<RuleSet> newRules) { |
412 var visitor = new _MediaRulesReplacer(ruleSet, newRules); | 411 var visitor = new _MediaRulesReplacer(ruleSet, newRules); |
413 visitor.visitStyleSheet(styleSheet); | 412 visitor.visitStyleSheet(styleSheet); |
414 return visitor._foundAndReplaced; | 413 return visitor._foundAndReplaced; |
415 } | 414 } |
416 | 415 |
417 _MediaRulesReplacer(this._ruleSet, this._newRules); | 416 _MediaRulesReplacer(this._ruleSet, this._newRules); |
418 | 417 |
419 visitMediaDirective(MediaDirective node) { | 418 visitMediaDirective(MediaDirective node) { |
420 var index = node.rulesets.indexOf(_ruleSet); | 419 var index = node.rulesets.indexOf(_ruleSet); |
421 if (index != -1) { | 420 if (index != -1) { |
(...skipping 30 matching lines...) Expand all Loading... |
452 void visitStyleSheet(StyleSheet ss) { | 451 void visitStyleSheet(StyleSheet ss) { |
453 _styleSheet = ss; | 452 _styleSheet = ss; |
454 super.visitStyleSheet(ss); | 453 super.visitStyleSheet(ss); |
455 _styleSheet = null; | 454 _styleSheet = null; |
456 } | 455 } |
457 | 456 |
458 void visitIncludeDirective(IncludeDirective node) { | 457 void visitIncludeDirective(IncludeDirective node) { |
459 if (map.containsKey(node.name)) { | 458 if (map.containsKey(node.name)) { |
460 var mixinDef = map[node.name]; | 459 var mixinDef = map[node.name]; |
461 if (mixinDef is MixinRulesetDirective) { | 460 if (mixinDef is MixinRulesetDirective) { |
462 _TopLevelIncludeReplacer.replace(_messages, _styleSheet, node, | 461 _TopLevelIncludeReplacer.replace( |
463 mixinDef.rulesets); | 462 _messages, _styleSheet, node, mixinDef.rulesets); |
464 } else if (currDef is MixinRulesetDirective && _anyRulesets(currDef)) { | 463 } else if (currDef is MixinRulesetDirective && _anyRulesets(currDef)) { |
465 // currDef is MixinRulesetDirective | 464 // currDef is MixinRulesetDirective |
466 MixinRulesetDirective mixinRuleset = currDef; | 465 MixinRulesetDirective mixinRuleset = currDef; |
467 int index = mixinRuleset.rulesets.indexOf(node as dynamic); | 466 int index = mixinRuleset.rulesets.indexOf(node as dynamic); |
468 mixinRuleset.rulesets.replaceRange(index, index + 1, [new NoOp()]); | 467 mixinRuleset.rulesets.replaceRange(index, index + 1, [new NoOp()]); |
469 _messages.warning( | 468 _messages.warning( |
470 'Using declaration mixin ${node.name} as top-level mixin', | 469 'Using declaration mixin ${node.name} as top-level mixin', |
471 node.span); | 470 node.span); |
472 } | 471 } |
473 } else { | 472 } else { |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
512 final Messages _messages; | 511 final Messages _messages; |
513 final IncludeDirective _include; | 512 final IncludeDirective _include; |
514 final List<RuleSet> _newRules; | 513 final List<RuleSet> _newRules; |
515 bool _foundAndReplaced = false; | 514 bool _foundAndReplaced = false; |
516 | 515 |
517 /** | 516 /** |
518 * Look for the [ruleSet] inside of an @media directive; if found then replace | 517 * Look for the [ruleSet] inside of an @media directive; if found then replace |
519 * with the [newRules]. If [ruleSet] is found and replaced return true. | 518 * with the [newRules]. If [ruleSet] is found and replaced return true. |
520 */ | 519 */ |
521 static bool replace(Messages messages, StyleSheet styleSheet, | 520 static bool replace(Messages messages, StyleSheet styleSheet, |
522 IncludeDirective include, List<RuleSet>newRules) { | 521 IncludeDirective include, List<RuleSet> newRules) { |
523 var visitor = new _TopLevelIncludeReplacer(messages, include, newRules); | 522 var visitor = new _TopLevelIncludeReplacer(messages, include, newRules); |
524 visitor.visitStyleSheet(styleSheet); | 523 visitor.visitStyleSheet(styleSheet); |
525 return visitor._foundAndReplaced; | 524 return visitor._foundAndReplaced; |
526 } | 525 } |
527 | 526 |
528 _TopLevelIncludeReplacer(this._messages, this._include, this._newRules); | 527 _TopLevelIncludeReplacer(this._messages, this._include, this._newRules); |
529 | 528 |
530 visitStyleSheet(StyleSheet node) { | 529 visitStyleSheet(StyleSheet node) { |
531 var index = node.topLevels.indexOf(_include); | 530 var index = node.topLevels.indexOf(_include); |
532 if (index != -1) { | 531 if (index != -1) { |
(...skipping 15 matching lines...) Expand all Loading... |
548 super.visitMixinRulesetDirective(node); | 547 super.visitMixinRulesetDirective(node); |
549 } | 548 } |
550 } | 549 } |
551 | 550 |
552 /** | 551 /** |
553 * Utility function to match an include to a list of either Declarations or | 552 * Utility function to match an include to a list of either Declarations or |
554 * RuleSets, depending on type of mixin (ruleset or declaration). The include | 553 * RuleSets, depending on type of mixin (ruleset or declaration). The include |
555 * can be an include in a declaration or an include directive (top-level). | 554 * can be an include in a declaration or an include directive (top-level). |
556 */ | 555 */ |
557 int _findInclude(List list, var node) { | 556 int _findInclude(List list, var node) { |
558 IncludeDirective matchNode = (node is IncludeMixinAtDeclaration) ? | 557 IncludeDirective matchNode = |
559 node.include : node; | 558 (node is IncludeMixinAtDeclaration) ? node.include : node; |
560 | 559 |
561 var index = 0; | 560 var index = 0; |
562 for (var item in list) { | 561 for (var item in list) { |
563 var includeNode = (item is IncludeMixinAtDeclaration) ? | 562 var includeNode = (item is IncludeMixinAtDeclaration) ? item.include : item; |
564 item.include : item; | |
565 if (includeNode == matchNode) return index; | 563 if (includeNode == matchNode) return index; |
566 index++; | 564 index++; |
567 } | 565 } |
568 return -1; | 566 return -1; |
569 } | 567 } |
570 | 568 |
571 /** | 569 /** |
572 * Stamp out a mixin with the defined args substituted with the user's | 570 * Stamp out a mixin with the defined args substituted with the user's |
573 * parameters. | 571 * parameters. |
574 */ | 572 */ |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
717 DeclarationIncludes(this._messages, List<StyleSheet> styleSheets) { | 715 DeclarationIncludes(this._messages, List<StyleSheet> styleSheets) { |
718 for (var styleSheet in styleSheets) { | 716 for (var styleSheet in styleSheets) { |
719 visitTree(styleSheet); | 717 visitTree(styleSheet); |
720 } | 718 } |
721 } | 719 } |
722 | 720 |
723 bool _allIncludes(rulesets) => | 721 bool _allIncludes(rulesets) => |
724 rulesets.every((rule) => rule is IncludeDirective || rule is NoOp); | 722 rulesets.every((rule) => rule is IncludeDirective || rule is NoOp); |
725 | 723 |
726 CallMixin _createCallDeclMixin(MixinDefinition mixinDef) { | 724 CallMixin _createCallDeclMixin(MixinDefinition mixinDef) { |
727 callMap.putIfAbsent(mixinDef.name, () => | 725 callMap.putIfAbsent(mixinDef.name, |
728 callMap[mixinDef.name] = new CallMixin(mixinDef, varDefs)); | 726 () => callMap[mixinDef.name] = new CallMixin(mixinDef, varDefs)); |
729 return callMap[mixinDef.name]; | 727 return callMap[mixinDef.name]; |
730 } | 728 } |
731 | 729 |
732 void visitStyleSheet(StyleSheet ss) { | 730 void visitStyleSheet(StyleSheet ss) { |
733 _styleSheet = ss; | 731 _styleSheet = ss; |
734 super.visitStyleSheet(ss); | 732 super.visitStyleSheet(ss); |
735 _styleSheet = null; | 733 _styleSheet = null; |
736 } | 734 } |
737 | 735 |
738 void visitDeclarationGroup(DeclarationGroup node) { | 736 void visitDeclarationGroup(DeclarationGroup node) { |
739 currDeclGroup = node; | 737 currDeclGroup = node; |
740 super.visitDeclarationGroup(node); | 738 super.visitDeclarationGroup(node); |
741 currDeclGroup = null; | 739 currDeclGroup = null; |
742 } | 740 } |
743 | 741 |
744 void visitIncludeMixinAtDeclaration(IncludeMixinAtDeclaration node) { | 742 void visitIncludeMixinAtDeclaration(IncludeMixinAtDeclaration node) { |
745 if (map.containsKey(node.include.name)) { | 743 if (map.containsKey(node.include.name)) { |
746 var mixinDef = map[node.include.name]; | 744 var mixinDef = map[node.include.name]; |
747 | 745 |
748 // Fix up any mixin that is really a Declaration but has includes. | 746 // Fix up any mixin that is really a Declaration but has includes. |
749 if (mixinDef is MixinRulesetDirective) { | 747 if (mixinDef is MixinRulesetDirective) { |
750 if (!_allIncludes(mixinDef.rulesets) && currDeclGroup != null) { | 748 if (!_allIncludes(mixinDef.rulesets) && currDeclGroup != null) { |
751 var index = _findInclude(currDeclGroup.declarations, node); | 749 var index = _findInclude(currDeclGroup.declarations, node); |
752 if (index != -1) { | 750 if (index != -1) { |
753 currDeclGroup.declarations.replaceRange(index, index + 1, | 751 currDeclGroup.declarations.replaceRange( |
754 [new NoOp()]); | 752 index, index + 1, [new NoOp()]); |
755 } | 753 } |
756 _messages.warning( | 754 _messages.warning( |
757 "Using top-level mixin ${node.include.name} as a declaration", | 755 "Using top-level mixin ${node.include.name} as a declaration", |
758 node.span); | 756 node.span); |
759 } else { | 757 } else { |
760 // We're a list of @include(s) inside of a mixin ruleset - convert | 758 // We're a list of @include(s) inside of a mixin ruleset - convert |
761 // to a list of IncludeMixinAtDeclaration(s). | 759 // to a list of IncludeMixinAtDeclaration(s). |
762 var origRulesets = mixinDef.rulesets; | 760 var origRulesets = mixinDef.rulesets; |
763 var rulesets = []; | 761 var rulesets = []; |
764 if (origRulesets.every((ruleset) => ruleset is IncludeDirective)) { | 762 if (origRulesets.every((ruleset) => ruleset is IncludeDirective)) { |
765 origRulesets.forEach((ruleset) { | 763 origRulesets.forEach((ruleset) { |
766 rulesets.add(new IncludeMixinAtDeclaration(ruleset, | 764 rulesets |
767 ruleset.span)); | 765 .add(new IncludeMixinAtDeclaration(ruleset, ruleset.span)); |
768 }); | 766 }); |
769 _IncludeReplacer.replace(_styleSheet, node, rulesets); | 767 _IncludeReplacer.replace(_styleSheet, node, rulesets); |
770 } | 768 } |
771 } | 769 } |
772 } | 770 } |
773 | 771 |
774 if ( mixinDef.definedArgs.length > 0 && node.include.args.length > 0) { | 772 if (mixinDef.definedArgs.length > 0 && node.include.args.length > 0) { |
775 var callMixin = _createCallDeclMixin(mixinDef); | 773 var callMixin = _createCallDeclMixin(mixinDef); |
776 mixinDef = callMixin.transform(node.include.args); | 774 mixinDef = callMixin.transform(node.include.args); |
777 } | 775 } |
778 | 776 |
779 if (mixinDef is MixinDeclarationDirective) { | 777 if (mixinDef is MixinDeclarationDirective) { |
780 _IncludeReplacer.replace(_styleSheet, node, | 778 _IncludeReplacer.replace( |
781 mixinDef.declarations.declarations); | 779 _styleSheet, node, mixinDef.declarations.declarations); |
782 } | 780 } |
783 } else { | 781 } else { |
784 _messages.warning("Undefined mixin ${node.include.name}", node.span); | 782 _messages.warning("Undefined mixin ${node.include.name}", node.span); |
785 } | 783 } |
786 | 784 |
787 super.visitIncludeMixinAtDeclaration(node); | 785 super.visitIncludeMixinAtDeclaration(node); |
788 } | 786 } |
789 | 787 |
790 void visitIncludeDirective(IncludeDirective node) { | 788 void visitIncludeDirective(IncludeDirective node) { |
791 if (map.containsKey(node.name)) { | 789 if (map.containsKey(node.name)) { |
792 var mixinDef = map[node.name]; | 790 var mixinDef = map[node.name]; |
793 if (currDef is MixinDeclarationDirective && | 791 if (currDef is MixinDeclarationDirective && |
794 mixinDef is MixinDeclarationDirective) { | 792 mixinDef is MixinDeclarationDirective) { |
795 _IncludeReplacer.replace(_styleSheet, node, | 793 _IncludeReplacer.replace( |
796 mixinDef.declarations.declarations); | 794 _styleSheet, node, mixinDef.declarations.declarations); |
797 } else if (currDef is MixinDeclarationDirective) { | 795 } else if (currDef is MixinDeclarationDirective) { |
798 var decls = (currDef as MixinDeclarationDirective) | 796 var decls = |
799 .declarations.declarations; | 797 (currDef as MixinDeclarationDirective).declarations.declarations; |
800 var index = _findInclude(decls, node); | 798 var index = _findInclude(decls, node); |
801 if (index != -1) { | 799 if (index != -1) { |
802 decls.replaceRange(index, index + 1, [new NoOp()]); | 800 decls.replaceRange(index, index + 1, [new NoOp()]); |
803 } | 801 } |
804 } | 802 } |
805 } | 803 } |
806 | 804 |
807 super.visitIncludeDirective(node); | 805 super.visitIncludeDirective(node); |
808 } | 806 } |
809 | 807 |
(...skipping 18 matching lines...) Expand all Loading... |
828 } | 826 } |
829 | 827 |
830 void visitVarDefinition(VarDefinition node) { | 828 void visitVarDefinition(VarDefinition node) { |
831 // Only record var definitions that have multiple expressions (comma | 829 // Only record var definitions that have multiple expressions (comma |
832 // separated for mixin parameter substitution. | 830 // separated for mixin parameter substitution. |
833 var exprs = (node.expression as Expressions).expressions; | 831 var exprs = (node.expression as Expressions).expressions; |
834 if (exprs.length > 1) { | 832 if (exprs.length > 1) { |
835 varDefs[node.definedName] = node; | 833 varDefs[node.definedName] = node; |
836 } | 834 } |
837 super.visitVarDefinition(node); | 835 super.visitVarDefinition(node); |
838 } | 836 } |
839 | 837 |
840 void visitVarDefinitionDirective(VarDefinitionDirective node) { | 838 void visitVarDefinitionDirective(VarDefinitionDirective node) { |
841 visitVarDefinition(node.def); | 839 visitVarDefinition(node.def); |
842 } | 840 } |
843 } | 841 } |
844 | 842 |
845 /** @include as a top-level with ruleset(s). */ | 843 /** @include as a top-level with ruleset(s). */ |
846 class _IncludeReplacer extends Visitor { | 844 class _IncludeReplacer extends Visitor { |
847 final _include; | 845 final _include; |
848 final List<Declaration> _newDeclarations; | 846 final List<Declaration> _newDeclarations; |
849 bool _foundAndReplaced = false; | 847 bool _foundAndReplaced = false; |
850 | 848 |
851 /** | 849 /** |
852 * Look for the [ruleSet] inside of a @media directive; if found then replace | 850 * Look for the [ruleSet] inside of a @media directive; if found then replace |
853 * with the [newRules]. | 851 * with the [newRules]. |
854 */ | 852 */ |
855 static void replace(StyleSheet ss, var include, | 853 static void replace( |
856 List<Declaration> newDeclarations) { | 854 StyleSheet ss, var include, List<Declaration> newDeclarations) { |
857 var visitor = new _IncludeReplacer(include, newDeclarations); | 855 var visitor = new _IncludeReplacer(include, newDeclarations); |
858 visitor.visitStyleSheet(ss); | 856 visitor.visitStyleSheet(ss); |
859 } | 857 } |
860 | 858 |
861 _IncludeReplacer(this._include, this._newDeclarations); | 859 _IncludeReplacer(this._include, this._newDeclarations); |
862 | 860 |
863 void visitDeclarationGroup(DeclarationGroup node) { | 861 void visitDeclarationGroup(DeclarationGroup node) { |
864 var index = _findInclude(node.declarations, _include); | 862 var index = _findInclude(node.declarations, _include); |
865 if (index != -1) { | 863 if (index != -1) { |
866 node.declarations.insertAll(index + 1, _newDeclarations); | 864 node.declarations.insertAll(index + 1, _newDeclarations); |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
966 /** | 964 /** |
967 * Changes any selector that matches @extend. | 965 * Changes any selector that matches @extend. |
968 */ | 966 */ |
969 class InheritExtends extends Visitor { | 967 class InheritExtends extends Visitor { |
970 final Messages _messages; | 968 final Messages _messages; |
971 final AllExtends _allExtends; | 969 final AllExtends _allExtends; |
972 | 970 |
973 InheritExtends(this._messages, this._allExtends); | 971 InheritExtends(this._messages, this._allExtends); |
974 | 972 |
975 void visitSelectorGroup(SelectorGroup node) { | 973 void visitSelectorGroup(SelectorGroup node) { |
976 for (var selectorsIndex = 0; selectorsIndex < node.selectors.length; | 974 for (var selectorsIndex = 0; |
| 975 selectorsIndex < node.selectors.length; |
977 selectorsIndex++) { | 976 selectorsIndex++) { |
978 var selectors = node.selectors[selectorsIndex]; | 977 var selectors = node.selectors[selectorsIndex]; |
979 var isLastNone = false; | 978 var isLastNone = false; |
980 var selectorName = ""; | 979 var selectorName = ""; |
981 for (var index = 0; index < selectors.simpleSelectorSequences.length; | 980 for (var index = 0; |
| 981 index < selectors.simpleSelectorSequences.length; |
982 index++) { | 982 index++) { |
983 var simpleSeq = selectors.simpleSelectorSequences[index]; | 983 var simpleSeq = selectors.simpleSelectorSequences[index]; |
984 var namePart = simpleSeq.simpleSelector.toString(); | 984 var namePart = simpleSeq.simpleSelector.toString(); |
985 selectorName = (isLastNone) ? (selectorName + namePart) : namePart; | 985 selectorName = (isLastNone) ? (selectorName + namePart) : namePart; |
986 List<SelectorGroup> matches = _allExtends.inherits[selectorName]; | 986 List<SelectorGroup> matches = _allExtends.inherits[selectorName]; |
987 if (matches != null) { | 987 if (matches != null) { |
988 for (var match in matches) { | 988 for (var match in matches) { |
989 // Create a new group. | 989 // Create a new group. |
990 var newSelectors = selectors.clone(); | 990 var newSelectors = selectors.clone(); |
991 var newSeq = match.selectors[0].clone(); | 991 var newSeq = match.selectors[0].clone(); |
992 if (isLastNone) { | 992 if (isLastNone) { |
993 // Add the inherited selector. | 993 // Add the inherited selector. |
994 node.selectors.add(newSeq); | 994 node.selectors.add(newSeq); |
995 } else { | 995 } else { |
996 // Replace the selector sequence to the left of the pseudo class | 996 // Replace the selector sequence to the left of the pseudo class |
997 // or pseudo element. | 997 // or pseudo element. |
998 | 998 |
999 // Make new selector seq combinator the same as the original. | 999 // Make new selector seq combinator the same as the original. |
1000 var orgCombinator = | 1000 var orgCombinator = |
1001 newSelectors.simpleSelectorSequences[index].combinator; | 1001 newSelectors.simpleSelectorSequences[index].combinator; |
1002 newSeq.simpleSelectorSequences[0].combinator = orgCombinator; | 1002 newSeq.simpleSelectorSequences[0].combinator = orgCombinator; |
1003 | 1003 |
1004 newSelectors.simpleSelectorSequences.replaceRange(index, | 1004 newSelectors.simpleSelectorSequences.replaceRange( |
1005 index + 1, newSeq.simpleSelectorSequences); | 1005 index, index + 1, newSeq.simpleSelectorSequences); |
1006 node.selectors.add(newSelectors); | 1006 node.selectors.add(newSelectors); |
1007 } | 1007 } |
1008 isLastNone = false; | 1008 isLastNone = false; |
1009 } | 1009 } |
1010 } else { | 1010 } else { |
1011 isLastNone = simpleSeq.isCombinatorNone; | 1011 isLastNone = simpleSeq.isCombinatorNone; |
1012 } | 1012 } |
1013 } | 1013 } |
1014 } | 1014 } |
1015 super.visitSelectorGroup(node); | 1015 super.visitSelectorGroup(node); |
1016 } | 1016 } |
1017 } | 1017 } |
OLD | NEW |