sass 3.3.0 → 3.4.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -1
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/CONTRIBUTING.md +148 -0
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +76 -62
  7. data/Rakefile +104 -24
  8. data/VERSION +1 -1
  9. data/VERSION_DATE +1 -1
  10. data/VERSION_NAME +1 -1
  11. data/bin/sass +1 -1
  12. data/bin/scss +1 -1
  13. data/extra/sass-spec-ref.sh +32 -0
  14. data/extra/update_watch.rb +1 -1
  15. data/lib/sass/cache_stores/filesystem.rb +9 -5
  16. data/lib/sass/cache_stores/memory.rb +4 -5
  17. data/lib/sass/callbacks.rb +2 -2
  18. data/lib/sass/css.rb +12 -13
  19. data/lib/sass/deprecation.rb +55 -0
  20. data/lib/sass/engine.rb +106 -70
  21. data/lib/sass/environment.rb +39 -19
  22. data/lib/sass/error.rb +17 -20
  23. data/lib/sass/exec/base.rb +199 -0
  24. data/lib/sass/exec/sass_convert.rb +283 -0
  25. data/lib/sass/exec/sass_scss.rb +440 -0
  26. data/lib/sass/exec.rb +5 -771
  27. data/lib/sass/features.rb +9 -2
  28. data/lib/sass/importers/base.rb +8 -3
  29. data/lib/sass/importers/filesystem.rb +30 -38
  30. data/lib/sass/logger/base.rb +8 -2
  31. data/lib/sass/logger/delayed.rb +50 -0
  32. data/lib/sass/logger.rb +8 -3
  33. data/lib/sass/media.rb +1 -4
  34. data/lib/sass/plugin/compiler.rb +224 -90
  35. data/lib/sass/plugin/configuration.rb +38 -22
  36. data/lib/sass/plugin/merb.rb +2 -2
  37. data/lib/sass/plugin/rack.rb +3 -3
  38. data/lib/sass/plugin/rails.rb +1 -1
  39. data/lib/sass/plugin/staleness_checker.rb +4 -4
  40. data/lib/sass/plugin.rb +6 -5
  41. data/lib/sass/script/css_lexer.rb +1 -1
  42. data/lib/sass/script/css_parser.rb +2 -3
  43. data/lib/sass/script/css_variable_warning.rb +52 -0
  44. data/lib/sass/script/functions.rb +739 -318
  45. data/lib/sass/script/lexer.rb +134 -54
  46. data/lib/sass/script/parser.rb +252 -56
  47. data/lib/sass/script/tree/funcall.rb +13 -6
  48. data/lib/sass/script/tree/interpolation.rb +127 -4
  49. data/lib/sass/script/tree/list_literal.rb +31 -4
  50. data/lib/sass/script/tree/literal.rb +4 -0
  51. data/lib/sass/script/tree/node.rb +21 -3
  52. data/lib/sass/script/tree/operation.rb +54 -1
  53. data/lib/sass/script/tree/selector.rb +26 -0
  54. data/lib/sass/script/tree/string_interpolation.rb +59 -38
  55. data/lib/sass/script/tree/variable.rb +1 -1
  56. data/lib/sass/script/tree.rb +1 -0
  57. data/lib/sass/script/value/base.rb +17 -14
  58. data/lib/sass/script/value/bool.rb +0 -5
  59. data/lib/sass/script/value/color.rb +78 -42
  60. data/lib/sass/script/value/helpers.rb +119 -2
  61. data/lib/sass/script/value/list.rb +0 -15
  62. data/lib/sass/script/value/map.rb +1 -1
  63. data/lib/sass/script/value/null.rb +0 -5
  64. data/lib/sass/script/value/number.rb +112 -31
  65. data/lib/sass/script/value/string.rb +102 -13
  66. data/lib/sass/script/value.rb +0 -1
  67. data/lib/sass/script.rb +3 -3
  68. data/lib/sass/scss/css_parser.rb +24 -4
  69. data/lib/sass/scss/parser.rb +290 -383
  70. data/lib/sass/scss/rx.rb +17 -9
  71. data/lib/sass/scss/static_parser.rb +306 -4
  72. data/lib/sass/scss.rb +0 -2
  73. data/lib/sass/selector/abstract_sequence.rb +35 -18
  74. data/lib/sass/selector/comma_sequence.rb +114 -19
  75. data/lib/sass/selector/pseudo.rb +266 -0
  76. data/lib/sass/selector/sequence.rb +146 -40
  77. data/lib/sass/selector/simple.rb +22 -33
  78. data/lib/sass/selector/simple_sequence.rb +122 -39
  79. data/lib/sass/selector.rb +57 -197
  80. data/lib/sass/shared.rb +2 -2
  81. data/lib/sass/source/map.rb +31 -14
  82. data/lib/sass/source/position.rb +4 -4
  83. data/lib/sass/stack.rb +2 -8
  84. data/lib/sass/supports.rb +10 -13
  85. data/lib/sass/tree/at_root_node.rb +1 -0
  86. data/lib/sass/tree/charset_node.rb +1 -1
  87. data/lib/sass/tree/comment_node.rb +1 -1
  88. data/lib/sass/tree/css_import_node.rb +9 -1
  89. data/lib/sass/tree/directive_node.rb +8 -2
  90. data/lib/sass/tree/error_node.rb +18 -0
  91. data/lib/sass/tree/extend_node.rb +1 -1
  92. data/lib/sass/tree/function_node.rb +9 -0
  93. data/lib/sass/tree/import_node.rb +6 -5
  94. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  95. data/lib/sass/tree/node.rb +5 -3
  96. data/lib/sass/tree/prop_node.rb +6 -7
  97. data/lib/sass/tree/rule_node.rb +26 -11
  98. data/lib/sass/tree/visitors/check_nesting.rb +56 -32
  99. data/lib/sass/tree/visitors/convert.rb +59 -44
  100. data/lib/sass/tree/visitors/cssize.rb +34 -30
  101. data/lib/sass/tree/visitors/deep_copy.rb +6 -1
  102. data/lib/sass/tree/visitors/extend.rb +15 -13
  103. data/lib/sass/tree/visitors/perform.rb +87 -50
  104. data/lib/sass/tree/visitors/set_options.rb +15 -1
  105. data/lib/sass/tree/visitors/to_css.rb +72 -43
  106. data/lib/sass/util/multibyte_string_scanner.rb +0 -2
  107. data/lib/sass/util/normalized_map.rb +0 -1
  108. data/lib/sass/util/subset_map.rb +2 -3
  109. data/lib/sass/util.rb +334 -154
  110. data/lib/sass/version.rb +7 -7
  111. data/lib/sass.rb +10 -8
  112. data/test/sass/cache_test.rb +62 -20
  113. data/test/sass/callbacks_test.rb +1 -1
  114. data/test/sass/compiler_test.rb +24 -11
  115. data/test/sass/conversion_test.rb +241 -50
  116. data/test/sass/css2sass_test.rb +73 -5
  117. data/test/sass/css_variable_test.rb +132 -0
  118. data/test/sass/encoding_test.rb +219 -0
  119. data/test/sass/engine_test.rb +343 -260
  120. data/test/sass/exec_test.rb +12 -2
  121. data/test/sass/extend_test.rb +333 -44
  122. data/test/sass/functions_test.rb +353 -260
  123. data/test/sass/importer_test.rb +40 -21
  124. data/test/sass/logger_test.rb +1 -1
  125. data/test/sass/more_results/more_import.css +1 -1
  126. data/test/sass/more_templates/more1.sass +10 -10
  127. data/test/sass/more_templates/more_import.sass +2 -2
  128. data/test/sass/plugin_test.rb +24 -21
  129. data/test/sass/results/compact.css +1 -1
  130. data/test/sass/results/complex.css +4 -4
  131. data/test/sass/results/expanded.css +1 -1
  132. data/test/sass/results/import.css +1 -1
  133. data/test/sass/results/import_charset_ibm866.css +2 -2
  134. data/test/sass/results/mixins.css +17 -17
  135. data/test/sass/results/nested.css +1 -1
  136. data/test/sass/results/parent_ref.css +2 -2
  137. data/test/sass/results/script.css +5 -5
  138. data/test/sass/results/scss_import.css +1 -1
  139. data/test/sass/script_conversion_test.rb +71 -39
  140. data/test/sass/script_test.rb +714 -123
  141. data/test/sass/scss/css_test.rb +213 -30
  142. data/test/sass/scss/rx_test.rb +8 -4
  143. data/test/sass/scss/scss_test.rb +766 -22
  144. data/test/sass/source_map_test.rb +263 -95
  145. data/test/sass/superselector_test.rb +210 -0
  146. data/test/sass/templates/_partial.sass +1 -1
  147. data/test/sass/templates/basic.sass +10 -10
  148. data/test/sass/templates/bork1.sass +1 -1
  149. data/test/sass/templates/bork5.sass +1 -1
  150. data/test/sass/templates/compact.sass +10 -10
  151. data/test/sass/templates/complex.sass +187 -187
  152. data/test/sass/templates/compressed.sass +10 -10
  153. data/test/sass/templates/expanded.sass +10 -10
  154. data/test/sass/templates/import.sass +2 -2
  155. data/test/sass/templates/importee.sass +3 -3
  156. data/test/sass/templates/mixins.sass +22 -22
  157. data/test/sass/templates/multiline.sass +4 -4
  158. data/test/sass/templates/nested.sass +13 -13
  159. data/test/sass/templates/parent_ref.sass +12 -12
  160. data/test/sass/templates/script.sass +70 -70
  161. data/test/sass/templates/scss_import.scss +2 -1
  162. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +1 -1
  163. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +2 -2
  164. data/test/sass/templates/subdir/subdir.sass +3 -3
  165. data/test/sass/templates/units.sass +10 -10
  166. data/test/sass/test_helper.rb +1 -1
  167. data/test/sass/util/multibyte_string_scanner_test.rb +11 -3
  168. data/test/sass/util/normalized_map_test.rb +1 -1
  169. data/test/sass/util/subset_map_test.rb +2 -2
  170. data/test/sass/util_test.rb +46 -45
  171. data/test/sass/value_helpers_test.rb +5 -7
  172. data/test/sass-spec.yml +3 -0
  173. data/test/test_helper.rb +7 -6
  174. data/vendor/listen/CHANGELOG.md +1 -228
  175. data/vendor/listen/Gemfile +5 -15
  176. data/vendor/listen/README.md +111 -77
  177. data/vendor/listen/Rakefile +0 -42
  178. data/vendor/listen/lib/listen/adapter.rb +195 -82
  179. data/vendor/listen/lib/listen/adapters/bsd.rb +27 -64
  180. data/vendor/listen/lib/listen/adapters/darwin.rb +21 -58
  181. data/vendor/listen/lib/listen/adapters/linux.rb +23 -55
  182. data/vendor/listen/lib/listen/adapters/polling.rb +25 -34
  183. data/vendor/listen/lib/listen/adapters/windows.rb +50 -46
  184. data/vendor/listen/lib/listen/directory_record.rb +96 -61
  185. data/vendor/listen/lib/listen/listener.rb +135 -37
  186. data/vendor/listen/lib/listen/turnstile.rb +9 -5
  187. data/vendor/listen/lib/listen/version.rb +1 -1
  188. data/vendor/listen/lib/listen.rb +33 -19
  189. data/vendor/listen/listen.gemspec +6 -0
  190. data/vendor/listen/spec/listen/adapter_spec.rb +43 -77
  191. data/vendor/listen/spec/listen/adapters/polling_spec.rb +8 -8
  192. data/vendor/listen/spec/listen/directory_record_spec.rb +81 -56
  193. data/vendor/listen/spec/listen/listener_spec.rb +128 -39
  194. data/vendor/listen/spec/listen_spec.rb +15 -21
  195. data/vendor/listen/spec/spec_helper.rb +4 -0
  196. data/vendor/listen/spec/support/adapter_helper.rb +52 -15
  197. data/vendor/listen/spec/support/directory_record_helper.rb +7 -5
  198. data/vendor/listen/spec/support/listeners_helper.rb +30 -7
  199. metadata +310 -300
  200. data/CONTRIBUTING +0 -3
  201. data/ext/mkrf_conf.rb +0 -27
  202. data/lib/sass/script/value/deprecated_false.rb +0 -55
  203. data/lib/sass/scss/script_lexer.rb +0 -15
  204. data/lib/sass/scss/script_parser.rb +0 -25
  205. data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
  206. data/vendor/listen/lib/listen/multi_listener.rb +0 -143
  207. data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
  208. data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
