sass 3.2.0.alpha.11 → 3.2.0.alpha.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/REVISION +1 -1
  2. data/VERSION +1 -1
  3. data/lib/sass/cache_stores/base.rb +4 -9
  4. data/lib/sass/cache_stores/filesystem.rb +2 -0
  5. data/lib/sass/css.rb +2 -1
  6. data/lib/sass/engine.rb +28 -8
  7. data/lib/sass/environment.rb +26 -0
  8. data/lib/sass/exec.rb +13 -0
  9. data/lib/sass/importers/base.rb +2 -1
  10. data/lib/sass/script/funcall.rb +8 -0
  11. data/lib/sass/script/interpolation.rb +9 -0
  12. data/lib/sass/script/list.rb +7 -0
  13. data/lib/sass/script/literal.rb +5 -0
  14. data/lib/sass/script/node.rb +8 -0
  15. data/lib/sass/script/number.rb +28 -5
  16. data/lib/sass/script/operation.rb +8 -0
  17. data/lib/sass/script/string_interpolation.rb +9 -0
  18. data/lib/sass/script/unary_operation.rb +7 -0
  19. data/lib/sass/script/variable.rb +5 -0
  20. data/lib/sass/scss/parser.rb +26 -13
  21. data/lib/sass/scss/rx.rb +1 -1
  22. data/lib/sass/scss/static_parser.rb +2 -2
  23. data/lib/sass/tree/content_node.rb +9 -0
  24. data/lib/sass/tree/debug_node.rb +1 -6
  25. data/lib/sass/tree/each_node.rb +1 -7
  26. data/lib/sass/tree/extend_node.rb +1 -1
  27. data/lib/sass/tree/for_node.rb +2 -7
  28. data/lib/sass/tree/function_node.rb +1 -6
  29. data/lib/sass/tree/if_node.rb +1 -19
  30. data/lib/sass/tree/mixin_def_node.rb +5 -6
  31. data/lib/sass/tree/mixin_node.rb +2 -7
  32. data/lib/sass/tree/node.rb +4 -19
  33. data/lib/sass/tree/prop_node.rb +0 -5
  34. data/lib/sass/tree/return_node.rb +1 -6
  35. data/lib/sass/tree/rule_node.rb +9 -7
  36. data/lib/sass/tree/trace_node.rb +32 -0
  37. data/lib/sass/tree/variable_node.rb +1 -7
  38. data/lib/sass/tree/visitors/check_nesting.rb +30 -13
  39. data/lib/sass/tree/visitors/convert.rb +5 -1
  40. data/lib/sass/tree/visitors/cssize.rb +3 -3
  41. data/lib/sass/tree/visitors/deep_copy.rb +87 -0
  42. data/lib/sass/tree/visitors/perform.rb +36 -16
  43. data/lib/sass/tree/visitors/set_options.rb +97 -0
  44. data/lib/sass/tree/visitors/to_css.rb +5 -1
  45. data/lib/sass/tree/warn_node.rb +1 -7
  46. data/lib/sass/tree/while_node.rb +1 -7
  47. data/test/sass/cache_test.rb +15 -0
  48. data/test/sass/conversion_test.rb +38 -0
  49. data/test/sass/css2sass_test.rb +9 -0
  50. data/test/sass/engine_test.rb +248 -17
  51. data/test/sass/scss/css_test.rb +4 -2
  52. data/test/sass/scss/scss_test.rb +53 -12
  53. data/vendor/fssm/Gemfile +3 -0
  54. data/vendor/fssm/LICENSE +1 -1
  55. data/vendor/fssm/README.markdown +55 -27
  56. data/vendor/fssm/Rakefile +6 -54
  57. data/vendor/fssm/example.rb +6 -3
  58. data/vendor/fssm/fssm.gemspec +17 -70
  59. data/vendor/fssm/lib/fssm.rb +7 -3
  60. data/vendor/fssm/lib/fssm/backends/fsevents.rb +1 -1
  61. data/vendor/fssm/lib/fssm/backends/inotify.rb +2 -2
  62. data/vendor/fssm/lib/fssm/backends/polling.rb +2 -2
  63. data/vendor/fssm/lib/fssm/backends/rbfsevent.rb +42 -0
  64. data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +10 -10
  65. data/vendor/fssm/lib/fssm/monitor.rb +19 -9
  66. data/vendor/fssm/lib/fssm/path.rb +24 -21
  67. data/vendor/fssm/lib/fssm/pathname.rb +13 -479
  68. data/vendor/fssm/lib/fssm/state/directory.rb +29 -11
  69. data/vendor/fssm/lib/fssm/state/file.rb +1 -1
  70. data/vendor/fssm/lib/fssm/support.rb +41 -12
  71. data/vendor/fssm/lib/fssm/tree.rb +6 -6
  72. data/vendor/fssm/lib/fssm/version.rb +3 -0
  73. data/vendor/fssm/profile/prof-cache.rb +3 -3
  74. data/vendor/fssm/profile/prof-pathname-rubinius.rb +35 -0
  75. data/vendor/fssm/profile/prof-pathname.rb +7 -7
  76. data/vendor/fssm/spec/count_down_latch.rb +151 -0
  77. data/vendor/fssm/spec/monitor_spec.rb +202 -0
  78. data/vendor/fssm/spec/path_spec.rb +36 -15
  79. data/vendor/fssm/spec/spec_helper.rb +6 -6
  80. metadata +14 -4
  81. data/vendor/fssm/VERSION.yml +0 -5
