ruby_learner 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (188) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +6 -0
  7. data/Gemfile.lock +58 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +65 -0
  10. data/Rakefile +6 -0
  11. data/bin/console +14 -0
  12. data/bin/new_terminal +25 -0
  13. data/bin/setup +8 -0
  14. data/docs/happy_ruby/RussOlsen_EloquentRuby_c1.pdf +0 -0
  15. data/docs/happy_ruby/RussOlsen_EloquentRuby_c5.pdf +0 -0
  16. data/docs/happy_ruby/TanoshiiRuby_v3_c23.pdf +0 -0
  17. data/docs/happy_ruby/TanoshiiRuby_v5_c1.pdf +0 -0
  18. data/docs/happy_ruby/TanoshiiRuby_v5_c2-3.pdf +0 -0
  19. data/docs/happy_ruby/c2.ipynb +479 -0
  20. data/docs/happy_ruby/c3_4.ipynb +237 -0
  21. data/docs/seminar/8-1.org +18 -0
  22. data/exe/ruby_learner +5 -0
  23. data/lib/ruby_learner/h.rb +14 -0
  24. data/lib/ruby_learner/methods.rb +131 -0
  25. data/lib/ruby_learner/random_h.rb +16 -0
  26. data/lib/ruby_learner/ruby_learner.rb +43 -0
  27. data/lib/ruby_learner/sequential_h.rb +15 -0
  28. data/lib/ruby_learner/typing_practice.rb +21 -0
  29. data/lib/ruby_learner/version.rb +3 -0
  30. data/questions/random_check/.rspec +1 -0
  31. data/questions/random_check/random_h.rb +16 -0
  32. data/questions/random_check/section_1/.rspec +1 -0
  33. data/questions/random_check/section_1/lib/answer.rb +15 -0
  34. data/questions/random_check/section_1/lib/sentence.org +9 -0
  35. data/questions/random_check/section_1/lib/workplace.rb +5 -0
  36. data/questions/random_check/section_1/spec/spec_helper.rb +100 -0
  37. data/questions/random_check/section_1/spec/workplace_spec.rb +10 -0
  38. data/questions/random_check/section_2/.rspec +1 -0
  39. data/questions/random_check/section_2/lib/answer.rb +17 -0
  40. data/questions/random_check/section_2/lib/sentence.org +12 -0
  41. data/questions/random_check/section_2/lib/workplace.rb +5 -0
  42. data/questions/random_check/section_2/spec/.rspec +1 -0
  43. data/questions/random_check/section_2/spec/spec_helper.rb +100 -0
  44. data/questions/random_check/section_2/spec/workplace_spec.rb +11 -0
  45. data/questions/sequential_check/section_1/part_1/lib/answer.rb +9 -0
  46. data/questions/sequential_check/section_1/part_1/lib/sentence.org +9 -0
  47. data/questions/sequential_check/section_1/part_1/lib/workplace.rb +5 -0
  48. data/questions/sequential_check/section_1/part_1/spec/spec_helper.rb +100 -0
  49. data/questions/sequential_check/section_1/part_1/spec/workplace_spec.rb +10 -0
  50. data/questions/sequential_check/section_1/part_2/lib/answer.rb +16 -0
  51. data/questions/sequential_check/section_1/part_2/lib/sentence.org +12 -0
  52. data/questions/sequential_check/section_1/part_2/lib/workplace.rb +5 -0
  53. data/questions/sequential_check/section_1/part_2/spec/.rspec +1 -0
  54. data/questions/sequential_check/section_1/part_2/spec/spec_helper.rb +100 -0
  55. data/questions/sequential_check/section_1/part_2/spec/workplace_spec.rb +11 -0
  56. data/ruby_learner.gemspec +41 -0
  57. data/takahashi/docs/README.org +139 -0
  58. data/takahashi/docs/drill.html +875 -0
  59. data/takahashi/docs/drill.html~ +877 -0
  60. data/takahashi/docs/drill.org +446 -0
  61. data/takahashi/docs/ruby_for_beginner.html +2642 -0
  62. data/takahashi/docs/ruby_for_beginner.org +1430 -0
  63. data/takahashi/sample_prog/answer/10_1.rb +5 -0
  64. data/takahashi/sample_prog/answer/11_1.rb +5 -0
  65. data/takahashi/sample_prog/answer/11_2.rb +4 -0
  66. data/takahashi/sample_prog/answer/1_1.rb +1 -0
  67. data/takahashi/sample_prog/answer/1_2.rb +1 -0
  68. data/takahashi/sample_prog/answer/1_3.rb +1 -0
  69. data/takahashi/sample_prog/answer/2_1.rb +5 -0
  70. data/takahashi/sample_prog/answer/2_2.rb +12 -0
  71. data/takahashi/sample_prog/answer/3_1.rb +10 -0
  72. data/takahashi/sample_prog/answer/4_1.rb +7 -0
  73. data/takahashi/sample_prog/answer/5_1.rb +6 -0
  74. data/takahashi/sample_prog/answer/5_2.rb +3 -0
  75. data/takahashi/sample_prog/answer/6_1.rb +3 -0
  76. data/takahashi/sample_prog/answer/6_2.rb +5 -0
  77. data/takahashi/sample_prog/answer/6_3.rb +5 -0
  78. data/takahashi/sample_prog/answer/6_4.rb +7 -0
  79. data/takahashi/sample_prog/answer/7_1.rb +3 -0
  80. data/takahashi/sample_prog/answer/7_2.rb +8 -0
  81. data/takahashi/sample_prog/answer/9_1.rb +3 -0
  82. data/takahashi/sample_prog/answer/9_2.rb +5 -0
  83. data/takahashi/sample_prog/answer/9_3.rb +10 -0
  84. data/takahashi/sample_prog/answer/hello.rb +3 -0
  85. data/workshop/.rspec +1 -0
  86. data/workshop/emacs.d/ac-comphist.dat +50 -0
  87. data/workshop/emacs.d/cp5022x.el +156 -0
  88. data/workshop/emacs.d/elpa/archives/gnu/archive-contents +1240 -0
  89. data/workshop/emacs.d/elpa/archives/melpa/archive-contents +2 -0
  90. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete-autoloads.el +65 -0
  91. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete-config.el +551 -0
  92. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete-config.elc +0 -0
  93. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete-pkg.el +6 -0
  94. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete.el +2164 -0
  95. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/auto-complete.elc +0 -0
  96. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/ada-mode +72 -0
  97. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/c++-mode +99 -0
  98. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/c-mode +55 -0
  99. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/caml-mode +231 -0
  100. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/clojure-mode +580 -0
  101. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/clojurescript-mode +475 -0
  102. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/coq-mode +278 -0
  103. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/css-mode +874 -0
  104. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/erlang-mode +216 -0
  105. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/ess-julia-mode +37 -0
  106. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/go-mode +25 -0
  107. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/haskell-mode +679 -0
  108. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/java-mode +53 -0
  109. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/js-mode +148 -0
  110. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/julia-mode +37 -0
  111. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/lua-mode +21 -0
  112. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/nim-mode +70 -0
  113. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/objc-mode +161 -0
  114. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/octave-mode +46 -0
  115. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/php-mode +6144 -0
  116. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/python-mode +379 -0
  117. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/qml-mode +183 -0
  118. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/ruby-mode +181 -0
  119. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/scala-mode +1347 -0
  120. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/scheme-mode +216 -0
  121. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/sclang-mode +1481 -0
  122. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/sh-mode +182 -0
  123. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/swift-mode +87 -0
  124. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/tcl-mode +172 -0
  125. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/ts-mode +797 -0
  126. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/tuareg-mode +231 -0
  127. data/workshop/emacs.d/elpa/auto-complete-20170124.1845/dict/verilog-mode +313 -0
  128. data/workshop/emacs.d/elpa/better-defaults-20170613.2104/better-defaults-autoloads.el +16 -0
  129. data/workshop/emacs.d/elpa/better-defaults-20170613.2104/better-defaults-pkg.el +2 -0
  130. data/workshop/emacs.d/elpa/better-defaults-20170613.2104/better-defaults.el +90 -0
  131. data/workshop/emacs.d/elpa/better-defaults-20170613.2104/better-defaults.elc +0 -0
  132. data/workshop/emacs.d/elpa/haml-mode-20170923.2153/haml-mode-autoloads.el +26 -0
  133. data/workshop/emacs.d/elpa/haml-mode-20170923.2153/haml-mode-pkg.el +2 -0
  134. data/workshop/emacs.d/elpa/haml-mode-20170923.2153/haml-mode.el +877 -0
  135. data/workshop/emacs.d/elpa/haml-mode-20170923.2153/haml-mode.elc +0 -0
  136. data/workshop/emacs.d/elpa/haml-mode-readme.txt +8 -0
  137. data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-light-theme.el +918 -0
  138. data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-light-theme.elc +0 -0
  139. data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-theme-autoloads.el +32 -0
  140. data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-theme-pkg.el +8 -0
  141. data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-theme.el +912 -0
  142. data/workshop/emacs.d/elpa/material-theme-20171123.1040/material-theme.elc +0 -0
  143. data/workshop/emacs.d/elpa/ox-bibtex-chinese-readme.txt +21 -0
  144. data/workshop/emacs.d/elpa/popup-20160709.729/popup-autoloads.el +15 -0
  145. data/workshop/emacs.d/elpa/popup-20160709.729/popup-pkg.el +2 -0
  146. data/workshop/emacs.d/elpa/popup-20160709.729/popup.el +1432 -0
  147. data/workshop/emacs.d/elpa/popup-20160709.729/popup.elc +0 -0
  148. data/workshop/emacs.d/elpa/yaml-mode-20180212.1556/yaml-mode-autoloads.el +33 -0
  149. data/workshop/emacs.d/elpa/yaml-mode-20180212.1556/yaml-mode-pkg.el +2 -0
  150. data/workshop/emacs.d/elpa/yaml-mode-20180212.1556/yaml-mode.el +470 -0
  151. data/workshop/emacs.d/elpa/yaml-mode-20180212.1556/yaml-mode.elc +0 -0
  152. data/workshop/emacs.d/elpa/yaml-mode-readme.txt +25 -0
  153. data/workshop/emacs.d/haml-mode-master/.gitignore +1 -0
  154. data/workshop/emacs.d/haml-mode-master/.mailmap +2 -0
  155. data/workshop/emacs.d/haml-mode-master/MIT-LICENSE +20 -0
  156. data/workshop/emacs.d/haml-mode-master/README.md +47 -0
  157. data/workshop/emacs.d/haml-mode-master/haml-mode.el +887 -0
  158. data/workshop/emacs.d/iceberg_theme.el +202 -0
  159. data/workshop/emacs.d/init-open-recentf.el +133 -0
  160. data/workshop/emacs.d/init.el +229 -0
  161. data/workshop/emacs.d/inits/line-num.el +264 -0
  162. data/workshop/emacs.d/install-elisp.el +366 -0
  163. data/workshop/emacs.d/markdown-mode/markdown-mode.el +5978 -0
  164. data/workshop/emacs.d/notes +12 -0
  165. data/workshop/emacs.d/processing-mode/processing-mode.el +275 -0
  166. data/workshop/emacs.d/recentf +31 -0
  167. data/workshop/emacs.d/ruby-mode/inf-ruby.el +416 -0
  168. data/workshop/emacs.d/ruby-mode/rdoc-mode.el +130 -0
  169. data/workshop/emacs.d/ruby-mode/ruby-electric.el +205 -0
  170. data/workshop/emacs.d/ruby-mode/ruby-mode.el +1496 -0
  171. data/workshop/emacs.d/ruby-mode/ruby-style.el +78 -0
  172. data/workshop/emacs.d/ruby-mode/rubydb2x.el +104 -0
  173. data/workshop/emacs.d/ruby-mode/rubydb3x.el +115 -0
  174. data/workshop/emacs.d/ruby_learner_init.el +244 -0
  175. data/workshop/emacs.d/themes/dracula-theme.el +431 -0
  176. data/workshop/emacs.d/themes/iceberg-theme.el +205 -0
  177. data/workshop/emacs.d/themes/my-misterioso-theme.el +109 -0
  178. data/workshop/emacs.d/themes/my-wombat-theme.el +121 -0
  179. data/workshop/emacs.d/wiki-mode/wiki.el +976 -0
  180. data/workshop/emacs_help.org +34 -0
  181. data/workshop/lib/answer.rb +1 -0
  182. data/workshop/lib/sentence.org +1 -0
  183. data/workshop/lib/workplace.rb +1 -0
  184. data/workshop/restore/empty.rb +0 -0
  185. data/workshop/spec/spec_helper.rb +100 -0
  186. data/workshop/spec/workplace_spec.rb +1 -0
  187. data/workshop/training_data.txt +3 -0
  188. metadata +343 -0
