sass 3.2.7 → 3.3.0.rc.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 (184) hide show
  1. data/MIT-LICENSE +2 -2
  2. data/README.md +14 -2
  3. data/Rakefile +25 -1
  4. data/VERSION +1 -1
  5. data/VERSION_DATE +1 -1
  6. data/VERSION_NAME +1 -1
  7. data/lib/sass/cache_stores/base.rb +4 -2
  8. data/lib/sass/cache_stores/chain.rb +2 -1
  9. data/lib/sass/cache_stores/filesystem.rb +2 -6
  10. data/lib/sass/cache_stores/memory.rb +1 -1
  11. data/lib/sass/cache_stores/null.rb +2 -2
  12. data/lib/sass/callbacks.rb +1 -0
  13. data/lib/sass/css.rb +10 -10
  14. data/lib/sass/engine.rb +403 -150
  15. data/lib/sass/environment.rb +136 -57
  16. data/lib/sass/error.rb +7 -7
  17. data/lib/sass/exec.rb +123 -39
  18. data/lib/sass/features.rb +41 -0
  19. data/lib/sass/importers/base.rb +33 -2
  20. data/lib/sass/importers/deprecated_path.rb +45 -0
  21. data/lib/sass/importers/filesystem.rb +25 -14
  22. data/lib/sass/importers.rb +1 -0
  23. data/lib/sass/logger/base.rb +3 -3
  24. data/lib/sass/logger/log_level.rb +4 -6
  25. data/lib/sass/media.rb +19 -19
  26. data/lib/sass/plugin/compiler.rb +141 -101
  27. data/lib/sass/plugin/configuration.rb +18 -22
  28. data/lib/sass/plugin/merb.rb +1 -1
  29. data/lib/sass/plugin/staleness_checker.rb +24 -8
  30. data/lib/sass/plugin.rb +4 -2
  31. data/lib/sass/repl.rb +3 -3
  32. data/lib/sass/script/css_lexer.rb +9 -4
  33. data/lib/sass/script/css_parser.rb +6 -2
  34. data/lib/sass/script/functions.rb +1343 -590
  35. data/lib/sass/script/lexer.rb +84 -52
  36. data/lib/sass/script/parser.rb +217 -97
  37. data/lib/sass/script/tree/funcall.rb +290 -0
  38. data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +34 -13
  39. data/lib/sass/script/tree/list_literal.rb +80 -0
  40. data/lib/sass/script/tree/literal.rb +47 -0
  41. data/lib/sass/script/tree/map_literal.rb +64 -0
  42. data/lib/sass/script/{node.rb → tree/node.rb} +22 -12
  43. data/lib/sass/script/{operation.rb → tree/operation.rb} +17 -25
  44. data/lib/sass/script/tree/selector.rb +30 -0
  45. data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +5 -4
  46. data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +14 -9
  47. data/lib/sass/script/tree/variable.rb +57 -0
  48. data/lib/sass/script/tree.rb +16 -0
  49. data/lib/sass/script/{arg_list.rb → value/arg_list.rb} +4 -24
  50. data/lib/sass/script/value/base.rb +248 -0
  51. data/lib/sass/script/value/bool.rb +36 -0
  52. data/lib/sass/script/{color.rb → value/color.rb} +239 -195
  53. data/lib/sass/script/value/helpers.rb +155 -0
  54. data/lib/sass/script/value/list.rb +119 -0
  55. data/lib/sass/script/value/map.rb +70 -0
  56. data/lib/sass/script/value/null.rb +45 -0
  57. data/lib/sass/script/{number.rb → value/number.rb} +91 -65
  58. data/lib/sass/script/{string.rb → value/string.rb} +9 -11
  59. data/lib/sass/script/value.rb +11 -0
  60. data/lib/sass/script.rb +35 -8
  61. data/lib/sass/scss/css_parser.rb +2 -1
  62. data/lib/sass/scss/parser.rb +338 -170
  63. data/lib/sass/scss/rx.rb +5 -6
  64. data/lib/sass/scss/script_lexer.rb +1 -0
  65. data/lib/sass/scss/script_parser.rb +1 -0
  66. data/lib/sass/scss/static_parser.rb +23 -6
  67. data/lib/sass/selector/abstract_sequence.rb +2 -2
  68. data/lib/sass/selector/comma_sequence.rb +21 -16
  69. data/lib/sass/selector/sequence.rb +60 -34
  70. data/lib/sass/selector/simple.rb +11 -12
  71. data/lib/sass/selector/simple_sequence.rb +55 -33
  72. data/lib/sass/selector.rb +52 -48
  73. data/lib/sass/source/map.rb +211 -0
  74. data/lib/sass/source/position.rb +39 -0
  75. data/lib/sass/source/range.rb +41 -0
  76. data/lib/sass/stack.rb +120 -0
  77. data/lib/sass/supports.rb +12 -13
  78. data/lib/sass/tree/at_root_node.rb +82 -0
  79. data/lib/sass/tree/comment_node.rb +3 -3
  80. data/lib/sass/tree/css_import_node.rb +11 -11
  81. data/lib/sass/tree/debug_node.rb +2 -2
  82. data/lib/sass/tree/directive_node.rb +13 -2
  83. data/lib/sass/tree/each_node.rb +8 -8
  84. data/lib/sass/tree/extend_node.rb +13 -6
  85. data/lib/sass/tree/for_node.rb +4 -4
  86. data/lib/sass/tree/function_node.rb +5 -4
  87. data/lib/sass/tree/if_node.rb +1 -1
  88. data/lib/sass/tree/import_node.rb +4 -5
  89. data/lib/sass/tree/media_node.rb +4 -14
  90. data/lib/sass/tree/mixin_def_node.rb +4 -4
  91. data/lib/sass/tree/mixin_node.rb +21 -8
  92. data/lib/sass/tree/node.rb +29 -12
  93. data/lib/sass/tree/prop_node.rb +38 -18
  94. data/lib/sass/tree/return_node.rb +3 -2
  95. data/lib/sass/tree/root_node.rb +19 -3
  96. data/lib/sass/tree/rule_node.rb +25 -17
  97. data/lib/sass/tree/supports_node.rb +0 -13
  98. data/lib/sass/tree/trace_node.rb +2 -1
  99. data/lib/sass/tree/variable_node.rb +9 -3
  100. data/lib/sass/tree/visitors/base.rb +6 -6
  101. data/lib/sass/tree/visitors/check_nesting.rb +12 -9
  102. data/lib/sass/tree/visitors/convert.rb +63 -38
  103. data/lib/sass/tree/visitors/cssize.rb +63 -23
  104. data/lib/sass/tree/visitors/deep_copy.rb +6 -5
  105. data/lib/sass/tree/visitors/extend.rb +7 -7
  106. data/lib/sass/tree/visitors/perform.rb +256 -151
  107. data/lib/sass/tree/visitors/set_options.rb +6 -6
  108. data/lib/sass/tree/visitors/to_css.rb +231 -81
  109. data/lib/sass/tree/warn_node.rb +2 -2
  110. data/lib/sass/tree/while_node.rb +2 -2
  111. data/lib/sass/util/multibyte_string_scanner.rb +2 -0
  112. data/lib/sass/util/normalized_map.rb +65 -0
  113. data/lib/sass/util/ordered_hash.rb +188 -0
  114. data/lib/sass/util/subset_map.rb +3 -2
  115. data/lib/sass/util/test.rb +9 -0
  116. data/lib/sass/util.rb +220 -34
  117. data/lib/sass/version.rb +9 -9
  118. data/lib/sass.rb +14 -7
  119. data/test/sass/compiler_test.rb +213 -0
  120. data/test/sass/conversion_test.rb +235 -9
  121. data/test/sass/engine_test.rb +230 -60
  122. data/test/sass/exec_test.rb +86 -0
  123. data/test/sass/extend_test.rb +215 -147
  124. data/test/sass/functions_test.rb +584 -99
  125. data/test/sass/importer_test.rb +165 -17
  126. data/test/sass/plugin_test.rb +19 -13
  127. data/test/sass/script_conversion_test.rb +40 -0
  128. data/test/sass/script_test.rb +231 -21
  129. data/test/sass/scss/css_test.rb +14 -5
  130. data/test/sass/scss/scss_test.rb +1266 -66
  131. data/test/sass/source_map_test.rb +879 -0
  132. data/test/sass/templates/bork5.sass +3 -0
  133. data/test/sass/util/normalized_map_test.rb +30 -0
  134. data/test/sass/util_test.rb +90 -0
  135. data/test/sass/value_helpers_test.rb +181 -0
  136. data/test/test_helper.rb +7 -2
  137. metadata +316 -291
  138. data/lib/sass/script/bool.rb +0 -18
  139. data/lib/sass/script/funcall.rb +0 -231
  140. data/lib/sass/script/list.rb +0 -84
  141. data/lib/sass/script/literal.rb +0 -239
  142. data/lib/sass/script/null.rb +0 -34
  143. data/lib/sass/script/variable.rb +0 -58
  144. data/test/Gemfile +0 -3
  145. data/vendor/listen/CHANGELOG.md +0 -221
  146. data/vendor/listen/CONTRIBUTING.md +0 -38
  147. data/vendor/listen/Gemfile +0 -30
  148. data/vendor/listen/Guardfile +0 -8
  149. data/vendor/listen/LICENSE +0 -20
  150. data/vendor/listen/README.md +0 -315
  151. data/vendor/listen/Rakefile +0 -47
  152. data/vendor/listen/Vagrantfile +0 -96
  153. data/vendor/listen/lib/listen/adapter.rb +0 -214
  154. data/vendor/listen/lib/listen/adapters/bsd.rb +0 -112
  155. data/vendor/listen/lib/listen/adapters/darwin.rb +0 -85
  156. data/vendor/listen/lib/listen/adapters/linux.rb +0 -113
  157. data/vendor/listen/lib/listen/adapters/polling.rb +0 -67
  158. data/vendor/listen/lib/listen/adapters/windows.rb +0 -87
  159. data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
  160. data/vendor/listen/lib/listen/directory_record.rb +0 -371
  161. data/vendor/listen/lib/listen/listener.rb +0 -225
  162. data/vendor/listen/lib/listen/multi_listener.rb +0 -143
  163. data/vendor/listen/lib/listen/turnstile.rb +0 -28
  164. data/vendor/listen/lib/listen/version.rb +0 -3
  165. data/vendor/listen/lib/listen.rb +0 -40
  166. data/vendor/listen/listen.gemspec +0 -22
  167. data/vendor/listen/spec/listen/adapter_spec.rb +0 -183
  168. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +0 -36
  169. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +0 -37
  170. data/vendor/listen/spec/listen/adapters/linux_spec.rb +0 -47
  171. data/vendor/listen/spec/listen/adapters/polling_spec.rb +0 -68
  172. data/vendor/listen/spec/listen/adapters/windows_spec.rb +0 -30
  173. data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
  174. data/vendor/listen/spec/listen/directory_record_spec.rb +0 -1225
  175. data/vendor/listen/spec/listen/listener_spec.rb +0 -169
  176. data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
  177. data/vendor/listen/spec/listen/turnstile_spec.rb +0 -56
  178. data/vendor/listen/spec/listen_spec.rb +0 -73
  179. data/vendor/listen/spec/spec_helper.rb +0 -21
  180. data/vendor/listen/spec/support/adapter_helper.rb +0 -629
  181. data/vendor/listen/spec/support/directory_record_helper.rb +0 -55
  182. data/vendor/listen/spec/support/fixtures_helper.rb +0 -29
  183. data/vendor/listen/spec/support/listeners_helper.rb +0 -156
  184. data/vendor/listen/spec/support/platform_helper.rb +0 -15
