Skip to content

Commit 07c4a3a

Browse files
authored
Merge pull request #467 from jimblandy/in-macro
Simplify and correct angle bracket propertizing and macro argument detection.
2 parents e2bb3b0 + 42ba58d commit 07c4a3a

File tree

2 files changed

+99
-176
lines changed

2 files changed

+99
-176
lines changed

rust-mode-tests.el

Lines changed: 22 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3116,7 +3116,7 @@ macro_c!{
31163116
(syntax-ppss))))
31173117

31183118

3119-
(ert-deftest rust-test-in-macro-no-caching ()
3119+
(ert-deftest rust-test-in-macro-around-opening ()
31203120
(should-not
31213121
(with-temp-buffer
31223122
(insert
@@ -3125,66 +3125,38 @@ macro_c!{
31253125
struct Boo<D> {}
31263126
")
31273127
(rust-mode)
3128-
(search-backward "macro")
3129-
;; do not use the cache
3130-
(let ((rust-macro-scopes nil))
3131-
(rust-in-macro)))))
3132-
3133-
(ert-deftest rust-test-in-macro-fake-cache ()
3134-
(should
3135-
(with-temp-buffer
3136-
(insert
3137-
"fn foo<A>(a:A) {
3138-
macro_c!{
3139-
struct Boo<D> {}
3140-
")
3141-
(rust-mode)
3142-
(search-backward "macro")
3143-
;; make the cache lie to make the whole buffer in scope
3144-
;; we need to be at paren level 1 for this to work
3145-
(let ((rust-macro-scopes `((,(point-min) ,(point-max)))))
3146-
(rust-in-macro)))))
3147-
3148-
(ert-deftest rust-test-in-macro-broken-cache ()
3149-
(should-error
3150-
(with-temp-buffer
3151-
(insert
3152-
"fn foo<A>(a:A) {
3153-
macro_c!{
3154-
struct Boo<D> {}
3155-
")
3156-
(rust-mode)
3157-
(search-backward "Boo")
3158-
;; do we use the cache at all
3159-
(let ((rust-macro-scopes '(I should break)))
3160-
(rust-in-macro)))))
3128+
(search-backward "macro_c")
3129+
(and
3130+
(not (rust-in-macro))
3131+
(progn (forward-thing 'symbol 1) (not (rust-in-macro)))
3132+
(progn (forward-char 1) (rust-in-macro))
3133+
(progn (goto-char (point-max)) (rust-in-macro))))))
31613134

31623135
(ert-deftest rust-test-in-macro-nested ()
3163-
(should
3164-
(equal
3165-
(with-temp-buffer
3166-
(insert
3167-
"macro_rules! outer {
3136+
(with-temp-buffer
3137+
(insert
3138+
"macro_rules! outer {
31683139
() => { vec![] };
31693140
}")
3170-
(rust-mode)
3171-
(rust-macro-scope (point-min) (point-max)))
3172-
'((38 40) (20 45)))))
3141+
(rust-mode)
3142+
(should (progn (goto-char 20) (not (rust-in-macro))))
3143+
(should (progn (goto-char 21) (eq (rust-in-macro) 20)))
3144+
(should (progn (goto-char 38) (eq (rust-in-macro) 20)))
3145+
(should (progn (goto-char 39) (eq (rust-in-macro) 38)))
3146+
(should (progn (goto-char 40) (eq (rust-in-macro) 20)))
3147+
(should (progn (goto-char 44) (eq (rust-in-macro) 20)))
3148+
(should (progn (goto-char 45) (not (rust-in-macro))))))
31733149

31743150
(ert-deftest rust-test-in-macro-not-with-space ()
3175-
(should
3176-
(equal
3177-
(with-temp-buffer
3178-
(insert
3151+
(with-temp-buffer
3152+
(insert
31793153
"fn foo<T>() {
31803154
if !(mem::size_of::<T>() > 8) {
31813155
bar()
31823156
}
31833157
}")
3184-
(rust-mode)
3185-
(rust-macro-scope (point-min) (point-max)))
3186-
'empty)))
3187-
3158+
(rust-mode)
3159+
(should (progn (goto-char 24) (not (rust-in-macro))))))
31883160

31893161
(ert-deftest rust-test-paren-matching-type-with-module-name ()
31903162
(rust-test-matching-parens

rust-mode.el

Lines changed: 77 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,10 @@ See `prettify-symbols-compose-predicate'."
362362
"bool"
363363
"str" "char"))
364364

365+
(defconst rust-expression-introducers
366+
'("if" "while" "match" "return" "box" "in")
367+
"List of Rust keywords that are always followed by expressions.")
368+
365369
(defconst rust-number-with-type
366370
(eval-when-compile
367371
(concat
@@ -526,22 +530,36 @@ symbols."
526530
symbols)))))
527531

528532
(defun rust-looking-back-ident ()
529-
"Non-nil if we are looking backwards at a valid rust identifier."
530-
(let ((beg-of-symbol (save-excursion (forward-thing 'symbol -1) (point))))
531-
(looking-back rust-re-ident beg-of-symbol)))
533+
"Non-nil if we are looking backwards at a valid rust identifier.
534+
If we are, regexp match 0 is the identifier."
535+
(let ((outer-point (point)))
536+
(save-excursion
537+
(forward-thing 'symbol -1)
538+
(and (looking-at rust-re-ident)
539+
(eq (match-end 0) outer-point)))))
532540

533541
(defun rust-looking-back-macro ()
534-
"Non-nil if looking back at an ident followed by a !
535-
536-
This is stricter than rust syntax which allows a space between
537-
the ident and the ! symbol. If this space is allowed, then we
538-
would also need a keyword check to avoid `if !(condition)` being
539-
seen as a macro."
540-
(if (> (- (point) (point-min)) 1)
541-
(save-excursion
542-
(backward-char)
543-
(and (= ?! (char-after))
544-
(rust-looking-back-ident)))))
542+
"Non-nil if looking back at a potential macro name followed by a \"!\".
543+
If we are, regexp match 0 is the macro name."
544+
(save-excursion
545+
;; Look past whitespace and line breaks.
546+
;; > is okay because we only use it for \n and \r, not "*/"
547+
(skip-syntax-backward "->")
548+
(when (eq (char-before) ?!)
549+
(forward-char -1)
550+
(skip-syntax-backward "->")
551+
(when (rust-looking-back-ident)
552+
(let ((ident (match-string 0)))
553+
(not (member ident rust-expression-introducers)))))))
554+
555+
(defun rust-looking-back-macro-rules ()
556+
"Non-nil if looking back at \"macro_rules IDENT !\"."
557+
(save-excursion
558+
(skip-syntax-backward "->")
559+
(let ((outer-point (point)))
560+
(forward-thing 'symbol -2)
561+
(and (looking-at (concat "macro_rules\\s-*!\\s-*" rust-re-ident))
562+
(eq (match-end 0) outer-point)))))
545563

546564
;;; Syntax definitions and helpers
547565

@@ -562,90 +580,25 @@ seen as a macro."
562580
;; Rewind until the point no longer moves
563581
(setq continue (/= starting (point)))))))
564582

565-
(defvar-local rust-macro-scopes nil
566-
"Cache for the scopes calculated by `rust-macro-scope'.
567-
568-
This variable can be `let' bound directly or indirectly around
569-
`rust-macro-scope' as an optimization but should not be otherwise
570-
set.")
571-
572-
(defun rust-macro-scope (start end)
573-
"Return the scope of macros in the buffer.
574-
575-
The return value is a list of (START END) positions in the
576-
buffer.
577-
578-
If set START and END are optimizations which limit the return
579-
value to scopes which are approximately with this range."
580-
(save-excursion
581-
;; need to special case macro_rules which has unique syntax
582-
(let ((scope nil)
583-
(start (or start (point-min)))
584-
(end (or end (point-max))))
585-
(goto-char start)
586-
;; if there is a start move back to the previous top level,
587-
;; as any macros before that must have closed by this time.
588-
(let ((top (syntax-ppss-toplevel-pos (syntax-ppss))))
589-
(when top
590-
(goto-char top)))
591-
(while
592-
(and
593-
;; The movement below may have moved us passed end, in
594-
;; which case search-forward will error
595-
(< (point) end)
596-
(search-forward "!" end t))
597-
(let ((pt (point)))
598-
(cond
599-
;; in a string or comment is boring, move straight on
600-
((rust-in-str-or-cmnt))
601-
;; in a normal macro,
602-
((and (skip-chars-forward " \t\n\r")
603-
(memq (char-after)
604-
'(?\[ ?\( ?\{))
605-
;; Check that we have a macro declaration after.
606-
(rust-looking-back-macro))
607-
(let ((start (point)))
608-
(ignore-errors (forward-list))
609-
(setq scope (cons (list start (point)) scope))))
610-
;; macro_rules, why, why, why did you not use macro syntax??
611-
((save-excursion
612-
;; yuck -- last test moves point, even if it fails
613-
(goto-char (- pt 1))
614-
(skip-chars-backward " \t\n\r")
615-
(rust-looking-back-str "macro_rules"))
616-
(save-excursion
617-
(when (re-search-forward "[[({]" nil t)
618-
(backward-char)
619-
(let ((start (point)))
620-
(ignore-errors (forward-list))
621-
(setq scope (cons (list start (point)) scope)))))))))
622-
;; Return 'empty rather than nil, to indicate a buffer with no
623-
;; macros at all.
624-
(or scope 'empty))))
625-
626-
(defun rust-in-macro (&optional start end)
583+
(defun rust-in-macro ()
627584
"Return non-nil when point is within the scope of a macro.
628-
629-
If START and END are set, minimize the buffer analysis to
630-
approximately this location as an optimization.
631-
632-
Alternatively, if `rust-macro-scopes' is a list use the scope
633-
information in this variable. This last is an optimization and
634-
the caller is responsible for ensuring that the data in
635-
`rust-macro-scopes' is up to date."
636-
(when (> (rust-paren-level) 0)
637-
(let ((scopes
638-
(or
639-
rust-macro-scopes
640-
(rust-macro-scope start end))))
641-
;; `rust-macro-scope' can return the symbol `empty' if the
642-
;; buffer has no macros at all.
643-
(when (listp scopes)
644-
(seq-some
645-
(lambda (sc)
646-
(and (>= (point) (car sc))
647-
(< (point) (cadr sc))))
648-
scopes)))))
585+
If we are, return the position of the opening bracket of the macro's arguments."
586+
(let ((ppss (syntax-ppss)))
587+
;; If we're in a string or comment, we're definitely not on a token a macro
588+
;; will see.
589+
(when (not (or (nth 3 ppss) (nth 4 ppss)))
590+
;; Walk outward to enclosing parens, looking for one preceded by "ident !"
591+
;; or "macro_rules! ident".
592+
(let (result
593+
(enclosing (reverse (nth 9 ppss))))
594+
(save-excursion
595+
(while enclosing
596+
(goto-char (car enclosing))
597+
(if (or (rust-looking-back-macro)
598+
(rust-looking-back-macro-rules))
599+
(setq result (point) enclosing nil)
600+
(setq enclosing (cdr enclosing)))))
601+
result))))
649602

650603
(defun rust-looking-at-where ()
651604
"Return T when looking at the \"where\" keyword."
@@ -1089,7 +1042,7 @@ outside of this context."
10891042
(cond
10901043

10911044
;; Certain keywords always introduce expressions
1092-
((rust-looking-back-symbols '("if" "while" "match" "return" "box" "in")) t)
1045+
((rust-looking-back-symbols rust-expression-introducers) t)
10931046

10941047
;; "as" introduces a type
10951048
((rust-looking-back-symbols '("as")) nil)
@@ -1411,34 +1364,32 @@ whichever comes first."
14111364

14121365
(defun rust-syntax-propertize (start end)
14131366
"A `syntax-propertize-function' to apply properties from START to END."
1414-
;; Cache all macro scopes as an optimization. See issue #208
1415-
(let ((rust-macro-scopes (rust-macro-scope start end)))
1416-
(goto-char start)
1417-
(let ((str-start (rust-in-str-or-cmnt)))
1418-
(when str-start
1419-
(rust--syntax-propertize-raw-string str-start end)))
1420-
(funcall
1421-
(syntax-propertize-rules
1422-
;; Character literals.
1423-
(rust--char-literal-rx (1 "\"") (2 "\""))
1424-
;; Raw strings.
1425-
("\\(r\\)#*\""
1426-
(0 (ignore
1427-
(goto-char (match-end 0))
1428-
(unless (save-excursion (nth 8 (syntax-ppss (match-beginning 0))))
1429-
(put-text-property (match-beginning 1) (match-end 1)
1430-
'syntax-table (string-to-syntax "|"))
1431-
(rust--syntax-propertize-raw-string (match-beginning 0) end)))))
1432-
("[<>]"
1433-
(0 (ignore
1434-
(when (save-match-data
1435-
(save-excursion
1436-
(goto-char (match-beginning 0))
1437-
(rust-ordinary-lt-gt-p)))
1438-
(put-text-property (match-beginning 0) (match-end 0)
1439-
'syntax-table (string-to-syntax "."))
1440-
(goto-char (match-end 0)))))))
1441-
(point) end)))
1367+
(goto-char start)
1368+
(let ((str-start (rust-in-str-or-cmnt)))
1369+
(when str-start
1370+
(rust--syntax-propertize-raw-string str-start end)))
1371+
(funcall
1372+
(syntax-propertize-rules
1373+
;; Character literals.
1374+
(rust--char-literal-rx (1 "\"") (2 "\""))
1375+
;; Raw strings.
1376+
("\\(r\\)#*\""
1377+
(0 (ignore
1378+
(goto-char (match-end 0))
1379+
(unless (save-excursion (nth 8 (syntax-ppss (match-beginning 0))))
1380+
(put-text-property (match-beginning 1) (match-end 1)
1381+
'syntax-table (string-to-syntax "|"))
1382+
(rust--syntax-propertize-raw-string (match-beginning 0) end)))))
1383+
("[<>]"
1384+
(0 (ignore
1385+
(when (save-match-data
1386+
(save-excursion
1387+
(goto-char (match-beginning 0))
1388+
(rust-ordinary-lt-gt-p)))
1389+
(put-text-property (match-beginning 0) (match-end 0)
1390+
'syntax-table (string-to-syntax "."))
1391+
(goto-char (match-end 0)))))))
1392+
(point) end))
14421393

14431394
(defun rust-fill-prefix-for-comment-start (line-start)
14441395
"Determine what to use for `fill-prefix' based on the text at LINE-START."

0 commit comments

Comments
 (0)