sass 3.4.25 → 3.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (237) hide show
  1. checksums.yaml +4 -4
  2. data/CODE_OF_CONDUCT.md +1 -1
  3. data/CONTRIBUTING.md +3 -3
  4. data/README.md +17 -9
  5. data/VERSION +1 -1
  6. data/VERSION_DATE +1 -1
  7. data/VERSION_NAME +1 -1
  8. data/extra/sass-spec-ref.sh +9 -1
  9. data/lib/sass.rb +0 -7
  10. data/lib/sass/cache_stores/filesystem.rb +1 -1
  11. data/lib/sass/css.rb +1 -2
  12. data/lib/sass/engine.rb +39 -29
  13. data/lib/sass/environment.rb +26 -5
  14. data/lib/sass/error.rb +2 -2
  15. data/lib/sass/exec/base.rb +2 -13
  16. data/lib/sass/exec/sass_scss.rb +1 -5
  17. data/lib/sass/features.rb +1 -0
  18. data/lib/sass/importers/filesystem.rb +6 -4
  19. data/lib/sass/logger/base.rb +11 -0
  20. data/lib/sass/plugin/compiler.rb +20 -50
  21. data/lib/sass/plugin/configuration.rb +2 -2
  22. data/lib/sass/railtie.rb +1 -1
  23. data/lib/sass/script/css_parser.rb +4 -1
  24. data/lib/sass/script/functions.rb +308 -81
  25. data/lib/sass/script/lexer.rb +63 -9
  26. data/lib/sass/script/parser.rb +287 -118
  27. data/lib/sass/script/tree/funcall.rb +35 -34
  28. data/lib/sass/script/tree/interpolation.rb +0 -3
  29. data/lib/sass/script/tree/list_literal.rb +23 -8
  30. data/lib/sass/script/tree/map_literal.rb +2 -2
  31. data/lib/sass/script/tree/node.rb +0 -8
  32. data/lib/sass/script/tree/operation.rb +1 -8
  33. data/lib/sass/script/value.rb +2 -0
  34. data/lib/sass/script/value/arg_list.rb +1 -1
  35. data/lib/sass/script/value/base.rb +17 -0
  36. data/lib/sass/script/value/callable.rb +25 -0
  37. data/lib/sass/script/value/color.rb +8 -2
  38. data/lib/sass/script/value/function.rb +19 -0
  39. data/lib/sass/script/value/helpers.rb +37 -11
  40. data/lib/sass/script/value/list.rb +35 -14
  41. data/lib/sass/script/value/map.rb +2 -2
  42. data/lib/sass/script/value/number.rb +3 -2
  43. data/lib/sass/scss/css_parser.rb +6 -1
  44. data/lib/sass/scss/parser.rb +145 -56
  45. data/lib/sass/scss/rx.rb +5 -11
  46. data/lib/sass/scss/static_parser.rb +20 -42
  47. data/lib/sass/selector.rb +4 -0
  48. data/lib/sass/selector/abstract_sequence.rb +7 -6
  49. data/lib/sass/selector/comma_sequence.rb +9 -5
  50. data/lib/sass/selector/pseudo.rb +20 -3
  51. data/lib/sass/selector/sequence.rb +35 -10
  52. data/lib/sass/selector/simple.rb +9 -2
  53. data/lib/sass/selector/simple_sequence.rb +8 -4
  54. data/lib/sass/source/map.rb +2 -6
  55. data/lib/sass/stack.rb +21 -1
  56. data/lib/sass/tree/charset_node.rb +1 -1
  57. data/lib/sass/tree/prop_node.rb +45 -53
  58. data/lib/sass/tree/rule_node.rb +6 -8
  59. data/lib/sass/tree/visitors/check_nesting.rb +1 -1
  60. data/lib/sass/tree/visitors/convert.rb +2 -3
  61. data/lib/sass/tree/visitors/cssize.rb +4 -15
  62. data/lib/sass/tree/visitors/deep_copy.rb +1 -1
  63. data/lib/sass/tree/visitors/extend.rb +2 -8
  64. data/lib/sass/tree/visitors/perform.rb +23 -15
  65. data/lib/sass/tree/visitors/set_options.rb +1 -1
  66. data/lib/sass/tree/visitors/to_css.rb +49 -22
  67. data/lib/sass/util.rb +72 -310
  68. data/lib/sass/util/multibyte_string_scanner.rb +127 -131
  69. data/lib/sass/util/normalized_map.rb +1 -8
  70. data/lib/sass/version.rb +0 -4
  71. metadata +55 -202
  72. data/Rakefile +0 -453
  73. data/lib/sass/script/css_variable_warning.rb +0 -52
  74. data/lib/sass/util/cross_platform_random.rb +0 -19
  75. data/lib/sass/util/ordered_hash.rb +0 -192
  76. data/test/sass-spec.yml +0 -3
  77. data/test/sass/cache_test.rb +0 -131
  78. data/test/sass/callbacks_test.rb +0 -61
  79. data/test/sass/compiler_test.rb +0 -236
  80. data/test/sass/conversion_test.rb +0 -2188
  81. data/test/sass/css2sass_test.rb +0 -526
  82. data/test/sass/css_variable_test.rb +0 -132
  83. data/test/sass/data/hsl-rgb.txt +0 -319
  84. data/test/sass/encoding_test.rb +0 -219
  85. data/test/sass/engine_test.rb +0 -3447
  86. data/test/sass/exec_test.rb +0 -96
  87. data/test/sass/extend_test.rb +0 -1733
  88. data/test/sass/fixtures/test_staleness_check_across_importers.css +0 -1
  89. data/test/sass/fixtures/test_staleness_check_across_importers.scss +0 -1
  90. data/test/sass/functions_test.rb +0 -1977
  91. data/test/sass/importer_test.rb +0 -421
  92. data/test/sass/logger_test.rb +0 -58
  93. data/test/sass/mock_importer.rb +0 -49
  94. data/test/sass/more_results/more1.css +0 -9
  95. data/test/sass/more_results/more1_with_line_comments.css +0 -26
  96. data/test/sass/more_results/more_import.css +0 -29
  97. data/test/sass/more_templates/_more_partial.sass +0 -2
  98. data/test/sass/more_templates/more1.sass +0 -23
  99. data/test/sass/more_templates/more_import.sass +0 -11
  100. data/test/sass/plugin_test.rb +0 -556
  101. data/test/sass/results/alt.css +0 -4
  102. data/test/sass/results/basic.css +0 -9
  103. data/test/sass/results/cached_import_option.css +0 -3
  104. data/test/sass/results/compact.css +0 -5
  105. data/test/sass/results/complex.css +0 -86
  106. data/test/sass/results/compressed.css +0 -1
  107. data/test/sass/results/expanded.css +0 -19
  108. data/test/sass/results/filename_fn.css +0 -3
  109. data/test/sass/results/if.css +0 -3
  110. data/test/sass/results/import.css +0 -31
  111. data/test/sass/results/import_charset.css +0 -5
  112. data/test/sass/results/import_charset_1_8.css +0 -5
  113. data/test/sass/results/import_charset_ibm866.css +0 -5
  114. data/test/sass/results/import_content.css +0 -1
  115. data/test/sass/results/line_numbers.css +0 -49
  116. data/test/sass/results/mixins.css +0 -95
  117. data/test/sass/results/multiline.css +0 -24
  118. data/test/sass/results/nested.css +0 -22
  119. data/test/sass/results/options.css +0 -1
  120. data/test/sass/results/parent_ref.css +0 -13
  121. data/test/sass/results/script.css +0 -16
  122. data/test/sass/results/scss_import.css +0 -31
  123. data/test/sass/results/scss_importee.css +0 -2
  124. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +0 -1
  125. data/test/sass/results/subdir/subdir.css +0 -3
  126. data/test/sass/results/units.css +0 -11
  127. data/test/sass/results/warn.css +0 -0
  128. data/test/sass/results/warn_imported.css +0 -0
  129. data/test/sass/script_conversion_test.rb +0 -357
  130. data/test/sass/script_test.rb +0 -1431
  131. data/test/sass/scss/css_test.rb +0 -1281
  132. data/test/sass/scss/rx_test.rb +0 -160
  133. data/test/sass/scss/scss_test.rb +0 -4205
  134. data/test/sass/scss/test_helper.rb +0 -37
  135. data/test/sass/source_map_test.rb +0 -1055
  136. data/test/sass/superselector_test.rb +0 -210
  137. data/test/sass/templates/_cached_import_option_partial.scss +0 -1
  138. data/test/sass/templates/_double_import_loop2.sass +0 -1
  139. data/test/sass/templates/_filename_fn_import.scss +0 -11
  140. data/test/sass/templates/_imported_charset_ibm866.sass +0 -4
  141. data/test/sass/templates/_imported_charset_utf8.sass +0 -4
  142. data/test/sass/templates/_imported_content.sass +0 -3
  143. data/test/sass/templates/_partial.sass +0 -2
  144. data/test/sass/templates/_same_name_different_partiality.scss +0 -1
  145. data/test/sass/templates/alt.sass +0 -16
  146. data/test/sass/templates/basic.sass +0 -23
  147. data/test/sass/templates/bork1.sass +0 -2
  148. data/test/sass/templates/bork2.sass +0 -2
  149. data/test/sass/templates/bork3.sass +0 -2
  150. data/test/sass/templates/bork4.sass +0 -2
  151. data/test/sass/templates/bork5.sass +0 -3
  152. data/test/sass/templates/cached_import_option.scss +0 -3
  153. data/test/sass/templates/compact.sass +0 -17
  154. data/test/sass/templates/complex.sass +0 -305
  155. data/test/sass/templates/compressed.sass +0 -15
  156. data/test/sass/templates/double_import_loop1.sass +0 -1
  157. data/test/sass/templates/expanded.sass +0 -17
  158. data/test/sass/templates/filename_fn.scss +0 -18
  159. data/test/sass/templates/if.sass +0 -11
  160. data/test/sass/templates/import.sass +0 -12
  161. data/test/sass/templates/import_charset.sass +0 -9
  162. data/test/sass/templates/import_charset_1_8.sass +0 -6
  163. data/test/sass/templates/import_charset_ibm866.sass +0 -11
  164. data/test/sass/templates/import_content.sass +0 -4
  165. data/test/sass/templates/importee.less +0 -2
  166. data/test/sass/templates/importee.sass +0 -19
  167. data/test/sass/templates/line_numbers.sass +0 -13
  168. data/test/sass/templates/mixin_bork.sass +0 -5
  169. data/test/sass/templates/mixins.sass +0 -76
  170. data/test/sass/templates/multiline.sass +0 -20
  171. data/test/sass/templates/nested.sass +0 -25
  172. data/test/sass/templates/nested_bork1.sass +0 -2
  173. data/test/sass/templates/nested_bork2.sass +0 -2
  174. data/test/sass/templates/nested_bork3.sass +0 -2
  175. data/test/sass/templates/nested_bork4.sass +0 -2
  176. data/test/sass/templates/nested_import.sass +0 -2
  177. data/test/sass/templates/nested_mixin_bork.sass +0 -6
  178. data/test/sass/templates/options.sass +0 -2
  179. data/test/sass/templates/parent_ref.sass +0 -25
  180. data/test/sass/templates/same_name_different_ext.sass +0 -2
  181. data/test/sass/templates/same_name_different_ext.scss +0 -1
  182. data/test/sass/templates/same_name_different_partiality.scss +0 -1
  183. data/test/sass/templates/script.sass +0 -101
  184. data/test/sass/templates/scss_import.scss +0 -12
  185. data/test/sass/templates/scss_importee.scss +0 -1
  186. data/test/sass/templates/single_import_loop.sass +0 -1
  187. data/test/sass/templates/subdir/import_up1.scss +0 -1
  188. data/test/sass/templates/subdir/import_up2.scss +0 -1
  189. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +0 -2
  190. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +0 -3
  191. data/test/sass/templates/subdir/subdir.sass +0 -6
  192. data/test/sass/templates/units.sass +0 -11
  193. data/test/sass/templates/warn.sass +0 -3
  194. data/test/sass/templates/warn_imported.sass +0 -4
  195. data/test/sass/test_helper.rb +0 -8
  196. data/test/sass/util/multibyte_string_scanner_test.rb +0 -155
  197. data/test/sass/util/normalized_map_test.rb +0 -51
  198. data/test/sass/util/subset_map_test.rb +0 -91
  199. data/test/sass/util_test.rb +0 -438
  200. data/test/sass/value_helpers_test.rb +0 -179
  201. data/test/test_helper.rb +0 -110
  202. data/vendor/listen/CHANGELOG.md +0 -1
  203. data/vendor/listen/CONTRIBUTING.md +0 -38
  204. data/vendor/listen/Gemfile +0 -20
  205. data/vendor/listen/Guardfile +0 -8
  206. data/vendor/listen/LICENSE +0 -20
  207. data/vendor/listen/README.md +0 -349
  208. data/vendor/listen/Rakefile +0 -5
  209. data/vendor/listen/Vagrantfile +0 -96
  210. data/vendor/listen/lib/listen.rb +0 -54
  211. data/vendor/listen/lib/listen/adapter.rb +0 -327
  212. data/vendor/listen/lib/listen/adapters/bsd.rb +0 -75
  213. data/vendor/listen/lib/listen/adapters/darwin.rb +0 -48
  214. data/vendor/listen/lib/listen/adapters/linux.rb +0 -81
  215. data/vendor/listen/lib/listen/adapters/polling.rb +0 -58
  216. data/vendor/listen/lib/listen/adapters/windows.rb +0 -91
  217. data/vendor/listen/lib/listen/directory_record.rb +0 -406
  218. data/vendor/listen/lib/listen/listener.rb +0 -323
  219. data/vendor/listen/lib/listen/turnstile.rb +0 -32
  220. data/vendor/listen/lib/listen/version.rb +0 -3
  221. data/vendor/listen/listen.gemspec +0 -28
  222. data/vendor/listen/spec/listen/adapter_spec.rb +0 -149
  223. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +0 -36
  224. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +0 -37
  225. data/vendor/listen/spec/listen/adapters/linux_spec.rb +0 -47
  226. data/vendor/listen/spec/listen/adapters/polling_spec.rb +0 -68
  227. data/vendor/listen/spec/listen/adapters/windows_spec.rb +0 -30
  228. data/vendor/listen/spec/listen/directory_record_spec.rb +0 -1250
  229. data/vendor/listen/spec/listen/listener_spec.rb +0 -258
  230. data/vendor/listen/spec/listen/turnstile_spec.rb +0 -56
  231. data/vendor/listen/spec/listen_spec.rb +0 -67
  232. data/vendor/listen/spec/spec_helper.rb +0 -25
  233. data/vendor/listen/spec/support/adapter_helper.rb +0 -666
  234. data/vendor/listen/spec/support/directory_record_helper.rb +0 -57
  235. data/vendor/listen/spec/support/fixtures_helper.rb +0 -29
  236. data/vendor/listen/spec/support/listeners_helper.rb +0 -179
  237. data/vendor/listen/spec/support/platform_helper.rb +0 -15
