sass 3.3.0 → 3.4.25

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 (208) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -1
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/CONTRIBUTING.md +148 -0
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +76 -62
  7. data/Rakefile +104 -24
  8. data/VERSION +1 -1
  9. data/VERSION_DATE +1 -1
  10. data/VERSION_NAME +1 -1
  11. data/bin/sass +1 -1
  12. data/bin/scss +1 -1
  13. data/extra/sass-spec-ref.sh +32 -0
  14. data/extra/update_watch.rb +1 -1
  15. data/lib/sass/cache_stores/filesystem.rb +9 -5
  16. data/lib/sass/cache_stores/memory.rb +4 -5
  17. data/lib/sass/callbacks.rb +2 -2
  18. data/lib/sass/css.rb +12 -13
  19. data/lib/sass/deprecation.rb +55 -0
  20. data/lib/sass/engine.rb +106 -70
  21. data/lib/sass/environment.rb +39 -19
  22. data/lib/sass/error.rb +17 -20
  23. data/lib/sass/exec/base.rb +199 -0
  24. data/lib/sass/exec/sass_convert.rb +283 -0
  25. data/lib/sass/exec/sass_scss.rb +440 -0
  26. data/lib/sass/exec.rb +5 -771
  27. data/lib/sass/features.rb +9 -2
  28. data/lib/sass/importers/base.rb +8 -3
  29. data/lib/sass/importers/filesystem.rb +30 -38
  30. data/lib/sass/logger/base.rb +8 -2
  31. data/lib/sass/logger/delayed.rb +50 -0
  32. data/lib/sass/logger.rb +8 -3
  33. data/lib/sass/media.rb +1 -4
  34. data/lib/sass/plugin/compiler.rb +224 -90
  35. data/lib/sass/plugin/configuration.rb +38 -22
  36. data/lib/sass/plugin/merb.rb +2 -2
  37. data/lib/sass/plugin/rack.rb +3 -3
  38. data/lib/sass/plugin/rails.rb +1 -1
  39. data/lib/sass/plugin/staleness_checker.rb +4 -4
  40. data/lib/sass/plugin.rb +6 -5
  41. data/lib/sass/script/css_lexer.rb +1 -1
  42. data/lib/sass/script/css_parser.rb +2 -3
  43. data/lib/sass/script/css_variable_warning.rb +52 -0
  44. data/lib/sass/script/functions.rb +739 -318
  45. data/lib/sass/script/lexer.rb +134 -54
  46. data/lib/sass/script/parser.rb +252 -56
  47. data/lib/sass/script/tree/funcall.rb +13 -6
  48. data/lib/sass/script/tree/interpolation.rb +127 -4
  49. data/lib/sass/script/tree/list_literal.rb +31 -4
  50. data/lib/sass/script/tree/literal.rb +4 -0
  51. data/lib/sass/script/tree/node.rb +21 -3
  52. data/lib/sass/script/tree/operation.rb +54 -1
  53. data/lib/sass/script/tree/selector.rb +26 -0
  54. data/lib/sass/script/tree/string_interpolation.rb +59 -38
  55. data/lib/sass/script/tree/variable.rb +1 -1
  56. data/lib/sass/script/tree.rb +1 -0
  57. data/lib/sass/script/value/base.rb +17 -14
  58. data/lib/sass/script/value/bool.rb +0 -5
  59. data/lib/sass/script/value/color.rb +78 -42
  60. data/lib/sass/script/value/helpers.rb +119 -2
  61. data/lib/sass/script/value/list.rb +0 -15
  62. data/lib/sass/script/value/map.rb +1 -1
  63. data/lib/sass/script/value/null.rb +0 -5
  64. data/lib/sass/script/value/number.rb +112 -31
  65. data/lib/sass/script/value/string.rb +102 -13
  66. data/lib/sass/script/value.rb +0 -1
  67. data/lib/sass/script.rb +3 -3
  68. data/lib/sass/scss/css_parser.rb +24 -4
  69. data/lib/sass/scss/parser.rb +290 -383
  70. data/lib/sass/scss/rx.rb +17 -9
  71. data/lib/sass/scss/static_parser.rb +306 -4
  72. data/lib/sass/scss.rb +0 -2
  73. data/lib/sass/selector/abstract_sequence.rb +35 -18
  74. data/lib/sass/selector/comma_sequence.rb +114 -19
  75. data/lib/sass/selector/pseudo.rb +266 -0
  76. data/lib/sass/selector/sequence.rb +146 -40
  77. data/lib/sass/selector/simple.rb +22 -33
  78. data/lib/sass/selector/simple_sequence.rb +122 -39
  79. data/lib/sass/selector.rb +57 -197
  80. data/lib/sass/shared.rb +2 -2
  81. data/lib/sass/source/map.rb +31 -14
  82. data/lib/sass/source/position.rb +4 -4
  83. data/lib/sass/stack.rb +2 -8
  84. data/lib/sass/supports.rb +10 -13
  85. data/lib/sass/tree/at_root_node.rb +1 -0
  86. data/lib/sass/tree/charset_node.rb +1 -1
  87. data/lib/sass/tree/comment_node.rb +1 -1
  88. data/lib/sass/tree/css_import_node.rb +9 -1
  89. data/lib/sass/tree/directive_node.rb +8 -2
  90. data/lib/sass/tree/error_node.rb +18 -0
  91. data/lib/sass/tree/extend_node.rb +1 -1
  92. data/lib/sass/tree/function_node.rb +9 -0
  93. data/lib/sass/tree/import_node.rb +6 -5
  94. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  95. data/lib/sass/tree/node.rb +5 -3
  96. data/lib/sass/tree/prop_node.rb +6 -7
  97. data/lib/sass/tree/rule_node.rb +26 -11
  98. data/lib/sass/tree/visitors/check_nesting.rb +56 -32
  99. data/lib/sass/tree/visitors/convert.rb +59 -44
  100. data/lib/sass/tree/visitors/cssize.rb +34 -30
  101. data/lib/sass/tree/visitors/deep_copy.rb +6 -1
  102. data/lib/sass/tree/visitors/extend.rb +15 -13
  103. data/lib/sass/tree/visitors/perform.rb +87 -50
  104. data/lib/sass/tree/visitors/set_options.rb +15 -1
  105. data/lib/sass/tree/visitors/to_css.rb +72 -43
  106. data/lib/sass/util/multibyte_string_scanner.rb +0 -2
  107. data/lib/sass/util/normalized_map.rb +0 -1
  108. data/lib/sass/util/subset_map.rb +2 -3
  109. data/lib/sass/util.rb +334 -154
  110. data/lib/sass/version.rb +7 -7
  111. data/lib/sass.rb +10 -8
  112. data/test/sass/cache_test.rb +62 -20
  113. data/test/sass/callbacks_test.rb +1 -1
  114. data/test/sass/compiler_test.rb +24 -11
  115. data/test/sass/conversion_test.rb +241 -50
  116. data/test/sass/css2sass_test.rb +73 -5
  117. data/test/sass/css_variable_test.rb +132 -0
  118. data/test/sass/encoding_test.rb +219 -0
  119. data/test/sass/engine_test.rb +343 -260
  120. data/test/sass/exec_test.rb +12 -2
  121. data/test/sass/extend_test.rb +333 -44
  122. data/test/sass/functions_test.rb +353 -260
  123. data/test/sass/importer_test.rb +40 -21
  124. data/test/sass/logger_test.rb +1 -1
  125. data/test/sass/more_results/more_import.css +1 -1
  126. data/test/sass/more_templates/more1.sass +10 -10
  127. data/test/sass/more_templates/more_import.sass +2 -2
  128. data/test/sass/plugin_test.rb +24 -21
  129. data/test/sass/results/compact.css +1 -1
  130. data/test/sass/results/complex.css +4 -4
  131. data/test/sass/results/expanded.css +1 -1
  132. data/test/sass/results/import.css +1 -1
  133. data/test/sass/results/import_charset_ibm866.css +2 -2
  134. data/test/sass/results/mixins.css +17 -17
  135. data/test/sass/results/nested.css +1 -1
  136. data/test/sass/results/parent_ref.css +2 -2
  137. data/test/sass/results/script.css +5 -5
  138. data/test/sass/results/scss_import.css +1 -1
  139. data/test/sass/script_conversion_test.rb +71 -39
  140. data/test/sass/script_test.rb +714 -123
  141. data/test/sass/scss/css_test.rb +213 -30
  142. data/test/sass/scss/rx_test.rb +8 -4
  143. data/test/sass/scss/scss_test.rb +766 -22
  144. data/test/sass/source_map_test.rb +263 -95
  145. data/test/sass/superselector_test.rb +210 -0
  146. data/test/sass/templates/_partial.sass +1 -1
  147. data/test/sass/templates/basic.sass +10 -10
  148. data/test/sass/templates/bork1.sass +1 -1
  149. data/test/sass/templates/bork5.sass +1 -1
  150. data/test/sass/templates/compact.sass +10 -10
  151. data/test/sass/templates/complex.sass +187 -187
  152. data/test/sass/templates/compressed.sass +10 -10
  153. data/test/sass/templates/expanded.sass +10 -10
  154. data/test/sass/templates/import.sass +2 -2
  155. data/test/sass/templates/importee.sass +3 -3
  156. data/test/sass/templates/mixins.sass +22 -22
  157. data/test/sass/templates/multiline.sass +4 -4
  158. data/test/sass/templates/nested.sass +13 -13
  159. data/test/sass/templates/parent_ref.sass +12 -12
  160. data/test/sass/templates/script.sass +70 -70
  161. data/test/sass/templates/scss_import.scss +2 -1
  162. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +1 -1
  163. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +2 -2
  164. data/test/sass/templates/subdir/subdir.sass +3 -3
  165. data/test/sass/templates/units.sass +10 -10
  166. data/test/sass/test_helper.rb +1 -1
  167. data/test/sass/util/multibyte_string_scanner_test.rb +11 -3
  168. data/test/sass/util/normalized_map_test.rb +1 -1
  169. data/test/sass/util/subset_map_test.rb +2 -2
  170. data/test/sass/util_test.rb +46 -45
  171. data/test/sass/value_helpers_test.rb +5 -7
  172. data/test/sass-spec.yml +3 -0
  173. data/test/test_helper.rb +7 -6
  174. data/vendor/listen/CHANGELOG.md +1 -228
  175. data/vendor/listen/Gemfile +5 -15
  176. data/vendor/listen/README.md +111 -77
  177. data/vendor/listen/Rakefile +0 -42
  178. data/vendor/listen/lib/listen/adapter.rb +195 -82
  179. data/vendor/listen/lib/listen/adapters/bsd.rb +27 -64
  180. data/vendor/listen/lib/listen/adapters/darwin.rb +21 -58
  181. data/vendor/listen/lib/listen/adapters/linux.rb +23 -55
  182. data/vendor/listen/lib/listen/adapters/polling.rb +25 -34
  183. data/vendor/listen/lib/listen/adapters/windows.rb +50 -46
  184. data/vendor/listen/lib/listen/directory_record.rb +96 -61
  185. data/vendor/listen/lib/listen/listener.rb +135 -37
  186. data/vendor/listen/lib/listen/turnstile.rb +9 -5
  187. data/vendor/listen/lib/listen/version.rb +1 -1
  188. data/vendor/listen/lib/listen.rb +33 -19
  189. data/vendor/listen/listen.gemspec +6 -0
  190. data/vendor/listen/spec/listen/adapter_spec.rb +43 -77
  191. data/vendor/listen/spec/listen/adapters/polling_spec.rb +8 -8
  192. data/vendor/listen/spec/listen/directory_record_spec.rb +81 -56
  193. data/vendor/listen/spec/listen/listener_spec.rb +128 -39
  194. data/vendor/listen/spec/listen_spec.rb +15 -21
  195. data/vendor/listen/spec/spec_helper.rb +4 -0
  196. data/vendor/listen/spec/support/adapter_helper.rb +52 -15
  197. data/vendor/listen/spec/support/directory_record_helper.rb +7 -5
  198. data/vendor/listen/spec/support/listeners_helper.rb +30 -7
  199. metadata +310 -300
  200. data/CONTRIBUTING +0 -3
  201. data/ext/mkrf_conf.rb +0 -27
  202. data/lib/sass/script/value/deprecated_false.rb +0 -55
  203. data/lib/sass/scss/script_lexer.rb +0 -15
  204. data/lib/sass/scss/script_parser.rb +0 -25
  205. data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
  206. data/vendor/listen/lib/listen/multi_listener.rb +0 -143
  207. data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
  208. data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
