sass 3.1.0 → 3.3.0

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 (260) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING +1 -1
  3. data/MIT-LICENSE +2 -2
  4. data/README.md +29 -17
  5. data/Rakefile +43 -9
  6. data/VERSION +1 -1
  7. data/VERSION_DATE +1 -0
  8. data/VERSION_NAME +1 -1
  9. data/bin/sass +6 -1
  10. data/bin/sass-convert +6 -1
  11. data/bin/scss +6 -1
  12. data/ext/mkrf_conf.rb +27 -0
  13. data/lib/sass/cache_stores/base.rb +7 -3
  14. data/lib/sass/cache_stores/chain.rb +3 -2
  15. data/lib/sass/cache_stores/filesystem.rb +5 -7
  16. data/lib/sass/cache_stores/memory.rb +1 -1
  17. data/lib/sass/cache_stores/null.rb +2 -2
  18. data/lib/sass/callbacks.rb +2 -1
  19. data/lib/sass/css.rb +168 -53
  20. data/lib/sass/engine.rb +502 -174
  21. data/lib/sass/environment.rb +151 -111
  22. data/lib/sass/error.rb +7 -7
  23. data/lib/sass/exec.rb +176 -60
  24. data/lib/sass/features.rb +40 -0
  25. data/lib/sass/importers/base.rb +46 -7
  26. data/lib/sass/importers/deprecated_path.rb +51 -0
  27. data/lib/sass/importers/filesystem.rb +113 -30
  28. data/lib/sass/importers.rb +1 -0
  29. data/lib/sass/logger/base.rb +30 -0
  30. data/lib/sass/logger/log_level.rb +45 -0
  31. data/lib/sass/logger.rb +12 -0
  32. data/lib/sass/media.rb +213 -0
  33. data/lib/sass/plugin/compiler.rb +194 -104
  34. data/lib/sass/plugin/configuration.rb +18 -25
  35. data/lib/sass/plugin/merb.rb +1 -1
  36. data/lib/sass/plugin/staleness_checker.rb +37 -11
  37. data/lib/sass/plugin.rb +10 -13
  38. data/lib/sass/railtie.rb +2 -1
  39. data/lib/sass/repl.rb +5 -6
  40. data/lib/sass/script/css_lexer.rb +8 -4
  41. data/lib/sass/script/css_parser.rb +5 -2
  42. data/lib/sass/script/functions.rb +1547 -618
  43. data/lib/sass/script/lexer.rb +122 -72
  44. data/lib/sass/script/parser.rb +304 -135
  45. data/lib/sass/script/tree/funcall.rb +306 -0
  46. data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +43 -13
  47. data/lib/sass/script/tree/list_literal.rb +77 -0
  48. data/lib/sass/script/tree/literal.rb +45 -0
  49. data/lib/sass/script/tree/map_literal.rb +64 -0
  50. data/lib/sass/script/{node.rb → tree/node.rb} +30 -12
  51. data/lib/sass/script/{operation.rb → tree/operation.rb} +33 -21
  52. data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +14 -4
  53. data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +21 -9
  54. data/lib/sass/script/tree/variable.rb +57 -0
  55. data/lib/sass/script/tree.rb +15 -0
  56. data/lib/sass/script/value/arg_list.rb +36 -0
  57. data/lib/sass/script/value/base.rb +238 -0
  58. data/lib/sass/script/value/bool.rb +40 -0
  59. data/lib/sass/script/{color.rb → value/color.rb} +256 -74
  60. data/lib/sass/script/value/deprecated_false.rb +55 -0
  61. data/lib/sass/script/value/helpers.rb +155 -0
  62. data/lib/sass/script/value/list.rb +128 -0
  63. data/lib/sass/script/value/map.rb +70 -0
  64. data/lib/sass/script/value/null.rb +49 -0
  65. data/lib/sass/script/{number.rb → value/number.rb} +115 -62
  66. data/lib/sass/script/{string.rb → value/string.rb} +9 -11
  67. data/lib/sass/script/value.rb +12 -0
  68. data/lib/sass/script.rb +35 -9
  69. data/lib/sass/scss/css_parser.rb +2 -12
  70. data/lib/sass/scss/parser.rb +657 -230
  71. data/lib/sass/scss/rx.rb +17 -12
  72. data/lib/sass/scss/static_parser.rb +37 -6
  73. data/lib/sass/scss.rb +0 -1
  74. data/lib/sass/selector/abstract_sequence.rb +35 -3
  75. data/lib/sass/selector/comma_sequence.rb +29 -14
  76. data/lib/sass/selector/sequence.rb +371 -74
  77. data/lib/sass/selector/simple.rb +28 -13
  78. data/lib/sass/selector/simple_sequence.rb +163 -36
  79. data/lib/sass/selector.rb +138 -36
  80. data/lib/sass/shared.rb +3 -5
  81. data/lib/sass/source/map.rb +196 -0
  82. data/lib/sass/source/position.rb +39 -0
  83. data/lib/sass/source/range.rb +41 -0
  84. data/lib/sass/stack.rb +126 -0
  85. data/lib/sass/supports.rb +228 -0
  86. data/lib/sass/tree/at_root_node.rb +82 -0
  87. data/lib/sass/tree/comment_node.rb +34 -29
  88. data/lib/sass/tree/content_node.rb +9 -0
  89. data/lib/sass/tree/css_import_node.rb +60 -0
  90. data/lib/sass/tree/debug_node.rb +3 -3
  91. data/lib/sass/tree/directive_node.rb +33 -3
  92. data/lib/sass/tree/each_node.rb +9 -9
  93. data/lib/sass/tree/extend_node.rb +20 -6
  94. data/lib/sass/tree/for_node.rb +6 -6
  95. data/lib/sass/tree/function_node.rb +12 -4
  96. data/lib/sass/tree/if_node.rb +2 -15
  97. data/lib/sass/tree/import_node.rb +11 -5
  98. data/lib/sass/tree/media_node.rb +27 -11
  99. data/lib/sass/tree/mixin_def_node.rb +15 -4
  100. data/lib/sass/tree/mixin_node.rb +27 -7
  101. data/lib/sass/tree/node.rb +69 -35
  102. data/lib/sass/tree/prop_node.rb +47 -31
  103. data/lib/sass/tree/return_node.rb +4 -3
  104. data/lib/sass/tree/root_node.rb +20 -4
  105. data/lib/sass/tree/rule_node.rb +37 -26
  106. data/lib/sass/tree/supports_node.rb +38 -0
  107. data/lib/sass/tree/trace_node.rb +33 -0
  108. data/lib/sass/tree/variable_node.rb +10 -4
  109. data/lib/sass/tree/visitors/base.rb +5 -8
  110. data/lib/sass/tree/visitors/check_nesting.rb +67 -52
  111. data/lib/sass/tree/visitors/convert.rb +134 -53
  112. data/lib/sass/tree/visitors/cssize.rb +245 -51
  113. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  114. data/lib/sass/tree/visitors/extend.rb +68 -0
  115. data/lib/sass/tree/visitors/perform.rb +331 -105
  116. data/lib/sass/tree/visitors/set_options.rb +125 -0
  117. data/lib/sass/tree/visitors/to_css.rb +259 -95
  118. data/lib/sass/tree/warn_node.rb +3 -3
  119. data/lib/sass/tree/while_node.rb +3 -3
  120. data/lib/sass/util/cross_platform_random.rb +19 -0
  121. data/lib/sass/util/multibyte_string_scanner.rb +157 -0
  122. data/lib/sass/util/normalized_map.rb +130 -0
  123. data/lib/sass/util/ordered_hash.rb +192 -0
  124. data/lib/sass/util/subset_map.rb +11 -2
  125. data/lib/sass/util/test.rb +9 -0
  126. data/lib/sass/util.rb +565 -39
  127. data/lib/sass/version.rb +27 -15
  128. data/lib/sass.rb +39 -4
  129. data/test/sass/cache_test.rb +15 -0
  130. data/test/sass/compiler_test.rb +223 -0
  131. data/test/sass/conversion_test.rb +901 -107
  132. data/test/sass/css2sass_test.rb +94 -0
  133. data/test/sass/engine_test.rb +1059 -164
  134. data/test/sass/exec_test.rb +86 -0
  135. data/test/sass/extend_test.rb +933 -837
  136. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  137. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  138. data/test/sass/functions_test.rb +995 -136
  139. data/test/sass/importer_test.rb +338 -18
  140. data/test/sass/logger_test.rb +58 -0
  141. data/test/sass/more_results/more_import.css +2 -2
  142. data/test/sass/plugin_test.rb +114 -30
  143. data/test/sass/results/cached_import_option.css +3 -0
  144. data/test/sass/results/filename_fn.css +3 -0
  145. data/test/sass/results/import.css +2 -2
  146. data/test/sass/results/import_charset.css +1 -0
  147. data/test/sass/results/import_charset_1_8.css +1 -0
  148. data/test/sass/results/import_charset_ibm866.css +1 -0
  149. data/test/sass/results/import_content.css +1 -0
  150. data/test/sass/results/script.css +1 -1
  151. data/test/sass/results/scss_import.css +2 -2
  152. data/test/sass/results/units.css +2 -2
  153. data/test/sass/script_conversion_test.rb +43 -1
  154. data/test/sass/script_test.rb +380 -36
  155. data/test/sass/scss/css_test.rb +257 -75
  156. data/test/sass/scss/scss_test.rb +2322 -110
  157. data/test/sass/source_map_test.rb +887 -0
  158. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  159. data/test/sass/templates/_double_import_loop2.sass +1 -0
  160. data/test/sass/templates/_filename_fn_import.scss +11 -0
  161. data/test/sass/templates/_imported_content.sass +3 -0
  162. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  163. data/test/sass/templates/bork5.sass +3 -0
  164. data/test/sass/templates/cached_import_option.scss +3 -0
  165. data/test/sass/templates/double_import_loop1.sass +1 -0
  166. data/test/sass/templates/filename_fn.scss +18 -0
  167. data/test/sass/templates/import_charset.sass +2 -0
  168. data/test/sass/templates/import_charset_1_8.sass +2 -0
  169. data/test/sass/templates/import_charset_ibm866.sass +2 -0
  170. data/test/sass/templates/import_content.sass +4 -0
  171. data/test/sass/templates/same_name_different_ext.sass +2 -0
  172. data/test/sass/templates/same_name_different_ext.scss +1 -0
  173. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  174. data/test/sass/templates/single_import_loop.sass +1 -0
  175. data/test/sass/templates/subdir/import_up1.scss +1 -0
  176. data/test/sass/templates/subdir/import_up2.scss +1 -0
  177. data/test/sass/test_helper.rb +1 -1
  178. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  179. data/test/sass/util/normalized_map_test.rb +51 -0
  180. data/test/sass/util_test.rb +183 -0
  181. data/test/sass/value_helpers_test.rb +181 -0
  182. data/test/test_helper.rb +45 -5
  183. data/vendor/listen/CHANGELOG.md +228 -0
  184. data/vendor/listen/CONTRIBUTING.md +38 -0
  185. data/vendor/listen/Gemfile +30 -0
  186. data/vendor/listen/Guardfile +8 -0
  187. data/vendor/{fssm → listen}/LICENSE +1 -1
  188. data/vendor/listen/README.md +315 -0
  189. data/vendor/listen/Rakefile +47 -0
  190. data/vendor/listen/Vagrantfile +96 -0
  191. data/vendor/listen/lib/listen/adapter.rb +214 -0
  192. data/vendor/listen/lib/listen/adapters/bsd.rb +112 -0
  193. data/vendor/listen/lib/listen/adapters/darwin.rb +85 -0
  194. data/vendor/listen/lib/listen/adapters/linux.rb +113 -0
  195. data/vendor/listen/lib/listen/adapters/polling.rb +67 -0
  196. data/vendor/listen/lib/listen/adapters/windows.rb +87 -0
  197. data/vendor/listen/lib/listen/dependency_manager.rb +126 -0
  198. data/vendor/listen/lib/listen/directory_record.rb +371 -0
  199. data/vendor/listen/lib/listen/listener.rb +225 -0
  200. data/vendor/listen/lib/listen/multi_listener.rb +143 -0
  201. data/vendor/listen/lib/listen/turnstile.rb +28 -0
  202. data/vendor/listen/lib/listen/version.rb +3 -0
  203. data/vendor/listen/lib/listen.rb +40 -0
  204. data/vendor/listen/listen.gemspec +22 -0
  205. data/vendor/listen/spec/listen/adapter_spec.rb +183 -0
  206. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  207. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
  208. data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
  209. data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
  210. data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
  211. data/vendor/listen/spec/listen/dependency_manager_spec.rb +107 -0
  212. data/vendor/listen/spec/listen/directory_record_spec.rb +1225 -0
  213. data/vendor/listen/spec/listen/listener_spec.rb +169 -0
  214. data/vendor/listen/spec/listen/multi_listener_spec.rb +174 -0
  215. data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
  216. data/vendor/listen/spec/listen_spec.rb +73 -0
  217. data/vendor/listen/spec/spec_helper.rb +21 -0
  218. data/vendor/listen/spec/support/adapter_helper.rb +629 -0
  219. data/vendor/listen/spec/support/directory_record_helper.rb +55 -0
  220. data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
  221. data/vendor/listen/spec/support/listeners_helper.rb +156 -0
  222. data/vendor/listen/spec/support/platform_helper.rb +15 -0
  223. metadata +344 -271
  224. data/lib/sass/less.rb +0 -382
  225. data/lib/sass/script/bool.rb +0 -18
  226. data/lib/sass/script/funcall.rb +0 -162
  227. data/lib/sass/script/list.rb +0 -76
  228. data/lib/sass/script/literal.rb +0 -245
  229. data/lib/sass/script/variable.rb +0 -54
  230. data/lib/sass/scss/sass_parser.rb +0 -11
  231. data/test/sass/less_conversion_test.rb +0 -653
  232. data/vendor/fssm/README.markdown +0 -55
  233. data/vendor/fssm/Rakefile +0 -59
  234. data/vendor/fssm/VERSION.yml +0 -5
  235. data/vendor/fssm/example.rb +0 -9
  236. data/vendor/fssm/fssm.gemspec +0 -77
  237. data/vendor/fssm/lib/fssm/backends/fsevents.rb +0 -36
  238. data/vendor/fssm/lib/fssm/backends/inotify.rb +0 -26
  239. data/vendor/fssm/lib/fssm/backends/polling.rb +0 -25
  240. data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +0 -131
  241. data/vendor/fssm/lib/fssm/monitor.rb +0 -26
  242. data/vendor/fssm/lib/fssm/path.rb +0 -91
  243. data/vendor/fssm/lib/fssm/pathname.rb +0 -502
  244. data/vendor/fssm/lib/fssm/state/directory.rb +0 -57
  245. data/vendor/fssm/lib/fssm/state/file.rb +0 -24
  246. data/vendor/fssm/lib/fssm/support.rb +0 -63
  247. data/vendor/fssm/lib/fssm/tree.rb +0 -176
  248. data/vendor/fssm/lib/fssm.rb +0 -33
  249. data/vendor/fssm/profile/prof-cache.rb +0 -40
  250. data/vendor/fssm/profile/prof-fssm-pathname.html +0 -1231
  251. data/vendor/fssm/profile/prof-pathname.rb +0 -68
  252. data/vendor/fssm/profile/prof-plain-pathname.html +0 -988
  253. data/vendor/fssm/profile/prof.html +0 -2379
  254. data/vendor/fssm/spec/path_spec.rb +0 -75
  255. data/vendor/fssm/spec/root/duck/quack.txt +0 -0
  256. data/vendor/fssm/spec/root/file.css +0 -0
  257. data/vendor/fssm/spec/root/file.rb +0 -0
  258. data/vendor/fssm/spec/root/file.yml +0 -0
  259. data/vendor/fssm/spec/root/moo/cow.txt +0 -0
  260. data/vendor/fssm/spec/spec_helper.rb +0 -14
