oreorenasass 3.4.4 → 3.4.5

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 (176) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +50 -70
  4. data/Rakefile +5 -26
  5. data/VERSION +1 -1
  6. data/VERSION_NAME +1 -1
  7. data/bin/sass +1 -1
  8. data/bin/scss +1 -1
  9. data/lib/sass.rb +12 -19
  10. data/lib/sass/cache_stores/base.rb +2 -2
  11. data/lib/sass/cache_stores/chain.rb +1 -2
  12. data/lib/sass/cache_stores/filesystem.rb +5 -1
  13. data/lib/sass/cache_stores/memory.rb +1 -1
  14. data/lib/sass/cache_stores/null.rb +2 -2
  15. data/lib/sass/callbacks.rb +0 -1
  16. data/lib/sass/css.rb +13 -11
  17. data/lib/sass/engine.rb +173 -424
  18. data/lib/sass/environment.rb +58 -148
  19. data/lib/sass/error.rb +14 -11
  20. data/lib/sass/exec.rb +703 -5
  21. data/lib/sass/importers/base.rb +6 -49
  22. data/lib/sass/importers/filesystem.rb +19 -44
  23. data/lib/sass/logger.rb +4 -1
  24. data/lib/sass/logger/base.rb +4 -2
  25. data/lib/sass/logger/log_level.rb +7 -3
  26. data/lib/sass/media.rb +23 -20
  27. data/lib/sass/plugin.rb +7 -7
  28. data/lib/sass/plugin/compiler.rb +145 -304
  29. data/lib/sass/plugin/configuration.rb +23 -18
  30. data/lib/sass/plugin/merb.rb +1 -1
  31. data/lib/sass/plugin/staleness_checker.rb +3 -3
  32. data/lib/sass/repl.rb +3 -3
  33. data/lib/sass/script.rb +8 -35
  34. data/lib/sass/script/{value/arg_list.rb → arg_list.rb} +25 -9
  35. data/lib/sass/script/bool.rb +18 -0
  36. data/lib/sass/script/color.rb +606 -0
  37. data/lib/sass/script/css_lexer.rb +4 -8
  38. data/lib/sass/script/css_parser.rb +2 -5
  39. data/lib/sass/script/funcall.rb +245 -0
  40. data/lib/sass/script/functions.rb +408 -1491
  41. data/lib/sass/script/interpolation.rb +79 -0
  42. data/lib/sass/script/lexer.rb +68 -172
  43. data/lib/sass/script/list.rb +85 -0
  44. data/lib/sass/script/literal.rb +221 -0
  45. data/lib/sass/script/{tree/node.rb → node.rb} +12 -22
  46. data/lib/sass/script/{value/null.rb → null.rb} +7 -14
  47. data/lib/sass/script/{value/number.rb → number.rb} +75 -152
  48. data/lib/sass/script/{tree/operation.rb → operation.rb} +24 -17
  49. data/lib/sass/script/parser.rb +110 -245
  50. data/lib/sass/script/string.rb +51 -0
  51. data/lib/sass/script/{tree/string_interpolation.rb → string_interpolation.rb} +4 -5
  52. data/lib/sass/script/{tree/unary_operation.rb → unary_operation.rb} +6 -6
  53. data/lib/sass/script/variable.rb +58 -0
  54. data/lib/sass/scss/css_parser.rb +3 -9
  55. data/lib/sass/scss/parser.rb +421 -450
  56. data/lib/sass/scss/rx.rb +11 -19
  57. data/lib/sass/scss/static_parser.rb +7 -321
  58. data/lib/sass/selector.rb +194 -68
  59. data/lib/sass/selector/abstract_sequence.rb +14 -29
  60. data/lib/sass/selector/comma_sequence.rb +25 -108
  61. data/lib/sass/selector/sequence.rb +66 -159
  62. data/lib/sass/selector/simple.rb +25 -23
  63. data/lib/sass/selector/simple_sequence.rb +63 -173
  64. data/lib/sass/shared.rb +1 -1
  65. data/lib/sass/supports.rb +15 -13
  66. data/lib/sass/tree/charset_node.rb +1 -1
  67. data/lib/sass/tree/comment_node.rb +3 -3
  68. data/lib/sass/tree/css_import_node.rb +11 -11
  69. data/lib/sass/tree/debug_node.rb +2 -2
  70. data/lib/sass/tree/directive_node.rb +4 -21
  71. data/lib/sass/tree/each_node.rb +8 -8
  72. data/lib/sass/tree/extend_node.rb +7 -14
  73. data/lib/sass/tree/for_node.rb +4 -4
  74. data/lib/sass/tree/function_node.rb +4 -9
  75. data/lib/sass/tree/if_node.rb +1 -1
  76. data/lib/sass/tree/import_node.rb +5 -4
  77. data/lib/sass/tree/media_node.rb +14 -4
  78. data/lib/sass/tree/mixin_def_node.rb +4 -4
  79. data/lib/sass/tree/mixin_node.rb +8 -21
  80. data/lib/sass/tree/node.rb +12 -54
  81. data/lib/sass/tree/prop_node.rb +20 -39
  82. data/lib/sass/tree/return_node.rb +2 -3
  83. data/lib/sass/tree/root_node.rb +3 -19
  84. data/lib/sass/tree/rule_node.rb +22 -35
  85. data/lib/sass/tree/supports_node.rb +13 -0
  86. data/lib/sass/tree/trace_node.rb +1 -2
  87. data/lib/sass/tree/variable_node.rb +3 -9
  88. data/lib/sass/tree/visitors/base.rb +8 -5
  89. data/lib/sass/tree/visitors/check_nesting.rb +19 -49
  90. data/lib/sass/tree/visitors/convert.rb +56 -74
  91. data/lib/sass/tree/visitors/cssize.rb +74 -202
  92. data/lib/sass/tree/visitors/deep_copy.rb +5 -10
  93. data/lib/sass/tree/visitors/extend.rb +7 -7
  94. data/lib/sass/tree/visitors/perform.rb +185 -278
  95. data/lib/sass/tree/visitors/set_options.rb +6 -20
  96. data/lib/sass/tree/visitors/to_css.rb +81 -234
  97. data/lib/sass/tree/warn_node.rb +2 -2
  98. data/lib/sass/tree/while_node.rb +2 -2
  99. data/lib/sass/util.rb +152 -522
  100. data/lib/sass/util/multibyte_string_scanner.rb +0 -2
  101. data/lib/sass/util/subset_map.rb +3 -4
  102. data/lib/sass/util/test.rb +1 -0
  103. data/lib/sass/version.rb +22 -20
  104. data/test/Gemfile +3 -0
  105. data/test/Gemfile.lock +10 -0
  106. data/test/sass/cache_test.rb +20 -62
  107. data/test/sass/callbacks_test.rb +1 -1
  108. data/test/sass/conversion_test.rb +2 -296
  109. data/test/sass/css2sass_test.rb +4 -23
  110. data/test/sass/engine_test.rb +354 -411
  111. data/test/sass/exec_test.rb +2 -2
  112. data/test/sass/extend_test.rb +145 -324
  113. data/test/sass/functions_test.rb +86 -873
  114. data/test/sass/importer_test.rb +21 -241
  115. data/test/sass/logger_test.rb +1 -1
  116. data/test/sass/more_results/more_import.css +1 -1
  117. data/test/sass/plugin_test.rb +26 -16
  118. data/test/sass/results/compact.css +1 -1
  119. data/test/sass/results/complex.css +4 -4
  120. data/test/sass/results/expanded.css +1 -1
  121. data/test/sass/results/import.css +1 -1
  122. data/test/sass/results/import_charset_ibm866.css +2 -2
  123. data/test/sass/results/mixins.css +17 -17
  124. data/test/sass/results/nested.css +1 -1
  125. data/test/sass/results/parent_ref.css +2 -2
  126. data/test/sass/results/script.css +3 -3
  127. data/test/sass/results/scss_import.css +1 -1
  128. data/test/sass/script_conversion_test.rb +7 -36
  129. data/test/sass/script_test.rb +53 -485
  130. data/test/sass/scss/css_test.rb +28 -143
  131. data/test/sass/scss/rx_test.rb +4 -4
  132. data/test/sass/scss/scss_test.rb +325 -2119
  133. data/test/sass/templates/scss_import.scss +1 -2
  134. data/test/sass/test_helper.rb +1 -1
  135. data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
  136. data/test/sass/util/subset_map_test.rb +2 -2
  137. data/test/sass/util_test.rb +1 -86
  138. data/test/test_helper.rb +8 -37
  139. metadata +19 -66
  140. data/lib/sass/exec/base.rb +0 -187
  141. data/lib/sass/exec/sass_convert.rb +0 -264
  142. data/lib/sass/exec/sass_scss.rb +0 -424
  143. data/lib/sass/features.rb +0 -47
  144. data/lib/sass/script/tree.rb +0 -16
  145. data/lib/sass/script/tree/funcall.rb +0 -306
  146. data/lib/sass/script/tree/interpolation.rb +0 -118
  147. data/lib/sass/script/tree/list_literal.rb +0 -77
  148. data/lib/sass/script/tree/literal.rb +0 -45
  149. data/lib/sass/script/tree/map_literal.rb +0 -64
  150. data/lib/sass/script/tree/selector.rb +0 -26
  151. data/lib/sass/script/tree/variable.rb +0 -57
  152. data/lib/sass/script/value.rb +0 -11
  153. data/lib/sass/script/value/base.rb +0 -240
  154. data/lib/sass/script/value/bool.rb +0 -35
  155. data/lib/sass/script/value/color.rb +0 -680
  156. data/lib/sass/script/value/helpers.rb +0 -262
  157. data/lib/sass/script/value/list.rb +0 -113
  158. data/lib/sass/script/value/map.rb +0 -70
  159. data/lib/sass/script/value/string.rb +0 -97
  160. data/lib/sass/selector/pseudo.rb +0 -256
  161. data/lib/sass/source/map.rb +0 -210
  162. data/lib/sass/source/position.rb +0 -39
  163. data/lib/sass/source/range.rb +0 -41
  164. data/lib/sass/stack.rb +0 -120
  165. data/lib/sass/tree/at_root_node.rb +0 -83
  166. data/lib/sass/tree/error_node.rb +0 -18
  167. data/lib/sass/tree/keyframe_rule_node.rb +0 -15
  168. data/lib/sass/util/cross_platform_random.rb +0 -19
  169. data/lib/sass/util/normalized_map.rb +0 -130
  170. data/lib/sass/util/ordered_hash.rb +0 -192
  171. data/test/sass/compiler_test.rb +0 -232
  172. data/test/sass/encoding_test.rb +0 -219
  173. data/test/sass/source_map_test.rb +0 -977
  174. data/test/sass/superselector_test.rb +0 -191
  175. data/test/sass/util/normalized_map_test.rb +0 -51
  176. data/test/sass/value_helpers_test.rb +0 -179
