sass 3.3.0.alpha.149 → 3.3.0.alpha.162

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 (71) hide show
  1. data/REVISION +1 -1
  2. data/VERSION +1 -1
  3. data/VERSION_DATE +1 -1
  4. data/lib/sass/css.rb +1 -1
  5. data/lib/sass/engine.rb +4 -4
  6. data/lib/sass/environment.rb +1 -1
  7. data/lib/sass/exec.rb +1 -1
  8. data/lib/sass/media.rb +15 -15
  9. data/lib/sass/script.rb +32 -7
  10. data/lib/sass/script/css_lexer.rb +2 -2
  11. data/lib/sass/script/css_parser.rb +1 -1
  12. data/lib/sass/script/functions.rb +246 -232
  13. data/lib/sass/script/lexer.rb +24 -32
  14. data/lib/sass/script/parser.rb +84 -65
  15. data/lib/sass/script/tree.rb +14 -0
  16. data/lib/sass/script/tree/funcall.rb +242 -0
  17. data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +30 -13
  18. data/lib/sass/script/tree/list_literal.rb +65 -0
  19. data/lib/sass/script/tree/literal.rb +46 -0
  20. data/lib/sass/script/{node.rb → tree/node.rb} +10 -10
  21. data/lib/sass/script/{operation.rb → tree/operation.rb} +16 -27
  22. data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +4 -4
  23. data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +7 -8
  24. data/lib/sass/script/tree/variable.rb +56 -0
  25. data/lib/sass/script/value.rb +10 -0
  26. data/lib/sass/script/{arg_list.rb → value/arg_list.rb} +5 -20
  27. data/lib/sass/script/value/base.rb +222 -0
  28. data/lib/sass/script/{bool.rb → value/bool.rb} +2 -2
  29. data/lib/sass/script/{color.rb → value/color.rb} +22 -20
  30. data/lib/sass/script/{list.rb → value/list.rb} +15 -28
  31. data/lib/sass/script/{null.rb → value/null.rb} +3 -3
  32. data/lib/sass/script/{number.rb → value/number.rb} +19 -19
  33. data/lib/sass/script/{string.rb → value/string.rb} +7 -7
  34. data/lib/sass/scss/parser.rb +14 -4
  35. data/lib/sass/selector.rb +26 -26
  36. data/lib/sass/selector/abstract_sequence.rb +1 -1
  37. data/lib/sass/selector/simple.rb +6 -7
  38. data/lib/sass/source/position.rb +13 -0
  39. data/lib/sass/supports.rb +4 -4
  40. data/lib/sass/tree/comment_node.rb +3 -3
  41. data/lib/sass/tree/css_import_node.rb +7 -7
  42. data/lib/sass/tree/debug_node.rb +2 -2
  43. data/lib/sass/tree/directive_node.rb +2 -2
  44. data/lib/sass/tree/each_node.rb +2 -2
  45. data/lib/sass/tree/extend_node.rb +4 -4
  46. data/lib/sass/tree/for_node.rb +4 -4
  47. data/lib/sass/tree/function_node.rb +4 -4
  48. data/lib/sass/tree/media_node.rb +3 -3
  49. data/lib/sass/tree/mixin_def_node.rb +4 -4
  50. data/lib/sass/tree/mixin_node.rb +6 -6
  51. data/lib/sass/tree/prop_node.rb +23 -15
  52. data/lib/sass/tree/return_node.rb +2 -2
  53. data/lib/sass/tree/rule_node.rb +3 -3
  54. data/lib/sass/tree/variable_node.rb +2 -2
  55. data/lib/sass/tree/visitors/convert.rb +2 -2
  56. data/lib/sass/tree/visitors/deep_copy.rb +5 -5
  57. data/lib/sass/tree/visitors/perform.rb +7 -7
  58. data/lib/sass/tree/visitors/set_options.rb +6 -6
  59. data/lib/sass/tree/visitors/to_css.rb +1 -1
  60. data/lib/sass/tree/warn_node.rb +2 -2
  61. data/lib/sass/tree/while_node.rb +2 -2
  62. data/lib/sass/util.rb +2 -2
  63. data/test/sass/engine_test.rb +6 -6
  64. data/test/sass/functions_test.rb +20 -20
  65. data/test/sass/plugin_test.rb +2 -2
  66. data/test/sass/script_test.rb +38 -29
  67. data/test/test_helper.rb +1 -1
  68. metadata +23 -19
  69. data/lib/sass/script/funcall.rb +0 -238
  70. data/lib/sass/script/literal.rb +0 -221
  71. data/lib/sass/script/variable.rb +0 -58