@@ -175,7 +175,11 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
175
175
  keywords = node.keywords.map {|k, v| "$#{dasherize(k)}: #{v.to_sass(@options)}"}.join(', ')
176
176
  arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
177
177
  end
178
- "#{tab_str}#{@format == :sass ? '+' : '@include '}#{dasherize(node.name)}#{arglist}#{semi}\n"
178
+ "#{tab_str}#{@format == :sass ? '+' : '@include '}#{dasherize(node.name)}#{arglist}#{node.has_children ? yield : semi}\n"
179
+ end
180
+
181
+ def visit_content(node)
182
+ "#{tab_str}@content#{semi}\n"
179
183
  end
180
184
 
181
185
  def visit_prop(node)
@@ -121,9 +121,9 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
121
121
  (node.children.empty? ? [] : [node]) + media
122
122
  end
123
123
 
124
- # Asserts that all the mixin's children are valid in their new location.
125
- def visit_mixin(node)
126
- # Don't use #visit_children to avoid adding the mixin node to the list of parents.
124
+ # Asserts that all the traced children are valid in their new location.
125
+ def visit_trace(node)
126
+ # Don't use #visit_children to avoid adding the trace node to the list of parents.
127
127
  node.children.map {|c| visit(c)}.flatten
128
128
  rescue Sass::SyntaxError => e
129
129
  e.modify_backtrace(:mixin => node.name, :filename => node.filename, :line => node.line)
@@ -0,0 +1,87 @@
1
+ # A visitor for copying the full structure of a Sass tree.
2
+ class Sass::Tree::Visitors::DeepCopy < Sass::Tree::Visitors::Base
3
+ protected
4
+
5
+ def visit(node)
6
+ super(node.dup)
7
+ end
8
+
9
+ def visit_children(parent)
10
+ parent.children = parent.children.map {|c| visit(c)}
11
+ parent
12
+ end
13
+
14
+ def visit_debug(node)
15
+ node.expr = node.expr.deep_copy
16
+ yield
17
+ end
18
+
19
+ def visit_each(node)
20
+ node.list = node.list.deep_copy
21
+ yield
22
+ end
23
+
24
+ def visit_extend(node)
25
+ node.selector = node.selector.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
26
+ yield
27
+ end
28
+
29
+ def visit_for(node)
30
+ node.from = node.from.deep_copy
31
+ node.to = node.to.deep_copy
32
+ yield
33
+ end
34
+
35
+ def visit_function(node)
36
+ node.args = node.args.map {|k, v| [k.deep_copy, v && v.deep_copy]}
37
+ yield
38
+ end
39
+
40
+ def visit_if(node)
41
+ node.expr = node.expr.deep_copy if node.expr
42
+ node.else = visit(node.else) if node.else
43
+ yield
44
+ end
45
+
46
+ def visit_mixin_def(node)
47
+ node.args = node.args.map {|k, v| [k.deep_copy, v && v.deep_copy]}
48
+ yield
49
+ end
50
+
51
+ def visit_mixin(node)
52
+ node.args = node.args.map {|a| a.deep_copy}
53
+ node.keywords = Hash[node.keywords.map {|k, v| [k, v.deep_copy]}]
54
+ yield
55
+ end
56
+
57
+ def visit_prop(node)
58
+ node.name = node.name.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
59
+ node.value = node.value.deep_copy
60
+ yield
61
+ end
62
+
63
+ def visit_return(node)
64
+ node.expr = node.expr.deep_copy
65
+ yield
66
+ end
67
+
68
+ def visit_rule(node)
69
+ node.rule = node.rule.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
70
+ yield
71
+ end
72
+
73
+ def visit_variable(node)
74
+ node.expr = node.expr.deep_copy
75
+ yield
76
+ end
77
+
78
+ def visit_warn(node)
79
+ node.expr = node.expr.deep_copy
80
+ yield
81
+ end
82
+
83
+ def visit_while(node)
84
+ node.expr = node.expr.deep_copy
85
+ yield
86
+ end
87
+ end
@@ -13,7 +13,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
13
13
  @environment = env