@@ -1,11 +1,25 @@
1
1
  # A visitor for converting a Sass tree into CSS.
2
2
  class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
3
- protected
3
+ # The source mapping for the generated CSS file. This is only set if
4
+ # `build_source_mapping` is passed to the constructor and \{Sass::Engine#render} has been
5
+ # run.
6
+ attr_reader :source_mapping
4
7
 
5
- def initialize
8
+ # @param build_source_mapping [Boolean] Whether to build a
9
+ # \{Sass::Source::Map} while creating the CSS output. The mapping will
10
+ # be available from \{#source\_mapping} after the visitor has completed.
11
+ def initialize(build_source_mapping = false)
6
12
  @tabs = 0
13
+ @line = 1
14
+ @offset = 1
15
+ @result = ""
16
+ @source_mapping = Sass::Source::Map.new if build_source_mapping
7
17
  end
8
18
 
19
+ # Runs the visitor on `node`.
20
+ #
21
+ # @param node [Sass::Tree::Node] The root node of the tree to convert to CSS>
22
+ # @return [String] The CSS output.
9
23
  def visit(node)
10
24
  super
11
25
  rescue Sass::SyntaxError => e
@@ -13,6 +27,8 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
13
27
  raise e
14
28
  end
15
29
 
30
+ protected
31
+
16
32
  def with_tabs(tabs)
