merbjedi-haml 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. data/FAQ +138 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +332 -0
  4. data/REVISION +1 -0
  5. data/Rakefile +184 -0
  6. data/VERSION +1 -0
  7. data/bin/css2sass +7 -0
  8. data/bin/haml +9 -0
  9. data/bin/html2haml +7 -0
  10. data/bin/sass +8 -0
  11. data/extra/haml-mode.el +434 -0
  12. data/extra/sass-mode.el +98 -0
  13. data/init.rb +8 -0
  14. data/lib/haml.rb +1025 -0
  15. data/lib/haml/buffer.rb +255 -0
  16. data/lib/haml/engine.rb +268 -0
  17. data/lib/haml/error.rb +22 -0
  18. data/lib/haml/exec.rb +395 -0
  19. data/lib/haml/filters.rb +276 -0
  20. data/lib/haml/helpers.rb +465 -0
  21. data/lib/haml/helpers/action_view_extensions.rb +45 -0
  22. data/lib/haml/helpers/action_view_mods.rb +181 -0
  23. data/lib/haml/html.rb +218 -0
  24. data/lib/haml/precompiler.rb +896 -0
  25. data/lib/haml/shared.rb +45 -0
  26. data/lib/haml/template.rb +51 -0
  27. data/lib/haml/template/patch.rb +58 -0
  28. data/lib/haml/template/plugin.rb +72 -0
  29. data/lib/haml/util.rb +77 -0
  30. data/lib/haml/version.rb +47 -0
  31. data/lib/sass.rb +1062 -0
  32. data/lib/sass/css.rb +388 -0
  33. data/lib/sass/engine.rb +501 -0
  34. data/lib/sass/environment.rb +33 -0
  35. data/lib/sass/error.rb +35 -0
  36. data/lib/sass/plugin.rb +203 -0
  37. data/lib/sass/plugin/merb.rb +56 -0
  38. data/lib/sass/plugin/rails.rb +24 -0
  39. data/lib/sass/repl.rb +44 -0
  40. data/lib/sass/script.rb +38 -0
  41. data/lib/sass/script/bool.rb +13 -0
  42. data/lib/sass/script/color.rb +97 -0
  43. data/lib/sass/script/funcall.rb +28 -0
  44. data/lib/sass/script/functions.rb +122 -0
  45. data/lib/sass/script/lexer.rb +144 -0
  46. data/lib/sass/script/literal.rb +60 -0
  47. data/lib/sass/script/number.rb +231 -0
  48. data/lib/sass/script/operation.rb +30 -0
  49. data/lib/sass/script/parser.rb +142 -0
  50. data/lib/sass/script/string.rb +42 -0
  51. data/lib/sass/script/unary_operation.rb +21 -0
  52. data/lib/sass/script/variable.rb +20 -0
  53. data/lib/sass/tree/attr_node.rb +64 -0
  54. data/lib/sass/tree/comment_node.rb +30 -0
  55. data/lib/sass/tree/debug_node.rb +22 -0
  56. data/lib/sass/tree/directive_node.rb +50 -0
  57. data/lib/sass/tree/file_node.rb +27 -0
  58. data/lib/sass/tree/for_node.rb +29 -0
  59. data/lib/sass/tree/if_node.rb +27 -0
  60. data/lib/sass/tree/mixin_def_node.rb +18 -0
  61. data/lib/sass/tree/mixin_node.rb +34 -0
  62. data/lib/sass/tree/node.rb +97 -0
  63. data/lib/sass/tree/rule_node.rb +120 -0
  64. data/lib/sass/tree/variable_node.rb +24 -0
  65. data/lib/sass/tree/while_node.rb +20 -0
  66. data/rails/init.rb +1 -0
  67. data/test/benchmark.rb +99 -0
  68. data/test/haml/engine_test.rb +852 -0
  69. data/test/haml/helper_test.rb +224 -0
  70. data/test/haml/html2haml_test.rb +92 -0
  71. data/test/haml/markaby/standard.mab +52 -0
  72. data/test/haml/mocks/article.rb +6 -0
  73. data/test/haml/results/content_for_layout.xhtml +15 -0
  74. data/test/haml/results/eval_suppressed.xhtml +9 -0
  75. data/test/haml/results/filters.xhtml +62 -0
  76. data/test/haml/results/helpers.xhtml +93 -0
  77. data/test/haml/results/helpful.xhtml +10 -0
  78. data/test/haml/results/just_stuff.xhtml +68 -0
  79. data/test/haml/results/list.xhtml +12 -0
  80. data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
  81. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  82. data/test/haml/results/original_engine.xhtml +20 -0
  83. data/test/haml/results/partial_layout.xhtml +5 -0
  84. data/test/haml/results/partials.xhtml +21 -0
  85. data/test/haml/results/render_layout.xhtml +3 -0
  86. data/test/haml/results/silent_script.xhtml +74 -0
  87. data/test/haml/results/standard.xhtml +42 -0
  88. data/test/haml/results/tag_parsing.xhtml +23 -0
  89. data/test/haml/results/very_basic.xhtml +5 -0
  90. data/test/haml/results/whitespace_handling.xhtml +89 -0
  91. data/test/haml/rhtml/_av_partial_1.rhtml +12 -0
  92. data/test/haml/rhtml/_av_partial_2.rhtml +8 -0
  93. data/test/haml/rhtml/action_view.rhtml +62 -0
  94. data/test/haml/rhtml/standard.rhtml +54 -0
  95. data/test/haml/template_test.rb +204 -0
  96. data/test/haml/templates/_av_partial_1.haml +9 -0
  97. data/test/haml/templates/_av_partial_1_ugly.haml +9 -0
  98. data/test/haml/templates/_av_partial_2.haml +5 -0
  99. data/test/haml/templates/_av_partial_2_ugly.haml +5 -0
  100. data/test/haml/templates/_layout.erb +3 -0
  101. data/test/haml/templates/_layout_for_partial.haml +3 -0
  102. data/test/haml/templates/_partial.haml +8 -0
  103. data/test/haml/templates/_text_area.haml +3 -0
  104. data/test/haml/templates/action_view.haml +47 -0
  105. data/test/haml/templates/action_view_ugly.haml +47 -0
  106. data/test/haml/templates/breakage.haml +8 -0
  107. data/test/haml/templates/content_for_layout.haml +10 -0
  108. data/test/haml/templates/eval_suppressed.haml +11 -0
  109. data/test/haml/templates/filters.haml +66 -0
  110. data/test/haml/templates/helpers.haml +95 -0
  111. data/test/haml/templates/helpful.haml +11 -0
  112. data/test/haml/templates/just_stuff.haml +83 -0
  113. data/test/haml/templates/list.haml +12 -0
  114. data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
  115. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  116. data/test/haml/templates/original_engine.haml +17 -0
  117. data/test/haml/templates/partial_layout.haml +3 -0
  118. data/test/haml/templates/partialize.haml +1 -0
  119. data/test/haml/templates/partials.haml +12 -0
  120. data/test/haml/templates/render_layout.haml +2 -0
  121. data/test/haml/templates/silent_script.haml +40 -0
  122. data/test/haml/templates/standard.haml +42 -0
  123. data/test/haml/templates/standard_ugly.haml +42 -0
  124. data/test/haml/templates/tag_parsing.haml +21 -0
  125. data/test/haml/templates/very_basic.haml +4 -0
  126. data/test/haml/templates/whitespace_handling.haml +87 -0
  127. data/test/linked_rails.rb +12 -0
  128. data/test/sass/css2sass_test.rb +193 -0
  129. data/test/sass/engine_test.rb +752 -0
  130. data/test/sass/functions_test.rb +96 -0
  131. data/test/sass/more_results/more1.css +9 -0
  132. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  133. data/test/sass/more_results/more_import.css +29 -0
  134. data/test/sass/more_templates/_more_partial.sass +2 -0
  135. data/test/sass/more_templates/more1.sass +23 -0
  136. data/test/sass/more_templates/more_import.sass +11 -0
  137. data/test/sass/plugin_test.rb +208 -0
  138. data/test/sass/results/alt.css +4 -0
  139. data/test/sass/results/basic.css +9 -0
  140. data/test/sass/results/compact.css +5 -0
  141. data/test/sass/results/complex.css +87 -0
  142. data/test/sass/results/compressed.css +1 -0
  143. data/test/sass/results/expanded.css +19 -0
  144. data/test/sass/results/import.css +29 -0
  145. data/test/sass/results/line_numbers.css +49 -0
  146. data/test/sass/results/mixins.css +95 -0
  147. data/test/sass/results/multiline.css +24 -0
  148. data/test/sass/results/nested.css +22 -0
  149. data/test/sass/results/parent_ref.css +13 -0
  150. data/test/sass/results/script.css +16 -0
  151. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  152. data/test/sass/results/subdir/subdir.css +3 -0
  153. data/test/sass/results/units.css +11 -0
  154. data/test/sass/script_test.rb +152 -0
  155. data/test/sass/templates/_partial.sass +2 -0
  156. data/test/sass/templates/alt.sass +16 -0
  157. data/test/sass/templates/basic.sass +23 -0
  158. data/test/sass/templates/bork.sass +2 -0
  159. data/test/sass/templates/bork2.sass +2 -0
  160. data/test/sass/templates/compact.sass +17 -0
  161. data/test/sass/templates/complex.sass +309 -0
  162. data/test/sass/templates/compressed.sass +15 -0
  163. data/test/sass/templates/expanded.sass +17 -0
  164. data/test/sass/templates/import.sass +11 -0
  165. data/test/sass/templates/importee.sass +19 -0
  166. data/test/sass/templates/line_numbers.sass +13 -0
  167. data/test/sass/templates/mixins.sass +76 -0
  168. data/test/sass/templates/multiline.sass +20 -0
  169. data/test/sass/templates/nested.sass +25 -0
  170. data/test/sass/templates/parent_ref.sass +25 -0
  171. data/test/sass/templates/script.sass +101 -0
  172. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  173. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  174. data/test/sass/templates/subdir/subdir.sass +6 -0
  175. data/test/sass/templates/units.sass +11 -0
  176. data/test/test_helper.rb +21 -0
  177. metadata +273 -0