@@ -0,0 +1,125 @@
1
+ # A visitor for setting options on the Sass tree
2
+ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
3
+ # @param root [Tree::Node] The root node of the tree to visit.
4
+ # @param options [{Symbol => Object}] The options has to set.
5
+ def self.visit(root, options); new(options).send(:visit, root); end
6
+
7
+ protected
8
+
9
+ def initialize(options)
10
+ @options = options
11
+ end
12
+
13
+ def visit(node)
14
+ node.instance_variable_set('@options', @options)
15
+ super
16
+ end
17
+
18
+ def visit_debug(node)
19
+ node.expr.options = @options
20
+ yield
21
+ end
22
+
23
+ def visit_each(node)
24
+ node.list.options = @options
25
+ yield
26
+ end
27
+
28
+ def visit_extend(node)
29
+ node.selector.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)}
30
+ yield
31
+ end
32
+
33
+ def visit_for(node)
34
+ node.from.options = @options
35
+ node.to.options = @options
36
+ yield
37
+ end
38
+
39
+ def visit_function(node)
40
+ node.args.each do |k, v|
41
+ k.options = @options
42
+ v.options = @options if v
43
+ end
44
+ yield
45
+ end
46
+
47
+ def visit_if(node)
48
+ node.expr.options = @options if node.expr
49
+ visit(node.else) if node.else
50
+ yield
51
+ end
52
+
53
+ def visit_import(node)
54
+ # We have no good way of propagating the new options through an Engine
55
+ # instance, so we just null it out. This also lets us avoid caching an
56
+ # imported Engine along with the importing source tree.
57
+ node.imported_file = nil
58
+ yield
59
+ end
60
+
61
+ def visit_mixindef(node)
62
+ node.args.each do |k, v|
63
+ k.options = @options
64
+ v.options = @options if v
65
+ end
66
+ yield
67
+ end
68
+
69
+ def visit_mixin(node)
70
+ node.args.each {|a| a.options = @options}
71
+ node.keywords.each {|k, v| v.options = @options}
72
+ yield
73
+ end
74
+
75
+ def visit_prop(node)
76
+ node.name.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)}
77
+ node.value.options = @options
78
+ yield
79
+ end
80
+
81
+ def visit_return(node)
82
+ node.expr.options = @options
83
+ yield
84
+ end
85
+
86
+ def visit_rule(node)
87
+ node.rule.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)}
88
+ yield
89
+ end
90
+
91
+ def visit_variable(node)
92
+ node.expr.options = @options
93
+ yield
94
+ end
95
+
96
+ def visit_warn(node)
97
+ node.expr.options = @options
98
+ yield
99
+ end
100
+
101
+ def visit_while(node)
102
+ node.expr.options = @options
103
+ yield
104
+ end
105
+
106
+ def visit_directive(node)
107
+ node.value.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)}
108
+ yield
109
+ end
110
+
111
+ def visit_media(node)
112
+ node.query.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)}
113
+ yield
114
+ end
115
+
116
+ def visit_cssimport(node)
117
+ node.query.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)} if node.query
118
+ yield
119
+ end
120
+
121
+ def visit_supports(node)
122
+ node.condition.options = @options
123
+ yield
124
+ end
125
+ end
@@ -1,11 +1,25 @@
1
1
  # A visitor for converting a Sass tree into CSS.
