summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.emacs.d/lisp/markdown-mode.el6
-rw-r--r--.emacs.d/lisp/rust-cargo.el3
-rw-r--r--.emacs.d/lisp/rust-common.el20
-rw-r--r--.emacs.d/lisp/rust-compile.el9
-rw-r--r--.emacs.d/lisp/rust-mode-tests.el202
-rw-r--r--.emacs.d/lisp/rust-mode-treesitter.el25
-rw-r--r--.emacs.d/lisp/rust-mode.el1575
-rw-r--r--.emacs.d/lisp/rust-playpen.el42
-rw-r--r--.emacs.d/lisp/rust-prog-mode.el1509
-rw-r--r--.emacs.d/lisp/rust-rustfmt.el94
-rw-r--r--.emacs.d/lisp/rust-utils.el38
-rw-r--r--.emacs.d/lisp/yasnippet.el107
12 files changed, 1883 insertions, 1747 deletions
diff --git a/.emacs.d/lisp/markdown-mode.el b/.emacs.d/lisp/markdown-mode.el
index 8094e02..1514edb 100644
--- a/.emacs.d/lisp/markdown-mode.el
+++ b/.emacs.d/lisp/markdown-mode.el
@@ -1855,6 +1855,10 @@ START and END delimit region to propertize."
1855 '(face markdown-markup-face invisible markdown-markup) 1855 '(face markdown-markup-face invisible markdown-markup)
1856 "List of properties and values to apply to markup.") 1856 "List of properties and values to apply to markup.")
1857 1857
1858(defconst markdown-line-break-properties
1859 '(face markdown-line-break-face invisible markdown-markup)
1860 "List of properties and values to apply to line break markup.")
1861
1858(defconst markdown-language-keyword-properties 1862(defconst markdown-language-keyword-properties
1859 '(face markdown-language-keyword-face invisible markdown-markup) 1863 '(face markdown-language-keyword-face invisible markdown-markup)
1860 "List of properties and values to apply to code block language names.") 1864 "List of properties and values to apply to code block language names.")
@@ -2298,7 +2302,7 @@ Depending on your font, some reasonable choices are:
2298 (markdown--match-highlighting . ((3 markdown-markup-properties) 2302 (markdown--match-highlighting . ((3 markdown-markup-properties)
2299 (4 'markdown-highlighting-face) 2303 (4 'markdown-highlighting-face)
2300 (5 markdown-markup-properties))) 2304 (5 markdown-markup-properties)))
2301 (,markdown-regex-line-break . (1 'markdown-line-break-face prepend)) 2305 (,markdown-regex-line-break . (1 markdown-line-break-properties prepend))
2302 (markdown-match-escape . ((1 markdown-markup-properties prepend))) 2306 (markdown-match-escape . ((1 markdown-markup-properties prepend)))
2303 (markdown-fontify-sub-superscripts) 2307 (markdown-fontify-sub-superscripts)
2304 (markdown-match-inline-attributes . ((0 markdown-markup-properties prepend))) 2308 (markdown-match-inline-attributes . ((0 markdown-markup-properties prepend)))
diff --git a/.emacs.d/lisp/rust-cargo.el b/.emacs.d/lisp/rust-cargo.el
index cfbcd81..bda23d8 100644
--- a/.emacs.d/lisp/rust-cargo.el
+++ b/.emacs.d/lisp/rust-cargo.el
@@ -46,7 +46,8 @@
46 (when (/= ret 0) 46 (when (/= ret 0)
47 (error "`cargo locate-project' returned %s status: %s" ret (buffer-string))) 47 (error "`cargo locate-project' returned %s status: %s" ret (buffer-string)))
48 (goto-char 0) 48 (goto-char 0)
49 (let ((output (json-read))) 49 (let ((output (let ((json-object-type 'alist))
50 (json-read))))
50 (cdr (assoc-string "root" output))))))) 51 (cdr (assoc-string "root" output)))))))
51 52
52(defun rust-buffer-crate () 53(defun rust-buffer-crate ()
diff --git a/.emacs.d/lisp/rust-common.el b/.emacs.d/lisp/rust-common.el
new file mode 100644
index 0000000..b7dd695
--- /dev/null
+++ b/.emacs.d/lisp/rust-common.el
@@ -0,0 +1,20 @@
1;;; rust-common.el --- Common code for both modes -*-lexical-binding: t-*-
2;;; Commentary:
3
4;; rust-common code for both prog-mode and tree-sitter one
5
6;;; Code:
7(require 'rust-rustfmt)
8
9(defcustom rust-before-save-hook 'rust-before-save-method
10 "Function for formatting before save."
11 :type 'function
12 :group 'rust-mode)
13
14(defcustom rust-after-save-hook 'rust-after-save-method
15 "Default method to handle rustfmt invocation after save."
16 :type 'function
17 :group 'rust-mode)
18
19(provide 'rust-common)
20;;; rust-common.el ends here
diff --git a/.emacs.d/lisp/rust-compile.el b/.emacs.d/lisp/rust-compile.el
index 1bb3103..04ac6b6 100644
--- a/.emacs.d/lisp/rust-compile.el
+++ b/.emacs.d/lisp/rust-compile.el
@@ -34,6 +34,12 @@ See `compilation-error-regexp-alist' for help on their format.")
34 "Specifications for matching code references in rustc invocations. 34 "Specifications for matching code references in rustc invocations.
35See `compilation-error-regexp-alist' for help on their format.") 35See `compilation-error-regexp-alist' for help on their format.")
36 36
37(defvar rustc-panics-compilation-regexps
38 (let ((re (concat "thread '[^']+' panicked at " rustc-compilation-location)))
39 (cons re '(2 3 4 nil 1)))
40 "Specifications for matching panics in rustc invocations.
41See `compilation-error-regexp-alist' for help on their format.")
42
37;; Match test run failures and panics during compilation as 43;; Match test run failures and panics during compilation as
38;; compilation warnings 44;; compilation warnings
39(defvar cargo-compilation-regexps 45(defvar cargo-compilation-regexps
@@ -75,6 +81,9 @@ the compilation window until the top of the error is visible."
75 (add-to-list 'compilation-error-regexp-alist 'rustc-colon) 81 (add-to-list 'compilation-error-regexp-alist 'rustc-colon)
76 (add-to-list 'compilation-error-regexp-alist-alist 82 (add-to-list 'compilation-error-regexp-alist-alist
77 (cons 'cargo cargo-compilation-regexps)) 83 (cons 'cargo cargo-compilation-regexps))
84 (add-to-list 'compilation-error-regexp-alist-alist
85 (cons 'rustc-panics rustc-panics-compilation-regexps))
86 (add-to-list 'compilation-error-regexp-alist 'rustc-panics)
78 (add-to-list 'compilation-error-regexp-alist 'cargo) 87 (add-to-list 'compilation-error-regexp-alist 'cargo)
79 (add-hook 'next-error-hook #'rustc-scroll-down-after-next-error))) 88 (add-hook 'next-error-hook #'rustc-scroll-down-after-next-error)))
80 89
diff --git a/.emacs.d/lisp/rust-mode-tests.el b/.emacs.d/lisp/rust-mode-tests.el
index e4949b2..62cd261 100644
--- a/.emacs.d/lisp/rust-mode-tests.el
+++ b/.emacs.d/lisp/rust-mode-tests.el
@@ -45,14 +45,23 @@
45(put 'rust-compare-code-after-manip 'ert-explainer 45(put 'rust-compare-code-after-manip 'ert-explainer
46 'rust-test-explain-bad-manip) 46 'rust-test-explain-bad-manip)
47 47
48(defun rust-test-manip-code (original point-pos manip-func expected) 48(defun rust-test-manip-code (original manip-pos manip-func expected &optional final-pos)
49 (with-temp-buffer 49 (with-temp-buffer
50 (rust-mode) 50 (rust-mode)
51 (insert original) 51 (insert original)
52 (goto-char point-pos) 52 (goto-char manip-pos)
53 (funcall manip-func) 53 (funcall manip-func)
54 (should (rust-compare-code-after-manip 54 (should (rust-compare-code-after-manip
55 original point-pos manip-func expected (buffer-string))))) 55 original manip-pos manip-func expected (buffer-string)))
56 (if final-pos
57 (should (equal (point) final-pos)))))
58
59(defmacro rust-test-with-standard-fill-settings (&rest body)
60 (declare (indent defun))
61 `(let ((fill-column rust-test-fill-column)
62 (sentence-end-double-space t)
63 (colon-double-space nil))
64 ,@body))
56 65
57(defun rust-test-fill-paragraph (unfilled expected &optional start-pos end-pos) 66(defun rust-test-fill-paragraph (unfilled expected &optional start-pos end-pos)
58 "We're going to run through many scenarios here--the point should be able to be anywhere from the start-pos (defaults to 1) through end-pos (defaults to the length of what was passed in) and (fill-paragraph) should return the same result. It should also work with fill-region from start-pos to end-pos. 67 "We're going to run through many scenarios here--the point should be able to be anywhere from the start-pos (defaults to 1) through end-pos (defaults to the length of what was passed in) and (fill-paragraph) should return the same result. It should also work with fill-region from start-pos to end-pos.
@@ -78,7 +87,7 @@ Also, the result should be the same regardless of whether the code is at the beg
78 (concat padding-beginning unfilled padding-end) 87 (concat padding-beginning unfilled padding-end)
79 pos 88 pos
80 (lambda () 89 (lambda ()
81 (let ((fill-column rust-test-fill-column)) 90 (rust-test-with-standard-fill-settings
82 (fill-paragraph))) 91 (fill-paragraph)))
83 (concat padding-beginning expected padding-end))))) 92 (concat padding-beginning expected padding-end)))))
84 ;; In addition to all the fill-paragraph tests, check that it works using fill-region 93 ;; In addition to all the fill-paragraph tests, check that it works using fill-region
@@ -86,7 +95,7 @@ Also, the result should be the same regardless of whether the code is at the beg
86 unfilled 95 unfilled
87 start-pos 96 start-pos
88 (lambda () 97 (lambda ()
89 (let ((fill-column rust-test-fill-column)) 98 (rust-test-with-standard-fill-settings
90 (fill-region start-pos end-pos))) 99 (fill-region start-pos end-pos)))
91 expected) 100 expected)
92 )) 101 ))
@@ -1094,6 +1103,25 @@ fn test4();")
1094 (beginning-of-defun 2) 1103 (beginning-of-defun 2)
1095 (should (eq (point) fn-1))))) 1104 (should (eq (point) fn-1)))))
1096 1105
1106(ert-deftest rust-beginning-of-defun-pub-scoped ()
1107 (let (fn-1-start fn-1-end fn-2-start fn-2-end)
1108 (with-temp-buffer
1109 (rust-mode)
1110 (setq fn-1-start (point))
1111 (insert "pub(crate::mod1) fn test2() {}\n")
1112 (setq fn-1-end (point))
1113 (setq fn-2-start (point))
1114 (insert "pub(self) fn test1() {}\n")
1115 (setq fn-3-end (point))
1116
1117 (goto-char (point-max))
1118
1119 (beginning-of-defun)
1120 (should (eq (point) fn-2-start))
1121
1122 (beginning-of-defun)
1123 (should (eq (point) fn-1-start)))))
1124
1097(ert-deftest rust-end-of-defun-from-middle-of-fn () 1125(ert-deftest rust-end-of-defun-from-middle-of-fn ()
1098 (rust-test-motion 1126 (rust-test-motion
1099 rust-test-motion-string 1127 rust-test-motion-string
@@ -1136,6 +1164,25 @@ fn test4();")
1136 'between-fn1-fn2 1164 'between-fn1-fn2
1137 #'end-of-defun -2)) 1165 #'end-of-defun -2))
1138 1166
1167(ert-deftest rust-end-of-defun-pub-scoped ()
1168 (let (fn-1-start fn-1-end fn-2-start fn-2-end)
1169 (with-temp-buffer
1170 (rust-mode)
1171 (setq fn-1-start (point))
1172 (insert "pub(crate::mod1) fn test2() {}\n")
1173 (setq fn-1-end (point))
1174 (setq fn-2-start (point))
1175 (insert "pub(self) fn test1() {}\n")
1176 (setq fn-2-end (point))
1177
1178 (goto-char (point-min))
1179
1180 (end-of-defun)
1181 (should (eq (point) fn-1-end))
1182
1183 (end-of-defun)
1184 (should (eq (point) fn-2-end)))))
1185
1139(ert-deftest rust-mark-defun-from-middle-of-fn () 1186(ert-deftest rust-mark-defun-from-middle-of-fn ()
1140 (rust-test-region 1187 (rust-test-region
1141 rust-test-region-string 1188 rust-test-region-string
@@ -2752,7 +2799,7 @@ fn foo<T:Fn() -> X<Y>>() -> Z {
2752 ) 2799 )
2753 )) 2800 ))
2754 2801
2755(ert-deftest rust-test-paren-matching-lt-ops-in-fn-params () 2802(ert-deftest rust-test-paren-matching-lt-ops-in-fn-params-1 ()
2756 (rust-test-matching-parens 2803 (rust-test-matching-parens
2757 " 2804 "
2758fn foo(x:i32) { 2805fn foo(x:i32) {
@@ -2764,7 +2811,7 @@ fn foo(x:i32) {
2764 ) 2811 )
2765 )) 2812 ))
2766 2813
2767(ert-deftest rust-test-paren-matching-lt-ops-in-fn-params () 2814(ert-deftest rust-test-paren-matching-lt-ops-in-fn-params-2 ()
2768 (rust-test-matching-parens 2815 (rust-test-matching-parens
2769 " 2816 "
2770fn foo(x:i32) -> bool { 2817fn foo(x:i32) -> bool {
@@ -3116,7 +3163,7 @@ macro_c!{
3116 (syntax-ppss)))) 3163 (syntax-ppss))))
3117 3164
3118 3165
3119(ert-deftest rust-test-in-macro-no-caching () 3166(ert-deftest rust-test-in-macro-around-opening ()
3120 (should-not 3167 (should-not
3121 (with-temp-buffer 3168 (with-temp-buffer
3122 (insert 3169 (insert
@@ -3125,66 +3172,38 @@ macro_c!{
3125 struct Boo<D> {} 3172 struct Boo<D> {}
3126") 3173")
3127 (rust-mode) 3174 (rust-mode)
3128 (search-backward "macro") 3175 (search-backward "macro_c")
3129 ;; do not use the cache 3176 (and
3130 (let ((rust-macro-scopes nil)) 3177 (not (rust-in-macro))
3131 (rust-in-macro))))) 3178 (progn (forward-thing 'symbol 1) (not (rust-in-macro)))
3132 3179 (progn (forward-char 1) (rust-in-macro))
3133(ert-deftest rust-test-in-macro-fake-cache () 3180 (progn (goto-char (point-max)) (rust-in-macro))))))
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)))))
3161 3181
3162(ert-deftest rust-test-in-macro-nested () 3182(ert-deftest rust-test-in-macro-nested ()
3163 (should 3183 (with-temp-buffer
3164 (equal 3184 (insert
3165 (with-temp-buffer 3185 "macro_rules! outer {
3166 (insert
3167 "macro_rules! outer {
3168 () => { vec![] }; 3186 () => { vec![] };
3169}") 3187}")
3170 (rust-mode) 3188 (rust-mode)
3171 (rust-macro-scope (point-min) (point-max))) 3189 (should (progn (goto-char 20) (not (rust-in-macro))))
3172 '((38 40) (20 45))))) 3190 (should (progn (goto-char 21) (eq (rust-in-macro) 20)))
3191 (should (progn (goto-char 38) (eq (rust-in-macro) 20)))
3192 (should (progn (goto-char 39) (eq (rust-in-macro) 38)))
3193 (should (progn (goto-char 40) (eq (rust-in-macro) 20)))
3194 (should (progn (goto-char 44) (eq (rust-in-macro) 20)))
3195 (should (progn (goto-char 45) (not (rust-in-macro))))))
3173 3196
3174(ert-deftest rust-test-in-macro-not-with-space () 3197(ert-deftest rust-test-in-macro-not-with-space ()
3175 (should 3198 (with-temp-buffer
3176 (equal 3199 (insert
3177 (with-temp-buffer
3178 (insert
3179 "fn foo<T>() { 3200 "fn foo<T>() {
3180 if !(mem::size_of::<T>() > 8) { 3201 if !(mem::size_of::<T>() > 8) {
3181 bar() 3202 bar()
3182 } 3203 }
3183}") 3204}")
3184 (rust-mode) 3205 (rust-mode)
3185 (rust-macro-scope (point-min) (point-max))) 3206 (should (progn (goto-char 24) (not (rust-in-macro))))))
3186 'empty)))
3187
3188 3207
3189(ert-deftest rust-test-paren-matching-type-with-module-name () 3208(ert-deftest rust-test-paren-matching-type-with-module-name ()
3190 (rust-test-matching-parens 3209 (rust-test-matching-parens
@@ -3430,19 +3449,72 @@ impl Two<'a> {
3430 "Foo" font-lock-type-face 3449 "Foo" font-lock-type-face
3431 "in" font-lock-keyword-face))) 3450 "in" font-lock-keyword-face)))
3432 3451
3433(ert-deftest rust-test-dbg-wrap-symbol () 3452(ert-deftest rust-test-dbg-wrap-sexp ()
3453 "a valid sexp ahead of current pos"
3434 (rust-test-manip-code 3454 (rust-test-manip-code
3435 "let x = add(first, second);" 3455 "let x = add(first, second);"
3436 15 3456 15
3437 #'rust-dbg-wrap-or-unwrap 3457 #'rust-dbg-wrap-or-unwrap
3438 "let x = add(dbg!(first), second);")) 3458 "let x = add(dbg!(first), second);"
3459 24))
3460
3461(ert-deftest rust-test-dbg-wrap-sexp-fallback ()
3462 "a invalid sexp ahead of current pos"
3463 ;; inside
3464 (rust-test-manip-code
3465 "if let Ok(val) = may_val {}"
3466 27
3467 #'rust-dbg-wrap-or-unwrap
3468 "if let Ok(val) = may_val {dbg!()}"
3469 32)
3470 ;; before
3471 (rust-test-manip-code
3472 "let a = {}"
3473 9
3474 #'rust-dbg-wrap-or-unwrap
3475 "let a = dbg!({})"
3476 17))
3477
3478(ert-deftest rust-test-dbg-wrap-empty-line ()
3479 (rust-test-manip-code
3480 "let a = 1;
3481
3482let b = 1;"
3483 12
3484 #'rust-dbg-wrap-or-unwrap
3485 "let a = 1;
3486dbg!()
3487let b = 1;"
3488 17))
3489
3490(ert-deftest rust-test-dbg-wrap-empty-before-comment ()
3491 (rust-test-manip-code
3492 "let a = 1;
3493// comment
3494let b = 1;"
3495 12
3496 #'rust-dbg-wrap-or-unwrap
3497 "let a = 1;
3498dbg!()// comment
3499let b = 1;"
3500 17)
3501 ;; between statements and comments
3502 (rust-test-manip-code
3503 "let a = 1;// comment
3504let b = 1;"
3505 11
3506 #'rust-dbg-wrap-or-unwrap
3507 "let a = 1;dbg!()// comment
3508let b = 1;"
3509 16))
3439 3510
3440(ert-deftest rust-test-dbg-wrap-symbol-unbalanced () 3511(ert-deftest rust-test-dbg-wrap-symbol-unbalanced ()
3441 (rust-test-manip-code 3512 (rust-test-manip-code
3442 "let x = add((first, second);" 3513 "let x = add((first, second);"
3443 14 3514 14
3444 #'rust-dbg-wrap-or-unwrap 3515 #'rust-dbg-wrap-or-unwrap
3445 "let x = add((dbg!(first), second);")) 3516 "let x = add((dbg!(first), second);"
3517 25))
3446 3518
3447(ert-deftest rust-test-dbg-wrap-region () 3519(ert-deftest rust-test-dbg-wrap-region ()
3448 (rust-test-manip-code 3520 (rust-test-manip-code
@@ -3453,7 +3525,8 @@ impl Two<'a> {
3453 (push-mark nil t t) 3525 (push-mark nil t t)
3454 (goto-char 26) 3526 (goto-char 26)
3455 (rust-dbg-wrap-or-unwrap)) 3527 (rust-dbg-wrap-or-unwrap))
3456 "let x = dbg!(add(first, second));")) 3528 "let x = dbg!(add(first, second));"
3529 33))
3457 3530
3458(defun rust-test-dbg-unwrap (position) 3531(defun rust-test-dbg-unwrap (position)
3459 (rust-test-manip-code 3532 (rust-test-manip-code
@@ -3527,6 +3600,7 @@ impl Two<'a> {
3527 (insert "warning found a -> b\n --> file3.rs:12:34\n\n") 3600 (insert "warning found a -> b\n --> file3.rs:12:34\n\n")
3528 (insert "note: `ZZZ` could also refer to the constant imported here -> b\n --> file4.rs:12:34\n\n") 3601 (insert "note: `ZZZ` could also refer to the constant imported here -> b\n --> file4.rs:12:34\n\n")
3529 (insert " ::: file5.rs:12:34\n\n") 3602 (insert " ::: file5.rs:12:34\n\n")
3603 (insert "thread 'main' panicked at src/file7.rs:12:34:\n\n")
3530 ;; should not match 3604 ;; should not match
3531 (insert "werror found a -> b\n --> no_match.rs:12:34\n\n") 3605 (insert "werror found a -> b\n --> no_match.rs:12:34\n\n")
3532 (insert "error[E0061]: this function takes 1 parameter but 2 parameters were supplied\n --> file6.rs:132:34 3606 (insert "error[E0061]: this function takes 1 parameter but 2 parameters were supplied\n --> file6.rs:132:34
@@ -3545,11 +3619,13 @@ impl Two<'a> {
3545 ("file6.rs" "132" "34" compilation-error "file6.rs:132:34")) 3619 ("file6.rs" "132" "34" compilation-error "file6.rs:132:34"))
3546 (("file5.rs" "12" "34" compilation-info "file5.rs:12:34")) 3620 (("file5.rs" "12" "34" compilation-info "file5.rs:12:34"))
3547 ((like-previous-one "82" back-to-indentation compilation-info "82") 3621 ((like-previous-one "82" back-to-indentation compilation-info "82")
3548 (like-previous-one "132" back-to-indentation compilation-info "132"))) 3622 (like-previous-one "132" back-to-indentation compilation-info "132"))
3623 (("src/file7.rs" "12" "34" nil "src/file7.rs:12:34")))
3549 (mapcar #'rust-collect-matches 3624 (mapcar #'rust-collect-matches
3550 (list rustc-compilation-regexps 3625 (list rustc-compilation-regexps
3551 rustc-colon-compilation-regexps 3626 rustc-colon-compilation-regexps
3552 rustc-refs-compilation-regexps)))))) 3627 rustc-refs-compilation-regexps
3628 rustc-panics-compilation-regexps))))))
3553 3629
3554;; If electric-pair-mode is available, load it and run the tests that use it. If not, 3630;; If electric-pair-mode is available, load it and run the tests that use it. If not,
3555;; no error--the tests will be skipped. 3631;; no error--the tests will be skipped.
diff --git a/.emacs.d/lisp/rust-mode-treesitter.el b/.emacs.d/lisp/rust-mode-treesitter.el
new file mode 100644
index 0000000..89c7cd4
--- /dev/null
+++ b/.emacs.d/lisp/rust-mode-treesitter.el
@@ -0,0 +1,25 @@
1;;; rust-mode-treesitter.el --- use native rust-ts-mode -*-lexical-binding: t-*-
2;;; Commentary:
3
4;; Derive from rust-ts-mode instead of prog-mode
5
6;;; Code:
7
8(when (version<= "29.1" emacs-version)
9 ;; We have the when macro because of
10 ;; https://github.com/rust-lang/rust-mode/issues/520
11 (require 'treesit)
12 (require 'rust-ts-mode)
13 (require 'rust-common)
14
15 (define-derived-mode rust-mode rust-ts-mode "Rust"
16 "Major mode for Rust code.
17
18\\{rust-mode-map}"
19 :group 'rust-mode
20
21 (add-hook 'before-save-hook rust-before-save-hook nil t)
22 (add-hook 'after-save-hook rust-after-save-hook nil t)))
23
24(provide 'rust-mode-treesitter)
25;;; rust-mode-treesitter.el ends here
diff --git a/.emacs.d/lisp/rust-mode.el b/.emacs.d/lisp/rust-mode.el
index 80106b8..a0f6542 100644
--- a/.emacs.d/lisp/rust-mode.el
+++ b/.emacs.d/lisp/rust-mode.el
@@ -1,7 +1,7 @@
1;;; rust-mode.el --- A major-mode for editing Rust source code -*-lexical-binding: t-*- 1;;; rust-mode.el --- A major-mode for editing Rust source code -*-lexical-binding: t-*-
2 2
3;; Version: 1.0.5 3;; Version: 1.0.5
4;; Author: Mozilla 4;; Author: Mozilla <rust-mode@noreply.github.com>
5;; Url: https://github.com/rust-lang/rust-mode 5;; Url: https://github.com/rust-lang/rust-mode
6;; Keywords: languages 6;; Keywords: languages
7;; Package-Requires: ((emacs "25.1")) 7;; Package-Requires: ((emacs "25.1"))
@@ -14,8 +14,11 @@
14;; This package implements a major-mode for editing Rust source code. 14;; This package implements a major-mode for editing Rust source code.
15 15
16;;; Code: 16;;; Code:
17(require 'rust-common)
17 18
18(eval-when-compile (require 'rx)) 19(eval-when-compile
20 (require 'rx)
21 (require 'subr-x))
19 22
20(defvar rust-load-optional-libraries t 23(defvar rust-load-optional-libraries t
21 "Whether loading `rust-mode' also loads optional libraries. 24 "Whether loading `rust-mode' also loads optional libraries.
@@ -27,26 +30,6 @@ This variable might soon be remove again.")
27 (require 'rust-playpen) 30 (require 'rust-playpen)
28 (require 'rust-rustfmt)) 31 (require 'rust-rustfmt))
29 32
30(defvar electric-pair-inhibit-predicate)
31(defvar electric-pair-skip-self)
32(defvar electric-indent-chars)
33
34(defcustom rust-before-save-hook 'rust-before-save-method
35 "Function for formatting before save."
36 :type 'function
37 :group 'rust-mode)
38
39(defcustom rust-after-save-hook 'rust-after-save-method
40 "Default method to handle rustfmt invocation after save."
41 :type 'function
42 :group 'rust-mode)
43
44(defvar rust-prettify-symbols-alist
45 '(("&&" . ?∧) ("||" . ?∨)
46 ("<=" . ?≤) (">=" . ?≥) ("!=" . ?≠)
47 ("INFINITY" . ?∞) ("->" . ?→) ("=>" . ?⇒))
48 "Alist of symbol prettifications used for `prettify-symbols-alist'.")
49
50;;; Customization 33;;; Customization
51 34
52(defgroup rust-mode nil 35(defgroup rust-mode nil
@@ -54,37 +37,12 @@ This variable might soon be remove again.")
54 :link '(url-link "https://www.rust-lang.org/") 37 :link '(url-link "https://www.rust-lang.org/")
55 :group 'languages) 38 :group 'languages)
56 39
57(defcustom rust-indent-offset 4 40(defcustom rust-mode-treesitter-derive nil
58 "Indent Rust code by this number of spaces." 41 "Whether rust-mode should derive from the new treesitter mode `rust-ts-mode'
59 :type 'integer 42instead of `prog-mode'. This option requires emacs29+."
60 :group 'rust-mode 43 :version "29.1"
61 :safe #'integerp)
62
63(defcustom rust-indent-method-chain nil
64 "Indent Rust method chains, aligned by the `.' operators."
65 :type 'boolean
66 :group 'rust-mode
67 :safe #'booleanp)
68
69(defcustom rust-indent-where-clause nil
70 "Indent lines starting with the `where' keyword following a function or trait.
71When nil, `where' will be aligned with `fn' or `trait'."
72 :type 'boolean 44 :type 'boolean
73 :group 'rust-mode 45 :group 'rustic)
74 :safe #'booleanp)
75
76(defcustom rust-match-angle-brackets t
77 "Whether to enable angle bracket (`<' and `>') matching where appropriate."
78 :type 'boolean
79 :safe #'booleanp
80 :group 'rust-mode)
81
82(defcustom rust-indent-return-type-to-arguments t
83 "Indent a line starting with the `->' (RArrow) following a function, aligning
84to the function arguments. When nil, `->' will be indented one level."
85 :type 'boolean
86 :group 'rust-mode
87 :safe #'booleanp)
88 46
89;;; Faces 47;;; Faces
90 48
@@ -97,141 +55,6 @@ to the function arguments. When nil, `->' will be indented one level."
97(define-obsolete-face-alias 'rust-string-interpolation-face 55(define-obsolete-face-alias 'rust-string-interpolation-face
98 'rust-string-interpolation "0.6.0") 56 'rust-string-interpolation "0.6.0")
99 57
100(defface rust-unsafe
101 '((t :inherit font-lock-warning-face))
102 "Face for the `unsafe' keyword."
103 :group 'rust-mode)
104
105(defface rust-question-mark
106 '((t :weight bold :inherit font-lock-builtin-face))
107 "Face for the question mark operator."
108 :group 'rust-mode)
109
110(defface rust-ampersand-face
111 '((t :inherit default))
112 "Face for the ampersand reference mark."
113 :group 'rust-mode)
114
115(defface rust-builtin-formatting-macro
116 '((t :inherit font-lock-builtin-face))
117 "Face for builtin formatting macros (print! &c.)."
118 :group 'rust-mode)
119
120(defface rust-string-interpolation
121 '((t :slant italic :inherit font-lock-string-face))
122 "Face for interpolating braces in builtin formatting macro strings."
123 :group 'rust-mode)
124
125;;; Syntax
126
127(defun rust-re-word (inner) (concat "\\<" inner "\\>"))
128(defun rust-re-grab (inner) (concat "\\(" inner "\\)"))
129(defun rust-re-shy (inner) (concat "\\(?:" inner "\\)"))
130
131(defconst rust-re-ident "[[:word:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*")
132(defconst rust-re-lc-ident "[[:lower:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*")
133(defconst rust-re-uc-ident "[[:upper:]][[:word:][:multibyte:]_[:digit:]]*")
134(defvar rust-re-vis
135 ;; pub | pub ( crate ) | pub ( self ) | pub ( super ) | pub ( in SimplePath )
136 (concat
137 "pub"
138 (rust-re-shy
139 (concat
140 "[[:space:]]*([[:space:]]*"
141 (rust-re-shy
142 (concat "crate" "\\|"
143 "\\(?:s\\(?:elf\\|uper\\)\\)" "\\|"
144 ;; in SimplePath
145 (rust-re-shy
146 (concat
147 "in[[:space:]]+"
148 rust-re-ident
149 (rust-re-shy (concat "::" rust-re-ident)) "*"))))
150 "[[:space:]]*)"))
151 "?"))
152(defconst rust-re-unsafe "unsafe")
153(defconst rust-re-extern "extern")
154(defconst rust-re-async-or-const "async\\|const")
155(defconst rust-re-generic
156 (concat "<[[:space:]]*'" rust-re-ident "[[:space:]]*>"))
157(defconst rust-re-union
158 (rx-to-string
159 `(seq
160 (or space line-start)
161 (group symbol-start "union" symbol-end)
162 (+ space) (regexp ,rust-re-ident))))
163
164(defun rust-re-item-def (itype)
165 (concat (rust-re-word itype)
166 (rust-re-shy rust-re-generic) "?"
167 "[[:space:]]+" (rust-re-grab rust-re-ident)))
168
169;; TODO some of this does only make sense for `fn' (unsafe, extern...)
170;; and not other items
171(defun rust-re-item-def-imenu (itype)
172 (concat "^[[:space:]]*"
173 (rust-re-shy (concat rust-re-vis "[[:space:]]+")) "?"
174 (rust-re-shy (concat (rust-re-word "default") "[[:space:]]+")) "?"
175 (rust-re-shy (concat (rust-re-shy rust-re-async-or-const) "[[:space:]]+")) "?"
176 (rust-re-shy (concat (rust-re-word rust-re-unsafe) "[[:space:]]+")) "?"
177 (rust-re-shy (concat (rust-re-word rust-re-extern) "[[:space:]]+"
178 (rust-re-shy "\"[^\"]+\"[[:space:]]+") "?")) "?"
179 (rust-re-item-def itype)))
180
181(defvar rust-imenu-generic-expression
182 (append (mapcar #'(lambda (x)
183 (list (capitalize x) (rust-re-item-def-imenu x) 1))
184 '("enum" "struct" "union" "type" "mod" "fn" "trait" "impl"))
185 `(("Macro" ,(rust-re-item-def-imenu "macro_rules!") 1)))
186 "Value for `imenu-generic-expression' in Rust mode.
187
188Create a hierarchical index of the item definitions in a Rust file.
189
190Imenu will show all the enums, structs, etc. in their own subheading.
191Use idomenu (imenu with `ido-mode') for best mileage.")
192
193(defvar rust-mode-syntax-table
194 (let ((table (make-syntax-table)))
195
196 ;; Operators
197 (dolist (i '(?+ ?- ?* ?/ ?% ?& ?| ?^ ?! ?< ?> ?~ ?@))
198 (modify-syntax-entry i "." table))
199
200 ;; Strings
201 (modify-syntax-entry ?\" "\"" table)
202 (modify-syntax-entry ?\\ "\\" table)
203
204 ;; Angle brackets. We suppress this with syntactic propertization
205 ;; when needed
206 (modify-syntax-entry ?< "(>" table)
207 (modify-syntax-entry ?> ")<" table)
208
209 ;; Comments
210 (modify-syntax-entry ?/ ". 124b" table)
211 (modify-syntax-entry ?* ". 23n" table)
212 (modify-syntax-entry ?\n "> b" table)
213 (modify-syntax-entry ?\^m "> b" table)
214
215 table)
216 "Syntax definitions and helpers.")
217
218;;; Prettify
219
220(defun rust--prettify-symbols-compose-p (start end match)
221 "Return true iff the symbol MATCH should be composed.
222See `prettify-symbols-compose-predicate'."
223 (and (fboundp 'prettify-symbols-default-compose-p)
224 (prettify-symbols-default-compose-p start end match)
225 ;; Make sure || is not a closure with 0 arguments and && is not
226 ;; a double reference.
227 (pcase match
228 ("||" (not (save-excursion
229 (goto-char start)
230 (looking-back "\\(?:\\<move\\|[[({:=,;]\\) *"
231 (line-beginning-position)))))
232 ("&&" (char-equal (char-after end) ?\s))
233 (_ t))))
234
235;;; Mode 58;;; Mode
236 59
237(defvar rust-mode-map 60(defvar rust-mode-map
@@ -248,1384 +71,16 @@ See `prettify-symbols-compose-predicate'."
248 map) 71 map)
249 "Keymap for Rust major mode.") 72 "Keymap for Rust major mode.")
250 73
251;;;###autoload 74(if (and (version<= "29.1" emacs-version) rust-mode-treesitter-derive)
252(define-derived-mode rust-mode prog-mode "Rust" 75 (require 'rust-mode-treesitter)
253 "Major mode for Rust code. 76 (require 'rust-prog-mode))
254
255\\{rust-mode-map}"
256 :group 'rust-mode
257 :syntax-table rust-mode-syntax-table
258
259 ;; Syntax
260 (setq-local syntax-propertize-function #'rust-syntax-propertize)
261
262 ;; Indentation
263 (setq-local indent-line-function 'rust-mode-indent-line)
264
265 ;; Fonts
266 (setq-local font-lock-defaults
267 '(rust-font-lock-keywords
268 nil nil nil nil
269 (font-lock-syntactic-face-function
270 . rust-mode-syntactic-face-function)))
271
272 ;; Misc
273 (setq-local comment-start "// ")
274 (setq-local comment-end "")
275 (setq-local open-paren-in-column-0-is-defun-start nil)
276 77
277 ;; Auto indent on } 78;;;###autoload
278 (setq-local electric-indent-chars 79(autoload 'rust-mode "rust-mode" "Major mode for Rust code." t)
279 (cons ?} (and (boundp 'electric-indent-chars)
280 electric-indent-chars)))
281
282 ;; Allow paragraph fills for comments
283 (setq-local comment-start-skip "\\(?://[/!]*\\|/\\*[*!]?\\)[[:space:]]*")
284 (setq-local paragraph-start
285 (concat "[[:space:]]*\\(?:"
286 comment-start-skip
287 "\\|\\*/?[[:space:]]*\\|\\)$"))
288 (setq-local paragraph-separate paragraph-start)
289 (setq-local normal-auto-fill-function #'rust-do-auto-fill)
290 (setq-local fill-paragraph-function #'rust-fill-paragraph)
291 (setq-local fill-forward-paragraph-function #'rust-fill-forward-paragraph)
292 (setq-local adaptive-fill-function #'rust-find-fill-prefix)
293 (setq-local adaptive-fill-first-line-regexp "")
294 (setq-local comment-multi-line t)
295 (setq-local comment-line-break-function #'rust-comment-indent-new-line)
296 (setq-local imenu-generic-expression rust-imenu-generic-expression)
297 (setq-local imenu-syntax-alist '((?! . "w"))) ; For macro_rules!
298 (setq-local beginning-of-defun-function #'rust-beginning-of-defun)
299 (setq-local end-of-defun-function #'rust-end-of-defun)
300 (setq-local parse-sexp-lookup-properties t)
301 (setq-local electric-pair-inhibit-predicate
302 #'rust-electric-pair-inhibit-predicate-wrap)
303 (add-function :before-until (local 'electric-pair-skip-self)
304 #'rust-electric-pair-skip-self)
305 ;; Configure prettify
306 (setq prettify-symbols-alist rust-prettify-symbols-alist)
307 (setq prettify-symbols-compose-predicate #'rust--prettify-symbols-compose-p)
308
309 (add-hook 'before-save-hook rust-before-save-hook nil t)
310 (add-hook 'after-save-hook rust-after-save-hook nil t))
311 80
312;;;###autoload 81;;;###autoload
313(add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-mode)) 82(add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-mode))
314 83
315(defvar rust-top-item-beg-re
316 (concat "\\s-*\\(?:priv\\|pub\\)?\\s-*"
317 ;; TODO some of this does only make sense for `fn' (unsafe, extern...)
318 ;; and not other items
319 (rust-re-shy (concat (rust-re-shy rust-re-vis) "[[:space:]]+")) "?"
320 (rust-re-shy (concat (rust-re-shy rust-re-async-or-const) "[[:space:]]+")) "?"
321 (rust-re-shy (concat (rust-re-shy rust-re-unsafe) "[[:space:]]+")) "?"
322 (regexp-opt
323 '("enum" "struct" "union" "type" "mod" "fn" "static" "impl"
324 "extern" "trait" "async"))
325 "\\_>")
326 "Start of a Rust item.")
327
328(defconst rust-re-type-or-constructor
329 (rx symbol-start
330 (group upper (0+ (any word nonascii digit "_")))
331 symbol-end))
332
333(defconst rust-keywords
334 '("as" "async" "await"
335 "box" "break"
336 "const" "continue" "crate"
337 "do" "dyn"
338 "else" "enum" "extern" "existential"
339 "false" "fn" "for"
340 "if" "impl" "in"
341 "let" "loop"
342 "match" "mod" "move" "mut"
343 "priv" "pub"
344 "ref" "return"
345 "self" "static" "struct" "super"
346 "true" "trait" "type" "try"
347 "use"
348 "virtual"
349 "where" "while"
350 "yield")
351 "Font-locking definitions and helpers.")
352
353(defconst rust-special-types
354 '("u8" "i8"
355 "u16" "i16"
356 "u32" "i32"
357 "u64" "i64"
358 "u128" "i128"
359
360 "f32" "f64"
361 "isize" "usize"
362 "bool"
363 "str" "char"))
364
365(defconst rust-number-with-type
366 (eval-when-compile
367 (concat
368 "\\_<\\(?:0[box]?\\|[1-9]\\)[[:digit:]a-fA-F_.]*\\(?:[eE][+-]?[[:digit:]_]\\)?"
369 (regexp-opt '("u8" "i8" "u16" "i16" "u32" "i32" "u64" "i64"
370 "u128" "i128" "usize" "isize" "f32" "f64")
371 t)
372 "\\_>"))
373 "Regular expression matching a number with a type suffix.")
374
375(defvar rust-builtin-formatting-macros
376 '("eprint"
377 "eprintln"
378 "format"
379 "print"
380 "println")
381 "List of builtin Rust macros for string formatting.
382This is used by `rust-font-lock-keywords'.
383\(`write!' is handled separately).")
384
385(defvar rust-formatting-macro-opening-re
386 "[[:space:]\n]*[({[][[:space:]\n]*"
387 "Regular expression to match the opening delimiter of a Rust formatting macro.")
388
389(defvar rust-start-of-string-re
390 "\\(?:r#*\\)?\""
391 "Regular expression to match the start of a Rust raw string.")
392
393(defun rust-path-font-lock-matcher (re-ident)
394 "Match occurrences of RE-IDENT followed by a double-colon.
395Examples include to match names like \"foo::\" or \"Foo::\".
396Does not match type annotations of the form \"foo::<\"."
397 `(lambda (limit)
398 (catch 'rust-path-font-lock-matcher
399 (while t
400 (let* ((symbol-then-colons (rx-to-string '(seq (group (regexp ,re-ident)) "::")))
401 (match (re-search-forward symbol-then-colons limit t)))
402 (cond
403 ;; If we didn't find a match, there are no more occurrences
404 ;; of foo::, so return.
405 ((null match) (throw 'rust-path-font-lock-matcher nil))
406 ;; If this isn't a type annotation foo::<, we've found a
407 ;; match, so a return it!
408 ((not (looking-at (rx (0+ space) "<")))
409 (throw 'rust-path-font-lock-matcher match))))))))
410
411(defvar rust-font-lock-keywords
412 (append
413 `(
414 ;; Keywords proper
415 (,(regexp-opt rust-keywords 'symbols) . font-lock-keyword-face)
416
417 ;; Contextual keywords
418 ("\\_<\\(default\\)[[:space:]]+fn\\_>" 1 font-lock-keyword-face)
419 (,rust-re-union 1 font-lock-keyword-face)
420
421 ;; Special types
422 (,(regexp-opt rust-special-types 'symbols) . font-lock-type-face)
423
424 ;; The unsafe keyword
425 ("\\_<unsafe\\_>" . 'rust-unsafe)
426
427 ;; Attributes like `#[bar(baz)]` or `#![bar(baz)]` or `#[bar = "baz"]`
428 (,(rust-re-grab (concat "#\\!?\\[" rust-re-ident "[^]]*\\]"))
429 1 font-lock-preprocessor-face keep)
430
431 ;; Builtin formatting macros
432 (,(concat (rust-re-grab
433 (concat (rust-re-word (regexp-opt rust-builtin-formatting-macros))
434 "!"))
435 rust-formatting-macro-opening-re
436 "\\(?:" rust-start-of-string-re "\\)?")
437 (1 'rust-builtin-formatting-macro)
438 (rust-string-interpolation-matcher
439 (rust-end-of-string)
440 nil
441 (0 'rust-string-interpolation t nil)))
442
443 ;; write! macro
444 (,(concat (rust-re-grab (concat (rust-re-word "write\\(ln\\)?") "!"))
445 rust-formatting-macro-opening-re
446 "[[:space:]]*[^\"]+,[[:space:]]*"
447 rust-start-of-string-re)
448 (1 'rust-builtin-formatting-macro)
449 (rust-string-interpolation-matcher
450 (rust-end-of-string)
451 nil
452 (0 'rust-string-interpolation t nil)))
453
454 ;; Syntax extension invocations like `foo!`, highlight including the !
455 (,(concat (rust-re-grab (concat rust-re-ident "!")) "[({[:space:][]")
456 1 font-lock-preprocessor-face)
457
458 ;; Field names like `foo:`, highlight excluding the :
459 (,(concat (rust-re-grab rust-re-ident) "[[:space:]]*:[^:]")
460 1 font-lock-variable-name-face)
461
462 ;; CamelCase Means Type Or Constructor
463 (,rust-re-type-or-constructor 1 font-lock-type-face)
464
465 ;; Type-inferred binding
466 (,(concat "\\_<\\(?:let\\s-+ref\\|let\\|ref\\|for\\)\\s-+\\(?:mut\\s-+\\)?"
467 (rust-re-grab rust-re-ident)
468 "\\_>")
469 1 font-lock-variable-name-face)
470
471 ;; Type names like `Foo::`, highlight excluding the ::
472 (,(rust-path-font-lock-matcher rust-re-uc-ident) 1 font-lock-type-face)
473
474 ;; Module names like `foo::`, highlight excluding the ::
475 (,(rust-path-font-lock-matcher rust-re-lc-ident) 1 font-lock-constant-face)
476
477 ;; Lifetimes like `'foo`
478 (,(concat "'" (rust-re-grab rust-re-ident) "[^']") 1 font-lock-variable-name-face)
479
480 ;; Question mark operator
481 ("\\?" . 'rust-question-mark)
482 ("\\(&+\\)\\(?:'\\(?:\\<\\|_\\)\\|\\<\\|[[({:*_|]\\)"
483 1 'rust-ampersand-face)
484 ;; Numbers with type suffix
485 (,rust-number-with-type 1 font-lock-type-face)
486 )
487
488 ;; Ensure we highlight `Foo` in `struct Foo` as a type.
489 (mapcar #'(lambda (x)
490 (list (rust-re-item-def (car x))
491 1 (cdr x)))
492 '(("enum" . font-lock-type-face)
493 ("struct" . font-lock-type-face)
494 ("union" . font-lock-type-face)
495 ("type" . font-lock-type-face)
496 ("mod" . font-lock-constant-face)
497 ("use" . font-lock-constant-face)
498 ("fn" . font-lock-function-name-face)))))
499
500(defun rust-end-of-string ()
501 "Skip to the end of the current string."
502 (save-excursion
503 (skip-syntax-forward "^\"|")
504 (skip-syntax-forward "\"|")
505 (point)))
506
507(defun rust-looking-back-str (str)
508 "Return non-nil if there's a match on the text before point and STR.
509Like `looking-back' but for fixed strings rather than regexps (so
510that it's not so slow)."
511 (let ((len (length str)))
512 (and (> (point) len)
513 (equal str (buffer-substring-no-properties (- (point) len) (point))))))
514
515(defun rust-looking-back-symbols (symbols)
516 "Return non-nil if the point is after a member of SYMBOLS.
517SYMBOLS is a list of strings that represent the respective
518symbols."
519 (save-excursion
520 (let* ((pt-orig (point))
521 (beg-of-symbol (progn (forward-thing 'symbol -1) (point)))
522 (end-of-symbol (progn (forward-thing 'symbol 1) (point))))
523 (and
524 (= end-of-symbol pt-orig)
525 (member (buffer-substring-no-properties beg-of-symbol pt-orig)
526 symbols)))))
527
528(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)))
532
533(defun rust-looking-back-macro ()
534 "Non-nil if looking back at an ident followed by a !
535
536This is stricter than rust syntax which allows a space between
537the ident and the ! symbol. If this space is allowed, then we
538would also need a keyword check to avoid `if !(condition)` being
539seen as a macro."
540 (if (> (- (point) (point-min)) 1)
541 (save-excursion
542 (backward-char)
543 (and (= ?! (char-after))
544 (rust-looking-back-ident)))))
545
546;;; Syntax definitions and helpers
547
548(defun rust-paren-level () (nth 0 (syntax-ppss)))
549(defun rust-in-str () (nth 3 (syntax-ppss)))
550(defun rust-in-str-or-cmnt () (nth 8 (syntax-ppss)))
551(defun rust-rewind-past-str-cmnt () (goto-char (nth 8 (syntax-ppss))))
552
553(defun rust-rewind-irrelevant ()
554 (let ((continue t))
555 (while continue
556 (let ((starting (point)))
557 (skip-chars-backward "[:space:]\n")
558 (when (rust-looking-back-str "*/")
559 (backward-char))
560 (when (rust-in-str-or-cmnt)
561 (rust-rewind-past-str-cmnt))
562 ;; Rewind until the point no longer moves
563 (setq continue (/= starting (point)))))))
564
565(defvar-local rust-macro-scopes nil
566 "Cache for the scopes calculated by `rust-macro-scope'.
567
568This variable can be `let' bound directly or indirectly around
569`rust-macro-scope' as an optimization but should not be otherwise
570set.")
571
572(defun rust-macro-scope (start end)
573 "Return the scope of macros in the buffer.
574
575The return value is a list of (START END) positions in the
576buffer.
577
578If set START and END are optimizations which limit the return
579value 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)
627 "Return non-nil when point is within the scope of a macro.
628
629If START and END are set, minimize the buffer analysis to
630approximately this location as an optimization.
631
632Alternatively, if `rust-macro-scopes' is a list use the scope
633information in this variable. This last is an optimization and
634the 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)))))
649
650(defun rust-looking-at-where ()
651 "Return T when looking at the \"where\" keyword."
652 (and (looking-at-p "\\bwhere\\b")
653 (not (rust-in-str-or-cmnt))))
654
655(defun rust-rewind-to-where (&optional limit)
656 "Rewind the point to the closest occurrence of the \"where\" keyword.
657Return T iff a where-clause was found. Does not rewind past
658LIMIT when passed, otherwise only stops at the beginning of the
659buffer."
660 (when (re-search-backward "\\bwhere\\b" limit t)
661 (if (rust-in-str-or-cmnt)
662 (rust-rewind-to-where limit)
663 t)))
664
665(defconst rust-re-pre-expression-operators "[-=!%&*/:<>[{(|.^;}]")
666
667(defconst rust-re-special-types (regexp-opt rust-special-types 'symbols))
668
669(defun rust-align-to-expr-after-brace ()
670 (save-excursion
671 (forward-char)
672 ;; We don't want to indent out to the open bracket if the
673 ;; open bracket ends the line
674 (when (not (looking-at "[[:blank:]]*\\(?://.*\\)?$"))
675 (when (looking-at "[[:space:]]")
676 (forward-word 1)
677 (backward-word 1))
678 (current-column))))
679
680(defun rust-rewind-to-beginning-of-current-level-expr ()
681 (let ((current-level (rust-paren-level)))
682 (back-to-indentation)
683 (when (looking-at "->")
684 (rust-rewind-irrelevant)
685 (back-to-indentation))
686 (while (> (rust-paren-level) current-level)
687 (backward-up-list)
688 (back-to-indentation))
689 ;; When we're in the where clause, skip over it. First find out the start
690 ;; of the function and its paren level.
691 (let ((function-start nil) (function-level nil))
692 (save-excursion
693 (rust-beginning-of-defun)
694 (back-to-indentation)
695 ;; Avoid using multiple-value-bind
696 (setq function-start (point)
697 function-level (rust-paren-level)))
698 ;; On a where clause
699 (when (or (rust-looking-at-where)
700 ;; or in one of the following lines, e.g.
701 ;; where A: Eq
702 ;; B: Hash <- on this line
703 (and (save-excursion
704 (rust-rewind-to-where function-start))
705 (= current-level function-level)))
706 (goto-char function-start)))))
707
708(defun rust-align-to-method-chain ()
709 (save-excursion
710 ;; for method-chain alignment to apply, we must be looking at
711 ;; another method call or field access or something like
712 ;; that. This avoids rather "eager" jumps in situations like:
713 ;;
714 ;; {
715 ;; something.foo()
716 ;; <indent>
717 ;;
718 ;; Without this check, we would wind up with the cursor under the
719 ;; `.`. In an older version, I had the inverse of the current
720 ;; check, where we checked for situations that should NOT indent,
721 ;; vs checking for the one situation where we SHOULD. It should be
722 ;; clear that this is more robust, but also I find it mildly less
723 ;; annoying to have to press tab again to align to a method chain
724 ;; than to have an over-eager indent in all other cases which must
725 ;; be undone via tab.
726
727 (when (looking-at (concat "\s*\." rust-re-ident))
728 (forward-line -1)
729 (end-of-line)
730 ;; Keep going up (looking for a line that could contain a method chain)
731 ;; while we're in a comment or on a blank line. Stop when the paren
732 ;; level changes.
733 (let ((level (rust-paren-level)))
734 (while (and (or (rust-in-str-or-cmnt)
735 ;; Only whitespace (or nothing) from the beginning to
736 ;; the end of the line.
737 (looking-back "^\s*" (line-beginning-position)))
738 (= (rust-paren-level) level))
739 (forward-line -1)
740 (end-of-line)))
741
742 (let
743 ;; skip-dot-identifier is used to position the point at the
744 ;; `.` when looking at something like
745 ;;
746 ;; foo.bar
747 ;; ^ ^
748 ;; | |
749 ;; | position of point
750 ;; returned offset
751 ;;
752 ((skip-dot-identifier
753 (lambda ()
754 (when (and (rust-looking-back-ident)
755 (save-excursion
756 (forward-thing 'symbol -1)
757 (= ?. (char-before))))
758 (forward-thing 'symbol -1)
759 (backward-char)
760 (- (current-column) rust-indent-offset)))))
761 (cond
762 ;; foo.bar(...)
763 ((looking-back "[)?]" (1- (point)))
764 (backward-list 1)
765 (funcall skip-dot-identifier))
766
767 ;; foo.bar
768 (t (funcall skip-dot-identifier)))))))
769
770(defun rust-mode-indent-line ()
771 (interactive)
772 (let ((indent
773 (save-excursion
774 (back-to-indentation)
775 ;; Point is now at beginning of current line
776 (let* ((level (rust-paren-level))
777 (baseline
778 ;; Our "baseline" is one level out from the
779 ;; indentation of the expression containing the
780 ;; innermost enclosing opening bracket. That way
781 ;; if we are within a block that has a different
782 ;; indentation than this mode would give it, we
783 ;; still indent the inside of it correctly relative
784 ;; to the outside.
785 (if (= 0 level)
786 0
787 (or
788 (when rust-indent-method-chain
789 (rust-align-to-method-chain))
790 (save-excursion
791 (rust-rewind-irrelevant)
792 (backward-up-list)
793 (rust-rewind-to-beginning-of-current-level-expr)
794 (+ (current-column) rust-indent-offset))))))
795 (cond
796 ;; Indent inside a non-raw string only if the previous line
797 ;; ends with a backslash that is inside the same string
798 ((nth 3 (syntax-ppss))
799 (let*
800 ((string-begin-pos (nth 8 (syntax-ppss)))
801 (end-of-prev-line-pos
802 (and (not (rust--same-line-p (point) (point-min)))
803 (line-end-position 0))))
804 (when
805 (and
806 ;; If the string begins with an "r" it's a raw string and
807 ;; we should not change the indentation
808 (/= ?r (char-after string-begin-pos))
809
810 ;; If we're on the first line this will be nil and the
811 ;; rest does not apply
812 end-of-prev-line-pos
813
814 ;; The end of the previous line needs to be inside the
815 ;; current string...
816 (> end-of-prev-line-pos string-begin-pos)
817
818 ;; ...and end with a backslash
819 (= ?\\ (char-before end-of-prev-line-pos)))
820
821 ;; Indent to the same level as the previous line, or the
822 ;; start of the string if the previous line starts the string
823 (if (rust--same-line-p end-of-prev-line-pos string-begin-pos)
824 ;; The previous line is the start of the string.
825 ;; If the backslash is the only character after the
826 ;; string beginning, indent to the next indent
827 ;; level. Otherwise align with the start of the string.
828 (if (> (- end-of-prev-line-pos string-begin-pos) 2)
829 (save-excursion
830 (goto-char (+ 1 string-begin-pos))
831 (current-column))
832 baseline)
833
834 ;; The previous line is not the start of the string, so
835 ;; match its indentation.
836 (save-excursion
837 (goto-char end-of-prev-line-pos)
838 (back-to-indentation)
839 (current-column))))))
840
841 ;; A function return type is indented to the corresponding
842 ;; function arguments, if -to-arguments is selected.
843 ((and rust-indent-return-type-to-arguments
844 (looking-at "->"))
845 (save-excursion
846 (backward-list)
847 (or (rust-align-to-expr-after-brace)
848 (+ baseline rust-indent-offset))))
849
850 ;; A closing brace is 1 level unindented
851 ((looking-at "[]})]") (- baseline rust-indent-offset))
852
853 ;; Doc comments in /** style with leading * indent to line up the *s
854 ((and (nth 4 (syntax-ppss)) (looking-at "*"))
855 (+ 1 baseline))
856
857 ;; When the user chose not to indent the start of the where
858 ;; clause, put it on the baseline.
859 ((and (not rust-indent-where-clause)
860 (rust-looking-at-where))
861 baseline)
862
863 ;; If we're in any other token-tree / sexp, then:
864 (t
865 (or
866 ;; If we are inside a pair of braces, with something after the
867 ;; open brace on the same line and ending with a comma, treat
868 ;; it as fields and align them.
869 (when (> level 0)
870 (save-excursion
871 (rust-rewind-irrelevant)
872 (backward-up-list)
873 ;; Point is now at the beginning of the containing set of braces
874 (rust-align-to-expr-after-brace)))
875
876 ;; When where-clauses are spread over multiple lines, clauses
877 ;; should be aligned on the type parameters. In this case we
878 ;; take care of the second and following clauses (the ones
879 ;; that don't start with "where ")
880 (save-excursion
881 ;; Find the start of the function, we'll use this to limit
882 ;; our search for "where ".
883 (let ((function-start nil) (function-level nil))
884 (save-excursion
885 ;; If we're already at the start of a function,
886 ;; don't go back any farther. We can easily do
887 ;; this by moving to the end of the line first.
888 (end-of-line)
889 (rust-beginning-of-defun)
890 (back-to-indentation)
891 ;; Avoid using multiple-value-bind
892 (setq function-start (point)
893 function-level (rust-paren-level)))
894 ;; When we're not on a line starting with "where ", but
895 ;; still on a where-clause line, go to "where "
896 (when (and
897 (not (rust-looking-at-where))
898 ;; We're looking at something like "F: ..."
899 (looking-at (concat rust-re-ident ":"))
900 ;; There is a "where " somewhere after the
901 ;; start of the function.
902 (rust-rewind-to-where function-start)
903 ;; Make sure we're not inside the function
904 ;; already (e.g. initializing a struct) by
905 ;; checking we are the same level.
906 (= function-level level))
907 ;; skip over "where"
908 (forward-char 5)
909 ;; Unless "where" is at the end of the line
910 (if (eolp)
911 ;; in this case the type parameters bounds are just
912 ;; indented once
913 (+ baseline rust-indent-offset)
914 ;; otherwise, skip over whitespace,
915 (skip-chars-forward "[:space:]")
916 ;; get the column of the type parameter and use that
917 ;; as indentation offset
918 (current-column)))))
919
920 (progn
921 (back-to-indentation)
922 ;; Point is now at the beginning of the current line
923 (if (or
924 ;; If this line begins with "else" or "{", stay on the
925 ;; baseline as well (we are continuing an expression,
926 ;; but the "else" or "{" should align with the beginning
927 ;; of the expression it's in.)
928 ;; Or, if this line starts a comment, stay on the
929 ;; baseline as well.
930 (looking-at "\\<else\\>\\|{\\|/[/*]")
931
932 ;; If this is the start of a top-level item,
933 ;; stay on the baseline.
934 (looking-at rust-top-item-beg-re)
935
936 (save-excursion
937 (rust-rewind-irrelevant)
938 ;; Point is now at the end of the previous line
939 (or
940 ;; If we are at the start of the buffer, no
941 ;; indentation is needed, so stay at baseline...
942 (= (point) 1)
943 ;; ..or if the previous line ends with any of these:
944 ;; { ? : ( , ; [ }
945 ;; then we are at the beginning of an
946 ;; expression, so stay on the baseline...
947 (looking-back "[(,:;[{}]\\|[^|]|" (- (point) 2))
948 ;; or if the previous line is the end of an
949 ;; attribute, stay at the baseline...
950 (progn (rust-rewind-to-beginning-of-current-level-expr)
951 (looking-at "#")))))
952 baseline
953
954 ;; Otherwise, we are continuing the same expression from
955 ;; the previous line, so add one additional indent level
956 (+ baseline rust-indent-offset))))))))))
957
958 (when indent
959 ;; If we're at the beginning of the line (before or at the current
960 ;; indentation), jump with the indentation change. Otherwise, save the
961 ;; excursion so that adding the indentations will leave us at the
962 ;; equivalent position within the line to where we were before.
963 (if (<= (current-column) (current-indentation))
964 (indent-line-to indent)
965 (save-excursion (indent-line-to indent))))))
966
967(defun rust--same-line-p (pos1 pos2)
968 "Return non-nil if POS1 and POS2 are on the same line."
969 (save-excursion (= (progn (goto-char pos1) (line-end-position))
970 (progn (goto-char pos2) (line-end-position)))))
971
972;;; Font-locking definitions and helpers
973
974(defun rust-next-string-interpolation (limit)
975 "Search forward from point for next Rust interpolation marker before LIMIT.
976Set point to the end of the occurrence found, and return match beginning
977and end."
978 (catch 'match
979 (save-match-data
980 (save-excursion
981 (while (search-forward "{" limit t)
982 (if (eql (char-after (point)) ?{)
983 (forward-char)
984 (let ((start (match-beginning 0)))
985 ;; According to fmt_macros::Parser::next, an opening brace
986 ;; must be followed by an optional argument and/or format
987 ;; specifier, then a closing brace. A single closing brace
988 ;; without a corresponding unescaped opening brace is an
989 ;; error. We don't need to do anything special with
990 ;; arguments, specifiers, or errors, so we only search for
991 ;; the single closing brace.
992 (when (search-forward "}" limit t)
993 (throw 'match (list start (point)))))))))))
994
995(defun rust-string-interpolation-matcher (limit)
996 "Match next Rust interpolation marker before LIMIT and set match data if found.
997Returns nil if not within a Rust string."
998 (when (rust-in-str)
999 (let ((match (rust-next-string-interpolation limit)))
1000 (when match
1001 (set-match-data match)
1002 (goto-char (cadr match))
1003 match))))
1004
1005(defun rust-syntax-class-before-point ()
1006 (when (> (point) 1)
1007 (syntax-class (syntax-after (1- (point))))))
1008
1009(defun rust-rewind-qualified-ident ()
1010 (while (rust-looking-back-ident)
1011 (backward-sexp)
1012 (when (save-excursion (rust-rewind-irrelevant) (rust-looking-back-str "::"))
1013 (rust-rewind-irrelevant)
1014 (backward-char 2)
1015 (rust-rewind-irrelevant))))
1016
1017(defun rust-rewind-type-param-list ()
1018 (cond
1019 ((and (rust-looking-back-str ">") (equal 5 (rust-syntax-class-before-point)))
1020 (backward-sexp)
1021 (rust-rewind-irrelevant))
1022
1023 ;; We need to be able to back up past the Fn(args) -> RT form as well. If
1024 ;; we're looking back at this, we want to end up just after "Fn".
1025 ((member (char-before) '(?\] ?\) ))
1026 (let* ((is-paren (rust-looking-back-str ")"))
1027 (dest (save-excursion
1028 (backward-sexp)
1029 (rust-rewind-irrelevant)
1030 (or
1031 (when (rust-looking-back-str "->")
1032 (backward-char 2)
1033 (rust-rewind-irrelevant)
1034 (when (rust-looking-back-str ")")
1035 (backward-sexp)
1036 (point)))
1037 (and is-paren (point))))))
1038 (when dest
1039 (goto-char dest))))))
1040
1041(defun rust-rewind-to-decl-name ()
1042 "Return the point at the beginning of the name in a declaration.
1043I.e. if we are before an ident that is part of a declaration that
1044can have a where clause, rewind back to just before the name of
1045the subject of that where clause and return the new point.
1046Otherwise return nil."
1047 (let* ((ident-pos (point))
1048 (newpos (save-excursion
1049 (rust-rewind-irrelevant)
1050 (rust-rewind-type-param-list)
1051 (cond
1052 ((rust-looking-back-symbols
1053 '("fn" "trait" "enum" "struct" "union" "impl" "type"))
1054 ident-pos)
1055
1056 ((equal 5 (rust-syntax-class-before-point))
1057 (backward-sexp)
1058 (rust-rewind-to-decl-name))
1059
1060 ((looking-back "[:,'+=]" (1- (point)))
1061 (backward-char)
1062 (rust-rewind-to-decl-name))
1063
1064 ((rust-looking-back-str "->")
1065 (backward-char 2)
1066 (rust-rewind-to-decl-name))
1067
1068 ((rust-looking-back-ident)
1069 (rust-rewind-qualified-ident)
1070 (rust-rewind-to-decl-name))))))
1071 (when newpos (goto-char newpos))
1072 newpos))
1073
1074(defun rust-is-in-expression-context (token)
1075 "Return t if what comes right after the point is part of an
1076expression (as opposed to starting a type) by looking at what
1077comes before. Takes a symbol that roughly indicates what is
1078after the point.
1079
1080This function is used as part of `rust-is-lt-char-operator' as
1081part of angle bracket matching, and is not intended to be used
1082outside of this context."
1083 (save-excursion
1084 (let ((postchar (char-after)))
1085 (rust-rewind-irrelevant)
1086 ;; A type alias or ascription could have a type param list. Skip backwards past it.
1087 (when (member token '(ambiguous-operator open-brace))
1088 (rust-rewind-type-param-list))
1089 (cond
1090
1091 ;; Certain keywords always introduce expressions
1092 ((rust-looking-back-symbols '("if" "while" "match" "return" "box" "in")) t)
1093
1094 ;; "as" introduces a type
1095 ((rust-looking-back-symbols '("as")) nil)
1096
1097 ;; An open angle bracket never introduces expression context WITHIN the angle brackets
1098 ((and (equal token 'open-brace) (equal postchar ?<)) nil)
1099
1100 ;; An ident! followed by an open brace is a macro invocation. Consider
1101 ;; it to be an expression.
1102 ((and (equal token 'open-brace) (rust-looking-back-macro)) t)
1103
1104 ;; In a brace context a "]" introduces an expression.
1105 ((and (eq token 'open-brace) (rust-looking-back-str "]")))
1106
1107 ;; An identifier is right after an ending paren, bracket, angle bracket
1108 ;; or curly brace. It's a type if the last sexp was a type.
1109 ((and (equal token 'ident) (equal 5 (rust-syntax-class-before-point)))
1110 (backward-sexp)
1111 (rust-is-in-expression-context 'open-brace))
1112
1113 ;; If a "for" appears without a ; or { before it, it's part of an
1114 ;; "impl X for y", so the y is a type. Otherwise it's
1115 ;; introducing a loop, so the y is an expression
1116 ((and (equal token 'ident) (rust-looking-back-symbols '("for")))
1117 (backward-sexp)
1118 (rust-rewind-irrelevant)
1119 (looking-back "[{;]" (1- (point))))
1120
1121 ((rust-looking-back-ident)
1122 (rust-rewind-qualified-ident)
1123 (rust-rewind-irrelevant)
1124 (cond
1125 ((equal token 'open-brace)
1126 ;; We now know we have:
1127 ;; ident <maybe type params> [{([]
1128 ;; where [{([] denotes either a {, ( or [.
1129 ;; This character is bound as postchar.
1130 (cond
1131 ;; If postchar is a paren or square bracket, then if the
1132 ;; brace is a type if the identifier is one
1133 ((member postchar '(?\( ?\[ )) (rust-is-in-expression-context 'ident))
1134
1135 ;; If postchar is a curly brace, the brace can only be a type if
1136 ;; ident2 is the name of an enum, struct or trait being declared.
1137 ;; Note that if there is a -> before the ident then the ident would
1138 ;; be a type but the { is not.
1139 ((equal ?{ postchar)
1140 (not (and (rust-rewind-to-decl-name)
1141 (progn
1142 (rust-rewind-irrelevant)
1143 (rust-looking-back-symbols
1144 '("enum" "struct" "union" "trait" "type"))))))))
1145
1146 ((equal token 'ambiguous-operator)
1147 (cond
1148 ;; An ampersand after an ident has to be an operator rather
1149 ;; than a & at the beginning of a ref type
1150 ((equal postchar ?&) t)
1151
1152 ;; A : followed by a type then an = introduces an
1153 ;; expression (unless it is part of a where clause of a
1154 ;; "type" declaration)
1155 ((and (equal postchar ?=)
1156 (looking-back "[^:]:" (- (point) 2))
1157 (not (save-excursion
1158 (and (rust-rewind-to-decl-name)
1159 (progn (rust-rewind-irrelevant)
1160 (rust-looking-back-symbols '("type"))))))))
1161
1162 ;; "let ident =" introduces an expression--and so does "const" and "mut"
1163 ((and (equal postchar ?=) (rust-looking-back-symbols '("let" "const" "mut"))) t)
1164
1165 ;; As a specific special case, see if this is the = in this situation:
1166 ;; enum EnumName<type params> { Ident =
1167 ;; In this case, this is a c-like enum and despite Ident
1168 ;; representing a type, what comes after the = is an expression
1169 ((and
1170 (> (rust-paren-level) 0)
1171 (save-excursion
1172 (backward-up-list)
1173 (rust-rewind-irrelevant)
1174 (rust-rewind-type-param-list)
1175 (and
1176 (rust-looking-back-ident)
1177 (progn
1178 (rust-rewind-qualified-ident)
1179 (rust-rewind-irrelevant)
1180 (rust-looking-back-str "enum")))))
1181 t)
1182
1183 ;; Otherwise the ambiguous operator is a type if the identifier is a type
1184 ((rust-is-in-expression-context 'ident) t)))
1185
1186 ((equal token 'colon)
1187 (cond
1188 ;; If we see a ident: not inside any braces/parens, we're at top level.
1189 ;; There are no allowed expressions after colons there, just types.
1190 ((<= (rust-paren-level) 0) nil)
1191
1192 ;; We see ident: inside a list
1193 ((looking-back "[{,]" (1- (point)))
1194 (backward-up-list)
1195
1196 ;; If a : appears whose surrounding paren/brackets/braces are
1197 ;; anything other than curly braces, it can't be a field
1198 ;; initializer and must be denoting a type.
1199 (when (looking-at "{")
1200 (rust-rewind-irrelevant)
1201 (rust-rewind-type-param-list)
1202 (when (rust-looking-back-ident)
1203 ;; We have a context that looks like this:
1204 ;; ident2 <maybe type params> { [maybe paren-balanced code ending in comma] ident1:
1205 ;; the point is sitting just after ident2, and we trying to
1206 ;; figure out if the colon introduces an expression or a type.
1207 ;; The answer is that ident1 is a field name, and what comes
1208 ;; after the colon is an expression, if ident2 is an
1209 ;; expression.
1210 (rust-rewind-qualified-ident)
1211 (rust-is-in-expression-context 'ident))))
1212
1213 ;; Otherwise, if the ident: appeared with anything other than , or {
1214 ;; before it, it can't be part of a struct initializer and therefore
1215 ;; must be denoting a type.
1216 (t nil)))))
1217
1218 ;; An operator-like character after a string is indeed an operator
1219 ((and (equal token 'ambiguous-operator)
1220 (member (rust-syntax-class-before-point) '(5 7 15))) t)
1221
1222 ;; A colon that has something other than an identifier before it is a
1223 ;; type ascription
1224 ((equal token 'colon) nil)
1225
1226 ;; A :: introduces a type (or module, but not an expression in any case)
1227 ((rust-looking-back-str "::") nil)
1228
1229 ((rust-looking-back-str ":")
1230 (backward-char)
1231 (rust-is-in-expression-context 'colon))
1232
1233 ;; A -> introduces a type
1234 ((rust-looking-back-str "->") nil)
1235
1236 ;; If we are up against the beginning of a list, or after a comma inside
1237 ;; of one, back up out of it and check what the list itself is
1238 ((or
1239 (equal 4 (rust-syntax-class-before-point))
1240 (rust-looking-back-str ","))
1241 (condition-case nil
1242 (progn
1243 (backward-up-list)
1244 (rust-is-in-expression-context 'open-brace))
1245 (scan-error nil)))
1246
1247 ;; A => introduces an expression
1248 ((rust-looking-back-str "=>") t)
1249
1250 ;; A == introduces an expression
1251 ((rust-looking-back-str "==") t)
1252
1253 ;; These operators can introduce expressions or types
1254 ((looking-back "[-+=!?&*]" (1- (point)))
1255 (backward-char)
1256 (rust-is-in-expression-context 'ambiguous-operator))
1257
1258 ;; These operators always introduce expressions. (Note that if this
1259 ;; regexp finds a < it must not be an angle bracket, or it'd
1260 ;; have been caught in the syntax-class check above instead of this.)
1261 ((looking-back rust-re-pre-expression-operators (1- (point))) t)))))
1262
1263(defun rust-is-lt-char-operator ()
1264 "Return non-nil if the `<' sign just after point is an operator.
1265Otherwise, if it is an opening angle bracket, then return nil."
1266 (let ((case-fold-search nil))
1267 (save-excursion
1268 (rust-rewind-irrelevant)
1269 ;; We are now just after the character syntactically before the <.
1270 (cond
1271
1272 ;; If we are looking back at a < that is not an angle bracket (but not
1273 ;; two of them) then this is the second < in a bit shift operator
1274 ((and (rust-looking-back-str "<")
1275 (not (equal 4 (rust-syntax-class-before-point)))
1276 (not (rust-looking-back-str "<<"))))
1277
1278 ;; On the other hand, if we are after a closing paren/brace/bracket it
1279 ;; can only be an operator, not an angle bracket. Likewise, if we are
1280 ;; after a string it's an operator. (The string case could actually be
1281 ;; valid in rust for character literals.)
1282 ((member (rust-syntax-class-before-point) '(5 7 15)) t)
1283
1284 ;; If we are looking back at an operator, we know that we are at
1285 ;; the beginning of an expression, and thus it has to be an angle
1286 ;; bracket (starting a "<Type as Trait>::" construct.)
1287 ((looking-back rust-re-pre-expression-operators (1- (point))) nil)
1288
1289 ;; If we are looking back at a keyword, it's an angle bracket
1290 ;; unless that keyword is "self", "true" or "false"
1291 ((rust-looking-back-symbols rust-keywords)
1292 (rust-looking-back-symbols '("self" "true" "false")))
1293
1294 ((rust-looking-back-str "?")
1295 (rust-is-in-expression-context 'ambiguous-operator))
1296
1297 ;; If we're looking back at an identifier, this depends on whether
1298 ;; the identifier is part of an expression or a type
1299 ((rust-looking-back-ident)
1300 (backward-sexp)
1301 (or
1302 ;; The special types can't take type param lists, so a < after one is
1303 ;; always an operator
1304 (looking-at rust-re-special-types)
1305
1306 (rust-is-in-expression-context 'ident)))
1307
1308 ;; Otherwise, assume it's an angle bracket
1309 ))))
1310
1311(defun rust-electric-pair-inhibit-predicate-wrap (char)
1312 "Prevent \"matching\" with a `>' when CHAR is the less-than operator.
1313This wraps the default defined by `electric-pair-inhibit-predicate'."
1314 (or
1315 (when (= ?< char)
1316 (save-excursion
1317 (backward-char)
1318 (rust-is-lt-char-operator)))
1319 (funcall (default-value 'electric-pair-inhibit-predicate) char)))
1320
1321(defun rust-electric-pair-skip-self (char)
1322 "Skip CHAR instead of inserting a second closing character.
1323This is added to the default skips defined by `electric-pair-skip-self'."
1324 (= ?> char))
1325
1326(defun rust-ordinary-lt-gt-p ()
1327 "Test whether the `<' or `>' at point is an ordinary operator of some kind.
1328
1329This returns t if the `<' or `>' is an ordinary operator (like
1330less-than) or part of one (like `->'); and nil if the character
1331should be considered a paired angle bracket."
1332 (cond
1333 ;; If matching is turned off suppress all of them
1334 ((not rust-match-angle-brackets) t)
1335
1336 ;; This is a cheap check so we do it early.
1337 ;; Don't treat the > in -> or => as an angle bracket
1338 ((and (= (following-char) ?>) (memq (preceding-char) '(?- ?=))) t)
1339
1340 ;; We don't take < or > in strings or comments to be angle brackets
1341 ((rust-in-str-or-cmnt) t)
1342
1343 ;; Inside a macro we don't really know the syntax. Any < or > may be an
1344 ;; angle bracket or it may not. But we know that the other braces have
1345 ;; to balance regardless of the < and >, so if we don't treat any < or >
1346 ;; as angle brackets it won't mess up any paren balancing.
1347 ((rust-in-macro) t)
1348
1349 ((= (following-char) ?<)
1350 (rust-is-lt-char-operator))
1351
1352 ;; Since rust-ordinary-lt-gt-p is called only when either < or > are at the point,
1353 ;; we know that the following char must be > in the clauses below.
1354
1355 ;; If we are at top level and not in any list, it can't be a closing
1356 ;; angle bracket
1357 ((>= 0 (rust-paren-level)) t)
1358
1359 ;; Otherwise, treat the > as a closing angle bracket if it would
1360 ;; match an opening one
1361 ((save-excursion
1362 (backward-up-list)
1363 (/= (following-char) ?<)))))
1364
1365(defun rust-mode-syntactic-face-function (state)
1366 "Return face that distinguishes doc and normal comments in given syntax STATE."
1367 (if (nth 3 state)
1368 'font-lock-string-face
1369 (save-excursion
1370 (goto-char (nth 8 state))
1371 (if (looking-at "/\\([*][*!][^*!]\\|/[/!][^/!]\\)")
1372 'font-lock-doc-face
1373 'font-lock-comment-face))))
1374
1375(eval-and-compile
1376 (defconst rust--char-literal-rx
1377 (rx (seq
1378 (group "'")
1379 (or
1380 (seq
1381 "\\"
1382 (or
1383 (: "u{" (** 1 6 xdigit) "}")
1384 (: "x" (= 2 xdigit))
1385 (any "'nrt0\"\\")))
1386 (not (any "'\\")))
1387 (group "'")))
1388 "A regular expression matching a character literal."))
1389
1390(defun rust--syntax-propertize-raw-string (str-start end)
1391 "A helper for rust-syntax-propertize.
1392
1393This will apply the appropriate string syntax to the character
1394from the STR-START up to the end of the raw string, or to END,
1395whichever comes first."
1396 (when (save-excursion
1397 (goto-char str-start)
1398 (looking-at "r\\(#*\\)\\(\"\\)"))
1399 ;; In a raw string, so try to find the end.
1400 (let ((hashes (match-string 1)))
1401 ;; Match \ characters at the end of the string to suppress
1402 ;; their normal character-quote syntax.
1403 (when (re-search-forward (concat "\\(\\\\*\\)\\(\"" hashes "\\)") end t)
1404 (put-text-property (match-beginning 1) (match-end 1)
1405 'syntax-table (string-to-syntax "_"))
1406 (put-text-property (1- (match-end 2)) (match-end 2)
1407 'syntax-table (string-to-syntax "|"))
1408 (goto-char (match-end 0))))))
1409
1410;;; Syntax Propertize
1411
1412(defun rust-syntax-propertize (start end)
1413 "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)))
1442
1443(defun rust-fill-prefix-for-comment-start (line-start)
1444 "Determine what to use for `fill-prefix' based on the text at LINE-START."
1445 (let ((result
1446 ;; Replace /* with same number of spaces
1447 (replace-regexp-in-string
1448 "\\(?:/\\*+?\\)[!*]?"
1449 (lambda (s)
1450 ;; We want the * to line up with the first * of the
1451 ;; comment start
1452 (let ((offset (if (eq t
1453 (compare-strings "/*" nil nil
1454 s
1455 (- (length s) 2)
1456 (length s)))
1457 1 2)))
1458 (concat (make-string (- (length s) offset)
1459 ?\x20) "*")))
1460 line-start)))
1461 ;; Make sure we've got at least one space at the end
1462 (if (not (= (aref result (- (length result) 1)) ?\x20))
1463 (setq result (concat result " ")))
1464 result))
1465
1466(defun rust-in-comment-paragraph (body)
1467 ;; We might move the point to fill the next comment, but we don't want it
1468 ;; seeming to jump around on the user
1469 (save-excursion
1470 ;; If we're outside of a comment, with only whitespace and then a comment
1471 ;; in front, jump to the comment and prepare to fill it.
1472 (when (not (nth 4 (syntax-ppss)))
1473 (beginning-of-line)
1474 (when (looking-at (concat "[[:space:]\n]*" comment-start-skip))
1475 (goto-char (match-end 0))))
1476
1477 ;; We need this when we're moving the point around and then checking syntax
1478 ;; while doing paragraph fills, because the cache it uses isn't always
1479 ;; invalidated during this.
1480 (syntax-ppss-flush-cache 1)
1481 ;; If we're at the beginning of a comment paragraph with nothing but
1482 ;; whitespace til the next line, jump to the next line so that we use the
1483 ;; existing prefix to figure out what the new prefix should be, rather than
1484 ;; inferring it from the comment start.
1485 (let ((next-bol (line-beginning-position 2)))
1486 (while (save-excursion
1487 (end-of-line)
1488 (syntax-ppss-flush-cache 1)
1489 (and (nth 4 (syntax-ppss))
1490 (save-excursion
1491 (beginning-of-line)
1492 (looking-at paragraph-start))
1493 (looking-at "[[:space:]]*$")
1494 (nth 4 (syntax-ppss next-bol))))
1495 (goto-char next-bol)))
1496
1497 (syntax-ppss-flush-cache 1)
1498 ;; If we're on the last line of a multiline-style comment that started
1499 ;; above, back up one line so we don't mistake the * of the */ that ends
1500 ;; the comment for a prefix.
1501 (when (save-excursion
1502 (and (nth 4 (syntax-ppss (line-beginning-position 1)))
1503 (looking-at "[[:space:]]*\\*/")))
1504 (goto-char (line-end-position 0)))
1505 (funcall body)))
1506
1507(defun rust-with-comment-fill-prefix (body)
1508 (let*
1509 ((line-string (buffer-substring-no-properties
1510 (line-beginning-position) (line-end-position)))
1511 (line-comment-start
1512 (when (nth 4 (syntax-ppss))
1513 (cond
1514 ;; If we're inside the comment and see a * prefix, use it
1515 ((string-match "^\\([[:space:]]*\\*+[[:space:]]*\\)"
1516 line-string)
1517 (match-string 1 line-string))
1518 ;; If we're at the start of a comment, figure out what prefix
1519 ;; to use for the subsequent lines after it
1520 ((string-match (concat "[[:space:]]*" comment-start-skip) line-string)
1521 (rust-fill-prefix-for-comment-start
1522 (match-string 0 line-string))))))
1523 (fill-prefix
1524 (or line-comment-start
1525 fill-prefix)))
1526 (funcall body)))
1527
1528(defun rust-find-fill-prefix ()
1529 (rust-in-comment-paragraph
1530 (lambda ()
1531 (rust-with-comment-fill-prefix
1532 (lambda ()
1533 fill-prefix)))))
1534
1535(defun rust-fill-paragraph (&rest args)
1536 "Special wrapping for `fill-paragraph'.
1537This handles multi-line comments with a * prefix on each line."
1538 (rust-in-comment-paragraph
1539 (lambda ()
1540 (rust-with-comment-fill-prefix
1541 (lambda ()
1542 (let
1543 ((fill-paragraph-function
1544 (if (not (eq fill-paragraph-function #'rust-fill-paragraph))
1545 fill-paragraph-function))
1546 (fill-paragraph-handle-comment t))
1547 (apply #'fill-paragraph args)
1548 t))))))
1549
1550(defun rust-do-auto-fill (&rest args)
1551 "Special wrapping for `do-auto-fill'.
1552This handles multi-line comments with a * prefix on each line."
1553 (rust-with-comment-fill-prefix
1554 (lambda ()
1555 (apply #'do-auto-fill args)
1556 t)))
1557
1558(defun rust-fill-forward-paragraph (arg)
1559 ;; This is to work around some funny behavior when a paragraph separator is
1560 ;; at the very top of the file and there is a fill prefix.
1561 (let ((fill-prefix nil)) (forward-paragraph arg)))
1562
1563(defun rust-comment-indent-new-line (&optional arg)
1564 (rust-with-comment-fill-prefix
1565 (lambda () (comment-indent-new-line arg))))
1566
1567;;; Defun Motions
1568
1569(defun rust-beginning-of-defun (&optional arg)
1570 "Move backward to the beginning of the current defun.
1571
1572With ARG, move backward multiple defuns. Negative ARG means
1573move forward.
1574
1575This is written mainly to be used as `beginning-of-defun-function' for Rust.
1576Don't move to the beginning of the line. `beginning-of-defun',
1577which calls this, does that afterwards."
1578 (interactive "p")
1579 (let* ((arg (or arg 1))
1580 (magnitude (abs arg))
1581 (sign (if (< arg 0) -1 1)))
1582 ;; If moving forward, don't find the defun we might currently be
1583 ;; on.
1584 (when (< sign 0)
1585 (end-of-line))
1586 (catch 'done
1587 (dotimes (_ magnitude)
1588 ;; Search until we find a match that is not in a string or comment.
1589 (while (if (re-search-backward (concat "^\\(" rust-top-item-beg-re "\\)")
1590 nil 'move sign)
1591 (rust-in-str-or-cmnt)
1592 ;; Did not find it.
1593 (throw 'done nil)))))
1594 t))
1595
1596(defun rust-end-of-defun ()
1597 "Move forward to the next end of defun.
1598
1599With argument, do it that many times.
1600Negative argument -N means move back to Nth preceding end of defun.
1601
1602Assume that this is called after `beginning-of-defun'. So point is
1603at the beginning of the defun body.
1604
1605This is written mainly to be used as `end-of-defun-function' for Rust."
1606 (interactive)
1607 ;; Find the opening brace
1608 (if (re-search-forward "[{]" nil t)
1609 (progn
1610 (goto-char (match-beginning 0))
1611 ;; Go to the closing brace
1612 (condition-case nil
1613 (forward-sexp)
1614 (scan-error
1615 ;; The parentheses are unbalanced; instead of being unable
1616 ;; to fontify, just jump to the end of the buffer
1617 (goto-char (point-max)))))
1618 ;; There is no opening brace, so consider the whole buffer to be one "defun"
1619 (goto-char (point-max))))
1620
1621;;; _
1622
1623(defun rust-mode-reload ()
1624 (interactive)
1625 (unload-feature 'rust-mode)
1626 (require 'rust-mode)
1627 (rust-mode))
1628
1629(provide 'rust-mode) 84(provide 'rust-mode)
1630(require 'rust-utils) 85(require 'rust-utils)
1631 86
diff --git a/.emacs.d/lisp/rust-playpen.el b/.emacs.d/lisp/rust-playpen.el
index 1a9e583..a80a184 100644
--- a/.emacs.d/lisp/rust-playpen.el
+++ b/.emacs.d/lisp/rust-playpen.el
@@ -16,6 +16,12 @@
16 :type 'string 16 :type 'string
17 :group 'rust-mode) 17 :group 'rust-mode)
18 18
19(defcustom rust-playpen-enable-shortener t
20 "Enable shortended URL for playpen links."
21 :type 'boolean
22 :safe #'booleanp
23 :group 'rust-mode)
24
19(defcustom rust-shortener-url-format "https://is.gd/create.php?format=simple&url=%s" 25(defcustom rust-shortener-url-format "https://is.gd/create.php?format=simple&url=%s"
20 "Format string to use for creating the shortened link of a playpen submission." 26 "Format string to use for creating the shortened link of a playpen submission."
21 :type 'string 27 :type 'string
@@ -28,26 +34,28 @@
28 (interactive "r") 34 (interactive "r")
29 (let* ((data (buffer-substring begin end)) 35 (let* ((data (buffer-substring begin end))
30 (escaped-data (url-hexify-string data)) 36 (escaped-data (url-hexify-string data))
31 (escaped-playpen-url (url-hexify-string 37 (playpen-url (format rust-playpen-url-format escaped-data))
32 (format rust-playpen-url-format escaped-data)))) 38 (escaped-playpen-url (url-hexify-string playpen-url)))
33 (if (> (length escaped-playpen-url) 5000) 39 (if (> (length escaped-playpen-url) 5000)
34 (error "encoded playpen data exceeds 5000 character limit (length %s)" 40 (error "encoded playpen data exceeds 5000 character limit (length %s)"
35 (length escaped-playpen-url)) 41 (length escaped-playpen-url))
36 (let ((shortener-url (format rust-shortener-url-format escaped-playpen-url)) 42 (if (not rust-playpen-enable-shortener)
37 (url-request-method "POST")) 43 (message "%s" playpen-url)
38 (url-retrieve shortener-url 44 (let ((shortener-url (format rust-shortener-url-format escaped-playpen-url))
39 (lambda (state) 45 (url-request-method "POST"))
40 ;; filter out the headers etc. included at the 46 (url-retrieve shortener-url
41 ;; start of the buffer: the relevant text 47 (lambda (state)
42 ;; (shortened url or error message) is exactly 48 ;; filter out the headers etc. included at the
43 ;; the last line. 49 ;; start of the buffer: the relevant text
44 (goto-char (point-max)) 50 ;; (shortened url or error message) is exactly
45 (let ((last-line (thing-at-point 'line t)) 51 ;; the last line.
46 (err (plist-get state :error))) 52 (goto-char (point-max))
47 (kill-buffer) 53 (let ((last-line (thing-at-point 'line t))
48 (if err 54 (err (plist-get state :error)))
49 (error "failed to shorten playpen url: %s" last-line) 55 (kill-buffer)
50 (message "%s" last-line))))))))) 56 (if err
57 (error "failed to shorten playpen url: %s" last-line)
58 (message "%s" last-line))))))))))
51 59
52(defun rust-playpen-buffer () 60(defun rust-playpen-buffer ()
53 "Create a shareable URL for the contents of the buffer on the Rust playpen." 61 "Create a shareable URL for the contents of the buffer on the Rust playpen."
diff --git a/.emacs.d/lisp/rust-prog-mode.el b/.emacs.d/lisp/rust-prog-mode.el
new file mode 100644
index 0000000..51e802d
--- /dev/null
+++ b/.emacs.d/lisp/rust-prog-mode.el
@@ -0,0 +1,1509 @@
1;;; rust-prog-mode.el --- old rust-mode without treesitter -*-lexical-binding: t-*-
2;;; Commentary:
3
4;; rust-mode code deriving from prog-mode instead of rust-ts-mode
5
6;;; Code:
7(require 'rust-common)
8
9(defvar electric-pair-inhibit-predicate)
10(defvar electric-pair-skip-self)
11(defvar electric-indent-chars)
12
13(defvar rust-prettify-symbols-alist
14 '(("&&" . ?∧) ("||" . ?∨)
15 ("<=" . ?≤) (">=" . ?≥) ("!=" . ?≠)
16 ("INFINITY" . ?∞) ("->" . ?→) ("=>" . ?⇒))
17 "Alist of symbol prettifications used for `prettify-symbols-alist'.")
18
19(defcustom rust-indent-offset 4
20 "Indent Rust code by this number of spaces."
21 :type 'integer
22 :group 'rust-mode
23 :safe #'integerp)
24
25(defcustom rust-indent-method-chain nil
26 "Indent Rust method chains, aligned by the `.' operators."
27 :type 'boolean
28 :group 'rust-mode
29 :safe #'booleanp)
30
31(defcustom rust-indent-where-clause nil
32 "Indent lines starting with the `where' keyword following a function or trait.
33When nil, `where' will be aligned with `fn' or `trait'."
34 :type 'boolean
35 :group 'rust-mode
36 :safe #'booleanp)
37
38(defcustom rust-match-angle-brackets t
39 "Whether to enable angle bracket (`<' and `>') matching where appropriate."
40 :type 'boolean
41 :safe #'booleanp
42 :group 'rust-mode)
43
44(defcustom rust-indent-return-type-to-arguments t
45 "Indent a line starting with the `->' (RArrow) following a function, aligning
46to the function arguments. When nil, `->' will be indented one level."
47 :type 'boolean
48 :group 'rust-mode
49 :safe #'booleanp)
50
51(defface rust-unsafe
52 '((t :inherit font-lock-warning-face))
53 "Face for the `unsafe' keyword."
54 :group 'rust-mode)
55
56(defface rust-question-mark
57 '((t :weight bold :inherit font-lock-builtin-face))
58 "Face for the question mark operator."
59 :group 'rust-mode)
60
61(defface rust-ampersand-face
62 '((t :inherit default))
63 "Face for the ampersand reference mark."
64 :group 'rust-mode)
65
66(defface rust-builtin-formatting-macro
67 '((t :inherit font-lock-builtin-face))
68 "Face for builtin formatting macros (print! &c.)."
69 :group 'rust-mode)
70
71(defface rust-string-interpolation
72 '((t :slant italic :inherit font-lock-string-face))
73 "Face for interpolating braces in builtin formatting macro strings."
74 :group 'rust-mode)
75
76;;; Syntax
77
78(defun rust-re-word (inner) (concat "\\<" inner "\\>"))
79(defun rust-re-grab (inner) (concat "\\(" inner "\\)"))
80(defun rust-re-shy (inner) (concat "\\(?:" inner "\\)"))
81
82(defconst rust-re-ident "[[:word:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*")
83(defconst rust-re-lc-ident "[[:lower:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*")
84(defconst rust-re-uc-ident "[[:upper:]][[:word:][:multibyte:]_[:digit:]]*")
85(defvar rust-re-vis
86 ;; pub | pub ( crate ) | pub ( self ) | pub ( super ) | pub ( in SimplePath )
87 (concat
88 "pub"
89 (rust-re-shy
90 (concat
91 "[[:space:]]*([[:space:]]*"
92 (rust-re-shy
93 (concat "crate" "\\|"
94 "\\(?:s\\(?:elf\\|uper\\)\\)" "\\|"
95 ;; in SimplePath
96 (rust-re-shy
97 (concat
98 "in[[:space:]]+"
99 rust-re-ident
100 (rust-re-shy (concat "::" rust-re-ident)) "*"))))
101 "[[:space:]]*)"))
102 "?"))
103(defconst rust-re-unsafe "unsafe")
104(defconst rust-re-extern "extern")
105(defconst rust-re-async-or-const "async\\|const")
106(defconst rust-re-generic
107 (concat "<[[:space:]]*'" rust-re-ident "[[:space:]]*>"))
108(defconst rust-re-union
109 (rx-to-string
110 `(seq
111 (or space line-start)
112 (group symbol-start "union" symbol-end)
113 (+ space) (regexp ,rust-re-ident))))
114
115(defun rust-re-item-def (itype)
116 (concat (rust-re-word itype)
117 (rust-re-shy rust-re-generic) "?"
118 "[[:space:]]+" (rust-re-grab rust-re-ident)))
119
120;; TODO some of this does only make sense for `fn' (unsafe, extern...)
121;; and not other items
122(defun rust-re-item-def-imenu (itype)
123 (concat "^[[:space:]]*"
124 (rust-re-shy (concat rust-re-vis "[[:space:]]+")) "?"
125 (rust-re-shy (concat (rust-re-word "default") "[[:space:]]+")) "?"
126 (rust-re-shy (concat (rust-re-shy rust-re-async-or-const) "[[:space:]]+")) "?"
127 (rust-re-shy (concat (rust-re-word rust-re-unsafe) "[[:space:]]+")) "?"
128 (rust-re-shy (concat (rust-re-word rust-re-extern) "[[:space:]]+"
129 (rust-re-shy "\"[^\"]+\"[[:space:]]+") "?")) "?"
130 (rust-re-item-def itype)))
131
132(defvar rust-imenu-generic-expression
133 (append (mapcar #'(lambda (x)
134 (list (capitalize x) (rust-re-item-def-imenu x) 1))
135 '("enum" "struct" "union" "type" "mod" "fn" "trait" "impl"))
136 `(("Macro" ,(rust-re-item-def-imenu "macro_rules!") 1)))
137 "Value for `imenu-generic-expression' in Rust mode.
138
139Create a hierarchical index of the item definitions in a Rust file.
140
141Imenu will show all the enums, structs, etc. in their own subheading.
142Use idomenu (imenu with `ido-mode') for best mileage.")
143
144;;; Prettify
145
146(defun rust--prettify-symbols-compose-p (start end match)
147 "Return true iff the symbol MATCH should be composed.
148See `prettify-symbols-compose-predicate'."
149 (and (fboundp 'prettify-symbols-default-compose-p)
150 (prettify-symbols-default-compose-p start end match)
151 ;; Make sure || is not a closure with 0 arguments and && is not
152 ;; a double reference.
153 (pcase match
154 ("||" (not (save-excursion
155 (goto-char start)
156 (looking-back "\\(?:\\<move\\|[[({:=,;]\\) *"
157 (line-beginning-position)))))
158 ("&&" (char-equal (char-after end) ?\s))
159 (_ t))))
160
161(defvar rust-top-item-beg-re
162 (concat "\\s-*\\(?:priv\\|pub\\)?\\s-*"
163 ;; TODO some of this does only make sense for `fn' (unsafe, extern...)
164 ;; and not other items
165 (rust-re-shy (concat (rust-re-shy rust-re-vis) "[[:space:]]+")) "?"
166 (rust-re-shy (concat (rust-re-shy rust-re-async-or-const) "[[:space:]]+")) "?"
167 (rust-re-shy (concat (rust-re-shy rust-re-unsafe) "[[:space:]]+")) "?"
168 (regexp-opt
169 '("enum" "struct" "union" "type" "mod" "fn" "static" "impl"
170 "extern" "trait" "async"))
171 "\\_>")
172 "Start of a Rust item.")
173
174(defconst rust-re-type-or-constructor
175 (rx symbol-start
176 (group upper (0+ (any word nonascii digit "_")))
177 symbol-end))
178
179(defconst rust-keywords
180 '("as" "async" "await"
181 "box" "break"
182 "const" "continue" "crate"
183 "do" "dyn"
184 "else" "enum" "extern" "existential"
185 "false" "fn" "for"
186 "if" "impl" "in"
187 "let" "loop"
188 "match" "mod" "move" "mut"
189 "priv" "pub"
190 "ref" "return"
191 "self" "static" "struct" "super"
192 "true" "trait" "type" "try"
193 "use"
194 "virtual"
195 "where" "while"
196 "yield")
197 "Font-locking definitions and helpers.")
198
199(defconst rust-special-types
200 '("u8" "i8"
201 "u16" "i16"
202 "u32" "i32"
203 "u64" "i64"
204 "u128" "i128"
205
206 "f32" "f64"
207 "isize" "usize"
208 "bool"
209 "str" "char"))
210
211(defconst rust-expression-introducers
212 '("if" "while" "match" "return" "box" "in")
213 "List of Rust keywords that are always followed by expressions.")
214
215(defconst rust-number-with-type
216 (eval-when-compile
217 (concat
218 "\\_<\\(?:0[box]?\\|[1-9]\\)[[:digit:]a-fA-F_.]*\\(?:[eE][+-]?[[:digit:]_]\\)?"
219 (regexp-opt '("u8" "i8" "u16" "i16" "u32" "i32" "u64" "i64"
220 "u128" "i128" "usize" "isize" "f32" "f64")
221 t)
222 "\\_>"))
223 "Regular expression matching a number with a type suffix.")
224
225(defvar rust-builtin-formatting-macros
226 '("eprint"
227 "eprintln"
228 "format"
229 "print"
230 "println")
231 "List of builtin Rust macros for string formatting.
232This is used by `rust-font-lock-keywords'.
233\(`write!' is handled separately).")
234
235(defvar rust-formatting-macro-opening-re
236 "[[:space:]\n]*[({[][[:space:]\n]*"
237 "Regular expression to match the opening delimiter of a Rust formatting macro.")
238
239(defvar rust-start-of-string-re
240 "\\(?:r#*\\)?\""
241 "Regular expression to match the start of a Rust raw string.")
242
243(defun rust-path-font-lock-matcher (re-ident)
244 "Match occurrences of RE-IDENT followed by a double-colon.
245Examples include to match names like \"foo::\" or \"Foo::\".
246Does not match type annotations of the form \"foo::<\"."
247 `(lambda (limit)
248 (catch 'rust-path-font-lock-matcher
249 (while t
250 (let* ((symbol-then-colons (rx-to-string '(seq (group (regexp ,re-ident)) "::")))
251 (match (re-search-forward symbol-then-colons limit t)))
252 (cond
253 ;; If we didn't find a match, there are no more occurrences
254 ;; of foo::, so return.
255 ((null match) (throw 'rust-path-font-lock-matcher nil))
256 ;; If this isn't a type annotation foo::<, we've found a
257 ;; match, so a return it!
258 ((not (looking-at (rx (0+ space) "<")))
259 (throw 'rust-path-font-lock-matcher match))))))))
260
261(defvar rust-font-lock-keywords
262 (append
263 `(
264 ;; Keywords proper
265 (,(regexp-opt rust-keywords 'symbols) . font-lock-keyword-face)
266
267 ;; Contextual keywords
268 ("\\_<\\(default\\)[[:space:]]+fn\\_>" 1 font-lock-keyword-face)
269 (,rust-re-union 1 font-lock-keyword-face)
270
271 ;; Special types
272 (,(regexp-opt rust-special-types 'symbols) . font-lock-type-face)
273
274 ;; The unsafe keyword
275 ("\\_<unsafe\\_>" . 'rust-unsafe)
276
277 ;; Attributes like `#[bar(baz)]` or `#![bar(baz)]` or `#[bar = "baz"]`
278 (,(rust-re-grab (concat "#\\!?\\[" rust-re-ident "[^]]*\\]"))
279 1 font-lock-preprocessor-face keep)
280
281 ;; Builtin formatting macros
282 (,(concat (rust-re-grab
283 (concat (rust-re-word (regexp-opt rust-builtin-formatting-macros))
284 "!"))
285 rust-formatting-macro-opening-re
286 "\\(?:" rust-start-of-string-re "\\)?")
287 (1 'rust-builtin-formatting-macro)
288 (rust-string-interpolation-matcher
289 (rust-end-of-string)
290 nil
291 (0 'rust-string-interpolation t nil)))
292
293 ;; write! macro
294 (,(concat (rust-re-grab (concat (rust-re-word "write\\(ln\\)?") "!"))
295 rust-formatting-macro-opening-re
296 "[[:space:]]*[^\"]+,[[:space:]]*"
297 rust-start-of-string-re)
298 (1 'rust-builtin-formatting-macro)
299 (rust-string-interpolation-matcher
300 (rust-end-of-string)
301 nil
302 (0 'rust-string-interpolation t nil)))
303
304 ;; Syntax extension invocations like `foo!`, highlight including the !
305 (,(concat (rust-re-grab (concat rust-re-ident "!")) "[({[:space:][]")
306 1 font-lock-preprocessor-face)
307
308 ;; Field names like `foo:`, highlight excluding the :
309 (,(concat (rust-re-grab rust-re-ident) "[[:space:]]*:[^:]")
310 1 font-lock-variable-name-face)
311
312 ;; CamelCase Means Type Or Constructor
313 (,rust-re-type-or-constructor 1 font-lock-type-face)
314
315 ;; Type-inferred binding
316 (,(concat "\\_<\\(?:let\\s-+ref\\|let\\|ref\\|for\\)\\s-+\\(?:mut\\s-+\\)?"
317 (rust-re-grab rust-re-ident)
318 "\\_>")
319 1 font-lock-variable-name-face)
320
321 ;; Type names like `Foo::`, highlight excluding the ::
322 (,(rust-path-font-lock-matcher rust-re-uc-ident) 1 font-lock-type-face)
323
324 ;; Module names like `foo::`, highlight excluding the ::
325 (,(rust-path-font-lock-matcher rust-re-lc-ident) 1 font-lock-constant-face)
326
327 ;; Lifetimes like `'foo`
328 (,(concat "'" (rust-re-grab rust-re-ident) "[^']") 1 font-lock-variable-name-face)
329
330 ;; Question mark operator
331 ("\\?" . 'rust-question-mark)
332 ("\\(&+\\)\\(?:'\\(?:\\<\\|_\\)\\|\\<\\|[[({:*_|]\\)"
333 1 'rust-ampersand-face)
334 ;; Numbers with type suffix
335 (,rust-number-with-type 1 font-lock-type-face)
336 )
337
338 ;; Ensure we highlight `Foo` in `struct Foo` as a type.
339 (mapcar #'(lambda (x)
340 (list (rust-re-item-def (car x))
341 1 (cdr x)))
342 '(("enum" . font-lock-type-face)
343 ("struct" . font-lock-type-face)
344 ("union" . font-lock-type-face)
345 ("type" . font-lock-type-face)
346 ("mod" . font-lock-constant-face)
347 ("use" . font-lock-constant-face)
348 ("fn" . font-lock-function-name-face)))))
349
350(defun rust-end-of-string ()
351 "Skip to the end of the current string."
352 (save-excursion
353 (skip-syntax-forward "^\"|")
354 (skip-syntax-forward "\"|")
355 (point)))
356
357(defun rust-looking-back-str (str)
358 "Return non-nil if there's a match on the text before point and STR.
359Like `looking-back' but for fixed strings rather than regexps (so
360that it's not so slow)."
361 (let ((len (length str)))
362 (and (> (point) len)
363 (equal str (buffer-substring-no-properties (- (point) len) (point))))))
364
365(defun rust-looking-back-symbols (symbols)
366 "Return non-nil if the point is after a member of SYMBOLS.
367SYMBOLS is a list of strings that represent the respective
368symbols."
369 (save-excursion
370 (let* ((pt-orig (point))
371 (beg-of-symbol (progn (forward-thing 'symbol -1) (point)))
372 (end-of-symbol (progn (forward-thing 'symbol 1) (point))))
373 (and
374 (= end-of-symbol pt-orig)
375 (member (buffer-substring-no-properties beg-of-symbol pt-orig)
376 symbols)))))
377
378(defun rust-looking-back-ident ()
379 "Non-nil if we are looking backwards at a valid rust identifier.
380If we are, regexp match 0 is the identifier."
381 (let ((outer-point (point)))
382 (save-excursion
383 (forward-thing 'symbol -1)
384 (and (looking-at rust-re-ident)
385 (eq (match-end 0) outer-point)))))
386
387(defun rust-looking-back-macro ()
388 "Non-nil if looking back at a potential macro name followed by a \"!\".
389If we are, regexp match 0 is the macro name."
390 (save-excursion
391 ;; Look past whitespace and line breaks.
392 ;; > is okay because we only use it for \n and \r, not "*/"
393 (skip-syntax-backward "->")
394 (when (eq (char-before) ?!)
395 (forward-char -1)
396 (skip-syntax-backward "->")
397 (when (rust-looking-back-ident)
398 (let ((ident (match-string 0)))
399 (not (member ident rust-expression-introducers)))))))
400
401(defun rust-looking-back-macro-rules ()
402 "Non-nil if looking back at \"macro_rules IDENT !\"."
403 (save-excursion
404 (skip-syntax-backward "->")
405 (let ((outer-point (point)))
406 (forward-thing 'symbol -2)
407 (and (looking-at (concat "macro_rules\\s-*!\\s-*" rust-re-ident))
408 (eq (match-end 0) outer-point)))))
409
410;;; Syntax definitions and helpers
411
412(defun rust-paren-level () (nth 0 (syntax-ppss)))
413(defun rust-in-str () (nth 3 (syntax-ppss)))
414(defun rust-in-str-or-cmnt () (nth 8 (syntax-ppss)))
415(defun rust-rewind-past-str-cmnt () (goto-char (nth 8 (syntax-ppss))))
416
417(defun rust-rewind-irrelevant ()
418 (let ((continue t))
419 (while continue
420 (let ((starting (point)))
421 (skip-chars-backward "[:space:]\n")
422 (when (rust-looking-back-str "*/")
423 (backward-char))
424 (when (rust-in-str-or-cmnt)
425 (rust-rewind-past-str-cmnt))
426 ;; Rewind until the point no longer moves
427 (setq continue (/= starting (point)))))))
428
429(defun rust-in-macro ()
430 "Return non-nil when point is within the scope of a macro.
431If we are, return the position of the opening bracket of the macro's arguments."
432 (let ((ppss (syntax-ppss)))
433 ;; If we're in a string or comment, we're definitely not on a token a macro
434 ;; will see.
435 (when (not (or (nth 3 ppss) (nth 4 ppss)))
436 ;; Walk outward to enclosing parens, looking for one preceded by "ident !"
437 ;; or "macro_rules! ident".
438 (let (result
439 (enclosing (reverse (nth 9 ppss))))
440 (save-excursion
441 (while enclosing
442 (goto-char (car enclosing))
443 (if (or (rust-looking-back-macro)
444 (rust-looking-back-macro-rules))
445 (setq result (point) enclosing nil)
446 (setq enclosing (cdr enclosing)))))
447 result))))
448
449(defun rust-looking-at-where ()
450 "Return T when looking at the \"where\" keyword."
451 (and (looking-at-p "\\bwhere\\b")
452 (not (rust-in-str-or-cmnt))))
453
454(defun rust-rewind-to-where (&optional limit)
455 "Rewind the point to the closest occurrence of the \"where\" keyword.
456Return T iff a where-clause was found. Does not rewind past
457LIMIT when passed, otherwise only stops at the beginning of the
458buffer."
459 (when (re-search-backward "\\bwhere\\b" limit t)
460 (if (rust-in-str-or-cmnt)
461 (rust-rewind-to-where limit)
462 t)))
463
464(defconst rust-re-pre-expression-operators "[-=!%&*/:<>[{(|.^;}]")
465
466(defconst rust-re-special-types (regexp-opt rust-special-types 'symbols))
467
468(defun rust-align-to-expr-after-brace ()
469 (save-excursion
470 (forward-char)
471 ;; We don't want to indent out to the open bracket if the
472 ;; open bracket ends the line
473 (when (not (looking-at "[[:blank:]]*\\(?://.*\\)?$"))
474 (when (looking-at "[[:space:]]")
475 (forward-word 1)
476 (backward-word 1))
477 (current-column))))
478
479(defun rust-rewind-to-beginning-of-current-level-expr ()
480 (let ((current-level (rust-paren-level)))
481 (back-to-indentation)
482 (when (looking-at "->")
483 (rust-rewind-irrelevant)
484 (back-to-indentation))
485 (while (> (rust-paren-level) current-level)
486 (backward-up-list)
487 (back-to-indentation))
488 ;; When we're in the where clause, skip over it. First find out the start
489 ;; of the function and its paren level.
490 (let ((function-start nil) (function-level nil))
491 (save-excursion
492 (rust-beginning-of-defun)
493 (back-to-indentation)
494 ;; Avoid using multiple-value-bind
495 (setq function-start (point)
496 function-level (rust-paren-level)))
497 ;; On a where clause
498 (when (or (rust-looking-at-where)
499 ;; or in one of the following lines, e.g.
500 ;; where A: Eq
501 ;; B: Hash <- on this line
502 (and (save-excursion
503 (rust-rewind-to-where function-start))
504 (= current-level function-level)))
505 (goto-char function-start)))))
506
507(defun rust-align-to-method-chain ()
508 (save-excursion
509 ;; for method-chain alignment to apply, we must be looking at
510 ;; another method call or field access or something like
511 ;; that. This avoids rather "eager" jumps in situations like:
512 ;;
513 ;; {
514 ;; something.foo()
515 ;; <indent>
516 ;;
517 ;; Without this check, we would wind up with the cursor under the
518 ;; `.`. In an older version, I had the inverse of the current
519 ;; check, where we checked for situations that should NOT indent,
520 ;; vs checking for the one situation where we SHOULD. It should be
521 ;; clear that this is more robust, but also I find it mildly less
522 ;; annoying to have to press tab again to align to a method chain
523 ;; than to have an over-eager indent in all other cases which must
524 ;; be undone via tab.
525
526 (when (looking-at (concat "\s*\." rust-re-ident))
527 (forward-line -1)
528 (end-of-line)
529 ;; Keep going up (looking for a line that could contain a method chain)
530 ;; while we're in a comment or on a blank line. Stop when the paren
531 ;; level changes.
532 (let ((level (rust-paren-level)))
533 (while (and (or (rust-in-str-or-cmnt)
534 ;; Only whitespace (or nothing) from the beginning to
535 ;; the end of the line.
536 (looking-back "^\s*" (line-beginning-position)))
537 (= (rust-paren-level) level))
538 (forward-line -1)
539 (end-of-line)))
540
541 (let
542 ;; skip-dot-identifier is used to position the point at the
543 ;; `.` when looking at something like
544 ;;
545 ;; foo.bar
546 ;; ^ ^
547 ;; | |
548 ;; | position of point
549 ;; returned offset
550 ;;
551 ((skip-dot-identifier
552 (lambda ()
553 (when (and (rust-looking-back-ident)
554 (save-excursion
555 (forward-thing 'symbol -1)
556 (= ?. (char-before))))
557 (forward-thing 'symbol -1)
558 (backward-char)
559 (- (current-column) rust-indent-offset)))))
560 (cond
561 ;; foo.bar(...)
562 ((looking-back "[)?]" (1- (point)))
563 (backward-list 1)
564 (funcall skip-dot-identifier))
565
566 ;; foo.bar
567 (t (funcall skip-dot-identifier)))))))
568
569(defun rust-mode-indent-line ()
570 (interactive)
571 (let ((indent
572 (save-excursion
573 (back-to-indentation)
574 ;; Point is now at beginning of current line
575 (let* ((level (rust-paren-level))
576 (baseline
577 ;; Our "baseline" is one level out from the
578 ;; indentation of the expression containing the
579 ;; innermost enclosing opening bracket. That way
580 ;; if we are within a block that has a different
581 ;; indentation than this mode would give it, we
582 ;; still indent the inside of it correctly relative
583 ;; to the outside.
584 (if (= 0 level)
585 0
586 (or
587 (when rust-indent-method-chain
588 (rust-align-to-method-chain))
589 (save-excursion
590 (rust-rewind-irrelevant)
591 (backward-up-list)
592 (rust-rewind-to-beginning-of-current-level-expr)
593 (+ (current-column) rust-indent-offset))))))
594 (cond
595 ;; Indent inside a non-raw string only if the previous line
596 ;; ends with a backslash that is inside the same string
597 ((nth 3 (syntax-ppss))
598 (let*
599 ((string-begin-pos (nth 8 (syntax-ppss)))
600 (end-of-prev-line-pos
601 (and (not (rust--same-line-p (point) (point-min)))
602 (line-end-position 0))))
603 (when
604 (and
605 ;; If the string begins with an "r" it's a raw string and
606 ;; we should not change the indentation
607 (/= ?r (char-after string-begin-pos))
608
609 ;; If we're on the first line this will be nil and the
610 ;; rest does not apply
611 end-of-prev-line-pos
612
613 ;; The end of the previous line needs to be inside the
614 ;; current string...
615 (> end-of-prev-line-pos string-begin-pos)
616
617 ;; ...and end with a backslash
618 (= ?\\ (char-before end-of-prev-line-pos)))
619
620 ;; Indent to the same level as the previous line, or the
621 ;; start of the string if the previous line starts the string
622 (if (rust--same-line-p end-of-prev-line-pos string-begin-pos)
623 ;; The previous line is the start of the string.
624 ;; If the backslash is the only character after the
625 ;; string beginning, indent to the next indent
626 ;; level. Otherwise align with the start of the string.
627 (if (> (- end-of-prev-line-pos string-begin-pos) 2)
628 (save-excursion
629 (goto-char (+ 1 string-begin-pos))
630 (current-column))
631 baseline)
632
633 ;; The previous line is not the start of the string, so
634 ;; match its indentation.
635 (save-excursion
636 (goto-char end-of-prev-line-pos)
637 (back-to-indentation)
638 (current-column))))))
639
640 ;; A function return type is indented to the corresponding
641 ;; function arguments, if -to-arguments is selected.
642 ((and rust-indent-return-type-to-arguments
643 (looking-at "->"))
644 (save-excursion
645 (backward-list)
646 (or (rust-align-to-expr-after-brace)
647 (+ baseline rust-indent-offset))))
648
649 ;; A closing brace is 1 level unindented
650 ((looking-at "[]})]") (- baseline rust-indent-offset))
651
652 ;; Doc comments in /** style with leading * indent to line up the *s
653 ((and (nth 4 (syntax-ppss)) (looking-at "*"))
654 (+ 1 baseline))
655
656 ;; When the user chose not to indent the start of the where
657 ;; clause, put it on the baseline.
658 ((and (not rust-indent-where-clause)
659 (rust-looking-at-where))
660 baseline)
661
662 ;; If we're in any other token-tree / sexp, then:
663 (t
664 (or
665 ;; If we are inside a pair of braces, with something after the
666 ;; open brace on the same line and ending with a comma, treat
667 ;; it as fields and align them.
668 (when (> level 0)
669 (save-excursion
670 (rust-rewind-irrelevant)
671 (backward-up-list)
672 ;; Point is now at the beginning of the containing set of braces
673 (rust-align-to-expr-after-brace)))
674
675 ;; When where-clauses are spread over multiple lines, clauses
676 ;; should be aligned on the type parameters. In this case we
677 ;; take care of the second and following clauses (the ones
678 ;; that don't start with "where ")
679 (save-excursion
680 ;; Find the start of the function, we'll use this to limit
681 ;; our search for "where ".
682 (let ((function-start nil) (function-level nil))
683 (save-excursion
684 ;; If we're already at the start of a function,
685 ;; don't go back any farther. We can easily do
686 ;; this by moving to the end of the line first.
687 (end-of-line)
688 (rust-beginning-of-defun)
689 (back-to-indentation)
690 ;; Avoid using multiple-value-bind
691 (setq function-start (point)
692 function-level (rust-paren-level)))
693 ;; When we're not on a line starting with "where ", but
694 ;; still on a where-clause line, go to "where "
695 (when (and
696 (not (rust-looking-at-where))
697 ;; We're looking at something like "F: ..."
698 (looking-at (concat rust-re-ident ":"))
699 ;; There is a "where " somewhere after the
700 ;; start of the function.
701 (rust-rewind-to-where function-start)
702 ;; Make sure we're not inside the function
703 ;; already (e.g. initializing a struct) by
704 ;; checking we are the same level.
705 (= function-level level))
706 ;; skip over "where"
707 (forward-char 5)
708 ;; Unless "where" is at the end of the line
709 (if (eolp)
710 ;; in this case the type parameters bounds are just
711 ;; indented once
712 (+ baseline rust-indent-offset)
713 ;; otherwise, skip over whitespace,
714 (skip-chars-forward "[:space:]")
715 ;; get the column of the type parameter and use that
716 ;; as indentation offset
717 (current-column)))))
718
719 (progn
720 (back-to-indentation)
721 ;; Point is now at the beginning of the current line
722 (if (or
723 ;; If this line begins with "else" or "{", stay on the
724 ;; baseline as well (we are continuing an expression,
725 ;; but the "else" or "{" should align with the beginning
726 ;; of the expression it's in.)
727 ;; Or, if this line starts a comment, stay on the
728 ;; baseline as well.
729 (looking-at "\\<else\\>\\|{\\|/[/*]")
730
731 ;; If this is the start of a top-level item,
732 ;; stay on the baseline.
733 (looking-at rust-top-item-beg-re)
734
735 (save-excursion
736 (rust-rewind-irrelevant)
737 ;; Point is now at the end of the previous line
738 (or
739 ;; If we are at the start of the buffer, no
740 ;; indentation is needed, so stay at baseline...
741 (= (point) 1)
742 ;; ..or if the previous line ends with any of these:
743 ;; { ? : ( , ; [ }
744 ;; then we are at the beginning of an
745 ;; expression, so stay on the baseline...
746 (looking-back "[(,:;[{}]\\|[^|]|" (- (point) 2))
747 ;; or if the previous line is the end of an
748 ;; attribute, stay at the baseline...
749 (progn (rust-rewind-to-beginning-of-current-level-expr)
750 (looking-at "#")))))
751 baseline
752
753 ;; Otherwise, we are continuing the same expression from
754 ;; the previous line, so add one additional indent level
755 (+ baseline rust-indent-offset))))))))))
756
757 (when indent
758 ;; If we're at the beginning of the line (before or at the current
759 ;; indentation), jump with the indentation change. Otherwise, save the
760 ;; excursion so that adding the indentations will leave us at the
761 ;; equivalent position within the line to where we were before.
762 (if (<= (current-column) (current-indentation))
763 (indent-line-to indent)
764 (save-excursion (indent-line-to indent))))))
765
766(defun rust--same-line-p (pos1 pos2)
767 "Return non-nil if POS1 and POS2 are on the same line."
768 (save-excursion (= (progn (goto-char pos1) (line-end-position))
769 (progn (goto-char pos2) (line-end-position)))))
770
771;;; Font-locking definitions and helpers
772
773(defun rust-next-string-interpolation (limit)
774 "Search forward from point for next Rust interpolation marker before LIMIT.
775Set point to the end of the occurrence found, and return match beginning
776and end."
777 (catch 'match
778 (save-match-data
779 (save-excursion
780 (while (search-forward "{" limit t)
781 (if (eql (char-after (point)) ?{)
782 (forward-char)
783 (let ((start (match-beginning 0)))
784 ;; According to fmt_macros::Parser::next, an opening brace
785 ;; must be followed by an optional argument and/or format
786 ;; specifier, then a closing brace. A single closing brace
787 ;; without a corresponding unescaped opening brace is an
788 ;; error. We don't need to do anything special with
789 ;; arguments, specifiers, or errors, so we only search for
790 ;; the single closing brace.
791 (when (search-forward "}" limit t)
792 (throw 'match (list start (point)))))))))))
793
794(defun rust-string-interpolation-matcher (limit)
795 "Match next Rust interpolation marker before LIMIT and set match data if found.
796Returns nil if not within a Rust string."
797 (when-let (((rust-in-str))
798 (match (rust-next-string-interpolation limit)))
799 (set-match-data match)
800 (goto-char (cadr match))
801 match))
802
803(defun rust-syntax-class-before-point ()
804 (when (> (point) 1)
805 (syntax-class (syntax-after (1- (point))))))
806
807(defun rust-rewind-qualified-ident ()
808 (while (rust-looking-back-ident)
809 (backward-sexp)
810 (when (save-excursion (rust-rewind-irrelevant) (rust-looking-back-str "::"))
811 (rust-rewind-irrelevant)
812 (backward-char 2)
813 (rust-rewind-irrelevant))))
814
815(defun rust-rewind-type-param-list ()
816 (cond
817 ((and (rust-looking-back-str ">") (equal 5 (rust-syntax-class-before-point)))
818 (backward-sexp)
819 (rust-rewind-irrelevant))
820
821 ;; We need to be able to back up past the Fn(args) -> RT form as well. If
822 ;; we're looking back at this, we want to end up just after "Fn".
823 ((member (char-before) '(?\] ?\) ))
824 (let ((is-paren (rust-looking-back-str ")")))
825 (when-let ((dest (save-excursion
826 (backward-sexp)
827 (rust-rewind-irrelevant)
828 (or
829 (when (rust-looking-back-str "->")
830 (backward-char 2)
831 (rust-rewind-irrelevant)
832 (when (rust-looking-back-str ")")
833 (backward-sexp)
834 (point)))
835 (and is-paren (point))))))
836 (goto-char dest))))))
837
838(defun rust-rewind-to-decl-name ()
839 "Return the point at the beginning of the name in a declaration.
840I.e. if we are before an ident that is part of a declaration that
841can have a where clause, rewind back to just before the name of
842the subject of that where clause and return the new point.
843Otherwise return nil."
844 (let* ((ident-pos (point))
845 (newpos (save-excursion
846 (rust-rewind-irrelevant)
847 (rust-rewind-type-param-list)
848 (cond
849 ((rust-looking-back-symbols
850 '("fn" "trait" "enum" "struct" "union" "impl" "type"))
851 ident-pos)
852
853 ((equal 5 (rust-syntax-class-before-point))
854 (backward-sexp)
855 (rust-rewind-to-decl-name))
856
857 ((looking-back "[:,'+=]" (1- (point)))
858 (backward-char)
859 (rust-rewind-to-decl-name))
860
861 ((rust-looking-back-str "->")
862 (backward-char 2)
863 (rust-rewind-to-decl-name))
864
865 ((rust-looking-back-ident)
866 (rust-rewind-qualified-ident)
867 (rust-rewind-to-decl-name))))))
868 (when newpos (goto-char newpos))
869 newpos))
870
871(defun rust-is-in-expression-context (token)
872 "Return t if what comes right after the point is part of an
873expression (as opposed to starting a type) by looking at what
874comes before. Takes a symbol that roughly indicates what is
875after the point.
876
877This function is used as part of `rust-is-lt-char-operator' as
878part of angle bracket matching, and is not intended to be used
879outside of this context."
880 (save-excursion
881 (let ((postchar (char-after)))
882 (rust-rewind-irrelevant)
883 ;; A type alias or ascription could have a type param list. Skip backwards past it.
884 (when (member token '(ambiguous-operator open-brace))
885 (rust-rewind-type-param-list))
886 (cond
887
888 ;; Certain keywords always introduce expressions
889 ((rust-looking-back-symbols rust-expression-introducers) t)
890
891 ;; "as" introduces a type
892 ((rust-looking-back-symbols '("as")) nil)
893
894 ;; An open angle bracket never introduces expression context WITHIN the angle brackets
895 ((and (equal token 'open-brace) (equal postchar ?<)) nil)
896
897 ;; An ident! followed by an open brace is a macro invocation. Consider
898 ;; it to be an expression.
899 ((and (equal token 'open-brace) (rust-looking-back-macro)) t)
900
901 ;; In a brace context a "]" introduces an expression.
902 ((and (eq token 'open-brace) (rust-looking-back-str "]")))
903
904 ;; An identifier is right after an ending paren, bracket, angle bracket
905 ;; or curly brace. It's a type if the last sexp was a type.
906 ((and (equal token 'ident) (equal 5 (rust-syntax-class-before-point)))
907 (backward-sexp)
908 (rust-is-in-expression-context 'open-brace))
909
910 ;; If a "for" appears without a ; or { before it, it's part of an
911 ;; "impl X for y", so the y is a type. Otherwise it's
912 ;; introducing a loop, so the y is an expression
913 ((and (equal token 'ident) (rust-looking-back-symbols '("for")))
914 (backward-sexp)
915 (rust-rewind-irrelevant)
916 (looking-back "[{;]" (1- (point))))
917
918 ((rust-looking-back-ident)
919 (rust-rewind-qualified-ident)
920 (rust-rewind-irrelevant)
921 (cond
922 ((equal token 'open-brace)
923 ;; We now know we have:
924 ;; ident <maybe type params> [{([]
925 ;; where [{([] denotes either a {, ( or [.
926 ;; This character is bound as postchar.
927 (cond
928 ;; If postchar is a paren or square bracket, then if the
929 ;; brace is a type if the identifier is one
930 ((member postchar '(?\( ?\[ )) (rust-is-in-expression-context 'ident))
931
932 ;; If postchar is a curly brace, the brace can only be a type if
933 ;; ident2 is the name of an enum, struct or trait being declared.
934 ;; Note that if there is a -> before the ident then the ident would
935 ;; be a type but the { is not.
936 ((equal ?{ postchar)
937 (not (and (rust-rewind-to-decl-name)
938 (progn
939 (rust-rewind-irrelevant)
940 (rust-looking-back-symbols
941 '("enum" "struct" "union" "trait" "type"))))))))
942
943 ((equal token 'ambiguous-operator)
944 (cond
945 ;; An ampersand after an ident has to be an operator rather
946 ;; than a & at the beginning of a ref type
947 ((equal postchar ?&) t)
948
949 ;; A : followed by a type then an = introduces an
950 ;; expression (unless it is part of a where clause of a
951 ;; "type" declaration)
952 ((and (equal postchar ?=)
953 (looking-back "[^:]:" (- (point) 2))
954 (not (save-excursion
955 (and (rust-rewind-to-decl-name)
956 (progn (rust-rewind-irrelevant)
957 (rust-looking-back-symbols '("type"))))))))
958
959 ;; "let ident =" introduces an expression--and so does "const" and "mut"
960 ((and (equal postchar ?=) (rust-looking-back-symbols '("let" "const" "mut"))) t)
961
962 ;; As a specific special case, see if this is the = in this situation:
963 ;; enum EnumName<type params> { Ident =
964 ;; In this case, this is a c-like enum and despite Ident
965 ;; representing a type, what comes after the = is an expression
966 ((and
967 (> (rust-paren-level) 0)
968 (save-excursion
969 (backward-up-list)
970 (rust-rewind-irrelevant)
971 (rust-rewind-type-param-list)
972 (and
973 (rust-looking-back-ident)
974 (progn
975 (rust-rewind-qualified-ident)
976 (rust-rewind-irrelevant)
977 (rust-looking-back-str "enum")))))
978 t)
979
980 ;; Otherwise the ambiguous operator is a type if the identifier is a type
981 ((rust-is-in-expression-context 'ident) t)))
982
983 ((equal token 'colon)
984 (cond
985 ;; If we see a ident: not inside any braces/parens, we're at top level.
986 ;; There are no allowed expressions after colons there, just types.
987 ((<= (rust-paren-level) 0) nil)
988
989 ;; We see ident: inside a list
990 ((looking-back "[{,]" (1- (point)))
991 (backward-up-list)
992
993 ;; If a : appears whose surrounding paren/brackets/braces are
994 ;; anything other than curly braces, it can't be a field
995 ;; initializer and must be denoting a type.
996 (when (looking-at "{")
997 (rust-rewind-irrelevant)
998 (rust-rewind-type-param-list)
999 (when (rust-looking-back-ident)
1000 ;; We have a context that looks like this:
1001 ;; ident2 <maybe type params> { [maybe paren-balanced code ending in comma] ident1:
1002 ;; the point is sitting just after ident2, and we trying to
1003 ;; figure out if the colon introduces an expression or a type.
1004 ;; The answer is that ident1 is a field name, and what comes
1005 ;; after the colon is an expression, if ident2 is an
1006 ;; expression.
1007 (rust-rewind-qualified-ident)
1008 (rust-is-in-expression-context 'ident))))
1009
1010 ;; Otherwise, if the ident: appeared with anything other than , or {
1011 ;; before it, it can't be part of a struct initializer and therefore
1012 ;; must be denoting a type.
1013 (t nil)))))
1014
1015 ;; An operator-like character after a string is indeed an operator
1016 ((and (equal token 'ambiguous-operator)
1017 (member (rust-syntax-class-before-point) '(5 7 15))) t)
1018
1019 ;; A colon that has something other than an identifier before it is a
1020 ;; type ascription
1021 ((equal token 'colon) nil)
1022
1023 ;; A :: introduces a type (or module, but not an expression in any case)
1024 ((rust-looking-back-str "::") nil)
1025
1026 ((rust-looking-back-str ":")
1027 (backward-char)
1028 (rust-is-in-expression-context 'colon))
1029
1030 ;; A -> introduces a type
1031 ((rust-looking-back-str "->") nil)
1032
1033 ;; If we are up against the beginning of a list, or after a comma inside
1034 ;; of one, back up out of it and check what the list itself is
1035 ((or
1036 (equal 4 (rust-syntax-class-before-point))
1037 (rust-looking-back-str ","))
1038 (condition-case nil
1039 (progn
1040 (backward-up-list)
1041 (rust-is-in-expression-context 'open-brace))
1042 (scan-error nil)))
1043
1044 ;; A => introduces an expression
1045 ((rust-looking-back-str "=>") t)
1046
1047 ;; A == introduces an expression
1048 ((rust-looking-back-str "==") t)
1049
1050 ;; These operators can introduce expressions or types
1051 ((looking-back "[-+=!?&*]" (1- (point)))
1052 (backward-char)
1053 (rust-is-in-expression-context 'ambiguous-operator))
1054
1055 ;; These operators always introduce expressions. (Note that if this
1056 ;; regexp finds a < it must not be an angle bracket, or it'd
1057 ;; have been caught in the syntax-class check above instead of this.)
1058 ((looking-back rust-re-pre-expression-operators (1- (point))) t)))))
1059
1060(defun rust-is-lt-char-operator ()
1061 "Return non-nil if the `<' sign just after point is an operator.
1062Otherwise, if it is an opening angle bracket, then return nil."
1063 (let ((case-fold-search nil))
1064 (save-excursion
1065 (rust-rewind-irrelevant)
1066 ;; We are now just after the character syntactically before the <.
1067 (cond
1068
1069 ;; If we are looking back at a < that is not an angle bracket (but not
1070 ;; two of them) then this is the second < in a bit shift operator
1071 ((and (rust-looking-back-str "<")
1072 (not (equal 4 (rust-syntax-class-before-point)))
1073 (not (rust-looking-back-str "<<"))))
1074
1075 ;; On the other hand, if we are after a closing paren/brace/bracket it
1076 ;; can only be an operator, not an angle bracket. Likewise, if we are
1077 ;; after a string it's an operator. (The string case could actually be
1078 ;; valid in rust for character literals.)
1079 ((member (rust-syntax-class-before-point) '(5 7 15)) t)
1080
1081 ;; If we are looking back at an operator, we know that we are at
1082 ;; the beginning of an expression, and thus it has to be an angle
1083 ;; bracket (starting a "<Type as Trait>::" construct.)
1084 ((looking-back rust-re-pre-expression-operators (1- (point))) nil)
1085
1086 ;; If we are looking back at a keyword, it's an angle bracket
1087 ;; unless that keyword is "self", "true" or "false"
1088 ((rust-looking-back-symbols rust-keywords)
1089 (rust-looking-back-symbols '("self" "true" "false")))
1090
1091 ((rust-looking-back-str "?")
1092 (rust-is-in-expression-context 'ambiguous-operator))
1093
1094 ;; If we're looking back at an identifier, this depends on whether
1095 ;; the identifier is part of an expression or a type
1096 ((rust-looking-back-ident)
1097 (backward-sexp)
1098 (or
1099 ;; The special types can't take type param lists, so a < after one is
1100 ;; always an operator
1101 (looking-at rust-re-special-types)
1102
1103 (rust-is-in-expression-context 'ident)))
1104
1105 ;; Otherwise, assume it's an angle bracket
1106 ))))
1107
1108(defun rust-electric-pair-inhibit-predicate-wrap (char)
1109 "Prevent \"matching\" with a `>' when CHAR is the less-than operator.
1110This wraps the default defined by `electric-pair-inhibit-predicate'."
1111 (or
1112 (when (= ?< char)
1113 (save-excursion
1114 (backward-char)
1115 (rust-is-lt-char-operator)))
1116 (funcall (default-value 'electric-pair-inhibit-predicate) char)))
1117
1118(defun rust-electric-pair-skip-self (char)
1119 "Skip CHAR instead of inserting a second closing character.
1120This is added to the default skips defined by `electric-pair-skip-self'."
1121 (= ?> char))
1122
1123(defun rust-ordinary-lt-gt-p ()
1124 "Test whether the `<' or `>' at point is an ordinary operator of some kind.
1125
1126This returns t if the `<' or `>' is an ordinary operator (like
1127less-than) or part of one (like `->'); and nil if the character
1128should be considered a paired angle bracket."
1129 (cond
1130 ;; If matching is turned off suppress all of them
1131 ((not rust-match-angle-brackets) t)
1132
1133 ;; This is a cheap check so we do it early.
1134 ;; Don't treat the > in -> or => as an angle bracket
1135 ((and (= (following-char) ?>) (memq (preceding-char) '(?- ?=))) t)
1136
1137 ;; We don't take < or > in strings or comments to be angle brackets
1138 ((rust-in-str-or-cmnt) t)
1139
1140 ;; Inside a macro we don't really know the syntax. Any < or > may be an
1141 ;; angle bracket or it may not. But we know that the other braces have
1142 ;; to balance regardless of the < and >, so if we don't treat any < or >
1143 ;; as angle brackets it won't mess up any paren balancing.
1144 ((rust-in-macro) t)
1145
1146 ((= (following-char) ?<)
1147 (rust-is-lt-char-operator))
1148
1149 ;; Since rust-ordinary-lt-gt-p is called only when either < or > are at the point,
1150 ;; we know that the following char must be > in the clauses below.
1151
1152 ;; If we are at top level and not in any list, it can't be a closing
1153 ;; angle bracket
1154 ((>= 0 (rust-paren-level)) t)
1155
1156 ;; Otherwise, treat the > as a closing angle bracket if it would
1157 ;; match an opening one
1158 ((save-excursion
1159 (backward-up-list)
1160 (/= (following-char) ?<)))))
1161
1162(defun rust-mode-syntactic-face-function (state)
1163 "Return face that distinguishes doc and normal comments in given syntax STATE."
1164 (if (nth 3 state)
1165 'font-lock-string-face
1166 (save-excursion
1167 (goto-char (nth 8 state))
1168 (if (looking-at "/\\([*][*!][^*!]\\|/[/!][^/!]\\)")
1169 'font-lock-doc-face
1170 'font-lock-comment-face))))
1171
1172(eval-and-compile
1173 (defconst rust--char-literal-rx
1174 (rx (seq
1175 (group "'")
1176 (or
1177 (seq
1178 "\\"
1179 (or
1180 (: "u{" (** 1 6 xdigit) "}")
1181 (: "x" (= 2 xdigit))
1182 (any "'nrt0\"\\")))
1183 (not (any "'\\")))
1184 (group "'")))
1185 "A regular expression matching a character literal."))
1186
1187(defun rust-fill-prefix-for-comment-start (line-start)
1188 "Determine what to use for `fill-prefix' based on the text at LINE-START."
1189 (let ((result
1190 ;; Replace /* with same number of spaces
1191 (replace-regexp-in-string
1192 "\\(?:/\\*+?\\)[!*]?"
1193 (lambda (s)
1194 ;; We want the * to line up with the first * of the
1195 ;; comment start
1196 (let ((offset (if (eq t
1197 (compare-strings "/*" nil nil
1198 s
1199 (- (length s) 2)
1200 (length s)))
1201 1 2)))
1202 (concat (make-string (- (length s) offset)
1203 ?\x20) "*")))
1204 line-start)))
1205 ;; Make sure we've got at least one space at the end
1206 (if (not (= (aref result (- (length result) 1)) ?\x20))
1207 (setq result (concat result " ")))
1208 result))
1209
1210(defun rust-in-comment-paragraph (body)
1211 ;; We might move the point to fill the next comment, but we don't want it
1212 ;; seeming to jump around on the user
1213 (save-excursion
1214 ;; If we're outside of a comment, with only whitespace and then a comment
1215 ;; in front, jump to the comment and prepare to fill it.
1216 (when (not (nth 4 (syntax-ppss)))
1217 (beginning-of-line)
1218 (when (looking-at (concat "[[:space:]\n]*" comment-start-skip))
1219 (goto-char (match-end 0))))
1220
1221 ;; We need this when we're moving the point around and then checking syntax
1222 ;; while doing paragraph fills, because the cache it uses isn't always
1223 ;; invalidated during this.
1224 (syntax-ppss-flush-cache 1)
1225 ;; If we're at the beginning of a comment paragraph with nothing but
1226 ;; whitespace til the next line, jump to the next line so that we use the
1227 ;; existing prefix to figure out what the new prefix should be, rather than
1228 ;; inferring it from the comment start.
1229 (let ((next-bol (line-beginning-position 2)))
1230 (while (save-excursion
1231 (end-of-line)
1232 (syntax-ppss-flush-cache 1)
1233 (and (nth 4 (syntax-ppss))
1234 (save-excursion
1235 (beginning-of-line)
1236 (looking-at paragraph-start))
1237 (looking-at "[[:space:]]*$")
1238 (nth 4 (syntax-ppss next-bol))))
1239 (goto-char next-bol)))
1240
1241 (syntax-ppss-flush-cache 1)
1242 ;; If we're on the last line of a multiline-style comment that started
1243 ;; above, back up one line so we don't mistake the * of the */ that ends
1244 ;; the comment for a prefix.
1245 (when (save-excursion
1246 (and (nth 4 (syntax-ppss (line-beginning-position 1)))
1247 (looking-at "[[:space:]]*\\*/")))
1248 (goto-char (line-end-position 0)))
1249 (funcall body)))
1250
1251(defun rust-with-comment-fill-prefix (body)
1252 (let*
1253 ((line-string (buffer-substring-no-properties
1254 (line-beginning-position) (line-end-position)))
1255 (line-comment-start
1256 (when (nth 4 (syntax-ppss))
1257 (cond
1258 ;; If we're inside the comment and see a * prefix, use it
1259 ((string-match "^\\([[:space:]]*\\*+[[:space:]]*\\)"
1260 line-string)
1261 (match-string 1 line-string))
1262 ;; If we're at the start of a comment, figure out what prefix
1263 ;; to use for the subsequent lines after it
1264 ((string-match (concat "[[:space:]]*" comment-start-skip) line-string)
1265 (rust-fill-prefix-for-comment-start
1266 (match-string 0 line-string))))))
1267 (fill-prefix
1268 (or line-comment-start
1269 fill-prefix)))
1270 (funcall body)))
1271
1272(defun rust-find-fill-prefix ()
1273 (rust-in-comment-paragraph
1274 (lambda ()
1275 (rust-with-comment-fill-prefix
1276 (lambda ()
1277 fill-prefix)))))
1278
1279(defun rust-fill-paragraph (&rest args)
1280 "Special wrapping for `fill-paragraph'.
1281This handles multi-line comments with a * prefix on each line."
1282 (rust-in-comment-paragraph
1283 (lambda ()
1284 (rust-with-comment-fill-prefix
1285 (lambda ()
1286 (let
1287 ((fill-paragraph-function
1288 (if (not (eq fill-paragraph-function #'rust-fill-paragraph))
1289 fill-paragraph-function))
1290 (fill-paragraph-handle-comment t))
1291 (apply #'fill-paragraph args)
1292 t))))))
1293
1294(defun rust-do-auto-fill (&rest args)
1295 "Special wrapping for `do-auto-fill'.
1296This handles multi-line comments with a * prefix on each line."
1297 (rust-with-comment-fill-prefix
1298 (lambda ()
1299 (apply #'do-auto-fill args)
1300 t)))
1301
1302(defun rust-fill-forward-paragraph (arg)
1303 ;; This is to work around some funny behavior when a paragraph separator is
1304 ;; at the very top of the file and there is a fill prefix.
1305 (let ((fill-prefix nil)) (forward-paragraph arg)))
1306
1307(defun rust-comment-indent-new-line (&optional arg)
1308 (rust-with-comment-fill-prefix
1309 (lambda () (comment-indent-new-line arg))))
1310
1311;;; Defun Motions
1312
1313(defun rust-beginning-of-defun (&optional arg)
1314 "Move backward to the beginning of the current defun.
1315
1316With ARG, move backward multiple defuns. Negative ARG means
1317move forward.
1318
1319This is written mainly to be used as `beginning-of-defun-function' for Rust.
1320Don't move to the beginning of the line. `beginning-of-defun',
1321which calls this, does that afterwards."
1322 (interactive "p")
1323 (let* ((arg (or arg 1))
1324 (magnitude (abs arg))
1325 (sign (if (< arg 0) -1 1)))
1326 ;; If moving forward, don't find the defun we might currently be
1327 ;; on.
1328 (when (< sign 0)
1329 (end-of-line))
1330 (catch 'done
1331 (dotimes (_ magnitude)
1332 ;; Search until we find a match that is not in a string or comment.
1333 (while (if (re-search-backward (concat "^\\(" rust-top-item-beg-re "\\)")
1334 nil 'move sign)
1335 (rust-in-str-or-cmnt)
1336 ;; Did not find it.
1337 (throw 'done nil)))))
1338 t))
1339
1340(defun rust-end-of-defun ()
1341 "Move forward to the next end of defun.
1342
1343With argument, do it that many times.
1344Negative argument -N means move back to Nth preceding end of defun.
1345
1346Assume that this is called after `beginning-of-defun'. So point is
1347at the beginning of the defun body.
1348
1349This is written mainly to be used as `end-of-defun-function' for Rust."
1350 (interactive)
1351 ;; Find the opening brace
1352 (if (re-search-forward "[{]" nil t)
1353 (progn
1354 (goto-char (match-beginning 0))
1355 ;; Go to the closing brace
1356 (condition-case nil
1357 (forward-sexp)
1358 (scan-error
1359 ;; The parentheses are unbalanced; instead of being unable
1360 ;; to fontify, just jump to the end of the buffer
1361 (goto-char (point-max)))))
1362 ;; There is no opening brace, so consider the whole buffer to be one "defun"
1363 (goto-char (point-max))))
1364
1365;;; _
1366
1367(defun rust-mode-reload ()
1368 (interactive)
1369 (unload-feature 'rust-mode)
1370 (require 'rust-mode)
1371 (rust-mode))
1372
1373(defvar rust-mode-syntax-table
1374 (let ((table (make-syntax-table)))
1375
1376 ;; Operators
1377 (dolist (i '(?+ ?- ?* ?/ ?% ?& ?| ?^ ?! ?< ?> ?~ ?@))
1378 (modify-syntax-entry i "." table))
1379
1380 ;; Strings
1381 (modify-syntax-entry ?\" "\"" table)
1382 (modify-syntax-entry ?\\ "\\" table)
1383
1384 ;; Angle brackets. We suppress this with syntactic propertization
1385 ;; when needed
1386 (modify-syntax-entry ?< "(>" table)
1387 (modify-syntax-entry ?> ")<" table)
1388
1389 ;; Comments
1390 (modify-syntax-entry ?/ ". 124b" table)
1391 (modify-syntax-entry ?* ". 23n" table)
1392 (modify-syntax-entry ?\n "> b" table)
1393 (modify-syntax-entry ?\^m "> b" table)
1394
1395 table)
1396 "Syntax definitions and helpers.")
1397
1398(defun rust--syntax-propertize-raw-string (str-start end)
1399 "A helper for rust-syntax-propertize.
1400
1401This will apply the appropriate string syntax to the character
1402from the STR-START up to the end of the raw string, or to END,
1403whichever comes first."
1404 (when (save-excursion
1405 (goto-char str-start)
1406 (looking-at "r\\(#*\\)\\(\"\\)"))
1407 ;; In a raw string, so try to find the end.
1408 (let ((hashes (match-string 1)))
1409 ;; Match \ characters at the end of the string to suppress
1410 ;; their normal character-quote syntax.
1411 (when (re-search-forward (concat "\\(\\\\*\\)\\(\"" hashes "\\)") end t)
1412 (put-text-property (match-beginning 1) (match-end 1)
1413 'syntax-table (string-to-syntax "_"))
1414 (put-text-property (1- (match-end 2)) (match-end 2)
1415 'syntax-table (string-to-syntax "|"))
1416 (goto-char (match-end 0))))))
1417
1418;;; Syntax Propertize
1419
1420(defun rust-syntax-propertize (start end)
1421 "A `syntax-propertize-function' to apply properties from START to END."
1422 (goto-char start)
1423 (when-let ((str-start (rust-in-str-or-cmnt)))
1424 (rust--syntax-propertize-raw-string str-start end))
1425 (funcall
1426 (syntax-propertize-rules
1427 ;; Character literals.
1428 (rust--char-literal-rx (1 "\"") (2 "\""))
1429 ;; Raw strings.
1430 ("\\(r\\)#*\""
1431 (0 (ignore
1432 (goto-char (match-end 0))
1433 (unless (save-excursion (nth 8 (syntax-ppss (match-beginning 0))))
1434 (put-text-property (match-beginning 1) (match-end 1)
1435 'syntax-table (string-to-syntax "|"))
1436 (rust--syntax-propertize-raw-string (match-beginning 0) end)))))
1437 ("[<>]"
1438 (0 (ignore
1439 (when (save-match-data
1440 (save-excursion
1441 (goto-char (match-beginning 0))
1442 (rust-ordinary-lt-gt-p)))
1443 (put-text-property (match-beginning 0) (match-end 0)
1444 'syntax-table (string-to-syntax "."))
1445 (goto-char (match-end 0)))))))
1446 (point) end))
1447
1448(define-derived-mode rust-mode prog-mode "Rust"
1449 "Major mode for Rust code.
1450
1451\\{rust-mode-map}"
1452 :group 'rust-mode
1453 :syntax-table rust-mode-syntax-table
1454
1455 ;; Syntax
1456 (setq-local syntax-propertize-function #'rust-syntax-propertize)
1457
1458 ;; Indentation
1459 (setq-local indent-line-function 'rust-mode-indent-line)
1460
1461 ;; Fonts
1462 (setq-local font-lock-defaults
1463 '(rust-font-lock-keywords
1464 nil nil nil nil
1465 (font-lock-syntactic-face-function
1466 . rust-mode-syntactic-face-function)))
1467
1468 ;; Misc
1469 (setq-local comment-start "// ")
1470 (setq-local comment-end "")
1471 (setq-local open-paren-in-column-0-is-defun-start nil)
1472
1473 ;; Auto indent on }
1474 (setq-local electric-indent-chars
1475 (cons ?} (and (boundp 'electric-indent-chars)
1476 electric-indent-chars)))
1477
1478 ;; Allow paragraph fills for comments
1479 (setq-local comment-start-skip "\\(?://[/!]*\\|/\\*[*!]?\\)[[:space:]]*")
1480 (setq-local paragraph-start
1481 (concat "[[:space:]]*\\(?:"
1482 comment-start-skip
1483 "\\|\\*/?[[:space:]]*\\|\\)$"))
1484 (setq-local paragraph-separate paragraph-start)
1485 (setq-local normal-auto-fill-function #'rust-do-auto-fill)
1486 (setq-local fill-paragraph-function #'rust-fill-paragraph)
1487 (setq-local fill-forward-paragraph-function #'rust-fill-forward-paragraph)
1488 (setq-local adaptive-fill-function #'rust-find-fill-prefix)
1489 (setq-local adaptive-fill-first-line-regexp "")
1490 (setq-local comment-multi-line t)
1491 (setq-local comment-line-break-function #'rust-comment-indent-new-line)
1492 (setq-local imenu-generic-expression rust-imenu-generic-expression)
1493 (setq-local imenu-syntax-alist '((?! . "w"))) ; For macro_rules!
1494 (setq-local beginning-of-defun-function #'rust-beginning-of-defun)
1495 (setq-local end-of-defun-function #'rust-end-of-defun)
1496 (setq-local parse-sexp-lookup-properties t)
1497 (setq-local electric-pair-inhibit-predicate
1498 #'rust-electric-pair-inhibit-predicate-wrap)
1499 (add-function :before-until (local 'electric-pair-skip-self)
1500 #'rust-electric-pair-skip-self)
1501 ;; Configure prettify
1502 (setq prettify-symbols-alist rust-prettify-symbols-alist)
1503 (setq prettify-symbols-compose-predicate #'rust--prettify-symbols-compose-p)
1504
1505 (add-hook 'before-save-hook rust-before-save-hook nil t)
1506 (add-hook 'after-save-hook rust-after-save-hook nil t))
1507
1508(provide 'rust-prog-mode)
1509;;; rust-prog-mode.el ends here
diff --git a/.emacs.d/lisp/rust-rustfmt.el b/.emacs.d/lisp/rust-rustfmt.el
index bb88647..f609980 100644
--- a/.emacs.d/lisp/rust-rustfmt.el
+++ b/.emacs.d/lisp/rust-rustfmt.el
@@ -41,49 +41,54 @@
41 41
42(defun rust--format-call (buf) 42(defun rust--format-call (buf)
43 "Format BUF using rustfmt." 43 "Format BUF using rustfmt."
44 (with-current-buffer (get-buffer-create rust-rustfmt-buffername) 44 (let ((path exec-path))
45 (view-mode +1) 45 (with-current-buffer (get-buffer-create rust-rustfmt-buffername)
46 (let ((inhibit-read-only t)) 46 (setq-local exec-path path)
47 (erase-buffer) 47 (view-mode +1)
48 (insert-buffer-substring buf) 48 (let ((inhibit-read-only t))
49 (let* ((tmpf (make-temp-file "rustfmt")) 49 (erase-buffer)
50 (ret (apply #'call-process-region 50 (insert-buffer-substring buf)
51 (point-min) 51 (let* ((tmpf (make-temp-file "rustfmt"))
52 (point-max) 52 (ret (apply #'call-process-region
53 rust-rustfmt-bin 53 (point-min)
54 t 54 (point-max)
55 `(t ,tmpf) 55 rust-rustfmt-bin
56 nil 56 t
57 rust-rustfmt-switches))) 57 `(t ,tmpf)
58 (unwind-protect 58 nil
59 (cond 59 rust-rustfmt-switches)))
60 ((zerop ret) 60 (unwind-protect
61 (if (not (string= (buffer-string) 61 (cond
62 (with-current-buffer buf (buffer-string)))) 62 ((zerop ret)
63 ;; replace-buffer-contents was in emacs 26.1, but it 63 (if (not (string= (buffer-string)
64 ;; was broken for non-ASCII strings, so we need 26.2. 64 (with-current-buffer buf (buffer-string))))
65 (if (and (fboundp 'replace-buffer-contents) 65 ;; replace-buffer-contents was in emacs 26.1, but it
66 (version<= "26.2" emacs-version)) 66 ;; was broken for non-ASCII strings, so we need 26.2.
67 (with-current-buffer buf 67 (if (and (fboundp 'replace-buffer-contents)
68 (replace-buffer-contents rust-rustfmt-buffername)) 68 (version<= "26.2" emacs-version))
69 (copy-to-buffer buf (point-min) (point-max)))) 69 (with-current-buffer buf
70 (kill-buffer)) 70 (replace-buffer-contents rust-rustfmt-buffername))
71 ((= ret 3) 71 (copy-to-buffer buf (point-min) (point-max))))
72 (if (not (string= (buffer-string) 72 (let ((win (get-buffer-window rust-rustfmt-buffername)))
73 (with-current-buffer buf (buffer-string)))) 73 (if win
74 (copy-to-buffer buf (point-min) (point-max))) 74 (quit-window t win)
75 (erase-buffer) 75 (kill-buffer rust-rustfmt-buffername))))
76 (insert-file-contents tmpf) 76 ((= ret 3)
77 (rust--format-fix-rustfmt-buffer (buffer-name buf)) 77 (if (not (string= (buffer-string)
78 (error "Rustfmt could not format some lines, see %s buffer for details" 78 (with-current-buffer buf (buffer-string))))
79 rust-rustfmt-buffername)) 79 (copy-to-buffer buf (point-min) (point-max)))
80 (t 80 (erase-buffer)
81 (erase-buffer) 81 (insert-file-contents tmpf)
82 (insert-file-contents tmpf) 82 (rust--format-fix-rustfmt-buffer (buffer-name buf))
83 (rust--format-fix-rustfmt-buffer (buffer-name buf)) 83 (error "Rustfmt could not format some lines, see %s buffer for details"
84 (error "Rustfmt failed, see %s buffer for details" 84 rust-rustfmt-buffername))
85 rust-rustfmt-buffername)))) 85 (t
86 (delete-file tmpf))))) 86 (erase-buffer)
87 (insert-file-contents tmpf)
88 (rust--format-fix-rustfmt-buffer (buffer-name buf))
89 (error "Rustfmt failed, see %s buffer for details"
90 rust-rustfmt-buffername))))
91 (delete-file tmpf))))))
87 92
88;; Since we run rustfmt through stdin we get <stdin> markers in the 93;; Since we run rustfmt through stdin we get <stdin> markers in the
89;; output. This replaces them with the buffer name instead. 94;; output. This replaces them with the buffer name instead.
@@ -147,7 +152,8 @@ rustfmt complain in the echo area."
147 (goto-char (point-min)) 152 (goto-char (point-min))
148 (forward-line (1- (car target-point))) 153 (forward-line (1- (car target-point)))
149 (forward-char (1- (cdr target-point)))) 154 (forward-char (1- (cdr target-point))))
150 (message target-problem))))) 155 (unless rust-format-show-buffer
156 (message target-problem))))))
151 157
152(defconst rust--format-word "\ 158(defconst rust--format-word "\
153\\b\\(else\\|enum\\|fn\\|for\\|if\\|let\\|loop\\|\ 159\\b\\(else\\|enum\\|fn\\|for\\|if\\|let\\|loop\\|\
diff --git a/.emacs.d/lisp/rust-utils.el b/.emacs.d/lisp/rust-utils.el
index cb55172..d93bd0a 100644
--- a/.emacs.d/lisp/rust-utils.el
+++ b/.emacs.d/lisp/rust-utils.el
@@ -41,22 +41,22 @@ visit the new file."
41if not. Move cursor to the end of macro." 41if not. Move cursor to the end of macro."
42 (when (rust-in-str) 42 (when (rust-in-str)
43 (up-list -1 t t)) 43 (up-list -1 t t))
44 (setq safe-to-forward t) 44 (let ((safe-to-forward t))
45 (save-excursion 45 (save-excursion
46 (condition-case nil 46 (condition-case nil
47 (forward-sexp) 47 (forward-sexp)
48 (error (setq safe-to-forward nil) 48 (error (setq safe-to-forward nil)
49 nil))) 49 nil)))
50 (cond 50 (cond
51 ((not safe-to-forward) 51 ((not safe-to-forward)
52 (rust-insert-dbg-alone)) 52 (rust-insert-dbg-alone))
53 (t 53 (t
54 (insert "(") 54 (insert "(")
55 (forward-sexp) 55 (forward-sexp)
56 (insert ")") 56 (insert ")")
57 (backward-sexp) 57 (backward-sexp)
58 (insert "dbg!") 58 (insert "dbg!")
59 (forward-sexp)))) 59 (forward-sexp)))))
60 60
61(defun rust-insert-dbg-region () 61(defun rust-insert-dbg-region ()
62 "Insert the dbg! macro around a region. Move cursor to the end of macro." 62 "Insert the dbg! macro around a region. Move cursor to the end of macro."
@@ -77,9 +77,9 @@ if not. Move cursor to the end of macro."
77(defun rust-dbg-wrap-or-unwrap () 77(defun rust-dbg-wrap-or-unwrap ()
78 "Either remove or add the dbg! macro." 78 "Either remove or add the dbg! macro."
79 (interactive) 79 (interactive)
80 80
81 (cond 81 (cond
82 82
83 ;; region 83 ;; region
84 ((region-active-p) 84 ((region-active-p)
85 (rust-insert-dbg-region)) 85 (rust-insert-dbg-region))
@@ -106,7 +106,7 @@ if not. Move cursor to the end of macro."
106 (delete-pair)) 106 (delete-pair))
107 (t (rust-insert-dbg-sexp))))) 107 (t (rust-insert-dbg-sexp)))))
108 ) 108 )
109) 109 )
110 110
111(defun rust-toggle-mutability () 111(defun rust-toggle-mutability ()
112 "Toggles the mutability of the variable defined on the current line" 112 "Toggles the mutability of the variable defined on the current line"
diff --git a/.emacs.d/lisp/yasnippet.el b/.emacs.d/lisp/yasnippet.el
index d130cb9..fa36be7 100644
--- a/.emacs.d/lisp/yasnippet.el
+++ b/.emacs.d/lisp/yasnippet.el
@@ -824,6 +824,10 @@ which decides on the snippet to expand.")
824 ;; Prevent sharing the tail. 824 ;; Prevent sharing the tail.
825 (append lists '(()) ))))))) 825 (append lists '(()) )))))))
826 826
827(defun yas--flush-all-parents (mode)
828 (if (get mode 'yas--all-parents)
829 (put mode 'yas--all-parents nil)))
830
827(defun yas--all-parents (mode) 831(defun yas--all-parents (mode)
828 "Like `derived-mode-all-parents' but obeying `yas--parents'." 832 "Like `derived-mode-all-parents' but obeying `yas--parents'."
829 (or (get mode 'yas--all-parents) ;; FIXME: Use `with-memoization'? 833 (or (get mode 'yas--all-parents) ;; FIXME: Use `with-memoization'?
@@ -854,6 +858,7 @@ which decides on the snippet to expand.")
854 'fundamental-mode) 858 'fundamental-mode)
855 ,(let ((alias (symbol-function mode))) 859 ,(let ((alias (symbol-function mode)))
856 (when (symbolp alias) alias)) 860 (when (symbolp alias) alias))
861 ,@(get mode 'derived-mode-extra-parents)
857 ,@(gethash mode yas--parents))))))))) 862 ,@(gethash mode yas--parents)))))))))
858 (dolist (parent all-parents) 863 (dolist (parent all-parents)
859 (cl-pushnew mode (get parent 'yas--cached-children))) 864 (cl-pushnew mode (get parent 'yas--cached-children)))
@@ -1833,10 +1838,17 @@ the current buffers contents."
1833 (insert "\n\n")) 1838 (insert "\n\n"))
1834 ;; Normal case. 1839 ;; Normal case.
1835 (let ((snippet-table (yas--table-get-create mode)) 1840 (let ((snippet-table (yas--table-get-create mode))
1841 (uuids nil)
1836 (template nil)) 1842 (template nil))
1837 (dolist (snippet snippets) 1843 (dolist (snippet snippets)
1838 (setq template (yas--define-snippets-1 snippet 1844 (setq template (yas--define-snippets-1 snippet
1839 snippet-table))) 1845 snippet-table))
1846 (let ((uuid (yas--template-uuid template)))
1847 (if (member uuid uuids)
1848 ;; It's normal for a snippet to override another one
1849 ;; in `snippet-table`, but not one in `snippets`.
1850 (message "Multiple snippets with same identity: %S" uuid)
1851 (push uuid uuids))))
1840 template))) 1852 template)))
1841 1853
1842 1854
@@ -2002,6 +2014,9 @@ prefix argument."
2002 (with-current-buffer buffer 2014 (with-current-buffer buffer
2003 yas--editing-template)) 2015 yas--editing-template))
2004 (buffer-list)))) 2016 (buffer-list))))
2017
2018 (mapatoms #'yas--flush-all-parents)
2019
2005 ;; Warn if there are buffers visiting snippets, since reloading will break 2020 ;; Warn if there are buffers visiting snippets, since reloading will break
2006 ;; any on-line editing of those buffers. 2021 ;; any on-line editing of those buffers.
2007 ;; 2022 ;;
@@ -3994,7 +4009,7 @@ SNIPPET may be a snippet structure (e.g., as returned by
3994`yas-lookup-snippet'), or just a snippet body (which is a string 4009`yas-lookup-snippet'), or just a snippet body (which is a string
3995for normal snippets, and a list for command snippets)." 4010for normal snippets, and a list for command snippets)."
3996 (cl-assert (and yas-minor-mode 4011 (cl-assert (and yas-minor-mode
3997 (memq 'yas--post-command-handler post-command-hook)) 4012 (memq #'yas--post-command-handler post-command-hook))
3998 nil 4013 nil
3999 "[yas] `yas-expand-snippet' needs properly setup `yas-minor-mode'") 4014 "[yas] `yas-expand-snippet' needs properly setup `yas-minor-mode'")
4000 (run-hooks 'yas-before-expand-snippet-hook) 4015 (run-hooks 'yas-before-expand-snippet-hook)
@@ -4715,20 +4730,22 @@ When multiple expressions are found, only the last one counts."
4715 (point))) 4730 (point)))
4716 (number (and (match-string-no-properties 1) 4731 (number (and (match-string-no-properties 1)
4717 (string-to-number (match-string-no-properties 1)))) 4732 (string-to-number (match-string-no-properties 1))))
4718 (brand-new-field (and real-match-end-0 4733 (field2 (match-string-no-properties 2))
4719 ;; break if on "$(" immediately 4734 (simple-fom (string-match-p "\\`[0-9]+\\'" field2))
4720 ;; after the ":", this will be 4735 (brand-new-field
4721 ;; caught as a mirror with 4736 (and ;; break if on "$(" immediately after the ":", this
4722 ;; transform later. 4737 ;; will be caught as a mirror with transform later.
4723 (not (string-match-p "\\`\\$[ \t\n]*(" 4738 (not (string-match-p "\\`\\$[ \t\n]*(" field2))
4724 (match-string-no-properties 2))) 4739 ;; allow ${0: some exit text}
4725 ;; allow ${0: some exit text} 4740 ;; (not (and number (zerop number)))
4726 ;; (not (and number (zerop number))) 4741 (yas--make-field number
4727 (yas--make-field number 4742 (yas--make-marker (match-beginning 2))
4728 (yas--make-marker (match-beginning 2)) 4743 (yas--make-marker (1- real-match-end-0))
4729 (yas--make-marker (1- real-match-end-0)) 4744 parent-field))))
4730 parent-field)))) 4745 (cond
4731 (when brand-new-field 4746 ((and (not number) simple-fom)
4747 (yas--one-simple-fom snippet field2))
4748 (brand-new-field
4732 (goto-char real-match-end-0) 4749 (goto-char real-match-end-0)
4733 (push (cons (1- real-match-end-0) real-match-end-0) 4750 (push (cons (1- real-match-end-0) real-match-end-0)
4734 yas--dollar-regions) 4751 yas--dollar-regions)
@@ -4737,9 +4754,11 @@ When multiple expressions are found, only the last one counts."
4737 (push brand-new-field (yas--snippet-fields snippet)) 4754 (push brand-new-field (yas--snippet-fields snippet))
4738 (save-excursion 4755 (save-excursion
4739 (save-restriction 4756 (save-restriction
4740 (narrow-to-region (yas--field-start brand-new-field) (yas--field-end brand-new-field)) 4757 (narrow-to-region (yas--field-start brand-new-field)
4758 (yas--field-end brand-new-field))
4741 (goto-char (point-min)) 4759 (goto-char (point-min))
4742 (yas--field-parse-create snippet brand-new-field))))))) 4760 (yas--field-parse-create snippet brand-new-field))))))))
4761
4743 ;; if we entered from a parent field, now search for the 4762 ;; if we entered from a parent field, now search for the
4744 ;; `yas--multi-dollar-lisp-expression-regexp'. This is used for 4763 ;; `yas--multi-dollar-lisp-expression-regexp'. This is used for
4745 ;; primary field transformations 4764 ;; primary field transformations
@@ -4796,31 +4815,35 @@ When multiple expressions are found, only the last one counts."
4796(defun yas--simple-fom-create (snippet) 4815(defun yas--simple-fom-create (snippet)
4797 "Parse the simple \"$n\" fields/mirrors/exitmarkers in SNIPPET." 4816 "Parse the simple \"$n\" fields/mirrors/exitmarkers in SNIPPET."
4798 (while (re-search-forward yas--simple-mirror-regexp nil t) 4817 (while (re-search-forward yas--simple-mirror-regexp nil t)
4799 (let ((number (string-to-number (match-string-no-properties 1)))) 4818 (yas--one-simple-fom snippet (match-string-no-properties 1))))
4800 (cond ((zerop number) 4819
4801 (setf (yas--snippet-exit snippet) 4820(defun yas--one-simple-fom (snippet numstring)
4802 (yas--make-exit (yas--make-marker (match-end 0)))) 4821 (let ((number (string-to-number numstring)))
4803 (push (cons (match-beginning 0) (yas--exit-marker (yas--snippet-exit snippet))) 4822 (cond ((zerop number)
4804 yas--dollar-regions)) 4823 (setf (yas--snippet-exit snippet)
4805 (t 4824 (yas--make-exit (yas--make-marker (match-end 0))))
4806 (let ((field (yas--snippet-find-field snippet number)) 4825 (push (cons (match-beginning 0)
4807 (fom)) 4826 (yas--exit-marker (yas--snippet-exit snippet)))
4808 (if field 4827 yas--dollar-regions))
4809 (push 4828 (t
4810 (setq fom (yas--make-mirror 4829 (let ((field (yas--snippet-find-field snippet number))
4811 (yas--make-marker (match-beginning 0)) 4830 (fom))
4812 (yas--make-marker (match-beginning 0)) 4831 (if field
4813 nil))
4814 (yas--field-mirrors field))
4815 (push 4832 (push
4816 (setq fom (yas--make-field number 4833 (setq fom (yas--make-mirror
4817 (yas--make-marker (match-beginning 0)) 4834 (yas--make-marker (match-beginning 0))
4818 (yas--make-marker (match-beginning 0)) 4835 (yas--make-marker (match-beginning 0))
4819 nil)) 4836 nil))
4820 (yas--snippet-fields snippet))) 4837 (yas--field-mirrors field))
4821 (yas--calculate-simple-fom-parentage snippet fom)) 4838 (push
4822 (push (cons (match-beginning 0) (match-end 0)) 4839 (setq fom (yas--make-field number
4823 yas--dollar-regions)))))) 4840 (yas--make-marker (match-beginning 0))
4841 (yas--make-marker (match-beginning 0))
4842 nil))
4843 (yas--snippet-fields snippet)))
4844 (yas--calculate-simple-fom-parentage snippet fom))
4845 (push (cons (match-beginning 0) (match-end 0))
4846 yas--dollar-regions)))))
4824 4847
4825(defun yas--delete-regions (regions) 4848(defun yas--delete-regions (regions)
4826 "Sort disjuct REGIONS by start point, then delete from the back." 4849 "Sort disjuct REGIONS by start point, then delete from the back."