17
33
  old_tabs, @tabs = @tabs, tabs
18
34
  yield
@@ -20,97 +36,206 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
20
36
  @tabs = old_tabs
21
37
  end
22
38
 
39
+ # Associate all output produced in a block with a given node. Used for source
40
+ # mapping.
41
+ def for_node(node, attr_prefix = nil)
42
+ return yield unless @source_mapping
43
+ start_pos = Sass::Source::Position.new(@line, @offset)
44
+ yield
45
+
46
+ range_attr = attr_prefix ? :"#{attr_prefix}_source_range" : :source_range
47
+ return if node.invisible? || !node.send(range_attr)
48
+ source_range = node.send(range_attr)
49
+ target_end_pos = Sass::Source::Position.new(@line, @offset)
50
+ target_range = Sass::Source::Range.new(start_pos, target_end_pos, nil)
51
+ @source_mapping.add(source_range, target_range)
52
+ end
53
+
54
+ # Move the output cursor back `chars` characters.
55
+ def erase!(chars)
56
+ return if chars == 0
57
+ str = @result.slice!(-chars..-1)
58
+ newlines = str.count("\n")
59
+ if newlines > 0
60
+ @line -= newlines
61
+ @offset = @result[@result.rindex("\n") || 0..-1].size
62
+ else
63
+ @offset -= chars
64
+ end
65
+ end
66
+
67
+ # Avoid allocating lots of new strings for `#output`. This is important
68
+ # because `#output` is called all the time.
69
+ NEWLINE = "\n"
70
+
71
+ # Add `s` to the output string and update the line and offset information
72
+ # accordingly.
73
+ def output(s)
74
+ if @lstrip
75
+ s = s.gsub(/\A\s+/, "")
76
+ @lstrip = false
77
+ end
78
+
79
+ newlines = s.count(NEWLINE)
80
+ if newlines > 0
81
+ @line += newlines
82
+ @offset = s[s.rindex(NEWLINE)..-1].size
83
+ else
84
+ @offset += s.size
85
+ end
86
+
87
+ @result << s
88
+ end
89
+
90
+ # Strip all trailing whitespace from the output string.
91
+ def rstrip!
92
+ erase! @result.length - 1 - (@result.rindex(/[^\s]/) || -1)
93
+ end
94
+
95
+ # lstrip the first output in the given block.
96
+ def lstrip
97
+ old_lstrip = @lstrip
98
+ @lstrip = true
99
+ yield
100
+ ensure
101
+ @lstrip = @lstrip && old_lstrip
102
+ end
103
+
104
+ # Prepend `prefix` to the output string.
105
+ def prepend!(prefix)
106
+ @result.insert 0, prefix
107
+ return unless @source_mapping
108
+
109
+ line_delta = prefix.count("\n")
110
+ offset_delta = prefix.gsub(/.*\n/, '').size
111
+ @source_mapping.shift_output_offsets(offset_delta)
112
+ @source_mapping.shift_output_lines(line_delta)
113
+ end
114
+
23
115
  def visit_root(node)