@@ -14,13 +14,20 @@ module Sass::Script::Value
14
14
  # @return [Symbol]
15
15
  attr_reader :separator
16
16
 
17
+ # Whether the list is surrounded by square brackets.
18
+ #
19
+ # @return [Boolean]
20
+ attr_reader :bracketed
21
+
17
22
  # Creates a new list.
18
23
  #
19
24
  # @param value [Array<Value>] See \{#value}
20
25
  # @param separator [Symbol] See \{#separator}
21
- def initialize(value, separator)
26
+ # @param bracketed [Boolean] See \{#bracketed}
27
+ def initialize(value, separator: nil, bracketed: false)
22
28
  super(value)
23
29
  @separator = separator
30
+ @bracketed = bracketed
24
31
  end
25
32
 
26
33
  # @see Value#options=
@@ -33,24 +40,30 @@ module Sass::Script::Value
33
40
  def eq(other)
34
41
  Sass::Script::Value::Bool.new(
35
42
  other.is_a?(List) && value == other.value &&
36
- separator == other.separator)
43
+ separator == other.separator && bracketed == other.bracketed)
37
44
  end
38
45
 
39
46
  def hash
40
- @hash ||= [value, separator].hash
47
+ @hash ||= [value, separator, bracketed].hash
41
48
  end
