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
@@ -0,0 +1,228 @@
1
+ # A visitor for converting a Sass tree into CSS.
2
+ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
3
+ protected
4
+
5
+ def initialize
6
+ @tabs = 0
7
+ end
8
+
9
+ def visit(node)
10
+ super
11
+ rescue Sass::SyntaxError => e
12
+ e.modify_backtrace(:filename => node.filename, :line => node.line)
13
+ raise e
14
+ end
15
+
16
+ def with_tabs(tabs)
17
+ old_tabs, @tabs = @tabs, tabs
18
+ yield
19
+ ensure
20
+ @tabs = old_tabs
21
+ end
22
+
23
+ def visit_root(node)
24
+ result = String.new
25
+ node.children.each do |child|
26
+ 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
41
+ end
42
+ end
43
+
44
+ result = "@charset \"#{result.encoding.name}\";#{
45
+ node.style == :compressed ? '' : "\n"
46
+ }".encode(result.encoding) + result
47
+ end
48
+ result
49
+ rescue Sass::SyntaxError => e
50
+ e.sass_template ||= node.template
51
+ raise e
52
+ end
53
+
54
+ def visit_charset(node)
55
+ "@charset \"#{node.name}\";"
56
+ end
57
+
58
+ def visit_comment(node)
59
+ return if node.invisible?
60
+ spaces = (' ' * [@tabs - node.resolved_value[/^ */].size, 0].max)
61
+
62
+ content = node.resolved_value.gsub(/^/, spaces)
63
+ content.gsub!(%r{^(\s*)//(.*)$}) {|md| "#{$1}/*#{$2} */"} if node.type == :silent
64
+ content.gsub!(/\n +(\* *(?!\/))?/, ' ') if (node.style == :compact || node.style == :compressed) && node.type != :loud
65
+ content
66
+ end
67
+
68
+ def visit_directive(node)
69
+ was_in_directive = @in_directive
70
+ tab_str = ' ' * @tabs
71
+ return tab_str + node.resolved_value + ";" unless node.has_children
72
+ return tab_str + node.resolved_value + " {}" if node.children.empty?
73
+ @in_directive = @in_directive || !node.is_a?(Sass::Tree::MediaNode)
74
+ result = if node.style == :compressed
75
+ "#{node.resolved_value}{"
76
+ else
77
+ "#{tab_str}#{node.resolved_value} {" + (node.style == :compact ? ' ' : "\n")
78
+ end
79
+ was_prop = false
80
+ first = true
81
+ node.children.each do |child|
82
+ next if child.invisible?
83
+ if node.style == :compact
84
+ if child.is_a?(Sass::Tree::PropNode)
85
+ with_tabs(first || was_prop ? 0 : @tabs + 1) {result << visit(child) << ' '}
86
+ else
87
+ result[-1] = "\n" if was_prop
88
+ rendered = with_tabs(@tabs + 1) {visit(child).dup}
89
+ rendered = rendered.lstrip if first
90
+ result << rendered.rstrip + "\n"
91
+ end
92
+ was_prop = child.is_a?(Sass::Tree::PropNode)
93
+ first = false
94
+ elsif node.style == :compressed
95
+ result << (was_prop ? ";" : "") << with_tabs(0) {visit(child)}
96
+ was_prop = child.is_a?(Sass::Tree::PropNode)
97
+ else
98
+ result << with_tabs(@tabs + 1) {visit(child)} + "\n"
99
+ end
100
+ end
101
+ result.rstrip + if node.style == :compressed
102
+ "}"
103
+ else
104
+ (node.style == :expanded ? "\n" : " ") + "}\n"
105
+ end
106
+ ensure
107
+ @in_directive = was_in_directive
108
+ end
109
+
110
+ def visit_media(node)
111
+ str = with_tabs(@tabs + node.tabs) {visit_directive(node)}
112
+ str.gsub!(/\n\Z/, '') unless node.style == :compressed || node.group_end
113
+ str
114
+ end
115
+
116
+ def visit_supports(node)
117
+ visit_media(node)
118
+ end
119
+
120
+ def visit_cssimport(node)
121
+ visit_directive(node)
122
+ end
123
+
124
+ def visit_prop(node)
125
+ return if node.resolved_value.empty?
126
+ tab_str = ' ' * (@tabs + node.tabs)
127
+ if node.style == :compressed
128
+ "#{tab_str}#{node.resolved_name}:#{node.resolved_value}"
129
+ else
130
+ "#{tab_str}#{node.resolved_name}: #{node.resolved_value};"
131
+ end
132
+ end
133
+
134
+ def visit_rule(node)
135
+ with_tabs(@tabs + node.tabs) do
136
+ rule_separator = node.style == :compressed ? ',' : ', '
137
+ line_separator =
138
+ case node.style
139
+ when :nested, :expanded; "\n"
140
+ when :compressed; ""
141
+ else; " "
142
+ end
143
+ rule_indent = ' ' * @tabs
144
+ per_rule_indent, total_indent = [:nested, :expanded].include?(node.style) ? [rule_indent, ''] : ['', rule_indent]
145
+
146
+ joined_rules = node.resolved_rules.members.map do |seq|
147
+ next if seq.has_placeholder?
148
+ rule_part = seq.to_a.join
149
+ if node.style == :compressed
150
+ rule_part.gsub!(/([^,])\s*\n\s*/m, '\1 ')
151
+ rule_part.gsub!(/\s*([,+>])\s*/m, '\1')
152
+ rule_part.strip!
153
+ end
154
+ rule_part
155
+ end.compact.join(rule_separator)
156
+
157
+ joined_rules.sub!(/\A\s*/, per_rule_indent)
158
+ joined_rules.gsub!(/\s*\n\s*/, "#{line_separator}#{per_rule_indent}")
159
+ total_rule = total_indent << joined_rules
160
+
161
+ to_return = ''
162
+ old_spaces = ' ' * @tabs
163
+ if node.style != :compressed
164
+ if node.options[:debug_info] && !@in_directive
165
+ to_return << visit(debug_info_rule(node.debug_info, node.options)) << "\n"
166
+ elsif node.options[:trace_selectors]
167
+ to_return << "#{old_spaces}/* "
168
+ to_return << node.stack_trace.join("\n #{old_spaces}")
169
+ to_return << " */\n"
170
+ elsif node.options[:line_comments]
171
+ to_return << "#{old_spaces}/* line #{node.line}"
172
+
173
+ if node.filename
174
+ relative_filename = if node.options[:css_filename]
175
+ begin
176
+ Pathname.new(node.filename).relative_path_from(
177
+ Pathname.new(File.dirname(node.options[:css_filename]))).to_s
178
+ rescue ArgumentError
179
+ nil
180
+ end
181
+ end
182
+ relative_filename ||= node.filename
183
+ to_return << ", #{relative_filename}"
184
+ end
185
+
186
+ to_return << " */\n"
187
+ end
188
+ end
189
+
190
+ if node.style == :compact
191
+ properties = with_tabs(0) {node.children.map {|a| visit(a)}.join(' ')}
192
+ to_return << "#{total_rule} { #{properties} }#{"\n" if node.group_end}"
193
+ elsif node.style == :compressed
194
+ properties = with_tabs(0) {node.children.map {|a| visit(a)}.join(';')}
195
+ to_return << "#{total_rule}{#{properties}}"
196
+ else
197
+ properties = with_tabs(@tabs + 1) {node.children.map {|a| visit(a)}.join("\n")}
198
+ end_props = (node.style == :expanded ? "\n" + old_spaces : ' ')
199
+ to_return << "#{total_rule} {\n#{properties}#{end_props}}#{"\n" if node.group_end}"
200
+ end
201
+
202
+ to_return
203
+ end
204
+ end
205
+
206
+ private
207
+
208
+ def debug_info_rule(debug_info, options)
209
+ node = Sass::Tree::DirectiveNode.resolved("@media -sass-debug-info")
210
+ Sass::Util.hash_to_a(debug_info.map {|k, v| [k.to_s, v.to_s]}).each do |k, v|
211
+ rule = Sass::Tree::RuleNode.new([""])
212
+ rule.resolved_rules = Sass::Selector::CommaSequence.new(
213
+ [Sass::Selector::Sequence.new(
214
+ [Sass::Selector::SimpleSequence.new(
215
+ [Sass::Selector::Element.new(k.to_s.gsub(/[^\w-]/, "\\\\\\0"), nil)],
216
+ false)
217
+ ])
218
+ ])
219
+ prop = Sass::Tree::PropNode.new([""], Sass::Script::String.new(''), :new)
220
+ prop.resolved_name = "font-family"
221
+ prop.resolved_value = Sass::SCSS::RX.escape_ident(v.to_s)
222
+ rule << prop
223
+ node << rule
224
+ end
225
+ node.options = options.merge(:debug_info => false, :line_comments => false, :style => :compressed)
226
+ node
227
+ end
228
+ end
@@ -0,0 +1,18 @@
1
+ module Sass
2
+ module Tree
3
+ # A dynamic node representing a Sass `@warn` statement.
4
+ #
5
+ # @see Sass::Tree
6
+ class WarnNode < Node
7
+ # The expression to print.
8
+ # @return [Script::Node]
9
+ attr_accessor :expr
10
+
11
+ # @param expr [Script::Node] The expression to print
12
+ def initialize(expr)
13
+ @expr = expr
14
+ super()
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ require 'sass/tree/node'
2
+
3
+ module Sass::Tree
4
+ # A dynamic node representing a Sass `@while` loop.
5
+ #
6
+ # @see Sass::Tree
7
+ class WhileNode < Node
8
+ # The parse tree for the continuation expression.
9
+ # @return [Script::Node]
10
+ attr_accessor :expr
11
+
12
+ # @param expr [Script::Node] See \{#expr}
13
+ def initialize(expr)
14
+ @expr = expr
15
+ super()
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,155 @@
1
+ require 'strscan'
2
+
3
+ if Sass::Util.ruby1_8?
4
+ Sass::Util::MultibyteStringScanner = StringScanner
5
+ else
6
+ if Sass::Util.rbx?
7
+ # Rubinius's StringScanner class implements some of its methods in terms of
8
+ # others, which causes us to double-count bytes in some cases if we do
9
+ # straightforward inheritance. To work around this, we use a delegate class.
10
+ require 'delegate'
11
+ class Sass::Util::MultibyteStringScanner < DelegateClass(StringScanner)
12
+ def initialize(str)
13
+ super(StringScanner.new(str))
14
+ @mb_pos = 0
15
+ @mb_matched_size = nil
16
+ @mb_last_pos = nil
17
+ end
18
+
19
+ def is_a?(klass)
20
+ __getobj__.is_a?(klass) || super
21
+ end
22
+ end
23
+ else
24
+ class Sass::Util::MultibyteStringScanner < StringScanner
25
+ def initialize(str)
26
+ super
27
+ @mb_pos = 0
28
+ @mb_matched_size = nil
29
+ @mb_last_pos = nil
30
+ end
31
+ end
32
+ end
33
+
34
+ # A wrapper of the native StringScanner class that works correctly with
35
+ # multibyte character encodings. The native class deals only in bytes, not
36
+ # characters, for methods like [#pos] and [#matched_size]. This class deals
37
+ # only in characters, instead.
38
+ class Sass::Util::MultibyteStringScanner
39
+ def self.new(str)
40
+ return StringScanner.new(str) if str.ascii_only?
41
+ super
42
+ end
43
+
44
+ alias_method :byte_pos, :pos
45
+ alias_method :byte_matched_size, :matched_size
46
+
47
+ def check(pattern); _match super; end
48
+ def check_until(pattern); _matched super; end
49
+ def getch; _forward _match super; end
50
+ def match?(pattern); _size check(pattern); end
51
+ def matched_size; @mb_matched_size; end
52
+ def peek(len); string[@mb_pos, len]; end
53
+ alias_method :peep, :peek
54
+ def pos; @mb_pos; end
55
+ alias_method :pointer, :pos
56
+ def rest_size; rest.size; end
57
+ def scan(pattern); _forward _match super; end
58
+ def scan_until(pattern); _forward _matched super; end
59
+ def skip(pattern); _size scan(pattern); end
60
+ def skip_until(pattern); _matched _size scan_until(pattern); end
61
+
62
+ def get_byte
63
+ raise "MultibyteStringScanner doesn't support #get_byte."
64
+ end
65
+
66
+ def getbyte
67
+ raise "MultibyteStringScanner doesn't support #getbyte."
68
+ end
69
+
70
+ def pos=(n)
71
+ @mb_last_pos = nil
72
+
73
+ # We set position kind of a lot during parsing, so we want it to be as
74
+ # efficient as possible. This is complicated by the fact that UTF-8 is a
75
+ # variable-length encoding, so it's difficult to find the byte length that
76
+ # corresponds to a given character length.
77
+ #
78
+ # Our heuristic here is to try to count the fewest possible characters. So
79
+ # if the new position is close to the current one, just count the
80
+ # characters between the two; if the new position is closer to the
81
+ # beginning of the string, just count the characters from there.
82
+ if @mb_pos - n < @mb_pos / 2
83
+ # New position is close to old position
84
+ byte_delta = @mb_pos > n ? -string[n...@mb_pos].bytesize : string[@mb_pos...n].bytesize
85
+ super(byte_pos + byte_delta)
86
+ else
87
+ # New position is close to BOS
88
+ super(string[0...n].bytesize)
89
+ end
90
+ @mb_pos = n
91
+ end
92
+
93
+ def reset
94
+ @mb_pos = 0
95
+ @mb_matched_size = nil
96
+ @mb_last_pos = nil
97
+ super
98
+ end
99
+
100
+ def scan_full(pattern, advance_pointer_p, return_string_p)
101
+ res = _match super(pattern, advance_pointer_p, true)
102
+ _forward res if advance_pointer_p
103
+ return res if return_string_p
104
+ end
105
+
106
+ def search_full(pattern, advance_pointer_p, return_string_p)
107
+ res = super(pattern, advance_pointer_p, true)
108
+ _forward res if advance_pointer_p
109
+ _matched((res if return_string_p))
110
+ end
111
+
112
+ def string=(str)
113
+ @mb_pos = 0
114
+ @mb_matched_size = nil
115
+ @mb_last_pos = nil
116
+ super
117
+ end
118
+
119
+ def terminate
120
+ @mb_pos = string.size
121
+ @mb_matched_size = nil
122
+ @mb_last_pos = nil
123
+ super
124
+ end
125
+ alias_method :clear, :terminate
126
+
127
+ def unscan
128
+ super
129
+ @mb_pos = @mb_last_pos
130
+ @mb_last_pos = @mb_matched_size = nil
131
+ end
132
+
133
+ private
134
+
135
+ def _size(str)
136
+ str && str.size
137
+ end
138
+
139
+ def _match(str)
140
+ @mb_matched_size = str && str.size
141
+ str
142
+ end
143
+
144
+ def _matched(res)
145
+ _match matched
146
+ res
147
+ end
148
+
149
+ def _forward(str)
150
+ @mb_last_pos = @mb_pos
151
+ @mb_pos += str.size if str
152
+ str
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,109 @@
1
+ require 'set'
2
+
3
+ module Sass
4
+ module Util
5
+ # A map from sets to values.
6
+ # A value is \{#\[]= set} by providing a set (the "set-set") and a value,
7
+ # which is then recorded as corresponding to that set.
8
+ # Values are \{#\[] accessed} by providing a set (the "get-set")
9
+ # and returning all values that correspond to set-sets
10
+ # that are subsets of the get-set.
11
+ #
12
+ # SubsetMap preserves the order of values as they're inserted.
13
+ #
14
+ # @example
15
+ # ssm = SubsetMap.new
16
+ # ssm[Set[1, 2]] = "Foo"
17
+ # ssm[Set[2, 3]] = "Bar"
18
+ # ssm[Set[1, 2, 3]] = "Baz"
19
+ #
20
+ # ssm[Set[1, 2, 3]] #=> ["Foo", "Bar", "Baz"]
21
+ class SubsetMap
22
+ # Creates a new, empty SubsetMap.
23
+ def initialize
24
+ @hash = {}
25
+ @vals = []
26
+ end
27
+
28
+ # Whether or not this SubsetMap has any key-value pairs.
29
+ #
30
+ # @return [Boolean]
31
+ def empty?
32
+ @hash.empty?
33
+ end
34
+
35
+ # Associates a value with a set.
36
+ # When `set` or any of its supersets is accessed,
37
+ # `value` will be among the values returned.
38
+ #
39
+ # Note that if the same `set` is passed to this method multiple times,
40
+ # all given `value`s will be associated with that `set`.
41
+ #
42
+ # This runs in `O(n)` time, where `n` is the size of `set`.
43
+ #
44
+ # @param set [#to_set] The set to use as the map key. May not be empty.
45
+ # @param value [Object] The value to associate with `set`.
46
+ # @raise [ArgumentError] If `set` is empty.
47
+ def []=(set, value)
48
+ raise ArgumentError.new("SubsetMap keys may not be empty.") if set.empty?
49
+
50
+ index = @vals.size
51
+ @vals << value
52
+ set.each do |k|
53
+ @hash[k] ||= []
54
+ @hash[k] << [set, set.to_set, index]
55
+ end
56
+ end
57
+
58
+ # Returns all values associated with subsets of `set`.
59
+ #
60
+ # In the worst case, this runs in `O(m*max(n, log m))` time,
61
+ # where `n` is the size of `set`
62
+ # and `m` is the number of assocations in the map.
63
+ # However, unless many keys in the map overlap with `set`,
64
+ # `m` will typically be much smaller.
65
+ #
66
+ # @param set [Set] The set to use as the map key.
67
+ # @return [Array<(Object, #to_set)>] An array of pairs,
68
+ # where the first value is the value associated with a subset of `set`,
69
+ # and the second value is that subset of `set`
70
+ # (or whatever `#to_set` object was used to set the value)
71
+ # This array is in insertion order.
72
+ # @see #[]
73
+ def get(set)
74
+ res = set.map do |k|
75
+ next unless subsets = @hash[k]
76
+ subsets.map do |subenum, subset, index|
77
+ next unless subset.subset?(set)
78
+ [index, subenum]
79
+ end
80
+ end
81
+ res = Sass::Util.flatten(res, 1)
82
+ res.compact!
83
+ res.uniq!
84
+ res.sort!
85
+ res.map! {|i, s| [@vals[i], s]}
86
+ return res
87
+ end
88
+
89
+ # Same as \{#get}, but doesn't return the subsets of the argument
90
+ # for which values were found.
91
+ #
92
+ # @param set [Set] The set to use as the map key.
93
+ # @return [Array] The array of all values
94
+ # associated with subsets of `set`, in insertion order.
95
+ # @see #get
96
+ def [](set)
97
+ get(set).map {|v, _| v}
98
+ end
99
+
100
+ # Iterates over each value in the subset map. Ignores keys completely. If
101
+ # multiple keys have the same value, this will return them multiple times.
102
+ #
103
+ # @yield [Object] Each value in the map.
104
+ def each_value
105
+ @vals.each {|v| yield v}
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,10 @@
1
+ module Sass
2
+ module Util
3
+ module Test
4
+ def skip(msg = nil, bt = caller)
5
+ super if defined?(super)
6
+ return
7
+ end
8
+ end
9
+ end
10
+ end