aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/progmodes/ruby-mode.el
diff options
context:
space:
mode:
authorKenichi Handa2010-09-16 11:11:13 +0900
committerKenichi Handa2010-09-16 11:11:13 +0900
commit38d50547c2a8195bed0aaeafbbc4c0f277d4e416 (patch)
tree388416c9f2cc4746d0d2d9e525a50a6c2f00f3d4 /lisp/progmodes/ruby-mode.el
parentfa3f60399014127e711f3f438004950cba0bddb9 (diff)
parent6139f995addcb8fce63deb30c7ed0e6f2b618b02 (diff)
downloademacs-38d50547c2a8195bed0aaeafbbc4c0f277d4e416.tar.gz
emacs-38d50547c2a8195bed0aaeafbbc4c0f277d4e416.zip
merge trunk
Diffstat (limited to 'lisp/progmodes/ruby-mode.el')
-rw-r--r--lisp/progmodes/ruby-mode.el390
1 files changed, 220 insertions, 170 deletions
diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el
index 0b92234bf1c..4d015de5198 100644
--- a/lisp/progmodes/ruby-mode.el
+++ b/lisp/progmodes/ruby-mode.el
@@ -100,17 +100,10 @@
100 100
101(defconst ruby-block-end-re "\\<end\\>") 101(defconst ruby-block-end-re "\\<end\\>")
102 102
103(defconst ruby-here-doc-beg-re 103(eval-and-compile
104 (defconst ruby-here-doc-beg-re
104 "\\(<\\)<\\(-\\)?\\(\\([a-zA-Z0-9_]+\\)\\|[\"]\\([^\"]+\\)[\"]\\|[']\\([^']+\\)[']\\)" 105 "\\(<\\)<\\(-\\)?\\(\\([a-zA-Z0-9_]+\\)\\|[\"]\\([^\"]+\\)[\"]\\|[']\\([^']+\\)[']\\)"
105 "Regexp to match the beginning of a heredoc.") 106 "Regexp to match the beginning of a heredoc."))
106
107(defconst ruby-here-doc-end-re
108 "^\\([ \t]+\\)?\\(.*\\)\\(.\\)$"
109 "Regexp to match the end of heredocs.
110
111This will actually match any line with one or more characters.
112It's useful in that it divides up the match string so that
113`ruby-here-doc-beg-match' can search for the beginning of the heredoc.")
114 107
115(defun ruby-here-doc-end-match () 108(defun ruby-here-doc-end-match ()
116 "Return a regexp to find the end of a heredoc. 109 "Return a regexp to find the end of a heredoc.
@@ -123,18 +116,6 @@ This should only be called after matching against `ruby-here-doc-beg-re'."
123 (match-string 5) 116 (match-string 5)
124 (match-string 6))))) 117 (match-string 6)))))
125 118
126(defun ruby-here-doc-beg-match ()
127 "Return a regexp to find the beginning of a heredoc.
128
129This should only be called after matching against `ruby-here-doc-end-re'."
130 (let ((contents (regexp-quote (concat (match-string 2) (match-string 3)))))
131 (concat "<<"
132 (let ((match (match-string 1)))
133 (if (and match (> (length match) 0))
134 (concat "\\(?:-\\([\"']?\\)\\|\\([\"']\\)" (match-string 1) "\\)"
135 contents "\\b\\(\\1\\|\\2\\)")
136 (concat "-?\\([\"']\\|\\)" contents "\\b\\1"))))))
137
138(defconst ruby-delimiter 119(defconst ruby-delimiter
139 (concat "[?$/%(){}#\"'`.:]\\|<<\\|\\[\\|\\]\\|\\<\\(" 120 (concat "[?$/%(){}#\"'`.:]\\|<<\\|\\[\\|\\]\\|\\<\\("
140 ruby-block-beg-re 121 ruby-block-beg-re
@@ -362,7 +343,7 @@ Also ignores spaces after parenthesis when 'space."
362 (back-to-indentation) 343 (back-to-indentation)
363 (current-column))) 344 (current-column)))
364 345
365(defun ruby-indent-line (&optional flag) 346(defun ruby-indent-line (&optional ignored)
366 "Correct the indentation of the current Ruby line." 347 "Correct the indentation of the current Ruby line."
367 (interactive) 348 (interactive)
368 (ruby-indent-to (ruby-calculate-indent))) 349 (ruby-indent-to (ruby-calculate-indent)))
@@ -405,8 +386,7 @@ and `\\' when preceded by `?'."
405 "TODO: document." 386 "TODO: document."
406 (save-excursion 387 (save-excursion
407 (store-match-data nil) 388 (store-match-data nil)
408 (let ((space (skip-chars-backward " \t")) 389 (let ((space (skip-chars-backward " \t")))
409 (start (point)))
410 (cond 390 (cond
411 ((bolp) t) 391 ((bolp) t)
412 ((progn 392 ((progn
@@ -700,7 +680,7 @@ and `\\' when preceded by `?'."
700 (beginning-of-line) 680 (beginning-of-line)
701 (let ((ruby-indent-point (point)) 681 (let ((ruby-indent-point (point))
702 (case-fold-search nil) 682 (case-fold-search nil)
703 state bol eol begin op-end 683 state eol begin op-end
704 (paren (progn (skip-syntax-forward " ") 684 (paren (progn (skip-syntax-forward " ")
705 (and (char-after) (matching-paren (char-after))))) 685 (and (char-after) (matching-paren (char-after)))))
706 (indent 0)) 686 (indent 0))
@@ -780,7 +760,6 @@ and `\\' when preceded by `?'."
780 (if (re-search-forward "^\\s *#" end t) 760 (if (re-search-forward "^\\s *#" end t)
781 (beginning-of-line) 761 (beginning-of-line)
782 (setq done t)))) 762 (setq done t))))
783 (setq bol (point))
784 (end-of-line) 763 (end-of-line)
785 ;; skip the comment at the end 764 ;; skip the comment at the end
786 (skip-chars-backward " \t") 765 (skip-chars-backward " \t")
@@ -1037,10 +1016,8 @@ With ARG, do it many times. Negative ARG means move forward."
1037 (ruby-beginning-of-defun) 1016 (ruby-beginning-of-defun)
1038 (re-search-backward "^\n" (- (point) 1) t)) 1017 (re-search-backward "^\n" (- (point) 1) t))
1039 1018
1040(defun ruby-indent-exp (&optional shutup-p) 1019(defun ruby-indent-exp (&optional ignored)
1041 "Indent each line in the balanced expression following the point. 1020 "Indent each line in the balanced expression following the point."
1042If a prefix arg is given or SHUTUP-P is non-nil, no errors
1043are signalled if a balanced expression isn't found."
1044 (interactive "*P") 1021 (interactive "*P")
1045 (let ((here (point-marker)) start top column (nest t)) 1022 (let ((here (point-marker)) start top column (nest t))
1046 (set-marker-insertion-type here t) 1023 (set-marker-insertion-type here t)
@@ -1133,58 +1110,208 @@ See `add-log-current-defun-function'."
1133 (if mlist (concat mlist mname) mname) 1110 (if mlist (concat mlist mname) mname)
1134 mlist))))) 1111 mlist)))))
1135 1112
1136(defconst ruby-font-lock-syntactic-keywords 1113(if (eval-when-compile (fboundp #'syntax-propertize-rules))
1137 `(;; #{ }, #$hoge, #@foo are not comments 1114 ;; New code that works independently from font-lock.
1138 ("\\(#\\)[{$@]" 1 (1 . nil)) 1115 (progn
1139 ;; the last $', $", $` in the respective string is not variable 1116 (defun ruby-syntax-propertize-function (start end)
1140 ;; the last ?', ?", ?` in the respective string is not ascii code 1117 "Syntactic keywords for Ruby mode. See `syntax-propertize-function'."
1141 ("\\(^\\|[\[ \t\n<+\(,=]\\)\\(['\"`]\\)\\(\\\\.\\|\\2\\|[^'\"`\n\\\\]\\)*?\\\\?[?$]\\(\\2\\)" 1118 (goto-char start)
1142 (2 (7 . nil)) 1119 (ruby-syntax-propertize-heredoc end)
1143 (4 (7 . nil))) 1120 (funcall
1144 ;; $' $" $` .... are variables 1121 (syntax-propertize-rules
1145 ;; ?' ?" ?` are ascii codes 1122 ;; #{ }, #$hoge, #@foo are not comments
1146 ("\\(^\\|[^\\\\]\\)\\(\\\\\\\\\\)*[?$]\\([#\"'`]\\)" 3 (1 . nil)) 1123 ("\\(#\\)[{$@]" (1 "."))
1147 ;; regexps 1124 ;; the last $', $", $` in the respective string is not variable
1148 ("\\(^\\|[=(,~?:;<>]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)" 1125 ;; the last ?', ?", ?` in the respective string is not ascii code
1149 (4 (7 . ?/)) 1126 ("\\(^\\|[\[ \t\n<+\(,=]\\)\\(['\"`]\\)\\(\\\\.\\|\\2\\|[^'\"`\n\\\\]\\)*?\\\\?[?$]\\(\\2\\)"
1150 (6 (7 . ?/))) 1127 (2 "\"")
1151 ("^=en\\(d\\)\\_>" 1 "!") 1128 (4 "\""))
1152 ("^\\(=\\)begin\\_>" 1 (ruby-comment-beg-syntax)) 1129 ;; $' $" $` .... are variables
1153 ;; Currently, the following case is highlighted incorrectly: 1130 ;; ?' ?" ?` are ascii codes
1154 ;; 1131 ("\\(^\\|[^\\\\]\\)\\(\\\\\\\\\\)*[?$]\\([#\"'`]\\)" (3 "."))
1155 ;; <<FOO 1132 ;; regexps
1156 ;; FOO 1133 ("\\(^\\|[=(,~?:;<>]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)"
1157 ;; <<BAR 1134 (4 "\"/")
1158 ;; <<BAZ 1135 (6 "\"/"))
1159 ;; BAZ 1136 ("^=en\\(d\\)\\_>" (1 "!"))
1160 ;; BAR 1137 ("^\\(=\\)begin\\_>" (1 "!"))
1161 ;; 1138 ;; Handle here documents.
1162 ;; This is because all here-doc beginnings are highlighted before any endings, 1139 ((concat ruby-here-doc-beg-re ".*\\(\n\\)")
1163 ;; so although <<BAR is properly marked as a beginning, when we get to <<BAZ 1140 (7 (prog1 "\"" (ruby-syntax-propertize-heredoc end)))))
1164 ;; it thinks <<BAR is part of a string so it's marked as well. 1141 (point) end))
1165 ;; 1142
1166 ;; This may be fixable by modifying ruby-in-here-doc-p to use 1143 (defun ruby-syntax-propertize-heredoc (limit)
1167 ;; ruby-in-non-here-doc-string-p rather than syntax-ppss-context, 1144 (let ((ppss (syntax-ppss))
1168 ;; but I don't want to try that until we've got unit tests set up 1145 (res '()))
1169 ;; to make sure I don't break anything else. 1146 (when (eq ?\n (nth 3 ppss))
1170 (,(concat ruby-here-doc-beg-re ".*\\(\n\\)") 1147 (save-excursion
1171 ,(+ 1 (regexp-opt-depth ruby-here-doc-beg-re)) 1148 (goto-char (nth 8 ppss))
1172 (ruby-here-doc-beg-syntax)) 1149 (beginning-of-line)
1173 (,ruby-here-doc-end-re 3 (ruby-here-doc-end-syntax))) 1150 (while (re-search-forward ruby-here-doc-beg-re
1174 "Syntactic keywords for Ruby mode. See `font-lock-syntactic-keywords'.") 1151 (line-end-position) t)
1175 1152 (push (concat (ruby-here-doc-end-match) "\n") res)))
1176(defun ruby-comment-beg-syntax () 1153 (let ((start (point)))
1177 "Return the syntax cell for a the first character of a =begin. 1154 ;; With multiple openers on the same line, we don't know in which
1155 ;; part `start' is, so we have to go back to the beginning.
1156 (when (cdr res)
1157 (goto-char (nth 8 ppss))
1158 (setq res (nreverse res)))
1159 (while (and res (re-search-forward (pop res) limit 'move))
1160 (if (null res)
1161 (put-text-property (1- (point)) (point)
1162 'syntax-table (string-to-syntax "\""))))
1163 ;; Make extra sure we don't move back, lest we could fall into an
1164 ;; inf-loop.
1165 (if (< (point) start) (goto-char start))))))
1166 )
1167
1168 ;; For Emacsen where syntax-propertize-rules is not (yet) available,
1169 ;; fallback on the old font-lock-syntactic-keywords stuff.
1170
1171 (defconst ruby-here-doc-end-re
1172 "^\\([ \t]+\\)?\\(.*\\)\\(\n\\)"
1173 "Regexp to match the end of heredocs.
1174
1175This will actually match any line with one or more characters.
1176It's useful in that it divides up the match string so that
1177`ruby-here-doc-beg-match' can search for the beginning of the heredoc.")
1178
1179 (defun ruby-here-doc-beg-match ()
1180 "Return a regexp to find the beginning of a heredoc.
1181
1182This should only be called after matching against `ruby-here-doc-end-re'."
1183 (let ((contents (regexp-quote (match-string 2))))
1184 (concat "<<"
1185 (let ((match (match-string 1)))
1186 (if (and match (> (length match) 0))
1187 (concat "\\(?:-\\([\"']?\\)\\|\\([\"']\\)" match "\\)"
1188 contents "\\b\\(\\1\\|\\2\\)")
1189 (concat "-?\\([\"']\\|\\)" contents "\\b\\1"))))))
1190
1191 (defconst ruby-font-lock-syntactic-keywords
1192 `( ;; #{ }, #$hoge, #@foo are not comments
1193 ("\\(#\\)[{$@]" 1 (1 . nil))
1194 ;; the last $', $", $` in the respective string is not variable
1195 ;; the last ?', ?", ?` in the respective string is not ascii code
1196 ("\\(^\\|[\[ \t\n<+\(,=]\\)\\(['\"`]\\)\\(\\\\.\\|\\2\\|[^'\"`\n\\\\]\\)*?\\\\?[?$]\\(\\2\\)"
1197 (2 (7 . nil))
1198 (4 (7 . nil)))
1199 ;; $' $" $` .... are variables
1200 ;; ?' ?" ?` are ascii codes
1201 ("\\(^\\|[^\\\\]\\)\\(\\\\\\\\\\)*[?$]\\([#\"'`]\\)" 3 (1 . nil))
1202 ;; regexps
1203 ("\\(^\\|[=(,~?:;<>]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)"
1204 (4 (7 . ?/))
1205 (6 (7 . ?/)))
1206 ("^=en\\(d\\)\\_>" 1 "!")
1207 ("^\\(=\\)begin\\_>" 1 (ruby-comment-beg-syntax))
1208 ;; Currently, the following case is highlighted incorrectly:
1209 ;;
1210 ;; <<FOO
1211 ;; FOO
1212 ;; <<BAR
1213 ;; <<BAZ
1214 ;; BAZ
1215 ;; BAR
1216 ;;
1217 ;; This is because all here-doc beginnings are highlighted before any endings,
1218 ;; so although <<BAR is properly marked as a beginning, when we get to <<BAZ
1219 ;; it thinks <<BAR is part of a string so it's marked as well.
1220 ;;
1221 ;; This may be fixable by modifying ruby-in-here-doc-p to use
1222 ;; ruby-in-non-here-doc-string-p rather than syntax-ppss-context,
1223 ;; but I don't want to try that until we've got unit tests set up
1224 ;; to make sure I don't break anything else.
1225 (,(concat ruby-here-doc-beg-re ".*\\(\n\\)")
1226 ,(+ 1 (regexp-opt-depth ruby-here-doc-beg-re))
1227 (ruby-here-doc-beg-syntax))
1228 (,ruby-here-doc-end-re 3 (ruby-here-doc-end-syntax)))
1229 "Syntactic keywords for Ruby mode. See `font-lock-syntactic-keywords'.")
1230
1231 (defun ruby-comment-beg-syntax ()
1232 "Return the syntax cell for a the first character of a =begin.
1178See the definition of `ruby-font-lock-syntactic-keywords'. 1233See the definition of `ruby-font-lock-syntactic-keywords'.
1179 1234
1180This returns a comment-delimiter cell as long as the =begin 1235This returns a comment-delimiter cell as long as the =begin
1181isn't in a string or another comment." 1236isn't in a string or another comment."
1182 (when (not (nth 3 (syntax-ppss))) 1237 (when (not (nth 3 (syntax-ppss)))
1183 (string-to-syntax "!"))) 1238 (string-to-syntax "!")))
1239
1240 (defun ruby-in-here-doc-p ()
1241 "Return whether or not the point is in a heredoc."
1242 (save-excursion
1243 (let ((old-point (point)) (case-fold-search nil))
1244 (beginning-of-line)
1245 (catch 'found-beg
1246 (while (re-search-backward ruby-here-doc-beg-re nil t)
1247 (if (not (or (ruby-in-ppss-context-p 'anything)
1248 (ruby-here-doc-find-end old-point)))
1249 (throw 'found-beg t)))))))
1250
1251 (defun ruby-here-doc-find-end (&optional limit)
1252 "Expects the point to be on a line with one or more heredoc openers.
1253Returns the buffer position at which all heredocs on the line
1254are terminated, or nil if they aren't terminated before the
1255buffer position `limit' or the end of the buffer."
1256 (save-excursion
1257 (beginning-of-line)
1258 (catch 'done
1259 (let ((eol (save-excursion (end-of-line) (point)))
1260 (case-fold-search nil)
1261 ;; Fake match data such that (match-end 0) is at eol
1262 (end-match-data (progn (looking-at ".*$") (match-data)))
1263 beg-match-data end-re)
1264 (while (re-search-forward ruby-here-doc-beg-re eol t)
1265 (setq beg-match-data (match-data))
1266 (setq end-re (ruby-here-doc-end-match))
1267
1268 (set-match-data end-match-data)
1269 (goto-char (match-end 0))
1270 (unless (re-search-forward end-re limit t) (throw 'done nil))
1271 (setq end-match-data (match-data))
1272
1273 (set-match-data beg-match-data)
1274 (goto-char (match-end 0)))
1275 (set-match-data end-match-data)
1276 (goto-char (match-end 0))
1277 (point)))))
1278
1279 (defun ruby-here-doc-beg-syntax ()
1280 "Return the syntax cell for a line that may begin a heredoc.
1281See the definition of `ruby-font-lock-syntactic-keywords'.
1282
1283This sets the syntax cell for the newline ending the line
1284containing the heredoc beginning so that cases where multiple
1285heredocs are started on one line are handled correctly."
1286 (save-excursion
1287 (goto-char (match-beginning 0))
1288 (unless (or (ruby-in-ppss-context-p 'non-heredoc)
1289 (ruby-in-here-doc-p))
1290 (string-to-syntax "\""))))
1291
1292 (defun ruby-here-doc-end-syntax ()
1293 "Return the syntax cell for a line that may end a heredoc.
1294See the definition of `ruby-font-lock-syntactic-keywords'."
1295 (let ((pss (syntax-ppss)) (case-fold-search nil))
1296 ;; If we aren't in a string, we definitely aren't ending a heredoc,
1297 ;; so we can just give up.
1298 ;; This means we aren't doing a full-document search
1299 ;; every time we enter a character.
1300 (when (ruby-in-ppss-context-p 'heredoc pss)
1301 (save-excursion
1302 (goto-char (nth 8 pss)) ; Go to the beginning of heredoc.
1303 (let ((eol (point)))
1304 (beginning-of-line)
1305 (if (and (re-search-forward (ruby-here-doc-beg-match) eol t) ; If there is a heredoc that matches this line...
1306 (not (ruby-in-ppss-context-p 'anything)) ; And that's not inside a heredoc/string/comment...
1307 (progn (goto-char (match-end 0)) ; And it's the last heredoc on its line...
1308 (not (re-search-forward ruby-here-doc-beg-re eol t))))
1309 (string-to-syntax "\"")))))))
1184 1310
1185(unless (functionp 'syntax-ppss) 1311 (unless (functionp 'syntax-ppss)
1186 (defun syntax-ppss (&optional pos) 1312 (defun syntax-ppss (&optional pos)
1187 (parse-partial-sexp (point-min) (or pos (point))))) 1313 (parse-partial-sexp (point-min) (or pos (point)))))
1314 )
1188 1315
1189(defun ruby-in-ppss-context-p (context &optional ppss) 1316(defun ruby-in-ppss-context-p (context &optional ppss)
1190 (let ((ppss (or ppss (syntax-ppss (point))))) 1317 (let ((ppss (or ppss (syntax-ppss (point)))))
@@ -1195,10 +1322,7 @@ isn't in a string or another comment."
1195 ((eq context 'string) 1322 ((eq context 'string)
1196 (nth 3 ppss)) 1323 (nth 3 ppss))
1197 ((eq context 'heredoc) 1324 ((eq context 'heredoc)
1198 (and (nth 3 ppss) 1325 (eq ?\n (nth 3 ppss)))
1199 ;; If it's generic string, it's a heredoc and we don't care
1200 ;; See `parse-partial-sexp'
1201 (not (numberp (nth 3 ppss)))))
1202 ((eq context 'non-heredoc) 1326 ((eq context 'non-heredoc)
1203 (and (ruby-in-ppss-context-p 'anything) 1327 (and (ruby-in-ppss-context-p 'anything)
1204 (not (ruby-in-ppss-context-p 'heredoc)))) 1328 (not (ruby-in-ppss-context-p 'heredoc))))
@@ -1210,77 +1334,6 @@ isn't in a string or another comment."
1210 "context name `" (symbol-name context) "' is unknown")))) 1334 "context name `" (symbol-name context) "' is unknown"))))
1211 t))) 1335 t)))
1212 1336
1213(defun ruby-in-here-doc-p ()
1214 "Return whether or not the point is in a heredoc."
1215 (save-excursion
1216 (let ((old-point (point)) (case-fold-search nil))
1217 (beginning-of-line)
1218 (catch 'found-beg
1219 (while (re-search-backward ruby-here-doc-beg-re nil t)
1220 (if (not (or (ruby-in-ppss-context-p 'anything)
1221 (ruby-here-doc-find-end old-point)))
1222 (throw 'found-beg t)))))))
1223
1224(defun ruby-here-doc-find-end (&optional limit)
1225 "Expects the point to be on a line with one or more heredoc openers.
1226Returns the buffer position at which all heredocs on the line
1227are terminated, or nil if they aren't terminated before the
1228buffer position `limit' or the end of the buffer."
1229 (save-excursion
1230 (beginning-of-line)
1231 (catch 'done
1232 (let ((eol (save-excursion (end-of-line) (point)))
1233 (case-fold-search nil)
1234 ;; Fake match data such that (match-end 0) is at eol
1235 (end-match-data (progn (looking-at ".*$") (match-data)))
1236 beg-match-data end-re)
1237 (while (re-search-forward ruby-here-doc-beg-re eol t)
1238 (setq beg-match-data (match-data))
1239 (setq end-re (ruby-here-doc-end-match))
1240
1241 (set-match-data end-match-data)
1242 (goto-char (match-end 0))
1243 (unless (re-search-forward end-re limit t) (throw 'done nil))
1244 (setq end-match-data (match-data))
1245
1246 (set-match-data beg-match-data)
1247 (goto-char (match-end 0)))
1248 (set-match-data end-match-data)
1249 (goto-char (match-end 0))
1250 (point)))))
1251
1252(defun ruby-here-doc-beg-syntax ()
1253 "Return the syntax cell for a line that may begin a heredoc.
1254See the definition of `ruby-font-lock-syntactic-keywords'.
1255
1256This sets the syntax cell for the newline ending the line
1257containing the heredoc beginning so that cases where multiple
1258heredocs are started on one line are handled correctly."
1259 (save-excursion
1260 (goto-char (match-beginning 0))
1261 (unless (or (ruby-in-ppss-context-p 'non-heredoc)
1262 (ruby-in-here-doc-p))
1263 (string-to-syntax "|"))))
1264
1265(defun ruby-here-doc-end-syntax ()
1266 "Return the syntax cell for a line that may end a heredoc.
1267See the definition of `ruby-font-lock-syntactic-keywords'."
1268 (let ((pss (syntax-ppss)) (case-fold-search nil))
1269 ;; If we aren't in a string, we definitely aren't ending a heredoc,
1270 ;; so we can just give up.
1271 ;; This means we aren't doing a full-document search
1272 ;; every time we enter a character.
1273 (when (ruby-in-ppss-context-p 'heredoc pss)
1274 (save-excursion
1275 (goto-char (nth 8 pss)) ; Go to the beginning of heredoc.
1276 (let ((eol (point)))
1277 (beginning-of-line)
1278 (if (and (re-search-forward (ruby-here-doc-beg-match) eol t) ; If there is a heredoc that matches this line...
1279 (not (ruby-in-ppss-context-p 'anything)) ; And that's not inside a heredoc/string/comment...
1280 (progn (goto-char (match-end 0)) ; And it's the last heredoc on its line...
1281 (not (re-search-forward ruby-here-doc-beg-re eol t))))
1282 (string-to-syntax "|")))))))
1283
1284(if (featurep 'xemacs) 1337(if (featurep 'xemacs)
1285 (put 'ruby-mode 'font-lock-defaults 1338 (put 'ruby-mode 'font-lock-defaults
1286 '((ruby-font-lock-keywords) 1339 '((ruby-font-lock-keywords)
@@ -1377,8 +1430,10 @@ See `font-lock-syntax-table'.")
1377 ) 1430 )
1378 "Additional expressions to highlight in Ruby mode.") 1431 "Additional expressions to highlight in Ruby mode.")
1379 1432
1433(defvar electric-indent-chars)
1434
1380;;;###autoload 1435;;;###autoload
1381(defun ruby-mode () 1436(define-derived-mode ruby-mode prog-mode "Ruby"
1382 "Major mode for editing Ruby scripts. 1437 "Major mode for editing Ruby scripts.
1383\\[ruby-indent-line] properly indents subexpressions of multi-line 1438\\[ruby-indent-line] properly indents subexpressions of multi-line
1384class, module, def, if, while, for, do, and case statements, taking 1439class, module, def, if, while, for, do, and case statements, taking
@@ -1387,27 +1442,22 @@ nesting into account.
1387The variable `ruby-indent-level' controls the amount of indentation. 1442The variable `ruby-indent-level' controls the amount of indentation.
1388 1443
1389\\{ruby-mode-map}" 1444\\{ruby-mode-map}"
1390 (interactive)
1391 (kill-all-local-variables)
1392 (use-local-map ruby-mode-map)
1393 (setq mode-name "Ruby")
1394 (setq major-mode 'ruby-mode)
1395 (ruby-mode-variables) 1445 (ruby-mode-variables)
1396 1446
1397 (set (make-local-variable 'indent-line-function)
1398 'ruby-indent-line)
1399 (set (make-local-variable 'imenu-create-index-function) 1447 (set (make-local-variable 'imenu-create-index-function)
1400 'ruby-imenu-create-index) 1448 'ruby-imenu-create-index)
1401 (set (make-local-variable 'add-log-current-defun-function) 1449 (set (make-local-variable 'add-log-current-defun-function)
1402 'ruby-add-log-current-method) 1450 'ruby-add-log-current-method)
1403 1451
1404 (add-hook 1452 (add-hook
1405 (cond ((boundp 'before-save-hook) 1453 (cond ((boundp 'before-save-hook) 'before-save-hook)
1406 (make-local-variable 'before-save-hook)
1407 'before-save-hook)
1408 ((boundp 'write-contents-functions) 'write-contents-functions) 1454 ((boundp 'write-contents-functions) 'write-contents-functions)
1409 ((boundp 'write-contents-hooks) 'write-contents-hooks)) 1455 ((boundp 'write-contents-hooks) 'write-contents-hooks))
1410 'ruby-mode-set-encoding) 1456 'ruby-mode-set-encoding nil 'local)
1457
1458 (set (make-local-variable 'electric-indent-chars)
1459 (append '(?\{ ?\}) (if (boundp 'electric-indent-chars)
1460 (default-value 'electric-indent-chars))))
1411 1461
1412 (set (make-local-variable 'font-lock-defaults) 1462 (set (make-local-variable 'font-lock-defaults)
1413 '((ruby-font-lock-keywords) nil nil)) 1463 '((ruby-font-lock-keywords) nil nil))
@@ -1415,12 +1465,12 @@ The variable `ruby-indent-level' controls the amount of indentation.
1415 ruby-font-lock-keywords) 1465 ruby-font-lock-keywords)
1416 (set (make-local-variable 'font-lock-syntax-table) 1466 (set (make-local-variable 'font-lock-syntax-table)
1417 ruby-font-lock-syntax-table) 1467 ruby-font-lock-syntax-table)
1418 (set (make-local-variable 'font-lock-syntactic-keywords)
1419 ruby-font-lock-syntactic-keywords)
1420 1468
1421 (if (fboundp 'run-mode-hooks) 1469 (if (eval-when-compile (fboundp 'syntax-propertize-rules))
1422 (run-mode-hooks 'ruby-mode-hook) 1470 (set (make-local-variable 'syntax-propertize-function)
1423 (run-hooks 'ruby-mode-hook))) 1471 #'ruby-syntax-propertize-function)
1472 (set (make-local-variable 'font-lock-syntactic-keywords)
1473 ruby-font-lock-syntactic-keywords)))
1424 1474
1425;;; Invoke ruby-mode when appropriate 1475;;; Invoke ruby-mode when appropriate
1426 1476