42
49
 
43
50
  # @see Value#to_s
44
51
  def to_s(opts = {})
45
- raise Sass::SyntaxError.new("() isn't a valid CSS value.") if value.empty?
46
- value.
52
+ if !bracketed && value.empty?
53
+ raise Sass::SyntaxError.new("#{inspect} isn't a valid CSS value.")
54
+ end
55
+
56
+ members = value.
47
57
  reject {|e| e.is_a?(Null) || e.is_a?(List) && e.value.empty?}.
48
- map {|e| e.to_s(opts)}.join(sep_str)
58
+ map {|e| e.to_s(opts)}
59
+
60
+ contents = members.join(sep_str)
61
+ bracketed ? "[#{contents}]" : contents
49
62
  end
50
63
 
51
64
  # @see Value#to_sass
52
65
  def to_sass(opts = {})
53
- return "()" if value.empty?
66
+ return bracketed ? "[]" : "()" if value.empty?
54
67
  members = value.map do |v|
55
68
  if element_needs_parens?(v)
56
69
  "(#{v.to_sass(opts)})"
@@ -58,19 +71,26 @@ module Sass::Script::Value
58
71
  v.to_sass(opts)
59
72
  end
60
73
  end
61
- return "(#{members.first},)" if members.length == 1 && separator == :comma
62
- members.join(sep_str(nil))
74
+
75
+ if separator == :comma && members.length == 1
76
+ return "#{bracketed ? '[' : '('}#{members.first},#{bracketed ? ']' : ')'}"
77
+ end
78
+
79
+ contents = members.join(sep_str(nil))
80
+ bracketed ? "[#{contents}]" : contents
63
81
  end