2
2
  class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
3
- protected
3
+ # The source mapping for the generated CSS file. This is only set if
4
+ # `build_source_mapping` is passed to the constructor and \{Sass::Engine#render} has been
5
+ # run.
6
+ attr_reader :source_mapping
4
7
 
5
- def initialize
8
+ # @param build_source_mapping [Boolean] Whether to build a
9
+ # \{Sass::Source::Map} while creating the CSS output. The mapping will
10
+ # be available from \{#source\_mapping} after the visitor has completed.
11
+ def initialize(build_source_mapping = false)
6
12
  @tabs = 0
13
+ @line = 1
14
+ @offset = 1
15
+ @result = ""
16
+ @source_mapping = Sass::Source::Map.new if build_source_mapping
7
17
  end
8
18
 
19
+ # Runs the visitor on `node`.
20
+ #
21
+ # @param node [Sass::Tree::Node] The root node of the tree to convert to CSS>
22
+ # @return [String] The CSS output.
9
23
  def visit(node)
10
24
  super
11
25
  rescue Sass::SyntaxError => e
@@ -13,6 +27,8 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
13
27
  raise e
14
28
  end
15
29
 
30
+ protected
31
+
16
32
  def with_tabs(tabs)
17
33
  old_tabs, @tabs = @tabs, tabs