@@ -123,29 +123,8 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
123
123
 
124
124
  # Registers an extension in the `@extends` subset map.
125
125
  def visit_extend(node)
126
- node.resolved_selector.members.each do |seq|
127
- if seq.members.size > 1
128
- raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: can't extend nested selectors")
129
- end
130
-
131
- sseq = seq.members.first
132
- if !sseq.is_a?(Sass::Selector::SimpleSequence)
133
- raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: invalid selector")
134
- elsif sseq.members.any? {|ss| ss.is_a?(Sass::Selector::Parent)}
135
- raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: can't extend parent selectors")
136
- end
137
-
138
- sel = sseq.members
139
- parent.resolved_rules.members.each do |member|
140
- unless member.members.last.is_a?(Sass::Selector::SimpleSequence)
141
- raise Sass::SyntaxError.new("#{member} can't extend: invalid selector")
142
- end
143
-
144
- parent_directives = @parents.select {|p| p.is_a?(Sass::Tree::DirectiveNode)}
145
- @extends[sel] = Extend.new(member, sel, node, parent_directives, :not_found)
146
- end
147
- end
148
-
126
+ parent.resolved_rules.populate_extends(@extends, node.resolved_selector, node,
127
+ @parents.select {|p| p.is_a?(Sass::Tree::DirectiveNode)})
149
128
  []
