summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimeon Simeonov2022-10-12 23:21:07 +0200
committerSimeon Simeonov2022-10-12 23:21:07 +0200
commit3ba23d3758e06d95b14ed4e89607e258d15ddbf7 (patch)
treef41ec9ee69791270c8e75e87d7893ad90dd3189b
parent4efa8ef4d3617973b2883605461ffa514675e5b2 (diff)
Add Rust mode and update lua-mode, markdown-mode and yaml-mode
-rw-r--r--.emacs6
-rw-r--r--.emacs.d/lisp/lua-mode.el991
-rw-r--r--.emacs.d/lisp/markdown-mode.el149
-rw-r--r--.emacs.d/lisp/rust-cargo.el124
-rw-r--r--.emacs.d/lisp/rust-compile.el83
-rw-r--r--.emacs.d/lisp/rust-mode-tests.el3637
-rw-r--r--.emacs.d/lisp/rust-mode.el1632
-rw-r--r--.emacs.d/lisp/rust-playpen.el59
-rw-r--r--.emacs.d/lisp/rust-rustfmt.el372
-rw-r--r--.emacs.d/lisp/rust-utils.el91
-rw-r--r--.emacs.d/lisp/yaml-mode.el6
11 files changed, 6716 insertions, 434 deletions
diff --git a/.emacs b/.emacs
index fa2617e..3946dcd 100644
--- a/.emacs
+++ b/.emacs
@@ -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
272Can be a string, where it denotes a command to be executed to
273start Lua process, or a (HOST . PORT) cons, that can be used to
274connect 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.
398Otherwise leading amount of whitespace on each line is preserved." 428Otherwise 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
435align with the column of the opening parenthesis, rather than
436just 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
443parenthesis. If nil, close parenthesis are aligned to the
444beginning 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
508This is a compilation of 5.1, 5.2 and 5.3 builtins taken from the 555This is a compilation of 5.1, 5.2 and 5.3 builtins taken from the
509index of respective Lua reference manuals.") 556index 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
515It's supposed to match sequences with following EBNF:
516
517ELT-REGEXP { SEP-REGEXP ELT-REGEXP } END-REGEXP
518
519The sequence is parsed one token at a time. If non-nil is
520returned, `match-data' will have one or more of the following
521groups set according to next matched token:
522
5231. matched element token
5242. unmatched garbage characters
5253. misplaced token (i.e. SEP-REGEXP when ELT-REGEXP is expected)
5264. matched separator token
5275. matched end token
528
529Blanks & comments between tokens are silently skipped.
530Groups 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
808Emacs syntax framework does not consider comment delimiters as
809part of the comment itself, but for this package it is useful to
810consider 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
832If point is not inside a comment, return nil." 818If 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
843If point is not inside string or comment, return nil." 835If 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
967With BACKWARD non-nil, return non-nil if point is within LIMIT
968going backward.
969
970If 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
981By default, point is placed after the string, with BACKWARD it is
982placed 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
973Direction is one of 'forward and 'backward. 995Direction is one of 'forward and 'backward.
974By default, matches in comments and strings are ignored, but what to ignore is 996
975configurable by specifying ignore-p. If the regexp is found, returns point 997Matches in comments and strings are ignored. If the regexp is
976position, nil otherwise. 998found, returns point position, nil otherwise."
977ignore-p returns true if the match at the current point position should be 999 (let ((search-func (if (eq direction 'forward)
978ignored, 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
1159is no block open/close open." 1193is 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
1208If LIMIT is given, stop at it or before.
1209
1210Return 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
1226If LIMIT is given, stop at it or before.
1227
1228Return 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
1173Moves point 1 line forward (or backward) skipping lines that contain 1249Moves point 1 line forward (or backward) skipping lines that contain
1174no Lua code besides comments. The point is put to the beginning of 1250no Lua code besides comments. The point is put to the beginning of
1175the line. 1251the line.
1176 1252
1177Returns final value of point as integer or nil if operation failed." 1253Returns 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
1273More specifically, return the point in the line that is continued. 1409More specifically, return the point in the line that is continued.
1274The criteria for a continuing statement are: 1410The 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
1444This 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.
1370This function has special case handling for two tokens: remove-matching, 1580This function has special case handling for two tokens: remove-matching,
1371and replace-matching. These two tokens are cleanup tokens that remove or 1581and replace-matching. These two tokens are cleanup tokens that remove or
1372alter the effect of a previously recorded indentation info. 1582alter 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
1380when a middle-of the block (the only case is 'else') is seen on the same line 1590when a middle-of the block (the only case is 'else') is seen on the same line
1381the block is opened." 1591the 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
1413indentation level, or indentation to some absolute column. This information 1633indentation level, or indentation to some absolute column. This information
1414is collected in a list of indentation info pairs, which denote absolute 1634is collected in a list of indentation info pairs, which denote absolute
1415and relative each, and the shift/column to indent to." 1635and 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
1453lua-calculate-indentation-info. Returns either the relative indentation 1674lua-calculate-indentation-info. Returns either the relative indentation
1454shift, or the absolute column to indent to." 1675shift, 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
1562in the sequence. 1815in the sequence.
1563 1816
1564If not, return nil." 1817If 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."
1706PROGRAM defaults to NAME, which defaults to `lua-default-application'. 1977PROGRAM defaults to NAME, which defaults to `lua-default-application'.
1707When called interactively, switch to the process buffer." 1978When 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.
2144Return 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].
191Possible settings are nil, t, and `indent-and-new-item'. 191Possible settings are nil, t, and \\='indent-and-new-item.
192 192
193When non-nil, pressing \\[newline] will call `newline-and-indent' 193When non-nil, pressing \\[newline] will call `newline-and-indent'
194to indent the following line according to the context using 194to 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
197a newline without indentation. 197a newline without indentation.
198 198
199When set to `indent-and-new-item' and the point is in a list item 199When set to \\='indent-and-new-item and the point is in a list item
200when \\[newline] is pressed, the list will be continued on the next 200when \\[newline] is pressed, the list will be continued on the next
201line, where a new item will be inserted. 201line, 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.
493The default value is `any', which instructs Emacs to use 493The 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
495windows based on the values of `split-width-threshold' and 495windows 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
497vertically split (left and right) windows, set this to `vertical' 497vertically split (left and right) windows, set this to \\='vertical
498or `right'. To force horizontally split (top and bottom) windows, 498or \\='right. To force horizontally split (top and bottom) windows,
499set this to `horizontal' or `below'. 499set this to \\='horizontal or \\='below.
500 500
501If this value is `any' and `display-buffer-alist' is set then 501If 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'.
519If set to `delete-on-export', delete on every export. When set to 519If 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.
867This matches typical bracketed [[WikiLinks]] as well as `aliased' 867This matches typical bracketed [[WikiLinks]] as well as \\='aliased
868wiki links of the form [[PageName|link text]]. 868wiki links of the form [[PageName|link text]].
869The meanings of the first and second components depend 869The meanings of the first and second components depend
870on the value of `markdown-wiki-link-alias-first'. 870on the value of `markdown-wiki-link-alias-first'.
@@ -1344,7 +1344,7 @@ block construct when it is matched using
1344to the text matching START-REGEX-OR-FUN, END-PROPERTY to END-REGEX-OR-FUN, and 1344to the text matching START-REGEX-OR-FUN, END-PROPERTY to END-REGEX-OR-FUN, and
1345MIDDLE-PROPERTY to the text in between the two. The value of *-PROPERTY is the 1345MIDDLE-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
1347MIDDLE-PROPERTY, the value is a false match data of the form `(begin end)', with 1347MIDDLE-PROPERTY, the value is a false match data of the form \\='(begin end), with
1348begin and end set to the edges of the \"middle\" text. This makes fontification 1348begin and end set to the edges of the \"middle\" text. This makes fontification
1349easier.") 1349easier.")
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
2565If the point is not inside a list item, return nil." 2565If 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.
2650Does not include trailing newlines when THING is `line'. Handles the 2650Does not include trailing newlines when THING is \\='line. Handles the
2651end of buffer case by setting both endpoints equal to the value of 2651end 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.
2653Return bounds of form (beg . end) if THING is found, or nil otherwise." 2653Return 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."
2764Uses text properties at the beginning of the line position. 2764Uses text properties at the beginning of the line position.
2765This includes pre blocks, tilde-fenced code blocks, and GFM 2765This includes pre blocks, tilde-fenced code blocks, and GFM
2766quoted code blocks. Return nil otherwise." 2766quoted 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.
2797This may be useful for tables and Pandoc's line_blocks extension." 2797This 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.
3031Use data stored in `markdown-pre' text property during syntax 3031Use data stored in \\='markdown-pre text property during syntax
3032analysis." 3032analysis."
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.
3037Use data stored in `markdown-gfm-code' text property during syntax 3037Use data stored in \\='markdown-gfm-code text property during syntax
3038analysis." 3038analysis."
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.
3059Use data stored in `markdown-blockquote' text property during syntax 3059Use data stored in \\='markdown-blockquote text property during syntax
3060analysis." 3060analysis."
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,
4982indent according to value of `markdown-indent-on-enter'. 4982indent according to value of `markdown-indent-on-enter'.
4983When it is nil, simply call `newline'. Otherwise, indent the next line 4983When it is nil, simply call `newline'. Otherwise, indent the next line
4984following RET using `markdown-indent-line'. Furthermore, when it 4984following RET using `markdown-indent-line'. Furthermore, when it
4985is set to `indent-and-new-item' and the point is in a list item, 4985is set to \\='indent-and-new-item and the point is in a list item,
4986start a new item with the same indentation. If the point is in an 4986start a new item with the same indentation. If the point is in an
4987empty list item, remove it (so that pressing RET twice when in a 4987empty list item, remove it (so that pressing RET twice when in a
4988list simply adds a blank line)." 4988list 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
9105table." 9108table."
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.
23See `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.
29See `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.
35See `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.
43See `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
47matches on the file name (which appears after `-->`), but the
48start of the error appears a few lines earlier. This hook runs
49after `next-error' (\\[next-error]); it simply scrolls down a few lines in
50the 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
60Also, 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 "/**
206This is my summary. Blah blah blah blah blah. Dilly dally dilly dally dilly dally doo.
207
208This is some more text. Fee fie fo fum. Humpty dumpty sat on a wall.
209*/"
210 "/**
211This is my summary. Blah blah
212blah blah blah. Dilly dally
213dilly dally dilly dally doo.
214
215This 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.
240fn foo() { }"
241 "/// This is my comment. This is
242/// more of my comment. This is
243/// even more.
244fn 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.
250fn bar() { }"
251 "fn foo() { }
252/// This is my comment. This is
253/// more of my comment. This is
254/// even more.
255fn 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 "/*
311This is a very very very very
312very 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 "
332struct Foo { bar: i32,
333 baz: i32 }
334
335struct 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
349fn 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:
360struct foo {
361 a:i32,
362 // comments too
363 b:char
364}
365
366fn 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]
376mod foo;
377
378pub static bar = Quux{a: b()}
379
380use foo::bar::baz;
381
382fn foo() { }
383"))
384
385(ert-deftest font-lock-multi-raw-strings-in-a-row ()
386 (rust-test-font-lock
387 "
388r\"foo\\\", \"bar\", r\"bar\";
389r\"foo\\.\", \"bar\", r\"bar\";
390r\"foo\\..\", \"bar\", r\"foo\\..\\bar\";
391r\"\\\", \"foo\", r\"\\foo\";
392not_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
412fn xyzzy(
413 a:i32,
414 b:char) { }
415
416fn 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
426fn foo(a:i32,
427 b:char) { }
428
429fn bar( a:i32,
430 b:char)
431 -> i32
432{ }
433
434fn 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 `-> {`
444fn foo1(a:i32, b:char) -> i32 {
445 let body;
446}
447
448fn foo2(a:i32,
449 b:char) -> i32 {
450 let body;
451}
452
453fn foo3(a:i32,
454 b:char)
455 -> i32 {
456 let body;
457}
458
459fn 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 "
470fn 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 "
481fn foo1(a: A, b: B) -> A
482 where A: Clone + Default, B: Eq {
483 let body;
484 Foo {
485 bar: 3
486 }
487}
488
489fn 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 "
503fn 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 "
519fn 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 "
535fn 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 "
549fn 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 "
564fn 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 "
579fn 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 "
596fn foo4a(a: A, b: B, c: C) -> D
597where 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 "
612fn foo4b(a: A, b: B, c: C) -> D
613where 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 "
629impl<'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)]
657pub 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 "
690fn 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 "
726fn 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 "
740fn foo()
741{
742 x();
743 let a =
744 b();
745}
746"
747 ))
748
749(ert-deftest indent-match ()
750 (test-indent
751 "
752fn 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 "
764fn 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 "
779fn 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 "
796fn 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 "
809fn foo() {
810 {
811this_block_is_over_to_the_left_for_some_reason();
812 }
813
814}
815"
816 16
817 #'indent-for-tab-command
818 "
819fn 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)]
835fn 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 "
844fn 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 "
855fn 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 "
869struct A {
870 x: u8
871 // comment
872}
873
874struct A {
875 x: u8
876 /* comment */
877}
878"
879 ))
880
881(defconst rust-test-motion-string
882 "
883fn fn1(arg: i32) -> bool {
884 let x = 5;
885 let y = b();
886 true
887}
888
889fn fn2(arg: i32) -> bool {
890 let x = 5;
891 let y = b();
892 true
893}
894
895pub fn fn3(arg: i32) -> bool {
896 let x = 5;
897 let y = b();
898 true
899}
900
901struct Foo {
902 x: i32
903}
904")
905(defconst rust-test-region-string rust-test-motion-string)
906(defconst rust-test-indent-motion-string
907 "
908fn blank_line(arg:i32) -> bool {
909
910}
911
912fn indenting_closing_brace() {
913 if(true) {
914}
915}
916
917fn indenting_middle_of_line() {
918 if(true) {
919 push_me_out();
920} else {
921 pull_me_back_in();
922}
923}
924
925fn 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
968POS-SYMBOL is a symbol found in `rust-test-positions-alist'.
969Convert 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
986If ARGS are provided, send them to MANIP-FUNC.
987
988INIT-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
999INIT-POS is the initial position of point.
1000If ARGS are provided, send them to MANIP-FUNC.
1001All 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#\"
1066fn test2();
1067\"#;")
1068 (setq p-1 (point))
1069 (setq fn-1 (1+ p-1))
1070 (insert "
1071fn test3() {
1072 /*
1073fn 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
1257list 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
1596fn 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#\";
1623not_a_string();
1624r##\"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 \"#;
1632not_in_a_string();
1633r##\"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\";
1642this_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##\"
16591...............................................50
16601...............................................50
16611...............................................50
16621...............195-->\"; let ...................50
16631...............................................50
16641...............................................50
16651...............................................50
16661...............................................50
16671...............................................50
16681......................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 "
1846fn 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 "
1857fn 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 "
1869fn 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 "
1880fn 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 "
1892fn 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 "
1904fn 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 "
1915fn 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 "
1926fn 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 "
1937fn 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 "
1950fn 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 "
1960fn 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 "
1969fn 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 "
1978fn 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 "
1988fn 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]
1993fn 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 "
2001fn 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
2016internal state getting corrupted when actions were done in a
2017specific sequence. The test seems arbitrary, and is, but it was
2018not clear how to narrow it down further.
2019
2020The cause of the bug was code that used to set
2021`syntax-begin-function' to `beginning-of-defun', which doesn't
2022actually fulfill the expectations--`syntax-begin-function' is
2023supposed to back out of all parens, but `beginning-of-defun'
2024could leave it inside parens if a fn appears inside them.
2025
2026Having said that, as I write this I don't understand fully what
2027internal state was corrupted and how. There wasn't an obvious
2028pattern 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
2041fn indenting_closing_brace() {
2042 if(true) {
2043}
2044}
2045
2046fn 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 "
2066impl 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 "
2116pub 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 "
2126pub 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 "
2137pub fn foo() {
2138 format!(\"
2139abc
2140def\");
2141}
2142
2143pub fn foo() {
2144 format!(\"la la la
2145la
2146la la\");
2147}
2148"
2149 ;; Should still indent the code parts but leave the string internals alone:
2150 "
2151 pub fn foo() {
2152 format!(\"
2153abc
2154def\");
2155}
2156
2157pub fn foo() {
2158 format!(\"la la la
2159la
2160la la\");
2161 }
2162"
2163 ))
2164
2165(ert-deftest test-indent-string-eol-backslash-mixed-with-literal-eol ()
2166 (test-indent
2167 "
2168fn foo() {
2169 println!(\"
2170Here 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
2177And another line not indented
2178\")
2179}
2180"
2181 "
2182fn foo() {
2183 println!(\"
2184Here 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 \\
2190with a line continuing it
2191And another line not indented
2192\")
2193}
2194"))
2195
2196(ert-deftest test-indent-string-eol-backslash-dont-touch-raw-strings ()
2197 (test-indent
2198 "
2199pub fn foo() {
2200 format!(r\"\
2201abc\
2202 def\");
2203}
2204
2205pub fn foo() {
2206 format!(r\"la la la
2207 la\
2208la la\");
2209}
2210"
2211 ;; Should still indent the code parts but leave the string internals alone:
2212 "
2213 pub fn foo() {
2214 format!(r\"\
2215abc\
2216 def\");
2217}
2218
2219pub fn foo() {
2220 format!(r\"la la la
2221 la\
2222la 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 "
2235pub 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 "
2245const a: &'static str = r#\"
2246{}\"#;
2247fn 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 "
2265fn 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 "
2607fn 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 "
2618fn 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 "
2628fn 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 "
2642fn 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 "
2664fn 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 "
2685struct Bla<T> {
2686 a:Option<(i32,Option<bool>)>,
2687 b:Option<T>,
2688 c:bool
2689}
2690
2691fn 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 "
2713fn 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 "
2727fn 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 "
2744fn 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 "
2758fn 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 "
2770fn 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 "
2783type Foo = Blah<Z,Y>;
2784type Bar<X> = (Foo, Bletch<X>);
2785type 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 "
2800struct A(Option<B>);
2801struct 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 "
2812enum 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
2841fn bluh<A>() where A:Fn()+MyTrait<i32>, MyTrait<A>::AssocType = Option<bool> {
2842}
2843
2844fn 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 "
2874fn 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 "
2890fn 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
2902fn 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 "
2933fn 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 "
2950fn 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 "
3038const BLA = 1 << 3;
3039const 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 "
3049enum 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 "
3064fn foo<A>(a:A) {
3065 macro_a!( foo::<ignore the bracets> );
3066 macro_b![ foo as Option<B> ];
3067}
3068
3069macro_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 "
3086fn foo<A>(a:A) {
3087 macro_rules! foo ( foo::<ignore the bracets> );
3088 macro_rules! bar [ foo as Option<B> ];
3089}
3090
3091macro_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 "
3192const X: libc::c_int = 1 << 2;
3193fn 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 "
3207fn 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 "
3220fn 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 "
3242fn 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 "
3263enum 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 "
3273enum Boo<'a,T> where T:Fn() -> Option<&'a str> + 'a {
3274 Thingy(Option<&'a T>)
3275}
3276
3277fn foo<'a>() -> Result<B,C> where C::X: D<A>, B:FnMut() -> Option<Q>+'a {
3278 Foo(a < b)
3279}
3280
3281type 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 "
3344fn f1() {
3345}
3346
3347unsafe fn f2() {
3348}
3349
3350extern \"C\" fn f3() {
3351}
3352
3353pub extern fn f4() {
3354}
3355
3356extern \"rust-intrinsic\" fn f5() {
3357}
3358
3359async fn f6() {
3360}
3361
3362const fn f7() {
3363}
3364
3365async const fn not_a_match() {
3366}
3367
3368fn f8<'a>() {
3369}
3370
3371pub ( in self::super ) fn f9() {
3372}
3373
3374pub ( in super ) fn f10() {
3375}
3376
3377pub(in crate) fn f11() {
3378}
3379
3380pub (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 "
3400impl<'a> One<'a> {
3401 fn one() {}
3402}
3403
3404impl 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 |
353482 | fn duration_ms_since(time: &Option<SystemTime>) -> u128 {
3535 | ------------------------------------------------------- defined here
3536...
3537132 | 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.
22This 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.
71When 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
84to 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
188Create a hierarchical index of the item definitions in a Rust file.
189
190Imenu will show all the enums, structs, etc. in their own subheading.
191Use idomenu (imenu with `ido-mode') for best mileage.")
192
193(defvar rust-mode-syntax-table
194 (let ((table (make-syntax-table)))
195
196 ;; Operators
197 (dolist (i '(?+ ?- ?* ?/ ?% ?& ?| ?^ ?! ?< ?> ?~ ?@))
198 (modify-syntax-entry i "." table))
199
200 ;; Strings
201 (modify-syntax-entry ?\" "\"" table)
202 (modify-syntax-entry ?\\ "\\" table)
203
204 ;; Angle brackets. We suppress this with syntactic propertization
205 ;; when needed
206 (modify-syntax-entry ?< "(>" table)
207 (modify-syntax-entry ?> ")<" table)
208
209 ;; Comments
210 (modify-syntax-entry ?/ ". 124b" table)
211 (modify-syntax-entry ?* ". 23n" table)
212 (modify-syntax-entry ?\n "> b" table)
213 (modify-syntax-entry ?\^m "> b" table)
214
215 table)
216 "Syntax definitions and helpers.")
217
218;;; Prettify
219
220(defun rust--prettify-symbols-compose-p (start end match)
221 "Return true iff the symbol MATCH should be composed.
222See `prettify-symbols-compose-predicate'."
223 (and (fboundp 'prettify-symbols-default-compose-p)
224 (prettify-symbols-default-compose-p start end match)
225 ;; Make sure || is not a closure with 0 arguments and && is not
226 ;; a double reference.
227 (pcase match
228 ("||" (not (save-excursion
229 (goto-char start)
230 (looking-back "\\(?:\\<move\\|[[({:=,;]\\) *"
231 (line-beginning-position)))))
232 ("&&" (char-equal (char-after end) ?\s))
233 (_ t))))
234
235;;; Mode
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.
382This is used by `rust-font-lock-keywords'.
383\(`write!' is handled separately).")
384
385(defvar rust-formatting-macro-opening-re
386 "[[:space:]\n]*[({[][[:space:]\n]*"
387 "Regular expression to match the opening delimiter of a Rust formatting macro.")
388
389(defvar rust-start-of-string-re
390 "\\(?:r#*\\)?\""
391 "Regular expression to match the start of a Rust raw string.")
392
393(defun rust-path-font-lock-matcher (re-ident)
394 "Match occurrences of RE-IDENT followed by a double-colon.
395Examples include to match names like \"foo::\" or \"Foo::\".
396Does not match type annotations of the form \"foo::<\"."
397 `(lambda (limit)
398 (catch 'rust-path-font-lock-matcher
399 (while t
400 (let* ((symbol-then-colons (rx-to-string '(seq (group (regexp ,re-ident)) "::")))
401 (match (re-search-forward symbol-then-colons limit t)))
402 (cond
403 ;; If we didn't find a match, there are no more occurrences
404 ;; of foo::, so return.
405 ((null match) (throw 'rust-path-font-lock-matcher nil))
406 ;; If this isn't a type annotation foo::<, we've found a
407 ;; match, so a return it!
408 ((not (looking-at (rx (0+ space) "<")))
409 (throw 'rust-path-font-lock-matcher match))))))))
410
411(defvar rust-font-lock-keywords
412 (append
413 `(
414 ;; Keywords proper
415 (,(regexp-opt rust-keywords 'symbols) . font-lock-keyword-face)
416
417 ;; Contextual keywords
418 ("\\_<\\(default\\)[[:space:]]+fn\\_>" 1 font-lock-keyword-face)
419 (,rust-re-union 1 font-lock-keyword-face)
420
421 ;; Special types
422 (,(regexp-opt rust-special-types 'symbols) . font-lock-type-face)
423
424 ;; The unsafe keyword
425 ("\\_<unsafe\\_>" . 'rust-unsafe)
426
427 ;; Attributes like `#[bar(baz)]` or `#![bar(baz)]` or `#[bar = "baz"]`
428 (,(rust-re-grab (concat "#\\!?\\[" rust-re-ident "[^]]*\\]"))
429 1 font-lock-preprocessor-face keep)
430
431 ;; Builtin formatting macros
432 (,(concat (rust-re-grab
433 (concat (rust-re-word (regexp-opt rust-builtin-formatting-macros))
434 "!"))
435 rust-formatting-macro-opening-re
436 "\\(?:" rust-start-of-string-re "\\)?")
437 (1 'rust-builtin-formatting-macro)
438 (rust-string-interpolation-matcher
439 (rust-end-of-string)
440 nil
441 (0 'rust-string-interpolation t nil)))
442
443 ;; write! macro
444 (,(concat (rust-re-grab (concat (rust-re-word "write\\(ln\\)?") "!"))
445 rust-formatting-macro-opening-re
446 "[[:space:]]*[^\"]+,[[:space:]]*"
447 rust-start-of-string-re)
448 (1 'rust-builtin-formatting-macro)
449 (rust-string-interpolation-matcher
450 (rust-end-of-string)
451 nil
452 (0 'rust-string-interpolation t nil)))
453
454 ;; Syntax extension invocations like `foo!`, highlight including the !
455 (,(concat (rust-re-grab (concat rust-re-ident "!")) "[({[:space:][]")
456 1 font-lock-preprocessor-face)
457
458 ;; Field names like `foo:`, highlight excluding the :
459 (,(concat (rust-re-grab rust-re-ident) "[[:space:]]*:[^:]")
460 1 font-lock-variable-name-face)
461
462 ;; CamelCase Means Type Or Constructor
463 (,rust-re-type-or-constructor 1 font-lock-type-face)
464
465 ;; Type-inferred binding
466 (,(concat "\\_<\\(?:let\\s-+ref\\|let\\|ref\\|for\\)\\s-+\\(?:mut\\s-+\\)?"
467 (rust-re-grab rust-re-ident)
468 "\\_>")
469 1 font-lock-variable-name-face)
470
471 ;; Type names like `Foo::`, highlight excluding the ::
472 (,(rust-path-font-lock-matcher rust-re-uc-ident) 1 font-lock-type-face)
473
474 ;; Module names like `foo::`, highlight excluding the ::
475 (,(rust-path-font-lock-matcher rust-re-lc-ident) 1 font-lock-constant-face)
476
477 ;; Lifetimes like `'foo`
478 (,(concat "'" (rust-re-grab rust-re-ident) "[^']") 1 font-lock-variable-name-face)
479
480 ;; Question mark operator
481 ("\\?" . 'rust-question-mark)
482 ("\\(&+\\)\\(?:'\\(?:\\<\\|_\\)\\|\\<\\|[[({:*_|]\\)"
483 1 'rust-ampersand-face)
484 ;; Numbers with type suffix
485 (,rust-number-with-type 1 font-lock-type-face)
486 )
487
488 ;; Ensure we highlight `Foo` in `struct Foo` as a type.
489 (mapcar #'(lambda (x)
490 (list (rust-re-item-def (car x))
491 1 (cdr x)))
492 '(("enum" . font-lock-type-face)
493 ("struct" . font-lock-type-face)
494 ("union" . font-lock-type-face)
495 ("type" . font-lock-type-face)
496 ("mod" . font-lock-constant-face)
497 ("use" . font-lock-constant-face)
498 ("fn" . font-lock-function-name-face)))))
499
500(defun rust-end-of-string ()
501 "Skip to the end of the current string."
502 (save-excursion
503 (skip-syntax-forward "^\"|")
504 (skip-syntax-forward "\"|")
505 (point)))
506
507(defun rust-looking-back-str (str)
508 "Return non-nil if there's a match on the text before point and STR.
509Like `looking-back' but for fixed strings rather than regexps (so
510that it's not so slow)."
511 (let ((len (length str)))
512 (and (> (point) len)
513 (equal str (buffer-substring-no-properties (- (point) len) (point))))))
514
515(defun rust-looking-back-symbols (symbols)
516 "Return non-nil if the point is after a member of SYMBOLS.
517SYMBOLS is a list of strings that represent the respective
518symbols."
519 (save-excursion
520 (let* ((pt-orig (point))
521 (beg-of-symbol (progn (forward-thing 'symbol -1) (point)))
522 (end-of-symbol (progn (forward-thing 'symbol 1) (point))))
523 (and
524 (= end-of-symbol pt-orig)
525 (member (buffer-substring-no-properties beg-of-symbol pt-orig)
526 symbols)))))
527
528(defun rust-looking-back-ident ()
529 "Non-nil if we are looking backwards at a valid rust identifier."
530 (let ((beg-of-symbol (save-excursion (forward-thing 'symbol -1) (point))))
531 (looking-back rust-re-ident beg-of-symbol)))
532
533(defun rust-looking-back-macro ()
534 "Non-nil if looking back at an ident followed by a !
535
536This is stricter than rust syntax which allows a space between
537the ident and the ! symbol. If this space is allowed, then we
538would also need a keyword check to avoid `if !(condition)` being
539seen as a macro."
540 (if (> (- (point) (point-min)) 1)
541 (save-excursion
542 (backward-char)
543 (and (= ?! (char-after))
544 (rust-looking-back-ident)))))
545
546;;; Syntax definitions and helpers
547
548(defun rust-paren-level () (nth 0 (syntax-ppss)))
549(defun rust-in-str () (nth 3 (syntax-ppss)))
550(defun rust-in-str-or-cmnt () (nth 8 (syntax-ppss)))
551(defun rust-rewind-past-str-cmnt () (goto-char (nth 8 (syntax-ppss))))
552
553(defun rust-rewind-irrelevant ()
554 (let ((continue t))
555 (while continue
556 (let ((starting (point)))
557 (skip-chars-backward "[:space:]\n")
558 (when (rust-looking-back-str "*/")
559 (backward-char))
560 (when (rust-in-str-or-cmnt)
561 (rust-rewind-past-str-cmnt))
562 ;; Rewind until the point no longer moves
563 (setq continue (/= starting (point)))))))
564
565(defvar-local rust-macro-scopes nil
566 "Cache for the scopes calculated by `rust-macro-scope'.
567
568This variable can be `let' bound directly or indirectly around
569`rust-macro-scope' as an optimization but should not be otherwise
570set.")
571
572(defun rust-macro-scope (start end)
573 "Return the scope of macros in the buffer.
574
575The return value is a list of (START END) positions in the
576buffer.
577
578If set START and END are optimizations which limit the return
579value to scopes which are approximately with this range."
580 (save-excursion
581 ;; need to special case macro_rules which has unique syntax
582 (let ((scope nil)
583 (start (or start (point-min)))
584 (end (or end (point-max))))
585 (goto-char start)
586 ;; if there is a start move back to the previous top level,
587 ;; as any macros before that must have closed by this time.
588 (let ((top (syntax-ppss-toplevel-pos (syntax-ppss))))
589 (when top
590 (goto-char top)))
591 (while
592 (and
593 ;; The movement below may have moved us passed end, in
594 ;; which case search-forward will error
595 (< (point) end)
596 (search-forward "!" end t))
597 (let ((pt (point)))
598 (cond
599 ;; in a string or comment is boring, move straight on
600 ((rust-in-str-or-cmnt))
601 ;; in a normal macro,
602 ((and (skip-chars-forward " \t\n\r")
603 (memq (char-after)
604 '(?\[ ?\( ?\{))
605 ;; Check that we have a macro declaration after.
606 (rust-looking-back-macro))
607 (let ((start (point)))
608 (ignore-errors (forward-list))
609 (setq scope (cons (list start (point)) scope))))
610 ;; macro_rules, why, why, why did you not use macro syntax??
611 ((save-excursion
612 ;; yuck -- last test moves point, even if it fails
613 (goto-char (- pt 1))
614 (skip-chars-backward " \t\n\r")
615 (rust-looking-back-str "macro_rules"))
616 (save-excursion
617 (when (re-search-forward "[[({]" nil t)
618 (backward-char)
619 (let ((start (point)))
620 (ignore-errors (forward-list))
621 (setq scope (cons (list start (point)) scope)))))))))
622 ;; Return 'empty rather than nil, to indicate a buffer with no
623 ;; macros at all.
624 (or scope 'empty))))
625
626(defun rust-in-macro (&optional start end)
627 "Return non-nil when point is within the scope of a macro.
628
629If START and END are set, minimize the buffer analysis to
630approximately this location as an optimization.
631
632Alternatively, if `rust-macro-scopes' is a list use the scope
633information in this variable. This last is an optimization and
634the caller is responsible for ensuring that the data in
635`rust-macro-scopes' is up to date."
636 (when (> (rust-paren-level) 0)
637 (let ((scopes
638 (or
639 rust-macro-scopes
640 (rust-macro-scope start end))))
641 ;; `rust-macro-scope' can return the symbol `empty' if the
642 ;; buffer has no macros at all.
643 (when (listp scopes)
644 (seq-some
645 (lambda (sc)
646 (and (>= (point) (car sc))
647 (< (point) (cadr sc))))
648 scopes)))))
649
650(defun rust-looking-at-where ()
651 "Return T when looking at the \"where\" keyword."
652 (and (looking-at-p "\\bwhere\\b")
653 (not (rust-in-str-or-cmnt))))
654
655(defun rust-rewind-to-where (&optional limit)
656 "Rewind the point to the closest occurrence of the \"where\" keyword.
657Return T iff a where-clause was found. Does not rewind past
658LIMIT when passed, otherwise only stops at the beginning of the
659buffer."
660 (when (re-search-backward "\\bwhere\\b" limit t)
661 (if (rust-in-str-or-cmnt)
662 (rust-rewind-to-where limit)
663 t)))
664
665(defconst rust-re-pre-expression-operators "[-=!%&*/:<>[{(|.^;}]")
666
667(defconst rust-re-special-types (regexp-opt rust-special-types 'symbols))
668
669(defun rust-align-to-expr-after-brace ()
670 (save-excursion
671 (forward-char)
672 ;; We don't want to indent out to the open bracket if the
673 ;; open bracket ends the line
674 (when (not (looking-at "[[:blank:]]*\\(?://.*\\)?$"))
675 (when (looking-at "[[:space:]]")
676 (forward-word 1)
677 (backward-word 1))
678 (current-column))))
679
680(defun rust-rewind-to-beginning-of-current-level-expr ()
681 (let ((current-level (rust-paren-level)))
682 (back-to-indentation)
683 (when (looking-at "->")
684 (rust-rewind-irrelevant)
685 (back-to-indentation))
686 (while (> (rust-paren-level) current-level)
687 (backward-up-list)
688 (back-to-indentation))
689 ;; When we're in the where clause, skip over it. First find out the start
690 ;; of the function and its paren level.
691 (let ((function-start nil) (function-level nil))
692 (save-excursion
693 (rust-beginning-of-defun)
694 (back-to-indentation)
695 ;; Avoid using multiple-value-bind
696 (setq function-start (point)
697 function-level (rust-paren-level)))
698 ;; On a where clause
699 (when (or (rust-looking-at-where)
700 ;; or in one of the following lines, e.g.
701 ;; where A: Eq
702 ;; B: Hash <- on this line
703 (and (save-excursion
704 (rust-rewind-to-where function-start))
705 (= current-level function-level)))
706 (goto-char function-start)))))
707
708(defun rust-align-to-method-chain ()
709 (save-excursion
710 ;; for method-chain alignment to apply, we must be looking at
711 ;; another method call or field access or something like
712 ;; that. This avoids rather "eager" jumps in situations like:
713 ;;
714 ;; {
715 ;; something.foo()
716 ;; <indent>
717 ;;
718 ;; Without this check, we would wind up with the cursor under the
719 ;; `.`. In an older version, I had the inverse of the current
720 ;; check, where we checked for situations that should NOT indent,
721 ;; vs checking for the one situation where we SHOULD. It should be
722 ;; clear that this is more robust, but also I find it mildly less
723 ;; annoying to have to press tab again to align to a method chain
724 ;; than to have an over-eager indent in all other cases which must
725 ;; be undone via tab.
726
727 (when (looking-at (concat "\s*\." rust-re-ident))
728 (forward-line -1)
729 (end-of-line)
730 ;; Keep going up (looking for a line that could contain a method chain)
731 ;; while we're in a comment or on a blank line. Stop when the paren
732 ;; level changes.
733 (let ((level (rust-paren-level)))
734 (while (and (or (rust-in-str-or-cmnt)
735 ;; Only whitespace (or nothing) from the beginning to
736 ;; the end of the line.
737 (looking-back "^\s*" (line-beginning-position)))
738 (= (rust-paren-level) level))
739 (forward-line -1)
740 (end-of-line)))
741
742 (let
743 ;; skip-dot-identifier is used to position the point at the
744 ;; `.` when looking at something like
745 ;;
746 ;; foo.bar
747 ;; ^ ^
748 ;; | |
749 ;; | position of point
750 ;; returned offset
751 ;;
752 ((skip-dot-identifier
753 (lambda ()
754 (when (and (rust-looking-back-ident)
755 (save-excursion
756 (forward-thing 'symbol -1)
757 (= ?. (char-before))))
758 (forward-thing 'symbol -1)
759 (backward-char)
760 (- (current-column) rust-indent-offset)))))
761 (cond
762 ;; foo.bar(...)
763 ((looking-back "[)?]" (1- (point)))
764 (backward-list 1)
765 (funcall skip-dot-identifier))
766
767 ;; foo.bar
768 (t (funcall skip-dot-identifier)))))))
769
770(defun rust-mode-indent-line ()
771 (interactive)
772 (let ((indent
773 (save-excursion
774 (back-to-indentation)
775 ;; Point is now at beginning of current line
776 (let* ((level (rust-paren-level))
777 (baseline
778 ;; Our "baseline" is one level out from the
779 ;; indentation of the expression containing the
780 ;; innermost enclosing opening bracket. That way
781 ;; if we are within a block that has a different
782 ;; indentation than this mode would give it, we
783 ;; still indent the inside of it correctly relative
784 ;; to the outside.
785 (if (= 0 level)
786 0
787 (or
788 (when rust-indent-method-chain
789 (rust-align-to-method-chain))
790 (save-excursion
791 (rust-rewind-irrelevant)
792 (backward-up-list)
793 (rust-rewind-to-beginning-of-current-level-expr)
794 (+ (current-column) rust-indent-offset))))))
795 (cond
796 ;; Indent inside a non-raw string only if the previous line
797 ;; ends with a backslash that is inside the same string
798 ((nth 3 (syntax-ppss))
799 (let*
800 ((string-begin-pos (nth 8 (syntax-ppss)))
801 (end-of-prev-line-pos
802 (and (not (rust--same-line-p (point) (point-min)))
803 (line-end-position 0))))
804 (when
805 (and
806 ;; If the string begins with an "r" it's a raw string and
807 ;; we should not change the indentation
808 (/= ?r (char-after string-begin-pos))
809
810 ;; If we're on the first line this will be nil and the
811 ;; rest does not apply
812 end-of-prev-line-pos
813
814 ;; The end of the previous line needs to be inside the
815 ;; current string...
816 (> end-of-prev-line-pos string-begin-pos)
817
818 ;; ...and end with a backslash
819 (= ?\\ (char-before end-of-prev-line-pos)))
820
821 ;; Indent to the same level as the previous line, or the
822 ;; start of the string if the previous line starts the string
823 (if (rust--same-line-p end-of-prev-line-pos string-begin-pos)
824 ;; The previous line is the start of the string.
825 ;; If the backslash is the only character after the
826 ;; string beginning, indent to the next indent
827 ;; level. Otherwise align with the start of the string.
828 (if (> (- end-of-prev-line-pos string-begin-pos) 2)
829 (save-excursion
830 (goto-char (+ 1 string-begin-pos))
831 (current-column))
832 baseline)
833
834 ;; The previous line is not the start of the string, so
835 ;; match its indentation.
836 (save-excursion
837 (goto-char end-of-prev-line-pos)
838 (back-to-indentation)
839 (current-column))))))
840
841 ;; A function return type is indented to the corresponding
842 ;; function arguments, if -to-arguments is selected.
843 ((and rust-indent-return-type-to-arguments
844 (looking-at "->"))
845 (save-excursion
846 (backward-list)
847 (or (rust-align-to-expr-after-brace)
848 (+ baseline rust-indent-offset))))
849
850 ;; A closing brace is 1 level unindented
851 ((looking-at "[]})]") (- baseline rust-indent-offset))
852
853 ;; Doc comments in /** style with leading * indent to line up the *s
854 ((and (nth 4 (syntax-ppss)) (looking-at "*"))
855 (+ 1 baseline))
856
857 ;; When the user chose not to indent the start of the where
858 ;; clause, put it on the baseline.
859 ((and (not rust-indent-where-clause)
860 (rust-looking-at-where))
861 baseline)
862
863 ;; If we're in any other token-tree / sexp, then:
864 (t
865 (or
866 ;; If we are inside a pair of braces, with something after the
867 ;; open brace on the same line and ending with a comma, treat
868 ;; it as fields and align them.
869 (when (> level 0)
870 (save-excursion
871 (rust-rewind-irrelevant)
872 (backward-up-list)
873 ;; Point is now at the beginning of the containing set of braces
874 (rust-align-to-expr-after-brace)))
875
876 ;; When where-clauses are spread over multiple lines, clauses
877 ;; should be aligned on the type parameters. In this case we
878 ;; take care of the second and following clauses (the ones
879 ;; that don't start with "where ")
880 (save-excursion
881 ;; Find the start of the function, we'll use this to limit
882 ;; our search for "where ".
883 (let ((function-start nil) (function-level nil))
884 (save-excursion
885 ;; If we're already at the start of a function,
886 ;; don't go back any farther. We can easily do
887 ;; this by moving to the end of the line first.
888 (end-of-line)
889 (rust-beginning-of-defun)
890 (back-to-indentation)
891 ;; Avoid using multiple-value-bind
892 (setq function-start (point)
893 function-level (rust-paren-level)))
894 ;; When we're not on a line starting with "where ", but
895 ;; still on a where-clause line, go to "where "
896 (when (and
897 (not (rust-looking-at-where))
898 ;; We're looking at something like "F: ..."
899 (looking-at (concat rust-re-ident ":"))
900 ;; There is a "where " somewhere after the
901 ;; start of the function.
902 (rust-rewind-to-where function-start)
903 ;; Make sure we're not inside the function
904 ;; already (e.g. initializing a struct) by
905 ;; checking we are the same level.
906 (= function-level level))
907 ;; skip over "where"
908 (forward-char 5)
909 ;; Unless "where" is at the end of the line
910 (if (eolp)
911 ;; in this case the type parameters bounds are just
912 ;; indented once
913 (+ baseline rust-indent-offset)
914 ;; otherwise, skip over whitespace,
915 (skip-chars-forward "[:space:]")
916 ;; get the column of the type parameter and use that
917 ;; as indentation offset
918 (current-column)))))
919
920 (progn
921 (back-to-indentation)
922 ;; Point is now at the beginning of the current line
923 (if (or
924 ;; If this line begins with "else" or "{", stay on the
925 ;; baseline as well (we are continuing an expression,
926 ;; but the "else" or "{" should align with the beginning
927 ;; of the expression it's in.)
928 ;; Or, if this line starts a comment, stay on the
929 ;; baseline as well.
930 (looking-at "\\<else\\>\\|{\\|/[/*]")
931
932 ;; If this is the start of a top-level item,
933 ;; stay on the baseline.
934 (looking-at rust-top-item-beg-re)
935
936 (save-excursion
937 (rust-rewind-irrelevant)
938 ;; Point is now at the end of the previous line
939 (or
940 ;; If we are at the start of the buffer, no
941 ;; indentation is needed, so stay at baseline...
942 (= (point) 1)
943 ;; ..or if the previous line ends with any of these:
944 ;; { ? : ( , ; [ }
945 ;; then we are at the beginning of an
946 ;; expression, so stay on the baseline...
947 (looking-back "[(,:;[{}]\\|[^|]|" (- (point) 2))
948 ;; or if the previous line is the end of an
949 ;; attribute, stay at the baseline...
950 (progn (rust-rewind-to-beginning-of-current-level-expr)
951 (looking-at "#")))))
952 baseline
953
954 ;; Otherwise, we are continuing the same expression from
955 ;; the previous line, so add one additional indent level
956 (+ baseline rust-indent-offset))))))))))
957
958 (when indent
959 ;; If we're at the beginning of the line (before or at the current
960 ;; indentation), jump with the indentation change. Otherwise, save the
961 ;; excursion so that adding the indentations will leave us at the
962 ;; equivalent position within the line to where we were before.
963 (if (<= (current-column) (current-indentation))
964 (indent-line-to indent)
965 (save-excursion (indent-line-to indent))))))
966
967(defun rust--same-line-p (pos1 pos2)
968 "Return non-nil if POS1 and POS2 are on the same line."
969 (save-excursion (= (progn (goto-char pos1) (line-end-position))
970 (progn (goto-char pos2) (line-end-position)))))
971
972;;; Font-locking definitions and helpers
973
974(defun rust-next-string-interpolation (limit)
975 "Search forward from point for next Rust interpolation marker before LIMIT.
976Set point to the end of the occurrence found, and return match beginning
977and end."
978 (catch 'match
979 (save-match-data
980 (save-excursion
981 (while (search-forward "{" limit t)
982 (if (eql (char-after (point)) ?{)
983 (forward-char)
984 (let ((start (match-beginning 0)))
985 ;; According to fmt_macros::Parser::next, an opening brace
986 ;; must be followed by an optional argument and/or format
987 ;; specifier, then a closing brace. A single closing brace
988 ;; without a corresponding unescaped opening brace is an
989 ;; error. We don't need to do anything special with
990 ;; arguments, specifiers, or errors, so we only search for
991 ;; the single closing brace.
992 (when (search-forward "}" limit t)
993 (throw 'match (list start (point)))))))))))
994
995(defun rust-string-interpolation-matcher (limit)
996 "Match next Rust interpolation marker before LIMIT and set match data if found.
997Returns nil if not within a Rust string."
998 (when (rust-in-str)
999 (let ((match (rust-next-string-interpolation limit)))
1000 (when match
1001 (set-match-data match)
1002 (goto-char (cadr match))
1003 match))))
1004
1005(defun rust-syntax-class-before-point ()
1006 (when (> (point) 1)
1007 (syntax-class (syntax-after (1- (point))))))
1008
1009(defun rust-rewind-qualified-ident ()
1010 (while (rust-looking-back-ident)
1011 (backward-sexp)
1012 (when (save-excursion (rust-rewind-irrelevant) (rust-looking-back-str "::"))
1013 (rust-rewind-irrelevant)
1014 (backward-char 2)
1015 (rust-rewind-irrelevant))))
1016
1017(defun rust-rewind-type-param-list ()
1018 (cond
1019 ((and (rust-looking-back-str ">") (equal 5 (rust-syntax-class-before-point)))
1020 (backward-sexp)
1021 (rust-rewind-irrelevant))
1022
1023 ;; We need to be able to back up past the Fn(args) -> RT form as well. If
1024 ;; we're looking back at this, we want to end up just after "Fn".
1025 ((member (char-before) '(?\] ?\) ))
1026 (let* ((is-paren (rust-looking-back-str ")"))
1027 (dest (save-excursion
1028 (backward-sexp)
1029 (rust-rewind-irrelevant)
1030 (or
1031 (when (rust-looking-back-str "->")
1032 (backward-char 2)
1033 (rust-rewind-irrelevant)
1034 (when (rust-looking-back-str ")")
1035 (backward-sexp)
1036 (point)))
1037 (and is-paren (point))))))
1038 (when dest
1039 (goto-char dest))))))
1040
1041(defun rust-rewind-to-decl-name ()
1042 "Return the point at the beginning of the name in a declaration.
1043I.e. if we are before an ident that is part of a declaration that
1044can have a where clause, rewind back to just before the name of
1045the subject of that where clause and return the new point.
1046Otherwise return nil."
1047 (let* ((ident-pos (point))
1048 (newpos (save-excursion
1049 (rust-rewind-irrelevant)
1050 (rust-rewind-type-param-list)
1051 (cond
1052 ((rust-looking-back-symbols
1053 '("fn" "trait" "enum" "struct" "union" "impl" "type"))
1054 ident-pos)
1055
1056 ((equal 5 (rust-syntax-class-before-point))
1057 (backward-sexp)
1058 (rust-rewind-to-decl-name))
1059
1060 ((looking-back "[:,'+=]" (1- (point)))
1061 (backward-char)
1062 (rust-rewind-to-decl-name))
1063
1064 ((rust-looking-back-str "->")
1065 (backward-char 2)
1066 (rust-rewind-to-decl-name))
1067
1068 ((rust-looking-back-ident)
1069 (rust-rewind-qualified-ident)
1070 (rust-rewind-to-decl-name))))))
1071 (when newpos (goto-char newpos))
1072 newpos))
1073
1074(defun rust-is-in-expression-context (token)
1075 "Return t if what comes right after the point is part of an
1076expression (as opposed to starting a type) by looking at what
1077comes before. Takes a symbol that roughly indicates what is
1078after the point.
1079
1080This function is used as part of `rust-is-lt-char-operator' as
1081part of angle bracket matching, and is not intended to be used
1082outside of this context."
1083 (save-excursion
1084 (let ((postchar (char-after)))
1085 (rust-rewind-irrelevant)
1086 ;; A type alias or ascription could have a type param list. Skip backwards past it.
1087 (when (member token '(ambiguous-operator open-brace))
1088 (rust-rewind-type-param-list))
1089 (cond
1090
1091 ;; Certain keywords always introduce expressions
1092 ((rust-looking-back-symbols '("if" "while" "match" "return" "box" "in")) t)
1093
1094 ;; "as" introduces a type
1095 ((rust-looking-back-symbols '("as")) nil)
1096
1097 ;; An open angle bracket never introduces expression context WITHIN the angle brackets
1098 ((and (equal token 'open-brace) (equal postchar ?<)) nil)
1099
1100 ;; An ident! followed by an open brace is a macro invocation. Consider
1101 ;; it to be an expression.
1102 ((and (equal token 'open-brace) (rust-looking-back-macro)) t)
1103
1104 ;; In a brace context a "]" introduces an expression.
1105 ((and (eq token 'open-brace) (rust-looking-back-str "]")))
1106
1107 ;; An identifier is right after an ending paren, bracket, angle bracket
1108 ;; or curly brace. It's a type if the last sexp was a type.
1109 ((and (equal token 'ident) (equal 5 (rust-syntax-class-before-point)))
1110 (backward-sexp)
1111 (rust-is-in-expression-context 'open-brace))
1112
1113 ;; If a "for" appears without a ; or { before it, it's part of an
1114 ;; "impl X for y", so the y is a type. Otherwise it's
1115 ;; introducing a loop, so the y is an expression
1116 ((and (equal token 'ident) (rust-looking-back-symbols '("for")))
1117 (backward-sexp)
1118 (rust-rewind-irrelevant)
1119 (looking-back "[{;]" (1- (point))))
1120
1121 ((rust-looking-back-ident)
1122 (rust-rewind-qualified-ident)
1123 (rust-rewind-irrelevant)
1124 (cond
1125 ((equal token 'open-brace)
1126 ;; We now know we have:
1127 ;; ident <maybe type params> [{([]
1128 ;; where [{([] denotes either a {, ( or [.
1129 ;; This character is bound as postchar.
1130 (cond
1131 ;; If postchar is a paren or square bracket, then if the
1132 ;; brace is a type if the identifier is one
1133 ((member postchar '(?\( ?\[ )) (rust-is-in-expression-context 'ident))
1134
1135 ;; If postchar is a curly brace, the brace can only be a type if
1136 ;; ident2 is the name of an enum, struct or trait being declared.
1137 ;; Note that if there is a -> before the ident then the ident would
1138 ;; be a type but the { is not.
1139 ((equal ?{ postchar)
1140 (not (and (rust-rewind-to-decl-name)
1141 (progn
1142 (rust-rewind-irrelevant)
1143 (rust-looking-back-symbols
1144 '("enum" "struct" "union" "trait" "type"))))))))
1145
1146 ((equal token 'ambiguous-operator)
1147 (cond
1148 ;; An ampersand after an ident has to be an operator rather
1149 ;; than a & at the beginning of a ref type
1150 ((equal postchar ?&) t)
1151
1152 ;; A : followed by a type then an = introduces an
1153 ;; expression (unless it is part of a where clause of a
1154 ;; "type" declaration)
1155 ((and (equal postchar ?=)
1156 (looking-back "[^:]:" (- (point) 2))
1157 (not (save-excursion
1158 (and (rust-rewind-to-decl-name)
1159 (progn (rust-rewind-irrelevant)
1160 (rust-looking-back-symbols '("type"))))))))
1161
1162 ;; "let ident =" introduces an expression--and so does "const" and "mut"
1163 ((and (equal postchar ?=) (rust-looking-back-symbols '("let" "const" "mut"))) t)
1164
1165 ;; As a specific special case, see if this is the = in this situation:
1166 ;; enum EnumName<type params> { Ident =
1167 ;; In this case, this is a c-like enum and despite Ident
1168 ;; representing a type, what comes after the = is an expression
1169 ((and
1170 (> (rust-paren-level) 0)
1171 (save-excursion
1172 (backward-up-list)
1173 (rust-rewind-irrelevant)
1174 (rust-rewind-type-param-list)
1175 (and
1176 (rust-looking-back-ident)
1177 (progn
1178 (rust-rewind-qualified-ident)
1179 (rust-rewind-irrelevant)
1180 (rust-looking-back-str "enum")))))
1181 t)
1182
1183 ;; Otherwise the ambiguous operator is a type if the identifier is a type
1184 ((rust-is-in-expression-context 'ident) t)))
1185
1186 ((equal token 'colon)
1187 (cond
1188 ;; If we see a ident: not inside any braces/parens, we're at top level.
1189 ;; There are no allowed expressions after colons there, just types.
1190 ((<= (rust-paren-level) 0) nil)
1191
1192 ;; We see ident: inside a list
1193 ((looking-back "[{,]" (1- (point)))
1194 (backward-up-list)
1195
1196 ;; If a : appears whose surrounding paren/brackets/braces are
1197 ;; anything other than curly braces, it can't be a field
1198 ;; initializer and must be denoting a type.
1199 (when (looking-at "{")
1200 (rust-rewind-irrelevant)
1201 (rust-rewind-type-param-list)
1202 (when (rust-looking-back-ident)
1203 ;; We have a context that looks like this:
1204 ;; ident2 <maybe type params> { [maybe paren-balanced code ending in comma] ident1:
1205 ;; the point is sitting just after ident2, and we trying to
1206 ;; figure out if the colon introduces an expression or a type.
1207 ;; The answer is that ident1 is a field name, and what comes
1208 ;; after the colon is an expression, if ident2 is an
1209 ;; expression.
1210 (rust-rewind-qualified-ident)
1211 (rust-is-in-expression-context 'ident))))
1212
1213 ;; Otherwise, if the ident: appeared with anything other than , or {
1214 ;; before it, it can't be part of a struct initializer and therefore
1215 ;; must be denoting a type.
1216 (t nil)))))
1217
1218 ;; An operator-like character after a string is indeed an operator
1219 ((and (equal token 'ambiguous-operator)
1220 (member (rust-syntax-class-before-point) '(5 7 15))) t)
1221
1222 ;; A colon that has something other than an identifier before it is a
1223 ;; type ascription
1224 ((equal token 'colon) nil)
1225
1226 ;; A :: introduces a type (or module, but not an expression in any case)
1227 ((rust-looking-back-str "::") nil)
1228
1229 ((rust-looking-back-str ":")
1230 (backward-char)
1231 (rust-is-in-expression-context 'colon))
1232
1233 ;; A -> introduces a type
1234 ((rust-looking-back-str "->") nil)
1235
1236 ;; If we are up against the beginning of a list, or after a comma inside
1237 ;; of one, back up out of it and check what the list itself is
1238 ((or
1239 (equal 4 (rust-syntax-class-before-point))
1240 (rust-looking-back-str ","))
1241 (condition-case nil
1242 (progn
1243 (backward-up-list)
1244 (rust-is-in-expression-context 'open-brace))
1245 (scan-error nil)))
1246
1247 ;; A => introduces an expression
1248 ((rust-looking-back-str "=>") t)
1249
1250 ;; A == introduces an expression
1251 ((rust-looking-back-str "==") t)
1252
1253 ;; These operators can introduce expressions or types
1254 ((looking-back "[-+=!?&*]" (1- (point)))
1255 (backward-char)
1256 (rust-is-in-expression-context 'ambiguous-operator))
1257
1258 ;; These operators always introduce expressions. (Note that if this
1259 ;; regexp finds a < it must not be an angle bracket, or it'd
1260 ;; have been caught in the syntax-class check above instead of this.)
1261 ((looking-back rust-re-pre-expression-operators (1- (point))) t)))))
1262
1263(defun rust-is-lt-char-operator ()
1264 "Return non-nil if the `<' sign just after point is an operator.
1265Otherwise, if it is an opening angle bracket, then return nil."
1266 (let ((case-fold-search nil))
1267 (save-excursion
1268 (rust-rewind-irrelevant)
1269 ;; We are now just after the character syntactically before the <.
1270 (cond
1271
1272 ;; If we are looking back at a < that is not an angle bracket (but not
1273 ;; two of them) then this is the second < in a bit shift operator
1274 ((and (rust-looking-back-str "<")
1275 (not (equal 4 (rust-syntax-class-before-point)))
1276 (not (rust-looking-back-str "<<"))))
1277
1278 ;; On the other hand, if we are after a closing paren/brace/bracket it
1279 ;; can only be an operator, not an angle bracket. Likewise, if we are
1280 ;; after a string it's an operator. (The string case could actually be
1281 ;; valid in rust for character literals.)
1282 ((member (rust-syntax-class-before-point) '(5 7 15)) t)
1283
1284 ;; If we are looking back at an operator, we know that we are at
1285 ;; the beginning of an expression, and thus it has to be an angle
1286 ;; bracket (starting a "<Type as Trait>::" construct.)
1287 ((looking-back rust-re-pre-expression-operators (1- (point))) nil)
1288
1289 ;; If we are looking back at a keyword, it's an angle bracket
1290 ;; unless that keyword is "self", "true" or "false"
1291 ((rust-looking-back-symbols rust-keywords)
1292 (rust-looking-back-symbols '("self" "true" "false")))
1293
1294 ((rust-looking-back-str "?")
1295 (rust-is-in-expression-context 'ambiguous-operator))
1296
1297 ;; If we're looking back at an identifier, this depends on whether
1298 ;; the identifier is part of an expression or a type
1299 ((rust-looking-back-ident)
1300 (backward-sexp)
1301 (or
1302 ;; The special types can't take type param lists, so a < after one is
1303 ;; always an operator
1304 (looking-at rust-re-special-types)
1305
1306 (rust-is-in-expression-context 'ident)))
1307
1308 ;; Otherwise, assume it's an angle bracket
1309 ))))
1310
1311(defun rust-electric-pair-inhibit-predicate-wrap (char)
1312 "Prevent \"matching\" with a `>' when CHAR is the less-than operator.
1313This wraps the default defined by `electric-pair-inhibit-predicate'."
1314 (or
1315 (when (= ?< char)
1316 (save-excursion
1317 (backward-char)
1318 (rust-is-lt-char-operator)))
1319 (funcall (default-value 'electric-pair-inhibit-predicate) char)))
1320
1321(defun rust-electric-pair-skip-self (char)
1322 "Skip CHAR instead of inserting a second closing character.
1323This is added to the default skips defined by `electric-pair-skip-self'."
1324 (= ?> char))
1325
1326(defun rust-ordinary-lt-gt-p ()
1327 "Test whether the `<' or `>' at point is an ordinary operator of some kind.
1328
1329This returns t if the `<' or `>' is an ordinary operator (like
1330less-than) or part of one (like `->'); and nil if the character
1331should be considered a paired angle bracket."
1332 (cond
1333 ;; If matching is turned off suppress all of them
1334 ((not rust-match-angle-brackets) t)
1335
1336 ;; This is a cheap check so we do it early.
1337 ;; Don't treat the > in -> or => as an angle bracket
1338 ((and (= (following-char) ?>) (memq (preceding-char) '(?- ?=))) t)
1339
1340 ;; We don't take < or > in strings or comments to be angle brackets
1341 ((rust-in-str-or-cmnt) t)
1342
1343 ;; Inside a macro we don't really know the syntax. Any < or > may be an
1344 ;; angle bracket or it may not. But we know that the other braces have
1345 ;; to balance regardless of the < and >, so if we don't treat any < or >
1346 ;; as angle brackets it won't mess up any paren balancing.
1347 ((rust-in-macro) t)
1348
1349 ((= (following-char) ?<)
1350 (rust-is-lt-char-operator))
1351
1352 ;; Since rust-ordinary-lt-gt-p is called only when either < or > are at the point,
1353 ;; we know that the following char must be > in the clauses below.
1354
1355 ;; If we are at top level and not in any list, it can't be a closing
1356 ;; angle bracket
1357 ((>= 0 (rust-paren-level)) t)
1358
1359 ;; Otherwise, treat the > as a closing angle bracket if it would
1360 ;; match an opening one
1361 ((save-excursion
1362 (backward-up-list)
1363 (/= (following-char) ?<)))))
1364
1365(defun rust-mode-syntactic-face-function (state)
1366 "Return face that distinguishes doc and normal comments in given syntax STATE."
1367 (if (nth 3 state)
1368 'font-lock-string-face
1369 (save-excursion
1370 (goto-char (nth 8 state))
1371 (if (looking-at "/\\([*][*!][^*!]\\|/[/!][^/!]\\)")
1372 'font-lock-doc-face
1373 'font-lock-comment-face))))
1374
1375(eval-and-compile
1376 (defconst rust--char-literal-rx
1377 (rx (seq
1378 (group "'")
1379 (or
1380 (seq
1381 "\\"
1382 (or
1383 (: "u{" (** 1 6 xdigit) "}")
1384 (: "x" (= 2 xdigit))
1385 (any "'nrt0\"\\")))
1386 (not (any "'\\")))
1387 (group "'")))
1388 "A regular expression matching a character literal."))
1389
1390(defun rust--syntax-propertize-raw-string (str-start end)
1391 "A helper for rust-syntax-propertize.
1392
1393This will apply the appropriate string syntax to the character
1394from the STR-START up to the end of the raw string, or to END,
1395whichever comes first."
1396 (when (save-excursion
1397 (goto-char str-start)
1398 (looking-at "r\\(#*\\)\\(\"\\)"))
1399 ;; In a raw string, so try to find the end.
1400 (let ((hashes (match-string 1)))
1401 ;; Match \ characters at the end of the string to suppress
1402 ;; their normal character-quote syntax.
1403 (when (re-search-forward (concat "\\(\\\\*\\)\\(\"" hashes "\\)") end t)
1404 (put-text-property (match-beginning 1) (match-end 1)
1405 'syntax-table (string-to-syntax "_"))
1406 (put-text-property (1- (match-end 2)) (match-end 2)
1407 'syntax-table (string-to-syntax "|"))
1408 (goto-char (match-end 0))))))
1409
1410;;; Syntax Propertize
1411
1412(defun rust-syntax-propertize (start end)
1413 "A `syntax-propertize-function' to apply properties from START to END."
1414 ;; Cache all macro scopes as an optimization. See issue #208
1415 (let ((rust-macro-scopes (rust-macro-scope start end)))
1416 (goto-char start)
1417 (let ((str-start (rust-in-str-or-cmnt)))
1418 (when str-start
1419 (rust--syntax-propertize-raw-string str-start end)))
1420 (funcall
1421 (syntax-propertize-rules
1422 ;; Character literals.
1423 (rust--char-literal-rx (1 "\"") (2 "\""))
1424 ;; Raw strings.
1425 ("\\(r\\)#*\""
1426 (0 (ignore
1427 (goto-char (match-end 0))
1428 (unless (save-excursion (nth 8 (syntax-ppss (match-beginning 0))))
1429 (put-text-property (match-beginning 1) (match-end 1)
1430 'syntax-table (string-to-syntax "|"))
1431 (rust--syntax-propertize-raw-string (match-beginning 0) end)))))
1432 ("[<>]"
1433 (0 (ignore
1434 (when (save-match-data
1435 (save-excursion
1436 (goto-char (match-beginning 0))
1437 (rust-ordinary-lt-gt-p)))
1438 (put-text-property (match-beginning 0) (match-end 0)
1439 'syntax-table (string-to-syntax "."))
1440 (goto-char (match-end 0)))))))
1441 (point) end)))
1442
1443(defun rust-fill-prefix-for-comment-start (line-start)
1444 "Determine what to use for `fill-prefix' based on the text at LINE-START."
1445 (let ((result
1446 ;; Replace /* with same number of spaces
1447 (replace-regexp-in-string
1448 "\\(?:/\\*+?\\)[!*]?"
1449 (lambda (s)
1450 ;; We want the * to line up with the first * of the
1451 ;; comment start
1452 (let ((offset (if (eq t
1453 (compare-strings "/*" nil nil
1454 s
1455 (- (length s) 2)
1456 (length s)))
1457 1 2)))
1458 (concat (make-string (- (length s) offset)
1459 ?\x20) "*")))
1460 line-start)))
1461 ;; Make sure we've got at least one space at the end
1462 (if (not (= (aref result (- (length result) 1)) ?\x20))
1463 (setq result (concat result " ")))
1464 result))
1465
1466(defun rust-in-comment-paragraph (body)
1467 ;; We might move the point to fill the next comment, but we don't want it
1468 ;; seeming to jump around on the user
1469 (save-excursion
1470 ;; If we're outside of a comment, with only whitespace and then a comment
1471 ;; in front, jump to the comment and prepare to fill it.
1472 (when (not (nth 4 (syntax-ppss)))
1473 (beginning-of-line)
1474 (when (looking-at (concat "[[:space:]\n]*" comment-start-skip))
1475 (goto-char (match-end 0))))
1476
1477 ;; We need this when we're moving the point around and then checking syntax
1478 ;; while doing paragraph fills, because the cache it uses isn't always
1479 ;; invalidated during this.
1480 (syntax-ppss-flush-cache 1)
1481 ;; If we're at the beginning of a comment paragraph with nothing but
1482 ;; whitespace til the next line, jump to the next line so that we use the
1483 ;; existing prefix to figure out what the new prefix should be, rather than
1484 ;; inferring it from the comment start.
1485 (let ((next-bol (line-beginning-position 2)))
1486 (while (save-excursion
1487 (end-of-line)
1488 (syntax-ppss-flush-cache 1)
1489 (and (nth 4 (syntax-ppss))
1490 (save-excursion
1491 (beginning-of-line)
1492 (looking-at paragraph-start))
1493 (looking-at "[[:space:]]*$")
1494 (nth 4 (syntax-ppss next-bol))))
1495 (goto-char next-bol)))
1496
1497 (syntax-ppss-flush-cache 1)
1498 ;; If we're on the last line of a multiline-style comment that started
1499 ;; above, back up one line so we don't mistake the * of the */ that ends
1500 ;; the comment for a prefix.
1501 (when (save-excursion
1502 (and (nth 4 (syntax-ppss (line-beginning-position 1)))
1503 (looking-at "[[:space:]]*\\*/")))
1504 (goto-char (line-end-position 0)))
1505 (funcall body)))
1506
1507(defun rust-with-comment-fill-prefix (body)
1508 (let*
1509 ((line-string (buffer-substring-no-properties
1510 (line-beginning-position) (line-end-position)))
1511 (line-comment-start
1512 (when (nth 4 (syntax-ppss))
1513 (cond
1514 ;; If we're inside the comment and see a * prefix, use it
1515 ((string-match "^\\([[:space:]]*\\*+[[:space:]]*\\)"
1516 line-string)
1517 (match-string 1 line-string))
1518 ;; If we're at the start of a comment, figure out what prefix
1519 ;; to use for the subsequent lines after it
1520 ((string-match (concat "[[:space:]]*" comment-start-skip) line-string)
1521 (rust-fill-prefix-for-comment-start
1522 (match-string 0 line-string))))))
1523 (fill-prefix
1524 (or line-comment-start
1525 fill-prefix)))
1526 (funcall body)))
1527
1528(defun rust-find-fill-prefix ()
1529 (rust-in-comment-paragraph
1530 (lambda ()
1531 (rust-with-comment-fill-prefix
1532 (lambda ()
1533 fill-prefix)))))
1534
1535(defun rust-fill-paragraph (&rest args)
1536 "Special wrapping for `fill-paragraph'.
1537This handles multi-line comments with a * prefix on each line."
1538 (rust-in-comment-paragraph
1539 (lambda ()
1540 (rust-with-comment-fill-prefix
1541 (lambda ()
1542 (let
1543 ((fill-paragraph-function
1544 (if (not (eq fill-paragraph-function #'rust-fill-paragraph))
1545 fill-paragraph-function))
1546 (fill-paragraph-handle-comment t))
1547 (apply #'fill-paragraph args)
1548 t))))))
1549
1550(defun rust-do-auto-fill (&rest args)
1551 "Special wrapping for `do-auto-fill'.
1552This handles multi-line comments with a * prefix on each line."
1553 (rust-with-comment-fill-prefix
1554 (lambda ()
1555 (apply #'do-auto-fill args)
1556 t)))
1557
1558(defun rust-fill-forward-paragraph (arg)
1559 ;; This is to work around some funny behavior when a paragraph separator is
1560 ;; at the very top of the file and there is a fill prefix.
1561 (let ((fill-prefix nil)) (forward-paragraph arg)))
1562
1563(defun rust-comment-indent-new-line (&optional arg)
1564 (rust-with-comment-fill-prefix
1565 (lambda () (comment-indent-new-line arg))))
1566
1567;;; Defun Motions
1568
1569(defun rust-beginning-of-defun (&optional arg)
1570 "Move backward to the beginning of the current defun.
1571
1572With ARG, move backward multiple defuns. Negative ARG means
1573move forward.
1574
1575This is written mainly to be used as `beginning-of-defun-function' for Rust.
1576Don't move to the beginning of the line. `beginning-of-defun',
1577which calls this, does that afterwards."
1578 (interactive "p")
1579 (let* ((arg (or arg 1))
1580 (magnitude (abs arg))
1581 (sign (if (< arg 0) -1 1)))
1582 ;; If moving forward, don't find the defun we might currently be
1583 ;; on.
1584 (when (< sign 0)
1585 (end-of-line))
1586 (catch 'done
1587 (dotimes (_ magnitude)
1588 ;; Search until we find a match that is not in a string or comment.
1589 (while (if (re-search-backward (concat "^\\(" rust-top-item-beg-re "\\)")
1590 nil 'move sign)
1591 (rust-in-str-or-cmnt)
1592 ;; Did not find it.
1593 (throw 'done nil)))))
1594 t))
1595
1596(defun rust-end-of-defun ()
1597 "Move forward to the next end of defun.
1598
1599With argument, do it that many times.
1600Negative argument -N means move back to Nth preceding end of defun.
1601
1602Assume that this is called after `beginning-of-defun'. So point is
1603at the beginning of the defun body.
1604
1605This is written mainly to be used as `end-of-defun-function' for Rust."
1606 (interactive)
1607 ;; Find the opening brace
1608 (if (re-search-forward "[{]" nil t)
1609 (progn
1610 (goto-char (match-beginning 0))
1611 ;; Go to the closing brace
1612 (condition-case nil
1613 (forward-sexp)
1614 (scan-error
1615 ;; The parentheses are unbalanced; instead of being unable
1616 ;; to fontify, just jump to the end of the buffer
1617 (goto-char (point-max)))))
1618 ;; There is no opening brace, so consider the whole buffer to be one "defun"
1619 (goto-char (point-max))))
1620
1621;;; _
1622
1623(defun rust-mode-reload ()
1624 (interactive)
1625 (unload-feature 'rust-mode)
1626 (require 'rust-mode)
1627 (rust-mode))
1628
1629(provide 'rust-mode)
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
115In case of multiple problems cycles through them. Displays the
116rustfmt 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\\|\
154match\\|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
239Return 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
18For example, if the current buffer is visiting the file `foo.rs',
19then this function creates the directory `foo' and renames the
20file to `foo/mod.rs'. The current buffer will be updated to
21visit 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."
426otherwise do nothing." 426otherwise 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)