From: Dmitry Gutov Date: Fri, 31 May 2013 06:04:33 +0000 (+0400) Subject: * lisp/progmodes/ruby-mode.el (ruby-syntax-expansion-allowed-p): New X-Git-Tag: emacs-24.3.90~173^2^2~42^2~45^2~387^2~2026^2~117 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=19bb8e629352f9438e95e62559bf73940cd3d885;p=emacs.git * lisp/progmodes/ruby-mode.el (ruby-syntax-expansion-allowed-p): New function, checks if expression expansion is allowed in given parse state. (ruby-syntax-propertize-expansion): Use it. (ruby-syntax-propertize-function): Bind `case-fold-search' to nil around the body. * test/automated/ruby-mode-tests.el: New tests, for percent literals and expression expansion. --- diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 87774cdb2a5..c4dc36245f2 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,12 @@ +2013-05-31 Dmitry Gutov + + * progmodes/ruby-mode.el (ruby-syntax-expansion-allowed-p): New + function, checks if point is inside a literal that allows + expression expansion. + (ruby-syntax-propertize-expansion): Use it. + (ruby-syntax-propertize-function): Bind `case-fold-search' to nil + around the body. + 2013-05-30 Juri Linkov * isearch.el (isearch-mode-map): Bind `isearch-toggle-invisible' diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index a96ee64a229..b7a635199ab 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el @@ -1349,6 +1349,7 @@ If the result is do-end block, it will always be multiline." (declare-function ruby-syntax-propertize-percent-literal "ruby-mode" (limit)) ;; Unusual code layout confuses the byte-compiler. (declare-function ruby-syntax-propertize-expansion "ruby-mode" ()) +(declare-function ruby-syntax-expansion-allowed-p "ruby-mode" (parse-state)) (if (eval-when-compile (fboundp #'syntax-propertize-rules)) ;; New code that works independently from font-lock. @@ -1380,51 +1381,52 @@ It will be properly highlighted even when the call omits parens.") (defun ruby-syntax-propertize-function (start end) "Syntactic keywords for Ruby mode. See `syntax-propertize-function'." - (goto-char start) - (remove-text-properties start end '(ruby-expansion-match-data)) - (ruby-syntax-propertize-heredoc end) - (ruby-syntax-enclosing-percent-literal end) - (funcall - (syntax-propertize-rules - ;; $' $" $` .... are variables. - ;; ?' ?" ?` are ascii codes. - ("\\([?$]\\)[#\"'`]" - (1 (unless (save-excursion - ;; Not within a string. - (nth 3 (syntax-ppss (match-beginning 0)))) - (string-to-syntax "\\")))) - ;; Regular expressions. Start with matching unescaped slash. - ("\\(?:\\=\\|[^\\]\\)\\(?:\\\\\\\\\\)*\\(/\\)" - (1 (let ((state (save-excursion (syntax-ppss (match-beginning 1))))) - (when (or - ;; Beginning of a regexp. - (and (null (nth 8 state)) - (save-excursion - (forward-char -1) - (looking-back ruby-syntax-before-regexp-re - (point-at-bol)))) - ;; End of regexp. We don't match the whole - ;; regexp at once because it can have - ;; string interpolation inside, or span - ;; several lines. - (eq ?/ (nth 3 state))) - (string-to-syntax "\"/"))))) - ;; Expression expansions in strings. We're handling them - ;; here, so that the regexp rule never matches inside them. - (ruby-expression-expansion-re - (0 (ignore (ruby-syntax-propertize-expansion)))) - ("^=en\\(d\\)\\_>" (1 "!")) - ("^\\(=\\)begin\\_>" (1 "!")) - ;; Handle here documents. - ((concat ruby-here-doc-beg-re ".*\\(\n\\)") - (7 (unless (ruby-singleton-class-p (match-beginning 0)) - (put-text-property (match-beginning 7) (match-end 7) - 'syntax-table (string-to-syntax "\"")) - (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))))) - (point) end)) + (let (case-fold-search) + (goto-char start) + (remove-text-properties start end '(ruby-expansion-match-data)) + (ruby-syntax-propertize-heredoc end) + (ruby-syntax-enclosing-percent-literal end) + (funcall + (syntax-propertize-rules + ;; $' $" $` .... are variables. + ;; ?' ?" ?` are ascii codes. + ("\\([?$]\\)[#\"'`]" + (1 (unless (save-excursion + ;; Not within a string. + (nth 3 (syntax-ppss (match-beginning 0)))) + (string-to-syntax "\\")))) + ;; Regular expressions. Start with matching unescaped slash. + ("\\(?:\\=\\|[^\\]\\)\\(?:\\\\\\\\\\)*\\(/\\)" + (1 (let ((state (save-excursion (syntax-ppss (match-beginning 1))))) + (when (or + ;; Beginning of a regexp. + (and (null (nth 8 state)) + (save-excursion + (forward-char -1) + (looking-back ruby-syntax-before-regexp-re + (point-at-bol)))) + ;; End of regexp. We don't match the whole + ;; regexp at once because it can have + ;; string interpolation inside, or span + ;; several lines. + (eq ?/ (nth 3 state))) + (string-to-syntax "\"/"))))) + ;; Expression expansions in strings. We're handling them + ;; here, so that the regexp rule never matches inside them. + (ruby-expression-expansion-re + (0 (ignore (ruby-syntax-propertize-expansion)))) + ("^=en\\(d\\)\\_>" (1 "!")) + ("^\\(=\\)begin\\_>" (1 "!")) + ;; Handle here documents. + ((concat ruby-here-doc-beg-re ".*\\(\n\\)") + (7 (unless (ruby-singleton-class-p (match-beginning 0)) + (put-text-property (match-beginning 7) (match-end 7) + 'syntax-table (string-to-syntax "\"")) + (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))))) + (point) end))) (defun ruby-syntax-propertize-heredoc (limit) (let ((ppss (syntax-ppss)) @@ -1496,9 +1498,10 @@ It will be properly highlighted even when the call omits parens.") (defun ruby-syntax-propertize-expansion () ;; Save the match data to a text property, for font-locking later. ;; Set the syntax of all double quotes and backticks to punctuation. - (let ((beg (match-beginning 2)) - (end (match-end 2))) - (when (and beg (save-excursion (nth 3 (syntax-ppss beg)))) + (let* ((beg (match-beginning 2)) + (end (match-end 2)) + (state (and beg (save-excursion (syntax-ppss beg))))) + (when (ruby-syntax-expansion-allowed-p state) (put-text-property beg (1+ beg) 'ruby-expansion-match-data (match-data)) (goto-char beg) @@ -1506,6 +1509,17 @@ It will be properly highlighted even when the call omits parens.") (put-text-property (match-beginning 0) (match-end 0) 'syntax-table (string-to-syntax ".")))))) + (defun ruby-syntax-expansion-allowed-p (parse-state) + "Return non-nil if expression expansion is allowed." + (let ((term (nth 3 parse-state))) + (cond + ((memq term '(?\" ?` ?\n))) + ((eq term t) + (save-match-data + (save-excursion + (goto-char (nth 8 parse-state)) + (looking-at "%\\(?:[QWrx]\\|\\W\\)"))))))) + (defun ruby-syntax-propertize-expansions (start end) (save-excursion (goto-char start) diff --git a/test/ChangeLog b/test/ChangeLog index 56e019ec4af..98fb2e3da1f 100644 --- a/test/ChangeLog +++ b/test/ChangeLog @@ -1,3 +1,8 @@ +2013-05-31 Dmitry Gutov + + * automated/ruby-mode-tests.el: New tests, for percent literals + and expression expansion. + 2013-05-29 Leo Liu * indent/octave.m: Tweak. diff --git a/test/automated/ruby-mode-tests.el b/test/automated/ruby-mode-tests.el index e52927a2968..6ed2a8ad377 100644 --- a/test/automated/ruby-mode-tests.el +++ b/test/automated/ruby-mode-tests.el @@ -353,6 +353,23 @@ VALUES-PLIST is a list with alternating index and value elements." ;; It's confused by the closing paren in the middle. (ruby-assert-state s 8 nil))) +(ert-deftest ruby-interpolation-inside-double-quoted-percent-literals () + (ruby-assert-face "%Q{foo #@bar}" 8 font-lock-variable-name-face) + (ruby-assert-face "%W{foo #@bar}" 8 font-lock-variable-name-face) + (ruby-assert-face "%r{foo #@bar}" 8 font-lock-variable-name-face) + (ruby-assert-face "%x{foo #@bar}" 8 font-lock-variable-name-face)) + +(ert-deftest ruby-no-interpolation-in-single-quoted-literals () + (ruby-assert-face "'foo #@bar'" 7 font-lock-string-face) + (ruby-assert-face "%q{foo #@bar}" 8 font-lock-string-face) + (ruby-assert-face "%w{foo #@bar}" 8 font-lock-string-face) + (ruby-assert-face "%s{foo #@bar}" 8 font-lock-string-face)) + +(ert-deftest ruby-no-unknown-percent-literals () + ;; No folding of case. + (ruby-assert-face "%S{foo}" 4 nil) + (ruby-assert-face "%R{foo}" 4 nil)) + (ert-deftest ruby-add-log-current-method-examples () (let ((pairs '(("foo" . "#foo") ("C.foo" . ".foo")