14
14
  end
15
15
 
16
- # If an exception is raised, this add proper metadata to the backtrace.
16
+ # If an exception is raised, this adds proper metadata to the backtrace.
17
17
  def visit(node)
18
18
  super(node.dup)
19
19
  rescue Sass::SyntaxError => e
@@ -89,8 +89,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
89
89
  # Runs SassScript interpolation in the selector,
90
90
  # and then parses the result into a {Sass::Selector::CommaSequence}.
91
91
  def visit_extend(node)
92
- parser = Sass::SCSS::CssParser.new(run_interp(node.selector), node.line)
93
- node.resolved_selector = parser.parse_selector(node.filename)
92
+ parser = Sass::SCSS::CssParser.new(run_interp(node.selector), node.filename, node.line)
93
+ node.resolved_selector = parser.parse_selector
94
94
  node
95
95
  end
96
96
 
@@ -116,7 +116,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
116
116
  # Loads the function into the environment.
117
117
  def visit_function(node)
118
118
  @environment.set_function(node.name,
119
- Sass::Callable.new(node.name, node.args, @environment, node.children))
119
+ Sass::Callable.new(node.name, node.args, @environment, node.children, !:has_content))
120
120
  []
121
121
  end
122
122
 
@@ -155,7 +155,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
155
155
  # Loads a mixin into the environment.
156
156
  def visit_mixindef(node)
157
157
  @environment.set_mixin(node.name,
158
- Sass::Callable.new(node.name, node.args, @environment, node.children))
158
+ Sass::Callable.new(node.name, node.args, @environment, node.children, node.has_content))
159
159
  []
160
160
  end
161
161
 
@@ -168,6 +168,10 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
168
168
  original_env.prepare_frame(:mixin => node.name)
169
169
  raise Sass::SyntaxError.new("Undefined mixin '#{node.name}'.") unless mixin = @environment.mixin(node.name)
170
170
 
171
+ if node.children.any? && !mixin.has_content
172
+ raise Sass::SyntaxError.new(%Q{Mixin "#{node.name}" does not accept a content block.})
173
+ end
174
+
171
175
  passed_args = node.args.dup
172
176
  passed_keywords = node.keywords.dup
173
177
 
@@ -196,9 +200,12 @@ END
196
200
  raise Sass::SyntaxError.new("Mixin #{node.name} is missing parameter #{var.inspect}.") unless env.var(var.name)
197
201
  env
198
202
  end
203
+ environment.caller = Sass::Environment.new(original_env)
204
+ environment.content = node.children if node.has_children
199
205
 
200
- with_environment(environment) {node.children = mixin.tree.map {|c| visit(c)}.flatten}
201
- node
206
+ trace_node = Sass::Tree::TraceNode.from_node(node.name, node)
207
+ with_environment(environment) {trace_node.children = mixin.tree.map {|c| visit(c)}.flatten}
208
+ trace_node
202
209
  rescue Sass::SyntaxError => e
203
210
  if original_env # Don't add backtrace info if this is an @include loop
204
211
  e.modify_backtrace(:mixin => node.name, :line => node.line)
@@ -209,6 +216,17 @@ END
209
216
  original_env.pop_frame if original_env
210
217
  end
211
218
 
219
+ def visit_content(node)
220
+ raise Sass::SyntaxError.new("No @content passed.") unless content = @environment.content
221
+ trace_node = Sass::Tree::TraceNode.from_node('@content', node)
222
+ with_environment(@environment.caller) {trace_node.children = content.map {|c| visit(c.dup)}.flatten}
223
+ trace_node
224
+ rescue Sass::SyntaxError => e
225
+ e.modify_backtrace(:mixin => '@content', :line => node.line)
226
+ e.add_backtrace(:line => node.line)
227
+ raise e
228
+ end
229
+
212
230
  # Runs any SassScript that may be embedded in a property.
213
231
  def visit_prop(node)
214
232
  node.resolved_name = run_interp(node.name)
@@ -225,8 +243,13 @@ END
225
243
  # Runs SassScript interpolation in the selector,
226
244
  # and then parses the result into a {Sass::Selector::CommaSequence}.
227
245
  def visit_rule(node)
