Skip to content
This repository
Browse code

updated haml

  • Loading branch information...
commit 55e5030cb367054847b917a8c546882a44772bc0 1 parent b0b99d0
Jim Gay authored November 03, 2010

Showing 50 changed files with 760 additions and 362 deletions. Show diff stats Hide diff stats

  1. 1  CHANGELOG
  2. 54  vendor/plugins/haml/Rakefile
  3. 2  vendor/plugins/haml/VERSION
  4. 44  vendor/plugins/haml/doc-src/HAML_CHANGELOG.md
  5. 83  vendor/plugins/haml/doc-src/SASS_CHANGELOG.md
  6. 17  vendor/plugins/haml/doc-src/SASS_REFERENCE.md
  7. 4  vendor/plugins/haml/extra/haml-mode.el
  8. 2  vendor/plugins/haml/extra/sass-mode.el
  9. 2  vendor/plugins/haml/haml.gemspec
  10. 2  vendor/plugins/haml/lib/haml/engine.rb
  11. 10  vendor/plugins/haml/lib/haml/exec.rb
  12. 75  vendor/plugins/haml/lib/haml/precompiler.rb
  13. 22  vendor/plugins/haml/lib/haml/template/plugin.rb
  14. 13  vendor/plugins/haml/lib/haml/util.rb
  15. 16  vendor/plugins/haml/lib/sass/engine.rb
  16. 43  vendor/plugins/haml/lib/sass/less.rb
  17. 5  vendor/plugins/haml/lib/sass/plugin.rb
  18. 3  vendor/plugins/haml/lib/sass/script/color.rb
  19. 11  vendor/plugins/haml/lib/sass/script/funcall.rb
  20. 2  vendor/plugins/haml/lib/sass/script/interpolation.rb
  21. 2  vendor/plugins/haml/lib/sass/script/literal.rb
  22. 11  vendor/plugins/haml/lib/sass/script/node.rb
  23. 21  vendor/plugins/haml/lib/sass/script/number.rb
  24. 19  vendor/plugins/haml/lib/sass/script/operation.rb
  25. 11  vendor/plugins/haml/lib/sass/script/parser.rb
  26. 2  vendor/plugins/haml/lib/sass/script/string_interpolation.rb
  27. 5  vendor/plugins/haml/lib/sass/scss/css_parser.rb
  28. 10  vendor/plugins/haml/lib/sass/scss/parser.rb
  29. 2  vendor/plugins/haml/lib/sass/scss/static_parser.rb
  30. 37  vendor/plugins/haml/lib/sass/tree/charset_node.rb
  31. 4  vendor/plugins/haml/lib/sass/tree/directive_node.rb
  32. 36  vendor/plugins/haml/lib/sass/tree/root_node.rb
  33. 2  vendor/plugins/haml/lib/sass/tree/rule_node.rb
  34. 281  vendor/plugins/haml/test/haml/engine_test.rb
  35. 8  vendor/plugins/haml/test/sass/conversion_test.rb
  36. 50  vendor/plugins/haml/test/sass/engine_test.rb
  37. 27  vendor/plugins/haml/test/sass/less_conversion_test.rb
  38. 15  vendor/plugins/haml/test/sass/plugin_test.rb
  39. 4  vendor/plugins/haml/test/sass/results/import_charset.css
  40. 4  vendor/plugins/haml/test/sass/results/import_charset_1_8.css
  41. 4  vendor/plugins/haml/test/sass/results/import_charset_ibm866.css
  42. 60  vendor/plugins/haml/test/sass/script_conversion_test.rb
  43. 19  vendor/plugins/haml/test/sass/script_test.rb
  44. 35  vendor/plugins/haml/test/sass/scss/css_test.rb
  45. 14  vendor/plugins/haml/test/sass/scss/scss_test.rb
  46. 4  vendor/plugins/haml/test/sass/templates/_imported_charset_ibm866.sass
  47. 4  vendor/plugins/haml/test/sass/templates/_imported_charset_utf8.sass
  48. 7  vendor/plugins/haml/test/sass/templates/import_charset.sass
  49. 4  vendor/plugins/haml/test/sass/templates/import_charset_1_8.sass
  50. 9  vendor/plugins/haml/test/sass/templates/import_charset_ibm866.sass
1  CHANGELOG
@@ -2,6 +2,7 @@
2 2
 
3 3
 === Edge
4 4
 
  5
+* Update Haml to 3.0.23 [Jim Gay]
5 6
 * Localize <r:date> [Jim Gay]
6 7
 * Make any date column a valid value for <r:date /> [Jim Gay]
7 8
 * Move url methods to path [Jim Gay]
54  vendor/plugins/haml/Rakefile
@@ -42,7 +42,7 @@ END
42 42
 # Don't use Rake::GemPackageTast because we want prerequisites to run
43 43
 # before we load the gemspec.
44 44
 desc "Build all the packages."
45  
-task :package => [:revision_file, :submodules] do
  45
+task :package => [:revision_file, :submodules, :permissions] do
46 46
   load scope('haml.gemspec')
47 47
   Gem::Builder.new(HAML_GEMSPEC).build
48 48
   pkg = "#{HAML_GEMSPEC.name}-#{HAML_GEMSPEC.version}"