@@ -19,13 +19,13 @@ module Sass
19
19
  # `source_range`: \[`Sass::Source::Range`\]
20
20
  # : The range in the source file in which the token appeared.
21
21
  #
22
- # `pos`: \[`Fixnum`\]
22
+ # `pos`: \[`Integer`\]
23
23
  # : The scanner position at which the SassScript token appeared.
24
24
  Token = Struct.new(:type, :value, :source_range, :pos)
25
25
 
26
26
  # The line number of the lexer's current position.
27
27
  #
28
- # @return [Fixnum]
28
+ # @return [Integer]
29
29
  def line
30
30
  return @line unless @tok
31
31
  @tok.source_range.start_pos.line
@@ -34,7 +34,7 @@ module Sass
34
34
  # The number of bytes into the current line
35
35
  # of the lexer's current position (1-based).
36
36
  #
37
- # @return [Fixnum]
37
+ # @return [Integer]
38
38
  def offset
39
39
  return @offset unless @tok
40
40
  @tok.source_range.start_pos.offset
@@ -71,8 +71,8 @@ module Sass
71
71
  OPERATORS_REVERSE = Sass::Util.map_hash(OPERATORS) {|k, v| [v, k]}
72
72
 
73
73
  TOKEN_NAMES = Sass::Util.map_hash(OPERATORS_REVERSE) {|k, v| [k, v.inspect]}.merge(
74
- :const => "variable (e.g. $foo)",
75
- :ident => "identifier (e.g. middle)")
74
+ :const => "variable (e.g. $foo)",
75
+ :ident => "identifier (e.g. middle)")
76
76
 
