summaryrefslogtreecommitdiff
path: root/rust-mode.el
diff options
context:
space:
mode:
Diffstat (limited to 'rust-mode.el')
-rw-r--r--rust-mode.el203
1 files changed, 77 insertions, 126 deletions
diff --git a/rust-mode.el b/rust-mode.el
index 80106b8..780474f 100644
--- a/rust-mode.el
+++ b/rust-mode.el
@@ -362,6 +362,10 @@ See `prettify-symbols-compose-predicate'."
"bool"
"str" "char"))
+(defconst rust-expression-introducers
+ '("if" "while" "match" "return" "box" "in")
+ "List of Rust keywords that are always followed by expressions.")
+
(defconst rust-number-with-type
(eval-when-compile
(concat
@@ -526,22 +530,36 @@ symbols."
symbols)))))
(defun rust-looking-back-ident ()
- "Non-nil if we are looking backwards at a valid rust identifier."
- (let ((beg-of-symbol (save-excursion (forward-thing 'symbol -1) (point))))
- (looking-back rust-re-ident beg-of-symbol)))
+ "Non-nil if we are looking backwards at a valid rust identifier.
+If we are, regexp match 0 is the identifier."
+ (let ((outer-point (point)))
+ (save-excursion
+ (forward-thing 'symbol -1)
+ (and (looking-at rust-re-ident)
+ (eq (match-end 0) outer-point)))))
(defun rust-looking-back-macro ()
- "Non-nil if looking back at an ident followed by a !
-
-This is stricter than rust syntax which allows a space between
-the ident and the ! symbol. If this space is allowed, then we
-would also need a keyword check to avoid `if !(condition)` being
-seen as a macro."
- (if (> (- (point) (point-min)) 1)
- (save-excursion
- (backward-char)
- (and (= ?! (char-after))
- (rust-looking-back-ident)))))
+ "Non-nil if looking back at a potential macro name followed by a \"!\".
+If we are, regexp match 0 is the macro name."
+ (save-excursion
+ ;; Look past whitespace and line breaks.
+ ;; > is okay because we only use it for \n and \r, not "*/"
+ (skip-syntax-backward "->")
+ (when (eq (char-before) ?!)
+ (forward-char -1)
+ (skip-syntax-backward "->")
+ (when (rust-looking-back-ident)
+ (let ((ident (match-string 0)))
+ (not (member ident rust-expression-introducers)))))))
+
+(defun rust-looking-back-macro-rules ()
+ "Non-nil if looking back at \"macro_rules IDENT !\"."
+ (save-excursion
+ (skip-syntax-backward "->")
+ (let ((outer-point (point)))
+ (forward-thing 'symbol -2)
+ (and (looking-at (concat "macro_rules\\s-*!\\s-*" rust-re-ident))
+ (eq (match-end 0) outer-point)))))
;;; Syntax definitions and helpers
@@ -562,90 +580,25 @@ seen as a macro."
;; Rewind until the point no longer moves
(setq continue (/= starting (point)))))))
-(defvar-local rust-macro-scopes nil
- "Cache for the scopes calculated by `rust-macro-scope'.
-
-This variable can be `let' bound directly or indirectly around
-`rust-macro-scope' as an optimization but should not be otherwise
-set.")
-
-(defun rust-macro-scope (start end)
- "Return the scope of macros in the buffer.
-
-The return value is a list of (START END) positions in the
-buffer.
-
-If set START and END are optimizations which limit the return
-value to scopes which are approximately with this range."
- (save-excursion
- ;; need to special case macro_rules which has unique syntax
- (let ((scope nil)
- (start (or start (point-min)))
- (end (or end (point-max))))
- (goto-char start)
- ;; if there is a start move back to the previous top level,
- ;; as any macros before that must have closed by this time.
- (let ((top (syntax-ppss-toplevel-pos (syntax-ppss))))
- (when top
- (goto-char top)))
- (while
- (and
- ;; The movement below may have moved us passed end, in
- ;; which case search-forward will error
- (< (point) end)
- (search-forward "!" end t))
- (let ((pt (point)))
- (cond
- ;; in a string or comment is boring, move straight on
- ((rust-in-str-or-cmnt))
- ;; in a normal macro,
- ((and (skip-chars-forward " \t\n\r")
- (memq (char-after)
- '(?\[ ?\( ?\{))
- ;; Check that we have a macro declaration after.
- (rust-looking-back-macro))
- (let ((start (point)))
- (ignore-errors (forward-list))
- (setq scope (cons (list start (point)) scope))))
- ;; macro_rules, why, why, why did you not use macro syntax??
- ((save-excursion
- ;; yuck -- last test moves point, even if it fails
- (goto-char (- pt 1))
- (skip-chars-backward " \t\n\r")
- (rust-looking-back-str "macro_rules"))
- (save-excursion
- (when (re-search-forward "[[({]" nil t)
- (backward-char)
- (let ((start (point)))
- (ignore-errors (forward-list))
- (setq scope (cons (list start (point)) scope)))))))))
- ;; Return 'empty rather than nil, to indicate a buffer with no
- ;; macros at all.
- (or scope 'empty))))
-
-(defun rust-in-macro (&optional start end)
+(defun rust-in-macro ()
"Return non-nil when point is within the scope of a macro.
-
-If START and END are set, minimize the buffer analysis to
-approximately this location as an optimization.
-
-Alternatively, if `rust-macro-scopes' is a list use the scope
-information in this variable. This last is an optimization and
-the caller is responsible for ensuring that the data in
-`rust-macro-scopes' is up to date."
- (when (> (rust-paren-level) 0)
- (let ((scopes
- (or
- rust-macro-scopes
- (rust-macro-scope start end))))
- ;; `rust-macro-scope' can return the symbol `empty' if the
- ;; buffer has no macros at all.
- (when (listp scopes)
- (seq-some
- (lambda (sc)
- (and (>= (point) (car sc))
- (< (point) (cadr sc))))
- scopes)))))
+If we are, return the position of the opening bracket of the macro's arguments."
+ (let ((ppss (syntax-ppss)))
+ ;; If we're in a string or comment, we're definitely not on a token a macro
+ ;; will see.
+ (when (not (or (nth 3 ppss) (nth 4 ppss)))
+ ;; Walk outward to enclosing parens, looking for one preceded by "ident !"
+ ;; or "macro_rules! ident".
+ (let (result
+ (enclosing (reverse (nth 9 ppss))))
+ (save-excursion
+ (while enclosing
+ (goto-char (car enclosing))
+ (if (or (rust-looking-back-macro)
+ (rust-looking-back-macro-rules))
+ (setq result (point) enclosing nil)
+ (setq enclosing (cdr enclosing)))))
+ result))))
(defun rust-looking-at-where ()
"Return T when looking at the \"where\" keyword."
@@ -1089,7 +1042,7 @@ outside of this context."
(cond
;; Certain keywords always introduce expressions
- ((rust-looking-back-symbols '("if" "while" "match" "return" "box" "in")) t)
+ ((rust-looking-back-symbols rust-expression-introducers) t)
;; "as" introduces a type
((rust-looking-back-symbols '("as")) nil)
@@ -1411,34 +1364,32 @@ whichever comes first."
(defun rust-syntax-propertize (start end)
"A `syntax-propertize-function' to apply properties from START to END."
- ;; Cache all macro scopes as an optimization. See issue #208
- (let ((rust-macro-scopes (rust-macro-scope start end)))
- (goto-char start)
- (let ((str-start (rust-in-str-or-cmnt)))
- (when str-start
- (rust--syntax-propertize-raw-string str-start end)))
- (funcall
- (syntax-propertize-rules
- ;; Character literals.
- (rust--char-literal-rx (1 "\"") (2 "\""))
- ;; Raw strings.
- ("\\(r\\)#*\""
- (0 (ignore
- (goto-char (match-end 0))
- (unless (save-excursion (nth 8 (syntax-ppss (match-beginning 0))))
- (put-text-property (match-beginning 1) (match-end 1)
- 'syntax-table (string-to-syntax "|"))
- (rust--syntax-propertize-raw-string (match-beginning 0) end)))))
- ("[<>]"
- (0 (ignore
- (when (save-match-data
- (save-excursion
- (goto-char (match-beginning 0))
- (rust-ordinary-lt-gt-p)))
- (put-text-property (match-beginning 0) (match-end 0)
- 'syntax-table (string-to-syntax "."))
- (goto-char (match-end 0)))))))
- (point) end)))
+ (goto-char start)
+ (let ((str-start (rust-in-str-or-cmnt)))
+ (when str-start
+ (rust--syntax-propertize-raw-string str-start end)))
+ (funcall
+ (syntax-propertize-rules
+ ;; Character literals.
+ (rust--char-literal-rx (1 "\"") (2 "\""))
+ ;; Raw strings.
+ ("\\(r\\)#*\""
+ (0 (ignore
+ (goto-char (match-end 0))
+ (unless (save-excursion (nth 8 (syntax-ppss (match-beginning 0))))
+ (put-text-property (match-beginning 1) (match-end 1)
+ 'syntax-table (string-to-syntax "|"))
+ (rust--syntax-propertize-raw-string (match-beginning 0) end)))))
+ ("[<>]"
+ (0 (ignore
+ (when (save-match-data
+ (save-excursion
+ (goto-char (match-beginning 0))
+ (rust-ordinary-lt-gt-p)))
+ (put-text-property (match-beginning 0) (match-end 0)
+ 'syntax-table (string-to-syntax "."))
+ (goto-char (match-end 0)))))))
+ (point) end))
(defun rust-fill-prefix-for-comment-start (line-start)
"Determine what to use for `fill-prefix' based on the text at LINE-START."