sass 3.3.0 → 3.4.25

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  require 'set'
2
3
 
3
4
  module Sass
@@ -15,21 +16,20 @@ module Sass
15
16
  # warnings and source maps.
16
17
  # @param importer [Sass::Importers::Base] The importer used to import the
17
18
  # file being parsed. Used for source maps.
18
- # @param line [Fixnum] The 1-based line on which the source string appeared,
19
+ # @param line [Integer] The 1-based line on which the source string appeared,
19
20
  # if it's part of another document.
20
- # @param offset [Fixnum] The 1-based character (not byte) offset in the line on
21
+ # @param offset [Integer] The 1-based character (not byte) offset in the line on
21
22
  # which the source string starts. Used for error reporting and sourcemap
22
23
  # building.
23
- # @comment
24
- # rubocop:disable ParameterLists
25
24
  def initialize(str, filename, importer, line = 1, offset = 1)
26
- # rubocop:enable ParameterLists
27
25
  @template = str
28
26
  @filename = filename
29
27
  @importer = importer
30
28
  @line = line
31
29
  @offset = offset
32
30
  @strs = []
31
+ @expected = nil
32
+ @throw_error = false
33
33
  end
34
34
 
35
35
  # Parses an SCSS document.
@@ -39,7 +39,7 @@ module Sass
39
39
  def parse
40
40
  init_scanner!
41
41
  root = stylesheet
42
- expected("selector or at-rule") unless @scanner.eos?
42
+ expected("selector or at-rule") unless root && @scanner.eos?
43
43
  root
44
44
  end
45
45
 
@@ -54,6 +54,15 @@ module Sass
54
54
  interp_ident
55
55
  end
56
56
 
57
+ # Parses a supports clause for an @import directive
58
+ def parse_supports_clause
59
+ init_scanner!
60
+ ss
61
+ clause = supports_clause
62
+ ss
63
+ clause
64
+ end
65
+
57
66
  # Parses a media query list.
58
67
  #
59
68
  # @return [Sass::Media::QueryList] The parsed query list
@@ -62,7 +71,7 @@ module Sass
62
71
  def parse_media_query_list
63
72
  init_scanner!
64
73
  ql = media_query_list
65
- expected("media query list") unless @scanner.eos?
74
+ expected("media query list") unless ql && @scanner.eos?
66
75
  ql
67
76
  end
68
77
 
@@ -74,7 +83,7 @@ module Sass
74
83
  def parse_at_root_query
75
84
  init_scanner!
76
85
  query = at_root_query
77
- expected("@at-root query list") unless @scanner.eos?
86
+ expected("@at-root query list") unless query && @scanner.eos?
78
87
  query
79
88
  end
80
89
 
@@ -86,7 +95,7 @@ module Sass
86
95
  def parse_supports_condition
87
96
  init_scanner!
88
97
  condition = supports_condition
89
- expected("supports condition") unless @scanner.eos?
98
+ expected("supports condition") unless condition && @scanner.eos?
90
99
  condition
91
100
  end
92
101
 
@@ -107,7 +116,7 @@ module Sass
107
116
  if @template.is_a?(StringScanner)
108
117
  @template
109
118
  else
110
- Sass::Util::MultibyteStringScanner.new(@template.gsub("\r", ""))
119
+ Sass::Util::MultibyteStringScanner.new(@template.tr("\r", ""))
111
120
  end
112
121
  end
113
122
 
