sass 3.4.25 → 3.7.4

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.
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