(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")
(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))
'((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")
(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))
'("|" "||")))
(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
"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)))
(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)))
(line-end-position))
(ruby-smie--forward-token)) ;Fully redundant.
(t ";")))
+ ((equal tok "&.") ".")
(t tok)))))))))
(defun ruby-smie--backward-token ()
" @ ")
(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)
(line-end-position))
(ruby-smie--backward-token)) ;Fully redundant.
(t ";")))
+ ((equal tok "&.") ".")
(t tok)))))))
(defun ruby-smie--indent-to-stmt ()
;; 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
(`(: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")
((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)))
(goto-char ruby-indent-point)
(beginning-of-line)
(skip-syntax-forward " ")
- (if (looking-at "\\.[^.]")
+ (if (looking-at "\\.[^.]\\|&\\.")
(+ indent ruby-indent-level)
indent))))
(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
(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.
(ignore
(goto-char (match-end 1)))
- (string-to-syntax "\\"))))
- ;; Part of symbol when at the end of a method name.
+ (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) '(?@ ?$))))
'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"
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