@@ -0,0 +1,877 @@
1
+ ;;; haml-mode.el --- Major mode for editing Haml files
2
+
3
+ ;; Copyright (c) 2007, 2008 Natalie Weizenbaum
4
+
5
+ ;; Author: Natalie Weizenbaum
6
+ ;; URL: https://github.com/nex3/haml-mode
7
+ ;; Package-Requires: ((emacs "24") (cl-lib "0.5"))
8
+ ;; Package-Version: 20170923.2153
9
+ ;; Package-X-Original-Version: 0
10
+ ;; Created: 2007-03-08
11
+ ;; By: Natalie Weizenbaum
12
+ ;; Keywords: markup, languages, html
13
+
14
+ ;;; Commentary:
15
+
16
+ ;; Because Haml's indentation schema is similar
17
+ ;; to that of YAML and Python, many indentation-related
18
+ ;; functions are similar to those in yaml-mode and python-mode.
19
+
20
+ ;; To install, save this on your load path and add the following to
21
+ ;; your .emacs file:
22
+ ;;
23
+ ;; (require 'haml-mode)
24
+
25
+ ;;; Code:
26
+
27
+ (require 'cl-lib)
28
+ (require 'ruby-mode)
29
+
30
+ ;; Additional (optional) libraries for fontification
31
+ (require 'css-mode nil t)
32
+ (require 'textile-mode nil t)
33
+ (require 'markdown-mode nil t)
34
+ (require 'js nil t)
35
+
36
+ ;; User definable variables
37
+
38
+ (defgroup haml nil
39
+ "Support for the Haml template language."
40
+ :group 'languages
41
+ :prefix "haml-")
42
+
43
+ (defcustom haml-mode-hook nil
44
+ "Hook run when entering Haml mode."
45
+ :type 'hook
46
+ :group 'haml)
47
+
48
+ (defcustom haml-indent-offset 2
49
+ "Amount of offset per level of indentation."
50
+ :type 'integer
51
+ :group 'haml)
52
+
53
+ (defcustom haml-backspace-backdents-nesting t
54
+ "Non-nil to have `haml-electric-backspace' re-indent blocks of code.
55
+ This means that all code nested beneath the backspaced line is
56
+ re-indented along with the line itself."
57
+ :type 'boolean
58
+ :group 'haml)
59
+
60
+ (defvar haml-indent-function 'haml-indent-p
61
+ "A function for checking if nesting is allowed.
62
+ This function should look at the current line and return t
63
+ if the next line could be nested within this line.
64
+
65
+ The function can also return a positive integer to indicate
66
+ a specific level to which the current line could be indented.")
67
+
68
+ (defconst haml-tag-beg-re
69
+ "^[ \t]*\\([%\\.#][a-z0-9_:\\-]+\\)+\\(?:(.*)\\|{.*}\\|\\[.*\\]\\)*"
70
+ "A regexp matching the beginning of a Haml tag, through (), {}, and [].")
71
+
72
+ (defvar haml-block-openers
73
+ `(,(concat haml-tag-beg-re "[><]*[ \t]*$")
74
+ "^[ \t]*[&!]?[-=~].*do[ \t]*\\(|.*|[ \t]*\\)?$"
75
+ ,(concat "^[ \t]*[&!]?[-=~][ \t]*\\("
76
+ (regexp-opt '("if" "unless" "while" "until" "else" "for"
77
+ "begin" "elsif" "rescue" "ensure" "when"))
78
+ "\\)")
79
+ "^[ \t]*/\\(\\[.*\\]\\)?[ \t]*$"
80
+ "^[ \t]*-#"
81
+ "^[ \t]*:")
82
+ "A list of regexps that match lines of Haml that open blocks.
83
+ That is, a Haml line that can have text nested beneath it should
84
+ be matched by a regexp in this list.")
85
+
86
+
87
+ ;; Font lock
88
+
89
+ (defun haml-nested-regexp (re)
90
+ "Create a regexp to match a block starting with RE.
91
+ The line containing RE is matched, as well as all lines indented beneath it."
92
+ (concat "^\\([ \t]*\\)\\(" re "\\)\\([ \t]*\\(?:\n\\(?:\\1 +[^\n]*\\)?\\)*\n?\\)$"))
93
+
94
+ (defconst haml-font-lock-keywords
95
+ `((haml-highlight-interpolation 1 font-lock-variable-name-face prepend)
96
+ (haml-highlight-ruby-tag 1 font-lock-preprocessor-face)
97
+ (haml-highlight-ruby-script 1 font-lock-preprocessor-face)
98
+ ;; TODO: distinguish between "/" comments, which can contain HAML
99
+ ;; output directives, and "-#", which are completely ignored
100
+ haml-highlight-comment
101
+ haml-highlight-filter
102
+ ("^!!!.*" 0 font-lock-constant-face)
103
+ ("\\s| *$" 0 font-lock-string-face)))
104
+
105
+ (defconst haml-filter-re (haml-nested-regexp ":[[:alnum:]_\\-]+"))
106
+ (defconst haml-comment-re (haml-nested-regexp "\\(?:-\\#\\|/\\)[^\n]*"))
107
+
108
+ (defun haml-highlight-comment (limit)
109
+ "Highlight any -# or / comment found up to LIMIT."
110
+ (when (re-search-forward haml-comment-re limit t)
111
+ (let ((beg (match-beginning 0))
112
+ (end (match-end 0)))
113
+ (put-text-property beg end 'face 'font-lock-comment-face)
114
+ (goto-char end))))
115
+
116
+ ;; Fontifying sub-regions for other languages
117
+
118
+ (defun haml-fontify-region
119
+ (beg end keywords syntax-table syntax-propertize-fn)
120
+ "Fontify a region between BEG and END using another mode's fontification.
121
+
122
+ KEYWORDS, SYNTAX-TABLE, SYNTACTIC-KEYWORDS and
123
+ SYNTAX-PROPERTIZE-FN are the values of that mode's
124
+ `font-lock-keywords', `font-lock-syntax-table',
125
+ `font-lock-syntactic-keywords', and `syntax-propertize-function'
126
+ respectively."
127
+ (save-excursion
128
+ (save-match-data
129
+ (let ((font-lock-keywords keywords)
130
+ (font-lock-syntax-table syntax-table)
131
+ (syntax-propertize-function syntax-propertize-fn)
132
+ (font-lock-multiline 'undecided)
133
+ (font-lock-dont-widen t)
134
+ font-lock-keywords-only
135
+ font-lock-extend-region-functions
136
+ font-lock-keywords-case-fold-search)
137
+ (save-restriction
138
+ (narrow-to-region (1- beg) end)
139
+ ;; font-lock-fontify-region apparently isn't inclusive,
140
+ ;; so we have to move the beginning back one char
141
+ (font-lock-fontify-region (1- beg) end))))))
142
+
143
+ (defun haml-fontify-region-as-ruby (beg end)
144
+ "Use Ruby's font-lock variables to fontify the region between BEG and END."
145
+ (haml-fontify-region beg end ruby-font-lock-keywords
146
+ ruby-font-lock-syntax-table
147
+ (if (fboundp 'ruby-syntax-propertize)
148
+ 'ruby-syntax-propertize
149
+ 'ruby-syntax-propertize-function)))
150
+
151
+ (defun haml-fontify-region-as-css (beg end)
152
+ "Fontify CSS code from BEG to END.
153
+
154
+ This requires that `css-mode' is available.
155
+ `css-mode' is included with Emacs 23."
156
+ (when (boundp 'css-font-lock-keywords)
157
+ (haml-fontify-region beg end
158
+ css-font-lock-keywords
159
+ css-mode-syntax-table
160
+ 'css-syntax-propertize-function)))
161
+
162
+ (defun haml-fontify-region-as-javascript (beg end)
163
+ "Fontify javascript code from BEG to END.
164
+
165
+ This requires that Karl Landström's javascript mode be available, either as the
166
+ \"js.el\" bundled with Emacs >= 23, or as \"javascript.el\" found in ELPA and
167
+ elsewhere."
168
+ (when js--font-lock-keywords-3
169
+ (when (and (fboundp 'js--update-quick-match-re)
170
+ (null js--quick-match-re-func))
171
+ (js--update-quick-match-re))
172
+ (haml-fontify-region beg end
173
+ js--font-lock-keywords-3
174
+ js-mode-syntax-table
175
+ #'js-syntax-propertize)))
176
+
177
+ (defun haml-fontify-region-as-textile (beg end)
178
+ "Highlight textile from BEG to END.
179
+
180
+ This requires that `textile-mode' be available.
181
+
182
+ Note that the results are not perfect, since `textile-mode' expects
183
+ certain constructs such as \"h1.\" to be at the beginning of a line,
184
+ and indented Haml filters always have leading whitespace."
185
+ (if (boundp 'textile-font-lock-keywords)
186
+ (haml-fontify-region beg end textile-font-lock-keywords textile-mode-syntax-table nil)))
187
+
188
+ (defun haml-fontify-region-as-markdown (beg end)
189
+ "Highlight markdown from BEG to END.
190
+
191
+ This requires that `markdown-mode' be available."
192
+ (if (boundp 'markdown-mode-font-lock-keywords)
193
+ (haml-fontify-region beg end
194
+ markdown-mode-font-lock-keywords
195
+ markdown-mode-syntax-table
196
+ nil)))
197
+
198
+ (defvar haml-fontify-filter-functions-alist
199
+ '(("ruby" . haml-fontify-region-as-ruby)
200
+ ("css" . haml-fontify-region-as-css)
201
+ ("javascript" . haml-fontify-region-as-javascript)
202
+ ("textile" . haml-fontify-region-as-textile)
203
+ ("markdown" . haml-fontify-region-as-markdown))
204
+ "An alist of (FILTER-NAME . FUNCTION) used to fontify code regions.
205
+ FILTER-NAME is a string and FUNCTION is a function which will be
206
+ used to fontify the filter's indented code region. FUNCTION will
207
+ be passed the extents of that region in two arguments BEG and
208
+ END.")
209
+
210
+ (defun haml-highlight-filter (limit)
211
+ "Highlight any :filter region found in the text up to LIMIT."
212
+ (when (re-search-forward haml-filter-re limit t)
213
+ ;; fontify the filter name
214
+ (put-text-property (match-beginning 2) (1+ (match-end 2))
215
+ 'face font-lock-preprocessor-face)
216
+ (let ((filter-name (substring (match-string 2) 1))
217
+ (code-start (1+ (match-beginning 3)))
218
+ (code-end (match-end 3)))
219
+ (save-match-data
220
+ (funcall (or (cdr (assoc filter-name haml-fontify-filter-functions-alist))
221
+ #'(lambda (beg end)
222
+ (put-text-property beg end
223
+ 'face
224
+ 'font-lock-string-face)))
225
+ code-start code-end))
226
+ (goto-char (match-end 0)))))
227
+
228
+ (defconst haml-possibly-multiline-code-re
229
+ "\\(\\(?:.*?,[ \t]*\n\\)*.*\\)"
230
+ "Regexp to match trailing ruby code which may continue onto subsequent lines.")
231
+
232
+ (defconst haml-ruby-script-re
233
+ (concat "^[ \t]*\\(-\\|[&!]?\\(?:=\\|~\\)\\)[^=]" haml-possibly-multiline-code-re)
234
+ "Regexp to match -, = or ~ blocks and any continued code lines.")
235
+
236
+ (defun haml-highlight-ruby-script (limit)
237
+ "Highlight a Ruby script expression (-, =, or ~).
238
+ LIMIT works as it does in `re-search-forward'."
239
+ (when (re-search-forward haml-ruby-script-re limit t)
240
+ (haml-fontify-region-as-ruby (match-beginning 2) (match-end 2))))
241
+
242
+ (defun haml-move (re)
243
+ "Try matching and moving to the end of regular expression RE.
244
+ Returns non-nil if the expression was sucessfully matched."
245
+ (when (looking-at re)
246
+ (goto-char (match-end 0))
247
+ t))
248
+
249
+ (defun haml-highlight-ruby-tag (limit)
250
+ "Highlight Ruby code within a Haml tag.
251
+ LIMIT works as it does in `re-search-forward'.
252
+
253
+ This highlights the tag attributes and object refs of the tag,
254
+ as well as the script expression (-, =, or ~) following the tag.
255
+
256
+ For example, this will highlight all of the following:
257
+ %p{:foo => 'bar'}
258
+ %p[@bar]
259
+ %p= 'baz'
260
+ %p{:foo => 'bar'}[@bar]= 'baz'"
261
+ (when (re-search-forward "^[ \t]*[%.#]" limit t)
262
+ (forward-char -1)
263
+
264
+ ;; Highlight tag, classes, and ids
265
+ (while (haml-move "\\([.#%]\\)[a-z0-9_:\\-]*")
266
+ (put-text-property (match-beginning 0) (match-end 0) 'face
267
+ (cl-case (char-after (match-beginning 1))
268
+ (?% font-lock-keyword-face)
269
+ (?# font-lock-function-name-face)
270
+ (?. font-lock-variable-name-face))))
271
+
272
+ (cl-block loop
273
+ (while t
274
+ (let ((eol (save-excursion (end-of-line) (point))))
275
+ (cl-case (char-after)
276
+ ;; Highlight obj refs
277
+ (?\[
278
+ (forward-char 1)
279
+ (let ((beg (point)))
280
+ (haml-limited-forward-sexp eol)
281
+ (haml-fontify-region-as-ruby beg (point))))
282
+ ;; Highlight new attr hashes
283
+ (?\(
284
+ (forward-char 1)
285
+ (while
286
+ (and (haml-parse-new-attr-hash
287
+ (lambda (type beg end)
288
+ (cl-case type
289
+ (name (put-text-property beg end
290
+ 'face
291
+ font-lock-constant-face))
292
+ (value (haml-fontify-region-as-ruby beg end)))))
293
+ (not (eobp)))
294
+ (forward-line 1)
295
+ (beginning-of-line)))
296
+ ;; Highlight old attr hashes
297
+ (?\{
298
+ (let ((beg (point)))
299
+ (haml-limited-forward-sexp eol)
300
+
301
+ ;; Check for multiline
302
+ (while (and (eolp) (eq (char-before) ?,) (not (eobp)))
303
+ (forward-line)
304
+ (let ((eol (save-excursion (end-of-line) (point))))
305
+ ;; If no sexps are closed,
306
+ ;; we're still continuing a multiline hash
307
+ (if (>= (car (parse-partial-sexp (point) eol)) 0)
308
+ (end-of-line)
309
+ ;; If sexps have been closed,
310
+ ;; set the point at the end of the total sexp
311
+ (goto-char beg)
312
+ (haml-limited-forward-sexp eol))))
313
+
314
+ (haml-fontify-region-as-ruby (1+ beg) (point))))
315
+ (t (cl-return-from loop))))))
316
+
317
+ ;; Move past end chars
318
+ (haml-move "[<>&!]+")
319
+ ;; Highlight script
320
+ (if (looking-at (concat "\\([=~]\\) " haml-possibly-multiline-code-re))
321
+ (haml-fontify-region-as-ruby (match-beginning 2) (match-end 2))
322
+ ;; Give font-lock something to highlight
323
+ (forward-char -1)
324
+ (looking-at "\\(\\)"))
325
+ t))
326
+
327
+ (defun haml-highlight-interpolation (limit)
328
+ "Highlight Ruby interpolation (#{foo}).
329
+ LIMIT works as it does in `re-search-forward'."
330
+ (when (re-search-forward "\\(#{\\)" limit t)
331
+ (save-match-data
332
+ (forward-char -1)
333
+ (let ((beg (point)))
334
+ (haml-limited-forward-sexp limit)
335
+ (haml-fontify-region-as-ruby (1+ beg) (point)))
336
+ (when (eq (char-before) ?\})
337
+ (put-text-property (1- (point)) (point)
338
+ 'face font-lock-variable-name-face))
339
+ t)))
340
+
341
+ (defun haml-limited-forward-sexp (limit &optional arg)
342
+ "Move forward using `forward-sexp' or to LIMIT, whichever comes first.
343
+ With ARG, do it that many times."
344
+ (let (forward-sexp-function)
345
+ (condition-case err
346
+ (save-restriction
347
+ (narrow-to-region (point) limit)
348
+ (forward-sexp arg))
349
+ (scan-error
350
+ (unless (equal (nth 1 err) "Unbalanced parentheses")
351
+ (signal 'scan-error (cdr err)))
352
+ (goto-char limit)))))
353
+
354
+ (defun haml-find-containing-block (re)
355
+ "If point is inside a block matching RE, return (start . end) for the block."
356
+ (save-excursion
357
+ (let ((pos (point))
358
+ start end)
359
+ (beginning-of-line)
360
+ (when (and
361
+ (or (looking-at re)
362
+ (when (re-search-backward re nil t)
363
+ (looking-at re)))
364
+ (< pos (match-end 0)))
365
+ (setq start (match-beginning 0)
366
+ end (match-end 0)))
367
+ (when start
368
+ (cons start end)))))
369
+
370
+ (defun haml-maybe-extend-region (extender)
371
+ "Maybe extend the font lock region using EXTENDER.
372
+ With point at the beginning of the font lock region, EXTENDER is called.
373
+ If it returns a (START . END) pair, those positions are used to possibly
374
+ extend the font lock region."
375
+ (let ((old-beg font-lock-beg)
376
+ (old-end font-lock-end))
377
+ (save-excursion
378
+ (goto-char font-lock-beg)
379
+ (let ((new-bounds (funcall extender)))
380
+ (when new-bounds
381
+ (setq font-lock-beg (min font-lock-beg (car new-bounds))
382
+ font-lock-end (max font-lock-end (cdr new-bounds))))))
383
+ (or (/= old-beg font-lock-beg)
384
+ (/= old-end font-lock-end))))
385
+
386
+ (defun haml-extend-region-nested-below ()
387
+ "Extend the font-lock region to any subsequent indented lines."
388
+ (haml-maybe-extend-region
389
+ (lambda ()
390
+ (beginning-of-line)
391
+ (when (looking-at (haml-nested-regexp "[^ \t].*"))
392
+ (cons (match-beginning 0) (match-end 0))))))
393
+
394
+ (defun haml-extend-region-to-containing-block (re)
395
+ "Extend the font-lock region to the smallest containing block matching RE."
396
+ (haml-maybe-extend-region
397
+ (lambda ()
398
+ (haml-find-containing-block re))))
399
+
400
+ (defun haml-extend-region-filter ()
401
+ "Extend the font-lock region to an enclosing filter."
402
+ (haml-extend-region-to-containing-block haml-filter-re))
403
+
404
+ (defun haml-extend-region-comment ()
405
+ "Extend the font-lock region to an enclosing comment."
406
+ (haml-extend-region-to-containing-block haml-comment-re))
407
+
408
+ (defun haml-extend-region-ruby-script ()
409
+ "Extend the font-lock region to encompass any current -/=/~ line."
410
+ (haml-extend-region-to-containing-block haml-ruby-script-re))
411
+
412
+ (defun haml-extend-region-multiline-hashes ()
413
+ "Extend the font-lock region to encompass multiline attribute hashes."
414
+ (haml-maybe-extend-region
415
+ (lambda ()
416
+ (let ((attr-props (haml-parse-multiline-attr-hash))
417
+ multiline-end
418
+ start)
419
+ (when attr-props
420
+ (setq start (cdr (assq 'point attr-props)))
421
+
422
+ (end-of-line)
423
+ ;; Move through multiline attrs
424
+ (when (eq (char-before) ?,)
425
+ (save-excursion
426
+ (while (progn (end-of-line)
427
+ (and (eq (char-before) ?,) (not (eobp))))
428
+ (forward-line))
429
+
430
+ (forward-line -1)
431
+ (end-of-line)
432
+ (setq multiline-end (point))))
433
+
434
+ (goto-char (+ (cdr (assq 'point attr-props))
435
+ (cdr (assq 'hash-indent attr-props))
436
+ -1))
437
+ (haml-limited-forward-sexp
438
+ (or multiline-end
439
+ (save-excursion (end-of-line) (point))))
440
+ (cons start (point)))))))
441
+
442
+ (defun haml-extend-region-contextual ()
443
+ "Extend the font lock region piecemeal.
444
+
445
+ The result of calling this function repeatedly until it returns
446
+ nil is that (FONT-LOCK-BEG . FONT-LOCK-END) will be the smallest
447
+ possible region in which font-locking could be affected by
448
+ changes in the initial region."
449
+ (or
450
+ (haml-extend-region-filter)
451
+ (haml-extend-region-comment)
452
+ (haml-extend-region-ruby-script)
453
+ (haml-extend-region-multiline-hashes)
454
+ (haml-extend-region-nested-below)
455
+ (font-lock-extend-region-multiline)))
456
+
457
+
458
+ ;; Mode setup
459
+
460
+ (defvar haml-mode-syntax-table
461
+ (let ((table (make-syntax-table)))
462
+ (modify-syntax-entry ?: "." table)
463
+ (modify-syntax-entry ?' "\"" table)
464
+ table)
465
+ "Syntax table in use in `haml-mode' buffers.")
466
+
467
+ (defvar haml-mode-map
468
+ (let ((map (make-sparse-keymap)))
469
+ (define-key map [backspace] 'haml-electric-backspace)
470
+ (define-key map "\C-?" 'haml-electric-backspace)
471
+ (define-key map "\C-c\C-f" 'haml-forward-sexp)
472
+ (define-key map "\C-c\C-b" 'haml-backward-sexp)
473
+ (define-key map "\C-c\C-u" 'haml-up-list)
474
+ (define-key map "\C-c\C-d" 'haml-down-list)
475
+ (define-key map "\C-c\C-k" 'haml-kill-line-and-indent)
476
+ (define-key map "\C-c\C-r" 'haml-output-region)
477
+ (define-key map "\C-c\C-l" 'haml-output-buffer)
478
+ map))
479
+
480
+ ;;;###autoload
481
+ (define-derived-mode haml-mode prog-mode "Haml"
482
+ "Major mode for editing Haml files.
483
+
484
+ \\{haml-mode-map}"
485
+ (setq font-lock-extend-region-functions '(haml-extend-region-contextual))
486
+ (set (make-local-variable 'jit-lock-contextually) t)
487
+ (set (make-local-variable 'font-lock-multiline) t)
488
+ (set (make-local-variable 'indent-line-function) 'haml-indent-line)
489
+ (set (make-local-variable 'indent-region-function) 'haml-indent-region)
490
+ (set (make-local-variable 'parse-sexp-lookup-properties) t)
491
+ (set (make-local-variable 'comment-start) "-#")
492
+ (setq font-lock-defaults '((haml-font-lock-keywords) t t))
493
+ (when (boundp 'electric-indent-inhibit)
494
+ (setq electric-indent-inhibit t))
495
+ (setq indent-tabs-mode nil))
496
+
497
+ ;; Useful functions
498
+
499
+ (defun haml-comment-block ()
500
+ "Comment the current block of Haml code."
501
+ (interactive)
502
+ (save-excursion
503
+ (let ((indent (current-indentation)))
504
+ (back-to-indentation)
505
+ (insert "-#")
506
+ (newline)
507
+ (indent-to indent)
508
+ (beginning-of-line)
509
+ (haml-mark-sexp)
510
+ (haml-reindent-region-by haml-indent-offset))))
511
+
512
+ (defun haml-uncomment-block ()
513
+ "Uncomment the current block of Haml code."
514
+ (interactive)
515
+ (save-excursion
516
+ (beginning-of-line)
517
+ (while (not (looking-at haml-comment-re))
518
+ (haml-up-list)
519
+ (beginning-of-line))
520
+ (haml-mark-sexp)
521
+ (kill-line 1)
522
+ (haml-reindent-region-by (- haml-indent-offset))))
523
+
524
+ (defun haml-replace-region (start end)
525
+ "Replace the current block of Haml code with the HTML equivalent.
526
+ Called from a program, START and END specify the region to indent."
527
+ (interactive "r")
528
+ (save-excursion
529
+ (goto-char end)
530
+ (setq end (point-marker))
531
+ (goto-char start)
532
+ (let ((ci (current-indentation)))
533
+ (while (re-search-forward "^ +" end t)
534
+ (replace-match (make-string (- (current-indentation) ci) ? ))))
535
+ (shell-command-on-region start end "haml" "haml-output" t)))
536
+
537
+ (defun haml-output-region (start end)
538
+ "Displays the HTML output for the current block of Haml code.
539
+ Called from a program, START and END specify the region to indent."
540
+ (interactive "r")
541
+ (kill-new (buffer-substring start end))
542
+ (with-temp-buffer
543
+ (yank)
544
+ (haml-indent-region (point-min) (point-max))
545
+ (shell-command-on-region (point-min) (point-max) "haml" "haml-output")))
546
+
547
+ (defun haml-output-buffer ()
548
+ "Displays the HTML output for entire buffer."
549
+ (interactive)
550
+ (haml-output-region (point-min) (point-max)))
551
+
552
+ ;; Navigation
553
+
554
+ (defun haml-forward-through-whitespace (&optional backward)
555
+ "Move the point forward through any whitespace.
556
+ The point will move forward at least one line, until it reaches
557
+ either the end of the buffer or a line with no whitespace.
558
+
559
+ If BACKWARD is non-nil, move the point backward instead."
560
+ (let ((arg (if backward -1 1))
561
+ (endp (if backward 'bobp 'eobp)))
562
+ (cl-loop do (forward-line arg)
563
+ while (and (not (funcall endp))
564
+ (looking-at "^[ \t]*$")))))
565
+
566
+ (defun haml-at-indent-p ()
567
+ "Return non-nil if the point is before any text on the line."
568
+ (let ((opoint (point)))
569
+ (save-excursion
570
+ (back-to-indentation)
571
+ (>= (point) opoint))))
572
+
573
+ (defun haml-forward-sexp (&optional arg)
574
+ "Move forward across one nested expression.
575
+ With ARG, do it that many times. Negative arg -N means move
576
+ backward across N balanced expressions.
577
+
578
+ A sexp in Haml is defined as a line of Haml code as well as any
579
+ lines nested beneath it."
580
+ (interactive "p")
581
+ (or arg (setq arg 1))
582
+ (if (and (< arg 0) (not (haml-at-indent-p)))
583
+ (back-to-indentation)
584
+ (while (/= arg 0)
585
+ (let ((indent (current-indentation)))
586
+ (cl-loop do (haml-forward-through-whitespace (< arg 0))
587
+ while (and (not (eobp))
588
+ (not (bobp))
589
+ (> (current-indentation) indent)))
590
+ (unless (eobp)
591
+ (back-to-indentation))
592
+ (setq arg (+ arg (if (> arg 0) -1 1)))))))
593
+
594
+ (defun haml-backward-sexp (&optional arg)
595
+ "Move backward across one nested expression.
596
+ With ARG, do it that many times. Negative arg -N means move
597
+ forward across N balanced expressions.
598
+
599
+ A sexp in Haml is defined as a line of Haml code as well as any
600
+ lines nested beneath it."
601
+ (interactive "p")
602
+ (haml-forward-sexp (if arg (- arg) -1)))
603
+
604
+ (defun haml-up-list (&optional arg)
605
+ "Move out of one level of nesting.
606
+ With ARG, do this that many times."
607
+ (interactive "p")
608
+ (or arg (setq arg 1))
609
+ (while (> arg 0)
610
+ (let ((indent (current-indentation)))
611
+ (cl-loop do (haml-forward-through-whitespace t)
612
+ while (and (not (bobp))
613
+ (>= (current-indentation) indent)))
614
+ (setq arg (1- arg))))
615
+ (back-to-indentation))
616
+
617
+ (defun haml-down-list (&optional arg)
618
+ "Move down one level of nesting.
619
+ With ARG, do this that many times."
620
+ (interactive "p")
621
+ (or arg (setq arg 1))
622
+ (while (> arg 0)
623
+ (let ((indent (current-indentation)))
624
+ (haml-forward-through-whitespace)
625
+ (when (<= (current-indentation) indent)
626
+ (haml-forward-through-whitespace t)
627
+ (back-to-indentation)
628
+ (error "Nothing is nested beneath this line"))
629
+ (setq arg (1- arg))))
630
+ (back-to-indentation))
631
+
632
+ (defun haml-mark-sexp ()
633
+ "Mark the next Haml block."
634
+ (let ((forward-sexp-function 'haml-forward-sexp))
635
+ (mark-sexp)))
636
+
637
+ (defun haml-mark-sexp-but-not-next-line ()
638
+ "Mark the next Haml block, but not the next line.
639
+ Put the mark at the end of the last line of the sexp rather than
640
+ the first non-whitespace character of the next line."
641
+ (haml-mark-sexp)
642
+ (set-mark
643
+ (save-excursion
644
+ (goto-char (mark))
645
+ (unless (eobp)
646
+ (forward-line -1)
647
+ (end-of-line))
648
+ (point))))
649
+
650
+ ;; Indentation and electric keys
651
+
652
+ (defvar haml-empty-elements
653
+ '("area" "base" "br" "col" "command" "embed" "hr" "img" "input"
654
+ "keygen" "link" "meta" "param" "source" "track" "wbr")
655
+ "A list of html elements which may not contain content.
656
+
657
+ See http://www.w3.org/TR/html-markup/syntax.html.")
658
+
659
+ (defun haml-unnestable-tag-p ()
660
+ "Return t if the current line is an empty element tag, or one with content."
661
+ (when (looking-at haml-tag-beg-re)
662
+ (save-excursion
663
+ (goto-char (match-end 0))
664
+ (or (string-match-p (concat "%" (regexp-opt haml-empty-elements) "\\b")
665
+ (match-string 1))
666
+ (progn
667
+ (when (looking-at "[{(]")
668
+ (ignore-errors (forward-sexp)))
669
+ (looking-at "\\(?:=\\|==\\| \\)[[:blank:]]*[^[:blank:]\r\n]+"))))))
670
+
671
+ (defun haml-indent-p ()
672
+ "Return t if the current line can have lines nested beneath it."
673
+ (let ((attr-props (haml-parse-multiline-attr-hash)))
674
+ (if attr-props
675
+ (if (haml-unclosed-attr-hash-p)
676
+ (cdr (assq 'hash-indent attr-props))
677
+ (+ (cdr (assq 'indent attr-props)) haml-indent-offset))
678
+ (unless (or (haml-unnestable-tag-p))
679
+ (cl-loop for opener in haml-block-openers
680
+ if (looking-at opener) return t
681
+ finally return nil)))))
682
+
683
+ (cl-defun haml-parse-multiline-attr-hash ()
684
+ "Parses a multiline attribute hash, and returns
685
+ an alist with the following keys:
686
+
687
+ INDENT is the indentation of the line beginning the hash.
688
+
689
+ HASH-INDENT is the indentation of the first character
690
+ within the attribute hash.
691
+
692
+ POINT is the character position at the beginning of the line
693
+ beginning the hash."
694
+ (save-excursion
695
+ (while t
696
+ (beginning-of-line)
697
+ (if (looking-at (concat haml-tag-beg-re "\\([{(]\\)"))
698
+ (progn
699
+ (goto-char (1- (match-end 0)))
700
+ (haml-limited-forward-sexp (save-excursion (end-of-line) (point)))
701
+ (cl-return-from haml-parse-multiline-attr-hash
702
+ (when (or (string-equal (match-string 1) "(") (eq (char-before) ?,))
703
+ `((indent . ,(current-indentation))
704
+ (hash-indent . ,(- (match-end 0) (match-beginning 0)))
705
+ (point . ,(match-beginning 0))))))
706
+ (when (bobp) (cl-return-from haml-parse-multiline-attr-hash))
707
+ (forward-line -1)
708
+ (unless (haml-unclosed-attr-hash-p)
709
+ (cl-return-from haml-parse-multiline-attr-hash))))))
710
+
711
+ (cl-defun haml-unclosed-attr-hash-p ()
712
+ "Return t if this line has an unclosed attribute hash, new or old."
713
+ (save-excursion
714
+ (end-of-line)
715
+ (when (eq (char-before) ?,) (cl-return-from haml-unclosed-attr-hash-p t))
716
+ (re-search-backward "(\\|^")
717
+ (haml-move "(")
718
+ (haml-parse-new-attr-hash)))
719
+
720
+ (cl-defun haml-parse-new-attr-hash (&optional (fn (lambda (type beg end) ())))
721
+ "Parse a new-style attribute hash on this line, and returns
722
+ t if it's not finished on the current line.
723
+
724
+ FN should take three parameters: TYPE, BEG, and END.
725
+ TYPE is the type of text parsed ('name or 'value)
726
+ and BEG and END delimit that text in the buffer."
727
+ (let ((eol (save-excursion (end-of-line) (point))))
728
+ (while (not (haml-move ")"))
729
+ (haml-move "[ \t]*")
730
+ (unless (haml-move "[a-z0-9_:\\-]+")
731
+ (cl-return-from haml-parse-new-attr-hash (haml-move "[ \t]*$")))
732
+ (funcall fn 'name (match-beginning 0) (match-end 0))
733
+ (haml-move "[ \t]*")
734
+ (when (haml-move "=")
735
+ (haml-move "[ \t]*")
736
+ (unless (looking-at "[\"'@a-z0-9]") (cl-return-from haml-parse-new-attr-hash))
737
+ (let ((beg (point)))
738
+ (haml-limited-forward-sexp eol)
739
+ (funcall fn 'value beg (point)))
740
+ (haml-move "[ \t]*")))
741
+ nil))
742
+
743
+ (defun haml-compute-indentation ()
744
+ "Calculate the maximum sensible indentation for the current line."
745
+ (save-excursion
746
+ (beginning-of-line)
747
+ (if (bobp) (list 0 nil)
748
+ (haml-forward-through-whitespace t)
749
+ (let ((indent (funcall haml-indent-function)))
750
+ (cond
751
+ ((consp indent) indent)
752
+ ((integerp indent) (list indent t))
753
+ (indent (list (+ (current-indentation) haml-indent-offset) nil))
754
+ (t (list (current-indentation) nil)))))))
755
+
756
+ (defun haml-indent-region (start end)
757
+ "Indent each nonblank line in the region.
758
+ This is done by indenting the first line based on
759
+ `haml-compute-indentation' and preserving the relative
760
+ indentation of the rest of the region. START and END specify the
761
+ region to indent.
762
+
763
+ If this command is used multiple times in a row, it will cycle
764
+ between possible indentations."
765
+ (save-excursion
766
+ (goto-char end)
767
+ (setq end (point-marker))
768
+ (goto-char start)
769
+ (let (this-line-column current-column
770
+ (next-line-column
771
+ (if (and (equal last-command this-command) (/= (current-indentation) 0))
772
+ (* (/ (1- (current-indentation)) haml-indent-offset) haml-indent-offset)
773
+ (car (haml-compute-indentation)))))
774
+ (while (< (point) end)
775
+ (setq this-line-column next-line-column
776
+ current-column (current-indentation))
777
+ ;; Delete whitespace chars at beginning of line
778
+ (delete-horizontal-space)
779
+ (unless (eolp)
780
+ (setq next-line-column (save-excursion
781
+ (cl-loop do (forward-line 1)
782
+ while (and (not (eobp)) (looking-at "^[ \t]*$")))
783
+ (+ this-line-column
784
+ (- (current-indentation) current-column))))
785
+ ;; Don't indent an empty line
786
+ (unless (eolp) (indent-to this-line-column)))
787
+ (forward-line 1)))
788
+ (move-marker end nil)))
789
+
790
+ (defun haml-indent-line ()
791
+ "Indent the current line.
792
+ The first time this command is used, the line will be indented to the
793
+ maximum sensible indentation. Each immediately subsequent usage will
794
+ back-dent the line by `haml-indent-offset' spaces. On reaching column
795
+ 0, it will cycle back to the maximum sensible indentation."
796
+ (interactive "*")
797
+ (let ((ci (current-indentation))
798
+ (cc (current-column)))
799
+ (cl-destructuring-bind (need strict) (haml-compute-indentation)
800
+ (save-excursion
801
+ (beginning-of-line)
802
+ (delete-horizontal-space)
803
+ (if (and (not strict) (equal last-command this-command) (/= ci 0))
804
+ (indent-to (* (/ (1- ci) haml-indent-offset) haml-indent-offset))
805
+ (indent-to need))))
806
+ (when (< (current-column) (current-indentation))
807
+ (forward-to-indentation 0))))
808
+
809
+ (defun haml-reindent-region-by (n)
810
+ "Add N spaces to the beginning of each line in the region.
811
+ If N is negative, will remove the spaces instead. Assumes all
812
+ lines in the region have indentation >= that of the first line."
813
+ (let* ((ci (current-indentation))
814
+ (indent-rx
815
+ (concat "^"
816
+ (if indent-tabs-mode
817
+ (concat (make-string (/ ci tab-width) ?\t)
818
+ (make-string (mod ci tab-width) ?\t))
819
+ (make-string ci ?\s)))))
820
+ (save-excursion
821
+ (while (re-search-forward indent-rx (mark) t)
822
+ (let ((ci (current-indentation)))
823
+ (delete-horizontal-space)
824
+ (beginning-of-line)
825
+ (indent-to (max 0 (+ ci n))))))))
826
+
827
+ (defun haml-electric-backspace (arg)
828
+ "Delete characters or back-dent the current line.
829
+ If invoked following only whitespace on a line, will back-dent
830
+ the line and all nested lines to the immediately previous
831
+ multiple of `haml-indent-offset' spaces. With ARG, do it that
832
+ many times.
833
+
834
+ Set `haml-backspace-backdents-nesting' to nil to just back-dent
835
+ the current line."
836
+ (interactive "*p")
837
+ (if (or (/= (current-indentation) (current-column))
838
+ (bolp)
839
+ (save-excursion
840
+ (beginning-of-line)
841
+ (looking-at "^[ \t]+$")))
842
+ (backward-delete-char arg)
843
+ (save-excursion
844
+ (beginning-of-line)
845
+ (unwind-protect
846
+ (progn
847
+ (if haml-backspace-backdents-nesting
848
+ (haml-mark-sexp-but-not-next-line)
849
+ (set-mark (save-excursion (end-of-line) (point))))
850
+ (haml-reindent-region-by (* (- arg) haml-indent-offset)))
851
+ (pop-mark)))
852
+ (back-to-indentation)))
853
+
854
+ (defun haml-kill-line-and-indent ()
855
+ "Kill the current line, and re-indent all lines nested beneath it."
856
+ (interactive)
857
+ (beginning-of-line)
858
+ (haml-mark-sexp-but-not-next-line)
859
+ (kill-line 1)
860
+ (haml-reindent-region-by (* -1 haml-indent-offset)))
861
+
862
+ (defun haml-indent-string ()
863
+ "Return the indentation string for `haml-indent-offset'."
864
+ (mapconcat 'identity (make-list haml-indent-offset " ") ""))
865
+
866
+ ;;;###autoload
867
+ (add-to-list 'auto-mode-alist '("\\.haml\\'" . haml-mode))
868
+
869
+
870
+ ;; Local Variables:
871
+ ;; coding: utf-8
872
+ ;; byte-compile-warnings: (not cl-functions)
873
+ ;; eval: (checkdoc-minor-mode 1)
874
+ ;; End:
875
+
876
+ (provide 'haml-mode)
877
+ ;;; haml-mode.el ends here