aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2013-05-08 16:25:57 -0400
committerStefan Monnier2013-05-08 16:25:57 -0400
commita9e4425bc99733b6ecf3898801b6595f35c4ef30 (patch)
tree903ca31485cda0ddef4de71a44ed8283542a6d88
parent060ca4088d928e0808d13a551ea11b2fc00769d4 (diff)
downloademacs-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/ChangeLog10
-rw-r--r--lisp/progmodes/ruby-mode.el120
-rw-r--r--test/ChangeLog4
-rw-r--r--test/indent/ruby.rb36
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 @@
12013-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
12013-05-08 Eli Zaretskii <eliz@gnu.org> 112013-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 @@
12013-05-08 Stefan Monnier <monnier@iro.umontreal.ca>
2
3 * indent/ruby.rb: Fix indentation after =; add more cases.
4
12013-05-05 Stefan Monnier <monnier@iro.umontreal.ca> 52013-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.
12x = # "tot %q/to"; = 12x = # "tot %q/to"; =
13y = 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".
22x = toto / foo if /do bar/ =~ "dobar" 22x = toto / foo if /do bar/ =~ "dobar"
23 23
24def test1(arg)
25 puts "hello"
26end
27
28def 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
56end
57
24# Some Cucumber code: 58# Some Cucumber code:
25Given /toto/ do 59Given /toto/ do
26 print "hello" 60 print "hello"