64
82
 
65
83
  # @see Value#to_h
66
84
  def to_h
67
- return Sass::Util.ordered_hash if value.empty?
85
+ return {} if value.empty?
68
86
  super
69
87
  end
70
88
 
71
89
  # @see Value#inspect
72
90
  def inspect
73
- "(#{value.map {|e| e.inspect}.join(sep_str(nil))})"
91
+ (bracketed ? '[' : '(') +
92
+ value.map {|e| e.inspect}.join(sep_str(nil)) +
93
+ (bracketed ? ']' : ')')
74
94
  end
75
95
 
76
96
  # Asserts an index is within the list.
@@ -94,9 +114,10 @@ module Sass::Script::Value
94
114
 
95
115
  def element_needs_parens?(element)
96
116
  if element.is_a?(List)
97
- return false if element.value.empty?
98
- precedence = Sass::Script::Parser.precedence_of(separator)
99
- return Sass::Script::Parser.precedence_of(element.separator) <= precedence
117
+ return false if element.value.length < 2
118
+ return false if element.bracketed
119
+ precedence = Sass::Script::Parser.precedence_of(separator || :space)
120
+ return Sass::Script::Parser.precedence_of(element.separator || :space) <= precedence
100
121
  end
101
122
 
102
123
  return false unless separator == :space
