sass 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING +1 -1
  3. data/MIT-LICENSE +2 -2
  4. data/README.md +29 -17
  5. data/Rakefile +43 -9
  6. data/VERSION +1 -1
  7. data/VERSION_DATE +1 -0
  8. data/VERSION_NAME +1 -1
  9. data/bin/sass +6 -1
  10. data/bin/sass-convert +6 -1
  11. data/bin/scss +6 -1
  12. data/ext/mkrf_conf.rb +27 -0
  13. data/lib/sass/cache_stores/base.rb +7 -3
  14. data/lib/sass/cache_stores/chain.rb +3 -2
  15. data/lib/sass/cache_stores/filesystem.rb +5 -7
  16. data/lib/sass/cache_stores/memory.rb +1 -1
  17. data/lib/sass/cache_stores/null.rb +2 -2
  18. data/lib/sass/callbacks.rb +2 -1
  19. data/lib/sass/css.rb +168 -53
  20. data/lib/sass/engine.rb +502 -174
  21. data/lib/sass/environment.rb +151 -111
  22. data/lib/sass/error.rb +7 -7
  23. data/lib/sass/exec.rb +176 -60
  24. data/lib/sass/features.rb +40 -0
  25. data/lib/sass/importers/base.rb +46 -7
  26. data/lib/sass/importers/deprecated_path.rb +51 -0
  27. data/lib/sass/importers/filesystem.rb +113 -30
  28. data/lib/sass/importers.rb +1 -0
  29. data/lib/sass/logger/base.rb +30 -0
  30. data/lib/sass/logger/log_level.rb +45 -0
  31. data/lib/sass/logger.rb +12 -0
  32. data/lib/sass/media.rb +213 -0
  33. data/lib/sass/plugin/compiler.rb +194 -104
  34. data/lib/sass/plugin/configuration.rb +18 -25
  35. data/lib/sass/plugin/merb.rb +1 -1
  36. data/lib/sass/plugin/staleness_checker.rb +37 -11
  37. data/lib/sass/plugin.rb +10 -13
  38. data/lib/sass/railtie.rb +2 -1
  39. data/lib/sass/repl.rb +5 -6
  40. data/lib/sass/script/css_lexer.rb +8 -4
  41. data/lib/sass/script/css_parser.rb +5 -2
  42. data/lib/sass/script/functions.rb +1547 -618
  43. data/lib/sass/script/lexer.rb +122 -72
  44. data/lib/sass/script/parser.rb +304 -135
  45. data/lib/sass/script/tree/funcall.rb +306 -0
  46. data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +43 -13
  47. data/lib/sass/script/tree/list_literal.rb +77 -0
  48. data/lib/sass/script/tree/literal.rb +45 -0
  49. data/lib/sass/script/tree/map_literal.rb +64 -0
  50. data/lib/sass/script/{node.rb → tree/node.rb} +30 -12
  51. data/lib/sass/script/{operation.rb → tree/operation.rb} +33 -21
  52. data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +14 -4
  53. data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +21 -9
  54. data/lib/sass/script/tree/variable.rb +57 -0
  55. data/lib/sass/script/tree.rb +15 -0
  56. data/lib/sass/script/value/arg_list.rb +36 -0
  57. data/lib/sass/script/value/base.rb +238 -0
  58. data/lib/sass/script/value/bool.rb +40 -0
  59. data/lib/sass/script/{color.rb → value/color.rb} +256 -74
  60. data/lib/sass/script/value/deprecated_false.rb +55 -0
  61. data/lib/sass/script/value/helpers.rb +155 -0
  62. data/lib/sass/script/value/list.rb +128 -0
  63. data/lib/sass/script/value/map.rb +70 -0
  64. data/lib/sass/script/value/null.rb +49 -0
  65. data/lib/sass/script/{number.rb → value/number.rb} +115 -62
  66. data/lib/sass/script/{string.rb → value/string.rb} +9 -11
  67. data/lib/sass/script/value.rb +12 -0
  68. data/lib/sass/script.rb +35 -9
  69. data/lib/sass/scss/css_parser.rb +2 -12
  70. data/lib/sass/scss/parser.rb +657 -230
  71. data/lib/sass/scss/rx.rb +17 -12
  72. data/lib/sass/scss/static_parser.rb +37 -6
  73. data/lib/sass/scss.rb +0 -1
  74. data/lib/sass/selector/abstract_sequence.rb +35 -3
  75. data/lib/sass/selector/comma_sequence.rb +29 -14
  76. data/lib/sass/selector/sequence.rb +371 -74
  77. data/lib/sass/selector/simple.rb +28 -13
  78. data/lib/sass/selector/simple_sequence.rb +163 -36
  79. data/lib/sass/selector.rb +138 -36
  80. data/lib/sass/shared.rb +3 -5
  81. data/lib/sass/source/map.rb +196 -0
  82. data/lib/sass/source/position.rb +39 -0
  83. data/lib/sass/source/range.rb +41 -0
  84. data/lib/sass/stack.rb +126 -0
  85. data/lib/sass/supports.rb +228 -0
  86. data/lib/sass/tree/at_root_node.rb +82 -0
  87. data/lib/sass/tree/comment_node.rb +34 -29
  88. data/lib/sass/tree/content_node.rb +9 -0
  89. data/lib/sass/tree/css_import_node.rb +60 -0
  90. data/lib/sass/tree/debug_node.rb +3 -3
  91. data/lib/sass/tree/directive_node.rb +33 -3
  92. data/lib/sass/tree/each_node.rb +9 -9
  93. data/lib/sass/tree/extend_node.rb +20 -6
  94. data/lib/sass/tree/for_node.rb +6 -6
  95. data/lib/sass/tree/function_node.rb +12 -4
  96. data/lib/sass/tree/if_node.rb +2 -15
  97. data/lib/sass/tree/import_node.rb +11 -5
  98. data/lib/sass/tree/media_node.rb +27 -11
  99. data/lib/sass/tree/mixin_def_node.rb +15 -4
  100. data/lib/sass/tree/mixin_node.rb +27 -7
  101. data/lib/sass/tree/node.rb +69 -35
  102. data/lib/sass/tree/prop_node.rb +47 -31
  103. data/lib/sass/tree/return_node.rb +4 -3
  104. data/lib/sass/tree/root_node.rb +20 -4
  105. data/lib/sass/tree/rule_node.rb +37 -26
  106. data/lib/sass/tree/supports_node.rb +38 -0
  107. data/lib/sass/tree/trace_node.rb +33 -0
  108. data/lib/sass/tree/variable_node.rb +10 -4
  109. data/lib/sass/tree/visitors/base.rb +5 -8
  110. data/lib/sass/tree/visitors/check_nesting.rb +67 -52
  111. data/lib/sass/tree/visitors/convert.rb +134 -53
  112. data/lib/sass/tree/visitors/cssize.rb +245 -51
  113. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  114. data/lib/sass/tree/visitors/extend.rb +68 -0
  115. data/lib/sass/tree/visitors/perform.rb +331 -105
  116. data/lib/sass/tree/visitors/set_options.rb +125 -0
  117. data/lib/sass/tree/visitors/to_css.rb +259 -95
  118. data/lib/sass/tree/warn_node.rb +3 -3
  119. data/lib/sass/tree/while_node.rb +3 -3
  120. data/lib/sass/util/cross_platform_random.rb +19 -0
  121. data/lib/sass/util/multibyte_string_scanner.rb +157 -0
  122. data/lib/sass/util/normalized_map.rb +130 -0
  123. data/lib/sass/util/ordered_hash.rb +192 -0
  124. data/lib/sass/util/subset_map.rb +11 -2
  125. data/lib/sass/util/test.rb +9 -0
  126. data/lib/sass/util.rb +565 -39
  127. data/lib/sass/version.rb +27 -15
  128. data/lib/sass.rb +39 -4
  129. data/test/sass/cache_test.rb +15 -0
  130. data/test/sass/compiler_test.rb +223 -0
  131. data/test/sass/conversion_test.rb +901 -107
  132. data/test/sass/css2sass_test.rb +94 -0
  133. data/test/sass/engine_test.rb +1059 -164
  134. data/test/sass/exec_test.rb +86 -0
  135. data/test/sass/extend_test.rb +933 -837
  136. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  137. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  138. data/test/sass/functions_test.rb +995 -136
  139. data/test/sass/importer_test.rb +338 -18
  140. data/test/sass/logger_test.rb +58 -0
  141. data/test/sass/more_results/more_import.css +2 -2
  142. data/test/sass/plugin_test.rb +114 -30
  143. data/test/sass/results/cached_import_option.css +3 -0
  144. data/test/sass/results/filename_fn.css +3 -0
  145. data/test/sass/results/import.css +2 -2
  146. data/test/sass/results/import_charset.css +1 -0
  147. data/test/sass/results/import_charset_1_8.css +1 -0
  148. data/test/sass/results/import_charset_ibm866.css +1 -0
  149. data/test/sass/results/import_content.css +1 -0
  150. data/test/sass/results/script.css +1 -1
  151. data/test/sass/results/scss_import.css +2 -2
  152. data/test/sass/results/units.css +2 -2
  153. data/test/sass/script_conversion_test.rb +43 -1
  154. data/test/sass/script_test.rb +380 -36
  155. data/test/sass/scss/css_test.rb +257 -75
  156. data/test/sass/scss/scss_test.rb +2322 -110
  157. data/test/sass/source_map_test.rb +887 -0
  158. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  159. data/test/sass/templates/_double_import_loop2.sass +1 -0
  160. data/test/sass/templates/_filename_fn_import.scss +11 -0
  161. data/test/sass/templates/_imported_content.sass +3 -0
  162. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  163. data/test/sass/templates/bork5.sass +3 -0
  164. data/test/sass/templates/cached_import_option.scss +3 -0
  165. data/test/sass/templates/double_import_loop1.sass +1 -0
  166. data/test/sass/templates/filename_fn.scss +18 -0
  167. data/test/sass/templates/import_charset.sass +2 -0
  168. data/test/sass/templates/import_charset_1_8.sass +2 -0
  169. data/test/sass/templates/import_charset_ibm866.sass +2 -0
  170. data/test/sass/templates/import_content.sass +4 -0
  171. data/test/sass/templates/same_name_different_ext.sass +2 -0
  172. data/test/sass/templates/same_name_different_ext.scss +1 -0
  173. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  174. data/test/sass/templates/single_import_loop.sass +1 -0
  175. data/test/sass/templates/subdir/import_up1.scss +1 -0
  176. data/test/sass/templates/subdir/import_up2.scss +1 -0
  177. data/test/sass/test_helper.rb +1 -1
  178. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  179. data/test/sass/util/normalized_map_test.rb +51 -0
  180. data/test/sass/util_test.rb +183 -0
  181. data/test/sass/value_helpers_test.rb +181 -0
  182. data/test/test_helper.rb +45 -5
  183. data/vendor/listen/CHANGELOG.md +228 -0
  184. data/vendor/listen/CONTRIBUTING.md +38 -0
  185. data/vendor/listen/Gemfile +30 -0
  186. data/vendor/listen/Guardfile +8 -0
  187. data/vendor/{fssm → listen}/LICENSE +1 -1
  188. data/vendor/listen/README.md +315 -0
  189. data/vendor/listen/Rakefile +47 -0
  190. data/vendor/listen/Vagrantfile +96 -0
  191. data/vendor/listen/lib/listen/adapter.rb +214 -0
  192. data/vendor/listen/lib/listen/adapters/bsd.rb +112 -0
  193. data/vendor/listen/lib/listen/adapters/darwin.rb +85 -0
  194. data/vendor/listen/lib/listen/adapters/linux.rb +113 -0
  195. data/vendor/listen/lib/listen/adapters/polling.rb +67 -0
  196. data/vendor/listen/lib/listen/adapters/windows.rb +87 -0
  197. data/vendor/listen/lib/listen/dependency_manager.rb +126 -0
  198. data/vendor/listen/lib/listen/directory_record.rb +371 -0
  199. data/vendor/listen/lib/listen/listener.rb +225 -0
  200. data/vendor/listen/lib/listen/multi_listener.rb +143 -0
  201. data/vendor/listen/lib/listen/turnstile.rb +28 -0
  202. data/vendor/listen/lib/listen/version.rb +3 -0
  203. data/vendor/listen/lib/listen.rb +40 -0
  204. data/vendor/listen/listen.gemspec +22 -0
  205. data/vendor/listen/spec/listen/adapter_spec.rb +183 -0
  206. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  207. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
  208. data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
  209. data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
  210. data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
  211. data/vendor/listen/spec/listen/dependency_manager_spec.rb +107 -0
  212. data/vendor/listen/spec/listen/directory_record_spec.rb +1225 -0
  213. data/vendor/listen/spec/listen/listener_spec.rb +169 -0
  214. data/vendor/listen/spec/listen/multi_listener_spec.rb +174 -0
  215. data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
  216. data/vendor/listen/spec/listen_spec.rb +73 -0
  217. data/vendor/listen/spec/spec_helper.rb +21 -0
  218. data/vendor/listen/spec/support/adapter_helper.rb +629 -0
  219. data/vendor/listen/spec/support/directory_record_helper.rb +55 -0
  220. data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
  221. data/vendor/listen/spec/support/listeners_helper.rb +156 -0
  222. data/vendor/listen/spec/support/platform_helper.rb +15 -0
  223. metadata +344 -271
  224. data/lib/sass/less.rb +0 -382
  225. data/lib/sass/script/bool.rb +0 -18
  226. data/lib/sass/script/funcall.rb +0 -162
  227. data/lib/sass/script/list.rb +0 -76
  228. data/lib/sass/script/literal.rb +0 -245
  229. data/lib/sass/script/variable.rb +0 -54
  230. data/lib/sass/scss/sass_parser.rb +0 -11
  231. data/test/sass/less_conversion_test.rb +0 -653
  232. data/vendor/fssm/README.markdown +0 -55
  233. data/vendor/fssm/Rakefile +0 -59
  234. data/vendor/fssm/VERSION.yml +0 -5
  235. data/vendor/fssm/example.rb +0 -9
  236. data/vendor/fssm/fssm.gemspec +0 -77
  237. data/vendor/fssm/lib/fssm/backends/fsevents.rb +0 -36
  238. data/vendor/fssm/lib/fssm/backends/inotify.rb +0 -26
  239. data/vendor/fssm/lib/fssm/backends/polling.rb +0 -25
  240. data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +0 -131
  241. data/vendor/fssm/lib/fssm/monitor.rb +0 -26
  242. data/vendor/fssm/lib/fssm/path.rb +0 -91
  243. data/vendor/fssm/lib/fssm/pathname.rb +0 -502
  244. data/vendor/fssm/lib/fssm/state/directory.rb +0 -57
  245. data/vendor/fssm/lib/fssm/state/file.rb +0 -24
  246. data/vendor/fssm/lib/fssm/support.rb +0 -63
  247. data/vendor/fssm/lib/fssm/tree.rb +0 -176
  248. data/vendor/fssm/lib/fssm.rb +0 -33
  249. data/vendor/fssm/profile/prof-cache.rb +0 -40
  250. data/vendor/fssm/profile/prof-fssm-pathname.html +0 -1231
  251. data/vendor/fssm/profile/prof-pathname.rb +0 -68
  252. data/vendor/fssm/profile/prof-plain-pathname.html +0 -988
  253. data/vendor/fssm/profile/prof.html +0 -2379
  254. data/vendor/fssm/spec/path_spec.rb +0 -75
  255. data/vendor/fssm/spec/root/duck/quack.txt +0 -0
  256. data/vendor/fssm/spec/root/file.css +0 -0
  257. data/vendor/fssm/spec/root/file.rb +0 -0
  258. data/vendor/fssm/spec/root/file.yml +0 -0
  259. data/vendor/fssm/spec/root/moo/cow.txt +0 -0
  260. data/vendor/fssm/spec/spec_helper.rb +0 -14
