diff options
| author | Stefan Monnier | 2012-04-24 13:08:55 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2012-04-24 13:08:55 -0400 |
| commit | 1ec00a232a98f971c7b4c46f74636d14e48990a2 (patch) | |
| tree | fd11dadb7d6b2b9b18271fd53cc81f2ea6adec49 | |
| parent | b613912badfb9050e6310ee14fddc90e0fd16b2c (diff) | |
| parent | dfbd787fe6a5684d699926d698aaf9166812a81b (diff) | |
| download | emacs-1ec00a232a98f971c7b4c46f74636d14e48990a2.tar.gz emacs-1ec00a232a98f971c7b4c46f74636d14e48990a2.zip | |
* ruby-mode.el: Handle general delimited literals.
Fixes: debbugs:6286
| -rw-r--r-- | lisp/ChangeLog | 22 | ||||
| -rw-r--r-- | lisp/progmodes/ruby-mode.el | 89 | ||||
| -rw-r--r-- | test/ChangeLog | 4 | ||||
| -rw-r--r-- | test/indent/ruby.rb | 19 |
4 files changed, 120 insertions, 14 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 269d72e3754..0eda6b28936 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,25 @@ | |||
| 1 | 2012-04-24 Stefan Monnier <monnier@iro.umontreal.ca> | ||
| 2 | |||
| 3 | * progmodes/ruby-mode.el: Simplify last change, and cleanup code. | ||
| 4 | (ruby-syntax-propertize-regexp): Remove. | ||
| 5 | (ruby-syntax-propertize-function): Split regexp into chunks. | ||
| 6 | Match following code directly. | ||
| 7 | |||
| 8 | 2012-04-24 Dmitry Gutov <dgutov@yandex.ru> | ||
| 9 | |||
| 10 | * progmodes/ruby-mode.el: Handle Cucumber defs (bug#6286). | ||
| 11 | (ruby-syntax-propertize-regexp): New function. | ||
| 12 | (ruby-syntax-propertize-function): Use it to handle regexp not preceded | ||
| 13 | by a special keyword. | ||
| 14 | |||
| 15 | * progmodes/ruby-mode.el: Handle general delimited literals (bug#6286). | ||
| 16 | (ruby-syntax-general-delimiters-goto-beg) | ||
| 17 | (ruby-syntax-propertize-general-delimiters): New functions. | ||
| 18 | (ruby-syntax-propertize-function): Use them to handle GDL. | ||
| 19 | (ruby-font-lock-keywords): Move old handling of GDL... | ||
| 20 | (ruby-font-lock-syntactic-keywords): .. to here. | ||
| 21 | (ruby-calculate-indent): Adjust indentation for GDL. | ||
| 22 | |||
| 1 | 2012-04-24 Michael Albinus <michael.albinus@gmx.de> | 23 | 2012-04-24 Michael Albinus <michael.albinus@gmx.de> |
| 2 | 24 | ||
| 3 | * notifications.el (notifications-interface) | 25 | * notifications.el (notifications-interface) |
diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index 66aa256f947..5d79437c3c2 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el | |||
| @@ -784,7 +784,7 @@ and `\\' when preceded by `?'." | |||
| 784 | (not (looking-at "[a-z_]")))) | 784 | (not (looking-at "[a-z_]")))) |
| 785 | (and (looking-at ruby-operator-re) | 785 | (and (looking-at ruby-operator-re) |
| 786 | (not (ruby-special-char-p)) | 786 | (not (ruby-special-char-p)) |
| 787 | ;; operator at the end of line | 787 | ;; Operator at the end of line. |
| 788 | (let ((c (char-after (point)))) | 788 | (let ((c (char-after (point)))) |
| 789 | (and | 789 | (and |
| 790 | ;; (or (null begin) | 790 | ;; (or (null begin) |
| @@ -794,8 +794,9 @@ and `\\' when preceded by `?'." | |||
| 794 | ;; (not (or (eolp) (looking-at "#") | 794 | ;; (not (or (eolp) (looking-at "#") |
| 795 | ;; (and (eq (car (nth 1 state)) ?{) | 795 | ;; (and (eq (car (nth 1 state)) ?{) |
| 796 | ;; (looking-at "|")))))) | 796 | ;; (looking-at "|")))))) |
| 797 | (or (not (eq ?/ c)) | 797 | ;; Not a regexp or general delimited literal. |
| 798 | (null (nth 0 (ruby-parse-region (or begin parse-start) (point))))) | 798 | (null (nth 0 (ruby-parse-region (or begin parse-start) |
| 799 | (point)))) | ||
| 799 | (or (not (eq ?| (char-after (point)))) | 800 | (or (not (eq ?| (char-after (point)))) |
| 800 | (save-excursion | 801 | (save-excursion |
| 801 | (or (eolp) (forward-char -1)) | 802 | (or (eolp) (forward-char -1)) |
| @@ -1110,6 +1111,8 @@ See `add-log-current-defun-function'." | |||
| 1110 | mlist))))) | 1111 | mlist))))) |
| 1111 | 1112 | ||
| 1112 | (declare-function ruby-syntax-propertize-heredoc "ruby-mode" (limit)) | 1113 | (declare-function ruby-syntax-propertize-heredoc "ruby-mode" (limit)) |
| 1114 | (declare-function ruby-syntax-general-delimiters-goto-beg "ruby-mode" ()) | ||
| 1115 | (declare-function ruby-syntax-propertize-general-delimiters "ruby-mode" (limit)) | ||
| 1113 | 1116 | ||
| 1114 | (if (eval-when-compile (fboundp #'syntax-propertize-rules)) | 1117 | (if (eval-when-compile (fboundp #'syntax-propertize-rules)) |
| 1115 | ;; New code that works independently from font-lock. | 1118 | ;; New code that works independently from font-lock. |
| @@ -1118,26 +1121,48 @@ See `add-log-current-defun-function'." | |||
| 1118 | "Syntactic keywords for Ruby mode. See `syntax-propertize-function'." | 1121 | "Syntactic keywords for Ruby mode. See `syntax-propertize-function'." |
| 1119 | (goto-char start) | 1122 | (goto-char start) |
| 1120 | (ruby-syntax-propertize-heredoc end) | 1123 | (ruby-syntax-propertize-heredoc end) |
| 1124 | (ruby-syntax-general-delimiters-goto-beg) | ||
| 1121 | (funcall | 1125 | (funcall |
| 1122 | (syntax-propertize-rules | 1126 | (syntax-propertize-rules |
| 1123 | ;; #{ }, #$hoge, #@foo are not comments | 1127 | ;; #{ }, #$hoge, #@foo are not comments. |
| 1124 | ("\\(#\\)[{$@]" (1 ".")) | 1128 | ("\\(#\\)[{$@]" (1 ".")) |
| 1125 | ;; $' $" $` .... are variables | 1129 | ;; $' $" $` .... are variables. |
| 1126 | ;; ?' ?" ?` are ascii codes | 1130 | ;; ?' ?" ?` are ascii codes. |
| 1127 | ("\\([?$]\\)[#\"'`]" | 1131 | ("\\([?$]\\)[#\"'`]" |
| 1128 | (1 (unless (save-excursion | 1132 | (1 (unless (save-excursion |
| 1129 | ;; Not within a string. | 1133 | ;; Not within a string. |
| 1130 | (nth 3 (syntax-ppss (match-beginning 0)))) | 1134 | (nth 3 (syntax-ppss (match-beginning 0)))) |
| 1131 | (string-to-syntax "\\")))) | 1135 | (string-to-syntax "\\")))) |
| 1132 | ;; regexps | 1136 | ;; Regexps: regexps are distinguished from division either because |
| 1133 | ("\\(^\\|[[=(,~?:;<>]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)" | 1137 | ;; of the keyword/symbol before them, or because of the code |
| 1134 | (4 "\"/") | 1138 | ;; following them. |
| 1135 | (6 "\"/")) | 1139 | ((concat |
| 1140 | ;; Special tokens that can't be followed by a division operator. | ||
| 1141 | "\\(?:\\(^\\|[[=(,~?:;<>]\\|\\(?:^\\|\\s \\)" | ||
| 1142 | (regexp-opt '("if" "elsif" "unless" "while" "until" "when" "and" | ||
| 1143 | "or" "&&" "||" | ||
| 1144 | "gsub" "gsub!" "sub" "sub!" "scan" "split" "split!")) | ||
| 1145 | "\\)\\s *\\)?" | ||
| 1146 | ;; The regular expression itself. | ||
| 1147 | "\\(/\\)[^/\n\\\\]*\\(?:\\\\.[^/\n\\\\]*\\)*\\(/\\)" | ||
| 1148 | ;; Special code that cannot follow a division operator. | ||
| 1149 | ;; FIXME: Just because the second slash of "/foo/ do bar" can't | ||
| 1150 | ;; be a division, doesn't mean it can't *start* a regexp, as in | ||
| 1151 | ;; "x = toto/foo; if /do bar/". | ||
| 1152 | "\\([imxo]*\\s *\\(?:,\\|\\_<do\\_>\\)\\)?") | ||
| 1153 | (2 (when (or (match-beginning 1) (match-beginning 4)) | ||
| 1154 | (string-to-syntax "\"/"))) | ||
| 1155 | (3 (if (or (match-beginning 1) (match-beginning 4)) | ||
| 1156 | (string-to-syntax "\"/") | ||
| 1157 | (goto-char (match-end 2))))) | ||
| 1136 | ("^=en\\(d\\)\\_>" (1 "!")) | 1158 | ("^=en\\(d\\)\\_>" (1 "!")) |
| 1137 | ("^\\(=\\)begin\\_>" (1 "!")) | 1159 | ("^\\(=\\)begin\\_>" (1 "!")) |
| 1138 | ;; Handle here documents. | 1160 | ;; Handle here documents. |
| 1139 | ((concat ruby-here-doc-beg-re ".*\\(\n\\)") | 1161 | ((concat ruby-here-doc-beg-re ".*\\(\n\\)") |
| 1140 | (7 (prog1 "\"" (ruby-syntax-propertize-heredoc end))))) | 1162 | (7 (prog1 "\"" (ruby-syntax-propertize-heredoc end)))) |
| 1163 | ;; Handle percent literals: %w(), %q{}, etc. | ||
| 1164 | ("\\(?:^\\|[[ \t\n<+(,=]\\)\\(%\\)[qQrswWx]?\\([[:punct:]]\\)" | ||
| 1165 | (1 (prog1 "|" (ruby-syntax-propertize-general-delimiters end))))) | ||
| 1141 | (point) end)) | 1166 | (point) end)) |
| 1142 | 1167 | ||
| 1143 | (defun ruby-syntax-propertize-heredoc (limit) | 1168 | (defun ruby-syntax-propertize-heredoc (limit) |
| @@ -1163,6 +1188,41 @@ See `add-log-current-defun-function'." | |||
| 1163 | ;; Make extra sure we don't move back, lest we could fall into an | 1188 | ;; Make extra sure we don't move back, lest we could fall into an |
| 1164 | ;; inf-loop. | 1189 | ;; inf-loop. |
| 1165 | (if (< (point) start) (goto-char start)))))) | 1190 | (if (< (point) start) (goto-char start)))))) |
| 1191 | |||
| 1192 | (defun ruby-syntax-general-delimiters-goto-beg () | ||
| 1193 | (let ((state (syntax-ppss))) | ||
| 1194 | ;; Move to the start of the literal, in case it's multiline. | ||
| 1195 | ;; TODO: determine the literal type more reliably here? | ||
| 1196 | (when (eq t (nth 3 state)) | ||
| 1197 | (goto-char (nth 8 state)) | ||
| 1198 | (beginning-of-line)))) | ||
| 1199 | |||
| 1200 | (defun ruby-syntax-propertize-general-delimiters (limit) | ||
| 1201 | (goto-char (match-beginning 2)) | ||
| 1202 | (let* ((op (char-after)) | ||
| 1203 | (ops (char-to-string op)) | ||
| 1204 | (cl (or (cdr (aref (syntax-table) op)) | ||
| 1205 | (cdr (assoc op '((?< . ?>)))))) | ||
| 1206 | parse-sexp-lookup-properties) | ||
| 1207 | (ignore-errors | ||
| 1208 | (if cl | ||
| 1209 | (progn ; Paired delimiters. | ||
| 1210 | ;; Delimiter pairs of the same kind can be nested | ||
| 1211 | ;; inside the literal, as long as they are balanced. | ||
| 1212 | ;; Create syntax table that ignores other characters. | ||
| 1213 | (with-syntax-table (make-char-table 'syntax-table nil) | ||
| 1214 | (modify-syntax-entry op (concat "(" (char-to-string cl))) | ||
| 1215 | (modify-syntax-entry cl (concat ")" ops)) | ||
| 1216 | (modify-syntax-entry ?\\ "\\") | ||
| 1217 | (save-restriction | ||
| 1218 | (narrow-to-region (point) limit) | ||
| 1219 | (forward-list)))) ; skip to the paired character | ||
| 1220 | ;; Single character delimiter. | ||
| 1221 | (re-search-forward (concat "[^\\]\\(?:\\\\\\\\\\)*" | ||
| 1222 | (regexp-quote ops)) limit nil)) | ||
| 1223 | ;; If we reached here, the closing delimiter was found. | ||
| 1224 | (put-text-property (1- (point)) (point) | ||
| 1225 | 'syntax-table (string-to-syntax "|"))))) | ||
| 1166 | ) | 1226 | ) |
| 1167 | 1227 | ||
| 1168 | ;; For Emacsen where syntax-propertize-rules is not (yet) available, | 1228 | ;; For Emacsen where syntax-propertize-rules is not (yet) available, |
| @@ -1207,6 +1267,10 @@ This should only be called after matching against `ruby-here-doc-end-re'." | |||
| 1207 | (4 (7 . ?/)) | 1267 | (4 (7 . ?/)) |
| 1208 | (6 (7 . ?/))) | 1268 | (6 (7 . ?/))) |
| 1209 | ("^=en\\(d\\)\\_>" 1 "!") | 1269 | ("^=en\\(d\\)\\_>" 1 "!") |
| 1270 | ;; General delimited string. | ||
| 1271 | ("\\(^\\|[[ \t\n<+(,=]\\)\\(%[xrqQwW]?\\([^<[{(a-zA-Z0-9 \n]\\)[^\n\\\\]*\\(\\\\.[^\n\\\\]*\\)*\\(\\3\\)\\)" | ||
| 1272 | (3 "\"") | ||
| 1273 | (5 "\"")) | ||
| 1210 | ("^\\(=\\)begin\\_>" 1 (ruby-comment-beg-syntax)) | 1274 | ("^\\(=\\)begin\\_>" 1 (ruby-comment-beg-syntax)) |
| 1211 | ;; Currently, the following case is highlighted incorrectly: | 1275 | ;; Currently, the following case is highlighted incorrectly: |
| 1212 | ;; | 1276 | ;; |
| @@ -1415,9 +1479,6 @@ See `font-lock-syntax-table'.") | |||
| 1415 | 1 font-lock-variable-name-face) | 1479 | 1 font-lock-variable-name-face) |
| 1416 | '("\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+" | 1480 | '("\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+" |
| 1417 | 0 font-lock-variable-name-face) | 1481 | 0 font-lock-variable-name-face) |
| 1418 | ;; general delimited string | ||
| 1419 | '("\\(^\\|[[ \t\n<+(,=]\\)\\(%[xrqQwW]?\\([^<[{(a-zA-Z0-9 \n]\\)[^\n\\\\]*\\(\\\\.[^\n\\\\]*\\)*\\(\\3\\)\\)" | ||
| 1420 | (2 font-lock-string-face)) | ||
| 1421 | ;; constants | 1482 | ;; constants |
| 1422 | '("\\(^\\|[^_]\\)\\b\\([A-Z]+\\(\\w\\|_\\)*\\)" | 1483 | '("\\(^\\|[^_]\\)\\b\\([A-Z]+\\(\\w\\|_\\)*\\)" |
| 1423 | 2 font-lock-type-face) | 1484 | 2 font-lock-type-face) |
diff --git a/test/ChangeLog b/test/ChangeLog index 66f8592c79c..ff38a8fa9e1 100644 --- a/test/ChangeLog +++ b/test/ChangeLog | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | 2012-04-24 Stefan Monnier <monnier@iro.umontreal.ca> | ||
| 2 | |||
| 3 | * indent/ruby.rb: New file, to test new syntax-propertize code. | ||
| 4 | |||
| 1 | 2012-04-11 Glenn Morris <rgm@gnu.org> | 5 | 2012-04-11 Glenn Morris <rgm@gnu.org> |
| 2 | 6 | ||
| 3 | * automated/vc-bzr.el (vc-bzr-test-faulty-bzr-autoloads): New test. | 7 | * automated/vc-bzr.el (vc-bzr-test-faulty-bzr-autoloads): New test. |
diff --git a/test/indent/ruby.rb b/test/indent/ruby.rb new file mode 100644 index 00000000000..c4a747a1c78 --- /dev/null +++ b/test/indent/ruby.rb | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | # Don't mis-match "sub" at the end of words. | ||
| 2 | a = asub / aslb + bsub / bslb; | ||
| 3 | |||
| 4 | b = %Q{This is a "string"} | ||
| 5 | c = %w(foo | ||
| 6 | bar | ||
| 7 | baz) | ||
| 8 | d = %!hello! | ||
| 9 | |||
| 10 | # A "do" after a slash means that slash is not a division, but it doesn't imply | ||
| 11 | # it's a regexp-ender, since it can be a regexp-starter instead! | ||
| 12 | x = toto / foo; if /do bar/ then | ||
| 13 | toto = 1 | ||
| 14 | end | ||
| 15 | |||
| 16 | # Some Cucumber code: | ||
| 17 | Given /toto/ do | ||
| 18 | print "hello" | ||
| 19 | end | ||