150
129
  end
151
130
 
@@ -232,10 +211,22 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
232
211
  rules
233
212
  end
234
213
 
214
+ def visit_keyframerule(node)
215
+ return node unless node.has_children
216
+
217
+ yield
218
+
219
+ debubble(node.children, node)
220
+ end
221
+
235
222
  # Bubbles a directive up through RuleNodes.
236
223
  def visit_directive(node)
237
224
  return node unless node.has_children
238
- return bubble(node) if parent.is_a?(Sass::Tree::RuleNode)
225
+ if parent.is_a?(Sass::Tree::RuleNode)
226
+ # @keyframes shouldn't include the rule nodes, so we manually create a
227
+ # bubble that doesn't have the parent's contents for them.
228
+ return node.normalized_name == '@keyframes' ? Bubble.new(node) : bubble(node)
229
+ end
239
230
 
240
231
  yield
241
232
 
@@ -249,7 +240,8 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
249
240
  child.node.resolved_value == node.resolved_value
250
241
  end
251
242
 
252
- if directive_exists
243
+ # We know empty @keyframes directives do nothing.
244
+ if directive_exists || node.name == '@keyframes'
253
245
  []
254
246
  else
255
247
  empty_node = node.dup
@@ -315,19 +307,31 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
315
307
  # omitted.