@@ -2,13 +2,14 @@
2
2
  class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
3
3
  protected
4
4
 
5
+ def initialize
6
+ @parents = []
7
+ end
8
+
5
9
  def visit(node)
6
- if error = (@parent && (
7
- try_send("invalid_#{node_name @parent}_child?", @parent, node) ||
8
- try_send("invalid_#{node_name node}_parent?", @parent, node))) ||
9
- (@real_parent && (
10
- try_send("invalid_#{node_name @real_parent}_real_child?", @real_parent, node) ||
11
- try_send("invalid_#{node_name node}_real_parent?", @real_parent, node)))
10
+ if (error = @parent && (
11
+ try_send(@parent.class.invalid_child_method_name, @parent, node) ||
12
+ try_send(node.class.invalid_parent_method_name, @parent, node)))
12
13
  raise Sass::SyntaxError.new(error)
13
14
  end
14
15
  super
@@ -17,16 +18,18 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
17
18
  raise e
18
19
  end
19
20
 
20
- PARENT_CLASSES = [ Sass::Tree::EachNode, Sass::Tree::ForNode, Sass::Tree::IfNode,
21
- Sass::Tree::ImportNode, Sass::Tree::MixinNode, Sass::Tree::WhileNode]
21
+ CONTROL_NODES = [Sass::Tree::EachNode, Sass::Tree::ForNode, Sass::Tree::IfNode,
22
+ Sass::Tree::WhileNode, Sass::Tree::TraceNode]
23
+ SCRIPT_NODES = [Sass::Tree::ImportNode] + CONTROL_NODES
22
24
  def visit_children(parent)
