From 7ffd37219b311035fd346a126d7799cb53c6c73d Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Sat, 2 Nov 2013 05:10:10 +0400 Subject: [PATCH] * lisp/progmodes/ruby-mode.el Use `syntax-propertize-function' unconditionally. Remove now unnecessary forward declarations. Remove XEmacs-specific setup. (ruby-here-doc-end-re, ruby-here-doc-beg-match) (ruby-font-lock-syntactic-keywords) (ruby-comment-beg-syntax, ruby-in-here-doc-p) (ruby-here-doc-find-end, ruby-here-doc-beg-syntax) (ruby-here-doc-end-syntax): Remove. (ruby-mode): Don't check whether `syntax-propertize-rules' is defined as function. --- lisp/ChangeLog | 13 + lisp/progmodes/ruby-mode.el | 521 ++++++++++++------------------------ 2 files changed, 185 insertions(+), 349 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index dc8cc973423..3de7afba477 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,16 @@ +2013-11-02 Dmitry Gutov + + * progmodes/ruby-mode.el Use `syntax-propertize-function' + unconditionally. Remove now unnecessary forward declarations. + Remove XEmacs-specific setup. + (ruby-here-doc-end-re, ruby-here-doc-beg-match) + (ruby-font-lock-syntactic-keywords) + (ruby-comment-beg-syntax, ruby-in-here-doc-p) + (ruby-here-doc-find-end, ruby-here-doc-beg-syntax) + (ruby-here-doc-end-syntax): Remove. + (ruby-mode): Don't check whether `syntax-propertize-rules' is + defined as function. + 2013-11-02 Bozhidar Batsov * progmodes/ruby-mode.el (ruby-mode-variables, ruby-mode): Use `setq-local'. diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index c8d7169ca58..75c59ebe1fd 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el @@ -1515,349 +1515,182 @@ If the result is do-end block, it will always be multiline." (ruby-do-end-to-brace beg end))) (goto-char start)))) -(declare-function ruby-syntax-propertize-heredoc "ruby-mode" (limit)) -(declare-function ruby-syntax-enclosing-percent-literal "ruby-mode" (limit)) -(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)) -(declare-function ruby-syntax-propertize-function "ruby-mode" (start end)) - -(if (eval-when-compile (fboundp #'syntax-propertize-rules)) - ;; New code that works independently from font-lock. - (progn - (eval-and-compile - (defconst ruby-percent-literal-beg-re - "\\(%\\)[qQrswWxIi]?\\([[:punct:]]\\)" - "Regexp to match the beginning of percent literal.") - - (defconst ruby-syntax-methods-before-regexp - '("gsub" "gsub!" "sub" "sub!" "scan" "split" "split!" "index" "match" - "assert_match" "Given" "Then" "When") - "Methods that can take regexp as the first argument. +(eval-and-compile + (defconst ruby-percent-literal-beg-re + "\\(%\\)[qQrswWxIi]?\\([[:punct:]]\\)" + "Regexp to match the beginning of percent literal.") + + (defconst ruby-syntax-methods-before-regexp + '("gsub" "gsub!" "sub" "sub!" "scan" "split" "split!" "index" "match" + "assert_match" "Given" "Then" "When") + "Methods that can take regexp as the first argument. It will be properly highlighted even when the call omits parens.") - (defvar ruby-syntax-before-regexp-re - (concat - ;; Special tokens that can't be followed by a division operator. - "\\(^\\|[[=(,~;<>]" - ;; Distinguish ternary operator tokens. - ;; FIXME: They don't really have to be separated with spaces. - "\\|[?:] " - ;; Control flow keywords and operators following bol or whitespace. - "\\|\\(?:^\\|\\s \\)" - (regexp-opt '("if" "elsif" "unless" "while" "until" "when" "and" - "or" "not" "&&" "||")) - ;; Method name from the list. - "\\|\\_<" - (regexp-opt ruby-syntax-methods-before-regexp) - "\\)\\s *") - "Regexp to match text that can be followed by a regular expression.")) - - (defun ruby-syntax-propertize-function (start end) - "Syntactic keywords for Ruby mode. See `syntax-propertize-function'." - (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 character literals (one-char strings in 1.9+). - ("\\([?$]\\)[#\"'`]" - (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 (or (nth 8 (save-excursion - (syntax-ppss (match-beginning 0)))) - (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)) - (res '())) - (when (eq ?\n (nth 3 ppss)) - (save-excursion - (goto-char (nth 8 ppss)) - (beginning-of-line) - (while (re-search-forward ruby-here-doc-beg-re - (line-end-position) t) - (unless (ruby-singleton-class-p (match-beginning 0)) - (push (concat (ruby-here-doc-end-match) "\n") res)))) - (save-excursion - ;; With multiple openers on the same line, we don't know in which - ;; part `start' is, so we have to go back to the beginning. - (when (cdr res) - (goto-char (nth 8 ppss)) - (setq res (nreverse res))) - (while (and res (re-search-forward (pop res) limit 'move)) - (if (null res) - (put-text-property (1- (point)) (point) - 'syntax-table (string-to-syntax "\"")))) - ;; End up at bol following the heredoc openers. - ;; Propertize expression expansions from this point forward. - )))) - - (defun ruby-syntax-enclosing-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)) - (when (looking-at ruby-percent-literal-beg-re) - (ruby-syntax-propertize-percent-literal limit)) - (when (< (point) start) (goto-char start))))) - - (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))))))) - - (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)) - (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) - (while (re-search-forward "[\"`]" end 'move) - (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 "%\\(?:[QWrxI]\\|\\W\\)"))))))) - - (defun ruby-syntax-propertize-expansions (start end) - (save-excursion - (goto-char start) - (while (re-search-forward ruby-expression-expansion-re end 'move) - (ruby-syntax-propertize-expansion)))) - ) - - ;; For Emacsen where syntax-propertize-rules is not (yet) available, - ;; fallback on the old font-lock-syntactic-keywords stuff. - - (defconst ruby-here-doc-end-re - "^\\([ \t]+\\)?\\(.*\\)\\(\n\\)" - "Regexp to match the end of heredocs. - -This will actually match any line with one or more characters. -It's useful in that it divides up the match string so that -`ruby-here-doc-beg-match' can search for the beginning of the heredoc.") - - (defun ruby-here-doc-beg-match () - "Return a regexp to find the beginning of a heredoc. - -This should only be called after matching against `ruby-here-doc-end-re'." - (let ((contents (concat - (regexp-quote (concat (match-string 2) (match-string 3))) - (if (string= (match-string 3) "_") "\\B" "\\b")))) - (concat "<<" - (let ((match (match-string 1))) - (if (and match (> (length match) 0)) - (concat "\\(?:-\\([\"']?\\)\\|\\([\"']\\)" - (match-string 1) "\\)" - contents "\\(\\1\\|\\2\\)") - (concat "-?\\([\"']\\|\\)" contents "\\1")))))) - - (defconst ruby-font-lock-syntactic-keywords - `( - ;; the last $', $", $` in the respective string is not variable - ;; the last ?', ?", ?` in the respective string is not ascii code - ("\\(^\\|[\[ \t\n<+\(,=]\\)\\(['\"`]\\)\\(\\\\.\\|\\2\\|[^'\"`\n\\\\]\\)*?\\\\?[?$]\\(\\2\\)" - (2 (7 . nil)) - (4 (7 . nil))) - ;; $' $" $` .... are variables - ;; ?' ?" ?` are ascii codes - ("\\(^\\|[^\\\\]\\)\\(\\\\\\\\\\)*[?$]\\([#\"'`]\\)" 3 (1 . nil)) - ;; regexps - ("\\(^\\|[[=(,~?:;<>]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)" - (4 (7 . ?/)) - (6 (7 . ?/))) - ("^=en\\(d\\)\\_>" 1 "!") - ;; Percent literal. - ("\\(^\\|[[ \t\n<+(,=]\\)\\(%[xrqQwW]?\\([^<[{(a-zA-Z0-9 \n]\\)[^\n\\\\]*\\(\\\\.[^\n\\\\]*\\)*\\(\\3\\)\\)" - (3 "\"") - (5 "\"")) - ("^\\(=\\)begin\\_>" 1 (ruby-comment-beg-syntax)) - ;; Currently, the following case is highlighted incorrectly: - ;; - ;; <]" + ;; Distinguish ternary operator tokens. + ;; FIXME: They don't really have to be separated with spaces. + "\\|[?:] " + ;; Control flow keywords and operators following bol or whitespace. + "\\|\\(?:^\\|\\s \\)" + (regexp-opt '("if" "elsif" "unless" "while" "until" "when" "and" + "or" "not" "&&" "||")) + ;; Method name from the list. + "\\|\\_<" + (regexp-opt ruby-syntax-methods-before-regexp) + "\\)\\s *") + "Regexp to match text that can be followed by a regular expression.")) + +(defun ruby-syntax-propertize-function (start end) + "Syntactic keywords for Ruby mode. See `syntax-propertize-function'." + (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 character literals (one-char strings in 1.9+). + ("\\([?$]\\)[#\"'`]" + (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 (or (nth 8 (save-excursion + (syntax-ppss (match-beginning 0)))) + (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)) + (res '())) + (when (eq ?\n (nth 3 ppss)) + (save-excursion + (goto-char (nth 8 ppss)) (beginning-of-line) - (catch 'found-beg - (while (and (re-search-backward ruby-here-doc-beg-re nil t) - (not (ruby-singleton-class-p))) - (if (not (or (ruby-in-ppss-context-p 'anything) - (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 (point-at-eol)) - (case-fold-search nil) - ;; 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 () - "Return the syntax cell for a line that may begin a heredoc. -See the definition of `ruby-font-lock-syntactic-keywords'. - -This sets the syntax cell for the newline ending the line -containing the heredoc beginning so that cases where multiple -heredocs are started on one line are handled correctly." - (save-excursion - (goto-char (match-beginning 0)) - (unless (or (ruby-in-ppss-context-p 'non-heredoc) - (ruby-in-here-doc-p)) - (string-to-syntax "\"")))) - - (defun ruby-here-doc-end-syntax () - "Return the syntax cell for a line that may end a heredoc. -See the definition of `ruby-font-lock-syntactic-keywords'." - (let ((pss (syntax-ppss)) (case-fold-search nil)) - ;; If we aren't in a string, we definitely aren't ending a heredoc, - ;; so we can just give up. - ;; This means we aren't doing a full-document search - ;; every time we enter a character. - (when (ruby-in-ppss-context-p 'heredoc pss) + (while (re-search-forward ruby-here-doc-beg-re + (line-end-position) t) + (unless (ruby-singleton-class-p (match-beginning 0)) + (push (concat (ruby-here-doc-end-match) "\n") res)))) + (save-excursion + ;; With multiple openers on the same line, we don't know in which + ;; part `start' is, so we have to go back to the beginning. + (when (cdr res) + (goto-char (nth 8 ppss)) + (setq res (nreverse res))) + (while (and res (re-search-forward (pop res) limit 'move)) + (if (null res) + (put-text-property (1- (point)) (point) + 'syntax-table (string-to-syntax "\"")))) + ;; End up at bol following the heredoc openers. + ;; Propertize expression expansions from this point forward. + )))) + +(defun ruby-syntax-enclosing-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)) + (when (looking-at ruby-percent-literal-beg-re) + (ruby-syntax-propertize-percent-literal limit)) + (when (< (point) start) (goto-char start))))) + +(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))))))) + +(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)) + (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) + (while (re-search-forward "[\"`]" end 'move) + (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 pss)) ; Go to the beginning of heredoc. - (let ((eol (point))) - (beginning-of-line) - (if (and (re-search-forward (ruby-here-doc-beg-match) eol t) ; If there is a heredoc that matches this line... - (not (ruby-in-ppss-context-p 'anything)) ; 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)))) - (string-to-syntax "\""))))))) + (goto-char (nth 8 parse-state)) + (looking-at "%\\(?:[QWrxI]\\|\\W\\)"))))))) - (unless (functionp 'syntax-ppss) - (defun syntax-ppss (&optional pos) - (parse-partial-sexp (point-min) (or pos (point))))) - ) +(defun ruby-syntax-propertize-expansions (start end) + (save-excursion + (goto-char start) + (while (re-search-forward ruby-expression-expansion-re end 'move) + (ruby-syntax-propertize-expansion)))) (defun ruby-in-ppss-context-p (context &optional ppss) (let ((ppss (or ppss (syntax-ppss (point))))) @@ -1880,14 +1713,6 @@ See the definition of `ruby-font-lock-syntactic-keywords'." "context name `" (symbol-name context) "' is unknown")))) t))) -(if (featurep 'xemacs) - (put 'ruby-mode 'font-lock-defaults - '((ruby-font-lock-keywords) - nil nil nil - beginning-of-line - (font-lock-syntactic-keywords - . ruby-font-lock-syntactic-keywords)))) - (defvar ruby-font-lock-syntax-table (let ((tbl (copy-syntax-table ruby-mode-syntax-table))) (modify-syntax-entry ?_ "w" tbl) @@ -2082,9 +1907,7 @@ The variable `ruby-indent-level' controls the amount of indentation. (setq-local font-lock-keywords ruby-font-lock-keywords) (setq-local font-lock-syntax-table ruby-font-lock-syntax-table) - (if (eval-when-compile (fboundp 'syntax-propertize-rules)) - (setq-local syntax-propertize-function #'ruby-syntax-propertize-function) - (setq-local font-lock-syntactic-keywords ruby-font-lock-syntactic-keywords))) + (setq-local syntax-propertize-function #'ruby-syntax-propertize-function)) ;;; Invoke ruby-mode when appropriate -- 2.39.2