>From bebf7f22ef746e1d20a5cdd4312684e02f0222f9 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Sat, 28 Apr 2012 19:12:13 +0400 Subject: [PATCH 1/2] * lisp/progmodes/ruby-mode.el: Don't propertize percent literals in strings or comments (bug#6286) Change all names and references to "general delimited literals" to "percent literals", seems to be a shorter and more popular term. ruby-percent-literal-beg-re: New constant. (ruby-propertize-containing-percent-literal): Check the type of literal more carefully. Only propertize it but not other syntax above the point. (ruby-syntax-propertize-percent-literal): Only propertize when not inside basic string or comment. When the literal is unclosed, leave the text after it unpropertized. --- lisp/progmodes/ruby-mode.el | 84 ++++++++++++++++++++++++------------------- test/indent/ruby.rb | 7 ++++ 2 files changed, 54 insertions(+), 37 deletions(-) diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index 5d79437..432680f 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el @@ -794,7 +794,7 @@ and `\\' when preceded by `?'." ;; (not (or (eolp) (looking-at "#") ;; (and (eq (car (nth 1 state)) ?{) ;; (looking-at "|")))))) - ;; Not a regexp or general delimited literal. + ;; Not a regexp or percent literal. (null (nth 0 (ruby-parse-region (or begin parse-start) (point)))) (or (not (eq ?| (char-after (point)))) @@ -1111,17 +1111,21 @@ See `add-log-current-defun-function'." mlist))))) (declare-function ruby-syntax-propertize-heredoc "ruby-mode" (limit)) -(declare-function ruby-syntax-general-delimiters-goto-beg "ruby-mode" ()) -(declare-function ruby-syntax-propertize-general-delimiters "ruby-mode" (limit)) +(declare-function ruby-propertize-containing-percent-literal "ruby-mode" (limit)) +(declare-function ruby-syntax-propertize-percent-literal "ruby-mode" (limit)) (if (eval-when-compile (fboundp #'syntax-propertize-rules)) ;; New code that works independently from font-lock. (progn + (defconst ruby-percent-literal-beg-re + "\\(%\\)[qQrswWx]?\\([[:punct:]]\\)" + "Regexp to match the beginning of percent literal.") + (defun ruby-syntax-propertize-function (start end) "Syntactic keywords for Ruby mode. See `syntax-propertize-function'." (goto-char start) (ruby-syntax-propertize-heredoc end) - (ruby-syntax-general-delimiters-goto-beg) + (ruby-propertize-containing-percent-literal end) (funcall (syntax-propertize-rules ;; #{ }, #$hoge, #@foo are not comments. @@ -1161,8 +1165,8 @@ See `add-log-current-defun-function'." ((concat ruby-here-doc-beg-re ".*\\(\n\\)") (7 (prog1 "\"" (ruby-syntax-propertize-heredoc end)))) ;; Handle percent literals: %w(), %q{}, etc. - ("\\(?:^\\|[[ \t\n<+(,=]\\)\\(%\\)[qQrswWx]?\\([[:punct:]]\\)" - (1 (prog1 "|" (ruby-syntax-propertize-general-delimiters end))))) + ((concat "\\(?:^\\|[[ \t\n<+(,=]\\)" ruby-percent-literal-beg-re) + (1 (prog1 "|" (ruby-syntax-propertize-percent-literal end))))) (point) end)) (defun ruby-syntax-propertize-heredoc (limit) @@ -1189,40 +1193,46 @@ See `add-log-current-defun-function'." ;; inf-loop. (if (< (point) start) (goto-char start)))))) - (defun ruby-syntax-general-delimiters-goto-beg () - (let ((state (syntax-ppss))) - ;; Move to the start of the literal, in case it's multiline. - ;; TODO: determine the literal type more reliably here? + (defun ruby-propertize-containing-percent-literal (limit) + (let ((state (syntax-ppss)) + (start (point))) + ;; When already inside percent literal, re-propertize it. (when (eq t (nth 3 state)) (goto-char (nth 8 state)) - (beginning-of-line)))) + (when (looking-at ruby-percent-literal-beg-re) + (ruby-syntax-propertize-percent-literal limit)) + (when (< (point) start) (goto-char start))))) - (defun ruby-syntax-propertize-general-delimiters (limit) + (defun ruby-syntax-propertize-percent-literal (limit) (goto-char (match-beginning 2)) - (let* ((op (char-after)) - (ops (char-to-string op)) - (cl (or (cdr (aref (syntax-table) op)) - (cdr (assoc op '((?< . ?>)))))) - parse-sexp-lookup-properties) - (ignore-errors - (if cl - (progn ; Paired delimiters. - ;; Delimiter pairs of the same kind can be nested - ;; inside the literal, as long as they are balanced. - ;; Create syntax table that ignores other characters. - (with-syntax-table (make-char-table 'syntax-table nil) - (modify-syntax-entry op (concat "(" (char-to-string cl))) - (modify-syntax-entry cl (concat ")" ops)) - (modify-syntax-entry ?\\ "\\") - (save-restriction - (narrow-to-region (point) limit) - (forward-list)))) ; skip to the paired character - ;; Single character delimiter. - (re-search-forward (concat "[^\\]\\(?:\\\\\\\\\\)*" - (regexp-quote ops)) limit nil)) - ;; If we reached here, the closing delimiter was found. - (put-text-property (1- (point)) (point) - 'syntax-table (string-to-syntax "|"))))) + ;; Not inside a simple string or comment. + (when (eq t (nth 3 (syntax-ppss))) + (let* ((op (char-after)) + (ops (char-to-string op)) + (cl (or (cdr (aref (syntax-table) op)) + (cdr (assoc op '((?< . ?>)))))) + parse-sexp-lookup-properties) + (condition-case nil + (progn + (if cl ; Paired delimiters. + ;; Delimiter pairs of the same kind can be nested + ;; inside the literal, as long as they are balanced. + ;; Create syntax table that ignores other characters. + (with-syntax-table (make-char-table 'syntax-table nil) + (modify-syntax-entry op (concat "(" (char-to-string cl))) + (modify-syntax-entry cl (concat ")" ops)) + (modify-syntax-entry ?\\ "\\") + (save-restriction + (narrow-to-region (point) limit) + (forward-list))) ; skip to the paired character + ;; Single character delimiter. + (re-search-forward (concat "[^\\]\\(?:\\\\\\\\\\)*" + (regexp-quote ops)) limit nil)) + ;; Found the closing delimiter. + (put-text-property (1- (point)) (point) 'syntax-table + (string-to-syntax "|"))) + ;; Unclosed literal, leave the following text unpropertized. + ((scan-error search-failed) (goto-char limit)))))) ) ;; For Emacsen where syntax-propertize-rules is not (yet) available, @@ -1267,7 +1277,7 @@ This should only be called after matching against `ruby-here-doc-end-re'." (4 (7 . ?/)) (6 (7 . ?/))) ("^=en\\(d\\)\\_>" 1 "!") - ;; General delimited string. + ;; Percent literal. ("\\(^\\|[[ \t\n<+(,=]\\)\\(%[xrqQwW]?\\([^<[{(a-zA-Z0-9 \n]\\)[^\n\\\\]*\\(\\\\.[^\n\\\\]*\\)*\\(\\3\\)\\)" (3 "\"") (5 "\"")) diff --git a/test/indent/ruby.rb b/test/indent/ruby.rb index c4a747a..7a9d123 100644 --- a/test/indent/ruby.rb +++ b/test/indent/ruby.rb @@ -7,6 +7,13 @@ c = %w(foo baz) d = %!hello! +# Don't propertize percent literals inside strings. +"(%s, %s)" % [123, 456] + +# Nor inside comments. +x = # "tot %q/to"; = +y = 2 / 3 + # A "do" after a slash means that slash is not a division, but it doesn't imply # it's a regexp-ender, since it can be a regexp-starter instead! x = toto / foo; if /do bar/ then -- 1.7.10.msysgit.1