24
- result = String.new
25
116
  node.children.each do |child|
26
117
  next if child.invisible?
27
- child_str = visit(child)
28
- result << child_str + (node.style == :compressed ? '' : "\n")
29
- end
30
- result.rstrip!
31
- return "" if result.empty?
32
- result << "\n"
33
- unless Sass::Util.ruby1_8? || result.ascii_only?
34
- if node.children.first.is_a?(Sass::Tree::CharsetNode)
35
- begin
36
- encoding = node.children.first.name
37
- # Default to big-endian encoding, because we have to decide somehow
38
- encoding << 'BE' if encoding =~ /\Autf-(16|32)\Z/i
39
- result = result.encode(Encoding.find(encoding))
40
- rescue EncodingError
118
+ visit(child)
119
+ unless node.style == :compressed
120
+ output "\n"
121
+ if child.is_a?(Sass::Tree::DirectiveNode) && child.has_children && !child.bubbles?
122
+ output "\n"
41
123
  end
42
124
  end
125
+ end
126
+ rstrip!
127
+ return "" if @result.empty?
43
128
 
44
- result = "@charset \"#{result.encoding.name}\";#{
45
- node.style == :compressed ? '' : "\n"
46
- }".encode(result.encoding) + result
129
+ output "\n"
130
+ return @result if Sass::Util.ruby1_8? || @result.ascii_only?
131
+
132
+ if node.children.first.is_a?(Sass::Tree::CharsetNode)
133
+ begin
134
+ encoding = node.children.first.name
135
+ # Default to big-endian encoding, because we have to decide somehow
136
+ encoding << 'BE' if encoding =~ /\Autf-(16|32)\Z/i
137
+ @result = @result.encode(Encoding.find(encoding))
138
+ rescue EncodingError
139
+ end
47
140
  end