@@ -12,7 +12,7 @@ module Sass::Script::Value
12
12
  #
13
13
  # @param hash [Hash<Node, Node>]
14
14
  def initialize(hash)
15
- super(Sass::Util.ordered_hash(hash))
15
+ super(hash)
16
16
  end
17
17
 
18
18
  # @see Value#options=
@@ -32,7 +32,7 @@ module Sass::Script::Value
32
32
  # @see Value#to_a
33
33
  def to_a
34
34
  value.map do |k, v|
35
- list = List.new([k, v], :space)
35
+ list = List.new([k, v], separator: :space)
36
36
  list.options = options
37
37
  list
38
38
  end
@@ -34,7 +34,7 @@ module Sass::Script::Value
34
34
  attr_accessor :original
35
35
 
36
36
  def self.precision
37
- Thread.current[:sass_numeric_precision] || Thread.main[:sass_numeric_precision] || 5
37
+ Thread.current[:sass_numeric_precision] || Thread.main[:sass_numeric_precision] || 10
38
38
  end
39
39
 
40
40
  # Sets the number of digits of precision
@@ -189,6 +189,7 @@ module Sass::Script::Value
189
189
  # @raise [Sass::UnitConversionError] if `other` has incompatible units
190
190
  def mod(other)
191
191
  if other.is_a?(Number)
192
+ return Number.new(Float::NAN) if other.value == 0
192
193
  operate(other, :%)
193
194
  else
194
195
  raise NoMethodError.new(nil, :mod)
@@ -472,7 +473,7 @@ module Sass::Script::Value
472
473
  sans_common_units(@numerator_units, @denominator_units)
473
474
 
474
475
  @denominator_units.each_with_index do |d, i|
475
- next unless convertable?(d) && (u = @numerator_units.find(&method(:convertable?)))
476
+ next unless convertable?(d) && (u = @numerator_units.find {|n| convertable?([n, d])})
476
477
  @value /= conversion_factor(d, u)
477
478
  @denominator_units.delete_at(i)
478
479
  @numerator_units.delete_at(@numerator_units.index(u))
@@ -47,7 +47,12 @@ module Sass
47
47
  def keyframes_ruleset
48
48
  start_pos = source_position
49
49
  return unless (selector = keyframes_selector)
50
- block(node(Sass::Tree::KeyframeRuleNode.new(selector.strip), start_pos), :ruleset)
50
+ block(
51
+ node(
52
+ Sass::Tree::KeyframeRuleNode.new(
53
+ Sass::Util.strip_except_escapes(selector)),
54
+ start_pos),
55
+ :ruleset)
51
56
  end
52
57
 
53
58
  @sass_script_parser = Sass::Script::CssParser
@@ -99,6 +99,18 @@ module Sass
99
99
  condition
100
100
  end
101
101
 
102
+ # Parses a custom property value.
103
+ #
104
+ # @return [Array<String, Sass::Script;:Tree::Node>] The interpolated value.
105
+ # @raise [Sass::SyntaxError] if there's a syntax error in the value,
106
+ # or if it doesn't take up the entire input string.
107
+ def parse_declaration_value
108
+ init_scanner!
109
+ value = declaration_value
110
+ expected('"}"') unless value && @scanner.eos?
111
+ value
112
+ end
113
+
102
114
  private
103
115
 
104
116
  include Sass::SCSS::RX
@@ -191,7 +203,7 @@ module Sass
191
203
  def directive
192
204
  start_pos = source_position
193
205
  return unless tok(/@/)
194
- name = tok!(IDENT)
206
+ name = ident!
195
207
  ss
196
208
 
197
209
  if (dir = special_directive(name, start_pos))
@@ -228,14 +240,14 @@ module Sass
228
240
  end
229
241
 
230
242
  def mixin_directive(start_pos)
231
- name = tok! IDENT
243
+ name = ident!
232
244
  args, splat = sass_script(:parse_mixin_definition_arglist)
233
245
  ss
234
246
  block(node(Sass::Tree::MixinDefNode.new(name, args, splat), start_pos), :directive)
235
247
  end
236
248
 
237
249
  def include_directive(start_pos)
238
- name = tok! IDENT
250
+ name = ident!
239
251
  args, keywords, splat, kwarg_splat = sass_script(:parse_mixin_include_arglist)
240
252
  ss