@@ -0,0 +1,79 @@
1
+ module Sass::Script
2
+ # A SassScript object representing `#{}` interpolation outside a string.
3
+ #
4
+ # @see StringInterpolation
5
+ class Interpolation < Node
6
+ # Interpolation in a property is of the form `before #{mid} after`.
7
+ #
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.
17
+ def initialize(before, mid, after, wb, wa, originally_text = false)
18
+ @before = before
19
+ @mid = mid
20
+ @after = after
21
+ @whitespace_before = wb
22
+ @whitespace_after = wa
23
+ @originally_text = originally_text
24
+ end
25
+
26
+ # @return [String] A human-readable s-expression representation of the interpolation
27
+ def inspect
28
+ "(interpolation #{@before.inspect} #{@mid.inspect} #{@after.inspect})"
29
+ end
30
+
31
+ # @see Node#to_sass
32
+ def to_sass(opts = {})
33
+ res = ""
34
+ res << @before.to_sass(opts) if @before
35
+ res << ' ' if @before && @whitespace_before
36
+ res << '#{' unless @originally_text
37
+ res << @mid.to_sass(opts)
38
+ res << '}' unless @originally_text
39
+ res << ' ' if @after && @whitespace_after
40
+ res << @after.to_sass(opts) if @after
41
+ res
42
+ end
43
+
44
+ # Returns the three components of the interpolation, `before`, `mid`, and `after`.
45
+ #
46
+ # @return [Array<Node>]
47
+ # @see #initialize
48
+ # @see Node#children
49
+ def children
50
+ [@before, @mid, @after].compact
51
+ end
52
+
53
+ # @see Node#deep_copy
54
+ def deep_copy
55
+ node = dup
56
+ node.instance_variable_set('@before', @before.deep_copy) if @before
57
+ node.instance_variable_set('@mid', @mid.deep_copy)
58
+ node.instance_variable_set('@after', @after.deep_copy) if @after
59
+ node
60
+ end
61
+
62
+ protected
63
+
64
+ # Evaluates the interpolation.
65
+ #
66
+ # @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
68
+ def _perform(environment)
69
+ res = ""
70
+ res << @before.perform(environment).to_s if @before
71
+ res << " " if @before && @whitespace_before
72
+ val = @mid.perform(environment)
73
+ res << (val.is_a?(Sass::Script::String) ? val.value : val.to_s)
74
+ res << " " if @after && @whitespace_after
75
+ res << @after.perform(environment).to_s if @after
76
+ opts(Sass::Script::String.new(res))
77
+ end
78
+ end
79
+ end
@@ -16,29 +16,26 @@ module Sass
16
16
  # `value`: \[`Object`\]