@@ -0,0 +1,14 @@
1
+ # The module containing nodes in the SassScript parse tree. These nodes are
2
+ # all subclasses of {Sass::Script::Tree::Node}.
3
+ module Sass::Script::Tree
4
+ end
5
+
6
+ require 'sass/script/tree/node'
7
+ require 'sass/script/tree/variable'
8
+ require 'sass/script/tree/funcall'
9
+ require 'sass/script/tree/operation'
10
+ require 'sass/script/tree/unary_operation'
11
+ require 'sass/script/tree/interpolation'
12
+ require 'sass/script/tree/string_interpolation'
13
+ require 'sass/script/tree/literal'
14
+ require 'sass/script/tree/list_literal'
@@ -0,0 +1,242 @@
1
+ require 'sass/script/functions'
2
+
3
+ module Sass::Script::Tree
4
+ # A SassScript parse node representing a function call.
5
+ #
6
+ # A function call either calls one of the functions in
7
+ # {Sass::Script::Functions}, or if no function with the given name exists it
8
+ # returns a string representation of the function call.
9
+ class Funcall < Node
10
+ # The name of the function.
11
+ #
12
+ # @return [String]
13
+ attr_reader :name
14
+
15
+ # The arguments to the function.
16
+ #
17
+ # @return [Array<Node>]
18
+ attr_reader :args
19
+
20
+ # The keyword arguments to the function.
21
+ #
22
+ # @return [{String => Node}]
23
+ attr_reader :keywords
24
+
25
+ # The splat argument for this function, if one exists.
26
+ #
27
+ # @return [Node?]
28
+ attr_accessor :splat
29
+
30
+ # @param name [String] See \{#name}
31
+ # @param args [Array<Node>] See \{#args}
32
+ # @param splat [Node] See \{#splat}
33
+ # @param keywords [{String => Node}] See \{#keywords}
34
+ def initialize(name, args, keywords, splat)
35
+ @name = name
36
+ @args = args
37
+ @keywords = keywords
38
+ @splat = splat
39
+ super()
40
+ end
41
+
42
+ # @return [String] A string representation of the function call
43
+ def inspect
44
+ args = @args.map {|a| a.inspect}.join(', ')
45
+ keywords = Sass::Util.hash_to_a(@keywords).
46
+ map {|k, v| "$#{k}: #{v.inspect}"}.join(', ')
47
+ if self.splat
48
+ splat = (args.empty? && keywords.empty?) ? "" : ", "
49
+ splat = "#{splat}#{self.splat.inspect}..."
50
+ end
51
+ "#{name}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat})"
52
+ end
53
+
54
+ # @see Node#to_sass
55
+ def to_sass(opts = {})
56
+ arg_to_sass = lambda do |arg|
57
+ sass = arg.to_sass(opts)
58
+ sass = "(#{sass})" if arg.is_a?(Sass::Script::Tree::ListLiteral) && arg.separator == :comma
59
+ sass
60
+ end
61
+
62
+ args = @args.map(&arg_to_sass).join(', ')
63
+ keywords = Sass::Util.hash_to_a(@keywords).
64
+ map {|k, v| "$#{dasherize(k, opts)}: #{arg_to_sass[v]}"}.join(', ')
65
+ if self.splat
66
+ splat = (args.empty? && keywords.empty?) ? "" : ", "
67
+ splat = "#{splat}#{arg_to_sass[self.splat]}..."
68
+ end
69
+ "#{dasherize(name, opts)}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat})"
70
+ end
71
+
72
+ # Returns the arguments to the function.
73
+ #
74
+ # @return [Array<Node>]
75
+ # @see Node#children
76
+ def children
77
+ res = @args + @keywords.values
78
+ res << @splat if @splat
79
+ res
80
+ end
81
+
82
+ # @see Node#deep_copy
83
+ def deep_copy
84
+ node = dup
85
+ node.instance_variable_set('@args', args.map {|a| a.deep_copy})
86
+ node.instance_variable_set('@keywords', Hash[keywords.map {|k, v| [k, v.deep_copy]}])
87
+ node
88
+ end
89
+
90
+ protected
91
+
92
+ # Evaluates the function call.
93
+ #
94
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
95
+ # @return [Sass::Script::Value] The SassScript object that is the value of the function call
96
+ # @raise [Sass::SyntaxError] if the function call raises an ArgumentError
97
+ def _perform(environment)
98
+ args = @args.map {|a| a.perform(environment)}
99
+ splat = @splat.perform(environment) if @splat
100
+ if fn = environment.function(@name)
101
+ keywords = Sass::Util.map_hash(@keywords) {|k, v| [k, v.perform(environment)]}
102
+ return perform_sass_fn(fn, args, keywords, splat)
103
+ end
104
+
105
+ ruby_name = @name.tr('-', '_')
106
+ args = construct_ruby_args(ruby_name, args, splat, environment)
107
+
108
+ unless Sass::Script::Functions.callable?(ruby_name)
109
+ opts(to_literal(args))
110
+ else
111
+ local_environment = Sass::Environment.new(environment.global_env, environment.options)
112
+ opts(Sass::Script::Functions::EvaluationContext.new(local_environment).send(ruby_name, *args))
113
+ end
114
+ rescue ArgumentError => e
115
+ message = e.message
116
+
117
+ # If this is a legitimate Ruby-raised argument error, re-raise it.
118
+ # Otherwise, it's an error in the user's stylesheet, so wrap it.
119
+ if Sass::Util.rbx?
120
+ # Rubinius has a different error report string than vanilla Ruby. It
121
+ # also doesn't put the actual method for which the argument error was
122
+ # thrown in the backtrace, nor does it include `send`, so we look for
123
+ # `_perform`.
124
+ if e.message =~ /^method '([^']+)': given (\d+), expected (\d+)/
125
+ error_name, given, expected = $1, $2, $3
126
+ raise e if error_name != ruby_name || e.backtrace[0] !~ /:in `_perform'$/
127
+ message = "wrong number of arguments (#{given} for #{expected})"
128
+ end
129
+ elsif Sass::Util.jruby?
130
+ if Sass::Util.jruby1_6?
131
+ should_maybe_raise = e.message =~ /^wrong number of arguments \((\d+) for (\d+)\)/ &&
132
+ # The one case where JRuby does include the Ruby name of the function
133
+ # is manually-thrown ArgumentErrors, which are indistinguishable from
134
+ # legitimate ArgumentErrors. We treat both of these as
135
+ # Sass::SyntaxErrors even though it can hide Ruby errors.
136
+ e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
137
+ else
138
+ should_maybe_raise = e.message =~ /^wrong number of arguments calling `[^`]+` \((\d+) for (\d+)\)/
139
+ given, expected = $1, $2
140
+ end
141
+
142
+ if should_maybe_raise
143
+ # JRuby 1.7 includes __send__ before send and _perform.
144
+ trace = e.backtrace.dup
145
+ raise e if !Sass::Util.jruby1_6? && trace.shift !~ /:in `__send__'$/
146
+
147
+ # JRuby (as of 1.7.2) doesn't put the actual method
148
+ # for which the argument error was thrown in the backtrace, so we
149
+ # detect whether our send threw an argument error.
150
+ if !(trace[0] =~ /:in `send'$/ && trace[1] =~ /:in `_perform'$/)
151
+ raise e
152
+ elsif !Sass::Util.jruby1_6?
153
+ # JRuby 1.7 doesn't use standard formatting for its ArgumentErrors.
154
+ message = "wrong number of arguments (#{given} for #{expected})"
155
+ end
156
+ end
157
+ elsif e.message =~ /^wrong number of arguments \(\d+ for \d+\)/ &&
158
+ e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
159
+ raise e
160
+ end
161
+ raise Sass::SyntaxError.new("#{message} for `#{name}'")
162
+ end
163
+
164
+ # Compass historically overrode this before it changed name to {#to\_value}.
165
+ # We should get rid of it in the future.
166
+ def to_literal(args)
167
+ to_value(args)
168
+ end
169
+
170
+ # This method is factored out from `_perform` so that compass can override
171
+ # it with a cross-browser implementation for functions that require vendor prefixes
172
+ # in the generated css.
173
+ def to_value(args)
174
+ Sass::Script::Value::String.new("#{name}(#{args.join(', ')})")
175
+ end
176
+
177
+ private
178
+
179
+ def construct_ruby_args(name, args, splat, environment)
180
+ args += splat.to_a if splat
181
+
182
+ # If variable arguments were passed, there won't be any explicit keywords.
183
+ if splat.is_a?(Sass::Script::Value::ArgList)
184
+ kwargs_size = splat.keywords.size
185
+ splat.keywords_accessed = false
186
+ else
187
+ kwargs_size = @keywords.size
188
+ end
189
+
190
+ unless signature = Sass::Script::Functions.signature(name.to_sym, args.size, kwargs_size)
191
+ return args if @keywords.empty?
192
+ raise Sass::SyntaxError.new("Function #{name} doesn't support keyword arguments")
193
+ end
194
+ keywords = splat.is_a?(Sass::Script::Value::ArgList) ? splat.keywords :
195
+ Sass::Util.map_hash(@keywords) {|k, v| [k, v.perform(environment)]}
196
+
197
+ # If the user passes more non-keyword args than the function expects,
198
+ # but it does expect keyword args, Ruby's arg handling won't raise an error.
199
+ # Since we don't want to make functions think about this,
200
+ # we'll handle it for them here.
201
+ if signature.var_kwargs && !signature.var_args && args.size > signature.args.size
202
+ raise Sass::SyntaxError.new(
203
+ "#{args[signature.args.size].inspect} is not a keyword argument for `#{name}'")
204
+ elsif keywords.empty?
205
+ return args
206
+ end
207
+
208
+ args = args + signature.args[args.size..-1].map do |argname|
209
+ if keywords.has_key?(argname)
210
+ keywords.delete(argname)
211
+ else
212
+ raise Sass::SyntaxError.new("Function #{name} requires an argument named $#{argname}")
213
+ end
214
+ end
215
+
216
+ if keywords.size > 0
217
+ if signature.var_kwargs
218
+ args << keywords
219
+ else
220
+ argname = keywords.keys.sort.first
221
+ if signature.args.include?(argname)
222
+ raise Sass::SyntaxError.new("Function #{name} was passed argument $#{argname} both by position and by name")
223
+ else
224
+ raise Sass::SyntaxError.new("Function #{name} doesn't have an argument named $#{argname}")
225
+ end
226
+ end
227
+ end
228
+
229
+ args
230
+ end
231
+
232
+ def perform_sass_fn(function, args, keywords, splat)
233
+ Sass::Tree::Visitors::Perform.perform_arguments(function, args, keywords, splat) do |env|
234
+ val = catch :_sass_return do
235
+ function.tree.each {|c| Sass::Tree::Visitors::Perform.visit(c, env)}
236
+ raise Sass::SyntaxError.new("Function #{@name} finished without @return")
237
+ end
238
+ val
239
+ end
240
+ end
241
+ end
242
+ end
@@ -1,19 +1,36 @@
1
- module Sass::Script
1
+ module Sass::Script::Tree
2
2
  # A SassScript object representing `#{}` interpolation outside a string.