18
34
  yield
@@ -20,197 +36,345 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
20
36
  @tabs = old_tabs
21
37
  end
22
38
 
39
+ # Associate all output produced in a block with a given node. Used for source
40
+ # mapping.
41
+ def for_node(node, attr_prefix = nil)
42
+ return yield unless @source_mapping
43
+ start_pos = Sass::Source::Position.new(@line, @offset)
44
+ yield
45
+
46
+ range_attr = attr_prefix ? :"#{attr_prefix}_source_range" : :source_range
47
+ return if node.invisible? || !node.send(range_attr)
48
+ source_range = node.send(range_attr)
49
+ target_end_pos = Sass::Source::Position.new(@line, @offset)
50
+ target_range = Sass::Source::Range.new(start_pos, target_end_pos, nil)
51
+ @source_mapping.add(source_range, target_range)
52
+ end
53
+
54
+ # Move the output cursor back `chars` characters.
55
+ def erase!(chars)
56
+ return if chars == 0
57
+ str = @result.slice!(-chars..-1)
58
+ newlines = str.count("\n")
59
+ if newlines > 0
60
+ @line -= newlines
61
+ @offset = @result[@result.rindex("\n") || 0..-1].size
62
+ else
63
+ @offset -= chars
64
+ end
65
+ end
66
+
67
+ # Avoid allocating lots of new strings for `#output`. This is important
68
+ # because `#output` is called all the time.
69
+ NEWLINE = "\n"
70
+
71
+ # Add `s` to the output string and update the line and offset information
72
+ # accordingly.
73
+ def output(s)
74
+ if @lstrip
75
+ s = s.gsub(/\A\s+/, "")
76
+ @lstrip = false
77
+ end
78
+
79
+ newlines = s.count(NEWLINE)
80
+ if newlines > 0
81
+ @line += newlines
82
+ @offset = s[s.rindex(NEWLINE)..-1].size
83
+ else
84
+ @offset += s.size
85
+ end
86
+
87
+ @result << s
88
+ end
89
+
90
+ # Strip all trailing whitespace from the output string.
91
+ def rstrip!
92
+ erase! @result.length - 1 - (@result.rindex(/[^\s]/) || -1)
93
+ end
94
+
95
+ # lstrip the first output in the given block.
96
+ def lstrip
97
+ old_lstrip = @lstrip
98
+ @lstrip = true
99
+ yield
100
+ ensure
101
+ @lstrip = @lstrip && old_lstrip
102
+ end
103
+
104
+ # Prepend `prefix` to the output string.
105
+ def prepend!(prefix)
106
+ @result.insert 0, prefix
107
+ return unless @source_mapping
108
+
109
+ line_delta = prefix.count("\n")
110
+ offset_delta = prefix.gsub(/.*\n/, '').size
111
+ @source_mapping.shift_output_offsets(offset_delta)
112
+ @source_mapping.shift_output_lines(line_delta)
113
+ end
114
+
23
115
  def visit_root(node)