241
253
  include_node = node(
@@ -254,7 +266,7 @@ module Sass
254
266
  end
255
267
 
256
268
  def function_directive(start_pos)
257
- name = tok! IDENT
269
+ name = ident!
258
270
  args, splat = sass_script(:parse_function_definition_arglist)
259
271
  ss
260
272
  block(node(Sass::Tree::FunctionNode.new(name, args, splat), start_pos), :function)
@@ -274,7 +286,7 @@ module Sass
274
286
 
275
287
  def for_directive(start_pos)
276
288
  tok!(/\$/)
277
- var = tok! IDENT
289
+ var = ident!
278
290
  ss
279
291
 
280
292
  tok!(/from/)
@@ -291,12 +303,12 @@ module Sass
291
303
 
292
304
  def each_directive(start_pos)
293
305
  tok!(/\$/)
294
- vars = [tok!(IDENT)]
306
+ vars = [ident!]
295
307
  ss
296
308
  while tok(/,/)
297
309
  ss
298
310
  tok!(/\$/)
299
- vars << tok!(IDENT)
311
+ vars << ident!
300
312
  ss
301
313
  end
302
314
 
@@ -465,12 +477,23 @@ module Sass
465
477
  return unless tok(/\(/)
466
478
  res = ['(']
467
479
  ss
468
- res << sass_script(:parse)
480
+ stop_at = Set[:single_eq, :lt, :lte, :gt, :gte]
481
+ res << sass_script(:parse_until, stop_at)
469
482
 
470
483
  if tok(/:/)
471
484
  res << ': '
472
485
  ss
473
486
  res << sass_script(:parse)
487
+ elsif comparison1 = tok(/=|[<>]=?/)
488
+ res << ' ' << comparison1 << ' '
489
+ ss
490
+ res << sass_script(:parse_until, stop_at)
491
+ if ((comparison1 == ">" || comparison1 == ">=") && comparison2 = tok(/>=?/)) ||
492
+ ((comparison1 == "<" || comparison1 == "<=") && comparison2 = tok(/<=?/))
493
+ res << ' ' << comparison2 << ' '
494
+ ss
495
+ res << sass_script(:parse_until, stop_at)
496
+ end
474
497
  end
475
498
  res << tok!(/\)/)
476
499
  ss
@@ -530,10 +553,10 @@ module Sass
530
553
  end
531
554
 
532
555
  def at_root_directive_list
533
- return unless (first = tok(IDENT))
556
+ return unless (first = ident)
534
557
  arr = [first]
535
558
  ss
536
- while (e = tok(IDENT))
559
+ while (e = ident)
537
560
  arr << e
538
561
  ss
539
562
  end
@@ -624,12 +647,12 @@ module Sass
624
647
  def variable
625
648
  return unless tok(/\$/)
626
649
  start_pos = source_position
627
- name = tok!(IDENT)
650
+ name = ident!
628
651
  ss; tok!(/:/); ss
629
652
 
630
653
  expr = sass_script(:parse)
631
654
  while tok(/!/)
632
- flag_name = tok!(IDENT)
655
+ flag_name = ident!
633
656
  if flag_name == 'default'
634
657
  guarded ||= true
635
658
  elsif flag_name == 'global'
@@ -767,6 +790,12 @@ module Sass
767
790
  mid = [str {ss}]
768
791
  return name + mid unless tok(/:/)
769
792
  mid << ':'
793
+
794
+ # If this is a CSS variable, parse it as a property no matter what.
795
+ if name.first.is_a?(String) && name.first.start_with?("--")
796
+ return css_variable_declaration(name, name_start_pos, name_end_pos)
797
+ end
798
+
770
799
  return name + mid + [':'] if tok(/:/)
771
800
  mid << str {ss}
772
801
  post_colon_whitespace = !mid.last.empty?
@@ -775,7 +804,7 @@ module Sass
775
804
  value_start_pos = source_position
776
805
  value = nil
777
806
  error = catch_error do
778
- value = value!(name.first.is_a?(String) && name.first.start_with?("--"))
807
+ value = value!
779
808
  if tok?(/\{/)
780
809
  # Properties that are ambiguous with selectors can't have additional
781
810
  # properties nested beneath them.
@@ -802,7 +831,7 @@ module Sass
802
831
  ss
803
832
  require_block = tok?(/\{/)
804
833
 
805
- node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new),
834
+ node = node(Sass::Tree::PropNode.new(name.flatten.compact, [value], :new),
806
835
  name_start_pos, value_end_pos)
807
836
  node.name_source_range = range(name_start_pos, name_end_pos)
808
837
  node.value_source_range = range(value_start_pos, value_end_pos)
@@ -811,13 +840,21 @@ module Sass
811
840
  nested_properties! node
812
841
  end
813
842
 
814
- # This production is similar to the CSS [`<any-value>`][any-value]
815
- # production, but as the name implies, not quite the same. It's meant to
816
- # consume values that could be a selector, an expression, or a combination
817
- # of both. It respects strings and comments and supports interpolation. It
818
- # will consume up to "{", "}", ";", or "!".
819
- #
820
- # [any-value]: http://dev.w3.org/csswg/css-variables/#typedef-any-value
843
+ def css_variable_declaration(name, name_start_pos, name_end_pos)
844
+ value_start_pos = source_position
845
+ value = declaration_value
846
+ value_end_pos = source_position
847
+
848
+ node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new),
849
+ name_start_pos, value_end_pos)
850
+ node.name_source_range = range(name_start_pos, name_end_pos)
851
+ node.value_source_range = range(value_start_pos, value_end_pos)
852
+ node
853
+ end
854
+
855
+ # This production consumes values that could be a selector, an expression,
856
+ # or a combination of both. It respects strings and comments and supports
857
+ # interpolation. It will consume up to "{", "}", ";", or "!".
821
858
  #
