diff options
| author | Stefan Monnier | 2013-05-08 16:25:57 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2013-05-08 16:25:57 -0400 |
| commit | a9e4425bc99733b6ecf3898801b6595f35c4ef30 (patch) | |
| tree | 903ca31485cda0ddef4de71a44ed8283542a6d88 | |
| parent | 060ca4088d928e0808d13a551ea11b2fc00769d4 (diff) | |
| download | emacs-a9e4425bc99733b6ecf3898801b6595f35c4ef30.tar.gz emacs-a9e4425bc99733b6ecf3898801b6595f35c4ef30.zip | |
* lisp/progmodes/ruby-mode.el: First cut at SMIE support.
(ruby-use-smie): New var.
(ruby-smie-grammar): New constant.
(ruby-smie--bosp, ruby-smie--implicit-semi-p)
(ruby-smie--forward-token, ruby-smie--backward-token)
(ruby-smie-rules): New functions.
(ruby-mode-variables): Setup SMIE if applicable.
* test/indent/ruby.rb: Fix indentation after =; add more cases.
| -rw-r--r-- | lisp/ChangeLog | 10 | ||||
| -rw-r--r-- | lisp/progmodes/ruby-mode.el | 120 | ||||
| -rw-r--r-- | test/ChangeLog | 4 | ||||
| -rw-r--r-- | test/indent/ruby.rb | 36 |
4 files changed, 165 insertions, 5 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 1abcc933847..12e2007c126 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,13 @@ | |||
| 1 | 2013-05-08 Stefan Monnier <monnier@iro.umontreal.ca> | ||
| 2 | |||
| 3 | * progmodes/ruby-mode.el: First cut at SMIE support. | ||
| 4 | (ruby-use-smie): New var. | ||
| 5 | (ruby-smie-grammar): New constant. | ||
| 6 | (ruby-smie--bosp, ruby-smie--implicit-semi-p) | ||
| 7 | (ruby-smie--forward-token, ruby-smie--backward-token) | ||
| 8 | (ruby-smie-rules): New functions. | ||
| 9 | (ruby-mode-variables): Setup SMIE if applicable. | ||
| 10 | |||
| 1 | 2013-05-08 Eli Zaretskii <eliz@gnu.org> | 11 | 2013-05-08 Eli Zaretskii <eliz@gnu.org> |
| 2 | 12 | ||
| 3 | * simple.el (line-move-visual): Signal beginning/end of buffer | 13 | * simple.el (line-move-visual): Signal beginning/end of buffer |
diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index 631badac34c..3ea55c4eabf 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el | |||
| @@ -148,13 +148,16 @@ This should only be called after matching against `ruby-here-doc-beg-re'." | |||
| 148 | (define-abbrev-table 'ruby-mode-abbrev-table () | 148 | (define-abbrev-table 'ruby-mode-abbrev-table () |
| 149 | "Abbrev table in use in Ruby mode buffers.") | 149 | "Abbrev table in use in Ruby mode buffers.") |
| 150 | 150 | ||
| 151 | (defvar ruby-use-smie nil) | ||
| 152 | |||
| 151 | (defvar ruby-mode-map | 153 | (defvar ruby-mode-map |
| 152 | (let ((map (make-sparse-keymap))) | 154 | (let ((map (make-sparse-keymap))) |
| 153 | (define-key map (kbd "M-C-b") 'ruby-backward-sexp) | 155 | (unless ruby-use-smie |
| 154 | (define-key map (kbd "M-C-f") 'ruby-forward-sexp) | 156 | (define-key map (kbd "M-C-b") 'ruby-backward-sexp) |
| 157 | (define-key map (kbd "M-C-f") 'ruby-forward-sexp) | ||
| 158 | (define-key map (kbd "M-C-q") 'ruby-indent-exp)) | ||
| 155 | (define-key map (kbd "M-C-p") 'ruby-beginning-of-block) | 159 | (define-key map (kbd "M-C-p") 'ruby-beginning-of-block) |
| 156 | (define-key map (kbd "M-C-n") 'ruby-end-of-block) | 160 | (define-key map (kbd "M-C-n") 'ruby-end-of-block) |
| 157 | (define-key map (kbd "M-C-q") 'ruby-indent-exp) | ||
| 158 | (define-key map (kbd "C-c {") 'ruby-toggle-block) | 161 | (define-key map (kbd "C-c {") 'ruby-toggle-block) |
| 159 | map) | 162 | map) |
| 160 | "Keymap used in Ruby mode.") | 163 | "Keymap used in Ruby mode.") |
| @@ -236,6 +239,111 @@ Also ignores spaces after parenthesis when 'space." | |||
| 236 | (put 'ruby-comment-column 'safe-local-variable 'integerp) | 239 | (put 'ruby-comment-column 'safe-local-variable 'integerp) |
| 237 | (put 'ruby-deep-arglist 'safe-local-variable 'booleanp) | 240 | (put 'ruby-deep-arglist 'safe-local-variable 'booleanp) |
| 238 | 241 | ||
| 242 | ;;; SMIE support | ||
| 243 | |||
| 244 | (require 'smie) | ||
| 245 | |||
| 246 | (defconst ruby-smie-grammar | ||
| 247 | ;; FIXME: Add support for Cucumber. | ||
| 248 | (smie-prec2->grammar | ||
| 249 | (smie-bnf->prec2 | ||
| 250 | '((id) | ||
| 251 | (insts (inst) (insts ";" insts)) | ||
| 252 | (inst (exp) (inst "iuwu-mod" exp)) | ||
| 253 | (exp (exp1) (exp "," exp)) | ||
| 254 | (exp1 (exp2) (exp2 "?" exp1 ":" exp1)) | ||
| 255 | (exp2 ("def" insts "end") | ||
| 256 | ("begin" insts-rescue-insts "end") | ||
| 257 | ("do" insts "end") | ||
| 258 | ("class" insts "end") ("module" insts "end") | ||
| 259 | ("for" for-body "end") | ||
| 260 | ("[" expseq "]") | ||
| 261 | ("{" hashvals "}") | ||
| 262 | ("while" insts "end") | ||
| 263 | ("until" insts "end") | ||
| 264 | ("unless" insts "end") | ||
| 265 | ("if" if-body "end") | ||
| 266 | ("case" cases "end")) | ||
| 267 | (for-body (for-head ";" insts)) | ||
| 268 | (for-head (id "in" exp)) | ||
| 269 | (cases (exp "then" insts) ;; FIXME: Ruby also allows (exp ":" insts). | ||
| 270 | (cases "when" cases) (insts "else" insts)) | ||
| 271 | (expseq (exp) );;(expseq "," expseq) | ||
| 272 | (hashvals (id "=>" exp1) (hashvals "," hashvals)) | ||
| 273 | (insts-rescue-insts (insts) | ||
| 274 | (insts-rescue-insts "rescue" insts-rescue-insts) | ||
| 275 | (insts-rescue-insts "ensure" insts-rescue-insts)) | ||
| 276 | (itheni (insts) (exp "then" insts)) | ||
| 277 | (ielsei (itheni) (itheni "else" insts)) | ||
| 278 | (if-body (ielsei) (if-body "elsif" if-body))) | ||
| 279 | '((nonassoc "in") (assoc ";") (assoc ",")) | ||
| 280 | '((assoc "when")) | ||
| 281 | '((assoc "elsif")) | ||
| 282 | '((assoc "rescue" "ensure")) | ||
| 283 | '((assoc ","))))) | ||
| 284 | |||
| 285 | (defun ruby-smie--bosp () | ||
| 286 | (save-excursion (skip-chars-backward " \t") | ||
| 287 | (or (bolp) (eq (char-before) ?\;)))) | ||
| 288 | |||
| 289 | (defun ruby-smie--implicit-semi-p () | ||
| 290 | (save-excursion | ||
| 291 | (skip-chars-backward " \t") | ||
| 292 | (not (or (bolp) | ||
| 293 | (memq (char-before) '(?\; ?- ?+ ?* ?/ ?:)) | ||
| 294 | (and (memq (char-before) '(?\? ?=)) | ||
| 295 | (not (memq (char-syntax (char-before (1- (point)))) | ||
| 296 | '(?w ?_)))))))) | ||
| 297 | |||
| 298 | (defun ruby-smie--forward-token () | ||
| 299 | (skip-chars-forward " \t") | ||
| 300 | (if (and (looking-at "[\n#]") | ||
| 301 | ;; Only add implicit ; when needed. | ||
| 302 | (ruby-smie--implicit-semi-p)) | ||
| 303 | (progn | ||
| 304 | (if (eolp) (forward-char 1) (forward-comment 1)) | ||
| 305 | ";") | ||
| 306 | (forward-comment (point-max)) | ||
| 307 | (let ((tok (smie-default-forward-token))) | ||
| 308 | (cond | ||
| 309 | ((member tok '("unless" "if" "while" "until")) | ||
| 310 | (if (save-excursion (forward-word -1) (ruby-smie--bosp)) | ||
| 311 | tok "iuwu-mod")) | ||
| 312 | (t tok))))) | ||
| 313 | |||
| 314 | (defun ruby-smie--backward-token () | ||
| 315 | (let ((pos (point))) | ||
| 316 | (forward-comment (- (point))) | ||
| 317 | (if (and (> pos (line-end-position)) | ||
| 318 | (ruby-smie--implicit-semi-p)) | ||
| 319 | (progn (skip-chars-forward " \t") | ||
| 320 | ";") | ||
| 321 | (let ((tok (smie-default-backward-token))) | ||
| 322 | (cond | ||
| 323 | ((member tok '("unless" "if" "while" "until")) | ||
| 324 | (if (ruby-smie--bosp) | ||
| 325 | tok "iuwu-mod")) | ||
| 326 | (t tok)))))) | ||
| 327 | |||
| 328 | (defun ruby-smie-rules (kind token) | ||
| 329 | (pcase (cons kind token) | ||
| 330 | (`(:elem . basic) ruby-indent-level) | ||
| 331 | (`(:after . ";") | ||
| 332 | (if (smie-rule-parent-p "def" "begin" "do" "class" "module" "for" | ||
| 333 | "[" "{" "while" "until" "unless" | ||
| 334 | "if" "then" "elsif" "else" "when" | ||
| 335 | "rescue" "ensure") | ||
| 336 | (smie-rule-parent ruby-indent-level) | ||
| 337 | ;; For (invalid) code between switch and case. | ||
| 338 | ;; (if (smie-parent-p "switch") 4) | ||
| 339 | 0)) | ||
| 340 | (`(:before . ,(or `"else" `"then" `"elsif")) 0) | ||
| 341 | (`(:before . ,(or `"when")) | ||
| 342 | (if (not (smie-rule-sibling-p)) 0)) ;; ruby-indent-level | ||
| 343 | ;; Hack attack: Since newlines are separators, don't try to align args that | ||
| 344 | ;; appear on a separate line. | ||
| 345 | (`(:list-intro . ";") t))) | ||
| 346 | |||
| 239 | (defun ruby-imenu-create-index-in-block (prefix beg end) | 347 | (defun ruby-imenu-create-index-in-block (prefix beg end) |
| 240 | "Create an imenu index of methods inside a block." | 348 | "Create an imenu index of methods inside a block." |
| 241 | (let ((index-alist '()) (case-fold-search nil) | 349 | (let ((index-alist '()) (case-fold-search nil) |
| @@ -290,7 +398,11 @@ Also ignores spaces after parenthesis when 'space." | |||
| 290 | (set-syntax-table ruby-mode-syntax-table) | 398 | (set-syntax-table ruby-mode-syntax-table) |
| 291 | (setq local-abbrev-table ruby-mode-abbrev-table) | 399 | (setq local-abbrev-table ruby-mode-abbrev-table) |
| 292 | (setq indent-tabs-mode ruby-indent-tabs-mode) | 400 | (setq indent-tabs-mode ruby-indent-tabs-mode) |
| 293 | (set (make-local-variable 'indent-line-function) 'ruby-indent-line) | 401 | (if ruby-use-smie |
| 402 | (smie-setup ruby-smie-grammar #'ruby-smie-rules | ||
| 403 | :forward-token #'ruby-smie--forward-token | ||
| 404 | :backward-token #'ruby-smie--backward-token) | ||
| 405 | (set (make-local-variable 'indent-line-function) 'ruby-indent-line)) | ||
| 294 | (set (make-local-variable 'require-final-newline) t) | 406 | (set (make-local-variable 'require-final-newline) t) |
| 295 | (set (make-local-variable 'comment-start) "# ") | 407 | (set (make-local-variable 'comment-start) "# ") |
| 296 | (set (make-local-variable 'comment-end) "") | 408 | (set (make-local-variable 'comment-end) "") |
diff --git a/test/ChangeLog b/test/ChangeLog index 48d499a9fa4..c11d5d26c13 100644 --- a/test/ChangeLog +++ b/test/ChangeLog | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | 2013-05-08 Stefan Monnier <monnier@iro.umontreal.ca> | ||
| 2 | |||
| 3 | * indent/ruby.rb: Fix indentation after =; add more cases. | ||
| 4 | |||
| 1 | 2013-05-05 Stefan Monnier <monnier@iro.umontreal.ca> | 5 | 2013-05-05 Stefan Monnier <monnier@iro.umontreal.ca> |
| 2 | 6 | ||
| 3 | * indent/pascal.pas: Add test for mis-identified comments. | 7 | * indent/pascal.pas: Add test for mis-identified comments. |
diff --git a/test/indent/ruby.rb b/test/indent/ruby.rb index 4f2e9e63377..90c6dcdc65c 100644 --- a/test/indent/ruby.rb +++ b/test/indent/ruby.rb | |||
| @@ -10,7 +10,7 @@ d = %(hello (nested) world) | |||
| 10 | 10 | ||
| 11 | # Or inside comments. | 11 | # Or inside comments. |
| 12 | x = # "tot %q/to"; = | 12 | x = # "tot %q/to"; = |
| 13 | y = 2 / 3 | 13 | y = 2 / 3 |
| 14 | 14 | ||
| 15 | # Regexp after whitelisted method. | 15 | # Regexp after whitelisted method. |
| 16 | "abc".sub /b/, 'd' | 16 | "abc".sub /b/, 'd' |
| @@ -21,6 +21,40 @@ a = asub / aslb + bsub / bslb; | |||
| 21 | # Highlight the regexp after "if". | 21 | # Highlight the regexp after "if". |
| 22 | x = toto / foo if /do bar/ =~ "dobar" | 22 | x = toto / foo if /do bar/ =~ "dobar" |
| 23 | 23 | ||
| 24 | def test1(arg) | ||
| 25 | puts "hello" | ||
| 26 | end | ||
| 27 | |||
| 28 | def test2 (arg) | ||
| 29 | a = "apple" | ||
| 30 | |||
| 31 | if a == 2 | ||
| 32 | puts "hello" | ||
| 33 | else | ||
| 34 | puts "there" | ||
| 35 | end | ||
| 36 | |||
| 37 | if a == 2 then | ||
| 38 | puts "hello" | ||
| 39 | elsif a == 3 | ||
| 40 | puts "hello3" | ||
| 41 | elsif a == 3 then | ||
| 42 | puts "hello3" | ||
| 43 | else | ||
| 44 | puts "there" | ||
| 45 | end | ||
| 46 | |||
| 47 | case a | ||
| 48 | when "a" | ||
| 49 | 6 | ||
| 50 | # when "b" : | ||
| 51 | # 7 | ||
| 52 | # when "c" : 2 | ||
| 53 | when "d" then 4 | ||
| 54 | else 5 | ||
| 55 | end | ||
| 56 | end | ||
| 57 | |||
| 24 | # Some Cucumber code: | 58 | # Some Cucumber code: |
| 25 | Given /toto/ do | 59 | Given /toto/ do |
| 26 | print "hello" | 60 | print "hello" |