316
308
  # @return [List<Sass::Tree::Node, Bubble>]
317
309
  def debubble(children, parent = nil)
310
+ # Keep track of the previous parent so that we don't divide `parent`
311
+ # unnecessarily if the `@at-root` doesn't produce any new nodes (e.g.
312
+ # `@at-root {@extend %foo}`).
313
+ previous_parent = nil
314
+
318
315
  Sass::Util.slice_by(children) {|c| c.is_a?(Bubble)}.map do |(is_bubble, slice)|
319
316
  unless is_bubble
320
317
  next slice unless parent
321
- new_parent = parent.dup
322
- new_parent.children = slice
323
- next new_parent
318
+ if previous_parent
319
+ previous_parent.children.push(*slice)
320
+ next []
321
+ else
322
+ previous_parent = new_parent = parent.dup
323
+ new_parent.children = slice
324
+ next new_parent
325
+ end
324
326
  end
325
327
 
326
- next slice.map do |bubble|
328
+ slice.map do |bubble|
327
329
  next unless (node = block_given? ? yield(bubble.node) : bubble.node)
328
330
  node.tabs += bubble.tabs
329
331
  node.group_end = bubble.group_end
330
- [visit(node)].flatten
332
+ results = [visit(node)].flatten
333
+ previous_parent = nil unless results.empty?
334
+ results
331
335
  end.compact
332
336
  end.flatten
333
337
  end
@@ -16,6 +16,11 @@ class Sass::Tree::Visitors::DeepCopy < Sass::Tree::Visitors::Base
16
16
  yield
17
17
  end
18
18
 
19
+ def visit_error(node)
20
+ node.expr = node.expr.deep_copy
21
+ yield
22
+ end
23
+
19
24
  def visit_each(node)
20
25
  node.list = node.list.deep_copy
21
26
  yield
