diff options
| author | Simeon Simeonov | 2024-03-14 22:27:34 +0100 |
|---|---|---|
| committer | Simeon Simeonov | 2024-03-14 22:27:34 +0100 |
| commit | 236114bdeab5edab09969e5ad398c32c2738dead (patch) | |
| tree | 42b337b433de9ada45f2aa441833c5cde42c3a1e | |
| parent | 9fffbc025f80ef22a77ccc62f53baa990568b214 (diff) | |
Upgrade markdown-mode, rust and yasnippet
| -rw-r--r-- | .emacs.d/lisp/markdown-mode.el | 6 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-cargo.el | 3 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-common.el | 20 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-compile.el | 9 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-mode-tests.el | 202 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-mode-treesitter.el | 25 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-mode.el | 1575 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-playpen.el | 42 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-prog-mode.el | 1509 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-rustfmt.el | 94 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-utils.el | 38 | ||||
| -rw-r--r-- | .emacs.d/lisp/yasnippet.el | 107 |
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. |
| 35 | See `compilation-error-regexp-alist' for help on their format.") | 35 | See `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. | ||
| 41 | See `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 | " |
| 2758 | fn foo(x:i32) { | 2805 | fn 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 | " |
| 2770 | fn foo(x:i32) -> bool { | 2817 | fn 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 | |||
| 3482 | let b = 1;" | ||
| 3483 | 12 | ||
| 3484 | #'rust-dbg-wrap-or-unwrap | ||
| 3485 | "let a = 1; | ||
| 3486 | dbg!() | ||
| 3487 | let 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 | ||
| 3494 | let b = 1;" | ||
| 3495 | 12 | ||
| 3496 | #'rust-dbg-wrap-or-unwrap | ||
| 3497 | "let a = 1; | ||
| 3498 | dbg!()// comment | ||
| 3499 | let b = 1;" | ||
| 3500 | 17) | ||
| 3501 | ;; between statements and comments | ||
| 3502 | (rust-test-manip-code | ||
| 3503 | "let a = 1;// comment | ||
| 3504 | let b = 1;" | ||
| 3505 | 11 | ||
| 3506 | #'rust-dbg-wrap-or-unwrap | ||
| 3507 | "let a = 1;dbg!()// comment | ||
| 3508 | let 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 | 42 | instead 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. | ||
| 71 | When 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 | ||
| 84 | to 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 | |||
| 188 | Create a hierarchical index of the item definitions in a Rust file. | ||
| 189 | |||
| 190 | Imenu will show all the enums, structs, etc. in their own subheading. | ||
| 191 | Use 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. | ||
| 222 | See `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. | ||
| 382 | This 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. | ||
| 395 | Examples include to match names like \"foo::\" or \"Foo::\". | ||
| 396 | Does 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. | ||
| 509 | Like `looking-back' but for fixed strings rather than regexps (so | ||
| 510 | that 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. | ||
| 517 | SYMBOLS is a list of strings that represent the respective | ||
| 518 | symbols." | ||
| 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 | |||
| 536 | This is stricter than rust syntax which allows a space between | ||
| 537 | the ident and the ! symbol. If this space is allowed, then we | ||
| 538 | would also need a keyword check to avoid `if !(condition)` being | ||
| 539 | seen as a macro." | ||
| 540 | (if (> (- (point) (point-min)) 1) | ||
| 541 | (save-excursion | ||
| 542 | (backward-char) | ||
| 543 | (and (= ?! (char-after)) | ||
| 544 | (rust-looking-back-ident))))) | ||
| 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 | |||
| 568 | This variable can be `let' bound directly or indirectly around | ||
| 569 | `rust-macro-scope' as an optimization but should not be otherwise | ||
| 570 | set.") | ||
| 571 | |||
| 572 | (defun rust-macro-scope (start end) | ||
| 573 | "Return the scope of macros in the buffer. | ||
| 574 | |||
| 575 | The return value is a list of (START END) positions in the | ||
| 576 | buffer. | ||
| 577 | |||
| 578 | If set START and END are optimizations which limit the return | ||
| 579 | value to scopes which are approximately with this range." | ||
| 580 | (save-excursion | ||
| 581 | ;; need to special case macro_rules which has unique syntax | ||
| 582 | (let ((scope nil) | ||
| 583 | (start (or start (point-min))) | ||
| 584 | (end (or end (point-max)))) | ||
| 585 | (goto-char start) | ||
| 586 | ;; if there is a start move back to the previous top level, | ||
| 587 | ;; as any macros before that must have closed by this time. | ||
| 588 | (let ((top (syntax-ppss-toplevel-pos (syntax-ppss)))) | ||
| 589 | (when top | ||
| 590 | (goto-char top))) | ||
| 591 | (while | ||
| 592 | (and | ||
| 593 | ;; The movement below may have moved us passed end, in | ||
| 594 | ;; which case search-forward will error | ||
| 595 | (< (point) end) | ||
| 596 | (search-forward "!" end t)) | ||
| 597 | (let ((pt (point))) | ||
| 598 | (cond | ||
| 599 | ;; in a string or comment is boring, move straight on | ||
| 600 | ((rust-in-str-or-cmnt)) | ||
| 601 | ;; in a normal macro, | ||
| 602 | ((and (skip-chars-forward " \t\n\r") | ||
| 603 | (memq (char-after) | ||
| 604 | '(?\[ ?\( ?\{)) | ||
| 605 | ;; Check that we have a macro declaration after. | ||
| 606 | (rust-looking-back-macro)) | ||
| 607 | (let ((start (point))) | ||
| 608 | (ignore-errors (forward-list)) | ||
| 609 | (setq scope (cons (list start (point)) scope)))) | ||
| 610 | ;; macro_rules, why, why, why did you not use macro syntax?? | ||
| 611 | ((save-excursion | ||
| 612 | ;; yuck -- last test moves point, even if it fails | ||
| 613 | (goto-char (- pt 1)) | ||
| 614 | (skip-chars-backward " \t\n\r") | ||
| 615 | (rust-looking-back-str "macro_rules")) | ||
| 616 | (save-excursion | ||
| 617 | (when (re-search-forward "[[({]" nil t) | ||
| 618 | (backward-char) | ||
| 619 | (let ((start (point))) | ||
| 620 | (ignore-errors (forward-list)) | ||
| 621 | (setq scope (cons (list start (point)) scope))))))))) | ||
| 622 | ;; Return 'empty rather than nil, to indicate a buffer with no | ||
| 623 | ;; macros at all. | ||
| 624 | (or scope 'empty)))) | ||
| 625 | |||
| 626 | (defun rust-in-macro (&optional start end) | ||
| 627 | "Return non-nil when point is within the scope of a macro. | ||
| 628 | |||
| 629 | If START and END are set, minimize the buffer analysis to | ||
| 630 | approximately this location as an optimization. | ||
| 631 | |||
| 632 | Alternatively, if `rust-macro-scopes' is a list use the scope | ||
| 633 | information in this variable. This last is an optimization and | ||
| 634 | the caller is responsible for ensuring that the data in | ||
| 635 | `rust-macro-scopes' is up to date." | ||
| 636 | (when (> (rust-paren-level) 0) | ||
| 637 | (let ((scopes | ||
| 638 | (or | ||
| 639 | rust-macro-scopes | ||
| 640 | (rust-macro-scope start end)))) | ||
| 641 | ;; `rust-macro-scope' can return the symbol `empty' if the | ||
| 642 | ;; buffer has no macros at all. | ||
| 643 | (when (listp scopes) | ||
| 644 | (seq-some | ||
| 645 | (lambda (sc) | ||
| 646 | (and (>= (point) (car sc)) | ||
| 647 | (< (point) (cadr sc)))) | ||
| 648 | scopes))))) | ||
| 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. | ||
| 657 | Return T iff a where-clause was found. Does not rewind past | ||
| 658 | LIMIT when passed, otherwise only stops at the beginning of the | ||
| 659 | buffer." | ||
| 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. | ||
| 976 | Set point to the end of the occurrence found, and return match beginning | ||
| 977 | and 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. | ||
| 997 | Returns 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. | ||
| 1043 | I.e. if we are before an ident that is part of a declaration that | ||
| 1044 | can have a where clause, rewind back to just before the name of | ||
| 1045 | the subject of that where clause and return the new point. | ||
| 1046 | Otherwise 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 | ||
| 1076 | expression (as opposed to starting a type) by looking at what | ||
| 1077 | comes before. Takes a symbol that roughly indicates what is | ||
| 1078 | after the point. | ||
| 1079 | |||
| 1080 | This function is used as part of `rust-is-lt-char-operator' as | ||
| 1081 | part of angle bracket matching, and is not intended to be used | ||
| 1082 | outside 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. | ||
| 1265 | Otherwise, 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. | ||
| 1313 | This 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. | ||
| 1323 | This 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 | |||
| 1329 | This returns t if the `<' or `>' is an ordinary operator (like | ||
| 1330 | less-than) or part of one (like `->'); and nil if the character | ||
| 1331 | should 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 | |||
| 1393 | This will apply the appropriate string syntax to the character | ||
| 1394 | from the STR-START up to the end of the raw string, or to END, | ||
| 1395 | whichever 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'. | ||
| 1537 | This 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'. | ||
| 1552 | This 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 | |||
| 1572 | With ARG, move backward multiple defuns. Negative ARG means | ||
| 1573 | move forward. | ||
| 1574 | |||
| 1575 | This is written mainly to be used as `beginning-of-defun-function' for Rust. | ||
| 1576 | Don't move to the beginning of the line. `beginning-of-defun', | ||
| 1577 | which 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 | |||
| 1599 | With argument, do it that many times. | ||
| 1600 | Negative argument -N means move back to Nth preceding end of defun. | ||
| 1601 | |||
| 1602 | Assume that this is called after `beginning-of-defun'. So point is | ||
| 1603 | at the beginning of the defun body. | ||
| 1604 | |||
| 1605 | This 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. | ||
| 33 | When 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 | ||
| 46 | to 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 | |||
| 139 | Create a hierarchical index of the item definitions in a Rust file. | ||
| 140 | |||
| 141 | Imenu will show all the enums, structs, etc. in their own subheading. | ||
| 142 | Use 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. | ||
| 148 | See `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. | ||
| 232 | This 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. | ||
| 245 | Examples include to match names like \"foo::\" or \"Foo::\". | ||
| 246 | Does 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. | ||
| 359 | Like `looking-back' but for fixed strings rather than regexps (so | ||
| 360 | that 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. | ||
| 367 | SYMBOLS is a list of strings that represent the respective | ||
| 368 | symbols." | ||
| 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. | ||
| 380 | If 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 \"!\". | ||
| 389 | If 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. | ||
| 431 | If 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. | ||
| 456 | Return T iff a where-clause was found. Does not rewind past | ||
| 457 | LIMIT when passed, otherwise only stops at the beginning of the | ||
| 458 | buffer." | ||
| 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. | ||
| 775 | Set point to the end of the occurrence found, and return match beginning | ||
| 776 | and 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. | ||
| 796 | Returns 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. | ||
| 840 | I.e. if we are before an ident that is part of a declaration that | ||
| 841 | can have a where clause, rewind back to just before the name of | ||
| 842 | the subject of that where clause and return the new point. | ||
| 843 | Otherwise 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 | ||
| 873 | expression (as opposed to starting a type) by looking at what | ||
| 874 | comes before. Takes a symbol that roughly indicates what is | ||
| 875 | after the point. | ||
| 876 | |||
| 877 | This function is used as part of `rust-is-lt-char-operator' as | ||
| 878 | part of angle bracket matching, and is not intended to be used | ||
| 879 | outside 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. | ||
| 1062 | Otherwise, 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. | ||
| 1110 | This 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. | ||
| 1120 | This 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 | |||
| 1126 | This returns t if the `<' or `>' is an ordinary operator (like | ||
| 1127 | less-than) or part of one (like `->'); and nil if the character | ||
| 1128 | should 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'. | ||
| 1281 | This 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'. | ||
| 1296 | This 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 | |||
| 1316 | With ARG, move backward multiple defuns. Negative ARG means | ||
| 1317 | move forward. | ||
| 1318 | |||
| 1319 | This is written mainly to be used as `beginning-of-defun-function' for Rust. | ||
| 1320 | Don't move to the beginning of the line. `beginning-of-defun', | ||
| 1321 | which 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 | |||
| 1343 | With argument, do it that many times. | ||
| 1344 | Negative argument -N means move back to Nth preceding end of defun. | ||
| 1345 | |||
| 1346 | Assume that this is called after `beginning-of-defun'. So point is | ||
| 1347 | at the beginning of the defun body. | ||
| 1348 | |||
| 1349 | This 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 | |||
| 1401 | This will apply the appropriate string syntax to the character | ||
| 1402 | from the STR-START up to the end of the raw string, or to END, | ||
| 1403 | whichever 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." | |||
| 41 | if not. Move cursor to the end of macro." | 41 | if 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 |
| 3995 | for normal snippets, and a list for command snippets)." | 4010 | for 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." |