23
25
  old_parent = @parent
24
- @parent = parent unless is_any_of?(parent, PARENT_CLASSES)
25
- old_real_parent, @real_parent = @real_parent, parent
26
+ @parent = parent unless is_any_of?(parent, SCRIPT_NODES) ||
27
+ (parent.bubbles? && !old_parent.is_a?(Sass::Tree::RootNode))
28
+ @parents.push parent
26
29
  super
27
30
  ensure
28
31
  @parent = old_parent
29
- @real_parent = old_real_parent
32
+ @parents.pop
30
33
  end
31
34
 
32
35
  def visit_root(node)
@@ -44,72 +47,85 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
44
47
  raise e
45
48
  end
46
49
 
47
- def invalid_charset_parent?(parent, child)
48
- "@charset may only be used at the root of a document." unless parent.is_a?(Sass::Tree::RootNode)
50
+ def visit_mixindef(node)
51
+ @current_mixin_def, old_mixin_def = node, @current_mixin_def
52
+ yield
53
+ ensure
54
+ @current_mixin_def = old_mixin_def
49
55
  end
50
56
 
51
- INVALID_EXTEND_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::MixinDefNode]
52
- def invalid_extend_parent?(parent, child)
53
- unless is_any_of?(parent, INVALID_EXTEND_PARENTS)
54
- "Extend directives may only be used within rules."
57
+ def invalid_content_parent?(parent, child)
58
+ if @current_mixin_def
59
+ @current_mixin_def.has_content = true
60
+ nil
61
+ else
62
+ "@content may only be used within a mixin."
55
63
  end
