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
@@ -26,11 +26,10 @@ module Sass
26
26
 
27
27
  # @see Base#retrieve
28
28
  def retrieve(key, sha)
29
- if @contents.has_key?(key)
30
- return unless @contents[key][:sha] == sha
31
- obj = @contents[key][:obj]
32
- obj.respond_to?(:deep_copy) ? obj.deep_copy : obj.dup
33
- end
29
+ return unless @contents.has_key?(key)
30
+ return unless @contents[key][:sha] == sha
31
+ obj = @contents[key][:obj]
32
+ obj.respond_to?(:deep_copy) ? obj.deep_copy : obj.dup
34
33
  end
35
34
 
36
35
  # @see Base#store
@@ -51,12 +51,12 @@ module Sass
51
51
  def define_callback(name)
52
52
  class_eval <<RUBY, __FILE__, __LINE__ + 1
53
53
  def on_#{name}(&block)
54
- @_sass_callbacks ||= {}
54
+ @_sass_callbacks = {} unless defined? @_sass_callbacks
55
55
  (@_sass_callbacks[#{name.inspect}] ||= []) << block
56
56
  end
57
57
 
58
58
  def run_#{name}(*args)
59
- return unless @_sass_callbacks
59
+ return unless defined? @_sass_callbacks
60
60
  return unless @_sass_callbacks[#{name.inspect}]
61
61
  @_sass_callbacks[#{name.inspect}].each {|c| c[*args]}
62
62
  end
data/lib/sass/css.rb CHANGED
@@ -18,7 +18,7 @@ module Sass
18
18
  # that can be converted to Unicode.
19
19
  # If the stylesheet contains an `@charset` declaration,
20
20
  # that overrides the Ruby encoding
21
- # (see {file:SASS_REFERENCE.md#encodings the encoding documentation})
21
+ # (see {file:SASS_REFERENCE.md#Encodings the encoding documentation})
22
22
  # @option options :old [Boolean] (false)
23
23
  # Whether or not to output old property syntax
24
24
  # (`:color blue` as opposed to `color: blue`).
@@ -31,10 +31,11 @@ module Sass
31
31
  template = template.read
32
32
  end
33
33
 
34
- @options = options.dup
34
+ @options = options.merge(:_convert => true)
35
35
  # Backwards compatibility
36
36
  @options[:old] = true if @options[:alternate] == false
37
37
  @template = template
38
+ @checked_encoding = false
38
39
  end
39
40
 
40
41
  # Converts the CSS template into Sass or SCSS code.
@@ -67,9 +68,7 @@ module Sass
67
68
  def check_encoding!
68
69
  return if @checked_encoding
69
70
  @checked_encoding = true
70
- @template, @original_encoding = Sass::Util.check_sass_encoding(@template) do |msg, line|
71
- raise Sass::SyntaxError.new(msg, :line => line)
72
- end
71
+ @template, @original_encoding = Sass::Util.check_sass_encoding(@template)
73
72
  end
74
73
 
75
74
  # Parses the CSS template and applies various transformations
@@ -77,14 +76,14 @@ module Sass
77
76
  # @return [Tree::Node] The root node of the parsed tree
78
77
  def build_tree
79
78
  root = Sass::SCSS::CssParser.new(@template, @options[:filename], nil).parse
80
- parse_selectors root
81
- expand_commas root
82
- nest_seqs root
83
- parent_ref_rules root
84
- flatten_rules root
85
- bubble_subject root
86
- fold_commas root
87
- dump_selectors root
79
+ parse_selectors(root)
80
+ expand_commas(root)
81
+ nest_seqs(root)
82
+ parent_ref_rules(root)
83
+ flatten_rules(root)
84
+ bubble_subject(root)
85
+ fold_commas(root)
86
+ dump_selectors(root)
88
87
  root
89
88
  end
90
89
 
@@ -0,0 +1,55 @@
1
+ module Sass
2
+ # A deprecation warning that should only be printed once for a given line in a
3
+ # given file.
4
+ #
5
+ # A global Deprecation instance should be created for each type of deprecation
6
+ # warning, and `warn` should be called each time a warning is needed.
7
+ class Deprecation
8
+ @@allow_double_warnings = false
9
+
10
+ # Runs a block in which double deprecation warnings for the same location
11
+ # are allowed.
12
+ def self.allow_double_warnings
13
+ old_allow_double_warnings = @@allow_double_warnings
14
+ @@allow_double_warnings = true
15
+ yield
16
+ ensure
17
+ @@allow_double_warnings = old_allow_double_warnings
18
+ end
19
+
20
+ def initialize
21
+ # A set of filename, line pairs for which warnings have been emitted.
22
+ @seen = Set.new
23
+ end
24
+
25
+ # Prints `message` as a deprecation warning associated with `filename`,
26
+ # `line`, and optionally `column`.
27
+ #
28
+ # This ensures that only one message will be printed for each line of a
29
+ # given file.
30
+ #
31
+ # @overload warn(filename, line, message)
32
+ # @param filename [String, nil]
33
+ # @param line [Number]
34
+ # @param message [String]
35
+ # @overload warn(filename, line, column, message)
36
+ # @param filename [String, nil]
37
+ # @param line [Number]
38
+ # @param column [Number]
39
+ # @param message [String]
40
+ def warn(filename, line, column_or_message, message = nil)
41
+ return if !@@allow_double_warnings && @seen.add?([filename, line]).nil?
42
+ if message
43
+ column = column_or_message
44
+ else
45
+ message = column_or_message
46
+ end
47
+
48
+ location = "line #{line}"
49
+ location << ", column #{column}" if column
50
+ location << " of #{filename}" if filename
51
+
52
+ Sass::Util.sass_warn("DEPRECATION WARNING on #{location}:\n#{message}")
53
+ end
54
+ end
55
+ end
data/lib/sass/engine.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'set'
2
2
  require 'digest/sha1'
3
3
  require 'sass/cache_stores'
4
+ require 'sass/deprecation'
4
5
  require 'sass/source/position'
5
6
  require 'sass/source/range'
6
7
  require 'sass/source/map'
@@ -30,6 +31,8 @@ require 'sass/tree/warn_node'
30
31
  require 'sass/tree/import_node'
31
32
  require 'sass/tree/charset_node'
32
33
  require 'sass/tree/at_root_node'
34
+ require 'sass/tree/keyframe_rule_node'
35
+ require 'sass/tree/error_node'
33
36
  require 'sass/tree/visitors/base'
34
37
  require 'sass/tree/visitors/perform'
35
38
  require 'sass/tree/visitors/cssize'
@@ -82,23 +85,25 @@ module Sass
82
85
  # This class handles the parsing and compilation of the Sass template.
83
86
  # Example usage:
84
87
  #
85
- # template = File.load('stylesheets/sassy.sass')
88
+ # template = File.read('stylesheets/sassy.sass')
86
89
  # sass_engine = Sass::Engine.new(template)
87
90
  # output = sass_engine.render
88
91
  # puts output
89
92
  class Engine
93
+ @@old_property_deprecation = Deprecation.new
94
+
90
95
  # A line of Sass code.
91
96
  #
92
97
  # `text`: `String`
93
98
  # : The text in the line, without any whitespace at the beginning or end.
94
99
  #
95
- # `tabs`: `Fixnum`
100
+ # `tabs`: `Integer`
96
101
  # : The level of indentation of the line.
97
102
  #
98
- # `index`: `Fixnum`
103
+ # `index`: `Integer`
99
104
  # : The line number in the original document.
100
105
  #
101
- # `offset`: `Fixnum`
106
+ # `offset`: `Integer`
102
107
  # : The number of bytes in on the line that the text begins.
103
108
  # This ends up being the number of bytes of leading whitespace.
104
109
  #
@@ -155,7 +160,7 @@ module Sass
155
160
  # @api public
156
161
  DEFAULT_OPTIONS = {
157
162
  :style => :nested,
158
- :load_paths => ['.'],
163
+ :load_paths => [],
159
164
  :cache => true,
160
165
  :cache_location => './.sass-cache',
161
166
  :syntax => :sass,
@@ -166,11 +171,11 @@ module Sass
166
171
  # default values and resolving aliases.
167
172
  #
168
173
  # @param options [{Symbol => Object}] The options hash;
169
- # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
174
+ # see {file:SASS_REFERENCE.md#Options the Sass options documentation}
170
175
  # @return [{Symbol => Object}] The normalized options hash.
171
176
  # @private
172
177
  def self.normalize_options(options)
173
- options = DEFAULT_OPTIONS.merge(options.reject {|k, v| v.nil?})
178
+ options = DEFAULT_OPTIONS.merge(options.reject {|_k, v| v.nil?})
174
179
 
175
180
  # If the `:filename` option is passed in without an importer,
176
181
  # assume it's using the default filesystem importer.
@@ -206,6 +211,8 @@ module Sass
206
211
  when :alternate; options[:property_syntax] = :new
207
212
  when :normal; options[:property_syntax] = :old
208
213
  end
214
+ options[:sourcemap] = :auto if options[:sourcemap] == true
215
+ options[:sourcemap] = :none if options[:sourcemap] == false
209
216
 
210
217
  options
211
218
  end
@@ -218,14 +225,14 @@ module Sass
218
225
  #
219
226
  # @param filename [String] The path to the Sass or SCSS file
220
227
  # @param options [{Symbol => Object}] The options hash;
221
- # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
228
+ # See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
222
229
  # @return [Sass::Engine] The Engine for the given Sass or SCSS file.
223
230
  # @raise [Sass::SyntaxError] if there's an error in the document.
224
231
  def self.for_file(filename, options)
225
232
  had_syntax = options[:syntax]
226
233
 
227
234
  if had_syntax
228
- # Use what was explicitly specificed
235
+ # Use what was explicitly specified
229
236
  elsif filename =~ /\.scss$/
230
237
  options.merge!(:syntax => :scss)
231
238
  elsif filename =~ /\.sass$/
@@ -236,7 +243,7 @@ module Sass
236
243
  end
237
244
 
238
245
  # The options for the Sass engine.
239
- # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
246
+ # See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
240
247
  #
241
248
  # @return [{Symbol => Object}]
242
249
  attr_reader :options
@@ -253,14 +260,17 @@ module Sass
253
260
  # that can be converted to Unicode.
254
261
  # If the template contains an `@charset` declaration,
255
262
  # that overrides the Ruby encoding
256
- # (see {file:SASS_REFERENCE.md#encodings the encoding documentation})
263
+ # (see {file:SASS_REFERENCE.md#Encodings the encoding documentation})
257
264
  # @param options [{Symbol => Object}] An options hash.
258
- # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
265
+ # See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
259
266
  # @see {Sass::Engine.for_file}
260
267
  # @see {Sass::Plugin}
261
268
  def initialize(template, options = {})
262
269
  @options = self.class.normalize_options(options)
263
270
  @template = template
271
+ @checked_encoding = false
272
+ @filename = nil
273
+ @line = nil
264
274
  end
265
275
 
266
276
  # Render the template to CSS.
@@ -271,8 +281,8 @@ module Sass
271
281
  # cannot be converted to UTF-8
272
282
  # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
273
283
  def render
274
- return encode_and_set_charset(_to_tree.render) unless @options[:quiet]
275
- Sass::Util.silence_sass_warnings {encode_and_set_charset(_to_tree.render)}
284
+ return _to_tree.render unless @options[:quiet]
285
+ Sass::Util.silence_sass_warnings {_to_tree.render}
276
286
  end
277
287
 
278
288
  # Render the template to CSS and return the source map.
@@ -315,7 +325,7 @@ module Sass
315
325
  # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
316
326
  def source_encoding
317
327
  check_encoding!
318
- @original_encoding
328
+ @source_encoding
319
329
  end
320
330
 
321
331
  # Gets a set of all the documents
@@ -360,7 +370,10 @@ Error generating source map: couldn't determine public URL for "#{filename}".
360
370
  Without a public URL, there's nothing for the source map to link to.
361
371
  An importer was not set for this file.
362
372
  ERR
363
- elsif Sass::Util.silence_warnings {importer.public_url(filename, sourcemap_dir).nil?}
373
+ elsif Sass::Util.silence_warnings do
374
+ sourcemap_dir = nil if @options[:sourcemap] == :file
375
+ importer.public_url(filename, sourcemap_dir).nil?
376
+ end
364
377
  raise Sass::SyntaxError.new(<<ERR)
365
378
  Error generating source map: couldn't determine public URL for "#{filename}".
366
379
  Without a public URL, there's nothing for the source map to link to.
@@ -375,23 +388,12 @@ ERR
375
388
  rendered << "/*# sourceMappingURL="
376
389
  rendered << Sass::Util.escape_uri(sourcemap_uri)
377
390
  rendered << " */\n"
378
- rendered = encode_and_set_charset(rendered)
379
391
  return rendered, sourcemap
380
392
  end
381
393
 
382
- def encode_and_set_charset(rendered)
383
- return rendered if Sass::Util.ruby1_8?
384
- begin
385
- # Try to convert the result to the original encoding,
386
- # but if that doesn't work fall back on UTF-8
387
- rendered = rendered.encode(source_encoding)
388
- rescue EncodingError
389
- end
390
- rendered.gsub(Regexp.new('\A@charset "(.*?)"'.encode(source_encoding)),
391
- "@charset \"#{source_encoding.name}\"".encode(source_encoding))
392
- end
393
-
394
394
  def _to_tree
395
+ check_encoding!
396
+
395
397
  if (@options[:cache] || @options[:read_cache]) &&
396
398
  @options[:filename] && @options[:importer]
397
399
  key = sassc_key
@@ -403,8 +405,6 @@ ERR
403
405
  end
404
406
  end
405
407
 
406
- check_encoding!
407
-
408
408
  if @options[:syntax] == :scss
409
409
  root = Sass::SCSS::Parser.new(@template, @options[:filename], @options[:importer]).parse
410
410
  else
@@ -436,9 +436,7 @@ ERR
436
436
  def check_encoding!
437
437
  return if @checked_encoding
438
438
  @checked_encoding = true
439
- @template, @original_encoding = Sass::Util.check_sass_encoding(@template) do |msg, line|
440
- raise Sass::SyntaxError.new(msg, :line => line)
441
- end
439
+ @template, @source_encoding = Sass::Util.check_sass_encoding(@template)
442
440
  end
443
441
 
444
442
  def tabulate(string)
@@ -446,7 +444,7 @@ ERR
446
444
  comment_tab_str = nil
447
445
  first = true
448
446
  lines = []
449
- string.gsub(/\r\n|\r|\n/, "\n").scan(/^[^\n]*?$/).each_with_index do |line, index|
447
+ string.scan(/^[^\n]*?$/).each_with_index do |line, index|
450
448
  index += (@options[:line] || 1)
451
449
  if line.strip.empty?
452
450
  lines.last.text << "\n" if lines.last && lines.last.comment?
@@ -484,7 +482,7 @@ ERR
484
482
 
485
483
  line_tabs = line_tab_str.scan(tab_str).size
486
484
  if tab_str * line_tabs != line_tab_str
487
- message = <<END.strip.gsub("\n", ' ')
485
+ message = <<END.strip.tr("\n", ' ')
488
486
  Inconsistent indentation: #{Sass::Shared.human_indentation line_tab_str, true} used for indentation,
489
487
  but the rest of the document was indented using #{Sass::Shared.human_indentation tab_str}.
490
488
  END
@@ -497,15 +495,13 @@ END
497
495
  end
498
496
 
499
497
  # @comment
500
- # rubocop:disable ParameterLists
501
498
  def try_comment(line, last, tab_str, comment_tab_str, index)
502
- # rubocop:enable ParameterLists
503
499
  return unless last && last.comment?
504
500
  # Nested comment stuff must be at least one whitespace char deeper
505
501
  # than the normal indentation
506
502
  return unless line =~ /^#{tab_str}\s/
507
503
  unless line =~ /^(?:#{comment_tab_str})(.*)$/
508
- raise SyntaxError.new(<<MSG.strip.gsub("\n", " "), :line => index)
504
+ raise SyntaxError.new(<<MSG.strip.tr("\n", " "), :line => index)
509
505
  Inconsistent indentation:
510
506
  previous line was indented by #{Sass::Shared.human_indentation comment_tab_str},
511
507
  but this line was indented by #{Sass::Shared.human_indentation line[/^\s*/]}.
@@ -582,8 +578,8 @@ MSG
582
578
  if continued_comment &&
583
579
  child.line == continued_comment.line +
584
580
  continued_comment.lines + 1
585
- continued_comment.value.last.sub!(/ \*\/\Z/, '')
586
- child.value.first.gsub!(/\A\/\*/, ' *')
581
+ continued_comment.value.last.sub!(%r{ \*/\Z}, '')
582
+ child.value.first.gsub!(%r{\A/\*}, ' *')
587
583
  continued_comment.value += ["\n"] + child.value
588
584
  next
589
585
  end
@@ -632,6 +628,11 @@ WARNING
632
628
  raise SyntaxError.new("Invalid property: \"#{line.text}\".",
633
629
  :line => @line) if name.nil? || value.nil?
634
630
 
631
+ @@old_property_deprecation.warn(@options[:filename], @line, <<WARNING)
632
+ Old-style properties like "#{line.text}" are deprecated and will be an error in future versions of Sass.
633
+ Use "#{name}: #{value}" instead.
634
+ WARNING
635
+
635
636
  value_start_offset = name_end_offset = name_start_offset + name.length
636
637
  unless value.empty?
637
638
  # +1 and -1 both compensate for the leading ':', which is part of line.text
@@ -725,9 +726,12 @@ WARNING
725
726
  expr = Sass::Script::Tree::Literal.new(Sass::Script::Value::String.new(""))
726
727
  end_offset = start_offset
727
728
  else
728
- expr = parse_script(value, :offset => to_parser_offset(start_offset))
729
+ expr = parse_script(value,
730
+ :offset => to_parser_offset(start_offset),
731
+ :css_variable => name.start_with?("--"))
729
732
  end_offset = expr.source_range.end_pos.offset - 1
730
733
  end
734
+
731
735
  node = Tree::PropNode.new(parse_interp(name), expr, prop)
732
736
  node.value_source_range = Sass::Source::Range.new(
733
737
  Sass::Source::Position.new(line.index, to_parser_offset(start_offset)),
@@ -784,7 +788,19 @@ WARNING
784
788
  else
785
789
  :normal
786
790
  end
787
- Tree::CommentNode.new(value, type)
791
+ comment = Tree::CommentNode.new(value, type)
792
+ comment.line = line.index
793
+ text = line.text.rstrip
794
+ if text.include?("\n")
795
+ end_offset = text.length - text.rindex("\n")
796
+ else
797
+ end_offset = to_parser_offset(line.offset + text.length)
798
+ end
799
+ comment.source_range = Sass::Source::Range.new(
800
+ Sass::Source::Position.new(@line, to_parser_offset(line.offset)),
801
+ Sass::Source::Position.new(@line + text.count("\n"), end_offset),
802
+ @options[:filename])
803
+ comment
788
804
  else
789
805
  Tree::RuleNode.new(parse_interp(line.text), full_line_range(line))
790
806
  end
@@ -792,16 +808,14 @@ WARNING
792
808
 
793
809
  DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for,
794
810
  :each, :while, :if, :else, :extend, :import, :media, :charset, :content,
795
- :at_root]
811
+ :at_root, :error]
796
812
 
797
- # @comment
798
- # rubocop:disable MethodLength
799
813
  def parse_directive(parent, line, root)
800
814
  directive, whitespace, value = line.text[1..-1].split(/(\s+)/, 2)
801
815
  raise SyntaxError.new("Invalid directive: '@'.") unless directive
802
816
  offset = directive.size + whitespace.size + 1 if whitespace
803
817
 
804
- directive_name = directive.gsub('-', '_').to_sym
818
+ directive_name = directive.tr('-', '_').to_sym
805
819
  if DIRECTIVES.include?(directive_name)
806
820
  return send("parse_#{directive_name}_directive", parent, line, root, value, offset)
807
821
  end
@@ -834,6 +848,14 @@ WARNING
834
848
  Tree::DebugNode.new(parse_script(value, :offset => offset))
835
849
  end
836
850
 
851
+ def parse_error_directive(parent, line, root, value, offset)
852
+ raise SyntaxError.new("Invalid error directive '@error': expected expression.") unless value
853
+ raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath error directives.",
854
+ :line => @line + 1) unless line.children.empty?
855
+ offset = line.offset + line.text.index(value).to_i
856
+ Tree::ErrorNode.new(parse_script(value, :offset => offset))
857
+ end
858
+
837
859
  def parse_extend_directive(parent, line, root, value, offset)
838
860
  raise SyntaxError.new("Invalid extend directive '@extend': expected expression.") unless value
839
861
  raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath extend directives.",
@@ -848,8 +870,6 @@ WARNING
848
870
  )
849
871
  Tree::ExtendNode.new(interp_parsed, optional, selector_range)
850
872
  end
851
- # @comment
852
- # rubocop:enable MethodLength
853
873
 
854
874
  def parse_warn_directive(parent, line, root, value, offset)
855
875
  raise SyntaxError.new("Invalid warn directive '@warn': expected expression.") unless value
@@ -1009,15 +1029,25 @@ WARNING
1009
1029
  script_parser = Sass::Script::Parser.new(scanner, @line, to_parser_offset(offset), @options)
1010
1030
  str = script_parser.parse_string
1011
1031
 
1012
- media_parser = Sass::SCSS::Parser.new(scanner,
1013
- @options[:filename], @options[:importer],
1014
- @line, str.source_range.end_pos.offset)
1015
- if (media = media_parser.parse_media_query_list)
1016
- end_pos = Sass::Source::Position.new(@line, media_parser.offset + 1)
1017
- node = Tree::CssImportNode.new(str, media.to_a)
1018
- else
1032
+ if scanner.eos?
1019
1033
  end_pos = str.source_range.end_pos
1020
1034
  node = Tree::CssImportNode.new(str)
1035
+ else
1036
+ supports_parser = Sass::SCSS::Parser.new(scanner,
1037
+ @options[:filename], @options[:importer],
1038
+ @line, str.source_range.end_pos.offset)
1039
+ supports_condition = supports_parser.parse_supports_clause
1040
+
1041
+ if scanner.eos?
1042
+ node = Tree::CssImportNode.new(str, [], supports_condition)
1043
+ else
1044
+ media_parser = Sass::SCSS::Parser.new(scanner,
1045
+ @options[:filename], @options[:importer],
1046
+ @line, str.source_range.end_pos.offset)
1047
+ media = media_parser.parse_media_query_list
1048
+ end_pos = Sass::Source::Position.new(@line, media_parser.offset + 1)
1049
+ node = Tree::CssImportNode.new(str, media.to_a, supports_condition)
1050
+ end
1021
1051
  end
1022
1052
 
1023
1053
  node.source_range = Sass::Source::Range.new(
@@ -1026,7 +1056,7 @@ WARNING
1026
1056
  return node
1027
1057
  end
1028
1058
 
1029
- unless (str = scanner.scan(Sass::SCSS::RX::STRING))
1059
+ unless (quoted_val = scanner.scan(Sass::SCSS::RX::STRING))
1030
1060
  scanned = scanner.scan(/[^,;]+/)
1031
1061
  node = Tree::ImportNode.new(scanned)
1032
1062
  start_parser_offset = to_parser_offset(offset)
@@ -1038,21 +1068,21 @@ WARNING
1038
1068
  end
1039
1069
 
1040
1070
  start_offset = offset
1041
- offset += str.length
1042
- val = scanner[1] || scanner[2]
1071
+ offset += scanner.matched.length
1072
+ val = Sass::Script::Value::String.value(scanner[1] || scanner[2])
1043
1073
  scanned = scanner.scan(/\s*/)
1044
1074
  if !scanner.match?(/[,;]|$/)
1045
1075
  offset += scanned.length if scanned
1046
1076
  media_parser = Sass::SCSS::Parser.new(scanner,
1047
1077
  @options[:filename], @options[:importer], @line, offset)
1048
1078
  media = media_parser.parse_media_query_list
1049
- node = Tree::CssImportNode.new(str || uri, media.to_a)
1079
+ node = Tree::CssImportNode.new(quoted_val, media.to_a)
1050
1080
  node.source_range = Sass::Source::Range.new(
1051
1081
  Sass::Source::Position.new(@line, to_parser_offset(start_offset)),
1052
1082
  Sass::Source::Position.new(@line, media_parser.offset),
1053
1083
  @options[:filename], @options[:importer])
1054
1084
  elsif val =~ %r{^(https?:)?//}
1055
- node = Tree::CssImportNode.new("url(#{val})")
1085
+ node = Tree::CssImportNode.new(quoted_val)
1056
1086
  node.source_range = Sass::Source::Range.new(
1057
1087
  Sass::Source::Position.new(@line, to_parser_offset(start_offset)),
1058
1088
  Sass::Source::Position.new(@line, to_parser_offset(offset)),
@@ -1124,9 +1154,9 @@ WARNING
1124
1154
  end
1125
1155
 
1126
1156
  def parse_script(script, options = {})
1127
- line = options[:line] || @line
1128
- offset = options[:offset] || @offset + 1
1129
- Script.parse(script, line, offset, @options)
1157
+ line = options.delete(:line) || @line
1158
+ offset = options.delete(:offset) || @offset + 1
1159
+ Script.parse(script, line, offset, @options.merge(options))
1130
1160
  end
1131
1161
 
1132
1162
  def format_comment_text(text, silent)
@@ -1138,9 +1168,10 @@ WARNING
1138
1168
  end
1139
1169
 
1140
1170
  return "/* */" if content.empty?
1141
- content.last.gsub!(/ ?\*\/ *$/, '')
1171
+ content.last.gsub!(%r{ ?\*/ *$}, '')
1172
+ first = content.shift unless removed_first
1142
1173
  content.map! {|l| l.gsub!(/^\*( ?)/, '\1') || (l.empty? ? "" : " ") + l}
1143
- content.first.gsub!(/^ /, '') unless removed_first
1174
+ content.unshift first unless removed_first
1144
1175
  if silent
1145
1176
  "/*" + content.join("\n *") + " */"
1146
1177
  else
@@ -1178,10 +1209,15 @@ WARNING
1178
1209
  res << "\\" * (escapes - 1) << '#{'
1179
1210
  else
1180
1211
  res << "\\" * [0, escapes - 1].max
1181
- # Add 1 to emulate to_parser_offset.
1182
- res << Script::Parser.new(
1183
- scan, line, offset + scan.pos - scan.matched_size + 1, options).
1184
- parse_interpolated
1212
+ if scan[1].include?("\n")
1213
+ line += scan[1].count("\n")
1214
+ offset = scan.matched_size - scan[1].rindex("\n")
1215
+ else
1216
+ offset += scan.matched_size
1217
+ end
1218
+ node = Script::Parser.new(scan, line, offset, options).parse_interpolated
1219
+ offset = node.source_range.end_pos.offset
1220
+ res << node
1185
1221
  end
1186
1222
  end
1187
1223
  res << rest
@@ -41,7 +41,7 @@ module Sass
41
41
  if @#{name}s.include?(name)
42
42
  @#{name}s[name] = value
43
43
  true
44
- elsif @parent
44
+ elsif @parent && !@parent.global?
45
45
  @parent.try_set_#{name}(name, value)
46
46
  else
47
47
  false
@@ -53,6 +53,10 @@ module Sass
53
53
  @#{name}s ||= {}
54
54
  @#{name}s[name.tr('_', '-')] = value
55
55
  end
56
+
57
+ def set_global_#{name}(name, value)
58
+ global_env.set_#{name}(name, value)
59
+ end
56
60
  RUBY
57
61
  end
58
62
  end
@@ -76,28 +80,27 @@ module Sass
76
80
  # Sass::Callable
77
81
  inherited_hash_reader :function
78
82
 
79
- # Whether a warning has been emitted for assigning to the given
80
- # global variable. This is a set of tuples containing the name of
81
- # the variable, its filename, and its line number.
82
- #
83
- # @return [Set<[String, String, int]>]
84
- attr_reader :global_warning_given
85
-
86
- # Whether a warning has been emitted for misusing a deprecated false value.
87
- # This is a set of tuples containing the filename and its line number.
88
- #
89
- # @return [Set<[String, int]>]
90
- attr_reader :deprecated_false_warning_given
91
-
92
83
  # @param options [{Symbol => Object}] The options hash. See
93
- # {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
84
+ # {file:SASS_REFERENCE.md#Options the Sass options documentation}.
94
85
  # @param parent [Environment] See \{#parent}
95
86
  def initialize(parent = nil, options = nil)
96
87
  @parent = parent
97
88
  @options = options || (parent && parent.options) || {}
98
- @stack = Sass::Stack.new if @parent.nil?
99
- @global_warning_given = Set.new
100
- @deprecated_false_warning_given = Set.new
89
+ @stack = @parent.nil? ? Sass::Stack.new : nil
90
+ @caller = nil
91
+ @content = nil
92
+ @filename = nil
93
+ @functions = nil
94
+ @mixins = nil
95
+ @selector = nil
96
+ @vars = nil
97
+ end
98
+
99
+ # Returns whether this is the global environment.
100
+ #
101
+ # @return [Boolean]
102
+ def global?
103
+ @parent.nil?
101
104
  end
102
105
 
103
106
  # The environment of the caller of this environment's mixin or function.
@@ -128,7 +131,7 @@ module Sass
128
131
  #
129
132
  # @return [Environment]
130
133
  def global_env
131
- @global_env ||= @parent.nil? ? self : @parent.global_env
134
+ @global_env ||= global? ? self : @parent.global_env
132
135
  end
133
136
 
134
137
  # The import/mixin stack.
@@ -192,4 +195,21 @@ module Sass
192
195
  @content ||= env.is_a?(ReadOnlyEnvironment) ? env : ReadOnlyEnvironment.new(env, env.options)
193
196
  end
194
197
  end
198
+
199
+ # An environment that can write to in-scope global variables, but doesn't
200
+ # create new variables in the global scope. Useful for top-level control
201
+ # directives.
202
+ class SemiGlobalEnvironment < Environment
203
+ def try_set_var(name, value)
204
+ @vars ||= {}
205
+ if @vars.include?(name)
206
+ @vars[name] = value
207
+ true
208
+ elsif @parent
209
+ @parent.try_set_var(name, value)
210
+ else
211
+ false
212
+ end
213
+ end
214
+ end
195
215
  end