100329
|
1 ;;; ruby-mode.el --- Major mode for editing Ruby files
|
|
2
|
|
3 ;; Copyright (C) 1994, 1995, 1996 1997, 1998, 1999, 2000, 2001,
|
|
4 ;; 2002,2003, 2004, 2005, 2006, 2007, 2008
|
|
5 ;; Free Software Foundation, Inc.
|
|
6
|
|
7 ;; Authors: Yukihiro Matsumoto, Nobuyoshi Nakada
|
|
8 ;; URL: http://www.emacswiki.org/cgi-bin/wiki/RubyMode
|
|
9 ;; Created: Fri Feb 4 14:49:13 JST 1994
|
|
10 ;; Keywords: languages ruby
|
|
11 ;; Version: 1.0
|
|
12
|
|
13 ;; This file is part of GNU Emacs.
|
|
14
|
|
15 ;; GNU Emacs is free software: you can redistribute it and/or modify
|
|
16 ;; it under the terms of the GNU General Public License as published by
|
|
17 ;; the Free Software Foundation, either version 3 of the License, or
|
|
18 ;; (at your option) any later version.
|
|
19
|
|
20 ;; GNU Emacs is distributed in the hope that it will be useful,
|
|
21 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
23 ;; GNU General Public License for more details.
|
|
24
|
|
25 ;; You should have received a copy of the GNU General Public License
|
|
26 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
|
27
|
|
28 ;;; Commentary:
|
|
29
|
|
30 ;; Provides font-locking, indentation support, and navigation for Ruby code.
|
|
31 ;;
|
|
32 ;; If you're installing manually, you should add this to your .emacs
|
|
33 ;; file after putting it on your load path:
|
|
34 ;;
|
|
35 ;; (autoload 'ruby-mode "ruby-mode" "Major mode for ruby files" t)
|
|
36 ;; (add-to-list 'auto-mode-alist '("\\.rb$" . ruby-mode))
|
|
37 ;; (add-to-list 'interpreter-mode-alist '("ruby" . ruby-mode))
|
|
38 ;;
|
|
39 ;; Still needs more docstrings; search below for TODO.
|
|
40
|
|
41 ;;; Code:
|
|
42
|
|
43 (eval-when-compile (require 'cl))
|
|
44
|
|
45 (defconst ruby-keyword-end-re
|
|
46 (if (string-match "\\_>" "ruby")
|
|
47 "\\_>"
|
|
48 "\\>"))
|
|
49
|
|
50 (defconst ruby-block-beg-keywords
|
|
51 '("class" "module" "def" "if" "unless" "case" "while" "until" "for" "begin" "do")
|
|
52 "Keywords at the beginning of blocks.")
|
|
53
|
|
54 (defconst ruby-block-beg-re
|
|
55 (regexp-opt ruby-block-beg-keywords)
|
|
56 "Regexp to match the beginning of blocks.")
|
|
57
|
|
58 (defconst ruby-non-block-do-re
|
|
59 (concat (regexp-opt '("while" "until" "for" "rescue") t) ruby-keyword-end-re)
|
|
60 "Regexp to match keywords that nest without blocks.")
|
|
61
|
|
62 (defconst ruby-indent-beg-re
|
|
63 (concat "\\(\\s *" (regexp-opt '("class" "module" "def") t) "\\)\\|"
|
|
64 (regexp-opt '("if" "unless" "case" "while" "until" "for" "begin")))
|
|
65 "Regexp to match where the indentation gets deeper.")
|
|
66
|
|
67 (defconst ruby-modifier-beg-keywords
|
|
68 '("if" "unless" "while" "until")
|
|
69 "Modifiers that are the same as the beginning of blocks.")
|
|
70
|
|
71 (defconst ruby-modifier-beg-re
|
|
72 (regexp-opt ruby-modifier-beg-keywords)
|
|
73 "Regexp to match modifiers same as the beginning of blocks.")
|
|
74
|
|
75 (defconst ruby-modifier-re
|
|
76 (regexp-opt (cons "rescue" ruby-modifier-beg-keywords))
|
|
77 "Regexp to match modifiers.")
|
|
78
|
|
79 (defconst ruby-block-mid-keywords
|
|
80 '("then" "else" "elsif" "when" "rescue" "ensure")
|
|
81 "Keywords where the indentation gets shallower in middle of block statements.")
|
|
82
|
|
83 (defconst ruby-block-mid-re
|
|
84 (regexp-opt ruby-block-mid-keywords)
|
|
85 "Regexp to match where the indentation gets shallower in middle of block statements.")
|
|
86
|
|
87 (defconst ruby-block-op-keywords
|
|
88 '("and" "or" "not")
|
|
89 "Regexp to match boolean keywords.")
|
|
90
|
|
91 (defconst ruby-block-hanging-re
|
|
92 (regexp-opt (append ruby-modifier-beg-keywords ruby-block-op-keywords))
|
|
93 "Regexp to match hanging block modifiers.")
|
|
94
|
|
95 (defconst ruby-block-end-re "\\<end\\>")
|
|
96
|
|
97 (defconst ruby-here-doc-beg-re
|
|
98 "\\(<\\)<\\(-\\)?\\(\\([a-zA-Z0-9_]+\\)\\|[\"]\\([^\"]+\\)[\"]\\|[']\\([^']+\\)[']\\)"
|
|
99 "Regexp to match the beginning of a heredoc.")
|
|
100
|
|
101 (defconst ruby-here-doc-end-re
|
|
102 "^\\([ \t]+\\)?\\(.*\\)\\(.\\)$"
|
|
103 "Regexp to match the end of heredocs.
|
|
104
|
|
105 This will actually match any line with one or more characters.
|
|
106 It's useful in that it divides up the match string so that
|
|
107 `ruby-here-doc-beg-match' can search for the beginning of the heredoc.")
|
|
108
|
|
109 (defun ruby-here-doc-end-match ()
|
|
110 "Return a regexp to find the end of a heredoc.
|
|
111
|
|
112 This should only be called after matching against `ruby-here-doc-beg-re'."
|
|
113 (concat "^"
|
|
114 (if (match-string 2) "[ \t]*" nil)
|
|
115 (regexp-quote
|
|
116 (or (match-string 4)
|
|
117 (match-string 5)
|
|
118 (match-string 6)))))
|
|
119
|
|
120 (defun ruby-here-doc-beg-match ()
|
|
121 "Return a regexp to find the beginning of a heredoc.
|
|
122
|
|
123 This should only be called after matching against `ruby-here-doc-end-re'."
|
|
124 (let ((contents (regexp-quote (concat (match-string 2) (match-string 3)))))
|
|
125 (concat "<<"
|
|
126 (let ((match (match-string 1)))
|
|
127 (if (and match (> (length match) 0))
|
|
128 (concat "\\(?:-\\([\"']?\\)\\|\\([\"']\\)" (match-string 1) "\\)"
|
|
129 contents "\\b\\(\\1\\|\\2\\)")
|
|
130 (concat "-?\\([\"']\\|\\)" contents "\\b\\1"))))))
|
|
131
|
|
132 (defconst ruby-delimiter
|
|
133 (concat "[?$/%(){}#\"'`.:]\\|<<\\|\\[\\|\\]\\|\\<\\("
|
|
134 ruby-block-beg-re
|
|
135 "\\)\\>\\|" ruby-block-end-re
|
|
136 "\\|^=begin\\|" ruby-here-doc-beg-re))
|
|
137
|
|
138 (defconst ruby-negative
|
|
139 (concat "^[ \t]*\\(\\(" ruby-block-mid-re "\\)\\>\\|"
|
|
140 ruby-block-end-re "\\|}\\|\\]\\)")
|
|
141 "Regexp to match where the indentation gets shallower.")
|
|
142
|
|
143 (defconst ruby-operator-re "[-,.+*/%&|^~=<>:]"
|
|
144 "Regexp to match operators.")
|
|
145
|
|
146 (defconst ruby-symbol-chars "a-zA-Z0-9_"
|
|
147 "List of characters that symbol names may contain.")
|
|
148 (defconst ruby-symbol-re (concat "[" ruby-symbol-chars "]")
|
|
149 "Regexp to match symbols.")
|
|
150
|
|
151 (defvar ruby-mode-abbrev-table nil
|
|
152 "Abbrev table in use in ruby-mode buffers.")
|
|
153
|
|
154 (define-abbrev-table 'ruby-mode-abbrev-table ())
|
|
155
|
|
156 (defvar ruby-mode-map
|
|
157 (let ((map (make-sparse-keymap)))
|
|
158 (define-key map "{" 'ruby-electric-brace)
|
|
159 (define-key map "}" 'ruby-electric-brace)
|
|
160 (define-key map (kbd "M-C-a") 'ruby-beginning-of-defun)
|
|
161 (define-key map (kbd "M-C-e") 'ruby-end-of-defun)
|
|
162 (define-key map (kbd "M-C-b") 'ruby-backward-sexp)
|
|
163 (define-key map (kbd "M-C-f") 'ruby-forward-sexp)
|
|
164 (define-key map (kbd "M-C-p") 'ruby-beginning-of-block)
|
|
165 (define-key map (kbd "M-C-n") 'ruby-end-of-block)
|
|
166 (define-key map (kbd "M-C-h") 'ruby-mark-defun)
|
|
167 (define-key map (kbd "M-C-q") 'ruby-indent-exp)
|
|
168 (define-key map (kbd "TAB") 'ruby-indent-line)
|
|
169 (define-key map (kbd "C-M-h") 'backward-kill-word)
|
|
170 (define-key map (kbd "C-j") 'reindent-then-newline-and-indent)
|
|
171 (define-key map (kbd "C-m") 'newline)
|
|
172 map)
|
|
173 "Keymap used in ruby-mode.")
|
|
174
|
|
175 (defvar ruby-mode-syntax-table
|
|
176 (let ((table (make-syntax-table)))
|
|
177 (modify-syntax-entry ?\' "\"" table)
|
|
178 (modify-syntax-entry ?\" "\"" table)
|
|
179 (modify-syntax-entry ?\` "\"" table)
|
|
180 (modify-syntax-entry ?# "<" table)
|
|
181 (modify-syntax-entry ?\n ">" table)
|
|
182 (modify-syntax-entry ?\\ "\\" table)
|
|
183 (modify-syntax-entry ?$ "." table)
|
|
184 (modify-syntax-entry ?? "_" table)
|
|
185 (modify-syntax-entry ?_ "_" table)
|
|
186 (modify-syntax-entry ?< "." table)
|
|
187 (modify-syntax-entry ?> "." table)
|
|
188 (modify-syntax-entry ?& "." table)
|
|
189 (modify-syntax-entry ?| "." table)
|
|
190 (modify-syntax-entry ?% "." table)
|
|
191 (modify-syntax-entry ?= "." table)
|
|
192 (modify-syntax-entry ?/ "." table)
|
|
193 (modify-syntax-entry ?+ "." table)
|
|
194 (modify-syntax-entry ?* "." table)
|
|
195 (modify-syntax-entry ?- "." table)
|
|
196 (modify-syntax-entry ?\; "." table)
|
|
197 (modify-syntax-entry ?\( "()" table)
|
|
198 (modify-syntax-entry ?\) ")(" table)
|
|
199 (modify-syntax-entry ?\{ "(}" table)
|
|
200 (modify-syntax-entry ?\} "){" table)
|
|
201 (modify-syntax-entry ?\[ "(]" table)
|
|
202 (modify-syntax-entry ?\] ")[" table)
|
|
203 table)
|
|
204 "Syntax table to use in ruby-mode.")
|
|
205
|
|
206 (defcustom ruby-indent-tabs-mode nil
|
|
207 "Indentation can insert tabs in ruby mode if this is non-nil."
|
|
208 :type 'boolean :group 'ruby)
|
|
209
|
|
210 (defcustom ruby-indent-level 2
|
|
211 "Indentation of ruby statements."
|
|
212 :type 'integer :group 'ruby)
|
|
213
|
|
214 (defcustom ruby-comment-column 32
|
|
215 "Indentation column of comments."
|
|
216 :type 'integer :group 'ruby)
|
|
217
|
|
218 (defcustom ruby-deep-arglist t
|
|
219 "Deep indent lists in parenthesis when non-nil.
|
|
220 Also ignores spaces after parenthesis when 'space."
|
|
221 :group 'ruby)
|
|
222
|
|
223 (defcustom ruby-deep-indent-paren '(?\( ?\[ ?\] t)
|
|
224 "Deep indent lists in parenthesis when non-nil. t means continuous line.
|
|
225 Also ignores spaces after parenthesis when 'space."
|
|
226 :group 'ruby)
|
|
227
|
|
228 (defcustom ruby-deep-indent-paren-style 'space
|
|
229 "Default deep indent style."
|
|
230 :options '(t nil space) :group 'ruby)
|
|
231
|
|
232 (defcustom ruby-encoding-map '((shift_jis . cp932) (shift-jis . cp932))
|
|
233 "Alist to map encoding name from Emacs to Ruby."
|
|
234 :group 'ruby)
|
|
235
|
|
236 (defcustom ruby-insert-encoding-magic-comment t
|
|
237 "*Insert a magic emacs 'coding' comment upon save if this is non-nil."
|
|
238 :type 'boolean :group 'ruby)
|
|
239
|
|
240 (defcustom ruby-use-encoding-map t
|
|
241 "Use `ruby-encoding-map' to set encoding magic comment if this is non-nil."
|
|
242 :type 'boolean :group 'ruby)
|
|
243
|
|
244 ;; Safe file variables
|
|
245 (put 'ruby-indent-tabs-mode 'safe-local-variable 'booleanp)
|
|
246 (put 'ruby-indent-level 'safe-local-variable 'integerp)
|
|
247 (put 'ruby-comment-column 'safe-local-variable 'integerp)
|
|
248 (put 'ruby-deep-arglist 'safe-local-variable 'booleanp)
|
|
249
|
|
250 (defun ruby-imenu-create-index-in-block (prefix beg end)
|
|
251 "Create an imenu index of methods inside a block."
|
|
252 (let ((index-alist '()) (case-fold-search nil)
|
|
253 name next pos decl sing)
|
|
254 (goto-char beg)
|
|
255 (while (re-search-forward "^\\s *\\(\\(class\\s +\\|\\(class\\s *<<\\s *\\)\\|module\\s +\\)\\([^\(<\n ]+\\)\\|\\(def\\|alias\\)\\s +\\([^\(\n ]+\\)\\)" end t)
|
|
256 (setq sing (match-beginning 3))
|
|
257 (setq decl (match-string 5))
|
|
258 (setq next (match-end 0))
|
|
259 (setq name (or (match-string 4) (match-string 6)))
|
|
260 (setq pos (match-beginning 0))
|
|
261 (cond
|
|
262 ((string= "alias" decl)
|
|
263 (if prefix (setq name (concat prefix name)))
|
|
264 (push (cons name pos) index-alist))
|
|
265 ((string= "def" decl)
|
|
266 (if prefix
|
|
267 (setq name
|
|
268 (cond
|
|
269 ((string-match "^self\." name)
|
|
270 (concat (substring prefix 0 -1) (substring name 4)))
|
|
271 (t (concat prefix name)))))
|
|
272 (push (cons name pos) index-alist)
|
|
273 (ruby-accurate-end-of-block end))
|
|
274 (t
|
|
275 (if (string= "self" name)
|
|
276 (if prefix (setq name (substring prefix 0 -1)))
|
|
277 (if prefix (setq name (concat (substring prefix 0 -1) "::" name)))
|
|
278 (push (cons name pos) index-alist))
|
|
279 (ruby-accurate-end-of-block end)
|
|
280 (setq beg (point))
|
|
281 (setq index-alist
|
|
282 (nconc (ruby-imenu-create-index-in-block
|
|
283 (concat name (if sing "." "#"))
|
|
284 next beg) index-alist))
|
|
285 (goto-char beg))))
|
|
286 index-alist))
|
|
287
|
|
288 (defun ruby-imenu-create-index ()
|
|
289 "Create an imenu index of all methods in the buffer."
|
|
290 (nreverse (ruby-imenu-create-index-in-block nil (point-min) nil)))
|
|
291
|
|
292 (defun ruby-accurate-end-of-block (&optional end)
|
|
293 "TODO: document."
|
|
294 (let (state
|
|
295 (end (or end (point-max))))
|
|
296 (while (and (setq state (apply 'ruby-parse-partial end state))
|
|
297 (>= (nth 2 state) 0) (< (point) end)))))
|
|
298
|
|
299 (defun ruby-mode-variables ()
|
|
300 "Set up initial buffer-local variables for ruby-mode."
|
|
301 (set-syntax-table ruby-mode-syntax-table)
|
|
302 (setq local-abbrev-table ruby-mode-abbrev-table)
|
|
303 (setq indent-tabs-mode ruby-indent-tabs-mode)
|
|
304 (set (make-local-variable 'indent-line-function) 'ruby-indent-line)
|
|
305 (set (make-local-variable 'require-final-newline) t)
|
|
306 (set (make-local-variable 'comment-start) "# ")
|
|
307 (set (make-local-variable 'comment-end) "")
|
|
308 (set (make-local-variable 'comment-column) ruby-comment-column)
|
|
309 (set (make-local-variable 'comment-start-skip) "#+ *")
|
|
310 (set (make-local-variable 'parse-sexp-ignore-comments) t)
|
|
311 (set (make-local-variable 'parse-sexp-lookup-properties) t)
|
|
312 (set (make-local-variable 'paragraph-start) (concat "$\\|" page-delimiter))
|
|
313 (set (make-local-variable 'paragraph-separate) paragraph-start)
|
|
314 (set (make-local-variable 'paragraph-ignore-fill-prefix) t))
|
|
315
|
|
316 (defun ruby-mode-set-encoding ()
|
|
317 "Insert a magic comment header with the proper encoding if necessary."
|
|
318 (save-excursion
|
|
319 (widen)
|
|
320 (goto-char (point-min))
|
|
321 (when (re-search-forward "[^\0-\177]" nil t)
|
|
322 (goto-char (point-min))
|
|
323 (let ((coding-system
|
|
324 (or coding-system-for-write
|
|
325 buffer-file-coding-system)))
|
|
326 (if coding-system
|
|
327 (setq coding-system
|
|
328 (or (coding-system-get coding-system 'mime-charset)
|
|
329 (coding-system-change-eol-conversion coding-system nil))))
|
|
330 (setq coding-system
|
|
331 (if coding-system
|
|
332 (symbol-name
|
|
333 (or (and ruby-use-encoding-map
|
|
334 (cdr (assq coding-system ruby-encoding-map)))
|
|
335 coding-system))
|
|
336 "ascii-8bit"))
|
|
337 (if (looking-at "^#![^\n]*ruby") (beginning-of-line 2))
|
|
338 (cond ((looking-at "\\s *#.*-\*-\\s *\\(en\\)?coding\\s *:\\s *\\([-a-z0-9_]*\\)\\s *\\(;\\|-\*-\\)")
|
|
339 (unless (string= (match-string 2) coding-system)
|
|
340 (goto-char (match-beginning 2))
|
|
341 (delete-region (point) (match-end 2))
|
|
342 (and (looking-at "-\*-")
|
|
343 (let ((n (skip-chars-backward " ")))
|
|
344 (cond ((= n 0) (insert " ") (backward-char))
|
|
345 ((= n -1) (insert " "))
|
|
346 ((forward-char)))))
|
|
347 (insert coding-system)))
|
|
348 ((looking-at "\\s *#.*coding\\s *[:=]"))
|
|
349 (t (when ruby-insert-encoding-magic-comment
|
|
350 (insert "# -*- coding: " coding-system " -*-\n"))))))))
|
|
351
|
|
352 (defun ruby-current-indentation ()
|
|
353 "Return the indentation level of current line."
|
|
354 (save-excursion
|
|
355 (beginning-of-line)
|
|
356 (back-to-indentation)
|
|
357 (current-column)))
|
|
358
|
|
359 (defun ruby-indent-line (&optional flag)
|
|
360 "Correct the indentation of the current ruby line."
|
|
361 (interactive)
|
|
362 (ruby-indent-to (ruby-calculate-indent)))
|
|
363
|
|
364 (defun ruby-indent-to (column)
|
|
365 "Indent the current line to COLUMN."
|
|
366 (when column
|
|
367 (let (shift top beg)
|
|
368 (and (< column 0) (error "invalid nest"))
|
|
369 (setq shift (current-column))
|
|
370 (beginning-of-line)
|
|
371 (setq beg (point))
|
|
372 (back-to-indentation)
|
|
373 (setq top (current-column))
|
|
374 (skip-chars-backward " \t")
|
|
375 (if (>= shift top) (setq shift (- shift top))
|
|
376 (setq shift 0))
|
|
377 (if (and (bolp)
|
|
378 (= column top))
|
|
379 (move-to-column (+ column shift))
|
|
380 (move-to-column top)
|
|
381 (delete-region beg (point))
|
|
382 (beginning-of-line)
|
|
383 (indent-to column)
|
|
384 (move-to-column (+ column shift))))))
|
|
385
|
|
386 (defun ruby-special-char-p (&optional pos)
|
|
387 "Return t if the character before POS is a special character.
|
|
388 If omitted, POS defaults to the current point.
|
|
389 Special characters are `?', `$', `:' when preceded by whitespace,
|
|
390 and `\\' when preceded by `?'."
|
|
391 (setq pos (or pos (point)))
|
|
392 (let ((c (char-before pos)) (b (and (< (point-min) pos)
|
|
393 (char-before (1- pos)))))
|
|
394 (cond ((or (eq c ??) (eq c ?$)))
|
|
395 ((and (eq c ?:) (or (not b) (eq (char-syntax b) ? ))))
|
|
396 ((eq c ?\\) (eq b ??)))))
|
|
397
|
|
398 (defun ruby-expr-beg (&optional option)
|
|
399 "TODO: document."
|
|
400 (save-excursion
|
|
401 (store-match-data nil)
|
|
402 (let ((space (skip-chars-backward " \t"))
|
|
403 (start (point)))
|
|
404 (cond
|
|
405 ((bolp) t)
|
|
406 ((progn
|
|
407 (forward-char -1)
|
|
408 (and (looking-at "\\?")
|
|
409 (or (eq (char-syntax (char-before (point))) ?w)
|
|
410 (ruby-special-char-p))))
|
|
411 nil)
|
|
412 ((and (eq option 'heredoc) (< space 0)) t)
|
|
413 ((or (looking-at ruby-operator-re)
|
|
414 (looking-at "[\\[({,;]")
|
|
415 (and (looking-at "[!?]")
|
|
416 (or (not (eq option 'modifier))
|
|
417 (bolp)
|
|
418 (save-excursion (forward-char -1) (looking-at "\\Sw$"))))
|
|
419 (and (looking-at ruby-symbol-re)
|
|
420 (skip-chars-backward ruby-symbol-chars)
|
|
421 (cond
|
|
422 ((looking-at (regexp-opt
|
|
423 (append ruby-block-beg-keywords
|
|
424 ruby-block-op-keywords
|
|
425 ruby-block-mid-keywords)
|
|
426 'words))
|
|
427 (goto-char (match-end 0))
|
|
428 (not (looking-at "\\s_")))
|
|
429 ((eq option 'expr-qstr)
|
|
430 (looking-at "[a-zA-Z][a-zA-z0-9_]* +%[^ \t]"))
|
|
431 ((eq option 'expr-re)
|
|
432 (looking-at "[a-zA-Z][a-zA-z0-9_]* +/[^ \t]"))
|
|
433 (t nil)))))))))
|
|
434
|
|
435 (defun ruby-forward-string (term &optional end no-error expand)
|
|
436 "TODO: document."
|
|
437 (let ((n 1) (c (string-to-char term))
|
|
438 (re (if expand
|
|
439 (concat "[^\\]\\(\\\\\\\\\\)*\\([" term "]\\|\\(#{\\)\\)")
|
|
440 (concat "[^\\]\\(\\\\\\\\\\)*[" term "]"))))
|
|
441 (while (and (re-search-forward re end no-error)
|
|
442 (if (match-beginning 3)
|
|
443 (ruby-forward-string "}{" end no-error nil)
|
|
444 (> (setq n (if (eq (char-before (point)) c)
|
|
445 (1- n) (1+ n))) 0)))
|
|
446 (forward-char -1))
|
|
447 (cond ((zerop n))
|
|
448 (no-error nil)
|
|
449 ((error "unterminated string")))))
|
|
450
|
|
451 (defun ruby-deep-indent-paren-p (c)
|
|
452 "TODO: document."
|
|
453 (cond ((listp ruby-deep-indent-paren)
|
|
454 (let ((deep (assoc c ruby-deep-indent-paren)))
|
|
455 (cond (deep
|
|
456 (or (cdr deep) ruby-deep-indent-paren-style))
|
|
457 ((memq c ruby-deep-indent-paren)
|
|
458 ruby-deep-indent-paren-style))))
|
|
459 ((eq c ruby-deep-indent-paren) ruby-deep-indent-paren-style)
|
|
460 ((eq c ?\( ) ruby-deep-arglist)))
|
|
461
|
|
462 (defun ruby-parse-partial (&optional end in-string nest depth pcol indent)
|
|
463 "TODO: document throughout function body."
|
|
464 (or depth (setq depth 0))
|
|
465 (or indent (setq indent 0))
|
|
466 (when (re-search-forward ruby-delimiter end 'move)
|
|
467 (let ((pnt (point)) w re expand)
|
|
468 (goto-char (match-beginning 0))
|
|
469 (cond
|
|
470 ((and (memq (char-before) '(?@ ?$)) (looking-at "\\sw"))
|
|
471 (goto-char pnt))
|
|
472 ((looking-at "[\"`]") ;skip string
|
|
473 (cond
|
|
474 ((and (not (eobp))
|
|
475 (ruby-forward-string (buffer-substring (point) (1+ (point))) end t t))
|
|
476 nil)
|
|
477 (t
|
|
478 (setq in-string (point))
|
|
479 (goto-char end))))
|
|
480 ((looking-at "'")
|
|
481 (cond
|
|
482 ((and (not (eobp))
|
|
483 (re-search-forward "[^\\]\\(\\\\\\\\\\)*'" end t))
|
|
484 nil)
|
|
485 (t
|
|
486 (setq in-string (point))
|
|
487 (goto-char end))))
|
|
488 ((looking-at "/=")
|
|
489 (goto-char pnt))
|
|
490 ((looking-at "/")
|
|
491 (cond
|
|
492 ((and (not (eobp)) (ruby-expr-beg 'expr-re))
|
|
493 (if (ruby-forward-string "/" end t t)
|
|
494 nil
|
|
495 (setq in-string (point))
|
|
496 (goto-char end)))
|
|
497 (t
|
|
498 (goto-char pnt))))
|
|
499 ((looking-at "%")
|
|
500 (cond
|
|
501 ((and (not (eobp))
|
|
502 (ruby-expr-beg 'expr-qstr)
|
|
503 (not (looking-at "%="))
|
|
504 (looking-at "%[QqrxWw]?\\([^a-zA-Z0-9 \t\n]\\)"))
|
|
505 (goto-char (match-beginning 1))
|
|
506 (setq expand (not (memq (char-before) '(?q ?w))))
|
|
507 (setq w (match-string 1))
|
|
508 (cond
|
|
509 ((string= w "[") (setq re "]["))
|
|
510 ((string= w "{") (setq re "}{"))
|
|
511 ((string= w "(") (setq re ")("))
|
|
512 ((string= w "<") (setq re "><"))
|
|
513 ((and expand (string= w "\\"))
|
|
514 (setq w (concat "\\" w))))
|
|
515 (unless (cond (re (ruby-forward-string re end t expand))
|
|
516 (expand (ruby-forward-string w end t t))
|
|
517 (t (re-search-forward
|
|
518 (if (string= w "\\")
|
|
519 "\\\\[^\\]*\\\\"
|
|
520 (concat "[^\\]\\(\\\\\\\\\\)*" w))
|
|
521 end t)))
|
|
522 (setq in-string (point))
|
|
523 (goto-char end)))
|
|
524 (t
|
|
525 (goto-char pnt))))
|
|
526 ((looking-at "\\?") ;skip ?char
|
|
527 (cond
|
|
528 ((and (ruby-expr-beg)
|
|
529 (looking-at "?\\(\\\\C-\\|\\\\M-\\)*\\\\?."))
|
|
530 (goto-char (match-end 0)))
|
|
531 (t
|
|
532 (goto-char pnt))))
|
|
533 ((looking-at "\\$") ;skip $char
|
|
534 (goto-char pnt)
|
|
535 (forward-char 1))
|
|
536 ((looking-at "#") ;skip comment
|
|
537 (forward-line 1)
|
|
538 (goto-char (point))
|
|
539 )
|
|
540 ((looking-at "[\\[{(]")
|
|
541 (let ((deep (ruby-deep-indent-paren-p (char-after))))
|
|
542 (if (and deep (or (not (eq (char-after) ?\{)) (ruby-expr-beg)))
|
|
543 (progn
|
|
544 (and (eq deep 'space) (looking-at ".\\s +[^# \t\n]")
|
|
545 (setq pnt (1- (match-end 0))))
|
|
546 (setq nest (cons (cons (char-after (point)) pnt) nest))
|
|
547 (setq pcol (cons (cons pnt depth) pcol))
|
|
548 (setq depth 0))
|
|
549 (setq nest (cons (cons (char-after (point)) pnt) nest))
|
|
550 (setq depth (1+ depth))))
|
|
551 (goto-char pnt)
|
|
552 )
|
|
553 ((looking-at "[])}]")
|
|
554 (if (ruby-deep-indent-paren-p (matching-paren (char-after)))
|
|
555 (setq depth (cdr (car pcol)) pcol (cdr pcol))
|
|
556 (setq depth (1- depth)))
|
|
557 (setq nest (cdr nest))
|
|
558 (goto-char pnt))
|
|
559 ((looking-at ruby-block-end-re)
|
|
560 (if (or (and (not (bolp))
|
|
561 (progn
|
|
562 (forward-char -1)
|
|
563 (setq w (char-after (point)))
|
|
564 (or (eq ?_ w)
|
|
565 (eq ?. w))))
|
|
566 (progn
|
|
567 (goto-char pnt)
|
|
568 (setq w (char-after (point)))
|
|
569 (or (eq ?_ w)
|
|
570 (eq ?! w)
|
|
571 (eq ?? w))))
|
|
572 nil
|
|
573 (setq nest (cdr nest))
|
|
574 (setq depth (1- depth)))
|
|
575 (goto-char pnt))
|
|
576 ((looking-at "def\\s +[^(\n;]*")
|
|
577 (if (or (bolp)
|
|
578 (progn
|
|
579 (forward-char -1)
|
|
580 (not (eq ?_ (char-after (point))))))
|
|
581 (progn
|
|
582 (setq nest (cons (cons nil pnt) nest))
|
|
583 (setq depth (1+ depth))))
|
|
584 (goto-char (match-end 0)))
|
|
585 ((looking-at (concat "\\<\\(" ruby-block-beg-re "\\)\\>"))
|
|
586 (and
|
|
587 (save-match-data
|
|
588 (or (not (looking-at (concat "do" ruby-keyword-end-re)))
|
|
589 (save-excursion
|
|
590 (back-to-indentation)
|
|
591 (not (looking-at ruby-non-block-do-re)))))
|
|
592 (or (bolp)
|
|
593 (progn
|
|
594 (forward-char -1)
|
|
595 (setq w (char-after (point)))
|
|
596 (not (or (eq ?_ w)
|
|
597 (eq ?. w)))))
|
|
598 (goto-char pnt)
|
|
599 (setq w (char-after (point)))
|
|
600 (not (eq ?_ w))
|
|
601 (not (eq ?! w))
|
|
602 (not (eq ?? w))
|
|
603 (skip-chars-forward " \t")
|
|
604 (goto-char (match-beginning 0))
|
|
605 (or (not (looking-at ruby-modifier-re))
|
|
606 (ruby-expr-beg 'modifier))
|
|
607 (goto-char pnt)
|
|
608 (setq nest (cons (cons nil pnt) nest))
|
|
609 (setq depth (1+ depth)))
|
|
610 (goto-char pnt))
|
|
611 ((looking-at ":\\(['\"]\\)")
|
|
612 (goto-char (match-beginning 1))
|
|
613 (ruby-forward-string (buffer-substring (match-beginning 1) (match-end 1)) end))
|
|
614 ((looking-at ":\\([-,.+*/%&|^~<>]=?\\|===?\\|<=>\\)")
|
|
615 (goto-char (match-end 0)))
|
|
616 ((looking-at ":\\([a-zA-Z_][a-zA-Z_0-9]*[!?=]?\\)?")
|
|
617 (goto-char (match-end 0)))
|
|
618 ((or (looking-at "\\.\\.\\.?")
|
|
619 (looking-at "\\.[0-9]+")
|
|
620 (looking-at "\\.[a-zA-Z_0-9]+")
|
|
621 (looking-at "\\."))
|
|
622 (goto-char (match-end 0)))
|
|
623 ((looking-at "^=begin")
|
|
624 (if (re-search-forward "^=end" end t)
|
|
625 (forward-line 1)
|
|
626 (setq in-string (match-end 0))
|
|
627 (goto-char end)))
|
|
628 ((looking-at "<<")
|
|
629 (cond
|
|
630 ((and (ruby-expr-beg 'heredoc)
|
|
631 (looking-at "<<\\(-\\)?\\(\\([\"'`]\\)\\([^\n]+?\\)\\3\\|\\(?:\\sw\\|\\s_\\)+\\)"))
|
|
632 (setq re (regexp-quote (or (match-string 4) (match-string 2))))
|
|
633 (if (match-beginning 1) (setq re (concat "\\s *" re)))
|
|
634 (let* ((id-end (goto-char (match-end 0)))
|
|
635 (line-end-position (save-excursion (end-of-line) (point)))
|
|
636 (state (list in-string nest depth pcol indent)))
|
|
637 ;; parse the rest of the line
|
|
638 (while (and (> line-end-position (point))
|
|
639 (setq state (apply 'ruby-parse-partial
|
|
640 line-end-position state))))
|
|
641 (setq in-string (car state)
|
|
642 nest (nth 1 state)
|
|
643 depth (nth 2 state)
|
|
644 pcol (nth 3 state)
|
|
645 indent (nth 4 state))
|
|
646 ;; skip heredoc section
|
|
647 (if (re-search-forward (concat "^" re "$") end 'move)
|
|
648 (forward-line 1)
|
|
649 (setq in-string id-end)
|
|
650 (goto-char end))))
|
|
651 (t
|
|
652 (goto-char pnt))))
|
|
653 ((looking-at "^__END__$")
|
|
654 (goto-char pnt))
|
|
655 ((and (looking-at ruby-here-doc-beg-re)
|
|
656 (boundp 'ruby-indent-point))
|
|
657 (if (re-search-forward (ruby-here-doc-end-match)
|
|
658 ruby-indent-point t)
|
|
659 (forward-line 1)
|
|
660 (setq in-string (match-end 0))
|
|
661 (goto-char ruby-indent-point)))
|
|
662 (t
|
|
663 (error (format "bad string %s"
|
|
664 (buffer-substring (point) pnt)
|
|
665 ))))))
|
|
666 (list in-string nest depth pcol))
|
|
667
|
|
668 (defun ruby-parse-region (start end)
|
|
669 "TODO: document."
|
|
670 (let (state)
|
|
671 (save-excursion
|
|
672 (if start
|
|
673 (goto-char start)
|
|
674 (ruby-beginning-of-indent))
|
|
675 (save-restriction
|
|
676 (narrow-to-region (point) end)
|
|
677 (while (and (> end (point))
|
|
678 (setq state (apply 'ruby-parse-partial end state))))))
|
|
679 (list (nth 0 state) ; in-string
|
|
680 (car (nth 1 state)) ; nest
|
|
681 (nth 2 state) ; depth
|
|
682 (car (car (nth 3 state))) ; pcol
|
|
683 ;(car (nth 5 state)) ; indent
|
|
684 )))
|
|
685
|
|
686 (defun ruby-indent-size (pos nest)
|
|
687 "Returns the indentation level in spaces NEST levels deeper than POS."
|
|
688 (+ pos (* (or nest 1) ruby-indent-level)))
|
|
689
|
|
690 (defun ruby-calculate-indent (&optional parse-start)
|
|
691 "Returns the proper indentation level of the current line."
|
|
692 ;; TODO: Document body
|
|
693 (save-excursion
|
|
694 (beginning-of-line)
|
|
695 (let ((ruby-indent-point (point))
|
|
696 (case-fold-search nil)
|
|
697 state bol eol begin op-end
|
|
698 (paren (progn (skip-syntax-forward " ")
|
|
699 (and (char-after) (matching-paren (char-after)))))
|
|
700 (indent 0))
|
|
701 (if parse-start
|
|
702 (goto-char parse-start)
|
|
703 (ruby-beginning-of-indent)
|
|
704 (setq parse-start (point)))
|
|
705 (back-to-indentation)
|
|
706 (setq indent (current-column))
|
|
707 (setq state (ruby-parse-region parse-start ruby-indent-point))
|
|
708 (cond
|
|
709 ((nth 0 state) ; within string
|
|
710 (setq indent nil)) ; do nothing
|
|
711 ((car (nth 1 state)) ; in paren
|
|
712 (goto-char (setq begin (cdr (nth 1 state))))
|
|
713 (let ((deep (ruby-deep-indent-paren-p (car (nth 1 state)))))
|
|
714 (if deep
|
|
715 (cond ((and (eq deep t) (eq (car (nth 1 state)) paren))
|
|
716 (skip-syntax-backward " ")
|
|
717 (setq indent (1- (current-column))))
|
|
718 ((let ((s (ruby-parse-region (point) ruby-indent-point)))
|
|
719 (and (nth 2 s) (> (nth 2 s) 0)
|
|
720 (or (goto-char (cdr (nth 1 s))) t)))
|
|
721 (forward-word -1)
|
|
722 (setq indent (ruby-indent-size (current-column)
|
|
723 (nth 2 state))))
|
|
724 (t
|
|
725 (setq indent (current-column))
|
|
726 (cond ((eq deep 'space))
|
|
727 (paren (setq indent (1- indent)))
|
|
728 (t (setq indent (ruby-indent-size (1- indent) 1))))))
|
|
729 (if (nth 3 state) (goto-char (nth 3 state))
|
|
730 (goto-char parse-start) (back-to-indentation))
|
|
731 (setq indent (ruby-indent-size (current-column) (nth 2 state))))
|
|
732 (and (eq (car (nth 1 state)) paren)
|
|
733 (ruby-deep-indent-paren-p (matching-paren paren))
|
|
734 (search-backward (char-to-string paren))
|
|
735 (setq indent (current-column)))))
|
|
736 ((and (nth 2 state) (> (nth 2 state) 0)) ; in nest
|
|
737 (if (null (cdr (nth 1 state)))
|
|
738 (error "invalid nest"))
|
|
739 (goto-char (cdr (nth 1 state)))
|
|
740 (forward-word -1) ; skip back a keyword
|
|
741 (setq begin (point))
|
|
742 (cond
|
|
743 ((looking-at "do\\>[^_]") ; iter block is a special case
|
|
744 (if (nth 3 state) (goto-char (nth 3 state))
|
|
745 (goto-char parse-start) (back-to-indentation))
|
|
746 (setq indent (ruby-indent-size (current-column) (nth 2 state))))
|
|
747 (t
|
|
748 (setq indent (+ (current-column) ruby-indent-level)))))
|
|
749
|
|
750 ((and (nth 2 state) (< (nth 2 state) 0)) ; in negative nest
|
|
751 (setq indent (ruby-indent-size (current-column) (nth 2 state)))))
|
|
752 (when indent
|
|
753 (goto-char ruby-indent-point)
|
|
754 (end-of-line)
|
|
755 (setq eol (point))
|
|
756 (beginning-of-line)
|
|
757 (cond
|
|
758 ((and (not (ruby-deep-indent-paren-p paren))
|
|
759 (re-search-forward ruby-negative eol t))
|
|
760 (and (not (eq ?_ (char-after (match-end 0))))
|
|
761 (setq indent (- indent ruby-indent-level))))
|
|
762 ((and
|
|
763 (save-excursion
|
|
764 (beginning-of-line)
|
|
765 (not (bobp)))
|
|
766 (or (ruby-deep-indent-paren-p t)
|
|
767 (null (car (nth 1 state)))))
|
|
768 ;; goto beginning of non-empty no-comment line
|
|
769 (let (end done)
|
|
770 (while (not done)
|
|
771 (skip-chars-backward " \t\n")
|
|
772 (setq end (point))
|
|
773 (beginning-of-line)
|
|
774 (if (re-search-forward "^\\s *#" end t)
|
|
775 (beginning-of-line)
|
|
776 (setq done t))))
|
|
777 (setq bol (point))
|
|
778 (end-of-line)
|
|
779 ;; skip the comment at the end
|
|
780 (skip-chars-backward " \t")
|
|
781 (let (end (pos (point)))
|
|
782 (beginning-of-line)
|
|
783 (while (and (re-search-forward "#" pos t)
|
|
784 (setq end (1- (point)))
|
|
785 (or (ruby-special-char-p end)
|
|
786 (and (setq state (ruby-parse-region parse-start end))
|
|
787 (nth 0 state))))
|
|
788 (setq end nil))
|
|
789 (goto-char (or end pos))
|
|
790 (skip-chars-backward " \t")
|
|
791 (setq begin (if (and end (nth 0 state)) pos (cdr (nth 1 state))))
|
|
792 (setq state (ruby-parse-region parse-start (point))))
|
|
793 (or (bobp) (forward-char -1))
|
|
794 (and
|
|
795 (or (and (looking-at ruby-symbol-re)
|
|
796 (skip-chars-backward ruby-symbol-chars)
|
|
797 (looking-at (concat "\\<\\(" ruby-block-hanging-re "\\)\\>"))
|
|
798 (not (eq (point) (nth 3 state)))
|
|
799 (save-excursion
|
|
800 (goto-char (match-end 0))
|
|
801 (not (looking-at "[a-z_]"))))
|
|
802 (and (looking-at ruby-operator-re)
|
|
803 (not (ruby-special-char-p))
|
|
804 ;; operator at the end of line
|
|
805 (let ((c (char-after (point))))
|
|
806 (and
|
|
807 ;; (or (null begin)
|
|
808 ;; (save-excursion
|
|
809 ;; (goto-char begin)
|
|
810 ;; (skip-chars-forward " \t")
|
|
811 ;; (not (or (eolp) (looking-at "#")
|
|
812 ;; (and (eq (car (nth 1 state)) ?{)
|
|
813 ;; (looking-at "|"))))))
|
|
814 (or (not (eq ?/ c))
|
|
815 (null (nth 0 (ruby-parse-region (or begin parse-start) (point)))))
|
|
816 (or (not (eq ?| (char-after (point))))
|
|
817 (save-excursion
|
|
818 (or (eolp) (forward-char -1))
|
|
819 (cond
|
|
820 ((search-backward "|" nil t)
|
|
821 (skip-chars-backward " \t\n")
|
|
822 (and (not (eolp))
|
|
823 (progn
|
|
824 (forward-char -1)
|
|
825 (not (looking-at "{")))
|
|
826 (progn
|
|
827 (forward-word -1)
|
|
828 (not (looking-at "do\\>[^_]")))))
|
|
829 (t t))))
|
|
830 (not (eq ?, c))
|
|
831 (setq op-end t)))))
|
|
832 (setq indent
|
|
833 (cond
|
|
834 ((and
|
|
835 (null op-end)
|
|
836 (not (looking-at (concat "\\<\\(" ruby-block-hanging-re "\\)\\>")))
|
|
837 (eq (ruby-deep-indent-paren-p t) 'space)
|
|
838 (not (bobp)))
|
|
839 (widen)
|
|
840 (goto-char (or begin parse-start))
|
|
841 (skip-syntax-forward " ")
|
|
842 (current-column))
|
|
843 ((car (nth 1 state)) indent)
|
|
844 (t
|
|
845 (+ indent ruby-indent-level))))))))
|
|
846 (goto-char ruby-indent-point)
|
|
847 (beginning-of-line)
|
|
848 (skip-syntax-forward " ")
|
|
849 (if (looking-at "\\.[^.]")
|
|
850 (+ indent ruby-indent-level)
|
|
851 indent))))
|
|
852
|
|
853 (defun ruby-electric-brace (arg)
|
|
854 "Inserts a brace and re-indents the current line."
|
|
855 (interactive "P")
|
|
856 (self-insert-command (prefix-numeric-value arg))
|
|
857 (ruby-indent-line t))
|
|
858
|
|
859 ;; TODO: Why isn't one ruby-*-of-defun written in terms of the other?
|
|
860 (defun ruby-beginning-of-defun (&optional arg)
|
|
861 "Move backward to the beginning of the current top-level defun.
|
|
862 With ARG, move backward multiple defuns. Negative ARG means
|
|
863 move forward."
|
|
864 (interactive "p")
|
|
865 (and (re-search-backward (concat "^\\(" ruby-block-beg-re "\\)\\b")
|
|
866 nil 'move (or arg 1))
|
|
867 (beginning-of-line)))
|
|
868
|
|
869 (defun ruby-end-of-defun (&optional arg)
|
|
870 "Move forward to the end of the current top-level defun.
|
|
871 With ARG, move forward multiple defuns. Negative ARG means
|
|
872 move backward."
|
|
873 (interactive "p")
|
|
874 (and (re-search-forward (concat "^\\(" ruby-block-end-re "\\)\\($\\|\\b[^_]\\)")
|
|
875 nil 'move (or arg 1))
|
|
876 (beginning-of-line))
|
|
877 (forward-line 1))
|
|
878
|
|
879 (defun ruby-beginning-of-indent ()
|
|
880 "TODO: document"
|
|
881 ;; I don't understand this function.
|
|
882 ;; It seems like it should move to the line where indentation should deepen,
|
|
883 ;; but ruby-indent-beg-re only accounts for whitespace before class, module and def,
|
|
884 ;; so this will only match other block beginners at the beginning of the line.
|
|
885 (and (re-search-backward (concat "^\\(" ruby-indent-beg-re "\\)\\b") nil 'move)
|
|
886 (beginning-of-line)))
|
|
887
|
|
888 (defun ruby-move-to-block (n)
|
|
889 "Moves to the beginning (N < 0) or the end (N > 0) of the current block
|
|
890 or blocks containing the current block."
|
|
891 ;; TODO: Make this work for n > 1,
|
|
892 ;; make it not loop for n = 0,
|
|
893 ;; document body
|
|
894 (let (start pos done down)
|
|
895 (setq start (ruby-calculate-indent))
|
|
896 (setq down (looking-at (if (< n 0) ruby-block-end-re
|
|
897 (concat "\\<\\(" ruby-block-beg-re "\\)\\>"))))
|
|
898 (while (and (not done) (not (if (< n 0) (bobp) (eobp))))
|
|
899 (forward-line n)
|
|
900 (cond
|
|
901 ((looking-at "^\\s *$"))
|
|
902 ((looking-at "^\\s *#"))
|
|
903 ((and (> n 0) (looking-at "^=begin\\>"))
|
|
904 (re-search-forward "^=end\\>"))
|
|
905 ((and (< n 0) (looking-at "^=end\\>"))
|
|
906 (re-search-backward "^=begin\\>"))
|
|
907 (t
|
|
908 (setq pos (current-indentation))
|
|
909 (cond
|
|
910 ((< start pos)
|
|
911 (setq down t))
|
|
912 ((and down (= pos start))
|
|
913 (setq done t))
|
|
914 ((> start pos)
|
|
915 (setq done t)))))
|
|
916 (if done
|
|
917 (save-excursion
|
|
918 (back-to-indentation)
|
|
919 (if (looking-at (concat "\\<\\(" ruby-block-mid-re "\\)\\>"))
|
|
920 (setq done nil))))))
|
|
921 (back-to-indentation))
|
|
922
|
|
923 (defun ruby-beginning-of-block (&optional arg)
|
|
924 "Move backward to the beginning of the current block.
|
|
925 With ARG, move up multiple blocks."
|
|
926 (interactive "p")
|
|
927 (ruby-move-to-block (- (or arg 1))))
|
|
928
|
|
929 (defun ruby-end-of-block (&optional arg)
|
|
930 "Move forward to the end of the current block.
|
|
931 With ARG, move out of multiple blocks."
|
|
932 ;; Passing a value > 1 to ruby-move-to-block currently doesn't work.
|
|
933 (interactive)
|
|
934 (ruby-move-to-block (or arg 1)))
|
|
935
|
|
936 (defun ruby-forward-sexp (&optional arg)
|
|
937 "Move forward across one balanced expression (sexp).
|
|
938 With ARG, do it many times. Negative ARG means move backward."
|
|
939 ;; TODO: Document body
|
|
940 (interactive "p")
|
|
941 (if (and (numberp arg) (< arg 0))
|
|
942 (ruby-backward-sexp (- arg))
|
|
943 (let ((i (or arg 1)))
|
|
944 (condition-case nil
|
|
945 (while (> i 0)
|
|
946 (skip-syntax-forward " ")
|
|
947 (cond ((looking-at "\\?\\(\\\\[CM]-\\)*\\\\?\\S ")
|
|
948 (goto-char (match-end 0)))
|
|
949 ((progn
|
|
950 (skip-chars-forward ",.:;|&^~=!?\\+\\-\\*")
|
|
951 (looking-at "\\s("))
|
|
952 (goto-char (scan-sexps (point) 1)))
|
|
953 ((and (looking-at (concat "\\<\\(" ruby-block-beg-re "\\)\\>"))
|
|
954 (not (eq (char-before (point)) ?.))
|
|
955 (not (eq (char-before (point)) ?:)))
|
|
956 (ruby-end-of-block)
|
|
957 (forward-word 1))
|
|
958 ((looking-at "\\(\\$\\|@@?\\)?\\sw")
|
|
959 (while (progn
|
|
960 (while (progn (forward-word 1) (looking-at "_")))
|
|
961 (cond ((looking-at "::") (forward-char 2) t)
|
|
962 ((> (skip-chars-forward ".") 0))
|
|
963 ((looking-at "\\?\\|!\\(=[~=>]\\|[^~=]\\)")
|
|
964 (forward-char 1) nil)))))
|
|
965 ((let (state expr)
|
|
966 (while
|
|
967 (progn
|
|
968 (setq expr (or expr (ruby-expr-beg)
|
|
969 (looking-at "%\\sw?\\Sw\\|[\"'`/]")))
|
|
970 (nth 1 (setq state (apply 'ruby-parse-partial nil state))))
|
|
971 (setq expr t)
|
|
972 (skip-chars-forward "<"))
|
|
973 (not expr))))
|
|
974 (setq i (1- i)))
|
|
975 ((error) (forward-word 1)))
|
|
976 i)))
|
|
977
|
|
978 (defun ruby-backward-sexp (&optional arg)
|
|
979 "Move backward across one balanced expression (sexp).
|
|
980 With ARG, do it many times. Negative ARG means move forward."
|
|
981 ;; TODO: Document body
|
|
982 (interactive "p")
|
|
983 (if (and (numberp arg) (< arg 0))
|
|
984 (ruby-forward-sexp (- arg))
|
|
985 (let ((i (or arg 1)))
|
|
986 (condition-case nil
|
|
987 (while (> i 0)
|
|
988 (skip-chars-backward " \t\n,.:;|&^~=!?\\+\\-\\*")
|
|
989 (forward-char -1)
|
|
990 (cond ((looking-at "\\s)")
|
|
991 (goto-char (scan-sexps (1+ (point)) -1))
|
|
992 (case (char-before)
|
|
993 (?% (forward-char -1))
|
|
994 ('(?q ?Q ?w ?W ?r ?x)
|
|
995 (if (eq (char-before (1- (point))) ?%) (forward-char -2))))
|
|
996 nil)
|
|
997 ((looking-at "\\s\"\\|\\\\\\S_")
|
|
998 (let ((c (char-to-string (char-before (match-end 0)))))
|
|
999 (while (and (search-backward c)
|
|
1000 (eq (logand (skip-chars-backward "\\") 1)
|
|
1001 1))))
|
|
1002 nil)
|
|
1003 ((looking-at "\\s.\\|\\s\\")
|
|
1004 (if (ruby-special-char-p) (forward-char -1)))
|
|
1005 ((looking-at "\\s(") nil)
|
|
1006 (t
|
|
1007 (forward-char 1)
|
|
1008 (while (progn (forward-word -1)
|
|
1009 (case (char-before)
|
|
1010 (?_ t)
|
|
1011 (?. (forward-char -1) t)
|
|
1012 ((?$ ?@)
|
|
1013 (forward-char -1)
|
|
1014 (and (eq (char-before) (char-after)) (forward-char -1)))
|
|
1015 (?:
|
|
1016 (forward-char -1)
|
|
1017 (eq (char-before) :)))))
|
|
1018 (if (looking-at ruby-block-end-re)
|
|
1019 (ruby-beginning-of-block))
|
|
1020 nil))
|
|
1021 (setq i (1- i)))
|
|
1022 ((error)))
|
|
1023 i)))
|
|
1024
|
|
1025 (defun ruby-mark-defun ()
|
|
1026 "Put mark at end of this Ruby function, point at beginning."
|
|
1027 (interactive)
|
|
1028 (push-mark (point))
|
|
1029 (ruby-end-of-defun)
|
|
1030 (push-mark (point) nil t)
|
|
1031 (ruby-beginning-of-defun)
|
|
1032 (re-search-backward "^\n" (- (point) 1) t))
|
|
1033
|
|
1034 (defun ruby-indent-exp (&optional shutup-p)
|
|
1035 "Indent each line in the balanced expression following the point.
|
|
1036 If a prefix arg is given or SHUTUP-P is non-nil, no errors
|
|
1037 are signalled if a balanced expression isn't found."
|
|
1038 (interactive "*P")
|
|
1039 (let ((here (point-marker)) start top column (nest t))
|
|
1040 (set-marker-insertion-type here t)
|
|
1041 (unwind-protect
|
|
1042 (progn
|
|
1043 (beginning-of-line)
|
|
1044 (setq start (point) top (current-indentation))
|
|
1045 (while (and (not (eobp))
|
|
1046 (progn
|
|
1047 (setq column (ruby-calculate-indent start))
|
|
1048 (cond ((> column top)
|
|
1049 (setq nest t))
|
|
1050 ((and (= column top) nest)
|
|
1051 (setq nest nil) t))))
|
|
1052 (ruby-indent-to column)
|
|
1053 (beginning-of-line 2)))
|
|
1054 (goto-char here)
|
|
1055 (set-marker here nil))))
|
|
1056
|
|
1057 (defun ruby-add-log-current-method ()
|
|
1058 "Returns the current method name as a string.
|
|
1059 This string includes all namespaces.
|
|
1060
|
|
1061 For example:
|
|
1062
|
|
1063 #exit
|
|
1064 String#gsub
|
|
1065 Net::HTTP#active?
|
|
1066 File::open.
|
|
1067
|
|
1068 See `add-log-current-defun-function'."
|
|
1069 ;; TODO: Document body
|
|
1070 ;; Why does this append a period to class methods?
|
|
1071 (condition-case nil
|
|
1072 (save-excursion
|
|
1073 (let (mname mlist (indent 0))
|
|
1074 ;; get current method (or class/module)
|
|
1075 (if (re-search-backward
|
|
1076 (concat "^[ \t]*\\(def\\|class\\|module\\)[ \t]+"
|
|
1077 "\\("
|
|
1078 ;; \\. and :: for class method
|
|
1079 "\\([A-Za-z_]" ruby-symbol-re "*\\|\\.\\|::" "\\)"
|
|
1080 "+\\)")
|
|
1081 nil t)
|
|
1082 (progn
|
|
1083 (setq mname (match-string 2))
|
|
1084 (unless (string-equal "def" (match-string 1))
|
|
1085 (setq mlist (list mname) mname nil))
|
|
1086 (goto-char (match-beginning 1))
|
|
1087 (setq indent (current-column))
|
|
1088 (beginning-of-line)))
|
|
1089 ;; nest class/module
|
|
1090 (while (and (> indent 0)
|
|
1091 (re-search-backward
|
|
1092 (concat
|
|
1093 "^[ \t]*\\(class\\|module\\)[ \t]+"
|
|
1094 "\\([A-Z]" ruby-symbol-re "*\\)")
|
|
1095 nil t))
|
|
1096 (goto-char (match-beginning 1))
|
|
1097 (if (< (current-column) indent)
|
|
1098 (progn
|
|
1099 (setq mlist (cons (match-string 2) mlist))
|
|
1100 (setq indent (current-column))
|
|
1101 (beginning-of-line))))
|
|
1102 (when mname
|
|
1103 (let ((mn (split-string mname "\\.\\|::")))
|
|
1104 (if (cdr mn)
|
|
1105 (progn
|
|
1106 (cond
|
|
1107 ((string-equal "" (car mn))
|
|
1108 (setq mn (cdr mn) mlist nil))
|
|
1109 ((string-equal "self" (car mn))
|
|
1110 (setq mn (cdr mn)))
|
|
1111 ((let ((ml (nreverse mlist)))
|
|
1112 (while ml
|
|
1113 (if (string-equal (car ml) (car mn))
|
|
1114 (setq mlist (nreverse (cdr ml)) ml nil))
|
|
1115 (or (setq ml (cdr ml)) (nreverse mlist))))))
|
|
1116 (if mlist
|
|
1117 (setcdr (last mlist) mn)
|
|
1118 (setq mlist mn))
|
|
1119 (setq mn (last mn 2))
|
|
1120 (setq mname (concat "." (cadr mn)))
|
|
1121 (setcdr mn nil))
|
|
1122 (setq mname (concat "#" mname)))))
|
|
1123 ;; generate string
|
|
1124 (if (consp mlist)
|
|
1125 (setq mlist (mapconcat (function identity) mlist "::")))
|
|
1126 (if mname
|
|
1127 (if mlist (concat mlist mname) mname)
|
|
1128 mlist)))))
|
|
1129
|
|
1130 (defconst ruby-font-lock-syntactic-keywords
|
|
1131 `(;; #{ }, #$hoge, #@foo are not comments
|
|
1132 ("\\(#\\)[{$@]" 1 (1 . nil))
|
|
1133 ;; the last $', $", $` in the respective string is not variable
|
|
1134 ;; the last ?', ?", ?` in the respective string is not ascii code
|
|
1135 ("\\(^\\|[\[ \t\n<+\(,=]\\)\\(['\"`]\\)\\(\\\\.\\|\\2\\|[^'\"`\n\\\\]\\)*?\\\\?[?$]\\(\\2\\)"
|
|
1136 (2 (7 . nil))
|
|
1137 (4 (7 . nil)))
|
|
1138 ;; $' $" $` .... are variables
|
|
1139 ;; ?' ?" ?` are ascii codes
|
|
1140 ("\\(^\\|[^\\\\]\\)\\(\\\\\\\\\\)*[?$]\\([#\"'`]\\)" 3 (1 . nil))
|
|
1141 ;; regexps
|
|
1142 ("\\(^\\|[=(,~?:;<>]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)"
|
|
1143 (4 (7 . ?/))
|
|
1144 (6 (7 . ?/)))
|
|
1145 ("^=en\\(d\\)\\_>" 1 "!")
|
|
1146 ("^\\(=\\)begin\\_>" 1 (ruby-comment-beg-syntax))
|
|
1147 ;; Currently, the following case is highlighted incorrectly:
|
|
1148 ;;
|
|
1149 ;; <<FOO
|
|
1150 ;; FOO
|
|
1151 ;; <<BAR
|
|
1152 ;; <<BAZ
|
|
1153 ;; BAZ
|
|
1154 ;; BAR
|
|
1155 ;;
|
|
1156 ;; This is because all here-doc beginnings are highlighted before any endings,
|
|
1157 ;; so although <<BAR is properly marked as a beginning, when we get to <<BAZ
|
|
1158 ;; it thinks <<BAR is part of a string so it's marked as well.
|
|
1159 ;;
|
|
1160 ;; This may be fixable by modifying ruby-in-here-doc-p to use
|
|
1161 ;; ruby-in-non-here-doc-string-p rather than syntax-ppss-context,
|
|
1162 ;; but I don't want to try that until we've got unit tests set up
|
|
1163 ;; to make sure I don't break anything else.
|
|
1164 (,(concat ruby-here-doc-beg-re ".*\\(\n\\)")
|
|
1165 ,(+ 1 (regexp-opt-depth ruby-here-doc-beg-re))
|
|
1166 (ruby-here-doc-beg-syntax))
|
|
1167 (,ruby-here-doc-end-re 3 (ruby-here-doc-end-syntax)))
|
|
1168 "Syntactic keywords for ruby-mode. See `font-lock-syntactic-keywords'.")
|
|
1169
|
|
1170 (defun ruby-comment-beg-syntax ()
|
|
1171 "Returns the syntax cell for a the first character of a =begin.
|
|
1172 See the definition of `ruby-font-lock-syntactic-keywords'.
|
|
1173
|
|
1174 This returns a comment-delimiter cell as long as the =begin
|
|
1175 isn't in a string or another comment."
|
|
1176 (when (not (nth 3 (syntax-ppss)))
|
|
1177 (string-to-syntax "!")))
|
|
1178
|
|
1179 (unless (functionp 'syntax-ppss)
|
|
1180 (defun syntax-ppss (&optional pos)
|
|
1181 (parse-partial-sexp (point-min) (or pos (point)))))
|
|
1182
|
|
1183 (defun ruby-in-ppss-context-p (context &optional ppss)
|
|
1184 (let ((ppss (or ppss (syntax-ppss (point)))))
|
|
1185 (if (cond
|
|
1186 ((eq context 'anything)
|
|
1187 (or (nth 3 ppss)
|
|
1188 (nth 4 ppss)))
|
|
1189 ((eq context 'string)
|
|
1190 (nth 3 ppss))
|
|
1191 ((eq context 'heredoc)
|
|
1192 (and (nth 3 ppss)
|
|
1193 ;; If it's generic string, it's a heredoc and we don't care
|
|
1194 ;; See `parse-partial-sexp'
|
|
1195 (not (numberp (nth 3 ppss)))))
|
|
1196 ((eq context 'non-heredoc)
|
|
1197 (and (ruby-in-ppss-context-p 'anything)
|
|
1198 (not (ruby-in-ppss-context-p 'heredoc))))
|
|
1199 ((eq context 'comment)
|
|
1200 (nth 4 ppss))
|
|
1201 (t
|
|
1202 (error (concat
|
|
1203 "Internal error on `ruby-in-ppss-context-p': "
|
|
1204 "context name `" (symbol-name context) "' is unknown"))))
|
|
1205 t)))
|
|
1206
|
|
1207 (defun ruby-in-here-doc-p ()
|
|
1208 "Returns whether or not the point is in a heredoc."
|
|
1209 (save-excursion
|
|
1210 (let ((old-point (point)) (case-fold-search nil))
|
|
1211 (beginning-of-line)
|
|
1212 (catch 'found-beg
|
|
1213 (while (re-search-backward ruby-here-doc-beg-re nil t)
|
|
1214 (if (not (or (ruby-in-ppss-context-p 'anything)
|
|
1215 (ruby-here-doc-find-end old-point)))
|
|
1216 (throw 'found-beg t)))))))
|
|
1217
|
|
1218 (defun ruby-here-doc-find-end (&optional limit)
|
|
1219 "Expects the point to be on a line with one or more heredoc
|
|
1220 openers. Returns the buffer position at which all heredocs on the
|
|
1221 line are terminated, or nil if they aren't terminated before the
|
|
1222 buffer position `limit' or the end of the buffer."
|
|
1223 (save-excursion
|
|
1224 (beginning-of-line)
|
|
1225 (catch 'done
|
|
1226 (let ((eol (save-excursion (end-of-line) (point)))
|
|
1227 (case-fold-search nil)
|
|
1228 ;; Fake match data such that (match-end 0) is at eol
|
|
1229 (end-match-data (progn (looking-at ".*$") (match-data)))
|
|
1230 beg-match-data end-re)
|
|
1231 (while (re-search-forward ruby-here-doc-beg-re eol t)
|
|
1232 (setq beg-match-data (match-data))
|
|
1233 (setq end-re (ruby-here-doc-end-match))
|
|
1234
|
|
1235 (set-match-data end-match-data)
|
|
1236 (goto-char (match-end 0))
|
|
1237 (unless (re-search-forward end-re limit t) (throw 'done nil))
|
|
1238 (setq end-match-data (match-data))
|
|
1239
|
|
1240 (set-match-data beg-match-data)
|
|
1241 (goto-char (match-end 0)))
|
|
1242 (set-match-data end-match-data)
|
|
1243 (goto-char (match-end 0))
|
|
1244 (point)))))
|
|
1245
|
|
1246 (defun ruby-here-doc-beg-syntax ()
|
|
1247 "Returns the syntax cell for a line that may begin a heredoc.
|
|
1248 See the definition of `ruby-font-lock-syntactic-keywords'.
|
|
1249
|
|
1250 This sets the syntax cell for the newline ending the line
|
|
1251 containing the heredoc beginning so that cases where multiple
|
|
1252 heredocs are started on one line are handled correctly."
|
|
1253 (save-excursion
|
|
1254 (goto-char (match-beginning 0))
|
|
1255 (unless (or (ruby-in-ppss-context-p 'non-heredoc)
|
|
1256 (ruby-in-here-doc-p))
|
|
1257 (string-to-syntax "|"))))
|
|
1258
|
|
1259 (defun ruby-here-doc-end-syntax ()
|
|
1260 "Returns the syntax cell for a line that may end a heredoc.
|
|
1261 See the definition of `ruby-font-lock-syntactic-keywords'."
|
|
1262 (let ((pss (syntax-ppss)) (case-fold-search nil))
|
|
1263 ;; If we aren't in a string, we definitely aren't ending a heredoc,
|
|
1264 ;; so we can just give up.
|
|
1265 ;; This means we aren't doing a full-document search
|
|
1266 ;; every time we enter a character.
|
|
1267 (when (ruby-in-ppss-context-p 'heredoc pss)
|
|
1268 (save-excursion
|
|
1269 (goto-char (nth 8 pss)) ; Go to the beginning of heredoc.
|
|
1270 (let ((eol (point)))
|
|
1271 (beginning-of-line)
|
|
1272 (if (and (re-search-forward (ruby-here-doc-beg-match) eol t) ; If there is a heredoc that matches this line...
|
|
1273 (not (ruby-in-ppss-context-p 'anything)) ; And that's not inside a heredoc/string/comment...
|
|
1274 (progn (goto-char (match-end 0)) ; And it's the last heredoc on its line...
|
|
1275 (not (re-search-forward ruby-here-doc-beg-re eol t))))
|
|
1276 (string-to-syntax "|")))))))
|
|
1277
|
|
1278 (if (featurep 'xemacs)
|
|
1279 (put 'ruby-mode 'font-lock-defaults
|
|
1280 '((ruby-font-lock-keywords)
|
|
1281 nil nil nil
|
|
1282 beginning-of-line
|
|
1283 (font-lock-syntactic-keywords
|
|
1284 . ruby-font-lock-syntactic-keywords))))
|
|
1285
|
|
1286 (defvar ruby-font-lock-syntax-table
|
|
1287 (let ((tbl (copy-syntax-table ruby-mode-syntax-table)))
|
|
1288 (modify-syntax-entry ?_ "w" tbl)
|
|
1289 tbl)
|
|
1290 "The syntax table to use for fontifying ruby-mode buffers.
|
|
1291 See `font-lock-syntax-table'.")
|
|
1292
|
|
1293 (defconst ruby-font-lock-keywords
|
|
1294 (list
|
|
1295 ;; functions
|
|
1296 '("^\\s *def\\s +\\([^( \t\n]+\\)"
|
|
1297 1 font-lock-function-name-face)
|
|
1298 ;; keywords
|
|
1299 (cons (concat
|
|
1300 "\\(^\\|[^_:.@$]\\|\\.\\.\\)\\b\\(defined\\?\\|"
|
|
1301 (regexp-opt
|
|
1302 '("alias_method"
|
|
1303 "alias"
|
|
1304 "and"
|
|
1305 "begin"
|
|
1306 "break"
|
|
1307 "case"
|
|
1308 "catch"
|
|
1309 "class"
|
|
1310 "def"
|
|
1311 "do"
|
|
1312 "elsif"
|
|
1313 "else"
|
|
1314 "fail"
|
|
1315 "ensure"
|
|
1316 "for"
|
|
1317 "end"
|
|
1318 "if"
|
|
1319 "in"
|
|
1320 "module_function"
|
|
1321 "module"
|
|
1322 "next"
|
|
1323 "not"
|
|
1324 "or"
|
|
1325 "public"
|
|
1326 "private"
|
|
1327 "protected"
|
|
1328 "raise"
|
|
1329 "redo"
|
|
1330 "rescue"
|
|
1331 "retry"
|
|
1332 "return"
|
|
1333 "then"
|
|
1334 "throw"
|
|
1335 "super"
|
|
1336 "unless"
|
|
1337 "undef"
|
|
1338 "until"
|
|
1339 "when"
|
|
1340 "while"
|
|
1341 "yield")
|
|
1342 t)
|
|
1343 "\\)"
|
|
1344 ruby-keyword-end-re)
|
|
1345 2)
|
|
1346 ;; here-doc beginnings
|
|
1347 (list ruby-here-doc-beg-re 0 'font-lock-string-face)
|
|
1348 ;; variables
|
|
1349 '("\\(^\\|[^_:.@$]\\|\\.\\.\\)\\b\\(nil\\|self\\|true\\|false\\)\\>"
|
|
1350 2 font-lock-variable-name-face)
|
|
1351 ;; variables
|
|
1352 '("\\(\\$\\([^a-zA-Z0-9 \n]\\|[0-9]\\)\\)\\W"
|
|
1353 1 font-lock-variable-name-face)
|
|
1354 '("\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+"
|
|
1355 0 font-lock-variable-name-face)
|
|
1356 ;; general delimited string
|
|
1357 '("\\(^\\|[[ \t\n<+(,=]\\)\\(%[xrqQwW]?\\([^<[{(a-zA-Z0-9 \n]\\)[^\n\\\\]*\\(\\\\.[^\n\\\\]*\\)*\\(\\3\\)\\)"
|
|
1358 (2 font-lock-string-face))
|
|
1359 ;; constants
|
|
1360 '("\\(^\\|[^_]\\)\\b\\([A-Z]+\\(\\w\\|_\\)*\\)"
|
|
1361 2 font-lock-type-face)
|
|
1362 ;; symbols
|
|
1363 '("\\(^\\|[^:]\\)\\(:\\([-+~]@?\\|[/%&|^`]\\|\\*\\*?\\|<\\(<\\|=>?\\)?\\|>[>=]?\\|===?\\|=~\\|\\[\\]=?\\|\\(\\w\\|_\\)+\\([!?=]\\|\\b_*\\)\\|#{[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\)\\)"
|
|
1364 2 font-lock-reference-face)
|
|
1365 ;; expression expansion
|
|
1366 '("#\\({[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\|\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+\\)"
|
|
1367 0 font-lock-variable-name-face t)
|
|
1368 ;; warn lower camel case
|
|
1369 ;'("\\<[a-z]+[a-z0-9]*[A-Z][A-Za-z0-9]*\\([!?]?\\|\\>\\)"
|
|
1370 ; 0 font-lock-warning-face)
|
|
1371 )
|
|
1372 "Additional expressions to highlight in ruby mode.")
|
|
1373
|
|
1374 ;;;###autoload
|
|
1375 (defun ruby-mode ()
|
|
1376 "Major mode for editing Ruby scripts.
|
|
1377 \\[ruby-indent-line] properly indents subexpressions of multi-line
|
|
1378 class, module, def, if, while, for, do, and case statements, taking
|
|
1379 nesting into account.
|
|
1380
|
|
1381 The variable ruby-indent-level controls the amount of indentation.
|
|
1382 \\{ruby-mode-map}"
|
|
1383 (interactive)
|
|
1384 (kill-all-local-variables)
|
|
1385 (use-local-map ruby-mode-map)
|
|
1386 (setq mode-name "Ruby")
|
|
1387 (setq major-mode 'ruby-mode)
|
|
1388 (ruby-mode-variables)
|
|
1389
|
|
1390 (set (make-local-variable 'imenu-create-index-function)
|
|
1391 'ruby-imenu-create-index)
|
|
1392 (set (make-local-variable 'add-log-current-defun-function)
|
|
1393 'ruby-add-log-current-method)
|
|
1394
|
|
1395 (add-hook
|
|
1396 (cond ((boundp 'before-save-hook)
|
|
1397 (make-local-variable 'before-save-hook)
|
|
1398 'before-save-hook)
|
|
1399 ((boundp 'write-contents-functions) 'write-contents-functions)
|
|
1400 ((boundp 'write-contents-hooks) 'write-contents-hooks))
|
|
1401 'ruby-mode-set-encoding)
|
|
1402
|
|
1403 (set (make-local-variable 'font-lock-defaults)
|
|
1404 '((ruby-font-lock-keywords) nil nil))
|
|
1405 (set (make-local-variable 'font-lock-keywords)
|
|
1406 ruby-font-lock-keywords)
|
|
1407 (set (make-local-variable 'font-lock-syntax-table)
|
|
1408 ruby-font-lock-syntax-table)
|
|
1409 (set (make-local-variable 'font-lock-syntactic-keywords)
|
|
1410 ruby-font-lock-syntactic-keywords)
|
|
1411
|
|
1412 (if (fboundp 'run-mode-hooks)
|
|
1413 (run-mode-hooks 'ruby-mode-hook)
|
|
1414 (run-hooks 'ruby-mode-hook)))
|
|
1415
|
|
1416 ;;; Invoke ruby-mode when appropriate
|
|
1417
|
|
1418 ;;;###autoload
|
|
1419 (add-to-list 'auto-mode-alist '("\\.rb$" . ruby-mode))
|
|
1420
|
|
1421 ;;;###autoload
|
|
1422 (add-to-list 'interpreter-mode-alist '("ruby" . ruby-mode))
|
|
1423 (add-to-list 'interpreter-mode-alist '("rbx" . ruby-mode))
|
|
1424 (add-to-list 'interpreter-mode-alist '("jruby" . ruby-mode))
|
|
1425 (add-to-list 'interpreter-mode-alist '("ruby1.9" . ruby-mode))
|
|
1426 (add-to-list 'interpreter-mode-alist '("ruby1.8" . ruby-mode))
|
|
1427
|
|
1428 (provide 'ruby-mode)
|
|
1429
|
|
1430 ;;; ruby-mode.el ends here
|