aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Gutov2013-05-19 10:01:23 +0400
committerDmitry Gutov2013-05-19 10:01:23 +0400
commit1a0a0a8a6a7c4cb47bb0d9bb5d3efa281cb70546 (patch)
tree9b1f66299f4384ac3bca1bc4044a2548077aacbb
parentc1a6c0a42021df3d41ba782460965c5cc0ae902c (diff)
downloademacs-1a0a0a8a6a7c4cb47bb0d9bb5d3efa281cb70546.tar.gz
emacs-1a0a0a8a6a7c4cb47bb0d9bb5d3efa281cb70546.zip
* lisp/progmodes/ruby-mode.el (ruby-expression-expansion-re): Allow to
start at point, so that expansion starting right after opening slash in a regexp is recognized. (ruby-syntax-before-regexp-re): New defvar, extracted from ruby-syntax-propertize-function. Since the value of this regexp is looked up at runtime now, we should be able to turn `ruby-syntax-methods-before-regexp' into a defcustom later. (ruby-syntax-propertize-function): Split regexp matching into two parts, for opening and closing slashes. That allows us to skip over string interpolations and support multiline regexps. Don't call `ruby-syntax-propertize-expansions', instead use another rule for them, which calls `ruby-syntax-propertize-expansion'. (ruby-syntax-propertize-expansions): Move `remove-text-properties' call to `ruby-syntax-propertize-function'. (ruby-syntax-propertize-expansion): Extracted from `ruby-syntax-propertize-expansions'. Handles one expansion. (ruby-syntax-propertize-heredoc): Explicitly call `ruby-syntax-propertize-expansions'. (ruby-syntax-propertize-percent-literal): Leave point right after the percent symbol, so that the expression expansion rule can propertize the contents. * test/automated/ruby-mode-tests.el (ruby-heredoc-highlights-interpolations) (ruby-regexp-skips-over-interpolation) (ruby-regexp-continues-till-end-when-unclosed) (ruby-regexp-can-be-multiline) (ruby-interpolation-inside-percent-literal): New tests. * test/indent/ruby.rb: Add multiline regexp example.
-rw-r--r--lisp/ChangeLog24
-rw-r--r--lisp/progmodes/ruby-mode.el140
-rw-r--r--test/ChangeLog10
-rw-r--r--test/automated/ruby-mode-tests.el19
-rw-r--r--test/indent/ruby.rb7
5 files changed, 140 insertions, 60 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 52711c47163..903dc74fc7d 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,27 @@
12013-05-19 Dmitry Gutov <dgutov@yandex.ru>
2
3 * progmodes/ruby-mode.el (ruby-expression-expansion-re): Allow to
4 start at point, so that expansion starting right after opening
5 slash in a regexp is recognized.
6 (ruby-syntax-before-regexp-re): New defvar, extracted from
7 ruby-syntax-propertize-function. Since the value of this regexp
8 is looked up at runtime now, we should be able to turn
9 `ruby-syntax-methods-before-regexp' into a defcustom later.
10 (ruby-syntax-propertize-function): Split regexp matching into two
11 parts, for opening and closing slashes. That allows us to skip
12 over string interpolations and support multiline regexps.
13 Don't call `ruby-syntax-propertize-expansions', instead use another rule
14 for them, which calls `ruby-syntax-propertize-expansion'.
15 (ruby-syntax-propertize-expansions): Move `remove-text-properties'
16 call to `ruby-syntax-propertize-function'.
17 (ruby-syntax-propertize-expansion): Extracted from
18 `ruby-syntax-propertize-expansions'. Handles one expansion.
19 (ruby-syntax-propertize-heredoc): Explicitly call
20 `ruby-syntax-propertize-expansions'.
21 (ruby-syntax-propertize-percent-literal): Leave point right after
22 the percent symbol, so that the expression expansion rule can
23 propertize the contents.
24
12013-05-18 Juri Linkov <juri@jurta.org> 252013-05-18 Juri Linkov <juri@jurta.org>
2 26
3 * man.el (Man-default-man-entry): Remove `-' from the end 27 * man.el (Man-default-man-entry): Remove `-' from the end
diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el
index 3ea55c4eabf..1732e0ba918 100644
--- a/lisp/progmodes/ruby-mode.el
+++ b/lisp/progmodes/ruby-mode.el
@@ -113,7 +113,7 @@
113 "Regexp to match the beginning of a heredoc.") 113 "Regexp to match the beginning of a heredoc.")
114 114
115 (defconst ruby-expression-expansion-re 115 (defconst ruby-expression-expansion-re
116 "[^\\]\\(\\\\\\\\\\)*\\(#\\({[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\|\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+\\)\\)")) 116 "\\(?:[^\\]\\|\\=\\)\\(\\\\\\\\\\)*\\(#\\({[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\|\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+\\)\\)"))
117 117
118(defun ruby-here-doc-end-match () 118(defun ruby-here-doc-end-match ()
119 "Return a regexp to find the end of a heredoc. 119 "Return a regexp to find the end of a heredoc.
@@ -1360,11 +1360,26 @@ If the result is do-end block, it will always be multiline."
1360 '("gsub" "gsub!" "sub" "sub!" "scan" "split" "split!" "index" "match" 1360 '("gsub" "gsub!" "sub" "sub!" "scan" "split" "split!" "index" "match"
1361 "assert_match" "Given" "Then" "When") 1361 "assert_match" "Given" "Then" "When")
1362 "Methods that can take regexp as the first argument. 1362 "Methods that can take regexp as the first argument.
1363It will be properly highlighted even when the call omits parens.")) 1363It will be properly highlighted even when the call omits parens.")
1364
1365 (defvar ruby-syntax-before-regexp-re
1366 (concat
1367 ;; Special tokens that can't be followed by a division operator.
1368 "\\(^\\|[[=(,~?:;<>]"
1369 ;; Control flow keywords and operators following bol or whitespace.
1370 "\\|\\(?:^\\|\\s \\)"
1371 (regexp-opt '("if" "elsif" "unless" "while" "until" "when" "and"
1372 "or" "not" "&&" "||"))
1373 ;; Method name from the list.
1374 "\\|\\_<"
1375 (regexp-opt ruby-syntax-methods-before-regexp)
1376 "\\)\\s *")
1377 "Regexp to match text that can be followed by a regular expression."))
1364 1378
1365 (defun ruby-syntax-propertize-function (start end) 1379 (defun ruby-syntax-propertize-function (start end)
1366 "Syntactic keywords for Ruby mode. See `syntax-propertize-function'." 1380 "Syntactic keywords for Ruby mode. See `syntax-propertize-function'."
1367 (goto-char start) 1381 (goto-char start)
1382 (remove-text-properties start end '(ruby-expansion-match-data))
1368 (ruby-syntax-propertize-heredoc end) 1383 (ruby-syntax-propertize-heredoc end)
1369 (ruby-syntax-enclosing-percent-literal end) 1384 (ruby-syntax-enclosing-percent-literal end)
1370 (funcall 1385 (funcall
@@ -1376,25 +1391,26 @@ It will be properly highlighted even when the call omits parens."))
1376 ;; Not within a string. 1391 ;; Not within a string.
1377 (nth 3 (syntax-ppss (match-beginning 0)))) 1392 (nth 3 (syntax-ppss (match-beginning 0))))
1378 (string-to-syntax "\\")))) 1393 (string-to-syntax "\\"))))
1379 ;; Regexps: regexps are distinguished from division because 1394 ;; Regular expressions. Start with matching unescaped slash.
1380 ;; of the keyword, symbol, or method name before them. 1395 ("\\(?:\\=\\|[^\\]\\)\\(?:\\\\\\\\\\)*\\(/\\)"
1381 ((concat 1396 (1 (let ((state (save-excursion (syntax-ppss (match-beginning 1)))))
1382 ;; Special tokens that can't be followed by a division operator. 1397 (when (or
1383 "\\(^\\|[[=(,~?:;<>]" 1398 ;; Beginning of a regexp.
1384 ;; Control flow keywords and operators following bol or whitespace. 1399 (and (null (nth 8 state))
1385 "\\|\\(?:^\\|\\s \\)" 1400 (save-excursion
1386 (regexp-opt '("if" "elsif" "unless" "while" "until" "when" "and" 1401 (forward-char -1)
1387 "or" "not" "&&" "||")) 1402 (looking-back ruby-syntax-before-regexp-re
1388 ;; Method name from the list. 1403 (point-at-bol))))
1389 "\\|\\_<" 1404 ;; End of regexp. We don't match the whole
1390 (regexp-opt ruby-syntax-methods-before-regexp) 1405 ;; regexp at once because it can have
1391 "\\)\\s *" 1406 ;; string interpolation inside, or span
1392 ;; The regular expression itself. 1407 ;; several lines.
1393 "\\(/\\)[^/\n\\\\]*\\(?:\\\\.[^/\n\\\\]*\\)*\\(/\\)") 1408 (eq ?/ (nth 3 state)))
1394 (3 (unless (nth 3 (syntax-ppss (match-beginning 2))) 1409 (string-to-syntax "\"/")))))
1395 (put-text-property (match-beginning 2) (match-end 2) 1410 ;; Expression expansions in strings. We're handling them
1396 'syntax-table (string-to-syntax "\"/")) 1411 ;; here, so that the regexp rule never matches inside them.
1397 (string-to-syntax "\"/")))) 1412 (ruby-expression-expansion-re
1413 (0 (ignore (ruby-syntax-propertize-expansion))))
1398 ("^=en\\(d\\)\\_>" (1 "!")) 1414 ("^=en\\(d\\)\\_>" (1 "!"))
1399 ("^\\(=\\)begin\\_>" (1 "!")) 1415 ("^\\(=\\)begin\\_>" (1 "!"))
1400 ;; Handle here documents. 1416 ;; Handle here documents.
@@ -1406,8 +1422,7 @@ It will be properly highlighted even when the call omits parens."))
1406 ;; Handle percent literals: %w(), %q{}, etc. 1422 ;; Handle percent literals: %w(), %q{}, etc.
1407 ((concat "\\(?:^\\|[[ \t\n<+(,=]\\)" ruby-percent-literal-beg-re) 1423 ((concat "\\(?:^\\|[[ \t\n<+(,=]\\)" ruby-percent-literal-beg-re)
1408 (1 (prog1 "|" (ruby-syntax-propertize-percent-literal end))))) 1424 (1 (prog1 "|" (ruby-syntax-propertize-percent-literal end)))))
1409 (point) end) 1425 (point) end))
1410 (ruby-syntax-propertize-expansions start end))
1411 1426
1412 (defun ruby-syntax-propertize-heredoc (limit) 1427 (defun ruby-syntax-propertize-heredoc (limit)
1413 (let ((ppss (syntax-ppss)) 1428 (let ((ppss (syntax-ppss))
@@ -1432,7 +1447,9 @@ It will be properly highlighted even when the call omits parens."))
1432 'syntax-table (string-to-syntax "\"")))) 1447 'syntax-table (string-to-syntax "\""))))
1433 ;; Make extra sure we don't move back, lest we could fall into an 1448 ;; Make extra sure we don't move back, lest we could fall into an
1434 ;; inf-loop. 1449 ;; inf-loop.
1435 (if (< (point) start) (goto-char start)))))) 1450 (if (< (point) start)
1451 (goto-char start)
1452 (ruby-syntax-propertize-expansions start (point)))))))
1436 1453
1437 (defun ruby-syntax-enclosing-percent-literal (limit) 1454 (defun ruby-syntax-enclosing-percent-literal (limit)
1438 (let ((state (syntax-ppss)) 1455 (let ((state (syntax-ppss))
@@ -1453,44 +1470,47 @@ It will be properly highlighted even when the call omits parens."))
1453 (cl (or (cdr (aref (syntax-table) op)) 1470 (cl (or (cdr (aref (syntax-table) op))
1454 (cdr (assoc op '((?< . ?>)))))) 1471 (cdr (assoc op '((?< . ?>))))))
1455 parse-sexp-lookup-properties) 1472 parse-sexp-lookup-properties)
1456 (condition-case nil 1473 (save-excursion
1457 (progn 1474 (condition-case nil
1458 (if cl ; Paired delimiters. 1475 (progn
1459 ;; Delimiter pairs of the same kind can be nested 1476 (if cl ; Paired delimiters.
1460 ;; inside the literal, as long as they are balanced. 1477 ;; Delimiter pairs of the same kind can be nested
1461 ;; Create syntax table that ignores other characters. 1478 ;; inside the literal, as long as they are balanced.
1462 (with-syntax-table (make-char-table 'syntax-table nil) 1479 ;; Create syntax table that ignores other characters.
1463 (modify-syntax-entry op (concat "(" (char-to-string cl))) 1480 (with-syntax-table (make-char-table 'syntax-table nil)
1464 (modify-syntax-entry cl (concat ")" ops)) 1481 (modify-syntax-entry op (concat "(" (char-to-string cl)))
1465 (modify-syntax-entry ?\\ "\\") 1482 (modify-syntax-entry cl (concat ")" ops))
1466 (save-restriction 1483 (modify-syntax-entry ?\\ "\\")
1467 (narrow-to-region (point) limit) 1484 (save-restriction
1468 (forward-list))) ; skip to the paired character 1485 (narrow-to-region (point) limit)
1469 ;; Single character delimiter. 1486 (forward-list))) ; skip to the paired character
1470 (re-search-forward (concat "[^\\]\\(?:\\\\\\\\\\)*" 1487 ;; Single character delimiter.
1471 (regexp-quote ops)) limit nil)) 1488 (re-search-forward (concat "[^\\]\\(?:\\\\\\\\\\)*"
1472 ;; Found the closing delimiter. 1489 (regexp-quote ops)) limit nil))
1473 (put-text-property (1- (point)) (point) 'syntax-table 1490 ;; Found the closing delimiter.
1474 (string-to-syntax "|"))) 1491 (put-text-property (1- (point)) (point) 'syntax-table
1475 ;; Unclosed literal, leave the following text unpropertized. 1492 (string-to-syntax "|")))
1476 ((scan-error search-failed) (goto-char limit)))))) 1493 ;; Unclosed literal, do nothing.
1494 ((scan-error search-failed)))))))
1495
1496 (defun ruby-syntax-propertize-expansion ()
1497 ;; Save the match data to a text property, for font-locking later.
1498 ;; Set the syntax of all double quotes and backticks to punctuation.
1499 (let ((beg (match-beginning 2))
1500 (end (match-end 2)))
1501 (when (and beg (save-excursion (nth 3 (syntax-ppss beg))))
1502 (put-text-property beg (1+ beg) 'ruby-expansion-match-data
1503 (match-data))
1504 (goto-char beg)
1505 (while (re-search-forward "[\"`]" end 'move)
1506 (put-text-property (match-beginning 0) (match-end 0)
1507 'syntax-table (string-to-syntax "."))))))
1477 1508
1478 (defun ruby-syntax-propertize-expansions (start end) 1509 (defun ruby-syntax-propertize-expansions (start end)
1479 (remove-text-properties start end '(ruby-expansion-match-data)) 1510 (save-excursion
1480 (goto-char start) 1511 (goto-char start)
1481 ;; Find all expression expansions and 1512 (while (re-search-forward ruby-expression-expansion-re end 'move)
1482 ;; - save the match data to a text property, for font-locking later, 1513 (ruby-syntax-propertize-expansion))))
1483 ;; - set the syntax of all double quotes and backticks to punctuation.
1484 (while (re-search-forward ruby-expression-expansion-re end 'move)
1485 (let ((beg (match-beginning 2))
1486 (end (match-end 2)))
1487 (when (and beg (save-excursion (nth 3 (syntax-ppss beg))))
1488 (put-text-property beg (1+ beg) 'ruby-expansion-match-data
1489 (match-data))
1490 (goto-char beg)
1491 (while (re-search-forward "[\"`]" end 'move)
1492 (put-text-property (match-beginning 0) (match-end 0)
1493 'syntax-table (string-to-syntax ".")))))))
1494 ) 1514 )
1495 1515
1496 ;; For Emacsen where syntax-propertize-rules is not (yet) available, 1516 ;; For Emacsen where syntax-propertize-rules is not (yet) available,
diff --git a/test/ChangeLog b/test/ChangeLog
index c11d5d26c13..5c17e09e962 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,3 +1,13 @@
12013-05-19 Dmitry Gutov <dgutov@yandex.ru>
2
3 * indent/ruby.rb: Add multiline regexp example.
4
5 * automated/ruby-mode-tests.el (ruby-heredoc-highlights-interpolations)
6 (ruby-regexp-skips-over-interpolation)
7 (ruby-regexp-continues-till-end-when-unclosed)
8 (ruby-regexp-can-be-multiline)
9 (ruby-interpolation-inside-percent-literal): New tests.
10
12013-05-08 Stefan Monnier <monnier@iro.umontreal.ca> 112013-05-08 Stefan Monnier <monnier@iro.umontreal.ca>
2 12
3 * indent/ruby.rb: Fix indentation after =; add more cases. 13 * indent/ruby.rb: Fix indentation after =; add more cases.
diff --git a/test/automated/ruby-mode-tests.el b/test/automated/ruby-mode-tests.el
index 23dc45ad509..e52927a2968 100644
--- a/test/automated/ruby-mode-tests.el
+++ b/test/automated/ruby-mode-tests.el
@@ -84,6 +84,9 @@ VALUES-PLIST is a list with alternating index and value elements."
84(ert-deftest ruby-singleton-class-no-heredoc-font-lock () 84(ert-deftest ruby-singleton-class-no-heredoc-font-lock ()
85 (ruby-assert-face "class<<a" 8 nil)) 85 (ruby-assert-face "class<<a" 8 nil))
86 86
87(ert-deftest ruby-heredoc-highlights-interpolations ()
88 (ruby-assert-face "s = <<EOS\n #{foo}\nEOS" 15 font-lock-variable-name-face))
89
87(ert-deftest ruby-deep-indent () 90(ert-deftest ruby-deep-indent ()
88 (let ((ruby-deep-arglist nil) 91 (let ((ruby-deep-arglist nil)
89 (ruby-deep-indent-paren '(?\( ?\{ ?\[ ?\] t))) 92 (ruby-deep-indent-paren '(?\( ?\{ ?\[ ?\] t)))
@@ -109,6 +112,15 @@ VALUES-PLIST is a list with alternating index and value elements."
109(ert-deftest ruby-regexp-starts-after-string () 112(ert-deftest ruby-regexp-starts-after-string ()
110 (ruby-assert-state "'(/', /\d+/" 3 ?/ 8)) 113 (ruby-assert-state "'(/', /\d+/" 3 ?/ 8))
111 114
115(ert-deftest ruby-regexp-skips-over-interpolation ()
116 (ruby-assert-state "/#{foobs.join('/')}/" 3 nil))
117
118(ert-deftest ruby-regexp-continues-till-end-when-unclosed ()
119 (ruby-assert-state "/bars" 3 ?/))
120
121(ert-deftest ruby-regexp-can-be-multiline ()
122 (ruby-assert-state "/bars\ntees # toots \nfoos/" 3 nil))
123
112(ert-deftest ruby-indent-simple () 124(ert-deftest ruby-indent-simple ()
113 (ruby-should-indent-buffer 125 (ruby-should-indent-buffer
114 "if foo 126 "if foo
@@ -325,6 +337,13 @@ VALUES-PLIST is a list with alternating index and value elements."
325 (search-forward "tee") 337 (search-forward "tee")
326 (should (string= (thing-at-point 'symbol) "tee"))))) 338 (should (string= (thing-at-point 'symbol) "tee")))))
327 339
340(ert-deftest ruby-interpolation-inside-percent-literal ()
341 (let ((s "%( #{boo} )"))
342 (ruby-assert-face s 1 font-lock-string-face)
343 (ruby-assert-face s 4 font-lock-variable-name-face)
344 (ruby-assert-face s 10 font-lock-string-face)
345 (ruby-assert-state s 8 nil)))
346
328(ert-deftest ruby-interpolation-inside-percent-literal-with-paren () 347(ert-deftest ruby-interpolation-inside-percent-literal-with-paren ()
329 :expected-result :failed 348 :expected-result :failed
330 (let ((s "%(^#{\")\"}^)")) 349 (let ((s "%(^#{\")\"}^)"))
diff --git a/test/indent/ruby.rb b/test/indent/ruby.rb
index 90c6dcdc65c..853f4dbf992 100644
--- a/test/indent/ruby.rb
+++ b/test/indent/ruby.rb
@@ -21,6 +21,11 @@ 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
24# Multiline regexp.
25/bars
26 tees # toots
27 nfoos/
28
24def test1(arg) 29def test1(arg)
25 puts "hello" 30 puts "hello"
26end 31end
@@ -47,6 +52,8 @@ def test2 (arg)
47 case a 52 case a
48 when "a" 53 when "a"
49 6 54 6
55 # Support for this syntax was removed in Ruby 1.9, so we
56 # probably don't need to handle it either.
50 # when "b" : 57 # when "b" :
51 # 7 58 # 7
52 # when "c" : 2 59 # when "c" : 2