56
64
  end
57
65
 
58
- def invalid_function_parent?(parent, child)
59
- "Functions may only be defined at the root of a document." unless parent.is_a?(Sass::Tree::RootNode)
66
+ def invalid_charset_parent?(parent, child)
67
+ "@charset may only be used at the root of a document." unless parent.is_a?(Sass::Tree::RootNode)
60
68
  end
61
69
 
62
- INVALID_FUNCTION_CHILDREN = [
63
- Sass::Tree::CommentNode, Sass::Tree::DebugNode, Sass::Tree::EachNode,
64
- Sass::Tree::ForNode, Sass::Tree::IfNode, Sass::Tree::ReturnNode,
65
- Sass::Tree::VariableNode, Sass::Tree::WarnNode, Sass::Tree::WhileNode
66
- ]
67
- def invalid_function_child?(parent, child)
68
- unless is_any_of?(child, INVALID_FUNCTION_CHILDREN)
69
- "Functions can only contain variable declarations and control directives."
70
+ VALID_EXTEND_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::MixinDefNode, Sass::Tree::MixinNode]
71
+ def invalid_extend_parent?(parent, child)
72
+ unless is_any_of?(parent, VALID_EXTEND_PARENTS)
73
+ return "Extend directives may only be used within rules."
70
74
  end