@@ -54,6 +54,16 @@ task :package => [:revision_file, :submodules] do
54 54
   sh %{gzip pkg/#{pkg}.tar}
55 55
 end
56 56
 
  57
+task :permissions do
  58
+  sh %{chmod -R a+rx bin}
  59
+  sh %{chmod -R a+r .}
  60
+  require 'shellwords'
  61
+  Dir.glob('test/**/*_test.rb') do |file|
  62
+    next if file =~ %r{^test/haml/spec/}
  63
+    sh %{chmod a+rx #{file}}
  64
+  end
  65
+end
  66
+
57 67
 task :revision_file do
58 68
   require 'lib/haml'
59 69
 
@@ -77,7 +87,7 @@ task :install => [:package] do
77 87
 end
78 88
 
79 89
 desc "Release a new Haml package to Rubyforge."
80  
-task :release => [:check_release, :release_elpa, :package] do
  90
+task :release => [:check_release, :package] do
81 91
   name = File.read(scope("VERSION_NAME")).strip
82 92
   version = File.read(scope("VERSION")).strip
83 93
   sh %{rubyforge add_release haml haml "#{name} (v#{version})" pkg/haml-#{version}.gem}
@@ -85,46 +95,6 @@ task :release => [:check_release, :release_elpa, :package] do
85 95
   sh %{gem push pkg/haml-#{version}.gem}
86 96
 end
87 97
 
88  
-# Releases haml-mode.el and sass-mode.el to ELPA.
89  
-task :release_elpa do
90  
-  require 'tlsmail'
91  
-  require 'time'
92  
-  require scope('lib/haml')
93  
-
94  
-  next if Haml.version[:prerelease]
95  
-  version = Haml.version[:number]
96  
-
97  
-  haml_unchanged = mode_unchanged?(:haml, version)
98  
-  sass_unchanged = mode_unchanged?(:sass, version)
99  
-  next if haml_unchanged && sass_unchanged
100  
-  raise "haml-mode.el and sass-mode.el are out of sync." if (!!haml_unchanged) ^ (!!sass_unchanged)
101  
-
102  
-  if sass_unchanged && File.read(scope("extra/sass-mode.el")).
103  
-      include?(";; Package-Requires: ((haml-mode #{sass_unchanged.inspect}))")
104  
-    raise "sass-mode.el doesn't require the same version of haml-mode."
105  
-  end
106  
-
107  
-  from = `git config user.email`.strip
108  
-  raise "Don't know how to send emails except via Gmail" unless from =~ /@gmail.com$/
109  
-
110  
-  to = "elpa@tromey.com"
111  
-  Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE)
112  
-  Net::SMTP.start('smtp.gmail.com', 587, 'gmail.com', from, read_password("GMail Password"), :login) do |smtp|
113  
-    smtp.send_message(<<CONTENT, from, to)
114  
-From: Nathan Weizenbaum <#{from}>
115  
-To: #{to}
116  
-Subject: Submitting haml-mode and sass-mode #{version}
117  
-Date: #{Time.now.rfc2822}
118  
-
119  
-haml-mode and sass-mode #{version} are packaged and ready to be included in ELPA.
120  
-They can be downloaded from:
121  
-
122  
-  http://github.com/nex3/haml/raw/#{Haml.version[:rev]}/extra/haml-mode.el
123  
-  http://github.com/nex3/haml/raw/#{Haml.version[:rev]}/extra/sass-mode.el
124  
-CONTENT
125  
-  end
126  
-end
127  
-
128 98
 # Ensures that the version have been updated for a new release.
129 99
 task :check_release do
130 100
   version = File.read(scope("VERSION")).strip
2  vendor/plugins/haml/VERSION
... ...
@@ -1 +1 @@
1  
-3.0.18
  1
+3.0.23
44  vendor/plugins/haml/doc-src/HAML_CHANGELOG.md
Source Rendered
@@ -3,6 +3,47 @@
3 3
 * Table of contents
4 4
 {:toc}
5 5
 
  6
+## 3.0.23
  7
+
  8
+[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.23).
  9
+
  10
+* Fix the error message for unloadable modules when running the executables under Ruby 1.9.2.
  11
+
  12
+* Fix an error when combining old-style and new-style attributes.
  13
+
  14
+## 3.0.22
  15
+
  16
+[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.22).
  17
+
  18
+* Allow an empty line after `case` but before `when`.
  19
+
  20
+* Remove `vendor/sass`, which snuck into the gem by mistake
  21
+  and was causing trouble for Heroku users (thanks to [Jacques Crocker](http://railsjedi.com/)).
  22
+
  23
+* Support the Rails 3.1 template handler API.
  24
+
  25
+## 3.0.21
  26
+
  27
+[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.21).
  28
+
  29
+* Fix the permissions errors for good.
  30
+
  31
+## 3.0.20
  32
+
  33
+[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.20).
  34
+
  35
+* Fix some permissions errors.
  36
+
  37
+## 3.0.19
  38
+
  39
+[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.19).
  40
+
  41
+* Fix the `:encoding` option under Ruby 1.9.2.
  42
+
  43
+* Fix interpolated if statement when HTML escaping is enabled.
  44
+
  45
+* Allow the `--unix-newlines` flag to work on Unix, where it's a no-op.
  46
+
6 47
 ## 3.0.18
7 48
 
8 49
 [Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.18).
@@ -14,6 +55,9 @@
14 55
 
15 56
 * Disambiguate references to the `Rails` module when `haml-rails` is installed.
16 57
 
  58
+* Fix a bug in `haml_tag` that would allow duplicate attributes to be added
  59
+  and make `data-` attributes not work.
  60
+
17 61
 * Compatibility with Rails 3 final.
18 62
 
19 63
 ## 3.0.17
83  vendor/plugins/haml/doc-src/SASS_CHANGELOG.md
Source Rendered
@@ -3,6 +3,89 @@
3 3
 * Table of contents
4 4
 {:toc}
5 5
 
  6
+## 3.0.23
  7
+
  8
+[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.23).
  9
+
  10
+* Fix the error message for unloadable modules when running the executables under Ruby 1.9.2.
  11
+
  12
+### `@charset` Change
  13
+
  14
+The behavior of `@charset` has changed in version 3.0.23
  15
+in order to work around a bug in Safari,
  16
+where `@charset` declarations placed anywhere other than the beginning of the document
  17
+cause some CSS rules to be ignored.
  18
+This change also makes `@charset`s in imported files behave in a more useful way.
  19
+
  20
+#### Ruby 1.9
  21
+
  22
+When using Ruby 1.9, which keeps track of the character encoding of the Sass document internally,
  23
+`@charset` directive in the Sass stylesheet and any stylesheets it imports
  24
+are no longer directly output to the generated CSS.
  25
+They're still used for determining the encoding of the input and output stylesheets,
  26
+but they aren't rendered in the same way other directives are.
  27
+
  28
+Instead, Sass adds a single `@charset` directive at the beginning of the output stylesheet
  29
+if necessary, whether or not the input stylesheet had a `@charset` directive.
  30
+It will add this directive if and only if the output stylesheet contains non-ASCII characters.
  31
+By default, the declared charset will be UTF-8,
  32
+but if the Sass stylesheet declares a different charset then that will be used instead if possible.
  33
+
  34
+One important consequence of this scheme is that it's possible for a Sass file
  35
+to import partials with different encodings (e.g. one encoded as UTF-8 and one as IBM866).
  36
+The output will then be UTF-8, unless the importing stylesheet
  37
+declares a different charset.
  38
+
  39
+#### Ruby 1.8
  40
+
  41
+Ruby 1.8 doesn't have good support for encodings, so it uses a simpler but less accurate
  42
+scheme for figuring out what `@charset` declaration to use for the output stylesheet.
  43
+It just takes the first `@charset` declaration to appear in the stylesheet
  44
+or any of its imports and moves it to the beginning of the document.
  45
+This means that under Ruby 1.8 it's *not* safe to import files with different encodings.
  46
+
  47
+## 3.0.22
  48
+
  49
+[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.22).
  50
+
  51
+* Remove `vendor/sass`, which snuck into the gem by mistake
  52
+  and was causing trouble for Heroku users (thanks to [Jacques Crocker](http://railsjedi.com/)).
  53
+
  54
+* `sass-convert` now understands better when it's acceptable
  55
+  to remove parentheses from expressions.
  56
+
  57
+## 3.0.21
  58
+
  59
+[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.21).
  60
+
  61
+* Fix the permissions errors for good.
  62
+
  63
+* Fix more `#options` attribute errors.
  64
+
  65
+## 3.0.20
  66
+
  67
+[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.20).
  68
+
  69
+* Fix some permissions errors.
  70
+
  71
+* Fix `#options` attribute errors when CSS functions were used with commas.
  72
+
  73
+## 3.0.19
  74
+
  75
+[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.19).
  76
+
  77
+* Make the alpha value for `rgba` colors respect {Sass::Script::Number::PRECISION}.
  78
+
  79
+* Remove all newlines in selectors in `:compressed` mode.
  80
+
  81
+* Make color names case-insensitive.
  82
+
  83
+* Properly detect SCSS files when using `sass -c`.
  84
+
  85
+* Remove spaces after commas in `:compressed` mode.
  86
+
  87
+* Allow the `--unix-newlines` flag to work on Unix, where it's a no-op.
  88
+
6 89
 ## 3.0.18
7 90
 
8 91
 [Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.18).
17  vendor/plugins/haml/doc-src/SASS_REFERENCE.md
Source Rendered
@@ -329,6 +329,23 @@ Note that Sass does not support the obscure `UTF-32-2143`,
329 329
 since Ruby does not have support for them
330 330
 and they're highly unlikely to ever be used in practice.
331 331
 
  332
+#### Output Encoding
  333
+
  334
+In general, Sass will try to encode the output stylesheet
  335
+using the same encoding as the input stylesheet.
  336
+In order for it to do this, though, the input stylesheet must have a `@charset` declaration;
  337
+otherwise, Sass will default to encoding the output stylesheet as UTF-8.
  338
+In addition, it will add a `@charset` declaration to the output
  339
+if it's not plain ASCII.
  340
+
  341
+When other stylesheets with `@charset` declarations are `@import`ed,
  342
+Sass will convert them to the same encoding as the main stylesheet.
  343
+
  344
+Note that Ruby 1.8 does not have good support for character encodings,
  345
+and so Sass behaves somewhat differently when running under it than under Ruby 1.9 and later.
  346
+In Ruby 1.8, Sass simply uses the first `@charset` declaration in the stylesheet
  347
+or any of the other stylesheets it `@import`s.
  348
+
332 349
 ## CSS Extensions
333 350
 
334 351
 ### Nested Rules
4  vendor/plugins/haml/extra/haml-mode.el
@@ -4,7 +4,7 @@
4 4
 
5 5
 ;; Author: Nathan Weizenbaum
6 6
 ;; URL: http://github.com/nex3/haml/tree/master
7  
-;; Version: 3.0.14
  7
+;; Version: 3.0.19
8 8
 ;; Created: 2007-03-08
9 9
 ;; By: Nathan Weizenbaum
10 10
 ;; Keywords: markup, language, html
@@ -73,7 +73,7 @@ a specific level to which the current line could be indented.")
73 73
   `(,(concat haml-tag-beg-re "[><]*[ \t]*$")
74 74
     "^[ \t]*[&!]?[-=~].*do[ \t]*\\(|.*|[ \t]*\\)?$"
75 75
     ,(concat "^[ \t]*[&!]?[-=~][ \t]*\\("
76  
-             (regexp-opt '("if" "unless" "while" "until" "else"
  76
+             (regexp-opt '("if" "unless" "while" "until" "else" "for"
77 77
                            "begin" "elsif" "rescue" "ensure" "when"))
78 78
              "\\)")
79 79
     "^[ \t]*/\\(\\[.*\\]\\)?[ \t]*$"
2  vendor/plugins/haml/extra/sass-mode.el
@@ -4,7 +4,7 @@
4 4
 
5 5
 ;; Author: Nathan Weizenbaum
6 6
 ;; URL: http://github.com/nex3/haml/tree/master
7  
-;; Version: 3.0.14
  7
+;; Version: 3.0.19
8 8
 ;; Created: 2007-03-15
9 9
 ;; By: Nathan Weizenbaum
10 10
 ;; Keywords: markup, language, css
2  vendor/plugins/haml/haml.gemspec
@@ -26,7 +26,7 @@ HAML_GEMSPEC = Gem::Specification.new do |spec|
26 26
 
27 27
   readmes = Dir['*'].reject{ |x| x =~ /(^|[^.a-z])[a-z]+/ || x == "TODO" }
28 28
   spec.executables = ['haml', 'html2haml', 'sass', 'css2sass', 'sass-convert']
29  
-  spec.files = Dir['rails/init.rb', 'lib/**/*', 'vendor/**/*',
  29
+  spec.files = Dir['rails/init.rb', 'lib/**/*', 'vendor/fssm/**/*',
30 30
     'bin/*', 'test/**/*', 'extra/**/*', 'Rakefile', 'init.rb',
31 31
     '.yardopts'] + readmes
32 32
   spec.homepage = 'http://haml-lang.com/'
2  vendor/plugins/haml/lib/haml/engine.rb
@@ -190,7 +190,7 @@ def render(scope = Object.new, locals = {}, &block)
190 190
     ensure
191 191
       # Get rid of the current buffer
192 192
       scope_object.instance_eval do
193  
-        @haml_buffer = buffer.upper
  193
+        @haml_buffer = buffer.upper if buffer
194 194
       end
195 195
     end
196 196
     alias_method :to_html, :render
10  vendor/plugins/haml/lib/haml/exec.rb
@@ -79,10 +79,8 @@ def set_opts(opts)
79 79
           @options[:trace] = true
80 80
         end
81 81
 
82  
-        if ::Haml::Util.windows?
83  
-          opts.on('--unix-newlines', 'Use Unix-style newlines in written files.') do
84  
-            @options[:unix_newlines] = true
85  
-          end
  82
+        opts.on('--unix-newlines', 'Use Unix-style newlines in written files.') do
  83
+          @options[:unix_newlines] = true if ::Haml::Util.windows?
86 84
         end
87 85
 
88 86
         opts.on_tail("-?", "-h", "--help", "Show this message") do
@@ -157,7 +155,7 @@ def open_file(filename, flag = 'r')
157 155
       end
158 156
 
159 157
       def handle_load_error(err)
160  
-        dep = err.message.scan(/^no such file to load -- (.*)/)[0]
  158
+        dep = err.message[/^no such file to load -- (.*)/, 1]
161 159
         raise err if @options[:trace] || dep.nil? || dep.empty?
162 160
         $stderr.puts <<MESSAGE
163 161
 Required dependency #{dep} not found!
@@ -357,7 +355,7 @@ def process_result
357 355
           input = @options[:input]
358 356
           output = @options[:output]
359 357
 
360  
-          @options[:syntax] ||= :scss if input.is_a?(File) && input.path =~ /\.scss$/
  358
+          @options[:for_engine][:syntax] ||= :scss if input.is_a?(File) && input.path =~ /\.scss$/
361 359
           tree =
362 360
             if input.is_a?(File) && !@options[:check_syntax]
363 361
               ::Sass::Files.tree_for(input.path, @options[:for_engine])
75  vendor/plugins/haml/lib/haml/precompiler.rb
@@ -118,8 +118,8 @@ def locals_code(names)
118 118
 
119 119
       names.map do |name|
120 120
         # Can't use || because someone might explicitly pass in false with a symbol
121  
-        sym_local = "_haml_locals[#{name.to_sym.inspect}]"
122  
-        str_local = "_haml_locals[#{name.to_s.inspect}]"
  121
+        sym_local = "_haml_locals[#{inspect_obj(name.to_sym)}]"
  122
+        str_local = "_haml_locals[#{inspect_obj(name.to_s)}]"
123 123
         "#{name} = #{sym_local}.nil? ? #{str_local} : #{sym_local}"
124 124
       end.join(';') + ';'
125 125
     end
@@ -320,7 +320,7 @@ def flush_merged_text
320 320
       @to_merge.each do |type, val, tabs|
321 321
         case type
322 322
         when :text
323  
-          str << val.inspect[1...-1]
  323
+          str << inspect_obj(val)[1...-1]
324 324
           mtabs += tabs
325 325
         when :script
326 326
           if mtabs != 0 && !@options[:ugly]
@@ -336,12 +336,14 @@ def flush_merged_text
336 336
         end
337 337
       end
338 338
 
339  
-      @precompiled <<
340  
-        if @options[:ugly]
341  
-          "_hamlout.buffer << \"#{str}\";"
342  
-        else
343  
-          "_hamlout.push_text(\"#{str}\", #{mtabs}, #{@dont_tab_up_next_text.inspect});"
344  
-        end
  339
+      unless str.empty?
  340
+        @precompiled <<
  341
+          if @options[:ugly]
  342
+            "_hamlout.buffer << \"#{str}\";"
  343
+          else
  344
+            "_hamlout.push_text(\"#{str}\", #{mtabs}, #{@dont_tab_up_next_text.inspect});"
  345
+          end
  346
+      end
345 347
       @precompiled << "\n" * newlines
346 348
       @to_merge = []
347 349
       @dont_tab_up_next_text = false
@@ -577,17 +579,17 @@ def parse_tag(line)
577 579
       raise SyntaxError.new("Invalid tag: \"#{line}\".") unless match = line.scan(/%([-:\w]+)([-:\w\.\#]*)(.*)/)[0]
578 580
       tag_name, attributes, rest = match
579 581
       new_attributes_hash = old_attributes_hash = last_line = object_ref = nil
580  
-      attributes_hashes = []
  582
+      attributes_hashes = {}
581 583
       while rest
582 584
         case rest[0]
583 585
         when ?{
584 586
           break if old_attributes_hash
585 587
           old_attributes_hash, rest, last_line = parse_old_attributes(rest)
586  
-          attributes_hashes << [:old, old_attributes_hash]
  588
+          attributes_hashes[:old] = old_attributes_hash
587 589
         when ?(
588 590
           break if new_attributes_hash
589 591
           new_attributes_hash, rest, last_line = parse_new_attributes(rest)
590  
-          attributes_hashes << [:new, new_attributes_hash]
  592
+          attributes_hashes[:new] = new_attributes_hash
591 593
         when ?[
592 594
           break if object_ref
593 595
           object_ref, rest = balance(rest, ?[, ?])
@@ -660,7 +662,7 @@ def parse_new_attributes(line)
660 662
         if type == :static
661 663
           static_attributes[name] = val
662 664
         else
663  
-          dynamic_attributes << name.inspect << " => " << val << ","
  665
+          dynamic_attributes << inspect_obj(name) << " => " << val << ","
664 666
         end
665 667
       end
666 668
       dynamic_attributes << "}"
@@ -695,7 +697,7 @@ def parse_new_attribute(scanner)
695 697
 
696 698
       return name, [:static, content.first[1]] if content.size == 1
697 699
       return name, [:dynamic,
698  
-        '"' + content.map {|(t, v)| t == :str ? v.inspect[1...-1] : "\#{#{v}}"}.join + '"']
  700
+        '"' + content.map {|(t, v)| t == :str ? inspect_obj(v)[1...-1] : "\#{#{v}}"}.join + '"']
699 701
     end
700 702
 
701 703
     # Parses a line that will render as an XHTML tag, and adds the code that will
@@ -755,16 +757,21 @@ def render_tag(line)
755 757
       object_ref = "nil" if object_ref.nil? || @options[:suppress_eval]
756 758
 
757 759
       attributes = Precompiler.parse_class_and_id(attributes)
758  
-      attributes_hashes.map! do |syntax, attributes_hash|
759  
-        if syntax == :old
760  
-          static_attributes = parse_static_hash(attributes_hash)
761  
-          attributes_hash = nil if static_attributes || @options[:suppress_eval]
762  
-        else
763  
-          static_attributes, attributes_hash = attributes_hash
764  
-        end
  760
+      attributes_list = []
  761
+
  762
+      if attributes_hashes[:new]
  763
+        static_attributes, attributes_hash = attributes_hashes[:new]
  764
+        Buffer.merge_attrs(attributes, static_attributes) if static_attributes
  765
+        attributes_list << attributes_hash
  766
+      end
  767
+
  768
+      if attributes_hashes[:old]
  769
+        static_attributes = parse_static_hash(attributes_hashes[:old])
765 770
         Buffer.merge_attrs(attributes, static_attributes) if static_attributes
766  
-        attributes_hash
767  
-      end.compact!
  771
+        attributes_list << attributes_hashes[:old] unless static_attributes || @options[:suppress_eval]
  772
+      end
  773
+
  774
+      attributes_list.compact!
768 775
 
769 776
       raise SyntaxError.new("Illegal nesting: nesting within a self-closing tag is illegal.", @next_line.index) if block_opened? && self_closing
770 777
       raise SyntaxError.new("There's no Ruby code for #{action} to evaluate.", last_line - 1) if parse && value.empty?
@@ -782,7 +789,7 @@ def render_tag(line)
782 789
         (nuke_inner_whitespace && block_opened?)
783 790
 
784 791
       # Check if we can render the tag directly to text and not process it in the buffer
785  
-      if object_ref == "nil" && attributes_hashes.empty? && !preserve_script
  792
+      if object_ref == "nil" && attributes_list.empty? && !preserve_script
786 793
         tag_closed = !block_opened? && !self_closing && !parse
787 794
 
788 795
         open_tag  = prerender_tag(tag_name, self_closing, attributes)
@@ -800,19 +807,19 @@ def render_tag(line)
800 807
         return if tag_closed
801 808
       else
802 809
         flush_merged_text
803  
-        content = parse ? 'nil' : value.inspect
804  
-        if attributes_hashes.empty?
805  
-          attributes_hashes = ''
806  
-        elsif attributes_hashes.size == 1
807  
-          attributes_hashes = ", #{attributes_hashes.first}"
  810
+        content = parse ? 'nil' : inspect_obj(value)
  811
+        if attributes_list.empty?
  812
+          attributes_list = ''
  813
+        elsif attributes_list.size == 1
  814
+          attributes_list = ", #{attributes_list.first}"
808 815
         else
809  
-          attributes_hashes = ", (#{attributes_hashes.join(").merge(")})"
  816
+          attributes_list = ", (#{attributes_list.join(").merge(")})"
810 817
         end
811 818
 
812 819
         args = [tag_name, self_closing, !block_opened?, preserve_tag, escape_html,
813 820
                 attributes, nuke_outer_whitespace, nuke_inner_whitespace
814  
-               ].map { |v| v.inspect }.join(', ')
815  
-        push_silent "_hamlout.open_tag(#{args}, #{object_ref}, #{content}#{attributes_hashes})"
  821
+               ].map {|v| inspect_obj(v)}.join(', ')
  822
+        push_silent "_hamlout.open_tag(#{args}, #{object_ref}, #{content}#{attributes_list})"
816 823
         @dont_tab_up_next_text = @dont_indent_next_line = dont_indent_next_line
817 824
       end
818 825
 
@@ -1017,14 +1024,14 @@ def contains_interpolation?(str)
1017 1024
 
1018 1025
     def unescape_interpolation(str, opts = {})
1019 1026
       res = ''
1020  
-      rest = Haml::Shared.handle_interpolation str.dump do |scan|
  1027
+      rest = Haml::Shared.handle_interpolation inspect_obj(str) do |scan|
1021 1028
         escapes = (scan[2].size - 1) / 2
1022 1029
         res << scan.matched[0...-3 - escapes]
1023 1030
         if escapes % 2 == 1
1024 1031
           res << '#{'
1025 1032
         else
1026 1033
           content = eval('"' + balance(scan, ?{, ?}, 1)[0][0...-1] + '"')
1027  
-          content = "Haml::Helpers.html_escape(#{content})" if opts[:escape_html]
  1034
+          content = "Haml::Helpers.html_escape((#{content}))" if opts[:escape_html]
1028 1035
           res << '#{' + content + "}"# Use eval to get rid of string escapes
1029 1036
         end
1030 1037
       end
22  vendor/plugins/haml/lib/haml/template/plugin.rb
@@ -2,12 +2,17 @@
2 2
 # using the > 2.0.1 template handler API.
3 3
 
4 4
 module Haml
5  
-  class Plugin < Haml::Util.av_template_class(:Handler)
6  
-    if (defined?(ActionView::TemplateHandlers) &&
7  
-        defined?(ActionView::TemplateHandlers::Compilable)) ||
8  
-       (defined?(ActionView::Template) &&
9  
-        defined?(ActionView::Template::Handlers) &&
10  
-        defined?(ActionView::Template::Handlers::Compilable))
  5
+  # In Rails 3.1+, template handlers don't inherit from anything. In <= 3.0, they do.
  6
+  # To avoid messy logic figuring this out, we just inherit from whatever the ERB handler does.
  7
+  class Plugin < Haml::Util.av_template_class(:Handlers)::ERB.superclass
  8
+    if ((defined?(ActionView::TemplateHandlers) &&
  9
+          defined?(ActionView::TemplateHandlers::Compilable)) ||
  10
+        (defined?(ActionView::Template) &&
  11
+          defined?(ActionView::Template::Handlers) &&
  12
+          defined?(ActionView::Template::Handlers::Compilable))) &&
  13
+        # In Rails 3.1+, we don't need to include Compilable.
  14
+        Haml::Util.av_template_class(:Handlers)::ERB.include?(
  15
+          Haml::Util.av_template_class(:Handlers)::Compilable)
11 16
       include Haml::Util.av_template_class(:Handlers)::Compilable
12 17
     end
13 18
 
@@ -29,6 +34,11 @@ def compile(template)
29 34
       Haml::Engine.new(source, options).send(:precompiled_with_ambles, [])
30 35
     end
31 36
 
  37
+    # In Rails 3.1+, #call takes the place of #compile
  38
+    def self.call(template)
  39
+      new.compile(template)
  40
+    end
  41
+
32 42
     def cache_fragment(block, name = {}, options = nil)
33 43
       @view.fragment_for(block, name, options) do
34 44
         eval("_hamlout.buffer", block.binding)
13  vendor/plugins/haml/lib/haml/util.rb
@@ -656,6 +656,19 @@ def set_eql?(set1, set2)
656 656
       set1.to_a.uniq.sort_by {|e| e.hash}.eql?(set2.to_a.uniq.sort_by {|e| e.hash})
657 657
     end
658 658
 
  659
+    # Like `Object#inspect`, but preserves non-ASCII characters rather than escaping them under Ruby 1.9.2.
  660
+    # This is necessary so that the precompiled Haml template can be `#encode`d into `@options[:encoding]`
  661
+    # before being evaluated.
  662
+    #
  663
+    # @param obj {Object}
  664
+    # @return {String}
  665
+    def inspect_obj(obj)
  666
+      return obj.inspect unless version_geq(::RUBY_VERSION, "1.9.2")
  667
+      return ':' + inspect_obj(obj.to_s) if obj.is_a?(Symbol)
  668
+      return obj.inspect unless obj.is_a?(String)
  669
+      '"' + obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]} + '"'
  670
+    end
  671
+
659 672
     ## Static Method Stuff
660 673
 
661 674
     # The context in which the ERB for \{#def\_static\_method} will be run.
16  vendor/plugins/haml/lib/sass/engine.rb
@@ -16,6 +16,7 @@
16 16
 require 'sass/tree/debug_node'
17 17
 require 'sass/tree/warn_node'
18 18
 require 'sass/tree/import_node'
  19
+require 'sass/tree/charset_node'
19 20
 require 'sass/selector'
20 21
 require 'sass/environment'
21 22
 require 'sass/script'
@@ -195,7 +196,14 @@ def source_encoding
195 196
     def _render
196 197
       rendered = _to_tree.render
197 198
       return rendered if ruby1_8?
198  
-      return rendered.encode(source_encoding)
  199
+      begin
  200
+        # Try to convert the result to the original encoding,
  201
+        # but if that doesn't work fall back on UTF-8
  202
+        rendered = rendered.encode(source_encoding)
  203
+      rescue EncodingError
  204
+      end
  205
+      rendered.gsub(Regexp.new('\A@charset "(.*?)"'.encode(source_encoding)),
  206
+        "@charset \"#{source_encoding.name}\"".encode(source_encoding))
199 207
     end
200 208
 
201 209
     def _to_tree
@@ -542,6 +550,12 @@ def parse_directive(parent, line, root)
542 550
           :line => @line + 1) unless line.children.empty?
543 551
         offset = line.offset + line.text.index(value).to_i
544 552
         Tree::WarnNode.new(parse_script(value, :offset => offset))
  553
+      elsif directive == "charset"
  554
+        name = value && value[/\A(["'])(.*)\1\Z/, 2] #"
  555
+        raise SyntaxError.new("Invalid charset directive '@charset': expected string.") unless name
  556
+        raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath charset directives.",
  557
+          :line => @line + 1) unless line.children.empty?
  558
+        Tree::CharsetNode.new(name)
545 559
       else
546 560
         Tree::DirectiveNode.new(line.text)
547 561
       end
43  vendor/plugins/haml/lib/sass/less.rb
@@ -246,24 +246,43 @@ def to_sass_tree
246 246
       private
247 247
 
248 248
       LESS_TO_SASS_OPERATORS = {"-" => :minus, "+" => :plus, "*" => :times, "/" => :div, "=" => :single_eq}
  249
+
249 250
       def _to_sass_tree(arr)
250  
-        return Sass::Script::UnaryOperation.new(_to_sass_tree(arr[1..-1]), :minus) if arr[0] == "-"
251  
-        _to_sass_tree2(*_sass_split(arr))
  251
+        e, rest = _to_sass_tree_plus_minus_eq(arr)
  252
+        until rest.empty?
  253
+          e2, rest = _to_sass_tree_plus_minus_eq(rest)
  254
+          e = Sass::Script::Operation.new(e, e2, :concat)
  255
+        end
  256
+        return e
252 257
       end
253 258
 
254  
-      def _to_sass_tree2(first, rest)
255  
-        return first if rest.empty?
256  
-        if rest[0].is_a?(Operator)
  259
+      def _to_sass_tree_plus_minus_eq(arr)
  260
+        e, rest = _to_sass_tree_times_div(arr)
  261
+        while rest[0] && rest[0].is_a?(Operator) && %w[+ - =].include?(rest[0])
257 262
           op = LESS_TO_SASS_OPERATORS[rest[0]]
258  
-          if op == :times || op == :div
259  
-            second, rest = _sass_split(rest[1..-1])
260  
-            return _to_sass_tree2(Sass::Script::Operation.new(first, second, op), rest)
261  
-          else
262  
-            return Sass::Script::Operation.new(first, _to_sass_tree(rest[1..-1]), op)
263  
-          end
  263
+          e2, rest = _to_sass_tree_times_div(rest[1..-1])
  264
+          e = Sass::Script::Operation.new(e, e2, op)
264 265
         end
  266
+        return e, rest
  267
+      end
265 268
 
266  
-        Sass::Script::Operation.new(first, _to_sass_tree(rest), :concat)
  269
+      def _to_sass_tree_times_div(arr)
  270
+        e, rest = _to_sass_tree_unary(arr)
  271
+        while rest[0] && rest[0].is_a?(Operator) && %w[* /].include?(rest[0])
  272
+          op = LESS_TO_SASS_OPERATORS[rest[0]]
  273
+          e2, rest = _to_sass_tree_unary(rest[1..-1])
  274
+          e = Sass::Script::Operation.new(e, e2, op)
  275
+        end
  276
+        return e, rest
  277
+      end
  278
+
  279
+      def _to_sass_tree_unary(arr)
  280
+        if arr[0] == "-"
  281
+          first, rest = _sass_split(arr[1..-1])
  282
+          return Sass::Script::UnaryOperation.new(first, :minus), rest
  283
+        else
  284
+          return _sass_split(arr[0..-1])
  285
+        end
267 286
       end
268 287
 
269 288
       def _sass_split(arr)
5  vendor/plugins/haml/lib/sass/plugin.rb
@@ -228,7 +228,10 @@ def update_stylesheet(filename, css)
228 228
       # Finally, write the file
229 229
       flag = 'w'
230 230
       flag = 'wb' if Haml::Util.windows? && options[:unix_newlines]
231  
-      File.open(css, flag) {|file| file.print(result)}
  231
+      File.open(css, flag) do |file|
  232
+        file.set_encoding(result.encoding) unless Haml::Util.ruby1_8?
  233
+        file.print(result)
  234
+      end
232 235
     end
233 236
 
234 237
     def try_delete_css(css)
3  vendor/plugins/haml/lib/sass/script/color.rb
@@ -401,7 +401,8 @@ def smallest
401 401
     end
402 402
 
403 403
     def rgba_str
404  
-      "rgba(#{rgb.join(', ')}, #{alpha % 1 == 0.0 ? alpha.to_i : alpha})"
  404
+      split = options[:style] == :compressed ? ',' : ', '
  405
+      "rgba(#{rgb.join(split)}#{split}#{Number.round(alpha)})"
405 406
     end
406 407
 
407 408
     def hex_str
11  vendor/plugins/haml/lib/sass/script/funcall.rb
... ...
@@ -1,4 +1,5 @@
1  
-require File.join(File.dirname(__FILE__), 'functions')
  1
+require 'sass/script/functions'
  2
+
2 3
 module Sass
3 4
   module Script
4 5
     # A SassScript parse node representing a function call.
@@ -63,12 +64,10 @@ def _perform(environment)
63 64
         args = self.args.map {|a| a.perform(environment)}
64 65
         ruby_name = name.gsub('-', '_')
65 66
         unless Haml::Util.has?(:public_instance_method, Functions, ruby_name) && ruby_name !~ /^__/
66  
-          return Script::String.new("#{name}(#{args.map {|a| a.perform(environment)}.join(', ')})")
  67
+          opts(Script::String.new("#{name}(#{args.map {|a| a.perform(environment)}.join(', ')})"))
  68
+        else
  69
+          opts(Functions::EvaluationContext.new(environment.options).send(ruby_name, *args))
67 70
         end
68  
-
69  
-        result = Functions::EvaluationContext.new(environment.options).send(ruby_name, *args)
70  
-        result.options = environment.options
71  
-        return result
72 71
       rescue ArgumentError => e
73 72
         raise e unless e.backtrace.any? {|t| t =~ /:in `(block in )?(#{name}|perform)'$/}
74 73
         raise Sass::SyntaxError.new("#{e.message} for `#{name}'")
2  vendor/plugins/haml/lib/sass/script/interpolation.rb
@@ -64,7 +64,7 @@ def _perform(environment)
64 64
       res << (val.is_a?(Sass::Script::String) ? val.value : val.to_s)
65 65
       res << " " if @after && @whitespace_after
66 66
       res << @after.perform(environment).to_s if @after
67  
-      Sass::Script::String.new(res)
  67
+      opts(Sass::Script::String.new(res))
68 68
     end
69 69
   end
70 70
 end
2  vendor/plugins/haml/lib/sass/script/literal.rb
@@ -117,7 +117,7 @@ def concat(other)
117 117
     # @return [Script::String] A string containing both literals
118 118
     #   separated by `", "`
119 119
     def comma(other)
120  
-      Sass::Script::String.new("#{self.to_s}, #{other.to_s}")
  120
+      Sass::Script::String.new("#{self.to_s},#{' ' unless options[:style] == :compressed}#{other.to_s}")
121 121
     end
122 122
 
123 123
     # The SassScript `=` operation
11  vendor/plugins/haml/lib/sass/script/node.rb
@@ -90,6 +90,8 @@ def dasherize(s, opts)
90 90
     end
91 91
 
92 92
     # Evaluates this node.
  93
+    # Note that all {Literal} objects created within this method
  94
+    # should have their \{#options} attribute set, probably via \{#opts}.
93 95
     #
94 96
     # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
95 97
     # @return [Literal] The SassScript object that is the value of the SassScript
@@ -97,5 +99,14 @@ def dasherize(s, opts)
97 99
     def _perform(environment)
98 100
       raise NotImplementedError.new("All subclasses of Sass::Script::Node must override #_perform.")
99 101
     end
  102
+
  103
+    # Sets the \{#options} field on the given literal and returns it
  104
+    #
  105
+    # @param literal [Literal]
  106
+    # @return [Literal]
  107
+    def opts(literal)
  108
+      literal.options = options
  109
+      literal
  110
+    end
100 111
   end
101 112
 end
21  vendor/plugins/haml/lib/sass/script/number.rb
@@ -248,15 +248,7 @@ def to_s
248 248
     #
249 249
     # @return [String] The representation
250 250
     def inspect(opts = {})
251  
-      value =
252  
-        if self.value.is_a?(Float) && (self.value.infinite? || self.value.nan?)
253  
-          self.value
254  
-        elsif int?
255  
-          self.value.to_i
256  
-        else
257  
-          (self.value * PRECISION).round / PRECISION
258  
-        end
259  
-      "#{value}#{unit_str}"
  251
+      "#{self.class.round(self.value)}#{unit_str}"
260 252
     end
261 253
     alias_method :to_sass, :inspect
262 254
 
@@ -334,6 +326,17 @@ def unit_str
334 326
 
335 327
     private
336 328
 
  329
+    # @private
  330
+    def self.round(num)
  331
+      if num.is_a?(Float) && (num.infinite? || num.nan?)
  332
+        num
  333
+      elsif num % 1 == 0.0
  334
+        num.to_i
  335
+      else
  336
+        (num * PRECISION).round / PRECISION
  337
+      end
  338
+    end
  339
+
337 340
     def operate(other, operation)
338 341
       this = self
339 342
       if [:+, :-, :<=, :<, :>, :>=].include?(operation)
19  vendor/plugins/haml/lib/sass/script/operation.rb
@@ -36,8 +36,8 @@ def inspect
36 36
     # @see Node#to_sass
37 37
     def to_sass(opts = {})
38 38
       pred = Sass::Script::Parser.precedence_of(@operator)
39  
-      o1 = operand_to_sass pred, @operand1, opts
40  
-      o2 = operand_to_sass pred, @operand2, opts
  39
+      o1 = operand_to_sass @operand1, :left, opts
  40
+      o2 = operand_to_sass @operand2, :right, opts
41 41
       sep =
42 42
         case @operator
43 43
         when :comma; ", "
@@ -72,9 +72,7 @@ def _perform(environment)
72 72
       end
73 73
 
74 74
       begin
75  
-        res = literal1.send(@operator, literal2)
76  
-        res.options = environment.options
77  
-        res
  75
+        opts(literal1.send(@operator, literal2))
78 76
       rescue NoMethodError => e
79 77
         raise e unless e.name.to_s == @operator.to_s
80 78
         raise Sass::SyntaxError.new("Undefined operation: \"#{literal1} #{@operator} #{literal2}\".")
@@ -83,9 +81,14 @@ def _perform(environment)
83 81
 
84 82
     private
85 83
 
86  
-    def operand_to_sass(pred, op, opts = {})
87  
-      return "(#{op.to_sass(opts)})" if op.is_a?(Operation) &&
88  
-        Sass::Script::Parser.precedence_of(op.operator) < pred
  84
+    def operand_to_sass(op, side, opts)
  85
+      return op.to_sass(opts) unless op.is_a?(Operation)
  86
+
  87
+      pred = Sass::Script::Parser.precedence_of(@operator)
  88
+      sub_pred = Sass::Script::Parser.precedence_of(op.operator)
  89
+      assoc = Sass::Script::Parser.associative?(@operator)
  90
+      return "(#{op.to_sass(opts)})" if sub_pred < pred ||
  91
+        (side == :right && sub_pred == pred && !assoc)
89 92
       op.to_sass(opts)
90 93
     end
91 94
   end
11  vendor/plugins/haml/lib/sass/script/parser.rb
@@ -128,6 +128,8 @@ def self.parse(*args)
128 128
         [:times, :div, :mod],
129 129
       ]
130 130
 
  131
+      ASSOCIATIVE = [:comma, :concat, :plus, :times]
  132
+
131 133
       class << self
132 134
         # Returns an integer representing the precedence
133 135
         # of the given operator.
@@ -141,6 +143,13 @@ def precedence_of(op)
141 143
           raise "[BUG] Unknown operator #{op}"
142 144
         end
143 145
 
  146
+        # Returns whether or not the given operation is associative.
  147
+        #
  148
+        # @private
  149
+        def associative?(op)
  150
+          ASSOCIATIVE.include?(op)
  151
+        end
  152
+
144 153
         private
145 154
 
146 155
         # Defines a simple left-associative production.
@@ -246,7 +255,7 @@ def ident
246 255
         return if @stop_at && @stop_at.include?(@lexer.peek.value)
247 256
 
248 257
         name = @lexer.next
249  
-        if color = Color::HTML4_COLORS[name.value]
  258
+        if color = Color::HTML4_COLORS[name.value.downcase]
250 259
           return node(Color.new(color))
251 260
         end
252 261
         node(Script::String.new(name.value, :identifier))
2  vendor/plugins/haml/lib/sass/script/string_interpolation.rb
@@ -72,7 +72,7 @@ def _perform(environment)
72 72
       mid = @mid.perform(environment)
73 73
       res << (mid.is_a?(Sass::Script::String) ? mid.value : mid.to_s)
74 74
       res << @after.perform(environment).value
75  
-      Sass::Script::String.new(res, before.type)
  75
+      opts(Sass::Script::String.new(res, before.type))
76 76
     end
77 77
 
78 78
     private
5  vendor/plugins/haml/lib/sass/scss/css_parser.rb
@@ -24,11 +24,6 @@ def interp_string; tok(STRING); end
24 24
       def interp_ident(ident = IDENT); tok(ident); end
25 25
       def use_css_import?; true; end
26 26
 
27  
-      def special_directive(name)
28  
-        return unless name == 'media' || name == 'import'
29  
-        super
30  
-      end
31  
-
32 27
       def block_child(context)
33 28
         case context
34 29
         when :ruleset
10  vendor/plugins/haml/lib/sass/scss/parser.rb
@@ -98,7 +98,8 @@ def process_comment(text, node)
98 98
         node << comment
99 99
       end
100 100
 
101  
-      DIRECTIVES = Set[:mixin, :include, :debug, :warn, :for, :while, :if, :extend, :import, :media]
  101
+      DIRECTIVES = Set[:mixin, :include, :debug, :warn, :for, :while, :if, :extend, :import,
  102
+        :media, :charset]
102 103
 
103 104
       def directive
104 105
         return unless tok(/@/)
@@ -289,6 +290,13 @@ def media_expr
289 290
         true
290 291
       end
291 292
 
  293
+      def charset_directive
  294
+        tok! STRING
  295
+        name = @scanner[1] || @scanner[2]
  296
+        ss
  297
+        node(Sass::Tree::CharsetNode.new(name))
  298
+      end
  299
+
292 300
       def variable
293 301
         return unless tok(/\$/)
294 302
         name = tok!(IDENT)
2  vendor/plugins/haml/lib/sass/scss/static_parser.rb
@@ -32,7 +32,7 @@ def interp_ident(ident = IDENT); s = tok(ident) and [s]; end
32 32
       def use_css_import?; true; end
33 33
 
34 34
       def special_directive(name)
35  
-        return unless name == 'media' || name == 'import'
  35
+        return unless  %w[media import charset].include?(name)
36 36
         super
37 37
       end
38 38
     end
37  vendor/plugins/haml/lib/sass/tree/charset_node.rb
... ...
@@ -0,0 +1,37 @@
  1
+module Sass::Tree
  2
+  # A static node representing an unproccessed Sass `@charset` directive.
  3
+  #
  4
+  # @see Sass::Tree
  5
+  class CharsetNode < Node
  6
+    # The name of the charset.
  7
+    #
  8
+    # @return [String]
  9
+    attr_accessor :name
  10
+
  11
+    # @param name [String] see \{#name}
  12
+    def initialize(name)
  13
+      @name = name
  14
+      super()
  15
+    end
  16
+
  17
+    # @see Node#invisible?
  18
+    def invisible?
  19
+      !Haml::Util.ruby1_8?
  20
+    end
  21
+
  22
+    protected
  23
+
  24
+    # @see Node#to_src
  25
+    def to_src(tabs, opts, fmt)
  26
+      "#{'  ' * tabs}@charset \"#{name}\"#{semi fmt}\n"
  27
+    end
  28
+
  29
+    # Computes the CSS for the directive.
  30
+    #
  31
+    # @param tabs [Fixnum] The level of indentation for the CSS
  32
+    # @return [String] The resulting CSS
  33
+    def _to_s(tabs)
  34
+      "@charset \"#{name}\";"
  35
+    end
  36
+  end
  37
+end
4  vendor/plugins/haml/lib/sass/tree/directive_node.rb
@@ -4,8 +4,8 @@ module Sass::Tree
4 4
   # are handled by their own nodes;
5 5
   # only CSS directives like `@media` and `@font-face` become {DirectiveNode}s.
6 6
   #
7  
-  # `@import` is a bit of a weird case;
8  
-  # it becomes an {ImportNode}.
  7
+  # `@import` and `@charset` are special cases;
  8
+  # they become {ImportNode}s and {CharsetNode}s, respectively.
9 9
   #
10 10
   # @see Sass::Tree
11 11
   class DirectiveNode < Node
36  vendor/plugins/haml/lib/sass/tree/root_node.rb
@@ -47,7 +47,7 @@ def perform(environment)
47 47
       # @return [(Tree::Node, Haml::Util::SubsetMap)] The resulting tree of static nodes
48 48
       #   *and* the extensions defined for this tree
49 49
       def cssize(extends = Haml::Util::SubsetMap.new, parent = nil)
50  
-        return super(extends), extends
  50
+        return super(extends, parent), extends
51 51
       rescue Sass::SyntaxError => e
52 52
         e.sass_template = @template
53 53
         raise e
@@ -106,7 +106,39 @@ def _to_s(*args)
106 106
         end
107 107
         result.rstrip!
108 108
         return "" if result.empty?
109  
-        return result + "\n"
  109
+        result << "\n"
  110
+        unless Haml::Util.ruby1_8? || result.ascii_only?
  111
+          if children.first.is_a?(CharsetNode)
  112
+            begin
  113
+              encoding = children.first.name
  114
+              # Default to big-endian encoding, because we have to decide somehow
  115
+              encoding << 'BE' if encoding =~ /\Autf-(16|32)\Z/i
  116
+              result = result.encode(Encoding.find(encoding))
  117
+            rescue EncodingError
  118
+            end
  119
+          end
  120
+
  121
+          result = "@charset \"#{result.encoding.name}\";#{
  122
+            style == :compressed ? '' : "\n"
  123
+          }".encode(result.encoding) + result
  124
+        end
  125
+        result
  126
+      end
  127
+
  128
+      # In Ruby 1.8, ensures that there's only one @charset directive
  129
+      # and that it's at the top of the document.
  130
+      #
  131
+      # @see Node#cssize
  132
+      def cssize!(extends, parent)
  133
+        super
  134
+
  135
+        # In Ruby 1.9 we can make all @charset nodes invisible
  136
+        # and infer the final @charset from the encoding of the final string.
  137
+        if Haml::Util.ruby1_8? && parent.nil?
  138
+          charset = self.children.find {|c| c.is_a?(CharsetNode)}
  139
+          self.children.reject! {|c| c.is_a?(CharsetNode)}
  140
+          self.children.unshift charset if charset
  141
+        end
110 142
       end
111 143
 
112 144
       # Returns an error message if the given child node is invalid,
2  vendor/plugins/haml/lib/sass/tree/rule_node.rb
@@ -134,7 +134,7 @@ def _to_s(tabs)
134 134
       per_rule_indent, total_indent = [:nested, :expanded].include?(style) ? [rule_indent, ''] : ['', rule_indent]
135 135
 
136 136
       total_rule = total_indent + resolved_rules.members.
137  
-        map {|seq| seq.to_a.join}.
  137
+        map {|seq| seq.to_a.join.gsub(/([^,])\n/m, style == :compressed ? '\1 ' : "\\1\n")}.
138