sass 3.2.7 → 3.3.0.rc.1

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 (184) hide show
  1. data/MIT-LICENSE +2 -2
  2. data/README.md +14 -2
  3. data/Rakefile +25 -1
  4. data/VERSION +1 -1
  5. data/VERSION_DATE +1 -1
  6. data/VERSION_NAME +1 -1
  7. data/lib/sass/cache_stores/base.rb +4 -2
  8. data/lib/sass/cache_stores/chain.rb +2 -1
  9. data/lib/sass/cache_stores/filesystem.rb +2 -6
  10. data/lib/sass/cache_stores/memory.rb +1 -1
  11. data/lib/sass/cache_stores/null.rb +2 -2
  12. data/lib/sass/callbacks.rb +1 -0
  13. data/lib/sass/css.rb +10 -10
  14. data/lib/sass/engine.rb +403 -150
  15. data/lib/sass/environment.rb +136 -57
  16. data/lib/sass/error.rb +7 -7
  17. data/lib/sass/exec.rb +123 -39
  18. data/lib/sass/features.rb +41 -0
  19. data/lib/sass/importers/base.rb +33 -2
  20. data/lib/sass/importers/deprecated_path.rb +45 -0
  21. data/lib/sass/importers/filesystem.rb +25 -14
  22. data/lib/sass/importers.rb +1 -0
  23. data/lib/sass/logger/base.rb +3 -3
  24. data/lib/sass/logger/log_level.rb +4 -6
  25. data/lib/sass/media.rb +19 -19
  26. data/lib/sass/plugin/compiler.rb +141 -101
  27. data/lib/sass/plugin/configuration.rb +18 -22
  28. data/lib/sass/plugin/merb.rb +1 -1
  29. data/lib/sass/plugin/staleness_checker.rb +24 -8
  30. data/lib/sass/plugin.rb +4 -2
  31. data/lib/sass/repl.rb +3 -3
  32. data/lib/sass/script/css_lexer.rb +9 -4
  33. data/lib/sass/script/css_parser.rb +6 -2
  34. data/lib/sass/script/functions.rb +1343 -590
  35. data/lib/sass/script/lexer.rb +84 -52
  36. data/lib/sass/script/parser.rb +217 -97
  37. data/lib/sass/script/tree/funcall.rb +290 -0
  38. data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +34 -13
  39. data/lib/sass/script/tree/list_literal.rb +80 -0
  40. data/lib/sass/script/tree/literal.rb +47 -0
  41. data/lib/sass/script/tree/map_literal.rb +64 -0
  42. data/lib/sass/script/{node.rb → tree/node.rb} +22 -12
  43. data/lib/sass/script/{operation.rb → tree/operation.rb} +17 -25
  44. data/lib/sass/script/tree/selector.rb +30 -0
  45. data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +5 -4
  46. data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +14 -9
  47. data/lib/sass/script/tree/variable.rb +57 -0
  48. data/lib/sass/script/tree.rb +16 -0
  49. data/lib/sass/script/{arg_list.rb → value/arg_list.rb} +4 -24
  50. data/lib/sass/script/value/base.rb +248 -0
  51. data/lib/sass/script/value/bool.rb +36 -0
  52. data/lib/sass/script/{color.rb → value/color.rb} +239 -195
  53. data/lib/sass/script/value/helpers.rb +155 -0
  54. data/lib/sass/script/value/list.rb +119 -0
  55. data/lib/sass/script/value/map.rb +70 -0
  56. data/lib/sass/script/value/null.rb +45 -0
  57. data/lib/sass/script/{number.rb → value/number.rb} +91 -65
  58. data/lib/sass/script/{string.rb → value/string.rb} +9 -11
  59. data/lib/sass/script/value.rb +11 -0
  60. data/lib/sass/script.rb +35 -8
  61. data/lib/sass/scss/css_parser.rb +2 -1
  62. data/lib/sass/scss/parser.rb +338 -170
  63. data/lib/sass/scss/rx.rb +5 -6
  64. data/lib/sass/scss/script_lexer.rb +1 -0
  65. data/lib/sass/scss/script_parser.rb +1 -0
  66. data/lib/sass/scss/static_parser.rb +23 -6
  67. data/lib/sass/selector/abstract_sequence.rb +2 -2
  68. data/lib/sass/selector/comma_sequence.rb +21 -16
  69. data/lib/sass/selector/sequence.rb +60 -34
  70. data/lib/sass/selector/simple.rb +11 -12
  71. data/lib/sass/selector/simple_sequence.rb +55 -33
  72. data/lib/sass/selector.rb +52 -48
  73. data/lib/sass/source/map.rb +211 -0
  74. data/lib/sass/source/position.rb +39 -0
  75. data/lib/sass/source/range.rb +41 -0
  76. data/lib/sass/stack.rb +120 -0
  77. data/lib/sass/supports.rb +12 -13
  78. data/lib/sass/tree/at_root_node.rb +82 -0
  79. data/lib/sass/tree/comment_node.rb +3 -3
  80. data/lib/sass/tree/css_import_node.rb +11 -11
  81. data/lib/sass/tree/debug_node.rb +2 -2
  82. data/lib/sass/tree/directive_node.rb +13 -2
  83. data/lib/sass/tree/each_node.rb +8 -8
  84. data/lib/sass/tree/extend_node.rb +13 -6
  85. data/lib/sass/tree/for_node.rb +4 -4
  86. data/lib/sass/tree/function_node.rb +5 -4
  87. data/lib/sass/tree/if_node.rb +1 -1
  88. data/lib/sass/tree/import_node.rb +4 -5
  89. data/lib/sass/tree/media_node.rb +4 -14
  90. data/lib/sass/tree/mixin_def_node.rb +4 -4
  91. data/lib/sass/tree/mixin_node.rb +21 -8
  92. data/lib/sass/tree/node.rb +29 -12
  93. data/lib/sass/tree/prop_node.rb +38 -18
  94. data/lib/sass/tree/return_node.rb +3 -2
  95. data/lib/sass/tree/root_node.rb +19 -3
  96. data/lib/sass/tree/rule_node.rb +25 -17
  97. data/lib/sass/tree/supports_node.rb +0 -13
  98. data/lib/sass/tree/trace_node.rb +2 -1
  99. data/lib/sass/tree/variable_node.rb +9 -3
  100. data/lib/sass/tree/visitors/base.rb +6 -6
  101. data/lib/sass/tree/visitors/check_nesting.rb +12 -9
  102. data/lib/sass/tree/visitors/convert.rb +63 -38
  103. data/lib/sass/tree/visitors/cssize.rb +63 -23
  104. data/lib/sass/tree/visitors/deep_copy.rb +6 -5
  105. data/lib/sass/tree/visitors/extend.rb +7 -7
  106. data/lib/sass/tree/visitors/perform.rb +256 -151
  107. data/lib/sass/tree/visitors/set_options.rb +6 -6
  108. data/lib/sass/tree/visitors/to_css.rb +231 -81
  109. data/lib/sass/tree/warn_node.rb +2 -2
  110. data/lib/sass/tree/while_node.rb +2 -2
  111. data/lib/sass/util/multibyte_string_scanner.rb +2 -0
  112. data/lib/sass/util/normalized_map.rb +65 -0
  113. data/lib/sass/util/ordered_hash.rb +188 -0
  114. data/lib/sass/util/subset_map.rb +3 -2
  115. data/lib/sass/util/test.rb +9 -0
  116. data/lib/sass/util.rb +220 -34
  117. data/lib/sass/version.rb +9 -9
  118. data/lib/sass.rb +14 -7
  119. data/test/sass/compiler_test.rb +213 -0
  120. data/test/sass/conversion_test.rb +235 -9
  121. data/test/sass/engine_test.rb +230 -60
  122. data/test/sass/exec_test.rb +86 -0
  123. data/test/sass/extend_test.rb +215 -147
  124. data/test/sass/functions_test.rb +584 -99
  125. data/test/sass/importer_test.rb +165 -17
  126. data/test/sass/plugin_test.rb +19 -13
  127. data/test/sass/script_conversion_test.rb +40 -0
  128. data/test/sass/script_test.rb +231 -21
  129. data/test/sass/scss/css_test.rb +14 -5
  130. data/test/sass/scss/scss_test.rb +1266 -66
  131. data/test/sass/source_map_test.rb +879 -0
  132. data/test/sass/templates/bork5.sass +3 -0
  133. data/test/sass/util/normalized_map_test.rb +30 -0
  134. data/test/sass/util_test.rb +90 -0
  135. data/test/sass/value_helpers_test.rb +181 -0
  136. data/test/test_helper.rb +7 -2
  137. metadata +316 -291
  138. data/lib/sass/script/bool.rb +0 -18
  139. data/lib/sass/script/funcall.rb +0 -231
  140. data/lib/sass/script/list.rb +0 -84
  141. data/lib/sass/script/literal.rb +0 -239
  142. data/lib/sass/script/null.rb +0 -34
  143. data/lib/sass/script/variable.rb +0 -58
  144. data/test/Gemfile +0 -3
  145. data/vendor/listen/CHANGELOG.md +0 -221
  146. data/vendor/listen/CONTRIBUTING.md +0 -38
  147. data/vendor/listen/Gemfile +0 -30
  148. data/vendor/listen/Guardfile +0 -8
  149. data/vendor/listen/LICENSE +0 -20
  150. data/vendor/listen/README.md +0 -315
  151. data/vendor/listen/Rakefile +0 -47
  152. data/vendor/listen/Vagrantfile +0 -96
  153. data/vendor/listen/lib/listen/adapter.rb +0 -214
  154. data/vendor/listen/lib/listen/adapters/bsd.rb +0 -112
  155. data/vendor/listen/lib/listen/adapters/darwin.rb +0 -85
  156. data/vendor/listen/lib/listen/adapters/linux.rb +0 -113
  157. data/vendor/listen/lib/listen/adapters/polling.rb +0 -67
  158. data/vendor/listen/lib/listen/adapters/windows.rb +0 -87
  159. data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
  160. data/vendor/listen/lib/listen/directory_record.rb +0 -371
  161. data/vendor/listen/lib/listen/listener.rb +0 -225
  162. data/vendor/listen/lib/listen/multi_listener.rb +0 -143
  163. data/vendor/listen/lib/listen/turnstile.rb +0 -28
  164. data/vendor/listen/lib/listen/version.rb +0 -3
  165. data/vendor/listen/lib/listen.rb +0 -40
  166. data/vendor/listen/listen.gemspec +0 -22
  167. data/vendor/listen/spec/listen/adapter_spec.rb +0 -183
  168. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +0 -36
  169. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +0 -37
  170. data/vendor/listen/spec/listen/adapters/linux_spec.rb +0 -47
  171. data/vendor/listen/spec/listen/adapters/polling_spec.rb +0 -68
  172. data/vendor/listen/spec/listen/adapters/windows_spec.rb +0 -30
  173. data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
  174. data/vendor/listen/spec/listen/directory_record_spec.rb +0 -1225
  175. data/vendor/listen/spec/listen/listener_spec.rb +0 -169
  176. data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
  177. data/vendor/listen/spec/listen/turnstile_spec.rb +0 -56
  178. data/vendor/listen/spec/listen_spec.rb +0 -73
  179. data/vendor/listen/spec/spec_helper.rb +0 -21
  180. data/vendor/listen/spec/support/adapter_helper.rb +0 -629
  181. data/vendor/listen/spec/support/directory_record_helper.rb +0 -55
  182. data/vendor/listen/spec/support/fixtures_helper.rb +0 -29
  183. data/vendor/listen/spec/support/listeners_helper.rb +0 -156
  184. data/vendor/listen/spec/support/platform_helper.rb +0 -15
