@@ -106,7 +106,12 @@ symbols."
106
106
(looking-back rust-re-ident beg-of-symbol)))
107
107
108
108
(defun rust-looking-back-macro ()
109
- " Non-nil if looking back at an ident followed by a !"
109
+ " Non-nil if looking back at an ident followed by a !
110
+
111
+ This is stricter than rust syntax which allows a space between
112
+ the ident and the ! symbol. If this space is allowed, then we
113
+ would also need a keyword check to avoid `if !(condition)` being
114
+ seen as a macro."
110
115
(if (> (- (point ) (point-min )) 1 )
111
116
(save-excursion
112
117
(backward-char )
@@ -260,18 +265,90 @@ to the function arguments. When nil, `->' will be indented one level."
260
265
; ; Rewind until the point no longer moves
261
266
(setq continue (/= starting (point )))))))
262
267
263
- (defun rust-in-macro ()
268
+ (defvar-local rust-macro-scopes nil
269
+ " Cache for the scopes calculated by `rust-macro-scope' .
270
+
271
+ This variable can be `let' bound directly or indirectly around
272
+ `rust-macro-scope' as an optimization but should not be otherwise
273
+ set." )
274
+
275
+ (defun rust-macro-scope (start end )
276
+ " Return the scope of macros in the buffer.
277
+
278
+ The return value is a list of (START END) positions in the
279
+ buffer.
280
+
281
+ If set START and END are optimizations which limit the return
282
+ value to scopes which are approximately with this range."
264
283
(save-excursion
265
- (when (> (rust-paren-level) 0 )
266
- (backward-up-list )
267
- (rust-rewind-irrelevant)
268
- (or (rust-looking-back-macro)
269
- (and (rust-looking-back-ident)
270
- (save-excursion
271
- (backward-sexp )
272
- (rust-rewind-irrelevant)
273
- (rust-looking-back-str " macro_rules!" )))
274
- (rust-in-macro)))))
284
+ ; ; need to special case macro_rules which has unique syntax
285
+ (let ((scope nil )
286
+ (start (or start (point-min )))
287
+ (end (or end (point-max ))))
288
+ (goto-char start)
289
+ ; ; if there is a start move back to the previous top level,
290
+ ; ; as any macros before that must have closed by this time.
291
+ (let ((top (syntax-ppss-toplevel-pos (syntax-ppss ))))
292
+ (when top
293
+ (goto-char top)))
294
+ (while
295
+ (and
296
+ ; ; The movement below may have moved us passed end, in
297
+ ; ; which case search-forward will error
298
+ (< (point ) end)
299
+ (search-forward " !" end t ))
300
+ (let ((pt (point )))
301
+ (cond
302
+ ; ; in a string or comment is boring, move straight on
303
+ ((rust-in-str-or-cmnt))
304
+ ; ; in a normal macro,
305
+ ((and (skip-chars-forward " \t\n\r " )
306
+ (memq (char-after )
307
+ '(?\[ ?\( ?\{ ))
308
+ ; ; Check that we have a macro declaration after.
309
+ (rust-looking-back-macro))
310
+ (let ((start (point )))
311
+ (ignore-errors (forward-list ))
312
+ (setq scope (cons (list start (point )) scope))))
313
+ ; ; macro_rules, why, why, why did you not use macro syntax??
314
+ ((save-excursion
315
+ ; ; yuck -- last test moves point, even if it fails
316
+ (goto-char (- pt 1 ))
317
+ (skip-chars-backward " \t\n\r " )
318
+ (rust-looking-back-str " macro_rules" ))
319
+ (save-excursion
320
+ (when (re-search-forward " [[({]" nil t )
321
+ (backward-char )
322
+ (let ((start (point )))
323
+ (ignore-errors (forward-list ))
324
+ (setq scope (cons (list start (point )) scope)))))))))
325
+ ; ; Return 'empty rather than nil, to indicate a buffer with no
326
+ ; ; macros at all.
327
+ (or scope 'empty ))))
328
+
329
+ (defun rust-in-macro (&optional start end )
330
+ " Return non-nil when point is within the scope of a macro.
331
+
332
+ If START and END are set, minimize the buffer analysis to
333
+ approximately this location as an optimization.
334
+
335
+ Alternatively, if `rust-macro-scopes' is a list use the scope
336
+ information in this variable. This last is an optimization and
337
+ the caller is responsible for ensuring that the data in
338
+ `rust-macro-scopes' is up to date."
339
+ (when (> (rust-paren-level) 0 )
340
+ (let ((scopes
341
+ (or
342
+ rust-macro-scopes
343
+ (rust-macro-scope start end))))
344
+ ; ; `rust-macro-scope' can return the symbol `empty' if the
345
+ ; ; buffer has no macros at all.
346
+ (when (listp scopes)
347
+ (seq-some
348
+ (lambda (sc )
349
+ (and (>= (point ) (car sc))
350
+ (< (point ) (cadr sc))))
351
+ scopes)))))
275
352
276
353
(defun rust-looking-at-where ()
277
354
" Return T when looking at the \" where\" keyword."
@@ -1208,32 +1285,34 @@ whichever comes first."
1208
1285
1209
1286
(defun rust-syntax-propertize (start end )
1210
1287
" A `syntax-propertize-function' to apply properties from START to END."
1211
- (goto-char start)
1212
- (let ((str-start (rust-in-str-or-cmnt)))
1213
- (when str-start
1214
- (rust--syntax-propertize-raw-string str-start end)))
1215
- (funcall
1216
- (syntax-propertize-rules
1217
- ; ; Character literals.
1218
- (rust--char-literal-rx (1 " \" " ) (2 " \" " ))
1219
- ; ; Raw strings.
1220
- (" \\ (r\\ )#*\" "
1221
- (0 (ignore
1222
- (goto-char (match-end 0 ))
1223
- (unless (save-excursion (nth 8 (syntax-ppss (match-beginning 0 ))))
1224
- (put-text-property (match-beginning 1 ) (match-end 1 )
1225
- 'syntax-table (string-to-syntax " |" ))
1226
- (rust--syntax-propertize-raw-string (match-beginning 0 ) end)))))
1227
- (" [<>]"
1228
- (0 (ignore
1229
- (when (save-match-data
1230
- (save-excursion
1231
- (goto-char (match-beginning 0 ))
1232
- (rust-ordinary-lt-gt-p)))
1233
- (put-text-property (match-beginning 0 ) (match-end 0 )
1234
- 'syntax-table (string-to-syntax " ." ))
1235
- (goto-char (match-end 0 )))))))
1236
- (point ) end))
1288
+ ; ; Cache all macro scopes as an optimization. See issue #208
1289
+ (let ((rust-macro-scopes (rust-macro-scope start end)))
1290
+ (goto-char start)
1291
+ (let ((str-start (rust-in-str-or-cmnt)))
1292
+ (when str-start
1293
+ (rust--syntax-propertize-raw-string str-start end)))
1294
+ (funcall
1295
+ (syntax-propertize-rules
1296
+ ; ; Character literals.
1297
+ (rust--char-literal-rx (1 " \" " ) (2 " \" " ))
1298
+ ; ; Raw strings.
1299
+ (" \\ (r\\ )#*\" "
1300
+ (0 (ignore
1301
+ (goto-char (match-end 0 ))
1302
+ (unless (save-excursion (nth 8 (syntax-ppss (match-beginning 0 ))))
1303
+ (put-text-property (match-beginning 1 ) (match-end 1 )
1304
+ 'syntax-table (string-to-syntax " |" ))
1305
+ (rust--syntax-propertize-raw-string (match-beginning 0 ) end)))))
1306
+ (" [<>]"
1307
+ (0 (ignore
1308
+ (when (save-match-data
1309
+ (save-excursion
1310
+ (goto-char (match-beginning 0 ))
1311
+ (rust-ordinary-lt-gt-p)))
1312
+ (put-text-property (match-beginning 0 ) (match-end 0 )
1313
+ 'syntax-table (string-to-syntax " ." ))
1314
+ (goto-char (match-end 0 )))))))
1315
+ (point ) end)))
1237
1316
1238
1317
(defun rust-fill-prefix-for-comment-start (line-start )
1239
1318
" Determine what to use for `fill-prefix' based on the text at LINE-START."
0 commit comments