71
75
  end
72
76
 
73
- INVALID_IMPORT_PARENTS = [
74
- Sass::Tree::IfNode, Sass::Tree::ForNode, Sass::Tree::WhileNode,
75
- Sass::Tree::EachNode, Sass::Tree::MixinDefNode
76
- ]
77
+ INVALID_IMPORT_PARENTS = CONTROL_NODES +
78
+ [Sass::Tree::MixinDefNode, Sass::Tree::MixinNode]
77
79
  def invalid_import_parent?(parent, child)
78
- if is_any_of?(@real_parent, INVALID_IMPORT_PARENTS)
80
+ unless (@parents.map {|p| p.class} & INVALID_IMPORT_PARENTS).empty?
79
81
  return "Import directives may not be used within control directives or mixins."
80
82
  end
81
83
  return if parent.is_a?(Sass::Tree::RootNode)
82
84
  return "CSS import directives may only be used at the root of a document." if child.css_import?
83
- # If this is a nested @import, we need to make sure it doesn't have anything
84
- # that's legal at top-level but not in the current context (e.g. mixin defs).
85
- child.imported_file.to_tree.children.each {|c| visit(c)}
86
- nil
87
85
  rescue Sass::SyntaxError => e
88
86
  e.modify_backtrace(:filename => child.imported_file.options[:filename])
89
87
  e.add_backtrace(:filename => child.filename, :line => child.line)
90
88
  raise e
91
89
  end
92
90
 
93
- def invalid_import_real_parent?(parent, child)
94
-
91
+ def invalid_mixindef_parent?(parent, child)
92
+ unless (@parents.map {|p| p.class} & INVALID_IMPORT_PARENTS).empty?
93
+ return "Mixins may not be defined within control directives or other mixins."
94
+ end
95
95
  end
96
96
 
97
- def invalid_mixindef_parent?(parent, child)
98
- "Mixins may only be defined at the root of a document." unless parent.is_a?(Sass::Tree::RootNode)
97
+ def invalid_function_parent?(parent, child)
98
+ unless (@parents.map {|p| p.class} & INVALID_IMPORT_PARENTS).empty?
99
+ return "Functions may not be defined within control directives or other mixins."
100
+ end
99
101
  end
100
102
 
101
- INVALID_PROP_CHILDREN = [Sass::Tree::CommentNode, Sass::Tree::PropNode]
103
+ VALID_FUNCTION_CHILDREN = [
104
+ Sass::Tree::CommentNode, Sass::Tree::DebugNode, Sass::Tree::ReturnNode,
105
+ Sass::Tree::VariableNode, Sass::Tree::WarnNode
106
+ ] + CONTROL_NODES
107
+ def invalid_function_child?(parent, child)
108
+ unless is_any_of?(child, VALID_FUNCTION_CHILDREN)
109
+ "Functions can only contain variable declarations and control directives."
110
+ end
111
+ end
112
+
113
+ VALID_PROP_CHILDREN = CONTROL_NODES + [Sass::Tree::CommentNode,
114
+ Sass::Tree::PropNode,
115
+ Sass::Tree::MixinNode]
102
116
  def invalid_prop_child?(parent, child)
103
- unless is_any_of?(child, INVALID_PROP_CHILDREN)
117
+ unless is_any_of?(child, VALID_PROP_CHILDREN)
104
118
  "Illegal nesting: Only properties may be nested beneath properties."
105
119
  end
106
120
  end
107
121
 
108
- INVALID_PROP_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::PropNode,
109
- Sass::Tree::MixinDefNode, Sass::Tree::DirectiveNode]
122
+ VALID_PROP_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::PropNode,
123
+ Sass::Tree::MixinDefNode, Sass::Tree::DirectiveNode,
124
+ Sass::Tree::MixinNode]
110
125
  def invalid_prop_parent?(parent, child)
111
- unless is_any_of?(parent, INVALID_PROP_PARENTS)
112
- "Properties are only allowed within rules, directives, or other properties." + child.pseudo_class_selector_message
126
+ unless is_any_of?(parent, VALID_PROP_PARENTS)
127
+ "Properties are only allowed within rules, directives, mixin includes, or other properties." +
128
+ child.pseudo_class_selector_message
113
129
  end