17
17
  # : The Ruby object corresponding to the value of the token.
18
18
  #
19
- # `source_range`: \[`Sass::Source::Range`\]
20
- # : The range in the source file in which the token appeared.
19
+ # `line`: \[`Fixnum`\]
20
+ # : The line of the source file on which the token appears.
21
+ #
22
+ # `offset`: \[`Fixnum`\]
23
+ # : The number of bytes into the line the SassScript token appeared.
21
24
  #
22
25
  # `pos`: \[`Fixnum`\]
23
26
  # : The scanner position at which the SassScript token appeared.
24
- Token = Struct.new(:type, :value, :source_range, :pos)
27
+ Token = Struct.new(:type, :value, :line, :offset, :pos)
25
28
 
26
29
  # The line number of the lexer's current position.
27
30
  #
28
31
  # @return [Fixnum]
29
- def line
30
- return @line unless @tok
31
- @tok.source_range.start_pos.line
32
- end
32
+ attr_reader :line
33
33
 
34
34
  # The number of bytes into the current line
35
- # of the lexer's current position (1-based).
35
+ # of the lexer's current position.
36
36
  #
37
37
  # @return [Fixnum]
38
- def offset
39
- return @offset unless @tok
40
- @tok.source_range.start_pos.offset
41
- end
38
+ attr_reader :offset
42
39
 
