xass 0.1.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 (242) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +11 -0
  3. data/CONTRIBUTING +3 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +201 -0
  6. data/Rakefile +349 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/push +13 -0
  10. data/bin/sass +13 -0
  11. data/bin/sass-convert +12 -0
  12. data/bin/scss +13 -0
  13. data/extra/update_watch.rb +13 -0
  14. data/init.rb +18 -0
  15. data/lib/sass/cache_stores/base.rb +88 -0
  16. data/lib/sass/cache_stores/chain.rb +33 -0
  17. data/lib/sass/cache_stores/filesystem.rb +64 -0
  18. data/lib/sass/cache_stores/memory.rb +47 -0
  19. data/lib/sass/cache_stores/null.rb +25 -0
  20. data/lib/sass/cache_stores.rb +15 -0
  21. data/lib/sass/callbacks.rb +66 -0
  22. data/lib/sass/css.rb +409 -0
  23. data/lib/sass/engine.rb +930 -0
  24. data/lib/sass/environment.rb +101 -0
  25. data/lib/sass/error.rb +201 -0
  26. data/lib/sass/exec.rb +707 -0
  27. data/lib/sass/importers/base.rb +139 -0
  28. data/lib/sass/importers/filesystem.rb +186 -0
  29. data/lib/sass/importers.rb +22 -0
  30. data/lib/sass/logger/base.rb +32 -0
  31. data/lib/sass/logger/log_level.rb +49 -0
  32. data/lib/sass/logger.rb +15 -0
  33. data/lib/sass/media.rb +213 -0
  34. data/lib/sass/plugin/compiler.rb +406 -0
  35. data/lib/sass/plugin/configuration.rb +123 -0
  36. data/lib/sass/plugin/generic.rb +15 -0
  37. data/lib/sass/plugin/merb.rb +48 -0
  38. data/lib/sass/plugin/rack.rb +60 -0
  39. data/lib/sass/plugin/rails.rb +47 -0
  40. data/lib/sass/plugin/staleness_checker.rb +199 -0
  41. data/lib/sass/plugin.rb +133 -0
  42. data/lib/sass/railtie.rb +10 -0
  43. data/lib/sass/repl.rb +57 -0
  44. data/lib/sass/root.rb +7 -0
  45. data/lib/sass/script/arg_list.rb +52 -0
  46. data/lib/sass/script/bool.rb +18 -0
  47. data/lib/sass/script/color.rb +606 -0
  48. data/lib/sass/script/css_lexer.rb +29 -0
  49. data/lib/sass/script/css_parser.rb +31 -0
  50. data/lib/sass/script/funcall.rb +245 -0
  51. data/lib/sass/script/functions.rb +1543 -0
  52. data/lib/sass/script/interpolation.rb +79 -0
  53. data/lib/sass/script/lexer.rb +345 -0
  54. data/lib/sass/script/list.rb +85 -0
  55. data/lib/sass/script/literal.rb +221 -0
  56. data/lib/sass/script/node.rb +99 -0
  57. data/lib/sass/script/null.rb +37 -0
  58. data/lib/sass/script/number.rb +453 -0
  59. data/lib/sass/script/operation.rb +110 -0
  60. data/lib/sass/script/parser.rb +502 -0
  61. data/lib/sass/script/string.rb +51 -0
  62. data/lib/sass/script/string_interpolation.rb +103 -0
  63. data/lib/sass/script/unary_operation.rb +69 -0
  64. data/lib/sass/script/variable.rb +58 -0
  65. data/lib/sass/script.rb +39 -0
  66. data/lib/sass/scss/css_parser.rb +36 -0
  67. data/lib/sass/scss/parser.rb +1180 -0
  68. data/lib/sass/scss/rx.rb +133 -0
  69. data/lib/sass/scss/script_lexer.rb +15 -0
  70. data/lib/sass/scss/script_parser.rb +25 -0
  71. data/lib/sass/scss/static_parser.rb +54 -0
  72. data/lib/sass/scss.rb +16 -0
  73. data/lib/sass/selector/abstract_sequence.rb +94 -0
  74. data/lib/sass/selector/comma_sequence.rb +92 -0
  75. data/lib/sass/selector/sequence.rb +507 -0
  76. data/lib/sass/selector/simple.rb +119 -0
  77. data/lib/sass/selector/simple_sequence.rb +215 -0
  78. data/lib/sass/selector.rb +452 -0
  79. data/lib/sass/shared.rb +76 -0
  80. data/lib/sass/supports.rb +229 -0
  81. data/lib/sass/tree/charset_node.rb +22 -0
  82. data/lib/sass/tree/comment_node.rb +82 -0
  83. data/lib/sass/tree/content_node.rb +9 -0
  84. data/lib/sass/tree/css_import_node.rb +60 -0
  85. data/lib/sass/tree/debug_node.rb +18 -0
  86. data/lib/sass/tree/directive_node.rb +42 -0
  87. data/lib/sass/tree/each_node.rb +24 -0
  88. data/lib/sass/tree/extend_node.rb +36 -0
  89. data/lib/sass/tree/for_node.rb +36 -0
  90. data/lib/sass/tree/function_node.rb +34 -0
  91. data/lib/sass/tree/if_node.rb +52 -0
  92. data/lib/sass/tree/import_node.rb +75 -0
  93. data/lib/sass/tree/media_node.rb +58 -0
  94. data/lib/sass/tree/mixin_def_node.rb +38 -0
  95. data/lib/sass/tree/mixin_node.rb +39 -0
  96. data/lib/sass/tree/node.rb +196 -0
  97. data/lib/sass/tree/prop_node.rb +152 -0
  98. data/lib/sass/tree/return_node.rb +18 -0
  99. data/lib/sass/tree/root_node.rb +78 -0
  100. data/lib/sass/tree/rule_node.rb +132 -0
  101. data/lib/sass/tree/supports_node.rb +51 -0
  102. data/lib/sass/tree/trace_node.rb +32 -0
  103. data/lib/sass/tree/variable_node.rb +30 -0
  104. data/lib/sass/tree/visitors/base.rb +75 -0
  105. data/lib/sass/tree/visitors/check_nesting.rb +147 -0
  106. data/lib/sass/tree/visitors/convert.rb +316 -0
  107. data/lib/sass/tree/visitors/cssize.rb +241 -0
  108. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  109. data/lib/sass/tree/visitors/extend.rb +68 -0
  110. data/lib/sass/tree/visitors/perform.rb +446 -0
  111. data/lib/sass/tree/visitors/set_options.rb +125 -0
  112. data/lib/sass/tree/visitors/to_css.rb +228 -0
  113. data/lib/sass/tree/warn_node.rb +18 -0
  114. data/lib/sass/tree/while_node.rb +18 -0
  115. data/lib/sass/util/multibyte_string_scanner.rb +155 -0
  116. data/lib/sass/util/subset_map.rb +109 -0
  117. data/lib/sass/util/test.rb +10 -0
  118. data/lib/sass/util.rb +948 -0
  119. data/lib/sass/version.rb +126 -0
  120. data/lib/sass.rb +95 -0
  121. data/rails/init.rb +1 -0
  122. data/test/Gemfile +3 -0
  123. data/test/Gemfile.lock +10 -0
  124. data/test/sass/cache_test.rb +89 -0
  125. data/test/sass/callbacks_test.rb +61 -0
  126. data/test/sass/conversion_test.rb +1760 -0
  127. data/test/sass/css2sass_test.rb +458 -0
  128. data/test/sass/data/hsl-rgb.txt +319 -0
  129. data/test/sass/engine_test.rb +3244 -0
  130. data/test/sass/exec_test.rb +86 -0
  131. data/test/sass/extend_test.rb +1482 -0
  132. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  133. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  134. data/test/sass/functions_test.rb +1139 -0
  135. data/test/sass/importer_test.rb +192 -0
  136. data/test/sass/logger_test.rb +58 -0
  137. data/test/sass/mock_importer.rb +49 -0
  138. data/test/sass/more_results/more1.css +9 -0
  139. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  140. data/test/sass/more_results/more_import.css +29 -0
  141. data/test/sass/more_templates/_more_partial.sass +2 -0
  142. data/test/sass/more_templates/more1.sass +23 -0
  143. data/test/sass/more_templates/more_import.sass +11 -0
  144. data/test/sass/plugin_test.rb +564 -0
  145. data/test/sass/results/alt.css +4 -0
  146. data/test/sass/results/basic.css +9 -0
  147. data/test/sass/results/cached_import_option.css +3 -0
  148. data/test/sass/results/compact.css +5 -0
  149. data/test/sass/results/complex.css +86 -0
  150. data/test/sass/results/compressed.css +1 -0
  151. data/test/sass/results/expanded.css +19 -0
  152. data/test/sass/results/filename_fn.css +3 -0
  153. data/test/sass/results/if.css +3 -0
  154. data/test/sass/results/import.css +31 -0
  155. data/test/sass/results/import_charset.css +5 -0
  156. data/test/sass/results/import_charset_1_8.css +5 -0
  157. data/test/sass/results/import_charset_ibm866.css +5 -0
  158. data/test/sass/results/import_content.css +1 -0
  159. data/test/sass/results/line_numbers.css +49 -0
  160. data/test/sass/results/mixins.css +95 -0
  161. data/test/sass/results/multiline.css +24 -0
  162. data/test/sass/results/nested.css +22 -0
  163. data/test/sass/results/options.css +1 -0
  164. data/test/sass/results/parent_ref.css +13 -0
  165. data/test/sass/results/script.css +16 -0
  166. data/test/sass/results/scss_import.css +31 -0
  167. data/test/sass/results/scss_importee.css +2 -0
  168. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  169. data/test/sass/results/subdir/subdir.css +3 -0
  170. data/test/sass/results/units.css +11 -0
  171. data/test/sass/results/warn.css +0 -0
  172. data/test/sass/results/warn_imported.css +0 -0
  173. data/test/sass/script_conversion_test.rb +299 -0
  174. data/test/sass/script_test.rb +622 -0
  175. data/test/sass/scss/css_test.rb +1100 -0
  176. data/test/sass/scss/rx_test.rb +156 -0
  177. data/test/sass/scss/scss_test.rb +2106 -0
  178. data/test/sass/scss/test_helper.rb +37 -0
  179. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  180. data/test/sass/templates/_double_import_loop2.sass +1 -0
  181. data/test/sass/templates/_filename_fn_import.scss +11 -0
  182. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  183. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  184. data/test/sass/templates/_imported_content.sass +3 -0
  185. data/test/sass/templates/_partial.sass +2 -0
  186. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  187. data/test/sass/templates/alt.sass +16 -0
  188. data/test/sass/templates/basic.sass +23 -0
  189. data/test/sass/templates/bork1.sass +2 -0
  190. data/test/sass/templates/bork2.sass +2 -0
  191. data/test/sass/templates/bork3.sass +2 -0
  192. data/test/sass/templates/bork4.sass +2 -0
  193. data/test/sass/templates/bork5.sass +3 -0
  194. data/test/sass/templates/cached_import_option.scss +3 -0
  195. data/test/sass/templates/compact.sass +17 -0
  196. data/test/sass/templates/complex.sass +305 -0
  197. data/test/sass/templates/compressed.sass +15 -0
  198. data/test/sass/templates/double_import_loop1.sass +1 -0
  199. data/test/sass/templates/expanded.sass +17 -0
  200. data/test/sass/templates/filename_fn.scss +18 -0
  201. data/test/sass/templates/if.sass +11 -0
  202. data/test/sass/templates/import.sass +12 -0
  203. data/test/sass/templates/import_charset.sass +9 -0
  204. data/test/sass/templates/import_charset_1_8.sass +6 -0
  205. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  206. data/test/sass/templates/import_content.sass +4 -0
  207. data/test/sass/templates/importee.less +2 -0
  208. data/test/sass/templates/importee.sass +19 -0
  209. data/test/sass/templates/line_numbers.sass +13 -0
  210. data/test/sass/templates/mixin_bork.sass +5 -0
  211. data/test/sass/templates/mixins.sass +76 -0
  212. data/test/sass/templates/multiline.sass +20 -0
  213. data/test/sass/templates/nested.sass +25 -0
  214. data/test/sass/templates/nested_bork1.sass +2 -0
  215. data/test/sass/templates/nested_bork2.sass +2 -0
  216. data/test/sass/templates/nested_bork3.sass +2 -0
  217. data/test/sass/templates/nested_bork4.sass +2 -0
  218. data/test/sass/templates/nested_import.sass +2 -0
  219. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  220. data/test/sass/templates/options.sass +2 -0
  221. data/test/sass/templates/parent_ref.sass +25 -0
  222. data/test/sass/templates/same_name_different_ext.sass +2 -0
  223. data/test/sass/templates/same_name_different_ext.scss +1 -0
  224. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  225. data/test/sass/templates/script.sass +101 -0
  226. data/test/sass/templates/scss_import.scss +11 -0
  227. data/test/sass/templates/scss_importee.scss +1 -0
  228. data/test/sass/templates/single_import_loop.sass +1 -0
  229. data/test/sass/templates/subdir/import_up1.scss +1 -0
  230. data/test/sass/templates/subdir/import_up2.scss +1 -0
  231. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  232. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  233. data/test/sass/templates/subdir/subdir.sass +6 -0
  234. data/test/sass/templates/units.sass +11 -0
  235. data/test/sass/templates/warn.sass +3 -0
  236. data/test/sass/templates/warn_imported.sass +4 -0
  237. data/test/sass/test_helper.rb +8 -0
  238. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  239. data/test/sass/util/subset_map_test.rb +91 -0
  240. data/test/sass/util_test.rb +382 -0
  241. data/test/test_helper.rb +80 -0
  242. metadata +354 -0