@@ -50,7 +55,7 @@ class Sass::Tree::Visitors::DeepCopy < Sass::Tree::Visitors::Base
50
55
 
51
56
  def visit_mixin(node)
52
57
  node.args = node.args.map {|a| a.deep_copy}
53
- node.keywords = Hash[node.keywords.map {|k, v| [k, v.deep_copy]}]
58
+ node.keywords = Sass::Util::NormalizedMap.new(Hash[node.keywords.map {|k, v| [k, v.deep_copy]}])
54
59
  yield
55
60
  end
56
61
 
@@ -44,25 +44,27 @@ class Sass::Tree::Visitors::Extend < Sass::Tree::Visitors::Base
44
44
  node.resolved_rules = node.resolved_rules.do_extend(@extends, @parent_directives)
45
45
  end
46
46
 
47
- private
47
+ class << self
48
+ private
48
49
 
49
- def self.check_extends_fired!(extends)
50
- extends.each_value do |ex|
51
- next if ex.result == :succeeded || ex.node.optional?
52
- message = "\"#{ex.extender}\" failed to @extend \"#{ex.target.join}\"."
53
- reason =
54
- if ex.result == :not_found
55
- "The selector \"#{ex.target.join}\" was not found."
56
- else
57
- "No selectors matching \"#{ex.target.join}\" could be unified with \"#{ex.extender}\"."
58
- end
50
+ def check_extends_fired!(extends)
51
+ extends.each_value do |ex|
52
+ next if ex.result == :succeeded || ex.node.optional?
53
+ message = "\"#{ex.extender}\" failed to @extend \"#{ex.target.join}\"."
54
+ reason =
55
+ if ex.result == :not_found
56
+ "The selector \"#{ex.target.join}\" was not found."
57
+ else
58
+ "No selectors matching \"#{ex.target.join}\" could be unified with \"#{ex.extender}\"."
59
+ end
59
60
 
60
- # TODO(nweiz): this should use the Sass stack trace of the extend node.
61
- raise Sass::SyntaxError.new(<<MESSAGE, :filename => ex.node.filename, :line => ex.node.line)
61
+ # TODO(nweiz): this should use the Sass stack trace of the extend node.
62
+ raise Sass::SyntaxError.new(<<MESSAGE, :filename => ex.node.filename, :line => ex.node.line)
62
63
  #{message}
63
64
  #{reason}
64
65
  Use "@extend #{ex.target.join} !optional" if the extend should be able to fail.
65
66
  MESSAGE
67
+ end
66
68
  end
67
69
  end
68
70
  end
@@ -1,5 +1,7 @@
1
1
  # A visitor for converting a dynamic Sass tree into a static Sass tree.
2
2
  class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
3
+ @@function_name_deprecation = Sass::Deprecation.new
4
+
3
5
  class << self
4
6
  # @param root [Tree::Node] The root node of the tree to visit.
5
7
  # @param environment [Sass::Environment] The lexical environment.
@@ -11,7 +13,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
11
13
  # @api private
12
14
  # @comment
13
15
  # rubocop:disable MethodLength
14
- def perform_arguments(callable, args, splat)
16
+ def perform_arguments(callable, args, splat, environment)
15
17
  desc = "#{callable.type.capitalize} #{callable.name}"
16
18
  downcase_desc = "#{callable.type} #{callable.name}"
17
19
 
@@ -41,20 +43,26 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
41
43
  # raising happens in the ensure clause at the end of this function.
42
44
  return if keyword_exception && !callable.splat
43
45
 
44
- if args.size > callable.args.size && !callable.splat
45
- takes = callable.args.size
46
- passed = args.size
47
- raise Sass::SyntaxError.new(
48
- "#{desc} takes #{takes} argument#{'s' unless takes == 1} " +
49
- "but #{passed} #{passed == 1 ? 'was' : 'were'} passed.")
50
- end
51
-
52
46
  splat_sep = :comma
53
47
  if splat
54
48
  args += splat.to_a
55
49
  splat_sep = splat.separator
56
50
  end
57
51
 
