haml_ejs 0.0.1

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 (358) hide show
  1. data/.yardopts +11 -0
  2. data/CONTRIBUTING +3 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +68 -0
  5. data/Rakefile +419 -0
  6. data/VERSION +1 -0
  7. data/VERSION_NAME +1 -0
  8. data/bin/haml +9 -0
  9. data/bin/html2haml +7 -0
  10. data/extra/update_watch.rb +13 -0
  11. data/init.rb +18 -0
  12. data/lib/haml.rb +49 -0
  13. data/lib/haml/buffer.rb +297 -0
  14. data/lib/haml/compiler.rb +486 -0
  15. data/lib/haml/engine.rb +312 -0
  16. data/lib/haml/error.rb +22 -0
  17. data/lib/haml/exec.rb +362 -0
  18. data/lib/haml/filters.rb +388 -0
  19. data/lib/haml/haml_ejs.rb +57 -0
  20. data/lib/haml/helpers.rb +608 -0
  21. data/lib/haml/helpers/action_view_extensions.rb +57 -0
  22. data/lib/haml/helpers/action_view_mods.rb +257 -0
  23. data/lib/haml/helpers/xss_mods.rb +165 -0
  24. data/lib/haml/html.rb +412 -0
  25. data/lib/haml/html/erb.rb +141 -0
  26. data/lib/haml/parser.rb +760 -0
  27. data/lib/haml/railtie.rb +19 -0
  28. data/lib/haml/root.rb +7 -0
  29. data/lib/haml/shared.rb +78 -0
  30. data/lib/haml/template.rb +99 -0
  31. data/lib/haml/template/options.rb +16 -0
  32. data/lib/haml/template/patch.rb +58 -0
  33. data/lib/haml/template/plugin.rb +123 -0
  34. data/lib/haml/util.rb +842 -0
  35. data/lib/haml/version.rb +109 -0
  36. data/lib/sass.rb +8 -0
  37. data/lib/sass/plugin.rb +10 -0
  38. data/lib/sass/rails2_shim.rb +9 -0
  39. data/lib/sass/rails3_shim.rb +16 -0
  40. data/rails/init.rb +1 -0
  41. data/test/benchmark.rb +91 -0
  42. data/test/gemfiles/Gemfile.rails-2.0.x +8 -0
  43. data/test/gemfiles/Gemfile.rails-2.0.x.lock +38 -0
  44. data/test/gemfiles/Gemfile.rails-2.1.x +8 -0
  45. data/test/gemfiles/Gemfile.rails-2.1.x.lock +38 -0
  46. data/test/gemfiles/Gemfile.rails-2.2.x +8 -0
  47. data/test/gemfiles/Gemfile.rails-2.2.x.lock +38 -0
  48. data/test/gemfiles/Gemfile.rails-2.3.x +8 -0
  49. data/test/gemfiles/Gemfile.rails-2.3.x.lock +40 -0
  50. data/test/gemfiles/Gemfile.rails-3.0.x +8 -0
  51. data/test/gemfiles/Gemfile.rails-3.0.x.lock +85 -0
  52. data/test/gemfiles/Gemfile.rails-3.1.x +8 -0
  53. data/test/gemfiles/Gemfile.rails-3.1.x.lock +98 -0
  54. data/test/gemfiles/Gemfile.rails-xss-2.3.x +9 -0
  55. data/test/gemfiles/Gemfile.rails-xss-2.3.x.lock +42 -0
  56. data/test/haml-ejs/haml-ejs_test.rb +22 -0
  57. data/test/haml-ejs/templates/conditional.input +3 -0
  58. data/test/haml-ejs/templates/conditional.output +3 -0
  59. data/test/haml-ejs/templates/conditional_and_interpolation.input +2 -0
  60. data/test/haml-ejs/templates/conditional_and_interpolation.output +3 -0
  61. data/test/haml-ejs/templates/conditional_else.input +5 -0
  62. data/test/haml-ejs/templates/conditional_else.output +5 -0
  63. data/test/haml-ejs/templates/conditional_else_elsif.input +7 -0
  64. data/test/haml-ejs/templates/conditional_else_elsif.output +7 -0
  65. data/test/haml-ejs/templates/conditional_elsif.input +5 -0
  66. data/test/haml-ejs/templates/conditional_elsif.output +5 -0
  67. data/test/haml-ejs/templates/conditional_unless.input +3 -0
  68. data/test/haml-ejs/templates/conditional_unless.output +3 -0
  69. data/test/haml-ejs/templates/conditional_unless_else.input +5 -0
  70. data/test/haml-ejs/templates/conditional_unless_else.output +5 -0
  71. data/test/haml-ejs/templates/conditional_unless_elsif_else.input +5 -0
  72. data/test/haml-ejs/templates/conditional_unless_elsif_else.output +5 -0
  73. data/test/haml-ejs/templates/interpolation.input +2 -0
  74. data/test/haml-ejs/templates/interpolation.output +1 -0
  75. data/test/haml-ejs/templates/iteration.input +2 -0
  76. data/test/haml-ejs/templates/iteration.output +3 -0
  77. data/test/haml-ejs/templates/suite.input +26 -0
  78. data/test/haml-ejs/templates/suite.output +27 -0
  79. data/test/haml/engine_test.rb +1925 -0
  80. data/test/haml/erb/_av_partial_1.erb +12 -0
  81. data/test/haml/erb/_av_partial_2.erb +8 -0
  82. data/test/haml/erb/action_view.erb +62 -0
  83. data/test/haml/erb/standard.erb +55 -0
  84. data/test/haml/helper_test.rb +454 -0
  85. data/test/haml/html2haml/erb_tests.rb +440 -0
  86. data/test/haml/html2haml_test.rb +336 -0
  87. data/test/haml/markaby/standard.mab +52 -0
  88. data/test/haml/mocks/article.rb +6 -0
  89. data/test/haml/results/content_for_layout.xhtml +12 -0
  90. data/test/haml/results/eval_suppressed.xhtml +9 -0
  91. data/test/haml/results/filters.xhtml +62 -0
  92. data/test/haml/results/helpers.xhtml +70 -0
  93. data/test/haml/results/helpful.xhtml +10 -0
  94. data/test/haml/results/just_stuff.xhtml +70 -0
  95. data/test/haml/results/list.xhtml +12 -0
  96. data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
  97. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  98. data/test/haml/results/original_engine.xhtml +20 -0
  99. data/test/haml/results/partial_layout.xhtml +5 -0
  100. data/test/haml/results/partials.xhtml +21 -0
  101. data/test/haml/results/render_layout.xhtml +3 -0
  102. data/test/haml/results/silent_script.xhtml +74 -0
  103. data/test/haml/results/standard.xhtml +162 -0
  104. data/test/haml/results/tag_parsing.xhtml +23 -0
  105. data/test/haml/results/very_basic.xhtml +5 -0
  106. data/test/haml/results/whitespace_handling.xhtml +89 -0
  107. data/test/haml/spec/README.md +97 -0
  108. data/test/haml/spec/lua_haml_spec.lua +30 -0
  109. data/test/haml/spec/ruby_haml_test.rb +19 -0
  110. data/test/haml/spec/tests.json +534 -0
  111. data/test/haml/spec_test.rb +44 -0
  112. data/test/haml/template_test.rb +419 -0
  113. data/test/haml/templates/_av_partial_1.haml +9 -0
  114. data/test/haml/templates/_av_partial_1_ugly.haml +9 -0
  115. data/test/haml/templates/_av_partial_2.haml +5 -0
  116. data/test/haml/templates/_av_partial_2_ugly.haml +5 -0
  117. data/test/haml/templates/_layout.erb +3 -0
  118. data/test/haml/templates/_layout_for_partial.haml +3 -0
  119. data/test/haml/templates/_partial.haml +8 -0
  120. data/test/haml/templates/_text_area.haml +3 -0
  121. data/test/haml/templates/action_view.haml +47 -0
  122. data/test/haml/templates/action_view_ugly.haml +47 -0
  123. data/test/haml/templates/breakage.haml +8 -0
  124. data/test/haml/templates/content_for_layout.haml +8 -0
  125. data/test/haml/templates/eval_suppressed.haml +11 -0
  126. data/test/haml/templates/filters.haml +66 -0
  127. data/test/haml/templates/helpers.haml +55 -0
  128. data/test/haml/templates/helpful.haml +11 -0
  129. data/test/haml/templates/just_stuff.haml +85 -0
  130. data/test/haml/templates/list.haml +12 -0
  131. data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
  132. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  133. data/test/haml/templates/original_engine.haml +17 -0
  134. data/test/haml/templates/partial_layout.haml +10 -0
  135. data/test/haml/templates/partialize.haml +1 -0
  136. data/test/haml/templates/partials.haml +12 -0
  137. data/test/haml/templates/render_layout.haml +2 -0
  138. data/test/haml/templates/silent_script.haml +40 -0
  139. data/test/haml/templates/standard.haml +43 -0
  140. data/test/haml/templates/standard_ugly.haml +43 -0
  141. data/test/haml/templates/tag_parsing.haml +21 -0
  142. data/test/haml/templates/very_basic.haml +4 -0
  143. data/test/haml/templates/whitespace_handling.haml +87 -0
  144. data/test/haml/util_test.rb +300 -0
  145. data/test/linked_rails.rb +42 -0
  146. data/test/test_helper.rb +75 -0
  147. data/vendor/sass/CONTRIBUTING +3 -0
  148. data/vendor/sass/MIT-LICENSE +20 -0
  149. data/vendor/sass/README.md +201 -0
  150. data/vendor/sass/Rakefile +339 -0
  151. data/vendor/sass/TODO +39 -0
  152. data/vendor/sass/VERSION +1 -0
  153. data/vendor/sass/VERSION_NAME +1 -0
  154. data/vendor/sass/bin/sass +8 -0
  155. data/vendor/sass/bin/sass-convert +7 -0
  156. data/vendor/sass/bin/scss +8 -0
  157. data/vendor/sass/doc-src/FAQ.md +35 -0
  158. data/vendor/sass/doc-src/INDENTED_SYNTAX.md +210 -0
  159. data/vendor/sass/doc-src/SASS_CHANGELOG.md +2214 -0
  160. data/vendor/sass/doc-src/SASS_REFERENCE.md +1957 -0
  161. data/vendor/sass/doc-src/SCSS_FOR_SASS_USERS.md +155 -0
  162. data/vendor/sass/ext/extconf.rb +10 -0
  163. data/vendor/sass/extra/update_watch.rb +13 -0
  164. data/vendor/sass/init.rb +18 -0
  165. data/vendor/sass/lib/sass.rb +72 -0
  166. data/vendor/sass/lib/sass/cache_stores.rb +15 -0
  167. data/vendor/sass/lib/sass/cache_stores/base.rb +84 -0
  168. data/vendor/sass/lib/sass/cache_stores/chain.rb +33 -0
  169. data/vendor/sass/lib/sass/cache_stores/filesystem.rb +58 -0
  170. data/vendor/sass/lib/sass/cache_stores/memory.rb +47 -0
  171. data/vendor/sass/lib/sass/cache_stores/null.rb +25 -0
  172. data/vendor/sass/lib/sass/callbacks.rb +66 -0
  173. data/vendor/sass/lib/sass/css.rb +294 -0
  174. data/vendor/sass/lib/sass/engine.rb +862 -0
  175. data/vendor/sass/lib/sass/environment.rb +155 -0
  176. data/vendor/sass/lib/sass/error.rb +201 -0
  177. data/vendor/sass/lib/sass/exec.rb +659 -0
  178. data/vendor/sass/lib/sass/importers.rb +22 -0
  179. data/vendor/sass/lib/sass/importers/base.rb +138 -0
  180. data/vendor/sass/lib/sass/importers/filesystem.rb +144 -0
  181. data/vendor/sass/lib/sass/less.rb +382 -0
  182. data/vendor/sass/lib/sass/plugin.rb +136 -0
  183. data/vendor/sass/lib/sass/plugin/compiler.rb +358 -0
  184. data/vendor/sass/lib/sass/plugin/configuration.rb +125 -0
  185. data/vendor/sass/lib/sass/plugin/generic.rb +15 -0
  186. data/vendor/sass/lib/sass/plugin/merb.rb +48 -0
  187. data/vendor/sass/lib/sass/plugin/rack.rb +60 -0
  188. data/vendor/sass/lib/sass/plugin/rails.rb +47 -0
  189. data/vendor/sass/lib/sass/plugin/staleness_checker.rb +173 -0
  190. data/vendor/sass/lib/sass/railtie.rb +9 -0
  191. data/vendor/sass/lib/sass/repl.rb +58 -0
  192. data/vendor/sass/lib/sass/root.rb +7 -0
  193. data/vendor/sass/lib/sass/script.rb +40 -0
  194. data/vendor/sass/lib/sass/script/bool.rb +18 -0
  195. data/vendor/sass/lib/sass/script/color.rb +480 -0
  196. data/vendor/sass/lib/sass/script/css_lexer.rb +29 -0
  197. data/vendor/sass/lib/sass/script/css_parser.rb +31 -0
  198. data/vendor/sass/lib/sass/script/funcall.rb +162 -0
  199. data/vendor/sass/lib/sass/script/functions.rb +1343 -0
  200. data/vendor/sass/lib/sass/script/interpolation.rb +70 -0
  201. data/vendor/sass/lib/sass/script/lexer.rb +334 -0
  202. data/vendor/sass/lib/sass/script/list.rb +76 -0
  203. data/vendor/sass/lib/sass/script/literal.rb +245 -0
  204. data/vendor/sass/lib/sass/script/node.rb +91 -0
  205. data/vendor/sass/lib/sass/script/number.rb +429 -0
  206. data/vendor/sass/lib/sass/script/operation.rb +91 -0
  207. data/vendor/sass/lib/sass/script/parser.rb +467 -0
  208. data/vendor/sass/lib/sass/script/string.rb +51 -0
  209. data/vendor/sass/lib/sass/script/string_interpolation.rb +94 -0
  210. data/vendor/sass/lib/sass/script/unary_operation.rb +57 -0
  211. data/vendor/sass/lib/sass/script/variable.rb +54 -0
  212. data/vendor/sass/lib/sass/scss.rb +17 -0
  213. data/vendor/sass/lib/sass/scss/css_parser.rb +46 -0
  214. data/vendor/sass/lib/sass/scss/parser.rb +920 -0
  215. data/vendor/sass/lib/sass/scss/rx.rb +127 -0
  216. data/vendor/sass/lib/sass/scss/sass_parser.rb +11 -0
  217. data/vendor/sass/lib/sass/scss/script_lexer.rb +15 -0
  218. data/vendor/sass/lib/sass/scss/script_parser.rb +25 -0
  219. data/vendor/sass/lib/sass/scss/static_parser.rb +40 -0
  220. data/vendor/sass/lib/sass/selector.rb +361 -0
  221. data/vendor/sass/lib/sass/selector/abstract_sequence.rb +62 -0
  222. data/vendor/sass/lib/sass/selector/comma_sequence.rb +81 -0
  223. data/vendor/sass/lib/sass/selector/sequence.rb +233 -0
  224. data/vendor/sass/lib/sass/selector/simple.rb +113 -0
  225. data/vendor/sass/lib/sass/selector/simple_sequence.rb +134 -0
  226. data/vendor/sass/lib/sass/shared.rb +78 -0
  227. data/vendor/sass/lib/sass/tree/charset_node.rb +22 -0
  228. data/vendor/sass/lib/sass/tree/comment_node.rb +77 -0
  229. data/vendor/sass/lib/sass/tree/debug_node.rb +18 -0
  230. data/vendor/sass/lib/sass/tree/directive_node.rb +23 -0
  231. data/vendor/sass/lib/sass/tree/each_node.rb +24 -0
  232. data/vendor/sass/lib/sass/tree/extend_node.rb +29 -0
  233. data/vendor/sass/lib/sass/tree/for_node.rb +36 -0
  234. data/vendor/sass/lib/sass/tree/function_node.rb +27 -0
  235. data/vendor/sass/lib/sass/tree/if_node.rb +65 -0
  236. data/vendor/sass/lib/sass/tree/import_node.rb +68 -0
  237. data/vendor/sass/lib/sass/tree/media_node.rb +32 -0
  238. data/vendor/sass/lib/sass/tree/mixin_def_node.rb +27 -0
  239. data/vendor/sass/lib/sass/tree/mixin_node.rb +32 -0
  240. data/vendor/sass/lib/sass/tree/node.rb +204 -0
  241. data/vendor/sass/lib/sass/tree/prop_node.rb +155 -0
  242. data/vendor/sass/lib/sass/tree/return_node.rb +18 -0
  243. data/vendor/sass/lib/sass/tree/root_node.rb +28 -0
  244. data/vendor/sass/lib/sass/tree/rule_node.rb +129 -0
  245. data/vendor/sass/lib/sass/tree/variable_node.rb +30 -0
  246. data/vendor/sass/lib/sass/tree/visitors/base.rb +75 -0
  247. data/vendor/sass/lib/sass/tree/visitors/check_nesting.rb +134 -0
  248. data/vendor/sass/lib/sass/tree/visitors/convert.rb +255 -0
  249. data/vendor/sass/lib/sass/tree/visitors/cssize.rb +175 -0
  250. data/vendor/sass/lib/sass/tree/visitors/perform.rb +301 -0
  251. data/vendor/sass/lib/sass/tree/visitors/to_css.rb +216 -0
  252. data/vendor/sass/lib/sass/tree/warn_node.rb +18 -0
  253. data/vendor/sass/lib/sass/tree/while_node.rb +18 -0
  254. data/vendor/sass/lib/sass/util.rb +669 -0
  255. data/vendor/sass/lib/sass/util/subset_map.rb +101 -0
  256. data/vendor/sass/lib/sass/version.rb +112 -0
  257. data/vendor/sass/rails/init.rb +1 -0
  258. data/vendor/sass/sass.gemspec +32 -0
  259. data/vendor/sass/test/sass/cache_test.rb +74 -0
  260. data/vendor/sass/test/sass/callbacks_test.rb +61 -0
  261. data/vendor/sass/test/sass/conversion_test.rb +1203 -0
  262. data/vendor/sass/test/sass/css2sass_test.rb +364 -0
  263. data/vendor/sass/test/sass/data/hsl-rgb.txt +319 -0
  264. data/vendor/sass/test/sass/engine_test.rb +2469 -0
  265. data/vendor/sass/test/sass/extend_test.rb +1348 -0
  266. data/vendor/sass/test/sass/functions_test.rb +1025 -0
  267. data/vendor/sass/test/sass/importer_test.rb +82 -0
  268. data/vendor/sass/test/sass/less_conversion_test.rb +653 -0
  269. data/vendor/sass/test/sass/mock_importer.rb +49 -0
  270. data/vendor/sass/test/sass/more_results/more1.css +9 -0
  271. data/vendor/sass/test/sass/more_results/more1_with_line_comments.css +26 -0
  272. data/vendor/sass/test/sass/more_results/more_import.css +29 -0
  273. data/vendor/sass/test/sass/more_templates/_more_partial.sass +2 -0
  274. data/vendor/sass/test/sass/more_templates/more1.sass +23 -0
  275. data/vendor/sass/test/sass/more_templates/more_import.sass +11 -0
  276. data/vendor/sass/test/sass/plugin_test.rb +469 -0
  277. data/vendor/sass/test/sass/results/alt.css +4 -0
  278. data/vendor/sass/test/sass/results/basic.css +9 -0
  279. data/vendor/sass/test/sass/results/compact.css +5 -0
  280. data/vendor/sass/test/sass/results/complex.css +86 -0
  281. data/vendor/sass/test/sass/results/compressed.css +1 -0
  282. data/vendor/sass/test/sass/results/expanded.css +19 -0
  283. data/vendor/sass/test/sass/results/if.css +3 -0
  284. data/vendor/sass/test/sass/results/import.css +31 -0
  285. data/vendor/sass/test/sass/results/import_charset.css +4 -0
  286. data/vendor/sass/test/sass/results/import_charset_1_8.css +4 -0
  287. data/vendor/sass/test/sass/results/import_charset_ibm866.css +4 -0
  288. data/vendor/sass/test/sass/results/line_numbers.css +49 -0
  289. data/vendor/sass/test/sass/results/mixins.css +95 -0
  290. data/vendor/sass/test/sass/results/multiline.css +24 -0
  291. data/vendor/sass/test/sass/results/nested.css +22 -0
  292. data/vendor/sass/test/sass/results/options.css +1 -0
  293. data/vendor/sass/test/sass/results/parent_ref.css +13 -0
  294. data/vendor/sass/test/sass/results/script.css +16 -0
  295. data/vendor/sass/test/sass/results/scss_import.css +31 -0
  296. data/vendor/sass/test/sass/results/scss_importee.css +2 -0
  297. data/vendor/sass/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  298. data/vendor/sass/test/sass/results/subdir/subdir.css +3 -0
  299. data/vendor/sass/test/sass/results/units.css +11 -0
  300. data/vendor/sass/test/sass/results/warn.css +0 -0
  301. data/vendor/sass/test/sass/results/warn_imported.css +0 -0
  302. data/vendor/sass/test/sass/script_conversion_test.rb +283 -0
  303. data/vendor/sass/test/sass/script_test.rb +496 -0
  304. data/vendor/sass/test/sass/scss/css_test.rb +916 -0
  305. data/vendor/sass/test/sass/scss/rx_test.rb +156 -0
  306. data/vendor/sass/test/sass/scss/scss_test.rb +1249 -0
  307. data/vendor/sass/test/sass/scss/test_helper.rb +37 -0
  308. data/vendor/sass/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  309. data/vendor/sass/test/sass/templates/_imported_charset_utf8.sass +4 -0
  310. data/vendor/sass/test/sass/templates/_partial.sass +2 -0
  311. data/vendor/sass/test/sass/templates/alt.sass +16 -0
  312. data/vendor/sass/test/sass/templates/basic.sass +23 -0
  313. data/vendor/sass/test/sass/templates/bork1.sass +2 -0
  314. data/vendor/sass/test/sass/templates/bork2.sass +2 -0
  315. data/vendor/sass/test/sass/templates/bork3.sass +2 -0
  316. data/vendor/sass/test/sass/templates/bork4.sass +2 -0
  317. data/vendor/sass/test/sass/templates/compact.sass +17 -0
  318. data/vendor/sass/test/sass/templates/complex.sass +305 -0
  319. data/vendor/sass/test/sass/templates/compressed.sass +15 -0
  320. data/vendor/sass/test/sass/templates/expanded.sass +17 -0
  321. data/vendor/sass/test/sass/templates/if.sass +11 -0
  322. data/vendor/sass/test/sass/templates/import.sass +12 -0
  323. data/vendor/sass/test/sass/templates/import_charset.sass +7 -0
  324. data/vendor/sass/test/sass/templates/import_charset_1_8.sass +4 -0
  325. data/vendor/sass/test/sass/templates/import_charset_ibm866.sass +9 -0
  326. data/vendor/sass/test/sass/templates/importee.less +2 -0
  327. data/vendor/sass/test/sass/templates/importee.sass +19 -0
  328. data/vendor/sass/test/sass/templates/line_numbers.sass +13 -0
  329. data/vendor/sass/test/sass/templates/mixin_bork.sass +5 -0
  330. data/vendor/sass/test/sass/templates/mixins.sass +76 -0
  331. data/vendor/sass/test/sass/templates/multiline.sass +20 -0
  332. data/vendor/sass/test/sass/templates/nested.sass +25 -0
  333. data/vendor/sass/test/sass/templates/nested_bork1.sass +2 -0
  334. data/vendor/sass/test/sass/templates/nested_bork2.sass +2 -0
  335. data/vendor/sass/test/sass/templates/nested_bork3.sass +2 -0
  336. data/vendor/sass/test/sass/templates/nested_bork4.sass +2 -0
  337. data/vendor/sass/test/sass/templates/nested_import.sass +2 -0
  338. data/vendor/sass/test/sass/templates/nested_mixin_bork.sass +6 -0
  339. data/vendor/sass/test/sass/templates/options.sass +2 -0
  340. data/vendor/sass/test/sass/templates/parent_ref.sass +25 -0
  341. data/vendor/sass/test/sass/templates/script.sass +101 -0
  342. data/vendor/sass/test/sass/templates/scss_import.scss +11 -0
  343. data/vendor/sass/test/sass/templates/scss_importee.scss +1 -0
  344. data/vendor/sass/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  345. data/vendor/sass/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  346. data/vendor/sass/test/sass/templates/subdir/subdir.sass +6 -0
  347. data/vendor/sass/test/sass/templates/units.sass +11 -0
  348. data/vendor/sass/test/sass/templates/warn.sass +3 -0
  349. data/vendor/sass/test/sass/templates/warn_imported.sass +4 -0
  350. data/vendor/sass/test/sass/test_helper.rb +8 -0
  351. data/vendor/sass/test/sass/util/subset_map_test.rb +91 -0
  352. data/vendor/sass/test/sass/util_test.rb +254 -0
  353. data/vendor/sass/test/test_helper.rb +69 -0
  354. data/vendor/sass/yard/callbacks.rb +29 -0
  355. data/vendor/sass/yard/default/fulldoc/html/css/common.sass +26 -0
  356. data/vendor/sass/yard/default/layout/html/footer.erb +12 -0
  357. data/vendor/sass/yard/inherited_hash.rb +41 -0
  358. metadata +458 -0
