haml-edge 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. data/EDGE_GEM_VERSION +1 -0
  2. data/FAQ +138 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +332 -0
  5. data/REVISION +1 -0
  6. data/Rakefile +226 -0
  7. data/VERSION +1 -0
  8. data/bin/css2sass +7 -0
  9. data/bin/haml +9 -0
  10. data/bin/html2haml +7 -0
  11. data/bin/sass +8 -0
  12. data/extra/edge_gem_watch.rb +13 -0
  13. data/extra/haml-mode.el +596 -0
  14. data/extra/sass-mode.el +200 -0
  15. data/init.rb +8 -0
  16. data/lib/haml/buffer.rb +255 -0
  17. data/lib/haml/engine.rb +268 -0
  18. data/lib/haml/error.rb +22 -0
  19. data/lib/haml/exec.rb +395 -0
  20. data/lib/haml/filters.rb +275 -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/helpers.rb +488 -0
  24. data/lib/haml/html.rb +222 -0
  25. data/lib/haml/precompiler.rb +904 -0
  26. data/lib/haml/shared.rb +45 -0
  27. data/lib/haml/template/patch.rb +58 -0
  28. data/lib/haml/template/plugin.rb +72 -0
  29. data/lib/haml/template.rb +42 -0
  30. data/lib/haml/util.rb +88 -0
  31. data/lib/haml/version.rb +47 -0
  32. data/lib/haml.rb +1044 -0
  33. data/lib/sass/css.rb +388 -0
  34. data/lib/sass/engine.rb +495 -0
  35. data/lib/sass/environment.rb +46 -0
  36. data/lib/sass/error.rb +35 -0
  37. data/lib/sass/plugin/merb.rb +56 -0
  38. data/lib/sass/plugin/rails.rb +24 -0
  39. data/lib/sass/plugin.rb +204 -0
  40. data/lib/sass/repl.rb +51 -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 +29 -0
  44. data/lib/sass/script/functions.rb +134 -0
  45. data/lib/sass/script/lexer.rb +148 -0
  46. data/lib/sass/script/literal.rb +82 -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 +9 -0
  51. data/lib/sass/script/unary_operation.rb +21 -0
  52. data/lib/sass/script/variable.rb +20 -0
  53. data/lib/sass/script.rb +38 -0
  54. data/lib/sass/tree/attr_node.rb +64 -0
  55. data/lib/sass/tree/comment_node.rb +30 -0
  56. data/lib/sass/tree/debug_node.rb +22 -0
  57. data/lib/sass/tree/directive_node.rb +50 -0
  58. data/lib/sass/tree/file_node.rb +27 -0
  59. data/lib/sass/tree/for_node.rb +29 -0
  60. data/lib/sass/tree/if_node.rb +27 -0
  61. data/lib/sass/tree/mixin_def_node.rb +18 -0
  62. data/lib/sass/tree/mixin_node.rb +35 -0
  63. data/lib/sass/tree/node.rb +99 -0
  64. data/lib/sass/tree/rule_node.rb +161 -0
  65. data/lib/sass/tree/variable_node.rb +24 -0
  66. data/lib/sass/tree/while_node.rb +21 -0
  67. data/lib/sass.rb +1062 -0
  68. data/rails/init.rb +1 -0
  69. data/test/benchmark.rb +99 -0
  70. data/test/haml/engine_test.rb +795 -0
  71. data/test/haml/helper_test.rb +228 -0
  72. data/test/haml/html2haml_test.rb +108 -0
  73. data/test/haml/markaby/standard.mab +52 -0
  74. data/test/haml/mocks/article.rb +6 -0
  75. data/test/haml/results/content_for_layout.xhtml +15 -0
  76. data/test/haml/results/eval_suppressed.xhtml +9 -0
  77. data/test/haml/results/filters.xhtml +62 -0
  78. data/test/haml/results/helpers.xhtml +93 -0
  79. data/test/haml/results/helpful.xhtml +10 -0
  80. data/test/haml/results/just_stuff.xhtml +68 -0
  81. data/test/haml/results/list.xhtml +12 -0
  82. data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
  83. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  84. data/test/haml/results/original_engine.xhtml +20 -0
  85. data/test/haml/results/partial_layout.xhtml +5 -0
  86. data/test/haml/results/partials.xhtml +21 -0
  87. data/test/haml/results/render_layout.xhtml +3 -0
  88. data/test/haml/results/silent_script.xhtml +74 -0
  89. data/test/haml/results/standard.xhtml +162 -0
  90. data/test/haml/results/tag_parsing.xhtml +23 -0
  91. data/test/haml/results/very_basic.xhtml +5 -0
  92. data/test/haml/results/whitespace_handling.xhtml +89 -0
  93. data/test/haml/rhtml/_av_partial_1.rhtml +12 -0
  94. data/test/haml/rhtml/_av_partial_2.rhtml +8 -0
  95. data/test/haml/rhtml/action_view.rhtml +62 -0
  96. data/test/haml/rhtml/standard.rhtml +54 -0
  97. data/test/haml/template_test.rb +204 -0
  98. data/test/haml/templates/_av_partial_1.haml +9 -0
  99. data/test/haml/templates/_av_partial_1_ugly.haml +9 -0
  100. data/test/haml/templates/_av_partial_2.haml +5 -0
  101. data/test/haml/templates/_av_partial_2_ugly.haml +5 -0
  102. data/test/haml/templates/_layout.erb +3 -0
  103. data/test/haml/templates/_layout_for_partial.haml +3 -0
  104. data/test/haml/templates/_partial.haml +8 -0
  105. data/test/haml/templates/_text_area.haml +3 -0
  106. data/test/haml/templates/action_view.haml +47 -0
  107. data/test/haml/templates/action_view_ugly.haml +47 -0
  108. data/test/haml/templates/breakage.haml +8 -0
  109. data/test/haml/templates/content_for_layout.haml +10 -0
  110. data/test/haml/templates/eval_suppressed.haml +11 -0
  111. data/test/haml/templates/filters.haml +66 -0
  112. data/test/haml/templates/helpers.haml +95 -0
  113. data/test/haml/templates/helpful.haml +11 -0
  114. data/test/haml/templates/just_stuff.haml +83 -0
  115. data/test/haml/templates/list.haml +12 -0
  116. data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
  117. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  118. data/test/haml/templates/original_engine.haml +17 -0
  119. data/test/haml/templates/partial_layout.haml +3 -0
  120. data/test/haml/templates/partialize.haml +1 -0
  121. data/test/haml/templates/partials.haml +12 -0
  122. data/test/haml/templates/render_layout.haml +2 -0
  123. data/test/haml/templates/silent_script.haml +40 -0
  124. data/test/haml/templates/standard.haml +42 -0
  125. data/test/haml/templates/standard_ugly.haml +42 -0
  126. data/test/haml/templates/tag_parsing.haml +21 -0
  127. data/test/haml/templates/very_basic.haml +4 -0
  128. data/test/haml/templates/whitespace_handling.haml +87 -0
  129. data/test/haml/util_test.rb +87 -0
  130. data/test/linked_rails.rb +12 -0
  131. data/test/sass/css2sass_test.rb +193 -0
  132. data/test/sass/engine_test.rb +709 -0
  133. data/test/sass/functions_test.rb +109 -0
  134. data/test/sass/more_results/more1.css +9 -0
  135. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  136. data/test/sass/more_results/more_import.css +29 -0
  137. data/test/sass/more_templates/_more_partial.sass +2 -0
  138. data/test/sass/more_templates/more1.sass +23 -0
  139. data/test/sass/more_templates/more_import.sass +11 -0
  140. data/test/sass/plugin_test.rb +213 -0
  141. data/test/sass/results/alt.css +4 -0
  142. data/test/sass/results/basic.css +9 -0
  143. data/test/sass/results/compact.css +5 -0
  144. data/test/sass/results/complex.css +87 -0
  145. data/test/sass/results/compressed.css +1 -0
  146. data/test/sass/results/expanded.css +19 -0
  147. data/test/sass/results/import.css +29 -0
  148. data/test/sass/results/line_numbers.css +49 -0
  149. data/test/sass/results/mixins.css +95 -0
  150. data/test/sass/results/multiline.css +24 -0
  151. data/test/sass/results/nested.css +22 -0
  152. data/test/sass/results/parent_ref.css +13 -0
  153. data/test/sass/results/script.css +16 -0
  154. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  155. data/test/sass/results/subdir/subdir.css +3 -0
  156. data/test/sass/results/units.css +11 -0
  157. data/test/sass/script_test.rb +250 -0
  158. data/test/sass/templates/_partial.sass +2 -0
  159. data/test/sass/templates/alt.sass +16 -0
  160. data/test/sass/templates/basic.sass +23 -0
  161. data/test/sass/templates/bork.sass +2 -0
  162. data/test/sass/templates/bork2.sass +2 -0
  163. data/test/sass/templates/compact.sass +17 -0
  164. data/test/sass/templates/complex.sass +309 -0
  165. data/test/sass/templates/compressed.sass +15 -0
  166. data/test/sass/templates/expanded.sass +17 -0
  167. data/test/sass/templates/import.sass +11 -0
  168. data/test/sass/templates/importee.sass +19 -0
  169. data/test/sass/templates/line_numbers.sass +13 -0
  170. data/test/sass/templates/mixins.sass +76 -0
  171. data/test/sass/templates/multiline.sass +20 -0
  172. data/test/sass/templates/nested.sass +25 -0
  173. data/test/sass/templates/parent_ref.sass +25 -0
  174. data/test/sass/templates/script.sass +101 -0
  175. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  176. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  177. data/test/sass/templates/subdir/subdir.sass +6 -0
  178. data/test/sass/templates/units.sass +11 -0
  179. data/test/test_helper.rb +21 -0
  180. metadata +278 -0
