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