43
40
  # A hash from operator strings to the corresponding token types.
44
41
  OPERATORS = {
@@ -70,9 +67,10 @@ module Sass
70
67
 
71
68
  OPERATORS_REVERSE = Sass::Util.map_hash(OPERATORS) {|k, v| [v, k]}
72
69
 
73
- TOKEN_NAMES = Sass::Util.map_hash(OPERATORS_REVERSE) {|k, v| [k, v.inspect]}.merge(
70
+ TOKEN_NAMES = Sass::Util.map_hash(OPERATORS_REVERSE) {|k, v| [k, v.inspect]}.merge({
74
71
  :const => "variable (e.g. $foo)",
75
- :ident => "identifier (e.g. middle)")
72
+ :ident => "identifier (e.g. middle)"
73
+ })
76
74
 
77
75
  # A list of operator strings ordered with longer names first
78
76
  # so that `>` and `<` don't clobber `>=` and `<=`.
@@ -82,8 +80,6 @@ module Sass
82
80
  # with identifier names.
83
81
  IDENT_OP_NAMES = OP_NAMES.select {|k, v| k =~ /^\w+/}
84
82
 
85
- PARSEABLE_NUMBER = /(?:(\d*\.\d+)|(\d+))(?:[eE]([+-]?\d+))?(#{UNIT})?/
86
-
87
83
  # A hash of regular expressions that are used for tokenizing.
88
84
  REGULAR_EXPRESSIONS = {
89
85
  :whitespace => /\s+/,
@@ -91,22 +87,16 @@ module Sass
91
87
  :single_line_comment => SINGLE_LINE_COMMENT,
92
88
  :variable => /(\$)(#{IDENT})/,
93
89
  :ident => /(#{IDENT})(\()?/,
94
- :number => PARSEABLE_NUMBER,
95
- :unary_minus_number => /-#{PARSEABLE_NUMBER}/,
90
+ :number => /(-)?(?:(\d*\.\d+)|(\d+))([a-zA-Z%]+)?/,
96
91
  :color => HEXCOLOR,
97
- :id => /##{IDENT}/,
98
- :selector => /&/,
99
- :ident_op => /(#{Regexp.union(*IDENT_OP_NAMES.map do |s|
100
- Regexp.new(Regexp.escape(s) + "(?!#{NMCHAR}|\Z)")
101
- end)})/,
102
- :op => /(#{Regexp.union(*OP_NAMES)})/,
92
+ :ident_op => %r{(#{Regexp.union(*IDENT_OP_NAMES.map{|s| Regexp.new(Regexp.escape(s) + "(?!#{NMCHAR}|\Z)")})})},
93
+ :op => %r{(#{Regexp.union(*OP_NAMES)})},
103
94
  }
104
95
 
105
96
  class << self
106
97
  private
107
-
108
98
  def string_re(open, close)
109
- /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/m
99
+ /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/
110
100
  end
111
101
  end
112
102
 
@@ -142,10 +132,10 @@ module Sass
142
132
  }
143
133
 
144
134
  # @param str [String, StringScanner] The source text to lex
145
- # @param line [Fixnum] The 1-based line on which the SassScript appears.
146
- # Used for error reporting and sourcemap building
147
- # @param offset [Fixnum] The 1-based character (not byte) offset in the line in the source.
148
- # Used for error reporting and sourcemap building
135
+ # @param line [Fixnum] The line on which the SassScript appears.
136
+ # Used for error reporting
137
+ # @param offset [Fixnum] The number of characters in on which the SassScript appears.
138
+ # Used for error reporting
149
139
  # @param options [{Symbol => Object}] An options hash;
150
140
  # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
151
141
  def initialize(str, line, offset, options)
@@ -164,7 +154,7 @@ module Sass
164
154
  @tok ||= read_token
165
155
  @tok, tok = nil, @tok
166
156
  @prev = tok
167
- tok
157
+ return tok
168
158
  end
169
159
 
170
160
  # Returns whether or not there's whitespace before the next token.
@@ -189,11 +179,7 @@ module Sass
189
179
  # Rewinds the underlying StringScanner
190
180
  # to before the token returned by \{#peek}.
191
181
  def unpeek!
192
- if @tok
193
- @scanner.pos = @tok.pos
194
- @line = @tok.source_range.start_pos.line
195
- @offset = @tok.source_range.start_pos.offset
196
- end
182
+ @scanner.pos = @tok.pos if @tok
197
183
  end
198
184
 
199
185
  # @return [Boolean] Whether or not there's more source text to lex.
@@ -236,11 +222,13 @@ module Sass
236
222
 
237
223
  def read_token
238
224
  return if done?
239
- start_pos = source_position
240
- value = token
241
- return unless value
242
- type, val = value
243
- Token.new(type, val, range(start_pos), @scanner.pos - @scanner.matched_size)
225
+ return unless value = token
226
+ type, val, size = value
227
+ size ||= @scanner.matched_size
228
+
229
+ val.line = @line if val.is_a?(Script::Node)
230
+ Token.new(type, val, @line,
231
+ current_position - size, @scanner.pos - size)
244
232
  end
245
233
 
246
234
  def whitespace
@@ -250,19 +238,13 @@ module Sass
250
238
  end
251
239
 
252
240
  def token
253
- if after_interpolation? && (interp = @interpolation_stack.pop)
254
- interp_type, interp_value = interp
255
- if interp_type == :special_fun
256
- return special_fun_body(interp_value)
257
- else
258
- raise "[BUG]: Unknown interp_type #{interp_type}" unless interp_type == :string
259
- return string(interp_value, true)
260
- end
241
+ if after_interpolation? && (interp_type = @interpolation_stack.pop)
242
+ return string(interp_type, true)
261
243
  end
262
244
 
263
- variable || string(:double, false) || string(:single, false) || number || id || color ||
264
- selector || string(:uri, false) || raw(UNICODERANGE) || special_fun || special_val ||
265
- ident_op || ident || op
245
+ variable || string(:double, false) || string(:single, false) || number || color ||
246
+ string(:uri, false) || raw(UNICODERANGE) || special_fun || special_val || ident_op ||
247
+ ident || op
266
248
  end
267
249
 
268
250
  def variable
@@ -281,168 +263,82 @@ module Sass
281
263
  end
282
264
 
283
265
  def string(re, open)
284
- line, offset = @line, @offset
285
266
  return unless scan(STRING_REGULAR_EXPRESSIONS[re][open])
286
- if @scanner[0] =~ /([^\\]|^)\n/
287
- filename = @options[:filename]
288
- Sass::Util.sass_warn <<MESSAGE
289
- DEPRECATION WARNING on line #{line}, column #{offset}#{" of #{filename}" if filename}:
290
- Unescaped multiline strings are deprecated and will be removed in a future version of Sass.
291
- To include a newline in a string, use "\\a" or "\\a " as in CSS.
292
- MESSAGE
293
- end
294
-
295
- if @scanner[2] == '#{' # '
267
+ if @scanner[2] == '#{' #'
296
268
  @scanner.pos -= 2 # Don't actually consume the #{
297
- @offset -= 2
298
- @interpolation_stack << [:string, re]
269
+ @interpolation_stack << re
299
270
  end
300
271
  str =
301
272
  if re == :uri
302
- url = "#{'url(' unless open}#{@scanner[1]}#{')' unless @scanner[2] == '#{'}"
303
- Script::Value::String.new(url)
273
+ Script::String.new("#{'url(' unless open}#{@scanner[1]}#{')' unless @scanner[2] == '#{'}")
304
274
  else
305
- Script::Value::String.new(Sass::Script::Value::String.value(@scanner[1]), :string)
275
+ Script::String.new(@scanner[1].gsub(/\\(['"]|\#\{)/, '\1'), :string)
306
276
  end
307
277
  [:string, str]
308
278
  end
309
279
 
310
280
  def number
311
- # Handling unary minus is complicated by the fact that whitespace is an
312
- # operator in SassScript. We want "1-2" to be parsed as "1 - 2", but we
313
- # want "1 -2" to be parsed as "1 (-2)". To accomplish this, we only
314
- # parse a unary minus as part of a number literal if there's whitespace
315
- # before and not after it. Cases like "(-2)" are handled by the unary
316
- # minus logic in the parser instead.
317
- if @scanner.peek(1) == '-'
318
- return if @scanner.pos == 0
319
- unary_minus_allowed =
320
- case @scanner.string[@scanner.pos - 1, 1]
321
- when /\s/; true
322
- when '/'; @scanner.pos != 1 && @scanner.string[@scanner.pos - 2, 1] == '*'
323
- else; false
324
- end
325
-
326
- return unless unary_minus_allowed
327
- return unless scan(REGULAR_EXPRESSIONS[:unary_minus_number])
328
- minus = true
329
- else
330
- return unless scan(REGULAR_EXPRESSIONS[:number])
331
- minus = false
332
- end
333
-
334
- value = (@scanner[1] ? @scanner[1].to_f : @scanner[2].to_i) * (minus ? -1 : 1)
335
- value *= 10**@scanner[3].to_i if @scanner[3]
336
- script_number = Script::Value::Number.new(value, Array(@scanner[4]))
337
- [:number, script_number]
338
- end
339
-
340
- def id
341
- # Colors and ids are tough to tell apart, because they overlap but
342
- # neither is a superset of the other. "#xyz" is an id but not a color,
343
- # "#000" is a color but not an id, "#abc" is both, and "#0" is neither.
344
- # We need to handle all these cases correctly.
345
- #
346
- # To do so, we first try to parse something as an id. If this works and
347
- # the id is also a valid color, we return the color. Otherwise, we
348
- # return the id. If it didn't parse as an id, we then try to parse it as
349
- # a color. If *this* works, we return the color, and if it doesn't we
350
- # give up and throw an error.
351
- #
352
- # IDs in properties are used in the Basic User Interface Module
353
- # (http://www.w3.org/TR/css3-ui/).
354
- return unless scan(REGULAR_EXPRESSIONS[:id])
355
- if @scanner[0] =~ /^\#[0-9a-fA-F]+$/ && (@scanner[0].length == 4 || @scanner[0].length == 7)
356
- return [:color, Script::Value::Color.from_hex(@scanner[0])]
357
- end
358
- [:ident, @scanner[0]]
281
+ return unless scan(REGULAR_EXPRESSIONS[:number])
282
+ value = @scanner[2] ? @scanner[2].to_f : @scanner[3].to_i
283
+ value = -value if @scanner[1]
284
+ [:number, Script::Number.new(value, Array(@scanner[4]))]
359
285
  end
360
286
 
361
287
  def color
362
- return unless @scanner.match?(REGULAR_EXPRESSIONS[:color])
363
- return unless @scanner[0].length == 4 || @scanner[0].length == 7
364
- script_color = Script::Value::Color.from_hex(scan(REGULAR_EXPRESSIONS[:color]))
365
- [:color, script_color]
366
- end
367
-
368
- def selector
369
- start_pos = source_position
370
- return unless scan(REGULAR_EXPRESSIONS[:selector])
371
- script_selector = Script::Tree::Selector.new
372
- script_selector.source_range = range(start_pos)
373
- [:selector, script_selector]
288
+ return unless s = scan(REGULAR_EXPRESSIONS[:color])
289
+ raise Sass::SyntaxError.new(<<MESSAGE.rstrip) unless s.size == 4 || s.size == 7
290
+ Colors must have either three or six digits: '#{s}'
291
+ MESSAGE
292
+ value = s.scan(/^#(..?)(..?)(..?)$/).first.
293
+ map {|num| num.ljust(2, num).to_i(16)}
294
+ [:color, Script::Color.new(value)]
374
295
  end
375
296
 
376
297
  def special_fun
377
- prefix = scan(/((-[\w-]+-)?(calc|element)|expression|progid:[a-z\.]*)\(/i)
378
- return unless prefix
379
- special_fun_body(1, prefix)
380
- end
381
-
382
- def special_fun_body(parens, prefix = nil)
383
- str = prefix || ''
384
- while (scanned = scan(/.*?([()]|\#\{)/m))
385
- str << scanned
386
- if scanned[-1] == ?(
387
- parens += 1
388
- next
389
- elsif scanned[-1] == ?)
390
- parens -= 1
391
- next unless parens == 0
392
- else
393
- raise "[BUG] Unreachable" unless @scanner[1] == '#{' # '
394
- str.slice!(-2..-1)
395
- @scanner.pos -= 2 # Don't actually consume the #{
396
- @offset -= 2
397
- @interpolation_stack << [:special_fun, parens]
398
- end
399
-
400
- return [:special_fun, Sass::Script::Value::String.new(str)]
401
- end
402
-
403
- scan(/.*/)
404
- expected!('")"')
298
+ return unless str1 = scan(/((-[\w-]+-)?(calc|element)|expression|progid:[a-z\.]*)\(/i)
299
+ str2, _ = Sass::Shared.balance(@scanner, ?(, ?), 1)
300
+ c = str2.count("\n")
301
+ old_line = @line
302
+ old_offset = @offset
303
+ @line += c
304
+ @offset = (c == 0 ? @offset + str2.size : str2[/\n(.*)/, 1].size)
305
+ [:special_fun,
306
+ Sass::Util.merge_adjacent_strings(
307
+ [str1] + Sass::Engine.parse_interp(str2, old_line, old_offset, @options)),
308
+ str1.size + str2.size]
405
309
  end
406
310
 
407
311
  def special_val
408
312
  return unless scan(/!important/i)
409
- [:string, Script::Value::String.new("!important")]
313
+ [:string, Script::String.new("!important")]
410
314
  end
411
315
 
412
316
  def ident_op
413
- op = scan(REGULAR_EXPRESSIONS[:ident_op])
414
- return unless op
317
+ return unless op = scan(REGULAR_EXPRESSIONS[:ident_op])
415
318
  [OPERATORS[op]]
416
319
  end
417
320
 
418
321
  def op
419
- op = scan(REGULAR_EXPRESSIONS[:op])
420
- return unless op
322
+ return unless op = scan(REGULAR_EXPRESSIONS[:op])
421
323
  @interpolation_stack << nil if op == :begin_interpolation
422
324
  [OPERATORS[op]]
423
325
  end
424
326
 
425
327
  def raw(rx)
426
- val = scan(rx)
427
- return unless val
328
+ return unless val = scan(rx)
428
329
  [:raw, val]
429
330
  end
430
331
 
431
332
  def scan(re)
432
- str = @scanner.scan(re)
433
- return unless str
333
+ return unless str = @scanner.scan(re)
434
334
  c = str.count("\n")
435
335
  @line += c
436
- @offset = (c == 0 ? @offset + str.size : str.size - str.rindex("\n"))
336
+ @offset = (c == 0 ? @offset + str.size : str[/\n(.*)/, 1].size)
437
337
  str
438
338
  end
439
339
 
440
- def range(start_pos, end_pos = source_position)
441
- Sass::Source::Range.new(start_pos, end_pos, @options[:filename], @options[:importer])
442
- end
443
-
444
- def source_position
445
- Sass::Source::Position.new(@line, @offset)
340
+ def current_position
341
+ @offset + 1
446
342
  end
447
343
  end
448
344
  end
@@ -0,0 +1,85 @@
1
+ module Sass::Script
2
+ # A SassScript object representing a CSS list.
3
+ # This includes both comma-separated lists and space-separated lists.
4
+ class List < Literal
5
+ # The Ruby array containing the contents of the list.
6
+ #
7
+ # @return [Array<Literal>]
8
+ attr_reader :value
9
+ alias_method :children, :value
10
+ alias_method :to_a, :value
11
+
12
+ # The operator separating the values of the list.
13
+ # Either `:comma` or `:space`.
14
+ #
15
+ # @return [Symbol]
16
+ attr_reader :separator
17
+
18
+ # Creates a new list.
19
+ #
20
+ # @param value [Array<Literal>] See \{#value}
21
+ # @param separator [String] See \{#separator}
22
+ def initialize(value, separator)
23
+ super(value)
24
+ @separator = separator
25
+ end
26
+
27
+ # @see Node#deep_copy
28
+ def deep_copy
29
+ node = dup
30
+ node.instance_variable_set('@value', value.map {|c| c.deep_copy})
31
+ node
32
+ end
33
+
34
+ # @see Node#eq
35
+ def eq(other)
36
+ Sass::Script::Bool.new(
37
+ other.is_a?(List) && self.value == other.value &&
38
+ self.separator == other.separator)
39
+ end
40
+
41
+ # @see Node#to_s
42
+ def to_s(opts = {})
43
+ raise Sass::SyntaxError.new("() isn't a valid CSS value.") if value.empty?
44
+ return value.reject {|e| e.is_a?(Null) || e.is_a?(List) && e.value.empty?}.map {|e| e.to_s(opts)}.join(sep_str)
45
+ end
46
+
47
+ # @see Node#to_sass
48
+ def to_sass(opts = {})
49
+ return "()" if value.empty?
50
+ precedence = Sass::Script::Parser.precedence_of(separator)
51
+ value.reject {|e| e.is_a?(Null)}.map do |v|
52
+ if v.is_a?(List) && Sass::Script::Parser.precedence_of(v.separator) <= precedence ||
53
+ separator == :space && v.is_a?(UnaryOperation) && (v.operator == :minus || v.operator == :plus)
54
+ "(#{v.to_sass(opts)})"
55
+ else
56
+ v.to_sass(opts)
57
+ end
58
+ end.join(sep_str(nil))
59
+ end
60
+
61
+ # @see Node#inspect
62
+ def inspect
63
+ "(#{to_sass})"
64
+ end
65
+
66
+ protected
67
+
68
+ # @see Node#_perform
69
+ def _perform(environment)
70
+ list = Sass::Script::List.new(
71
+ value.map {|e| e.perform(environment)},
72
+ separator)
73
+ list.options = self.options
74
+ list
75
+ end
76
+
77
+ private
78
+
79
+ def sep_str(opts = self.options)
80
+ return ' ' if separator == :space
81
+ return ',' if opts && opts[:style] == :compressed
82
+ return ', '
83
+ end
84
+ end
85
+ end