This is a multi-part message in MIME format. --------------030104020206030305070401 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Here's a third patch that fixes a bug in the second and uses a quicker method of determining if the point is in a here-doc. To do so, it enables parse-sexp-lookup-properties, which causes syntax-sensitive functions to respect font-lock-syntactic-keywords. I think this is a good thing to enable in general, because it means that forward-sexp and similar functions treat regexps the same way they treat strings and so forth. Nathan Weizenbaum wrote: > It was designed to fix the following case: > > <<FOO > " > FOO > bar > > where "bar" is wrongly highlighted in font-lock-string-face. It also > fixes the following case: > > <<FOO > <<BAR > BAR > FOO > > in which FOO is *not* highlighted in font-lock-string-face when it > should be. > > I've attached a modified version that works with the case you pointed > out. > > Yukihiro Matsumoto wrote: >> In message "Re: [ruby-core:17615] [PATCH] ruby-mode.el: Fix here-doc >> strings with inner quotes" >> on Sat, 5 Jul 2008 17:57:32 +0900, Nathan Weizenbaum >> <nex342 / gmail.com> writes: >> | >> |[1 <text/plain; ISO-8859-1 (7bit)>] >> |At the moment, ruby-mode.el uses font-lock-keywords as opposed to >> |font-lock-syntactic-keywords to highlight here-doc strings. This >> means |that Emacs doesn't actually recognize them as strings, so >> unbalanced |quotes paint the whole buffer in font-lock-string-face. >> In addition, it |improperly highlights strings that look like nested >> heredocs, like the |following: >> | >> |<<FOO >> |<<BAR >> |BAR >> |FOO >> | >> |The attached patch uses font-lock-syntactic-keywords instead, so >> that |heredocs actually register as strings, so stray quotation marks >> don't |wreak havoc. >> >> I am not sure what this patch fixes. But at least, the patched >> version does not work well for the following code: >> >> print <<FOO, <<BAR >> <<BAR >> foo >> FOO >> bar >> BAR >> >> it should highlight here-doc up to the last BAR. >> >> matz. >> >> >> > --------------030104020206030305070401 Content-Type: text/x-diff; name ere-doc.3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename ere-doc.3.patch" Index: ruby-mode.el --- ruby-mode.el (revision 18050) +++ ruby-mode.el (working copy) @@ -48,16 +48,26 @@ (defconst ruby-block-end-re "\\<end\\>") (defconst ruby-here-doc-beg-re - "<<\\(-\\)?\\(\\([a-zA-Z0-9_]+\\)\\|[\"]\\([^\"]+\\)[\"]\\|[']\\([^']+\\)[']\\)") + "\\(<\\)<\\(-\\)?\\(\\([a-zA-Z0-9_]+\\)\\|[\"]\\([^\"]+\\)[\"]\\|[']\\([^']+\\)[']\\)") +(defconst ruby-here-doc-end-re + "^\\([ \t]+\\)?\\(.*\\)\\(.\\)$") + (defun ruby-here-doc-end-match () (concat "^" - (if (match-string 1) "[ \t]*" nil) + (if (match-string 2) "[ \t]*" nil) (regexp-quote - (or (match-string 3) - (match-string 4) - (match-string 5))))) + (or (match-string 4) + (match-string 5) + (match-string 6))))) +(defun ruby-here-doc-beg-match () + (let ((contents (regexp-quote (concat (match-string 2) (match-string 3))))) + (concat "<<" + (if (match-string 1) + (concat "\\(?:-\\(?1:[\"']\\)\\|\\(?1:[\"']\\)" (match-string 1) "\\)" contents "\\1") + (concat "-?\\([\"']\\|\\)" contents "\\1"))))) + (defconst ruby-delimiter (concat "[?$/%(){}#\"'`.:]\\|<<\\|\\[\\|\\]\\|\\<\\(" ruby-block-beg-re @@ -226,6 +236,8 @@ (setq indent-tabs-mode ruby-indent-tabs-mode) (make-local-variable 'parse-sexp-ignore-comments) (setq parse-sexp-ignore-comments t) + (make-local-variable 'parse-sexp-lookup-properties) + (setq parse-sexp-lookup-properties t) (make-local-variable 'paragraph-start) (setq paragraph-start (concat "$\\|" page-delimiter)) (make-local-variable 'paragraph-separate) @@ -1007,7 +1019,7 @@ (setq font-lock-variable-name-face font-lock-type-face)) (setq ruby-font-lock-syntactic-keywords - '( + `( ;; #{ }, #$hoge, #@foo are not comments ("\\(#\\)[{$@]" 1 (1 . nil)) ;; the last $', $", $` in the respective string is not variable @@ -1023,8 +1035,28 @@ (4 (7 . ?/)) (6 (7 . ?/))) ("^\\( )begin\\(\\s \\|$\\)" 1 (7 . nil)) - ("^\\( )end\\(\\s \\|$\\)" 1 (7 . nil)))) + ("^\\( )end\\(\\s \\|$\\)" 1 (7 . nil)) + (,(concat ruby-here-doc-beg-re ".*\\(?100:\n\\)") + 100 (ruby-here-doc-beg-syntax)) + (,ruby-here-doc-end-re 3 (ruby-here-doc-end-syntax)))) + (defun ruby-here-doc-beg-syntax () + (save-excursion + (goto-char (match-beginning 0)) + (if (null (syntax-ppss-context (syntax-ppss))) (string-to-syntax "|")))) + + (defun ruby-here-doc-end-syntax () + (save-excursion + (goto-char (match-beginning 0)) + (let ((beg-exists (re-search-backward (ruby-here-doc-beg-match) nil t)) + (beg-end (match-end 0)) + (eol (save-excursion (end-of-line) (point)))) + (if (and beg-exists ; If there is a heredoc that matches this line... + (null (syntax-ppss-context (syntax-ppss))) ; And that's not inside another heredoc/comment... + (progn (goto-char beg-end) ; And it's the last heredoc on its line... + (not (re-search-forward ruby-here-doc-beg-re eol t)))) + (string-to-syntax "|"))))) + (if (featurep 'xemacs) (put 'ruby-mode 'font-lock-defaults '((ruby-font-lock-keywords) @@ -1064,35 +1096,7 @@ (modify-syntax-entry ?_ "w" tbl) tbl)) - (defun ruby-font-lock-here-docs (limit) - (if (re-search-forward ruby-here-doc-beg-re limit t) - (let (beg) - (beginning-of-line) - (forward-line) - (setq beg (point)) - (if (re-search-forward (ruby-here-doc-end-match) nil t) - (progn - (set-match-data (list beg (point))) - t))))) - - (defun ruby-font-lock-maybe-here-docs (limit) - (let (beg) - (save-excursion - (if (re-search-backward ruby-here-doc-beg-re nil t) - (progn - (beginning-of-line) - (forward-line) - (setq beg (point))))) - (if (and beg - (let ((end-match (ruby-here-doc-end-match))) - (and (not (re-search-backward end-match beg t)) - (re-search-forward end-match nil t)))) - (progn - (set-match-data (list beg (point))) - t) - nil))) - - (defvar ruby-font-lock-keywords + (defconst ruby-font-lock-keywords (list ;; functions '("^\\s *def\\s +\\([^( \t\n]+\\)" @@ -1141,6 +1145,8 @@ "\\|") "\\)\\>\\)") 2) + ;; here-doc beginnings + (list ruby-here-doc-beg-re 0 'font-lock-string-face) ;; variables '("\\(^\\|[^_:.@$]\\|\\.\\.\\)\\b\\(nil\\|self\\|true\\|false\\)\\>" 2 font-lock-variable-name-face) @@ -1154,13 +1160,6 @@ 0 font-lock-comment-face t) '(ruby-font-lock-maybe-docs 0 font-lock-comment-face t) - ;; "here" document - '(ruby-font-lock-here-docs - 0 font-lock-string-face t) - '(ruby-font-lock-maybe-here-docs - 0 font-lock-string-face t) - `(,ruby-here-doc-beg-re - 0 font-lock-string-face t) ;; general delimited string '("\\(^\\|[[ \t\n<+(, \)\\(%[xrqQwW]?\\([^<[{(a-zA-Z0-9 \n]\\)[^\n\\\\]*\\(\\\\.[^\n\\\\]*\\)*\\(\\3\\)\\)" (2 font-lock-string-face)) --------------030104020206030305070401--