24
- result = String.new
25
116
  node.children.each do |child|
26
117
  next if child.invisible?
27
- child_str = visit(child)
28
- result << child_str + (node.style == :compressed ? '' : "\n")
29
- end
30
- result.rstrip!
31
- return "" if result.empty?
32
- result << "\n"
33
- unless Sass::Util.ruby1_8? || result.ascii_only?
34
- if node.children.first.is_a?(Sass::Tree::CharsetNode)
35
- begin
36
- encoding = node.children.first.name
37
- # Default to big-endian encoding, because we have to decide somehow
38
- encoding << 'BE' if encoding =~ /\Autf-(16|32)\Z/i
39
- result = result.encode(Encoding.find(encoding))
40
- rescue EncodingError
118
+ visit(child)
119
+ unless node.style == :compressed
120
+ output "\n"
121
+ if child.is_a?(Sass::Tree::DirectiveNode) && child.has_children && !child.bubbles?
122
+ output "\n"
41
123
  end
42
124
  end
125
+ end
126
+ rstrip!
127
+ return "" if @result.empty?
43
128
 
44
- result = "@charset \"#{result.encoding.name}\";#{
45
- node.style == :compressed ? '' : "\n"
46
- }".encode(result.encoding) + result
129
+ output "\n"
130
+ return @result if Sass::Util.ruby1_8? || @result.ascii_only?
131
+
132
+ if node.children.first.is_a?(Sass::Tree::CharsetNode)
133
+ begin
134
+ encoding = node.children.first.name
135
+ # Default to big-endian encoding, because we have to decide somehow
136
+ encoding << 'BE' if encoding =~ /\Autf-(16|32)\Z/i
137
+ @result = @result.encode(Encoding.find(encoding))
138
+ rescue EncodingError
139
+ end
47
140
  end
48
- result
141
+
142
+ prepend! "@charset \"#{@result.encoding.name}\";#{
143
+ node.style == :compressed ? '' : "\n"
144
+ }".encode(@result.encoding)
145
+ @result
49
146
  rescue Sass::SyntaxError => e
