X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/c69aef14ddc150da07dcd3527b5a9fb99d6cef34..63efcc268635dea78c6bd80749eae4ee2c72d717:/lisp/progmodes/ruby-mode.el diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index fb942b34dd..d237074197 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el @@ -420,7 +420,10 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." (defun ruby-smie--bosp () (save-excursion (skip-chars-backward " \t") - (or (bolp) (memq (char-before) '(?\; ?=))))) + (or (and (bolp) + ;; Newline is escaped. + (not (eq (char-before (1- (point))) ?\\))) + (memq (char-before) '(?\; ?=))))) (defun ruby-smie--implicit-semi-p () (save-excursion @@ -428,17 +431,11 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." (not (or (bolp) (memq (char-before) '(?\[ ?\()) (and (memq (char-before) - '(?\; ?- ?+ ?* ?/ ?: ?. ?, ?\\ ?& ?> ?< ?% ?~ ?^)) - ;; Not a binary operator symbol. - (not (eq (char-before (1- (point))) ?:)) - ;; Not the end of a regexp or a percent literal. - (not (memq (car (syntax-after (1- (point)))) '(7 15)))) - (and (eq (char-before) ?\?) - (equal (save-excursion (ruby-smie--backward-token)) "?")) - (and (eq (char-before) ?=) - ;; Not a symbol :==, :!=, or a foo= method. - (string-match "\\`\\s." (save-excursion - (ruby-smie--backward-token)))) + '(?\; ?- ?+ ?* ?/ ?: ?. ?, ?\\ ?& ?> ?< ?% ?~ ?^ ?= ??)) + ;; Not a binary operator symbol like :+ or :[]=. + ;; Or a (method or symbol) name ending with ?. + ;; Or the end of a regexp or a percent literal. + (not (memq (car (syntax-after (1- (point)))) '(3 7 15)))) (and (eq (char-before) ?|) (member (save-excursion (ruby-smie--backward-token)) '("|" "||"))) @@ -482,12 +479,16 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." "else" "elsif" "do" "end" "and") 'symbols)))) (memq (car (syntax-after pos)) '(7 15)) - (looking-at "[([]\\|[-+!~]\\sw\\|:\\(?:\\sw\\|\\s.\\)"))))) + (looking-at "[([]\\|[-+!~:]\\(?:\\sw\\|\\s_\\)"))))) -(defun ruby-smie--at-dot-call () +(defun ruby-smie--before-method-name () + ;; Only need to be accurate when method has keyword name. (and (eq ?w (char-syntax (following-char))) - (eq (char-before) ?.) - (not (eq (char-before (1- (point))) ?.)))) + (or + (and + (eq (char-before) ?.) + (not (eq (char-before (1- (point))) ?.))) + (looking-back "^\\s *def\\s +\\=" (line-beginning-position))))) (defun ruby-smie--forward-token () (let ((pos (point))) @@ -508,11 +509,9 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." (save-excursion (ruby-smie--args-separator-p (prog1 (point) (goto-char pos))))) " @ ") - ((looking-at ":\\s.+") - (goto-char (match-end 0)) (match-string 0)) ;bug#15208. ((looking-at "\\s\"") "") ;A string. (t - (let ((dot (ruby-smie--at-dot-call)) + (let ((dot (ruby-smie--before-method-name)) (tok (smie-default-forward-token))) (when dot (setq tok (concat "." tok))) @@ -556,11 +555,9 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." " @ ") (t (let ((tok (smie-default-backward-token)) - (dot (ruby-smie--at-dot-call))) + (dot (ruby-smie--before-method-name))) (when dot (setq tok (concat "." tok))) - (when (and (eq ?: (char-before)) (string-match "\\`\\s." tok)) - (forward-char -1) (setq tok (concat ":" tok))) ;; bug#15208. (cond ((member tok '("unless" "if" "while" "until")) (if (ruby-smie--bosp) @@ -669,7 +666,7 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." ;; Align to the previous `when', but look up the virtual ;; indentation of `case'. (if (smie-rule-sibling-p) 0 (smie-rule-parent))) - (`(:after . ,(or "=" "iuwu-mod" "+" "-" "*" "/" "&&" "||" "%" "**" "^" "&" + (`(:after . ,(or "=" "+" "-" "*" "/" "&&" "||" "%" "**" "^" "&" "<=>" ">" "<" ">=" "<=" "==" "===" "!=" "<<" ">>" "+=" "-=" "*=" "/=" "%=" "**=" "&=" "|=" "^=" "|" "<<=" ">>=" "&&=" "||=" "and" "or")) @@ -682,6 +679,8 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." (if (ruby-smie--indent-to-stmt-p token) (ruby-smie--indent-to-stmt) (cons 'column (current-column))))) + (`(:before . "iuwu-mod") + (smie-rule-parent ruby-indent-level)) )) (defun ruby--at-indentation-p (&optional point) @@ -889,12 +888,18 @@ and `\\' when preceded by `?'." ((and (eq c ?:) (or (not b) (eq (char-syntax b) ? )))) ((eq c ?\\) (eq b ??))))) -(defun ruby-singleton-class-p (&optional pos) +(defun ruby-verify-heredoc (&optional pos) (save-excursion (when pos (goto-char pos)) - (forward-word -1) - (and (or (bolp) (not (eq (char-before (point)) ?_))) - (looking-at ruby-singleton-class-re)))) + ;; Not right after a symbol or prefix character. + ;; Method names are only allowed when separated by + ;; whitespace. Not a limitation in Ruby, but it's hard for + ;; us to do better. + (when (not (memq (car (syntax-after (1- (point)))) '(2 3 6 10))) + (or (not (memq (char-before) '(?\s ?\t))) + (ignore (forward-word -1)) + (eq (char-before) ?_) + (not (looking-at ruby-singleton-class-re)))))) (defun ruby-expr-beg (&optional option) "Check if point is possibly at the beginning of an expression. @@ -914,7 +919,7 @@ Can be one of `heredoc', `modifier', `expr-qstr', `expr-re'." nil) ((looking-at ruby-operator-re)) ((eq option 'heredoc) - (and (< space 0) (not (ruby-singleton-class-p start)))) + (and (< space 0) (ruby-verify-heredoc start))) ((or (looking-at "[\\[({,;]") (and (looking-at "[!?]") (or (not (eq option 'modifier)) @@ -1842,17 +1847,22 @@ It will be properly highlighted even when the call omits parens.") (syntax-propertize-rules ;; $' $" $` .... are variables. ;; ?' ?" ?` are character literals (one-char strings in 1.9+). - ("\\([?$]\\)[#\"'`]" + ("\\([?$]\\)[#\"'`:?]" (1 (if (save-excursion (nth 3 (syntax-ppss (match-beginning 0)))) ;; Within a string, skip. - (goto-char (match-end 1)) - (string-to-syntax "\\")))) - ;; Part of symbol when at the end of a method name. + (ignore + (goto-char (match-end 1))) + (put-text-property (match-end 1) (match-end 0) + 'syntax-table (string-to-syntax "_")) + (string-to-syntax "'")))) + ;; Symbols with special characters. + ("\\(^\\|[^:]\\)\\(:\\([-+~]@?\\|[/%&|^`]\\|\\*\\*?\\|<\\(<\\|=>?\\)?\\|>[>=]?\\|===?\\|=~\\|![~=]?\\|\\[\\]=?\\)\\)" + (3 (string-to-syntax "_"))) + ;; Part of method name when at the end of it. ("[!?]" (0 (unless (save-excursion (or (nth 8 (syntax-ppss (match-beginning 0))) - (eq (char-before) ?:) (let (parse-sexp-lookup-properties) (zerop (skip-syntax-backward "w_"))) (memq (preceding-char) '(?@ ?$)))) @@ -1887,15 +1897,18 @@ It will be properly highlighted even when the call omits parens.") ("^\\(=\\)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))) + (7 (when (and (not (nth 8 (save-excursion + (syntax-ppss (match-beginning 0))))) + (ruby-verify-heredoc (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))))) + ((concat "\\(?:^\\|[[ \t\n<+(,=*]\\)" ruby-percent-literal-beg-re) + (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 @@ -1910,7 +1923,7 @@ It will be properly highlighted even when the call omits parens.") (beginning-of-line) (while (re-search-forward ruby-here-doc-beg-re (line-end-position) t) - (unless (ruby-singleton-class-p (match-beginning 0)) + (when (ruby-verify-heredoc (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 @@ -1938,35 +1951,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. @@ -2158,7 +2169,7 @@ See `font-lock-syntax-table'.") (1 font-lock-builtin-face)) ;; Here-doc beginnings. (,ruby-here-doc-beg-re - (0 (unless (ruby-singleton-class-p (match-beginning 0)) + (0 (when (ruby-verify-heredoc (match-beginning 0)) 'font-lock-string-face))) ;; Perl-ish keywords. "\\_<\\(?:BEGIN\\|END\\)\\_>\\|^__END__$" @@ -2169,17 +2180,14 @@ See `font-lock-syntax-table'.") ;; Keywords that evaluate to certain values. ("\\_<__\\(?:LINE\\|ENCODING\\|FILE\\)__\\_>" (0 font-lock-builtin-face)) - ;; Symbols with symbol characters. + ;; Symbols. ("\\(^\\|[^:]\\)\\(:@?\\(?:\\w\\|_\\)+\\)\\([!?=]\\)?" (2 font-lock-constant-face) (3 (unless (and (eq (char-before (match-end 3)) ?=) (eq (char-after (match-end 3)) ?>)) - ;; bug#18466 + ;; bug#18644 font-lock-constant-face) nil t)) - ;; Symbols with special characters. - ("\\(^\\|[^:]\\)\\(:\\([-+~]@?\\|[/%&|^`]\\|\\*\\*?\\|<\\(<\\|=>?\\)?\\|>[>=]?\\|===?\\|=~\\|![~=]?\\|\\[\\]=?\\|#{[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\)\\)" - 2 font-lock-constant-face) ;; Special globals. (,(concat "\\$\\(?:[:\"!@;,/\\._><\\$?~=*&`'+0-9]\\|-[0adFiIlpvw]\\|" (regexp-opt '("LOAD_PATH" "LOADED_FEATURES" "PROGRAM_NAME" @@ -2219,7 +2227,7 @@ See `font-lock-syntax-table'.") 1 font-lock-negation-char-face) ;; Character literals. ;; FIXME: Support longer escape sequences. - ("\\_<\\?\\\\?\\S " 0 font-lock-string-face) + ("\\?\\\\?\\_<.\\_>" 0 font-lock-string-face) ;; Regexp options. ("\\(?:\\s|\\|/\\)\\([imxo]+\\)" 1 (when (save-excursion