5 ;;; $Date: 2008-05-19 00:02:36 +0900 (Mon, 19 May 2008) $
6 ;;; created at: Fri Feb 4 14:49:13 JST 1994
9 (defconst ruby-mode-revision "$Revision: 16458 $")
11 (defconst ruby-mode-version
13 (string-match "[0-9.]+" ruby-mode-revision)
14 (substring ruby-mode-revision (match-beginning 0) (match-end 0))))
17 "Major mode for editing ruby scripts."
21 (defcustom ruby-indent-level 2
22 "*Indentation of ruby statements."
26 (defconst ruby-block-beg-re
27 "class\\|module\\|def\\|if\\|unless\\|case\\|while\\|until\\|for\\|begin\\|do"
30 (defconst ruby-non-block-do-re
31 "\\(while\\|until\\|for\\|rescue\\)\\>[^_]"
34 (defconst ruby-indent-beg-re
35 "\\(\\s *\\(class\\|module\\|def\\)\\)\\|if\\|unless\\|case\\|while\\|until\\|for\\|begin"
38 (defconst ruby-modifier-beg-re
39 "if\\|unless\\|while\\|until"
42 (defconst ruby-modifier-re
43 (concat ruby-modifier-beg-re "\\|rescue")
46 (defconst ruby-block-mid-re
47 "then\\|else\\|elsif\\|when\\|rescue\\|ensure"
50 (defconst ruby-block-op-re
54 (defconst ruby-block-hanging-re
55 (concat ruby-modifier-beg-re "\\|" ruby-block-op-re)
58 (defconst ruby-block-end-re "\\<end\\>")
60 (defconst ruby-here-doc-beg-re
61 "<<\\(-\\)?\\(\\([a-zA-Z0-9_]+\\)\\|[\"]\\([^\"]+\\)[\"]\\|[']\\([^']+\\)[']\\)")
63 (defun ruby-here-doc-end-match ()
65 (if (match-string 1) "[ \t]*" nil)
71 (defconst ruby-delimiter
72 (concat "[?$/%(){}#\"'`.:]\\|<<\\|\\[\\|\\]\\|\\<\\("
74 "\\)\\>\\|" ruby-block-end-re
75 "\\|^=begin\\|" ruby-here-doc-beg-re)
78 (defconst ruby-negative
79 (concat "^[ \t]*\\(\\(" ruby-block-mid-re "\\)\\>\\|"
80 ruby-block-end-re "\\|}\\|\\]\\)")
83 (defconst ruby-operator-chars "-,.+*/%&|^~=<>:")
84 (defconst ruby-operator-re (concat "[" ruby-operator-chars "]"))
86 (defconst ruby-symbol-chars "a-zA-Z0-9_")
87 (defconst ruby-symbol-re (concat "[" ruby-symbol-chars "]"))
89 (defvar ruby-mode-abbrev-table nil
90 "Abbrev table in use in `ruby-mode' buffers.")
92 (define-abbrev-table 'ruby-mode-abbrev-table ())
94 (defvar ruby-mode-map nil "Keymap used in `ruby-mode'.")
98 (setq ruby-mode-map (make-sparse-keymap))
99 (define-key ruby-mode-map "{" 'ruby-electric-brace)
100 (define-key ruby-mode-map "}" 'ruby-electric-brace)
101 (define-key ruby-mode-map "\e\C-a" 'ruby-beginning-of-defun)
102 (define-key ruby-mode-map "\e\C-e" 'ruby-end-of-defun)
103 (define-key ruby-mode-map "\e\C-b" 'ruby-backward-sexp)
104 (define-key ruby-mode-map "\e\C-f" 'ruby-forward-sexp)
105 (define-key ruby-mode-map "\e\C-p" 'ruby-beginning-of-block)
106 (define-key ruby-mode-map "\e\C-n" 'ruby-end-of-block)
107 (define-key ruby-mode-map "\e\C-h" 'ruby-mark-defun)
108 (define-key ruby-mode-map "\e\C-q" 'ruby-indent-exp)
109 (define-key ruby-mode-map "\t" 'ruby-indent-command)
110 (define-key ruby-mode-map "\C-c\C-e" 'ruby-insert-end)
111 (define-key ruby-mode-map "\C-j" 'ruby-reindent-then-newline-and-indent)
112 (define-key ruby-mode-map "\C-m" 'newline))
114 (defvar ruby-mode-syntax-table nil
115 "Syntax table in use in `ruby-mode' buffers.")
117 (if ruby-mode-syntax-table
119 (setq ruby-mode-syntax-table (make-syntax-table))
120 (modify-syntax-entry ?\' "\"" ruby-mode-syntax-table)
121 (modify-syntax-entry ?\" "\"" ruby-mode-syntax-table)
122 (modify-syntax-entry ?\` "\"" ruby-mode-syntax-table)
123 (modify-syntax-entry ?# "<" ruby-mode-syntax-table)
124 (modify-syntax-entry ?\n ">" ruby-mode-syntax-table)
125 (modify-syntax-entry ?\\ "\\" ruby-mode-syntax-table)
126 (modify-syntax-entry ?$ "." ruby-mode-syntax-table)
127 (modify-syntax-entry ?? "_" ruby-mode-syntax-table)
128 (modify-syntax-entry ?_ "_" ruby-mode-syntax-table)
129 (modify-syntax-entry ?< "." ruby-mode-syntax-table)
130 (modify-syntax-entry ?> "." ruby-mode-syntax-table)
131 (modify-syntax-entry ?& "." ruby-mode-syntax-table)
132 (modify-syntax-entry ?| "." ruby-mode-syntax-table)
133 (modify-syntax-entry ?% "." ruby-mode-syntax-table)
134 (modify-syntax-entry ?= "." ruby-mode-syntax-table)
135 (modify-syntax-entry ?/ "." ruby-mode-syntax-table)
136 (modify-syntax-entry ?+ "." ruby-mode-syntax-table)
137 (modify-syntax-entry ?* "." ruby-mode-syntax-table)
138 (modify-syntax-entry ?- "." ruby-mode-syntax-table)
139 (modify-syntax-entry ?\; "." ruby-mode-syntax-table)
140 (modify-syntax-entry ?\( "()" ruby-mode-syntax-table)
141 (modify-syntax-entry ?\) ")(" ruby-mode-syntax-table)
142 (modify-syntax-entry ?\{ "(}" ruby-mode-syntax-table)
143 (modify-syntax-entry ?\} "){" ruby-mode-syntax-table)
144 (modify-syntax-entry ?\[ "(]" ruby-mode-syntax-table)
145 (modify-syntax-entry ?\] ")[" ruby-mode-syntax-table)
148 (defcustom ruby-indent-tabs-mode nil
149 "*Indentation can insert tabs in ruby mode if this is non-nil."
150 :type 'boolean :group 'ruby)
152 (defcustom ruby-indent-level 2
153 "*Indentation of ruby statements."
154 :type 'integer :group 'ruby)
156 (defcustom ruby-comment-column 32
157 "*Indentation column of comments."
158 :type 'integer :group 'ruby)
160 (defcustom ruby-deep-arglist t
161 "*Deep indent lists in parenthesis when non-nil.
162 Also ignores spaces after parenthesis when 'space."
165 (defcustom ruby-deep-indent-paren '(?\( ?\[ ?\] t)
166 "*Deep indent lists in parenthesis when non-nil. t means continuous line.
167 Also ignores spaces after parenthesis when 'space."
170 (defcustom ruby-deep-indent-paren-style 'space
171 "Default deep indent style."
172 :options '(t nil space) :group 'ruby)
174 (eval-when-compile (require 'cl))
175 (defun ruby-imenu-create-index-in-block (prefix beg end)
176 (let ((index-alist '()) (case-fold-search nil)
177 name next pos decl sing)
179 (while (re-search-forward "^\\s *\\(\\(class\\>\\(\\s *<<\\)?\\|module\\>\\)\\s *\\([^\(<\n ]+\\)\\|\\(def\\|alias\\)\\>\\s *\\([^\(\n ]+\\)\\)" end t)
180 (setq sing (match-beginning 3))
181 (setq decl (match-string 5))
182 (setq next (match-end 0))
183 (setq name (or (match-string 4) (match-string 6)))
184 (setq pos (match-beginning 0))
186 ((string= "alias" decl)
187 (if prefix (setq name (concat prefix name)))
188 (push (cons name pos) index-alist))
189 ((string= "def" decl)
193 ((string-match "^self\." name)
194 (concat (substring prefix 0 -1) (substring name 4)))
195 (t (concat prefix name)))))
196 (push (cons name pos) index-alist)
197 (ruby-accurate-end-of-block end))
199 (if (string= "self" name)
200 (if prefix (setq name (substring prefix 0 -1)))
201 (if prefix (setq name (concat (substring prefix 0 -1) "::" name)))
202 (push (cons name pos) index-alist))
203 (ruby-accurate-end-of-block end)
206 (nconc (ruby-imenu-create-index-in-block
207 (concat name (if sing "." "#"))
208 next beg) index-alist))
212 (defun ruby-imenu-create-index ()
213 (nreverse (ruby-imenu-create-index-in-block nil (point-min) nil)))
215 (defun ruby-accurate-end-of-block (&optional end)
217 (or end (setq end (point-max)))
218 (while (and (setq state (apply 'ruby-parse-partial end state))
219 (>= (nth 2 state) 0) (< (point) end)))))
221 (defun ruby-mode-variables ()
222 (set-syntax-table ruby-mode-syntax-table)
223 (setq local-abbrev-table ruby-mode-abbrev-table)
224 (make-local-variable 'indent-line-function)
225 (setq indent-line-function 'ruby-indent-line)
226 (make-local-variable 'require-final-newline)
227 (setq require-final-newline t)
228 (make-variable-buffer-local 'comment-start)
229 (setq comment-start "# ")
230 (make-variable-buffer-local 'comment-end)
231 (setq comment-end "")
232 (make-variable-buffer-local 'comment-column)
233 (setq comment-column ruby-comment-column)
234 (make-variable-buffer-local 'comment-start-skip)
235 (setq comment-start-skip "#+ *")
236 (setq indent-tabs-mode ruby-indent-tabs-mode)
237 (make-local-variable 'parse-sexp-ignore-comments)
238 (setq parse-sexp-ignore-comments t)
239 (make-local-variable 'paragraph-start)
240 (setq paragraph-start (concat "$\\|" page-delimiter))
241 (make-local-variable 'paragraph-separate)
242 (setq paragraph-separate paragraph-start)
243 (make-local-variable 'paragraph-ignore-fill-prefix)
244 (setq paragraph-ignore-fill-prefix t))
248 "Major mode for editing ruby scripts.
249 \\[ruby-indent-command] properly indents subexpressions of multi-line
250 class, module, def, if, while, for, do, and case statements, taking
251 nesting into account.
253 The variable `ruby-indent-level' controls the amount of indentation.
256 (kill-all-local-variables)
257 (use-local-map ruby-mode-map)
258 (setq mode-name "Ruby")
259 (setq major-mode 'ruby-mode)
260 (ruby-mode-variables)
262 (make-local-variable 'imenu-create-index-function)
263 (setq imenu-create-index-function 'ruby-imenu-create-index)
265 (make-local-variable 'add-log-current-defun-function)
266 (setq add-log-current-defun-function 'ruby-add-log-current-method)
268 (set (make-local-variable 'font-lock-defaults) '((ruby-font-lock-keywords) nil nil))
269 (set (make-local-variable 'font-lock-keywords) ruby-font-lock-keywords)
270 (set (make-local-variable 'font-lock-syntax-table) ruby-font-lock-syntax-table)
271 (set (make-local-variable 'font-lock-syntactic-keywords) ruby-font-lock-syntactic-keywords)
273 (run-mode-hooks 'ruby-mode-hook))
275 (defun ruby-current-indentation ()
278 (back-to-indentation)
281 (defun ruby-indent-line (&optional flag)
282 "Correct indentation of the current ruby line."
283 (ruby-indent-to (ruby-calculate-indent)))
285 (defun ruby-indent-command ()
287 (ruby-indent-line t))
289 (defun ruby-indent-to (x)
292 (and (< x 0) (error "invalid nest"))
293 (setq shift (current-column))
296 (back-to-indentation)
297 (setq top (current-column))
298 (skip-chars-backward " \t")
299 (if (>= shift top) (setq shift (- shift top))
303 (move-to-column (+ x shift))
305 (delete-region beg (point))
308 (move-to-column (+ x shift))))))
310 (defun ruby-special-char-p (&optional pnt)
311 (setq pnt (or pnt (point)))
312 (let ((c (char-before pnt)) (b (and (< (point-min) pnt) (char-before (1- pnt)))))
313 (cond ((or (eq c ??) (eq c ?$)))
314 ((and (eq c ?:) (or (not b) (eq (char-syntax b) ? ))))
315 ((eq c ?\\) (eq b ??)))))
317 (defun ruby-expr-beg (&optional option)
319 (store-match-data nil)
320 (let ((space (skip-chars-backward " \t"))
326 (and (looking-at "\\?")
327 (or (eq (char-syntax (char-before (point))) ?w)
328 (ruby-special-char-p))))
330 ((and (eq option 'heredoc) (< space 0)) t)
331 ((or (looking-at ruby-operator-re)
332 (looking-at "[\\[({,;]")
333 (and (looking-at "[!?]")
334 (or (not (eq option 'modifier))
336 (save-excursion (forward-char -1) (looking-at "\\Sw$"))))
337 (and (looking-at ruby-symbol-re)
338 (skip-chars-backward ruby-symbol-chars)
340 ((or (looking-at (concat "\\<\\(" ruby-block-beg-re
342 "|" ruby-block-mid-re "\\)\\>")))
343 (goto-char (match-end 0))
344 (not (looking-at "\\s_")))
345 ((eq option 'expr-qstr)
346 (looking-at "[a-zA-Z][a-zA-z0-9_]* +%[^ \t]"))
347 ((eq option 'expr-re)
348 (looking-at "[a-zA-Z][a-zA-z0-9_]* +/[^ \t]"))
351 (defun ruby-forward-string (term &optional end no-error expand)
352 (let ((n 1) (c (string-to-char term))
354 (concat "[^\\]\\(\\\\\\\\\\)*\\([" term "]\\|\\(#{\\)\\)")
355 (concat "[^\\]\\(\\\\\\\\\\)*[" term "]"))))
356 (while (and (re-search-forward re end no-error)
357 (if (match-beginning 3)
358 (ruby-forward-string "}{" end no-error nil)
359 (> (setq n (if (eq (char-before (point)) c)
364 ((error "unterminated string")))))
366 (defun ruby-deep-indent-paren-p (c)
367 (cond ((listp ruby-deep-indent-paren)
368 (let ((deep (assoc c ruby-deep-indent-paren)))
370 (or (cdr deep) ruby-deep-indent-paren-style))
371 ((memq c ruby-deep-indent-paren)
372 ruby-deep-indent-paren-style))))
373 ((eq c ruby-deep-indent-paren) ruby-deep-indent-paren-style)
374 ((eq c ?\( ) ruby-deep-arglist)))
376 (defun ruby-parse-partial (&optional end in-string nest depth pcol indent)
377 (or depth (setq depth 0))
378 (or indent (setq indent 0))
379 (when (re-search-forward ruby-delimiter end 'move)
380 (let ((pnt (point)) w re expand)
381 (goto-char (match-beginning 0))
383 ((and (memq (char-before) '(?@ ?$)) (looking-at "\\sw"))
385 ((looking-at "[\"`]") ;skip string
388 (ruby-forward-string (buffer-substring (point) (1+ (point))) end t t))
391 (setq in-string (point))
396 (re-search-forward "[^\\]\\(\\\\\\\\\\)*'" end t))
399 (setq in-string (point))
405 ((and (not (eobp)) (ruby-expr-beg 'expr-re))
406 (if (ruby-forward-string "/" end t t)
408 (setq in-string (point))
415 (ruby-expr-beg 'expr-qstr)
416 (not (looking-at "%="))
417 (looking-at "%[QqrxWw]?\\([^a-zA-Z0-9 \t\n]\\)"))
418 (goto-char (match-beginning 1))
419 (setq expand (not (memq (char-before) '(?q ?w))))
420 (setq w (match-string 1))
422 ((string= w "[") (setq re "]["))
423 ((string= w "{") (setq re "}{"))
424 ((string= w "(") (setq re ")("))
425 ((string= w "<") (setq re "><"))
426 ((and expand (string= w "\\"))
427 (setq w (concat "\\" w))))
428 (unless (cond (re (ruby-forward-string re end t expand))
429 (expand (ruby-forward-string w end t t))
430 (t (re-search-forward
433 (concat "[^\\]\\(\\\\\\\\\\)*" w))
435 (setq in-string (point))
439 ((looking-at "\\?") ;skip ?char
441 ((and (ruby-expr-beg)
442 (looking-at "?\\(\\\\C-\\|\\\\M-\\)*\\\\?."))
443 (goto-char (match-end 0)))
446 ((looking-at "\\$") ;skip $char
449 ((looking-at "#") ;skip comment
453 ((looking-at "[\\[{(]")
454 (let ((deep (ruby-deep-indent-paren-p (char-after))))
455 (if (and deep (or (not (eq (char-after) ?\{)) (ruby-expr-beg)))
457 (and (eq deep 'space) (looking-at ".\\s +[^# \t\n]")
458 (setq pnt (1- (match-end 0))))
459 (setq nest (cons (cons (char-after (point)) pnt) nest))
460 (setq pcol (cons (cons pnt depth) pcol))
462 (setq nest (cons (cons (char-after (point)) pnt) nest))
463 (setq depth (1+ depth))))
466 ((looking-at "[])}]")
467 (if (ruby-deep-indent-paren-p (matching-paren (char-after)))
468 (setq depth (cdr (car pcol)) pcol (cdr pcol))
469 (setq depth (1- depth)))
470 (setq nest (cdr nest))
472 ((looking-at ruby-block-end-re)
473 (if (or (and (not (bolp))
476 (setq w (char-after (point)))
481 (setq w (char-after (point)))
486 (setq nest (cdr nest))
487 (setq depth (1- depth)))
489 ((looking-at "def\\s +[^(\n;]*")
493 (not (eq ?_ (char-after (point))))))
495 (setq nest (cons (cons nil pnt) nest))
496 (setq depth (1+ depth))))
497 (goto-char (match-end 0)))
498 ((looking-at (concat "\\<\\(" ruby-block-beg-re "\\)\\>"))
501 (or (not (looking-at "do\\>[^_]"))
503 (back-to-indentation)
504 (not (looking-at ruby-non-block-do-re)))))
508 (setq w (char-after (point)))
512 (setq w (char-after (point)))
516 (skip-chars-forward " \t")
517 (goto-char (match-beginning 0))
518 (or (not (looking-at ruby-modifier-re))
519 (ruby-expr-beg 'modifier))
521 (setq nest (cons (cons nil pnt) nest))
522 (setq depth (1+ depth)))
524 ((looking-at ":\\(['\"]\\)")
525 (goto-char (match-beginning 1))
526 (ruby-forward-string (buffer-substring (match-beginning 1) (match-end 1)) end))
527 ((looking-at ":\\([-,.+*/%&|^~<>]=?\\|===?\\|<=>\\)")
528 (goto-char (match-end 0)))
529 ((looking-at ":\\([a-zA-Z_][a-zA-Z_0-9]*[!?=]?\\)?")
530 (goto-char (match-end 0)))
531 ((or (looking-at "\\.\\.\\.?")
532 (looking-at "\\.[0-9]+")
533 (looking-at "\\.[a-zA-Z_0-9]+")
535 (goto-char (match-end 0)))
536 ((looking-at "^=begin")
537 (if (re-search-forward "^=end" end t)
539 (setq in-string (match-end 0))
543 ((and (ruby-expr-beg 'heredoc)
544 (looking-at "<<\\(-\\)?\\(\\([\"'`]\\)\\([^\n]+?\\)\\3\\|\\(?:\\sw\\|\\s_\\)+\\)"))
545 (setq re (regexp-quote (or (match-string 4) (match-string 2))))
546 (if (match-beginning 1) (setq re (concat "\\s *" re)))
547 (let* ((id-end (goto-char (match-end 0)))
548 (line-end-position (save-excursion (end-of-line) (point)))
549 (state (list in-string nest depth pcol indent)))
550 ;; parse the rest of the line
551 (while (and (> line-end-position (point))
552 (setq state (apply 'ruby-parse-partial
553 line-end-position state))))
554 (setq in-string (car state)
558 indent (nth 4 state))
559 ;; skip heredoc section
560 (if (re-search-forward (concat "^" re "$") end 'move)
562 (setq in-string id-end)
566 ((looking-at "^__END__$")
568 ((looking-at ruby-here-doc-beg-re)
569 (if (re-search-forward (ruby-here-doc-end-match)
572 (setq in-string (match-end 0))
573 (goto-char indent-point)))
575 (error (format "bad string %s"
576 (buffer-substring (point) pnt)
578 (list in-string nest depth pcol))
580 (defun ruby-parse-region (start end)
585 (ruby-beginning-of-indent))
587 (narrow-to-region (point) end)
588 (while (and (> end (point))
589 (setq state (apply 'ruby-parse-partial end state))))))
590 (list (nth 0 state) ; in-string
591 (car (nth 1 state)) ; nest
592 (nth 2 state) ; depth
593 (car (car (nth 3 state))) ; pcol
594 ;(car (nth 5 state)) ; indent
597 (defun ruby-indent-size (pos nest)
598 (+ pos (* (or nest 1) ruby-indent-level)))
600 (defun ruby-calculate-indent (&optional parse-start)
603 (let ((indent-point (point))
604 (case-fold-search nil)
605 state bol eol begin op-end
606 (paren (progn (skip-syntax-forward " ")
607 (and (char-after) (matching-paren (char-after)))))
610 (goto-char parse-start)
611 (ruby-beginning-of-indent)
612 (setq parse-start (point)))
613 (back-to-indentation)
614 (setq indent (current-column))
615 (setq state (ruby-parse-region parse-start indent-point))
617 ((nth 0 state) ; within string
618 (setq indent nil)) ; do nothing
619 ((car (nth 1 state)) ; in paren
620 (goto-char (setq begin (cdr (nth 1 state))))
621 (let ((deep (ruby-deep-indent-paren-p (car (nth 1 state)))))
623 (cond ((and (eq deep t) (eq (car (nth 1 state)) paren))
624 (skip-syntax-backward " ")
625 (setq indent (1- (current-column))))
626 ((let ((s (ruby-parse-region (point) indent-point)))
627 (and (nth 2 s) (> (nth 2 s) 0)
628 (or (goto-char (cdr (nth 1 s))) t)))
630 (setq indent (ruby-indent-size (current-column) (nth 2 state))))
632 (setq indent (current-column))
633 (cond ((eq deep 'space))
634 (paren (setq indent (1- indent)))
635 (t (setq indent (ruby-indent-size (1- indent) 1))))))
636 (if (nth 3 state) (goto-char (nth 3 state))
637 (goto-char parse-start) (back-to-indentation))
638 (setq indent (ruby-indent-size (current-column) (nth 2 state))))
639 (and (eq (car (nth 1 state)) paren)
640 (ruby-deep-indent-paren-p (matching-paren paren))
641 (search-backward (char-to-string paren))
642 (setq indent (current-column)))))
643 ((and (nth 2 state) (> (nth 2 state) 0)) ; in nest
644 (if (null (cdr (nth 1 state)))
645 (error "invalid nest"))
646 (goto-char (cdr (nth 1 state)))
647 (forward-word -1) ; skip back a keyword
650 ((looking-at "do\\>[^_]") ; iter block is a special case
651 (if (nth 3 state) (goto-char (nth 3 state))
652 (goto-char parse-start) (back-to-indentation))
653 (setq indent (ruby-indent-size (current-column) (nth 2 state))))
655 (setq indent (+ (current-column) ruby-indent-level)))))
657 ((and (nth 2 state) (< (nth 2 state) 0)) ; in negative nest
658 (setq indent (ruby-indent-size (current-column) (nth 2 state)))))
660 (goto-char indent-point)
665 ((and (not (ruby-deep-indent-paren-p paren))
666 (re-search-forward ruby-negative eol t))
667 (and (not (eq ?_ (char-after (match-end 0))))
668 (setq indent (- indent ruby-indent-level))))
673 (or (ruby-deep-indent-paren-p t)
674 (null (car (nth 1 state)))))
675 ;; goto beginning of non-empty no-comment line
678 (skip-chars-backward " \t\n")
681 (if (re-search-forward "^\\s *#" end t)
686 ;; skip the comment at the end
687 (skip-chars-backward " \t")
688 (let (end (pos (point)))
690 (while (and (re-search-forward "#" pos t)
691 (setq end (1- (point)))
692 (or (ruby-special-char-p end)
693 (and (setq state (ruby-parse-region parse-start end))
696 (goto-char (or end pos))
697 (skip-chars-backward " \t")
698 (setq begin (if (nth 0 state) pos (cdr (nth 1 state))))
699 (setq state (ruby-parse-region parse-start (point))))
700 (or (bobp) (forward-char -1))
702 (or (and (looking-at ruby-symbol-re)
703 (skip-chars-backward ruby-symbol-chars)
704 (looking-at (concat "\\<\\(" ruby-block-hanging-re "\\)\\>"))
705 (not (eq (point) (nth 3 state)))
707 (goto-char (match-end 0))
708 (not (looking-at "[a-z_]"))))
709 (and (looking-at ruby-operator-re)
710 (not (ruby-special-char-p))
711 ;; operator at the end of line
712 (let ((c (char-after (point))))
717 ;; (skip-chars-forward " \t")
718 ;; (not (or (eolp) (looking-at "#")
719 ;; (and (eq (car (nth 1 state)) ?{)
720 ;; (looking-at "|"))))))
722 (null (nth 0 (ruby-parse-region (or begin parse-start) (point)))))
723 (or (not (eq ?| (char-after (point))))
725 (or (eolp) (forward-char -1))
727 ((search-backward "|" nil t)
728 (skip-chars-backward " \t\n")
732 (not (looking-at "{")))
735 (not (looking-at "do\\>[^_]")))))
743 (not (looking-at (concat "\\<\\(" ruby-block-hanging-re "\\)\\>")))
744 (eq (ruby-deep-indent-paren-p t) 'space)
748 (goto-char (or begin parse-start))
749 (skip-syntax-forward " ")
751 ((car (nth 1 state)) indent)
753 (+ indent ruby-indent-level))))))))
756 (defun ruby-electric-brace (arg)
758 (insert-char last-command-char 1)
761 (self-insert-command (prefix-numeric-value arg)))
764 (defmacro defun-region-command (func args &rest body)
765 (let ((intr (car body)))
766 (when (featurep 'xemacs)
767 (if (stringp intr) (setq intr (cadr body)))
768 (and (eq (car intr) 'interactive)
769 (setq intr (cdr intr))
770 (setcar intr (concat "_" (car intr)))))
771 (cons 'defun (cons func (cons args body))))))
773 (defun-region-command ruby-beginning-of-defun (&optional arg)
774 "Move backward to next beginning-of-defun.
775 With argument, do this that many times.
776 Returns t unless search stops due to end of buffer."
778 (and (re-search-backward (concat "^\\(" ruby-block-beg-re "\\)\\b")
779 nil 'move (or arg 1))
780 (progn (beginning-of-line) t)))
782 (defun ruby-beginning-of-indent ()
783 (and (re-search-backward (concat "^\\(" ruby-indent-beg-re "\\)\\b")
789 (defun-region-command ruby-end-of-defun (&optional arg)
790 "Move forward to next end of defun.
791 An end of a defun is found by moving forward from the beginning of one."
793 (and (re-search-forward (concat "^\\(" ruby-block-end-re "\\)\\($\\|\\b[^_]\\)")
794 nil 'move (or arg 1))
795 (progn (beginning-of-line) t))
798 (defun ruby-move-to-block (n)
799 (let (start pos done down)
800 (setq start (ruby-calculate-indent))
801 (setq down (looking-at (if (< n 0) ruby-block-end-re
802 (concat "\\<\\(" ruby-block-beg-re "\\)\\>"))))
803 (while (and (not done) (not (if (< n 0) (bobp) (eobp))))
806 ((looking-at "^\\s *$"))
807 ((looking-at "^\\s *#"))
808 ((and (> n 0) (looking-at "^=begin\\>"))
809 (re-search-forward "^=end\\>"))
810 ((and (< n 0) (looking-at "^=end\\>"))
811 (re-search-backward "^=begin\\>"))
813 (setq pos (current-indentation))
817 ((and down (= pos start))
823 (back-to-indentation)
824 (if (looking-at (concat "\\<\\(" ruby-block-mid-re "\\)\\>"))
826 (back-to-indentation))
828 (defun-region-command ruby-beginning-of-block (&optional arg)
829 "Move backward to next beginning-of-block"
831 (ruby-move-to-block (- (or arg 1))))
833 (defun-region-command ruby-end-of-block (&optional arg)
834 "Move forward to next beginning-of-block"
836 (ruby-move-to-block (or arg 1)))
838 (defun-region-command ruby-forward-sexp (&optional cnt)
840 (if (and (numberp cnt) (< cnt 0))
841 (ruby-backward-sexp (- cnt))
842 (let ((i (or cnt 1)))
845 (skip-syntax-forward " ")
846 (cond ((looking-at "\\?\\(\\\\[CM]-\\)*\\\\?\\S ")
847 (goto-char (match-end 0)))
849 (skip-chars-forward ",.:;|&^~=!?\\+\\-\\*")
851 (goto-char (scan-sexps (point) 1)))
852 ((and (looking-at (concat "\\<\\(" ruby-block-beg-re "\\)\\>"))
853 (not (eq (char-before (point)) ?.))
854 (not (eq (char-before (point)) ?:)))
857 ((looking-at "\\(\\$\\|@@?\\)?\\sw")
859 (while (progn (forward-word 1) (looking-at "_")))
860 (cond ((looking-at "::") (forward-char 2) t)
861 ((> (skip-chars-forward ".") 0))
862 ((looking-at "\\?\\|!\\(=[~=>]\\|[^~=]\\)")
863 (forward-char 1) nil)))))
867 (setq expr (or expr (ruby-expr-beg)
868 (looking-at "%\\sw?\\Sw\\|[\"'`/]")))
869 (nth 1 (setq state (apply 'ruby-parse-partial nil state))))
871 (skip-chars-forward "<"))
874 ((error) (forward-word 1)))
877 (defun-region-command ruby-backward-sexp (&optional cnt)
879 (if (and (numberp cnt) (< cnt 0))
880 (ruby-forward-sexp (- cnt))
881 (let ((i (or cnt 1)))
884 (skip-chars-backward " \t\n,.:;|&^~=!?\\+\\-\\*")
886 (cond ((looking-at "\\s)")
887 (goto-char (scan-sexps (1+ (point)) -1))
889 (?% (forward-char -1))
890 ('(?q ?Q ?w ?W ?r ?x)
891 (if (eq (char-before (1- (point))) ?%) (forward-char -2))))
893 ((looking-at "\\s\"\\|\\\\\\S_")
894 (let ((c (char-to-string (char-before (match-end 0)))))
895 (while (and (search-backward c)
896 (oddp (skip-chars-backward "\\")))))
898 ((looking-at "\\s.\\|\\s\\")
899 (if (ruby-special-char-p) (forward-char -1)))
900 ((looking-at "\\s(") nil)
903 (while (progn (forward-word -1)
906 (?. (forward-char -1) t)
909 (and (eq (char-before) (char-after)) (forward-char -1)))
912 (eq (char-before) :)))))
913 (if (looking-at ruby-block-end-re)
914 (ruby-beginning-of-block))
920 (defun ruby-reindent-then-newline-and-indent ()
925 (indent-according-to-mode)
926 (delete-region (point) (progn (skip-chars-backward " \t") (point))))
927 (indent-according-to-mode))
929 (fset 'ruby-encomment-region (symbol-function 'comment-region))
931 (defun ruby-decomment-region (beg end)
935 (while (re-search-forward "^\\([ \t]*\\)#" end t)
936 (replace-match "\\1" nil nil)
938 (ruby-indent-line)))))
940 (defun ruby-insert-end ()
946 (defun ruby-mark-defun ()
947 "Put mark at end of this Ruby function, point at beginning."
951 (push-mark (point) nil t)
952 (ruby-beginning-of-defun)
953 (re-search-backward "^\n" (- (point) 1) t))
955 (defun ruby-indent-exp (&optional shutup-p)
956 "Indent each line in the balanced expression following point syntactically.
957 If optional SHUTUP-P is non-nil, no errors are signalled if no
958 balanced expression is found."
960 (let ((here (point-marker)) start top column (nest t))
961 (set-marker-insertion-type here t)
965 (setq start (point) top (current-indentation))
966 (while (and (not (eobp))
968 (setq column (ruby-calculate-indent start))
969 (cond ((> column top)
971 ((and (= column top) nest)
972 (setq nest nil) t))))
973 (ruby-indent-to column)
974 (beginning-of-line 2)))
976 (set-marker here nil))))
978 (defun ruby-add-log-current-method ()
979 "Return current method string."
982 (let ((mlist nil) (indent 0))
983 ;; get current method (or class/module)
984 (if (re-search-backward
985 (concat "^[ \t]*\\(def\\|class\\|module\\)[ \t]+"
987 ;; \\. for class method
988 "\\(" ruby-symbol-re "\\|\\." "\\)"
992 (setq mlist (list (match-string 2)))
993 (goto-char (match-beginning 1))
994 (setq indent (current-column))
995 (beginning-of-line)))
997 (while (and (> indent 0)
1000 "^[ \t]*\\(class\\|module\\)[ \t]+"
1001 "\\([A-Z]" ruby-symbol-re "+\\)")
1003 (goto-char (match-beginning 1))
1004 (if (< (current-column) indent)
1006 (setq mlist (cons (match-string 2) mlist))
1007 (setq indent (current-column))
1008 (beginning-of-line))))
1011 (mapconcat (function identity) mlist "::")
1015 ((featurep 'font-lock)
1016 (or (boundp 'font-lock-variable-name-face)
1017 (setq font-lock-variable-name-face font-lock-type-face))
1019 (setq ruby-font-lock-syntactic-keywords
1021 ;; #{ }, #$hoge, #@foo are not comments
1022 ("\\(#\\)[{$@]" 1 (1 . nil))
1023 ;; the last $', $", $` in the respective string is not variable
1024 ;; the last ?', ?", ?` in the respective string is not ascii code
1025 ("\\(^\\|[\[ \t\n<+\(,=]\\)\\(['\"`]\\)\\(\\\\.\\|\\2\\|[^'\"`\n\\\\]\\)*?\\\\?[?$]\\(\\2\\)"
1028 ;; $' $" $` .... are variables
1029 ;; ?' ?" ?` are ascii codes
1030 ("\\(^\\|[^\\\\]\\)\\(\\\\\\\\\\)*[?$]\\([#\"'`]\\)" 3 (1 . nil))
1032 ("\\(^\\|[=(,~?:;<>]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)"
1035 ("^\\(=\\)begin\\(\\s \\|$\\)" 1 (7 . nil))
1036 ("^\\(=\\)end\\(\\s \\|$\\)" 1 (7 . nil))))
1038 (if (featurep 'xemacs)
1039 (put 'ruby-mode 'font-lock-defaults
1040 '((ruby-font-lock-keywords)
1043 (font-lock-syntactic-keywords
1044 . ruby-font-lock-syntactic-keywords))))
1046 (defun ruby-font-lock-docs (limit)
1047 (if (re-search-forward "^=begin\\(\\s \\|$\\)" limit t)
1052 (if (re-search-forward "^=end\\(\\s \\|$\\)" limit t)
1054 (set-match-data (list beg (point)))
1057 (defun ruby-font-lock-maybe-docs (limit)
1060 (if (and (re-search-backward "^=\\(begin\\|end\\)\\(\\s \\|$\\)" nil t)
1061 (string= (match-string 1) "begin"))
1064 (setq beg (point)))))
1065 (if (and beg (and (re-search-forward "^=\\(begin\\|end\\)\\(\\s \\|$\\)" nil t)
1066 (string= (match-string 1) "end")))
1068 (set-match-data (list beg (point)))
1072 (defvar ruby-font-lock-syntax-table
1073 (let* ((tbl (copy-syntax-table ruby-mode-syntax-table)))
1074 (modify-syntax-entry ?_ "w" tbl)
1077 (defun ruby-font-lock-here-docs (limit)
1078 (if (re-search-forward ruby-here-doc-beg-re limit t)
1083 (if (re-search-forward (ruby-here-doc-end-match) nil t)
1085 (set-match-data (list beg (point)))
1088 (defun ruby-font-lock-maybe-here-docs (limit)
1091 (if (re-search-backward ruby-here-doc-beg-re nil t)
1095 (setq beg (point)))))
1097 (let ((end-match (ruby-here-doc-end-match)))
1098 (and (not (re-search-backward end-match beg t))
1099 (re-search-forward end-match nil t))))
1101 (set-match-data (list beg (point)))
1105 (defvar ruby-font-lock-keywords
1108 '("^\\s *def\\s +\\([^( \t\n]+\\)"
1109 1 font-lock-function-name-face)
1112 "\\(^\\|[^_:.@$]\\|\\.\\.\\)\\b\\(defined\\?\\|\\("
1155 '("\\(^\\|[^_:.@$]\\|\\.\\.\\)\\b\\(nil\\|self\\|true\\|false\\)\\>"
1156 2 font-lock-variable-name-face)
1158 '("\\(\\$\\([^a-zA-Z0-9 \n]\\|[0-9]\\)\\)\\W"
1159 1 font-lock-variable-name-face)
1160 '("\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+"
1161 0 font-lock-variable-name-face)
1162 ;; embedded document
1163 '(ruby-font-lock-docs
1164 0 font-lock-comment-face t)
1165 '(ruby-font-lock-maybe-docs
1166 0 font-lock-comment-face t)
1168 '(ruby-font-lock-here-docs
1169 0 font-lock-string-face t)
1170 '(ruby-font-lock-maybe-here-docs
1171 0 font-lock-string-face t)
1172 `(,ruby-here-doc-beg-re
1173 0 font-lock-string-face t)
1174 ;; general delimited string
1175 '("\\(^\\|[[ \t\n<+(,=]\\)\\(%[xrqQwW]?\\([^<[{(a-zA-Z0-9 \n]\\)[^\n\\\\]*\\(\\\\.[^\n\\\\]*\\)*\\(\\3\\)\\)"
1176 (2 font-lock-string-face))
1178 '("\\(^\\|[^_]\\)\\b\\([A-Z]+\\(\\w\\|_\\)*\\)"
1179 2 font-lock-type-face)
1181 '("\\(^\\|[^:]\\)\\(:\\([-+~]@?\\|[/%&|^`]\\|\\*\\*?\\|<\\(<\\|=>?\\)?\\|>[>=]?\\|===?\\|=~\\|\\[\\]=?\\|\\(\\w\\|_\\)+\\([!?=]\\|\\b_*\\)\\|#{[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\)\\)"
1182 2 font-lock-reference-face)
1183 ;; expression expansion
1184 '("#\\({[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\|\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+\\)"
1185 0 font-lock-variable-name-face t)
1186 ;; warn lower camel case
1187 ;'("\\<[a-z]+[a-z0-9]*[A-Z][A-Za-z0-9]*\\([!?]?\\|\\>\\)"
1188 ; 0 font-lock-warning-face)
1190 "*Additional expressions to highlight in ruby mode."))
1192 ((featurep 'hilit19)
1193 (hilit-set-mode-patterns
1195 '(("[^$\\?]\\(\"[^\\\"]*\\(\\\\\\(.\\|\n\\)[^\\\"]*\\)*\"\\)" 1 string)
1196 ("[^$\\?]\\('[^\\']*\\(\\\\\\(.\\|\n\\)[^\\']*\\)*'\\)" 1 string)
1197 ("[^$\\?]\\(`[^\\`]*\\(\\\\\\(.\\|\n\\)[^\\`]*\\)*`\\)" 1 string)
1198 ("^\\s *#.*$" nil comment)
1199 ("[^$@?\\]\\(#[^$@{\n].*$\\)" 1 comment)
1200 ("[^a-zA-Z_]\\(\\?\\(\\\\[CM]-\\)*.\\)" 1 string)
1201 ("^\\s *\\(require\\|load\\).*$" nil include)
1202 ("^\\s *\\(include\\|alias\\|undef\\).*$" nil decl)
1203 ("^\\s *\\<\\(class\\|def\\|module\\)\\>" "[)\n;]" defun)
1204 ("[^_]\\<\\(begin\\|case\\|else\\|elsif\\|end\\|ensure\\|for\\|if\\|unless\\|rescue\\|then\\|when\\|while\\|until\\|do\\|yield\\)\\>\\([^_]\\|$\\)" 1 defun)
1205 ("[^_]\\<\\(and\\|break\\|next\\|raise\\|fail\\|in\\|not\\|or\\|redo\\|retry\\|return\\|super\\|yield\\|catch\\|throw\\|self\\|nil\\)\\>\\([^_]\\|$\\)" 1 keyword)
1206 ("\\$\\(.\\|\\sw+\\)" nil type)
1207 ("[$@].[a-zA-Z_0-9]*" nil struct)
1208 ("^__END__" nil label))))
1212 ;;;###autoload(add-to-list 'auto-mode-alist '("\\.rb$" . ruby-mode))
1213 ;;;###autoload(add-to-list 'auto-mode-alist '("\\.rake$" . ruby-mode))
1215 (provide 'ruby-mode)
1217 ;;; ruby-mode.el ends here