77
77
  # A list of operator strings ordered with longer names first
78
78
  # so that `>` and `<` don't clobber `>=` and `<=`.
@@ -80,7 +80,9 @@ module Sass
80
80
 
81
81
  # A sub-list of {OP_NAMES} that only includes operators
82
82
  # with identifier names.
83
- IDENT_OP_NAMES = OP_NAMES.select {|k, v| k =~ /^\w+/}
83
+ IDENT_OP_NAMES = OP_NAMES.select {|k, _v| k =~ /^\w+/}
84
+
85
+ PARSEABLE_NUMBER = /(?:(\d*\.\d+)|(\d+))(?:[eE]([+-]?\d+))?(#{UNIT})?/
84
86
 
85
87
  # A hash of regular expressions that are used for tokenizing.
86
88
  REGULAR_EXPRESSIONS = {
@@ -89,9 +91,11 @@ module Sass
89
91
  :single_line_comment => SINGLE_LINE_COMMENT,
90
92
  :variable => /(\$)(#{IDENT})/,
91
93
  :ident => /(#{IDENT})(\()?/,
92
- :number => /(?:(\d*\.\d+)|(\d+))([a-zA-Z%]+)?/,
93
- :unary_minus_number => /-(?:(\d*\.\d+)|(\d+))([a-zA-Z%]+)?/,
94
+ :number => PARSEABLE_NUMBER,
95
+ :unary_minus_number => /-#{PARSEABLE_NUMBER}/,
94
96
  :color => HEXCOLOR,
97
+ :id => /##{IDENT}/,
98
+ :selector => /&/,
95
99
  :ident_op => /(#{Regexp.union(*IDENT_OP_NAMES.map do |s|
96
100
  Regexp.new(Regexp.escape(s) + "(?!#{NMCHAR}|\Z)")
97
101
  end)})/,
@@ -102,7 +106,7 @@ module Sass
102
106
  private
103
107
 
104
108
  def string_re(open, close)
105
- /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/
109
+ /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/m
106
110
  end
107
111
  end
108
112
 
@@ -138,12 +142,12 @@ module Sass
138
142
  }
139
143
 
140
144
  # @param str [String, StringScanner] The source text to lex
141
- # @param line [Fixnum] The 1-based line on which the SassScript appears.
145
+ # @param line [Integer] The 1-based line on which the SassScript appears.
142
146
  # Used for error reporting and sourcemap building
143
- # @param offset [Fixnum] The 1-based character (not byte) offset in the line in the source.
147
+ # @param offset [Integer] The 1-based character (not byte) offset in the line in the source.
144
148
  # Used for error reporting and sourcemap building
145
149
  # @param options [{Symbol => Object}] An options hash;
146
- # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
150
+ # see {file:SASS_REFERENCE.md#Options the Sass options documentation}
147
151
  def initialize(str, line, offset, options)
148
152
  @scanner = str.is_a?(StringScanner) ? str : Sass::Util::MultibyteStringScanner.new(str)
149
153
  @line = line
@@ -151,6 +155,8 @@ module Sass
151
155
  @options = options
152
156
  @interpolation_stack = []
153
157
  @prev = nil
158
+ @tok = nil
159
+ @next_tok = nil
154
160
  end
155
161
 
156
162
  # Moves the lexer forward one token.
@@ -175,6 +181,13 @@ module Sass
175
181
  end
176
182
  end
177
183
 
184
+ # Returns the given character.
185
+ #
186
+ # @return [String]
187
+ def char(pos = @scanner.pos)
188
+ @scanner.string[pos, 1]
189
+ end
190
+
178
191
  # Returns the next token without moving the lexer forward.
179
192
  #
180
193
  # @return [Token] The next token
@@ -185,16 +198,16 @@ module Sass
185
198
  # Rewinds the underlying StringScanner
186
199
  # to before the token returned by \{#peek}.
187
200
  def unpeek!
188
- if @tok
189
- @scanner.pos = @tok.pos
190
- @line = @tok.source_range.start_pos.line
191
- @offset = @tok.source_range.start_pos.offset
192
- end
201
+ return unless @tok
202
+ @scanner.pos = @tok.pos
203
+ @line = @tok.source_range.start_pos.line
204
+ @offset = @tok.source_range.start_pos.offset
193
205
  end
194
206
 
195
207
  # @return [Boolean] Whether or not there's more source text to lex.
196
208
  def done?
197
- whitespace unless after_interpolation? && @interpolation_stack.last
209
+ return if @next_tok
210
+ whitespace unless after_interpolation? && !@interpolation_stack.empty?
198
211
  @scanner.eos? && @tok.nil?
199
212
  end
200
213
 
@@ -231,6 +244,11 @@ module Sass
231
244
  private
232
245
 
233
246
  def read_token
247
+ if (tok = @next_tok)
248
+ @next_tok = nil
249
+ return tok
250
+ end
251
+
234
252
  return if done?
235
253
  start_pos = source_position
236
254
  value = token
@@ -246,13 +264,19 @@ module Sass
246
264
  end
247
265
 
248
266
  def token
249
- if after_interpolation? && (interp_type = @interpolation_stack.pop)
250
- return string(interp_type, true)
267
+ if after_interpolation? && (interp = @interpolation_stack.pop)
268
+ interp_type, interp_value = interp
269
+ if interp_type == :special_fun
270
+ return special_fun_body(interp_value)
271
+ else
272
+ raise "[BUG]: Unknown interp_type #{interp_type}" unless interp_type == :string
273
+ return string(interp_value, true)
274
+ end
251
275
  end
252
276
 
253
- variable || string(:double, false) || string(:single, false) || number || color ||
254
- string(:uri, false) || raw(UNICODERANGE) || special_fun || special_val || ident_op ||
255
- ident || op
277
+ variable || string(:double, false) || string(:single, false) || number || id || color ||
278
+ selector || string(:uri, false) || raw(UNICODERANGE) || special_fun || special_val ||
279
+ ident_op || ident || op
256
280
  end
257
281
 
258
282
  def variable
@@ -271,18 +295,28 @@ module Sass
271
295
  end
272
296
 
273
297
  def string(re, open)
298
+ line, offset = @line, @offset
274
299
  return unless scan(STRING_REGULAR_EXPRESSIONS[re][open])
300
+ if @scanner[0] =~ /([^\\]|^)\n/
301
+ filename = @options[:filename]
302
+ Sass::Util.sass_warn <<MESSAGE
303
+ DEPRECATION WARNING on line #{line}, column #{offset}#{" of #{filename}" if filename}:
304
+ Unescaped multiline strings are deprecated and will be removed in a future version of Sass.
305
+ To include a newline in a string, use "\\a" or "\\a " as in CSS.
306
+ MESSAGE
307
+ end
308
+
275
309
  if @scanner[2] == '#{' # '
276
- @scanner.pos -= 2 # Don't actually consume the #{
277
- @offset -= 2
278
- @interpolation_stack << re
310
+ @interpolation_stack << [:string, re]
311
+ start_pos = Sass::Source::Position.new(@line, @offset - 2)
312
+ @next_tok = Token.new(:string_interpolation, range(start_pos), @scanner.pos - 2)
279
313
  end
280
314
  str =
281
315
  if re == :uri
282
316
  url = "#{'url(' unless open}#{@scanner[1]}#{')' unless @scanner[2] == '#{'}"
283
317
  Script::Value::String.new(url)
284
318
  else
285
- Script::Value::String.new(@scanner[1].gsub(/\\(['"]|\#\{)/, '\1'), :string)
319
+ Script::Value::String.new(Sass::Script::Value::String.value(@scanner[1]), :string)
286
320
  end
287
321
  [:string, str]
288
322
  end
@@ -296,10 +330,12 @@ module Sass
296
330
  # minus logic in the parser instead.
297
331
  if @scanner.peek(1) == '-'
298
332
  return if @scanner.pos == 0
299
- @scanner.pos -= 1
300
- # Don't use @scanner.scan so we don't mess up the match data.
301
- unary_minus_allowed = @scanner.peek(1) =~ /\s/
302
- @scanner.pos += 1
333
+ unary_minus_allowed =
334
+ case @scanner.string[@scanner.pos - 1, 1]
335
+ when /\s/; true
336
+ when '/'; @scanner.pos != 1 && @scanner.string[@scanner.pos - 2, 1] == '*'
337
+ else; false
338
+ end
303
339
 
304
340
  return unless unary_minus_allowed
305
341
  return unless scan(REGULAR_EXPRESSIONS[:unary_minus_number])
@@ -310,37 +346,80 @@ module Sass
310
346
  end
311
347
 
312
348
  value = (@scanner[1] ? @scanner[1].to_f : @scanner[2].to_i) * (minus ? -1 : 1)
313
- script_number = Script::Value::Number.new(value, Array(@scanner[3]))
349
+ value *= 10**@scanner[3].to_i if @scanner[3]
350
+ script_number = Script::Value::Number.new(value, Array(@scanner[4]))
314
351
  [:number, script_number]
315
352
  end
316
353
 
354
+ def id
355
+ # Colors and ids are tough to tell apart, because they overlap but
356
+ # neither is a superset of the other. "#xyz" is an id but not a color,
357
+ # "#000" is a color but not an id, "#abc" is both, and "#0" is neither.
358
+ # We need to handle all these cases correctly.
359
+ #
360
+ # To do so, we first try to parse something as an id. If this works and
361
+ # the id is also a valid color, we return the color. Otherwise, we
362
+ # return the id. If it didn't parse as an id, we then try to parse it as
363
+ # a color. If *this* works, we return the color, and if it doesn't we
364
+ # give up and throw an error.
365
+ #
366
+ # IDs in properties are used in the Basic User Interface Module
367
+ # (http://www.w3.org/TR/css3-ui/).
368
+ return unless scan(REGULAR_EXPRESSIONS[:id])
369
+ if @scanner[0] =~ /^\#[0-9a-fA-F]+$/ && (@scanner[0].length == 4 || @scanner[0].length == 7)
370
+ return [:color, Script::Value::Color.from_hex(@scanner[0])]
371
+ end
372
+ [:ident, @scanner[0]]
373
+ end
374
+
317
375
  def color
318
- s = scan(REGULAR_EXPRESSIONS[:color])
319
- return unless s
320
- raise Sass::SyntaxError.new(<<MESSAGE.rstrip) unless s.size == 4 || s.size == 7
321
- Colors must have either three or six digits: '#{s}'
322
- MESSAGE
323
- script_color = Script::Value::Color.from_hex(s)
376
+ return unless @scanner.match?(REGULAR_EXPRESSIONS[:color])
377
+ return unless @scanner[0].length == 4 || @scanner[0].length == 7
378
+ script_color = Script::Value::Color.from_hex(scan(REGULAR_EXPRESSIONS[:color]))
324
379
  [:color, script_color]
325
380
  end
326
381
 
382
+ def selector
383
+ start_pos = source_position
384
+ return unless scan(REGULAR_EXPRESSIONS[:selector])
385
+ script_selector = Script::Tree::Selector.new
386
+ script_selector.source_range = range(start_pos)
387
+ [:selector, script_selector]
388
+ end
389
+
327
390
  def special_fun
328
- str1 = scan(/((-[\w-]+-)?(calc|element)|expression|progid:[a-z\.]*)\(/i)
329
- return unless str1
330
- str2, _ = Sass::Shared.balance(@scanner, ?(, ?), 1)
331
- c = str2.count("\n")
332
- old_line = @line
333
- old_offset = @offset
334
- @line += c
335
- @offset = c == 0 ? @offset + str2.size : str2[/\n([^\n]*)/, 1].size + 1
336
- [:special_fun,
337
- Sass::Util.merge_adjacent_strings(
338
- [str1] + Sass::Engine.parse_interp(str2, old_line, old_offset, @options)),
339
- str1.size + str2.size]
391
+ prefix = scan(/((-[\w-]+-)?(calc|element)|expression|progid:[a-z\.]*)\(/i)
392
+ return unless prefix
393
+ special_fun_body(1, prefix)
394
+ end
395
+
396
+ def special_fun_body(parens, prefix = nil)
397
+ str = prefix || ''
398
+ while (scanned = scan(/.*?([()]|\#\{)/m))
399
+ str << scanned
400
+ if scanned[-1] == ?(
401
+ parens += 1
402
+ next
403
+ elsif scanned[-1] == ?)
404
+ parens -= 1
405
+ next unless parens == 0
406
+ else
407
+ raise "[BUG] Unreachable" unless @scanner[1] == '#{' # '
408
+ str.slice!(-2..-1)
409
+ @interpolation_stack << [:special_fun, parens]
410
+ start_pos = Sass::Source::Position.new(@line, @offset - 2)
411
+ @next_tok = Token.new(:string_interpolation, range(start_pos), @scanner.pos - 2)
412
+ end
413
+
414
+ return [:special_fun, Sass::Script::Value::String.new(str)]
415
+ end
416
+
417
+ scan(/.*/)
418
+ expected!('")"')
340
419
  end
341
420
 
342
421
  def special_val
343
- return unless scan(/!important/i)
422
+ return unless scan(/!#{W}important/i)
344
423
  [:string, Script::Value::String.new("!important")]
345
424
  end
346
425
 
@@ -353,8 +432,9 @@ MESSAGE
353
432
  def op
354
433
  op = scan(REGULAR_EXPRESSIONS[:op])
355
434
  return unless op
356
- @interpolation_stack << nil if op == :begin_interpolation
357
- [OPERATORS[op]]
435
+ name = OPERATORS[op]
436
+ @interpolation_stack << nil if name == :begin_interpolation
437
+ [name]
358
438
  end
359
439
 
360
440
  def raw(rx)
@@ -368,7 +448,7 @@ MESSAGE
368
448
  return unless str
369
449
  c = str.count("\n")
370
450
  @line += c
371
- @offset = (c == 0 ? @offset + str.size : str.size - str.rindex("\n") + 1)
451
+ @offset = (c == 0 ? @offset + str.size : str.size - str.rindex("\n"))
372
452
  str
373
453
  end
374
454