3
3
  #
4
4
  # @see StringInterpolation
5
5
  class Interpolation < Node
6
+ # @return [Node] The SassScript before the interpolation
7
+ attr_reader :before
8
+
9
+ # @return [Node] The SassScript within the interpolation
10
+ attr_reader :mid
11
+
12
+ # @return [Node] The SassScript after the interpolation
13
+ attr_reader :after
14
+
15
+ # @return [Boolean] Whether there was whitespace between `before` and `#{`
16
+ attr_reader :whitespace_before
17
+
18
+ # @return [Boolean] Whether there was whitespace between `}` and `after`
19
+ attr_reader :whitespace_after
20
+
21
+ # @return [Boolean] Whether the original format of the interpolation was
22
+ # plain text, not an interpolation. This is used when converting back to
23
+ # SassScript.
24
+ attr_reader :originally_text
25
+
6
26
  # Interpolation in a property is of the form `before #{mid} after`.
7
27
  #
8
- # @param before [Node] The SassScript before the interpolation
9
- # @param mid [Node] The SassScript within the interpolation
10
- # @param after [Node] The SassScript after the interpolation
11
- # @param wb [Boolean] Whether there was whitespace between `before` and `#{`
12
- # @param wa [Boolean] Whether there was whitespace between `}` and `after`
13
- # @param originally_text [Boolean]
14
- # Whether the original format of the interpolation was plain text,
15
- # not an interpolation.
16
- # This is used when converting back to SassScript.
28
+ # @param before [Node] See {#before}
29
+ # @param mid [Node] See {#mid}
30
+ # @param after [Node] See {#after}
31
+ # @param wb [Boolean] See {#whitespace\_before}
32
+ # @param wa [Boolean] See {#whitespace\_after}
33
+ # @param originally_text [Boolean] See {#originally\_text}
17
34
  def initialize(before, mid, after, wb, wa, originally_text = false)
