From 6e63b3e997e1ac2fa9b58f0d2edeca3cd83628f2 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Mon, 7 Mar 2016 02:58:49 +0200 Subject: [PATCH] Guard against nested percent literals * lisp/progmodes/ruby-mode.el (ruby-syntax-propertize-percent-literal): Don't check the syntax status. (ruby-syntax-propertize): Check it here. And also guard against being in a larger percent literal. * test/automated/ruby-mode-tests.el (ruby-no-nested-percent-literals): New test. --- lisp/progmodes/ruby-mode.el | 61 ++++++++++++++++--------------- test/automated/ruby-mode-tests.el | 8 ++++ 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index fa94992ab79..1395828cff9 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el @@ -1901,7 +1901,10 @@ It will be properly highlighted even when the call omits parens.") (ruby-syntax-propertize-heredoc end)))) ;; Handle percent literals: %w(), %q{}, etc. ((concat "\\(?:^\\|[[ \t\n<+(,=]\\)" ruby-percent-literal-beg-re) - (1 (prog1 "|" (ruby-syntax-propertize-percent-literal end))))) + (1 (unless (nth 8 (save-excursion (syntax-ppss (match-beginning 1)))) + ;; Not inside a string, a comment, or a percent literal. + (ruby-syntax-propertize-percent-literal end) + (string-to-syntax "|"))))) (point) end))) (define-obsolete-function-alias @@ -1944,35 +1947,33 @@ It will be properly highlighted even when the call omits parens.") (defun ruby-syntax-propertize-percent-literal (limit) (goto-char (match-beginning 2)) - ;; 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) - (save-excursion - (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, do nothing. - ((scan-error search-failed))))))) + (let* ((op (char-after)) + (ops (char-to-string op)) + (cl (or (cdr (aref (syntax-table) op)) + (cdr (assoc op '((?< . ?>)))))) + parse-sexp-lookup-properties) + (save-excursion + (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, do nothing. + ((scan-error search-failed)))))) (defun ruby-syntax-propertize-expansion () ;; Save the match data to a text property, for font-locking later. diff --git a/test/automated/ruby-mode-tests.el b/test/automated/ruby-mode-tests.el index da8d77c5157..7073f7a04e2 100644 --- a/test/automated/ruby-mode-tests.el +++ b/test/automated/ruby-mode-tests.el @@ -461,6 +461,14 @@ VALUES-PLIST is a list with alternating index and value elements." (ruby-assert-face "%S{foo}" 4 nil) (ruby-assert-face "%R{foo}" 4 nil)) +(ert-deftest ruby-no-nested-percent-literals () + (ruby-with-temp-buffer "a = %w[b %()]" + (syntax-propertize (point)) + (should (null (nth 8 (syntax-ppss)))) + (should (eq t (nth 3 (syntax-ppss (1- (point-max)))))) + (search-backward "[") + (should (eq t (nth 3 (syntax-ppss)))))) + (ert-deftest ruby-add-log-current-method-examples () (let ((pairs '(("foo" . "#foo") ("C.foo" . ".foo") -- 2.39.2