diff options
| author | Simeon Simeonov | 2022-10-12 23:21:07 +0200 |
|---|---|---|
| committer | Simeon Simeonov | 2022-10-12 23:21:07 +0200 |
| commit | 3ba23d3758e06d95b14ed4e89607e258d15ddbf7 (patch) | |
| tree | f41ec9ee69791270c8e75e87d7893ad90dd3189b | |
| parent | 4efa8ef4d3617973b2883605461ffa514675e5b2 (diff) | |
Add Rust mode and update lua-mode, markdown-mode and yaml-mode
| -rw-r--r-- | .emacs | 6 | ||||
| -rw-r--r-- | .emacs.d/lisp/lua-mode.el | 991 | ||||
| -rw-r--r-- | .emacs.d/lisp/markdown-mode.el | 149 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-cargo.el | 124 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-compile.el | 83 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-mode-tests.el | 3637 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-mode.el | 1632 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-playpen.el | 59 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-rustfmt.el | 372 | ||||
| -rw-r--r-- | .emacs.d/lisp/rust-utils.el | 91 | ||||
| -rw-r--r-- | .emacs.d/lisp/yaml-mode.el | 6 |
11 files changed, 6716 insertions, 434 deletions
| @@ -108,12 +108,14 @@ | |||
| 108 | ;;(autoload 'php-mode "php-mode" "Mode for editing PHP source files") | 108 | ;;(autoload 'php-mode "php-mode" "Mode for editing PHP source files") |
| 109 | ;;(add-to-list 'auto-mode-alist '("\\.\\(inc\\|php[s34]?\\)" . php-mode)) | 109 | ;;(add-to-list 'auto-mode-alist '("\\.\\(inc\\|php[s34]?\\)" . php-mode)) |
| 110 | 110 | ||
| 111 | |||
| 112 | ;; Lua | 111 | ;; Lua |
| 113 | (autoload 'lua-mode "lua-mode" "Lua editing mode." t) | 112 | (autoload 'lua-mode "lua-mode" "Lua editing mode." t) |
| 114 | (add-to-list 'auto-mode-alist '("\\.lua$" . lua-mode)) | 113 | (add-to-list 'auto-mode-alist '("\\.lua$" . lua-mode)) |
| 115 | ;;(add-to-list 'interpreter-mode-alist '("lua" . lua-mode)) | 114 | ;;(add-to-list 'interpreter-mode-alist '("lua" . lua-mode)) |
| 116 | 115 | ||
| 116 | ;; Rust | ||
| 117 | ;; (autoload 'rust-mode "rust-mode" nil t) | ||
| 118 | ;; (add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-mode)) | ||
| 117 | 119 | ||
| 118 | ;; Markdown | 120 | ;; Markdown |
| 119 | (autoload 'markdown-mode "markdown-mode" | 121 | (autoload 'markdown-mode "markdown-mode" |
| @@ -125,12 +127,10 @@ | |||
| 125 | "Major mode for editing GitHub Flavored Markdown files" t) | 127 | "Major mode for editing GitHub Flavored Markdown files" t) |
| 126 | (add-to-list 'auto-mode-alist '("README\\.md\\'" . gfm-mode)) | 128 | (add-to-list 'auto-mode-alist '("README\\.md\\'" . gfm-mode)) |
| 127 | 129 | ||
| 128 | |||
| 129 | ;; YAML | 130 | ;; YAML |
| 130 | (require 'yaml-mode) | 131 | (require 'yaml-mode) |
| 131 | (add-to-list 'auto-mode-alist '("\\.yml$" . yaml-mode)) | 132 | (add-to-list 'auto-mode-alist '("\\.yml$" . yaml-mode)) |
| 132 | 133 | ||
| 133 | |||
| 134 | ;; yasnippet | 134 | ;; yasnippet |
| 135 | (require 'yasnippet) | 135 | (require 'yasnippet) |
| 136 | (yas-global-mode 1) | 136 | (yas-global-mode 1) |
diff --git a/.emacs.d/lisp/lua-mode.el b/.emacs.d/lisp/lua-mode.el index 640e490..2d6a2d5 100644 --- a/.emacs.d/lisp/lua-mode.el +++ b/.emacs.d/lisp/lua-mode.el | |||
| @@ -11,16 +11,16 @@ | |||
| 11 | ;; Paul Du Bois <pld-lua@gelatinous.com> and | 11 | ;; Paul Du Bois <pld-lua@gelatinous.com> and |
| 12 | ;; Aaron Smith <aaron-lua@gelatinous.com>. | 12 | ;; Aaron Smith <aaron-lua@gelatinous.com>. |
| 13 | ;; | 13 | ;; |
| 14 | ;; URL: http://immerrr.github.com/lua-mode | 14 | ;; URL: https://immerrr.github.io/lua-mode |
| 15 | ;; Version: 20151025 | 15 | ;; Version: 20210802 |
| 16 | ;; Package-Requires: ((emacs "24.3")) | 16 | ;; Package-Requires: ((emacs "24.3")) |
| 17 | ;; | 17 | ;; |
| 18 | ;; This file is NOT part of Emacs. | 18 | ;; This file is NOT part of Emacs. |
| 19 | ;; | 19 | ;; |
| 20 | ;; This program is free software; you can redistribute it and/or | 20 | ;; This program is free software; you can redistribute it and/or modify |
| 21 | ;; modify it under the terms of the GNU General Public License | 21 | ;; it under the terms of the GNU General Public License as published by |
| 22 | ;; as published by the Free Software Foundation; either version 2 | 22 | ;; the Free Software Foundation, either version 3 of the License, or |
| 23 | ;; of the License, or (at your option) any later version. | 23 | ;; (at your option) any later version. |
| 24 | ;; | 24 | ;; |
| 25 | ;; This program is distributed in the hope that it will be useful, | 25 | ;; This program is distributed in the hope that it will be useful, |
| 26 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | 26 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| @@ -28,15 +28,13 @@ | |||
| 28 | ;; GNU General Public License for more details. | 28 | ;; GNU General Public License for more details. |
| 29 | ;; | 29 | ;; |
| 30 | ;; You should have received a copy of the GNU General Public License | 30 | ;; You should have received a copy of the GNU General Public License |
| 31 | ;; along with this program; if not, write to the Free Software | 31 | ;; along with this program. If not, see <https://www.gnu.org/licenses/>. |
| 32 | ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
| 33 | ;; MA 02110-1301, USA. | ||
| 34 | 32 | ||
| 35 | ;; Keywords: languages, processes, tools | 33 | ;; Keywords: languages, processes, tools |
| 36 | 34 | ||
| 37 | ;; This field is expanded to commit SHA, date & associated heads/tags during | 35 | ;; This field is expanded to commit SHA and commit date during the |
| 38 | ;; archive creation. | 36 | ;; archive creation. |
| 39 | ;; Revision: $Format:%h (%cD %d)$ | 37 | ;; Revision: $Format:%h (%cD)$ |
| 40 | ;; | 38 | ;; |
| 41 | 39 | ||
| 42 | ;;; Commentary: | 40 | ;;; Commentary: |
| @@ -53,6 +51,12 @@ | |||
| 53 | ;; - Var `lua-indent-string-contents': | 51 | ;; - Var `lua-indent-string-contents': |
| 54 | ;; set to `t` if you like to have contents of multiline strings to be | 52 | ;; set to `t` if you like to have contents of multiline strings to be |
| 55 | ;; indented like comments | 53 | ;; indented like comments |
| 54 | ;; - Var `lua-indent-nested-block-content-align': | ||
| 55 | ;; set to `nil' to stop aligning the content of nested blocks with the | ||
| 56 | ;; open parenthesis | ||
| 57 | ;; - Var `lua-indent-close-paren-align': | ||
| 58 | ;; set to `t' to align close parenthesis with the open parenthesis, | ||
| 59 | ;; rather than with the beginning of the line | ||
| 56 | ;; - Var `lua-mode-hook': | 60 | ;; - Var `lua-mode-hook': |
| 57 | ;; list of functions to execute when lua-mode is initialized | 61 | ;; list of functions to execute when lua-mode is initialized |
| 58 | ;; - Var `lua-documentation-url': | 62 | ;; - Var `lua-documentation-url': |
| @@ -115,7 +119,7 @@ | |||
| 115 | 119 | ||
| 116 | (lua-name (symbol (seq (+ (any alpha "_")) (* (any alnum "_"))))) | 120 | (lua-name (symbol (seq (+ (any alpha "_")) (* (any alnum "_"))))) |
| 117 | (lua-funcname (seq lua-name (* ws "." ws lua-name) | 121 | (lua-funcname (seq lua-name (* ws "." ws lua-name) |
| 118 | (opt ws ":" ws lua-name))) | 122 | (opt ws ":" ws lua-name))) |
| 119 | (lua-funcheader | 123 | (lua-funcheader |
| 120 | ;; Outer (seq ...) is here to shy-group the definition | 124 | ;; Outer (seq ...) is here to shy-group the definition |
| 121 | (seq (or (seq (symbol "function") ws (group-n 1 lua-funcname)) | 125 | (seq (or (seq (symbol "function") ws (group-n 1 lua-funcname)) |
| @@ -123,15 +127,26 @@ | |||
| 123 | (symbol "function"))))) | 127 | (symbol "function"))))) |
| 124 | (lua-number | 128 | (lua-number |
| 125 | (seq (or (seq (+ digit) (opt ".") (* digit)) | 129 | (seq (or (seq (+ digit) (opt ".") (* digit)) |
| 126 | (seq (* digit) (opt ".") (+ digit))) | 130 | (seq (* digit) (opt ".") (+ digit))) |
| 127 | (opt (regexp "[eE][+-]?[0-9]+")))) | 131 | (opt (regexp "[eE][+-]?[0-9]+")))) |
| 128 | (lua-assignment-op (seq "=" (or buffer-end (not (any "="))))) | 132 | (lua-assignment-op (seq "=" (or buffer-end (not (any "="))))) |
| 129 | (lua-token (or "+" "-" "*" "/" "%" "^" "#" "==" "~=" "<=" ">=" "<" | 133 | (lua-operator (or "+" "-" "*" "/" "%" "^" "#" "==" "~=" "<=" ">=" "<" |
| 130 | ">" "=" ";" ":" "," "." ".." "...")) | 134 | ">" "=" ";" ":" "," "." ".." "...")) |
| 135 | (lua-keyword-operator (symbol "and" "not" "or")) | ||
| 131 | (lua-keyword | 136 | (lua-keyword |
| 132 | (symbol "and" "break" "do" "else" "elseif" "end" "for" "function" | 137 | (symbol "break" "do" "else" "elseif" "end" "for" "function" |
| 133 | "goto" "if" "in" "local" "not" "or" "repeat" "return" | 138 | "goto" "if" "in" "local" "repeat" "return" |
| 134 | "then" "until" "while")))) | 139 | "then" "until" "while")) |
| 140 | (lua-up-to-9-variables | ||
| 141 | (seq (group-n 1 lua-name) ws | ||
| 142 | (? "," ws (group-n 2 lua-name) ws | ||
| 143 | (? "," ws (group-n 3 lua-name) ws | ||
| 144 | (? "," ws (group-n 4 lua-name) ws | ||
| 145 | (? "," ws (group-n 5 lua-name) ws | ||
| 146 | (? "," ws (group-n 6 lua-name) ws | ||
| 147 | (? "," ws (group-n 7 lua-name) ws | ||
| 148 | (? "," ws (group-n 8 lua-name) ws | ||
| 149 | (? "," ws (group-n 9 lua-name) ws)))))))))))) | ||
| 135 | 150 | ||
| 136 | (defmacro lua-rx (&rest regexps) | 151 | (defmacro lua-rx (&rest regexps) |
| 137 | (eval `(rx-let ,lua--rx-bindings | 152 | (eval `(rx-let ,lua--rx-bindings |
| @@ -185,7 +200,7 @@ element is itself expanded with `lua-rx-to-string'. " | |||
| 185 | (setq form (if (eq 1 (length form)) | 200 | (setq form (if (eq 1 (length form)) |
| 186 | (car form) | 201 | (car form) |
| 187 | (append '(or) form))) | 202 | (append '(or) form))) |
| 188 | (and (fboundp 'rx-form) ; Silence Emacs 27's byte-compiler. | 203 | (and (fboundp 'rx-form) ; Silence Emacs 27's byte-compiler. |
| 189 | (rx-form `(seq symbol-start ,form symbol-end) rx-parent))) | 204 | (rx-form `(seq symbol-start ,form symbol-end) rx-parent))) |
| 190 | 205 | ||
| 191 | (setq lua-rx-constituents (copy-sequence rx-constituents)) | 206 | (setq lua-rx-constituents (copy-sequence rx-constituents)) |
| @@ -208,14 +223,25 @@ element is itself expanded with `lua-rx-to-string'. " | |||
| 208 | (opt (regexp "[eE][+-]?[0-9]+")))) | 223 | (opt (regexp "[eE][+-]?[0-9]+")))) |
| 209 | (lua-assignment-op | 224 | (lua-assignment-op |
| 210 | :rx (seq "=" (or buffer-end (not (any "="))))) | 225 | :rx (seq "=" (or buffer-end (not (any "="))))) |
| 211 | (lua-token | 226 | (lua-operator |
| 212 | :rx (or "+" "-" "*" "/" "%" "^" "#" "==" "~=" "<=" ">=" "<" | 227 | :rx (or "+" "-" "*" "/" "%" "^" "#" "==" "~=" "<=" ">=" "<" |
| 213 | ">" "=" ";" ":" "," "." ".." "...")) | 228 | ">" "=" ";" ":" "," "." ".." "...")) |
| 229 | (lua-keyword-operator | ||
| 230 | :rx (symbol "and" "not" "or")) | ||
| 214 | (lua-keyword | 231 | (lua-keyword |
| 215 | :rx (symbol "and" "break" "do" "else" "elseif" "end" "for" "function" | 232 | :rx (symbol "break" "do" "else" "elseif" "end" "for" "function" |
| 216 | "goto" "if" "in" "local" "not" "or" "repeat" "return" | 233 | "goto" "if" "in" "local" "repeat" "return" |
| 217 | "then" "until" "while"))) | 234 | "then" "until" "while")) |
| 218 | )))) | 235 | (lua-up-to-9-variables |
| 236 | :rx (seq (group-n 1 lua-name) ws | ||
| 237 | (? "," ws (group-n 2 lua-name) ws | ||
| 238 | (? "," ws (group-n 3 lua-name) ws | ||
| 239 | (? "," ws (group-n 4 lua-name) ws | ||
| 240 | (? "," ws (group-n 5 lua-name) ws | ||
| 241 | (? "," ws (group-n 6 lua-name) ws | ||
| 242 | (? "," ws (group-n 7 lua-name) ws | ||
| 243 | (? "," ws (group-n 8 lua-name) ws | ||
| 244 | (? "," ws (group-n 9 lua-name) ws))))))))))))))) | ||
| 219 | 245 | ||
| 220 | 246 | ||
| 221 | ;; Local variables | 247 | ;; Local variables |
| @@ -241,7 +267,11 @@ element is itself expanded with `lua-rx-to-string'. " | |||
| 241 | :group 'lua) | 267 | :group 'lua) |
| 242 | 268 | ||
| 243 | (defcustom lua-default-application "lua" | 269 | (defcustom lua-default-application "lua" |
| 244 | "Default application to run in Lua process." | 270 | "Default application to run in Lua process. |
| 271 | |||
| 272 | Can be a string, where it denotes a command to be executed to | ||
| 273 | start Lua process, or a (HOST . PORT) cons, that can be used to | ||
| 274 | connect to Lua process running remotely." | ||
| 245 | :type '(choice (string) | 275 | :type '(choice (string) |
| 246 | (cons string integer)) | 276 | (cons string integer)) |
| 247 | :group 'lua) | 277 | :group 'lua) |
| @@ -279,7 +309,7 @@ Should be a list of strings." | |||
| 279 | "The active Lua process") | 309 | "The active Lua process") |
| 280 | 310 | ||
| 281 | (defvar lua-process-buffer nil | 311 | (defvar lua-process-buffer nil |
| 282 | "Buffer used for communication with the Lua process") | 312 | "Buffer used for communication with the Lua process.") |
| 283 | 313 | ||
| 284 | (defun lua--customize-set-prefix-key (prefix-key-sym prefix-key-val) | 314 | (defun lua--customize-set-prefix-key (prefix-key-sym prefix-key-val) |
| 285 | (cl-assert (eq prefix-key-sym 'lua-prefix-key)) | 315 | (cl-assert (eq prefix-key-sym 'lua-prefix-key)) |
| @@ -327,13 +357,13 @@ If the latter is nil, the keymap translates into `lua-mode-map' verbatim.") | |||
| 327 | #'lua-electric-match)) | 357 | #'lua-electric-match)) |
| 328 | lua--electric-indent-chars)) | 358 | lua--electric-indent-chars)) |
| 329 | (define-key result-map [menu-bar lua-mode] (cons "Lua" lua-mode-menu)) | 359 | (define-key result-map [menu-bar lua-mode] (cons "Lua" lua-mode-menu)) |
| 360 | (define-key result-map [remap backward-up-list] 'lua-backward-up-list) | ||
| 330 | 361 | ||
| 331 | ;; FIXME: see if the declared logic actually works | ||
| 332 | ;; handle prefix-keyed bindings: | 362 | ;; handle prefix-keyed bindings: |
| 333 | ;; * if no prefix, set prefix-map as parent, i.e. | 363 | ;; * if no prefix, set prefix-map as parent, i.e. |
| 334 | ;; if key is not defined look it up in prefix-map | 364 | ;; if key is not defined look it up in prefix-map |
| 335 | ;; * if prefix is set, bind the prefix-map to that key | 365 | ;; * if prefix is set, bind the prefix-map to that key |
| 336 | (if (boundp 'lua-prefix-key) | 366 | (if lua-prefix-key |
| 337 | (define-key result-map (vector lua-prefix-key) lua-prefix-mode-map) | 367 | (define-key result-map (vector lua-prefix-key) lua-prefix-mode-map) |
| 338 | (set-keymap-parent result-map lua-prefix-mode-map)) | 368 | (set-keymap-parent result-map lua-prefix-mode-map)) |
| 339 | result-map) | 369 | result-map) |
| @@ -372,8 +402,8 @@ If the latter is nil, the keymap translates into `lua-mode-map' verbatim.") | |||
| 372 | ;; NOTE: this doesn't traverse `compilation-search-path' when | 402 | ;; NOTE: this doesn't traverse `compilation-search-path' when |
| 373 | ;; looking for filename. | 403 | ;; looking for filename. |
| 374 | (not (file-exists-p (expand-file-name | 404 | (not (file-exists-p (expand-file-name |
| 375 | filename | 405 | filename |
| 376 | (when directory (expand-file-name directory)))))) | 406 | (when directory (expand-file-name directory)))))) |
| 377 | (setq ad-return-value (current-buffer)) | 407 | (setq ad-return-value (current-buffer)) |
| 378 | ad-do-it)) | 408 | ad-do-it)) |
| 379 | 409 | ||
| @@ -397,7 +427,24 @@ Usually, stdin:XX line number points to nowhere." | |||
| 397 | "If non-nil, contents of multiline string will be indented. | 427 | "If non-nil, contents of multiline string will be indented. |
| 398 | Otherwise leading amount of whitespace on each line is preserved." | 428 | Otherwise leading amount of whitespace on each line is preserved." |
| 399 | :group 'lua | 429 | :group 'lua |
| 400 | :type 'boolean) | 430 | :type 'boolean |
| 431 | :safe #'booleanp) | ||
| 432 | |||
| 433 | (defcustom lua-indent-nested-block-content-align t | ||
| 434 | "If non-nil, the contents of nested blocks are indented to | ||
| 435 | align with the column of the opening parenthesis, rather than | ||
| 436 | just forward by `lua-indent-level'." | ||
| 437 | :group 'lua | ||
| 438 | :type 'boolean | ||
| 439 | :safe #'booleanp) | ||
| 440 | |||
| 441 | (defcustom lua-indent-close-paren-align t | ||
| 442 | "If non-nil, close parenthesis are aligned with their open | ||
| 443 | parenthesis. If nil, close parenthesis are aligned to the | ||
| 444 | beginning of the line." | ||
| 445 | :group 'lua | ||
| 446 | :type 'boolean | ||
| 447 | :safe #'booleanp) | ||
| 401 | 448 | ||
| 402 | (defcustom lua-jump-on-traceback t | 449 | (defcustom lua-jump-on-traceback t |
| 403 | "*Jump to innermost traceback location in *lua* buffer. When this | 450 | "*Jump to innermost traceback location in *lua* buffer. When this |
| @@ -476,118 +523,38 @@ traceback location." | |||
| 476 | "offset"))))) | 523 | "offset"))))) |
| 477 | 524 | ||
| 478 | (cl-labels | 525 | (cl-labels |
| 479 | ((module-name-re (x) | 526 | ((module-name-re (x) |
| 480 | (concat "\\(?1:\\_<" | 527 | (concat "\\(?1:\\_<" |
| 481 | (if (listp x) (car x) x) | 528 | (if (listp x) (car x) x) |
| 482 | "\\_>\\)")) | 529 | "\\_>\\)")) |
| 483 | (module-members-re (x) (if (listp x) | 530 | (module-members-re (x) (if (listp x) |
| 484 | (concat "\\(?:[ \t]*\\.[ \t]*" | 531 | (concat "\\(?:[ \t]*\\.[ \t]*" |
| 485 | "\\_<\\(?2:" | 532 | "\\_<\\(?2:" |
| 486 | (regexp-opt (cdr x)) | 533 | (regexp-opt (cdr x)) |
| 487 | "\\)\\_>\\)?") | 534 | "\\)\\_>\\)?") |
| 488 | ""))) | 535 | ""))) |
| 489 | 536 | ||
| 490 | (concat | 537 | (concat |
| 491 | ;; common prefix: | 538 | ;; common prefix: |
| 492 | ;; - beginning-of-line | 539 | ;; - beginning-of-line |
| 493 | ;; - or neither of [ '.', ':' ] to exclude "foo.string.rep" | 540 | ;; - or neither of [ '.', ':' ] to exclude "foo.string.rep" |
| 494 | ;; - or concatenation operator ".." | 541 | ;; - or concatenation operator ".." |
| 495 | "\\(?:^\\|[^:. \t]\\|[.][.]\\)" | 542 | "\\(?:^\\|[^:. \t]\\|[.][.]\\)" |
| 496 | ;; optional whitespace | 543 | ;; optional whitespace |
| 497 | "[ \t]*" | 544 | "[ \t]*" |
| 498 | "\\(?:" | 545 | "\\(?:" |
| 499 | ;; any of modules/functions | 546 | ;; any of modules/functions |
| 500 | (mapconcat (lambda (x) (concat (module-name-re x) | 547 | (mapconcat (lambda (x) (concat (module-name-re x) |
| 501 | (module-members-re x))) | 548 | (module-members-re x))) |
| 502 | modules | 549 | modules |
| 503 | "\\|") | 550 | "\\|") |
| 504 | "\\)")))) | 551 | "\\)")))) |
| 505 | 552 | ||
| 506 | "A regexp that matches Lua builtin functions & variables. | 553 | "A regexp that matches Lua builtin functions & variables. |
| 507 | 554 | ||
| 508 | This is a compilation of 5.1, 5.2 and 5.3 builtins taken from the | 555 | This is a compilation of 5.1, 5.2 and 5.3 builtins taken from the |
| 509 | index of respective Lua reference manuals.") | 556 | index of respective Lua reference manuals.") |
| 510 | 557 | ||
| 511 | (eval-and-compile | ||
| 512 | (defun lua-make-delimited-matcher (elt-regexp sep-regexp end-regexp) | ||
| 513 | "Construct matcher function for `font-lock-keywords' to match a sequence. | ||
| 514 | |||
| 515 | It's supposed to match sequences with following EBNF: | ||
| 516 | |||
| 517 | ELT-REGEXP { SEP-REGEXP ELT-REGEXP } END-REGEXP | ||
| 518 | |||
| 519 | The sequence is parsed one token at a time. If non-nil is | ||
| 520 | returned, `match-data' will have one or more of the following | ||
| 521 | groups set according to next matched token: | ||
| 522 | |||
| 523 | 1. matched element token | ||
| 524 | 2. unmatched garbage characters | ||
| 525 | 3. misplaced token (i.e. SEP-REGEXP when ELT-REGEXP is expected) | ||
| 526 | 4. matched separator token | ||
| 527 | 5. matched end token | ||
| 528 | |||
| 529 | Blanks & comments between tokens are silently skipped. | ||
| 530 | Groups 6-9 can be used in any of argument regexps." | ||
| 531 | (let* | ||
| 532 | ((delimited-matcher-re-template | ||
| 533 | "\\=\\(?2:.*?\\)\\(?:\\(?%s:\\(?4:%s\\)\\|\\(?5:%s\\)\\)\\|\\(?%s:\\(?1:%s\\)\\)\\)") | ||
| 534 | ;; There's some magic to this regexp. It works as follows: | ||
| 535 | ;; | ||
| 536 | ;; A. start at (point) | ||
| 537 | ;; B. non-greedy match of garbage-characters (?2:) | ||
| 538 | ;; C. try matching separator (?4:) or end-token (?5:) | ||
| 539 | ;; D. try matching element (?1:) | ||
| 540 | ;; | ||
| 541 | ;; Simple, but there's a trick: pt.C and pt.D are embraced by one more | ||
| 542 | ;; group whose purpose is determined only after the template is | ||
| 543 | ;; formatted (?%s:): | ||
| 544 | ;; | ||
| 545 | ;; - if element is expected, then D's parent group becomes "shy" and C's | ||
| 546 | ;; parent becomes group 3 (aka misplaced token), so if D matches when | ||
| 547 | ;; an element is expected, it'll be marked with warning face. | ||
| 548 | ;; | ||
| 549 | ;; - if separator-or-end-token is expected, then it's the opposite: | ||
| 550 | ;; C's parent becomes shy and D's will be matched as misplaced token. | ||
| 551 | (elt-expected-re (format delimited-matcher-re-template | ||
| 552 | 3 sep-regexp end-regexp "" elt-regexp)) | ||
| 553 | (sep-or-end-expected-re (format delimited-matcher-re-template | ||
| 554 | "" sep-regexp end-regexp 3 elt-regexp))) | ||
| 555 | |||
| 556 | (lambda (end) | ||
| 557 | (let* ((prev-elt-p (match-beginning 1)) | ||
| 558 | (prev-end-p (match-beginning 5)) | ||
| 559 | |||
| 560 | (regexp (if prev-elt-p sep-or-end-expected-re elt-expected-re)) | ||
| 561 | (comment-start (lua-comment-start-pos (syntax-ppss))) | ||
| 562 | (parse-stop end)) | ||
| 563 | |||
| 564 | ;; If token starts inside comment, or end-token was encountered, stop. | ||
| 565 | (when (and (not comment-start) | ||
| 566 | (not prev-end-p)) | ||
| 567 | ;; Skip all comments & whitespace. forward-comment doesn't have boundary | ||
| 568 | ;; argument, so make sure point isn't beyond parse-stop afterwards. | ||
| 569 | (while (and (< (point) end) | ||
| 570 | (forward-comment 1))) | ||
| 571 | (goto-char (min (point) parse-stop)) | ||
| 572 | |||
| 573 | ;; Reuse comment-start variable to store beginning of comment that is | ||
| 574 | ;; placed before line-end-position so as to make sure token search doesn't | ||
| 575 | ;; enter that comment. | ||
| 576 | (setq comment-start | ||
| 577 | (lua-comment-start-pos | ||
| 578 | (save-excursion | ||
| 579 | (parse-partial-sexp (point) parse-stop | ||
| 580 | nil nil nil 'stop-inside-comment))) | ||
| 581 | parse-stop (or comment-start parse-stop)) | ||
| 582 | |||
| 583 | ;; Now, let's match stuff. If regular matcher fails, declare a span of | ||
| 584 | ;; non-blanks 'garbage', and the next iteration will start from where the | ||
| 585 | ;; garbage ends. If couldn't match any garbage, move point to the end | ||
| 586 | ;; and return nil. | ||
| 587 | (or (re-search-forward regexp parse-stop t) | ||
| 588 | (re-search-forward "\\(?1:\\(?2:[^ \t]+\\)\\)" parse-stop 'skip) | ||
| 589 | (prog1 nil (goto-char end))))))))) | ||
| 590 | |||
| 591 | 558 | ||
| 592 | (defvar lua-font-lock-keywords | 559 | (defvar lua-font-lock-keywords |
| 593 | `(;; highlight the hash-bang line "#!/foo/bar/lua" as comment | 560 | `(;; highlight the hash-bang line "#!/foo/bar/lua" as comment |
| @@ -598,59 +565,67 @@ Groups 6-9 can be used in any of argument regexps." | |||
| 598 | . font-lock-constant-face) | 565 | . font-lock-constant-face) |
| 599 | 566 | ||
| 600 | ;; Keywords | 567 | ;; Keywords |
| 601 | (,(lua-rx lua-keyword) | 568 | (,(lua-rx (or lua-keyword lua-keyword-operator)) |
| 602 | . font-lock-keyword-face) | 569 | . font-lock-keyword-face) |
| 603 | 570 | ||
| 604 | ;; Labels used by the "goto" statement | 571 | ;; Labels used by the "goto" statement |
| 605 | ;; Highlights the following syntax: ::label:: | 572 | ;; Highlights the following syntax: ::label:: |
| 606 | (,(lua-rx "::" ws lua-name ws "::") | 573 | (,(lua-rx "::" ws lua-name ws "::") |
| 607 | . font-lock-constant-face) | 574 | . font-lock-constant-face) |
| 608 | 575 | ||
| 609 | ;; Highlights the name of the label in the "goto" statement like | 576 | ;; Highlights the name of the label in the "goto" statement like |
| 610 | ;; "goto label" | 577 | ;; "goto label" |
| 611 | (,(lua-rx (symbol (seq "goto" ws+ (group-n 1 lua-name)))) | 578 | (,(lua-rx (symbol (seq "goto" ws+ (group-n 1 lua-name)))) |
| 612 | (1 font-lock-constant-face)) | 579 | (1 font-lock-constant-face)) |
| 613 | 580 | ||
| 614 | ;; Highlight Lua builtin functions and variables | 581 | ;; Highlight Lua builtin functions and variables |
| 615 | (,lua--builtins | 582 | (,lua--builtins |
| 616 | (1 font-lock-builtin-face) (2 font-lock-builtin-face nil noerror)) | 583 | (1 font-lock-builtin-face) (2 font-lock-builtin-face nil noerror)) |
| 617 | 584 | ||
| 618 | ("^[ \t]*\\_<for\\_>" | 585 | (,(lua-rx (symbol "for") ws+ lua-up-to-9-variables) |
| 619 | (,(lua-make-delimited-matcher (lua-rx lua-name) "," | 586 | (1 font-lock-variable-name-face) |
| 620 | (lua-rx (or (symbol "in") lua-assignment-op))) | 587 | (2 font-lock-variable-name-face nil noerror) |
| 621 | nil nil | 588 | (3 font-lock-variable-name-face nil noerror) |
| 622 | (1 font-lock-variable-name-face nil noerror) | 589 | (4 font-lock-variable-name-face nil noerror) |
| 623 | (2 font-lock-warning-face t noerror) | 590 | (5 font-lock-variable-name-face nil noerror) |
| 624 | (3 font-lock-warning-face t noerror))) | 591 | (6 font-lock-variable-name-face nil noerror) |
| 625 | 592 | (7 font-lock-variable-name-face nil noerror) | |
| 626 | ;; Handle local variable/function names | 593 | (8 font-lock-variable-name-face nil noerror) |
| 627 | ;; local blalba, xyzzy = | 594 | (9 font-lock-variable-name-face nil noerror)) |
| 628 | ;; ^^^^^^ ^^^^^ | ||
| 629 | ;; | ||
| 630 | ;; local function foobar(x,y,z) | ||
| 631 | ;; ^^^^^^ | ||
| 632 | ;; local foobar = function(x,y,z) | ||
| 633 | ;; ^^^^^^ | ||
| 634 | ("^[ \t]*\\_<local\\_>" | ||
| 635 | (0 font-lock-keyword-face) | ||
| 636 | |||
| 637 | ;; (* nonl) at the end is to consume trailing characters or otherwise they | ||
| 638 | ;; delimited matcher would attempt to parse them afterwards and wrongly | ||
| 639 | ;; highlight parentheses as incorrect variable name characters. | ||
| 640 | (,(lua-rx point ws lua-funcheader (* nonl)) | ||
| 641 | nil nil | ||
| 642 | (1 font-lock-function-name-face nil noerror)) | ||
| 643 | 595 | ||
| 644 | (,(lua-make-delimited-matcher (lua-rx lua-name) "," | 596 | (,(lua-rx (symbol "function") (? ws+ lua-funcname) ws "(" ws lua-up-to-9-variables) |
| 645 | (lua-rx lua-assignment-op)) | 597 | (1 font-lock-variable-name-face) |
| 646 | nil nil | 598 | (2 font-lock-variable-name-face nil noerror) |
| 647 | (1 font-lock-variable-name-face nil noerror) | 599 | (3 font-lock-variable-name-face nil noerror) |
| 648 | (2 font-lock-warning-face t noerror) | 600 | (4 font-lock-variable-name-face nil noerror) |
| 649 | (3 font-lock-warning-face t noerror))) | 601 | (5 font-lock-variable-name-face nil noerror) |
| 602 | (6 font-lock-variable-name-face nil noerror) | ||
| 603 | (7 font-lock-variable-name-face nil noerror) | ||
| 604 | (8 font-lock-variable-name-face nil noerror) | ||
| 605 | (9 font-lock-variable-name-face nil noerror)) | ||
| 650 | 606 | ||
| 651 | (,(lua-rx (or bol ";") ws lua-funcheader) | 607 | (,(lua-rx lua-funcheader) |
| 652 | (1 font-lock-function-name-face)) | 608 | (1 font-lock-function-name-face)) |
| 653 | 609 | ||
| 610 | ;; local x, y, z | ||
| 611 | ;; local x = ..... | ||
| 612 | ;; | ||
| 613 | ;; NOTE: this is intentionally below funcheader matcher, so that in | ||
| 614 | ;; | ||
| 615 | ;; local foo = function() ... | ||
| 616 | ;; | ||
| 617 | ;; "foo" is fontified as function-name-face, and variable-name-face is not applied. | ||
| 618 | (,(lua-rx (symbol "local") ws+ lua-up-to-9-variables) | ||
| 619 | (1 font-lock-variable-name-face) | ||
| 620 | (2 font-lock-variable-name-face nil noerror) | ||
| 621 | (3 font-lock-variable-name-face nil noerror) | ||
| 622 | (4 font-lock-variable-name-face nil noerror) | ||
| 623 | (5 font-lock-variable-name-face nil noerror) | ||
| 624 | (6 font-lock-variable-name-face nil noerror) | ||
| 625 | (7 font-lock-variable-name-face nil noerror) | ||
| 626 | (8 font-lock-variable-name-face nil noerror) | ||
| 627 | (9 font-lock-variable-name-face nil noerror)) | ||
| 628 | |||
| 654 | (,(lua-rx (or (group-n 1 | 629 | (,(lua-rx (or (group-n 1 |
| 655 | "@" (symbol "author" "copyright" "field" "release" | 630 | "@" (symbol "author" "copyright" "field" "release" |
| 656 | "return" "see" "usage" "description")) | 631 | "return" "see" "usage" "description")) |
| @@ -662,13 +637,14 @@ Groups 6-9 can be used in any of argument regexps." | |||
| 662 | "Default expressions to highlight in Lua mode.") | 637 | "Default expressions to highlight in Lua mode.") |
| 663 | 638 | ||
| 664 | (defvar lua-imenu-generic-expression | 639 | (defvar lua-imenu-generic-expression |
| 665 | `((nil ,(lua-rx (or bol ";") ws (opt (seq (symbol "local") ws)) lua-funcheader) 1)) | 640 | `(("Requires" ,(lua-rx (or bol ";") ws (opt (seq (symbol "local") ws)) (group-n 1 lua-name) ws "=" ws (symbol "require")) 1) |
| 641 | (nil ,(lua-rx (or bol ";") ws (opt (seq (symbol "local") ws)) lua-funcheader) 1)) | ||
| 666 | "Imenu generic expression for lua-mode. See `imenu-generic-expression'.") | 642 | "Imenu generic expression for lua-mode. See `imenu-generic-expression'.") |
| 667 | 643 | ||
| 668 | (defvar lua-sexp-alist '(("then" . "end") | 644 | (defvar lua-sexp-alist '(("then" . "end") |
| 669 | ("function" . "end") | 645 | ("function" . "end") |
| 670 | ("do" . "end") | 646 | ("do" . "end") |
| 671 | ("repeat" . "until"))) | 647 | ("repeat" . "until"))) |
| 672 | 648 | ||
| 673 | (defvar lua-mode-abbrev-table nil | 649 | (defvar lua-mode-abbrev-table nil |
| 674 | "Abbreviation table used in lua-mode buffers.") | 650 | "Abbreviation table used in lua-mode buffers.") |
| @@ -709,11 +685,11 @@ Groups 6-9 can be used in any of argument regexps." | |||
| 709 | :syntax-table lua-mode-syntax-table | 685 | :syntax-table lua-mode-syntax-table |
| 710 | :group 'lua | 686 | :group 'lua |
| 711 | (setq-local font-lock-defaults '(lua-font-lock-keywords ;; keywords | 687 | (setq-local font-lock-defaults '(lua-font-lock-keywords ;; keywords |
| 712 | nil ;; keywords-only | 688 | nil ;; keywords-only |
| 713 | nil ;; case-fold | 689 | nil ;; case-fold |
| 714 | nil ;; syntax-alist | 690 | nil ;; syntax-alist |
| 715 | nil ;; syntax-begin | 691 | nil ;; syntax-begin |
| 716 | )) | 692 | )) |
| 717 | 693 | ||
| 718 | (setq-local syntax-propertize-function | 694 | (setq-local syntax-propertize-function |
| 719 | 'lua--propertize-multiline-bounds) | 695 | 'lua--propertize-multiline-bounds) |
| @@ -733,7 +709,7 @@ Groups 6-9 can be used in any of argument regexps." | |||
| 733 | ;; If electric-indent-chars is not defined, electric indentation is done | 709 | ;; If electric-indent-chars is not defined, electric indentation is done |
| 734 | ;; via `lua-mode-map'. | 710 | ;; via `lua-mode-map'. |
| 735 | (setq-local electric-indent-chars | 711 | (setq-local electric-indent-chars |
| 736 | (append electric-indent-chars lua--electric-indent-chars))) | 712 | (append electric-indent-chars lua--electric-indent-chars))) |
| 737 | 713 | ||
| 738 | 714 | ||
| 739 | ;; setup menu bar entry (XEmacs style) | 715 | ;; setup menu bar entry (XEmacs style) |
| @@ -770,7 +746,7 @@ Groups 6-9 can be used in any of argument regexps." | |||
| 770 | "Insert character and adjust indentation." | 746 | "Insert character and adjust indentation." |
| 771 | (interactive "P") | 747 | (interactive "P") |
| 772 | (let (blink-paren-function) | 748 | (let (blink-paren-function) |
| 773 | (self-insert-command (prefix-numeric-value arg))) | 749 | (self-insert-command (prefix-numeric-value arg))) |
| 774 | (if lua-electric-flag | 750 | (if lua-electric-flag |
| 775 | (lua-indent-line)) | 751 | (lua-indent-line)) |
| 776 | (blink-matching-open)) | 752 | (blink-matching-open)) |
| @@ -826,22 +802,41 @@ This function replaces previous prefix-key binding with a new one." | |||
| 826 | "Returns true if the point is in a string." | 802 | "Returns true if the point is in a string." |
| 827 | (save-excursion (elt (syntax-ppss pos) 3))) | 803 | (save-excursion (elt (syntax-ppss pos) 3))) |
| 828 | 804 | ||
| 829 | (defun lua-comment-start-pos (parsing-state) | 805 | (defun lua--containing-double-hyphen-start-pos () |
| 806 | "Return position of the beginning comment delimiter (--). | ||
| 807 | |||
| 808 | Emacs syntax framework does not consider comment delimiters as | ||
| 809 | part of the comment itself, but for this package it is useful to | ||
| 810 | consider point as inside comment when it is between the two hyphens" | ||
| 811 | (and (eql (char-before) ?-) | ||
| 812 | (eql (char-after) ?-) | ||
| 813 | (1- (point)))) | ||
| 814 | |||
| 815 | (defun lua-comment-start-pos (&optional parsing-state) | ||
| 830 | "Return position of comment containing current point. | 816 | "Return position of comment containing current point. |
| 831 | 817 | ||
| 832 | If point is not inside a comment, return nil." | 818 | If point is not inside a comment, return nil." |
| 833 | (and parsing-state (nth 4 parsing-state) (nth 8 parsing-state))) | 819 | (unless parsing-state (setq parsing-state (syntax-ppss))) |
| 820 | (and | ||
| 821 | ;; Not a string | ||
| 822 | (not (nth 3 parsing-state)) | ||
| 823 | ;; Syntax-based comment | ||
| 824 | (or (and (nth 4 parsing-state) (nth 8 parsing-state)) | ||
| 825 | (lua--containing-double-hyphen-start-pos)))) | ||
| 834 | 826 | ||
| 835 | (defun lua-comment-or-string-p (&optional pos) | 827 | (defun lua-comment-or-string-p (&optional pos) |
| 836 | "Returns true if the point is in a comment or string." | 828 | "Returns true if the point is in a comment or string." |
| 837 | (save-excursion (let ((parse-result (syntax-ppss pos))) | 829 | (save-excursion (let ((parse-result (syntax-ppss pos))) |
| 838 | (or (elt parse-result 3) (elt parse-result 4))))) | 830 | (or (elt parse-result 3) (lua-comment-start-pos parse-result))))) |
| 839 | 831 | ||
| 840 | (defun lua-comment-or-string-start-pos (&optional pos) | 832 | (defun lua-comment-or-string-start-pos (&optional pos) |
| 841 | "Returns start position of string or comment which contains point. | 833 | "Returns start position of string or comment which contains point. |
| 842 | 834 | ||
| 843 | If point is not inside string or comment, return nil." | 835 | If point is not inside string or comment, return nil." |
| 844 | (save-excursion (elt (syntax-ppss pos) 8))) | 836 | (save-excursion |
| 837 | (when pos (goto-char pos)) | ||
| 838 | (or (elt (syntax-ppss pos) 8) | ||
| 839 | (lua--containing-double-hyphen-start-pos)))) | ||
| 845 | 840 | ||
| 846 | ;; They're propertized as follows: | 841 | ;; They're propertized as follows: |
| 847 | ;; 1. generic-comment | 842 | ;; 1. generic-comment |
| @@ -885,23 +880,15 @@ If none can be found before reaching LIMIT, return nil." | |||
| 885 | ;; inside strings or comments ending either at EOL or at valid token. | 880 | ;; inside strings or comments ending either at EOL or at valid token. |
| 886 | (and (setq last-search-matched | 881 | (and (setq last-search-matched |
| 887 | (re-search-forward lua-ml-begin-regexp limit 'noerror)) | 882 | (re-search-forward lua-ml-begin-regexp limit 'noerror)) |
| 888 | 883 | ;; Ensure --[[ is not inside a comment or string. | |
| 889 | ;; Handle triple-hyphen '---[[' situation in which the multiline | ||
| 890 | ;; opener should be skipped. | ||
| 891 | ;; | 884 | ;; |
| 892 | ;; In HYPHEN1-HYPHEN2-BRACKET1-BRACKET2 situation (match-beginning | 885 | ;; This includes "---[[" sequence, in which "--" at the beginning |
| 893 | ;; 0) points to HYPHEN1, but if there's another hyphen before | 886 | ;; creates a single-line comment, and thus "-[[" is no longer a |
| 894 | ;; HYPHEN1, standard syntax table will only detect comment-start | 887 | ;; multi-line opener. |
| 895 | ;; at HYPHEN2. | ||
| 896 | ;; | 888 | ;; |
| 897 | ;; We could check for comment-start at HYPHEN2, but then we'd have | 889 | ;; XXX: need to ensure syntax-ppss beyond (match-beginning 0) is |
| 898 | ;; to flush syntax-ppss cache to remove the result saying that at | 890 | ;; not calculated, or otherwise we'll need to flush the cache. |
| 899 | ;; HYPHEN2 there's no comment or string, because under some | 891 | (lua-comment-or-string-start-pos (match-beginning 0)))) |
| 900 | ;; circumstances that would hide the fact that we put a | ||
| 901 | ;; comment-start property at HYPHEN1. | ||
| 902 | (or (lua-comment-or-string-start-pos (match-beginning 0)) | ||
| 903 | (and (eq ?- (char-after (match-beginning 0))) | ||
| 904 | (eq ?- (char-before (match-beginning 0))))))) | ||
| 905 | 892 | ||
| 906 | last-search-matched)) | 893 | last-search-matched)) |
| 907 | 894 | ||
| @@ -968,23 +955,70 @@ Return the amount the indentation changed by." | |||
| 968 | 0 | 955 | 0 |
| 969 | lua-indent-level)))))) | 956 | lua-indent-level)))))) |
| 970 | 957 | ||
| 971 | (defun lua-find-regexp (direction regexp &optional limit ignore-p) | 958 | |
| 959 | (defun lua--signum (x) | ||
| 960 | "Return 1 if X is positive, -1 if negative, 0 if zero." | ||
| 961 | ;; XXX: backport from cl-extras for Emacs24 | ||
| 962 | (cond ((> x 0) 1) ((< x 0) -1) (t 0))) | ||
| 963 | |||
| 964 | (defun lua--ensure-point-within-limit (limit backward) | ||
| 965 | "Return non-nil if point is within LIMIT going forward. | ||
| 966 | |||
| 967 | With BACKWARD non-nil, return non-nil if point is within LIMIT | ||
| 968 | going backward. | ||
| 969 | |||
| 970 | If point is beyond limit, move it onto limit." | ||
| 971 | (if (= (lua--signum (- (point) limit)) | ||
| 972 | (if backward 1 -1)) | ||
| 973 | t | ||
| 974 | (goto-char limit) | ||
| 975 | nil)) | ||
| 976 | |||
| 977 | |||
| 978 | (defun lua--escape-from-string (&optional backward) | ||
| 979 | "Move point outside of string if it is inside one. | ||
| 980 | |||
| 981 | By default, point is placed after the string, with BACKWARD it is | ||
| 982 | placed before the string." | ||
| 983 | (interactive) | ||
| 984 | (let ((parse-state (syntax-ppss))) | ||
| 985 | (when (nth 3 parse-state) | ||
| 986 | (if backward | ||
| 987 | (goto-char (nth 8 parse-state)) | ||
| 988 | (parse-partial-sexp (point) (line-end-position) nil nil (syntax-ppss) 'syntax-table)) | ||
| 989 | t))) | ||
| 990 | |||
| 991 | |||
| 992 | (defun lua-find-regexp (direction regexp &optional limit) | ||
| 972 | "Searches for a regular expression in the direction specified. | 993 | "Searches for a regular expression in the direction specified. |
| 994 | |||
| 973 | Direction is one of 'forward and 'backward. | 995 | Direction is one of 'forward and 'backward. |
| 974 | By default, matches in comments and strings are ignored, but what to ignore is | 996 | |
| 975 | configurable by specifying ignore-p. If the regexp is found, returns point | 997 | Matches in comments and strings are ignored. If the regexp is |
| 976 | position, nil otherwise. | 998 | found, returns point position, nil otherwise." |
| 977 | ignore-p returns true if the match at the current point position should be | 999 | (let ((search-func (if (eq direction 'forward) |
| 978 | ignored, nil otherwise." | ||
| 979 | (let ((ignore-func (or ignore-p 'lua-comment-or-string-p)) | ||
| 980 | (search-func (if (eq direction 'forward) | ||
| 981 | 're-search-forward 're-search-backward)) | 1000 | 're-search-forward 're-search-backward)) |
| 982 | (case-fold-search nil)) | 1001 | (case-fold-search nil)) |
| 983 | (catch 'found | 1002 | (cl-loop |
| 984 | (while (funcall search-func regexp limit t) | 1003 | always (or (null limit) |
| 985 | (if (and (not (funcall ignore-func (match-beginning 0))) | 1004 | (lua--ensure-point-within-limit limit (not (eq direction 'forward)))) |
| 986 | (not (funcall ignore-func (match-end 0)))) | 1005 | always (funcall search-func regexp limit 'noerror) |
| 987 | (throw 'found (point))))))) | 1006 | for match-beg = (match-beginning 0) |
| 1007 | for match-end = (match-end 0) | ||
| 1008 | while (or (lua-comment-or-string-p match-beg) | ||
| 1009 | (lua-comment-or-string-p match-end)) | ||
| 1010 | do (let ((parse-state (syntax-ppss))) | ||
| 1011 | (cond | ||
| 1012 | ;; Inside a string | ||
| 1013 | ((nth 3 parse-state) | ||
| 1014 | (lua--escape-from-string (not (eq direction 'forward)))) | ||
| 1015 | ;; Inside a comment | ||
| 1016 | ((nth 4 parse-state) | ||
| 1017 | (goto-char (nth 8 parse-state)) | ||
| 1018 | (when (eq direction 'forward) | ||
| 1019 | (forward-comment 1))))) | ||
| 1020 | finally return (point)))) | ||
| 1021 | |||
| 988 | 1022 | ||
| 989 | (defconst lua-block-regexp | 1023 | (defconst lua-block-regexp |
| 990 | (eval-when-compile | 1024 | (eval-when-compile |
| @@ -1050,7 +1084,7 @@ TOKEN-TYPE determines where the token occurs on a statement. open indicates that | |||
| 1050 | 1084 | ||
| 1051 | (defun lua-get-token-type (token-info) | 1085 | (defun lua-get-token-type (token-info) |
| 1052 | "Returns the relevant match regexp from token info" | 1086 | "Returns the relevant match regexp from token info" |
| 1053 | (nth 3 token-info)) | 1087 | (nth 3 token-info)) |
| 1054 | 1088 | ||
| 1055 | (defun lua-backwards-to-block-begin-or-end () | 1089 | (defun lua-backwards-to-block-begin-or-end () |
| 1056 | "Move backwards to nearest block begin or end. Returns nil if not successful." | 1090 | "Move backwards to nearest block begin or end. Returns nil if not successful." |
| @@ -1159,29 +1193,74 @@ If optional NOREPORT is non-nil, it won't flag an error if there | |||
| 1159 | is no block open/close open." | 1193 | is no block open/close open." |
| 1160 | (interactive) | 1194 | (interactive) |
| 1161 | ;; search backward to the beginning of the keyword if necessary | 1195 | ;; search backward to the beginning of the keyword if necessary |
| 1162 | (if (eq (char-syntax (following-char)) ?w) | 1196 | (when (and (eq (char-syntax (following-char)) ?w) |
| 1163 | (re-search-backward "\\_<" nil t)) | 1197 | (not (looking-at "\\_<"))) |
| 1198 | (re-search-backward "\\_<" nil t)) | ||
| 1164 | (let ((position (lua-goto-matching-block-token))) | 1199 | (let ((position (lua-goto-matching-block-token))) |
| 1165 | (if (and (not position) | 1200 | (if (and (not position) |
| 1166 | (not noreport)) | 1201 | (not noreport)) |
| 1167 | (error "Not on a block control keyword or brace") | 1202 | (error "Not on a block control keyword or brace") |
| 1168 | position))) | 1203 | position))) |
| 1169 | 1204 | ||
| 1205 | (defun lua-skip-ws-and-comments-backward (&optional limit) | ||
| 1206 | "Move point back skipping all whitespace and comments. | ||
| 1207 | |||
| 1208 | If LIMIT is given, stop at it or before. | ||
| 1209 | |||
| 1210 | Return non-nil if moved point." | ||
| 1211 | (interactive) | ||
| 1212 | (unless (lua-string-p) | ||
| 1213 | (let ((start-pos (point)) | ||
| 1214 | (comment-start-pos (lua-comment-start-pos))) | ||
| 1215 | (setq limit (min (point) (or limit (point-min)))) | ||
| 1216 | (when comment-start-pos | ||
| 1217 | (goto-char (max limit comment-start-pos))) | ||
| 1218 | (when (< limit (point)) (forward-comment (- limit (point)))) | ||
| 1219 | (when (< (point) limit) (goto-char limit)) | ||
| 1220 | (when (/= start-pos (point)) | ||
| 1221 | (point))))) | ||
| 1222 | |||
| 1223 | (defun lua-skip-ws-and-comments-forward (&optional limit) | ||
| 1224 | "Move point forward skipping all whitespace and comments. | ||
| 1225 | |||
| 1226 | If LIMIT is given, stop at it or before. | ||
| 1227 | |||
| 1228 | Return non-nil if moved point." | ||
| 1229 | (interactive) | ||
| 1230 | (unless (lua-string-p) | ||
| 1231 | (let ((start-pos (point)) | ||
| 1232 | (comment-start-pos (lua-comment-start-pos))) | ||
| 1233 | (setq limit (max (point) (or limit (point-max)))) | ||
| 1234 | ;; Escape from current comment. It is necessary to use "while" because | ||
| 1235 | ;; luadoc parameters have non-comment face, and parse-partial-sexp with | ||
| 1236 | ;; 'syntax-table flag will stop on them. | ||
| 1237 | (when comment-start-pos | ||
| 1238 | (goto-char comment-start-pos) | ||
| 1239 | (forward-comment 1)) | ||
| 1240 | (when (< (point) limit) (forward-comment (- limit (point)))) | ||
| 1241 | (when (< limit (point)) (goto-char limit)) | ||
| 1242 | (when (/= start-pos (point)) | ||
| 1243 | (point))))) | ||
| 1244 | |||
| 1245 | |||
| 1170 | (defun lua-forward-line-skip-blanks (&optional back) | 1246 | (defun lua-forward-line-skip-blanks (&optional back) |
| 1171 | "Move 1 line forward (back if BACK is non-nil) skipping blank lines. | 1247 | "Move 1 line forward/backward and skip all insignificant ws/comment lines. |
| 1172 | 1248 | ||
| 1173 | Moves point 1 line forward (or backward) skipping lines that contain | 1249 | Moves point 1 line forward (or backward) skipping lines that contain |
| 1174 | no Lua code besides comments. The point is put to the beginning of | 1250 | no Lua code besides comments. The point is put to the beginning of |
| 1175 | the line. | 1251 | the line. |
| 1176 | 1252 | ||
| 1177 | Returns final value of point as integer or nil if operation failed." | 1253 | Returns final value of point as integer or nil if operation failed." |
| 1178 | (catch 'found | 1254 | (let ((start-pos (point))) |
| 1179 | (while t | 1255 | (if back |
| 1180 | (unless (eql (forward-line (if back -1 1)) 0) ;; 0 means success | 1256 | (progn |
| 1181 | (throw 'found nil)) | 1257 | (beginning-of-line) |
| 1182 | (unless (or (looking-at "\\s *\\(--.*\\)?$") | 1258 | (lua-skip-ws-and-comments-backward)) |
| 1183 | (lua-comment-or-string-p)) | 1259 | (forward-line) |
| 1184 | (throw 'found (point)))))) | 1260 | (lua-skip-ws-and-comments-forward)) |
| 1261 | (beginning-of-line) | ||
| 1262 | (when (> (count-lines start-pos (point)) 0) | ||
| 1263 | (point)))) | ||
| 1185 | 1264 | ||
| 1186 | (eval-when-compile | 1265 | (eval-when-compile |
| 1187 | (defconst lua-operator-class | 1266 | (defconst lua-operator-class |
| @@ -1190,17 +1269,17 @@ Returns final value of point as integer or nil if operation failed." | |||
| 1190 | (defconst lua-cont-eol-regexp | 1269 | (defconst lua-cont-eol-regexp |
| 1191 | (eval-when-compile | 1270 | (eval-when-compile |
| 1192 | (concat | 1271 | (concat |
| 1193 | "\\(\\_<" | 1272 | "\\(?:\\(?1:\\_<" |
| 1194 | (regexp-opt '("and" "or" "not" "in" "for" "while" | 1273 | (regexp-opt '("and" "or" "not" "in" "for" "while" |
| 1195 | "local" "function" "if" "until" "elseif" "return") | 1274 | "local" "function" "if" "until" "elseif" "return") |
| 1196 | t) | 1275 | t) |
| 1197 | "\\_>\\|" | 1276 | "\\_>\\)\\|" |
| 1198 | "\\(^\\|[^" lua-operator-class "]\\)" | 1277 | "\\(?:^\\|[^" lua-operator-class "]\\)\\(?2:" |
| 1199 | (regexp-opt '("+" "-" "*" "/" "%" "^" ".." "==" | 1278 | (regexp-opt '("+" "-" "*" "/" "%" "^" ".." "==" |
| 1200 | "=" "<" ">" "<=" ">=" "~=" "." ":" | 1279 | "=" "<" ">" "<=" ">=" "~=" "." ":" |
| 1201 | "&" "|" "~" ">>" "<<" "~") | 1280 | "&" "|" "~" ">>" "<<" "~" ",") |
| 1202 | t) | 1281 | t) |
| 1203 | "\\)" | 1282 | "\\)\\)" |
| 1204 | "\\s *\\=")) | 1283 | "\\s *\\=")) |
| 1205 | "Regexp that matches the ending of a line that needs continuation. | 1284 | "Regexp that matches the ending of a line that needs continuation. |
| 1206 | 1285 | ||
| @@ -1212,14 +1291,14 @@ an optional whitespace till the end of the line.") | |||
| 1212 | (eval-when-compile | 1291 | (eval-when-compile |
| 1213 | (concat | 1292 | (concat |
| 1214 | "\\=\\s *" | 1293 | "\\=\\s *" |
| 1215 | "\\(\\_<" | 1294 | "\\(?:\\(?1:\\_<" |
| 1216 | (regexp-opt '("and" "or" "not") t) | 1295 | (regexp-opt '("and" "or" "not" "in") t) |
| 1217 | "\\_>\\|" | 1296 | "\\_>\\)\\|\\(?2:" |
| 1218 | (regexp-opt '("+" "-" "*" "/" "%" "^" ".." "==" | 1297 | (regexp-opt '("," "+" "-" "*" "/" "%" "^" ".." "==" |
| 1219 | "=" "<" ">" "<=" ">=" "~=" "." ":" | 1298 | "=" "<" ">" "<=" ">=" "~=" "." ":" |
| 1220 | "&" "|" "~" ">>" "<<" "~") | 1299 | "&" "|" "~" ">>" "<<" "~") |
| 1221 | t) | 1300 | t) |
| 1222 | "\\($\\|[^" lua-operator-class "]\\)" | 1301 | "\\)\\(?:$\\|[^" lua-operator-class "]\\)" |
| 1223 | "\\)")) | 1302 | "\\)")) |
| 1224 | "Regexp that matches a line that continues previous one. | 1303 | "Regexp that matches a line that continues previous one. |
| 1225 | 1304 | ||
| @@ -1231,44 +1310,101 @@ previous one even though it looked like an end-of-statement.") | |||
| 1231 | (defun lua-last-token-continues-p () | 1310 | (defun lua-last-token-continues-p () |
| 1232 | "Return non-nil if the last token on this line is a continuation token." | 1311 | "Return non-nil if the last token on this line is a continuation token." |
| 1233 | (let ((line-begin (line-beginning-position)) | 1312 | (let ((line-begin (line-beginning-position)) |
| 1234 | (line-end (line-end-position))) | 1313 | return-value) |
| 1235 | (save-excursion | 1314 | (save-excursion |
| 1236 | (end-of-line) | 1315 | (end-of-line) |
| 1237 | ;; we need to check whether the line ends in a comment and | 1316 | (lua-skip-ws-and-comments-backward line-begin) |
| 1238 | ;; skip that one. | 1317 | (setq return-value (and (re-search-backward lua-cont-eol-regexp line-begin t) |
| 1239 | (while (lua-find-regexp 'backward "-" line-begin 'lua-string-p) | 1318 | (or (match-beginning 1) |
| 1240 | (if (looking-at "--") | 1319 | (match-beginning 2)))) |
| 1241 | (setq line-end (point)))) | 1320 | (if (and return-value |
| 1242 | (goto-char line-end) | 1321 | (string-equal (match-string-no-properties 0) "return")) |
| 1243 | (re-search-backward lua-cont-eol-regexp line-begin t)))) | 1322 | ;; "return" keyword is ambiguous and depends on next token |
| 1323 | (unless (save-excursion | ||
| 1324 | (goto-char (match-end 0)) | ||
| 1325 | (forward-comment (point-max)) | ||
| 1326 | (and | ||
| 1327 | ;; Not continuing: at end of file | ||
| 1328 | (not (eobp)) | ||
| 1329 | (or | ||
| 1330 | ;; "function" keyword: it is a continuation, e.g. | ||
| 1331 | ;; | ||
| 1332 | ;; return | ||
| 1333 | ;; function() return 123 end | ||
| 1334 | ;; | ||
| 1335 | (looking-at (lua-rx (symbol "function"))) | ||
| 1336 | ;; Looking at semicolon or any other keyword: not continuation | ||
| 1337 | (not (looking-at (lua-rx (or ";" lua-keyword))))))) | ||
| 1338 | (setq return-value nil))) | ||
| 1339 | return-value))) | ||
| 1340 | |||
| 1244 | 1341 | ||
| 1245 | (defun lua-first-token-continues-p () | 1342 | (defun lua-first-token-continues-p () |
| 1246 | "Return non-nil if the first token on this line is a continuation token." | 1343 | "Return non-nil if the first token on this line is a continuation token." |
| 1247 | (let ((line-end (line-end-position))) | 1344 | (let ((line-end (line-end-position))) |
| 1248 | (save-excursion | 1345 | (save-excursion |
| 1249 | (beginning-of-line) | 1346 | (beginning-of-line) |
| 1347 | (lua-skip-ws-and-comments-forward line-end) | ||
| 1250 | ;; if first character of the line is inside string, it's a continuation | 1348 | ;; if first character of the line is inside string, it's a continuation |
| 1251 | ;; if strings aren't supposed to be indented, `lua-calculate-indentation' won't even let | 1349 | ;; if strings aren't supposed to be indented, `lua-calculate-indentation' won't even let |
| 1252 | ;; the control inside this function | 1350 | ;; the control inside this function |
| 1253 | (re-search-forward lua-cont-bol-regexp line-end t)))) | 1351 | (and |
| 1352 | (re-search-forward lua-cont-bol-regexp line-end t) | ||
| 1353 | (or (match-beginning 1) | ||
| 1354 | (match-beginning 2)))))) | ||
| 1254 | 1355 | ||
| 1255 | (defconst lua-block-starter-regexp | ||
| 1256 | (eval-when-compile | ||
| 1257 | (concat | ||
| 1258 | "\\(\\_<" | ||
| 1259 | (regexp-opt '("do" "while" "repeat" "until" "if" "then" | ||
| 1260 | "else" "elseif" "end" "for" "local") t) | ||
| 1261 | "\\_>\\)"))) | ||
| 1262 | 1356 | ||
| 1263 | (defun lua-first-token-starts-block-p () | 1357 | (defun lua--backward-up-list-noerror () |
| 1264 | "Return non-nil if the first token on this line is a block starter token." | 1358 | "Safe version of lua-backward-up-list that does not signal an error." |
| 1265 | (let ((line-end (line-end-position))) | 1359 | (condition-case nil |
| 1266 | (save-excursion | 1360 | (lua-backward-up-list) |
| 1267 | (beginning-of-line) | 1361 | (scan-error nil))) |
| 1268 | (re-search-forward (concat "\\s *" lua-block-starter-regexp) line-end t)))) | 1362 | |
| 1363 | |||
| 1364 | (defun lua-backward-up-list () | ||
| 1365 | "Goto starter/opener of the block that contains point." | ||
| 1366 | (interactive) | ||
| 1367 | (let ((start-pos (point)) | ||
| 1368 | end-pos) | ||
| 1369 | (or | ||
| 1370 | ;; Return parent block opener token if it exists. | ||
| 1371 | (cl-loop | ||
| 1372 | ;; Search indentation modifier backward, return nil on failure. | ||
| 1373 | always (lua-find-regexp 'backward lua-indentation-modifier-regexp) | ||
| 1374 | ;; Fetch info about the found token | ||
| 1375 | for token = (match-string-no-properties 0) | ||
| 1376 | for token-info = (lua-get-block-token-info token) | ||
| 1377 | for token-type = (lua-get-token-type token-info) | ||
| 1378 | ;; If the token is a close token, continue to skip its opener. If not | ||
| 1379 | ;; close, stop and return found token. | ||
| 1380 | while (eq token-type 'close) | ||
| 1381 | ;; Find matching opener to skip it and continue from beginning. | ||
| 1382 | ;; | ||
| 1383 | ;; Return nil on failure. | ||
| 1384 | always (let ((position (lua-find-matching-token-word token 'backward))) | ||
| 1385 | (and position (goto-char position))) | ||
| 1386 | finally return token-info) | ||
| 1387 | (progn | ||
| 1388 | (setq end-pos (point)) | ||
| 1389 | (goto-char start-pos) | ||
| 1390 | (signal 'scan-error | ||
| 1391 | (list "Block open token not found" | ||
| 1392 | ;; If start-pos == end-pos, the "obstacle" is current | ||
| 1393 | (if (eql start-pos end-pos) start-pos (match-beginning 0)) | ||
| 1394 | (if (eql start-pos end-pos) start-pos (match-end 0)))))))) | ||
| 1395 | |||
| 1396 | (defun lua--continuation-breaking-line-p () | ||
| 1397 | "Return non-nil if looking at token(-s) that forbid continued line." | ||
| 1398 | (save-excursion | ||
| 1399 | (lua-skip-ws-and-comments-forward (line-end-position)) | ||
| 1400 | (looking-at (lua-rx (or (symbol "do" "while" "repeat" "until" | ||
| 1401 | "if" "then" "elseif" "else" | ||
| 1402 | "for" "local") | ||
| 1403 | lua-funcheader))))) | ||
| 1269 | 1404 | ||
| 1270 | (defun lua-is-continuing-statement-p (&optional parse-start) | 1405 | |
| 1271 | "Return non-nil if the line at PARSE-START continues a statement. | 1406 | (defun lua-is-continuing-statement-p-1 () |
| 1407 | "Return non-nil if current lined continues a statement. | ||
| 1272 | 1408 | ||
| 1273 | More specifically, return the point in the line that is continued. | 1409 | More specifically, return the point in the line that is continued. |
| 1274 | The criteria for a continuing statement are: | 1410 | The criteria for a continuing statement are: |
| @@ -1276,17 +1412,71 @@ The criteria for a continuing statement are: | |||
| 1276 | * the last token of the previous line is a continuing op, | 1412 | * the last token of the previous line is a continuing op, |
| 1277 | OR the first token of the current line is a continuing op | 1413 | OR the first token of the current line is a continuing op |
| 1278 | 1414 | ||
| 1415 | * the expression is not enclosed by a parentheses/braces/brackets" | ||
| 1416 | (let (prev-line continuation-pos parent-block-opener) | ||
| 1417 | (save-excursion (setq prev-line (lua-forward-line-skip-blanks 'back))) | ||
| 1418 | (and prev-line | ||
| 1419 | (not (lua--continuation-breaking-line-p)) | ||
| 1420 | (save-excursion | ||
| 1421 | (or | ||
| 1422 | ;; Binary operator or keyword that implies continuation. | ||
| 1423 | (and (setq continuation-pos | ||
| 1424 | (or (lua-first-token-continues-p) | ||
| 1425 | (save-excursion (and (goto-char prev-line) | ||
| 1426 | ;; check last token of previous nonblank line | ||
| 1427 | (lua-last-token-continues-p))))) | ||
| 1428 | (not | ||
| 1429 | ;; Operators/keywords does not create continuation inside some blocks: | ||
| 1430 | (and | ||
| 1431 | (setq parent-block-opener (car-safe (lua--backward-up-list-noerror))) | ||
| 1432 | (or | ||
| 1433 | ;; - inside parens/brackets | ||
| 1434 | (member parent-block-opener '("(" "[")) | ||
| 1435 | ;; - inside braces if it is a comma | ||
| 1436 | (and (eq (char-after continuation-pos) ?,) | ||
| 1437 | (equal parent-block-opener "{"))))) | ||
| 1438 | continuation-pos)))))) | ||
| 1439 | |||
| 1440 | |||
| 1441 | (defun lua-is-continuing-statement-p (&optional parse-start) | ||
| 1442 | "Returns non-nil if the line at PARSE-START should be indented as continuation line. | ||
| 1443 | |||
| 1444 | This true is when the line : | ||
| 1445 | |||
| 1446 | * is continuing a statement itself | ||
| 1447 | |||
| 1448 | * starts with a 1+ block-closer tokens, an top-most block opener is on a continuation line | ||
| 1279 | " | 1449 | " |
| 1280 | (let ((prev-line nil)) | 1450 | (save-excursion |
| 1281 | (save-excursion | 1451 | (if parse-start (goto-char parse-start)) |
| 1282 | (if parse-start (goto-char parse-start)) | 1452 | |
| 1283 | (save-excursion (setq prev-line (lua-forward-line-skip-blanks 'back))) | 1453 | ;; If line starts with a series of closer tokens, whether or not the line |
| 1284 | (and prev-line | 1454 | ;; is a continuation line is decided by the opener line, e.g. |
| 1285 | (not (lua-first-token-starts-block-p)) | 1455 | ;; |
| 1286 | (or (lua-first-token-continues-p) | 1456 | ;; x = foo + |
| 1287 | (and (goto-char prev-line) | 1457 | ;; long_function_name( |
| 1288 | ;; check last token of previous nonblank line | 1458 | ;; long_parameter_1, |
| 1289 | (lua-last-token-continues-p))))))) | 1459 | ;; long_parameter_2, |
| 1460 | ;; long_parameter_3, | ||
| 1461 | ;; ) + long_function_name2({ | ||
| 1462 | ;; long_parameter_1, | ||
| 1463 | ;; long_parameter_2, | ||
| 1464 | ;; long_parameter_3, | ||
| 1465 | ;; }) | ||
| 1466 | ;; | ||
| 1467 | ;; Final line, "})" is a continuation line, but it is decided by the | ||
| 1468 | ;; opener line, ") + long_function_name2({", which in its turn is decided | ||
| 1469 | ;; by the "long_function_name(" line, which is a continuation line | ||
| 1470 | ;; because the line before it ends with a binary operator. | ||
| 1471 | (cl-loop | ||
| 1472 | ;; Go to opener line | ||
| 1473 | while (and (lua--goto-line-beginning-rightmost-closer) | ||
| 1474 | (lua--backward-up-list-noerror)) | ||
| 1475 | ;; If opener line is continuing, repeat. If opener line is not | ||
| 1476 | ;; continuing, return nil. | ||
| 1477 | always (lua-is-continuing-statement-p-1) | ||
| 1478 | ;; We get here if there was no opener to go to: check current line. | ||
| 1479 | finally return (lua-is-continuing-statement-p-1)))) | ||
| 1290 | 1480 | ||
| 1291 | (defun lua-make-indentation-info-pair (found-token found-pos) | 1481 | (defun lua-make-indentation-info-pair (found-token found-pos) |
| 1292 | "Create a pair from FOUND-TOKEN and FOUND-POS for indentation calculation. | 1482 | "Create a pair from FOUND-TOKEN and FOUND-POS for indentation calculation. |
| @@ -1305,7 +1495,8 @@ Don't use standalone." | |||
| 1305 | (cons 'relative lua-indent-level)) | 1495 | (cons 'relative lua-indent-level)) |
| 1306 | 1496 | ||
| 1307 | ;; block openers | 1497 | ;; block openers |
| 1308 | ((member found-token (list "{" "(" "[")) | 1498 | ((and lua-indent-nested-block-content-align |
| 1499 | (member found-token (list "{" "(" "["))) | ||
| 1309 | (save-excursion | 1500 | (save-excursion |
| 1310 | (let ((found-bol (line-beginning-position))) | 1501 | (let ((found-bol (line-beginning-position))) |
| 1311 | (forward-comment (point-max)) | 1502 | (forward-comment (point-max)) |
| @@ -1326,9 +1517,10 @@ Don't use standalone." | |||
| 1326 | ;; nullify a previous then if on the same line. | 1517 | ;; nullify a previous then if on the same line. |
| 1327 | ((member found-token (list "until" "elseif")) | 1518 | ((member found-token (list "until" "elseif")) |
| 1328 | (save-excursion | 1519 | (save-excursion |
| 1329 | (let ((line (line-number-at-pos))) | 1520 | (let* ((line-beginning (line-beginning-position)) |
| 1330 | (if (and (lua-goto-matching-block-token found-pos 'backward) | 1521 | (same-line (and (lua-goto-matching-block-token found-pos 'backward) |
| 1331 | (= line (line-number-at-pos))) | 1522 | (<= line-beginning (point))))) |
| 1523 | (if same-line | ||
| 1332 | (cons 'remove-matching 0) | 1524 | (cons 'remove-matching 0) |
| 1333 | (cons 'relative 0))))) | 1525 | (cons 'relative 0))))) |
| 1334 | 1526 | ||
| @@ -1337,23 +1529,41 @@ Don't use standalone." | |||
| 1337 | ;; either the next line will be indented correctly, or the end on the same | 1529 | ;; either the next line will be indented correctly, or the end on the same |
| 1338 | ;; line will remove the effect of the else. | 1530 | ;; line will remove the effect of the else. |
| 1339 | ((string-equal found-token "else") | 1531 | ((string-equal found-token "else") |
| 1340 | (save-excursion | 1532 | (save-excursion |
| 1341 | (let ((line (line-number-at-pos))) | 1533 | (let* ((line-beginning (line-beginning-position)) |
| 1342 | (if (and (lua-goto-matching-block-token found-pos 'backward) | 1534 | (same-line (and (lua-goto-matching-block-token found-pos 'backward) |
| 1343 | (= line (line-number-at-pos))) | 1535 | (<= line-beginning (point))))) |
| 1344 | (cons 'replace-matching (cons 'relative lua-indent-level)) | 1536 | (if same-line |
| 1345 | (cons 'relative lua-indent-level))))) | 1537 | (cons 'replace-matching (cons 'relative lua-indent-level)) |
| 1538 | (cons 'relative lua-indent-level))))) | ||
| 1346 | 1539 | ||
| 1347 | ;; Block closers. If they are on the same line as their openers, they simply | 1540 | ;; Block closers. If they are on the same line as their openers, they simply |
| 1348 | ;; eat up the matching indentation modifier. Otherwise, they pull | 1541 | ;; eat up the matching indentation modifier. Otherwise, they pull |
| 1349 | ;; indentation back to the matching block opener. | 1542 | ;; indentation back to the matching block opener. |
| 1350 | ((member found-token (list ")" "}" "]" "end")) | 1543 | ((member found-token (list ")" "}" "]" "end")) |
| 1351 | (save-excursion | 1544 | (save-excursion |
| 1352 | (let ((line (line-number-at-pos))) | 1545 | (let* ((line-beginning (line-beginning-position)) |
| 1353 | (lua-goto-matching-block-token found-pos 'backward) | 1546 | (same-line (and (lua-goto-matching-block-token found-pos 'backward) |
| 1354 | (if (/= line (line-number-at-pos)) | 1547 | (<= line-beginning (point)))) |
| 1355 | (lua-calculate-indentation-info (point)) | 1548 | (opener-pos (point)) |
| 1356 | (cons 'remove-matching 0))))) | 1549 | opener-continuation-offset) |
| 1550 | (if same-line | ||
| 1551 | (cons 'remove-matching 0) | ||
| 1552 | (back-to-indentation) | ||
| 1553 | (setq opener-continuation-offset | ||
| 1554 | (if (lua-is-continuing-statement-p-1) lua-indent-level 0)) | ||
| 1555 | |||
| 1556 | ;; Accumulate indentation up to opener, including indentation. If | ||
| 1557 | ;; there were no other indentation modifiers until said opener, | ||
| 1558 | ;; ensure there is no continuation after the closer. | ||
| 1559 | `(multiple . ((absolute . ,(- (current-indentation) opener-continuation-offset)) | ||
| 1560 | ,@(when (/= opener-continuation-offset 0) | ||
| 1561 | (list (cons 'continued-line opener-continuation-offset))) | ||
| 1562 | ,@(delete nil (list (lua-calculate-indentation-info-1 nil opener-pos))) | ||
| 1563 | (cancel-continued-line . nil))))))) | ||
| 1564 | |||
| 1565 | ((member found-token '("do" "then")) | ||
| 1566 | `(multiple . ((cancel-continued-line . nil) (relative . ,lua-indent-level)))) | ||
| 1357 | 1567 | ||
| 1358 | ;; Everything else. This is from the original code: If opening a block | 1568 | ;; Everything else. This is from the original code: If opening a block |
| 1359 | ;; (match-data 1 exists), then push indentation one level up, if it is | 1569 | ;; (match-data 1 exists), then push indentation one level up, if it is |
| @@ -1365,8 +1575,8 @@ Don't use standalone." | |||
| 1365 | ;; end of a block matched | 1575 | ;; end of a block matched |
| 1366 | (- lua-indent-level)))))) | 1576 | (- lua-indent-level)))))) |
| 1367 | 1577 | ||
| 1368 | (defun lua-add-indentation-info-pair (pair info) | 1578 | (defun lua-add-indentation-info-pair (pair info-list) |
| 1369 | "Add the given indentation info PAIR to the list of indentation INFO. | 1579 | "Add the given indentation info PAIR to the list of indentation INFO-LIST. |
| 1370 | This function has special case handling for two tokens: remove-matching, | 1580 | This function has special case handling for two tokens: remove-matching, |
| 1371 | and replace-matching. These two tokens are cleanup tokens that remove or | 1581 | and replace-matching. These two tokens are cleanup tokens that remove or |
| 1372 | alter the effect of a previously recorded indentation info. | 1582 | alter the effect of a previously recorded indentation info. |
| @@ -1380,17 +1590,27 @@ and the cdr of the replace-matching info is added in its place. This is used | |||
| 1380 | when a middle-of the block (the only case is 'else') is seen on the same line | 1590 | when a middle-of the block (the only case is 'else') is seen on the same line |
| 1381 | the block is opened." | 1591 | the block is opened." |
| 1382 | (cond | 1592 | (cond |
| 1593 | ( (eq 'multiple (car pair)) | ||
| 1594 | (let ((info-pair-elts (cdr pair))) | ||
| 1595 | (while info-pair-elts | ||
| 1596 | (setq info-list (lua-add-indentation-info-pair (car info-pair-elts) info-list) | ||
| 1597 | info-pair-elts (cdr info-pair-elts))) | ||
| 1598 | info-list)) | ||
| 1599 | ( (eq 'cancel-continued-line (car pair)) | ||
| 1600 | (if (eq (caar info-list) 'continued-line) | ||
| 1601 | (cdr info-list) | ||
| 1602 | info-list)) | ||
| 1383 | ( (eq 'remove-matching (car pair)) | 1603 | ( (eq 'remove-matching (car pair)) |
| 1384 | ; Remove head of list | 1604 | ;; Remove head of list |
| 1385 | (cdr info)) | 1605 | (cdr info-list)) |
| 1386 | ( (eq 'replace-matching (car pair)) | 1606 | ( (eq 'replace-matching (car pair)) |
| 1387 | ; remove head of list, and add the cdr of pair instead | 1607 | ;; remove head of list, and add the cdr of pair instead |
| 1388 | (cons (cdr pair) (cdr info))) | 1608 | (cons (cdr pair) (cdr info-list))) |
| 1389 | ( (listp (cdr-safe pair)) | 1609 | ( (listp (cdr-safe pair)) |
| 1390 | (nconc pair info)) | 1610 | (nconc pair info-list)) |
| 1391 | ( t | 1611 | ( t |
| 1392 | ; Just add the pair | 1612 | ;; Just add the pair |
| 1393 | (cons pair info)))) | 1613 | (cons pair info-list)))) |
| 1394 | 1614 | ||
| 1395 | (defun lua-calculate-indentation-info-1 (indentation-info bound) | 1615 | (defun lua-calculate-indentation-info-1 (indentation-info bound) |
| 1396 | "Helper function for `lua-calculate-indentation-info'. | 1616 | "Helper function for `lua-calculate-indentation-info'. |
| @@ -1413,10 +1633,11 @@ The effect of each token can be either a shift relative to the current | |||
| 1413 | indentation level, or indentation to some absolute column. This information | 1633 | indentation level, or indentation to some absolute column. This information |
| 1414 | is collected in a list of indentation info pairs, which denote absolute | 1634 | is collected in a list of indentation info pairs, which denote absolute |
| 1415 | and relative each, and the shift/column to indent to." | 1635 | and relative each, and the shift/column to indent to." |
| 1416 | (let (indentation-info) | 1636 | (let (indentation-info cont-stmt-pos) |
| 1417 | 1637 | (while (setq cont-stmt-pos (lua-is-continuing-statement-p)) | |
| 1418 | (while (lua-is-continuing-statement-p) | 1638 | (lua-forward-line-skip-blanks 'back) |
| 1419 | (lua-forward-line-skip-blanks 'back)) | 1639 | (when (< cont-stmt-pos (point)) |
| 1640 | (goto-char cont-stmt-pos))) | ||
| 1420 | 1641 | ||
| 1421 | ;; calculate indentation modifiers for the line itself | 1642 | ;; calculate indentation modifiers for the line itself |
| 1422 | (setq indentation-info (list (cons 'absolute (current-indentation)))) | 1643 | (setq indentation-info (list (cons 'absolute (current-indentation)))) |
| @@ -1438,7 +1659,7 @@ and relative each, and the shift/column to indent to." | |||
| 1438 | 1659 | ||
| 1439 | ;; if it's the first non-continued line, subtract one level | 1660 | ;; if it's the first non-continued line, subtract one level |
| 1440 | (when (eq (car (car indentation-info)) 'continued-line) | 1661 | (when (eq (car (car indentation-info)) 'continued-line) |
| 1441 | (pop indentation-info))) | 1662 | (push (cons 'stop-continued-line (- lua-indent-level)) indentation-info))) |
| 1442 | 1663 | ||
| 1443 | ;; add modifiers found in this continuation line | 1664 | ;; add modifiers found in this continuation line |
| 1444 | (setq indentation-info | 1665 | (setq indentation-info |
| @@ -1448,19 +1669,29 @@ and relative each, and the shift/column to indent to." | |||
| 1448 | indentation-info)) | 1669 | indentation-info)) |
| 1449 | 1670 | ||
| 1450 | 1671 | ||
| 1451 | (defun lua-accumulate-indentation-info (info) | 1672 | (defun lua-accumulate-indentation-info (reversed-indentation-info) |
| 1452 | "Accumulates the indentation information previously calculated by | 1673 | "Accumulates the indentation information previously calculated by |
| 1453 | lua-calculate-indentation-info. Returns either the relative indentation | 1674 | lua-calculate-indentation-info. Returns either the relative indentation |
| 1454 | shift, or the absolute column to indent to." | 1675 | shift, or the absolute column to indent to." |
| 1455 | (let ((info-list (reverse info)) | 1676 | (let (indentation-info |
| 1456 | (type 'relative) | 1677 | (type 'relative) |
| 1457 | (accu 0)) | 1678 | (accu 0)) |
| 1679 | ;; Aggregate all neighbouring relative offsets, reversing the INFO list. | ||
| 1680 | (cl-dolist (elt reversed-indentation-info) | ||
| 1681 | (if (and (eq (car elt) 'relative) | ||
| 1682 | (eq (caar indentation-info) 'relative)) | ||
| 1683 | (setcdr (car indentation-info) (+ (cdar indentation-info) (cdr elt))) | ||
| 1684 | (push elt indentation-info))) | ||
| 1685 | |||
| 1686 | ;; Aggregate indentation info, taking 'absolute modifiers into account. | ||
| 1458 | (mapc (lambda (x) | 1687 | (mapc (lambda (x) |
| 1459 | (setq accu (if (eq 'absolute (car x)) | 1688 | (let ((new-val (cdr x))) |
| 1460 | (progn (setq type 'absolute) | 1689 | (if (eq 'absolute (car x)) |
| 1461 | (cdr x)) | 1690 | (progn (setq type 'absolute |
| 1462 | (+ accu (cdr x))))) | 1691 | accu new-val)) |
| 1463 | info-list) | 1692 | (setq accu (+ accu new-val))))) |
| 1693 | indentation-info) | ||
| 1694 | |||
| 1464 | (cons type accu))) | 1695 | (cons type accu))) |
| 1465 | 1696 | ||
| 1466 | (defun lua-calculate-indentation-block-modifier (&optional parse-end) | 1697 | (defun lua-calculate-indentation-block-modifier (&optional parse-end) |
| @@ -1510,6 +1741,7 @@ one." | |||
| 1510 | ;; right hand side | 1741 | ;; right hand side |
| 1511 | (or "{" | 1742 | (or "{" |
| 1512 | "function" | 1743 | "function" |
| 1744 | "(" | ||
| 1513 | (seq (group-n 1 (eval lua--function-name-rx) (* blank)) | 1745 | (seq (group-n 1 (eval lua--function-name-rx) (* blank)) |
| 1514 | (any "({"))))))) | 1746 | (any "({"))))))) |
| 1515 | 1747 | ||
| @@ -1551,6 +1783,27 @@ left-shifter expression. " | |||
| 1551 | (looking-at lua--left-shifter-regexp) | 1783 | (looking-at lua--left-shifter-regexp) |
| 1552 | (= old-point (match-end 1)))))) | 1784 | (= old-point (match-end 1)))))) |
| 1553 | 1785 | ||
| 1786 | (defun lua--goto-line-beginning-rightmost-closer (&optional parse-start) | ||
| 1787 | (let (case-fold-search pos line-end-pos return-val) | ||
| 1788 | (save-excursion | ||
| 1789 | (if parse-start (goto-char parse-start)) | ||
| 1790 | (setq line-end-pos (line-end-position)) | ||
| 1791 | (back-to-indentation) | ||
| 1792 | (unless (lua-comment-or-string-p) | ||
| 1793 | (cl-loop while (and (<= (point) line-end-pos) | ||
| 1794 | (looking-at lua-indentation-modifier-regexp)) | ||
| 1795 | for token-info = (lua-get-block-token-info (match-string 0)) | ||
| 1796 | for token-type = (lua-get-token-type token-info) | ||
| 1797 | while (not (eq token-type 'open)) | ||
| 1798 | do (progn | ||
| 1799 | (setq pos (match-beginning 0) | ||
| 1800 | return-val token-info) | ||
| 1801 | (goto-char (match-end 0)) | ||
| 1802 | (forward-comment (line-end-position)))))) | ||
| 1803 | (when pos | ||
| 1804 | (progn | ||
| 1805 | (goto-char pos) | ||
| 1806 | return-val)))) | ||
| 1554 | 1807 | ||
| 1555 | 1808 | ||
| 1556 | (defun lua-calculate-indentation-override (&optional parse-start) | 1809 | (defun lua-calculate-indentation-override (&optional parse-start) |
| @@ -1562,26 +1815,44 @@ line containing block-open token for the last block-close token | |||
| 1562 | in the sequence. | 1815 | in the sequence. |
| 1563 | 1816 | ||
| 1564 | If not, return nil." | 1817 | If not, return nil." |
| 1565 | (let (case-fold-search token-info block-token-pos) | 1818 | (let (case-fold-search rightmost-closer-info opener-info opener-pos) |
| 1566 | (save-excursion | 1819 | (save-excursion |
| 1567 | (if parse-start (goto-char parse-start)) | 1820 | (when (and (setq rightmost-closer-info (lua--goto-line-beginning-rightmost-closer parse-start)) |
| 1821 | (setq opener-info (lua--backward-up-list-noerror)) | ||
| 1822 | ;; Ensure opener matches closer. | ||
| 1823 | (string-match (lua-get-token-match-re rightmost-closer-info 'backward) | ||
| 1824 | (car opener-info))) | ||
| 1568 | 1825 | ||
| 1569 | (back-to-indentation) | 1826 | ;; Special case: "middle" tokens like for/do, while/do, if/then, |
| 1570 | (unless (lua-comment-or-string-p) | 1827 | ;; elseif/then: corresponding "end" or corresponding "else" must be |
| 1571 | (while | 1828 | ;; unindented to the beginning of the statement, which is not |
| 1572 | (and (looking-at lua-indentation-modifier-regexp) | 1829 | ;; necessarily the same as beginning of string that contains "do", e.g. |
| 1573 | (setq token-info (lua-get-block-token-info (match-string 0))) | 1830 | ;; |
| 1574 | (not (eq 'open (lua-get-token-type token-info)))) | 1831 | ;; while ( |
| 1575 | (setq block-token-pos (match-beginning 0)) | 1832 | ;; foo and |
| 1576 | (goto-char (match-end 0)) | 1833 | ;; bar) do |
| 1577 | (skip-syntax-forward " " (line-end-position))) | 1834 | ;; hello_world() |
| 1835 | ;; end | ||
| 1836 | (setq opener-pos (point)) | ||
| 1837 | (when (/= (- opener-pos (line-beginning-position)) (current-indentation)) | ||
| 1838 | (unless (or | ||
| 1839 | (and (string-equal (car opener-info) "do") | ||
| 1840 | (member (car (lua--backward-up-list-noerror)) '("while" "for"))) | ||
| 1841 | (and (string-equal (car opener-info) "then") | ||
| 1842 | (member (car (lua--backward-up-list-noerror)) '("if" "elseif")))) | ||
| 1843 | (goto-char opener-pos))) | ||
| 1844 | |||
| 1845 | ;; (let (cont-stmt-pos) | ||
| 1846 | ;; (while (setq cont-stmt-pos (lua-is-continuing-statement-p)) | ||
| 1847 | ;; (goto-char cont-stmt-pos))) | ||
| 1848 | ;; Exception cases: when the start of the line is an assignment, | ||
| 1849 | ;; go to the start of the assignment instead of the matching item | ||
| 1850 | (if (and lua-indent-close-paren-align | ||
| 1851 | (member (car opener-info) '("{" "(" "[")) | ||
| 1852 | (not (lua-point-is-after-left-shifter-p))) | ||
| 1853 | (current-column) | ||
| 1854 | (current-indentation)))))) | ||
| 1578 | 1855 | ||
| 1579 | (when (lua-goto-matching-block-token block-token-pos 'backward) | ||
| 1580 | ;; Exception cases: when the start of the line is an assignment, | ||
| 1581 | ;; go to the start of the assignment instead of the matching item | ||
| 1582 | (if (lua-point-is-after-left-shifter-p) | ||
| 1583 | (current-indentation) | ||
| 1584 | (current-column))))))) | ||
| 1585 | 1856 | ||
| 1586 | (defun lua-calculate-indentation () | 1857 | (defun lua-calculate-indentation () |
| 1587 | "Return appropriate indentation for current line as Lua code." | 1858 | "Return appropriate indentation for current line as Lua code." |
| @@ -1689,11 +1960,11 @@ This function just searches for a `end' at the beginning of a line." | |||
| 1689 | (goto-char (point-min)) | 1960 | (goto-char (point-min)) |
| 1690 | (while (re-search-forward "[\"'\\\t\\\n]" nil t) | 1961 | (while (re-search-forward "[\"'\\\t\\\n]" nil t) |
| 1691 | (cond | 1962 | (cond |
| 1692 | ((string= (match-string 0) "\n") | 1963 | ((string= (match-string 0) "\n") |
| 1693 | (replace-match "\\\\n")) | 1964 | (replace-match "\\\\n")) |
| 1694 | ((string= (match-string 0) "\t") | 1965 | ((string= (match-string 0) "\t") |
| 1695 | (replace-match "\\\\t")) | 1966 | (replace-match "\\\\t")) |
| 1696 | (t | 1967 | (t |
| 1697 | (replace-match "\\\\\\&" t)))) | 1968 | (replace-match "\\\\\\&" t)))) |
| 1698 | (concat "'" (buffer-string) "'")))) | 1969 | (concat "'" (buffer-string) "'")))) |
| 1699 | 1970 | ||
| @@ -1706,32 +1977,33 @@ This function just searches for a `end' at the beginning of a line." | |||
| 1706 | PROGRAM defaults to NAME, which defaults to `lua-default-application'. | 1977 | PROGRAM defaults to NAME, which defaults to `lua-default-application'. |
| 1707 | When called interactively, switch to the process buffer." | 1978 | When called interactively, switch to the process buffer." |
| 1708 | (interactive) | 1979 | (interactive) |
| 1709 | (or switches | ||
| 1710 | (setq switches lua-default-command-switches)) | ||
| 1711 | (setq name (or name (if (consp lua-default-application) | 1980 | (setq name (or name (if (consp lua-default-application) |
| 1712 | (car lua-default-application) | 1981 | (car lua-default-application) |
| 1713 | lua-default-application))) | 1982 | lua-default-application))) |
| 1714 | (setq program (or program lua-default-application)) | 1983 | (setq program (or program lua-default-application)) |
| 1715 | (setq lua-process-buffer (apply 'make-comint name program startfile switches)) | 1984 | ;; don't re-initialize if there already is a lua process |
| 1716 | (setq lua-process (get-buffer-process lua-process-buffer)) | 1985 | (unless (comint-check-proc (format "*%s*" name)) |
| 1717 | (set-process-query-on-exit-flag lua-process nil) | 1986 | (setq lua-process-buffer (apply #'make-comint name program startfile |
| 1718 | (with-current-buffer lua-process-buffer | 1987 | (or switches lua-default-command-switches))) |
| 1719 | ;; wait for prompt | 1988 | (setq lua-process (get-buffer-process lua-process-buffer)) |
| 1720 | (while (not (lua-prompt-line)) | 1989 | (set-process-query-on-exit-flag lua-process nil) |
| 1721 | (accept-process-output (get-buffer-process (current-buffer))) | 1990 | (with-current-buffer lua-process-buffer |
| 1722 | (goto-char (point-max))) | 1991 | ;; enable error highlighting in stack traces |
| 1723 | ;; send initialization code | 1992 | (require 'compile) |
| 1724 | (lua-send-string lua-process-init-code) | 1993 | (setq lua--repl-buffer-p t) |
| 1994 | (make-local-variable 'compilation-error-regexp-alist) | ||
| 1995 | (setq compilation-error-regexp-alist | ||
| 1996 | (cons (list lua-traceback-line-re 1 2) | ||
| 1997 | compilation-error-regexp-alist)) | ||
| 1998 | (compilation-shell-minor-mode 1) | ||
| 1999 | (setq-local comint-prompt-regexp lua-prompt-regexp) | ||
| 1725 | 2000 | ||
| 1726 | ;; enable error highlighting in stack traces | 2001 | ;; Don't send initialization code until seeing the prompt to ensure that |
| 1727 | (require 'compile) | 2002 | ;; the interpreter is ready. |
| 1728 | (setq lua--repl-buffer-p t) | 2003 | (while (not (lua-prompt-line)) |
| 1729 | (make-local-variable 'compilation-error-regexp-alist) | 2004 | (accept-process-output (get-buffer-process (current-buffer))) |
| 1730 | (setq compilation-error-regexp-alist | 2005 | (goto-char (point-max))) |
| 1731 | (cons (list lua-traceback-line-re 1 2) | 2006 | (lua-send-string lua-process-init-code))) |
| 1732 | compilation-error-regexp-alist)) | ||
| 1733 | (compilation-shell-minor-mode 1) | ||
| 1734 | (setq-local comint-prompt-regexp lua-prompt-regexp)) | ||
| 1735 | 2007 | ||
| 1736 | ;; when called interactively, switch to process buffer | 2008 | ;; when called interactively, switch to process buffer |
| 1737 | (if (called-interactively-p 'any) | 2009 | (if (called-interactively-p 'any) |
| @@ -1739,8 +2011,7 @@ When called interactively, switch to the process buffer." | |||
| 1739 | 2011 | ||
| 1740 | (defun lua-get-create-process () | 2012 | (defun lua-get-create-process () |
| 1741 | "Return active Lua process creating one if necessary." | 2013 | "Return active Lua process creating one if necessary." |
| 1742 | (unless (comint-check-proc lua-process-buffer) | 2014 | (lua-start-process) |
| 1743 | (lua-start-process)) | ||
| 1744 | lua-process) | 2015 | lua-process) |
| 1745 | 2016 | ||
| 1746 | (defun lua-kill-process () | 2017 | (defun lua-kill-process () |
| @@ -1868,12 +2139,22 @@ Create a Lua process if one doesn't already exist." | |||
| 1868 | (when (buffer-live-p lua-process-buffer) | 2139 | (when (buffer-live-p lua-process-buffer) |
| 1869 | (delete-windows-on lua-process-buffer))) | 2140 | (delete-windows-on lua-process-buffer))) |
| 1870 | 2141 | ||
| 2142 | (defun lua--funcname-char-p (c) | ||
| 2143 | "Check if character C is part of a function name. | ||
| 2144 | Return nil if C is nil. See `lua-funcname-at-point'." | ||
| 2145 | (and c (string-match-p "\\`[A-Za-z_.]\\'" (string c)))) | ||
| 2146 | |||
| 1871 | (defun lua-funcname-at-point () | 2147 | (defun lua-funcname-at-point () |
| 1872 | "Get current Name { '.' Name } sequence." | 2148 | "Get current Name { '.' Name } sequence." |
| 1873 | ;; FIXME: copying/modifying syntax table for each call may incur a penalty | 2149 | (when (or (lua--funcname-char-p (char-before)) |
| 1874 | (with-syntax-table (copy-syntax-table) | 2150 | (lua--funcname-char-p (char-after))) |
| 1875 | (modify-syntax-entry ?. "_") | 2151 | (save-excursion |
| 1876 | (current-word t))) | 2152 | (save-match-data |
| 2153 | (re-search-backward "\\`\\|[^A-Za-z_.]") | ||
| 2154 | ;; NOTE: `point' will be either at the start of the buffer or on a | ||
| 2155 | ;; non-symbol character. | ||
| 2156 | (re-search-forward "\\([A-Za-z_]+\\(?:\\.[A-Za-z_]+\\)*\\)") | ||
| 2157 | (match-string-no-properties 1))))) | ||
| 1877 | 2158 | ||
| 1878 | (defun lua-search-documentation () | 2159 | (defun lua-search-documentation () |
| 1879 | "Search Lua documentation for the word at the point." | 2160 | "Search Lua documentation for the word at the point." |
diff --git a/.emacs.d/lisp/markdown-mode.el b/.emacs.d/lisp/markdown-mode.el index 2162e4f..aa27e4e 100644 --- a/.emacs.d/lisp/markdown-mode.el +++ b/.emacs.d/lisp/markdown-mode.el | |||
| @@ -188,7 +188,7 @@ line around the header title." | |||
| 188 | 188 | ||
| 189 | (defcustom markdown-indent-on-enter t | 189 | (defcustom markdown-indent-on-enter t |
| 190 | "Determines indentation behavior when pressing \\[newline]. | 190 | "Determines indentation behavior when pressing \\[newline]. |
| 191 | Possible settings are nil, t, and `indent-and-new-item'. | 191 | Possible settings are nil, t, and \\='indent-and-new-item. |
| 192 | 192 | ||
| 193 | When non-nil, pressing \\[newline] will call `newline-and-indent' | 193 | When non-nil, pressing \\[newline] will call `newline-and-indent' |
| 194 | to indent the following line according to the context using | 194 | to indent the following line according to the context using |
| @@ -196,7 +196,7 @@ to indent the following line according to the context using | |||
| 196 | \\[electric-newline-and-maybe-indent] can still be used to insert | 196 | \\[electric-newline-and-maybe-indent] can still be used to insert |
| 197 | a newline without indentation. | 197 | a newline without indentation. |
| 198 | 198 | ||
| 199 | When set to `indent-and-new-item' and the point is in a list item | 199 | When set to \\='indent-and-new-item and the point is in a list item |
| 200 | when \\[newline] is pressed, the list will be continued on the next | 200 | when \\[newline] is pressed, the list will be continued on the next |
| 201 | line, where a new item will be inserted. | 201 | line, where a new item will be inserted. |
| 202 | 202 | ||
| @@ -490,15 +490,15 @@ completion." | |||
| 490 | 490 | ||
| 491 | (defcustom markdown-split-window-direction 'any | 491 | (defcustom markdown-split-window-direction 'any |
| 492 | "Preference for splitting windows for static and live preview. | 492 | "Preference for splitting windows for static and live preview. |
| 493 | The default value is `any', which instructs Emacs to use | 493 | The default value is \\='any, which instructs Emacs to use |
| 494 | `split-window-sensibly' to automatically choose how to split | 494 | `split-window-sensibly' to automatically choose how to split |
| 495 | windows based on the values of `split-width-threshold' and | 495 | windows based on the values of `split-width-threshold' and |
| 496 | `split-height-threshold' and the available windows. To force | 496 | `split-height-threshold' and the available windows. To force |
| 497 | vertically split (left and right) windows, set this to `vertical' | 497 | vertically split (left and right) windows, set this to \\='vertical |
| 498 | or `right'. To force horizontally split (top and bottom) windows, | 498 | or \\='right. To force horizontally split (top and bottom) windows, |
| 499 | set this to `horizontal' or `below'. | 499 | set this to \\='horizontal or \\='below. |
| 500 | 500 | ||
| 501 | If this value is `any' and `display-buffer-alist' is set then | 501 | If this value is \\='any and `display-buffer-alist' is set then |
| 502 | `display-buffer' is used for open buffer function" | 502 | `display-buffer' is used for open buffer function" |
| 503 | :group 'markdown | 503 | :group 'markdown |
| 504 | :type '(choice (const :tag "Automatic" any) | 504 | :type '(choice (const :tag "Automatic" any) |
| @@ -516,8 +516,8 @@ the buffer." | |||
| 516 | 516 | ||
| 517 | (defcustom markdown-live-preview-delete-export 'delete-on-destroy | 517 | (defcustom markdown-live-preview-delete-export 'delete-on-destroy |
| 518 | "Delete exported HTML file when using `markdown-live-preview-export'. | 518 | "Delete exported HTML file when using `markdown-live-preview-export'. |
| 519 | If set to `delete-on-export', delete on every export. When set to | 519 | If set to \\='delete-on-export, delete on every export. When set to |
| 520 | `delete-on-destroy' delete when quitting from command | 520 | \\='delete-on-destroy delete when quitting from command |
| 521 | `markdown-live-preview-mode'. Never delete if set to nil." | 521 | `markdown-live-preview-mode'. Never delete if set to nil." |
| 522 | :group 'markdown | 522 | :group 'markdown |
| 523 | :type '(choice | 523 | :type '(choice |
| @@ -864,7 +864,7 @@ Group 3 matches the text.") | |||
| 864 | (defconst markdown-regex-wiki-link | 864 | (defconst markdown-regex-wiki-link |
| 865 | "\\(?:^\\|[^\\]\\)\\(?1:\\(?2:\\[\\[\\)\\(?3:[^]|]+\\)\\(?:\\(?4:|\\)\\(?5:[^]]+\\)\\)?\\(?6:\\]\\]\\)\\)" | 865 | "\\(?:^\\|[^\\]\\)\\(?1:\\(?2:\\[\\[\\)\\(?3:[^]|]+\\)\\(?:\\(?4:|\\)\\(?5:[^]]+\\)\\)?\\(?6:\\]\\]\\)\\)" |
| 866 | "Regular expression for matching wiki links. | 866 | "Regular expression for matching wiki links. |
| 867 | This matches typical bracketed [[WikiLinks]] as well as `aliased' | 867 | This matches typical bracketed [[WikiLinks]] as well as \\='aliased |
| 868 | wiki links of the form [[PageName|link text]]. | 868 | wiki links of the form [[PageName|link text]]. |
| 869 | The meanings of the first and second components depend | 869 | The meanings of the first and second components depend |
| 870 | on the value of `markdown-wiki-link-alias-first'. | 870 | on the value of `markdown-wiki-link-alias-first'. |
| @@ -1344,7 +1344,7 @@ block construct when it is matched using | |||
| 1344 | to the text matching START-REGEX-OR-FUN, END-PROPERTY to END-REGEX-OR-FUN, and | 1344 | to the text matching START-REGEX-OR-FUN, END-PROPERTY to END-REGEX-OR-FUN, and |
| 1345 | MIDDLE-PROPERTY to the text in between the two. The value of *-PROPERTY is the | 1345 | MIDDLE-PROPERTY to the text in between the two. The value of *-PROPERTY is the |
| 1346 | `match-data' when the regexp was matched to the text. In the case of | 1346 | `match-data' when the regexp was matched to the text. In the case of |
| 1347 | MIDDLE-PROPERTY, the value is a false match data of the form `(begin end)', with | 1347 | MIDDLE-PROPERTY, the value is a false match data of the form \\='(begin end), with |
| 1348 | begin and end set to the edges of the \"middle\" text. This makes fontification | 1348 | begin and end set to the edges of the \"middle\" text. This makes fontification |
| 1349 | easier.") | 1349 | easier.") |
| 1350 | 1350 | ||
| @@ -1614,7 +1614,7 @@ start which was previously propertized." | |||
| 1614 | (markdown-maybe-funcall-regexp | 1614 | (markdown-maybe-funcall-regexp |
| 1615 | (caar correct-entry)) | 1615 | (caar correct-entry)) |
| 1616 | (buffer-substring | 1616 | (buffer-substring |
| 1617 | (point-at-bol) (point-at-eol))) | 1617 | (line-beginning-position) (line-end-position))) |
| 1618 | (- (match-end 1) (match-beginning 1)))) | 1618 | (- (match-end 1) (match-beginning 1)))) |
| 1619 | (end-reg (markdown-maybe-funcall-regexp | 1619 | (end-reg (markdown-maybe-funcall-regexp |
| 1620 | (cl-caadr correct-entry) start-length))) | 1620 | (cl-caadr correct-entry) start-length))) |
| @@ -1625,7 +1625,7 @@ start which was previously propertized." | |||
| 1625 | ;; we assume the opening constructs take up (only) an entire line, | 1625 | ;; we assume the opening constructs take up (only) an entire line, |
| 1626 | ;; so we re-check the current line | 1626 | ;; so we re-check the current line |
| 1627 | (let* ((block-start (match-beginning 0)) | 1627 | (let* ((block-start (match-beginning 0)) |
| 1628 | (cur-line (buffer-substring (point-at-bol) (point-at-eol))) | 1628 | (cur-line (buffer-substring (line-beginning-position) (line-end-position))) |
| 1629 | ;; find entry in `markdown-fenced-block-pairs' corresponding | 1629 | ;; find entry in `markdown-fenced-block-pairs' corresponding |
| 1630 | ;; to regex which was matched | 1630 | ;; to regex which was matched |
| 1631 | (correct-entry | 1631 | (correct-entry |
| @@ -1636,7 +1636,7 @@ start which was previously propertized." | |||
| 1636 | cur-line)) | 1636 | cur-line)) |
| 1637 | markdown-fenced-block-pairs)) | 1637 | markdown-fenced-block-pairs)) |
| 1638 | (enclosed-text-start | 1638 | (enclosed-text-start |
| 1639 | (save-excursion (1+ (point-at-eol)))) | 1639 | (save-excursion (1+ (line-end-position)))) |
| 1640 | (end-reg | 1640 | (end-reg |
| 1641 | (markdown-maybe-funcall-regexp | 1641 | (markdown-maybe-funcall-regexp |
| 1642 | (cl-caadr correct-entry) | 1642 | (cl-caadr correct-entry) |
| @@ -1651,7 +1651,7 @@ start which was previously propertized." | |||
| 1651 | (beginning-of-line) | 1651 | (beginning-of-line) |
| 1652 | (re-search-forward | 1652 | (re-search-forward |
| 1653 | (markdown-maybe-funcall-regexp (caar correct-entry)) | 1653 | (markdown-maybe-funcall-regexp (caar correct-entry)) |
| 1654 | (point-at-eol))) | 1654 | (line-end-position))) |
| 1655 | ;; mark starting, even if ending is outside of region | 1655 | ;; mark starting, even if ending is outside of region |
| 1656 | (put-text-property (match-beginning 0) (match-end 0) prop (match-data t)) | 1656 | (put-text-property (match-beginning 0) (match-end 0) prop (match-data t)) |
| 1657 | (markdown-propertize-end-match | 1657 | (markdown-propertize-end-match |
| @@ -2563,7 +2563,7 @@ the returned list would be | |||
| 2563 | (1 14 3 5 \"- \" nil (1 6 1 4 4 5 5 6)) | 2563 | (1 14 3 5 \"- \" nil (1 6 1 4 4 5 5 6)) |
| 2564 | 2564 | ||
| 2565 | If the point is not inside a list item, return nil." | 2565 | If the point is not inside a list item, return nil." |
| 2566 | (car (get-text-property (point-at-bol) 'markdown-list-item))) | 2566 | (car (get-text-property (line-beginning-position) 'markdown-list-item))) |
| 2567 | 2567 | ||
| 2568 | (defun markdown-list-item-at-point-p () | 2568 | (defun markdown-list-item-at-point-p () |
| 2569 | "Return t if there is a list item at the point and nil otherwise." | 2569 | "Return t if there is a list item at the point and nil otherwise." |
| @@ -2647,7 +2647,7 @@ The return value has the same form as that of | |||
| 2647 | 2647 | ||
| 2648 | (defun markdown-bounds-of-thing-at-point (thing) | 2648 | (defun markdown-bounds-of-thing-at-point (thing) |
| 2649 | "Call `bounds-of-thing-at-point' for THING with slight modifications. | 2649 | "Call `bounds-of-thing-at-point' for THING with slight modifications. |
| 2650 | Does not include trailing newlines when THING is `line'. Handles the | 2650 | Does not include trailing newlines when THING is \\='line. Handles the |
| 2651 | end of buffer case by setting both endpoints equal to the value of | 2651 | end of buffer case by setting both endpoints equal to the value of |
| 2652 | `point-max', since an empty region will trigger empty markup insertion. | 2652 | `point-max', since an empty region will trigger empty markup insertion. |
| 2653 | Return bounds of form (beg . end) if THING is found, or nil otherwise." | 2653 | Return bounds of form (beg . end) if THING is found, or nil otherwise." |
| @@ -2764,7 +2764,7 @@ data. See `markdown-code-block-at-point-p' for code blocks." | |||
| 2764 | Uses text properties at the beginning of the line position. | 2764 | Uses text properties at the beginning of the line position. |
| 2765 | This includes pre blocks, tilde-fenced code blocks, and GFM | 2765 | This includes pre blocks, tilde-fenced code blocks, and GFM |
| 2766 | quoted code blocks. Return nil otherwise." | 2766 | quoted code blocks. Return nil otherwise." |
| 2767 | (let ((bol (save-excursion (goto-char pos) (point-at-bol)))) | 2767 | (let ((bol (save-excursion (goto-char pos) (line-beginning-position)))) |
| 2768 | (or (get-text-property bol 'markdown-pre) | 2768 | (or (get-text-property bol 'markdown-pre) |
| 2769 | (let* ((bounds (markdown-get-enclosing-fenced-block-construct pos)) | 2769 | (let* ((bounds (markdown-get-enclosing-fenced-block-construct pos)) |
| 2770 | (second (cl-second bounds))) | 2770 | (second (cl-second bounds))) |
| @@ -2795,7 +2795,7 @@ Set match data for `markdown-regex-header'." | |||
| 2795 | (defun markdown-pipe-at-bol-p () | 2795 | (defun markdown-pipe-at-bol-p () |
| 2796 | "Return non-nil if the line begins with a pipe symbol. | 2796 | "Return non-nil if the line begins with a pipe symbol. |
| 2797 | This may be useful for tables and Pandoc's line_blocks extension." | 2797 | This may be useful for tables and Pandoc's line_blocks extension." |
| 2798 | (char-equal (char-after (point-at-bol)) ?|)) | 2798 | (char-equal (char-after (line-beginning-position)) ?|)) |
| 2799 | 2799 | ||
| 2800 | 2800 | ||
| 2801 | ;;; Markdown Font Lock Matching Functions ===================================== | 2801 | ;;; Markdown Font Lock Matching Functions ===================================== |
| @@ -2978,7 +2978,7 @@ $..$ or `markdown-regex-math-inline-double' for matching $$..$$." | |||
| 2978 | (set-match-data (cl-seventh bounds)) | 2978 | (set-match-data (cl-seventh bounds)) |
| 2979 | ;; Step at least one character beyond point. Otherwise | 2979 | ;; Step at least one character beyond point. Otherwise |
| 2980 | ;; `font-lock-fontify-keywords-region' infloops. | 2980 | ;; `font-lock-fontify-keywords-region' infloops. |
| 2981 | (goto-char (min (1+ (max (point-at-eol) first)) | 2981 | (goto-char (min (1+ (max (line-end-position) first)) |
| 2982 | (point-max))) | 2982 | (point-max))) |
| 2983 | t))) | 2983 | t))) |
| 2984 | 2984 | ||
| @@ -3028,13 +3028,13 @@ Restore match data previously stored in PROPERTY." | |||
| 3028 | 3028 | ||
| 3029 | (defun markdown-match-pre-blocks (last) | 3029 | (defun markdown-match-pre-blocks (last) |
| 3030 | "Match preformatted blocks from point to LAST. | 3030 | "Match preformatted blocks from point to LAST. |
| 3031 | Use data stored in `markdown-pre' text property during syntax | 3031 | Use data stored in \\='markdown-pre text property during syntax |
| 3032 | analysis." | 3032 | analysis." |
| 3033 | (markdown-match-propertized-text 'markdown-pre last)) | 3033 | (markdown-match-propertized-text 'markdown-pre last)) |
| 3034 | 3034 | ||
| 3035 | (defun markdown-match-gfm-code-blocks (last) | 3035 | (defun markdown-match-gfm-code-blocks (last) |
| 3036 | "Match GFM quoted code blocks from point to LAST. | 3036 | "Match GFM quoted code blocks from point to LAST. |
| 3037 | Use data stored in `markdown-gfm-code' text property during syntax | 3037 | Use data stored in \\='markdown-gfm-code text property during syntax |
| 3038 | analysis." | 3038 | analysis." |
| 3039 | (markdown-match-propertized-text 'markdown-gfm-code last)) | 3039 | (markdown-match-propertized-text 'markdown-gfm-code last)) |
| 3040 | 3040 | ||
| @@ -3056,7 +3056,7 @@ analysis." | |||
| 3056 | 3056 | ||
| 3057 | (defun markdown-match-blockquotes (last) | 3057 | (defun markdown-match-blockquotes (last) |
| 3058 | "Match blockquotes from point to LAST. | 3058 | "Match blockquotes from point to LAST. |
| 3059 | Use data stored in `markdown-blockquote' text property during syntax | 3059 | Use data stored in \\='markdown-blockquote text property during syntax |
| 3060 | analysis." | 3060 | analysis." |
| 3061 | (markdown-match-propertized-text 'markdown-blockquote last)) | 3061 | (markdown-match-propertized-text 'markdown-blockquote last)) |
| 3062 | 3062 | ||
| @@ -3108,8 +3108,8 @@ processed elements." | |||
| 3108 | (match-beginning 0) (match-end 2) 'face prohibited-faces) | 3108 | (match-beginning 0) (match-end 2) 'face prohibited-faces) |
| 3109 | (markdown-range-property-any | 3109 | (markdown-range-property-any |
| 3110 | (match-beginning 4) (match-end 0) 'face prohibited-faces) | 3110 | (match-beginning 4) (match-end 0) 'face prohibited-faces) |
| 3111 | (and (char-equal (char-after (point-at-bol)) ?<) | 3111 | (and (char-equal (char-after (line-beginning-position)) ?<) |
| 3112 | (char-equal (char-after (1+ (point-at-bol))) ?<))) | 3112 | (char-equal (char-after (1+ (line-beginning-position))) ?<))) |
| 3113 | (set-match-data nil) | 3113 | (set-match-data nil) |
| 3114 | (setq found t)))) | 3114 | (setq found t)))) |
| 3115 | ;; Match opening exclamation point (optional) and left bracket. | 3115 | ;; Match opening exclamation point (optional) and left bracket. |
| @@ -4478,13 +4478,13 @@ code block in an indirect buffer after insertion." | |||
| 4478 | (markdown-ensure-blank-line-before) | 4478 | (markdown-ensure-blank-line-before) |
| 4479 | (indent-to indent) | 4479 | (indent-to indent) |
| 4480 | (insert "```" gfm-open-brace lang gfm-close-brace) | 4480 | (insert "```" gfm-open-brace lang gfm-close-brace) |
| 4481 | (markdown-syntax-propertize-fenced-block-constructs (point-at-bol) end)) | 4481 | (markdown-syntax-propertize-fenced-block-constructs (line-beginning-position) end)) |
| 4482 | (let ((indent (current-indentation)) | 4482 | (let ((indent (current-indentation)) |
| 4483 | start-bol) | 4483 | start-bol) |
| 4484 | (delete-horizontal-space :backward-only) | 4484 | (delete-horizontal-space :backward-only) |
| 4485 | (markdown-ensure-blank-line-before) | 4485 | (markdown-ensure-blank-line-before) |
| 4486 | (indent-to indent) | 4486 | (indent-to indent) |
| 4487 | (setq start-bol (point-at-bol)) | 4487 | (setq start-bol (line-beginning-position)) |
| 4488 | (insert "```" gfm-open-brace lang gfm-close-brace "\n") | 4488 | (insert "```" gfm-open-brace lang gfm-close-brace "\n") |
| 4489 | (indent-to indent) | 4489 | (indent-to indent) |
| 4490 | (unless edit (insert ?\n)) | 4490 | (unless edit (insert ?\n)) |
| @@ -4982,7 +4982,7 @@ If the point is at a table, move to the next row. Otherwise, | |||
| 4982 | indent according to value of `markdown-indent-on-enter'. | 4982 | indent according to value of `markdown-indent-on-enter'. |
| 4983 | When it is nil, simply call `newline'. Otherwise, indent the next line | 4983 | When it is nil, simply call `newline'. Otherwise, indent the next line |
| 4984 | following RET using `markdown-indent-line'. Furthermore, when it | 4984 | following RET using `markdown-indent-line'. Furthermore, when it |
| 4985 | is set to `indent-and-new-item' and the point is in a list item, | 4985 | is set to \\='indent-and-new-item and the point is in a list item, |
| 4986 | start a new item with the same indentation. If the point is in an | 4986 | start a new item with the same indentation. If the point is in an |
| 4987 | empty list item, remove it (so that pressing RET twice when in a | 4987 | empty list item, remove it (so that pressing RET twice when in a |
| 4988 | list simply adds a blank line)." | 4988 | list simply adds a blank line)." |
| @@ -5786,7 +5786,7 @@ See `imenu-create-index-function' and `imenu--index-alist' for details." | |||
| 5786 | ;; Headings | 5786 | ;; Headings |
| 5787 | (goto-char (point-min)) | 5787 | (goto-char (point-min)) |
| 5788 | (while (re-search-forward markdown-regex-header (point-max) t) | 5788 | (while (re-search-forward markdown-regex-header (point-max) t) |
| 5789 | (when (and (not (markdown-code-block-at-point-p (point-at-bol))) | 5789 | (when (and (not (markdown-code-block-at-point-p (line-beginning-position))) |
| 5790 | (not (markdown-text-property-at-point 'markdown-yaml-metadata-begin))) | 5790 | (not (markdown-text-property-at-point 'markdown-yaml-metadata-begin))) |
| 5791 | (cond | 5791 | (cond |
| 5792 | ((setq heading (match-string-no-properties 1)) | 5792 | ((setq heading (match-string-no-properties 1)) |
| @@ -6236,7 +6236,7 @@ increase the indentation by one level." | |||
| 6236 | ((string-match-p "[\\*\\+-]\\|#\\." marker) | 6236 | ((string-match-p "[\\*\\+-]\\|#\\." marker) |
| 6237 | (insert new-indent marker)))) | 6237 | (insert new-indent marker)))) |
| 6238 | ;; Propertize the newly inserted list item now | 6238 | ;; Propertize the newly inserted list item now |
| 6239 | (markdown-syntax-propertize-list-items (point-at-bol) (point-at-eol))))) | 6239 | (markdown-syntax-propertize-list-items (line-beginning-position) (line-end-position))))) |
| 6240 | 6240 | ||
| 6241 | (defun markdown-move-list-item-up () | 6241 | (defun markdown-move-list-item-up () |
| 6242 | "Move the current list item up in the list when possible. | 6242 | "Move the current list item up in the list when possible. |
| @@ -6486,7 +6486,7 @@ means move forward N blocks." | |||
| 6486 | (beginning-of-line) | 6486 | (beginning-of-line) |
| 6487 | ;; Skip over code block endings. | 6487 | ;; Skip over code block endings. |
| 6488 | (when (markdown-range-properties-exist | 6488 | (when (markdown-range-properties-exist |
| 6489 | (point-at-bol) (point-at-eol) | 6489 | (line-beginning-position) (line-end-position) |
| 6490 | '(markdown-gfm-block-end | 6490 | '(markdown-gfm-block-end |
| 6491 | markdown-tilde-fence-end)) | 6491 | markdown-tilde-fence-end)) |
| 6492 | (forward-line -1)) | 6492 | (forward-line -1)) |
| @@ -6515,11 +6515,11 @@ means move forward N blocks." | |||
| 6515 | (not (markdown-cur-line-blank-p)) | 6515 | (not (markdown-cur-line-blank-p)) |
| 6516 | (not (looking-at markdown-regex-blockquote)) | 6516 | (not (looking-at markdown-regex-blockquote)) |
| 6517 | (not (markdown-range-properties-exist | 6517 | (not (markdown-range-properties-exist |
| 6518 | (point-at-bol) (point-at-eol) | 6518 | (line-beginning-position) (line-end-position) |
| 6519 | '(markdown-gfm-block-end | 6519 | '(markdown-gfm-block-end |
| 6520 | markdown-tilde-fence-end)))) | 6520 | markdown-tilde-fence-end)))) |
| 6521 | (setq skip (markdown-range-properties-exist | 6521 | (setq skip (markdown-range-properties-exist |
| 6522 | (point-at-bol) (point-at-eol) | 6522 | (line-beginning-position) (line-end-position) |
| 6523 | '(markdown-gfm-block-begin | 6523 | '(markdown-gfm-block-begin |
| 6524 | markdown-tilde-fence-begin))) | 6524 | markdown-tilde-fence-begin))) |
| 6525 | (forward-line -1)) | 6525 | (forward-line -1)) |
| @@ -6566,11 +6566,11 @@ means move backward N blocks." | |||
| 6566 | (not (markdown-cur-line-blank-p)) | 6566 | (not (markdown-cur-line-blank-p)) |
| 6567 | (not (looking-at markdown-regex-blockquote)) | 6567 | (not (looking-at markdown-regex-blockquote)) |
| 6568 | (not (markdown-range-properties-exist | 6568 | (not (markdown-range-properties-exist |
| 6569 | (point-at-bol) (point-at-eol) | 6569 | (line-beginning-position) (line-end-position) |
| 6570 | '(markdown-gfm-block-begin | 6570 | '(markdown-gfm-block-begin |
| 6571 | markdown-tilde-fence-begin)))) | 6571 | markdown-tilde-fence-begin)))) |
| 6572 | (setq skip (markdown-range-properties-exist | 6572 | (setq skip (markdown-range-properties-exist |
| 6573 | (point-at-bol) (point-at-eol) | 6573 | (line-beginning-position) (line-end-position) |
| 6574 | '(markdown-gfm-block-end | 6574 | '(markdown-gfm-block-end |
| 6575 | markdown-tilde-fence-end))) | 6575 | markdown-tilde-fence-end))) |
| 6576 | (forward-line)))))))) | 6576 | (forward-line)))))))) |
| @@ -6594,7 +6594,7 @@ ARG = -N means move forward N blocks." | |||
| 6594 | (cond | 6594 | (cond |
| 6595 | ;; Code blocks | 6595 | ;; Code blocks |
| 6596 | ((and (markdown-code-block-at-pos (point)) ;; this line | 6596 | ((and (markdown-code-block-at-pos (point)) ;; this line |
| 6597 | (markdown-code-block-at-pos (point-at-bol 0))) ;; previous line | 6597 | (markdown-code-block-at-pos (line-beginning-position 0))) ;; previous line |
| 6598 | (forward-line -1) | 6598 | (forward-line -1) |
| 6599 | (while (and (markdown-code-block-at-point-p) (not (bobp))) | 6599 | (while (and (markdown-code-block-at-point-p) (not (bobp))) |
| 6600 | (forward-line -1)) | 6600 | (forward-line -1)) |
| @@ -6849,7 +6849,7 @@ Only visible heading lines are considered, unless INVISIBLE-OK is non-nil." | |||
| 6849 | 6849 | ||
| 6850 | (defun markdown-on-heading-p () | 6850 | (defun markdown-on-heading-p () |
| 6851 | "Return non-nil if point is on a heading line." | 6851 | "Return non-nil if point is on a heading line." |
| 6852 | (get-text-property (point-at-bol) 'markdown-heading)) | 6852 | (get-text-property (line-beginning-position) 'markdown-heading)) |
| 6853 | 6853 | ||
| 6854 | (defun markdown-end-of-subtree (&optional invisible-OK) | 6854 | (defun markdown-end-of-subtree (&optional invisible-OK) |
| 6855 | "Move to the end of the current subtree. | 6855 | "Move to the end of the current subtree. |
| @@ -6895,7 +6895,7 @@ setext header, but should not be folded." | |||
| 6895 | (outline-next-visible-heading 1)) | 6895 | (outline-next-visible-heading 1)) |
| 6896 | (while (< (point) (point-max)) | 6896 | (while (< (point) (point-max)) |
| 6897 | (when (markdown-code-block-at-point-p) | 6897 | (when (markdown-code-block-at-point-p) |
| 6898 | (outline-flag-region (1- (point-at-bol)) (point-at-eol) t)) | 6898 | (outline-flag-region (1- (line-beginning-position)) (line-end-position) t)) |
| 6899 | (outline-next-visible-heading 1)))) | 6899 | (outline-next-visible-heading 1)))) |
| 6900 | 6900 | ||
| 6901 | (defvar markdown-cycle-global-status 1) | 6901 | (defvar markdown-cycle-global-status 1) |
| @@ -7847,8 +7847,7 @@ Translate filenames using `markdown-filename-translate-function'." | |||
| 7847 | (title-end (match-end 7)) | 7847 | (title-end (match-end 7)) |
| 7848 | (title (match-string-no-properties 7)) | 7848 | (title (match-string-no-properties 7)) |
| 7849 | ;; Markup part | 7849 | ;; Markup part |
| 7850 | (mp (list 'face 'markdown-markup-face | 7850 | (mp (list 'invisible 'markdown-markup |
| 7851 | 'invisible 'markdown-markup | ||
| 7852 | 'rear-nonsticky t | 7851 | 'rear-nonsticky t |
| 7853 | 'font-lock-multiline t)) | 7852 | 'font-lock-multiline t)) |
| 7854 | ;; Link part (without face) | 7853 | ;; Link part (without face) |
| @@ -7858,26 +7857,28 @@ Translate filenames using `markdown-filename-translate-function'." | |||
| 7858 | 'help-echo (if title (concat title "\n" url) url))) | 7857 | 'help-echo (if title (concat title "\n" url) url))) |
| 7859 | ;; URL part | 7858 | ;; URL part |
| 7860 | (up (list 'keymap markdown-mode-mouse-map | 7859 | (up (list 'keymap markdown-mode-mouse-map |
| 7861 | 'face 'markdown-url-face | ||
| 7862 | 'invisible 'markdown-markup | 7860 | 'invisible 'markdown-markup |
| 7863 | 'mouse-face 'markdown-highlight-face | 7861 | 'mouse-face 'markdown-highlight-face |
| 7864 | 'font-lock-multiline t)) | 7862 | 'font-lock-multiline t)) |
| 7865 | ;; URL composition character | 7863 | ;; URL composition character |
| 7866 | (url-char (markdown--first-displayable markdown-url-compose-char)) | 7864 | (url-char (markdown--first-displayable markdown-url-compose-char)) |
| 7867 | ;; Title part | 7865 | ;; Title part |
| 7868 | (tp (list 'face 'markdown-link-title-face | 7866 | (tp (list 'invisible 'markdown-markup |
| 7869 | 'invisible 'markdown-markup | ||
| 7870 | 'font-lock-multiline t))) | 7867 | 'font-lock-multiline t))) |
| 7871 | (dolist (g '(1 2 4 5 8)) | 7868 | (dolist (g '(1 2 4 5 8)) |
| 7872 | (when (match-end g) | 7869 | (when (match-end g) |
| 7873 | (add-text-properties (match-beginning g) (match-end g) mp))) | 7870 | (add-text-properties (match-beginning g) (match-end g) mp) |
| 7871 | (add-face-text-property (match-beginning g) (match-end g) 'markdown-markup-face))) | ||
| 7874 | ;; Preserve existing faces applied to link part (e.g., inline code) | 7872 | ;; Preserve existing faces applied to link part (e.g., inline code) |
| 7875 | (when link-start | 7873 | (when link-start |
| 7876 | (add-text-properties link-start link-end lp) | 7874 | (add-text-properties link-start link-end lp) |
| 7877 | (add-face-text-property link-start link-end | 7875 | (add-face-text-property link-start link-end 'markdown-link-face)) |
| 7878 | 'markdown-link-face 'append)) | 7876 | (when url-start |
| 7879 | (when url-start (add-text-properties url-start url-end up)) | 7877 | (add-text-properties url-start url-end up) |
| 7880 | (when title-start (add-text-properties url-end title-end tp)) | 7878 | (add-face-text-property url-start url-end 'markdown-url-face)) |
| 7879 | (when title-start | ||
| 7880 | (add-text-properties url-end title-end tp) | ||
| 7881 | (add-face-text-property url-end title-end 'markdown-link-title-face)) | ||
| 7881 | (when (and markdown-hide-urls url-start) | 7882 | (when (and markdown-hide-urls url-start) |
| 7882 | (compose-region url-start (or title-end url-end) url-char)) | 7883 | (compose-region url-start (or title-end url-end) url-char)) |
| 7883 | t))) | 7884 | t))) |
| @@ -7890,13 +7891,11 @@ Translate filenames using `markdown-filename-translate-function'." | |||
| 7890 | (ref-start (match-beginning 6)) | 7891 | (ref-start (match-beginning 6)) |
| 7891 | (ref-end (match-end 6)) | 7892 | (ref-end (match-end 6)) |
| 7892 | ;; Markup part | 7893 | ;; Markup part |
| 7893 | (mp (list 'face 'markdown-markup-face | 7894 | (mp (list 'invisible 'markdown-markup |
| 7894 | 'invisible 'markdown-markup | ||
| 7895 | 'rear-nonsticky t | 7895 | 'rear-nonsticky t |
| 7896 | 'font-lock-multiline t)) | 7896 | 'font-lock-multiline t)) |
| 7897 | ;; Link part | 7897 | ;; Link part |
| 7898 | (lp (list 'keymap markdown-mode-mouse-map | 7898 | (lp (list 'keymap markdown-mode-mouse-map |
| 7899 | 'face 'markdown-link-face | ||
| 7900 | 'mouse-face 'markdown-highlight-face | 7899 | 'mouse-face 'markdown-highlight-face |
| 7901 | 'font-lock-multiline t | 7900 | 'font-lock-multiline t |
| 7902 | 'help-echo (lambda (_ __ pos) | 7901 | 'help-echo (lambda (_ __ pos) |
| @@ -7908,16 +7907,20 @@ Translate filenames using `markdown-filename-translate-function'." | |||
| 7908 | ;; URL composition character | 7907 | ;; URL composition character |
| 7909 | (url-char (markdown--first-displayable markdown-url-compose-char)) | 7908 | (url-char (markdown--first-displayable markdown-url-compose-char)) |
| 7910 | ;; Reference part | 7909 | ;; Reference part |
| 7911 | (rp (list 'face 'markdown-reference-face | 7910 | (rp (list 'invisible 'markdown-markup |
| 7912 | 'invisible 'markdown-markup | ||
| 7913 | 'font-lock-multiline t))) | 7911 | 'font-lock-multiline t))) |
| 7914 | (dolist (g '(1 2 4 5 8)) | 7912 | (dolist (g '(1 2 4 5 8)) |
| 7915 | (when (match-end g) | 7913 | (when (match-end g) |
| 7916 | (add-text-properties (match-beginning g) (match-end g) mp))) | 7914 | (add-text-properties (match-beginning g) (match-end g) mp) |
| 7917 | (when link-start (add-text-properties link-start link-end lp)) | 7915 | (add-face-text-property (match-beginning g) (match-end g) 'markdown-markup-face))) |
| 7918 | (when ref-start (add-text-properties ref-start ref-end rp) | 7916 | (when link-start |
| 7919 | (when (and markdown-hide-urls (> (- ref-end ref-start) 2)) | 7917 | (add-text-properties link-start link-end lp) |
| 7920 | (compose-region ref-start ref-end url-char))) | 7918 | (add-face-text-property link-start link-end 'markdown-link-face)) |
| 7919 | (when ref-start | ||
| 7920 | (add-text-properties ref-start ref-end rp) | ||
| 7921 | (add-face-text-property ref-start ref-end 'markdown-reference-face) | ||
| 7922 | (when (and markdown-hide-urls (> (- ref-end ref-start) 2)) | ||
| 7923 | (compose-region ref-start ref-end url-char))) | ||
| 7921 | t))) | 7924 | t))) |
| 7922 | 7925 | ||
| 7923 | (defun markdown-fontify-angle-uris (last) | 7926 | (defun markdown-fontify-angle-uris (last) |
| @@ -8376,7 +8379,7 @@ return the number of paragraphs left to move." | |||
| 8376 | (while (and (not (eobp)) | 8379 | (while (and (not (eobp)) |
| 8377 | (> arg 0) | 8380 | (> arg 0) |
| 8378 | (= (forward-paragraph 1) 0) | 8381 | (= (forward-paragraph 1) 0) |
| 8379 | (or (markdown-code-block-at-pos (point-at-bol 0)) | 8382 | (or (markdown-code-block-at-pos (line-beginning-position 0)) |
| 8380 | (setq arg (1- arg))))) | 8383 | (setq arg (1- arg))))) |
| 8381 | ;; Move backward by one paragraph with negative ARG (always -1). | 8384 | ;; Move backward by one paragraph with negative ARG (always -1). |
| 8382 | (let ((start (point))) | 8385 | (let ((start (point))) |
| @@ -8394,7 +8397,7 @@ return the number of paragraphs left to move." | |||
| 8394 | ((looking-at "^|\\s-*") | 8397 | ((looking-at "^|\\s-*") |
| 8395 | (goto-char (match-end 0))) | 8398 | (goto-char (match-end 0))) |
| 8396 | ;; Return point if the paragraph passed was a code block. | 8399 | ;; Return point if the paragraph passed was a code block. |
| 8397 | ((markdown-code-block-at-pos (point-at-bol 2)) | 8400 | ((markdown-code-block-at-pos (line-beginning-position 2)) |
| 8398 | (goto-char start))))) | 8401 | (goto-char start))))) |
| 8399 | arg) | 8402 | arg) |
| 8400 | 8403 | ||
| @@ -8520,7 +8523,7 @@ Returns nil if non-applicable." | |||
| 8520 | (insert markup)) | 8523 | (insert markup)) |
| 8521 | (goto-char pos) | 8524 | (goto-char pos) |
| 8522 | (insert markup)) | 8525 | (insert markup)) |
| 8523 | (syntax-propertize (point-at-eol)) | 8526 | (syntax-propertize (line-end-position)) |
| 8524 | t))))) | 8527 | t))))) |
| 8525 | 8528 | ||
| 8526 | (defun markdown-toggle-gfm-checkbox () | 8529 | (defun markdown-toggle-gfm-checkbox () |
| @@ -8755,9 +8758,9 @@ Use matching function MATCHER." | |||
| 8755 | (end (match-end 0)) | 8758 | (end (match-end 0)) |
| 8756 | ;; Find positions outside opening and closing backquotes. | 8759 | ;; Find positions outside opening and closing backquotes. |
| 8757 | (bol-prev (progn (goto-char start) | 8760 | (bol-prev (progn (goto-char start) |
| 8758 | (if (bolp) (point-at-bol 0) (point-at-bol)))) | 8761 | (if (bolp) (line-beginning-position 0) (line-beginning-position)))) |
| 8759 | (eol-next (progn (goto-char end) | 8762 | (eol-next (progn (goto-char end) |
| 8760 | (if (bolp) (point-at-bol 2) (point-at-bol 3)))) | 8763 | (if (bolp) (line-beginning-position 2) (line-beginning-position 3)))) |
| 8761 | lang) | 8764 | lang) |
| 8762 | (if (and markdown-fontify-code-blocks-natively | 8765 | (if (and markdown-fontify-code-blocks-natively |
| 8763 | (or (setq lang (markdown-code-block-lang)) | 8766 | (or (setq lang (markdown-code-block-lang)) |
| @@ -8843,8 +8846,8 @@ at the END of code blocks." | |||
| 8843 | (save-excursion | 8846 | (save-excursion |
| 8844 | (if (fboundp 'edit-indirect-region) | 8847 | (if (fboundp 'edit-indirect-region) |
| 8845 | (let* ((bounds (markdown-get-enclosing-fenced-block-construct)) | 8848 | (let* ((bounds (markdown-get-enclosing-fenced-block-construct)) |
| 8846 | (begin (and bounds (not (null (nth 0 bounds))) (goto-char (nth 0 bounds)) (point-at-bol 2))) | 8849 | (begin (and bounds (not (null (nth 0 bounds))) (goto-char (nth 0 bounds)) (line-beginning-position 2))) |
| 8847 | (end (and bounds(not (null (nth 1 bounds))) (goto-char (nth 1 bounds)) (point-at-bol 1)))) | 8850 | (end (and bounds(not (null (nth 1 bounds))) (goto-char (nth 1 bounds)) (line-beginning-position 1)))) |
| 8848 | (if (and begin end) | 8851 | (if (and begin end) |
| 8849 | (let* ((indentation (and (goto-char (nth 0 bounds)) (current-indentation))) | 8852 | (let* ((indentation (and (goto-char (nth 0 bounds)) (current-indentation))) |
| 8850 | (lang (markdown-code-block-lang)) | 8853 | (lang (markdown-code-block-lang)) |
| @@ -9049,7 +9052,7 @@ This function assumes point is on a table." | |||
| 9049 | (while (and (re-search-forward | 9052 | (while (and (re-search-forward |
| 9050 | markdown-table-dline-regexp end t) | 9053 | markdown-table-dline-regexp end t) |
| 9051 | (setq cnt (1+ cnt)) | 9054 | (setq cnt (1+ cnt)) |
| 9052 | (< (point-at-eol) pos)))) | 9055 | (< (line-end-position) pos)))) |
| 9053 | cnt)) | 9056 | cnt)) |
| 9054 | 9057 | ||
| 9055 | (defun markdown--thing-at-wiki-link (pos) | 9058 | (defun markdown--thing-at-wiki-link (pos) |
| @@ -9080,7 +9083,7 @@ a table." | |||
| 9080 | (if (looking-at "|[^|\r\n]*") | 9083 | (if (looking-at "|[^|\r\n]*") |
| 9081 | (let* ((pos (match-beginning 0)) | 9084 | (let* ((pos (match-beginning 0)) |
| 9082 | (val (buffer-substring (1+ pos) (match-end 0)))) | 9085 | (val (buffer-substring (1+ pos) (match-end 0)))) |
| 9083 | (goto-char (min (point-at-eol) (+ 2 pos))) | 9086 | (goto-char (min (line-end-position) (+ 2 pos))) |
| 9084 | ;; Trim whitespaces | 9087 | ;; Trim whitespaces |
| 9085 | (setq val (replace-regexp-in-string "\\`[ \t]+" "" val) | 9088 | (setq val (replace-regexp-in-string "\\`[ \t]+" "" val) |
| 9086 | val (replace-regexp-in-string "[ \t]+\\'" "" val))) | 9089 | val (replace-regexp-in-string "[ \t]+\\'" "" val))) |
| @@ -9105,7 +9108,7 @@ beyond the last delimiter. This function assumes point is on a | |||
| 9105 | table." | 9108 | table." |
| 9106 | (beginning-of-line 1) | 9109 | (beginning-of-line 1) |
| 9107 | (when (> n 0) | 9110 | (when (> n 0) |
| 9108 | (while (and (> n 0) (search-forward "|" (point-at-eol) t)) | 9111 | (while (and (> n 0) (search-forward "|" (line-end-position) t)) |
| 9109 | (when (and (not (looking-back "\\\\|" (line-beginning-position))) | 9112 | (when (and (not (looking-back "\\\\|" (line-beginning-position))) |
| 9110 | (not (markdown--thing-at-wiki-link (match-beginning 0)))) | 9113 | (not (markdown--thing-at-wiki-link (match-beginning 0)))) |
| 9111 | (cl-decf n))) | 9114 | (cl-decf n))) |
| @@ -9260,8 +9263,8 @@ With optional argument ARG, insert below the current row." | |||
| 9260 | (unless (markdown-table-at-point-p) | 9263 | (unless (markdown-table-at-point-p) |
| 9261 | (user-error "Not at a table")) | 9264 | (user-error "Not at a table")) |
| 9262 | (let ((col (current-column))) | 9265 | (let ((col (current-column))) |
| 9263 | (kill-region (point-at-bol) | 9266 | (kill-region (line-beginning-position) |
| 9264 | (min (1+ (point-at-eol)) (point-max))) | 9267 | (min (1+ (line-end-position)) (point-max))) |
| 9265 | (unless (markdown-table-at-point-p) (beginning-of-line 0)) | 9268 | (unless (markdown-table-at-point-p) (beginning-of-line 0)) |
| 9266 | (move-to-column col))) | 9269 | (move-to-column col))) |
| 9267 | 9270 | ||
| @@ -9277,8 +9280,8 @@ With optional argument UP, move it up." | |||
| 9277 | (unless (markdown-table-at-point-p) | 9280 | (unless (markdown-table-at-point-p) |
| 9278 | (goto-char pos) (user-error "Cannot move row further")) | 9281 | (goto-char pos) (user-error "Cannot move row further")) |
| 9279 | (goto-char pos) (beginning-of-line 1) (setq pos (point)) | 9282 | (goto-char pos) (beginning-of-line 1) (setq pos (point)) |
| 9280 | (setq txt (buffer-substring (point) (1+ (point-at-eol)))) | 9283 | (setq txt (buffer-substring (point) (1+ (line-end-position)))) |
| 9281 | (delete-region (point) (1+ (point-at-eol))) | 9284 | (delete-region (point) (1+ (line-end-position))) |
| 9282 | (beginning-of-line tonew) | 9285 | (beginning-of-line tonew) |
| 9283 | (insert txt) (beginning-of-line 0) | 9286 | (insert txt) (beginning-of-line 0) |
| 9284 | (move-to-column col))) | 9287 | (move-to-column col))) |
| @@ -9655,7 +9658,7 @@ rows and columns and the column alignment." | |||
| 9655 | (make-list columns (concat align "|"))))) | 9658 | (make-list columns (concat align "|"))))) |
| 9656 | (if (string-match | 9659 | (if (string-match |
| 9657 | "^[ \t]*$" (buffer-substring-no-properties | 9660 | "^[ \t]*$" (buffer-substring-no-properties |
| 9658 | (point-at-bol) (point))) | 9661 | (line-beginning-position) (point))) |
| 9659 | (beginning-of-line 1) | 9662 | (beginning-of-line 1) |
| 9660 | (newline)) | 9663 | (newline)) |
| 9661 | (dotimes (_ rows) (insert line)) | 9664 | (dotimes (_ rows) (insert line)) |
diff --git a/.emacs.d/lisp/rust-cargo.el b/.emacs.d/lisp/rust-cargo.el new file mode 100644 index 0000000..cfbcd81 --- /dev/null +++ b/.emacs.d/lisp/rust-cargo.el | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | ;;; rust-cargo.el --- Support for cargo -*-lexical-binding: t-*- | ||
| 2 | ;;; Commentary: | ||
| 3 | |||
| 4 | ;; This library implements support for running `cargo'. | ||
| 5 | |||
| 6 | ;;; Code: | ||
| 7 | |||
| 8 | (require 'json) | ||
| 9 | |||
| 10 | ;;; Options | ||
| 11 | |||
| 12 | (defcustom rust-cargo-bin "cargo" | ||
| 13 | "Path to cargo executable." | ||
| 14 | :type 'string | ||
| 15 | :group 'rust-mode) | ||
| 16 | |||
| 17 | (defcustom rust-always-locate-project-on-open nil | ||
| 18 | "Whether to run `cargo locate-project' every time `rust-mode' is activated." | ||
| 19 | :type 'boolean | ||
| 20 | :group 'rust-mode) | ||
| 21 | |||
| 22 | (defcustom rust-cargo-default-arguments "" | ||
| 23 | "Default arguments when running common cargo commands." | ||
| 24 | :type 'string | ||
| 25 | :group 'rust-mode) | ||
| 26 | |||
| 27 | ;;; Buffer Project | ||
| 28 | |||
| 29 | (defvar-local rust-buffer-project nil) | ||
| 30 | |||
| 31 | (defun rust-buffer-project () | ||
| 32 | "Get project root if possible." | ||
| 33 | ;; Copy environment variables into the new buffer, since | ||
| 34 | ;; with-temp-buffer will re-use the variables' defaults, even if | ||
| 35 | ;; they have been changed in this variable using e.g. envrc-mode. | ||
| 36 | ;; See https://github.com/purcell/envrc/issues/12. | ||
| 37 | (let ((env process-environment) | ||
| 38 | (path exec-path)) | ||
| 39 | (with-temp-buffer | ||
| 40 | ;; Copy the entire environment just in case there's something we | ||
| 41 | ;; don't know we need. | ||
| 42 | (setq-local process-environment env) | ||
| 43 | ;; Set PATH so we can find cargo. | ||
| 44 | (setq-local exec-path path) | ||
| 45 | (let ((ret (process-file rust-cargo-bin nil (list (current-buffer) nil) nil "locate-project" "--workspace"))) | ||
| 46 | (when (/= ret 0) | ||
| 47 | (error "`cargo locate-project' returned %s status: %s" ret (buffer-string))) | ||
| 48 | (goto-char 0) | ||
| 49 | (let ((output (json-read))) | ||
| 50 | (cdr (assoc-string "root" output))))))) | ||
| 51 | |||
| 52 | (defun rust-buffer-crate () | ||
| 53 | "Try to locate Cargo.toml using `locate-dominating-file'." | ||
| 54 | (let ((dir (locate-dominating-file default-directory "Cargo.toml"))) | ||
| 55 | (if dir dir default-directory))) | ||
| 56 | |||
| 57 | (defun rust-update-buffer-project () | ||
| 58 | (setq-local rust-buffer-project (rust-buffer-project))) | ||
| 59 | |||
| 60 | (defun rust-maybe-initialize-buffer-project () | ||
| 61 | (setq-local rust-buffer-project nil) | ||
| 62 | (when rust-always-locate-project-on-open | ||
| 63 | (rust-update-buffer-project))) | ||
| 64 | |||
| 65 | (add-hook 'rust-mode-hook #'rust-maybe-initialize-buffer-project) | ||
| 66 | |||
| 67 | ;;; Internal | ||
| 68 | |||
| 69 | (defun rust--compile (format-string &rest args) | ||
| 70 | (when (null rust-buffer-project) | ||
| 71 | (rust-update-buffer-project)) | ||
| 72 | (let ((default-directory | ||
| 73 | (or (and rust-buffer-project | ||
| 74 | (file-name-directory rust-buffer-project)) | ||
| 75 | default-directory))) | ||
| 76 | (compile (apply #'format format-string args)))) | ||
| 77 | |||
| 78 | ;;; Commands | ||
| 79 | |||
| 80 | (defun rust-check () | ||
| 81 | "Compile using `cargo check`" | ||
| 82 | (interactive) | ||
| 83 | (rust--compile "%s check %s" rust-cargo-bin rust-cargo-default-arguments)) | ||
| 84 | |||
| 85 | (defun rust-compile () | ||
| 86 | "Compile using `cargo build`" | ||
| 87 | (interactive) | ||
| 88 | (rust--compile "%s build %s" rust-cargo-bin rust-cargo-default-arguments)) | ||
| 89 | |||
| 90 | (defun rust-compile-release () | ||
| 91 | "Compile using `cargo build --release`" | ||
| 92 | (interactive) | ||
| 93 | (rust--compile "%s build --release" rust-cargo-bin)) | ||
| 94 | |||
| 95 | (defun rust-run () | ||
| 96 | "Run using `cargo run`" | ||
| 97 | (interactive) | ||
| 98 | (rust--compile "%s run %s" rust-cargo-bin rust-cargo-default-arguments)) | ||
| 99 | |||
| 100 | (defun rust-run-release () | ||
| 101 | "Run using `cargo run --release`" | ||
| 102 | (interactive) | ||
| 103 | (rust--compile "%s run --release" rust-cargo-bin)) | ||
| 104 | |||
| 105 | (defun rust-test () | ||
| 106 | "Test using `cargo test`" | ||
| 107 | (interactive) | ||
| 108 | (rust--compile "%s test %s" rust-cargo-bin rust-cargo-default-arguments)) | ||
| 109 | |||
| 110 | (defun rust-run-clippy () | ||
| 111 | "Run `cargo clippy'." | ||
| 112 | (interactive) | ||
| 113 | (when (null rust-buffer-project) | ||
| 114 | (rust-update-buffer-project)) | ||
| 115 | (let* ((args (list rust-cargo-bin "clippy" | ||
| 116 | (concat "--manifest-path=" rust-buffer-project))) | ||
| 117 | ;; set `compile-command' temporarily so `compile' doesn't | ||
| 118 | ;; clobber the existing value | ||
| 119 | (compile-command (mapconcat #'shell-quote-argument args " "))) | ||
| 120 | (rust--compile compile-command))) | ||
| 121 | |||
| 122 | ;;; _ | ||
| 123 | (provide 'rust-cargo) | ||
| 124 | ;;; rust-cargo.el ends here | ||
diff --git a/.emacs.d/lisp/rust-compile.el b/.emacs.d/lisp/rust-compile.el new file mode 100644 index 0000000..1bb3103 --- /dev/null +++ b/.emacs.d/lisp/rust-compile.el | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | ;;; rust-compile.el --- Compile facilities -*-lexical-binding: t-*- | ||
| 2 | ;;; Commentary: | ||
| 3 | |||
| 4 | ;; This library teaches `compilation-mode' about "rustc" output. | ||
| 5 | |||
| 6 | ;;; Code: | ||
| 7 | |||
| 8 | (require 'compile) | ||
| 9 | |||
| 10 | ;;; _ | ||
| 11 | |||
| 12 | (defvar rustc-compilation-location | ||
| 13 | (let ((file "\\([^\n]+\\)") | ||
| 14 | (start-line "\\([0-9]+\\)") | ||
| 15 | (start-col "\\([0-9]+\\)")) | ||
| 16 | (concat "\\(" file ":" start-line ":" start-col "\\)"))) | ||
| 17 | |||
| 18 | (defvar rustc-compilation-regexps | ||
| 19 | (let ((re (concat "^\\(?:error\\|\\(warning\\)\\|\\(note\\)\\)[^\0]+?--> " | ||
| 20 | rustc-compilation-location))) | ||
| 21 | (cons re '(4 5 6 (1 . 2) 3))) | ||
| 22 | "Specifications for matching errors in rustc invocations. | ||
| 23 | See `compilation-error-regexp-alist' for help on their format.") | ||
| 24 | |||
| 25 | (defvar rustc-colon-compilation-regexps | ||
| 26 | (let ((re (concat "^ *::: " rustc-compilation-location))) | ||
| 27 | (cons re '(2 3 4 0 1))) | ||
| 28 | "Specifications for matching `:::` hints in rustc invocations. | ||
| 29 | See `compilation-error-regexp-alist' for help on their format.") | ||
| 30 | |||
| 31 | (defvar rustc-refs-compilation-regexps | ||
| 32 | (let ((re "^\\([0-9]+\\)[[:space:]]*|")) | ||
| 33 | (cons re '(nil 1 nil 0 1))) | ||
| 34 | "Specifications for matching code references in rustc invocations. | ||
| 35 | See `compilation-error-regexp-alist' for help on their format.") | ||
| 36 | |||
| 37 | ;; Match test run failures and panics during compilation as | ||
| 38 | ;; compilation warnings | ||
| 39 | (defvar cargo-compilation-regexps | ||
| 40 | '("', \\(\\([^:]+\\):\\([0-9]+\\)\\)" | ||
| 41 | 2 3 nil nil 1) | ||
| 42 | "Specifications for matching panics in cargo test invocations. | ||
| 43 | See `compilation-error-regexp-alist' for help on their format.") | ||
| 44 | |||
| 45 | (defun rustc-scroll-down-after-next-error () | ||
| 46 | "In the new style error messages, the regular expression | ||
| 47 | matches on the file name (which appears after `-->`), but the | ||
| 48 | start of the error appears a few lines earlier. This hook runs | ||
| 49 | after `next-error' (\\[next-error]); it simply scrolls down a few lines in | ||
| 50 | the compilation window until the top of the error is visible." | ||
| 51 | (save-selected-window | ||
| 52 | (when (eq major-mode 'rust-mode) | ||
| 53 | (select-window (get-buffer-window next-error-last-buffer 'visible)) | ||
| 54 | (when (save-excursion | ||
| 55 | (beginning-of-line) | ||
| 56 | (looking-at " *-->")) | ||
| 57 | (let ((start-of-error | ||
| 58 | (save-excursion | ||
| 59 | (beginning-of-line) | ||
| 60 | (while (not (looking-at "^[a-z]+:\\|^[a-z]+\\[E[0-9]+\\]:")) | ||
| 61 | (forward-line -1)) | ||
| 62 | (point)))) | ||
| 63 | (set-window-start (selected-window) start-of-error)))))) | ||
| 64 | |||
| 65 | (eval-after-load 'compile | ||
| 66 | '(progn | ||
| 67 | (add-to-list 'compilation-error-regexp-alist-alist | ||
| 68 | (cons 'rustc-refs rustc-refs-compilation-regexps)) | ||
| 69 | (add-to-list 'compilation-error-regexp-alist 'rustc-refs) | ||
| 70 | (add-to-list 'compilation-error-regexp-alist-alist | ||
| 71 | (cons 'rustc rustc-compilation-regexps)) | ||
| 72 | (add-to-list 'compilation-error-regexp-alist 'rustc) | ||
| 73 | (add-to-list 'compilation-error-regexp-alist-alist | ||
| 74 | (cons 'rustc-colon rustc-colon-compilation-regexps)) | ||
| 75 | (add-to-list 'compilation-error-regexp-alist 'rustc-colon) | ||
| 76 | (add-to-list 'compilation-error-regexp-alist-alist | ||
| 77 | (cons 'cargo cargo-compilation-regexps)) | ||
| 78 | (add-to-list 'compilation-error-regexp-alist 'cargo) | ||
| 79 | (add-hook 'next-error-hook #'rustc-scroll-down-after-next-error))) | ||
| 80 | |||
| 81 | ;;; _ | ||
| 82 | (provide 'rust-compile) | ||
| 83 | ;;; rust-compile.el ends here | ||
diff --git a/.emacs.d/lisp/rust-mode-tests.el b/.emacs.d/lisp/rust-mode-tests.el new file mode 100644 index 0000000..e4949b2 --- /dev/null +++ b/.emacs.d/lisp/rust-mode-tests.el | |||
| @@ -0,0 +1,3637 @@ | |||
| 1 | ;;; rust-mode-tests.el --- ERT tests for rust-mode.el -*- lexical-binding: t; -*- | ||
| 2 | |||
| 3 | (require 'rust-mode) | ||
| 4 | (require 'ert) | ||
| 5 | (require 'cl-lib) | ||
| 6 | (require 'compile) | ||
| 7 | (require 'imenu) | ||
| 8 | |||
| 9 | (defconst rust-test-fill-column 32) | ||
| 10 | (setq-default indent-tabs-mode nil) | ||
| 11 | |||
| 12 | (defmacro rust-test-silence (messages &rest body) | ||
| 13 | `(let ((f (lambda (orig-fun format-string &rest args) | ||
| 14 | (unless (member format-string ,messages) | ||
| 15 | (apply orig-fun format-string args))))) | ||
| 16 | (unwind-protect | ||
| 17 | (progn | ||
| 18 | (advice-add 'message :around f) | ||
| 19 | ,@body) | ||
| 20 | (advice-remove 'message f)))) | ||
| 21 | |||
| 22 | (defun rust-compare-code-after-manip (_original _point-pos _manip-func expected got) | ||
| 23 | (equal expected got)) | ||
| 24 | |||
| 25 | (defun rust-test-explain-bad-manip (original point-pos _manip-func expected got) | ||
| 26 | (if (equal expected got) | ||
| 27 | nil | ||
| 28 | (list | ||
| 29 | ;; The (goto-char) and (insert) business here is just for | ||
| 30 | ;; convenience--after an error, you can copy-paste that into emacs eval to | ||
| 31 | ;; insert the bare strings into a buffer | ||
| 32 | "Rust code was manipulated wrong after:" | ||
| 33 | `(insert ,original) | ||
| 34 | `(goto-char ,point-pos) | ||
| 35 | 'expected `(insert ,expected) | ||
| 36 | 'got `(insert ,got) | ||
| 37 | (cl-loop for i from 0 to (max (length original) (length expected)) | ||
| 38 | for oi = (if (< i (length got)) (elt got i)) | ||
| 39 | for ei = (if (< i (length expected)) (elt expected i)) | ||
| 40 | while (equal oi ei) | ||
| 41 | finally return `(first-difference-at | ||
| 42 | (goto-char ,(+ 1 i)) | ||
| 43 | expected ,(char-to-string ei) | ||
| 44 | got ,(char-to-string oi)))))) | ||
| 45 | (put 'rust-compare-code-after-manip 'ert-explainer | ||
| 46 | 'rust-test-explain-bad-manip) | ||
| 47 | |||
| 48 | (defun rust-test-manip-code (original point-pos manip-func expected) | ||
| 49 | (with-temp-buffer | ||
| 50 | (rust-mode) | ||
| 51 | (insert original) | ||
| 52 | (goto-char point-pos) | ||
| 53 | (funcall manip-func) | ||
| 54 | (should (rust-compare-code-after-manip | ||
| 55 | original point-pos manip-func expected (buffer-string))))) | ||
| 56 | |||
| 57 | (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. | ||
| 59 | |||
| 60 | Also, the result should be the same regardless of whether the code is at the beginning or end of the file. (If you're not careful, that can make a difference.) So we test each position given above with the passed code at the beginning, the end, neither and both. So we do this a total of 1 + (end-pos - start-pos)*4 times. Oy." | ||
| 61 | (let* ((start-pos (or start-pos 1)) | ||
| 62 | (end-pos (or end-pos (length unfilled))) | ||
| 63 | (padding "\n \n") | ||
| 64 | (padding-len (length padding))) | ||
| 65 | (cl-loop | ||
| 66 | for pad-at-beginning from 0 to 1 | ||
| 67 | do (cl-loop for pad-at-end from 0 to 1 | ||
| 68 | with padding-beginning = (if (= 0 pad-at-beginning) "" padding) | ||
| 69 | with padding-end = (if (= 0 pad-at-end) "" padding) | ||
| 70 | with padding-adjust = (* padding-len pad-at-beginning) | ||
| 71 | ;; If we're adding space to the beginning, and our start position | ||
| 72 | ;; is at the very beginning, we want to test within the added space. | ||
| 73 | ;; Otherwise adjust the start and end for the beginning padding. | ||
| 74 | with start-pos = (if (= 1 start-pos) 1 (+ padding-adjust start-pos)) | ||
| 75 | with end-pos = (+ end-pos padding-adjust) | ||
| 76 | do (cl-loop for pos from start-pos to end-pos | ||
| 77 | do (rust-test-manip-code | ||
| 78 | (concat padding-beginning unfilled padding-end) | ||
| 79 | pos | ||
| 80 | (lambda () | ||
| 81 | (let ((fill-column rust-test-fill-column)) | ||
| 82 | (fill-paragraph))) | ||
| 83 | (concat padding-beginning expected padding-end))))) | ||
| 84 | ;; In addition to all the fill-paragraph tests, check that it works using fill-region | ||
| 85 | (rust-test-manip-code | ||
| 86 | unfilled | ||
| 87 | start-pos | ||
| 88 | (lambda () | ||
| 89 | (let ((fill-column rust-test-fill-column)) | ||
| 90 | (fill-region start-pos end-pos))) | ||
| 91 | expected) | ||
| 92 | )) | ||
| 93 | |||
| 94 | (ert-deftest fill-paragraph-top-level-multi-line-style-doc-comment-second-line () | ||
| 95 | (rust-test-fill-paragraph | ||
| 96 | "/** | ||
| 97 | * This is a very very very very very very very long string | ||
| 98 | */" | ||
| 99 | "/** | ||
| 100 | * This is a very very very very | ||
| 101 | * very very very long string | ||
| 102 | */")) | ||
| 103 | |||
| 104 | |||
| 105 | (ert-deftest fill-paragraph-top-level-multi-line-style-doc-comment-first-line () | ||
| 106 | (rust-test-fill-paragraph | ||
| 107 | "/** This is a very very very very very very very long string | ||
| 108 | */" | ||
| 109 | "/** This is a very very very | ||
| 110 | * very very very very long | ||
| 111 | * string | ||
| 112 | */")) | ||
| 113 | |||
| 114 | (ert-deftest fill-paragraph-multi-paragraph-multi-line-style-doc-comment () | ||
| 115 | (let | ||
| 116 | ((multi-paragraph-unfilled | ||
| 117 | "/** | ||
| 118 | * This is the first really really really really really really really long paragraph | ||
| 119 | * | ||
| 120 | * This is the second really really really really really really long paragraph | ||
| 121 | */")) | ||
| 122 | (rust-test-fill-paragraph | ||
| 123 | multi-paragraph-unfilled | ||
| 124 | "/** | ||
| 125 | * This is the first really | ||
| 126 | * really really really really | ||
| 127 | * really really long paragraph | ||
| 128 | * | ||
| 129 | * This is the second really really really really really really long paragraph | ||
| 130 | */" | ||
| 131 | 1 89) | ||
| 132 | (rust-test-fill-paragraph | ||
| 133 | multi-paragraph-unfilled | ||
| 134 | "/** | ||
| 135 | * This is the first really really really really really really really long paragraph | ||
| 136 | * | ||
| 137 | * This is the second really | ||
| 138 | * really really really really | ||
| 139 | * really long paragraph | ||
| 140 | */" | ||
| 141 | 90))) | ||
| 142 | |||
| 143 | (ert-deftest fill-paragraph-multi-paragraph-single-line-style-doc-comment () | ||
| 144 | (let | ||
| 145 | ((multi-paragraph-unfilled | ||
| 146 | "/// This is the first really really really really really really really long paragraph | ||
| 147 | /// | ||
| 148 | /// This is the second really really really really really really long paragraph")) | ||
| 149 | (rust-test-fill-paragraph | ||
| 150 | multi-paragraph-unfilled | ||
| 151 | "/// This is the first really | ||
| 152 | /// really really really really | ||
| 153 | /// really really long paragraph | ||
| 154 | /// | ||
| 155 | /// This is the second really really really really really really long paragraph" | ||
| 156 | 1 86) | ||
| 157 | (rust-test-fill-paragraph | ||
| 158 | multi-paragraph-unfilled | ||
| 159 | "/// This is the first really really really really really really really long paragraph | ||
| 160 | /// | ||
| 161 | /// This is the second really | ||
| 162 | /// really really really really | ||
| 163 | /// really long paragraph" | ||
| 164 | 87))) | ||
| 165 | |||
| 166 | (ert-deftest fill-paragraph-multi-paragraph-single-line-style-indented () | ||
| 167 | (rust-test-fill-paragraph | ||
| 168 | " // This is the first really really really really really really really long paragraph | ||
| 169 | // | ||
| 170 | // This is the second really really really really really really long paragraph" | ||
| 171 | " // This is the first really | ||
| 172 | // really really really | ||
| 173 | // really really really | ||
| 174 | // long paragraph | ||
| 175 | // | ||
| 176 | // This is the second really really really really really really long paragraph" 1 89)) | ||
| 177 | |||
| 178 | (ert-deftest fill-paragraph-multi-line-style-comment () | ||
| 179 | (rust-test-fill-paragraph | ||
| 180 | "/* This is a very very very very very very very very long string | ||
| 181 | */" | ||
| 182 | "/* This is a very very very very | ||
| 183 | * very very very very long | ||
| 184 | * string | ||
| 185 | */")) | ||
| 186 | |||
| 187 | (ert-deftest fill-paragraph-multi-line-style-inner-doc-comment () | ||
| 188 | (rust-test-fill-paragraph | ||
| 189 | "/*! This is a very very very very very very very long string | ||
| 190 | */" | ||
| 191 | "/*! This is a very very very | ||
| 192 | * very very very very long | ||
| 193 | * string | ||
| 194 | */")) | ||
| 195 | |||
| 196 | (ert-deftest fill-paragraph-single-line-style-inner-doc-comment () | ||
| 197 | (rust-test-fill-paragraph | ||
| 198 | "//! This is a very very very very very very very long string" | ||
| 199 | "//! This is a very very very | ||
| 200 | //! very very very very long | ||
| 201 | //! string")) | ||
| 202 | |||
| 203 | (ert-deftest fill-paragraph-prefixless-multi-line-doc-comment () | ||
| 204 | (rust-test-fill-paragraph | ||
| 205 | "/** | ||
| 206 | This is my summary. Blah blah blah blah blah. Dilly dally dilly dally dilly dally doo. | ||
| 207 | |||
| 208 | This is some more text. Fee fie fo fum. Humpty dumpty sat on a wall. | ||
| 209 | */" | ||
| 210 | "/** | ||
| 211 | This is my summary. Blah blah | ||
| 212 | blah blah blah. Dilly dally | ||
| 213 | dilly dally dilly dally doo. | ||
| 214 | |||
| 215 | This is some more text. Fee fie fo fum. Humpty dumpty sat on a wall. | ||
| 216 | */" 4 90)) | ||
| 217 | |||
| 218 | (ert-deftest fill-paragraph-with-no-space-after-star-prefix () | ||
| 219 | (rust-test-fill-paragraph | ||
| 220 | "/** | ||
| 221 | *This is a very very very very very very very long string | ||
| 222 | */" | ||
| 223 | "/** | ||
| 224 | *This is a very very very very | ||
| 225 | *very very very long string | ||
| 226 | */")) | ||
| 227 | |||
| 228 | (ert-deftest fill-paragraph-single-line-style-with-code-before () | ||
| 229 | (rust-test-fill-paragraph | ||
| 230 | "fn foo() { } | ||
| 231 | /// This is my comment. This is more of my comment. This is even more." | ||
| 232 | "fn foo() { } | ||
| 233 | /// This is my comment. This is | ||
| 234 | /// more of my comment. This is | ||
| 235 | /// even more." 14)) | ||
| 236 | |||
| 237 | (ert-deftest fill-paragraph-single-line-style-with-code-after () | ||
| 238 | (rust-test-fill-paragraph | ||
| 239 | "/// This is my comment. This is more of my comment. This is even more. | ||
| 240 | fn foo() { }" | ||
| 241 | "/// This is my comment. This is | ||
| 242 | /// more of my comment. This is | ||
| 243 | /// even more. | ||
| 244 | fn foo() { }" 1 73)) | ||
| 245 | |||
| 246 | (ert-deftest fill-paragraph-single-line-style-code-before-and-after () | ||
| 247 | (rust-test-fill-paragraph | ||
| 248 | "fn foo() { } | ||
| 249 | /// This is my comment. This is more of my comment. This is even more. | ||
| 250 | fn bar() { }" | ||
| 251 | "fn foo() { } | ||
| 252 | /// This is my comment. This is | ||
| 253 | /// more of my comment. This is | ||
| 254 | /// even more. | ||
| 255 | fn bar() { }" 14 85)) | ||
| 256 | |||
| 257 | (defun test-auto-fill (initial position inserted expected) | ||
| 258 | (rust-test-manip-code | ||
| 259 | initial | ||
| 260 | position | ||
| 261 | (lambda () | ||
| 262 | (unwind-protect | ||
| 263 | (progn | ||
| 264 | (let ((fill-column rust-test-fill-column)) | ||
| 265 | (auto-fill-mode) | ||
| 266 | (goto-char position) | ||
| 267 | (insert inserted) | ||
| 268 | (syntax-ppss-flush-cache 1) | ||
| 269 | (funcall auto-fill-function))) | ||
| 270 | (auto-fill-mode t))) | ||
| 271 | expected)) | ||
| 272 | |||
| 273 | (ert-deftest auto-fill-multi-line-doc-comment () | ||
| 274 | (test-auto-fill | ||
| 275 | "/** | ||
| 276 | * | ||
| 277 | */" | ||
| 278 | 7 | ||
| 279 | " This is a very very very very very very very long string" | ||
| 280 | "/** | ||
| 281 | * This is a very very very very | ||
| 282 | * very very very long string | ||
| 283 | */")) | ||
| 284 | |||
| 285 | (ert-deftest auto-fill-single-line-doc-comment () | ||
| 286 | (test-auto-fill | ||
| 287 | "/// This is the first really | ||
| 288 | /// really really really really | ||
| 289 | /// really really long paragraph | ||
| 290 | /// | ||
| 291 | /// " | ||
| 292 | 103 | ||
| 293 | "This is the second really really really really really really long paragraph" | ||
| 294 | "/// This is the first really | ||
| 295 | /// really really really really | ||
| 296 | /// really really long paragraph | ||
| 297 | /// | ||
| 298 | /// This is the second really | ||
| 299 | /// really really really really | ||
| 300 | /// really long paragraph" | ||
| 301 | )) | ||
| 302 | |||
| 303 | (ert-deftest auto-fill-multi-line-prefixless () | ||
| 304 | (test-auto-fill | ||
| 305 | "/* | ||
| 306 | |||
| 307 | */" | ||
| 308 | 4 | ||
| 309 | "This is a very very very very very very very long string" | ||
| 310 | "/* | ||
| 311 | This is a very very very very | ||
| 312 | very very very long string | ||
| 313 | */" | ||
| 314 | )) | ||
| 315 | |||
| 316 | (defun test-indent (indented &optional deindented) | ||
| 317 | (let ((deindented (or deindented (replace-regexp-in-string "^[[:blank:]]*" " " indented)))) | ||
| 318 | (rust-test-manip-code | ||
| 319 | deindented | ||
| 320 | 1 | ||
| 321 | (lambda () | ||
| 322 | (rust-test-silence | ||
| 323 | '("%s %s" ; "Indenting..." progress-reporter-do-update | ||
| 324 | "%sdone") ; "Indenting...done" progress-reporter-done | ||
| 325 | (indent-region 1 (+ 1 (buffer-size))))) | ||
| 326 | indented))) | ||
| 327 | |||
| 328 | |||
| 329 | (ert-deftest indent-struct-fields-aligned () | ||
| 330 | (test-indent | ||
| 331 | " | ||
| 332 | struct Foo { bar: i32, | ||
| 333 | baz: i32 } | ||
| 334 | |||
| 335 | struct Blah {x:i32, | ||
| 336 | y:i32, | ||
| 337 | z:String")) | ||
| 338 | |||
| 339 | (ert-deftest indent-doc-comments () | ||
| 340 | (test-indent | ||
| 341 | " | ||
| 342 | /** | ||
| 343 | * This is a doc comment | ||
| 344 | * | ||
| 345 | */ | ||
| 346 | |||
| 347 | /// So is this | ||
| 348 | |||
| 349 | fn foo() { | ||
| 350 | /*! | ||
| 351 | * this is a nested doc comment | ||
| 352 | */ | ||
| 353 | \n //! And so is this | ||
| 354 | }")) | ||
| 355 | |||
| 356 | (ert-deftest indent-inside-braces () | ||
| 357 | (test-indent | ||
| 358 | " | ||
| 359 | // struct fields out one level: | ||
| 360 | struct foo { | ||
| 361 | a:i32, | ||
| 362 | // comments too | ||
| 363 | b:char | ||
| 364 | } | ||
| 365 | |||
| 366 | fn bar(x:Box<i32>) { // comment here should not affect the next indent | ||
| 367 | bla(); | ||
| 368 | bla(); | ||
| 369 | }")) | ||
| 370 | |||
| 371 | (ert-deftest indent-top-level () | ||
| 372 | (test-indent | ||
| 373 | " | ||
| 374 | // Everything here is at the top level and should not be indented | ||
| 375 | #[attrib] | ||
| 376 | mod foo; | ||
| 377 | |||
| 378 | pub static bar = Quux{a: b()} | ||
| 379 | |||
| 380 | use foo::bar::baz; | ||
| 381 | |||
| 382 | fn foo() { } | ||
| 383 | ")) | ||
| 384 | |||
| 385 | (ert-deftest font-lock-multi-raw-strings-in-a-row () | ||
| 386 | (rust-test-font-lock | ||
| 387 | " | ||
| 388 | r\"foo\\\", \"bar\", r\"bar\"; | ||
| 389 | r\"foo\\.\", \"bar\", r\"bar\"; | ||
| 390 | r\"foo\\..\", \"bar\", r\"foo\\..\\bar\"; | ||
| 391 | r\"\\\", \"foo\", r\"\\foo\"; | ||
| 392 | not_a_string(); | ||
| 393 | |||
| 394 | " | ||
| 395 | |||
| 396 | (apply #'append (mapcar (lambda (s) (list s 'font-lock-string-face)) | ||
| 397 | '("r\"foo\\\"" "\"bar\"" "r\"bar\"" | ||
| 398 | "r\"foo\\.\"" "\"bar\"" "r\"bar\"" | ||
| 399 | "r\"foo\\..\"" "\"bar\"" "r\"foo\\..\\bar\"" | ||
| 400 | "r\"\\\"" "\"foo\"" "r\"\\foo\""))) | ||
| 401 | )) | ||
| 402 | |||
| 403 | (ert-deftest font-lock-raw-string-after-normal-string-ending-in-r () | ||
| 404 | (rust-test-font-lock | ||
| 405 | "\"bar\" r\"foo\"" | ||
| 406 | '("\"bar\"" font-lock-string-face "r\"foo\"" font-lock-string-face))) | ||
| 407 | |||
| 408 | (ert-deftest indent-params-no-align () | ||
| 409 | (test-indent | ||
| 410 | " | ||
| 411 | // Indent out one level because no params appear on the first line | ||
| 412 | fn xyzzy( | ||
| 413 | a:i32, | ||
| 414 | b:char) { } | ||
| 415 | |||
| 416 | fn abcdef( | ||
| 417 | a:i32, | ||
| 418 | b:char) | ||
| 419 | -> char | ||
| 420 | { }")) | ||
| 421 | |||
| 422 | (ert-deftest indent-params-align () | ||
| 423 | (test-indent | ||
| 424 | " | ||
| 425 | // Align the second line of params to the first | ||
| 426 | fn foo(a:i32, | ||
| 427 | b:char) { } | ||
| 428 | |||
| 429 | fn bar( a:i32, | ||
| 430 | b:char) | ||
| 431 | -> i32 | ||
| 432 | { } | ||
| 433 | |||
| 434 | fn baz( a:i32, // should work with a comment here | ||
| 435 | b:char) | ||
| 436 | -> i32 | ||
| 437 | { } | ||
| 438 | ")) | ||
| 439 | |||
| 440 | (ert-deftest indent-open-after-arrow () | ||
| 441 | (test-indent | ||
| 442 | " | ||
| 443 | // Indent function body only one level after `-> {` | ||
| 444 | fn foo1(a:i32, b:char) -> i32 { | ||
| 445 | let body; | ||
| 446 | } | ||
| 447 | |||
| 448 | fn foo2(a:i32, | ||
| 449 | b:char) -> i32 { | ||
| 450 | let body; | ||
| 451 | } | ||
| 452 | |||
| 453 | fn foo3(a:i32, | ||
| 454 | b:char) | ||
| 455 | -> i32 { | ||
| 456 | let body; | ||
| 457 | } | ||
| 458 | |||
| 459 | fn foo4(a:i32, | ||
| 460 | b:char) | ||
| 461 | -> i32 where i32:A { | ||
| 462 | let body; | ||
| 463 | } | ||
| 464 | ")) | ||
| 465 | |||
| 466 | (ert-deftest indent-return-type-non-visual () | ||
| 467 | (let ((rust-indent-return-type-to-arguments nil)) | ||
| 468 | (test-indent | ||
| 469 | " | ||
| 470 | fn imagine_long_enough_to_wrap_at_arrow(a:i32, b:char) | ||
| 471 | -> i32 | ||
| 472 | { | ||
| 473 | let body; | ||
| 474 | } | ||
| 475 | "))) | ||
| 476 | |||
| 477 | (ert-deftest indent-body-after-where () | ||
| 478 | (let ((rust-indent-where-clause t)) | ||
| 479 | (test-indent | ||
| 480 | " | ||
| 481 | fn foo1(a: A, b: B) -> A | ||
| 482 | where A: Clone + Default, B: Eq { | ||
| 483 | let body; | ||
| 484 | Foo { | ||
| 485 | bar: 3 | ||
| 486 | } | ||
| 487 | } | ||
| 488 | |||
| 489 | fn foo2(a: A, b: B) -> A | ||
| 490 | where A: Clone + Default, B: Eq | ||
| 491 | { | ||
| 492 | let body; | ||
| 493 | Foo { | ||
| 494 | bar: 3 | ||
| 495 | } | ||
| 496 | } | ||
| 497 | "))) | ||
| 498 | |||
| 499 | (ert-deftest indent-align-where-clauses-style1a () | ||
| 500 | (let ((rust-indent-where-clause t)) | ||
| 501 | (test-indent | ||
| 502 | " | ||
| 503 | fn foo1a(a: A, b: B, c: C) -> D | ||
| 504 | where A: Clone + Default, | ||
| 505 | B: Eq, | ||
| 506 | C: PartialEq, | ||
| 507 | D: PartialEq { | ||
| 508 | let body; | ||
| 509 | Foo { | ||
| 510 | bar: 3 | ||
| 511 | } | ||
| 512 | } | ||
| 513 | "))) | ||
| 514 | |||
| 515 | (ert-deftest indent-align-where-clauses-style1b () | ||
| 516 | (let ((rust-indent-where-clause t)) | ||
| 517 | (test-indent | ||
| 518 | " | ||
| 519 | fn foo1b(a: A, b: B, c: C) -> D | ||
| 520 | where A: Clone + Default, | ||
| 521 | B: Eq, | ||
| 522 | C: PartialEq, | ||
| 523 | D: PartialEq | ||
| 524 | { | ||
| 525 | let body; | ||
| 526 | Foo { | ||
| 527 | bar: 3 | ||
| 528 | } | ||
| 529 | } | ||
| 530 | "))) | ||
| 531 | |||
| 532 | (ert-deftest indent-align-where-clauses-style2a () | ||
| 533 | (test-indent | ||
| 534 | " | ||
| 535 | fn foo2a(a: A, b: B, c: C) -> D where A: Clone + Default, | ||
| 536 | B: Eq, | ||
| 537 | C: PartialEq, | ||
| 538 | D: PartialEq { | ||
| 539 | let body; | ||
| 540 | Foo { | ||
| 541 | bar: 3 | ||
| 542 | } | ||
| 543 | } | ||
| 544 | ")) | ||
| 545 | |||
| 546 | (ert-deftest indent-align-where-clauses-style2b () | ||
| 547 | (test-indent | ||
| 548 | " | ||
| 549 | fn foo2b(a: A, b: B, c: C) -> D where A: Clone + Default, | ||
| 550 | B: Eq, | ||
| 551 | C: PartialEq, | ||
| 552 | D: PartialEq | ||
| 553 | { | ||
| 554 | let body; | ||
| 555 | Foo { | ||
| 556 | bar: 3 | ||
| 557 | } | ||
| 558 | } | ||
| 559 | ")) | ||
| 560 | |||
| 561 | (ert-deftest indent-align-where-clauses-style3a () | ||
| 562 | (test-indent | ||
| 563 | " | ||
| 564 | fn foo3a(a: A, b: B, c: C) -> D where | ||
| 565 | A: Clone + Default, | ||
| 566 | B: Eq, | ||
| 567 | C: PartialEq, | ||
| 568 | D: PartialEq { | ||
| 569 | let body; | ||
| 570 | Foo { | ||
| 571 | bar: 3 | ||
| 572 | } | ||
| 573 | } | ||
| 574 | ")) | ||
| 575 | |||
| 576 | (ert-deftest indent-align-where-clauses-style3b () | ||
| 577 | (test-indent | ||
| 578 | " | ||
| 579 | fn foo3b(a: A, b: B, c: C) -> D where | ||
| 580 | A: Clone + Default, | ||
| 581 | B: Eq, | ||
| 582 | C: PartialEq, | ||
| 583 | D: PartialEq | ||
| 584 | { | ||
| 585 | let body; | ||
| 586 | Foo { | ||
| 587 | bar: 3 | ||
| 588 | } | ||
| 589 | } | ||
| 590 | ")) | ||
| 591 | |||
| 592 | (ert-deftest indent-align-where-clauses-style4a () | ||
| 593 | (let ((rust-indent-where-clause nil)) | ||
| 594 | (test-indent | ||
| 595 | " | ||
| 596 | fn foo4a(a: A, b: B, c: C) -> D | ||
| 597 | where A: Clone + Default, | ||
| 598 | B: Eq, | ||
| 599 | C: PartialEq, | ||
| 600 | D: PartialEq { | ||
| 601 | let body; | ||
| 602 | Foo { | ||
| 603 | bar: 3 | ||
| 604 | } | ||
| 605 | } | ||
| 606 | "))) | ||
| 607 | |||
| 608 | (ert-deftest indent-align-where-clauses-style4b () | ||
| 609 | (let ((rust-indent-where-clause nil)) | ||
| 610 | (test-indent | ||
| 611 | " | ||
| 612 | fn foo4b(a: A, b: B, c: C) -> D | ||
| 613 | where A: Clone + Default, | ||
| 614 | B: Eq, | ||
| 615 | C: PartialEq, | ||
| 616 | D: PartialEq | ||
| 617 | { | ||
| 618 | let body; | ||
| 619 | Foo { | ||
| 620 | bar: 3 | ||
| 621 | } | ||
| 622 | } | ||
| 623 | "))) | ||
| 624 | |||
| 625 | (ert-deftest indent-align-where-clauses-impl-example () | ||
| 626 | (let ((rust-indent-where-clause t)) | ||
| 627 | (test-indent | ||
| 628 | " | ||
| 629 | impl<'a, K, Q: ?Sized, V, S> Index<&'a Q> for HashMap<K, V, S> | ||
| 630 | where K: Eq + Hash + Borrow<Q>, | ||
| 631 | Q: Eq + Hash, | ||
| 632 | S: HashState, | ||
| 633 | { | ||
| 634 | let body; | ||
| 635 | Foo { | ||
| 636 | bar: 3 | ||
| 637 | } | ||
| 638 | } | ||
| 639 | "))) | ||
| 640 | |||
| 641 | (ert-deftest indent-align-where-clauses-first-line () | ||
| 642 | (let ((rust-indent-where-clause t)) | ||
| 643 | (test-indent | ||
| 644 | "fn foo1(a: A, b: B) -> A | ||
| 645 | where A: Clone + Default, B: Eq { | ||
| 646 | let body; | ||
| 647 | Foo { | ||
| 648 | bar: 3 | ||
| 649 | } | ||
| 650 | } | ||
| 651 | "))) | ||
| 652 | |||
| 653 | (ert-deftest indent-align-where-in-comment1 () | ||
| 654 | (test-indent | ||
| 655 | "/// - there must not exist an edge U->V in the graph where: | ||
| 656 | #[derive(Clone, PartialEq, Eq)] | ||
| 657 | pub struct Region { // <-- this should be flush with left margin! | ||
| 658 | entry: BasicBlockIndex, | ||
| 659 | leaves: BTreeMap<BasicBlockIndex, usize>, | ||
| 660 | } | ||
| 661 | ")) | ||
| 662 | |||
| 663 | (ert-deftest indent-align-where-in-comment2 () | ||
| 664 | (let ((rust-indent-where-clause t)) | ||
| 665 | (test-indent | ||
| 666 | "fn foo<F,G>(f:F, g:G) | ||
| 667 | where F:Send, | ||
| 668 | // where | ||
| 669 | G:Sized | ||
| 670 | { | ||
| 671 | let body; | ||
| 672 | } | ||
| 673 | "))) | ||
| 674 | |||
| 675 | (ert-deftest indent-align-where-in-comment3 () | ||
| 676 | (let ((rust-indent-where-clause t)) | ||
| 677 | (test-indent | ||
| 678 | "fn foo<F,G>(f:F, g:G) | ||
| 679 | where F:Send, | ||
| 680 | // where F:ThisIsNotActualCode, | ||
| 681 | G:Sized | ||
| 682 | { | ||
| 683 | let body; | ||
| 684 | } | ||
| 685 | "))) | ||
| 686 | |||
| 687 | (ert-deftest indent-square-bracket-alignment () | ||
| 688 | (test-indent | ||
| 689 | " | ||
| 690 | fn args_on_the_next_line( // with a comment | ||
| 691 | a:i32, | ||
| 692 | b:String) { | ||
| 693 | let aaaaaa = [ | ||
| 694 | 1, | ||
| 695 | 2, | ||
| 696 | 3]; | ||
| 697 | let bbbbbbb = [1, 2, 3, | ||
| 698 | 4, 5, 6]; | ||
| 699 | let ccc = [ 10, 9, 8, | ||
| 700 | 7, 6, 5]; | ||
| 701 | } | ||
| 702 | ")) | ||
| 703 | |||
| 704 | (ert-deftest indent-closing-square-bracket () | ||
| 705 | (test-indent | ||
| 706 | "fn blergh() { | ||
| 707 | let list = vec![ | ||
| 708 | 1, | ||
| 709 | 2, | ||
| 710 | 3, | ||
| 711 | ]; | ||
| 712 | }")) | ||
| 713 | |||
| 714 | (ert-deftest indent-closing-paren () | ||
| 715 | (test-indent | ||
| 716 | "fn blergh() { | ||
| 717 | call( | ||
| 718 | a, | ||
| 719 | function | ||
| 720 | ); | ||
| 721 | }")) | ||
| 722 | |||
| 723 | (ert-deftest indent-nested-fns () | ||
| 724 | (test-indent | ||
| 725 | " | ||
| 726 | fn nexted_fns(a: fn(b:i32, | ||
| 727 | c:char) | ||
| 728 | -> i32, | ||
| 729 | d: i32) | ||
| 730 | -> u128 | ||
| 731 | { | ||
| 732 | 0 | ||
| 733 | } | ||
| 734 | " | ||
| 735 | )) | ||
| 736 | |||
| 737 | (ert-deftest indent-multi-line-expr () | ||
| 738 | (test-indent | ||
| 739 | " | ||
| 740 | fn foo() | ||
| 741 | { | ||
| 742 | x(); | ||
| 743 | let a = | ||
| 744 | b(); | ||
| 745 | } | ||
| 746 | " | ||
| 747 | )) | ||
| 748 | |||
| 749 | (ert-deftest indent-match () | ||
| 750 | (test-indent | ||
| 751 | " | ||
| 752 | fn foo() { | ||
| 753 | match blah { | ||
| 754 | Pattern => stuff(), | ||
| 755 | _ => whatever | ||
| 756 | } | ||
| 757 | } | ||
| 758 | " | ||
| 759 | )) | ||
| 760 | |||
| 761 | (ert-deftest indent-match-multiline-pattern () | ||
| 762 | (test-indent | ||
| 763 | " | ||
| 764 | fn foo() { | ||
| 765 | match blah { | ||
| 766 | Pattern | | ||
| 767 | Pattern2 => { | ||
| 768 | hello() | ||
| 769 | }, | ||
| 770 | _ => whatever | ||
| 771 | } | ||
| 772 | } | ||
| 773 | " | ||
| 774 | )) | ||
| 775 | |||
| 776 | (ert-deftest indent-indented-match () | ||
| 777 | (test-indent | ||
| 778 | " | ||
| 779 | fn foo() { | ||
| 780 | let x = | ||
| 781 | match blah { | ||
| 782 | Pattern | | ||
| 783 | Pattern2 => { | ||
| 784 | hello() | ||
| 785 | }, | ||
| 786 | _ => whatever | ||
| 787 | }; | ||
| 788 | y(); | ||
| 789 | } | ||
| 790 | " | ||
| 791 | )) | ||
| 792 | |||
| 793 | (ert-deftest indent-curly-braces-within-parens () | ||
| 794 | (test-indent | ||
| 795 | " | ||
| 796 | fn foo() { | ||
| 797 | let x = | ||
| 798 | foo(bar(|x| { | ||
| 799 | only_one_indent_here(); | ||
| 800 | })); | ||
| 801 | y(); | ||
| 802 | } | ||
| 803 | " | ||
| 804 | )) | ||
| 805 | |||
| 806 | (ert-deftest indent-weirdly-indented-block () | ||
| 807 | (rust-test-manip-code | ||
| 808 | " | ||
| 809 | fn foo() { | ||
| 810 | { | ||
| 811 | this_block_is_over_to_the_left_for_some_reason(); | ||
| 812 | } | ||
| 813 | |||
| 814 | } | ||
| 815 | " | ||
| 816 | 16 | ||
| 817 | #'indent-for-tab-command | ||
| 818 | " | ||
| 819 | fn foo() { | ||
| 820 | { | ||
| 821 | this_block_is_over_to_the_left_for_some_reason(); | ||
| 822 | } | ||
| 823 | |||
| 824 | } | ||
| 825 | " | ||
| 826 | )) | ||
| 827 | |||
| 828 | (ert-deftest indent-multi-line-attrib () | ||
| 829 | (test-indent | ||
| 830 | " | ||
| 831 | #[attrib( | ||
| 832 | this, | ||
| 833 | that, | ||
| 834 | theotherthing)] | ||
| 835 | fn function_with_multiline_attribute() {} | ||
| 836 | " | ||
| 837 | )) | ||
| 838 | |||
| 839 | |||
| 840 | ;; Make sure that in effort to cover match patterns we don't mistreat || or expressions | ||
| 841 | (ert-deftest indent-nonmatch-or-expression () | ||
| 842 | (test-indent | ||
| 843 | " | ||
| 844 | fn foo() { | ||
| 845 | let x = foo() || | ||
| 846 | bar(); | ||
| 847 | } | ||
| 848 | " | ||
| 849 | )) | ||
| 850 | |||
| 851 | ;; Closing braces in single char literals and strings should not confuse the indentation | ||
| 852 | (ert-deftest indent-closing-braces-in-char-literals () | ||
| 853 | (test-indent | ||
| 854 | " | ||
| 855 | fn foo() { | ||
| 856 | { bar('}'); } | ||
| 857 | { bar(']'); } | ||
| 858 | { bar(')'); } | ||
| 859 | } | ||
| 860 | " | ||
| 861 | )) | ||
| 862 | |||
| 863 | ;; This is a test for #103: a comment after the last struct member that does | ||
| 864 | ;; not have a trailing comma. The comment used to be indented one stop too | ||
| 865 | ;; far. | ||
| 866 | (ert-deftest indent-comment-after-last-struct-member () | ||
| 867 | (test-indent | ||
| 868 | " | ||
| 869 | struct A { | ||
| 870 | x: u8 | ||
| 871 | // comment | ||
| 872 | } | ||
| 873 | |||
| 874 | struct A { | ||
| 875 | x: u8 | ||
| 876 | /* comment */ | ||
| 877 | } | ||
| 878 | " | ||
| 879 | )) | ||
| 880 | |||
| 881 | (defconst rust-test-motion-string | ||
| 882 | " | ||
| 883 | fn fn1(arg: i32) -> bool { | ||
| 884 | let x = 5; | ||
| 885 | let y = b(); | ||
| 886 | true | ||
| 887 | } | ||
| 888 | |||
| 889 | fn fn2(arg: i32) -> bool { | ||
| 890 | let x = 5; | ||
| 891 | let y = b(); | ||
| 892 | true | ||
| 893 | } | ||
| 894 | |||
| 895 | pub fn fn3(arg: i32) -> bool { | ||
| 896 | let x = 5; | ||
| 897 | let y = b(); | ||
| 898 | true | ||
| 899 | } | ||
| 900 | |||
| 901 | struct Foo { | ||
| 902 | x: i32 | ||
| 903 | } | ||
| 904 | ") | ||
| 905 | (defconst rust-test-region-string rust-test-motion-string) | ||
| 906 | (defconst rust-test-indent-motion-string | ||
| 907 | " | ||
| 908 | fn blank_line(arg:i32) -> bool { | ||
| 909 | |||
| 910 | } | ||
| 911 | |||
| 912 | fn indenting_closing_brace() { | ||
| 913 | if(true) { | ||
| 914 | } | ||
| 915 | } | ||
| 916 | |||
| 917 | fn indenting_middle_of_line() { | ||
| 918 | if(true) { | ||
| 919 | push_me_out(); | ||
| 920 | } else { | ||
| 921 | pull_me_back_in(); | ||
| 922 | } | ||
| 923 | } | ||
| 924 | |||
| 925 | fn indented_already() { | ||
| 926 | |||
| 927 | // The previous line already has its spaces | ||
| 928 | } | ||
| 929 | ") | ||
| 930 | |||
| 931 | ;; Symbol -> (line column) | ||
| 932 | (defconst rust-test-positions-alist '((start-of-fn1 (2 0)) | ||
| 933 | (start-of-fn1-middle-of-line (2 15)) | ||
| 934 | (middle-of-fn1 (3 7)) | ||
| 935 | (end-of-fn1 (6 0)) | ||
| 936 | (between-fn1-fn2 (7 0)) | ||
| 937 | (start-of-fn2 (8 0)) | ||
| 938 | (middle-of-fn2 (10 4)) | ||
| 939 | (before-start-of-fn1 (1 0)) | ||
| 940 | (after-end-of-fn2 (13 0)) | ||
| 941 | (beginning-of-fn3 (14 0)) | ||
| 942 | (middle-of-fn3 (16 4)) | ||
| 943 | (middle-of-struct (21 10)) | ||
| 944 | (before-start-of-struct (19 0)) | ||
| 945 | (after-end-of-struct (23 0)) | ||
| 946 | (blank-line-indent-start (3 0)) | ||
| 947 | (blank-line-indent-target (3 4)) | ||
| 948 | (closing-brace-indent-start (8 1)) | ||
| 949 | (closing-brace-indent-target (8 5)) | ||
| 950 | (middle-push-indent-start (13 2)) | ||
| 951 | (middle-push-indent-target (13 9)) | ||
| 952 | (after-whitespace-indent-start (13 1)) | ||
| 953 | (after-whitespace-indent-target (13 8)) | ||
| 954 | (middle-pull-indent-start (15 19)) | ||
| 955 | (middle-pull-indent-target (15 12)) | ||
| 956 | (blank-line-indented-already-bol-start (20 0)) | ||
| 957 | (blank-line-indented-already-bol-target (20 4)) | ||
| 958 | (blank-line-indented-already-middle-start (20 2)) | ||
| 959 | (blank-line-indented-already-middle-target (20 4)) | ||
| 960 | (nonblank-line-indented-already-bol-start (21 0)) | ||
| 961 | (nonblank-line-indented-already-bol-target (21 4)) | ||
| 962 | (nonblank-line-indented-already-middle-start (21 2)) | ||
| 963 | (nonblank-line-indented-already-middle-target (21 4)))) | ||
| 964 | |||
| 965 | (defun rust-get-buffer-pos (pos-symbol) | ||
| 966 | "Get buffer position from POS-SYMBOL. | ||
| 967 | |||
| 968 | POS-SYMBOL is a symbol found in `rust-test-positions-alist'. | ||
| 969 | Convert the line-column information from that list into a buffer position value." | ||
| 970 | (interactive "P") | ||
| 971 | (let* ( | ||
| 972 | (line-and-column (cadr (assoc pos-symbol rust-test-positions-alist))) | ||
| 973 | (line (nth 0 line-and-column)) | ||
| 974 | (column (nth 1 line-and-column))) | ||
| 975 | (save-excursion | ||
| 976 | (goto-char (point-min)) | ||
| 977 | (forward-line (1- line)) | ||
| 978 | (move-to-column column) | ||
| 979 | (point)))) | ||
| 980 | |||
| 981 | ;;; FIXME: Maybe add an ERT explainer function (something that shows the | ||
| 982 | ;;; surrounding code of the final point, not just the position). | ||
| 983 | (defun rust-test-motion (source-code init-pos final-pos manip-func &rest args) | ||
| 984 | "Test that MANIP-FUNC moves point from INIT-POS to FINAL-POS. | ||
| 985 | |||
| 986 | If ARGS are provided, send them to MANIP-FUNC. | ||
| 987 | |||
| 988 | INIT-POS, FINAL-POS are position symbols found in `rust-test-positions-alist'." | ||
| 989 | (with-temp-buffer | ||
| 990 | (rust-mode) | ||
| 991 | (insert source-code) | ||
| 992 | (goto-char (rust-get-buffer-pos init-pos)) | ||
| 993 | (apply manip-func args) | ||
| 994 | (should (equal (point) (rust-get-buffer-pos final-pos))))) | ||
| 995 | |||
| 996 | (defun rust-test-region (source-code init-pos reg-beg reg-end manip-func &rest args) | ||
| 997 | "Test that MANIP-FUNC marks region from REG-BEG to REG-END. | ||
| 998 | |||
| 999 | INIT-POS is the initial position of point. | ||
| 1000 | If ARGS are provided, send them to MANIP-FUNC. | ||
| 1001 | All positions are position symbols found in `rust-test-positions-alist'." | ||
| 1002 | (with-temp-buffer | ||
| 1003 | (rust-mode) | ||
| 1004 | (insert source-code) | ||
| 1005 | (goto-char (rust-get-buffer-pos init-pos)) | ||
| 1006 | (apply manip-func args) | ||
| 1007 | (should (equal (list (region-beginning) (region-end)) | ||
| 1008 | (list (rust-get-buffer-pos reg-beg) | ||
| 1009 | (rust-get-buffer-pos reg-end)))))) | ||
| 1010 | |||
| 1011 | (ert-deftest rust-beginning-of-defun-from-middle-of-fn () | ||
| 1012 | (rust-test-motion | ||
| 1013 | rust-test-motion-string | ||
| 1014 | 'middle-of-fn1 | ||
| 1015 | 'start-of-fn1 | ||
| 1016 | #'beginning-of-defun)) | ||
| 1017 | |||
| 1018 | (ert-deftest rust-beginning-of-defun-from-end () | ||
| 1019 | (rust-test-motion | ||
| 1020 | rust-test-motion-string | ||
| 1021 | 'end-of-fn1 | ||
| 1022 | 'start-of-fn1 | ||
| 1023 | #'beginning-of-defun)) | ||
| 1024 | |||
| 1025 | (ert-deftest rust-beginning-of-defun-before-open-brace () | ||
| 1026 | (rust-test-motion | ||
| 1027 | rust-test-motion-string | ||
| 1028 | 'start-of-fn1-middle-of-line | ||
| 1029 | 'start-of-fn1 | ||
| 1030 | #'beginning-of-defun)) | ||
| 1031 | |||
| 1032 | (ert-deftest rust-beginning-of-defun-between-fns () | ||
| 1033 | (rust-test-motion | ||
| 1034 | rust-test-motion-string | ||
| 1035 | 'between-fn1-fn2 | ||
| 1036 | 'start-of-fn1 | ||
| 1037 | #'beginning-of-defun)) | ||
| 1038 | |||
| 1039 | (ert-deftest rust-beginning-of-defun-with-arg () | ||
| 1040 | (rust-test-motion | ||
| 1041 | rust-test-motion-string | ||
| 1042 | 'middle-of-fn2 | ||
| 1043 | 'start-of-fn1 | ||
| 1044 | #'beginning-of-defun 2)) | ||
| 1045 | |||
| 1046 | (ert-deftest rust-beginning-of-defun-with-negative-arg () | ||
| 1047 | (rust-test-motion | ||
| 1048 | rust-test-motion-string | ||
| 1049 | 'middle-of-fn1 | ||
| 1050 | 'beginning-of-fn3 | ||
| 1051 | #'beginning-of-defun -2)) | ||
| 1052 | |||
| 1053 | (ert-deftest rust-beginning-of-defun-pub-fn () | ||
| 1054 | (rust-test-motion | ||
| 1055 | rust-test-motion-string | ||
| 1056 | 'middle-of-fn3 | ||
| 1057 | 'beginning-of-fn3 | ||
| 1058 | #'beginning-of-defun)) | ||
| 1059 | |||
| 1060 | (ert-deftest rust-beginning-of-defun-string-comment () | ||
| 1061 | (let (fn-1 fn-2 p-1 p-2) | ||
| 1062 | (with-temp-buffer | ||
| 1063 | (rust-mode) | ||
| 1064 | (insert "fn test1() { | ||
| 1065 | let s=r#\" | ||
| 1066 | fn test2(); | ||
| 1067 | \"#;") | ||
| 1068 | (setq p-1 (point)) | ||
| 1069 | (setq fn-1 (1+ p-1)) | ||
| 1070 | (insert " | ||
| 1071 | fn test3() { | ||
| 1072 | /* | ||
| 1073 | fn test4();") | ||
| 1074 | (setq p-2 (point)) | ||
| 1075 | (insert "\n*/\n}\n") | ||
| 1076 | (setq fn-2 (point)) | ||
| 1077 | (insert "fn test5() { }") | ||
| 1078 | |||
| 1079 | (goto-char p-1) | ||
| 1080 | (beginning-of-defun) | ||
| 1081 | (should (eq (point) (point-min))) | ||
| 1082 | |||
| 1083 | (beginning-of-defun -2) | ||
| 1084 | (should (eq (point) fn-2)) | ||
| 1085 | |||
| 1086 | (goto-char p-2) | ||
| 1087 | (beginning-of-defun) | ||
| 1088 | (should (eq (point) fn-1)) | ||
| 1089 | |||
| 1090 | (beginning-of-defun -1) | ||
| 1091 | (should (eq (point) fn-2)) | ||
| 1092 | |||
| 1093 | (goto-char (point-max)) | ||
| 1094 | (beginning-of-defun 2) | ||
| 1095 | (should (eq (point) fn-1))))) | ||
| 1096 | |||
| 1097 | (ert-deftest rust-end-of-defun-from-middle-of-fn () | ||
| 1098 | (rust-test-motion | ||
| 1099 | rust-test-motion-string | ||
| 1100 | 'middle-of-fn1 | ||
| 1101 | 'between-fn1-fn2 | ||
| 1102 | #'end-of-defun)) | ||
| 1103 | |||
| 1104 | (ert-deftest rust-end-of-defun-from-beg () | ||
| 1105 | (rust-test-motion | ||
| 1106 | rust-test-motion-string | ||
| 1107 | 'start-of-fn1 | ||
| 1108 | 'between-fn1-fn2 | ||
| 1109 | #'end-of-defun)) | ||
| 1110 | |||
| 1111 | (ert-deftest rust-end-of-defun-before-open-brace () | ||
| 1112 | (rust-test-motion | ||
| 1113 | rust-test-motion-string | ||
| 1114 | 'start-of-fn1-middle-of-line | ||
| 1115 | 'between-fn1-fn2 | ||
| 1116 | #'end-of-defun)) | ||
| 1117 | |||
| 1118 | (ert-deftest rust-end-of-defun-between-fns () | ||
| 1119 | (rust-test-motion | ||
| 1120 | rust-test-motion-string | ||
| 1121 | 'between-fn1-fn2 | ||
| 1122 | 'after-end-of-fn2 | ||
| 1123 | #'end-of-defun)) | ||
| 1124 | |||
| 1125 | (ert-deftest rust-end-of-defun-with-arg () | ||
| 1126 | (rust-test-motion | ||
| 1127 | rust-test-motion-string | ||
| 1128 | 'middle-of-fn1 | ||
| 1129 | 'after-end-of-fn2 | ||
| 1130 | #'end-of-defun 2)) | ||
| 1131 | |||
| 1132 | (ert-deftest rust-end-of-defun-with-negative-arg () | ||
| 1133 | (rust-test-motion | ||
| 1134 | rust-test-motion-string | ||
| 1135 | 'middle-of-fn3 | ||
| 1136 | 'between-fn1-fn2 | ||
| 1137 | #'end-of-defun -2)) | ||
| 1138 | |||
| 1139 | (ert-deftest rust-mark-defun-from-middle-of-fn () | ||
| 1140 | (rust-test-region | ||
| 1141 | rust-test-region-string | ||
| 1142 | 'middle-of-fn2 | ||
| 1143 | 'between-fn1-fn2 'after-end-of-fn2 | ||
| 1144 | #'mark-defun)) | ||
| 1145 | |||
| 1146 | (ert-deftest rust-mark-defun-from-end () | ||
| 1147 | (rust-test-region | ||
| 1148 | rust-test-region-string | ||
| 1149 | 'end-of-fn1 | ||
| 1150 | 'before-start-of-fn1 'between-fn1-fn2 | ||
| 1151 | #'mark-defun)) | ||
| 1152 | |||
| 1153 | (ert-deftest rust-mark-defun-start-of-defun () | ||
| 1154 | (rust-test-region | ||
| 1155 | rust-test-region-string | ||
| 1156 | 'start-of-fn2 | ||
| 1157 | 'between-fn1-fn2 'after-end-of-fn2 | ||
| 1158 | #'mark-defun)) | ||
| 1159 | |||
| 1160 | (ert-deftest rust-mark-defun-from-middle-of-struct () | ||
| 1161 | (rust-test-region | ||
| 1162 | rust-test-region-string | ||
| 1163 | 'middle-of-struct | ||
| 1164 | 'before-start-of-struct 'after-end-of-struct | ||
| 1165 | #'mark-defun)) | ||
| 1166 | |||
| 1167 | (ert-deftest indent-line-blank-line-motion () | ||
| 1168 | (rust-test-motion | ||
| 1169 | rust-test-indent-motion-string | ||
| 1170 | 'blank-line-indent-start | ||
| 1171 | 'blank-line-indent-target | ||
| 1172 | #'indent-for-tab-command)) | ||
| 1173 | |||
| 1174 | (ert-deftest indent-line-closing-brace-motion () | ||
| 1175 | (rust-test-motion | ||
| 1176 | rust-test-indent-motion-string | ||
| 1177 | 'closing-brace-indent-start | ||
| 1178 | 'closing-brace-indent-target | ||
| 1179 | #'indent-for-tab-command)) | ||
| 1180 | |||
| 1181 | (ert-deftest indent-line-middle-push-motion () | ||
| 1182 | (rust-test-motion | ||
| 1183 | rust-test-indent-motion-string | ||
| 1184 | 'middle-push-indent-start | ||
| 1185 | 'middle-push-indent-target | ||
| 1186 | #'indent-for-tab-command)) | ||
| 1187 | |||
| 1188 | (ert-deftest indent-line-after-whitespace-motion () | ||
| 1189 | (rust-test-motion | ||
| 1190 | rust-test-indent-motion-string | ||
| 1191 | 'after-whitespace-indent-start | ||
| 1192 | 'after-whitespace-indent-target | ||
| 1193 | #'indent-for-tab-command)) | ||
| 1194 | |||
| 1195 | (ert-deftest indent-line-middle-pull-motion () | ||
| 1196 | (rust-test-motion | ||
| 1197 | rust-test-indent-motion-string | ||
| 1198 | 'middle-pull-indent-start | ||
| 1199 | 'middle-pull-indent-target | ||
| 1200 | #'indent-for-tab-command)) | ||
| 1201 | |||
| 1202 | (ert-deftest indent-line-blank-line-indented-already-bol () | ||
| 1203 | (rust-test-motion | ||
| 1204 | rust-test-indent-motion-string | ||
| 1205 | 'blank-line-indented-already-bol-start | ||
| 1206 | 'blank-line-indented-already-bol-target | ||
| 1207 | #'indent-for-tab-command)) | ||
| 1208 | |||
| 1209 | (ert-deftest indent-line-blank-line-indented-already-middle () | ||
| 1210 | (rust-test-motion | ||
| 1211 | rust-test-indent-motion-string | ||
| 1212 | 'blank-line-indented-already-middle-start | ||
| 1213 | 'blank-line-indented-already-middle-target | ||
| 1214 | #'indent-for-tab-command)) | ||
| 1215 | |||
| 1216 | (ert-deftest indent-line-nonblank-line-indented-already-bol () | ||
| 1217 | (rust-test-motion | ||
| 1218 | rust-test-indent-motion-string | ||
| 1219 | 'nonblank-line-indented-already-bol-start | ||
| 1220 | 'nonblank-line-indented-already-bol-target | ||
| 1221 | #'indent-for-tab-command)) | ||
| 1222 | |||
| 1223 | (ert-deftest indent-line-nonblank-line-indented-already-middle () | ||
| 1224 | (rust-test-motion | ||
| 1225 | rust-test-indent-motion-string | ||
| 1226 | 'nonblank-line-indented-already-middle-start | ||
| 1227 | 'nonblank-line-indented-already-middle-target | ||
| 1228 | #'indent-for-tab-command)) | ||
| 1229 | |||
| 1230 | (ert-deftest no-stack-overflow-in-rust-rewind-irrelevant () | ||
| 1231 | (with-temp-buffer | ||
| 1232 | (rust-mode) | ||
| 1233 | (insert "fn main() {\n let x = 1;") | ||
| 1234 | ;; Insert 150 separate comments on the same line | ||
| 1235 | (dotimes (_i 150) | ||
| 1236 | (insert "/* foo */ ")) | ||
| 1237 | ;; Rewinding from the last comment to the end of the let needs at least | ||
| 1238 | ;; 150 iterations, but if we limit the stack depth to 100 (this appears to | ||
| 1239 | ;; be some minimum), a recursive function would overflow, throwing an | ||
| 1240 | ;; error. | ||
| 1241 | (let ((max-lisp-eval-depth 100)) | ||
| 1242 | (rust-rewind-irrelevant) | ||
| 1243 | ;; Only a non-stack overflowing function would make it this far. Also | ||
| 1244 | ;; check that we rewound till after the ; | ||
| 1245 | (should (= (char-before) ?\;))))) | ||
| 1246 | |||
| 1247 | (defun rust-test-fontify-string (str) | ||
| 1248 | (with-temp-buffer | ||
| 1249 | (rust-mode) | ||
| 1250 | (insert str) | ||
| 1251 | (font-lock-flush) | ||
| 1252 | (font-lock-ensure) | ||
| 1253 | (buffer-string))) | ||
| 1254 | |||
| 1255 | (defun rust-test-group-str-by-face (str) | ||
| 1256 | "Fontify `STR' in rust-mode and group it by face, returning a | ||
| 1257 | list of substrings of `STR' each followed by its face." | ||
| 1258 | (cl-loop with fontified = (rust-test-fontify-string str) | ||
| 1259 | for start = 0 then end | ||
| 1260 | while start | ||
| 1261 | for end = (next-single-property-change start 'face fontified) | ||
| 1262 | for prop = (get-text-property start 'face fontified) | ||
| 1263 | for text = (substring-no-properties fontified start end) | ||
| 1264 | if prop | ||
| 1265 | append (list text prop))) | ||
| 1266 | |||
| 1267 | (defun rust-test-font-lock (source face-groups) | ||
| 1268 | "Test that `SOURCE' fontifies to the expected `FACE-GROUPS'" | ||
| 1269 | (should (equal (rust-test-group-str-by-face source) | ||
| 1270 | face-groups))) | ||
| 1271 | |||
| 1272 | (ert-deftest font-lock-attribute-simple () | ||
| 1273 | (rust-test-font-lock | ||
| 1274 | "#[foo]" | ||
| 1275 | '("#[foo]" font-lock-preprocessor-face))) | ||
| 1276 | |||
| 1277 | (ert-deftest font-lock-attribute-inner () | ||
| 1278 | (rust-test-font-lock | ||
| 1279 | "#![foo]" | ||
| 1280 | '("#![foo]" font-lock-preprocessor-face))) | ||
| 1281 | |||
| 1282 | (ert-deftest font-lock-attribute-key-value () | ||
| 1283 | (rust-test-font-lock | ||
| 1284 | "#[foo = \"bar\"]" | ||
| 1285 | '("#[foo = " font-lock-preprocessor-face | ||
| 1286 | "\"bar\"" font-lock-string-face | ||
| 1287 | "]" font-lock-preprocessor-face))) | ||
| 1288 | |||
| 1289 | (ert-deftest font-lock-attribute-around-comment () | ||
| 1290 | (rust-test-font-lock | ||
| 1291 | "#[foo /* bar */]" | ||
| 1292 | '("#[foo " font-lock-preprocessor-face | ||
| 1293 | "/* " font-lock-comment-delimiter-face | ||
| 1294 | "bar */" font-lock-comment-face | ||
| 1295 | "]" font-lock-preprocessor-face))) | ||
| 1296 | |||
| 1297 | (ert-deftest font-lock-attribute-inside-string () | ||
| 1298 | (rust-test-font-lock | ||
| 1299 | "\"#[foo]\"" | ||
| 1300 | '("\"#[foo]\"" font-lock-string-face))) | ||
| 1301 | |||
| 1302 | (ert-deftest font-lock-attribute-inside-comment () | ||
| 1303 | (rust-test-font-lock | ||
| 1304 | "/* #[foo] */" | ||
| 1305 | '("/* " font-lock-comment-delimiter-face | ||
| 1306 | "#[foo] */" font-lock-comment-face))) | ||
| 1307 | |||
| 1308 | (ert-deftest font-lock-number-with-type () | ||
| 1309 | (rust-test-font-lock | ||
| 1310 | "-123i32" | ||
| 1311 | '("i32" font-lock-type-face)) | ||
| 1312 | (rust-test-font-lock | ||
| 1313 | "123u32" | ||
| 1314 | '("u32" font-lock-type-face)) | ||
| 1315 | (rust-test-font-lock | ||
| 1316 | "123_123_u32" | ||
| 1317 | '("u32" font-lock-type-face)) | ||
| 1318 | (rust-test-font-lock | ||
| 1319 | "0xff_u8" | ||
| 1320 | '("u8" font-lock-type-face)) | ||
| 1321 | (rust-test-font-lock | ||
| 1322 | "0b1111_1111_1001_0000i64" | ||
| 1323 | '("i64" font-lock-type-face)) | ||
| 1324 | (rust-test-font-lock | ||
| 1325 | "0usize" | ||
| 1326 | '("usize" font-lock-type-face)) | ||
| 1327 | (rust-test-font-lock | ||
| 1328 | "123.0f64 + 1." | ||
| 1329 | '("f64" font-lock-type-face)) | ||
| 1330 | (rust-test-font-lock | ||
| 1331 | "0.1f32" | ||
| 1332 | '("f32" font-lock-type-face)) | ||
| 1333 | (rust-test-font-lock | ||
| 1334 | "12E+99_f64" | ||
| 1335 | '("f64" font-lock-type-face)) | ||
| 1336 | (rust-test-font-lock | ||
| 1337 | "5f32" | ||
| 1338 | '("f32" font-lock-type-face)) | ||
| 1339 | (rust-test-font-lock | ||
| 1340 | "0x5i32" | ||
| 1341 | '("i32" font-lock-type-face)) | ||
| 1342 | (rust-test-font-lock | ||
| 1343 | "1x5i32" | ||
| 1344 | '()) | ||
| 1345 | (rust-test-font-lock | ||
| 1346 | "0x5i321" | ||
| 1347 | '()) | ||
| 1348 | (rust-test-font-lock | ||
| 1349 | "fname5f32" | ||
| 1350 | '()) | ||
| 1351 | (rust-test-font-lock | ||
| 1352 | "0x5i32+1" | ||
| 1353 | '("i32" font-lock-type-face)) | ||
| 1354 | (rust-test-font-lock | ||
| 1355 | "f(0xFFi32)" | ||
| 1356 | '("i32" font-lock-type-face))) | ||
| 1357 | |||
| 1358 | (ert-deftest font-lock-double-quote-character-literal () | ||
| 1359 | (rust-test-font-lock | ||
| 1360 | "'\"'; let" | ||
| 1361 | '("'\"'" font-lock-string-face | ||
| 1362 | "let" font-lock-keyword-face))) | ||
| 1363 | |||
| 1364 | (ert-deftest font-lock-fn-contains-capital () | ||
| 1365 | (rust-test-font-lock | ||
| 1366 | "fn foo_Bar() {}" | ||
| 1367 | '("fn" font-lock-keyword-face | ||
| 1368 | "foo_Bar" font-lock-function-name-face))) | ||
| 1369 | |||
| 1370 | (ert-deftest font-lock-let-bindings () | ||
| 1371 | (rust-test-font-lock | ||
| 1372 | "let foo;" | ||
| 1373 | '("let" font-lock-keyword-face | ||
| 1374 | "foo" font-lock-variable-name-face)) | ||
| 1375 | (rust-test-font-lock | ||
| 1376 | "let ref foo;" | ||
| 1377 | '("let" font-lock-keyword-face | ||
| 1378 | "ref" font-lock-keyword-face | ||
| 1379 | "foo" font-lock-variable-name-face)) | ||
| 1380 | (rust-test-font-lock | ||
| 1381 | "let mut foo;" | ||
| 1382 | '("let" font-lock-keyword-face | ||
| 1383 | "mut" font-lock-keyword-face | ||
| 1384 | "foo" font-lock-variable-name-face)) | ||
| 1385 | (rust-test-font-lock | ||
| 1386 | "let foo = 1;" | ||
| 1387 | '("let" font-lock-keyword-face | ||
| 1388 | "foo" font-lock-variable-name-face)) | ||
| 1389 | (rust-test-font-lock | ||
| 1390 | "let mut foo = 1;" | ||
| 1391 | '("let" font-lock-keyword-face | ||
| 1392 | "mut" font-lock-keyword-face | ||
| 1393 | "foo" font-lock-variable-name-face)) | ||
| 1394 | (rust-test-font-lock | ||
| 1395 | "fn foo() { let bar = 1; }" | ||
| 1396 | '("fn" font-lock-keyword-face | ||
| 1397 | "foo" font-lock-function-name-face | ||
| 1398 | "let" font-lock-keyword-face | ||
| 1399 | "bar" font-lock-variable-name-face)) | ||
| 1400 | (rust-test-font-lock | ||
| 1401 | "fn foo() { let mut bar = 1; }" | ||
| 1402 | '("fn" font-lock-keyword-face | ||
| 1403 | "foo" font-lock-function-name-face | ||
| 1404 | "let" font-lock-keyword-face | ||
| 1405 | "mut" font-lock-keyword-face | ||
| 1406 | "bar" font-lock-variable-name-face))) | ||
| 1407 | |||
| 1408 | (ert-deftest font-lock-ampersand () | ||
| 1409 | (rust-test-font-lock | ||
| 1410 | "f(&a)" | ||
| 1411 | '("&" rust-ampersand-face)) | ||
| 1412 | (rust-test-font-lock | ||
| 1413 | "a && b &&& c" | ||
| 1414 | nil) | ||
| 1415 | (rust-test-font-lock | ||
| 1416 | "&'a v" | ||
| 1417 | '("&" rust-ampersand-face | ||
| 1418 | "a" font-lock-variable-name-face)) | ||
| 1419 | (rust-test-font-lock | ||
| 1420 | "&'static v" | ||
| 1421 | '("&" rust-ampersand-face | ||
| 1422 | "static" font-lock-keyword-face)) | ||
| 1423 | (rust-test-font-lock | ||
| 1424 | "&mut v" | ||
| 1425 | '("&" rust-ampersand-face | ||
| 1426 | "mut" font-lock-keyword-face)) | ||
| 1427 | (rust-test-font-lock | ||
| 1428 | "&f(&x)" | ||
| 1429 | '("&" rust-ampersand-face | ||
| 1430 | "&" rust-ampersand-face)) | ||
| 1431 | (rust-test-font-lock | ||
| 1432 | "fn f(x: &X)" | ||
| 1433 | '("fn" font-lock-keyword-face | ||
| 1434 | "f" font-lock-function-name-face | ||
| 1435 | "x" font-lock-variable-name-face | ||
| 1436 | "&" rust-ampersand-face | ||
| 1437 | "X" font-lock-type-face)) | ||
| 1438 | (rust-test-font-lock | ||
| 1439 | "f(&X{x})" | ||
| 1440 | '("&" rust-ampersand-face | ||
| 1441 | "X" font-lock-type-face)) | ||
| 1442 | (rust-test-font-lock | ||
| 1443 | "let x: &'_ f64 = &1.;" | ||
| 1444 | '("let" font-lock-keyword-face | ||
| 1445 | "x" font-lock-variable-name-face | ||
| 1446 | "&" rust-ampersand-face | ||
| 1447 | "_" font-lock-variable-name-face | ||
| 1448 | "f64" font-lock-type-face | ||
| 1449 | "&" rust-ampersand-face)) | ||
| 1450 | (rust-test-font-lock | ||
| 1451 | "let x = &&1;" | ||
| 1452 | '("let" font-lock-keyword-face | ||
| 1453 | "x" font-lock-variable-name-face | ||
| 1454 | "&&" rust-ampersand-face)) | ||
| 1455 | (rust-test-font-lock | ||
| 1456 | "let x = &*y;" | ||
| 1457 | '("let" font-lock-keyword-face | ||
| 1458 | "x" font-lock-variable-name-face | ||
| 1459 | "&" rust-ampersand-face)) | ||
| 1460 | (rust-test-font-lock | ||
| 1461 | "let x = &::std::f64::consts::PI;" | ||
| 1462 | '("let" font-lock-keyword-face | ||
| 1463 | "x" font-lock-variable-name-face | ||
| 1464 | "&" rust-ampersand-face | ||
| 1465 | "std" font-lock-constant-face | ||
| 1466 | "f64" font-lock-type-face | ||
| 1467 | "consts" font-lock-constant-face | ||
| 1468 | "PI" font-lock-type-face)) | ||
| 1469 | (rust-test-font-lock | ||
| 1470 | "let x = &(1, 2);" | ||
| 1471 | '("let" font-lock-keyword-face | ||
| 1472 | "x" font-lock-variable-name-face | ||
| 1473 | "&" rust-ampersand-face)) | ||
| 1474 | (rust-test-font-lock | ||
| 1475 | "let x = &{1};" | ||
| 1476 | '("let" font-lock-keyword-face | ||
| 1477 | "x" font-lock-variable-name-face | ||
| 1478 | "&" rust-ampersand-face)) | ||
| 1479 | (rust-test-font-lock | ||
| 1480 | "let f = &|x| {x + 1};" | ||
| 1481 | '("let" font-lock-keyword-face | ||
| 1482 | "f" font-lock-variable-name-face | ||
| 1483 | "&" rust-ampersand-face)) | ||
| 1484 | (rust-test-font-lock | ||
| 1485 | "let x: &_ = &1;" | ||
| 1486 | '("let" font-lock-keyword-face | ||
| 1487 | "x" font-lock-variable-name-face | ||
| 1488 | "&" rust-ampersand-face | ||
| 1489 | "&" rust-ampersand-face)) | ||
| 1490 | (rust-test-font-lock | ||
| 1491 | "&[1,2]" | ||
| 1492 | '("&" rust-ampersand-face))) | ||
| 1493 | |||
| 1494 | (ert-deftest font-lock-if-let-binding () | ||
| 1495 | (rust-test-font-lock | ||
| 1496 | "if let Some(var) = some_var { /* no-op */ }" | ||
| 1497 | '("if" font-lock-keyword-face | ||
| 1498 | "let" font-lock-keyword-face | ||
| 1499 | "Some" font-lock-type-face | ||
| 1500 | "/* " font-lock-comment-delimiter-face | ||
| 1501 | "no-op */" font-lock-comment-face))) | ||
| 1502 | |||
| 1503 | (ert-deftest font-lock-single-quote-character-literal () | ||
| 1504 | (rust-test-font-lock | ||
| 1505 | "fn main() { let ch = '\\''; }" | ||
| 1506 | '("fn" font-lock-keyword-face | ||
| 1507 | "main" font-lock-function-name-face | ||
| 1508 | "let" font-lock-keyword-face | ||
| 1509 | "ch" font-lock-variable-name-face | ||
| 1510 | "'\\''" font-lock-string-face))) | ||
| 1511 | |||
| 1512 | (ert-deftest font-lock-escaped-double-quote-character-literal () | ||
| 1513 | (rust-test-font-lock | ||
| 1514 | "fn main() { let ch = '\\\"'; }" | ||
| 1515 | '("fn" font-lock-keyword-face | ||
| 1516 | "main" font-lock-function-name-face | ||
| 1517 | "let" font-lock-keyword-face | ||
| 1518 | "ch" font-lock-variable-name-face | ||
| 1519 | "'\\\"'" font-lock-string-face))) | ||
| 1520 | |||
| 1521 | (ert-deftest font-lock-escaped-backslash-character-literal () | ||
| 1522 | (rust-test-font-lock | ||
| 1523 | "fn main() { let ch = '\\\\'; }" | ||
| 1524 | '("fn" font-lock-keyword-face | ||
| 1525 | "main" font-lock-function-name-face | ||
| 1526 | "let" font-lock-keyword-face | ||
| 1527 | "ch" font-lock-variable-name-face | ||
| 1528 | "'\\\\'" font-lock-string-face))) | ||
| 1529 | |||
| 1530 | (ert-deftest font-lock-hex-escape-character-literal () | ||
| 1531 | (rust-test-font-lock | ||
| 1532 | "let ch = '\\x1f';" | ||
| 1533 | '("let" font-lock-keyword-face | ||
| 1534 | "ch" font-lock-variable-name-face | ||
| 1535 | "'\\x1f'" font-lock-string-face))) | ||
| 1536 | |||
| 1537 | (ert-deftest font-lock-unicode-escape-character-literal () | ||
| 1538 | (rust-test-font-lock | ||
| 1539 | "let ch = '\\u{1ffff}';" | ||
| 1540 | '("let" font-lock-keyword-face | ||
| 1541 | "ch" font-lock-variable-name-face | ||
| 1542 | "'\\u{1ffff}'" font-lock-string-face))) | ||
| 1543 | |||
| 1544 | (ert-deftest font-lock-raw-strings-no-hashes () | ||
| 1545 | (rust-test-font-lock | ||
| 1546 | "r\"No hashes\";" | ||
| 1547 | '("r\"No hashes\"" font-lock-string-face))) | ||
| 1548 | |||
| 1549 | (ert-deftest font-lock-raw-strings-double-quote () | ||
| 1550 | (rust-test-font-lock | ||
| 1551 | "fn main() { | ||
| 1552 | r#\"With a double quote (\")\"#; | ||
| 1553 | } | ||
| 1554 | " | ||
| 1555 | '("fn" font-lock-keyword-face | ||
| 1556 | "main" font-lock-function-name-face | ||
| 1557 | "r#\"With a double quote (\")\"#" font-lock-string-face))) | ||
| 1558 | |||
| 1559 | (ert-deftest font-lock-raw-strings-two-hashes () | ||
| 1560 | (rust-test-font-lock | ||
| 1561 | "r##\"With two hashes\"##;" | ||
| 1562 | '("r##\"With two hashes\"##" font-lock-string-face))) | ||
| 1563 | |||
| 1564 | (ert-deftest font-lock-raw-strings-backslash-at-end () | ||
| 1565 | (rust-test-font-lock | ||
| 1566 | "r\"With a backslash at the end\\\";" | ||
| 1567 | '("r\"With a backslash at the end\\\"" font-lock-string-face))) | ||
| 1568 | |||
| 1569 | (ert-deftest font-lock-two-raw-strings () | ||
| 1570 | (rust-test-font-lock | ||
| 1571 | "fn main() { | ||
| 1572 | r\"With a backslash at the end\\\"; | ||
| 1573 | r##\"With two hashes\"##; | ||
| 1574 | }" | ||
| 1575 | '("fn" font-lock-keyword-face | ||
| 1576 | "main" font-lock-function-name-face | ||
| 1577 | "r\"With a backslash at the end\\\"" font-lock-string-face | ||
| 1578 | "r##\"With two hashes\"##" font-lock-string-face))) | ||
| 1579 | |||
| 1580 | (ert-deftest font-lock-raw-string-with-inner-hash () | ||
| 1581 | (rust-test-font-lock | ||
| 1582 | "r##\"I've got an octothorpe (#)\"##; foo()" | ||
| 1583 | '("r##\"I've got an octothorpe (#)\"##" font-lock-string-face))) | ||
| 1584 | |||
| 1585 | (ert-deftest font-lock-raw-string-with-inner-quote-and-hash () | ||
| 1586 | (rust-test-font-lock | ||
| 1587 | "not_the_string(); r##\"string \"# still same string\"##; not_the_string()" | ||
| 1588 | '("r##\"string \"# still same string\"##" font-lock-string-face))) | ||
| 1589 | |||
| 1590 | (ert-deftest font-lock-string-ending-with-r-not-raw-string () | ||
| 1591 | (rust-test-font-lock | ||
| 1592 | "fn f() { | ||
| 1593 | \"Er\"; | ||
| 1594 | } | ||
| 1595 | |||
| 1596 | fn g() { | ||
| 1597 | \"xs\"; | ||
| 1598 | }" | ||
| 1599 | '("fn" font-lock-keyword-face | ||
| 1600 | "f" font-lock-function-name-face | ||
| 1601 | "\"Er\"" font-lock-string-face | ||
| 1602 | "fn" font-lock-keyword-face | ||
| 1603 | "g" font-lock-function-name-face | ||
| 1604 | "\"xs\"" font-lock-string-face))) | ||
| 1605 | |||
| 1606 | (ert-deftest font-lock-string-ending-with-r-word-boundary () | ||
| 1607 | (with-temp-buffer | ||
| 1608 | (rust-mode) | ||
| 1609 | (insert "const foo = \"foo bar\"") | ||
| 1610 | (font-lock-flush) | ||
| 1611 | (font-lock-ensure) | ||
| 1612 | ;; right-word should move the point to the end of the words. | ||
| 1613 | (goto-char 14) | ||
| 1614 | (right-word) | ||
| 1615 | (should (equal 17 (point))) | ||
| 1616 | (right-word) | ||
| 1617 | (should (equal 21 (point))) | ||
| 1618 | )) | ||
| 1619 | |||
| 1620 | (ert-deftest font-lock-raw-string-trick-ending-followed-by-string-with-quote () | ||
| 1621 | (rust-test-font-lock | ||
| 1622 | "r\"With what looks like the start of a raw string at the end r#\"; | ||
| 1623 | not_a_string(); | ||
| 1624 | r##\"With \"embedded\" quote \"##;" | ||
| 1625 | '("r\"With what looks like the start of a raw string at the end r#\"" font-lock-string-face | ||
| 1626 | "r##\"With \"embedded\" quote \"##" font-lock-string-face))) | ||
| 1627 | |||
| 1628 | (ert-deftest font-lock-raw-string-starter-inside-raw-string () | ||
| 1629 | ;; Check that it won't look for a raw string beginning inside another raw string. | ||
| 1630 | (rust-test-font-lock | ||
| 1631 | "r#\"In the first string r\" in the first string \"#; | ||
| 1632 | not_in_a_string(); | ||
| 1633 | r##\"In the second string\"##;" | ||
| 1634 | '("r#\"In the first string r\" in the first string \"#" font-lock-string-face | ||
| 1635 | "r##\"In the second string\"##" font-lock-string-face))) | ||
| 1636 | |||
| 1637 | (ert-deftest font-lock-raw-string-starter-inside-comment () | ||
| 1638 | ;; Check that it won't look for a raw string beginning inside another raw string. | ||
| 1639 | (rust-test-font-lock | ||
| 1640 | "// r\" this is a comment | ||
| 1641 | \"this is a string\"; | ||
| 1642 | this_is_not_a_string();)" | ||
| 1643 | '("// " font-lock-comment-delimiter-face | ||
| 1644 | "r\" this is a comment\n" font-lock-comment-face | ||
| 1645 | "\"this is a string\"" font-lock-string-face))) | ||
| 1646 | |||
| 1647 | (ert-deftest font-lock-runaway-raw-string () | ||
| 1648 | (rust-test-font-lock | ||
| 1649 | "const Z = r#\"my raw string\";\n// oops this is still in the string" | ||
| 1650 | '("const" font-lock-keyword-face | ||
| 1651 | "Z" font-lock-type-face | ||
| 1652 | "r#\"my raw string\";\n// oops this is still in the string" font-lock-string-face)) | ||
| 1653 | ) | ||
| 1654 | |||
| 1655 | (ert-deftest font-lock-recognize-closing-raw-string () | ||
| 1656 | (with-temp-buffer | ||
| 1657 | (rust-mode) | ||
| 1658 | (insert "const foo = r##\" | ||
| 1659 | 1...............................................50 | ||
| 1660 | 1...............................................50 | ||
| 1661 | 1...............................................50 | ||
| 1662 | 1...............195-->\"; let ...................50 | ||
| 1663 | 1...............................................50 | ||
| 1664 | 1...............................................50 | ||
| 1665 | 1...............................................50 | ||
| 1666 | 1...............................................50 | ||
| 1667 | 1...............................................50 | ||
| 1668 | 1......................500......................50 | ||
| 1669 | \"#; | ||
| 1670 | ") | ||
| 1671 | (font-lock-flush) | ||
| 1672 | (font-lock-ensure) | ||
| 1673 | (goto-char 530) | ||
| 1674 | (insert "#") | ||
| 1675 | ;; We have now closed the raw string. Check that the whole string is | ||
| 1676 | ;; recognized after the change | ||
| 1677 | (font-lock-after-change-function (1- (point)) (point) 0) | ||
| 1678 | (should (equal 'font-lock-string-face (get-text-property 195 'face))) ;; The "let" | ||
| 1679 | (should (equal 'font-lock-string-face (get-text-property 500 'face))) ;; The "500" | ||
| 1680 | (should (equal nil (get-text-property 531 'face))) ;; The second ";" | ||
| 1681 | )) | ||
| 1682 | |||
| 1683 | ;;; Documentation comments | ||
| 1684 | |||
| 1685 | (ert-deftest font-lock-doc-line-comment-parent () | ||
| 1686 | (rust-test-font-lock | ||
| 1687 | "//! doc" | ||
| 1688 | '("//! doc" font-lock-doc-face))) | ||
| 1689 | |||
| 1690 | (ert-deftest font-lock-doc-line-comment-item () | ||
| 1691 | (rust-test-font-lock | ||
| 1692 | "/// doc" | ||
| 1693 | '("/// doc" font-lock-doc-face))) | ||
| 1694 | |||
| 1695 | (ert-deftest font-lock-nondoc-line () | ||
| 1696 | (rust-test-font-lock | ||
| 1697 | "////// doc" | ||
| 1698 | '("////// " font-lock-comment-delimiter-face | ||
| 1699 | "doc" font-lock-comment-face))) | ||
| 1700 | |||
| 1701 | (ert-deftest font-lock-doc-line-in-string () | ||
| 1702 | (rust-test-font-lock | ||
| 1703 | "\"/// doc\"" | ||
| 1704 | '("\"/// doc\"" font-lock-string-face)) | ||
| 1705 | |||
| 1706 | (rust-test-font-lock | ||
| 1707 | "\"//! doc\"" | ||
| 1708 | '("\"//! doc\"" font-lock-string-face))) | ||
| 1709 | |||
| 1710 | (ert-deftest font-lock-doc-line-in-nested-comment () | ||
| 1711 | (rust-test-font-lock | ||
| 1712 | "/* /// doc */" | ||
| 1713 | '("/* " font-lock-comment-delimiter-face | ||
| 1714 | "/// doc */" font-lock-comment-face)) | ||
| 1715 | |||
| 1716 | (rust-test-font-lock | ||
| 1717 | "/* //! doc */" | ||
| 1718 | '("/* " font-lock-comment-delimiter-face | ||
| 1719 | "//! doc */" font-lock-comment-face))) | ||
| 1720 | |||
| 1721 | |||
| 1722 | (ert-deftest font-lock-doc-block-comment-parent () | ||
| 1723 | (rust-test-font-lock | ||
| 1724 | "/*! doc */" | ||
| 1725 | '("/*! doc */" font-lock-doc-face))) | ||
| 1726 | |||
| 1727 | (ert-deftest font-lock-doc-block-comment-item () | ||
| 1728 | (rust-test-font-lock | ||
| 1729 | "/** doc */" | ||
| 1730 | '("/** doc */" font-lock-doc-face))) | ||
| 1731 | |||
| 1732 | (ert-deftest font-lock-nondoc-block-comment-item () | ||
| 1733 | (rust-test-font-lock | ||
| 1734 | "/***** doc */" | ||
| 1735 | '("/**" font-lock-comment-delimiter-face | ||
| 1736 | "*** doc */" font-lock-comment-face))) | ||
| 1737 | |||
| 1738 | (ert-deftest font-lock-doc-block-in-string () | ||
| 1739 | (rust-test-font-lock | ||
| 1740 | "\"/** doc */\"" | ||
| 1741 | '("\"/** doc */\"" font-lock-string-face)) | ||
| 1742 | (rust-test-font-lock | ||
| 1743 | "\"/*! doc */\"" | ||
| 1744 | '("\"/*! doc */\"" font-lock-string-face))) | ||
| 1745 | |||
| 1746 | (ert-deftest font-lock-module-def () | ||
| 1747 | (rust-test-font-lock | ||
| 1748 | "mod foo;" | ||
| 1749 | '("mod" font-lock-keyword-face | ||
| 1750 | "foo" font-lock-constant-face))) | ||
| 1751 | |||
| 1752 | (ert-deftest font-lock-module-use () | ||
| 1753 | (rust-test-font-lock | ||
| 1754 | "use foo;" | ||
| 1755 | '("use" font-lock-keyword-face | ||
| 1756 | "foo" font-lock-constant-face))) | ||
| 1757 | |||
| 1758 | (ert-deftest font-lock-module-path () | ||
| 1759 | (rust-test-font-lock | ||
| 1760 | "foo::bar" | ||
| 1761 | '("foo" font-lock-constant-face))) | ||
| 1762 | |||
| 1763 | (ert-deftest font-lock-submodule-path () | ||
| 1764 | (rust-test-font-lock | ||
| 1765 | "foo::bar::baz" | ||
| 1766 | '("foo" font-lock-constant-face | ||
| 1767 | "bar" font-lock-constant-face))) | ||
| 1768 | |||
| 1769 | (ert-deftest font-lock-type () | ||
| 1770 | (rust-test-font-lock | ||
| 1771 | "foo::Bar::baz" | ||
| 1772 | '("foo" font-lock-constant-face | ||
| 1773 | "Bar" font-lock-type-face))) | ||
| 1774 | |||
| 1775 | (ert-deftest font-lock-type-annotation () | ||
| 1776 | "Ensure type annotations are not confused with modules." | ||
| 1777 | (rust-test-font-lock | ||
| 1778 | "parse::<i32>();" | ||
| 1779 | ;; Only the i32 should have been highlighted. | ||
| 1780 | '("i32" font-lock-type-face)) | ||
| 1781 | (rust-test-font-lock | ||
| 1782 | "foo:: <i32>" | ||
| 1783 | ;; Only the i32 should have been highlighted. | ||
| 1784 | '("i32" font-lock-type-face))) | ||
| 1785 | |||
| 1786 | (ert-deftest font-lock-question-mark () | ||
| 1787 | "Ensure question mark operator is highlighted." | ||
| 1788 | (rust-test-font-lock | ||
| 1789 | "?" | ||
| 1790 | '("?" rust-question-mark)) | ||
| 1791 | (rust-test-font-lock | ||
| 1792 | "foo\(\)?;" | ||
| 1793 | '("?" rust-question-mark)) | ||
| 1794 | (rust-test-font-lock | ||
| 1795 | "foo\(bar\(\)?\);" | ||
| 1796 | '("?" rust-question-mark)) | ||
| 1797 | (rust-test-font-lock | ||
| 1798 | "\"?\"" | ||
| 1799 | '("\"?\"" font-lock-string-face)) | ||
| 1800 | (rust-test-font-lock | ||
| 1801 | "foo\(\"?\"\);" | ||
| 1802 | '("\"?\"" font-lock-string-face)) | ||
| 1803 | (rust-test-font-lock | ||
| 1804 | "// ?" | ||
| 1805 | '("// " font-lock-comment-delimiter-face | ||
| 1806 | "?" font-lock-comment-face)) | ||
| 1807 | (rust-test-font-lock | ||
| 1808 | "/// ?" | ||
| 1809 | '("/// ?" font-lock-doc-face)) | ||
| 1810 | (rust-test-font-lock | ||
| 1811 | "foo\(\"?\"\);" | ||
| 1812 | '("\"?\"" font-lock-string-face)) | ||
| 1813 | (rust-test-font-lock | ||
| 1814 | "foo\(\"?\"\)?;" | ||
| 1815 | '("\"?\"" font-lock-string-face | ||
| 1816 | "?" rust-question-mark))) | ||
| 1817 | |||
| 1818 | (ert-deftest rust-test-default-context-sensitive () | ||
| 1819 | (rust-test-font-lock | ||
| 1820 | "let default = 7; impl foo { default fn f() { } }" | ||
| 1821 | '("let" font-lock-keyword-face | ||
| 1822 | "default" font-lock-variable-name-face | ||
| 1823 | "impl" font-lock-keyword-face | ||
| 1824 | "default" font-lock-keyword-face | ||
| 1825 | "fn" font-lock-keyword-face | ||
| 1826 | "f" font-lock-function-name-face))) | ||
| 1827 | |||
| 1828 | (ert-deftest rust-test-union-context-sensitive () | ||
| 1829 | (rust-test-font-lock | ||
| 1830 | "let union = 7; union foo { x: &'union bar }" | ||
| 1831 | '("let" font-lock-keyword-face | ||
| 1832 | ;; The first union is a variable name. | ||
| 1833 | "union" font-lock-variable-name-face | ||
| 1834 | ;; The second union is a contextual keyword. | ||
| 1835 | "union" font-lock-keyword-face | ||
| 1836 | "foo" font-lock-type-face | ||
| 1837 | "x" font-lock-variable-name-face | ||
| 1838 | ;; This union is the name of a lifetime. | ||
| 1839 | "&" rust-ampersand-face | ||
| 1840 | "union" font-lock-variable-name-face | ||
| 1841 | "bar" font-lock-type-face))) | ||
| 1842 | |||
| 1843 | (ert-deftest indent-method-chains-no-align () | ||
| 1844 | (let ((rust-indent-method-chain nil)) (test-indent | ||
| 1845 | " | ||
| 1846 | fn main() { | ||
| 1847 | let x = thing.do_it() | ||
| 1848 | .aligned() | ||
| 1849 | .more_alignment(); | ||
| 1850 | } | ||
| 1851 | " | ||
| 1852 | ))) | ||
| 1853 | |||
| 1854 | (ert-deftest indent-method-chains-no-align-with-question-mark-operator () | ||
| 1855 | (let ((rust-indent-method-chain nil)) (test-indent | ||
| 1856 | " | ||
| 1857 | fn main() { | ||
| 1858 | let x = thing.do_it() | ||
| 1859 | .aligned() | ||
| 1860 | .more_alignment()? | ||
| 1861 | .more_alignment(); | ||
| 1862 | } | ||
| 1863 | " | ||
| 1864 | ))) | ||
| 1865 | |||
| 1866 | (ert-deftest indent-method-chains-with-align () | ||
| 1867 | (let ((rust-indent-method-chain t)) (test-indent | ||
| 1868 | " | ||
| 1869 | fn main() { | ||
| 1870 | let x = thing.do_it() | ||
| 1871 | .aligned() | ||
| 1872 | .more_alignment(); | ||
| 1873 | } | ||
| 1874 | " | ||
| 1875 | ))) | ||
| 1876 | |||
| 1877 | (ert-deftest indent-method-chains-with-align-with-question-mark-operator () | ||
| 1878 | (let ((rust-indent-method-chain t)) (test-indent | ||
| 1879 | " | ||
| 1880 | fn main() { | ||
| 1881 | let x = thing.do_it() | ||
| 1882 | .aligned() | ||
| 1883 | .more_alignment()? | ||
| 1884 | .more_alignment(); | ||
| 1885 | } | ||
| 1886 | " | ||
| 1887 | ))) | ||
| 1888 | |||
| 1889 | (ert-deftest indent-method-chains-with-align-and-second-stmt () | ||
| 1890 | (let ((rust-indent-method-chain t)) (test-indent | ||
| 1891 | " | ||
| 1892 | fn main() { | ||
| 1893 | let x = thing.do_it() | ||
| 1894 | .aligned() | ||
| 1895 | .more_alignment(); | ||
| 1896 | foo.bar(); | ||
| 1897 | } | ||
| 1898 | " | ||
| 1899 | ))) | ||
| 1900 | |||
| 1901 | (ert-deftest indent-method-chains-field () | ||
| 1902 | (let ((rust-indent-method-chain t)) (test-indent | ||
| 1903 | " | ||
| 1904 | fn main() { | ||
| 1905 | let x = thing.do_it | ||
| 1906 | .aligned | ||
| 1907 | .more_alignment(); | ||
| 1908 | } | ||
| 1909 | " | ||
| 1910 | ))) | ||
| 1911 | |||
| 1912 | (ert-deftest indent-method-chains-double-field-on-first-line () | ||
| 1913 | (let ((rust-indent-method-chain t)) (test-indent | ||
| 1914 | " | ||
| 1915 | fn main() { | ||
| 1916 | let x = thing.a.do_it | ||
| 1917 | .aligned | ||
| 1918 | .more_alignment(); | ||
| 1919 | } | ||
| 1920 | " | ||
| 1921 | ))) | ||
| 1922 | |||
| 1923 | (ert-deftest indent-method-chains-no-let () | ||
| 1924 | (let ((rust-indent-method-chain t)) (test-indent | ||
| 1925 | " | ||
| 1926 | fn main() { | ||
| 1927 | thing.a.do_it | ||
| 1928 | .aligned | ||
| 1929 | .more_alignment(); | ||
| 1930 | } | ||
| 1931 | " | ||
| 1932 | ))) | ||
| 1933 | |||
| 1934 | (ert-deftest indent-method-chains-look-over-comment () | ||
| 1935 | (let ((rust-indent-method-chain t)) (test-indent | ||
| 1936 | " | ||
| 1937 | fn main() { | ||
| 1938 | thing.a.do_it | ||
| 1939 | // A comment | ||
| 1940 | .aligned | ||
| 1941 | // Another comment | ||
| 1942 | .more_alignment(); | ||
| 1943 | } | ||
| 1944 | " | ||
| 1945 | ))) | ||
| 1946 | |||
| 1947 | (ert-deftest indent-method-chains-comment () | ||
| 1948 | (let ((rust-indent-method-chain t)) (test-indent | ||
| 1949 | " | ||
| 1950 | fn main() { | ||
| 1951 | // thing.do_it() | ||
| 1952 | // .aligned() | ||
| 1953 | } | ||
| 1954 | " | ||
| 1955 | ))) | ||
| 1956 | |||
| 1957 | (ert-deftest indent-method-chains-close-block () | ||
| 1958 | (let ((rust-indent-method-chain t)) (test-indent | ||
| 1959 | " | ||
| 1960 | fn main() { | ||
| 1961 | foo.bar() | ||
| 1962 | } | ||
| 1963 | " | ||
| 1964 | ))) | ||
| 1965 | |||
| 1966 | (ert-deftest indent-method-chains-after-comment () | ||
| 1967 | (let ((rust-indent-method-chain t)) (test-indent | ||
| 1968 | " | ||
| 1969 | fn main() { // comment here should not push next line out | ||
| 1970 | foo.bar() | ||
| 1971 | } | ||
| 1972 | " | ||
| 1973 | ))) | ||
| 1974 | |||
| 1975 | (ert-deftest indent-method-chains-after-comment2 () | ||
| 1976 | (let ((rust-indent-method-chain t)) (test-indent | ||
| 1977 | " | ||
| 1978 | fn main() { | ||
| 1979 | // Lorem ipsum lorem ipsum lorem ipsum lorem.ipsum | ||
| 1980 | foo.bar() | ||
| 1981 | } | ||
| 1982 | " | ||
| 1983 | ))) | ||
| 1984 | |||
| 1985 | (ert-deftest indent-function-after-where () | ||
| 1986 | (let ((rust-indent-method-chain t)) (test-indent | ||
| 1987 | " | ||
| 1988 | fn each_split_within<'a, F>(ss: &'a str, lim: usize, mut it: F) | ||
| 1989 | -> bool where F: FnMut(&'a str) -> bool { | ||
| 1990 | } | ||
| 1991 | |||
| 1992 | #[test] | ||
| 1993 | fn test_split_within() { | ||
| 1994 | } | ||
| 1995 | " | ||
| 1996 | ))) | ||
| 1997 | |||
| 1998 | (ert-deftest indent-function-after-where-nested () | ||
| 1999 | (let ((rust-indent-method-chain t)) (test-indent | ||
| 2000 | " | ||
| 2001 | fn outer() { | ||
| 2002 | fn each_split_within<'a, F>(ss: &'a str, lim: usize, mut it: F) | ||
| 2003 | -> bool where F: FnMut(&'a str) -> bool { | ||
| 2004 | } | ||
| 2005 | #[test] | ||
| 2006 | fn test_split_within() { | ||
| 2007 | } | ||
| 2008 | fn bar() { | ||
| 2009 | } | ||
| 2010 | } | ||
| 2011 | " | ||
| 2012 | ))) | ||
| 2013 | |||
| 2014 | (ert-deftest test-for-issue-36-syntax-corrupted-state () | ||
| 2015 | "This is a test for a issue #36, which involved emacs's | ||
| 2016 | internal state getting corrupted when actions were done in a | ||
| 2017 | specific sequence. The test seems arbitrary, and is, but it was | ||
| 2018 | not clear how to narrow it down further. | ||
| 2019 | |||
| 2020 | The cause of the bug was code that used to set | ||
| 2021 | `syntax-begin-function' to `beginning-of-defun', which doesn't | ||
| 2022 | actually fulfill the expectations--`syntax-begin-function' is | ||
| 2023 | supposed to back out of all parens, but `beginning-of-defun' | ||
| 2024 | could leave it inside parens if a fn appears inside them. | ||
| 2025 | |||
| 2026 | Having said that, as I write this I don't understand fully what | ||
| 2027 | internal state was corrupted and how. There wasn't an obvious | ||
| 2028 | pattern to what did and did not trip it." | ||
| 2029 | |||
| 2030 | ;; When bug #36 was present, the following test would pass, but running it | ||
| 2031 | ;; caused some unknown emacs state to be corrupted such that the following | ||
| 2032 | ;; test failed. Both the "blank_line" and "indented_closing_brace" functions | ||
| 2033 | ;; were needed to expose the error, for instance--deleting either of them | ||
| 2034 | ;; would make the failure go away. | ||
| 2035 | (with-temp-buffer | ||
| 2036 | (rust-mode) | ||
| 2037 | (insert "fn blank_line(arg:i32) -> bool { | ||
| 2038 | |||
| 2039 | } | ||
| 2040 | |||
| 2041 | fn indenting_closing_brace() { | ||
| 2042 | if(true) { | ||
| 2043 | } | ||
| 2044 | } | ||
| 2045 | |||
| 2046 | fn indented_already() { | ||
| 2047 | \n // The previous line already has its spaces | ||
| 2048 | } | ||
| 2049 | ") | ||
| 2050 | (font-lock-flush) | ||
| 2051 | (font-lock-ensure) | ||
| 2052 | (goto-char (point-min)) | ||
| 2053 | (forward-line 10) | ||
| 2054 | (move-to-column 0) | ||
| 2055 | (indent-for-tab-command) | ||
| 2056 | (should (equal (current-column) 4)) | ||
| 2057 | ) | ||
| 2058 | |||
| 2059 | ;; This is the test that would fail only after running the previous one. The | ||
| 2060 | ;; code is extracted from src/libstd/collections/table.rs in the rust tree. | ||
| 2061 | ;; It was not clear how to reduce it further--removing various bits of it | ||
| 2062 | ;; would make it no longer fail. In particular, changing only the comment at | ||
| 2063 | ;; the top of the "next" function was sufficient to make it no longer fail. | ||
| 2064 | (test-indent | ||
| 2065 | " | ||
| 2066 | impl Foo for Bar { | ||
| 2067 | |||
| 2068 | /// Modifies the bucket pointer in place to make it point to the next slot. | ||
| 2069 | pub fn next(&mut self) { | ||
| 2070 | // Branchless bucket | ||
| 2071 | // As we reach the end of the table... | ||
| 2072 | // We take the current idx: 0111111b | ||
| 2073 | // Xor it by its increment: ^ 1000000b | ||
| 2074 | // ------------ | ||
| 2075 | // 1111111b | ||
| 2076 | // Then AND with the capacity: & 1000000b | ||
| 2077 | // ------------ | ||
| 2078 | // to get the backwards offset: 1000000b | ||
| 2079 | let maybe_wraparound_dist = (self.idx ^ (self.idx + 1)) & self.table.capacity(); | ||
| 2080 | // Finally, we obtain the offset 1 or the offset -cap + 1. | ||
| 2081 | let dist = 1 - (maybe_wraparound_dist as isize); | ||
| 2082 | |||
| 2083 | self.idx += 1; | ||
| 2084 | |||
| 2085 | unsafe { | ||
| 2086 | self.raw = self.raw.offset(dist); | ||
| 2087 | } | ||
| 2088 | } | ||
| 2089 | |||
| 2090 | /// Reads a bucket at a given index, returning an enum indicating whether | ||
| 2091 | /// the appropriate types to call most of the other functions in | ||
| 2092 | /// this module. | ||
| 2093 | pub fn peek(self) { | ||
| 2094 | match foo { | ||
| 2095 | EMPTY_BUCKET => | ||
| 2096 | Empty(EmptyBucket { | ||
| 2097 | raw: self.raw, | ||
| 2098 | idx: self.idx, | ||
| 2099 | table: self.table | ||
| 2100 | }), | ||
| 2101 | _ => | ||
| 2102 | Full(FullBucket { | ||
| 2103 | raw: self.raw, | ||
| 2104 | idx: self.idx, | ||
| 2105 | table: self.table | ||
| 2106 | }) | ||
| 2107 | } | ||
| 2108 | } | ||
| 2109 | } | ||
| 2110 | " | ||
| 2111 | )) | ||
| 2112 | |||
| 2113 | (ert-deftest test-indent-string-with-eol-backslash () | ||
| 2114 | (test-indent | ||
| 2115 | " | ||
| 2116 | pub fn foo() { | ||
| 2117 | format!(\"abc \\ | ||
| 2118 | def\") | ||
| 2119 | } | ||
| 2120 | " | ||
| 2121 | )) | ||
| 2122 | |||
| 2123 | (ert-deftest test-indent-string-with-eol-backslash-at-start () | ||
| 2124 | (test-indent | ||
| 2125 | " | ||
| 2126 | pub fn foo() { | ||
| 2127 | format!(\"\\ | ||
| 2128 | abc \\ | ||
| 2129 | def\") | ||
| 2130 | } | ||
| 2131 | " | ||
| 2132 | )) | ||
| 2133 | |||
| 2134 | (ert-deftest test-indent-string-without-eol-backslash-indent-is-not-touched () | ||
| 2135 | (test-indent | ||
| 2136 | " | ||
| 2137 | pub fn foo() { | ||
| 2138 | format!(\" | ||
| 2139 | abc | ||
| 2140 | def\"); | ||
| 2141 | } | ||
| 2142 | |||
| 2143 | pub fn foo() { | ||
| 2144 | format!(\"la la la | ||
| 2145 | la | ||
| 2146 | la la\"); | ||
| 2147 | } | ||
| 2148 | " | ||
| 2149 | ;; Should still indent the code parts but leave the string internals alone: | ||
| 2150 | " | ||
| 2151 | pub fn foo() { | ||
| 2152 | format!(\" | ||
| 2153 | abc | ||
| 2154 | def\"); | ||
| 2155 | } | ||
| 2156 | |||
| 2157 | pub fn foo() { | ||
| 2158 | format!(\"la la la | ||
| 2159 | la | ||
| 2160 | la la\"); | ||
| 2161 | } | ||
| 2162 | " | ||
| 2163 | )) | ||
| 2164 | |||
| 2165 | (ert-deftest test-indent-string-eol-backslash-mixed-with-literal-eol () | ||
| 2166 | (test-indent | ||
| 2167 | " | ||
| 2168 | fn foo() { | ||
| 2169 | println!(\" | ||
| 2170 | Here is the beginning of the string | ||
| 2171 | and here is a line that is arbitrarily indented \\ | ||
| 2172 | and a continuation of that indented line | ||
| 2173 | and another arbitrary indentation | ||
| 2174 | still another | ||
| 2175 | yet another \\ | ||
| 2176 | with a line continuing it | ||
| 2177 | And another line not indented | ||
| 2178 | \") | ||
| 2179 | } | ||
| 2180 | " | ||
| 2181 | " | ||
| 2182 | fn foo() { | ||
| 2183 | println!(\" | ||
| 2184 | Here is the beginning of the string | ||
| 2185 | and here is a line that is arbitrarily indented \\ | ||
| 2186 | and a continuation of that indented line | ||
| 2187 | and another arbitrary indentation | ||
| 2188 | still another | ||
| 2189 | yet another \\ | ||
| 2190 | with a line continuing it | ||
| 2191 | And another line not indented | ||
| 2192 | \") | ||
| 2193 | } | ||
| 2194 | ")) | ||
| 2195 | |||
| 2196 | (ert-deftest test-indent-string-eol-backslash-dont-touch-raw-strings () | ||
| 2197 | (test-indent | ||
| 2198 | " | ||
| 2199 | pub fn foo() { | ||
| 2200 | format!(r\"\ | ||
| 2201 | abc\ | ||
| 2202 | def\"); | ||
| 2203 | } | ||
| 2204 | |||
| 2205 | pub fn foo() { | ||
| 2206 | format!(r\"la la la | ||
| 2207 | la\ | ||
| 2208 | la la\"); | ||
| 2209 | } | ||
| 2210 | " | ||
| 2211 | ;; Should still indent the code parts but leave the string internals alone: | ||
| 2212 | " | ||
| 2213 | pub fn foo() { | ||
| 2214 | format!(r\"\ | ||
| 2215 | abc\ | ||
| 2216 | def\"); | ||
| 2217 | } | ||
| 2218 | |||
| 2219 | pub fn foo() { | ||
| 2220 | format!(r\"la la la | ||
| 2221 | la\ | ||
| 2222 | la la\"); | ||
| 2223 | } | ||
| 2224 | " | ||
| 2225 | )) | ||
| 2226 | |||
| 2227 | (ert-deftest indent-inside-string-first-line () | ||
| 2228 | (test-indent | ||
| 2229 | ;; Needs to leave 1 space before "world" | ||
| 2230 | "\"hello \\\n world\"")) | ||
| 2231 | |||
| 2232 | (ert-deftest indent-multi-line-type-param-list () | ||
| 2233 | (test-indent | ||
| 2234 | " | ||
| 2235 | pub fn foo<T, | ||
| 2236 | V>() { | ||
| 2237 | hello(); | ||
| 2238 | }")) | ||
| 2239 | |||
| 2240 | (ert-deftest indent-open-paren-in-column0 () | ||
| 2241 | ;; Just pass the same text for the "deindented" argument. This | ||
| 2242 | ;; avoids the extra spaces normally inserted, which would mess up | ||
| 2243 | ;; the test because string contents aren't touched by reindentation. | ||
| 2244 | (let ((text " | ||
| 2245 | const a: &'static str = r#\" | ||
| 2246 | {}\"#; | ||
| 2247 | fn main() { | ||
| 2248 | let b = \"//\"; | ||
| 2249 | let c = \"\"; | ||
| 2250 | |||
| 2251 | } | ||
| 2252 | ")) | ||
| 2253 | (test-indent text text))) | ||
| 2254 | |||
| 2255 | (ert-deftest indent-question-mark-operator () | ||
| 2256 | (test-indent "fn foo() { | ||
| 2257 | if bar()? < 1 { | ||
| 2258 | } | ||
| 2259 | baz(); | ||
| 2260 | }")) | ||
| 2261 | |||
| 2262 | ;; Regression test for #212. | ||
| 2263 | (ert-deftest indent-left-shift () | ||
| 2264 | (test-indent " | ||
| 2265 | fn main() { | ||
| 2266 | let a = [[0u32, 0u32]; 1]; | ||
| 2267 | let i = 0; | ||
| 2268 | let x = a[i][(1 < i)]; | ||
| 2269 | let x = a[i][(1 << i)]; | ||
| 2270 | } | ||
| 2271 | ")) | ||
| 2272 | |||
| 2273 | (defun rust-test-matching-parens (content pairs &optional nonparen-positions) | ||
| 2274 | "Assert that in rust-mode, given a buffer with the given `content', | ||
| 2275 | emacs's paren matching will find all of the pairs of positions | ||
| 2276 | as matching braces. The list of nonparen-positions asserts | ||
| 2277 | specific positions that should NOT be considered to be | ||
| 2278 | parens/braces of any kind. | ||
| 2279 | |||
| 2280 | This does not assert that the `pairs' list is | ||
| 2281 | comprehensive--there can be additional pairs that don't appear | ||
| 2282 | in the list and the test still passes (as long as none of their | ||
| 2283 | positions appear in `nonparen-positions'.)" | ||
| 2284 | (with-temp-buffer | ||
| 2285 | (rust-mode) | ||
| 2286 | (insert content) | ||
| 2287 | (font-lock-flush) | ||
| 2288 | (font-lock-ensure) | ||
| 2289 | (dolist (pair pairs) | ||
| 2290 | (let* ((open-pos (nth 0 pair)) | ||
| 2291 | (close-pos (nth 1 pair))) | ||
| 2292 | (should (equal 4 (syntax-class (syntax-after open-pos)))) | ||
| 2293 | (should (equal 5 (syntax-class (syntax-after close-pos)))) | ||
| 2294 | (should (equal (scan-sexps open-pos 1) (+ 1 close-pos))) | ||
| 2295 | (should (equal (scan-sexps (+ 1 close-pos) -1) open-pos)))) | ||
| 2296 | (dolist (nonpar-pos nonparen-positions) | ||
| 2297 | (let ((nonpar-syntax-class (syntax-class (syntax-after nonpar-pos)))) | ||
| 2298 | (should-not (equal 4 nonpar-syntax-class)) | ||
| 2299 | (should-not (equal 5 nonpar-syntax-class)))))) | ||
| 2300 | |||
| 2301 | (ert-deftest rust-test-unmatched-single-quote-in-comment-paren-matching () | ||
| 2302 | ;; This was a bug from the char quote handling that affected the paren | ||
| 2303 | ;; matching. An unmatched quote char in a comment caused the problems. | ||
| 2304 | (rust-test-matching-parens | ||
| 2305 | "// If this appeared first in the file... | ||
| 2306 | \"\\ | ||
| 2307 | {\"; | ||
| 2308 | |||
| 2309 | // And the { was not the on the first column: | ||
| 2310 | { | ||
| 2311 | // This then messed up the paren matching: '\\' | ||
| 2312 | } | ||
| 2313 | |||
| 2314 | " | ||
| 2315 | '((97 150) ;; The { and } at the bottom | ||
| 2316 | ))) | ||
| 2317 | |||
| 2318 | (ert-deftest rust-test-two-character-quotes-in-a-row () | ||
| 2319 | (with-temp-buffer | ||
| 2320 | (rust-mode) | ||
| 2321 | (font-lock-flush) | ||
| 2322 | (font-lock-ensure) | ||
| 2323 | (insert "'\\n','a', fn") | ||
| 2324 | (font-lock-after-change-function 1 12 0) | ||
| 2325 | |||
| 2326 | (should (equal 'font-lock-string-face (get-text-property 3 'face))) | ||
| 2327 | (should (equal nil (get-text-property 5 'face))) | ||
| 2328 | (should (equal 'font-lock-string-face (get-text-property 7 'face))) | ||
| 2329 | (should (equal nil (get-text-property 9 'face))) | ||
| 2330 | (should (equal 'font-lock-keyword-face (get-text-property 12 'face))) | ||
| 2331 | ) | ||
| 2332 | ) | ||
| 2333 | |||
| 2334 | (ert-deftest single-quote-null-char () | ||
| 2335 | (rust-test-font-lock | ||
| 2336 | "'\\0' 'a' fn" | ||
| 2337 | '("'\\0'" font-lock-string-face | ||
| 2338 | "'a'" font-lock-string-face | ||
| 2339 | "fn" font-lock-keyword-face))) | ||
| 2340 | |||
| 2341 | (ert-deftest r-in-string-after-single-quoted-double-quote () | ||
| 2342 | (rust-test-font-lock | ||
| 2343 | "'\"';\n\"r\";\n\"oops\";" | ||
| 2344 | '("'\"'" font-lock-string-face | ||
| 2345 | "\"r\"" font-lock-string-face | ||
| 2346 | "\"oops\"" font-lock-string-face | ||
| 2347 | ))) | ||
| 2348 | |||
| 2349 | (ert-deftest char-literal-after-quote-in-raw-string () | ||
| 2350 | (rust-test-font-lock | ||
| 2351 | "r#\"\"\"#;\n'q'" | ||
| 2352 | '("r#\"\"\"#" font-lock-string-face | ||
| 2353 | "'q'" font-lock-string-face))) | ||
| 2354 | |||
| 2355 | (ert-deftest rust-macro-font-lock () | ||
| 2356 | (rust-test-font-lock | ||
| 2357 | "foo!\(\);" | ||
| 2358 | '("foo!" font-lock-preprocessor-face)) | ||
| 2359 | (rust-test-font-lock | ||
| 2360 | "foo!{};" | ||
| 2361 | '("foo!" font-lock-preprocessor-face)) | ||
| 2362 | (rust-test-font-lock | ||
| 2363 | "foo![];" | ||
| 2364 | '("foo!" font-lock-preprocessor-face))) | ||
| 2365 | |||
| 2366 | (ert-deftest rust-string-interpolation-matcher-works () | ||
| 2367 | (dolist (test '(("print!\(\"\"\)" 9 11 nil) | ||
| 2368 | ("print!\(\"abcd\"\)" 9 15 nil) | ||
| 2369 | ("print!\(\"abcd {{}}\"\);" 9 19 nil) | ||
| 2370 | ("print!\(\"abcd {{\"\);" 9 18 nil) | ||
| 2371 | ("print!\(\"abcd {}\"\);" 9 18 ((14 16))) | ||
| 2372 | ("print!\(\"abcd {{{}\"\);" 9 20 ((16 18))) | ||
| 2373 | ("print!\(\"abcd {}{{\"\);" 9 20 ((14 16))) | ||
| 2374 | ("print!\(\"abcd {} {{\"\);" 9 21 ((14 16))) | ||
| 2375 | ("print!\(\"abcd {}}}\"\);" 9 20 ((14 16))) | ||
| 2376 | ("print!\(\"abcd {{{}}}\"\);" 9 20 ((16 18))) | ||
| 2377 | ("print!\(\"abcd {0}\"\);" 9 18 ((14 17))) | ||
| 2378 | ("print!\(\"abcd {0} efgh\"\);" 9 23 ((14 17))) | ||
| 2379 | ("print!\(\"{1} abcd {0} efgh\"\);" 9 27 ((9 12) (18 21))) | ||
| 2380 | ("print!\(\"{{{1} abcd }} {0}}} {{efgh}}\"\);" 9 33 ((11 14) (23 26))))) | ||
| 2381 | (cl-destructuring-bind (text cursor limit matches) test | ||
| 2382 | (with-temp-buffer | ||
| 2383 | ;; make sure we have a clean slate | ||
| 2384 | (save-match-data | ||
| 2385 | (set-match-data nil) | ||
| 2386 | (insert text) | ||
| 2387 | (goto-char cursor) | ||
| 2388 | (if (null matches) | ||
| 2389 | (should (equal (progn | ||
| 2390 | (rust-string-interpolation-matcher limit) | ||
| 2391 | (match-data)) | ||
| 2392 | nil)) | ||
| 2393 | (dolist (pair matches) | ||
| 2394 | (rust-string-interpolation-matcher limit) | ||
| 2395 | (should (equal (match-beginning 0) (car pair))) | ||
| 2396 | (should (equal (match-end 0) (cadr pair)))))))))) | ||
| 2397 | |||
| 2398 | (ert-deftest rust-formatting-macro-font-lock () | ||
| 2399 | ;; test that the block delimiters aren't highlighted and the comment | ||
| 2400 | ;; is ignored | ||
| 2401 | (rust-test-font-lock | ||
| 2402 | "print!(\"\"); { /* print!(\"\"); */ }" | ||
| 2403 | '("print!" rust-builtin-formatting-macro | ||
| 2404 | "\"\"" font-lock-string-face | ||
| 2405 | "/* " font-lock-comment-delimiter-face | ||
| 2406 | "print!(\"\"); */" font-lock-comment-face)) | ||
| 2407 | ;; with newline directly following delimiter | ||
| 2408 | (rust-test-font-lock | ||
| 2409 | "print!(\n\"\"\n); { /* print!(\"\"); */ }" | ||
| 2410 | '("print!" rust-builtin-formatting-macro | ||
| 2411 | "\"\"" font-lock-string-face | ||
| 2412 | "/* " font-lock-comment-delimiter-face | ||
| 2413 | "print!(\"\"); */" font-lock-comment-face)) | ||
| 2414 | ;; with empty println!() | ||
| 2415 | (rust-test-font-lock | ||
| 2416 | "println!(); { /* println!(); */ }" | ||
| 2417 | '("println!" rust-builtin-formatting-macro | ||
| 2418 | "/* " font-lock-comment-delimiter-face | ||
| 2419 | "println!(); */" font-lock-comment-face)) | ||
| 2420 | ;; other delimiters | ||
| 2421 | (rust-test-font-lock | ||
| 2422 | "print!{\"\"}; { /* no-op */ }" | ||
| 2423 | '("print!" rust-builtin-formatting-macro | ||
| 2424 | "\"\"" font-lock-string-face | ||
| 2425 | "/* " font-lock-comment-delimiter-face | ||
| 2426 | "no-op */" font-lock-comment-face)) | ||
| 2427 | ;; other delimiters | ||
| 2428 | (rust-test-font-lock | ||
| 2429 | "print![\"\"]; { /* no-op */ }" | ||
| 2430 | '("print!" rust-builtin-formatting-macro | ||
| 2431 | "\"\"" font-lock-string-face | ||
| 2432 | "/* " font-lock-comment-delimiter-face | ||
| 2433 | "no-op */" font-lock-comment-face)) | ||
| 2434 | ;; no interpolation | ||
| 2435 | (rust-test-font-lock | ||
| 2436 | "print!(\"abcd\"); { /* no-op */ }" | ||
| 2437 | '("print!" rust-builtin-formatting-macro | ||
| 2438 | "\"abcd\"" font-lock-string-face | ||
| 2439 | "/* " font-lock-comment-delimiter-face | ||
| 2440 | "no-op */" font-lock-comment-face)) | ||
| 2441 | ;; only interpolation | ||
| 2442 | (rust-test-font-lock | ||
| 2443 | "print!(\"{}\"); { /* no-op */ }" | ||
| 2444 | '("print!" rust-builtin-formatting-macro | ||
| 2445 | "\"" font-lock-string-face | ||
| 2446 | "{}" rust-string-interpolation | ||
| 2447 | "\"" font-lock-string-face | ||
| 2448 | "/* " font-lock-comment-delimiter-face | ||
| 2449 | "no-op */" font-lock-comment-face)) | ||
| 2450 | ;; text + interpolation | ||
| 2451 | (rust-test-font-lock | ||
| 2452 | "print!(\"abcd {}\", foo); { /* no-op */ }" | ||
| 2453 | '("print!" rust-builtin-formatting-macro | ||
| 2454 | "\"abcd " font-lock-string-face | ||
| 2455 | "{}" rust-string-interpolation | ||
| 2456 | "\"" font-lock-string-face | ||
| 2457 | "/* " font-lock-comment-delimiter-face | ||
| 2458 | "no-op */" font-lock-comment-face)) | ||
| 2459 | ;; text + interpolation with specification | ||
| 2460 | (rust-test-font-lock | ||
| 2461 | "print!(\"abcd {0}\", foo); { /* no-op */ }" | ||
| 2462 | '("print!" rust-builtin-formatting-macro | ||
| 2463 | "\"abcd " font-lock-string-face | ||
| 2464 | "{0}" rust-string-interpolation | ||
| 2465 | "\"" font-lock-string-face | ||
| 2466 | "/* " font-lock-comment-delimiter-face | ||
| 2467 | "no-op */" font-lock-comment-face)) | ||
| 2468 | ;; text + interpolation with specification and escape | ||
| 2469 | (rust-test-font-lock | ||
| 2470 | "print!(\"abcd {0}}}\", foo); { /* no-op */ }" | ||
| 2471 | '("print!" rust-builtin-formatting-macro | ||
| 2472 | "\"abcd " font-lock-string-face | ||
| 2473 | "{0}" rust-string-interpolation | ||
| 2474 | "}}\"" font-lock-string-face | ||
| 2475 | "/* " font-lock-comment-delimiter-face | ||
| 2476 | "no-op */" font-lock-comment-face)) | ||
| 2477 | ;; multiple pairs | ||
| 2478 | (rust-test-font-lock | ||
| 2479 | "print!(\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }" | ||
| 2480 | '("print!" rust-builtin-formatting-macro | ||
| 2481 | "\"abcd " font-lock-string-face | ||
| 2482 | "{0}" rust-string-interpolation | ||
| 2483 | " efgh " font-lock-string-face | ||
| 2484 | "{1}" rust-string-interpolation | ||
| 2485 | "\"" font-lock-string-face | ||
| 2486 | "/* " font-lock-comment-delimiter-face | ||
| 2487 | "no-op */" font-lock-comment-face)) | ||
| 2488 | ;; println | ||
| 2489 | (rust-test-font-lock | ||
| 2490 | "println!(\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }" | ||
| 2491 | '("println!" rust-builtin-formatting-macro | ||
| 2492 | "\"abcd " font-lock-string-face | ||
| 2493 | "{0}" rust-string-interpolation | ||
| 2494 | " efgh " font-lock-string-face | ||
| 2495 | "{1}" rust-string-interpolation | ||
| 2496 | "\"" font-lock-string-face | ||
| 2497 | "/* " font-lock-comment-delimiter-face | ||
| 2498 | "no-op */" font-lock-comment-face)) | ||
| 2499 | ;; eprint | ||
| 2500 | (rust-test-font-lock | ||
| 2501 | "eprint!(\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }" | ||
| 2502 | '("eprint!" rust-builtin-formatting-macro | ||
| 2503 | "\"abcd " font-lock-string-face | ||
| 2504 | "{0}" rust-string-interpolation | ||
| 2505 | " efgh " font-lock-string-face | ||
| 2506 | "{1}" rust-string-interpolation | ||
| 2507 | "\"" font-lock-string-face | ||
| 2508 | "/* " font-lock-comment-delimiter-face | ||
| 2509 | "no-op */" font-lock-comment-face)) | ||
| 2510 | ;; eprintln | ||
| 2511 | (rust-test-font-lock | ||
| 2512 | "eprintln!(\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }" | ||
| 2513 | '("eprintln!" rust-builtin-formatting-macro | ||
| 2514 | "\"abcd " font-lock-string-face | ||
| 2515 | "{0}" rust-string-interpolation | ||
| 2516 | " efgh " font-lock-string-face | ||
| 2517 | "{1}" rust-string-interpolation | ||
| 2518 | "\"" font-lock-string-face | ||
| 2519 | "/* " font-lock-comment-delimiter-face | ||
| 2520 | "no-op */" font-lock-comment-face)) | ||
| 2521 | ;; format | ||
| 2522 | (rust-test-font-lock | ||
| 2523 | "format!(\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }" | ||
| 2524 | '("format!" rust-builtin-formatting-macro | ||
| 2525 | "\"abcd " font-lock-string-face | ||
| 2526 | "{0}" rust-string-interpolation | ||
| 2527 | " efgh " font-lock-string-face | ||
| 2528 | "{1}" rust-string-interpolation | ||
| 2529 | "\"" font-lock-string-face | ||
| 2530 | "/* " font-lock-comment-delimiter-face | ||
| 2531 | "no-op */" font-lock-comment-face)) | ||
| 2532 | ;; print + raw string | ||
| 2533 | (rust-test-font-lock | ||
| 2534 | "format!(r\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }" | ||
| 2535 | '("format!" rust-builtin-formatting-macro | ||
| 2536 | "r\"abcd " font-lock-string-face | ||
| 2537 | "{0}" rust-string-interpolation | ||
| 2538 | " efgh " font-lock-string-face | ||
| 2539 | "{1}" rust-string-interpolation | ||
| 2540 | "\"" font-lock-string-face | ||
| 2541 | "/* " font-lock-comment-delimiter-face | ||
| 2542 | "no-op */" font-lock-comment-face)) | ||
| 2543 | ;; print + raw string with hash | ||
| 2544 | (rust-test-font-lock | ||
| 2545 | "format!(r#\"abcd {0} efgh {1}\"#, foo, bar); { /* no-op */ }" | ||
| 2546 | '("format!" rust-builtin-formatting-macro | ||
| 2547 | "r#\"abcd " font-lock-string-face | ||
| 2548 | "{0}" rust-string-interpolation | ||
| 2549 | " efgh " font-lock-string-face | ||
| 2550 | "{1}" rust-string-interpolation | ||
| 2551 | "\"#" font-lock-string-face | ||
| 2552 | "/* " font-lock-comment-delimiter-face | ||
| 2553 | "no-op */" font-lock-comment-face)) | ||
| 2554 | ;; print + raw string with two hashes | ||
| 2555 | (rust-test-font-lock | ||
| 2556 | "format!(r##\"abcd {0} efgh {1}\"##, foo, bar); { /* no-op */ }" | ||
| 2557 | '("format!" rust-builtin-formatting-macro | ||
| 2558 | "r##\"abcd " font-lock-string-face | ||
| 2559 | "{0}" rust-string-interpolation | ||
| 2560 | " efgh " font-lock-string-face | ||
| 2561 | "{1}" rust-string-interpolation | ||
| 2562 | "\"##" font-lock-string-face | ||
| 2563 | "/* " font-lock-comment-delimiter-face | ||
| 2564 | "no-op */" font-lock-comment-face))) | ||
| 2565 | |||
| 2566 | (ert-deftest rust-write-macro-font-lock () | ||
| 2567 | (rust-test-font-lock | ||
| 2568 | "write!(f, \"abcd {0}}} efgh {1}\", foo, bar); { /* no-op */ }" | ||
| 2569 | '("write!" rust-builtin-formatting-macro | ||
| 2570 | "\"abcd " font-lock-string-face | ||
| 2571 | "{0}" rust-string-interpolation | ||
| 2572 | "}} efgh " font-lock-string-face | ||
| 2573 | "{1}" rust-string-interpolation | ||
| 2574 | "\"" font-lock-string-face | ||
| 2575 | "/* " font-lock-comment-delimiter-face | ||
| 2576 | "no-op */" font-lock-comment-face)) | ||
| 2577 | (rust-test-font-lock | ||
| 2578 | "writeln!(f, \"abcd {0}}} efgh {1}\", foo, bar); { /* no-op */ }" | ||
| 2579 | '("writeln!" rust-builtin-formatting-macro | ||
| 2580 | "\"abcd " font-lock-string-face | ||
| 2581 | "{0}" rust-string-interpolation | ||
| 2582 | "}} efgh " font-lock-string-face | ||
| 2583 | "{1}" rust-string-interpolation | ||
| 2584 | "\"" font-lock-string-face | ||
| 2585 | "/* " font-lock-comment-delimiter-face | ||
| 2586 | "no-op */" font-lock-comment-face)) | ||
| 2587 | (rust-test-font-lock | ||
| 2588 | "println!(\"123\"); eprintln!(\"123\"); cprintln!(\"123\");" | ||
| 2589 | '("println!" rust-builtin-formatting-macro | ||
| 2590 | "\"123\"" font-lock-string-face | ||
| 2591 | "eprintln!" rust-builtin-formatting-macro | ||
| 2592 | "\"123\"" font-lock-string-face | ||
| 2593 | "cprintln!" font-lock-preprocessor-face | ||
| 2594 | "\"123\"" font-lock-string-face))) | ||
| 2595 | |||
| 2596 | (ert-deftest font-lock-fontify-angle-brackets () | ||
| 2597 | "Test that angle bracket fontify" | ||
| 2598 | (should (equal (rust-test-fontify-string "<>") "<>")) | ||
| 2599 | (should (equal (rust-test-fontify-string "<foo>") "<foo>")) | ||
| 2600 | (should (equal (rust-test-fontify-string "<<>>") "<<>>")) | ||
| 2601 | (should (equal (rust-test-fontify-string "<>>") "<>>")) | ||
| 2602 | (should (equal (rust-test-fontify-string "<<>") "<<>"))) | ||
| 2603 | |||
| 2604 | (ert-deftest rust-test-basic-paren-matching () | ||
| 2605 | (rust-test-matching-parens | ||
| 2606 | " | ||
| 2607 | fn foo() { | ||
| 2608 | let a = [1, 2, 3]; | ||
| 2609 | }" | ||
| 2610 | '((8 9) ;; Parens of foo() | ||
| 2611 | (11 36) ;; Curly braces | ||
| 2612 | (25 33) ;; Square brackets | ||
| 2613 | ))) | ||
| 2614 | |||
| 2615 | (ert-deftest rust-test-paren-matching-generic-fn () | ||
| 2616 | (rust-test-matching-parens | ||
| 2617 | " | ||
| 2618 | fn foo<A>() { | ||
| 2619 | }" | ||
| 2620 | '((8 10) ;; Angle brackets <A> | ||
| 2621 | (11 12) ;; Parens | ||
| 2622 | (14 16) ;; Curly braces | ||
| 2623 | ))) | ||
| 2624 | |||
| 2625 | (ert-deftest rust-test-paren-matching-generic-fn-with-return-value () | ||
| 2626 | (rust-test-matching-parens | ||
| 2627 | " | ||
| 2628 | fn foo<A>() -> bool { | ||
| 2629 | false | ||
| 2630 | }" | ||
| 2631 | '((8 10) ;; Angle brackets <A> | ||
| 2632 | (11 12) ;; Parens | ||
| 2633 | (22 34) ;; Curly braces | ||
| 2634 | ) | ||
| 2635 | |||
| 2636 | '(15 ;; The ">" in "->" is not an angle bracket | ||
| 2637 | ))) | ||
| 2638 | |||
| 2639 | (ert-deftest rust-test-paren-matching-match-stmt () | ||
| 2640 | (rust-test-matching-parens | ||
| 2641 | " | ||
| 2642 | fn foo() { | ||
| 2643 | something_str(match <Type as Trait>::method() { | ||
| 2644 | Some(_) => \"Got some\", | ||
| 2645 | None => \"Nada\" | ||
| 2646 | }); | ||
| 2647 | }" | ||
| 2648 | '((8 9) ;; parens of fn foo | ||
| 2649 | (11 127) ;; curly braces of foo | ||
| 2650 | (30 124) ;; parens of something_str | ||
| 2651 | (37 51) ;; angle brackets of <Type as Trait> | ||
| 2652 | (60 61) ;; parens of method() | ||
| 2653 | (63 123) ;; curly braces of match | ||
| 2654 | (77 79) ;; parens of Some(_) | ||
| 2655 | ) | ||
| 2656 | |||
| 2657 | '(82 ;; > in first => | ||
| 2658 | 112 ;; > in second => | ||
| 2659 | ))) | ||
| 2660 | |||
| 2661 | (ert-deftest rust-test-paren-matching-bitshift-operators () | ||
| 2662 | (rust-test-matching-parens | ||
| 2663 | " | ||
| 2664 | fn foo(z:i32) { | ||
| 2665 | let a:Option<Result<i32,i32>> = Some(Ok(4 >> 1)); | ||
| 2666 | let b = a.map(|x| x.map(|y| y << 3)); | ||
| 2667 | let trick_question = z<<<Type as Trait>::method(); // First two <s are not brackets, third is | ||
| 2668 | }" | ||
| 2669 | '((34 50) ;; angle brackets of Option | ||
| 2670 | (41 49) ;; angle brackets of Result | ||
| 2671 | (142 156) ;; angle brackets of <Type as Trait> | ||
| 2672 | ) | ||
| 2673 | '(64 ;; The >> inside Some(Ok()) are not angle brackets | ||
| 2674 | 65 ;; The >> inside Some(Ok()) are not angle brackets | ||
| 2675 | 106 ;; The << inside map() are not angle brackets | ||
| 2676 | 107 ;; The << inside map() are not angle brackets | ||
| 2677 | 140 ;; The << before <Type as Trait> are not angle brackets | ||
| 2678 | 141 ;; The << before <Type as Trait> are not angle brackets | ||
| 2679 | 183 ;; The < inside the comment | ||
| 2680 | ))) | ||
| 2681 | |||
| 2682 | (ert-deftest rust-test-paren-matching-angle-bracket-after-colon-ident () | ||
| 2683 | (rust-test-matching-parens | ||
| 2684 | " | ||
| 2685 | struct Bla<T> { | ||
| 2686 | a:Option<(i32,Option<bool>)>, | ||
| 2687 | b:Option<T>, | ||
| 2688 | c:bool | ||
| 2689 | } | ||
| 2690 | |||
| 2691 | fn f(x:i32,y:Option<i32>) { | ||
| 2692 | let z:Option<i32> = None; | ||
| 2693 | let b:Bla<i8> = Bla{ | ||
| 2694 | a:None, | ||
| 2695 | b:None, | ||
| 2696 | c:x<y.unwrap(); | ||
| 2697 | } | ||
| 2698 | }" | ||
| 2699 | '((12 14) ;; Angle brackets of Bla<T> | ||
| 2700 | (30 49) ;; Outer angle brackets of a:Option<...> | ||
| 2701 | (42 47) ;; Inner angle brackets of Option<bool> | ||
| 2702 | (64 66) ;; Angle brackets of Option<T> | ||
| 2703 | (102 106) ;; Angle brackets of y:Option<i32> | ||
| 2704 | (127 131) ;; Angle brackets of z:Option<i32> | ||
| 2705 | (154 157) ;; Angle brackets of b:Bla<i8> | ||
| 2706 | ) | ||
| 2707 | '(209 ;; less than operator in c:x<y.unwrap... | ||
| 2708 | ))) | ||
| 2709 | |||
| 2710 | (ert-deftest rust-test-paren-matching-struct-literals () | ||
| 2711 | (rust-test-matching-parens | ||
| 2712 | " | ||
| 2713 | fn foo(x:i32) -> Bar { | ||
| 2714 | Bar { | ||
| 2715 | b:x<3 | ||
| 2716 | } | ||
| 2717 | }" | ||
| 2718 | '() | ||
| 2719 | '(17 ;; the -> is not a brace | ||
| 2720 | 46 ;; x<3 the < is a less than sign | ||
| 2721 | )) | ||
| 2722 | ) | ||
| 2723 | |||
| 2724 | (ert-deftest rust-test-paren-matching-nested-struct-literals () | ||
| 2725 | (rust-test-matching-parens | ||
| 2726 | " | ||
| 2727 | fn f(x:i32,y:i32) -> Foo<Bar> { | ||
| 2728 | Foo{ | ||
| 2729 | bar:Bar{ | ||
| 2730 | a:3, | ||
| 2731 | b:x<y | ||
| 2732 | } | ||
| 2733 | } | ||
| 2734 | } | ||
| 2735 | " | ||
| 2736 | '((26 30)) ;; Angle brackets of Foo<Bar> | ||
| 2737 | ) | ||
| 2738 | '(92 ;; less than operator x<y | ||
| 2739 | )) | ||
| 2740 | |||
| 2741 | (ert-deftest rust-test-paren-matching-fn-types-in-type-params () | ||
| 2742 | (rust-test-matching-parens | ||
| 2743 | " | ||
| 2744 | fn foo<T:Fn() -> X<Y>>() -> Z { | ||
| 2745 | } | ||
| 2746 | " | ||
| 2747 | '((8 23) ;; The angle brackets of foo<T...> | ||
| 2748 | (20 22 ;; The angle brackets of X<Y> | ||
| 2749 | )) | ||
| 2750 | '(17 ;; The first -> | ||
| 2751 | 28 ;; The second -> | ||
| 2752 | ) | ||
| 2753 | )) | ||
| 2754 | |||
| 2755 | (ert-deftest rust-test-paren-matching-lt-ops-in-fn-params () | ||
| 2756 | (rust-test-matching-parens | ||
| 2757 | " | ||
| 2758 | fn foo(x:i32) { | ||
| 2759 | f(x < 3); | ||
| 2760 | } | ||
| 2761 | " | ||
| 2762 | '() | ||
| 2763 | '(26 ;; The < inside f is a less than operator | ||
| 2764 | ) | ||
| 2765 | )) | ||
| 2766 | |||
| 2767 | (ert-deftest rust-test-paren-matching-lt-ops-in-fn-params () | ||
| 2768 | (rust-test-matching-parens | ||
| 2769 | " | ||
| 2770 | fn foo(x:i32) -> bool { | ||
| 2771 | return x < 3; | ||
| 2772 | } | ||
| 2773 | " | ||
| 2774 | '() | ||
| 2775 | '(17 ;; The -> | ||
| 2776 | 39 ;; The < after return is a less than operator | ||
| 2777 | ) | ||
| 2778 | )) | ||
| 2779 | |||
| 2780 | (ert-deftest rust-test-type-paren-matching-angle-brackets-in-type-items () | ||
| 2781 | (rust-test-matching-parens | ||
| 2782 | " | ||
| 2783 | type Foo = Blah<Z,Y>; | ||
| 2784 | type Bar<X> = (Foo, Bletch<X>); | ||
| 2785 | type ThisThing<Z,A,D,F> = HereYouGo<Z,Y<Fn(A) -> B<C<D>>,E<F>>>;" | ||
| 2786 | '((17 21) ;; Angle brackets of Blah<Z,Y> | ||
| 2787 | (32 34) ;; Angle brackets of Bar<X> | ||
| 2788 | (50 52) ;; Angle brackets of Bletch<X> | ||
| 2789 | (70 78) ;; Angle brackets of ThisThing<Z,A,D,F> | ||
| 2790 | (91 118) ;; Angle brackets of HereYouGo<...> | ||
| 2791 | (95 117) ;; Angle brackets of Y<Fn...> | ||
| 2792 | (106 111) ;; Angle brackets of B<C<D>> | ||
| 2793 | (108 110) ;; Angle brackets of C<D> | ||
| 2794 | (114 116) ;; Angle brackets of E<F> | ||
| 2795 | ))) | ||
| 2796 | |||
| 2797 | (ert-deftest rust-test-paren-matching-tuple-like-struct () | ||
| 2798 | (rust-test-matching-parens | ||
| 2799 | " | ||
| 2800 | struct A(Option<B>); | ||
| 2801 | struct C<Q>(Result<Q,i32>);" | ||
| 2802 | '((17 19) ;; The angle brackets <B> | ||
| 2803 | (10 20) ;; The parens of A(); | ||
| 2804 | (31 33) ;; The angle brackets of C<Q> | ||
| 2805 | (41 47) ;; The angle brackets of Result<Q,i32> | ||
| 2806 | ) | ||
| 2807 | '())) | ||
| 2808 | |||
| 2809 | (ert-deftest rust-test-paren-matching-in-enum () | ||
| 2810 | (rust-test-matching-parens | ||
| 2811 | " | ||
| 2812 | enum Boo<A> { | ||
| 2813 | TupleLike(Option<A>), | ||
| 2814 | StructLike{foo: Result<A,i32>} | ||
| 2815 | }" | ||
| 2816 | '((10 12) ;; Angle brackets of Boo<A> | ||
| 2817 | (36 38) ;; Angle brackets of Option<A> | ||
| 2818 | (68 74) ;; Angle brackets of Result<A,i32> | ||
| 2819 | ))) | ||
| 2820 | |||
| 2821 | (ert-deftest rust-test-paren-matching-assoc-type-bounds () | ||
| 2822 | (rust-test-matching-parens | ||
| 2823 | "impl <A:B<AssocType = C<A> >> Thing<A> {}" | ||
| 2824 | '((6 29) ;; Outer angle brackets of impl | ||
| 2825 | (10 28) ;; Outer angle brackets of B<AssocType = C<A>> | ||
| 2826 | (24 26) ;; Inner angle brackets of C<A> | ||
| 2827 | (36 38) ;; Angle brackets of Thing<A> | ||
| 2828 | ) | ||
| 2829 | )) | ||
| 2830 | |||
| 2831 | (ert-deftest rust-test-paren-matching-plus-signs-in-expressions-and-bounds () | ||
| 2832 | ;; Note that as I write this, the function "bluh" below does not compile, but | ||
| 2833 | ;; it warns that the equality constraint in a where clause is "not yet | ||
| 2834 | ;; supported." It seems that the compiler will support this eventually, so | ||
| 2835 | ;; the emacs mode needs to support it. | ||
| 2836 | (rust-test-matching-parens | ||
| 2837 | "fn foo<A:Trait1+Trait2<i32>,B>(a:A,b:B) -> bool where B:Trait3<Foo>+Trait4<Bar> { | ||
| 2838 | 2 + a < 3 && 3 + b > 11 | ||
| 2839 | } | ||
| 2840 | |||
| 2841 | fn bluh<A>() where A:Fn()+MyTrait<i32>, MyTrait<A>::AssocType = Option<bool> { | ||
| 2842 | } | ||
| 2843 | |||
| 2844 | fn fluh<C>() where C:Fn(i32) -> (i32, i32) + SomeTrait<i32>, C::AssocType = OtherThing<bool> { | ||
| 2845 | }" | ||
| 2846 | '((7 30) ;; Angle brackets of foo<...> | ||
| 2847 | (23 27) ;; Angle brackets of Trait2<i32> | ||
| 2848 | (63 67) ;; Angle brackets of Trait3<Foo> | ||
| 2849 | (75 79) ;; Angle brackets of Trait4<Bar> | ||
| 2850 | |||
| 2851 | (121 123) ;; Angle brackets of bluh<A> | ||
| 2852 | (147 151) ;; Angle brackets of MyTrait<i32> | ||
| 2853 | |||
| 2854 | (161 163) ;; Angle brackets of MyTrait<A> | ||
| 2855 | (184 189) ;; Angle brackets of Option<bool> | ||
| 2856 | |||
| 2857 | (203 205) ;; Angle brackets of <C> | ||
| 2858 | (250 254) ;; Angle brackets of SomeTrait<i32> | ||
| 2859 | (282 287) ;; Angle brackets of Option<bool> | ||
| 2860 | ) | ||
| 2861 | '(93 ;; Less-than sign of a < 3 | ||
| 2862 | 106 ;; Greater than sign of b > 11 | ||
| 2863 | ))) | ||
| 2864 | |||
| 2865 | (ert-deftest rust-test-paren-matching-generic-type-in-tuple-return-type () | ||
| 2866 | (rust-test-matching-parens | ||
| 2867 | "pub fn take(mut self) -> (EmptyBucket<K, V, M>, K, V) {}" | ||
| 2868 | '((38 46)) | ||
| 2869 | )) | ||
| 2870 | |||
| 2871 | (ert-deftest rust-test-paren-matching-references-and-logical-and () | ||
| 2872 | (rust-test-matching-parens | ||
| 2873 | " | ||
| 2874 | fn ampersand_check(a: &Option<i32>, b:bool) -> &Option<u32> { | ||
| 2875 | a.map(|x| { | ||
| 2876 | b && x < 32 | ||
| 2877 | }) | ||
| 2878 | }" | ||
| 2879 | '((31 35) ;; Option<i32> | ||
| 2880 | (56 60) ;; Option<u32> | ||
| 2881 | ) | ||
| 2882 | '(95 ;; x < 32 | ||
| 2883 | ) | ||
| 2884 | ) | ||
| 2885 | ) | ||
| 2886 | |||
| 2887 | (ert-deftest rust-test-paren-matching-lt-sign-in-if-statement () | ||
| 2888 | (rust-test-matching-parens | ||
| 2889 | " | ||
| 2890 | fn if_check(a:i32,b:i32,c:i32) { | ||
| 2891 | if a + b < c { | ||
| 2892 | |||
| 2893 | } | ||
| 2894 | if a < b { | ||
| 2895 | |||
| 2896 | } | ||
| 2897 | if (c < a) { | ||
| 2898 | |||
| 2899 | } | ||
| 2900 | } | ||
| 2901 | |||
| 2902 | fn while_check(x:i32,y:i32) -> bool { | ||
| 2903 | while x < y { | ||
| 2904 | } | ||
| 2905 | for x in y < x { | ||
| 2906 | } | ||
| 2907 | match y < z { | ||
| 2908 | true => (), _ => () | ||
| 2909 | } | ||
| 2910 | return z < y; | ||
| 2911 | }" | ||
| 2912 | '() | ||
| 2913 | '(48 ;; b < c | ||
| 2914 | 78 ;; a < b | ||
| 2915 | 109 ;; (c < a) | ||
| 2916 | |||
| 2917 | 184 ;; x < y | ||
| 2918 | 211 ;; y < x | ||
| 2919 | 235 ;; y < z | ||
| 2920 | 288 ;; z < y | ||
| 2921 | ))) | ||
| 2922 | |||
| 2923 | (ert-deftest rust-test-paren-matching-lt-expr-with-field () | ||
| 2924 | (rust-test-matching-parens | ||
| 2925 | "fn foo() { x.y < 3 }" | ||
| 2926 | '() | ||
| 2927 | '(16 ;; x.y < 3 | ||
| 2928 | ))) | ||
| 2929 | |||
| 2930 | (ert-deftest rust-test-paren-matching-lt-expr-with-quote () | ||
| 2931 | (rust-test-matching-parens | ||
| 2932 | " | ||
| 2933 | fn quote_check() { | ||
| 2934 | 'x' < y; | ||
| 2935 | \"y\" < x; | ||
| 2936 | r##\"z\"## < q; | ||
| 2937 | a <= 3 && b < '2' | ||
| 2938 | }" | ||
| 2939 | '() | ||
| 2940 | '(29 ;; 'x' < y | ||
| 2941 | 42 ;; "y" < x | ||
| 2942 | 60 ;; r##"z"## < q | ||
| 2943 | 71 ;; a <= '3' | ||
| 2944 | 81 ;; b < '2' | ||
| 2945 | ))) | ||
| 2946 | |||
| 2947 | (ert-deftest rust-test-paren-matching-keywords-capitalized-are-ok-type-names () | ||
| 2948 | (rust-test-matching-parens | ||
| 2949 | " | ||
| 2950 | fn foo() -> Box<i32> { | ||
| 2951 | let z:If<bool> = If(a < 3); | ||
| 2952 | }" | ||
| 2953 | '((17 21) ;; Box<i32> | ||
| 2954 | (37 42) ;; If<bool> | ||
| 2955 | ) | ||
| 2956 | '(51 ;; If(a < 3) | ||
| 2957 | ))) | ||
| 2958 | |||
| 2959 | (ert-deftest rust-test-paren-matching-lt-expression-inside-macro () | ||
| 2960 | (rust-test-matching-parens | ||
| 2961 | "fn bla() { assert!(x < y); }" | ||
| 2962 | '() | ||
| 2963 | '(22 ;; x < y | ||
| 2964 | ))) | ||
| 2965 | |||
| 2966 | (ert-deftest rust-test-paren-matching-array-types-with-generics () | ||
| 2967 | (rust-test-matching-parens | ||
| 2968 | "fn boo () -> [Option<i32>] {}" | ||
| 2969 | '((21 25)))) | ||
| 2970 | |||
| 2971 | (ert-deftest rust-test-paren-matching-angle-bracket-inner-reference () | ||
| 2972 | (rust-test-matching-parens | ||
| 2973 | "fn x() -> Option<&Node<T>> {}" | ||
| 2974 | '((17 26) ;; Option | ||
| 2975 | (23 25) ;; Node | ||
| 2976 | ))) | ||
| 2977 | |||
| 2978 | (ert-deftest rust-test-paren-matching-lt-operator-after-semicolon () | ||
| 2979 | (rust-test-matching-parens | ||
| 2980 | "fn f(x:i32) -> bool { (); x < 3 }" | ||
| 2981 | '() | ||
| 2982 | '(29 | ||
| 2983 | ))) | ||
| 2984 | |||
| 2985 | (ert-deftest rust-test-paren-matching-lt-operator-after-comma () | ||
| 2986 | (rust-test-matching-parens | ||
| 2987 | "fn foo() { | ||
| 2988 | (e, a < b) | ||
| 2989 | }" | ||
| 2990 | '((16 25) ;; The parens () | ||
| 2991 | ) | ||
| 2992 | '(22 ;; The < operator | ||
| 2993 | ))) | ||
| 2994 | |||
| 2995 | (ert-deftest rust-test-paren-matching-lt-operator-after-let () | ||
| 2996 | (rust-test-matching-parens | ||
| 2997 | "fn main() { | ||
| 2998 | let x = a < b; | ||
| 2999 | }" | ||
| 3000 | '((11 32) ;; The { } | ||
| 3001 | ) | ||
| 3002 | '(27 ;; The < operator | ||
| 3003 | ))) | ||
| 3004 | |||
| 3005 | (ert-deftest rust-test-paren-matching-two-lt-ops-in-a-row () | ||
| 3006 | (rust-test-matching-parens | ||
| 3007 | "fn next(&mut self) -> Option<<I as Iterator>::Item>" | ||
| 3008 | '((29 51) ;; Outer Option<> | ||
| 3009 | (30 44) ;; Inner <I as Iterator> | ||
| 3010 | ) | ||
| 3011 | '(21 | ||
| 3012 | ))) | ||
| 3013 | |||
| 3014 | (ert-deftest rust-test-paren-matching-lt-after-caret () | ||
| 3015 | (rust-test-matching-parens | ||
| 3016 | "fn foo() { x^2 < 3 }" | ||
| 3017 | '((10 20) ;; The { } | ||
| 3018 | ) | ||
| 3019 | '(16 ;; The < operator | ||
| 3020 | ))) | ||
| 3021 | |||
| 3022 | (ert-deftest rust-test-paren-matching-lt-operator-after-special-type () | ||
| 3023 | (rust-test-matching-parens | ||
| 3024 | "fn foo() { low as u128 <= c }" | ||
| 3025 | '((10 29)) | ||
| 3026 | '(24))) | ||
| 3027 | |||
| 3028 | (ert-deftest rust-test-paren-matching-lt-operator-after-closing-curly-brace () | ||
| 3029 | (rust-test-matching-parens | ||
| 3030 | "fn main() { if true {} a < 3 }" | ||
| 3031 | '((11 30) | ||
| 3032 | ) | ||
| 3033 | '(26))) | ||
| 3034 | |||
| 3035 | (ert-deftest rust-test-paren-matching-const () | ||
| 3036 | (rust-test-matching-parens | ||
| 3037 | " | ||
| 3038 | const BLA = 1 << 3; | ||
| 3039 | const BLUB = 2 < 4;" | ||
| 3040 | '() | ||
| 3041 | '(16 | ||
| 3042 | 17 ;; Both chars of the << in 1 << 3 | ||
| 3043 | 37 ;; The < in 2 < 4 | ||
| 3044 | ))) | ||
| 3045 | |||
| 3046 | (ert-deftest rust-test-paren-matching-c-like-enum () | ||
| 3047 | (rust-test-matching-parens | ||
| 3048 | " | ||
| 3049 | enum CLikeEnum { | ||
| 3050 | Two = 1 << 1, | ||
| 3051 | Four = 1 << 2 | ||
| 3052 | }" | ||
| 3053 | '((17 56 ;; The { } of the enum | ||
| 3054 | )) | ||
| 3055 | '(31 | ||
| 3056 | 32 ;; The first << | ||
| 3057 | 50 | ||
| 3058 | 51 ;; The second << | ||
| 3059 | ))) | ||
| 3060 | |||
| 3061 | (ert-deftest rust-test-paren-matching-no-angle-brackets-in-macros () | ||
| 3062 | (rust-test-matching-parens | ||
| 3063 | " | ||
| 3064 | fn foo<A>(a:A) { | ||
| 3065 | macro_a!( foo::<ignore the bracets> ); | ||
| 3066 | macro_b![ foo as Option<B> ]; | ||
| 3067 | } | ||
| 3068 | |||
| 3069 | macro_c!{ | ||
| 3070 | struct Boo<D> {} | ||
| 3071 | }" | ||
| 3072 | '((8 10)) | ||
| 3073 | ;; Inside macros, it should not find any angle brackets, even if it normally | ||
| 3074 | ;; would | ||
| 3075 | '(38 ;; macro_a < | ||
| 3076 | 57 ;; macro_a > | ||
| 3077 | 89 ;; macro_b < | ||
| 3078 | 91 ;; macro_b > | ||
| 3079 | 123 ;; macro_c < | ||
| 3080 | 125 ;; macro_d > | ||
| 3081 | ))) | ||
| 3082 | |||
| 3083 | (ert-deftest rust-test-paren-matching-no-angle-brackets-in-macro-rules () | ||
| 3084 | (rust-test-matching-parens | ||
| 3085 | " | ||
| 3086 | fn foo<A>(a:A) { | ||
| 3087 | macro_rules! foo ( foo::<ignore the bracets> ); | ||
| 3088 | macro_rules! bar [ foo as Option<B> ]; | ||
| 3089 | } | ||
| 3090 | |||
| 3091 | macro_c!{ | ||
| 3092 | struct Boo<D> {} | ||
| 3093 | }" | ||
| 3094 | '((8 10)) | ||
| 3095 | ;; Inside macros, it should not find any angle brackets, even if it normally | ||
| 3096 | ;; would | ||
| 3097 | '(47 ;; foo < | ||
| 3098 | 62 ;; foo > | ||
| 3099 | 107 ;; bar < | ||
| 3100 | 109 ;; bar > | ||
| 3101 | 141 ;; macro_c < | ||
| 3102 | 143 ;; macro_c > | ||
| 3103 | ))) | ||
| 3104 | |||
| 3105 | (ert-deftest rust-test-in-macro-do-not-fail-on-unbalance () | ||
| 3106 | (should | ||
| 3107 | ;; We don't care about the results here, so long as they do not error | ||
| 3108 | (with-temp-buffer | ||
| 3109 | (insert | ||
| 3110 | "fn foo<A>(a:A) { | ||
| 3111 | macro_c!{ | ||
| 3112 | struct Boo<D> {} | ||
| 3113 | ") | ||
| 3114 | (rust-mode) | ||
| 3115 | (goto-char (point-max)) | ||
| 3116 | (syntax-ppss)))) | ||
| 3117 | |||
| 3118 | |||
| 3119 | (ert-deftest rust-test-in-macro-no-caching () | ||
| 3120 | (should-not | ||
| 3121 | (with-temp-buffer | ||
| 3122 | (insert | ||
| 3123 | "fn foo<A>(a:A) { | ||
| 3124 | macro_c!{ | ||
| 3125 | struct Boo<D> {} | ||
| 3126 | ") | ||
| 3127 | (rust-mode) | ||
| 3128 | (search-backward "macro") | ||
| 3129 | ;; do not use the cache | ||
| 3130 | (let ((rust-macro-scopes nil)) | ||
| 3131 | (rust-in-macro))))) | ||
| 3132 | |||
| 3133 | (ert-deftest rust-test-in-macro-fake-cache () | ||
| 3134 | (should | ||
| 3135 | (with-temp-buffer | ||
| 3136 | (insert | ||
| 3137 | "fn foo<A>(a:A) { | ||
| 3138 | macro_c!{ | ||
| 3139 | struct Boo<D> {} | ||
| 3140 | ") | ||
| 3141 | (rust-mode) | ||
| 3142 | (search-backward "macro") | ||
| 3143 | ;; make the cache lie to make the whole buffer in scope | ||
| 3144 | ;; we need to be at paren level 1 for this to work | ||
| 3145 | (let ((rust-macro-scopes `((,(point-min) ,(point-max))))) | ||
| 3146 | (rust-in-macro))))) | ||
| 3147 | |||
| 3148 | (ert-deftest rust-test-in-macro-broken-cache () | ||
| 3149 | (should-error | ||
| 3150 | (with-temp-buffer | ||
| 3151 | (insert | ||
| 3152 | "fn foo<A>(a:A) { | ||
| 3153 | macro_c!{ | ||
| 3154 | struct Boo<D> {} | ||
| 3155 | ") | ||
| 3156 | (rust-mode) | ||
| 3157 | (search-backward "Boo") | ||
| 3158 | ;; do we use the cache at all | ||
| 3159 | (let ((rust-macro-scopes '(I should break))) | ||
| 3160 | (rust-in-macro))))) | ||
| 3161 | |||
| 3162 | (ert-deftest rust-test-in-macro-nested () | ||
| 3163 | (should | ||
| 3164 | (equal | ||
| 3165 | (with-temp-buffer | ||
| 3166 | (insert | ||
| 3167 | "macro_rules! outer { | ||
| 3168 | () => { vec![] }; | ||
| 3169 | }") | ||
| 3170 | (rust-mode) | ||
| 3171 | (rust-macro-scope (point-min) (point-max))) | ||
| 3172 | '((38 40) (20 45))))) | ||
| 3173 | |||
| 3174 | (ert-deftest rust-test-in-macro-not-with-space () | ||
| 3175 | (should | ||
| 3176 | (equal | ||
| 3177 | (with-temp-buffer | ||
| 3178 | (insert | ||
| 3179 | "fn foo<T>() { | ||
| 3180 | if !(mem::size_of::<T>() > 8) { | ||
| 3181 | bar() | ||
| 3182 | } | ||
| 3183 | }") | ||
| 3184 | (rust-mode) | ||
| 3185 | (rust-macro-scope (point-min) (point-max))) | ||
| 3186 | 'empty))) | ||
| 3187 | |||
| 3188 | |||
| 3189 | (ert-deftest rust-test-paren-matching-type-with-module-name () | ||
| 3190 | (rust-test-matching-parens | ||
| 3191 | " | ||
| 3192 | const X: libc::c_int = 1 << 2; | ||
| 3193 | fn main() { | ||
| 3194 | let z: libc::c_uint = 1 << 4; | ||
| 3195 | } | ||
| 3196 | " | ||
| 3197 | '((43 79)) ;; The curly braces | ||
| 3198 | '(27 | ||
| 3199 | 28 ;; The first << | ||
| 3200 | 73 | ||
| 3201 | 74 ;; The second << | ||
| 3202 | ))) | ||
| 3203 | |||
| 3204 | (ert-deftest rust-test-paren-matching-qualififed-struct-literal () | ||
| 3205 | (rust-test-matching-parens | ||
| 3206 | " | ||
| 3207 | fn foo() -> Fn(asd) -> F<V> { | ||
| 3208 | let z = foo::Struct{ b: 1 << 4, c: 2 < 4 } | ||
| 3209 | }" | ||
| 3210 | '((30 80) ;; Outer curly brackets | ||
| 3211 | ) | ||
| 3212 | '(62 | ||
| 3213 | 63 ;; The shift operator | ||
| 3214 | 73 ;; The less than operator | ||
| 3215 | ))) | ||
| 3216 | |||
| 3217 | (ert-deftest rust-test-paren-matching-let-mut () | ||
| 3218 | (rust-test-matching-parens | ||
| 3219 | " | ||
| 3220 | fn f() { | ||
| 3221 | let mut b = 1 < 3; | ||
| 3222 | let mut i = 1 << 3; | ||
| 3223 | } | ||
| 3224 | " | ||
| 3225 | '() | ||
| 3226 | '(28 ;; 1 < 3 | ||
| 3227 | 51 | ||
| 3228 | 52 ;; 1 << 3 | ||
| 3229 | ))) | ||
| 3230 | |||
| 3231 | (ert-deftest rust-test-paren-matching-as-ref-type () | ||
| 3232 | (rust-test-matching-parens | ||
| 3233 | "fn f() { | ||
| 3234 | let a = b as &Foo<Bar>; | ||
| 3235 | }" | ||
| 3236 | '((31 35) ;; Angle brackets Foo<Bar> | ||
| 3237 | ))) | ||
| 3238 | |||
| 3239 | (ert-deftest rust-test-paren-matching-type-ascription () | ||
| 3240 | (rust-test-matching-parens | ||
| 3241 | " | ||
| 3242 | fn rfc803() { | ||
| 3243 | let z = a < b:FunnkyThing<i32>; | ||
| 3244 | let s = Foo { | ||
| 3245 | a: b < 3, | ||
| 3246 | b: d:CrazyStuff<i32> < 3, | ||
| 3247 | c: 2 < x:CrazyStuff<u128> | ||
| 3248 | } | ||
| 3249 | }" | ||
| 3250 | '((45 49) ;; FunkyThing<i32> | ||
| 3251 | (111 115) ;; CrazyStuff<i32> | ||
| 3252 | (149 154) ;; CrazyStuff<u128> | ||
| 3253 | ) | ||
| 3254 | '(30 ;; a < b | ||
| 3255 | 83 ;; b < 3 | ||
| 3256 | 117 ;; d... < 3 | ||
| 3257 | 135 ;; 2 < x | ||
| 3258 | ))) | ||
| 3259 | |||
| 3260 | (ert-deftest rust-test-paren-matching-angle-brackets-in-enum-with-where-claause () | ||
| 3261 | (rust-test-matching-parens | ||
| 3262 | " | ||
| 3263 | enum MyEnum<T> where T:std::fmt::Debug { | ||
| 3264 | Thing(Option<T>) | ||
| 3265 | }" | ||
| 3266 | '((13 15) ;; MyEnum<T> | ||
| 3267 | (59 61) ;; Option<T> | ||
| 3268 | ))) | ||
| 3269 | |||
| 3270 | (ert-deftest rust-test-paren-matching-where-clauses-with-closure-types () | ||
| 3271 | (rust-test-matching-parens | ||
| 3272 | " | ||
| 3273 | enum Boo<'a,T> where T:Fn() -> Option<&'a str> + 'a { | ||
| 3274 | Thingy(Option<&'a T>) | ||
| 3275 | } | ||
| 3276 | |||
| 3277 | fn foo<'a>() -> Result<B,C> where C::X: D<A>, B:FnMut() -> Option<Q>+'a { | ||
| 3278 | Foo(a < b) | ||
| 3279 | } | ||
| 3280 | |||
| 3281 | type Foo<T> where T: Copy = Box<T>; | ||
| 3282 | " | ||
| 3283 | '((10 15) ;; Boo<'a,T> | ||
| 3284 | (39 47) ;; Option<&'a str> | ||
| 3285 | (72 78) ;; Option<&'a T> | ||
| 3286 | |||
| 3287 | (106 110) ;; Result<B,C> | ||
| 3288 | (125 127) ;; D<A> | ||
| 3289 | (149 151) ;; Option<Q> | ||
| 3290 | (184 186) ;; Foo<T> | ||
| 3291 | (207 209) ;; Box<T> | ||
| 3292 | ) | ||
| 3293 | |||
| 3294 | '(168 ;; Foo(a < b) | ||
| 3295 | ) | ||
| 3296 | )) | ||
| 3297 | |||
| 3298 | (ert-deftest rust-test-angle-bracket-matching-turned-off () | ||
| 3299 | (let ((rust-match-angle-brackets nil)) | ||
| 3300 | (rust-test-matching-parens | ||
| 3301 | "fn foo<a>() {}" | ||
| 3302 | '((10 11)) | ||
| 3303 | '(7 9)))) | ||
| 3304 | |||
| 3305 | |||
| 3306 | (ert-deftest redo-syntax-after-change-far-from-point () | ||
| 3307 | (let* | ||
| 3308 | ((tmp-file-name (make-temp-file "rust-mdoe-test-issue104")) | ||
| 3309 | (base-contents (apply #'concat (append '("fn foo() {\n\n}\n") (make-list 500 "// More stuff...\n") '("fn bar() {\n\n}\n"))))) | ||
| 3310 | ;; Create the temp file... | ||
| 3311 | (with-temp-file tmp-file-name | ||
| 3312 | (insert base-contents)) | ||
| 3313 | (with-temp-buffer | ||
| 3314 | (insert-file-contents tmp-file-name 'VISIT nil nil 'REPLACE) | ||
| 3315 | (rust-mode) | ||
| 3316 | (goto-char (point-max)) | ||
| 3317 | (should (= 0 (rust-paren-level))) | ||
| 3318 | (with-temp-file tmp-file-name | ||
| 3319 | (insert base-contents) | ||
| 3320 | (goto-char 12) ;; On the blank line in the middle of fn foo | ||
| 3321 | (insert " let z = 1 < 3;") | ||
| 3322 | ) | ||
| 3323 | (revert-buffer 'IGNORE-AUTO 'NOCONFIRM 'PRESERVE-MODES) | ||
| 3324 | (should (= 0 (rust-paren-level))) | ||
| 3325 | ) | ||
| 3326 | ) | ||
| 3327 | ) | ||
| 3328 | |||
| 3329 | (defun test-imenu (code expected-items) | ||
| 3330 | (with-temp-buffer | ||
| 3331 | (rust-mode) | ||
| 3332 | (insert code) | ||
| 3333 | (let ((actual-items | ||
| 3334 | ;; Replace ("item" . #<marker at ? in ?.rs) with "item" | ||
| 3335 | (mapcar (lambda (class) | ||
| 3336 | (cons (car class) | ||
| 3337 | (mapcar #'car (cdr class)))) | ||
| 3338 | (imenu--generic-function rust-imenu-generic-expression)))) | ||
| 3339 | (should (equal expected-items actual-items))))) | ||
| 3340 | |||
| 3341 | (ert-deftest rust-test-imenu-extern-unsafe-fn () | ||
| 3342 | (test-imenu | ||
| 3343 | " | ||
| 3344 | fn f1() { | ||
| 3345 | } | ||
| 3346 | |||
| 3347 | unsafe fn f2() { | ||
| 3348 | } | ||
| 3349 | |||
| 3350 | extern \"C\" fn f3() { | ||
| 3351 | } | ||
| 3352 | |||
| 3353 | pub extern fn f4() { | ||
| 3354 | } | ||
| 3355 | |||
| 3356 | extern \"rust-intrinsic\" fn f5() { | ||
| 3357 | } | ||
| 3358 | |||
| 3359 | async fn f6() { | ||
| 3360 | } | ||
| 3361 | |||
| 3362 | const fn f7() { | ||
| 3363 | } | ||
| 3364 | |||
| 3365 | async const fn not_a_match() { | ||
| 3366 | } | ||
| 3367 | |||
| 3368 | fn f8<'a>() { | ||
| 3369 | } | ||
| 3370 | |||
| 3371 | pub ( in self::super ) fn f9() { | ||
| 3372 | } | ||
| 3373 | |||
| 3374 | pub ( in super ) fn f10() { | ||
| 3375 | } | ||
| 3376 | |||
| 3377 | pub(in crate) fn f11() { | ||
| 3378 | } | ||
| 3379 | |||
| 3380 | pub (in self) fn f12() { | ||
| 3381 | } | ||
| 3382 | " | ||
| 3383 | '(("Fn" | ||
| 3384 | "f1" | ||
| 3385 | "f2" | ||
| 3386 | "f3" | ||
| 3387 | "f4" | ||
| 3388 | "f5" | ||
| 3389 | "f6" | ||
| 3390 | "f7" | ||
| 3391 | "f8" | ||
| 3392 | "f9" | ||
| 3393 | "f10" | ||
| 3394 | "f11" | ||
| 3395 | "f12")))) | ||
| 3396 | |||
| 3397 | (ert-deftest rust-test-imenu-impl-with-lifetime () | ||
| 3398 | (test-imenu | ||
| 3399 | " | ||
| 3400 | impl<'a> One<'a> { | ||
| 3401 | fn one() {} | ||
| 3402 | } | ||
| 3403 | |||
| 3404 | impl Two<'a> { | ||
| 3405 | fn two() {} | ||
| 3406 | } | ||
| 3407 | " | ||
| 3408 | '(("Impl" "One" "Two") | ||
| 3409 | ("Fn" "one" "two")))) | ||
| 3410 | |||
| 3411 | (ert-deftest font-lock-function-parameters () | ||
| 3412 | (rust-test-font-lock | ||
| 3413 | "fn foo(a: u32, b : u32) {}" | ||
| 3414 | '("fn" font-lock-keyword-face | ||
| 3415 | "foo" font-lock-function-name-face | ||
| 3416 | "a" font-lock-variable-name-face | ||
| 3417 | "u32" font-lock-type-face | ||
| 3418 | "b" font-lock-variable-name-face | ||
| 3419 | "u32" font-lock-type-face))) | ||
| 3420 | |||
| 3421 | (ert-deftest variable-in-for-loop () | ||
| 3422 | (rust-test-font-lock | ||
| 3423 | "for var in iter" | ||
| 3424 | '("for" font-lock-keyword-face | ||
| 3425 | "var" font-lock-variable-name-face | ||
| 3426 | "in" font-lock-keyword-face)) | ||
| 3427 | (rust-test-font-lock | ||
| 3428 | "for Foo{var} in iter" | ||
| 3429 | '("for" font-lock-keyword-face | ||
| 3430 | "Foo" font-lock-type-face | ||
| 3431 | "in" font-lock-keyword-face))) | ||
| 3432 | |||
| 3433 | (ert-deftest rust-test-dbg-wrap-symbol () | ||
| 3434 | (rust-test-manip-code | ||
| 3435 | "let x = add(first, second);" | ||
| 3436 | 15 | ||
| 3437 | #'rust-dbg-wrap-or-unwrap | ||
| 3438 | "let x = add(dbg!(first), second);")) | ||
| 3439 | |||
| 3440 | (ert-deftest rust-test-dbg-wrap-symbol-unbalanced () | ||
| 3441 | (rust-test-manip-code | ||
| 3442 | "let x = add((first, second);" | ||
| 3443 | 14 | ||
| 3444 | #'rust-dbg-wrap-or-unwrap | ||
| 3445 | "let x = add((dbg!(first), second);")) | ||
| 3446 | |||
| 3447 | (ert-deftest rust-test-dbg-wrap-region () | ||
| 3448 | (rust-test-manip-code | ||
| 3449 | "let x = add(first, second);" | ||
| 3450 | 9 | ||
| 3451 | (lambda () | ||
| 3452 | (transient-mark-mode 1) | ||
| 3453 | (push-mark nil t t) | ||
| 3454 | (goto-char 26) | ||
| 3455 | (rust-dbg-wrap-or-unwrap)) | ||
| 3456 | "let x = dbg!(add(first, second));")) | ||
| 3457 | |||
| 3458 | (defun rust-test-dbg-unwrap (position) | ||
| 3459 | (rust-test-manip-code | ||
| 3460 | "let x = add(dbg!(first), second);" | ||
| 3461 | position | ||
| 3462 | #'rust-dbg-wrap-or-unwrap | ||
| 3463 | "let x = add(first, second);")) | ||
| 3464 | |||
| 3465 | (ert-deftest rust-test-dbg-uwnrap-within () | ||
| 3466 | (rust-test-dbg-unwrap 19)) | ||
| 3467 | |||
| 3468 | (ert-deftest rust-test-dbg-uwnrap-on-paren () | ||
| 3469 | (rust-test-dbg-unwrap 17)) | ||
| 3470 | |||
| 3471 | (ert-deftest rust-test-dbg-uwnrap-on-dbg-middle () | ||
| 3472 | (rust-test-dbg-unwrap 15)) | ||
| 3473 | |||
| 3474 | (ert-deftest rust-test-dbg-uwnrap-on-dbg-start () | ||
| 3475 | (rust-test-dbg-unwrap 13)) | ||
| 3476 | |||
| 3477 | (ert-deftest rust-test-dbg-unwrap-inside-string-literal () | ||
| 3478 | (rust-test-manip-code | ||
| 3479 | "let x = \"foo, bar\"";" | ||
| 3480 | 15 | ||
| 3481 | #'rust-dbg-wrap-or-unwrap | ||
| 3482 | "let x = dbg!(\"foo, bar\")")) | ||
| 3483 | |||
| 3484 | (ert-deftest rust-test-project-located () | ||
| 3485 | (skip-unless (executable-find rust-cargo-bin)) | ||
| 3486 | (let* ((test-dir (expand-file-name "test-project/" default-directory)) | ||
| 3487 | (manifest-file (expand-file-name "Cargo.toml" test-dir))) | ||
| 3488 | (let ((default-directory test-dir)) | ||
| 3489 | (should (equal (expand-file-name (rust-buffer-project)) manifest-file))))) | ||
| 3490 | |||
| 3491 | (defun rust-collect-matches (spec) | ||
| 3492 | (let ((matches nil)) | ||
| 3493 | (goto-char (point-min)) | ||
| 3494 | (while (re-search-forward (car spec) nil t) | ||
| 3495 | (push | ||
| 3496 | (mapcar (lambda (r) | ||
| 3497 | (let ((match-pos | ||
| 3498 | (nth (cdr r) spec))) | ||
| 3499 | (cond ((and (eq :type (car r)) (consp match-pos)) | ||
| 3500 | (compilation-face match-pos)) | ||
| 3501 | ((eq :type (car r)) | ||
| 3502 | (cdr (assoc match-pos '((1 . compilation-warning) | ||
| 3503 | (0 . compilation-info) | ||
| 3504 | (2 . compilation-error))))) | ||
| 3505 | ((and (null match-pos) (eq :column (car r))) | ||
| 3506 | 'back-to-indentation) | ||
| 3507 | ((and (null match-pos) (eq :file (car r))) | ||
| 3508 | 'like-previous-one) | ||
| 3509 | ((null match-pos) | ||
| 3510 | (error (format "%S" (car r)))) | ||
| 3511 | (t | ||
| 3512 | (match-string match-pos))))) | ||
| 3513 | ;; see compilation-error-regexp-alist | ||
| 3514 | '((:file . 1) | ||
| 3515 | (:line . 2) | ||
| 3516 | (:column . 3) | ||
| 3517 | (:type . 4) | ||
| 3518 | (:mouse-highlight . 5))) | ||
| 3519 | matches)) | ||
| 3520 | (nreverse matches))) | ||
| 3521 | |||
| 3522 | (ert-deftest compilation-regexp-dashes () | ||
| 3523 | (with-temp-buffer | ||
| 3524 | ;; should match | ||
| 3525 | (insert "error found a -> b\n --> file1.rs:12:34\n\n") | ||
| 3526 | (insert "error[E1234]: found a -> b\n --> file2.rs:12:34\n\n") | ||
| 3527 | (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") | ||
| 3529 | (insert " ::: file5.rs:12:34\n\n") | ||
| 3530 | ;; should not match | ||
| 3531 | (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 | ||
| 3533 | | | ||
| 3534 | 82 | fn duration_ms_since(time: &Option<SystemTime>) -> u128 { | ||
| 3535 | | ------------------------------------------------------- defined here | ||
| 3536 | ... | ||
| 3537 | 132 | self.total_time_ms = duration_ms_since(&self.program_start, 2); | ||
| 3538 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| 3539 | ") | ||
| 3540 | (should (equal | ||
| 3541 | '((("file1.rs" "12" "34" compilation-error "file1.rs:12:34") | ||
| 3542 | ("file2.rs" "12" "34" compilation-error "file2.rs:12:34") | ||
| 3543 | ("file3.rs" "12" "34" compilation-warning "file3.rs:12:34") | ||
| 3544 | ("file4.rs" "12" "34" compilation-info "file4.rs:12:34") | ||
| 3545 | ("file6.rs" "132" "34" compilation-error "file6.rs:132:34")) | ||
| 3546 | (("file5.rs" "12" "34" compilation-info "file5.rs:12:34")) | ||
| 3547 | ((like-previous-one "82" back-to-indentation compilation-info "82") | ||
| 3548 | (like-previous-one "132" back-to-indentation compilation-info "132"))) | ||
| 3549 | (mapcar #'rust-collect-matches | ||
| 3550 | (list rustc-compilation-regexps | ||
| 3551 | rustc-colon-compilation-regexps | ||
| 3552 | rustc-refs-compilation-regexps)))))) | ||
| 3553 | |||
| 3554 | ;; 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. | ||
| 3556 | (require 'elec-pair nil t) | ||
| 3557 | |||
| 3558 | ;; The emacs 23 and 24 versions of ERT do not have test skipping | ||
| 3559 | ;; functionality. So don't even define these tests if elec-pair is | ||
| 3560 | ;; not available. | ||
| 3561 | (when (featurep 'elec-pair) | ||
| 3562 | (defun test-electric-pair-insert (original point-pos char closer) | ||
| 3563 | (let ((old-electric-pair-mode electric-pair-mode)) | ||
| 3564 | (electric-pair-mode 1) | ||
| 3565 | (unwind-protect | ||
| 3566 | (with-temp-buffer | ||
| 3567 | (rust-mode) | ||
| 3568 | (insert original) | ||
| 3569 | (font-lock-flush) | ||
| 3570 | (font-lock-ensure) | ||
| 3571 | |||
| 3572 | (goto-char point-pos) | ||
| 3573 | (deactivate-mark) | ||
| 3574 | (let ((last-command-event char)) (self-insert-command 1)) | ||
| 3575 | (should (equal (char-after) | ||
| 3576 | (or closer (aref original point-pos))))) | ||
| 3577 | (electric-pair-mode (or old-electric-pair-mode 1))))) | ||
| 3578 | |||
| 3579 | (ert-deftest rust-test-electric-pair-generic-fn () | ||
| 3580 | (test-electric-pair-insert "fn foo() { }" 7 ?< ?>)) | ||
| 3581 | |||
| 3582 | (ert-deftest rust-test-electric-pair-impl-param () | ||
| 3583 | (test-electric-pair-insert "impl Foo<T> for Bar<T>" 5 ?< ?>)) | ||
| 3584 | |||
| 3585 | (ert-deftest rust-test-electric-pair-impl-for-type-param () | ||
| 3586 | (test-electric-pair-insert "impl<T> Foo<T> for Bar" 22 ?< ?>)) | ||
| 3587 | |||
| 3588 | (ert-deftest rust-test-electric-pair-lt-expression () | ||
| 3589 | (test-electric-pair-insert "fn foo(bar:i32) -> bool { bar }" 30 ?< nil)) | ||
| 3590 | |||
| 3591 | (ert-deftest rust-test-electric-pair-lt-expression-in-struct-literal () | ||
| 3592 | (test-electric-pair-insert "fn foo(x:i32) -> Bar { Bar { a:(bleh() + whatever::<X>()), b:x } }" 63 ?< nil)) | ||
| 3593 | |||
| 3594 | (ert-deftest rust-test-electric-pair-lt-expression-capitalized-keyword () | ||
| 3595 | (test-electric-pair-insert "fn foo() -> Box" 16 ?< ?>)) | ||
| 3596 | |||
| 3597 | (ert-deftest rust-test-electric-pair-<-> () | ||
| 3598 | (let ((old-electric-pair-mode electric-pair-mode)) | ||
| 3599 | (electric-pair-mode 1) | ||
| 3600 | (unwind-protect | ||
| 3601 | (with-temp-buffer | ||
| 3602 | (electric-pair-mode 1) | ||
| 3603 | (rust-mode) | ||
| 3604 | (mapc (lambda (c) | ||
| 3605 | (let ((last-command-event c)) (self-insert-command 1))) | ||
| 3606 | "tmp<T>") | ||
| 3607 | (should | ||
| 3608 | (equal "tmp<T>" (buffer-substring-no-properties (point-min) | ||
| 3609 | (point-max))))) | ||
| 3610 | (electric-pair-mode (or old-electric-pair-mode 1)))))) | ||
| 3611 | |||
| 3612 | ;; Ensure the byte compiler knows about this function, even though it’s | ||
| 3613 | ;; conditionally-defined. | ||
| 3614 | (declare-function test-electric-pair-insert "rust-mode-tests" | ||
| 3615 | (original point-pos char closer)) | ||
| 3616 | |||
| 3617 | (ert-deftest rust-mode-map () | ||
| 3618 | (with-temp-buffer | ||
| 3619 | (let (from to match (match-count 0)) | ||
| 3620 | (rust-mode) | ||
| 3621 | (describe-buffer-bindings (current-buffer)) | ||
| 3622 | (goto-char (point-min)) | ||
| 3623 | (re-search-forward "Major Mode Bindings") | ||
| 3624 | (setq from (point)) | ||
| 3625 | (re-search-forward "") | ||
| 3626 | (setq to (point)) | ||
| 3627 | (goto-char from) | ||
| 3628 | (while (re-search-forward "^C-c.*$" to t) | ||
| 3629 | (setq match-count (1+ match-count)) | ||
| 3630 | (setq match (match-string 0)) | ||
| 3631 | (eval | ||
| 3632 | `(should | ||
| 3633 | (or | ||
| 3634 | (string-match "Prefix Command" ,match) | ||
| 3635 | (string-match "^C-c C" ,match))) | ||
| 3636 | t)) | ||
| 3637 | (should (< 0 match-count))))) | ||
diff --git a/.emacs.d/lisp/rust-mode.el b/.emacs.d/lisp/rust-mode.el new file mode 100644 index 0000000..80106b8 --- /dev/null +++ b/.emacs.d/lisp/rust-mode.el | |||
| @@ -0,0 +1,1632 @@ | |||
| 1 | ;;; rust-mode.el --- A major-mode for editing Rust source code -*-lexical-binding: t-*- | ||
| 2 | |||
| 3 | ;; Version: 1.0.5 | ||
| 4 | ;; Author: Mozilla | ||
| 5 | ;; Url: https://github.com/rust-lang/rust-mode | ||
| 6 | ;; Keywords: languages | ||
| 7 | ;; Package-Requires: ((emacs "25.1")) | ||
| 8 | |||
| 9 | ;; This file is distributed under the terms of both the MIT license and the | ||
| 10 | ;; Apache License (version 2.0). | ||
| 11 | |||
| 12 | ;;; Commentary: | ||
| 13 | |||
| 14 | ;; This package implements a major-mode for editing Rust source code. | ||
| 15 | |||
| 16 | ;;; Code: | ||
| 17 | |||
| 18 | (eval-when-compile (require 'rx)) | ||
| 19 | |||
| 20 | (defvar rust-load-optional-libraries t | ||
| 21 | "Whether loading `rust-mode' also loads optional libraries. | ||
| 22 | This variable might soon be remove again.") | ||
| 23 | |||
| 24 | (when rust-load-optional-libraries | ||
| 25 | (require 'rust-cargo) | ||
| 26 | (require 'rust-compile) | ||
| 27 | (require 'rust-playpen) | ||
| 28 | (require 'rust-rustfmt)) | ||
| 29 | |||
| 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 | ||
| 51 | |||
| 52 | (defgroup rust-mode nil | ||
| 53 | "Support for Rust code." | ||
| 54 | :link '(url-link "https://www.rust-lang.org/") | ||
| 55 | :group 'languages) | ||
| 56 | |||
| 57 | (defcustom rust-indent-offset 4 | ||
| 58 | "Indent Rust code by this number of spaces." | ||
| 59 | :type 'integer | ||
| 60 | :group 'rust-mode | ||
| 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 | ||
| 73 | :group 'rust-mode | ||
| 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 | |||
| 89 | ;;; Faces | ||
| 90 | |||
| 91 | (define-obsolete-face-alias 'rust-unsafe-face | ||
| 92 | 'rust-unsafe "0.6.0") | ||
| 93 | (define-obsolete-face-alias 'rust-question-mark-face | ||
| 94 | 'rust-question-mark "0.6.0") | ||
| 95 | (define-obsolete-face-alias 'rust-builtin-formatting-macro-face | ||
| 96 | 'rust-builtin-formatting-macro "0.6.0") | ||
| 97 | (define-obsolete-face-alias 'rust-string-interpolation-face | ||
| 98 | 'rust-string-interpolation "0.6.0") | ||
| 99 | |||
| 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 | ||
| 236 | |||
| 237 | (defvar rust-mode-map | ||
| 238 | (let ((map (make-sparse-keymap))) | ||
| 239 | (define-key map (kbd "C-c C-d") #'rust-dbg-wrap-or-unwrap) | ||
| 240 | (when rust-load-optional-libraries | ||
| 241 | (define-key map (kbd "C-c C-c C-u") 'rust-compile) | ||
| 242 | (define-key map (kbd "C-c C-c C-k") 'rust-check) | ||
| 243 | (define-key map (kbd "C-c C-c C-t") 'rust-test) | ||
| 244 | (define-key map (kbd "C-c C-c C-r") 'rust-run) | ||
| 245 | (define-key map (kbd "C-c C-c C-l") 'rust-run-clippy) | ||
| 246 | (define-key map (kbd "C-c C-f") 'rust-format-buffer) | ||
| 247 | (define-key map (kbd "C-c C-n") 'rust-goto-format-problem)) | ||
| 248 | map) | ||
| 249 | "Keymap for Rust major mode.") | ||
| 250 | |||
| 251 | ;;;###autoload | ||
| 252 | (define-derived-mode rust-mode prog-mode "Rust" | ||
| 253 | "Major mode for Rust code. | ||
| 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 | |||
| 277 | ;; Auto indent on } | ||
| 278 | (setq-local electric-indent-chars | ||
| 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 | |||
| 312 | ;;;###autoload | ||
| 313 | (add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-mode)) | ||
| 314 | |||
| 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) | ||
| 1630 | (require 'rust-utils) | ||
| 1631 | |||
| 1632 | ;;; rust-mode.el ends here | ||
diff --git a/.emacs.d/lisp/rust-playpen.el b/.emacs.d/lisp/rust-playpen.el new file mode 100644 index 0000000..1a9e583 --- /dev/null +++ b/.emacs.d/lisp/rust-playpen.el | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | ;;; rust-playpen.el --- Support for the Rust Playground -*- lexical-binding:t -*- | ||
| 2 | ;;; Commentary: | ||
| 3 | |||
| 4 | ;; This library implements support for the Rust Playground, | ||
| 5 | ;; which is hosted at https://play.rust-lang.org and developed | ||
| 6 | ;; at https://github.com/integer32llc/rust-playground. | ||
| 7 | |||
| 8 | ;;; Code: | ||
| 9 | |||
| 10 | (eval-when-compile (require 'url)) | ||
| 11 | |||
| 12 | ;;; Options | ||
| 13 | |||
| 14 | (defcustom rust-playpen-url-format "https://play.rust-lang.org/?code=%s" | ||
| 15 | "Format string to use when submitting code to the playpen." | ||
| 16 | :type 'string | ||
| 17 | :group 'rust-mode) | ||
| 18 | |||
| 19 | (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." | ||
| 21 | :type 'string | ||
| 22 | :group 'rust-mode) | ||
| 23 | |||
| 24 | ;;; Commands | ||
| 25 | |||
| 26 | (defun rust-playpen-region (begin end) | ||
| 27 | "Create a shareable URL for the region from BEGIN to END on the Rust playpen." | ||
| 28 | (interactive "r") | ||
| 29 | (let* ((data (buffer-substring begin end)) | ||
| 30 | (escaped-data (url-hexify-string data)) | ||
| 31 | (escaped-playpen-url (url-hexify-string | ||
| 32 | (format rust-playpen-url-format escaped-data)))) | ||
| 33 | (if (> (length escaped-playpen-url) 5000) | ||
| 34 | (error "encoded playpen data exceeds 5000 character limit (length %s)" | ||
| 35 | (length escaped-playpen-url)) | ||
| 36 | (let ((shortener-url (format rust-shortener-url-format escaped-playpen-url)) | ||
| 37 | (url-request-method "POST")) | ||
| 38 | (url-retrieve shortener-url | ||
| 39 | (lambda (state) | ||
| 40 | ;; filter out the headers etc. included at the | ||
| 41 | ;; start of the buffer: the relevant text | ||
| 42 | ;; (shortened url or error message) is exactly | ||
| 43 | ;; the last line. | ||
| 44 | (goto-char (point-max)) | ||
| 45 | (let ((last-line (thing-at-point 'line t)) | ||
| 46 | (err (plist-get state :error))) | ||
| 47 | (kill-buffer) | ||
| 48 | (if err | ||
| 49 | (error "failed to shorten playpen url: %s" last-line) | ||
| 50 | (message "%s" last-line))))))))) | ||
| 51 | |||
| 52 | (defun rust-playpen-buffer () | ||
| 53 | "Create a shareable URL for the contents of the buffer on the Rust playpen." | ||
| 54 | (interactive) | ||
| 55 | (rust-playpen-region (point-min) (point-max))) | ||
| 56 | |||
| 57 | ;;; _ | ||
| 58 | (provide 'rust-playpen) | ||
| 59 | ;;; rust-playpen.el ends here | ||
diff --git a/.emacs.d/lisp/rust-rustfmt.el b/.emacs.d/lisp/rust-rustfmt.el new file mode 100644 index 0000000..bb88647 --- /dev/null +++ b/.emacs.d/lisp/rust-rustfmt.el | |||
| @@ -0,0 +1,372 @@ | |||
| 1 | ;;; rust-rustfmt.el --- Support for rustfmt -*- lexical-binding:t -*- | ||
| 2 | ;;; Commentary: | ||
| 3 | |||
| 4 | ;; This library implements support for "rustfmt", a tool for | ||
| 5 | ;; formatting Rust code according to style guidelines. | ||
| 6 | |||
| 7 | ;;; Code: | ||
| 8 | ;;; Options | ||
| 9 | |||
| 10 | (defcustom rust-format-on-save nil | ||
| 11 | "Format future rust buffers before saving using rustfmt." | ||
| 12 | :type 'boolean | ||
| 13 | :safe #'booleanp | ||
| 14 | :group 'rust-mode) | ||
| 15 | |||
| 16 | (defcustom rust-format-show-buffer t | ||
| 17 | "Show *rustfmt* buffer if formatting detected problems." | ||
| 18 | :type 'boolean | ||
| 19 | :safe #'booleanp | ||
| 20 | :group 'rust-mode) | ||
| 21 | |||
| 22 | (defcustom rust-format-goto-problem t | ||
| 23 | "Jump to location of first detected problem when formatting buffer." | ||
| 24 | :type 'boolean | ||
| 25 | :safe #'booleanp | ||
| 26 | :group 'rust-mode) | ||
| 27 | |||
| 28 | (defcustom rust-rustfmt-bin "rustfmt" | ||
| 29 | "Path to rustfmt executable." | ||
| 30 | :type 'string | ||
| 31 | :group 'rust-mode) | ||
| 32 | |||
| 33 | (defcustom rust-rustfmt-switches '("--edition" "2018") | ||
| 34 | "Arguments to pass when invoking the `rustfmt' executable." | ||
| 35 | :type '(repeat string) | ||
| 36 | :group 'rust-mode) | ||
| 37 | |||
| 38 | ;;; _ | ||
| 39 | |||
| 40 | (defconst rust-rustfmt-buffername "*rustfmt*") | ||
| 41 | |||
| 42 | (defun rust--format-call (buf) | ||
| 43 | "Format BUF using rustfmt." | ||
| 44 | (with-current-buffer (get-buffer-create rust-rustfmt-buffername) | ||
| 45 | (view-mode +1) | ||
| 46 | (let ((inhibit-read-only t)) | ||
| 47 | (erase-buffer) | ||
| 48 | (insert-buffer-substring buf) | ||
| 49 | (let* ((tmpf (make-temp-file "rustfmt")) | ||
| 50 | (ret (apply #'call-process-region | ||
| 51 | (point-min) | ||
| 52 | (point-max) | ||
| 53 | rust-rustfmt-bin | ||
| 54 | t | ||
| 55 | `(t ,tmpf) | ||
| 56 | nil | ||
| 57 | rust-rustfmt-switches))) | ||
| 58 | (unwind-protect | ||
| 59 | (cond | ||
| 60 | ((zerop ret) | ||
| 61 | (if (not (string= (buffer-string) | ||
| 62 | (with-current-buffer buf (buffer-string)))) | ||
| 63 | ;; replace-buffer-contents was in emacs 26.1, but it | ||
| 64 | ;; was broken for non-ASCII strings, so we need 26.2. | ||
| 65 | (if (and (fboundp 'replace-buffer-contents) | ||
| 66 | (version<= "26.2" emacs-version)) | ||
| 67 | (with-current-buffer buf | ||
| 68 | (replace-buffer-contents rust-rustfmt-buffername)) | ||
| 69 | (copy-to-buffer buf (point-min) (point-max)))) | ||
| 70 | (kill-buffer)) | ||
| 71 | ((= ret 3) | ||
| 72 | (if (not (string= (buffer-string) | ||
| 73 | (with-current-buffer buf (buffer-string)))) | ||
| 74 | (copy-to-buffer buf (point-min) (point-max))) | ||
| 75 | (erase-buffer) | ||
| 76 | (insert-file-contents tmpf) | ||
| 77 | (rust--format-fix-rustfmt-buffer (buffer-name buf)) | ||
| 78 | (error "Rustfmt could not format some lines, see %s buffer for details" | ||
| 79 | rust-rustfmt-buffername)) | ||
| 80 | (t | ||
| 81 | (erase-buffer) | ||
| 82 | (insert-file-contents tmpf) | ||
| 83 | (rust--format-fix-rustfmt-buffer (buffer-name buf)) | ||
| 84 | (error "Rustfmt failed, see %s buffer for details" | ||
| 85 | rust-rustfmt-buffername)))) | ||
| 86 | (delete-file tmpf))))) | ||
| 87 | |||
| 88 | ;; Since we run rustfmt through stdin we get <stdin> markers in the | ||
| 89 | ;; output. This replaces them with the buffer name instead. | ||
| 90 | (defun rust--format-fix-rustfmt-buffer (buffer-name) | ||
| 91 | (save-match-data | ||
| 92 | (with-current-buffer (get-buffer rust-rustfmt-buffername) | ||
| 93 | (let ((inhibit-read-only t) | ||
| 94 | (fixed (format "--> %s:" buffer-name))) | ||
| 95 | (goto-char (point-min)) | ||
| 96 | (while (re-search-forward "--> \\(?:<stdin>\\|stdin\\):" nil t) | ||
| 97 | (replace-match fixed)))))) | ||
| 98 | |||
| 99 | ;; If rust-mode has been configured to navigate to source of the error | ||
| 100 | ;; or display it, do so -- and return true. Otherwise return nil to | ||
| 101 | ;; indicate nothing was done. | ||
| 102 | (defun rust--format-error-handler () | ||
| 103 | (let ((ok nil)) | ||
| 104 | (when rust-format-show-buffer | ||
| 105 | (display-buffer (get-buffer rust-rustfmt-buffername)) | ||
| 106 | (setq ok t)) | ||
| 107 | (when rust-format-goto-problem | ||
| 108 | (rust-goto-format-problem) | ||
| 109 | (setq ok t)) | ||
| 110 | ok)) | ||
| 111 | |||
| 112 | (defun rust-goto-format-problem () | ||
| 113 | "Jumps to problem reported by rustfmt, if any. | ||
| 114 | |||
| 115 | In case of multiple problems cycles through them. Displays the | ||
| 116 | rustfmt complain in the echo area." | ||
| 117 | (interactive) | ||
| 118 | ;; This uses position in *rustfmt* buffer to know which is the next | ||
| 119 | ;; error to jump to, and source: line in the buffer to figure which | ||
| 120 | ;; buffer it is from. | ||
| 121 | (let ((rustfmt (get-buffer rust-rustfmt-buffername))) | ||
| 122 | (if (not rustfmt) | ||
| 123 | (message "No %s, no problems." rust-rustfmt-buffername) | ||
| 124 | (let ((target-buffer (with-current-buffer rustfmt | ||
| 125 | (save-excursion | ||
| 126 | (goto-char (point-min)) | ||
| 127 | (when (re-search-forward "--> \\([^:]+\\):" nil t) | ||
| 128 | (match-string 1))))) | ||
| 129 | (target-point (with-current-buffer rustfmt | ||
| 130 | ;; No save-excursion, this is how we cycle through! | ||
| 131 | (let ((regex "--> [^:]+:\\([0-9]+\\):\\([0-9]+\\)")) | ||
| 132 | (when (or (re-search-forward regex nil t) | ||
| 133 | (progn (goto-char (point-min)) | ||
| 134 | (re-search-forward regex nil t))) | ||
| 135 | (cons (string-to-number (match-string 1)) | ||
| 136 | (string-to-number (match-string 2))))))) | ||
| 137 | (target-problem (with-current-buffer rustfmt | ||
| 138 | (save-excursion | ||
| 139 | (when (re-search-backward "^error:.+\n" nil t) | ||
| 140 | (forward-char (length "error: ")) | ||
| 141 | (let ((p0 (point))) | ||
| 142 | (if (re-search-forward "\nerror:.+\n" nil t) | ||
| 143 | (buffer-substring p0 (point)) | ||
| 144 | (buffer-substring p0 (point-max))))))))) | ||
| 145 | (when (and target-buffer (get-buffer target-buffer) target-point) | ||
| 146 | (switch-to-buffer target-buffer) | ||
| 147 | (goto-char (point-min)) | ||
| 148 | (forward-line (1- (car target-point))) | ||
| 149 | (forward-char (1- (cdr target-point)))) | ||
| 150 | (message target-problem))))) | ||
| 151 | |||
| 152 | (defconst rust--format-word "\ | ||
| 153 | \\b\\(else\\|enum\\|fn\\|for\\|if\\|let\\|loop\\|\ | ||
| 154 | match\\|struct\\|union\\|unsafe\\|while\\)\\b") | ||
| 155 | (defconst rust--format-line "\\(\n\\)") | ||
| 156 | |||
| 157 | ;; Counts number of matches of regex beginning up to max-beginning, | ||
| 158 | ;; leaving the point at the beginning of the last match. | ||
| 159 | (defun rust--format-count (regex max-beginning) | ||
| 160 | (let ((count 0) | ||
| 161 | save-point | ||
| 162 | beginning) | ||
| 163 | (while (and (< (point) max-beginning) | ||
| 164 | (re-search-forward regex max-beginning t)) | ||
| 165 | (setq count (1+ count)) | ||
| 166 | (setq beginning (match-beginning 1))) | ||
| 167 | ;; try one more in case max-beginning lies in the middle of a match | ||
| 168 | (setq save-point (point)) | ||
| 169 | (when (re-search-forward regex nil t) | ||
| 170 | (let ((try-beginning (match-beginning 1))) | ||
| 171 | (if (> try-beginning max-beginning) | ||
| 172 | (goto-char save-point) | ||
| 173 | (setq count (1+ count)) | ||
| 174 | (setq beginning try-beginning)))) | ||
| 175 | (when beginning (goto-char beginning)) | ||
| 176 | count)) | ||
| 177 | |||
| 178 | ;; Gets list describing pos or (point). | ||
| 179 | ;; The list contains: | ||
| 180 | ;; 1. the number of matches of rust--format-word, | ||
| 181 | ;; 2. the number of matches of rust--format-line after that, | ||
| 182 | ;; 3. the number of columns after that. | ||
| 183 | (defun rust--format-get-loc (buffer &optional pos) | ||
| 184 | (with-current-buffer buffer | ||
| 185 | (save-excursion | ||
| 186 | (let ((pos (or pos (point))) | ||
| 187 | words lines columns) | ||
| 188 | (goto-char (point-min)) | ||
| 189 | (setq words (rust--format-count rust--format-word pos)) | ||
| 190 | (setq lines (rust--format-count rust--format-line pos)) | ||
| 191 | (if (> lines 0) | ||
| 192 | (if (= (point) pos) | ||
| 193 | (setq columns -1) | ||
| 194 | (forward-char 1) | ||
| 195 | (goto-char pos) | ||
| 196 | (setq columns (current-column))) | ||
| 197 | (let ((initial-column (current-column))) | ||
| 198 | (goto-char pos) | ||
| 199 | (setq columns (- (current-column) initial-column)))) | ||
| 200 | (list words lines columns))))) | ||
| 201 | |||
| 202 | ;; Moves the point forward by count matches of regex up to max-pos, | ||
| 203 | ;; and returns new max-pos making sure final position does not include another match. | ||
| 204 | (defun rust--format-forward (regex count max-pos) | ||
| 205 | (when (< (point) max-pos) | ||
| 206 | (let ((beginning (point))) | ||
| 207 | (while (> count 0) | ||
| 208 | (setq count (1- count)) | ||
| 209 | (re-search-forward regex nil t) | ||
| 210 | (setq beginning (match-beginning 1))) | ||
| 211 | (when (re-search-forward regex nil t) | ||
| 212 | (setq max-pos (min max-pos (match-beginning 1)))) | ||
| 213 | (goto-char beginning))) | ||
| 214 | max-pos) | ||
| 215 | |||
| 216 | ;; Gets the position from a location list obtained using rust--format-get-loc. | ||
| 217 | (defun rust--format-get-pos (buffer loc) | ||
| 218 | (with-current-buffer buffer | ||
| 219 | (save-excursion | ||
| 220 | (goto-char (point-min)) | ||
| 221 | (let ((max-pos (point-max)) | ||
| 222 | (words (pop loc)) | ||
| 223 | (lines (pop loc)) | ||
| 224 | (columns (pop loc))) | ||
| 225 | (setq max-pos (rust--format-forward rust--format-word words max-pos)) | ||
| 226 | (setq max-pos (rust--format-forward rust--format-line lines max-pos)) | ||
| 227 | (when (> lines 0) (forward-char)) | ||
| 228 | (let ((initial-column (current-column)) | ||
| 229 | (save-point (point))) | ||
| 230 | (move-end-of-line nil) | ||
| 231 | (when (> (current-column) (+ initial-column columns)) | ||
| 232 | (goto-char save-point) | ||
| 233 | (forward-char columns))) | ||
| 234 | (min (point) max-pos))))) | ||
| 235 | |||
| 236 | (defun rust-format-diff-buffer () | ||
| 237 | "Show diff to current buffer from rustfmt. | ||
| 238 | |||
| 239 | Return the created process." | ||
| 240 | (interactive) | ||
| 241 | (unless (executable-find rust-rustfmt-bin) | ||
| 242 | (error "Could not locate executable %S" rust-rustfmt-bin)) | ||
| 243 | (let* ((buffer | ||
| 244 | (with-current-buffer | ||
| 245 | (get-buffer-create "*rustfmt-diff*") | ||
| 246 | (let ((inhibit-read-only t)) | ||
| 247 | (erase-buffer)) | ||
| 248 | (current-buffer))) | ||
| 249 | (proc | ||
| 250 | (apply #'start-process | ||
| 251 | "rustfmt-diff" | ||
| 252 | buffer | ||
| 253 | rust-rustfmt-bin | ||
| 254 | "--check" | ||
| 255 | (cons (buffer-file-name) | ||
| 256 | rust-rustfmt-switches)))) | ||
| 257 | (set-process-sentinel proc #'rust-format-diff-buffer-sentinel) | ||
| 258 | proc)) | ||
| 259 | |||
| 260 | (defun rust-format-diff-buffer-sentinel (process _e) | ||
| 261 | (when (eq 'exit (process-status process)) | ||
| 262 | (if (> (process-exit-status process) 0) | ||
| 263 | (with-current-buffer "*rustfmt-diff*" | ||
| 264 | (let ((inhibit-read-only t)) | ||
| 265 | (diff-mode)) | ||
| 266 | (pop-to-buffer (current-buffer))) | ||
| 267 | (message "rustfmt check passed.")))) | ||
| 268 | |||
| 269 | (defun rust--format-buffer-using-replace-buffer-contents () | ||
| 270 | (condition-case err | ||
| 271 | (progn | ||
| 272 | (rust--format-call (current-buffer)) | ||
| 273 | (message "Formatted buffer with rustfmt.")) | ||
| 274 | (error | ||
| 275 | (or (rust--format-error-handler) | ||
| 276 | (signal (car err) (cdr err)))))) | ||
| 277 | |||
| 278 | (defun rust--format-buffer-saving-position-manually () | ||
| 279 | (let* ((current (current-buffer)) | ||
| 280 | (base (or (buffer-base-buffer current) current)) | ||
| 281 | buffer-loc | ||
| 282 | window-loc) | ||
| 283 | (dolist (buffer (buffer-list)) | ||
| 284 | (when (or (eq buffer base) | ||
| 285 | (eq (buffer-base-buffer buffer) base)) | ||
| 286 | (push (list buffer | ||
| 287 | (rust--format-get-loc buffer nil)) | ||
| 288 | buffer-loc))) | ||
| 289 | (dolist (frame (frame-list)) | ||
| 290 | (dolist (window (window-list frame)) | ||
| 291 | (let ((buffer (window-buffer window))) | ||
| 292 | (when (or (eq buffer base) | ||
| 293 | (eq (buffer-base-buffer buffer) base)) | ||
| 294 | (let ((start (window-start window)) | ||
| 295 | (point (window-point window))) | ||
| 296 | (push (list window | ||
| 297 | (rust--format-get-loc buffer start) | ||
| 298 | (rust--format-get-loc buffer point)) | ||
| 299 | window-loc)))))) | ||
| 300 | (condition-case err | ||
| 301 | (unwind-protect | ||
| 302 | ;; save and restore window start position | ||
| 303 | ;; after reformatting | ||
| 304 | ;; to avoid the disturbing scrolling | ||
| 305 | (let ((w-start (window-start))) | ||
| 306 | (rust--format-call (current-buffer)) | ||
| 307 | (set-window-start (selected-window) w-start) | ||
| 308 | (message "Formatted buffer with rustfmt.")) | ||
| 309 | (dolist (loc buffer-loc) | ||
| 310 | (let* ((buffer (pop loc)) | ||
| 311 | (pos (rust--format-get-pos buffer (pop loc)))) | ||
| 312 | (with-current-buffer buffer | ||
| 313 | (goto-char pos)))) | ||
| 314 | (dolist (loc window-loc) | ||
| 315 | (let* ((window (pop loc)) | ||
| 316 | (buffer (window-buffer window)) | ||
| 317 | (start (rust--format-get-pos buffer (pop loc))) | ||
| 318 | (pos (rust--format-get-pos buffer (pop loc)))) | ||
| 319 | (unless (eq buffer current) | ||
| 320 | (set-window-start window start)) | ||
| 321 | (set-window-point window pos)))) | ||
| 322 | (error | ||
| 323 | (or (rust--format-error-handler) | ||
| 324 | (signal (car err) (cdr err))))))) | ||
| 325 | |||
| 326 | (defun rust-format-buffer () | ||
| 327 | "Format the current buffer using rustfmt." | ||
| 328 | (interactive) | ||
| 329 | (unless (executable-find rust-rustfmt-bin) | ||
| 330 | (error "Could not locate executable \"%s\"" rust-rustfmt-bin)) | ||
| 331 | ;; If emacs version >= 26.2, we can use replace-buffer-contents to | ||
| 332 | ;; preserve location and markers in buffer, otherwise we can try to | ||
| 333 | ;; save locations as best we can, though we still lose markers. | ||
| 334 | (save-excursion | ||
| 335 | (if (version<= "26.2" emacs-version) | ||
| 336 | (rust--format-buffer-using-replace-buffer-contents) | ||
| 337 | (rust--format-buffer-saving-position-manually)))) | ||
| 338 | |||
| 339 | (defun rust-enable-format-on-save () | ||
| 340 | "Enable formatting using rustfmt when saving buffer." | ||
| 341 | (interactive) | ||
| 342 | (setq-local rust-format-on-save t)) | ||
| 343 | |||
| 344 | (defun rust-disable-format-on-save () | ||
| 345 | "Disable formatting using rustfmt when saving buffer." | ||
| 346 | (interactive) | ||
| 347 | (setq-local rust-format-on-save nil)) | ||
| 348 | |||
| 349 | ;;; Hooks | ||
| 350 | |||
| 351 | (defun rust-before-save-method () | ||
| 352 | (when rust-format-on-save | ||
| 353 | (condition-case e | ||
| 354 | (rust-format-buffer) | ||
| 355 | (message (format "rust-before-save-hook: %S %S" | ||
| 356 | (car e) | ||
| 357 | (cdr e)))))) | ||
| 358 | |||
| 359 | (defun rust-after-save-method () | ||
| 360 | (when rust-format-on-save | ||
| 361 | (if (not (executable-find rust-rustfmt-bin)) | ||
| 362 | (error "Could not locate executable \"%s\"" rust-rustfmt-bin) | ||
| 363 | (when (get-buffer rust-rustfmt-buffername) | ||
| 364 | ;; KLDUGE: re-run the error handlers -- otherwise message area | ||
| 365 | ;; would show "Wrote ..." instead of the error description. | ||
| 366 | (or (rust--format-error-handler) | ||
| 367 | (message "rustfmt detected problems, see %s for more." | ||
| 368 | rust-rustfmt-buffername)))))) | ||
| 369 | |||
| 370 | ;;; _ | ||
| 371 | (provide 'rust-rustfmt) | ||
| 372 | ;;; rust-rustfmt.el ends here | ||
diff --git a/.emacs.d/lisp/rust-utils.el b/.emacs.d/lisp/rust-utils.el new file mode 100644 index 0000000..41f1ba8 --- /dev/null +++ b/.emacs.d/lisp/rust-utils.el | |||
| @@ -0,0 +1,91 @@ | |||
| 1 | ;;; rust-utils.el --- Various Rust utilities -*- lexical-binding:t -*- | ||
| 2 | ;;; Commentary: | ||
| 3 | |||
| 4 | ;; This library implements various utilities for dealing with Rust | ||
| 5 | ;; code. | ||
| 6 | |||
| 7 | ;;; Code: | ||
| 8 | |||
| 9 | (require 'thingatpt) | ||
| 10 | |||
| 11 | (require 'rust-mode) ; for `rust-in-str' and `rust-looking-back-str' | ||
| 12 | |||
| 13 | ;;; Promote module | ||
| 14 | |||
| 15 | (defun rust-promote-module-into-dir () | ||
| 16 | "Promote the module file visited by the current buffer into its own directory. | ||
| 17 | |||
| 18 | For example, if the current buffer is visiting the file `foo.rs', | ||
| 19 | then this function creates the directory `foo' and renames the | ||
| 20 | file to `foo/mod.rs'. The current buffer will be updated to | ||
| 21 | visit the new file." | ||
| 22 | (interactive) | ||
| 23 | (let ((filename (buffer-file-name))) | ||
| 24 | (if (not filename) | ||
| 25 | (message "Buffer is not visiting a file.") | ||
| 26 | (if (string-equal (file-name-nondirectory filename) "mod.rs") | ||
| 27 | (message "Won't promote a module file already named mod.rs.") | ||
| 28 | (let* ((basename (file-name-sans-extension | ||
| 29 | (file-name-nondirectory filename))) | ||
| 30 | (mod-dir (file-name-as-directory | ||
| 31 | (concat (file-name-directory filename) basename))) | ||
| 32 | (new-name (concat mod-dir "mod.rs"))) | ||
| 33 | (mkdir mod-dir t) | ||
| 34 | (rename-file filename new-name 1) | ||
| 35 | (set-visited-file-name new-name)))))) | ||
| 36 | |||
| 37 | ;;; dbg! macro | ||
| 38 | |||
| 39 | (defun rust-insert-dbg () | ||
| 40 | "Insert the dbg! macro." | ||
| 41 | (cond ((region-active-p) | ||
| 42 | (when (< (mark) (point)) | ||
| 43 | (exchange-point-and-mark)) | ||
| 44 | (let ((old-point (point))) | ||
| 45 | (insert-parentheses) | ||
| 46 | (goto-char old-point))) | ||
| 47 | (t | ||
| 48 | (when (rust-in-str) | ||
| 49 | (up-list -1 t t)) | ||
| 50 | (insert "(") | ||
| 51 | (forward-sexp) | ||
| 52 | (insert ")") | ||
| 53 | (backward-sexp))) | ||
| 54 | (insert "dbg!")) | ||
| 55 | |||
| 56 | ;;;###autoload | ||
| 57 | (defun rust-dbg-wrap-or-unwrap () | ||
| 58 | "Either remove or add the dbg! macro." | ||
| 59 | (interactive) | ||
| 60 | (save-excursion | ||
| 61 | (if (region-active-p) | ||
| 62 | (rust-insert-dbg) | ||
| 63 | |||
| 64 | (let ((beginning-of-symbol (ignore-errors (beginning-of-thing 'symbol)))) | ||
| 65 | (when beginning-of-symbol | ||
| 66 | (goto-char beginning-of-symbol))) | ||
| 67 | |||
| 68 | (let ((dbg-point (save-excursion | ||
| 69 | (or (and (looking-at-p "dbg!") (+ 4 (point))) | ||
| 70 | (ignore-errors | ||
| 71 | (while (not (rust-looking-back-str "dbg!")) | ||
| 72 | (backward-up-list)) | ||
| 73 | (point)))))) | ||
| 74 | (cond (dbg-point | ||
| 75 | (goto-char dbg-point) | ||
| 76 | (delete-char -4) | ||
| 77 | (delete-pair)) | ||
| 78 | (t (rust-insert-dbg))))))) | ||
| 79 | |||
| 80 | (defun rust-toggle-mutability () | ||
| 81 | "Toggles the mutability of the variable defined on the current line" | ||
| 82 | (interactive) | ||
| 83 | (save-excursion | ||
| 84 | (back-to-indentation) | ||
| 85 | (forward-word) | ||
| 86 | (if (string= " mut" (buffer-substring (point) (+ (point) 4))) | ||
| 87 | (delete-region (point) (+ (point) 4)) | ||
| 88 | (insert " mut")))) | ||
| 89 | ;;; _ | ||
| 90 | (provide 'rust-utils) | ||
| 91 | ;;; rust-utils.el ends here | ||
diff --git a/.emacs.d/lisp/yaml-mode.el b/.emacs.d/lisp/yaml-mode.el index 666f128..07ac224 100644 --- a/.emacs.d/lisp/yaml-mode.el +++ b/.emacs.d/lisp/yaml-mode.el | |||
| @@ -306,8 +306,8 @@ artificially limited to the value of | |||
| 306 | (if (eolp) (goto-char (1+ (point)))) | 306 | (if (eolp) (goto-char (1+ (point)))) |
| 307 | (unless (or (eobp) (>= (point) bound)) | 307 | (unless (or (eobp) (>= (point) bound)) |
| 308 | (let ((begin (point)) | 308 | (let ((begin (point)) |
| 309 | (end (min (1+ (point-at-eol)) bound))) | 309 | (end (min (1+ (line-end-position)) bound))) |
| 310 | (goto-char (point-at-bol)) | 310 | (goto-char (line-beginning-position)) |
| 311 | (while (and (looking-at yaml-blank-line-re) | 311 | (while (and (looking-at yaml-blank-line-re) |
| 312 | (not (bobp))) | 312 | (not (bobp))) |
| 313 | (forward-line -1)) | 313 | (forward-line -1)) |
| @@ -426,7 +426,7 @@ margin." | |||
| 426 | otherwise do nothing." | 426 | otherwise do nothing." |
| 427 | (interactive) | 427 | (interactive) |
| 428 | (save-excursion | 428 | (save-excursion |
| 429 | (goto-char (point-at-bol)) | 429 | (goto-char (line-beginning-position)) |
| 430 | (while (and (looking-at-p yaml-blank-line-re) (not (bobp))) | 430 | (while (and (looking-at-p yaml-blank-line-re) (not (bobp))) |
| 431 | (forward-line -1)) | 431 | (forward-line -1)) |
| 432 | (let ((nlines yaml-block-literal-search-lines) | 432 | (let ((nlines yaml-block-literal-search-lines) |
