sass 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|