@@ -0,0 +1,290 @@
1
+ require 'sass/script/functions'
2
+ require 'sass/util/normalized_map'
3
+
4
+ module Sass::Script::Tree
5
+ # A SassScript parse node representing a function call.
6
+ #
7
+ # A function call either calls one of the functions in
8
+ # {Sass::Script::Functions}, or if no function with the given name exists it
9
+ # returns a string representation of the function call.
10
+ class Funcall < Node
11
+ # The name of the function.
12
+ #
13
+ # @return [String]
14
+ attr_reader :name
15
+
16
+ # The arguments to the function.
17
+ #
18
+ # @return [Array<Node>]
19
+ attr_reader :args
20
+
21
+ # The keyword arguments to the function.
22
+ #
23
+ # @return [{String => Node}]
24
+ attr_reader :keywords
25
+
26
+ # The first splat argument for this function, if one exists.
27
+ #
28
+ # This could be a list of positional arguments, a map of keyword
29
+ # arguments, or an arglist containing both.
30
+ #
31
+ # @return [Node?]
32
+ attr_accessor :splat
33
+
34
+ # The second splat argument for this function, if one exists.
35
+ #
36
+ # If this exists, it's always a map of keyword arguments, and
37
+ # \{#splat} is always either a list or an arglist.
38
+ #
39
+ # @return [Node?]
40
+ attr_accessor :kwarg_splat
41
+
42
+ # @param name [String] See \{#name}
43
+ # @param args [Array<Node>] See \{#args}
44
+ # @param keywords [Sass::Util::NormalizedMap<Node>] See \{#keywords}
45
+ # @param splat [Node] See \{#splat}
46
+ # @param kwarg_splat [Node] See \{#kwarg_splat}
47
+ def initialize(name, args, keywords, splat, kwarg_splat)
48
+ @name = name
49
+ @args = args
50
+ @keywords = keywords
51
+ @splat = splat
52
+ @kwarg_splat = kwarg_splat
53
+ super()
54
+ end
55
+
56
+ # @return [String] A string representation of the function call
57
+ def inspect
58
+ args = @args.map {|a| a.inspect}.join(', ')
59
+ keywords = Sass::Util.hash_to_a(@keywords.as_stored).
60
+ map {|k, v| "$#{k}: #{v.inspect}"}.join(', ')
61
+ # rubocop:disable RedundantSelf
62
+ if self.splat
63
+ splat = args.empty? && keywords.empty? ? "" : ", "
64
+ splat = "#{splat}#{self.splat.inspect}..."
65
+ splat = "#{splat}, #{kwarg_splat.inspect}..." if kwarg_splat
66
+ end
67
+ # rubocop:enable RedundantSelf
68
+ "#{name}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat})"
69
+ end
70
+
71
+ # @see Node#to_sass
72
+ def to_sass(opts = {})
73
+ arg_to_sass = lambda do |arg|
74
+ sass = arg.to_sass(opts)
75
+ sass = "(#{sass})" if arg.is_a?(Sass::Script::Tree::ListLiteral) && arg.separator == :comma
76
+ sass
77
+ end
78
+
79
+ args = @args.map(&arg_to_sass)
80
+ keywords = Sass::Util.hash_to_a(@keywords.as_stored).
81
+ map {|k, v| "$#{dasherize(k, opts)}: #{arg_to_sass[v]}"}
82
+
83
+ # rubocop:disable RedundantSelf
84
+ if self.splat
85
+ splat = "#{arg_to_sass[self.splat]}..."
86
+ kwarg_splat = "#{arg_to_sass[self.kwarg_splat]}..." if self.kwarg_splat
87
+ end
88
+ # rubocop:enable RedundantSelf
89
+
90
+ arglist = [args, splat, keywords, kwarg_splat].flatten.compact.join(', ')
91
+ "#{dasherize(name, opts)}(#{arglist})"
92
+ end
93
+
94
+ # Returns the arguments to the function.
95
+ #
96
+ # @return [Array<Node>]
97
+ # @see Node#children
98
+ def children
99
+ res = @args + @keywords.values
100
+ res << @splat if @splat
101
+ res << @kwarg_splat if @kwarg_splat
102
+ res
103
+ end
104
+
105
+ # @see Node#deep_copy
106
+ def deep_copy
107
+ node = dup
108
+ node.instance_variable_set('@args', args.map {|a| a.deep_copy})
109
+ copied_keywords = Sass::Util::NormalizedMap.new
110
+ @keywords.as_stored.each {|k, v| copied_keywords[k] = v.deep_copy}
111
+ node.instance_variable_set('@keywords', copied_keywords)
112
+ node
113
+ end
114
+
115
+ protected
116
+
117
+ # Evaluates the function call.
118
+ #
119
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
120
+ # @return [Sass::Script::Value] The SassScript object that is the value of the function call
121
+ # @raise [Sass::SyntaxError] if the function call raises an ArgumentError
122
+ def _perform(environment)
123
+ args = Sass::Util.enum_with_index(@args).
124
+ map {|a, i| perform_arg(a, environment, signature && signature.args[i])}
125
+ keywords = Sass::Util.map_hash(@keywords) do |k, v|
126
+ [k, perform_arg(v, environment, k.tr('-', '_'))]
127
+ end
128
+ splat = Sass::Tree::Visitors::Perform.perform_splat(
129
+ @splat, keywords, @kwarg_splat, environment)
130
+ if (fn = environment.function(@name))
131
+ return perform_sass_fn(fn, args, splat, environment)
132
+ end
133
+
134
+ args = construct_ruby_args(ruby_name, args, splat, environment)
135
+
136
+ if Sass::Script::Functions.callable?(ruby_name)
137
+ local_environment = Sass::Environment.new(environment.global_env, environment.options)
138
+ local_environment.caller = Sass::ReadOnlyEnvironment.new(environment, environment.options)
139
+ opts(Sass::Script::Functions::EvaluationContext.new(
140
+ local_environment).send(ruby_name, *args))
141
+ else
142
+ opts(to_literal(args))
143
+ end
144
+ rescue ArgumentError => e
145
+ reformat_argument_error(e)
146
+ end
147
+
148
+ # Compass historically overrode this before it changed name to {Funcall#to_value}.
149
+ # We should get rid of it in the future.
150
+ def to_literal(args)
151
+ to_value(args)
152
+ end
153
+
154
+ # This method is factored out from `_perform` so that compass can override
155
+ # it with a cross-browser implementation for functions that require vendor prefixes
156
+ # in the generated css.
157
+ def to_value(args)
158
+ Sass::Script::Value::String.new("#{name}(#{args.join(', ')})")
159
+ end
160
+
161
+ private
162
+
163
+ def ruby_name
164
+ @ruby_name ||= @name.tr('-', '_')
165
+ end
166
+
167
+ def perform_arg(argument, environment, name)
168
+ return argument if signature && signature.delayed_args.include?(name)
169
+ argument.perform(environment)
170
+ end
171
+
172
+ def signature
173
+ @signature ||= Sass::Script::Functions.signature(name.to_sym, @args.size, @keywords.size)
174
+ end
175
+
176
+ def construct_ruby_args(name, args, splat, environment)
177
+ args += splat.to_a if splat
178
+
179
+ # All keywords are contained in splat.keywords for consistency,
180
+ # even if there were no splats passed in.
181
+ old_keywords_accessed = splat.keywords_accessed
182
+ keywords = splat.keywords
183
+ splat.keywords_accessed = old_keywords_accessed
184
+
185
+ unless (signature = Sass::Script::Functions.signature(name.to_sym, args.size, keywords.size))
186
+ return args if keywords.empty?
187
+ raise Sass::SyntaxError.new("Function #{name} doesn't support keyword arguments")
188
+ end
189
+
190
+ # If the user passes more non-keyword args than the function expects,
191
+ # but it does expect keyword args, Ruby's arg handling won't raise an error.
192
+ # Since we don't want to make functions think about this,
193
+ # we'll handle it for them here.
194
+ if signature.var_kwargs && !signature.var_args && args.size > signature.args.size
195
+ raise Sass::SyntaxError.new(
196
+ "#{args[signature.args.size].inspect} is not a keyword argument for `#{name}'")
197
+ elsif keywords.empty?
198
+ return args
199
+ end
200
+
201
+ args = args + (signature.args[args.size..-1] || []).map do |argname|
202
+ if keywords.has_key?(argname)
203
+ keywords.delete(argname)
204
+ else
205
+ raise Sass::SyntaxError.new("Function #{name} requires an argument named $#{argname}")
206
+ end
207
+ end
208
+
209
+ if keywords.size > 0
210
+ if signature.var_kwargs
211
+ args << keywords
212
+ else
213
+ argname = keywords.keys.sort.first
214
+ if signature.args.include?(argname)
215
+ raise Sass::SyntaxError.new(
216
+ "Function #{name} was passed argument $#{argname} both by position and by name")
217
+ else
218
+ raise Sass::SyntaxError.new(
219
+ "Function #{name} doesn't have an argument named $#{argname}")
220
+ end
221
+ end
222
+ end
223
+
224
+ args
225
+ end
226
+
227
+ def perform_sass_fn(function, args, splat, environment)
228
+ Sass::Tree::Visitors::Perform.perform_arguments(function, args, splat) do |env|
229
+ env.caller = Sass::Environment.new(environment)
230
+
231
+ val = catch :_sass_return do
232
+ function.tree.each {|c| Sass::Tree::Visitors::Perform.visit(c, env)}
233
+ raise Sass::SyntaxError.new("Function #{@name} finished without @return")
234
+ end
235
+ val
236
+ end
237
+ end
238
+
239
+ def reformat_argument_error(e)
240
+ message = e.message
241
+
242
+ # If this is a legitimate Ruby-raised argument error, re-raise it.
243
+ # Otherwise, it's an error in the user's stylesheet, so wrap it.
244
+ if Sass::Util.rbx?
245
+ # Rubinius has a different error report string than vanilla Ruby. It
246
+ # also doesn't put the actual method for which the argument error was
247
+ # thrown in the backtrace, nor does it include `send`, so we look for
248
+ # `_perform`.
249
+ if e.message =~ /^method '([^']+)': given (\d+), expected (\d+)/
250
+ error_name, given, expected = $1, $2, $3
251
+ raise e if error_name != ruby_name || e.backtrace[0] !~ /:in `_perform'$/
252
+ message = "wrong number of arguments (#{given} for #{expected})"
253
+ end
254
+ elsif Sass::Util.jruby?
255
+ if Sass::Util.jruby1_6?
256
+ should_maybe_raise = e.message =~ /^wrong number of arguments \((\d+) for (\d+)\)/ &&
257
+ # The one case where JRuby does include the Ruby name of the function
258
+ # is manually-thrown ArgumentErrors, which are indistinguishable from
259
+ # legitimate ArgumentErrors. We treat both of these as
260
+ # Sass::SyntaxErrors even though it can hide Ruby errors.
261
+ e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
262
+ else
263
+ should_maybe_raise =
264
+ e.message =~ /^wrong number of arguments calling `[^`]+` \((\d+) for (\d+)\)/
265
+ given, expected = $1, $2
266
+ end
267
+
268
+ if should_maybe_raise
269
+ # JRuby 1.7 includes __send__ before send and _perform.
270
+ trace = e.backtrace.dup
271
+ raise e if !Sass::Util.jruby1_6? && trace.shift !~ /:in `__send__'$/
272
+
273
+ # JRuby (as of 1.7.2) doesn't put the actual method
274
+ # for which the argument error was thrown in the backtrace, so we
275
+ # detect whether our send threw an argument error.
276
+ if !(trace[0] =~ /:in `send'$/ && trace[1] =~ /:in `_perform'$/)
277
+ raise e
278
+ elsif !Sass::Util.jruby1_6?
279
+ # JRuby 1.7 doesn't use standard formatting for its ArgumentErrors.
280
+ message = "wrong number of arguments (#{given} for #{expected})"
281
+ end
282
+ end
283
+ elsif e.message =~ /^wrong number of arguments \(\d+ for \d+\)/ &&
284
+ e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
285
+ raise e
286
+ end
287
+ raise Sass::SyntaxError.new("#{message} for `#{name}'")
288
+ end
289
+ end
290
+ end
@@ -1,20 +1,40 @@
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 {Interpolation#before}
29
+ # @param mid [Node] See {Interpolation#mid}
30
+ # @param after [Node] See {Interpolation#after}
31
+ # @param wb [Boolean] See {Interpolation#whitespace_before}
32
+ # @param wa [Boolean] See {Interpolation#whitespace_after}
33
+ # @param originally_text [Boolean] See {Interpolation#originally_text}
34
+ # @comment
35
+ # rubocop:disable ParameterLists
17
36
  def initialize(before, mid, after, wb, wa, originally_text = false)
37
+ # rubocop:enable ParameterLists
18
38
  @before = before
19
39
  @mid = mid
20
40
  @after = after
@@ -64,16 +84,17 @@ module Sass::Script
64
84
  # Evaluates the interpolation.
65
85
  #
66
86
  # @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
87
+ # @return [Sass::Script::Value::String]
88
+ # The SassScript string that is the value of the interpolation
68
89
  def _perform(environment)
69
90
  res = ""
70
91
  res << @before.perform(environment).to_s if @before
71
92
  res << " " if @before && @whitespace_before
72
93
  val = @mid.perform(environment)
73
- res << (val.is_a?(Sass::Script::String) ? val.value : val.to_s)
94
+ res << (val.is_a?(Sass::Script::Value::String) ? val.value : val.to_s)
74
95
  res << " " if @after && @whitespace_after
75
96
  res << @after.perform(environment).to_s if @after
76
- opts(Sass::Script::String.new(res))
97
+ opts(Sass::Script::Value::String.new(res))
77
98
  end
78
99
  end
79
100
  end
@@ -0,0 +1,80 @@
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 Value#to_sass
29
+ def to_sass(opts = {})
30
+ return "()" if elements.empty?
31
+ precedence = Sass::Script::Parser.precedence_of(separator)
32
+ members = elements.map do |v|
33
+ if v.is_a?(ListLiteral) && Sass::Script::Parser.precedence_of(v.separator) <= precedence ||
34
+ separator == :space && v.is_a?(UnaryOperation) &&
35
+ (v.operator == :minus || v.operator == :plus)
36
+ "(#{v.to_sass(opts)})"
37
+ else
38
+ v.to_sass(opts)
39
+ end
40
+ end
41
+
42
+ return "(#{members.first},)" if separator == :comma && members.length == 1
43
+
44
+ members.join(sep_str(nil))
45
+ end
46
+
47
+
48
+ # @see Node#deep_copy
49
+ def deep_copy
50
+ node = dup
51
+ node.instance_variable_set('@elements', elements.map {|e| e.deep_copy})
52
+ node
53
+ end
54
+
55
+ def inspect
56
+ "(#{elements.map {|e| e.inspect}.join(separator == :space ? ' ' : ', ')})"
57
+ end
58
+
59
+ protected
60
+
61
+ def _perform(environment)
62
+ list = Sass::Script::Value::List.new(
63
+ elements.map {|e| e.perform(environment)},
64
+ separator)
65
+ list.line = line
66
+ list.source_range = source_range
67
+ list.filename = filename
68
+ list.options = options
69
+ list
70
+ end
71
+
72
+ private
73
+
74
+ def sep_str(opts = options)
75
+ return ' ' if separator == :space
76
+ return ',' if opts && opts[:style] == :compressed
77
+ ', '
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,47 @@
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 value [Sass::Script::Value::Base]
15
+ # @see #value
16
+ def initialize(value)
17
+ @value = value
18
+ end
19
+
20
+ # @see Node#children
21
+ def children; []; end
22
+
23
+ # @see Node#to_sass
24
+ def to_sass(opts = {}); value.to_sass(opts); end
25
+
26
+ # @see Node#deep_copy
27
+ def deep_copy; dup; end
28
+
29
+ # @see Node#options=
30
+ def options=(options)
31
+ value.options = options
32
+ end
33
+
34
+ def inspect
35
+ value.inspect
36
+ end
37
+
38
+ protected
39
+
40
+ def _perform(environment)
41
+ value.line = line
42
+ value.source_range = source_range
43
+ value.filename = filename
44
+ value
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,64 @@
1
+ module Sass::Script::Tree
2
+ # A class representing a map literal. When resolved, this returns a
3
+ # {Sass::Script::Node::Map}.
4
+ class MapLiteral < Node
5
+ # The key/value pairs that make up this map node. This isn't a Hash so that
6
+ # we can detect key collisions once all the keys have been performed.
7
+ #
8
+ # @return [Array<(Node, Node)>]
9
+ attr_reader :pairs
10
+
11
+ # Creates a new map literal.
12
+ #
13
+ # @param pairs [Array<(Node, Node)>] See \{#pairs}
14
+ def initialize(pairs)
15
+ @pairs = pairs
16
+ end
17
+
18
+ # @see Node#children
19
+ def children
20
+ @pairs.flatten
21
+ end
22
+
23
+ # @see Node#to_sass
24
+ def to_sass(opts = {})
25
+ return "()" if pairs.empty?
26
+
27
+ to_sass = lambda do |value|
28
+ if value.is_a?(ListLiteral) && value.separator == :comma
29
+ "(#{value.to_sass(opts)})"
30
+ else
31
+ value.to_sass(opts)
32
+ end
33
+ end
34
+
35
+ "(" + pairs.map {|(k, v)| "#{to_sass[k]}: #{to_sass[v]}"}.join(', ') + ")"
36
+ end
37
+ alias_method :inspect, :to_sass
38
+
39
+ # @see Node#deep_copy
40
+ def deep_copy
41
+ node = dup
42
+ node.instance_variable_set('@pairs',
43
+ pairs.map {|(k, v)| [k.deep_copy, v.deep_copy]})
44
+ node
45
+ end
46
+
47
+ protected
48
+
49
+ # @see Node#_perform
50
+ def _perform(environment)
51
+ keys = Set.new
52
+ map = Sass::Script::Value::Map.new(Sass::Util.to_hash(pairs.map do |(k, v)|
53
+ k, v = k.perform(environment), v.perform(environment)
54
+ if keys.include?(k)
55
+ raise Sass::SyntaxError.new("Duplicate key #{k.inspect} in map #{to_sass}.")
56
+ end
57
+ keys << k
58
+ [k, v]
59
+ end))
60
+ map.options = options
61
+ map
62
+ end
63
+ end
64
+ 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.
@@ -13,6 +13,16 @@ module Sass::Script
13
13
  # @return [Fixnum]
14
14
  attr_accessor :line
15
15
 
16
+ # The source range in the document on which this node appeared.
17
+ #
18
+ # @return [Sass::Source::Range]
19
+ attr_accessor :source_range
20
+
21
+ # The file name of the document on which this node appeared.
22
+ #
23
+ # @return [String]
24
+ attr_accessor :filename
25
+
16
26
  # Sets the options hash for this node,
17
27
  # as well as for all child nodes.
18
28
  # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
@@ -22,7 +32,7 @@ module Sass::Script
22
32
  @options = options
23
33
  children.each do |c|
24
34
  if c.is_a? Hash
25
- c.values.each {|v| v.options = options }
35
+ c.values.each {|v| v.options = options}
26
36
  else
27
37
  c.options = options
28
38
  end
@@ -35,7 +45,7 @@ module Sass::Script
35
45
  # instead, override \{#\_perform}.
36
46
  #
37
47
  # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
38
- # @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
39
49
  def perform(environment)
40
50
  _perform(environment)
41
51
  rescue Sass::SyntaxError => e
@@ -70,30 +80,30 @@ module Sass::Script
70
80
  # Converts underscores to dashes if the :dasherize option is set.
71
81
  def dasherize(s, opts)
72
82
  if opts[:dasherize]
73
- s.gsub(/_/,'-')
83
+ s.gsub(/_/, '-')
74
84
  else
75
85
  s
76
86
  end
77
87
  end
78
88
 
79
89
  # Evaluates this node.
80
- # Note that all {Literal} objects created within this method
90
+ # Note that all {Sass::Script::Value} objects created within this method
81
91
  # should have their \{#options} attribute set, probably via \{#opts}.
82
92
  #
83
93
  # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
84
- # @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
85
95
  # @see #perform
86
96
  def _perform(environment)
87
97
  Sass::Util.abstract(self)
88
98
  end
89
99
 
90
- # Sets the \{#options} field on the given literal and returns it
100
+ # Sets the \{#options} field on the given value and returns it.
91
101
  #
92
- # @param literal [Literal]
93
- # @return [Literal]
94
- def opts(literal)
95
- literal.options = options
96
- literal
102
+ # @param value [Sass::Script::Value]
103
+ # @return [Sass::Script::Value]
104
+ def opts(value)
105
+ value.options = options
106
+ value
97
107
  end
98
108
  end
99
109
  end