data/lib/sass/css.rb ADDED
@@ -0,0 +1,409 @@
1
+ require File.dirname(__FILE__) + '/../sass'
2
+ require 'sass/tree/node'
3
+ require 'sass/scss/css_parser'
4
+
5
+ module Sass
6
+ # This class converts CSS documents into Sass or SCSS templates.
7
+ # It works by parsing the CSS document into a {Sass::Tree} structure,
8
+ # and then applying various transformations to the structure
9
+ # to produce more concise and idiomatic Sass/SCSS.
10
+ #
11
+ # Example usage:
12
+ #
13
+ # Sass::CSS.new("p { color: blue }").render(:sass) #=> "p\n color: blue"
14
+ # Sass::CSS.new("p { color: blue }").render(:scss) #=> "p {\n color: blue; }"
15
+ class CSS
16
+ # @param template [String] The CSS stylesheet.
17
+ # This stylesheet can be encoded using any encoding
18
+ # that can be converted to Unicode.
19
+ # If the stylesheet contains an `@charset` declaration,
20
+ # that overrides the Ruby encoding
21
+ # (see {file:SASS_REFERENCE.md#encodings the encoding documentation})
22
+ # @option options :old [Boolean] (false)
23
+ # Whether or not to output old property syntax
24
+ # (`:color blue` as opposed to `color: blue`).
25
+ # This is only meaningful when generating Sass code,
26
+ # rather than SCSS.
27
+ # @option options :indent [String] (" ")
28
+ # The string to use for indenting each line. Defaults to two spaces.
29
+ def initialize(template, options = {})
30
+ if template.is_a? IO
31
+ template = template.read
32
+ end
33
+
34
+ @options = options.dup
35
+ # Backwards compatibility
36
+ @options[:old] = true if @options[:alternate] == false
37
+ @template = template
38
+ end
39
+
40
+ # Converts the CSS template into Sass or SCSS code.
41
+ #
42
+ # @param fmt [Symbol] `:sass` or `:scss`, designating the format to return.
43
+ # @return [String] The resulting Sass or SCSS code
44
+ # @raise [Sass::SyntaxError] if there's an error parsing the CSS template
45
+ def render(fmt = :sass)
46
+ check_encoding!
47
+ build_tree.send("to_#{fmt}", @options).strip + "\n"
48
+ rescue Sass::SyntaxError => err
49
+ err.modify_backtrace(:filename => @options[:filename] || '(css)')
50
+ raise err
51
+ end
52
+
53
+ # Returns the original encoding of the document,
54
+ # or `nil` under Ruby 1.8.
55
+ #
56
+ # @return [Encoding, nil]
57
+ # @raise [Encoding::UndefinedConversionError] if the source encoding
58
+ # cannot be converted to UTF-8
59
+ # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
60
+ def source_encoding
61
+ check_encoding!
62
+ @original_encoding
63
+ end
64
+
65
+ private
66
+
67
+ def check_encoding!
68
+ return if @checked_encoding
69
+ @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
73
+ end
74
+
75
+ # Parses the CSS template and applies various transformations
76
+ #
77
+ # @return [Tree::Node] The root node of the parsed tree
78
+ def build_tree
79
+ root = Sass::SCSS::CssParser.new(@template, @options[:filename]).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
88
+ root
89
+ end
90
+
91
+ # Parse all the selectors in the document and assign them to
92
+ # {Sass::Tree::RuleNode#parsed_rules}.
93
+ #
94
+ # @param root [Tree::Node] The parent node
95
+ def parse_selectors(root)
96
+ root.children.each do |child|
97
+ next parse_selectors(child) if child.is_a?(Tree::DirectiveNode)
98
+ next unless child.is_a?(Tree::RuleNode)
99
+ parser = Sass::SCSS::CssParser.new(child.rule.first, child.filename, child.line)
100
+ child.parsed_rules = parser.parse_selector
101
+ end
102
+ end
103
+
104
+ # Transform
105
+ #
106
+ # foo, bar, baz
107
+ # color: blue
108
+ #
109
+ # into
110
+ #
111
+ # foo
112
+ # color: blue
113
+ # bar
114
+ # color: blue
115
+ # baz
116
+ # color: blue
117
+ #
118
+ # @param root [Tree::Node] The parent node
119
+ def expand_commas(root)
120
+ root.children.map! do |child|
121
+ # child.parsed_rules.members.size > 1 iff the rule contains a comma
122
+ unless child.is_a?(Tree::RuleNode) && child.parsed_rules.members.size > 1
123
+ expand_commas(child) if child.is_a?(Tree::DirectiveNode)
124
+ next child
125
+ end
126
+ child.parsed_rules.members.map do |seq|
127
+ node = Tree::RuleNode.new([])
128
+ node.parsed_rules = make_cseq(seq)
129
+ node.children = child.children
130
+ node
131
+ end
132
+ end
133
+ root.children.flatten!
134
+ end
135
+
136
+ # Make rules use nesting so that
137
+ #
138
+ # foo
139
+ # color: green
140
+ # foo bar
141
+ # color: red
142
+ # foo baz
143
+ # color: blue
144
+ #
145
+ # becomes
146
+ #
147
+ # foo
148
+ # color: green
149
+ # bar
150
+ # color: red
151
+ # baz
152
+ # color: blue
153
+ #
154
+ # @param root [Tree::Node] The parent node
155
+ def nest_seqs(root)
156
+ current_rule = nil
157
+ root.children.map! do |child|
158
+ unless child.is_a?(Tree::RuleNode)
159
+ nest_seqs(child) if child.is_a?(Tree::DirectiveNode)
160
+ next child
161
+ end
162
+
163
+ seq = first_seq(child)
164
+ seq.members.reject! {|sseq| sseq == "\n"}
165
+ first, rest = seq.members.first, seq.members[1..-1]
166
+
167
+ if current_rule.nil? || first_sseq(current_rule) != first
168
+ current_rule = Tree::RuleNode.new([])
169
+ current_rule.parsed_rules = make_seq(first)
170
+ end
171
+
172
+ unless rest.empty?
173
+ child.parsed_rules = make_seq(*rest)
174
+ current_rule << child
175
+ else
176
+ current_rule.children += child.children
177
+ end
178
+
179
+ current_rule
180
+ end
181
+ root.children.compact!
182
+ root.children.uniq!
183
+
184
+ root.children.each {|v| nest_seqs(v)}
185
+ end
186
+
187
+ # Make rules use parent refs so that
188
+ #
189
+ # foo
190
+ # color: green
191
+ # foo.bar
192
+ # color: blue
193
+ #
194
+ # becomes
195
+ #
196
+ # foo
197
+ # color: green
198
+ # &.bar
199
+ # color: blue
200
+ #
201
+ # @param root [Tree::Node] The parent node
202
+ def parent_ref_rules(root)
203
+ current_rule = nil
204
+ root.children.map! do |child|
205
+ unless child.is_a?(Tree::RuleNode)
206
+ parent_ref_rules(child) if child.is_a?(Tree::DirectiveNode)
207
+ next child
208
+ end
209
+
210
+ sseq = first_sseq(child)
211
+ next child unless sseq.is_a?(Sass::Selector::SimpleSequence)
212
+
213
+ firsts, rest = [sseq.members.first], sseq.members[1..-1]
214
+ firsts.push rest.shift if firsts.first.is_a?(Sass::Selector::Parent)
215
+
216
+ last_simple_subject = rest.empty? && sseq.subject?
217
+ if current_rule.nil? || first_sseq(current_rule).members != firsts ||
218
+ !!first_sseq(current_rule).subject? != !!last_simple_subject
219
+ current_rule = Tree::RuleNode.new([])
220
+ current_rule.parsed_rules = make_sseq(last_simple_subject, *firsts)
221
+ end
222
+
223
+ unless rest.empty?
224
+ rest.unshift Sass::Selector::Parent.new
225
+ child.parsed_rules = make_sseq(sseq.subject?, *rest)
226
+ current_rule << child
227
+ else
228
+ current_rule.children += child.children
229
+ end
230
+
231
+ current_rule
232
+ end
233
+ root.children.compact!
234
+ root.children.uniq!
235
+
236
+ root.children.each {|v| parent_ref_rules(v)}
237
+ end
238
+
239
+ # Flatten rules so that
240
+ #
241
+ # foo
242
+ # bar
243
+ # color: red
244
+ #
245
+ # becomes
246
+ #
247
+ # foo bar
248
+ # color: red
249
+ #
250
+ # and
251
+ #
252
+ # foo
253
+ # &.bar
254
+ # color: blue
255
+ #
256
+ # becomes
257
+ #
258
+ # foo.bar
259
+ # color: blue
260
+ #
261
+ # @param root [Tree::Node] The parent node
262
+ def flatten_rules(root)
263
+ root.children.each do |child|
264
+ case child
265
+ when Tree::RuleNode
266
+ flatten_rule(child)
267
+ when Tree::DirectiveNode
268
+ flatten_rules(child)
269
+ end
270
+ end
271
+ end
272
+
273
+ # Flattens a single rule.
274
+ #
275
+ # @param rule [Tree::RuleNode] The candidate for flattening
276
+ # @see #flatten_rules
277
+ def flatten_rule(rule)
278
+ while rule.children.size == 1 && rule.children.first.is_a?(Tree::RuleNode)
279
+ child = rule.children.first
280
+
281
+ if first_simple_sel(child).is_a?(Sass::Selector::Parent)
282
+ rule.parsed_rules = child.parsed_rules.resolve_parent_refs(rule.parsed_rules)
283
+ else
284
+ rule.parsed_rules = make_seq(*(first_seq(rule).members + first_seq(child).members))
285
+ end
286
+
287
+ rule.children = child.children
288
+ end
289
+
290
+ flatten_rules(rule)
291
+ end
292
+
293
+ def bubble_subject(root)
294
+ root.children.each do |child|
295
+ bubble_subject(child) if child.is_a?(Tree::RuleNode) || child.is_a?(Tree::DirectiveNode)
296
+ next unless child.is_a?(Tree::RuleNode) && !child.children.empty?
297
+ next unless child.children.all? do |c|
298
+ next unless c.is_a?(Tree::RuleNode)
299
+ first_simple_sel(c).is_a?(Sass::Selector::Parent) && first_sseq(c).subject?
300
+ end
301
+ first_sseq(child).subject = true
302
+ child.children.each {|c| first_sseq(c).subject = false}
303
+ end
304
+ end
305
+
306
+ # Transform
307
+ #
308
+ # foo
309
+ # bar
310
+ # color: blue
311
+ # baz
312
+ # color: blue
313
+ #
314
+ # into
315
+ #
316
+ # foo
317
+ # bar, baz
318
+ # color: blue
319
+ #
320
+ # @param rule [Tree::RuleNode] The candidate for flattening
321
+ def fold_commas(root)
322
+ prev_rule = nil
323
+ root.children.map! do |child|
324
+ unless child.is_a?(Tree::RuleNode)
325
+ fold_commas(child) if child.is_a?(Tree::DirectiveNode)
326
+ next child
327
+ end
328
+
329
+ if prev_rule && prev_rule.children == child.children
330
+ prev_rule.parsed_rules.members << first_seq(child)
331
+ next nil
332
+ end
333
+
334
+ fold_commas(child)
335
+ prev_rule = child
336
+ child
337
+ end
338
+ root.children.compact!
339
+ end
340
+
341
+ # Dump all the parsed {Sass::Tree::RuleNode} selectors to strings.
342
+ #
343
+ # @param root [Tree::Node] The parent node
344
+ def dump_selectors(root)
345
+ root.children.each do |child|
346
+ next dump_selectors(child) if child.is_a?(Tree::DirectiveNode)
347
+ next unless child.is_a?(Tree::RuleNode)
348
+ child.rule = [child.parsed_rules.to_s]
349
+ dump_selectors(child)
350
+ end
351
+ end
352
+
353
+ # Create a {Sass::Selector::CommaSequence}.
354
+ #
355
+ # @param seqs [Array<Sass::Selector::Sequence>]
356
+ # @return [Sass::Selector::CommaSequence]
357
+ def make_cseq(*seqs)
358
+ Sass::Selector::CommaSequence.new(seqs)
359
+ end
360
+
361
+ # Create a {Sass::Selector::CommaSequence} containing only a single
362
+ # {Sass::Selector::Sequence}.
363
+ #
364
+ # @param sseqs [Array<Sass::Selector::Sequence, String>]
365
+ # @return [Sass::Selector::CommaSequence]
366
+ def make_seq(*sseqs)
367
+ make_cseq(Sass::Selector::Sequence.new(sseqs))
368
+ end
369
+
370
+ # Create a {Sass::Selector::CommaSequence} containing only a single
371
+ # {Sass::Selector::Sequence} which in turn contains only a single
372
+ # {Sass::Selector::SimpleSequence}.
373
+ #
374
+ # @param subject [Boolean] Whether this is a subject selector
375
+ # @param sseqs [Array<Sass::Selector::Sequence, String>]
376
+ # @return [Sass::Selector::CommaSequence]
377
+ def make_sseq(subject, *sseqs)
378
+ make_seq(Sass::Selector::SimpleSequence.new(sseqs, subject))
379
+ end
380
+
381
+ # Return the first {Sass::Selector::Sequence} in a {Sass::Tree::RuleNode}.
382
+ #
383
+ # @param rule [Sass::Tree::RuleNode]
384
+ # @return [Sass::Selector::Sequence]
385
+ def first_seq(rule)
386
+ rule.parsed_rules.members.first
387
+ end
388
+
389
+ # Return the first {Sass::Selector::SimpleSequence} in a
390
+ # {Sass::Tree::RuleNode}.
391
+ #
392
+ # @param rule [Sass::Tree::RuleNode]
393
+ # @return [Sass::Selector::SimpleSequence, String]
394
+ def first_sseq(rule)
395
+ first_seq(rule).members.first
396
+ end
397
+
398
+ # Return the first {Sass::Selector::Simple} in a {Sass::Tree::RuleNode},
399
+ # unless the rule begins with a combinator.
400
+ #
401
+ # @param rule [Sass::Tree::RuleNode]
402
+ # @return [Sass::Selector::Simple?]
403
+ def first_simple_sel(rule)
404
+ sseq = first_sseq(rule)
405
+ return unless sseq.is_a?(Sass::Selector::SimpleSequence)
406
+ sseq.members.first
407
+ end
408
+ end
409
+ end