52
+ if args.size > callable.args.size && !callable.splat
53
+ extra_args_because_of_splat = splat && args.size - splat.to_a.size <= callable.args.size
54
+
55
+ takes = callable.args.size
56
+ passed = args.size
57
+ message = "#{desc} takes #{takes} argument#{'s' unless takes == 1} " +
58
+ "but #{passed} #{passed == 1 ? 'was' : 'were'} passed."
59
+ raise Sass::SyntaxError.new(message) unless extra_args_because_of_splat
60
+ # TODO: when the deprecation period is over, make this an error.
61
+ Sass::Util.sass_warn("WARNING: #{message}\n" +
62
+ environment.stack.to_s.gsub(/^/m, " " * 8) + "\n" +
63
+ "This will be an error in future versions of Sass.")
64
+ end
65
+
58
66
  env = Sass::Environment.new(callable.environment)
59
67
  callable.args.zip(args[0...callable.args.length]) do |(var, default), value|
60
68
  if value && keywords.has_key?(var.name)
@@ -144,6 +152,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
144
152
 
145
153
  def initialize(env)
146
154
  @environment = env
155
+ @in_keyframes = false
156
+ @at_root_without_rule = false
147
157
  end
148
158
 
149
159
  # If an exception is raised, this adds proper metadata to the backtrace.
@@ -207,11 +217,22 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
207
217
  []
208
218
  end
209
219
 
220
+ # Throws the expression as an error.
221
+ def visit_error(node)
222
+ res = node.expr.perform(@environment)
223
+ if res.is_a?(Sass::Script::Value::String)
224
+ res = res.value
225
+ else
226
+ res = res.to_sass
227
+ end
228
+ raise Sass::SyntaxError.new(res)
229
+ end
230
+
210
231
  # Runs the child nodes once for each value in the list.
211
232
  def visit_each(node)
212
233
  list = node.list.perform(@environment)
213
234
 
214
- with_environment Sass::Environment.new(@environment) do
235
+ with_environment Sass::SemiGlobalEnvironment.new(@environment) do
215
236
  list.to_a.map do |value|
216
237
  if node.vars.length == 1
217
238
  @environment.set_local_var(node.vars.first, value)
@@ -245,7 +266,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
245
266
  direction = from.to_i > to.to_i ? -1 : 1
246
267
  range = Range.new(direction * from.to_i, direction * to.to_i, node.exclusive)
247
268
 
248
- with_environment Sass::Environment.new(@environment) do
269
+ with_environment Sass::SemiGlobalEnvironment.new(@environment) do
249
270
  range.map do |i|
250
271
  @environment.set_local_var(node.var,
251
272
  Sass::Script::Value::Number.new(direction * i,
@@ -258,9 +279,18 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
258
279
  # Loads the function into the environment.
259
280
  def visit_function(node)
260
281
  env = Sass::Environment.new(@environment, node.options)
282
+
283
+ if node.normalized_name == 'calc' || node.normalized_name == 'element' ||
284
+ node.name == 'expression' || node.name == 'url'
285
+ @@function_name_deprecation.warn(node.filename, node.line, <<WARNING)
286
+ Naming a function "#{node.name}" is disallowed and will be an error in future versions of Sass.
287
+ This name conflicts with an existing CSS function with special parse rules.
288
+ WARNING
289
+ end
290
+
261
291
  @environment.set_local_function(node.name,
262
292
  Sass::Callable.new(node.name, node.args, node.splat, env,
263
- node.children, !:has_content, "function"))
293
+ node.children, false, "function"))
264
294
  []
265
295
  end
266
296
 
@@ -268,8 +298,9 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
268
298
  # otherwise, tries the else nodes.
269
299
  def visit_if(node)
270
300
  if node.expr.nil? || node.expr.perform(@environment).to_bool
271
- yield
272
- node.children
301
+ with_environment Sass::SemiGlobalEnvironment.new(@environment) do
302
+ node.children.map {|c| visit(c)}
303
+ end.flatten
273
304
  elsif node.else
274
305
  visit(node.else)
275
306
  else
@@ -282,6 +313,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
282
313
  def visit_import(node)
283
314
  if (path = node.css_import?)
284
315
  resolved_node = Sass::Tree::CssImportNode.resolved("url(#{path})")
316
+ resolved_node.options = node.options
285
317
  resolved_node.source_range = node.source_range
286
318
  return resolved_node
287
319
  end
@@ -320,14 +352,14 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
320
352
  raise Sass::SyntaxError.new("Undefined mixin '#{node.name}'.") unless mixin
321
353
 
322
354
  if node.children.any? && !mixin.has_content