50
147
  e.sass_template ||= node.template
51
148
  raise e
52
149
  end
53
150
 
54
151
  def visit_charset(node)
55
- "@charset \"#{node.name}\";"
56
- end
152
+ for_node(node) {output("@charset \"#{node.name}\";")}
153
+ end
57
154
 
58
155
  def visit_comment(node)
59
156
  return if node.invisible?
60
- spaces = (' ' * [@tabs - node.value[/^ */].size, 0].max)
157
+ spaces = (' ' * [@tabs - node.resolved_value[/^ */].size, 0].max)
61
158
 
62
- content = node.value.gsub(/^/, spaces).gsub(%r{^(\s*)//(.*)$}) do |md|
63
- "#{$1}/*#{$2} */"
159
+ content = node.resolved_value.gsub(/^/, spaces)
160
+ if node.type == :silent
161
+ content.gsub!(%r{^(\s*)//(.*)$}) {|md| "#{$1}/*#{$2} */"}
64
162
  end
65
- if content =~ /[^\\]\#\{.*\}/
66
- Sass::Util.sass_warn <<MESSAGE
67
- WARNING:
68
- On line #{node.line}#{" of '#{node.filename}'" if node.filename}
69
- Comments will evaluate the contents of interpolations (\#{ ... }) in Sass 3.2.
70
- Please escape the interpolation by adding a backslash before the hash sign.
71
- MESSAGE
72
- elsif content =~ /\\\#\{.*\}/
73
- content.gsub!(/\\(\#\{.*\})/, '\1')
163
+ if (node.style == :compact || node.style == :compressed) && node.type != :loud
164
+ content.gsub!(/\n +(\* *(?!\/))?/, ' ')
74
165
  end
75
- content.gsub!(/\n +(\* *(?!\/))?/, ' ') if (node.style == :compact || node.style == :compressed) && !node.loud
76
- content
166
+ for_node(node) {output(content)}
77
167
  end
78
168
 
169
+ # @comment
170
+ # rubocop:disable MethodLength
79
171
  def visit_directive(node)
80
- return node.value + ";" unless node.has_children
81
- return node.value + " {}" if node.children.empty?
82
- result = if node.style == :compressed
83
- "#{node.value}{"
84
- else
85
- "#{' ' * @tabs}#{node.value} {" + (node.style == :compact ? ' ' : "\n")
86
- end
172
+ was_in_directive = @in_directive
173
+ tab_str = ' ' * @tabs
174
+ if !node.has_children || node.children.empty?
175
+ output(tab_str)
176
+ for_node(node) {output(node.resolved_value)}
177
+ output(!node.has_children ? ";" : " {}")
178
+ return
179
+ end
180
+
181
+ @in_directive = @in_directive || !node.is_a?(Sass::Tree::MediaNode)
182
+ output(tab_str) if node.style != :compressed
183
+ for_node(node) {output(node.resolved_value)}
184
+ output(node.style == :compressed ? "{" : " {")
185
+ output(node.style == :compact ? ' ' : "\n") if node.style != :compressed
186
+
87
187
  was_prop = false
88
188
  first = true
89
189
  node.children.each do |child|
90
190
  next if child.invisible?
91
191
  if node.style == :compact
92
192
  if child.is_a?(Sass::Tree::PropNode)
93
- with_tabs(first || was_prop ? 0 : @tabs + 1) {result << visit(child) << ' '}
193
+ with_tabs(first || was_prop ? 0 : @tabs + 1) do
194
+ visit(child)
195
+ output(' ')
196
+ end
94
197
  else
95
- result[-1] = "\n" if was_prop
96
- rendered = with_tabs(@tabs + 1) {visit(child).dup}
97
- rendered = rendered.lstrip if first
98
- result << rendered.rstrip + "\n"
198
+ if was_prop
199
+ erase! 1
200
+ output "\n"
201
+ end
202
+
203
+ if first
204
+ lstrip {with_tabs(@tabs + 1) {visit(child)}}
205
+ else
206
+ with_tabs(@tabs + 1) {visit(child)}
207
+ end
208
+
209
+ rstrip!
210
+ output "\n"
99
211
  end
100
212
  was_prop = child.is_a?(Sass::Tree::PropNode)
101
213
  first = false
102
214
  elsif node.style == :compressed
103
- result << (was_prop ? ";" : "") << with_tabs(0) {visit(child)}
215
+ output(was_prop ? ";" : "")
216
+ with_tabs(0) {visit(child)}
104
217
  was_prop = child.is_a?(Sass::Tree::PropNode)
105
218
  else
106
- result << with_tabs(@tabs + 1) {visit(child)} + "\n"
219
+ with_tabs(@tabs + 1) {visit(child)}
220
+ output "\n"
107
221
  end
108
222
  end
109
- result.rstrip + if node.style == :compressed
110
- "}"
111
- else
112
- (node.style == :expanded ? "\n" : " ") + "}\n"
113
- end
223
+ rstrip!
224
+ if node.style == :expanded
225
+ output("\n#{tab_str}")
226
+ elsif node.style != :compressed
227
+ output(" ")
228
+ end
229
+ output("}")
230
+ ensure
231
+ @in_directive = was_in_directive
114
232
  end
233
+ # @comment
234
+ # rubocop:enable MethodLength
115
235
 
116
236
  def visit_media(node)
117
- str = with_tabs(@tabs + node.tabs) {visit_directive(node)}
118
- str.gsub!(/\n\Z/, '') unless node.style == :compressed || node.group_end
119
- str
237
+ with_tabs(@tabs + node.tabs) {visit_directive(node)}
238
+ output("\n") if node.group_end
239
+ end
240
+
241
+ def visit_supports(node)
242
+ visit_media(node)
243
+ end
244
+
245
+ def visit_cssimport(node)
246
+ visit_directive(node)
120
247
  end
121
248
 
122
249
  def visit_prop(node)
250
+ return if node.resolved_value.empty?
123
251
  tab_str = ' ' * (@tabs + node.tabs)
252
+ output(tab_str)
253
+ for_node(node, :name) {output(node.resolved_name)}
124
254
  if node.style == :compressed
125
- "#{tab_str}#{node.resolved_name}:#{node.resolved_value}#{'!important' if node.important}"
255
+ output(":")
256
+ for_node(node, :value) {output(node.resolved_value)}
126
257
  else
127
- "#{tab_str}#{node.resolved_name}: #{node.resolved_value}#{' !important' if node.important};"
258
+ output(": ")
259
+ for_node(node, :value) {output(node.resolved_value)}
260
+ output(";")
128
261
  end
129
262
  end
130
263
 
264
+ # @comment
265
+ # rubocop:disable MethodLength
131
266
  def visit_rule(node)
132
267
  with_tabs(@tabs + node.tabs) do
133
268
  rule_separator = node.style == :compressed ? ',' : ', '
134
269
  line_separator =
135
270
  case node.style
136
- when :nested, :expanded; "\n"
137
- when :compressed; ""
138
- else; " "
271
+ when :nested, :expanded; "\n"
272
+ when :compressed; ""
273
+ else; " "
139
274
  end
140
275
  rule_indent = ' ' * @tabs
141
- per_rule_indent, total_indent = [:nested, :expanded].include?(node.style) ? [rule_indent, ''] : ['', rule_indent]
276
+ per_rule_indent, total_indent = if [:nested, :expanded].include?(node.style)
277
+ [rule_indent, '']
278
+ else
279
+ ['', rule_indent]
280
+ end
142
281
 
143
282
  joined_rules = node.resolved_rules.members.map do |seq|
283
+ next if seq.has_placeholder?
144
284
  rule_part = seq.to_a.join
145
- rule_part.gsub!(/\s*([^,])\s*\n\s*/m, '\1 ') if node.style == :compressed
285
+ if node.style == :compressed
286
+ rule_part.gsub!(/([^,])\s*\n\s*/m, '\1 ')
287
+ rule_part.gsub!(/\s*([,+>])\s*/m, '\1')
288
+ rule_part.strip!
289
+ end
146
290
  rule_part
147
- end.join(rule_separator)
291
+ end.compact.join(rule_separator)
148
292
 
149
- joined_rules.sub!(/\A\s*/, per_rule_indent)
293
+ joined_rules.lstrip!
150
294
  joined_rules.gsub!(/\s*\n\s*/, "#{line_separator}#{per_rule_indent}")
151
- total_rule = total_indent << joined_rules
152
295
 
153
- to_return = ''
154
296
  old_spaces = ' ' * @tabs
155
- spaces = ' ' * (@tabs + 1)
156
297
  if node.style != :compressed
157
- if node.options[:debug_info]
158
- to_return << visit(debug_info_rule(node.debug_info, node.options)) << "\n"
298
+ if node.options[:debug_info] && !@in_directive
299
+ visit(debug_info_rule(node.debug_info, node.options))
300
+ output "\n"
301
+ elsif node.options[:trace_selectors]
302
+ output("#{old_spaces}/* ")
303
+ output(node.stack_trace.gsub("\n", "\n #{old_spaces}"))
304
+ output(" */\n")
159
305
  elsif node.options[:line_comments]
160
- to_return << "#{old_spaces}/* line #{node.line}"
306
+ output("#{old_spaces}/* line #{node.line}")
161
307
 
162
308
  if node.filename
163
- relative_filename = if node.options[:css_filename]
164
- begin
165
- Pathname.new(node.filename).relative_path_from(
166
- Pathname.new(File.dirname(node.options[:css_filename]))).to_s
167
- rescue ArgumentError
168
- nil
309
+ relative_filename =
310
+ if node.options[:css_filename]
311
+ begin
312
+ Sass::Util.pathname(node.filename).relative_path_from(
313
+ Sass::Util.pathname(File.dirname(node.options[:css_filename]))).to_s
314
+ rescue ArgumentError
315
+ nil
316
+ end
169
317
  end
170
- end
171
318
  relative_filename ||= node.filename
172
- to_return << ", #{relative_filename}"
319
+ output(", #{relative_filename}")
173
320
  end
174
321
 
175
- to_return << " */\n"
322
+ output(" */\n")
176
323
  end
177
324
  end
178
325
 
326
+ end_props, trailer, tabs = '', '', 0
179
327
  if node.style == :compact
180
- properties = with_tabs(0) {node.children.map {|a| visit(a)}.join(' ')}
181
- to_return << "#{total_rule} { #{properties} }#{"\n" if node.group_end}"
328
+ separator, end_props, bracket = ' ', ' ', ' { '
329
+ trailer = "\n" if node.group_end
182
330
  elsif node.style == :compressed
183
- properties = with_tabs(0) {node.children.map {|a| visit(a)}.join(';')}
184
- to_return << "#{total_rule}{#{properties}}"
331
+ separator, bracket = ';', '{'
185
332
  else
186
- properties = with_tabs(@tabs + 1) {node.children.map {|a| visit(a)}.join("\n")}
333
+ tabs = @tabs + 1
334
+ separator, bracket = "\n", " {\n"
335
+ trailer = "\n" if node.group_end
187
336
  end_props = (node.style == :expanded ? "\n" + old_spaces : ' ')
188
- to_return << "#{total_rule} {\n#{properties}#{end_props}}#{"\n" if node.group_end}"
337
+ end
338
+ output(total_indent + per_rule_indent)
339
+ for_node(node, :selector) {output(joined_rules)}
340
+ output(bracket)
341
+
342
+ with_tabs(tabs) do
343
+ node.children.each_with_index do |child, i|
344
+ output(separator) if i > 0
345
+ visit(child)
346
+ end
189
347
  end
190
348
 
191
- to_return
349
+ output(end_props)
350
+ output("}" + trailer)
192
351
  end
193
352
  end
353
+ # @comment
354
+ # rubocop:enable MethodLength
194
355
 
195
356
  private
196
357
 
197
358
  def debug_info_rule(debug_info, options)
198
- node = Sass::Tree::DirectiveNode.new("@media -sass-debug-info")
199
- debug_info.map {|k, v| [k.to_s, v.to_s]}.sort.each do |k, v|
359
+ node = Sass::Tree::DirectiveNode.resolved("@media -sass-debug-info")
360
+ Sass::Util.hash_to_a(debug_info.map {|k, v| [k.to_s, v.to_s]}).each do |k, v|
200
361
  rule = Sass::Tree::RuleNode.new([""])
201
362
  rule.resolved_rules = Sass::Selector::CommaSequence.new(
202
363
  [Sass::Selector::Sequence.new(
203
364
  [Sass::Selector::SimpleSequence.new(
204
- [Sass::Selector::Element.new(k.to_s.gsub(/[^\w-]/, "\\\\\\0"), nil)])
365
+ [Sass::Selector::Element.new(k.to_s.gsub(/[^\w-]/, "\\\\\\0"), nil)],
366
+ false)
205
367
  ])
206
368
  ])
207
- prop = Sass::Tree::PropNode.new([""], "", false, :new)
369
+ prop = Sass::Tree::PropNode.new([""], Sass::Script::Value::String.new(''), :new)
208
370
  prop.resolved_name = "font-family"
209
371
  prop.resolved_value = Sass::SCSS::RX.escape_ident(v.to_s)
210
372
  rule << prop
211
373
  node << rule
212
374
  end
213
- node.options = options.merge(:debug_info => false, :line_comments => false, :style => :compressed)
375
+ node.options = options.merge(:debug_info => false,
376
+ :line_comments => false,
377
+ :style => :compressed)
214
378
  node
215
379
  end
216
380
  end
@@ -5,10 +5,10 @@ module Sass
5
5
  # @see Sass::Tree
6
6
  class WarnNode < Node
7
7
  # The expression to print.
8
- # @return [Script::Node]
9
- attr_reader :expr
8
+ # @return [Script::Tree::Node]
9
+ attr_accessor :expr
10
10
 
11
- # @param expr [Script::Node] The expression to print
11
+ # @param expr [Script::Tree::Node] The expression to print
12
12
  def initialize(expr)
13
13
  @expr = expr
14
14
  super()
@@ -6,10 +6,10 @@ module Sass::Tree
6
6
  # @see Sass::Tree
7
7
  class WhileNode < Node
8
8
  # The parse tree for the continuation expression.
9
- # @return [Script::Node]
10
- attr_reader :expr
9
+ # @return [Script::Tree::Node]
10
+ attr_accessor :expr
11
11
 
12
- # @param expr [Script::Node] See \{#expr}
12
+ # @param expr [Script::Tree::Node] See \{#expr}
13
13
  def initialize(expr)
14
14
  @expr = expr
15
15
  super()
@@ -0,0 +1,19 @@
1
+ module Sass
2
+ module Util
3
+ # Ruby 1.8 doesn't support an actual Random class with a settable seed.
4
+ class CrossPlatformRandom
5
+ def initialize(seed = nil)
6
+ if Sass::Util.ruby1_8?
7
+ srand(seed) if seed
8
+ else
9
+ @random = seed ? ::Random.new(seed) : ::Random.new
10
+ end
11
+ end
12
+
13
+ def rand(*args)
14
+ return @random.rand(*args) if @random
15
+ Kernel.rand(*args)
16
+ end
17
+ end
18
+ end
19
+ end