228
- parser = Sass::SCSS::StaticParser.new(run_interp(node.rule), node.line)
229
- node.parsed_rules ||= parser.parse_selector(node.filename)
246
+ parser = Sass::SCSS::StaticParser.new(run_interp(node.rule), node.filename, node.line)
247
+ node.parsed_rules ||= parser.parse_selector
248
+ if node.options[:trace_selectors]
249
+ @environment.push_frame(:filename => node.filename, :line => node.line)
250
+ node.stack_trace = @environment.stack_trace
251
+ @environment.pop_frame
252
+ end
230
253
  yield
231
254
  end
232
255
 
@@ -243,13 +266,10 @@ END
243
266
  @environment.push_frame(:filename => node.filename, :line => node.line)
244
267
  res = node.expr.perform(@environment)
245
268
  res = res.value if res.is_a?(Sass::Script::String)
246
- msg = "WARNING: #{res}\n"
247
- @environment.stack.reverse.each_with_index do |entry, i|
248
- msg << " #{i == 0 ? "on" : "from"} line #{entry[:line]}" <<
249
- " of #{entry[:filename] || "an unknown file"}"
250
- msg << ", in `#{entry[:mixin]}'" if entry[:mixin]
251
- msg << "\n"
252
- end
269
+ msg = "WARNING: #{res}\n "
270
+ msg << @environment.stack_trace.join("\n ")
271
+ # JRuby doesn't automatically add a newline for #warn
272
+ msg << (RUBY_PLATFORM =~ /java/ ? "\n\n" : "\n")
253
273
  Sass::Util.sass_warn msg
254
274
  []
255
275
  ensure
@@ -0,0 +1,97 @@
1
+ # A visitor for setting options on the Sass tree
2
+ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
3
+ # @param root [Tree::Node] The root node of the tree to visit.
4
+ # @param options [{Symbol => Object}] The options has to set.
5
+ def self.visit(root, options); new(options).send(:visit, root); end
6
+
7
+ protected
8
+
9
+ def initialize(options)
10
+ @options = options
11
+ end
12
+
13
+ def visit(node)
14
+ node.instance_variable_set('@options', @options)
15
+ super
16
+ end
17
+
18
+ def visit_debug(node)
19
+ node.expr.options = @options
20
+ yield
21
+ end
22
+
23
+ def visit_each(node)
24
+ node.list.options = @options
25
+ yield
26
+ end
27
+
28
+ def visit_extend(node)
29
+ node.selector.each {|c| c.options = @options if c.is_a?(Sass::Script::Node)}
30
+ yield
31
+ end
32
+
33
+ def visit_for(node)
34
+ node.from.options = @options
35
+ node.to.options = @options
36
+ yield
37
+ end
38
+
39
+ def visit_function(node)
40
+ node.args.each do |k, v|
41
+ k.options = @options
42
+ v.options = @options if v
43
+ end
44
+ yield
45
+ end
46
+
47
+ def visit_if(node)
48
+ node.expr.options = @options if node.expr
49
+ visit(node.else) if node.else
50
+ yield
51
+ end
52
+
53
+ def visit_mixin_def(node)
54
+ node.args.each do |k, v|
55
+ k.options = @options
56
+ v.options = @options if v
57
+ end
58
+ yield
59
+ end
60
+
61
+ def visit_mixin(node)
62
+ node.args.each {|a| a.options = @options}
63
+ node.keywords.each {|k, v| v.options = @options}
64
+ yield
65
+ end
66
+
67
+ def visit_prop(node)
68
+ node.name.each {|c| c.options = @options if c.is_a?(Sass::Script::Node)}
69
+ node.value.options = @options
70
+ yield
71
+ end
72
+
73
+ def visit_return(node)
74
+ node.expr.options = @options
75
+ yield
76
+ end
77
+
78
+ def visit_rule(node)
79
+ node.rule.each {|c| c.options = @options if c.is_a?(Sass::Script::Node)}
80
+ yield
81
+ end
82
+
83
+ def visit_variable(node)
84
+ node.expr.options = @options
85
+ yield
86
+ end
87
+
88
+ def visit_warn(node)
89
+ node.expr.options = @options
90
+ yield
91
+ end
92
+
93
+ def visit_while(node)
94
+ node.expr.options = @options
95
+ yield
96
+ end
97
+ end
@@ -156,6 +156,10 @@ MESSAGE
156
156
  if node.style != :compressed
157
157
  if node.options[:debug_info]
158
158
  to_return << visit(debug_info_rule(node.debug_info, node.options)) << "\n"
159
+ elsif node.options[:trace_selectors]
160
+ to_return << "#{old_spaces}/* "
161
+ to_return << node.stack_trace.join("\n #{old_spaces}")
162
+ to_return << " */\n"
159
163
  elsif node.options[:line_comments]