323
- raise Sass::SyntaxError.new(%Q{Mixin "#{node.name}" does not accept a content block.})
355
+ raise Sass::SyntaxError.new(%(Mixin "#{node.name}" does not accept a content block.))
324
356
  end
325
357
 
326
358
  args = node.args.map {|a| a.perform(@environment)}
327
359
  keywords = Sass::Util.map_vals(node.keywords) {|v| v.perform(@environment)}
328
360
  splat = self.class.perform_splat(node.splat, keywords, node.kwarg_splat, @environment)
329
361
 
330
- self.class.perform_arguments(mixin, args, splat) do |env|
362
+ self.class.perform_arguments(mixin, args, splat, @environment) do |env|
331
363
  env.caller = Sass::Environment.new(@environment)
332
364
  env.content = [node.children, @environment] if node.has_children
333
365
 
@@ -377,18 +409,32 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
377
409
  # Runs SassScript interpolation in the selector,
378
410
  # and then parses the result into a {Sass::Selector::CommaSequence}.
379
411
  def visit_rule(node)
380
- old_at_root_without_rule, @at_root_without_rule = @at_root_without_rule, false
412
+ old_at_root_without_rule = @at_root_without_rule
381
413
  parser = Sass::SCSS::StaticParser.new(run_interp(node.rule),
382
414
  node.filename, node.options[:importer], node.line)
383
- node.parsed_rules ||= parser.parse_selector
384
- node.resolved_rules = node.parsed_rules.resolve_parent_refs(
385
- @environment.selector, !old_at_root_without_rule)
386
- node.stack_trace = @environment.stack.to_s if node.options[:trace_selectors]
387
- with_environment Sass::Environment.new(@environment, node.options) do
388
- @environment.selector = node.resolved_rules
389
- node.children = node.children.map {|c| visit(c)}.flatten
415
+ if @in_keyframes
416
+ keyframe_rule_node = Sass::Tree::KeyframeRuleNode.new(parser.parse_keyframes_selector)
417
+ keyframe_rule_node.options = node.options
418
+ keyframe_rule_node.line = node.line
419
+ keyframe_rule_node.filename = node.filename
420
+ keyframe_rule_node.source_range = node.source_range
421
+ keyframe_rule_node.has_children = node.has_children
422
+ with_environment Sass::Environment.new(@environment, node.options) do
423
+ keyframe_rule_node.children = node.children.map {|c| visit(c)}.flatten
424
+ end
425
+ keyframe_rule_node
426
+ else
427
+ @at_root_without_rule = false
428
+ node.parsed_rules ||= parser.parse_selector
429
+ node.resolved_rules = node.parsed_rules.resolve_parent_refs(
430
+ @environment.selector, !old_at_root_without_rule)
431
+ node.stack_trace = @environment.stack.to_s if node.options[:trace_selectors]
432
+ with_environment Sass::Environment.new(@environment, node.options) do
433
+ @environment.selector = node.resolved_rules
434
+ node.children = node.children.map {|c| visit(c)}.flatten
435
+ end
436
+ node
390
437
  end
391
- node
392
438
  ensure
393
439
  @at_root_without_rule = old_at_root_without_rule
394
440
  end
@@ -405,36 +451,24 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
405
451
  end
406
452
 
407
453
  old_at_root_without_rule = @at_root_without_rule
454
+ old_in_keyframes = @in_keyframes
408
455
  @at_root_without_rule = true if node.exclude?('rule')
456
+ @in_keyframes = false if node.exclude?('keyframes')
409
457
  yield
410
458
  ensure
459
+ @in_keyframes = old_in_keyframes
411
460
  @at_root_without_rule = old_at_root_without_rule
412
461
  end
413
462
 
414
463
  # Loads the new variable value into the environment.
415
464
  def visit_variable(node)
416
465
  env = @environment