@@ -149,16 +158,16 @@ module Sass
149
158
  silent = text =~ %r{\A//}
150
159
  loud = !silent && text =~ %r{\A/[/*]!}
151
160
  line = @line - text.count("\n")
161
+ comment_start = @scanner.pos - text.length
162
+ index_before_line = @scanner.string.rindex("\n", comment_start) || -1
163
+ offset = comment_start - index_before_line
152
164
 
153
165
  if silent
154
166
  value = [text.sub(%r{\A\s*//}, '/*').gsub(%r{^\s*//}, ' *') + ' */']
155
167
  else
156
- value = Sass::Engine.parse_interp(
157
- text, line, @scanner.pos - text.size, :filename => @filename)
158
- value.unshift(@scanner.
159
- string[0...@scanner.pos].
160
- reverse[/.*?\*\/(.*?)($|\Z)/, 1].
161
- reverse.gsub(/[^\s]/, ' '))
168
+ value = Sass::Engine.parse_interp(text, line, offset, :filename => @filename)
169
+ line_before_comment = @scanner.string[index_before_line + 1...comment_start]
170
+ value.unshift(line_before_comment.gsub(/[^\s]/, ' '))
162
171
  end
163
172
 
164
173
  type = if silent
@@ -168,14 +177,14 @@ module Sass
168
177
  else
169
178
  :normal
170
179
  end
171
- comment = Sass::Tree::CommentNode.new(value, type)
172
- comment.line = line
180
+ start_pos = Sass::Source::Position.new(line, offset)
181
+ comment = node(Sass::Tree::CommentNode.new(value, type), start_pos)
173
182
  node << comment
174
183
  end
175
184
 
176
185
  DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for,
177
186
  :each, :while, :if, :else, :extend, :import, :media, :charset, :content,
178
- :_moz_document, :at_root]
187
+ :_moz_document, :at_root, :error]
179
188
 
180
189
  PREFIXED_DIRECTIVES = Set[:supports]
181
190
 
@@ -191,10 +200,7 @@ module Sass
191
200
  return dir
192
201
  end
193
202
 
194
- # Most at-rules take expressions (e.g. @import),
195
- # but some (e.g. @page) take selector-like arguments.
196
- # Some take no arguments at all.
197
- val = expr || selector
203
+ val = almost_any_value
198
204
  val = val ? ["@#{name} "] + Sass::Util.strip_string_array(val) : ["@#{name}"]
199
205
  directive_body(val, start_pos)
200
206
  end
@@ -212,12 +218,12 @@ module Sass
212
218
  end
213
219
 
214
220
  def special_directive(name, start_pos)
215
- sym = name.gsub('-', '_').to_sym
221
+ sym = name.tr('-', '_').to_sym
216
222
  DIRECTIVES.include?(sym) && send("#{sym}_directive", start_pos)
217
223
  end
218
224
 
219
225
  def prefixed_directive(name, start_pos)
220
- sym = name.gsub(/^-[a-z0-9]+-/i, '').gsub('-', '_').to_sym
226
+ sym = deprefix(name).tr('-', '_').to_sym
221
227
  PREFIXED_DIRECTIVES.include?(sym) && send("#{sym}_directive", name, start_pos)
222
228
  end
223
229
 
@@ -350,10 +356,12 @@ module Sass
350
356
  end
351
357
 
352
358
  def extend_directive(start_pos)
353
- selector, selector_range = expr!(:selector_sequence)
359
+ selector_start_pos = source_position
360
+ @expected = "selector"
361
+ selector = Sass::Util.strip_string_array(expr!(:almost_any_value))
354
362
  optional = tok(OPTIONAL)
355
363
  ss
356
- node(Sass::Tree::ExtendNode.new(selector, !!optional, selector_range), start_pos)
364
+ node(Sass::Tree::ExtendNode.new(selector, !!optional, range(selector_start_pos)), start_pos)
357
365
  end
358
366
 
359
367
  def import_directive(start_pos)
@@ -371,24 +379,28 @@ module Sass
371
379
 
372
380
  def import_arg
373
381
  start_pos = source_position
374
- return unless (str = tok(STRING)) || (uri = tok?(/url\(/i))
382
+ return unless (str = string) || (uri = tok?(/url\(/i))
375
383
  if uri
376
384
  str = sass_script(:parse_string)
377
385
  ss
386
+ supports = supports_clause
387
+ ss
378
388
  media = media_query_list
379
389
  ss
380
- return node(Tree::CssImportNode.new(str, media.to_a), start_pos)
390
+ return node(Tree::CssImportNode.new(str, media.to_a, supports), start_pos)
381
391
  end
382
-
383
- path = @scanner[1] || @scanner[2]
384
392
  ss
385
393
 
394
+ supports = supports_clause
395
+ ss
386
396
  media = media_query_list
387
- if path =~ %r{^(https?:)?//} || media || use_css_import?
388
- return node(Sass::Tree::CssImportNode.new(str, media.to_a), start_pos)
397
+ if str =~ %r{^(https?:)?//} || media || supports || use_css_import?
398
+ return node(
399
+ Sass::Tree::CssImportNode.new(
400
+ Sass::Script::Value::String.quote(str), media.to_a, supports), start_pos)
389
401
  end
390
402
 
391
- node(Sass::Tree::ImportNode.new(path.strip), start_pos)
403
+ node(Sass::Tree::ImportNode.new(str.strip), start_pos)
392
404
  end
393
405
 
394
406
  def use_css_import?; false; end
@@ -471,8 +483,7 @@ module Sass
471
483
  alias_method :at_root_query, :query_expr
472
484
 
473
485
  def charset_directive(start_pos)
474
- tok! STRING
475
- name = @scanner[1] || @scanner[2]
486
+ name = expr!(:string)
476
487
  ss
477
488
  node(Sass::Tree::CharsetNode.new(name), start_pos)
478
489
  end
@@ -500,7 +511,7 @@ module Sass
500
511
 
501
512
  def moz_document_function
502
513
  val = interp_uri || _interp_string(:url_prefix) ||
503
- _interp_string(:domain) || function(!:allow_var) || interpolation
514
+ _interp_string(:domain) || function(false) || interpolation
504
515
  return unless val
505
516
  ss
506
517
  val
@@ -529,6 +540,10 @@ module Sass
529
540
  arr
530
541
  end
531
542
 
543
+ def error_directive(start_pos)
544
+ node(Sass::Tree::ErrorNode.new(sass_script(:parse)), start_pos)
545
+ end
546
+
532
547
  # http://www.w3.org/TR/css3-conditional/
533
548
  def supports_directive(name, start_pos)
534
549
  condition = expr!(:supports_condition)
@@ -542,10 +557,23 @@ module Sass
542
557
  node(node, start_pos)
543
558
  end
544
559
 
560
+ def supports_clause
561
+ return unless tok(/supports\(/i)
562
+ ss
563
+ supports = import_supports_condition
564
+ ss
565
+ tok!(/\)/)
566
+ supports
567
+ end
568
+
545
569
  def supports_condition
546
570
  supports_negation || supports_operator || supports_interpolation
547
571
  end
548
572
 
573
+ def import_supports_condition
574
+ supports_condition || supports_declaration
575
+ end
576
+
549
577
  def supports_negation
550
578
  return unless tok(/not/i)
551
579
  ss
@@ -555,7 +583,9 @@ module Sass
555
583
  def supports_operator
556
584
  cond = supports_condition_in_parens
557
585
  return unless cond
558
- while (op = tok(/and|or/i))
586
+ re = /and|or/i
587
+ while (op = tok(re))
588
+ re = /#{op}/i
559
589
  ss
560
590
  cond = Sass::Supports::Operator.new(
561
591
  cond, expr!(:supports_condition_in_parens), op)
@@ -563,6 +593,13 @@ module Sass
563
593
  cond
564
594
  end
565
595
 
596
+ def supports_declaration
597
+ name = sass_script(:parse)
598
+ tok!(/:/); ss
599
+ value = sass_script(:parse)
600
+ Sass::Supports::Declaration.new(name, value)
601
+ end
602
+
566
603
  def supports_condition_in_parens
567
604
  interp = supports_interpolation
568
605
  return interp if interp
@@ -571,19 +608,12 @@ module Sass
571
608
  tok!(/\)/); ss
572
609
  cond
573
610
  else
574
- name = sass_script(:parse)
575
- tok!(/:/); ss
576
- value = sass_script(:parse)
611
+ decl = supports_declaration
577
612
  tok!(/\)/); ss
578
- Sass::Supports::Declaration.new(name, value)
613
+ decl
579
614
  end
580
615
  end
581
616
 
582
- def supports_declaration_condition
583
- return unless tok(/\(/); ss
584
- supports_declaration_body
585
- end
586
-
587
617
  def supports_interpolation
588
618
  interp = interpolation
589
619
  return unless interp
@@ -619,15 +649,15 @@ module Sass
619
649
  # are disallowed by the CSS spec,
620
650
  # but they're included here for compatibility
621
651
  # with some proprietary MS properties
622
- str {ss if tok(/[\/,:.=]/)}
652
+ str {ss if tok(%r{[/,:.=]})}
623
653
  end
624
654
 
625
655
  def ruleset
626
656
  start_pos = source_position
627
- rules, source_range = selector_sequence
628
- return unless rules
629
- block(node(
630
- Sass::Tree::RuleNode.new(rules.flatten.compact, source_range), start_pos), :ruleset)
657
+ return unless (rules = almost_any_value)
658
+ block(
659
+ node(
660
+ Sass::Tree::RuleNode.new(rules, range(start_pos)), start_pos), :ruleset)
631
661
  end
632
662
 
633
663
  def block(node, context)
@@ -661,315 +691,177 @@ module Sass
661
691
  child_or_array.has_children
662
692
  end
663
693
 
664
- # This is a nasty hack, and the only place in the parser
665
- # that requires a large amount of backtracking.
666
- # The reason is that we can't figure out if certain strings
667
- # are declarations or rulesets with fixed finite lookahead.
668
- # For example, "foo:bar baz baz baz..." could be either a property
669
- # or a selector.
694
+ # When parsing the contents of a ruleset, it can be difficult to tell
695
+ # declarations apart from nested rulesets. Since we don't thoroughly parse
696
+ # selectors until after resolving interpolation, we can share a bunch of
697
+ # the parsing of the two, but we need to disambiguate them first. We use
698
+ # the following criteria:
699
+ #
700
+ # * If the entity doesn't start with an identifier followed by a colon,
701
+ # it's a selector. There are some additional mostly-unimportant cases
702
+ # here to support various declaration hacks.
703
+ #
704
+ # * If the colon is followed by another colon, it's a selector.
670
705
  #
671
- # To handle this, we simply check if it works as a property
672
- # (which is the most common case)
673
- # and, if it doesn't, try it as a ruleset.
706
+ # * Otherwise, if the colon is followed by anything other than
707
+ # interpolation or a character that's valid as the beginning of an
708
+ # identifier, it's a declaration.
674
709
  #
675
- # We could eke some more efficiency out of this
676
- # by handling some easy cases (first token isn't an identifier,
677
- # no colon after the identifier, whitespace after the colon),
678
- # but I'm not sure the gains would be worth the added complexity.
710
+ # * If the colon is followed by interpolation or a valid identifier, try
711
+ # parsing it as a declaration value. If this fails, backtrack and parse
712
+ # it as a selector.
713
+ #
714
+ # * If the declaration value value valid but is followed by "{", backtrack
715
+ # and parse it as a selector anyway. This ensures that ".foo:bar {" is
716
+ # always parsed as a selector and never as a property with nested
717
+ # properties beneath it.
679
718
  def declaration_or_ruleset
680
- old_use_property_exception, @use_property_exception =
681
- @use_property_exception, false
682
- decl_err = catch_error do
683
- decl = declaration
684
- unless decl && decl.has_children
685
- # We want an exception if it's not there,
686
- # but we don't want to consume if it is
687
- tok!(/[;}]/) unless tok?(/[;}]/)
688
- end
689
- return decl
690
- end
691
-
692
- ruleset_err = catch_error {return ruleset}
693
- rethrow(@use_property_exception ? decl_err : ruleset_err)
694
- ensure
695
- @use_property_exception = old_use_property_exception
696
- end
697
-
698
- def selector_sequence
699
719
  start_pos = source_position
700
- if (sel = tok(STATIC_SELECTOR, true))
701
- return [sel], range(start_pos)
702
- end
703
-
704
- rules = []
705
- v = selector
706
- return unless v
707
- rules.concat v
708
-
709
- ws = ''
710
- while tok(/,/)
711
- ws << str {ss}
712
- if (v = selector)
713
- rules << ',' << ws
714
- rules.concat v
715
- ws = ''
716
- end
717
- end
718
- return rules, range(start_pos)
719
- end
720
-
721
- def selector
722
- sel = _selector
723
- return unless sel
724
- sel.to_a
725
- end
720
+ declaration = try_declaration
726
721
 
727
- def selector_comma_sequence
728
- sel = _selector
729
- return unless sel
730
- selectors = [sel]
731
- ws = ''
732
- while tok(/,/)
733
- ws << str {ss}
734
- if (sel = _selector)
735
- selectors << sel
736
- if ws.include?("\n")
737
- selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members)
738
- end
739
- ws = ''
740
- end
722
+ if declaration.nil?
723
+ return unless (selector = almost_any_value)
724
+ elsif declaration.is_a?(Array)
725
+ selector = declaration
726
+ else
727
+ # Declaration should be a PropNode.
728
+ return declaration
741
729
  end
742
- Selector::CommaSequence.new(selectors)
743
- end
744
-
745
- def _selector
746
- # The combinator here allows the "> E" hack
747
- val = combinator || simple_selector_sequence
748
- return unless val
749
- nl = str {ss}.include?("\n")
750
- res = []
751
- res << val
752
- res << "\n" if nl
753
730
 
754
- while (val = combinator || simple_selector_sequence)
755
- res << val
756
- res << "\n" if str {ss}.include?("\n")
731
+ if (additional_selector = almost_any_value)
732
+ selector << additional_selector
757
733
  end
758
- Selector::Sequence.new(res.compact)
759
- end
760
734
 
761
- def combinator
762
- tok(PLUS) || tok(GREATER) || tok(TILDE) || reference_combinator
735
+ block(
736
+ node(
737
+ Sass::Tree::RuleNode.new(merge(selector), range(start_pos)), start_pos), :ruleset)
763
738
  end
764
739
 
765
- def reference_combinator
766
- return unless tok(/\//)
767
- res = ['/']
768
- ns, name = expr!(:qualified_name)
769
- res << ns << '|' if ns
770
- res << name << tok!(/\//)
771
- res = res.flatten
772
- res = res.join '' if res.all? {|e| e.is_a?(String)}
773
- res
774
- end
775
-
776
- def simple_selector_sequence
777
- # Returning expr by default allows for stuff like
778
- # http://www.w3.org/TR/css3-animations/#keyframes-
779
-
780
- start_pos = source_position
781
- e = element_name || id_selector ||
782
- class_selector || placeholder_selector || attrib || pseudo ||
783
- parent_selector || interpolation_selector
784
- return expr(!:allow_var) unless e
785
- res = [e]
786
-
787
- # The tok(/\*/) allows the "E*" hack
788
- while (v = id_selector || class_selector || placeholder_selector ||
789
- attrib || pseudo || interpolation_selector ||
790
- (tok(/\*/) && Selector::Universal.new(nil)))
791
- res << v
740
+ # Tries to parse a declaration, and returns the value parsed so far if it
741
+ # fails.
742
+ #
743
+ # This has three possible return types. It can return `nil`, indicating
744
+ # that parsing failed completely and the scanner hasn't moved forward at
745
+ # all. It can return an Array, indicating that parsing failed after
746
+ # consuming some text (possibly containing interpolation), which is
747
+ # returned. Or it can return a PropNode, indicating that parsing
748
+ # succeeded.
749
+ def try_declaration
750
+ # This allows the "*prop: val", ":prop: val", "#prop: val", and ".prop:
751
+ # val" hacks.
752
+ name_start_pos = source_position
753
+ if (s = tok(/[:\*\.]|\#(?!\{)/))
754
+ name = [s, str {ss}]
755
+ return name unless (ident = interp_ident)
756
+ name << ident
757
+ else
758
+ return unless (name = interp_ident)
759
+ name = Array(name)
792
760
  end
793
761
 
794
- pos = @scanner.pos
795
- line = @line
796
- if (sel = str? {simple_selector_sequence})
797
- @scanner.pos = pos
798
- @line = line
799
- begin
800
- # If we see "*E", don't force a throw because this could be the
801
- # "*prop: val" hack.
802
- expected('"{"') if res.length == 1 && res[0].is_a?(Selector::Universal)
803
- throw_error {expected('"{"')}
804
- rescue Sass::SyntaxError => e
805
- e.message << "\n\n\"#{sel}\" may only be used at the beginning of a compound selector."
806
- raise e
807
- end
762
+ if (comment = tok(COMMENT))
763
+ name << comment
808
764
  end
765
+ name_end_pos = source_position
809
766
 
810
- Selector::SimpleSequence.new(res, tok(/!/), range(start_pos))
811
- end
812
-
813
- def parent_selector
814
- return unless tok(/&/)
815
- Selector::Parent.new(interp_ident(NAME) || [])
816
- end
817
-
818
- def class_selector
819
- return unless tok(/\./)
820
- @expected = "class name"
821
- Selector::Class.new(merge(expr!(:interp_ident)))
822
- end
823
-
824
- def id_selector
825
- return unless tok(/#(?!\{)/)
826
- @expected = "id name"
827
- Selector::Id.new(merge(expr!(:interp_name)))
828
- end
829
-
830
- def placeholder_selector
831
- return unless tok(/%/)
832
- @expected = "placeholder name"
833
- Selector::Placeholder.new(merge(expr!(:interp_ident)))
834
- end
835
-
836
- def element_name
837
- ns, name = Sass::Util.destructure(qualified_name(:allow_star_name))
838
- return unless ns || name
767
+ mid = [str {ss}]
768
+ return name + mid unless tok(/:/)
769
+ mid << ':'
770
+ return name + mid + [':'] if tok(/:/)
771
+ mid << str {ss}
772
+ post_colon_whitespace = !mid.last.empty?
773
+ could_be_selector = !post_colon_whitespace && (tok?(IDENT_START) || tok?(INTERP_START))
839
774
 
840
- if name == '*'
841
- Selector::Universal.new(merge(ns))
842
- else
843
- Selector::Element.new(merge(name), merge(ns))
775
+ value_start_pos = source_position
776
+ value = nil
777
+ error = catch_error do
778
+ value = value!(name.first.is_a?(String) && name.first.start_with?("--"))
779
+ if tok?(/\{/)
780
+ # Properties that are ambiguous with selectors can't have additional
781
+ # properties nested beneath them.
782
+ tok!(/;/) if could_be_selector
783
+ elsif !tok?(/[;{}]/)
784
+ # We want an exception if there's no valid end-of-property character
785
+ # exists, but we don't want to consume it if it does.
786
+ tok!(/[;{}]/)
787
+ end
844
788
  end
845
- end
846
789
 
847
- def qualified_name(allow_star_name = false)
848
- name = interp_ident || tok(/\*/) || (tok?(/\|/) && "")
849
- return unless name
850
- return nil, name unless tok(/\|/)
790
+ if error
791
+ rethrow error unless could_be_selector
851
792
 
852
- return name, expr!(:interp_ident) unless allow_star_name
853
- @expected = "identifier or *"
854
- return name, interp_ident || tok!(/\*/)
855
- end
793
+ # If the value would be followed by a semicolon, it's definitely
794
+ # supposed to be a property, not a selector.
795
+ additional_selector = almost_any_value
796
+ rethrow error if tok?(/;/)
856
797
 
857
- def interpolation_selector
858
- if (script = interpolation)
859
- Selector::Interpolation.new(script)
798
+ return name + mid + (additional_selector || [])
860
799
  end
861
- end
862
800
 
863
- def attrib
864
- return unless tok(/\[/)
865
- ss
866
- ns, name = attrib_name!
801
+ value_end_pos = source_position
867
802
  ss
803
+ require_block = tok?(/\{/)
868
804
 
869
- op = tok(/=/) ||
870
- tok(INCLUDES) ||
871
- tok(DASHMATCH) ||
872
- tok(PREFIXMATCH) ||
873
- tok(SUFFIXMATCH) ||
874
- tok(SUBSTRINGMATCH)
875
- if op
876
- @expected = "identifier or string"
877
- ss
878
- val = interp_ident || expr!(:interp_string)
879
- ss
880
- end
881
- flags = interp_ident || interp_string
882
- tok!(/\]/)
883
-
884
- Selector::Attribute.new(merge(name), merge(ns), op, merge(val), merge(flags))
885
- end
886
-
887
- def attrib_name!
888
- if (name_or_ns = interp_ident)
889
- # E, E|E
890
- if tok(/\|(?!=)/)
891
- ns = name_or_ns
892
- name = interp_ident
893
- else
894
- name = name_or_ns
895
- end
896
- else
897
- # *|E or |E
898
- ns = [tok(/\*/) || ""]
899
- tok!(/\|/)
900
- name = expr!(:interp_ident)
901
- end
902
- return ns, name
903
- end
904
-
905
- def pseudo
906
- s = tok(/::?/)
907
- return unless s
908
- @expected = "pseudoclass or pseudoelement"
909
- name = expr!(:interp_ident)
910
- if tok(/\(/)
911
- ss
912
- arg = expr!(:pseudo_arg)
913
- while tok(/,/)
914
- arg << ',' << str {ss}
915
- arg.concat expr!(:pseudo_arg)
916
- end
917
- tok!(/\)/)
918
- end
919
- Selector::Pseudo.new(s == ':' ? :class : :element, merge(name), merge(arg))
920
- end
921
-
922
- def pseudo_arg
923
- # In the CSS spec, every pseudo-class/element either takes a pseudo
924
- # expression or a selector comma sequence as an argument. However, we
925
- # don't want to have to know which takes which, so we handle both at
926
- # once.
927
- #
928
- # However, there are some ambiguities between the two. For instance, "n"
929
- # could start a pseudo expression like "n+1", or it could start a
930
- # selector like "n|m". In order to handle this, we must regrettably
931
- # backtrack.
932
- expr, sel = nil, nil
933
- pseudo_err = catch_error do
934
- expr = pseudo_expr
935
- next if tok?(/[,)]/)
936
- expr = nil
937
- expected '")"'
938
- end
939
-
940
- return expr if expr
941
- sel_err = catch_error {sel = selector}
942
- return sel if sel
943
- rethrow pseudo_err if pseudo_err
944
- rethrow sel_err if sel_err
945
- nil
946
- end
805
+ node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new),
806
+ name_start_pos, value_end_pos)
807
+ node.name_source_range = range(name_start_pos, name_end_pos)
808
+ node.value_source_range = range(value_start_pos, value_end_pos)
947
809
 
948
- def pseudo_expr_token
949
- tok(PLUS) || tok(/[-*]/) || tok(NUMBER) || interp_string || tok(IDENT) || interpolation
810
+ return node unless require_block
811
+ nested_properties! node
950
812
  end
951
813
 
952
- def pseudo_expr
953
- e = pseudo_expr_token
954
- return unless e
955
- res = [e, str {ss}]
956
- while (e = pseudo_expr_token)
957
- res << e << str {ss}
814
+ # This production is similar to the CSS [`<any-value>`][any-value]
815
+ # production, but as the name implies, not quite the same. It's meant to
816
+ # consume values that could be a selector, an expression, or a combination
817
+ # of both. It respects strings and comments and supports interpolation. It
818
+ # will consume up to "{", "}", ";", or "!".
819
+ #
820
+ # [any-value]: http://dev.w3.org/csswg/css-variables/#typedef-any-value
821
+ #
822
+ # Values consumed by this production will usually be parsed more
823
+ # thoroughly once interpolation has been resolved.
824
+ def almost_any_value
825
+ return unless (tok = almost_any_value_token)
826
+ sel = [tok]
827
+ while (tok = almost_any_value_token)
828
+ sel << tok
958
829
  end
959
- res
830
+ merge(sel)
831
+ end
832
+
833
+ def almost_any_value_token
834
+ tok(%r{
835
+ (
836
+ \\.
837
+ |
838
+ (?!url\()
839
+ [^"'/\#!;\{\}] # "
840
+ |
841
+ # interp_uri will handle most url() calls, but not ones that take strings
842
+ url\(#{W}(?=")
843
+ |
844
+ /(?![/*])
845
+ |
846
+ \#(?!\{)
847
+ |
848
+ !(?![a-z]) # TODO: never consume "!" when issue 1126 is fixed.
849
+ )+
850
+ }xi) || tok(COMMENT) || tok(SINGLE_LINE_COMMENT) || interp_string || interp_uri ||
851
+ interpolation(:warn_for_color)
960
852
  end
961
853
 
962
854
  def declaration
963
- # This allows the "*prop: val", ":prop: val", and ".prop: val" hacks
855
+ # This allows the "*prop: val", ":prop: val", "#prop: val", and ".prop:
856
+ # val" hacks.
964
857
  name_start_pos = source_position
965
858
  if (s = tok(/[:\*\.]|\#(?!\{)/))
966
- @use_property_exception = s !~ /[\.\#]/
967
859
  name = [s, str {ss}, *expr!(:interp_ident)]
968
860
  else
969
- name = interp_ident
970
- return unless name
971
- name = [name] if name.is_a?(String)
861
+ return unless (name = interp_ident)
862
+ name = Array(name)
972
863
  end
864
+
973
865
  if (comment = tok(COMMENT))
974
866
  name << comment
975
867
  end
@@ -977,7 +869,9 @@ module Sass
977
869
  ss
978
870
 
979
871
  tok!(/:/)
980
- value_start_pos, space, value = value!
872
+ ss
873
+ value_start_pos = source_position
874
+ value = value!(name.first.is_a?(String) && name.first.start_with?("--"))
981
875
  value_end_pos = source_position
982
876
  ss
983
877
  require_block = tok?(/\{/)
@@ -988,19 +882,15 @@ module Sass
988
882
  node.value_source_range = range(value_start_pos, value_end_pos)
989
883
 
990
884
  return node unless require_block
991
- nested_properties! node, space
885
+ nested_properties! node
992
886
  end
993
887
 
994
- def value!
995
- space = !str {ss}.empty?
996
- value_start_pos = source_position
997
- @use_property_exception ||= space || !tok?(IDENT)
998
-
888
+ def value!(css_variable = false)
999
889
  if tok?(/\{/)
1000
890
  str = Sass::Script::Tree::Literal.new(Sass::Script::Value::String.new(""))
1001
891
  str.line = source_position.line
1002
892
  str.source_range = range(source_position)
1003
- return value_start_pos, true, str
893
+ return str
1004
894
  end
1005
895
 
1006
896
  start_pos = source_position
@@ -1013,18 +903,21 @@ module Sass
1013
903
  str = Sass::Script::Tree::Literal.new(Sass::Script::Value::String.new(val.strip))
1014
904
  str.line = start_pos.line
1015
905
  str.source_range = range(start_pos)
1016
- return value_start_pos, space, str
906
+ return str
1017
907
  end
1018
- return value_start_pos, space, sass_script(:parse)
908
+
909
+ sass_script(:parse, css_variable)
1019
910
  end
1020
911
 
1021
- def nested_properties!(node, space)
1022
- err(<<MESSAGE) unless space
1023
- Invalid CSS: a space is required between a property and its definition
1024
- when it has other properties nested beneath it.
1025
- MESSAGE
912
+ def nested_properties!(node)
913
+ if node.name.first.is_a?(String) && node.name.first.start_with?("--")
914
+ Sass::Util.sass_warn(<<WARNING)
915
+ DEPRECATION WARNING on line #{@line}#{" of #{@filename}" if @filename}:
916
+ Sass 3.6 will change the way CSS variables are parsed. Instead of being parsed as
917
+ normal properties, they will not allow any Sass-specific behavior other than \#{}.
918
+ WARNING
919
+ end
1026
920
 
1027
- @use_property_exception = true
1028
921
  @expected = 'expression (e.g. 1px, bold) or "{"'
1029
922
  block(node, :property)
1030
923
  end
@@ -1078,9 +971,14 @@ MESSAGE
1078
971
  var
1079
972
  end
1080
973
 
1081
- def interpolation
974
+ def interpolation(warn_for_color = false)
1082
975
  return unless tok(INTERP_START)
1083
- sass_script(:parse_interpolated)
976
+ sass_script(:parse_interpolated, warn_for_color)
977
+ end
978
+
979
+ def string
980
+ return unless tok(STRING)
981
+ Sass::Script::Value::String.value(@scanner[1] || @scanner[2])
1084
982
  end
1085
983
 
1086
984
  def interp_string
@@ -1107,10 +1005,10 @@ MESSAGE
1107
1005
  end
1108
1006
 
1109
1007
  def interp_ident(start = IDENT)
1110
- val = tok(start) || interpolation || tok(IDENT_HYPHEN_INTERP, true)
1008
+ val = tok(start) || interpolation(:warn_for_color) || tok(IDENT_HYPHEN_INTERP, true)
1111
1009
  return unless val
1112
1010
  res = [val]
1113
- while (val = tok(NAME) || interpolation)
1011
+ while (val = tok(NAME) || interpolation(:warn_for_color))
1114
1012
  res << val
1115
1013
  end
1116
1014
  res
@@ -1123,12 +1021,8 @@ MESSAGE
1123
1021
  return [var] if var
1124
1022
  end
1125
1023
 
1126
- def interp_name
1127
- interp_ident NAME
1128
- end
1129
-
1130
1024
  def str
1131
- @strs.push ""
1025
+ @strs.push String.new("")
1132
1026
  yield
1133
1027
  @strs.last
1134
1028
  ensure
@@ -1156,8 +1050,7 @@ MESSAGE
1156
1050
  node
1157
1051
  end
1158
1052
 
1159
- @sass_script_parser = Class.new(Sass::Script::Parser)
1160
- @sass_script_parser.send(:include, ScriptParser)
1053
+ @sass_script_parser = Sass::Script::Parser
1161
1054
 
1162
1055
  class << self
1163
1056
  # @private
@@ -1166,7 +1059,7 @@ MESSAGE
1166
1059
 
1167
1060
  def sass_script(*args)
1168
1061
  parser = self.class.sass_script_parser.new(@scanner, @line, @offset,
1169
- :filename => @filename, :importer => @importer)
1062
+ :filename => @filename, :importer => @importer, :allow_extra_text => true)
1170
1063
  result = parser.send(*args)
1171
1064
  unless @strs.empty?
1172
1065
  # Convert to CSS manually so that comments are ignored.
@@ -1191,25 +1084,26 @@ MESSAGE
1191
1084
  :media_expr => "media expression (e.g. (min-device-width: 800px))",
1192
1085
  :at_root_query => "@at-root query (e.g. (without: media))",
1193
1086
  :at_root_directive_list => '* or identifier',
1194
- :pseudo_arg => "expression (e.g. fr, 2n+1)",
1087
+ :pseudo_args => "expression (e.g. fr, 2n+1)",
1195
1088
  :interp_ident => "identifier",
1196
- :interp_name => "identifier",
1197
1089
  :qualified_name => "identifier",
1198
1090
  :expr => "expression (e.g. 1px, bold)",
1199
- :_selector => "selector",
1200
1091
  :selector_comma_sequence => "selector",
1201
- :simple_selector_sequence => "selector",
1092
+ :string => "string",
1202
1093
  :import_arg => "file to import (string or url())",
1203
1094
  :moz_document_function => "matching function (e.g. url-prefix(), domain())",
1204
1095
  :supports_condition => "@supports condition (e.g. (display: flexbox))",
1205
1096
  :supports_condition_in_parens => "@supports condition (e.g. (display: flexbox))",
1097
+ :a_n_plus_b => "An+B expression",
1098
+ :keyframes_selector_component => "from, to, or a percentage",
1099
+ :keyframes_selector => "keyframes selector (e.g. 10%)"
1206
1100
  }
1207
1101
 
1208
1102
  TOK_NAMES = Sass::Util.to_hash(Sass::SCSS::RX.constants.map do |c|
1209
1103
  [Sass::SCSS::RX.const_get(c), c.downcase]
1210
1104
  end).merge(
1211
1105
  IDENT => "identifier",
1212
- /[;}]/ => '";"',
1106
+ /[;{}]/ => '";"',
1213
1107
  /\b(without|with)\b/ => '"with" or "without"'
1214
1108
  )
1215
1109
 
@@ -1230,8 +1124,9 @@ MESSAGE
1230
1124
 
1231
1125
  unless name
1232
1126
  # Display basic regexps as plain old strings
1127
+ source = rx.source.gsub(%r{\\/}, '/')
1233
1128
  string = rx.source.gsub(/\\(.)/, '\1')
1234
- name = rx.source == Regexp.escape(string) ? string.inspect : rx.inspect
1129
+ name = source == Regexp.escape(string) ? string.inspect : rx.inspect
1235
1130
  end
1236
1131
 
1237
1132
  expected(name)
@@ -1260,14 +1155,20 @@ MESSAGE
1260
1155
  line = @line
1261
1156
  offset = @offset
1262
1157
  expected = @expected
1158
+
1159
+ logger = Sass::Logger::Delayed.install!
1263
1160
  if catch(:_sass_parser_error) {yield; false}
1264
1161
  @scanner.pos = pos
1265
1162
  @line = line
1266
1163
  @offset = offset
1267
1164
  @expected = expected
1268
1165
  {:pos => pos, :line => line, :expected => @expected, :block => block}
1166
+ else
1167
+ logger.flush
1168
+ nil
1269
1169
  end
1270
1170
  ensure
1171
+ logger.uninstall! if logger
1271
1172
  @throw_error = old_throw_error
1272
1173
  end
1273
1174
 
@@ -1314,33 +1215,39 @@ MESSAGE
1314
1215
 
1315
1216
  def tok(rx, last_group_lookahead = false)
1316
1217
  res = @scanner.scan(rx)
1317
- if res
1318
- # This fixes https://github.com/nex3/sass/issues/104, which affects
1319
- # Ruby 1.8.7 and REE. This fix is to replace the ?= zero-width
1320
- # positive lookahead operator in the Regexp (which matches without
1321
- # consuming the matched group), with a match that does consume the
1322
- # group, but then rewinds the scanner and removes the group from the
1323
- # end of the matched string. This fix makes the assumption that the
1324
- # matched group will always occur at the end of the match.
1325
- if last_group_lookahead && @scanner[-1]
1326
- @scanner.pos -= @scanner[-1].length
1327
- res.slice!(-@scanner[-1].length..-1)
1328
- end
1329
1218
 
1330
- newline_count = res.count(NEWLINE)
1331
- if newline_count > 0
1332
- @line += newline_count
1333
- @offset = res[res.rindex(NEWLINE)..-1].size
1334
- else
1335
- @offset += res.size
1336
- end
1219
+ return unless res
1220
+
1221
+ # This fixes https://github.com/nex3/sass/issues/104, which affects
1222
+ # Ruby 1.8.7 and REE. This fix is to replace the ?= zero-width
1223
+ # positive lookahead operator in the Regexp (which matches without
1224
+ # consuming the matched group), with a match that does consume the
1225
+ # group, but then rewinds the scanner and removes the group from the
1226
+ # end of the matched string. This fix makes the assumption that the
1227
+ # matched group will always occur at the end of the match.
1228
+ if last_group_lookahead && @scanner[-1]
1229
+ @scanner.pos -= @scanner[-1].length
1230
+ res.slice!(-@scanner[-1].length..-1)
1231
+ end
1337
1232
 
1338
- @expected = nil
1339
- if !@strs.empty? && rx != COMMENT && rx != SINGLE_LINE_COMMENT
1340
- @strs.each {|s| s << res}
1341
- end
1342
- res
1233
+ newline_count = res.count(NEWLINE)
1234
+ if newline_count > 0
1235
+ @line += newline_count
1236
+ @offset = res[res.rindex(NEWLINE)..-1].size
1237
+ else
1238
+ @offset += res.size
1343
1239
  end
1240
+
1241
+ @expected = nil
1242
+ if !@strs.empty? && rx != COMMENT && rx != SINGLE_LINE_COMMENT
1243
+ @strs.each {|s| s << res}
1244
+ end
1245
+ res
1246
+ end
1247
+
1248
+ # Remove a vendor prefix from `str`.
1249
+ def deprefix(str)
1250
+ str.gsub(/^-[a-zA-Z0-9]+-/, '')
1344
1251
  end
1345
1252
  end
1346
1253
  end