X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/066f3bc3f3d024b2e10ee11e09ae6aaa1003bbda..54fe3b6ec0557941c5759523b36bfdec21003f77:/lisp/progmodes/ruby-mode.el diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index fa94992ab7..e7b37acc3d 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el @@ -368,7 +368,7 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." (exp (exp1) (exp "," exp) (exp "=" exp) (id " @ " exp)) (exp1 (exp2) (exp2 "?" exp1 ":" exp1)) - (exp2 (exp3) (exp3 "." exp2)) + (exp2 (exp3) (exp3 "." exp3)) (exp3 ("def" insts "end") ("begin" insts-rescue-insts "end") ("do" insts "end") @@ -388,7 +388,7 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." (cases (exp "then" insts) (cases "when" cases) (insts "else" insts)) (expseq (exp) );;(expseq "," expseq) - (hashvals (id "=>" exp1) (hashvals "," hashvals)) + (hashvals (exp1 "=>" exp1) (hashvals "," hashvals)) (insts-rescue-insts (insts) (insts-rescue-insts "rescue" insts-rescue-insts) (insts-rescue-insts "ensure" insts-rescue-insts)) @@ -406,17 +406,18 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." '((right "=") (right "+=" "-=" "*=" "/=" "%=" "**=" "&=" "|=" "^=" "<<=" ">>=" "&&=" "||=") - (left ".." "...") - (left "+" "-") - (left "*" "/" "%" "**") + (nonassoc ".." "...") (left "&&" "||") - (left "^" "&" "|") (nonassoc "<=>") - (nonassoc ">" ">=" "<" "<=") (nonassoc "==" "===" "!=") (nonassoc "=~" "!~") + (nonassoc ">" ">=" "<" "<=") + (left "^" "&" "|") (left "<<" ">>") - (right ".")))))) + (left "+" "-") + (left "*" "/" "%") + (left "**") + (assoc ".")))))) (defun ruby-smie--bosp () (save-excursion (skip-chars-backward " \t") @@ -431,17 +432,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)) '("|" "||"))) @@ -449,8 +444,8 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." (member (save-excursion (ruby-smie--backward-token)) '("iuwu-mod" "and" "or"))) (save-excursion - (forward-comment 1) - (eq (char-after) ?.)))))) + (forward-comment (point-max)) + (looking-at "&?\\.")))))) (defun ruby-smie--redundant-do-p (&optional skip) (save-excursion @@ -485,12 +480,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))) @@ -511,11 +510,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))) @@ -539,6 +536,7 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." (line-end-position)) (ruby-smie--forward-token)) ;Fully redundant. (t ";"))) + ((equal tok "&.") ".") (t tok))))))))) (defun ruby-smie--backward-token () @@ -559,11 +557,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) @@ -586,6 +582,7 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." (line-end-position)) (ruby-smie--backward-token)) ;Fully redundant. (t ";"))) + ((equal tok "&.") ".") (t tok))))))) (defun ruby-smie--indent-to-stmt () @@ -633,19 +630,13 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." ;; because when `.' is inside the line, the ;; additional indentation from it looks out of place. ((smie-rule-parent-p ".") - (let (smie--parent) - (save-excursion - ;; Traverse up the parents until the parent is "." at - ;; indentation, or any other token. - (while (and (let ((parent (smie-indent--parent))) - (goto-char (cadr parent)) - (save-excursion - (unless (integerp (car parent)) (forward-char -1)) - (not (ruby-smie--bosp)))) - (progn - (setq smie--parent nil) - (smie-rule-parent-p ".")))) - (smie-rule-parent)))) + ;; Traverse up the call chain until the parent is not `.', + ;; or `.' at indentation, or at eol. + (while (and (not (ruby-smie--bosp)) + (equal (nth 2 (smie-backward-sexp ".")) ".") + (not (ruby-smie--bosp))) + (forward-char -1)) + (smie-indent-virtual)) (t (smie-rule-parent)))))) (`(:after . ,(or `"(" "[" "{")) ;; FIXME: Shouldn't this be the default behavior of @@ -665,7 +656,9 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." (`(:before . ".") (if (smie-rule-sibling-p) (and ruby-align-chained-calls 0) - ruby-indent-level)) + (smie-backward-sexp ".") + (cons 'column (+ (current-column) + ruby-indent-level)))) (`(:before . ,(or `"else" `"then" `"elsif" `"rescue" `"ensure")) (smie-rule-parent)) (`(:before . "when") @@ -894,12 +887,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-strictly -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-strictly -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. @@ -919,7 +918,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)) @@ -1152,7 +1151,7 @@ delimiter." ((looking-at "<<") (cond ((and (ruby-expr-beg 'heredoc) - (looking-at "<<\\(-\\)?\\(\\([\"'`]\\)\\([^\n]+?\\)\\3\\|\\(?:\\sw\\|\\s_\\)+\\)")) + (looking-at "<<\\([-~]\\)?\\(\\([\"'`]\\)\\([^\n]+?\\)\\3\\|\\(?:\\sw\\|\\s_\\)+\\)")) (setq re (regexp-quote (or (match-string 4) (match-string 2)))) (if (match-beginning 1) (setq re (concat "\\s *" re))) (let* ((id-end (goto-char (match-end 0))) @@ -1375,7 +1374,7 @@ delimiter." (goto-char ruby-indent-point) (beginning-of-line) (skip-syntax-forward " ") - (if (looking-at "\\.[^.]") + (if (looking-at "\\.[^.]\\|&\\.") (+ indent ruby-indent-level) indent)))) @@ -1800,9 +1799,9 @@ If the result is do-end block, it will always be multiline." (content (buffer-substring-no-properties (1+ min) (1- max)))) (setq content - (if (equal string-quote "\"") - (replace-regexp-in-string "\\\\\"" "\"" (replace-regexp-in-string "\\([^\\\\]\\)'" "\\1\\\\'" content)) - (replace-regexp-in-string "\\\\'" "'" (replace-regexp-in-string "\\([^\\\\]\\)\"" "\\1\\\\\"" content)))) + (if (equal string-quote "'") + (replace-regexp-in-string "\\\\\"" "\"" (replace-regexp-in-string "\\(\\`\\|[^\\\\]\\)'" "\\1\\\\'" content)) + (replace-regexp-in-string "\\\\'" "'" (replace-regexp-in-string "\\(\\`\\|[^\\\\]\\)\"" "\\1\\\\\"" content)))) (let ((orig-point (point))) (delete-region min max) (insert @@ -1848,17 +1847,24 @@ 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 (unless (nth 8 (syntax-ppss (match-beginning 3))) + (goto-char (match-end 0)) + (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) '(?@ ?$)))) @@ -1893,15 +1899,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 @@ -1916,7 +1925,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 @@ -1944,35 +1953,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. @@ -2164,28 +2171,25 @@ 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__$" - ;; Variables. + ;; Singleton objects. (,(concat ruby-font-lock-keyword-beg-re "\\_<\\(nil\\|true\\|false\\)\\_>") 1 font-lock-constant-face) ;; Keywords that evaluate to certain values. ("\\_<__\\(?:LINE\\|ENCODING\\|FILE\\)__\\_>" (0 font-lock-builtin-face)) - ;; Symbols with symbol characters. - ("\\(^\\|[^:]\\)\\(:@?\\(?:\\w\\|_\\)+\\)\\([!?=]\\)?" + ;; Symbols. + ("\\(^\\|[^:]\\)\\(:@\\{0,2\\}\\(?:\\sw\\|\\s_\\)+\\)" (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" @@ -2225,7 +2229,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