417
- identifier = [node.name, node.filename, node.line]
418
- if node.global
419
- env = env.global_env
420
- elsif env.parent && env.is_var_global?(node.name) &&
421
- !env.global_env.global_warning_given.include?(identifier)
422
- env.global_env.global_warning_given.add(identifier)
423
- var_expr = "$#{node.name}: #{node.expr.to_sass(env.options)} !global"
424
- var_expr << " !default" if node.guarded
425
- location = "on line #{node.line}"
426
- location << " of #{node.filename}" if node.filename
427
- Sass::Util.sass_warn <<WARNING
428
- DEPRECATION WARNING #{location}:
429
- Assigning to global variable "$#{node.name}" by default is deprecated.
430
- In future versions of Sass, this will create a new local variable.
431
- If you want to assign to the global variable, use "#{var_expr}" instead.
432
- Note that this will be incompatible with Sass 3.2.
433
- WARNING
466
+ env = env.global_env if node.global
467
+ if node.guarded
468
+ var = env.var(node.name)
469
+ return [] if var && !var.null?
434
470
  end
435
471
 
436
- var = env.var(node.name)
437
- return [] if node.guarded && var && !var.null?
438
472
  val = node.expr.perform(@environment)
439
473
  if node.expr.source_range
440
474
  val.source_range = node.expr.source_range
@@ -458,7 +492,7 @@ WARNING
458
492
  # Runs the child nodes until the continuation expression becomes false.
459
493
  def visit_while(node)
460
494
  children = []
461
- with_environment Sass::Environment.new(@environment) do
495
+ with_environment Sass::SemiGlobalEnvironment.new(@environment) do
462
496
  children += node.children.map {|c| visit(c)} while node.expr.perform(@environment).to_bool
463
497
  end
464
498
  children.flatten
@@ -466,10 +500,13 @@ WARNING
466
500
 
467
501
  def visit_directive(node)
468
502
  node.resolved_value = run_interp(node.value)
503
+ old_in_keyframes, @in_keyframes = @in_keyframes, node.normalized_name == "@keyframes"
469
504
  with_environment Sass::Environment.new(@environment) do
470
505
  node.children = node.children.map {|c| visit(c)}.flatten
471
506
  node
472
507
  end
508
+ ensure
509
+ @in_keyframes = old_in_keyframes
473
510
  end
474
511
 
475
512
  def visit_media(node)
@@ -487,11 +524,14 @@ WARNING
487
524
 
488
525
  def visit_cssimport(node)
489
526
  node.resolved_uri = run_interp([node.uri])
490
- if node.query
527
+ if node.query && !node.query.empty?
491
528
  parser = Sass::SCSS::StaticParser.new(run_interp(node.query),
492
529
  node.filename, node.options[:importer], node.line)
493
530
  node.resolved_query ||= parser.parse_media_query_list
494
531
  end
532
+ if node.supports_condition
533
+ node.supports_condition.perform(@environment)
534
+ end
495
535
  yield
496
536
  end
497
537
 
@@ -500,10 +540,7 @@ WARNING
500
540
  def run_interp_no_strip(text)
501
541
  text.map do |r|
502
542
  next r if r.is_a?(String)
503
- val = r.perform(@environment)
504
- # Interpolated strings should never render with quotes
505
- next val.value if val.is_a?(Sass::Script::Value::String)
506
- val.to_s
543
+ r.perform(@environment).to_s(:quote => :none)
507
544
  end.join
508
545
  end
509
546
 
@@ -15,11 +15,21 @@ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
15
15
  super
16
16
  end
17
17
 
18
+ def visit_comment(node)
19
+ node.value.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)}
20
+ yield
21
+ end
22
+
18
23
  def visit_debug(node)
19
24
  node.expr.options = @options
20
25
  yield
21
26
  end
22
27
 
28
+ def visit_error(node)
29
+ node.expr.options = @options
30
+ yield
31
+ end
32
+
23
33
  def visit_each(node)
24
34
  node.list.options = @options
25
35
  yield
@@ -41,6 +51,7 @@ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
41
51
  k.options = @options
42
52
  v.options = @options if v
43
53
  end
54
+ node.splat.options = @options if node.splat
44
55
  yield
45
56
  end
46
57
 
@@ -63,12 +74,15 @@ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
63
74
  k.options = @options
64
75
  v.options = @options if v
65
76
  end
77
+ node.splat.options = @options if node.splat
66
78
  yield
67
79
  end
68
80
 
69
81
  def visit_mixin(node)
70
82
  node.args.each {|a| a.options = @options}
71
- node.keywords.each {|k, v| v.options = @options}
83
+ node.keywords.each {|_k, v| v.options = @options}
84
+ node.splat.options = @options if node.splat
85
+ node.kwarg_splat.options = @options if node.kwarg_splat
72
86
  yield
73
87
  end
74
88