@@ -0,0 +1,596 @@
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
+ (require 'ruby-mode)
25
+
26
+ ;; User definable variables
27
+
28
+ (defgroup haml nil
29
+ "Support for the Haml template language."
30
+ :group 'languages
31
+ :prefix "haml-")
32
+
33
+ (defcustom haml-mode-hook nil
34
+ "Hook run when entering Haml mode."
35
+ :type 'hook
36
+ :group 'haml)
37
+
38
+ (defcustom haml-indent-offset 2
39
+ "Amount of offset per level of indentation."
40
+ :type 'integer
41
+ :group 'haml)
42
+
43
+ (defcustom haml-backspace-backdents-nesting t
44
+ "Non-nil to have `haml-electric-backspace' re-indent all code
45
+ nested beneath the backspaced line be re-indented along with the
46
+ line itself."
47
+ :type 'boolean
48
+ :group 'haml)
49
+
50
+ (defface haml-tab-face
51
+ '((((class color)) (:background "hotpink"))
52
+ (t (:reverse-video t)))
53
+ "Face to use for highlighting tabs in Haml files."
54
+ :group 'faces
55
+ :group 'haml)
56
+
57
+ (defvar haml-indent-function 'haml-indent-p
58
+ "This function should look at the current line and return t
59
+ if the next line could be nested within this line.
60
+
61
+ The function can also return a positive integer to indicate
62
+ a specific level to which the current line could be indented.")
63
+
64
+ (defvar haml-block-openers
65
+ `("^ *\\([%\\.#][a-z0-9_:\\-]*\\)+\\({.*}\\)?\\(\\[.*\\]\\)?[><]*[ \t]*$"
66
+ "^ *[&!]?[-=~].*do[ \t]*\\(|.*|[ \t]*\\)?$"
67
+ ,(concat "^ *[&!][-=~][ \t]*\\("
68
+ (regexp-opt '("if" "unless" "while" "until" "else"
69
+ "begin" "elsif" "rescue" "ensure" "when"))
70
+ "\\)")
71
+ "^ */\\(\\[.*\\]\\)?[ \t]*$"
72
+ "^ *-#"
73
+ "^ *:")
74
+ "A list of regexps that match lines of Haml that could have
75
+ text nested beneath them.")
76
+
77
+ ;; Font lock
78
+
79
+ (defun haml-nested-regexp (re)
80
+ (concat "^\\( *\\)" re "\\(\n\\(?:\\(?:\\1 .*\\| *\\)\n\\)*\\(?:\\1 .*\\| *\\)?\\)?"))
81
+
82
+ (defconst haml-font-lock-keywords
83
+ `((,(haml-nested-regexp "\\(?:-#\\|/\\).*") 0 font-lock-comment-face)
84
+ (,(haml-nested-regexp ":\\w+") 0 font-lock-string-face)
85
+ (haml-highlight-interpolation 1 font-lock-variable-name-face prepend)
86
+ (haml-highlight-ruby-tag 1 font-lock-preprocessor-face)
87
+ (haml-highlight-ruby-script 1 font-lock-preprocessor-face)
88
+ ("^ *\\(\t\\)" 1 'haml-tab-face)
89
+ ("^!!!.*" 0 font-lock-constant-face)
90
+ ("| *$" 0 font-lock-string-face)))
91
+
92
+ (defconst haml-filter-re "^ *:\\w+")
93
+ (defconst haml-comment-re "^ *\\(?:-\\#\\|/\\)")
94
+
95
+ (defun haml-fontify-region-as-ruby (beg end)
96
+ "Use Ruby's font-lock variables to fontify the region between BEG and END."
97
+ (save-excursion
98
+ (save-match-data
99
+ (let ((font-lock-keywords ruby-font-lock-keywords)
100
+ (font-lock-syntactic-keywords ruby-font-lock-syntactic-keywords)
101
+ font-lock-keywords-only
102
+ font-lock-extend-region-functions
103
+ font-lock-keywords-case-fold-search)
104
+ ;; font-lock-fontify-region apparently isn't inclusive,
105
+ ;; so we have to move the beginning back one char
106
+ (font-lock-fontify-region (- beg 1) end)))))
107
+
108
+ (defun haml-highlight-ruby-script (limit)
109
+ "Highlight a Ruby script expression (-, =, or ~)."
110
+ (when (re-search-forward "^ *\\(-\\|[&!]?[=~]\\) \\(.*\\)$" limit t)
111
+ (haml-fontify-region-as-ruby (match-beginning 2) (match-end 2))))
112
+
113
+ (defun haml-highlight-ruby-tag (limit)
114
+ "Highlight Ruby code within a Haml tag.
115
+
116
+ This highlights the tag attributes and object refs of the tag,
117
+ as well as the script expression (-, =, or ~) following the tag.
118
+
119
+ For example, this will highlight all of the following:
120
+ %p{:foo => 'bar'}
121
+ %p[@bar]
122
+ %p= 'baz'
123
+ %p{:foo => 'bar'}[@bar]= 'baz'"
124
+ (when (re-search-forward "^ *[%.#]" limit t)
125
+ (let ((eol (save-excursion (end-of-line) (point))))
126
+ (forward-char -1)
127
+
128
+ ;; Highlight tag, classes, and ids
129
+ (while (looking-at "[.#%][a-z0-9_:\\-]*")
130
+ (put-text-property (match-beginning 0) (match-end 0) 'face
131
+ (case (char-after)
132
+ (?% font-lock-function-name-face)
133
+ (?# font-lock-keyword-face)
134
+ (?. font-lock-type-face)))
135
+ (goto-char (match-end 0)))
136
+
137
+ ;; Highlight obj refs
138
+ (when (eq (char-after) ?\[)
139
+ (let ((beg (point)))
140
+ (haml-limited-forward-sexp eol)
141
+ (haml-fontify-region-as-ruby beg (point))))
142
+
143
+ ;; Highlight attr hashes
144
+ (when (eq (char-after) ?\{)
145
+ (let ((beg (+ 1 (point))))
146
+ (haml-limited-forward-sexp eol)
147
+
148
+ ;; Check for multiline
149
+ (while (and (eolp) (eq (char-before) ?,))
150
+ (forward-line)
151
+ (let ((eol (save-excursion (end-of-line) (point))))
152
+ ;; If no sexps are closed,
153
+ ;; we're still continuing a multiline hash
154
+ (if (>= (car (parse-partial-sexp (point) eol)) 0)
155
+ (end-of-line)
156
+ ;; If sexps have been closed,
157
+ ;; set the point at the end of the total sexp
158
+ (goto-char beg)
159
+ (haml-limited-forward-sexp eol))))
160
+
161
+ (haml-fontify-region-as-ruby beg (point))))
162
+
163
+ ;; Move past end chars
164
+ (when (looking-at "[<>&!]+") (goto-char (match-end 0)))
165
+ ;; Highlight script
166
+ (if (looking-at "\\([=~]\\) \\(.*\\)$")
167
+ (haml-fontify-region-as-ruby (match-beginning 2) (match-end 2))
168
+ ;; Give font-lock something to highlight
169
+ (forward-char -1)
170
+ (looking-at "\\(\\)"))
171
+ t)))
172
+
173
+ (defun haml-highlight-interpolation (limit)
174
+ "Highlight Ruby interpolation (#{foo})."
175
+ (when (re-search-forward "\\(#{\\)" limit t)
176
+ (save-match-data
177
+ (forward-char -1)
178
+ (let ((beg (point)))
179
+ (haml-limited-forward-sexp limit)
180
+ (haml-fontify-region-as-ruby (+ 1 beg) (point)))
181
+
182
+ (when (eq (char-before) ?})
183
+ (put-text-property (- (point) 1) (point)
184
+ 'face font-lock-variable-name-face))
185
+ t)))
186
+
187
+ (defun haml-limited-forward-sexp (limit &optional arg)
188
+ "Move forward using `forward-sexp' or to limit,
189
+ whichever comes first."
190
+ (let (forward-sexp-function)
191
+ (condition-case err
192
+ (save-restriction
193
+ (narrow-to-region (point) limit)
194
+ (forward-sexp arg))
195
+ (scan-error
196
+ (unless (equal (nth 1 err) "Unbalanced parentheses")
197
+ (signal 'scan-error (cdr err)))
198
+ (goto-char limit)))))
199
+
200
+ (defun* haml-extend-region-filters-comments ()
201
+ "Extend the font-lock region to encompass filters and comments."
202
+ (let ((old-beg font-lock-beg)
203
+ (old-end font-lock-end))
204
+ (save-excursion
205
+ (goto-char font-lock-beg)
206
+ (beginning-of-line)
207
+ (unless (or (looking-at haml-filter-re)
208
+ (looking-at haml-comment-re))
209
+ (return-from haml-extend-region-filters-comments))
210
+ (setq font-lock-beg (point))
211
+ (haml-forward-sexp)
212
+ (beginning-of-line)
213
+ (setq font-lock-end (max font-lock-end (point))))
214
+ (or (/= old-beg font-lock-beg)
215
+ (/= old-end font-lock-end))))
216
+
217
+ (defun* haml-extend-region-multiline-hashes ()
218
+ "Extend the font-lock region to encompass multiline attribute hashes."
219
+ (let ((old-beg font-lock-beg)
220
+ (old-end font-lock-end))
221
+ (save-excursion
222
+ (goto-char font-lock-beg)
223
+ (let ((attr-props (haml-parse-multiline-attr-hash))
224
+ multiline-end)
225
+ (when attr-props
226
+ (setq font-lock-beg (cdr (assq 'point attr-props)))
227
+
228
+ (end-of-line)
229
+ ;; Move through multiline attrs
230
+ (when (eq (char-before) ?,)
231
+ (save-excursion
232
+ (while (progn (end-of-line) (eq (char-before) ?,))
233
+ (forward-line))
234
+
235
+ (forward-line -1)
236
+ (end-of-line)
237
+ (setq multiline-end (point))))
238
+
239
+ (goto-char (+ (cdr (assq 'point attr-props))
240
+ (cdr (assq 'hash-indent attr-props))
241
+ -1))
242
+ (haml-limited-forward-sexp
243
+ (or multiline-end
244
+ (save-excursion (end-of-line) (point))))
245
+ (setq font-lock-end (max font-lock-end (point))))))
246
+ (or (/= old-beg font-lock-beg)
247
+ (/= old-end font-lock-end))))
248
+
249
+
250
+ ;; Mode setup
251
+
252
+ (defvar haml-mode-syntax-table
253
+ (let ((table (make-syntax-table)))
254
+ (modify-syntax-entry ?: "." table)
255
+ (modify-syntax-entry ?_ "w" table)
256
+ table)
257
+ "Syntax table in use in haml-mode buffers.")
258
+
259
+ (defvar haml-mode-map
260
+ (let ((map (make-sparse-keymap)))
261
+ (define-key map [backspace] 'haml-electric-backspace)
262
+ (define-key map "\C-?" 'haml-electric-backspace)
263
+ (define-key map "\C-c\C-f" 'haml-forward-sexp)
264
+ (define-key map "\C-c\C-b" 'haml-backward-sexp)
265
+ (define-key map "\C-c\C-u" 'haml-up-list)
266
+ (define-key map "\C-c\C-d" 'haml-down-list)
267
+ (define-key map "\C-c\C-k" 'haml-kill-line-and-indent)
268
+ (define-key map "\C-c\C-r" 'haml-output-region)
269
+ (define-key map "\C-c\C-l" 'haml-output-buffer)
270
+ map))
271
+
272
+ ;;;###autoload
273
+ (define-derived-mode haml-mode fundamental-mode "Haml"
274
+ "Major mode for editing Haml files.
275
+
276
+ \\{haml-mode-map}"
277
+ (set-syntax-table haml-mode-syntax-table)
278
+ (add-to-list 'font-lock-extend-region-functions 'haml-extend-region-filters-comments)
279
+ (add-to-list 'font-lock-extend-region-functions 'haml-extend-region-multiline-hashes)
280
+ (set (make-local-variable 'font-lock-multiline) t)
281
+ (set (make-local-variable 'indent-line-function) 'haml-indent-line)
282
+ (set (make-local-variable 'indent-region-function) 'haml-indent-region)
283
+ (set (make-local-variable 'parse-sexp-lookup-properties) t)
284
+ (setq comment-start "-#")
285
+ (setq indent-tabs-mode nil)
286
+ (setq font-lock-defaults '((haml-font-lock-keywords) t t)))
287
+
288
+ ;; Useful functions
289
+
290
+ (defun haml-comment-block ()
291
+ "Comment the current block of Haml code."
292
+ (interactive)
293
+ (save-excursion
294
+ (let ((indent (current-indentation)))
295
+ (back-to-indentation)
296
+ (insert "-#")
297
+ (newline)
298
+ (indent-to indent)
299
+ (beginning-of-line)
300
+ (haml-mark-sexp)
301
+ (haml-reindent-region-by haml-indent-offset))))
302
+
303
+ (defun haml-uncomment-block ()
304
+ "Uncomment the current block of Haml code."
305
+ (interactive)
306
+ (save-excursion
307
+ (beginning-of-line)
308
+ (while (not (looking-at haml-comment-re))
309
+ (haml-up-list)
310
+ (beginning-of-line))
311
+ (haml-mark-sexp)
312
+ (kill-line 1)
313
+ (haml-reindent-region-by (- haml-indent-offset))))
314
+
315
+ (defun haml-replace-region (start end)
316
+ "Replaces the current block of Haml code with the HTML equivalent."
317
+ (interactive "r")
318
+ (save-excursion
319
+ (goto-char end)
320
+ (setq end (point-marker))
321
+ (goto-char start)
322
+ (let ((ci (current-indentation)))
323
+ (while (re-search-forward "^ +" end t)
324
+ (replace-match (make-string (- (current-indentation) ci) ? ))))
325
+ (shell-command-on-region start end "haml" "haml-output" t)))
326
+
327
+ (defun haml-output-region (start end)
328
+ "Displays the HTML output for the current block of Haml code."
329
+ (interactive "r")
330
+ (kill-new (buffer-substring start end))
331
+ (with-temp-buffer
332
+ (yank)
333
+ (haml-indent-region (point-min) (point-max))
334
+ (shell-command-on-region (point-min) (point-max) "haml" "haml-output")))
335
+
336
+ (defun haml-output-buffer ()
337
+ "Displays the HTML output for entire buffer."
338
+ (interactive)
339
+ (haml-output-region (point-min) (point-max)))
340
+
341
+ ;; Navigation
342
+
343
+ (defun haml-forward-through-whitespace (&optional backward)
344
+ "Move the point forward at least one line, until it reaches
345
+ either the end of the buffer or a line with no whitespace.
346
+
347
+ If `backward' is non-nil, move the point backward instead."
348
+ (let ((arg (if backward -1 1))
349
+ (endp (if backward 'bobp 'eobp)))
350
+ (loop do (forward-line arg)
351
+ while (and (not (funcall endp))
352
+ (looking-at "^[ \t]*$")))))
353
+
354
+ (defun haml-at-indent-p ()
355
+ "Returns whether or not the point is at the first
356
+ non-whitespace character in a line or whitespace preceding that
357
+ character."
358
+ (let ((opoint (point)))
359
+ (save-excursion
360
+ (back-to-indentation)
361
+ (>= (point) opoint))))
362
+
363
+ (defun haml-forward-sexp (&optional arg)
364
+ "Move forward across one nested expression.
365
+ With `arg', do it that many times. Negative arg -N means move
366
+ backward across N balanced expressions.
367
+
368
+ A sexp in Haml is defined as a line of Haml code as well as any
369
+ lines nested beneath it."
370
+ (interactive "p")
371
+ (or arg (setq arg 1))
372
+ (if (and (< arg 0) (not (haml-at-indent-p)))
373
+ (back-to-indentation)
374
+ (while (/= arg 0)
375
+ (let ((indent (current-indentation)))
376
+ (loop do (haml-forward-through-whitespace (< arg 0))
377
+ while (and (not (eobp))
378
+ (not (bobp))
379
+ (> (current-indentation) indent)))
380
+ (back-to-indentation)
381
+ (setq arg (+ arg (if (> arg 0) -1 1)))))))
382
+
383
+ (defun haml-backward-sexp (&optional arg)
384
+ "Move backward across one nested expression.
385
+ With ARG, do it that many times. Negative arg -N means move
386
+ forward across N balanced expressions.
387
+
388
+ A sexp in Haml is defined as a line of Haml code as well as any
389
+ lines nested beneath it."
390
+ (interactive "p")
391
+ (haml-forward-sexp (if arg (- arg) -1)))
392
+
393
+ (defun haml-up-list (&optional arg)
394
+ "Move out of one level of nesting.
395
+ With ARG, do this that many times."
396
+ (interactive "p")
397
+ (or arg (setq arg 1))
398
+ (while (> arg 0)
399
+ (let ((indent (current-indentation)))
400
+ (loop do (haml-forward-through-whitespace t)
401
+ while (and (not (bobp))
402
+ (>= (current-indentation) indent)))
403
+ (setq arg (- arg 1))))
404
+ (back-to-indentation))
405
+
406
+ (defun haml-down-list (&optional arg)
407
+ "Move down one level of nesting.
408
+ With ARG, do this that many times."
409
+ (interactive "p")
410
+ (or arg (setq arg 1))
411
+ (while (> arg 0)
412
+ (let ((indent (current-indentation)))
413
+ (haml-forward-through-whitespace)
414
+ (when (<= (current-indentation) indent)
415
+ (haml-forward-through-whitespace t)
416
+ (back-to-indentation)
417
+ (error "Nothing is nested beneath this line"))
418
+ (setq arg (- arg 1))))
419
+ (back-to-indentation))
420
+
421
+ (defun haml-mark-sexp ()
422
+ "Marks the next Haml block."
423
+ (let ((forward-sexp-function 'haml-forward-sexp))
424
+ (mark-sexp)))
425
+
426
+ (defun haml-mark-sexp-but-not-next-line ()
427
+ "Marks the next Haml block, but puts the mark at the end of the
428
+ last line of the sexp rather than the first non-whitespace
429
+ character of the next line."
430
+ (haml-mark-sexp)
431
+ (set-mark
432
+ (save-excursion
433
+ (goto-char (mark))
434
+ (forward-line -1)
435
+ (end-of-line)
436
+ (point))))
437
+
438
+ ;; Indentation and electric keys
439
+
440
+ (defun* haml-indent-p ()
441
+ "Returns t if the current line can have lines nested beneath it."
442
+ (let ((attr-props (haml-parse-multiline-attr-hash)))
443
+ (when attr-props
444
+ (end-of-line)
445
+ (return-from haml-indent-p
446
+ (if (eq (char-before) ?,) (cdr (assq 'hash-indent attr-props))
447
+ (beginning-of-line)
448
+ (+ (cdr (assq 'indent attr-props)) haml-indent-offset)))))
449
+ (loop for opener in haml-block-openers
450
+ if (looking-at opener) return t
451
+ finally return nil))
452
+
453
+ (defun* haml-parse-multiline-attr-hash ()
454
+ "Parses a multiline attribute hash, and returns
455
+ an alist with the following keys:
456
+
457
+ INDENT is the indentation of the line beginning the hash.
458
+
459
+ HASH-INDENT is the indentation of the first character
460
+ within the attribute hash.
461
+
462
+ POINT is the character position at the beginning of the line
463
+ beginning the hash."
464
+ (save-excursion
465
+ (while t
466
+ (beginning-of-line)
467
+ (if (looking-at "^ *\\(?:[.#%][a-z0-9_:\\-]+\\)+{")
468
+ (progn
469
+ (goto-char (- (match-end 0) 1))
470
+ (haml-limited-forward-sexp (save-excursion (end-of-line) (point)))
471
+ (return-from haml-parse-multiline-attr-hash
472
+ (if (eq (char-before) ?,)
473
+ `((indent . ,(current-indentation))
474
+ (hash-indent . ,(- (match-end 0) (match-beginning 0)))
475
+ (point . ,(match-beginning 0)))
476
+ nil)))
477
+ (forward-line -1)
478
+ (end-of-line)
479
+ (when (not (eq (char-before) ?,))
480
+ (return-from haml-parse-multiline-attr-hash nil))))))
481
+
482
+ (defun haml-compute-indentation ()
483
+ "Calculate the maximum sensible indentation for the current line."
484
+ (save-excursion
485
+ (beginning-of-line)
486
+ (if (bobp) 0
487
+ (haml-forward-through-whitespace t)
488
+ (let ((indent (funcall haml-indent-function)))
489
+ (cond
490
+ ((integerp indent) indent)
491
+ (indent (+ (current-indentation) haml-indent-offset))
492
+ (t (current-indentation)))))))
493
+
494
+ (defun haml-indent-region (start end)
495
+ "Indent each nonblank line in the region.
496
+ This is done by indenting the first line based on
497
+ `haml-compute-indentation' and preserving the relative
498
+ indentation of the rest of the region.
499
+
500
+ If this command is used multiple times in a row, it will cycle
501
+ between possible indentations."
502
+ (save-excursion
503
+ (goto-char end)
504
+ (setq end (point-marker))
505
+ (goto-char start)
506
+ (let (this-line-column current-column
507
+ (next-line-column
508
+ (if (and (equal last-command this-command) (/= (current-indentation) 0))
509
+ (* (/ (- (current-indentation) 1) haml-indent-offset) haml-indent-offset)
510
+ (haml-compute-indentation))))
511
+ (while (< (point) end)
512
+ (setq this-line-column next-line-column
513
+ current-column (current-indentation))
514
+ ;; Delete whitespace chars at beginning of line
515
+ (delete-horizontal-space)
516
+ (unless (eolp)
517
+ (setq next-line-column (save-excursion
518
+ (loop do (forward-line 1)
519
+ while (and (not (eobp)) (looking-at "^[ \t]*$")))
520
+ (+ this-line-column
521
+ (- (current-indentation) current-column))))
522
+ ;; Don't indent an empty line
523
+ (unless (eolp) (indent-to this-line-column)))
524
+ (forward-line 1)))
525
+ (move-marker end nil)))
526
+
527
+ (defun haml-indent-line ()
528
+ "Indent the current line.
529
+ The first time this command is used, the line will be indented to the
530
+ maximum sensible indentation. Each immediately subsequent usage will
531
+ back-dent the line by `haml-indent-offset' spaces. On reaching column
532
+ 0, it will cycle back to the maximum sensible indentation."
533
+ (interactive "*")
534
+ (let ((ci (current-indentation))
535
+ (cc (current-column))
536
+ (need (haml-compute-indentation)))
537
+ (save-excursion
538
+ (beginning-of-line)
539
+ (delete-horizontal-space)
540
+ (if (and (equal last-command this-command) (/= ci 0))
541
+ (indent-to (* (/ (- ci 1) haml-indent-offset) haml-indent-offset))
542
+ (indent-to need)))
543
+ (if (< (current-column) (current-indentation))
544
+ (forward-to-indentation 0))))
545
+
546
+ (defun haml-reindent-region-by (n)
547
+ "Add N spaces to the beginning of each line in the region.
548
+ If N is negative, will remove the spaces instead. Assumes all
549
+ lines in the region have indentation >= that of the first line."
550
+ (let ((ci (current-indentation)))
551
+ (save-excursion
552
+ (replace-regexp (concat "^" (make-string ci ? ))
553
+ (make-string (max 0 (+ ci n)) ? )
554
+ nil (point) (mark)))))
555
+
556
+ (defun haml-electric-backspace (arg)
557
+ "Delete characters or back-dent the current line.
558
+ If invoked following only whitespace on a line, will back-dent
559
+ the line and all nested lines to the immediately previous
560
+ multiple of `haml-indent-offset' spaces.
561
+
562
+ Set `haml-backspace-backdents-nesting' to nil to just back-dent
563
+ the current line."
564
+ (interactive "*p")
565
+ (if (or (/= (current-indentation) (current-column))
566
+ (bolp)
567
+ (looking-at "^[ \t]+$"))
568
+ (backward-delete-char arg)
569
+ (save-excursion
570
+ (let ((ci (current-column)))
571
+ (beginning-of-line)
572
+ (if haml-backspace-backdents-nesting
573
+ (haml-mark-sexp-but-not-next-line)
574
+ (set-mark (save-excursion (end-of-line) (point))))
575
+ (haml-reindent-region-by (* (- arg) haml-indent-offset))
576
+ (back-to-indentation)
577
+ (pop-mark)))))
578
+
579
+ (defun haml-kill-line-and-indent ()
580
+ "Kill the current line, and re-indent all lines nested beneath it."
581
+ (interactive)
582
+ (beginning-of-line)
583
+ (haml-mark-sexp-but-not-next-line)
584
+ (kill-line 1)
585
+ (haml-reindent-region-by (* -1 haml-indent-offset)))
586
+
587
+ (defun haml-indent-string ()
588
+ "Return the indentation string for `haml-indent-offset'."
589
+ (mapconcat 'identity (make-list haml-indent-offset " ") ""))
590
+
591
+ ;;;###autoload
592
+ (add-to-list 'auto-mode-alist '("\\.haml$" . haml-mode))
593
+
594
+ ;; Setup/Activation
595
+ (provide 'haml-mode)
596
+ ;;; haml-mode.el ends here