From 1bcbc29d9554030b6e681ed6f1b111b26a7f3738 Mon Sep 17 00:00:00 2001 From: nobu Date: Tue, 22 Jul 2008 00:51:35 +0000 Subject: * misc/ruby-mode.el: fix here-doc strings with inner quotes. patches by Nathan Weizenbaum from [ruby-core:17615] through [ruby-core:17910]. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@18150 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- misc/ruby-mode.el | 142 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 96 insertions(+), 46 deletions(-) (limited to 'misc/ruby-mode.el') diff --git a/misc/ruby-mode.el b/misc/ruby-mode.el index 01f22898b3..3c70a64967 100644 --- a/misc/ruby-mode.el +++ b/misc/ruby-mode.el @@ -49,15 +49,27 @@ (defconst ruby-block-end-re "\\") (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 "<<" + (let ((match (match-string 1))) + (if (and match (> (length match) 0)) + (concat "\\(?:-\\([\"']?\\)\\|\\([\"']\\)" (match-string 1) "\\)" + contents "\\(\\1\\|\\2\\)") + (concat "-?\\([\"']\\|\\)" contents "\\1")))))) (defconst ruby-delimiter (concat "[?$/%(){}#\"'`.:]\\|<<\\|\\[\\|\\]\\|\\<\\(" @@ -172,8 +184,8 @@ Also ignores spaces after parenthesis when 'space." (eval-when-compile (require 'cl)) (defun ruby-imenu-create-index-in-block (prefix beg end) - (let ((index-alist '()) (case-fold-search nil) - name next pos decl sing) + (let ((index-alist '()) + name next pos decl sing) (goto-char beg) (while (re-search-forward "^\\s *\\(\\(class\\>\\(\\s *<<\\)?\\|module\\>\\)\\s *\\([^\(<\n ]+\\)\\|\\(def\\|alias\\)\\>\\s *\\([^\(\n ]+\\)\\)" end t) (setq sing (match-beginning 3)) @@ -220,6 +232,7 @@ Also ignores spaces after parenthesis when 'space." (defun ruby-mode-variables () (set-syntax-table ruby-mode-syntax-table) (setq local-abbrev-table ruby-mode-abbrev-table) + (setq case-fold-search nil) (make-local-variable 'indent-line-function) (setq indent-line-function 'ruby-indent-line) (make-local-variable 'require-final-newline) @@ -235,6 +248,8 @@ Also ignores spaces after parenthesis when 'space." (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) @@ -645,7 +660,6 @@ The variable ruby-indent-level controls the amount of indentation. (save-excursion (beginning-of-line) (let ((indent-point (point)) - (case-fold-search nil) state bol eol begin op-end (paren (progn (skip-syntax-forward " ") (and (char-after) (matching-paren (char-after))))) @@ -1090,7 +1104,7 @@ balanced expression is found." (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 @@ -1106,7 +1120,76 @@ balanced expression is found." (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 ".*\\(\n\\)") + ,(+ 1 (regexp-opt-depth ruby-here-doc-beg-re)) + (ruby-here-doc-beg-syntax)) + (,ruby-here-doc-end-re 3 (ruby-here-doc-end-syntax)))) + + (defun ruby-in-non-here-doc-string-p () + (let ((syntax (syntax-ppss))) + (or (nth 4 syntax) + ;; In a string *without* a generic delimiter + ;; If it's generic, it's a heredoc and we don't care + ;; See `parse-partial-sexp' + (numberp (nth 3 syntax))))) + + (defun ruby-in-here-doc-p () + (save-excursion + (let ((old-point (point))) + (beginning-of-line) + (catch 'found-beg + (while (re-search-backward ruby-here-doc-beg-re nil t) + (if (not (or (syntax-ppss-context (syntax-ppss)) + (ruby-here-doc-find-end old-point))) + (throw 'found-beg t))))))) + + (defun ruby-here-doc-find-end (&optional limit) + "Expects the point to be on a line with one or more heredoc +openers. Returns the buffer position at which all heredocs on the +line are terminated, or nil if they aren't terminated before the +buffer position `limit' or the end of the buffer." + (save-excursion + (beginning-of-line) + (catch 'done + (let ((eol (save-excursion (end-of-line) (point))) + ;; Fake match data such that (match-end 0) is at eol + (end-match-data (progn (looking-at ".*$") (match-data))) + beg-match-data end-re) + (while (re-search-forward ruby-here-doc-beg-re eol t) + (setq beg-match-data (match-data)) + (setq end-re (ruby-here-doc-end-match)) + + (set-match-data end-match-data) + (goto-char (match-end 0)) + (unless (re-search-forward end-re limit t) (throw 'done nil)) + (setq end-match-data (match-data)) + + (set-match-data beg-match-data) + (goto-char (match-end 0))) + (set-match-data end-match-data) + (goto-char (match-end 0)) + (point))))) + + (defun ruby-here-doc-beg-syntax () + (save-excursion + (goto-char (match-beginning 0)) + (unless (or (ruby-in-non-here-doc-string-p) + (ruby-in-here-doc-p)) + (string-to-syntax "|")))) + + (defun ruby-here-doc-end-syntax () + (save-excursion + (goto-char (match-end 0)) + (let ((old-point (point)) + (beg-exists (re-search-backward (ruby-here-doc-beg-match) nil t)) + (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 a heredoc/string/comment... + (progn (goto-char (match-end 0)) ; And it's the last heredoc on its line... + (not (re-search-forward ruby-here-doc-beg-re eol t))) + (eq old-point (ruby-here-doc-find-end old-point))) ; And it ends at this point... + (string-to-syntax "|"))))) (if (featurep 'xemacs) (put 'ruby-mode 'font-lock-defaults @@ -1147,35 +1230,7 @@ balanced expression is found." (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]+\\)" @@ -1224,6 +1279,8 @@ balanced expression is found." "\\|") "\\)\\>\\)") 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) @@ -1237,13 +1294,6 @@ balanced expression is found." 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)) -- cgit v1.2.3