160
164
  to_return << "#{old_spaces}/* line #{node.line}"
161
165
 
@@ -204,7 +208,7 @@ MESSAGE
204
208
  [Sass::Selector::Element.new(k.to_s.gsub(/[^\w-]/, "\\\\\\0"), nil)])
205
209
  ])
206
210
  ])
207
- prop = Sass::Tree::PropNode.new([""], "", :new)
211
+ prop = Sass::Tree::PropNode.new([""], Sass::Script::String.new(''), :new)
208
212
  prop.resolved_name = "font-family"
209
213
  prop.resolved_value = Sass::SCSS::RX.escape_ident(v.to_s)
210
214
  rule << prop
@@ -6,19 +6,13 @@ module Sass
6
6
  class WarnNode < Node
7
7
  # The expression to print.
8
8
  # @return [Script::Node]
9
- attr_reader :expr
9
+ attr_accessor :expr
10
10
 
11
11
  # @param expr [Script::Node] The expression to print
12
12
  def initialize(expr)
13
13
  @expr = expr
14
14
  super()
15
15
  end
16
-
17
- # Returns sub nodes that are not tree children.
18
- def subnodes
19
- Array(expr)
20
- end
21
-
22
16
  end
23
17
  end
24
18
  end
@@ -7,18 +7,12 @@ module Sass::Tree
7
7
  class WhileNode < Node
8
8
  # The parse tree for the continuation expression.
9
9
  # @return [Script::Node]
10
- attr_reader :expr
10
+ attr_accessor :expr
11
11
 
12
12
  # @param expr [Script::Node] See \{#expr}
13
13
  def initialize(expr)
14
14
  @expr = expr
15
15
  super()
16
16
  end
17
-
18
- # Returns sub nodes that are not tree children.
19
- def subnodes
20
- Array(expr)
21
- end
22
-
23
17
  end
24
18
  end
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  require File.dirname(__FILE__) + '/../test_helper'
3
+ require File.dirname(__FILE__) + '/test_helper'
3
4
  require 'sass/engine'
4
5
 
5
6
  class CacheTest < Test::Unit::TestCase
@@ -11,6 +12,7 @@ class CacheTest < Test::Unit::TestCase
11
12
 
12
13
  def teardown
13
14
  FileUtils.rm_rf @@cache_dir
15
+ clean_up_sassc
14
16
  end
15
17
 
16
18
  def test_file_cache_writes_a_file
@@ -64,6 +66,19 @@ class CacheTest < Test::Unit::TestCase
64
66
  assert_equal an_object, cache.retrieve("an_object", "")
65
67
  end
66
68
 
69
+ class Unmarshalable
70
+ def _dump(_)
71
+ raise 'Unmarshalable'
72
+ end
73
+ end
74
+
75
+ def test_cache_node_with_unmarshalable_option
76
+ engine = Sass::Engine.new("foo {a: b + c}",
77
+ :syntax => :scss, :object => Unmarshalable.new, :filename => 'file.scss',
78
+ :importer => Sass::Importers::Filesystem.new(absolutize('templates')))
79
+ engine.to_tree
80
+ end
81
+
67
82
  private
68
83
  def root_node
69
84
  Sass::Engine.new(<<-SCSS, :syntax => :scss).to_tree
@@ -1152,6 +1152,44 @@ SASS
1152
1152
  SCSS
1153
1153
  end
1154
1154
 
1155
+ def test_content_conversion
1156
+ assert_renders(<<SASS, <<SCSS)
1157
+ $color: blue
1158
+
1159
+ =context($class, $color: red)
1160
+ .\#{$class}
1161
+ background-color: $color
1162
+ @content
1163
+ border-color: $color
1164
+
1165
+ +context(parent)
1166
+ +context(child, $color: yellow)
1167
+ color: $color
1168
+ SASS
1169
+ $color: blue;
1170
+
1171
+ @mixin context($class, $color: red) {
1172
+ .\#{$class} {
1173
+ background-color: $color;
1174
+ @content;
1175
+ border-color: $color; } }
1176
+
1177
+ @include context(parent) {
1178
+ @include context(child, $color: yellow) {
1179
+ color: $color; } }
1180
+ SCSS
1181
+
1182
+ end
1183
+
1184
+ def test_empty_content
1185
+ assert_scss_to_scss(<<SCSS)
1186
+ @mixin foo {
1187
+ @content; }
1188
+
1189
+ @include foo {}
1190
+ SCSS
1191
+ end
1192
+
1155
1193
  private
1156
1194
 
1157
1195
  def assert_sass_to_sass(sass, options = {})