18
35
  @before = before
19
36
  @mid = mid
@@ -64,16 +81,16 @@ module Sass::Script
64
81
  # Evaluates the interpolation.
65
82
  #
66
83
  # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
67
- # @return [Sass::Script::String] The SassScript string that is the value of the interpolation
84
+ # @return [Sass::Script::Value::String] The SassScript string that is the value of the interpolation
68
85
  def _perform(environment)
69
86
  res = ""
70
87
  res << @before.perform(environment).to_s if @before
71
88
  res << " " if @before && @whitespace_before
72
89
  val = @mid.perform(environment)
73
- res << (val.is_a?(Sass::Script::String) ? val.value : val.to_s)
90
+ res << (val.is_a?(Sass::Script::Value::String) ? val.value : val.to_s)
74
91
  res << " " if @after && @whitespace_after
75
92
  res << @after.perform(environment).to_s if @after
76
- opts(Sass::Script::String.new(res))
93
+ opts(Sass::Script::Value::String.new(res))
77
94
  end
78
95
  end
79
96
  end
@@ -0,0 +1,65 @@
1
+ module Sass::Script::Tree
2
+ # A parse tree node representing a list literal. When resolved, this returns a
3
+ # {Sass::Tree::Value::List}.
4
+ class ListLiteral < Node
5
+ # The parse nodes for members of this list.
6
+ #
7
+ # @return [Array<Node>]
8
+ attr_reader :elements
9
+
10
+ # The operator separating the values of the list. Either `:comma` or
11
+ # `:space`.
12
+ #
13
+ # @return [Symbol]
14
+ attr_reader :separator
15
+
16
+ # Creates a new list literal.
17
+ #
18
+ # @param elements [Array<Node>] See \{#elements}
19
+ # @param separator [Symbol] See \{#separator}
20
+ def initialize(elements, separator)
21
+ @elements = elements
22
+ @separator = separator
23
+ end
24
+
25
+ # @see Node#children
26
+ def children; elements; end
27
+
28
+ # @see Node#to_sass
29
+ def to_sass(opts = {})
30
+ return "()" if elements.empty?
31
+ precedence = Sass::Script::Parser.precedence_of(separator)
32
+ elements.map do |v|
33
+ if v.is_a?(ListLiteral) && Sass::Script::Parser.precedence_of(v.separator) <= precedence
34
+ "(#{v.to_sass(opts)})"
35
+ else
36
+ v.to_sass(opts)
37
+ end
38
+ end.join(separator == :space ? ' ' : ', ')
39
+ end
40
+
41
+ # @see Node#deep_copy
42
+ def deep_copy
43
+ node = dup
44
+ node.instance_variable_set('@elements', elements.map {|e| e.deep_copy})
45
+ node
46
+ end
47
+
48
+ def inspect
49
+ "(#{elements.map {|e| e.inspect}.join(separator == :space ? ' ' : ', ')})"
50
+ end
51
+
52
+ protected
53
+
54
+ def _perform(environment)
55
+ list = Sass::Script::Value::List.new(
56
+ elements.map {|e| e.perform(environment)},
57
+ separator)
58
+ list.line = line
59
+ list.source_range = source_range
60
+ list.filename = filename
61
+ list.options = self.options
62
+ list
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,46 @@
1
+ module Sass::Script::Tree
2
+ # The parse tree node for a literal scalar value. This wraps an instance of
3
+ # {Sass::Script::Value::Base}.
4
+ #
5
+ # List literals should use {ListLiteral} instead.
6
+ class Literal < Node
7
+ # The wrapped value.
8
+ #
9
+ # @return [Sass::Script::Value::Base]
10
+ attr_reader :value
11
+
12
+ # Creates a new literal value.
13
+ #
14
+ # @param [Sass::Script::Value::Base] see {#value}
15
+ def initialize(value)
16
+ @value = value
17
+ end
18
+
19
+ # @see Node#children
20
+ def children; []; end
21
+
22
+ # @see Node#to_sass
23
+ def to_sass(opts = {}); value.to_sass(opts); end
24
+
25
+ # @see Node#deep_copy
26
+ def deep_copy; dup; end
27
+
28
+ # @see Node#options=
29
+ def options=(options)
30
+ value.options = options
31
+ end
32
+
33
+ def inspect
34
+ value.inspect
35
+ end
36
+
37
+ protected
38
+
39
+ def _perform(environment)
40
+ value.line = line
41
+ value.source_range = source_range
42
+ value.filename = filename
43
+ value
44
+ end
45
+ end
46
+ end
@@ -1,4 +1,4 @@
1
- module Sass::Script
1
+ module Sass::Script::Tree
2
2
  # The abstract superclass for SassScript parse tree nodes.
