hamlet 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +25 -0
  3. data/LICENSE +21 -0
  4. data/README.md +52 -0
  5. data/Rakefile +57 -0
  6. data/bin/hamletrb +7 -0
  7. data/extra/slim-mode.el +409 -0
  8. data/extra/test.hamlet +50 -0
  9. data/hamlet.gemspec +36 -0
  10. data/lib/hamlet.rb +7 -0
  11. data/lib/hamlet/engine.rb +3 -0
  12. data/lib/hamlet/parser.rb +147 -0
  13. data/lib/hamlet/template.rb +19 -0
  14. data/test/rails/Rakefile +7 -0
  15. data/test/rails/app/controllers/application_controller.rb +3 -0
  16. data/test/rails/app/controllers/parents_controller.rb +84 -0
  17. data/test/rails/app/controllers/slim_controller.rb +32 -0
  18. data/test/rails/app/helpers/application_helper.rb +2 -0
  19. data/test/rails/app/models/child.rb +3 -0
  20. data/test/rails/app/models/parent.rb +4 -0
  21. data/test/rails/app/views/layouts/application.html.slim +10 -0
  22. data/test/rails/app/views/parents/_form.html.slim +7 -0
  23. data/test/rails/app/views/parents/edit.html.slim +1 -0
  24. data/test/rails/app/views/parents/new.html.slim +1 -0
  25. data/test/rails/app/views/parents/show.html.slim +5 -0
  26. data/test/rails/app/views/slim/_partial.html.slim +1 -0
  27. data/test/rails/app/views/slim/content_for.html.slim +7 -0
  28. data/test/rails/app/views/slim/erb.html.erb +1 -0
  29. data/test/rails/app/views/slim/integers.html.slim +1 -0
  30. data/test/rails/app/views/slim/nil.html.slim +1 -0
  31. data/test/rails/app/views/slim/no_layout.html.slim +1 -0
  32. data/test/rails/app/views/slim/normal.html.slim +1 -0
  33. data/test/rails/app/views/slim/partial.html.slim +2 -0
  34. data/test/rails/app/views/slim/variables.html.slim +1 -0
  35. data/test/rails/config.ru +4 -0
  36. data/test/rails/config/application.rb +45 -0
  37. data/test/rails/config/boot.rb +10 -0
  38. data/test/rails/config/database.yml +4 -0
  39. data/test/rails/config/environment.rb +5 -0
  40. data/test/rails/config/environments/development.rb +26 -0
  41. data/test/rails/config/environments/production.rb +49 -0
  42. data/test/rails/config/environments/test.rb +35 -0
  43. data/test/rails/config/initializers/backtrace_silencers.rb +7 -0
  44. data/test/rails/config/initializers/inflections.rb +10 -0
  45. data/test/rails/config/initializers/mime_types.rb +5 -0
  46. data/test/rails/config/initializers/secret_token.rb +7 -0
  47. data/test/rails/config/initializers/session_store.rb +8 -0
  48. data/test/rails/config/locales/en.yml +5 -0
  49. data/test/rails/config/routes.rb +60 -0
  50. data/test/rails/db/migrate/20101220223037_parents_and_children.rb +17 -0
  51. data/test/rails/script/rails +6 -0
  52. data/test/rails/test/helper.rb +10 -0
  53. data/test/rails/test/test_slim.rb +77 -0
  54. data/test/slim/helper.rb +200 -0
  55. data/test/slim/test_chain_manipulation.rb +42 -0
  56. data/test/slim/test_code_blocks.rb +68 -0
  57. data/test/slim/test_code_escaping.rb +53 -0
  58. data/test/slim/test_code_evaluation.rb +281 -0
  59. data/test/slim/test_code_output.rb +159 -0
  60. data/test/slim/test_code_structure.rb +95 -0
  61. data/test/slim/test_embedded_engines.rb +141 -0
  62. data/test/slim/test_html_escaping.rb +40 -0
  63. data/test/slim/test_html_structure.rb +438 -0
  64. data/test/slim/test_parser_errors.rb +107 -0
  65. data/test/slim/test_pretty.rb +75 -0
  66. data/test/slim/test_ruby_errors.rb +187 -0
  67. data/test/slim/test_sections.rb +90 -0
  68. data/test/slim/test_slim_template.rb +118 -0
  69. data/test/slim/test_text_interpolation.rb +78 -0
  70. data/test/slim/test_wrapper.rb +39 -0
  71. metadata +230 -0
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,25 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ if ENV['TEMPLE'] == 'master'
6
+ gem 'temple', :git => 'git://github.com/judofyr/temple.git'
7
+ end
8
+
9
+ if ENV['RAILS']
10
+ if ENV['RAILS'] == 'master'
11
+ gem 'rails', :git => 'git://github.com/rails/rails.git'
12
+ # FIXME: Rails Gemfile is invalid!
13
+ gem 'journey', :git => 'git://github.com/rails/journey.git'
14
+ else
15
+ gem 'rails', "= #{ENV['RAILS']}"
16
+ end
17
+
18
+ if defined?(JRUBY_VERSION)
19
+ gem 'jdbc-sqlite3'
20
+ gem 'activerecord-jdbc-adapter'
21
+ gem 'activerecord-jdbcsqlite3-adapter'
22
+ else
23
+ gem 'sqlite3-ruby'
24
+ end
25
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2010 Slim Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ Hamlet is a template language whose goal is to reduce HTML syntax to the essential parts.
2
+
3
+ # Syntax
4
+
5
+ ``` html
6
+ <body>
7
+ <p>Some paragraph.
8
+ <ul data-attr=list>
9
+ <li>Item 1
10
+ <li>Item 2
11
+ ```
12
+
13
+ That Hamlet snippet is equivalent to:
14
+
15
+ ``` html
16
+ <body>
17
+ <p>Some paragraph.</p>
18
+ <ul data-attr="list">
19
+ <li>Item 1</li>
20
+ <li>Item 2</li>
21
+ </ul>
22
+ </body>
23
+ ```
24
+
25
+ see, it is just HTML! Designers love Hamlet because it is just HTML! Closing tags are inferred from whitespace.
26
+
27
+ ## Details
28
+
29
+ You can see the [original hamlet templating langauge](http://www.yesodweb.com/book/templates) and the
30
+ [javascript port](hamlet: https://github.com/gregwebs/hamlet.js).
31
+
32
+ This Hamlet works on top of [slim](https://github.com/stonean/slim/). Please see [slim documentation](http://slim-lang.com). There is one important difference: hamlet always defers to HTML syntax. In slim you have:
33
+
34
+ p data-attr=foo Text
35
+ | More Text
36
+
37
+ In hamlet you have:
38
+
39
+ <p data-attr=foo>Text
40
+ More Text
41
+
42
+ ## Whitespace
43
+
44
+ This is currently a bit of a Slim Frankenstein, but I added the same syntax that hamlet.js uses to indicate whitespace: a closing bracket
45
+
46
+ <p> White space to the left
47
+ > White space to the left again
48
+
49
+
50
+ ## Limitations
51
+
52
+ I just hacked this up the other day - let me know if there are any issues. After some more experience using Slim's syntax I plan on trying to reduce the total available syntax.
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ begin
2
+ require 'bundler'
3
+ Bundler::GemHelper.install_tasks
4
+ rescue Exception => e
5
+ end
6
+
7
+ require 'rake/testtask'
8
+
9
+ desc 'Run Slim benchmarks! (default parameters slow=false iterations=1000)'
10
+ task :bench, :iterations, :slow do
11
+ ruby('benchmarks/run.rb')
12
+ end
13
+
14
+ Rake::TestTask.new('test') do |t|
15
+ t.libs << 'lib' << 'test/slim'
16
+ t.test_files = FileList['test/slim/test_*.rb']
17
+ t.verbose = true
18
+ end
19
+
20
+ Rake::TestTask.new('test:rails') do |t|
21
+ t.libs << 'lib'
22
+ t.test_files = FileList['test/rails/test/test_*.rb']
23
+ t.verbose = true
24
+ end
25
+
26
+ task 'test:ci' do |t|
27
+ Rake::Task[ENV['TASK']].execute
28
+ end
29
+
30
+ begin
31
+ require 'rcov/rcovtask'
32
+ Rcov::RcovTask.new do |t|
33
+ t.libs << 'lib' << 'test/slim'
34
+ t.test_files = FileList['test/slim/test_*.rb']
35
+ t.verbose = true
36
+ end
37
+ rescue LoadError
38
+ task :rcov do
39
+ abort "RCov is not available. In order to run rcov, you must: gem install rcov"
40
+ end
41
+ end
42
+
43
+ begin
44
+ require 'yard'
45
+ YARD::Rake::YardocTask.new do |t|
46
+ t.files = %w(lib/**/*.rb)
47
+ end
48
+ rescue LoadError
49
+ task :yard do
50
+ abort "YARD is not available. In order to run yard, you must: gem install yard"
51
+ end
52
+ end
53
+
54
+ desc "Generate Documentation"
55
+ task :doc => :yard
56
+
57
+ task :default => 'test'
data/bin/hamletrb ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.dirname(__FILE__) + '/../lib'
4
+ require 'slim/command'
5
+
6
+ cmd = Slim::Command.new(ARGV)
7
+ cmd.run
@@ -0,0 +1,409 @@
1
+ ;;; slim-mode.el --- Major mode for editing Slim files
2
+
3
+ ;; Copyright (c) 2007, 2008 Nathan Weizenbaum
4
+ ;; Modified slightly for Slim by Daniel Mendler
5
+
6
+ ;; Author: Nathan Weizenbaum
7
+ ;; URL: http://github.com/stonean/slim
8
+ ;; Version: 1.0
9
+ ;; Keywords: markup, language
10
+
11
+ ;;; Commentary:
12
+
13
+ ;; Because Slim's indentation schema is similar
14
+ ;; to that of YAML and Python, many indentation-related
15
+ ;; functions are similar to those in yaml-mode and python-mode.
16
+
17
+ ;; TODO: This whole file has to be reworked to provide optimal support
18
+ ;; for Slim. This is only a slightly adapted haml version.
19
+
20
+ ;; To install, save this on your load path and add the following to
21
+ ;; your .emacs file:
22
+
23
+ ;;
24
+ ;; (require 'slim-mode)
25
+
26
+ ;;; Code:
27
+
28
+ (eval-when-compile (require 'cl))
29
+
30
+ ;; User definable variables
31
+
32
+ (defgroup slim nil
33
+ "Support for the Slim template language."
34
+ :group 'languages
35
+ :prefix "slim-")
36
+
37
+ (defcustom slim-mode-hook nil
38
+ "Hook run when entering Slim mode."
39
+ :type 'hook
40
+ :group 'slim)
41
+
42
+ (defcustom slim-indent-offset 2
43
+ "Amount of offset per level of indentation."
44
+ :type 'integer
45
+ :group 'slim)
46
+
47
+ (defcustom slim-backspace-backdents-nesting t
48
+ "Non-nil to have `slim-electric-backspace' re-indent all code
49
+ nested beneath the backspaced line be re-indented along with the
50
+ line itself."
51
+ :type 'boolean
52
+ :group 'slim)
53
+
54
+ (defface slim-tab-face
55
+ '((((class color)) (:background "hotpink"))
56
+ (t (:reverse-video t)))
57
+ "Face to use for highlighting tabs in Slim files."
58
+ :group 'faces
59
+ :group 'slim)
60
+
61
+ (defvar slim-indent-function 'slim-indent-p
62
+ "This function should look at the current line and return true
63
+ if the next line could be nested within this line.")
64
+
65
+ (defvar slim-block-openers
66
+ `("^ *\\([\\.#a-z][^ \t]*\\)\\(\\[.*\\]\\)?[ \t]*$"
67
+ "^ *[-=].*do[ \t]*\\(|.*|[ \t]*\\)?$"
68
+ ,(concat "^ *-[ \t]*\\("
69
+ (regexp-opt '("if" "unless" "while" "until" "else"
70
+ "begin" "elsif" "rescue" "ensure" "when"))
71
+ "\\)")
72
+ "^ *|"
73
+ "^ */"
74
+ "^ *[a-z0-9_]:")
75
+ "A list of regexps that match lines of Slim that could have
76
+ text nested beneath them.")
77
+
78
+ ;; Font lock
79
+
80
+ ;; Helper for nested block (comment, embedded, text)
81
+ (defun slim-nested-re (re)
82
+ (concat "^\\( *\\)" re "\n\\(?:\\(?:\\1 .*\\)\n\\)*"))
83
+
84
+ (defconst slim-font-lock-keywords
85
+ `((,(slim-nested-re "/.*") 0 font-lock-comment-face) ;; Comment block
86
+ (,(slim-nested-re "[a-z0-9_]+:") 0 font-lock-string-face) ;; Embedded block
87
+ (,(slim-nested-re "[\|'`].*") 0 font-lock-string-face) ;; Text block
88
+ ("^!.*" 0 font-lock-constant-face) ;; Directive
89
+ ("^ *\\(\t\\)" 1 'slim-tab-face)
90
+ ("\\('[^']*'\\)" 1 font-lock-string-face append) ;; Single quote string TODO
91
+ ("\\(\"[^\"]*\"\\)" 1 font-lock-string-face append) ;; Double quoted string TODO
92
+ ("@[a-z0-9_]+" 0 font-lock-variable-name-face append) ;; Class variable TODO
93
+ ("^ *\\(#[a-z0-9_]+\/?\\)" 1 font-lock-keyword-face) ;; #id
94
+ ("^ *\\(\\.[a-z0-9_]+\/?\\)" 1 font-lock-type-face) ;; .class
95
+ ("^ *\\([a-z0-9_]+\/?\\)" 1 font-lock-function-name-face) ;; div
96
+ ("^ *\\(#[a-z0-9_]+\/?\\)" (1 font-lock-keyword-face) ;; #id.class
97
+ ("\\.[a-z0-9_]+" nil nil (0 font-lock-type-face)))
98
+ ("^ *\\(\\.[a-z0-9_]+\/?\\)" (1 font-lock-type-face) ;; .class.class
99
+ ("\\.[a-z0-9_]+" nil nil (0 font-lock-type-face)))
100
+ ("^ *\\(\\.[a-z0-9_]+\/?\\)" (1 font-lock-type-face) ;; .class#id
101
+ ("\\#[a-z0-9_]+" nil nil (0 font-lock-keyword-face)))
102
+ ("^ *\\([a-z0-9_]+\/?\\)" (1 font-lock-function-name-face) ;; div.class
103
+ ("\\.[a-z0-9_]+" nil nil (0 font-lock-type-face)))
104
+ ("^ *\\([a-z0-9_]+\/?\\)" (1 font-lock-function-name-face) ;; div#id
105
+ ("\\#[a-z0-9_]+" nil nil (0 font-lock-keyword-face)))
106
+ ("^ *\\(\\(==?|-\\) .*\\)" 1 font-lock-preprocessor-face prepend) ;; ==, =, -
107
+ ("^ *[\\.#a-z0-9_]+\\(==? .*\\)" 1 font-lock-preprocessor-face prepend))) ;; tag ==, tag =
108
+
109
+ (defconst slim-embedded-re "^ *[a-z0-9_]+:")
110
+ (defconst slim-comment-re "^ */")
111
+
112
+ (defun* slim-extend-region ()
113
+ "Extend the font-lock region to encompass embedded engines and comments."
114
+ (let ((old-beg font-lock-beg)
115
+ (old-end font-lock-end))
116
+ (save-excursion
117
+ (goto-char font-lock-beg)
118
+ (beginning-of-line)
119
+ (unless (or (looking-at slim-embedded-re)
120
+ (looking-at slim-comment-re))
121
+ (return-from slim-extend-region))
122
+ (setq font-lock-beg (point))
123
+ (slim-forward-sexp)
124
+ (beginning-of-line)
125
+ (setq font-lock-end (max font-lock-end (point))))
126
+ (or (/= old-beg font-lock-beg)
127
+ (/= old-end font-lock-end))))
128
+
129
+
130
+ ;; Mode setup
131
+
132
+ (defvar slim-mode-syntax-table
133
+ (let ((table (make-syntax-table)))
134
+ (modify-syntax-entry ?: "." table)
135
+ (modify-syntax-entry ?_ "w" table)
136
+ table)
137
+ "Syntax table in use in slim-mode buffers.")
138
+
139
+ (defvar slim-mode-map
140
+ (let ((map (make-sparse-keymap)))
141
+ (define-key map [backspace] 'slim-electric-backspace)
142
+ (define-key map "\C-?" 'slim-electric-backspace)
143
+ (define-key map "\C-c\C-f" 'slim-forward-sexp)
144
+ (define-key map "\C-c\C-b" 'slim-backward-sexp)
145
+ (define-key map "\C-c\C-u" 'slim-up-list)
146
+ (define-key map "\C-c\C-d" 'slim-down-list)
147
+ (define-key map "\C-c\C-k" 'slim-kill-line-and-indent)
148
+ map))
149
+
150
+ ;;;###autoload
151
+ (define-derived-mode slim-mode fundamental-mode "Slim"
152
+ "Major mode for editing Slim files.
153
+
154
+ \\{slim-mode-map}"
155
+ (set-syntax-table slim-mode-syntax-table)
156
+ (add-to-list 'font-lock-extend-region-functions 'slim-extend-region)
157
+ (set (make-local-variable 'font-lock-multiline) t)
158
+ (set (make-local-variable 'indent-line-function) 'slim-indent-line)
159
+ (set (make-local-variable 'indent-region-function) 'slim-indent-region)
160
+ (set (make-local-variable 'parse-sexp-lookup-properties) t)
161
+ (setq comment-start "/")
162
+ (setq indent-tabs-mode nil)
163
+ (setq font-lock-defaults '((slim-font-lock-keywords) nil t)))
164
+
165
+ ;; Useful functions
166
+
167
+ (defun slim-comment-block ()
168
+ "Comment the current block of Slim code."
169
+ (interactive)
170
+ (save-excursion
171
+ (let ((indent (current-indentation)))
172
+ (back-to-indentation)
173
+ (insert "/")
174
+ (newline)
175
+ (indent-to indent)
176
+ (beginning-of-line)
177
+ (slim-mark-sexp)
178
+ (slim-reindent-region-by slim-indent-offset))))
179
+
180
+ (defun slim-uncomment-block ()
181
+ "Uncomment the current block of Slim code."
182
+ (interactive)
183
+ (save-excursion
184
+ (beginning-of-line)
185
+ (while (not (looking-at slim-comment-re))
186
+ (slim-up-list)
187
+ (beginning-of-line))
188
+ (slim-mark-sexp)
189
+ (kill-line 1)
190
+ (slim-reindent-region-by (- slim-indent-offset))))
191
+
192
+ ;; Navigation
193
+
194
+ (defun slim-forward-through-whitespace (&optional backward)
195
+ "Move the point forward at least one line, until it reaches
196
+ either the end of the buffer or a line with no whitespace.
197
+
198
+ If `backward' is non-nil, move the point backward instead."
199
+ (let ((arg (if backward -1 1))
200
+ (endp (if backward 'bobp 'eobp)))
201
+ (loop do (forward-line arg)
202
+ while (and (not (funcall endp))
203
+ (looking-at "^[ \t]*$")))))
204
+
205
+ (defun slim-at-indent-p ()
206
+ "Returns whether or not the point is at the first
207
+ non-whitespace character in a line or whitespace preceding that
208
+ character."
209
+ (let ((opoint (point)))
210
+ (save-excursion
211
+ (back-to-indentation)
212
+ (>= (point) opoint))))
213
+
214
+ (defun slim-forward-sexp (&optional arg)
215
+ "Move forward across one nested expression.
216
+ With `arg', do it that many times. Negative arg -N means move
217
+ backward across N balanced expressions.
218
+
219
+ A sexp in Slim is defined as a line of Slim code as well as any
220
+ lines nested beneath it."
221
+ (interactive "p")
222
+ (or arg (setq arg 1))
223
+ (if (and (< arg 0) (not (slim-at-indent-p)))
224
+ (back-to-indentation)
225
+ (while (/= arg 0)
226
+ (let ((indent (current-indentation)))
227
+ (loop do (slim-forward-through-whitespace (< arg 0))
228
+ while (and (not (eobp))
229
+ (not (bobp))
230
+ (> (current-indentation) indent)))
231
+ (back-to-indentation)
232
+ (setq arg (+ arg (if (> arg 0) -1 1)))))))
233
+
234
+ (defun slim-backward-sexp (&optional arg)
235
+ "Move backward across one nested expression.
236
+ With ARG, do it that many times. Negative arg -N means move
237
+ forward across N balanced expressions.
238
+
239
+ A sexp in Slim is defined as a line of Slim code as well as any
240
+ lines nested beneath it."
241
+ (interactive "p")
242
+ (slim-forward-sexp (if arg (- arg) -1)))
243
+
244
+ (defun slim-up-list (&optional arg)
245
+ "Move out of one level of nesting.
246
+ With ARG, do this that many times."
247
+ (interactive "p")
248
+ (or arg (setq arg 1))
249
+ (while (> arg 0)
250
+ (let ((indent (current-indentation)))
251
+ (loop do (slim-forward-through-whitespace t)
252
+ while (and (not (bobp))
253
+ (>= (current-indentation) indent)))
254
+ (setq arg (- arg 1))))
255
+ (back-to-indentation))
256
+
257
+ (defun slim-down-list (&optional arg)
258
+ "Move down one level of nesting.
259
+ With ARG, do this that many times."
260
+ (interactive "p")
261
+ (or arg (setq arg 1))
262
+ (while (> arg 0)
263
+ (let ((indent (current-indentation)))
264
+ (slim-forward-through-whitespace)
265
+ (when (<= (current-indentation) indent)
266
+ (slim-forward-through-whitespace t)
267
+ (back-to-indentation)
268
+ (error "Nothing is nested beneath this line"))
269
+ (setq arg (- arg 1))))
270
+ (back-to-indentation))
271
+
272
+ (defun slim-mark-sexp ()
273
+ "Marks the next Slim block."
274
+ (let ((forward-sexp-function 'slim-forward-sexp))
275
+ (mark-sexp)))
276
+
277
+ (defun slim-mark-sexp-but-not-next-line ()
278
+ "Marks the next Slim block, but puts the mark at the end of the
279
+ last line of the sexp rather than the first non-whitespace
280
+ character of the next line."
281
+ (slim-mark-sexp)
282
+ (set-mark
283
+ (save-excursion
284
+ (goto-char (mark))
285
+ (forward-line -1)
286
+ (end-of-line)
287
+ (point))))
288
+
289
+ ;; Indentation and electric keys
290
+
291
+ (defun slim-indent-p ()
292
+ "Returns true if the current line can have lines nested beneath it."
293
+ (loop for opener in slim-block-openers
294
+ if (looking-at opener) return t
295
+ finally return nil))
296
+
297
+ (defun slim-compute-indentation ()
298
+ "Calculate the maximum sensible indentation for the current line."
299
+ (save-excursion
300
+ (beginning-of-line)
301
+ (if (bobp) 0
302
+ (slim-forward-through-whitespace t)
303
+ (+ (current-indentation)
304
+ (if (funcall slim-indent-function) slim-indent-offset
305
+ 0)))))
306
+
307
+ (defun slim-indent-region (start end)
308
+ "Indent each nonblank line in the region.
309
+ This is done by indenting the first line based on
310
+ `slim-compute-indentation' and preserving the relative
311
+ indentation of the rest of the region.
312
+
313
+ If this command is used multiple times in a row, it will cycle
314
+ between possible indentations."
315
+ (save-excursion
316
+ (goto-char end)
317
+ (setq end (point-marker))
318
+ (goto-char start)
319
+ (let (this-line-column current-column
320
+ (next-line-column
321
+ (if (and (equal last-command this-command) (/= (current-indentation) 0))
322
+ (* (/ (- (current-indentation) 1) slim-indent-offset) slim-indent-offset)
323
+ (slim-compute-indentation))))
324
+ (while (< (point) end)
325
+ (setq this-line-column next-line-column
326
+ current-column (current-indentation))
327
+ ;; Delete whitespace chars at beginning of line
328
+ (delete-horizontal-space)
329
+ (unless (eolp)
330
+ (setq next-line-column (save-excursion
331
+ (loop do (forward-line 1)
332
+ while (and (not (eobp)) (looking-at "^[ \t]*$")))
333
+ (+ this-line-column
334
+ (- (current-indentation) current-column))))
335
+ ;; Don't indent an empty line
336
+ (unless (eolp) (indent-to this-line-column)))
337
+ (forward-line 1)))
338
+ (move-marker end nil)))
339
+
340
+ (defun slim-indent-line ()
341
+ "Indent the current line.
342
+ The first time this command is used, the line will be indented to the
343
+ maximum sensible indentation. Each immediately subsequent usage will
344
+ back-dent the line by `slim-indent-offset' spaces. On reaching column
345
+ 0, it will cycle back to the maximum sensible indentation."
346
+ (interactive "*")
347
+ (let ((ci (current-indentation))
348
+ (cc (current-column))
349
+ (need (slim-compute-indentation)))
350
+ (save-excursion
351
+ (beginning-of-line)
352
+ (delete-horizontal-space)
353
+ (if (and (equal last-command this-command) (/= ci 0))
354
+ (indent-to (* (/ (- ci 1) slim-indent-offset) slim-indent-offset))
355
+ (indent-to need)))
356
+ (if (< (current-column) (current-indentation))
357
+ (forward-to-indentation 0))))
358
+
359
+ (defun slim-reindent-region-by (n)
360
+ "Add N spaces to the beginning of each line in the region.
361
+ If N is negative, will remove the spaces instead. Assumes all
362
+ lines in the region have indentation >= that of the first line."
363
+ (let ((ci (current-indentation)))
364
+ (save-excursion
365
+ (replace-regexp (concat "^" (make-string ci ? ))
366
+ (make-string (max 0 (+ ci n)) ? )
367
+ nil (point) (mark)))))
368
+
369
+ (defun slim-electric-backspace (arg)
370
+ "Delete characters or back-dent the current line.
371
+ If invoked following only whitespace on a line, will back-dent
372
+ the line and all nested lines to the immediately previous
373
+ multiple of `slim-indent-offset' spaces.
374
+
375
+ Set `slim-backspace-backdents-nesting' to nil to just back-dent
376
+ the current line."
377
+ (interactive "*p")
378
+ (if (or (/= (current-indentation) (current-column))
379
+ (bolp)
380
+ (looking-at "^[ \t]+$"))
381
+ (backward-delete-char arg)
382
+ (save-excursion
383
+ (let ((ci (current-column)))
384
+ (beginning-of-line)
385
+ (if slim-backspace-backdents-nesting
386
+ (slim-mark-sexp-but-not-next-line)
387
+ (set-mark (save-excursion (end-of-line) (point))))
388
+ (slim-reindent-region-by (* (- arg) slim-indent-offset))
389
+ (back-to-indentation)
390
+ (pop-mark)))))
391
+
392
+ (defun slim-kill-line-and-indent ()
393
+ "Kill the current line, and re-indent all lines nested beneath it."
394
+ (interactive)
395
+ (beginning-of-line)
396
+ (slim-mark-sexp-but-not-next-line)
397
+ (kill-line 1)
398
+ (slim-reindent-region-by (* -1 slim-indent-offset)))
399
+
400
+ (defun slim-indent-string ()
401
+ "Return the indentation string for `slim-indent-offset'."
402
+ (mapconcat 'identity (make-list slim-indent-offset " ") ""))
403
+
404
+ ;;;###autoload
405
+ (add-to-list 'auto-mode-alist '("\\.slim$" . slim-mode))
406
+
407
+ ;; Setup/Activation
408
+ (provide 'slim-mode)
409
+ ;;; slim-mode.el ends here