822
859
  # Values consumed by this production will usually be parsed more
823
860
  # thoroughly once interpolation has been resolved.
@@ -851,6 +888,60 @@ module Sass
851
888
  interpolation(:warn_for_color)
852
889
  end
853
890
 
891
+ def declaration_value(top_level: true)
892
+ return unless (tok = declaration_value_token(top_level))
893
+ value = [tok]
894
+ while (tok = declaration_value_token(top_level))
895
+ value << tok
896
+ end
897
+ merge(value)
898
+ end
899
+
900
+ def declaration_value_token(top_level)
901
+ # This comes, more or less, from the [token consumption algorithm][].
902
+ # However, since we don't have to worry about the token semantics, we
903
+ # just consume everything until we come across a token with special
904
+ # semantics.
905
+ #
906
+ # [token consumption algorithm]: https://drafts.csswg.org/css-syntax-3/#consume-token.
907
+ result = tok(%r{
908
+ (
909
+ (?!
910
+ url\(
911
+ )
912
+ [^()\[\]{}"'#/ \t\r\n\f#{top_level ? ";" : ""}]
913
+ |
914
+ \#(?!\{)
915
+ |
916
+ /(?!\*)
917
+ )+
918
+ }xi) || interp_string || interp_uri || interpolation || tok(COMMENT)
919
+ return result if result
920
+
921
+ # Fold together multiple characters of whitespace that don't include
922
+ # newlines. The value only cares about the tokenization, so this is safe
923
+ # as long as we don't delete whitespace entirely. It's important that we
924
+ # fold here rather than post-processing, since we aren't allowed to fold
925
+ # whitespace within strings and we lose that context later on.
926
+ if (ws = tok(S))
927
+ return ws.include?("\n") ? ws.gsub(/\A[^\n]*/, '') : ' '
928
+ end
929
+
930
+ if tok(/\(/)
931
+ value = declaration_value(top_level: false)
932
+ tok!(/\)/)
933
+ ['(', *value, ')']
934
+ elsif tok(/\[/)
935
+ value = declaration_value(top_level: false)
936
+ tok!(/\]/)
937
+ ['[', *value, ']']
938
+ elsif tok(/\{/)
939
+ value = declaration_value(top_level: false)
940
+ tok!(/\}/)
941
+ ['{', *value, '}']
942
+ end
943
+ end
944
+
854
945
  def declaration
855
946
  # This allows the "*prop: val", ":prop: val", "#prop: val", and ".prop:
856
947
  # val" hacks.
@@ -871,12 +962,12 @@ module Sass
871
962
  tok!(/:/)
872
963
  ss
873
964
  value_start_pos = source_position
874
- value = value!(name.first.is_a?(String) && name.first.start_with?("--"))
965
+ value = value!
875
966
  value_end_pos = source_position
876
967
  ss
877
968
  require_block = tok?(/\{/)
878
969
 
879
- node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new),
970
+ node = node(Sass::Tree::PropNode.new(name.flatten.compact, [value], :new),
880
971
  name_start_pos, value_end_pos)
881
972
  node.name_source_range = range(name_start_pos, name_end_pos)
882
973
  node.value_source_range = range(value_start_pos, value_end_pos)
@@ -885,7 +976,7 @@ module Sass
885
976
  nested_properties! node
886
977
  end
887
978
 
888
- def value!(css_variable = false)
979
+ def value!
889
980
  if tok?(/\{/)
890
981
  str = Sass::Script::Tree::Literal.new(Sass::Script::Value::String.new(""))
891
982
  str.line = source_position.line
@@ -899,25 +990,19 @@ module Sass
899
990
  # we don't parse it at all, and instead return a plain old string
900
991
  # containing the value.
901
992
  # This results in a dramatic speed increase.
902
- if (val = tok(STATIC_VALUE, true))
903
- str = Sass::Script::Tree::Literal.new(Sass::Script::Value::String.new(val.strip))
993
+ if (val = tok(STATIC_VALUE))
994
+ # If val ends with escaped whitespace, leave it be.
995
+ str = Sass::Script::Tree::Literal.new(
996
+ Sass::Script::Value::String.new(
997
+ Sass::Util.strip_except_escapes(val)))
904
998
  str.line = start_pos.line
905
999
  str.source_range = range(start_pos)
906
1000
  return str
907
1001
  end
908
-
909
- sass_script(:parse, css_variable)
1002
+ sass_script(:parse)
910
1003
  end
911
1004
 
912
1005
  def nested_properties!(node)
913
- if node.name.first.is_a?(String) && node.name.first.start_with?("--")
914
- Sass::Util.sass_warn(<<WARNING)
915
- DEPRECATION WARNING on line #{@line}#{" of #{@filename}" if @filename}:
916
- Sass 3.6 will change the way CSS variables are parsed. Instead of being parsed as
917
- normal properties, they will not allow any Sass-specific behavior other than \#{}.
918
- WARNING
919
- end
920
-
921
1006
  @expected = 'expression (e.g. 1px, bold) or "{"'
922
1007
  block(node, :property)
923
1008
  end
@@ -966,7 +1051,7 @@ WARNING
966
1051
  def var_expr
967
1052
  return unless tok(/\$/)
968
1053
  line = @line
969
- var = Sass::Script::Tree::Variable.new(tok!(IDENT))
1054
+ var = Sass::Script::Tree::Variable.new(ident!)
970
1055
  var.line = line
971
1056
  var
972
1057
  end
@@ -1004,11 +1089,27 @@ WARNING
1004
1089
  res
1005
1090
  end
1006
1091
 
1007
- def interp_ident(start = IDENT)
1008
- val = tok(start) || interpolation(:warn_for_color) || tok(IDENT_HYPHEN_INTERP, true)
1092
+ def ident
1093
+ (ident = tok(IDENT)) && Sass::Util.normalize_ident_escapes(ident)
1094
+ end
1095
+
1096
+ def ident!
1097
+ Sass::Util.normalize_ident_escapes(tok!(IDENT))
1098
+ end
1099
+
1100
+ def name
1101
+ (name = tok(NAME)) && Sass::Util.normalize_ident_escapes(name)
1102
+ end
1103
+
1104
+ def name!
1105
+ Sass::Util.normalize_ident_escapes(tok!(NAME))
1106
+ end
1107
+
1108
+ def interp_ident
1109
+ val = ident || interpolation(:warn_for_color) || tok(IDENT_HYPHEN_INTERP)
1009
1110
  return unless val
1010
1111
  res = [val]
1011
- while (val = tok(NAME) || interpolation(:warn_for_color))
1112
+ while (val = name || interpolation(:warn_for_color))
1012
1113
  res << val
1013
1114
  end
1014
1115
  res
@@ -1084,7 +1185,7 @@ WARNING
1084
1185
  :media_expr => "media expression (e.g. (min-device-width: 800px))",
1085
1186
  :at_root_query => "@at-root query (e.g. (without: media))",
1086
1187
  :at_root_directive_list => '* or identifier',
1087
- :pseudo_args => "expression (e.g. fr, 2n+1)",
1188
+ :declaration_value => "expression (e.g. fr, 2n+1)",
1088
1189
  :interp_ident => "identifier",
1089
1190
  :qualified_name => "identifier",
1090
1191
  :expr => "expression (e.g. 1px, bold)",
@@ -1099,9 +1200,9 @@ WARNING
1099
1200
  :keyframes_selector => "keyframes selector (e.g. 10%)"
1100
1201
  }
1101
1202
 
1102
- TOK_NAMES = Sass::Util.to_hash(Sass::SCSS::RX.constants.map do |c|
1203
+ TOK_NAMES = Hash[Sass::SCSS::RX.constants.map do |c|
1103
1204
  [Sass::SCSS::RX.const_get(c), c.downcase]
1104
- end).merge(
1205
+ end].merge(
1105
1206
  IDENT => "identifier",
1106
1207
  /[;{}]/ => '";"',
1107
1208
  /\b(without|with)\b/ => '"with" or "without"'
@@ -1213,23 +1314,11 @@ WARNING
1213
1314
  # This is important because `#tok` is called all the time.
1214
1315
  NEWLINE = "\n"
1215
1316
 
1216
- def tok(rx, last_group_lookahead = false)
1317
+ def tok(rx)
1217
1318
  res = @scanner.scan(rx)
1218
1319
 
1219
1320
  return unless res
1220
1321
 
1221
- # This fixes https://github.com/nex3/sass/issues/104, which affects
1222
- # Ruby 1.8.7 and REE. This fix is to replace the ?= zero-width
1223
- # positive lookahead operator in the Regexp (which matches without
1224
- # consuming the matched group), with a match that does consume the
1225
- # group, but then rewinds the scanner and removes the group from the
1226
- # end of the matched string. This fix makes the assumption that the
1227
- # matched group will always occur at the end of the match.
1228
- if last_group_lookahead && @scanner[-1]
1229
- @scanner.pos -= @scanner[-1].length
1230
- res.slice!(-@scanner[-1].length..-1)
1231
- end
1232
-
1233
1322
  newline_count = res.count(NEWLINE)
1234
1323
  if newline_count > 0
1235
1324
  @line += newline_count