3
3
  #
4
4
  # Use \{#perform} to evaluate a parse tree.
@@ -45,7 +45,7 @@ module Sass::Script
45
45
  # instead, override \{#\_perform}.
46
46
  #
47
47
  # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
48
- # @return [Literal] The SassScript object that is the value of the SassScript
48
+ # @return [Sass::Script::Value] The SassScript object that is the value of the SassScript
49
49
  def perform(environment)
50
50
  _perform(environment)
51
51
  rescue Sass::SyntaxError => e
@@ -87,23 +87,23 @@ module Sass::Script
87
87
  end
88
88
 
89
89
  # Evaluates this node.
90
- # Note that all {Literal} objects created within this method
90
+ # Note that all {Sass::Script::Value} objects created within this method
91
91
  # should have their \{#options} attribute set, probably via \{#opts}.
92
92
  #
93
93
  # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
94
- # @return [Literal] The SassScript object that is the value of the SassScript
94
+ # @return [Sass::Script::Value] The SassScript object that is the value of the SassScript
95
95
  # @see #perform
96
96
  def _perform(environment)
97
97
  Sass::Util.abstract(self)
98
98
  end
99
99
 
100
- # Sets the \{#options} field on the given literal and returns it
100
+ # Sets the \{#options} field on the given value and returns it.
101
101
  #
102
- # @param literal [Literal]
103
- # @return [Literal]
104
- def opts(literal)
105
- literal.options = options
106
- literal
102
+ # @param value [Sass::Script::Value]
103
+ # @return [Sass::Script::Value]
104
+ def opts(value)
105
+ value.options = options
106
+ value
107
107
  end
108
108
  end
109
109
  end