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