114
130
  end
115
131
 
@@ -120,15 +136,14 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
120
136
  private
121
137
 
122
138
  def is_any_of?(val, classes)
123
- for c in classes
139
+ classes.each do |c|
124
140
  return true if val.is_a?(c)
125
141
  end
126
- return false
142
+ false
127
143
  end
128
144
 
129
- def try_send(method, *args, &block)
130
- return unless respond_to?(method)
131
- send(method, *args, &block)
145
+ def try_send(method, *args)
146
+ return unless respond_to?(method, true)
147
+ send(method, *args)
132
148
  end
133
149
  end
134
-
@@ -16,12 +16,18 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
16
16
  @options = options
17
17
  @format = format
18
18
  @tabs = 0
19
+ # 2 spaces by default
20
+ @tab_chars = @options[:indent] || " "
19
21
  end
20
22
 
21
23
  def visit_children(parent)
22
24
  @tabs += 1
23
25
  return @format == :sass ? "\n" : " {}\n" if parent.children.empty?
24
- (@format == :sass ? "\n" : " {\n") + super.join.rstrip + (@format == :sass ? "\n" : " }\n")
26
+ if @format == :sass
27
+ "\n" + super.join.rstrip + "\n"
28
+ else
29
+ " {\n" + super.join.rstrip + "\n#{ @tab_chars * (@tabs - 1)}}\n"
30
+ end
25
31
  ensure
26
32
  @tabs -= 1
27
33
  end
@@ -32,7 +38,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
32
38
  visit(child) +
33
39
  if nxt &&
34
40
  (child.is_a?(Sass::Tree::CommentNode) &&
35
- child.line + child.value.count("\n") + 1 == nxt.line) ||
41
+ child.line + child.lines + 1 == nxt.line) ||
36
42
  (child.is_a?(Sass::Tree::ImportNode) && nxt.is_a?(Sass::Tree::ImportNode) &&
37
43
  child.line + 1 == nxt.line) ||