48
- result
141
+
142
+ prepend! "@charset \"#{@result.encoding.name}\";#{
143
+ node.style == :compressed ? '' : "\n"
144
+ }".encode(@result.encoding)
145
+ @result
49
146
  rescue Sass::SyntaxError => e
50
147
  e.sass_template ||= node.template
51
148
  raise e
52
149
  end
53
150
 
54
151
  def visit_charset(node)
55
- "@charset \"#{node.name}\";"
56
- end
152
+ for_node(node) {output("@charset \"#{node.name}\";")}
153
+ end
57
154
 
58
155
  def visit_comment(node)
59
156
  return if node.invisible?
60
157
  spaces = (' ' * [@tabs - node.resolved_value[/^ */].size, 0].max)
61
158
 
62
159
  content = node.resolved_value.gsub(/^/, spaces)
63
- content.gsub!(%r{^(\s*)//(.*)$}) {|md| "#{$1}/*#{$2} */"} if node.type == :silent
64
- content.gsub!(/\n +(\* *(?!\/))?/, ' ') if (node.style == :compact || node.style == :compressed) && node.type != :loud
65
- content
160
+ if node.type == :silent
161
+ content.gsub!(%r{^(\s*)//(.*)$}) {|md| "#{$1}/*#{$2} */"}
162
+ end
163
+ if (node.style == :compact || node.style == :compressed) && node.type != :loud
164
+ content.gsub!(/\n +(\* *(?!\/))?/, ' ')
165
+ end
166
+ for_node(node) {output(content)}
66
167
  end
67
168
 
169
+ # @comment
170
+ # rubocop:disable MethodLength
68
171
  def visit_directive(node)
69
172
  was_in_directive = @in_directive
70
173
  tab_str = ' ' * @tabs
71
- return tab_str + node.resolved_value + ";" unless node.has_children
72
- return tab_str + node.resolved_value + " {}" if node.children.empty?
174
+ if !node.has_children || node.children.empty?
175
+ output(tab_str)
176
+ for_node(node) {output(node.resolved_value)}
177
+ output(!node.has_children ? ";" : " {}")
178
+ return
179
+ end
180
+
73
181
  @in_directive = @in_directive || !node.is_a?(Sass::Tree::MediaNode)
74
- result = if node.style == :compressed
75
- "#{node.resolved_value}{"
76
- else
77
- "#{tab_str}#{node.resolved_value} {" + (node.style == :compact ? ' ' : "\n")
78
- end
182
+ output(tab_str) if node.style != :compressed
183
+ for_node(node) {output(node.resolved_value)}
184
+ output(node.style == :compressed ? "{" : " {")
185
+ output(node.style == :compact ? ' ' : "\n") if node.style != :compressed
186
+
79
187
  was_prop = false
80
188
  first = true
81
189
  node.children.each do |child|
82
190
  next if child.invisible?
83
191
  if node.style == :compact
84
192
  if child.is_a?(Sass::Tree::PropNode)
85
- with_tabs(first || was_prop ? 0 : @tabs + 1) {result << visit(child) << ' '}
193
+ with_tabs(first || was_prop ? 0 : @tabs + 1) do
194
+ visit(child)
195
+ output(' ')
196
+ end
86
197
  else
87
- result[-1] = "\n" if was_prop
88
- rendered = with_tabs(@tabs + 1) {visit(child).dup}
89
- rendered = rendered.lstrip if first
90
- result << rendered.rstrip + "\n"
198
+ if was_prop
199
+ erase! 1
200
+ output "\n"
201
+ end
202
+
203
+ if first
204
+ lstrip {with_tabs(@tabs + 1) {visit(child)}}
205
+ else
206
+ with_tabs(@tabs + 1) {visit(child)}
207
+ end
208
+
209
+ rstrip!
210
+ output "\n"
91
211
  end
92
212
  was_prop = child.is_a?(Sass::Tree::PropNode)
93
213
  first = false
94
214
  elsif node.style == :compressed
95
- result << (was_prop ? ";" : "") << with_tabs(0) {visit(child)}
215
+ output(was_prop ? ";" : "")
216
+ with_tabs(0) {visit(child)}
96
217
  was_prop = child.is_a?(Sass::Tree::PropNode)
97
218
  else
98
- result << with_tabs(@tabs + 1) {visit(child)} + "\n"
219
+ with_tabs(@tabs + 1) {visit(child)}
220
+ output "\n"
99
221
  end
100
222
  end
101
- result.rstrip + if node.style == :compressed
102
- "}"
103
- else
104
- (node.style == :expanded ? "\n" : " ") + "}\n"
105
- end
223
+ rstrip!
224
+ if node.style == :expanded
225
+ output("\n#{tab_str}")
226
+ elsif node.style != :compressed
227
+ output(" ")
228
+ end
229
+ output("}")
106
230
  ensure
107
231
  @in_directive = was_in_directive
108
232
  end
233
+ # @comment
234
+ # rubocop:enable MethodLength
109
235
 
110
236
  def visit_media(node)
111
- str = with_tabs(@tabs + node.tabs) {visit_directive(node)}
112
- str.gsub!(/\n\Z/, '') unless node.style == :compressed || node.group_end
113
- str
237
+ with_tabs(@tabs + node.tabs) {visit_directive(node)}
238
+ output("\n") if node.group_end
114
239
  end
115
240
 
116
241
  def visit_supports(node)
@@ -124,24 +249,35 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
124
249
  def visit_prop(node)
125
250
  return if node.resolved_value.empty?
126
251
  tab_str = ' ' * (@tabs + node.tabs)
252
+ output(tab_str)
253
+ for_node(node, :name) {output(node.resolved_name)}
127
254
  if node.style == :compressed
128
- "#{tab_str}#{node.resolved_name}:#{node.resolved_value}"
255
+ output(":")
256
+ for_node(node, :value) {output(node.resolved_value)}
129
257
  else
130
- "#{tab_str}#{node.resolved_name}: #{node.resolved_value};"
258
+ output(": ")
259
+ for_node(node, :value) {output(node.resolved_value)}
260
+ output(";")
131
261
  end
132
262
  end
133
263
 
264
+ # @comment
265
+ # rubocop:disable MethodLength
134
266
  def visit_rule(node)
135
267
  with_tabs(@tabs + node.tabs) do
136
268
  rule_separator = node.style == :compressed ? ',' : ', '
137
269
  line_separator =
138
270
  case node.style
139
- when :nested, :expanded; "\n"
140
- when :compressed; ""
141
- else; " "
271
+ when :nested, :expanded; "\n"
272
+ when :compressed; ""
273
+ else; " "
142
274
  end
143
275
  rule_indent = ' ' * @tabs
144
- per_rule_indent, total_indent = [:nested, :expanded].include?(node.style) ? [rule_indent, ''] : ['', rule_indent]
276
+ per_rule_indent, total_indent = if [:nested, :expanded].include?(node.style)
277
+ [rule_indent, '']
278
+ else
279
+ ['', rule_indent]
280
+ end
145
281
 
146
282
  joined_rules = node.resolved_rules.members.map do |seq|
147
283
  next if seq.has_placeholder?
@@ -154,55 +290,67 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
154
290
  rule_part
155
291
  end.compact.join(rule_separator)
156
292
 
157
- joined_rules.sub!(/\A\s*/, per_rule_indent)
293
+ joined_rules.lstrip!
158
294
  joined_rules.gsub!(/\s*\n\s*/, "#{line_separator}#{per_rule_indent}")
159
- total_rule = total_indent << joined_rules
160
295
 
161
- to_return = ''
162
296
  old_spaces = ' ' * @tabs
163
- spaces = ' ' * (@tabs + 1)
164
297
  if node.style != :compressed
165
298
  if node.options[:debug_info] && !@in_directive
166
- to_return << visit(debug_info_rule(node.debug_info, node.options)) << "\n"
299
+ visit(debug_info_rule(node.debug_info, node.options))
300
+ output "\n"
167
301
  elsif node.options[:trace_selectors]
168
- to_return << "#{old_spaces}/* "
169
- to_return << node.stack_trace.join("\n #{old_spaces}")
170
- to_return << " */\n"
302
+ output("#{old_spaces}/* ")
303
+ output(node.stack_trace.gsub("\n", "\n #{old_spaces}"))
304
+ output(" */\n")
171
305
  elsif node.options[:line_comments]
172
- to_return << "#{old_spaces}/* line #{node.line}"
306
+ output("#{old_spaces}/* line #{node.line}")
173
307
 
174
308
  if node.filename
175
309
  relative_filename = if node.options[:css_filename]
176
- begin
177
- Pathname.new(node.filename).relative_path_from(
178
- Pathname.new(File.dirname(node.options[:css_filename]))).to_s
179
- rescue ArgumentError
180
- nil
181
- end
182
- end
310
+ begin
311
+ Pathname.new(node.filename).relative_path_from(
312
+ Pathname.new(File.dirname(node.options[:css_filename]))).to_s
313
+ rescue ArgumentError
314
+ nil
315
+ end
316
+ end
183
317
  relative_filename ||= node.filename
184
- to_return << ", #{relative_filename}"
318
+ output(", #{relative_filename}")
185
319
  end
186
320
 
187
- to_return << " */\n"
321
+ output(" */\n")
188
322
  end
189
323
  end
190
324
 
325
+ end_props, trailer, tabs = '', '', 0
191
326
  if node.style == :compact
192
- properties = with_tabs(0) {node.children.map {|a| visit(a)}.join(' ')}
193
- to_return << "#{total_rule} { #{properties} }#{"\n" if node.group_end}"
327
+ separator, end_props, bracket = ' ', ' ', ' { '
328
+ trailer = "\n" if node.group_end
194
329
  elsif node.style == :compressed
195
- properties = with_tabs(0) {node.children.map {|a| visit(a)}.join(';')}
196
- to_return << "#{total_rule}{#{properties}}"
330
+ separator, bracket = ';', '{'
197
331
  else
198
- properties = with_tabs(@tabs + 1) {node.children.map {|a| visit(a)}.join("\n")}
332
+ tabs = @tabs + 1
333
+ separator, bracket = "\n", " {\n"
334
+ trailer = "\n" if node.group_end
199
335
  end_props = (node.style == :expanded ? "\n" + old_spaces : ' ')
200
- to_return << "#{total_rule} {\n#{properties}#{end_props}}#{"\n" if node.group_end}"
336
+ end
337
+ output(total_indent + per_rule_indent)
338
+ for_node(node, :selector) {output(joined_rules)}
339
+ output(bracket)
340
+
341
+ with_tabs(tabs) do
342
+ node.children.each_with_index do |child, i|
343
+ output(separator) if i > 0
344
+ visit(child)
345
+ end
201
346
  end
202
347
 
203
- to_return
348
+ output(end_props)
349
+ output("}" + trailer)
204
350
  end
205
351
  end
352
+ # @comment
353
+ # rubocop:enable MethodLength
206
354
 
207
355
  private
208
356
 
@@ -217,13 +365,15 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
217
365
  false)
218
366
  ])
219
367
  ])