@@ -0,0 +1,47 @@
1
+ module Sass
2
+ module CacheStores
3
+ # A backend for the Sass cache using in-process memory.
4
+ class Memory < Base
5
+ # Since the {Memory} store is stored in the Sass tree's options hash,
6
+ # when the options get serialized as part of serializing the tree,
7
+ # you get crazy exponential growth in the size of the cached objects
8
+ # unless you don't dump the cache.
9
+ #
10
+ # @private
11
+ def _dump(depth)
12
+ ""
13
+ end
14
+
15
+ # If we deserialize this class, just make a new empty one.
16
+ #
17
+ # @private
18
+ def self._load(repr)
19
+ Memory.new
20
+ end
21
+
22
+ # Create a new, empty cache store.
23
+ def initialize
24
+ @contents = {}
25
+ end
26
+
27
+ # @see Base#retrieve
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
34
+ end
35
+
36
+ # @see Base#store
37
+ def store(key, sha, obj)
38
+ @contents[key] = {:sha => sha, :obj => obj}
39
+ end
40
+
41
+ # Destructively clear the cache.
42
+ def reset!
43
+ @contents = {}
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,25 @@
1
+ module Sass
2
+ module CacheStores
3
+ # Doesn't store anything, but records what things it should have stored.
4
+ # This doesn't currently have any use except for testing and debugging.
5
+ #
6
+ # @private
7
+ class Null < Base
8
+ def initialize
9
+ @keys = {}
10
+ end
11
+
12
+ def _retrieve(key, version, sha)
13
+ nil
14
+ end
15
+
16
+ def _store(key, version, sha, contents)
17
+ @keys[key] = true
18
+ end
19
+
20
+ def was_set?(key)
21
+ @keys[key]
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,66 @@
1
+ module Sass
2
+ # A lightweight infrastructure for defining and running callbacks.
3
+ # Callbacks are defined using \{#define\_callback\} at the class level,
4
+ # and called using `run_#{name}` at the instance level.
5
+ #
6
+ # Clients can add callbacks by calling the generated `on_#{name}` method,
7
+ # and passing in a block that's run when the callback is activated.
8
+ #
9
+ # @example Define a callback
10
+ # class Munger
11
+ # extend Sass::Callbacks
12
+ # define_callback :string_munged
13
+ #
14
+ # def munge(str)
15
+ # res = str.gsub(/[a-z]/, '\1\1')
16
+ # run_string_munged str, res
17
+ # res
18
+ # end
19
+ # end
20
+ #
21
+ # @example Use a callback
22
+ # m = Munger.new
23
+ # m.on_string_munged {|str, res| puts "#{str} was munged into #{res}!"}
24
+ # m.munge "bar" #=> bar was munged into bbaarr!
25
+ module Callbacks
26
+ # Automatically includes {InstanceMethods}
27
+ # when something extends this module.
28
+ #
29
+ # @param base [Module]
30
+ def self.extended(base)
31
+ base.send(:include, InstanceMethods)
32
+ end
33
+ protected
34
+
35
+ module InstanceMethods
36
+ # Removes all callbacks registered against this object.
37
+ def clear_callbacks!
38
+ @_sass_callbacks = {}
39
+ end
40
+ end
41
+
42
+ # Define a callback with the given name.
43
+ # This will define an `on_#{name}` method
44
+ # that registers a block,
45
+ # and a `run_#{name}` method that runs that block
46
+ # (optionall with some arguments).
47
+ #
48
+ # @param name [Symbol] The name of the callback
49
+ # @return [void]
50
+ def define_callback(name)
51
+ class_eval <<RUBY
52
+ def on_#{name}(&block)
53
+ @_sass_callbacks ||= {}
54
+ (@_sass_callbacks[#{name.inspect}] ||= []) << block
55
+ end
56
+
57
+ def run_#{name}(*args)
58
+ return unless @_sass_callbacks
59
+ return unless @_sass_callbacks[#{name.inspect}]
60
+ @_sass_callbacks[#{name.inspect}].each {|c| c[*args]}
61
+ end
62
+ private :run_#{name}
63
+ RUBY
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,294 @@
1
+ require File.dirname(__FILE__) + '/../sass'
2
+ require 'sass/tree/node'
3
+ require 'sass/scss/css_parser'
4
+ require 'strscan'
5
+
6
+ module Sass
7
+ # This class converts CSS documents into Sass or SCSS templates.
8
+ # It works by parsing the CSS document into a {Sass::Tree} structure,
9
+ # and then applying various transformations to the structure
10
+ # to produce more concise and idiomatic Sass/SCSS.
11
+ #
12
+ # Example usage:
13
+ #
14
+ # Sass::CSS.new("p { color: blue }").render(:sass) #=> "p\n color: blue"
15
+ # Sass::CSS.new("p { color: blue }").render(:scss) #=> "p {\n color: blue; }"
16
+ class CSS
17
+ # @param template [String] The CSS stylesheet.
18
+ # This stylesheet can be encoded using any encoding
19
+ # that can be converted to Unicode.
20
+ # If the stylesheet contains an `@charset` declaration,
21
+ # that overrides the Ruby encoding
22
+ # (see {file:SASS_REFERENCE.md#encodings the encoding documentation})
23
+ # @option options :old [Boolean] (false)
24
+ # Whether or not to output old property syntax
25
+ # (`:color blue` as opposed to `color: blue`).
26
+ # This is only meaningful when generating Sass code,
27
+ # rather than SCSS.
28
+ def initialize(template, options = {})
29
+ if template.is_a? IO
30
+ template = template.read
31
+ end
32
+
33
+ @options = options.dup
34
+ # Backwards compatibility
35
+ @options[:old] = true if @options[:alternate] == false
36
+ @template = template
37
+ end
38
+
39
+ # Converts the CSS template into Sass or SCSS code.
40
+ #
41
+ # @param fmt [Symbol] `:sass` or `:scss`, designating the format to return.
42
+ # @return [String] The resulting Sass or SCSS code
43
+ # @raise [Sass::SyntaxError] if there's an error parsing the CSS template
44
+ def render(fmt = :sass)
45
+ check_encoding!
46
+ build_tree.send("to_#{fmt}", @options).strip + "\n"
47
+ rescue Sass::SyntaxError => err
48
+ err.modify_backtrace(:filename => @options[:filename] || '(css)')
49
+ raise err
50
+ end
51
+
52
+ # Returns the original encoding of the document,
53
+ # or `nil` under Ruby 1.8.
54
+ #
55
+ # @return [Encoding, nil]
56
+ # @raise [Encoding::UndefinedConversionError] if the source encoding
57
+ # cannot be converted to UTF-8
58
+ # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
59
+ def source_encoding
60
+ check_encoding!
61
+ @original_encoding
62
+ end
63
+
64
+ private
65
+
66
+ def check_encoding!
67
+ return if @checked_encoding
68
+ @checked_encoding = true
69
+ @template, @original_encoding = Sass::Util.check_sass_encoding(@template) do |msg, line|
70
+ raise Sass::SyntaxError.new(msg, :line => line)
71
+ end
72
+ end
73
+
74
+ # Parses the CSS template and applies various transformations
75
+ #
76
+ # @return [Tree::Node] The root node of the parsed tree
77
+ def build_tree
78
+ root = Sass::SCSS::CssParser.new(@template).parse
79
+ expand_commas root
80
+ parent_ref_rules root
81
+ remove_parent_refs root
82
+ flatten_rules root
83
+ fold_commas root
84
+ root
85
+ end
86
+
87
+ # Transform
88
+ #
89
+ # foo, bar, baz
90
+ # color: blue
91
+ #
92
+ # into
93
+ #
94
+ # foo
95
+ # color: blue
96
+ # bar
97
+ # color: blue
98
+ # baz
99
+ # color: blue
100
+ #
101
+ # @param root [Tree::Node] The parent node
102
+ def expand_commas(root)
103
+ root.children.map! do |child|
104
+ unless child.is_a?(Tree::RuleNode) && child.rule.first.include?(',')
105
+ expand_commas(child) if child.is_a?(Tree::DirectiveNode)
106
+ next child
107
+ end
108
+ child.rule.first.split(',').map do |rule|
109
+ node = Tree::RuleNode.new([rule.strip])
110
+ node.children = child.children
111
+ node
112
+ end
113
+ end
114
+ root.children.flatten!
115
+ end
116
+
117
+ # Make rules use parent refs so that
118
+ #
119
+ # foo
120
+ # color: green
121
+ # foo.bar
122
+ # color: blue
123
+ #
124
+ # becomes
125
+ #
126
+ # foo
127
+ # color: green
128
+ # &.bar
129
+ # color: blue
130
+ #
131
+ # This has the side effect of nesting rules,
132
+ # so that
133
+ #
134
+ # foo
135
+ # color: green
136
+ # foo bar
137
+ # color: red
138
+ # foo baz
139
+ # color: blue
140
+ #
141
+ # becomes
142
+ #
143
+ # foo
144
+ # color: green
145
+ # & bar
146
+ # color: red
147
+ # & baz
148
+ # color: blue
149
+ #
150
+ # @param root [Tree::Node] The parent node
151
+ def parent_ref_rules(root)
152
+ current_rule = nil
153
+ root.children.map! do |child|
154
+ unless child.is_a?(Tree::RuleNode)
155
+ parent_ref_rules(child) if child.is_a?(Tree::DirectiveNode)
156
+ next child
157
+ end
158
+
159
+ first, rest = child.rule.first.scan(/\A(&?(?: .|[^ ])[^.#: \[]*)([.#: \[].*)?\Z/m).first
160
+
161
+ if current_rule.nil? || current_rule.rule.first != first
162
+ current_rule = Tree::RuleNode.new([first])
163
+ end
164
+
165
+ if rest
166
+ child.rule = ["&" + rest]
167
+ current_rule << child
168
+ else
169
+ current_rule.children += child.children
170
+ end
171
+
172
+ current_rule
173
+ end
174
+ root.children.compact!
175
+ root.children.uniq!
176
+
177
+ root.children.each { |v| parent_ref_rules(v) }
178
+ end
179
+
180
+ # Remove useless parent refs so that
181
+ #
182
+ # foo
183
+ # & bar
184
+ # color: blue
185
+ #
186
+ # becomes
187
+ #
188
+ # foo
189
+ # bar
190
+ # color: blue
191
+ #
192
+ # @param root [Tree::Node] The parent node
193
+ def remove_parent_refs(root)
194
+ root.children.each do |child|
195
+ case child
196
+ when Tree::RuleNode
197
+ child.rule.first.gsub! /^& +/, ''
198
+ remove_parent_refs child
199
+ when Tree::DirectiveNode
200
+ remove_parent_refs child
201
+ end
202
+ end
203
+ end
204
+
205
+ # Flatten rules so that
206
+ #
207
+ # foo
208
+ # bar
209
+ # color: red
210
+ #
211
+ # becomes
212
+ #
213
+ # foo bar
214
+ # color: red
215
+ #
216
+ # and
217
+ #
218
+ # foo
219
+ # &.bar
220
+ # color: blue
221
+ #
222
+ # becomes
223
+ #
224
+ # foo.bar
225
+ # color: blue
226
+ #
227
+ # @param root [Tree::Node] The parent node
228
+ def flatten_rules(root)
229
+ root.children.each do |child|
230
+ case child
231
+ when Tree::RuleNode
232
+ flatten_rule(child)
233
+ when Tree::DirectiveNode
234
+ flatten_rules(child)
235
+ end
236
+ end
237
+ end
238
+
239
+ # Flattens a single rule
240
+ #
241
+ # @param rule [Tree::RuleNode] The candidate for flattening
242
+ # @see #flatten_rules
243
+ def flatten_rule(rule)
244
+ while rule.children.size == 1 && rule.children.first.is_a?(Tree::RuleNode)
245
+ child = rule.children.first
246
+
247
+ if child.rule.first[0] == ?&
248
+ rule.rule = [child.rule.first.gsub(/^&/, rule.rule.first)]
249
+ else
250
+ rule.rule = ["#{rule.rule.first} #{child.rule.first}"]
251
+ end
252
+
253
+ rule.children = child.children
254
+ end
255
+
256
+ flatten_rules(rule)
257
+ end
258
+
259
+ # Transform
260
+ #
261
+ # foo
262
+ # bar
263
+ # color: blue
264
+ # baz
265
+ # color: blue
266
+ #
267
+ # into
268
+ #
269
+ # foo
270
+ # bar, baz
271
+ # color: blue
272
+ #
273
+ # @param rule [Tree::RuleNode] The candidate for flattening
274
+ def fold_commas(root)
275
+ prev_rule = nil
276
+ root.children.map! do |child|
277
+ unless child.is_a?(Tree::RuleNode)
278
+ fold_commas(child) if child.is_a?(Tree::DirectiveNode)
279
+ next child
280
+ end
281
+
282
+ if prev_rule && prev_rule.children == child.children
283
+ prev_rule.rule.first << ", #{child.rule.first}"
284
+ next nil
285
+ end
286
+
287
+ fold_commas(child)
288
+ prev_rule = child
289
+ child
290
+ end
291
+ root.children.compact!
292
+ end
293
+ end
294
+ end
@@ -0,0 +1,862 @@
1
+ require 'strscan'
2
+ require 'set'
3
+ require 'digest/sha1'
4
+ require 'sass/cache_stores'
5
+ require 'sass/tree/node'
6
+ require 'sass/tree/root_node'
7
+ require 'sass/tree/rule_node'
8
+ require 'sass/tree/comment_node'
9
+ require 'sass/tree/prop_node'
10
+ require 'sass/tree/directive_node'
11
+ require 'sass/tree/media_node'
12
+ require 'sass/tree/variable_node'
13
+ require 'sass/tree/mixin_def_node'
14
+ require 'sass/tree/mixin_node'
15
+ require 'sass/tree/function_node'
16
+ require 'sass/tree/return_node'
17
+ require 'sass/tree/extend_node'
18
+ require 'sass/tree/if_node'
19
+ require 'sass/tree/while_node'
20
+ require 'sass/tree/for_node'
21
+ require 'sass/tree/each_node'
22
+ require 'sass/tree/debug_node'
23
+ require 'sass/tree/warn_node'
24
+ require 'sass/tree/import_node'
25
+ require 'sass/tree/charset_node'
26
+ require 'sass/tree/visitors/base'
27
+ require 'sass/tree/visitors/perform'
28
+ require 'sass/tree/visitors/cssize'
29
+ require 'sass/tree/visitors/convert'
30
+ require 'sass/tree/visitors/to_css'
31
+ require 'sass/tree/visitors/check_nesting'
32
+ require 'sass/selector'
33
+ require 'sass/environment'
34
+ require 'sass/script'
35
+ require 'sass/scss'
36
+ require 'sass/error'
37
+ require 'sass/importers'
38
+ require 'sass/shared'
39
+
40
+ module Sass
41
+
42
+ # A Sass mixin or function.
43
+ #
44
+ # `name`: `String`
45
+ # : The name of the mixin/function.
46
+ #
47
+ # `args`: `Array<(String, Script::Node)>`
48
+ # : The arguments for the mixin/function.
49
+ # Each element is a tuple containing the name of the argument
50
+ # and the parse tree for the default value of the argument.
51
+ #
52
+ # `environment`: {Sass::Environment}
53
+ # : The environment in which the mixin/function was defined.
54
+ # This is captured so that the mixin/function can have access
55
+ # to local variables defined in its scope.
56
+ #
57
+ # `tree`: `Array<Tree::Node>`
58
+ # : The parse tree for the mixin/function.
59
+ Callable = Struct.new(:name, :args, :environment, :tree)
60
+
61
+ # This class handles the parsing and compilation of the Sass template.
62
+ # Example usage:
63
+ #
64
+ # template = File.load('stylesheets/sassy.sass')
65
+ # sass_engine = Sass::Engine.new(template)
66
+ # output = sass_engine.render
67
+ # puts output
68
+ class Engine
69
+ include Sass::Util
70
+
71
+ # A line of Sass code.
72
+ #
73
+ # `text`: `String`
74
+ # : The text in the line, without any whitespace at the beginning or end.
75
+ #
76
+ # `tabs`: `Fixnum`
77
+ # : The level of indentation of the line.
78
+ #
79
+ # `index`: `Fixnum`
80
+ # : The line number in the original document.
81
+ #
82
+ # `offset`: `Fixnum`
83
+ # : The number of bytes in on the line that the text begins.
84
+ # This ends up being the number of bytes of leading whitespace.
85
+ #
86
+ # `filename`: `String`
87
+ # : The name of the file in which this line appeared.
88
+ #
89
+ # `children`: `Array<Line>`
90
+ # : The lines nested below this one.
91
+ class Line < Struct.new(:text, :tabs, :index, :offset, :filename, :children)
92
+ def comment?
93
+ text[0] == COMMENT_CHAR && (text[1] == SASS_COMMENT_CHAR || text[1] == CSS_COMMENT_CHAR)
94
+ end
95
+ end
96
+
97
+ # The character that begins a CSS property.
98
+ PROPERTY_CHAR = ?:
99
+
100
+ # The character that designates the beginning of a comment,
101
+ # either Sass or CSS.
102
+ COMMENT_CHAR = ?/
103
+
104
+ # The character that follows the general COMMENT_CHAR and designates a Sass comment,
105
+ # which is not output as a CSS comment.
106
+ SASS_COMMENT_CHAR = ?/
107
+
108
+ # The character that follows the general COMMENT_CHAR and designates a CSS comment,
109
+ # which is embedded in the CSS document.
110
+ CSS_COMMENT_CHAR = ?*
111
+
112
+ # The character used to denote a compiler directive.
113
+ DIRECTIVE_CHAR = ?@
114
+
115
+ # Designates a non-parsed rule.
116
+ ESCAPE_CHAR = ?\\
117
+
118
+ # Designates block as mixin definition rather than CSS rules to output
119
+ MIXIN_DEFINITION_CHAR = ?=
120
+
121
+ # Includes named mixin declared using MIXIN_DEFINITION_CHAR
122
+ MIXIN_INCLUDE_CHAR = ?+
123
+
124
+ # The regex that matches and extracts data from
125
+ # properties of the form `:name prop`.
126
+ PROPERTY_OLD = /^:([^\s=:"]+)\s*(?:\s+|$)(.*)/
127
+
128
+ # The default options for Sass::Engine.
129
+ # @api public
130
+ DEFAULT_OPTIONS = {
131
+ :style => :nested,
132
+ :load_paths => ['.'],
133
+ :cache => true,
134
+ :cache_location => './.sass-cache',
135
+ :syntax => :sass,
136
+ :filesystem_importer => Sass::Importers::Filesystem
137
+ }.freeze
138
+
139
+ # Converts a Sass options hash into a standard form, filling in
140
+ # default values and resolving aliases.
141
+ #
142
+ # @param options [{Symbol => Object}] The options hash;
143
+ # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
144
+ # @return [{Symbol => Object}] The normalized options hash.
145
+ # @private
146
+ def self.normalize_options(options)
147
+ options = DEFAULT_OPTIONS.merge(options.reject {|k, v| v.nil?})
148
+
149
+ # If the `:filename` option is passed in without an importer,
150
+ # assume it's using the default filesystem importer.
151
+ options[:importer] ||= options[:filesystem_importer].new(".") if options[:filename]
152
+
153
+ # Tracks the original filename of the top-level Sass file
154
+ options[:original_filename] ||= options[:filename]
155
+
156
+ options[:cache_store] ||= Sass::CacheStores::Chain.new(
157
+ Sass::CacheStores::Memory.new, Sass::CacheStores::Filesystem.new(options[:cache_location]))
158
+ # Support both, because the docs said one and the other actually worked
159
+ # for quite a long time.
160
+ options[:line_comments] ||= options[:line_numbers]
161
+
162
+ options[:load_paths] = options[:load_paths].map do |p|
163
+ next p unless p.is_a?(String) || (defined?(Pathname) && p.is_a?(Pathname))
164
+ options[:filesystem_importer].new(p.to_s)
165
+ end
166
+
167
+ # Backwards compatibility
168
+ options[:property_syntax] ||= options[:attribute_syntax]
169
+ case options[:property_syntax]
170
+ when :alternate; options[:property_syntax] = :new
171
+ when :normal; options[:property_syntax] = :old
172
+ end
173
+
174
+ options
175
+ end
176
+
177
+ # Returns the {Sass::Engine} for the given file.
178
+ # This is preferable to Sass::Engine.new when reading from a file
179
+ # because it properly sets up the Engine's metadata,
180
+ # enables parse-tree caching,
181
+ # and infers the syntax from the filename.
182
+ #
183
+ # @param filename [String] The path to the Sass or SCSS file
184
+ # @param options [{Symbol => Object}] The options hash;
185
+ # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
186
+ # @return [Sass::Engine] The Engine for the given Sass or SCSS file.
187
+ # @raise [Sass::SyntaxError] if there's an error in the document.
188
+ def self.for_file(filename, options)
189
+ had_syntax = options[:syntax]
190
+
191
+ if had_syntax
192
+ # Use what was explicitly specificed
193
+ elsif filename =~ /\.scss$/
194
+ options.merge!(:syntax => :scss)
195
+ elsif filename =~ /\.sass$/
196
+ options.merge!(:syntax => :sass)
197
+ end
198
+
199
+ Sass::Engine.new(File.read(filename), options.merge(:filename => filename))
200
+ end
201
+
202
+ # The options for the Sass engine.
203
+ # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
204
+ #
205
+ # @return [{Symbol => Object}]
206
+ attr_reader :options
207
+
208
+ # Creates a new Engine. Note that Engine should only be used directly
209
+ # when compiling in-memory Sass code.
210
+ # If you're compiling a single Sass file from the filesystem,
211
+ # use \{Sass::Engine.for\_file}.
212
+ # If you're compiling multiple files from the filesystem,
213
+ # use {Sass::Plugin.
214
+ #
215
+ # @param template [String] The Sass template.
216
+ # This template can be encoded using any encoding
217
+ # that can be converted to Unicode.
218
+ # If the template contains an `@charset` declaration,
219
+ # that overrides the Ruby encoding
220
+ # (see {file:SASS_REFERENCE.md#encodings the encoding documentation})
221
+ # @param options [{Symbol => Object}] An options hash.
222
+ # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
223
+ # @see {Sass::Engine.for_file}
224
+ # @see {Sass::Plugin}
225
+ def initialize(template, options={})
226
+ @options = self.class.normalize_options(options)
227
+ @template = template
228
+ end
229
+
230
+ # Render the template to CSS.
231
+ #
232
+ # @return [String] The CSS
233
+ # @raise [Sass::SyntaxError] if there's an error in the document
234
+ # @raise [Encoding::UndefinedConversionError] if the source encoding
235
+ # cannot be converted to UTF-8
236
+ # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
237
+ def render
238
+ return _render unless @options[:quiet]
239
+ Sass::Util.silence_sass_warnings {_render}
240
+ end
241
+ alias_method :to_css, :render
242
+
243
+ # Parses the document into its parse tree. Memoized.
244
+ #
245
+ # @return [Sass::Tree::Node] The root of the parse tree.
246
+ # @raise [Sass::SyntaxError] if there's an error in the document
247
+ def to_tree
248
+ @tree ||= @options[:quiet] ?
249
+ Sass::Util.silence_sass_warnings {_to_tree} :
250
+ _to_tree
251
+ end
252
+
253
+ # Returns the original encoding of the document,
254
+ # or `nil` under Ruby 1.8.
255
+ #
256
+ # @return [Encoding, nil]
257
+ # @raise [Encoding::UndefinedConversionError] if the source encoding
258
+ # cannot be converted to UTF-8
259
+ # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
260
+ def source_encoding
261
+ check_encoding!
262
+ @original_encoding
263
+ end
264
+
265
+ # Gets a set of all the documents
266
+ # that are (transitive) dependencies of this document,
267
+ # not including the document itself.
268
+ #
269
+ # @return [[Sass::Engine]] The dependency documents.
270
+ def dependencies
271
+ _dependencies(Set.new, engines = Set.new)
272
+ engines - [self]
273
+ end
274
+
275
+ # Helper for \{#dependencies}.
276
+ #
277
+ # @private
278
+ def _dependencies(seen, engines)
279
+ return if seen.include?(key = [@options[:filename], @options[:importer]])
280
+ seen << key
281
+ engines << self
282
+ to_tree.grep(Tree::ImportNode) do |n|
283
+ next if n.css_import?
284
+ n.imported_file._dependencies(seen, engines)
285
+ end
286
+ end
287
+
288
+ private
289
+
290
+ def _render
291
+ rendered = _to_tree.render
292
+ return rendered if ruby1_8?
293
+ begin
294
+ # Try to convert the result to the original encoding,
295
+ # but if that doesn't work fall back on UTF-8
296
+ rendered = rendered.encode(source_encoding)
297
+ rescue EncodingError
298
+ end
299
+ rendered.gsub(Regexp.new('\A@charset "(.*?)"'.encode(source_encoding)),
300
+ "@charset \"#{source_encoding.name}\"".encode(source_encoding))
301
+ end
302
+
303
+ def _to_tree
304
+ if (@options[:cache] || @options[:read_cache]) &&
305
+ @options[:filename] && @options[:importer]
306
+ key = sassc_key
307
+ sha = Digest::SHA1.hexdigest(@template)
308
+
309
+ if root = @options[:cache_store].retrieve(key, sha)
310
+ @options = root.options.merge(@options)
311
+ root.options = @options
312
+ return root
313
+ end
314
+ end
315
+
316
+ check_encoding!
317
+
318
+ if @options[:syntax] == :scss
319
+ root = Sass::SCSS::Parser.new(@template).parse
320
+ else
321
+ root = Tree::RootNode.new(@template)
322
+ append_children(root, tree(tabulate(@template)).first, true)
323
+ end
324
+
325
+ root.options = @options
326
+ if @options[:cache] && key && sha
327
+ begin
328
+ old_options = root.options
329
+ root.options = {:importer => root.options[:importer]}
330
+ @options[:cache_store].store(key, sha, root)
331
+ ensure
332
+ root.options = old_options
333
+ end
334
+ end
335
+ root
336
+ rescue SyntaxError => e
337
+ e.modify_backtrace(:filename => @options[:filename], :line => @line)
338
+ e.sass_template = @template
339
+ raise e
340
+ end
341
+
342
+ def sassc_key
343
+ @options[:cache_store].key(*@options[:importer].key(@options[:filename], @options))
344
+ end
345
+
346
+ def check_encoding!
347
+ return if @checked_encoding
348
+ @checked_encoding = true
349
+ @template, @original_encoding = check_sass_encoding(@template) do |msg, line|
350
+ raise Sass::SyntaxError.new(msg, :line => line)
351
+ end
352
+ end
353
+
354
+ def tabulate(string)
355
+ tab_str = nil
356
+ comment_tab_str = nil
357
+ first = true
358
+ lines = []
359
+ string.gsub(/\r|\n|\r\n|\r\n/, "\n").scan(/^[^\n]*?$/).each_with_index do |line, index|
360
+ index += (@options[:line] || 1)
361
+ if line.strip.empty?
362
+ lines.last.text << "\n" if lines.last && lines.last.comment?
363
+ next
364
+ end
365
+
366
+ line_tab_str = line[/^\s*/]
367
+ unless line_tab_str.empty?
368
+ if tab_str.nil?
369
+ comment_tab_str ||= line_tab_str
370
+ next if try_comment(line, lines.last, "", comment_tab_str, index)
371
+ comment_tab_str = nil
372
+ end
373
+
374
+ tab_str ||= line_tab_str
375
+
376
+ raise SyntaxError.new("Indenting at the beginning of the document is illegal.",
377
+ :line => index) if first
378
+
379
+ raise SyntaxError.new("Indentation can't use both tabs and spaces.",
380
+ :line => index) if tab_str.include?(?\s) && tab_str.include?(?\t)
381
+ end
382
+ first &&= !tab_str.nil?
383
+ if tab_str.nil?
384
+ lines << Line.new(line.strip, 0, index, 0, @options[:filename], [])
385
+ next
386
+ end
387
+
388
+ comment_tab_str ||= line_tab_str
389
+ if try_comment(line, lines.last, tab_str * lines.last.tabs, comment_tab_str, index)
390
+ next
391
+ else
392
+ comment_tab_str = nil
393
+ end
394
+
395
+ line_tabs = line_tab_str.scan(tab_str).size
396
+ if tab_str * line_tabs != line_tab_str
397
+ message = <<END.strip.gsub("\n", ' ')
398
+ Inconsistent indentation: #{Sass::Shared.human_indentation line_tab_str, true} used for indentation,
399
+ but the rest of the document was indented using #{Sass::Shared.human_indentation tab_str}.
400
+ END
401
+ raise SyntaxError.new(message, :line => index)
402
+ end
403
+
404
+ lines << Line.new(line.strip, line_tabs, index, tab_str.size, @options[:filename], [])
405
+ end
406
+ lines
407
+ end
408
+
409
+ def try_comment(line, last, tab_str, comment_tab_str, index)
410
+ return unless last && last.comment?
411
+ # Nested comment stuff must be at least one whitespace char deeper
412
+ # than the normal indentation
413
+ return unless line =~ /^#{tab_str}\s/
414
+ unless line =~ /^(?:#{comment_tab_str})(.*)$/
415
+ raise SyntaxError.new(<<MSG.strip.gsub("\n", " "), :line => index)
416
+ Inconsistent indentation:
417
+ previous line was indented by #{Sass::Shared.human_indentation comment_tab_str},
418
+ but this line was indented by #{Sass::Shared.human_indentation line[/^\s*/]}.
419
+ MSG
420
+ end
421
+
422
+ last.text << "\n" << $1
423
+ true
424
+ end
425
+
426
+ def tree(arr, i = 0)
427
+ return [], i if arr[i].nil?
428
+
429
+ base = arr[i].tabs
430
+ nodes = []
431
+ while (line = arr[i]) && line.tabs >= base
432
+ if line.tabs > base
433
+ raise SyntaxError.new("The line was indented #{line.tabs - base} levels deeper than the previous line.",
434
+ :line => line.index) if line.tabs > base + 1
435
+
436
+ nodes.last.children, i = tree(arr, i)
437
+ else
438
+ nodes << line
439
+ i += 1
440
+ end
441
+ end
442
+ return nodes, i
443
+ end
444
+
445
+ def build_tree(parent, line, root = false)
446
+ @line = line.index
447
+ node_or_nodes = parse_line(parent, line, root)
448
+
449
+ Array(node_or_nodes).each do |node|
450
+ # Node is a symbol if it's non-outputting, like a variable assignment
451
+ next unless node.is_a? Tree::Node
452
+
453
+ node.line = line.index
454
+ node.filename = line.filename
455
+
456
+ append_children(node, line.children, false)
457
+ end
458
+
459
+ node_or_nodes
460
+ end
461
+
462
+ def append_children(parent, children, root)
463
+ continued_rule = nil
464
+ continued_comment = nil
465
+ children.each do |line|
466
+ child = build_tree(parent, line, root)
467
+
468
+ if child.is_a?(Tree::RuleNode)
469
+ if child.continued? && child.children.empty?
470
+ if continued_rule
471
+ continued_rule.add_rules child
472
+ else
473
+ continued_rule = child
474
+ end
475
+ next
476
+ elsif continued_rule
477
+ continued_rule.add_rules child
478
+ continued_rule.children = child.children
479
+ continued_rule, child = nil, continued_rule
480
+ end
481
+ elsif continued_rule
482
+ continued_rule = nil
483
+ end
484
+
485
+ if child.is_a?(Tree::CommentNode) && child.silent
486
+ if continued_comment &&
487
+ child.line == continued_comment.line +
488
+ continued_comment.value.count("\n") + 1
489
+ continued_comment.value << "\n" << child.value
490
+ next
491
+ end
492
+
493
+ continued_comment = child
494
+ end
495
+
496
+ check_for_no_children(child)
497
+ validate_and_append_child(parent, child, line, root)
498
+ end
499
+
500
+ parent
501
+ end
502
+
503
+ def validate_and_append_child(parent, child, line, root)
504
+ case child
505
+ when Array
506
+ child.each {|c| validate_and_append_child(parent, c, line, root)}
507
+ when Tree::Node
508
+ parent << child
509
+ end
510
+ end
511
+
512
+ def check_for_no_children(node)
513
+ return unless node.is_a?(Tree::RuleNode) && node.children.empty?
514
+ Sass::Util.sass_warn(<<WARNING.strip)
515
+ WARNING on line #{node.line}#{" of #{node.filename}" if node.filename}:
516
+ This selector doesn't have any properties and will not be rendered.
517
+ WARNING
518
+ end
519
+
520
+ def parse_line(parent, line, root)
521
+ case line.text[0]
522
+ when PROPERTY_CHAR
523
+ if line.text[1] == PROPERTY_CHAR ||
524
+ (@options[:property_syntax] == :new &&
525
+ line.text =~ PROPERTY_OLD && $2.empty?)
526
+ # Support CSS3-style pseudo-elements,
527
+ # which begin with ::,
528
+ # as well as pseudo-classes
529
+ # if we're using the new property syntax
530
+ Tree::RuleNode.new(parse_interp(line.text))
531
+ else
532
+ name, value = line.text.scan(PROPERTY_OLD)[0]
533
+ raise SyntaxError.new("Invalid property: \"#{line.text}\".",
534
+ :line => @line) if name.nil? || value.nil?
535
+ parse_property(name, parse_interp(name), value, :old, line)
536
+ end
537
+ when ?$
538
+ parse_variable(line)
539
+ when COMMENT_CHAR
540
+ parse_comment(line.text)
541
+ when DIRECTIVE_CHAR
542
+ parse_directive(parent, line, root)
543
+ when ESCAPE_CHAR
544
+ Tree::RuleNode.new(parse_interp(line.text[1..-1]))
545
+ when MIXIN_DEFINITION_CHAR
546
+ parse_mixin_definition(line)
547
+ when MIXIN_INCLUDE_CHAR
548
+ if line.text[1].nil? || line.text[1] == ?\s
549
+ Tree::RuleNode.new(parse_interp(line.text))
550
+ else
551
+ parse_mixin_include(line, root)
552
+ end
553
+ else
554
+ parse_property_or_rule(line)
555
+ end
556
+ end
557
+
558
+ def parse_property_or_rule(line)
559
+ scanner = StringScanner.new(line.text)
560
+ hack_char = scanner.scan(/[:\*\.]|\#(?!\{)/)
561
+ parser = Sass::SCSS::SassParser.new(scanner, @line)
562
+
563
+ unless res = parser.parse_interp_ident
564
+ return Tree::RuleNode.new(parse_interp(line.text))
565
+ end
566
+ res.unshift(hack_char) if hack_char
567
+ if comment = scanner.scan(Sass::SCSS::RX::COMMENT)
568
+ res << comment
569
+ end
570
+
571
+ name = line.text[0...scanner.pos]
572
+ if scanner.scan(/\s*:(?:\s|$)/)
573
+ parse_property(name, res, scanner.rest, :new, line)
574
+ else
575
+ res.pop if comment
576
+ Tree::RuleNode.new(res + parse_interp(scanner.rest))
577
+ end
578
+ end
579
+
580
+ def parse_property(name, parsed_name, value, prop, line)
581
+ if value.strip.empty?
582
+ expr = Sass::Script::String.new("")
583
+ else
584
+ important = false
585
+ if value =~ Sass::SCSS::RX::IMPORTANT
586
+ important = true
587
+ value = value.gsub(Sass::SCSS::RX::IMPORTANT,"")
588
+ end
589
+ expr = parse_script(value, :offset => line.offset + line.text.index(value))
590
+
591
+ end
592
+ Tree::PropNode.new(parse_interp(name), expr, important, prop)
593
+ end
594
+
595
+ def parse_variable(line)
596
+ name, value, default = line.text.scan(Script::MATCH)[0]
597
+ raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath variable declarations.",
598
+ :line => @line + 1) unless line.children.empty?
599
+ raise SyntaxError.new("Invalid variable: \"#{line.text}\".",
600
+ :line => @line) unless name && value
601
+
602
+ expr = parse_script(value, :offset => line.offset + line.text.index(value))
603
+
604
+ Tree::VariableNode.new(name, expr, default)
605
+ end
606
+
607
+ def parse_comment(line)
608
+ if line[1] == CSS_COMMENT_CHAR || line[1] == SASS_COMMENT_CHAR
609
+ silent = line[1] == SASS_COMMENT_CHAR
610
+ Tree::CommentNode.new(
611
+ format_comment_text(line[2..-1], silent),
612
+ silent)
613
+ else
614
+ Tree::RuleNode.new(parse_interp(line))
615
+ end
616
+ end
617
+
618
+ def parse_directive(parent, line, root)
619
+ directive, whitespace, value = line.text[1..-1].split(/(\s+)/, 2)
620
+ offset = directive.size + whitespace.size + 1 if whitespace
621
+
622
+ # If value begins with url( or ",
623
+ # it's a CSS @import rule and we don't want to touch it.
624
+ if directive == "import"
625
+ parse_import(line, value)
626
+ elsif directive == "mixin"
627
+ parse_mixin_definition(line)
628
+ elsif directive == "include"
629
+ parse_mixin_include(line, root)
630
+ elsif directive == "function"
631
+ parse_function(line, root)
632
+ elsif directive == "for"
633
+ parse_for(line, root, value)
634
+ elsif directive == "each"
635
+ parse_each(line, root, value)
636
+ elsif directive == "else"
637
+ parse_else(parent, line, value)
638
+ elsif directive == "while"
639
+ raise SyntaxError.new("Invalid while directive '@while': expected expression.") unless value
640
+ Tree::WhileNode.new(parse_script(value, :offset => offset))
641
+ elsif directive == "if"
642
+ raise SyntaxError.new("Invalid if directive '@if': expected expression.") unless value
643
+ Tree::IfNode.new(parse_script(value, :offset => offset))
644
+ elsif directive == "debug"
645
+ raise SyntaxError.new("Invalid debug directive '@debug': expected expression.") unless value
646
+ raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath debug directives.",
647
+ :line => @line + 1) unless line.children.empty?
648
+ offset = line.offset + line.text.index(value).to_i
649
+ Tree::DebugNode.new(parse_script(value, :offset => offset))
650
+ elsif directive == "extend"
651
+ raise SyntaxError.new("Invalid extend directive '@extend': expected expression.") unless value
652
+ raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath extend directives.",
653
+ :line => @line + 1) unless line.children.empty?
654
+ offset = line.offset + line.text.index(value).to_i
655
+ Tree::ExtendNode.new(parse_interp(value, offset))
656
+ elsif directive == "warn"
657
+ raise SyntaxError.new("Invalid warn directive '@warn': expected expression.") unless value
658
+ raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath warn directives.",
659
+ :line => @line + 1) unless line.children.empty?
660
+ offset = line.offset + line.text.index(value).to_i
661
+ Tree::WarnNode.new(parse_script(value, :offset => offset))
662
+ elsif directive == "return"
663
+ raise SyntaxError.new("Invalid @return: expected expression.") unless value
664
+ raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath return directives.",
665
+ :line => @line + 1) unless line.children.empty?
666
+ offset = line.offset + line.text.index(value).to_i
667
+ Tree::ReturnNode.new(parse_script(value, :offset => offset))
668
+ elsif directive == "charset"
669
+ name = value && value[/\A(["'])(.*)\1\Z/, 2] #"
670
+ raise SyntaxError.new("Invalid charset directive '@charset': expected string.") unless name
671
+ raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath charset directives.",
672
+ :line => @line + 1) unless line.children.empty?
673
+ Tree::CharsetNode.new(name)
674
+ elsif directive == "media"
675
+ Tree::MediaNode.new(value)
676
+ else
677
+ Tree::DirectiveNode.new(line.text)
678
+ end
679
+ end
680
+
681
+ def parse_for(line, root, text)
682
+ var, from_expr, to_name, to_expr = text.scan(/^([^\s]+)\s+from\s+(.+)\s+(to|through)\s+(.+)$/).first
683
+
684
+ if var.nil? # scan failed, try to figure out why for error message
685
+ if text !~ /^[^\s]+/
686
+ expected = "variable name"
687
+ elsif text !~ /^[^\s]+\s+from\s+.+/
688
+ expected = "'from <expr>'"
689
+ else
690
+ expected = "'to <expr>' or 'through <expr>'"
691
+ end
692
+ raise SyntaxError.new("Invalid for directive '@for #{text}': expected #{expected}.")
693
+ end
694
+ raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
695
+
696
+ var = var[1..-1]
697
+ parsed_from = parse_script(from_expr, :offset => line.offset + line.text.index(from_expr))
698
+ parsed_to = parse_script(to_expr, :offset => line.offset + line.text.index(to_expr))
699
+ Tree::ForNode.new(var, parsed_from, parsed_to, to_name == 'to')
700
+ end
701
+
702
+ def parse_each(line, root, text)
703
+ var, list_expr = text.scan(/^([^\s]+)\s+in\s+(.+)$/).first
704
+
705
+ if var.nil? # scan failed, try to figure out why for error message
706
+ if text !~ /^[^\s]+/
707
+ expected = "variable name"
708
+ elsif text !~ /^[^\s]+\s+from\s+.+/
709
+ expected = "'in <expr>'"
710
+ end
711
+ raise SyntaxError.new("Invalid for directive '@each #{text}': expected #{expected}.")
712
+ end
713
+ raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
714
+
715
+ var = var[1..-1]
716
+ parsed_list = parse_script(list_expr, :offset => line.offset + line.text.index(list_expr))
717
+ Tree::EachNode.new(var, parsed_list)
718
+ end
719
+
720
+ def parse_else(parent, line, text)
721
+ previous = parent.children.last
722
+ raise SyntaxError.new("@else must come after @if.") unless previous.is_a?(Tree::IfNode)
723
+
724
+ if text
725
+ if text !~ /^if\s+(.+)/
726
+ raise SyntaxError.new("Invalid else directive '@else #{text}': expected 'if <expr>'.")
727
+ end
728
+ expr = parse_script($1, :offset => line.offset + line.text.index($1))
729
+ end
730
+
731
+ node = Tree::IfNode.new(expr)
732
+ append_children(node, line.children, false)
733
+ previous.add_else node
734
+ nil
735
+ end
736
+
737
+ def parse_import(line, value)
738
+ raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath import directives.",
739
+ :line => @line + 1) unless line.children.empty?
740
+
741
+ scanner = StringScanner.new(value)
742
+ values = []
743
+
744
+ loop do
745
+ unless node = parse_import_arg(scanner)
746
+ raise SyntaxError.new("Invalid @import: expected file to import, was #{scanner.rest.inspect}",
747
+ :line => @line)
748
+ end
749
+ values << node
750
+ break unless scanner.scan(/,\s*/)
751
+ end
752
+
753
+ return values
754
+ end
755
+
756
+ def parse_import_arg(scanner)
757
+ return if scanner.eos?
758
+ unless (str = scanner.scan(Sass::SCSS::RX::STRING)) ||
759
+ (uri = scanner.scan(Sass::SCSS::RX::URI))
760
+ return Tree::ImportNode.new(scanner.scan(/[^,]+/))
761
+ end
762
+
763
+ val = scanner[1] || scanner[2]
764
+ scanner.scan(/\s*/)
765
+ if media = scanner.scan(/[^,].*/)
766
+ Tree::DirectiveNode.new("@import #{str || uri} #{media}")
767
+ elsif uri
768
+ Tree::DirectiveNode.new("@import #{uri}")
769
+ elsif val =~ /^http:\/\//
770
+ Tree::DirectiveNode.new("@import url(#{val})")
771
+ else
772
+ Tree::ImportNode.new(val)
773
+ end
774
+ end
775
+
776
+ MIXIN_DEF_RE = /^(?:=|@mixin)\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
777
+ def parse_mixin_definition(line)
778
+ name, arg_string = line.text.scan(MIXIN_DEF_RE).first
779
+ raise SyntaxError.new("Invalid mixin \"#{line.text[1..-1]}\".") if name.nil?
780
+
781
+ offset = line.offset + line.text.size - arg_string.size
782
+ args = Script::Parser.new(arg_string.strip, @line, offset, @options).
783
+ parse_mixin_definition_arglist
784
+ Tree::MixinDefNode.new(name, args)
785
+ end
786
+
787
+ MIXIN_INCLUDE_RE = /^(?:\+|@include)\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
788
+ def parse_mixin_include(line, root)
789
+ name, arg_string = line.text.scan(MIXIN_INCLUDE_RE).first
790
+ raise SyntaxError.new("Invalid mixin include \"#{line.text}\".") if name.nil?
791
+
792
+ offset = line.offset + line.text.size - arg_string.size
793
+ args, keywords = Script::Parser.new(arg_string.strip, @line, offset, @options).
794
+ parse_mixin_include_arglist
795
+ raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath mixin directives.",
796
+ :line => @line + 1) unless line.children.empty?
797
+ Tree::MixinNode.new(name, args, keywords)
798
+ end
799
+
800
+ FUNCTION_RE = /^@function\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
801
+ def parse_function(line, root)
802
+ name, arg_string = line.text.scan(FUNCTION_RE).first
803
+ raise SyntaxError.new("Invalid function definition \"#{line.text}\".") if name.nil?
804
+
805
+ offset = line.offset + line.text.size - arg_string.size
806
+ args = Script::Parser.new(arg_string.strip, @line, offset, @options).
807
+ parse_function_definition_arglist
808
+ Tree::FunctionNode.new(name, args)
809
+ end
810
+
811
+ def parse_script(script, options = {})
812
+ line = options[:line] || @line
813
+ offset = options[:offset] || 0
814
+ Script.parse(script, line, offset, @options)
815
+ end
816
+
817
+ def format_comment_text(text, silent)
818
+ content = text.split("\n")
819
+
820
+ if content.first && content.first.strip.empty?
821
+ removed_first = true
822
+ content.shift
823
+ end
824
+
825
+ return silent ? "//" : "/* */" if content.empty?
826
+ content.last.gsub!(%r{ ?\*/ *$}, '')
827
+ content.map! {|l| l.gsub!(/^\*( ?)/, '\1') || (l.empty? ? "" : " ") + l}
828
+ content.first.gsub!(/^ /, '') unless removed_first
829
+ if silent
830
+ "//" + content.join("\n//")
831
+ else
832
+ # The #gsub fixes the case of a trailing */
833
+ "/*" + content.join("\n *").gsub(/ \*\Z/, '') + " */"
834
+ end
835
+ end
836
+
837
+ def parse_interp(text, offset = 0)
838
+ self.class.parse_interp(text, @line, offset, :filename => @filename)
839
+ end
840
+
841
+ # It's important that this have strings (at least)
842
+ # at the beginning, the end, and between each Script::Node.
843
+ #
844
+ # @private
845
+ def self.parse_interp(text, line, offset, options)
846
+ res = []
847
+ rest = Sass::Shared.handle_interpolation text do |scan|
848
+ escapes = scan[2].size
849
+ res << scan.matched[0...-2 - escapes]
850
+ if escapes % 2 == 1
851
+ res << "\\" * (escapes - 1) << '#{'
852
+ else
853
+ res << "\\" * [0, escapes - 1].max
854
+ res << Script::Parser.new(
855
+ scan, line, offset + scan.pos - scan.matched_size, options).
856
+ parse_interpolated
857
+ end
858
+ end
859
+ res << rest
860
+ end
861
+ end
862
+ end