[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/rust-mode bfe4056 452/486: Re-implement rust-in-macro for
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/rust-mode bfe4056 452/486: Re-implement rust-in-macro for performance |
Date: |
Sat, 7 Aug 2021 09:26:13 -0400 (EDT) |
branch: elpa/rust-mode
commit bfe40565753295a4cf8403f4124710acd2827d21
Author: Phillip Lord <phillip.lord@russet.org.uk>
Commit: Nathan Moreau <nathan.moreau@m4x.org>
Re-implement rust-in-macro for performance
rust-in-macro could cause significant performance problems resulting
in a very choppy user experience. Reimplement rust-in macro in a
somewhat simpler manner and in way which allows both allows
restriction to parts of the buffer and caching of buffer analysis.
Optimize rust-syntax-propertize to use this caching mechanism.
Fixes #208
Fixes #288
---
rust-mode-tests.el | 110 ++++++++++++++++++++++++++++++++++++-
rust-mode.el | 155 ++++++++++++++++++++++++++++++++++++++++-------------
2 files changed, 225 insertions(+), 40 deletions(-)
diff --git a/rust-mode-tests.el b/rust-mode-tests.el
index 4860941..b6a23e5 100644
--- a/rust-mode-tests.el
+++ b/rust-mode-tests.el
@@ -2139,8 +2139,8 @@ fn main() {
(should (equal (scan-sexps (+ 1 close-pos) -1) open-pos))))
(dolist (nonpar-pos nonparen-positions)
(let ((nonpar-syntax-class (syntax-class (syntax-after nonpar-pos))))
- (should (not (equal 4 nonpar-syntax-class)))
- (should (not (equal 5 nonpar-syntax-class)))))))
+ (should-not (equal 4 nonpar-syntax-class))
+ (should-not (equal 5 nonpar-syntax-class))))))
(ert-deftest rust-test-unmatched-single-quote-in-comment-paren-matching ()
;; This was a bug from the char quote handling that affected the paren
@@ -2923,6 +2923,112 @@ macro_c!{
125 ;; macro_d >
)))
+(ert-deftest rust-test-paren-matching-no-angle-brackets-in-macro-rules ()
+ (rust-test-matching-parens
+ "
+fn foo<A>(a:A) {
+ macro_rules! foo ( foo::<ignore the bracets> );
+ macro_rules! bar [ foo as Option<B> ];
+}
+
+macro_c!{
+ struct Boo<D> {}
+}"
+ '((8 10))
+ ;; Inside macros, it should not find any angle brackets, even if it normally
+ ;; would
+ '(47 ;; foo <
+ 62 ;; foo >
+ 107 ;; bar <
+ 109 ;; bar >
+ 141 ;; macro_c <
+ 143 ;; macro_c >
+ )))
+
+(ert-deftest rust-test-in-macro-do-not-fail-on-unbalance ()
+ (should
+ ;; We don't care about the results here, so long as they do not error
+ (with-temp-buffer
+ (insert
+ "fn foo<A>(a:A) {
+ macro_c!{
+ struct Boo<D> {}
+")
+ (rust-mode)
+ (goto-char (point-max))
+ (syntax-ppss))))
+
+
+(ert-deftest rust-test-in-macro-no-caching ()
+ (should-not
+ (with-temp-buffer
+ (insert
+ "fn foo<A>(a:A) {
+ macro_c!{
+ struct Boo<D> {}
+")
+ (rust-mode)
+ (search-backward "macro")
+ ;; do not use the cache
+ (let ((rust-macro-scopes nil))
+ (rust-in-macro)))))
+
+(ert-deftest rust-test-in-macro-fake-cache ()
+ (should
+ (with-temp-buffer
+ (insert
+ "fn foo<A>(a:A) {
+ macro_c!{
+ struct Boo<D> {}
+")
+ (rust-mode)
+ (search-backward "macro")
+ ;; make the cache lie to make the whole buffer in scope
+ ;; we need to be at paren level 1 for this to work
+ (let ((rust-macro-scopes `((,(point-min) ,(point-max)))))
+ (rust-in-macro)))))
+
+(ert-deftest rust-test-in-macro-broken-cache ()
+ (should-error
+ (with-temp-buffer
+ (insert
+ "fn foo<A>(a:A) {
+ macro_c!{
+ struct Boo<D> {}
+")
+ (rust-mode)
+ (search-backward "Boo")
+ ;; do we use the cache at all
+ (let ((rust-macro-scopes '(I should break)))
+ (rust-in-macro)))))
+
+(ert-deftest rust-test-in-macro-nested ()
+ (should
+ (equal
+ (with-temp-buffer
+ (insert
+ "macro_rules! outer {
+ () => { vec![] };
+}")
+ (rust-mode)
+ (rust-macro-scope (point-min) (point-max)))
+ '((38 40) (20 45)))))
+
+(ert-deftest rust-test-in-macro-not-with-space ()
+ (should
+ (equal
+ (with-temp-buffer
+ (insert
+ "fn foo<T>() {
+ if !(mem::size_of::<T>() > 8) {
+ bar()
+ }
+}")
+ (rust-mode)
+ (rust-macro-scope (point-min) (point-max)))
+ 'empty)))
+
+
(ert-deftest rust-test-paren-matching-type-with-module-name ()
(rust-test-matching-parens
"
diff --git a/rust-mode.el b/rust-mode.el
index 925a9c8..a53d320 100644
--- a/rust-mode.el
+++ b/rust-mode.el
@@ -106,7 +106,12 @@ symbols."
(looking-back rust-re-ident beg-of-symbol)))
(defun rust-looking-back-macro ()
- "Non-nil if looking back at an ident followed by a !"
+ "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)
@@ -260,18 +265,90 @@ to the function arguments. When nil, `->' will be
indented one level."
;; Rewind until the point no longer moves
(setq continue (/= starting (point)))))))
-(defun rust-in-macro ()
+(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
- (when (> (rust-paren-level) 0)
- (backward-up-list)
- (rust-rewind-irrelevant)
- (or (rust-looking-back-macro)
- (and (rust-looking-back-ident)
- (save-excursion
- (backward-sexp)
- (rust-rewind-irrelevant)
- (rust-looking-back-str "macro_rules!")))
- (rust-in-macro)))))
+ ;; 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)
+ "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)))))
(defun rust-looking-at-where ()
"Return T when looking at the \"where\" keyword."
@@ -1208,32 +1285,34 @@ whichever comes first."
(defun rust-syntax-propertize (start end)
"A `syntax-propertize-function' to apply properties from START to 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))
+ ;; 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)))
(defun rust-fill-prefix-for-comment-start (line-start)
"Determine what to use for `fill-prefix' based on the text at LINE-START."
- [nongnu] elpa/rust-mode 86db9bd 166/486: Merge pull request #65 from tromey/blink-matching-paren, (continued)
- [nongnu] elpa/rust-mode 86db9bd 166/486: Merge pull request #65 from tromey/blink-matching-paren, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 53c558c 175/486: Factor out rust-conditional-re-search-forward, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 5231709 177/486: Speed up calls to "looking-back", ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode c801dda 197/486: Merge pull request #77 from tromey/fix-byte-compiler-add-lexbind, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode cdaf3fc 201/486: Update MELPA URL, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 1761a9c 204/486: Merge pull request #105 from MicahChalmer/remove-misc, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode f8d94cb 229/486: Use catch and throw instead of block and return, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 40c33fd 250/486: add code to handle new-style rustc errors, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode c69adaf 254/486: Add menu titles for items in imenu, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode dae5af7 298/486: Merge pull request #198 from tromey/indentation-fix, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode bfe4056 452/486: Re-implement rust-in-macro for performance,
ELPA Syncer <=
- [nongnu] elpa/rust-mode eaf95af 210/486: Merge pull request #111 from mrBliss/where-indentation, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 2540d7e 262/486: Go back to the current line and column after formatting., ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 52870b6 203/486: Remove unused require of misc.el, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 95d089c 211/486: Merge pull request #110 from mrBliss/fix-103, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 00e5366 224/486: Merge pull request #122 from kraai/remove-duplicate-keywords, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 0601540 205/486: Merge pull request #96 from MicahChalmer/add-licenses, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 1492106 232/486: Fix hook args for after-revert-hook, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 2f42da8 228/486: Add rust-promote-module-into-dir, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 0914b48 234/486: Merge pull request #133 from kraai/avoid-cl, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 917503b 244/486: Fix failing tests, and add a test for the fix., ELPA Syncer, 2021/08/07