220
- prop = Sass::Tree::PropNode.new([""], Sass::Script::String.new(''), :new)
368
+ prop = Sass::Tree::PropNode.new([""], Sass::Script::Value::String.new(''), :new)
221
369
  prop.resolved_name = "font-family"
222
370
  prop.resolved_value = Sass::SCSS::RX.escape_ident(v.to_s)
223
371
  rule << prop
224
372
  node << rule
225
373
  end
226
- node.options = options.merge(:debug_info => false, :line_comments => false, :style => :compressed)
374
+ node.options = options.merge(:debug_info => false,
375
+ :line_comments => false,
376
+ :style => :compressed)
227
377
  node
228
378
  end
229
379
  end
@@ -5,10 +5,10 @@ module Sass
5
5
  # @see Sass::Tree
6
6
  class WarnNode < Node
7
7
  # The expression to print.
8
- # @return [Script::Node]
8
+ # @return [Script::Tree::Node]
9
9
  attr_accessor :expr
10
10
 
11
- # @param expr [Script::Node] The expression to print
11
+ # @param expr [Script::Tree::Node] The expression to print
12
12
  def initialize(expr)
13
13
  @expr = expr
14
14
  super()
@@ -6,10 +6,10 @@ module Sass::Tree
6
6
  # @see Sass::Tree