38
44
  (child.is_a?(Sass::Tree::VariableNode) && nxt.is_a?(Sass::Tree::VariableNode) &&
@@ -49,8 +55,9 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
49
55
  end
50
56
 
51
57
  def visit_comment(node)
52
- content = if @format == :sass
53
- content = node.value.gsub(/\*\/$/, '').rstrip
58
+ value = interp_to_src(node.value)
59
+ if @format == :sass
60
+ content = value.gsub(/\*\/$/, '').rstrip
54
61
  if content =~ /\A[ \t]/
55
62
  # Re-indent SCSS comments like this:
56
63
  # /* foo
@@ -60,38 +67,27 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
60
67
  content.sub!(/\A([ \t]*)\/\*/, '/*\1')
61
68
  end
62
69
 
63
- content =
64
- unless content.include?("\n")
65
- content
70
+ if content.include?("\n")
71
+ content.gsub!(/\n \*/, "\n ")
72
+ spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
73
+ sep = node.type == :silent ? "\n//" : "\n *"
74
+ if spaces >= 2
75
+ content.gsub!(/\n /, sep)
66
76
  else
67
- content.gsub!(/\n( \*|\/\/)/, "\n ")
68
- spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
69
- sep = node.silent ? "\n//" : "\n *"
70
- if spaces >= 2
71
- content.gsub(/\n /, sep)
72
- else
73
- content.gsub(/\n#{' ' * spaces}/, sep)
74
- end
77
+ content.gsub!(/\n#{' ' * spaces}/, sep)
75
78
  end
79
+ end
76
80
 
77
- content.gsub!(/\A\/\*/, '//') if node.silent
81
+ content.gsub!(/\A\/\*/, '//') if node.type == :silent
78
82
  content.gsub!(/^/, tab_str)
79
- content.rstrip + "\n"
83
+ content = content.rstrip + "\n"
80
84
  else
81
- spaces = (' ' * [@tabs - node.value[/^ */].size, 0].max)
82
- content = if node.silent
83
- node.value.gsub(/^[\/ ]\*/, '//').gsub(/ *\*\/$/, '')
84
- else
85
- node.value
86
- end.gsub(/^/, spaces) + "\n"
87
- content
88
- end
89
- if node.loud
90
- if node.silent
91
- content.gsub!(%r{^\s*(//!?)}, '//!')
92
- else
93
- content.sub!(%r{^\s*(/\*)}, '/*!')
94
- end
85
+ spaces = (@tab_chars * [@tabs - value[/^ */].size, 0].max)
86
+ content = if node.type == :silent
87
+ value.gsub(/^[\/ ]\*/, '//').gsub(/ *\*\/$/, '')
88
+ else
89
+ value
90
+ end.gsub(/^/, spaces) + "\n"
95
91
  end
96
92
  content
97
93
  end
@@ -101,17 +97,20 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
101
97
  end
102
98
 
103
99
  def visit_directive(node)
104
- res = "#{tab_str}#{node.value}"
100
+ res = "#{tab_str}#{interp_to_src(node.value)}"
101
+ res.gsub!(/^@import \#\{(.*)\}([^}]*)$/, '@import \1\2')
105
102
  return res + "#{semi}\n" unless node.has_children
106
103
  res + yield + "\n"
107
104
  end
108
105
 
109
106
  def visit_each(node)
110
- "#{tab_str}@each $#{dasherize(node.var)} in #{node.list.to_sass(@options)}#{yield}"
107
+ vars = node.vars.map {|var| "$#{dasherize(var)}"}.join(", ")
108
+ "#{tab_str}@each #{vars} in #{node.list.to_sass(@options)}#{yield}"
111
109
  end
112
110
 
113
111
  def visit_extend(node)
114
- "#{tab_str}@extend #{selector_to_src(node.selector).lstrip}#{semi}\n"
112
+ "#{tab_str}@extend #{selector_to_src(node.selector).lstrip}#{semi}" +
113
+ "#{" !optional" if node.optional?}\n"
115
114
  end
116
115
 
117
116
  def visit_for(node)
@@ -123,16 +122,24 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
123
122
  args = node.args.map do |v, d|
124
123
  d ? "#{v.to_sass(@options)}: #{d.to_sass(@options)}" : v.to_sass(@options)
125
124
  end.join(", ")
125
+ if node.splat
126
+ args << ", " unless node.args.empty?
127
+ args << node.splat.to_sass(@options) << "..."
128
+ end
126
129
 
127
130
  "#{tab_str}@function #{dasherize(node.name)}(#{args})#{yield}"
128
131
  end
129
132
 
130
133
  def visit_if(node)
131
134
  name =
132
- if !@is_else; "if"
133
- elsif node.expr; "else if"
134
- else; "else"
135
+ if !@is_else
136
+ "if"
137
+ elsif node.expr
138
+ "else if"
139
+ else
140
+ "else"
135
141
  end
142
+ @is_else = false
136
143
  str = "#{tab_str}@#{name}"
137
144
  str << " #{node.expr.to_sass(@options)}" if node.expr
138
145
  str << yield
@@ -149,33 +156,73 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
149
156
  end
150
157
 
151
158
  def visit_media(node)
152
- "#{tab_str}@media #{node.query}#{yield}"
159
+ "#{tab_str}@media #{query_interp_to_src(node.query)}#{yield}"
160
+ end
161
+
162
+ def visit_supports(node)
163
+ "#{tab_str}@#{node.name} #{node.condition.to_src(@options)}#{yield}"
164
+ end
165
+
166
+ def visit_cssimport(node)
167
+ if node.uri.is_a?(Sass::Script::Tree::Node)
168
+ str = "#{tab_str}@import #{node.uri.to_sass(@options)}"
169
+ else
170
+ str = "#{tab_str}@import #{node.uri}"
171
+ end
172
+ str << " #{interp_to_src(node.query)}" unless node.query.empty?
173
+ "#{str}#{semi}\n"
153
174
  end
154
175
 
155
176
  def visit_mixindef(node)
156
177
  args =
157
- if node.args.empty?
178
+ if node.args.empty? && node.splat.nil?
158
179
  ""
159
180
  else
160
- '(' + node.args.map do |v, d|
181
+ str = '('
182
+ str << node.args.map do |v, d|
161
183
  if d
162
184
  "#{v.to_sass(@options)}: #{d.to_sass(@options)}"
163
185
  else
164
186
  v.to_sass(@options)
165
187
  end
166
- end.join(", ") + ')'
188
+ end.join(", ")
189
+
190
+ if node.splat
191
+ str << ", " unless node.args.empty?
192
+ str << node.splat.to_sass(@options) << '...'
193
+ end
194
+
195
+ str << ')'
167
196
  end
168
-
197
+
169
198
  "#{tab_str}#{@format == :sass ? '=' : '@mixin '}#{dasherize(node.name)}#{args}#{yield}"
170
199
  end
171
200
 
172
201
  def visit_mixin(node)
173
- unless node.args.empty? && node.keywords.empty?
174
- args = node.args.map {|a| a.to_sass(@options)}.join(", ")
175
- keywords = node.keywords.map {|k, v| "$#{dasherize(k)}: #{v.to_sass(@options)}"}.join(', ')
176
- arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
202
+ arg_to_sass = lambda do |arg|
203
+ sass = arg.to_sass(@options)
204
+ sass = "(#{sass})" if arg.is_a?(Sass::Script::Tree::ListLiteral) && arg.separator == :comma
205
+ sass
177
206
  end
178
- "#{tab_str}#{@format == :sass ? '+' : '@include '}#{dasherize(node.name)}#{arglist}#{semi}\n"
207
+
208
+ unless node.args.empty? && node.keywords.empty? && node.splat.nil?
209
+ args = node.args.map(&arg_to_sass)
210
+ keywords = Sass::Util.hash_to_a(node.keywords.as_stored).
211
+ map {|k, v| "$#{dasherize(k)}: #{arg_to_sass[v]}"}
212
+
213
+ if node.splat
214
+ splat = "#{arg_to_sass[node.splat]}..."
215
+ kwarg_splat = "#{arg_to_sass[node.kwarg_splat]}..." if node.kwarg_splat
216
+ end
217
+
218
+ arglist = "(#{[args, splat, keywords, kwarg_splat].flatten.compact.join(', ')})"
219
+ end
220
+ "#{tab_str}#{@format == :sass ? '+' : '@include '}" +
221
+ "#{dasherize(node.name)}#{arglist}#{node.has_children ? yield : semi}\n"
222
+ end
223
+
224
+ def visit_content(node)
225
+ "#{tab_str}@content#{semi}\n"
179
226
  end
180
227
 
181
228
  def visit_prop(node)
@@ -196,7 +243,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
196
243
  elsif @format == :scss
197
244
  name = selector_to_scss(node.rule)
198
245
  res = name + yield
199
- if node.children.last.is_a?(Sass::Tree::CommentNode) && node.children.last.silent
246
+ if node.children.last.is_a?(Sass::Tree::CommentNode) && node.children.last.type == :silent
200
247
  res.slice!(-3..-1)
201
248
  res << "\n" << tab_str << "}\n"
202
249
  end
@@ -205,7 +252,8 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
205
252
  end
206
253
 
207
254
  def visit_variable(node)
208
- "#{tab_str}$#{dasherize(node.name)}: #{node.expr.to_sass(@options)}#{' !default' if node.guarded}#{semi}\n"
255
+ "#{tab_str}$#{dasherize(node.name)}: #{node.expr.to_sass(@options)}" +
256
+ "#{' !global' if node.global}#{' !default' if node.guarded}#{semi}\n"
209
257
  end
210
258
 
211
259
  def visit_warn(node)
@@ -216,8 +264,42 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
216
264
  "#{tab_str}@while #{node.expr.to_sass(@options)}#{yield}"
217
265
  end
218
266
 
267
+ def visit_atroot(node)
268
+ if node.query
269
+ "#{tab_str}@at-root #{query_interp_to_src(node.query)}#{yield}"
270
+ elsif node.children.length == 1 && node.children.first.is_a?(Sass::Tree::RuleNode)
271
+ rule = node.children.first
272
+ "#{tab_str}@at-root #{selector_to_src(rule.rule)}#{visit_children(rule)}"
273
+ else
274
+ "#{tab_str}@at-root#{yield}"
275
+ end
276
+ end
277
+
219
278
  private
220
279
 
280
+ def interp_to_src(interp)
281
+ interp.map do |r|
282
+ next r if r.is_a?(String)
283
+ "\#{#{r.to_sass(@options)}}"
284
+ end.join
285
+ end
286
+
287
+ # Like interp_to_src, but removes the unnecessary `#{}` around the keys and
288
+ # values in query expressions.
289
+ def query_interp_to_src(interp)
290
+ Sass::Util.enum_with_index(interp).map do |r, i|
291
+ next r if r.is_a?(String)
292
+ before, after = interp[i - 1], interp[i + 1]
293
+ if before.is_a?(String) && after.is_a?(String) &&
294
+ ((before[-1] == ?( && after[0] == ?:) ||
295
+ (before =~ /:\s*/ && after[0] == ?)))
296
+ r.to_sass(@options)
297
+ else
298
+ "\#{#{r.to_sass(@options)}}"
299
+ end
300
+ end.join
301
+ end
302
+
221
303
  def selector_to_src(sel)
222
304
  @format == :sass ? selector_to_sass(sel) : selector_to_scss(sel)
223
305
  end
@@ -225,7 +307,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
225
307
  def selector_to_sass(sel)
226
308
  sel.map do |r|
227
309
  if r.is_a?(String)
228
- r.gsub(/(,[ \t]*)?\n\s*/) {$1 ? $1 + "\n" : " "}
310
+ r.gsub(/(,)?([ \t]*)\n\s*/) {$1 ? "#{$1}#{$2}\n" : " "}
229
311
  else
230
312
  "\#{#{r.to_sass(@options)}}"
231
313
  end
@@ -233,8 +315,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
233
315
  end
234
316
 
235
317
  def selector_to_scss(sel)
236
- sel.map {|r| r.is_a?(String) ? r : "\#{#{r.to_sass(@options)}}"}.
237
- join.gsub(/^[ \t]*/, tab_str)
318
+ interp_to_src(sel).gsub(/^[ \t]*/, tab_str).gsub(/[ \t]*$/, '')
238
319
  end
239
320
 
240
321
  def semi
@@ -242,7 +323,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
242
323
  end
243
324
 
244
325
  def tab_str
245
- ' ' * @tabs
326
+ @tab_chars * @tabs
246
327
  end
247
328
 
248
329
  def dasherize(s)