sass 3.1.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
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)