7
7
  class WhileNode < Node
8
8
  # The parse tree for the continuation expression.
9
- # @return [Script::Node]
9
+ # @return [Script::Tree::Node]
10
10
  attr_accessor :expr
11
11
 
12
- # @param expr [Script::Node] See \{#expr}
12
+ # @param expr [Script::Tree::Node] See \{#expr}
13
13
  def initialize(expr)
14
14
  @expr = expr
15
15
  super()
@@ -1,7 +1,9 @@
1
1
  require 'strscan'
2
2
 
3
3
  if Sass::Util.ruby1_8?
4
+ # rubocop:disable ConstantName
4
5
  Sass::Util::MultibyteStringScanner = StringScanner
6
+ # rubocop:enable ConstantName
5
7
  else
6
8
  if Sass::Util.rbx?
7
9
  # Rubinius's StringScanner class implements some of its methods in terms of
@@ -0,0 +1,65 @@
1
+ require 'delegate'
2
+ require 'sass/util'
3
+
4
+ module Sass
5
+ module Util
6
+ # A hash that normalizes its string keys while still allowing you to get back
7
+ # to the original keys that were stored. If several different values normalize
8
+ # to the same value, whichever is stored last wins.
9
+ require 'sass/util/ordered_hash' if ruby1_8?
10
+ class NormalizedMap < DelegateClass(ruby1_8? ? OrderedHash : Hash)
11
+ # Create a normalized map
12
+ def initialize
13
+ @key_strings = {}
14
+ @map = Util.ruby1_8? ? OrderedHash.new : {}
15
+
16
+ # We delegate all hash methods that are not overridden here to @map.
17
+ super(@map)
18
+ end
19
+
20
+ # Specifies how to transform the key.
21
+ #
22
+ # This can be overridden to create other normalization behaviors.
23
+ def normalize(key)
24
+ key.tr("-", "_")
25
+ end
26
+
27
+ # @private
28
+ def []=(k, v)
29
+ normalized = normalize(k)
30
+ super(normalized, v)
31
+ @key_strings[normalized] = k
32
+ end
33
+
34
+ # @private
35
+ def [](k)
36
+ super(normalize(k))
37
+ end
38
+
39
+ # @private
40
+ def has_key?(k)
41
+ super(normalize(k))
42
+ end
43
+
44
+ # @private
45
+ def delete(k)
46
+ normalized = normalize(k)
47
+ @key_strings.delete(normalized)
48
+ super(normalized)
49
+ end
50
+
51
+ # @return [Hash] Hash with the keys as they were stored (before normalization).
52
+ def as_stored
53
+ Sass::Util.map_keys(@map) {|k| @key_strings[k]}
54
+ end
55
+
56
+ # this is magically invoked by ruby, not sure why DelegateClass doesn't take care of it.
57
+ # @private
58
+ def initialize_dup(other)
59
+ super
60
+ @map = other.instance_variable_get("@map").dup
61
+ __setobj__(@map)
62
+ end
63
+ end
64
+ end
65
+ end