sass 3.3.0 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +58 -50
- data/Rakefile +1 -4
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/VERSION_NAME +1 -1
- data/bin/sass +1 -1
- data/bin/scss +1 -1
- data/lib/sass/cache_stores/filesystem.rb +6 -2
- data/lib/sass/css.rb +1 -3
- data/lib/sass/engine.rb +37 -46
- data/lib/sass/environment.rb +13 -17
- data/lib/sass/error.rb +6 -9
- data/lib/sass/exec/base.rb +187 -0
- data/lib/sass/exec/sass_convert.rb +264 -0
- data/lib/sass/exec/sass_scss.rb +424 -0
- data/lib/sass/exec.rb +5 -771
- data/lib/sass/features.rb +7 -0
- data/lib/sass/importers/base.rb +7 -2
- data/lib/sass/importers/filesystem.rb +9 -25
- data/lib/sass/importers.rb +0 -1
- data/lib/sass/media.rb +1 -4
- data/lib/sass/plugin/compiler.rb +200 -83
- data/lib/sass/plugin/staleness_checker.rb +1 -1
- data/lib/sass/plugin.rb +3 -3
- data/lib/sass/script/css_lexer.rb +1 -1
- data/lib/sass/script/functions.rb +622 -268
- data/lib/sass/script/lexer.rb +99 -34
- data/lib/sass/script/parser.rb +24 -23
- data/lib/sass/script/tree/funcall.rb +1 -1
- data/lib/sass/script/tree/interpolation.rb +20 -2
- data/lib/sass/script/tree/selector.rb +26 -0
- data/lib/sass/script/tree/string_interpolation.rb +1 -1
- data/lib/sass/script/tree.rb +1 -0
- data/lib/sass/script/value/base.rb +7 -5
- data/lib/sass/script/value/bool.rb +0 -5
- data/lib/sass/script/value/color.rb +39 -21
- data/lib/sass/script/value/helpers.rb +107 -0
- data/lib/sass/script/value/list.rb +0 -15
- data/lib/sass/script/value/null.rb +0 -5
- data/lib/sass/script/value/number.rb +62 -14
- data/lib/sass/script/value/string.rb +59 -11
- data/lib/sass/script/value.rb +0 -1
- data/lib/sass/scss/css_parser.rb +8 -2
- data/lib/sass/scss/parser.rb +190 -328
- data/lib/sass/scss/rx.rb +15 -6
- data/lib/sass/scss/static_parser.rb +298 -1
- data/lib/sass/selector/abstract_sequence.rb +28 -13
- data/lib/sass/selector/comma_sequence.rb +92 -13
- data/lib/sass/selector/pseudo.rb +256 -0
- data/lib/sass/selector/sequence.rb +94 -24
- data/lib/sass/selector/simple.rb +14 -25
- data/lib/sass/selector/simple_sequence.rb +97 -33
- data/lib/sass/selector.rb +57 -194
- data/lib/sass/shared.rb +1 -1
- data/lib/sass/source/map.rb +26 -12
- data/lib/sass/stack.rb +0 -6
- data/lib/sass/supports.rb +2 -3
- data/lib/sass/tree/at_root_node.rb +1 -0
- data/lib/sass/tree/charset_node.rb +1 -1
- data/lib/sass/tree/directive_node.rb +8 -2
- data/lib/sass/tree/error_node.rb +18 -0
- data/lib/sass/tree/extend_node.rb +1 -1
- data/lib/sass/tree/function_node.rb +4 -0
- data/lib/sass/tree/keyframe_rule_node.rb +15 -0
- data/lib/sass/tree/prop_node.rb +1 -1
- data/lib/sass/tree/rule_node.rb +12 -7
- data/lib/sass/tree/visitors/check_nesting.rb +38 -10
- data/lib/sass/tree/visitors/convert.rb +16 -18
- data/lib/sass/tree/visitors/cssize.rb +29 -29
- data/lib/sass/tree/visitors/deep_copy.rb +5 -0
- data/lib/sass/tree/visitors/perform.rb +45 -33
- data/lib/sass/tree/visitors/set_options.rb +14 -0
- data/lib/sass/tree/visitors/to_css.rb +15 -14
- data/lib/sass/util/subset_map.rb +1 -1
- data/lib/sass/util.rb +222 -99
- data/lib/sass/version.rb +5 -5
- data/lib/sass.rb +0 -5
- data/test/sass/cache_test.rb +62 -20
- data/test/sass/callbacks_test.rb +1 -1
- data/test/sass/compiler_test.rb +19 -10
- data/test/sass/conversion_test.rb +58 -1
- data/test/sass/css2sass_test.rb +23 -4
- data/test/sass/encoding_test.rb +219 -0
- data/test/sass/engine_test.rb +136 -199
- data/test/sass/exec_test.rb +2 -2
- data/test/sass/extend_test.rb +236 -19
- data/test/sass/functions_test.rb +295 -253
- data/test/sass/importer_test.rb +31 -21
- data/test/sass/logger_test.rb +1 -1
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/plugin_test.rb +14 -13
- data/test/sass/results/compact.css +1 -1
- data/test/sass/results/complex.css +4 -4
- data/test/sass/results/expanded.css +1 -1
- data/test/sass/results/import.css +1 -1
- data/test/sass/results/import_charset_ibm866.css +2 -2
- data/test/sass/results/mixins.css +17 -17
- data/test/sass/results/nested.css +1 -1
- data/test/sass/results/parent_ref.css +2 -2
- data/test/sass/results/script.css +3 -3
- data/test/sass/results/scss_import.css +1 -1
- data/test/sass/script_conversion_test.rb +10 -7
- data/test/sass/script_test.rb +288 -74
- data/test/sass/scss/css_test.rb +141 -24
- data/test/sass/scss/rx_test.rb +4 -4
- data/test/sass/scss/scss_test.rb +457 -18
- data/test/sass/source_map_test.rb +115 -25
- data/test/sass/superselector_test.rb +191 -0
- data/test/sass/templates/scss_import.scss +2 -1
- data/test/sass/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
- data/test/sass/util/normalized_map_test.rb +1 -1
- data/test/sass/util/subset_map_test.rb +2 -2
- data/test/sass/util_test.rb +31 -1
- data/test/sass/value_helpers_test.rb +5 -7
- data/test/test_helper.rb +2 -2
- data/vendor/listen/CHANGELOG.md +1 -228
- data/vendor/listen/Gemfile +5 -15
- data/vendor/listen/README.md +111 -77
- data/vendor/listen/Rakefile +0 -42
- data/vendor/listen/lib/listen/adapter.rb +195 -82
- data/vendor/listen/lib/listen/adapters/bsd.rb +27 -64
- data/vendor/listen/lib/listen/adapters/darwin.rb +21 -58
- data/vendor/listen/lib/listen/adapters/linux.rb +23 -55
- data/vendor/listen/lib/listen/adapters/polling.rb +25 -34
- data/vendor/listen/lib/listen/adapters/windows.rb +50 -46
- data/vendor/listen/lib/listen/directory_record.rb +96 -61
- data/vendor/listen/lib/listen/listener.rb +135 -37
- data/vendor/listen/lib/listen/turnstile.rb +9 -5
- data/vendor/listen/lib/listen/version.rb +1 -1
- data/vendor/listen/lib/listen.rb +33 -19
- data/vendor/listen/listen.gemspec +6 -0
- data/vendor/listen/spec/listen/adapter_spec.rb +43 -77
- data/vendor/listen/spec/listen/adapters/polling_spec.rb +8 -8
- data/vendor/listen/spec/listen/directory_record_spec.rb +81 -56
- data/vendor/listen/spec/listen/listener_spec.rb +128 -39
- data/vendor/listen/spec/listen_spec.rb +15 -21
- data/vendor/listen/spec/spec_helper.rb +4 -0
- data/vendor/listen/spec/support/adapter_helper.rb +52 -15
- data/vendor/listen/spec/support/directory_record_helper.rb +7 -5
- data/vendor/listen/spec/support/listeners_helper.rb +30 -7
- metadata +25 -22
- data/ext/mkrf_conf.rb +0 -27
- data/lib/sass/importers/deprecated_path.rb +0 -51
- data/lib/sass/script/value/deprecated_false.rb +0 -55
- data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
- data/vendor/listen/lib/listen/multi_listener.rb +0 -143
- data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
- data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
data/lib/sass/scss/parser.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
require 'set'
|
2
3
|
|
3
4
|
module Sass
|
@@ -39,7 +40,7 @@ module Sass
|
|
39
40
|
def parse
|
40
41
|
init_scanner!
|
41
42
|
root = stylesheet
|
42
|
-
expected("selector or at-rule") unless @scanner.eos?
|
43
|
+
expected("selector or at-rule") unless root && @scanner.eos?
|
43
44
|
root
|
44
45
|
end
|
45
46
|
|
@@ -62,7 +63,7 @@ module Sass
|
|
62
63
|
def parse_media_query_list
|
63
64
|
init_scanner!
|
64
65
|
ql = media_query_list
|
65
|
-
expected("media query list") unless @scanner.eos?
|
66
|
+
expected("media query list") unless ql && @scanner.eos?
|
66
67
|
ql
|
67
68
|
end
|
68
69
|
|
@@ -74,7 +75,7 @@ module Sass
|
|
74
75
|
def parse_at_root_query
|
75
76
|
init_scanner!
|
76
77
|
query = at_root_query
|
77
|
-
expected("@at-root query list") unless @scanner.eos?
|
78
|
+
expected("@at-root query list") unless query && @scanner.eos?
|
78
79
|
query
|
79
80
|
end
|
80
81
|
|
@@ -86,7 +87,7 @@ module Sass
|
|
86
87
|
def parse_supports_condition
|
87
88
|
init_scanner!
|
88
89
|
condition = supports_condition
|
89
|
-
expected("supports condition") unless @scanner.eos?
|
90
|
+
expected("supports condition") unless condition && @scanner.eos?
|
90
91
|
condition
|
91
92
|
end
|
92
93
|
|
@@ -155,10 +156,15 @@ module Sass
|
|
155
156
|
else
|
156
157
|
value = Sass::Engine.parse_interp(
|
157
158
|
text, line, @scanner.pos - text.size, :filename => @filename)
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
159
|
+
string_before_comment = @scanner.string[0...@scanner.pos - text.length]
|
160
|
+
newline_before_comment = string_before_comment.rindex("\n")
|
161
|
+
last_line_before_comment =
|
162
|
+
if newline_before_comment
|
163
|
+
string_before_comment[newline_before_comment + 1..-1]
|
164
|
+
else
|
165
|
+
string_before_comment
|
166
|
+
end
|
167
|
+
value.unshift(last_line_before_comment.gsub(/[^\s]/, ' '))
|
162
168
|
end
|
163
169
|
|
164
170
|
type = if silent
|
@@ -175,7 +181,7 @@ module Sass
|
|
175
181
|
|
176
182
|
DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for,
|
177
183
|
:each, :while, :if, :else, :extend, :import, :media, :charset, :content,
|
178
|
-
:_moz_document, :at_root]
|
184
|
+
:_moz_document, :at_root, :error]
|
179
185
|
|
180
186
|
PREFIXED_DIRECTIVES = Set[:supports]
|
181
187
|
|
@@ -191,10 +197,7 @@ module Sass
|
|
191
197
|
return dir
|
192
198
|
end
|
193
199
|
|
194
|
-
|
195
|
-
# but some (e.g. @page) take selector-like arguments.
|
196
|
-
# Some take no arguments at all.
|
197
|
-
val = expr || selector
|
200
|
+
val = almost_any_value
|
198
201
|
val = val ? ["@#{name} "] + Sass::Util.strip_string_array(val) : ["@#{name}"]
|
199
202
|
directive_body(val, start_pos)
|
200
203
|
end
|
@@ -217,7 +220,7 @@ module Sass
|
|
217
220
|
end
|
218
221
|
|
219
222
|
def prefixed_directive(name, start_pos)
|
220
|
-
sym = name
|
223
|
+
sym = deprefix(name).gsub('-', '_').to_sym
|
221
224
|
PREFIXED_DIRECTIVES.include?(sym) && send("#{sym}_directive", name, start_pos)
|
222
225
|
end
|
223
226
|
|
@@ -350,10 +353,12 @@ module Sass
|
|
350
353
|
end
|
351
354
|
|
352
355
|
def extend_directive(start_pos)
|
353
|
-
|
356
|
+
selector_start_pos = source_position
|
357
|
+
@expected = "selector"
|
358
|
+
selector = Sass::Util.strip_string_array(expr!(:almost_any_value))
|
354
359
|
optional = tok(OPTIONAL)
|
355
360
|
ss
|
356
|
-
node(Sass::Tree::ExtendNode.new(selector, !!optional,
|
361
|
+
node(Sass::Tree::ExtendNode.new(selector, !!optional, range(selector_start_pos)), start_pos)
|
357
362
|
end
|
358
363
|
|
359
364
|
def import_directive(start_pos)
|
@@ -371,7 +376,7 @@ module Sass
|
|
371
376
|
|
372
377
|
def import_arg
|
373
378
|
start_pos = source_position
|
374
|
-
return unless (str =
|
379
|
+
return unless (str = string) || (uri = tok?(/url\(/i))
|
375
380
|
if uri
|
376
381
|
str = sass_script(:parse_string)
|
377
382
|
ss
|
@@ -379,16 +384,15 @@ module Sass
|
|
379
384
|
ss
|
380
385
|
return node(Tree::CssImportNode.new(str, media.to_a), start_pos)
|
381
386
|
end
|
382
|
-
|
383
|
-
path = @scanner[1] || @scanner[2]
|
384
387
|
ss
|
385
388
|
|
386
389
|
media = media_query_list
|
387
|
-
if
|
388
|
-
return node(Sass::Tree::CssImportNode.new(
|
390
|
+
if str =~ %r{^(https?:)?//} || media || use_css_import?
|
391
|
+
return node(Sass::Tree::CssImportNode.new(
|
392
|
+
Sass::Script::Value::String.quote(str), media.to_a), start_pos)
|
389
393
|
end
|
390
394
|
|
391
|
-
node(Sass::Tree::ImportNode.new(
|
395
|
+
node(Sass::Tree::ImportNode.new(str.strip), start_pos)
|
392
396
|
end
|
393
397
|
|
394
398
|
def use_css_import?; false; end
|
@@ -471,8 +475,7 @@ module Sass
|
|
471
475
|
alias_method :at_root_query, :query_expr
|
472
476
|
|
473
477
|
def charset_directive(start_pos)
|
474
|
-
|
475
|
-
name = @scanner[1] || @scanner[2]
|
478
|
+
name = expr!(:string)
|
476
479
|
ss
|
477
480
|
node(Sass::Tree::CharsetNode.new(name), start_pos)
|
478
481
|
end
|
@@ -529,6 +532,10 @@ module Sass
|
|
529
532
|
arr
|
530
533
|
end
|
531
534
|
|
535
|
+
def error_directive(start_pos)
|
536
|
+
node(Sass::Tree::ErrorNode.new(sass_script(:parse)), start_pos)
|
537
|
+
end
|
538
|
+
|
532
539
|
# http://www.w3.org/TR/css3-conditional/
|
533
540
|
def supports_directive(name, start_pos)
|
534
541
|
condition = expr!(:supports_condition)
|
@@ -624,10 +631,9 @@ module Sass
|
|
624
631
|
|
625
632
|
def ruleset
|
626
633
|
start_pos = source_position
|
627
|
-
rules
|
628
|
-
return unless rules
|
634
|
+
return unless (rules = almost_any_value)
|
629
635
|
block(node(
|
630
|
-
Sass::Tree::RuleNode.new(rules
|
636
|
+
Sass::Tree::RuleNode.new(rules, range(start_pos)), start_pos), :ruleset)
|
631
637
|
end
|
632
638
|
|
633
639
|
def block(node, context)
|
@@ -661,315 +667,171 @@ module Sass
|
|
661
667
|
child_or_array.has_children
|
662
668
|
end
|
663
669
|
|
664
|
-
#
|
665
|
-
#
|
666
|
-
#
|
667
|
-
#
|
668
|
-
#
|
669
|
-
#
|
670
|
+
# When parsing the contents of a ruleset, it can be difficult to tell
|
671
|
+
# declarations apart from nested rulesets. Since we don't thoroughly parse
|
672
|
+
# selectors until after resolving interpolation, we can share a bunch of
|
673
|
+
# the parsing of the two, but we need to disambiguate them first. We use
|
674
|
+
# the following criteria:
|
675
|
+
#
|
676
|
+
# * If the entity doesn't start with an identifier followed by a colon,
|
677
|
+
# it's a selector. There are some additional mostly-unimportant cases
|
678
|
+
# here to support various declaration hacks.
|
679
|
+
#
|
680
|
+
# * If the colon is followed by another colon, it's a selector.
|
670
681
|
#
|
671
|
-
#
|
672
|
-
#
|
673
|
-
#
|
682
|
+
# * Otherwise, if the colon is followed by anything other than
|
683
|
+
# interpolation or a character that's valid as the beginning of an
|
684
|
+
# identifier, it's a declaration.
|
674
685
|
#
|
675
|
-
#
|
676
|
-
#
|
677
|
-
#
|
678
|
-
#
|
686
|
+
# * If the colon is followed by interpolation or a valid identifier, try
|
687
|
+
# parsing it as a declaration value. If this fails, backtrack and parse
|
688
|
+
# it as a selector.
|
689
|
+
#
|
690
|
+
# * If the declaration value value valid but is followed by "{", backtrack
|
691
|
+
# and parse it as a selector anyway. This ensures that ".foo:bar {" is
|
692
|
+
# always parsed as a selector and never as a property with nested
|
693
|
+
# properties beneath it.
|
679
694
|
def declaration_or_ruleset
|
680
|
-
old_use_property_exception, @use_property_exception =
|
681
|
-
@use_property_exception, false
|
682
|
-
decl_err = catch_error do
|
683
|
-
decl = declaration
|
684
|
-
unless decl && decl.has_children
|
685
|
-
# We want an exception if it's not there,
|
686
|
-
# but we don't want to consume if it is
|
687
|
-
tok!(/[;}]/) unless tok?(/[;}]/)
|
688
|
-
end
|
689
|
-
return decl
|
690
|
-
end
|
691
|
-
|
692
|
-
ruleset_err = catch_error {return ruleset}
|
693
|
-
rethrow(@use_property_exception ? decl_err : ruleset_err)
|
694
|
-
ensure
|
695
|
-
@use_property_exception = old_use_property_exception
|
696
|
-
end
|
697
|
-
|
698
|
-
def selector_sequence
|
699
695
|
start_pos = source_position
|
700
|
-
|
701
|
-
return [sel], range(start_pos)
|
702
|
-
end
|
703
|
-
|
704
|
-
rules = []
|
705
|
-
v = selector
|
706
|
-
return unless v
|
707
|
-
rules.concat v
|
708
|
-
|
709
|
-
ws = ''
|
710
|
-
while tok(/,/)
|
711
|
-
ws << str {ss}
|
712
|
-
if (v = selector)
|
713
|
-
rules << ',' << ws
|
714
|
-
rules.concat v
|
715
|
-
ws = ''
|
716
|
-
end
|
717
|
-
end
|
718
|
-
return rules, range(start_pos)
|
719
|
-
end
|
696
|
+
declaration = try_declaration
|
720
697
|
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
sel = _selector
|
729
|
-
return unless sel
|
730
|
-
selectors = [sel]
|
731
|
-
ws = ''
|
732
|
-
while tok(/,/)
|
733
|
-
ws << str {ss}
|
734
|
-
if (sel = _selector)
|
735
|
-
selectors << sel
|
736
|
-
if ws.include?("\n")
|
737
|
-
selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members)
|
738
|
-
end
|
739
|
-
ws = ''
|
740
|
-
end
|
698
|
+
if declaration.nil?
|
699
|
+
return unless (selector = almost_any_value)
|
700
|
+
elsif declaration.is_a?(Array)
|
701
|
+
selector = declaration
|
702
|
+
else
|
703
|
+
# Declaration should be a PropNode.
|
704
|
+
return declaration
|
741
705
|
end
|
742
|
-
Selector::CommaSequence.new(selectors)
|
743
|
-
end
|
744
|
-
|
745
|
-
def _selector
|
746
|
-
# The combinator here allows the "> E" hack
|
747
|
-
val = combinator || simple_selector_sequence
|
748
|
-
return unless val
|
749
|
-
nl = str {ss}.include?("\n")
|
750
|
-
res = []
|
751
|
-
res << val
|
752
|
-
res << "\n" if nl
|
753
706
|
|
754
|
-
|
755
|
-
|
756
|
-
res << "\n" if str {ss}.include?("\n")
|
707
|
+
if (additional_selector = almost_any_value)
|
708
|
+
selector << additional_selector
|
757
709
|
end
|
758
|
-
Selector::Sequence.new(res.compact)
|
759
|
-
end
|
760
|
-
|
761
|
-
def combinator
|
762
|
-
tok(PLUS) || tok(GREATER) || tok(TILDE) || reference_combinator
|
763
|
-
end
|
764
710
|
|
765
|
-
|
766
|
-
|
767
|
-
res = ['/']
|
768
|
-
ns, name = expr!(:qualified_name)
|
769
|
-
res << ns << '|' if ns
|
770
|
-
res << name << tok!(/\//)
|
771
|
-
res = res.flatten
|
772
|
-
res = res.join '' if res.all? {|e| e.is_a?(String)}
|
773
|
-
res
|
711
|
+
block(node(
|
712
|
+
Sass::Tree::RuleNode.new(merge(selector), range(start_pos)), start_pos), :ruleset)
|
774
713
|
end
|
775
714
|
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
#
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
715
|
+
# Tries to parse a declaration, and returns the value parsed so far if it
|
716
|
+
# fails.
|
717
|
+
#
|
718
|
+
# This has three possible return types. It can return `nil`, indicating
|
719
|
+
# that parsing failed completely and the scanner hasn't moved forward at
|
720
|
+
# all. It can return an Array, indicating that parsing failed after
|
721
|
+
# consuming some text (possibly containing interpolation), which is
|
722
|
+
# returned. Or it can return a PropNode, indicating that parsing
|
723
|
+
# succeeded.
|
724
|
+
def try_declaration
|
725
|
+
# This allows the "*prop: val", ":prop: val", "#prop: val", and ".prop:
|
726
|
+
# val" hacks.
|
727
|
+
name_start_pos = source_position
|
728
|
+
if (s = tok(/[:\*\.]|\#(?!\{)/))
|
729
|
+
name = [s, str {ss}]
|
730
|
+
return name unless (ident = interp_ident)
|
731
|
+
name << ident
|
732
|
+
else
|
733
|
+
return unless (name = interp_ident)
|
734
|
+
name = Array(name)
|
792
735
|
end
|
793
736
|
|
794
|
-
|
795
|
-
|
796
|
-
if (sel = str? {simple_selector_sequence})
|
797
|
-
@scanner.pos = pos
|
798
|
-
@line = line
|
799
|
-
begin
|
800
|
-
# If we see "*E", don't force a throw because this could be the
|
801
|
-
# "*prop: val" hack.
|
802
|
-
expected('"{"') if res.length == 1 && res[0].is_a?(Selector::Universal)
|
803
|
-
throw_error {expected('"{"')}
|
804
|
-
rescue Sass::SyntaxError => e
|
805
|
-
e.message << "\n\n\"#{sel}\" may only be used at the beginning of a compound selector."
|
806
|
-
raise e
|
807
|
-
end
|
737
|
+
if (comment = tok(COMMENT))
|
738
|
+
name << comment
|
808
739
|
end
|
740
|
+
name_end_pos = source_position
|
809
741
|
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
def class_selector
|
819
|
-
return unless tok(/\./)
|
820
|
-
@expected = "class name"
|
821
|
-
Selector::Class.new(merge(expr!(:interp_ident)))
|
822
|
-
end
|
823
|
-
|
824
|
-
def id_selector
|
825
|
-
return unless tok(/#(?!\{)/)
|
826
|
-
@expected = "id name"
|
827
|
-
Selector::Id.new(merge(expr!(:interp_name)))
|
828
|
-
end
|
829
|
-
|
830
|
-
def placeholder_selector
|
831
|
-
return unless tok(/%/)
|
832
|
-
@expected = "placeholder name"
|
833
|
-
Selector::Placeholder.new(merge(expr!(:interp_ident)))
|
834
|
-
end
|
835
|
-
|
836
|
-
def element_name
|
837
|
-
ns, name = Sass::Util.destructure(qualified_name(:allow_star_name))
|
838
|
-
return unless ns || name
|
742
|
+
mid = [str {ss}]
|
743
|
+
return name + mid unless tok(/:/)
|
744
|
+
mid << ':'
|
745
|
+
return name + mid + [':'] if tok(/:/)
|
746
|
+
mid << str {ss}
|
747
|
+
post_colon_whitespace = !mid.last.empty?
|
748
|
+
could_be_selector = !post_colon_whitespace && (tok?(IDENT_START) || tok?(INTERP_START))
|
839
749
|
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
750
|
+
value_start_pos = source_position
|
751
|
+
value = nil
|
752
|
+
error = catch_error do
|
753
|
+
value = value!
|
754
|
+
if tok?(/\{/)
|
755
|
+
# Properties that are ambiguous with selectors can't have additional
|
756
|
+
# properties nested beneath them.
|
757
|
+
tok!(/;/) if could_be_selector
|
758
|
+
elsif !tok?(/[;{}]/)
|
759
|
+
# We want an exception if there's no valid end-of-property character
|
760
|
+
# exists, but we don't want to consume it if it does.
|
761
|
+
tok!(/[;{}]/)
|
762
|
+
end
|
844
763
|
end
|
845
|
-
end
|
846
764
|
|
847
|
-
|
848
|
-
|
849
|
-
return unless name
|
850
|
-
return nil, name unless tok(/\|/)
|
765
|
+
if error
|
766
|
+
rethrow error unless could_be_selector
|
851
767
|
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
768
|
+
# If the value would be followed by a semicolon, it's definitely
|
769
|
+
# supposed to be a property, not a selector.
|
770
|
+
additional_selector = almost_any_value
|
771
|
+
rethrow error if tok?(/;/)
|
856
772
|
|
857
|
-
|
858
|
-
if (script = interpolation)
|
859
|
-
Selector::Interpolation.new(script)
|
773
|
+
return name + mid + (additional_selector || [])
|
860
774
|
end
|
861
|
-
end
|
862
775
|
|
863
|
-
|
864
|
-
return unless tok(/\[/)
|
865
|
-
ss
|
866
|
-
ns, name = attrib_name!
|
776
|
+
value_end_pos = source_position
|
867
777
|
ss
|
778
|
+
require_block = tok?(/\{/)
|
868
779
|
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
tok(SUFFIXMATCH) ||
|
874
|
-
tok(SUBSTRINGMATCH)
|
875
|
-
if op
|
876
|
-
@expected = "identifier or string"
|
877
|
-
ss
|
878
|
-
val = interp_ident || expr!(:interp_string)
|
879
|
-
ss
|
880
|
-
end
|
881
|
-
flags = interp_ident || interp_string
|
882
|
-
tok!(/\]/)
|
883
|
-
|
884
|
-
Selector::Attribute.new(merge(name), merge(ns), op, merge(val), merge(flags))
|
885
|
-
end
|
780
|
+
node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new),
|
781
|
+
name_start_pos, value_end_pos)
|
782
|
+
node.name_source_range = range(name_start_pos, name_end_pos)
|
783
|
+
node.value_source_range = range(value_start_pos, value_end_pos)
|
886
784
|
|
887
|
-
|
888
|
-
|
889
|
-
# E, E|E
|
890
|
-
if tok(/\|(?!=)/)
|
891
|
-
ns = name_or_ns
|
892
|
-
name = interp_ident
|
893
|
-
else
|
894
|
-
name = name_or_ns
|
895
|
-
end
|
896
|
-
else
|
897
|
-
# *|E or |E
|
898
|
-
ns = [tok(/\*/) || ""]
|
899
|
-
tok!(/\|/)
|
900
|
-
name = expr!(:interp_ident)
|
901
|
-
end
|
902
|
-
return ns, name
|
785
|
+
return node unless require_block
|
786
|
+
nested_properties! node
|
903
787
|
end
|
904
788
|
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
end
|
921
|
-
|
922
|
-
def pseudo_arg
|
923
|
-
# In the CSS spec, every pseudo-class/element either takes a pseudo
|
924
|
-
# expression or a selector comma sequence as an argument. However, we
|
925
|
-
# don't want to have to know which takes which, so we handle both at
|
926
|
-
# once.
|
927
|
-
#
|
928
|
-
# However, there are some ambiguities between the two. For instance, "n"
|
929
|
-
# could start a pseudo expression like "n+1", or it could start a
|
930
|
-
# selector like "n|m". In order to handle this, we must regrettably
|
931
|
-
# backtrack.
|
932
|
-
expr, sel = nil, nil
|
933
|
-
pseudo_err = catch_error do
|
934
|
-
expr = pseudo_expr
|
935
|
-
next if tok?(/[,)]/)
|
936
|
-
expr = nil
|
937
|
-
expected '")"'
|
789
|
+
# This production is similar to the CSS [`<any-value>`][any-value]
|
790
|
+
# production, but as the name implies, not quite the same. It's meant to
|
791
|
+
# consume values that could be a selector, an expression, or a combination
|
792
|
+
# of both. It respects strings and comments and supports interpolation. It
|
793
|
+
# will consume up to "{", "}", ";", or "!".
|
794
|
+
#
|
795
|
+
# [any-value]: http://dev.w3.org/csswg/css-variables/#typedef-any-value
|
796
|
+
#
|
797
|
+
# Values consumed by this production will usually be parsed more
|
798
|
+
# thoroughly once interpolation has been resolved.
|
799
|
+
def almost_any_value
|
800
|
+
return unless (tok = almost_any_value_token)
|
801
|
+
sel = [tok]
|
802
|
+
while (tok = almost_any_value_token)
|
803
|
+
sel << tok
|
938
804
|
end
|
939
|
-
|
940
|
-
return expr if expr
|
941
|
-
sel_err = catch_error {sel = selector}
|
942
|
-
return sel if sel
|
943
|
-
rethrow pseudo_err if pseudo_err
|
944
|
-
rethrow sel_err if sel_err
|
945
|
-
nil
|
805
|
+
merge(sel)
|
946
806
|
end
|
947
807
|
|
948
|
-
def
|
949
|
-
tok(
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
808
|
+
def almost_any_value_token
|
809
|
+
tok(%r{
|
810
|
+
(
|
811
|
+
(?!url\()
|
812
|
+
[^"/\#!;\{\}] # "
|
813
|
+
|
|
814
|
+
/(?![/*])
|
815
|
+
|
|
816
|
+
\#(?!\{)
|
817
|
+
|
|
818
|
+
!(?![a-z]) # TODO: never consume "!" when issue 1126 is fixed.
|
819
|
+
)+
|
820
|
+
}xi) || tok(COMMENT) || tok(SINGLE_LINE_COMMENT) || interp_string || interp_uri ||
|
821
|
+
interpolation(:warn_for_color)
|
960
822
|
end
|
961
823
|
|
962
824
|
def declaration
|
963
|
-
# This allows the "*prop: val", ":prop: val",
|
825
|
+
# This allows the "*prop: val", ":prop: val", "#prop: val", and ".prop:
|
826
|
+
# val" hacks.
|
964
827
|
name_start_pos = source_position
|
965
828
|
if (s = tok(/[:\*\.]|\#(?!\{)/))
|
966
|
-
@use_property_exception = s !~ /[\.\#]/
|
967
829
|
name = [s, str {ss}, *expr!(:interp_ident)]
|
968
830
|
else
|
969
|
-
name = interp_ident
|
970
|
-
|
971
|
-
name = [name] if name.is_a?(String)
|
831
|
+
return unless (name = interp_ident)
|
832
|
+
name = Array(name)
|
972
833
|
end
|
834
|
+
|
973
835
|
if (comment = tok(COMMENT))
|
974
836
|
name << comment
|
975
837
|
end
|
@@ -977,7 +839,9 @@ module Sass
|
|
977
839
|
ss
|
978
840
|
|
979
841
|
tok!(/:/)
|
980
|
-
|
842
|
+
ss
|
843
|
+
value_start_pos = source_position
|
844
|
+
value = value!
|
981
845
|
value_end_pos = source_position
|
982
846
|
ss
|
983
847
|
require_block = tok?(/\{/)
|
@@ -988,19 +852,15 @@ module Sass
|
|
988
852
|
node.value_source_range = range(value_start_pos, value_end_pos)
|
989
853
|
|
990
854
|
return node unless require_block
|
991
|
-
nested_properties! node
|
855
|
+
nested_properties! node
|
992
856
|
end
|
993
857
|
|
994
858
|
def value!
|
995
|
-
space = !str {ss}.empty?
|
996
|
-
value_start_pos = source_position
|
997
|
-
@use_property_exception ||= space || !tok?(IDENT)
|
998
|
-
|
999
859
|
if tok?(/\{/)
|
1000
860
|
str = Sass::Script::Tree::Literal.new(Sass::Script::Value::String.new(""))
|
1001
861
|
str.line = source_position.line
|
1002
862
|
str.source_range = range(source_position)
|
1003
|
-
return
|
863
|
+
return str
|
1004
864
|
end
|
1005
865
|
|
1006
866
|
start_pos = source_position
|
@@ -1013,18 +873,12 @@ module Sass
|
|
1013
873
|
str = Sass::Script::Tree::Literal.new(Sass::Script::Value::String.new(val.strip))
|
1014
874
|
str.line = start_pos.line
|
1015
875
|
str.source_range = range(start_pos)
|
1016
|
-
return
|
876
|
+
return str
|
1017
877
|
end
|
1018
|
-
|
878
|
+
sass_script(:parse)
|
1019
879
|
end
|
1020
880
|
|
1021
|
-
def nested_properties!(node
|
1022
|
-
err(<<MESSAGE) unless space
|
1023
|
-
Invalid CSS: a space is required between a property and its definition
|
1024
|
-
when it has other properties nested beneath it.
|
1025
|
-
MESSAGE
|
1026
|
-
|
1027
|
-
@use_property_exception = true
|
881
|
+
def nested_properties!(node)
|
1028
882
|
@expected = 'expression (e.g. 1px, bold) or "{"'
|
1029
883
|
block(node, :property)
|
1030
884
|
end
|
@@ -1078,9 +932,14 @@ MESSAGE
|
|
1078
932
|
var
|
1079
933
|
end
|
1080
934
|
|
1081
|
-
def interpolation
|
935
|
+
def interpolation(warn_for_color = false)
|
1082
936
|
return unless tok(INTERP_START)
|
1083
|
-
sass_script(:parse_interpolated)
|
937
|
+
sass_script(:parse_interpolated, warn_for_color)
|
938
|
+
end
|
939
|
+
|
940
|
+
def string
|
941
|
+
return unless tok(STRING)
|
942
|
+
Sass::Script::Value::String.value(@scanner[1] || @scanner[2])
|
1084
943
|
end
|
1085
944
|
|
1086
945
|
def interp_string
|
@@ -1107,10 +966,10 @@ MESSAGE
|
|
1107
966
|
end
|
1108
967
|
|
1109
968
|
def interp_ident(start = IDENT)
|
1110
|
-
val = tok(start) || interpolation || tok(IDENT_HYPHEN_INTERP, true)
|
969
|
+
val = tok(start) || interpolation(:warn_for_color) || tok(IDENT_HYPHEN_INTERP, true)
|
1111
970
|
return unless val
|
1112
971
|
res = [val]
|
1113
|
-
while (val = tok(NAME) || interpolation)
|
972
|
+
while (val = tok(NAME) || interpolation(:warn_for_color))
|
1114
973
|
res << val
|
1115
974
|
end
|
1116
975
|
res
|
@@ -1123,10 +982,6 @@ MESSAGE
|
|
1123
982
|
return [var] if var
|
1124
983
|
end
|
1125
984
|
|
1126
|
-
def interp_name
|
1127
|
-
interp_ident NAME
|
1128
|
-
end
|
1129
|
-
|
1130
985
|
def str
|
1131
986
|
@strs.push ""
|
1132
987
|
yield
|
@@ -1191,25 +1046,26 @@ MESSAGE
|
|
1191
1046
|
:media_expr => "media expression (e.g. (min-device-width: 800px))",
|
1192
1047
|
:at_root_query => "@at-root query (e.g. (without: media))",
|
1193
1048
|
:at_root_directive_list => '* or identifier',
|
1194
|
-
:
|
1049
|
+
:pseudo_args => "expression (e.g. fr, 2n+1)",
|
1195
1050
|
:interp_ident => "identifier",
|
1196
|
-
:interp_name => "identifier",
|
1197
1051
|
:qualified_name => "identifier",
|
1198
1052
|
:expr => "expression (e.g. 1px, bold)",
|
1199
|
-
:_selector => "selector",
|
1200
1053
|
:selector_comma_sequence => "selector",
|
1201
|
-
:
|
1054
|
+
:string => "string",
|
1202
1055
|
:import_arg => "file to import (string or url())",
|
1203
1056
|
:moz_document_function => "matching function (e.g. url-prefix(), domain())",
|
1204
1057
|
:supports_condition => "@supports condition (e.g. (display: flexbox))",
|
1205
1058
|
:supports_condition_in_parens => "@supports condition (e.g. (display: flexbox))",
|
1059
|
+
:a_n_plus_b => "An+B expression",
|
1060
|
+
:keyframes_selector_component => "from, to, or a percentage",
|
1061
|
+
:keyframes_selector => "keyframes selector (e.g. 10%)"
|
1206
1062
|
}
|
1207
1063
|
|
1208
1064
|
TOK_NAMES = Sass::Util.to_hash(Sass::SCSS::RX.constants.map do |c|
|
1209
1065
|
[Sass::SCSS::RX.const_get(c), c.downcase]
|
1210
1066
|
end).merge(
|
1211
1067
|
IDENT => "identifier",
|
1212
|
-
/[;}]/ => '";"',
|
1068
|
+
/[;{}]/ => '";"',
|
1213
1069
|
/\b(without|with)\b/ => '"with" or "without"'
|
1214
1070
|
)
|
1215
1071
|
|
@@ -1230,8 +1086,9 @@ MESSAGE
|
|
1230
1086
|
|
1231
1087
|
unless name
|
1232
1088
|
# Display basic regexps as plain old strings
|
1089
|
+
source = rx.source.gsub(/\\\//, '/')
|
1233
1090
|
string = rx.source.gsub(/\\(.)/, '\1')
|
1234
|
-
name =
|
1091
|
+
name = source == Regexp.escape(string) ? string.inspect : rx.inspect
|
1235
1092
|
end
|
1236
1093
|
|
1237
1094
|
expected(name)
|
@@ -1342,6 +1199,11 @@ MESSAGE
|
|
1342
1199
|
res
|
1343
1200
|
end
|
1344
1201
|
end
|
1202
|
+
|
1203
|
+
# Remove a vendor prefix from `str`.
|
1204
|
+
def deprefix(str)
|
1205
|
+
str.gsub(/^-[a-zA-Z0-9]+-/, '')
|
1206
|
+
end
|
1345
1207
|
end
|
1346
1208
|
end
|
1347
1209
|
end
|