diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index bf26497..e93f8b3 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el @@ -793,8 +793,8 @@ and `\\' when preceded by `?'." ;; (not (or (eolp) (looking-at "#") ;; (and (eq (car (nth 1 state)) ?{) ;; (looking-at "|")))))) - (or (not (eq ?/ c)) - (null (nth 0 (ruby-parse-region (or begin parse-start) (point))))) + ;; not a regexp or general delimited literal + (null (nth 0 (ruby-parse-region (or begin parse-start) (point)))) (or (not (eq ?| (char-after (point)))) (save-excursion (or (eolp) (forward-char -1)) @@ -1117,6 +1117,7 @@ See `add-log-current-defun-function'." "Syntactic keywords for Ruby mode. See `syntax-propertize-function'." (goto-char start) (ruby-syntax-propertize-heredoc end) + (ruby-syntax-general-delimiters-goto-beg) (funcall (syntax-propertize-rules ;; #{ }, #$hoge, #@foo are not comments @@ -1136,7 +1137,10 @@ See `add-log-current-defun-function'." ("^\\(=\\)begin\\_>" (1 "!")) ;; Handle here documents. ((concat ruby-here-doc-beg-re ".*\\(\n\\)") - (7 (prog1 "\"" (ruby-syntax-propertize-heredoc end))))) + (7 (prog1 "\"" (ruby-syntax-propertize-heredoc end)))) + ;; Handle percent literals: %w(), %q{}, etc. + ("\\(?:^\\|[[ \t\n<+(,=]\\)\\(%\\)[qQrswWx]?\\([[:punct:]]\\)" + (1 (prog1 "|" (ruby-syntax-propertize-general-delimiters end))))) (point) end)) (defun ruby-syntax-propertize-heredoc (limit) @@ -1162,6 +1166,41 @@ See `add-log-current-defun-function'." ;; Make extra sure we don't move back, lest we could fall into an ;; inf-loop. (if (< (point) start) (goto-char start)))))) + + (defun ruby-syntax-general-delimiters-goto-beg () + (let ((state (syntax-ppss))) + ;; Move to the start of the literal, in case it's multiline. + ;; TODO: determine the literal type more reliably here? + (when (eq t (nth 3 state)) + (goto-char (nth 8 state)) + (beginning-of-line)))) + + (defun ruby-syntax-propertize-general-delimiters (limit) + (goto-char (match-beginning 2)) + (let* ((op (char-after)) + (ops (char-to-string op)) + (cl (or (cdr (aref (syntax-table) op)) + (cdr (assoc op '((?< . ?>)))))) + parse-sexp-lookup-properties) + (ignore-errors + (if cl + (progn ; 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)) + ;; if we reached here, the closing delimiter was found + (put-text-property (1- (point)) (point) + 'syntax-table (string-to-syntax "|"))))) ) ;; For Emacsen where syntax-propertize-rules is not (yet) available, @@ -1206,6 +1245,10 @@ This should only be called after matching against `ruby-here-doc-end-re'." (4 (7 . ?/)) (6 (7 . ?/))) ("^=en\\(d\\)\\_>" 1 "!") + ;; general delimited string + ("\\(^\\|[[ \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: ;; @@ -1414,9 +1457,6 @@ See `font-lock-syntax-table'.") 1 font-lock-variable-name-face) '("\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+" 0 font-lock-variable-name-face) - ;; general delimited string - '("\\(^\\|[[ \t\n<+(,=]\\)\\(%[xrqQwW]?\\([^<[{(a-zA-Z0-9 \n]\\)[^\n\\\\]*\\(\\\\.[^\n\\\\]*\\)*\\(\\3\\)\\)" - (2 font-lock-string-face)) ;; constants '("\\(^\\|[^_]\\)\\b\\([A-Z]+\\(\\w\\|_\\)*\\)" 2 font-lock-type-face)