@@ -0,0 +1 @@
1
+ (unknown)
@@ -0,0 +1,184 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ # ----- Benchmarking -----
5
+
6
+ desc <<END
7
+ Benchmark haml against ERb.
8
+ TIMES=n sets the number of runs. Defaults to 1000.
9
+ END
10
+ task :benchmark do
11
+ sh "ruby test/benchmark.rb #{ENV['TIMES']}"
12
+ end
13
+
14
+ # ----- Default: Testing ------
15
+
16
+ if ENV["RUN_CODE_RUN"] == "true"
17
+ task :default => :"test:rails_compatibility"
18
+ else
19
+ task :default => :test
20
+ end
21
+
22
+ require 'rake/testtask'
23
+
24
+ Rake::TestTask.new do |t|
25
+ t.libs << 'lib'
26
+ test_files = FileList['test/**/*_test.rb']
27
+ test_files.exclude('test/rails/*')
28
+ t.test_files = test_files
29
+ t.verbose = true
30
+ end
31
+ Rake::Task[:test].send(:add_comment, <<END)
32
+ To run with an alternate version of Rails, make test/rails a symlink to that version.
33
+ END
34
+
35
+ # ----- Packaging -----
36
+
37
+ require 'rake/gempackagetask'
38
+ load 'haml.gemspec'
39
+
40
+ Rake::GemPackageTask.new(HAML_GEMSPEC) do |pkg|
41
+ if Rake.application.top_level_tasks.include?('release')
42
+ pkg.need_tar_gz = true
43
+ pkg.need_tar_bz2 = true
44
+ pkg.need_zip = true
45
+ end
46
+ end
47
+
48
+ task :revision_file do
49
+ require 'lib/haml'
50
+
51
+ if Haml.version[:rev] && !Rake.application.top_level_tasks.include?('release')
52
+ File.open('REVISION', 'w') { |f| f.puts Haml.version[:rev] }
53
+ elsif Rake.application.top_level_tasks.include?('release')
54
+ File.open('REVISION', 'w') { |f| f.puts "(release)" }
55
+ else
56
+ File.open('REVISION', 'w') { |f| f.puts "(unknown)" }
57
+ end
58
+ end
59
+ Rake::Task[:package].prerequisites.insert(0, :revision_file)
60
+
61
+ # We also need to get rid of this file after packaging.
62
+ at_exit { File.delete('REVISION') rescue nil }
63
+
64
+ desc "Install Haml as a gem."
65
+ task :install => [:package] do
66
+ sudo = RUBY_PLATFORM =~ /win32/ ? '' : 'sudo'
67
+ sh %{#{sudo} gem install --no-ri pkg/haml-#{File.read('VERSION').strip}}
68
+ end
69
+
70
+ desc "Release a new Haml package to Rubyforge. Requires the NAME and VERSION flags."
71
+ task :release => [:package] do
72
+ name, version = ENV['NAME'], ENV['VERSION']
73
+ raise "Must supply NAME and VERSION for release task." unless name && version
74
+ sh %{rubyforge login}
75
+ sh %{rubyforge add_release haml haml "#{name} (v#{version})" pkg/haml-#{version}.gem}
76
+ sh %{rubyforge add_file haml haml "#{name} (v#{version})" pkg/haml-#{version}.tar.gz}
77
+ sh %{rubyforge add_file haml haml "#{name} (v#{version})" pkg/haml-#{version}.tar.bz2}
78
+ sh %{rubyforge add_file haml haml "#{name} (v#{version})" pkg/haml-#{version}.zip}
79
+ end
80
+
81
+ # ----- Documentation -----
82
+
83
+ begin
84
+ require 'hanna/rdoctask'
85
+ rescue LoadError
86
+ require 'rake/rdoctask'
87
+ end
88
+
89
+ Rake::RDocTask.new do |rdoc|
90
+ rdoc.title = 'Haml/Sass'
91
+ rdoc.options << '--line-numbers' << '--inline-source'
92
+ rdoc.rdoc_files.include(*FileList.new('*') do |list|
93
+ list.exclude(/(^|[^.a-z])[a-z]+/)
94
+ list.exclude('TODO')
95
+ end.to_a)
96
+ rdoc.rdoc_files.include('lib/**/*.rb')
97
+ rdoc.rdoc_files.exclude('TODO')
98
+ rdoc.rdoc_files.exclude('lib/haml/buffer.rb')
99
+ rdoc.rdoc_files.exclude('lib/sass/tree/*')
100
+ rdoc.rdoc_dir = 'rdoc'
101
+ rdoc.main = 'README.rdoc'
102
+ end
103
+
104
+ # ----- Coverage -----
105
+
106
+ begin
107
+ require 'rcov/rcovtask'
108
+
109
+ Rcov::RcovTask.new do |t|
110
+ t.test_files = FileList['test/**/*_test.rb']
111
+ t.rcov_opts << '-x' << '"^\/"'
112
+ if ENV['NON_NATIVE']
113
+ t.rcov_opts << "--no-rcovrt"
114
+ end
115
+ t.verbose = true
116
+ end
117
+ rescue LoadError; end
118
+
119
+ # ----- Profiling -----
120
+
121
+ begin
122
+ require 'ruby-prof'
123
+
124
+ desc <<END
125
+ Run a profile of haml.
126
+ ENGINE=str sets the engine to be profiled. Defaults to Haml.
127
+ TIMES=n sets the number of runs. Defaults to 1000.
128
+ FILE=str sets the file to profile.
129
+ Defaults to 'standard' for Haml and 'complex' for Sass.
130
+ OUTPUT=str sets the ruby-prof output format.
131
+ Can be Flat, CallInfo, or Graph. Defaults to Flat. Defaults to Flat.
132
+ END
133
+ task :profile do
134
+ engine = (ENV['ENGINE'] || 'haml').downcase
135
+ times = (ENV['TIMES'] || '1000').to_i
136
+ file = ENV['FILE']
137
+
138
+ if engine == 'sass'
139
+ require 'lib/sass'
140
+
141
+ file = File.read("#{File.dirname(__FILE__)}/test/sass/templates/#{file || 'complex'}.sass")
142
+ result = RubyProf.profile { times.times { Sass::Engine.new(file).render } }
143
+ else
144
+ require 'lib/haml'
145
+
146
+ file = File.read("#{File.dirname(__FILE__)}/test/haml/templates/#{file || 'standard'}.haml")
147
+ obj = Object.new
148
+ Haml::Engine.new(file).def_method(obj, :render)
149
+ result = RubyProf.profile { times.times { obj.render } }
150
+ end
151
+
152
+ RubyProf.const_get("#{(ENV['OUTPUT'] || 'Flat').capitalize}Printer").new(result).print
153
+ end
154
+ rescue LoadError; end
155
+
156
+ # ----- Testing Multiple Rails Versions -----
157
+
158
+ rails_versions = [
159
+ "v2.3.0",
160
+ "v2.2.2",
161
+ "v2.1.2",
162
+ "v2.0.5"
163
+ ]
164
+
165
+ namespace :test do
166
+ desc "Test all supported versions of rails. This takes a while."
167
+ task :rails_compatibility do
168
+ `rm -rf test/rails`
169
+ puts "Checking out rails. Please wait."
170
+ `git clone git://github.com/rails/rails.git test/rails` rescue nil
171
+ begin
172
+ rails_versions.each do |version|
173
+ Dir.chdir "test/rails" do
174
+ `git checkout #{version}`
175
+ end
176
+ puts "Testing Rails #{version}"
177
+ Rake::Task['test'].reenable
178
+ Rake::Task['test'].execute
179
+ end
180
+ ensure
181
+ `rm -rf test/rails`
182
+ end
183
+ end
184
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.1.0
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/haml'
4
+ require 'haml/exec'
5
+
6
+ opts = Haml::Exec::CSS2Sass.new(ARGV)
7
+ opts.parse!
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # The command line Haml parser.
3
+
4
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
5
+ require 'haml'
6
+ require 'haml/exec'
7
+
8
+ opts = Haml::Exec::Haml.new(ARGV)
9
+ opts.parse!
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/haml'
4
+ require 'haml/exec'
5
+
6
+ opts = Haml::Exec::HTML2Haml.new(ARGV)
7
+ opts.parse!
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # The command line Sass parser.
3
+
4
+ require File.dirname(__FILE__) + '/../lib/haml'
5
+ require 'haml/exec'
6
+
7
+ opts = Haml::Exec::Sass.new(ARGV)
8
+ opts.parse!
@@ -0,0 +1,434 @@
1
+ ;;; haml-mode.el --- Major mode for editing Haml files
2
+
3
+ ;; Copyright (c) 2007, 2008 Nathan Weizenbaum
4
+
5
+ ;; Author: Nathan Weizenbaum
6
+ ;; URL: http://github.com/nex3/haml/tree/master
7
+ ;; Version: 1.0
8
+ ;; Keywords: markup, language
9
+
10
+ ;;; Commentary:
11
+
12
+ ;; Because Haml's indentation schema is similar
13
+ ;; to that of YAML and Python, many indentation-related
14
+ ;; functions are similar to those in yaml-mode and python-mode.
15
+
16
+ ;; To install, save this on your load path and add the following to
17
+ ;; your .emacs file:
18
+ ;;
19
+ ;; (require 'haml-mode)
20
+
21
+ ;;; Code:
22
+
23
+ (eval-when-compile (require 'cl))
24
+
25
+ ;; User definable variables
26
+
27
+ (defgroup haml nil
28
+ "Support for the Haml template language."
29
+ :group 'languages
30
+ :prefix "haml-")
31
+
32
+ (defcustom haml-mode-hook nil
33
+ "Hook run when entering Haml mode."
34
+ :type 'hook
35
+ :group 'haml)
36
+
37
+ (defcustom haml-indent-offset 2
38
+ "Amount of offset per level of indentation."
39
+ :type 'integer
40
+ :group 'haml)
41
+
42
+ (defcustom haml-backspace-backdents-nesting t
43
+ "Non-nil to have `haml-electric-backspace' re-indent all code
44
+ nested beneath the backspaced line be re-indented along with the
45
+ line itself."
46
+ :type 'boolean
47
+ :group 'haml)
48
+
49
+ (defface haml-tab-face
50
+ '((((class color)) (:background "hotpink"))
51
+ (t (:reverse-video t)))
52
+ "Face to use for highlighting tabs in Haml files."
53
+ :group 'faces
54
+ :group 'haml)
55
+
56
+ (defvar haml-indent-function 'haml-indent-p
57
+ "This function should look at the current line and return true
58
+ if the next line could be nested within this line.")
59
+
60
+ (defvar haml-block-openers
61
+ `("^ *\\([%\\.#][^ \t]*\\)\\(\\[.*\\]\\)?\\({.*}\\)?\\(\\[.*\\]\\)?[ \t]*$"
62
+ "^ *[-=].*do[ \t]*\\(|.*|[ \t]*\\)?$"
63
+ ,(concat "^ *-[ \t]*\\("
64
+ (regexp-opt '("if" "unless" "while" "until" "else"
65
+ "begin" "elsif" "rescue" "ensure" "when"))
66
+ "\\)")
67
+ "^ */\\(\\[.*\\]\\)?[ \t]*$"
68
+ "^ *-#"
69
+ "^ *:")
70
+ "A list of regexps that match lines of Haml that could have
71
+ text nested beneath them.")
72
+
73
+ ;; Font lock
74
+
75
+ (defun haml-nested-regexp (re)
76
+ (concat "^\\( *\\)" re "\n\\(?:\\(?:\\1 .*\\| *\\)\n\\)*"))
77
+
78
+ (defconst haml-font-lock-keywords
79
+ `((,(haml-nested-regexp "-#.*") 0 font-lock-comment-face)
80
+ (,(haml-nested-regexp ":\\w+") 0 font-lock-string-face)
81
+ ("^ *\\(\t\\)" 1 'haml-tab-face)
82
+ ("^!!!.*" 0 font-lock-constant-face)
83
+ ("\\('[^']*'\\)" 1 font-lock-string-face append)
84
+ ("\\(\"[^\"]*\"\\)" 1 font-lock-string-face append)
85
+ ("@[a-z0-9_]+" 0 font-lock-variable-name-face append)
86
+ ("| *$" 0 font-lock-string-face)
87
+ ("^[ \t]*\\(/.*\\)$" 1 font-lock-comment-face append)
88
+ ("^ *\\(#[a-z0-9_]+\/?\\)" 1 font-lock-keyword-face)
89
+ ("^ *\\(\\.[a-z0-9_]+\/?\\)" 1 font-lock-type-face)
90
+ ("^ *\\(%[a-z0-9_]+\/?\\)" 1 font-lock-function-name-face)
91
+ ("^ *\\(#[a-z0-9_]+\/?\\)" (1 font-lock-keyword-face)
92
+ ("\\.[a-z0-9_]+" nil nil (0 font-lock-type-face)))
93
+ ("^ *\\(\\.[a-z0-9_]+\/?\\)" (1 font-lock-type-face)
94
+ ("\\.[a-z0-9_]+" nil nil (0 font-lock-type-face)))
95
+ ("^ *\\(\\.[a-z0-9_]+\/?\\)" (1 font-lock-type-face)
96
+ ("\\#[a-z0-9_]+" nil nil (0 font-lock-keyword-face)))
97
+ ("^ *\\(%[a-z0-9_]+\/?\\)" (1 font-lock-function-name-face)
98
+ ("\\.[a-z0-9_]+" nil nil (0 font-lock-type-face)))
99
+ ("^ *\\(%[a-z0-9_]+\/?\\)" (1 font-lock-function-name-face)
100
+ ("\\#[a-z0-9_]+" nil nil (0 font-lock-keyword-face)))
101
+ ("^ *\\([~=-] .*\\)" 1 font-lock-preprocessor-face prepend)
102
+ ("^ *[\\.#%a-z0-9_]+\\([~=-] .*\\)" 1 font-lock-preprocessor-face prepend)
103
+ ("^ *[\\.#%a-z0-9_]+\\({[^}]+}\\)" 1 font-lock-preprocessor-face prepend)
104
+ ("^ *[\\.#%a-z0-9_]+\\(\\[[^]]+\\]\\)" 1 font-lock-preprocessor-face prepend)))
105
+
106
+ (defconst haml-filter-re "^ *\\(:\\)\\w+")
107
+ (defconst haml-comment-re "^ *\\(-\\)\\#")
108
+
109
+ (defun* haml-extend-region ()
110
+ "Extend the font-lock region to encompass filters and comments."
111
+ (let ((old-beg font-lock-beg)
112
+ (old-end font-lock-end))
113
+ (save-excursion
114
+ (goto-char font-lock-beg)
115
+ (beginning-of-line)
116
+ (unless (or (looking-at haml-filter-re)
117
+ (looking-at haml-comment-re))
118
+ (return-from haml-extend-region))
119
+ (setq font-lock-beg (point))
120
+ (haml-forward-sexp)
121
+ (beginning-of-line)
122
+ (setq font-lock-end (max font-lock-end (point))))
123
+ (or (/= old-beg font-lock-beg)
124
+ (/= old-end font-lock-end))))
125
+
126
+
127
+ ;; Mode setup
128
+
129
+ (defvar haml-mode-syntax-table
130
+ (let ((table (make-syntax-table)))
131
+ (modify-syntax-entry ?: "." table)
132
+ (modify-syntax-entry ?_ "w" table)
133
+ table)
134
+ "Syntax table in use in haml-mode buffers.")
135
+
136
+ (defvar haml-mode-map
137
+ (let ((map (make-sparse-keymap)))
138
+ (define-key map [backspace] 'haml-electric-backspace)
139
+ (define-key map "\C-?" 'haml-electric-backspace)
140
+ (define-key map "\C-c\C-f" 'haml-forward-sexp)
141
+ (define-key map "\C-c\C-b" 'haml-backward-sexp)
142
+ (define-key map "\C-c\C-u" 'haml-up-list)
143
+ (define-key map "\C-c\C-d" 'haml-down-list)
144
+ (define-key map "\C-c\C-k" 'haml-kill-line-and-indent)
145
+ (define-key map "\C-c\C-r" 'haml-output-region)
146
+ (define-key map "\C-c\C-l" 'haml-output-buffer)
147
+ map))
148
+
149
+ ;;;###autoload
150
+ (define-derived-mode haml-mode fundamental-mode "Haml"
151
+ "Major mode for editing Haml files.
152
+
153
+ \\{haml-mode-map}"
154
+ (set-syntax-table haml-mode-syntax-table)
155
+ (add-to-list 'font-lock-extend-region-functions 'haml-extend-region)
156
+ (set (make-local-variable 'font-lock-multiline) t)
157
+ (set (make-local-variable 'indent-line-function) 'haml-indent-line)
158
+ (set (make-local-variable 'indent-region-function) 'haml-indent-region)
159
+ (set (make-local-variable 'parse-sexp-lookup-properties) t)
160
+ (setq comment-start "-#")
161
+ (setq indent-tabs-mode nil)
162
+ (setq font-lock-defaults '((haml-font-lock-keywords) nil t)))
163
+
164
+ ;; Useful functions
165
+
166
+ (defun haml-comment-block ()
167
+ "Comment the current block of Haml code."
168
+ (interactive)
169
+ (save-excursion
170
+ (let ((indent (current-indentation)))
171
+ (back-to-indentation)
172
+ (insert "-#")
173
+ (newline)
174
+ (indent-to indent)
175
+ (beginning-of-line)
176
+ (haml-mark-sexp)
177
+ (haml-reindent-region-by haml-indent-offset))))
178
+
179
+ (defun haml-uncomment-block ()
180
+ "Uncomment the current block of Haml code."
181
+ (interactive)
182
+ (save-excursion
183
+ (beginning-of-line)
184
+ (while (not (looking-at haml-comment-re))
185
+ (haml-up-list)
186
+ (beginning-of-line))
187
+ (haml-mark-sexp)
188
+ (kill-line 1)
189
+ (haml-reindent-region-by (- haml-indent-offset))))
190
+
191
+ (defun haml-replace-region (start end)
192
+ "Replaces the current block of Haml code with the HTML equivalent."
193
+ (interactive "r")
194
+ (save-excursion
195
+ (goto-char end)
196
+ (setq end (point-marker))
197
+ (goto-char start)
198
+ (let ((ci (current-indentation)))
199
+ (while (re-search-forward "^ +" end t)
200
+ (replace-match (make-string (- (current-indentation) ci) ? ))))
201
+ (shell-command-on-region start end "haml" "haml-output" t)))
202
+
203
+ (defun haml-output-region (start end)
204
+ "Displays the HTML output for the current block of Haml code."
205
+ (interactive "r")
206
+ (kill-new (buffer-substring start end))
207
+ (with-temp-buffer
208
+ (yank)
209
+ (haml-indent-region (point-min) (point-max))
210
+ (shell-command-on-region (point-min) (point-max) "haml" "haml-output")))
211
+
212
+ (defun haml-output-buffer ()
213
+ "Displays the HTML output for entire buffer."
214
+ (interactive)
215
+ (haml-output-region (point-min) (point-max)))
216
+
217
+ ;; Navigation
218
+
219
+ (defun haml-forward-through-whitespace (&optional backward)
220
+ "Move the point forward at least one line, until it reaches
221
+ either the end of the buffer or a line with no whitespace.
222
+
223
+ If `backward' is non-nil, move the point backward instead."
224
+ (let ((arg (if backward -1 1))
225
+ (endp (if backward 'bobp 'eobp)))
226
+ (loop do (forward-line arg)
227
+ while (and (not (funcall endp))
228
+ (looking-at "^[ \t]*$")))))
229
+
230
+ (defun haml-at-indent-p ()
231
+ "Returns whether or not the point is at the first
232
+ non-whitespace character in a line or whitespace preceding that
233
+ character."
234
+ (let ((opoint (point)))
235
+ (save-excursion
236
+ (back-to-indentation)
237
+ (>= (point) opoint))))
238
+
239
+ (defun haml-forward-sexp (&optional arg)
240
+ "Move forward across one nested expression.
241
+ With `arg', do it that many times. Negative arg -N means move
242
+ backward across N balanced expressions.
243
+
244
+ A sexp in Haml is defined as a line of Haml code as well as any
245
+ lines nested beneath it."
246
+ (interactive "p")
247
+ (or arg (setq arg 1))
248
+ (if (and (< arg 0) (not (haml-at-indent-p)))
249
+ (back-to-indentation)
250
+ (while (/= arg 0)
251
+ (let ((indent (current-indentation)))
252
+ (loop do (haml-forward-through-whitespace (< arg 0))
253
+ while (and (not (eobp))
254
+ (not (bobp))
255
+ (> (current-indentation) indent)))
256
+ (back-to-indentation)
257
+ (setq arg (+ arg (if (> arg 0) -1 1)))))))
258
+
259
+ (defun haml-backward-sexp (&optional arg)
260
+ "Move backward across one nested expression.
261
+ With ARG, do it that many times. Negative arg -N means move
262
+ forward across N balanced expressions.
263
+
264
+ A sexp in Haml is defined as a line of Haml code as well as any
265
+ lines nested beneath it."
266
+ (interactive "p")
267
+ (haml-forward-sexp (if arg (- arg) -1)))
268
+
269
+ (defun haml-up-list (&optional arg)
270
+ "Move out of one level of nesting.
271
+ With ARG, do this that many times."
272
+ (interactive "p")
273
+ (or arg (setq arg 1))
274
+ (while (> arg 0)
275
+ (let ((indent (current-indentation)))
276
+ (loop do (haml-forward-through-whitespace t)
277
+ while (and (not (bobp))
278
+ (>= (current-indentation) indent)))
279
+ (setq arg (- arg 1))))
280
+ (back-to-indentation))
281
+
282
+ (defun haml-down-list (&optional arg)
283
+ "Move down one level of nesting.
284
+ With ARG, do this that many times."
285
+ (interactive "p")
286
+ (or arg (setq arg 1))
287
+ (while (> arg 0)
288
+ (let ((indent (current-indentation)))
289
+ (haml-forward-through-whitespace)
290
+ (when (<= (current-indentation) indent)
291
+ (haml-forward-through-whitespace t)
292
+ (back-to-indentation)
293
+ (error "Nothing is nested beneath this line"))
294
+ (setq arg (- arg 1))))
295
+ (back-to-indentation))
296
+
297
+ (defun haml-mark-sexp ()
298
+ "Marks the next Haml block."
299
+ (let ((forward-sexp-function 'haml-forward-sexp))
300
+ (mark-sexp)))
301
+
302
+ (defun haml-mark-sexp-but-not-next-line ()
303
+ "Marks the next Haml block, but puts the mark at the end of the
304
+ last line of the sexp rather than the first non-whitespace
305
+ character of the next line."
306
+ (haml-mark-sexp)
307
+ (set-mark
308
+ (save-excursion
309
+ (goto-char (mark))
310
+ (forward-line -1)
311
+ (end-of-line)
312
+ (point))))
313
+
314
+ ;; Indentation and electric keys
315
+
316
+ (defun haml-indent-p ()
317
+ "Returns true if the current line can have lines nested beneath it."
318
+ (loop for opener in haml-block-openers
319
+ if (looking-at opener) return t
320
+ finally return nil))
321
+
322
+ (defun haml-compute-indentation ()
323
+ "Calculate the maximum sensible indentation for the current line."
324
+ (save-excursion
325
+ (beginning-of-line)
326
+ (if (bobp) 0
327
+ (haml-forward-through-whitespace t)
328
+ (+ (current-indentation)
329
+ (if (funcall haml-indent-function) haml-indent-offset
330
+ 0)))))
331
+
332
+ (defun haml-indent-region (start end)
333
+ "Indent each nonblank line in the region.
334
+ This is done by indenting the first line based on
335
+ `haml-compute-indentation' and preserving the relative
336
+ indentation of the rest of the region.
337
+
338
+ If this command is used multiple times in a row, it will cycle
339
+ between possible indentations."
340
+ (save-excursion
341
+ (goto-char end)
342
+ (setq end (point-marker))
343
+ (goto-char start)
344
+ (let (this-line-column current-column
345
+ (next-line-column
346
+ (if (and (equal last-command this-command) (/= (current-indentation) 0))
347
+ (* (/ (- (current-indentation) 1) haml-indent-offset) haml-indent-offset)
348
+ (haml-compute-indentation))))
349
+ (while (< (point) end)
350
+ (setq this-line-column next-line-column
351
+ current-column (current-indentation))
352
+ ;; Delete whitespace chars at beginning of line
353
+ (delete-horizontal-space)
354
+ (unless (eolp)
355
+ (setq next-line-column (save-excursion
356
+ (loop do (forward-line 1)
357
+ while (and (not (eobp)) (looking-at "^[ \t]*$")))
358
+ (+ this-line-column
359
+ (- (current-indentation) current-column))))
360
+ ;; Don't indent an empty line
361
+ (unless (eolp) (indent-to this-line-column)))
362
+ (forward-line 1)))
363
+ (move-marker end nil)))
364
+
365
+ (defun haml-indent-line ()
366
+ "Indent the current line.
367
+ The first time this command is used, the line will be indented to the
368
+ maximum sensible indentation. Each immediately subsequent usage will
369
+ back-dent the line by `haml-indent-offset' spaces. On reaching column
370
+ 0, it will cycle back to the maximum sensible indentation."
371
+ (interactive "*")
372
+ (let ((ci (current-indentation))
373
+ (cc (current-column))
374
+ (need (haml-compute-indentation)))
375
+ (save-excursion
376
+ (beginning-of-line)
377
+ (delete-horizontal-space)
378
+ (if (and (equal last-command this-command) (/= ci 0))
379
+ (indent-to (* (/ (- ci 1) haml-indent-offset) haml-indent-offset))
380
+ (indent-to need)))
381
+ (if (< (current-column) (current-indentation))
382
+ (forward-to-indentation 0))))
383
+
384
+ (defun haml-reindent-region-by (n)
385
+ "Add N spaces to the beginning of each line in the region.
386
+ If N is negative, will remove the spaces instead. Assumes all
387
+ lines in the region have indentation >= that of the first line."
388
+ (let ((ci (current-indentation)))
389
+ (save-excursion
390
+ (replace-regexp (concat "^" (make-string ci ? ))
391
+ (make-string (max 0 (+ ci n)) ? )
392
+ nil (point) (mark)))))
393
+
394
+ (defun haml-electric-backspace (arg)
395
+ "Delete characters or back-dent the current line.
396
+ If invoked following only whitespace on a line, will back-dent
397
+ the line and all nested lines to the immediately previous
398
+ multiple of `haml-indent-offset' spaces.
399
+
400
+ Set `haml-backspace-backdents-nesting' to nil to just back-dent
401
+ the current line."
402
+ (interactive "*p")
403
+ (if (or (/= (current-indentation) (current-column))
404
+ (bolp)
405
+ (looking-at "^[ \t]+$"))
406
+ (backward-delete-char arg)
407
+ (save-excursion
408
+ (let ((ci (current-column)))
409
+ (beginning-of-line)
410
+ (if haml-backspace-backdents-nesting
411
+ (haml-mark-sexp-but-not-next-line)
412
+ (set-mark (save-excursion (end-of-line) (point))))
413
+ (haml-reindent-region-by (* (- arg) haml-indent-offset))
414
+ (back-to-indentation)
415
+ (pop-mark)))))
416
+
417
+ (defun haml-kill-line-and-indent ()
418
+ "Kill the current line, and re-indent all lines nested beneath it."
419
+ (interactive)
420
+ (beginning-of-line)
421
+ (haml-mark-sexp-but-not-next-line)
422
+ (kill-line 1)
423
+ (haml-reindent-region-by (* -1 haml-indent-offset)))
424
+
425
+ (defun haml-indent-string ()
426
+ "Return the indentation string for `haml-indent-offset'."
427
+ (mapconcat 'identity (make-list haml-indent-offset " ") ""))
428
+
429
+ ;;;###autoload
430
+ (add-to-list 'auto-mode-alist '("\\.haml$" . haml-mode))
431
+
432
+ ;; Setup/Activation
433
+ (provide 'haml-mode)
434
+ ;;; haml-mode.el ends here