;;; ruby-mode.el --- Major mode for editing Ruby files
-;; Copyright (C) 1994-2014 Free Software Foundation, Inc.
+;; Copyright (C) 1994-2015 Free Software Foundation, Inc.
;; Authors: Yukihiro Matsumoto
;; Nobuyoshi Nakada
"Regexp to match the beginning of a heredoc.")
(defconst ruby-expression-expansion-re
- "\\(?:[^\\]\\|\\=\\)\\(\\\\\\\\\\)*\\(#\\({[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\|\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+\\)\\)"))
+ "\\(?:[^\\]\\|\\=\\)\\(\\\\\\\\\\)*\\(#\\({[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\|\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+\\|\\$[^a-zA-Z \n]\\)\\)"))
(defun ruby-here-doc-end-match ()
"Return a regexp to find the end of a heredoc.
(define-key map (kbd "M-C-p") 'ruby-beginning-of-block)
(define-key map (kbd "M-C-n") 'ruby-end-of-block)
(define-key map (kbd "C-c {") 'ruby-toggle-block)
+ (define-key map (kbd "C-c '") 'ruby-toggle-string-quotes)
map)
"Keymap used in Ruby mode.")
["End of Block" ruby-end-of-block t]
["Toggle Block" ruby-toggle-block t]
"--"
+ ["Toggle String Quotes" ruby-toggle-string-quotes t]
+ "--"
["Backward Sexp" ruby-backward-sexp
:visible (not ruby-use-smie)]
["Backward Sexp" backward-sexp
;; because we want to reject hanging tokens at bol, too.
(unless (or (eolp) (forward-comment 1))
(cons 'column (current-column)))))
+ (`(:before . " @ ")
+ (save-excursion
+ (skip-chars-forward " \t")
+ (cons 'column (current-column))))
(`(:before . "do") (ruby-smie--indent-to-stmt))
(`(:before . ".")
(if (smie-rule-sibling-p)
:forward-token #'ruby-smie--forward-token
:backward-token #'ruby-smie--backward-token)
(setq-local indent-line-function 'ruby-indent-line))
- (setq-local require-final-newline t)
(setq-local comment-start "# ")
(setq-local comment-end "")
(setq-local comment-column ruby-comment-column)
(ruby-do-end-to-brace beg end)))
(goto-char start))))
+(defun ruby--string-region ()
+ "Return region for string at point."
+ (let ((state (syntax-ppss)))
+ (when (memq (nth 3 state) '(?' ?\"))
+ (save-excursion
+ (goto-char (nth 8 state))
+ (forward-sexp)
+ (list (nth 8 state) (point))))))
+
+(defun ruby-string-at-point-p ()
+ "Check if cursor is at a string or not."
+ (ruby--string-region))
+
+(defun ruby--inverse-string-quote (string-quote)
+ "Get the inverse string quoting for STRING-QUOTE."
+ (if (equal string-quote "\"") "'" "\""))
+
+(defun ruby-toggle-string-quotes ()
+ "Toggle string literal quoting between single and double."
+ (interactive)
+ (when (ruby-string-at-point-p)
+ (let* ((region (ruby--string-region))
+ (min (nth 0 region))
+ (max (nth 1 region))
+ (string-quote (ruby--inverse-string-quote (buffer-substring-no-properties min (1+ min))))
+ (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))))
+ (let ((orig-point (point)))
+ (delete-region min max)
+ (insert
+ (format "%s%s%s" string-quote content string-quote))
+ (goto-char orig-point)))))
+
(eval-and-compile
(defconst ruby-percent-literal-beg-re
"\\(%\\)[qQrswWxIi]?\\([[:punct:]]\\)"
;; $' $" $` .... 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))))
+ (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.
("[!?]"
(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) '(?@ ?$))))
"include"
"module_function"
"prepend"
+ "private_class_method"
+ "private_constant"
+ "public_class_method"
+ "public_constant"
"refine"
"using")
'symbols))
1 font-lock-variable-name-face)
;; Keywords that evaluate to certain values.
("\\_<__\\(?:LINE\\|ENCODING\\|FILE\\)__\\_>"
- (0 font-lock-variable-name-face))
+ (0 font-lock-builtin-face))
;; Symbols.
("\\(^\\|[^:]\\)\\(:\\([-+~]@?\\|[/%&|^`]\\|\\*\\*?\\|<\\(<\\|=>?\\)?\\|>[>=]?\\|===?\\|=~\\|![~=]?\\|\\[\\]=?\\|@?\\(\\w\\|_\\)+\\([!?=]\\|\\b_*\\)\\|#{[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\)\\)"
2 font-lock-constant-face)
- ;; Variables.
- ("\\(\\$\\([^a-zA-Z0-9 \n]\\|[0-9]\\)\\)\\W"
- 1 font-lock-variable-name-face)
+ ;; Special globals.
+ (,(concat "\\$\\(?:[:\"!@;,/\\._><\\$?~=*&`'+0-9]\\|-[0adFiIlpvw]\\|"
+ (regexp-opt '("LOAD_PATH" "LOADED_FEATURES" "PROGRAM_NAME"
+ "ERROR_INFO" "ERROR_POSITION"
+ "FS" "FIELD_SEPARATOR"
+ "OFS" "OUTPUT_FIELD_SEPARATOR"
+ "RS" "INPUT_RECORD_SEPARATOR"
+ "ORS" "OUTPUT_RECORD_SEPARATOR"
+ "NR" "INPUT_LINE_NUMBER"
+ "LAST_READ_LINE" "DEFAULT_OUTPUT" "DEFAULT_INPUT"
+ "PID" "PROCESS_ID" "CHILD_STATUS"
+ "LAST_MATCH_INFO" "IGNORECASE"
+ "ARGV" "MATCH" "PREMATCH" "POSTMATCH"
+ "LAST_PAREN_MATCH" "stdin" "stdout" "stderr"
+ "DEBUG" "FILENAME" "VERBOSE" "SAFE" "CLASSPATH"
+ "JRUBY_VERSION" "JRUBY_REVISION" "ENV_JAVA"))
+ "\\_>\\)")
+ 0 font-lock-builtin-face)
("\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+"
0 font-lock-variable-name-face)
;; Constants.
(ruby-match-expression-expansion
2 font-lock-variable-name-face t)
;; Negation char.
- ("[^[:alnum:]_]\\(!\\)[^=]"
+ ("\\(?:^\\|[^[:alnum:]_]\\)\\(!+\\)[^=~]"
1 font-lock-negation-char-face)
;; Character literals.
;; FIXME: Support longer escape sequences.
("\\_<\\?\\\\?\\S " 0 font-lock-string-face)
+ ;; Regexp options.
+ ("\\(?:\\s|\\|/\\)\\([imxo]+\\)"
+ 1 (when (save-excursion
+ (let ((state (syntax-ppss (match-beginning 0))))
+ (and (nth 3 state)
+ (or (eq (char-after) ?/)
+ (progn
+ (goto-char (nth 8 state))
+ (looking-at "%r"))))))
+ font-lock-preprocessor-face))
)
"Additional expressions to highlight in Ruby mode.")
(add-to-list 'auto-mode-alist
(cons (purecopy (concat "\\(?:\\."
"rb\\|ru\\|rake\\|thor"
- "\\|jbuilder\\|gemspec\\|podspec"
+ "\\|jbuilder\\|rabl\\|gemspec\\|podspec"
"\\|/"
"\\(?:Gem\\|Rake\\|Cap\\|Thor"
+ "\\|Puppet\\|Berks"
"\\|Vagrant\\|Guard\\|Pod\\)file"
"\\)\\'")) 'ruby-mode))