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