diff options
Diffstat (limited to '.emacs.d/lisp/rust-mode.el')
| -rw-r--r-- | .emacs.d/lisp/rust-mode.el | 1575 |
1 files changed, 15 insertions, 1560 deletions
diff --git a/.emacs.d/lisp/rust-mode.el b/.emacs.d/lisp/rust-mode.el index 80106b8..a0f6542 100644 --- a/.emacs.d/lisp/rust-mode.el +++ b/.emacs.d/lisp/rust-mode.el | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | ;;; rust-mode.el --- A major-mode for editing Rust source code -*-lexical-binding: t-*- | 1 | ;;; rust-mode.el --- A major-mode for editing Rust source code -*-lexical-binding: t-*- |
| 2 | 2 | ||
| 3 | ;; Version: 1.0.5 | 3 | ;; Version: 1.0.5 |
| 4 | ;; Author: Mozilla | 4 | ;; Author: Mozilla <rust-mode@noreply.github.com> |
| 5 | ;; Url: https://github.com/rust-lang/rust-mode | 5 | ;; Url: https://github.com/rust-lang/rust-mode |
| 6 | ;; Keywords: languages | 6 | ;; Keywords: languages |
| 7 | ;; Package-Requires: ((emacs "25.1")) | 7 | ;; Package-Requires: ((emacs "25.1")) |
| @@ -14,8 +14,11 @@ | |||
| 14 | ;; This package implements a major-mode for editing Rust source code. | 14 | ;; This package implements a major-mode for editing Rust source code. |
| 15 | 15 | ||
| 16 | ;;; Code: | 16 | ;;; Code: |
| 17 | (require 'rust-common) | ||
| 17 | 18 | ||
| 18 | (eval-when-compile (require 'rx)) | 19 | (eval-when-compile |
| 20 | (require 'rx) | ||
| 21 | (require 'subr-x)) | ||
| 19 | 22 | ||
| 20 | (defvar rust-load-optional-libraries t | 23 | (defvar rust-load-optional-libraries t |
| 21 | "Whether loading `rust-mode' also loads optional libraries. | 24 | "Whether loading `rust-mode' also loads optional libraries. |
| @@ -27,26 +30,6 @@ This variable might soon be remove again.") | |||
| 27 | (require 'rust-playpen) | 30 | (require 'rust-playpen) |
| 28 | (require 'rust-rustfmt)) | 31 | (require 'rust-rustfmt)) |
| 29 | 32 | ||
| 30 | (defvar electric-pair-inhibit-predicate) | ||
| 31 | (defvar electric-pair-skip-self) | ||
| 32 | (defvar electric-indent-chars) | ||
| 33 | |||
| 34 | (defcustom rust-before-save-hook 'rust-before-save-method | ||
| 35 | "Function for formatting before save." | ||
| 36 | :type 'function | ||
| 37 | :group 'rust-mode) | ||
| 38 | |||
| 39 | (defcustom rust-after-save-hook 'rust-after-save-method | ||
| 40 | "Default method to handle rustfmt invocation after save." | ||
| 41 | :type 'function | ||
| 42 | :group 'rust-mode) | ||
| 43 | |||
| 44 | (defvar rust-prettify-symbols-alist | ||
| 45 | '(("&&" . ?∧) ("||" . ?∨) | ||
| 46 | ("<=" . ?≤) (">=" . ?≥) ("!=" . ?≠) | ||
| 47 | ("INFINITY" . ?∞) ("->" . ?→) ("=>" . ?⇒)) | ||
| 48 | "Alist of symbol prettifications used for `prettify-symbols-alist'.") | ||
| 49 | |||
| 50 | ;;; Customization | 33 | ;;; Customization |
| 51 | 34 | ||
| 52 | (defgroup rust-mode nil | 35 | (defgroup rust-mode nil |
| @@ -54,37 +37,12 @@ This variable might soon be remove again.") | |||
| 54 | :link '(url-link "https://www.rust-lang.org/") | 37 | :link '(url-link "https://www.rust-lang.org/") |
| 55 | :group 'languages) | 38 | :group 'languages) |
| 56 | 39 | ||
| 57 | (defcustom rust-indent-offset 4 | 40 | (defcustom rust-mode-treesitter-derive nil |
| 58 | "Indent Rust code by this number of spaces." | 41 | "Whether rust-mode should derive from the new treesitter mode `rust-ts-mode' |
| 59 | :type 'integer | 42 | instead of `prog-mode'. This option requires emacs29+." |
| 60 | :group 'rust-mode | 43 | :version "29.1" |
| 61 | :safe #'integerp) | ||
| 62 | |||
| 63 | (defcustom rust-indent-method-chain nil | ||
| 64 | "Indent Rust method chains, aligned by the `.' operators." | ||
| 65 | :type 'boolean | ||
| 66 | :group 'rust-mode | ||
| 67 | :safe #'booleanp) | ||
| 68 | |||
| 69 | (defcustom rust-indent-where-clause nil | ||
| 70 | "Indent lines starting with the `where' keyword following a function or trait. | ||
| 71 | When nil, `where' will be aligned with `fn' or `trait'." | ||
| 72 | :type 'boolean | 44 | :type 'boolean |
| 73 | :group 'rust-mode | 45 | :group 'rustic) |
| 74 | :safe #'booleanp) | ||
| 75 | |||
| 76 | (defcustom rust-match-angle-brackets t | ||
| 77 | "Whether to enable angle bracket (`<' and `>') matching where appropriate." | ||
| 78 | :type 'boolean | ||
| 79 | :safe #'booleanp | ||
| 80 | :group 'rust-mode) | ||
| 81 | |||
| 82 | (defcustom rust-indent-return-type-to-arguments t | ||
| 83 | "Indent a line starting with the `->' (RArrow) following a function, aligning | ||
| 84 | to the function arguments. When nil, `->' will be indented one level." | ||
| 85 | :type 'boolean | ||
| 86 | :group 'rust-mode | ||
| 87 | :safe #'booleanp) | ||
| 88 | 46 | ||
| 89 | ;;; Faces | 47 | ;;; Faces |
| 90 | 48 | ||
| @@ -97,141 +55,6 @@ to the function arguments. When nil, `->' will be indented one level." | |||
| 97 | (define-obsolete-face-alias 'rust-string-interpolation-face | 55 | (define-obsolete-face-alias 'rust-string-interpolation-face |
| 98 | 'rust-string-interpolation "0.6.0") | 56 | 'rust-string-interpolation "0.6.0") |
| 99 | 57 | ||
| 100 | (defface rust-unsafe | ||
| 101 | '((t :inherit font-lock-warning-face)) | ||
| 102 | "Face for the `unsafe' keyword." | ||
| 103 | :group 'rust-mode) | ||
| 104 | |||
| 105 | (defface rust-question-mark | ||
| 106 | '((t :weight bold :inherit font-lock-builtin-face)) | ||
| 107 | "Face for the question mark operator." | ||
| 108 | :group 'rust-mode) | ||
| 109 | |||
| 110 | (defface rust-ampersand-face | ||
| 111 | '((t :inherit default)) | ||
| 112 | "Face for the ampersand reference mark." | ||
| 113 | :group 'rust-mode) | ||
| 114 | |||
| 115 | (defface rust-builtin-formatting-macro | ||
| 116 | '((t :inherit font-lock-builtin-face)) | ||
| 117 | "Face for builtin formatting macros (print! &c.)." | ||
| 118 | :group 'rust-mode) | ||
| 119 | |||
| 120 | (defface rust-string-interpolation | ||
| 121 | '((t :slant italic :inherit font-lock-string-face)) | ||
| 122 | "Face for interpolating braces in builtin formatting macro strings." | ||
| 123 | :group 'rust-mode) | ||
| 124 | |||
| 125 | ;;; Syntax | ||
| 126 | |||
| 127 | (defun rust-re-word (inner) (concat "\\<" inner "\\>")) | ||
| 128 | (defun rust-re-grab (inner) (concat "\\(" inner "\\)")) | ||
| 129 | (defun rust-re-shy (inner) (concat "\\(?:" inner "\\)")) | ||
| 130 | |||
| 131 | (defconst rust-re-ident "[[:word:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*") | ||
| 132 | (defconst rust-re-lc-ident "[[:lower:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*") | ||
| 133 | (defconst rust-re-uc-ident "[[:upper:]][[:word:][:multibyte:]_[:digit:]]*") | ||
| 134 | (defvar rust-re-vis | ||
| 135 | ;; pub | pub ( crate ) | pub ( self ) | pub ( super ) | pub ( in SimplePath ) | ||
| 136 | (concat | ||
| 137 | "pub" | ||
| 138 | (rust-re-shy | ||
| 139 | (concat | ||
| 140 | "[[:space:]]*([[:space:]]*" | ||
| 141 | (rust-re-shy | ||
| 142 | (concat "crate" "\\|" | ||
| 143 | "\\(?:s\\(?:elf\\|uper\\)\\)" "\\|" | ||
| 144 | ;; in SimplePath | ||
| 145 | (rust-re-shy | ||
| 146 | (concat | ||
| 147 | "in[[:space:]]+" | ||
| 148 | rust-re-ident | ||
| 149 | (rust-re-shy (concat "::" rust-re-ident)) "*")))) | ||
| 150 | "[[:space:]]*)")) | ||
| 151 | "?")) | ||
| 152 | (defconst rust-re-unsafe "unsafe") | ||
| 153 | (defconst rust-re-extern "extern") | ||
| 154 | (defconst rust-re-async-or-const "async\\|const") | ||
| 155 | (defconst rust-re-generic | ||
| 156 | (concat "<[[:space:]]*'" rust-re-ident "[[:space:]]*>")) | ||
| 157 | (defconst rust-re-union | ||
| 158 | (rx-to-string | ||
| 159 | `(seq | ||
| 160 | (or space line-start) | ||
| 161 | (group symbol-start "union" symbol-end) | ||
| 162 | (+ space) (regexp ,rust-re-ident)))) | ||
| 163 | |||
| 164 | (defun rust-re-item-def (itype) | ||
| 165 | (concat (rust-re-word itype) | ||
| 166 | (rust-re-shy rust-re-generic) "?" | ||
| 167 | "[[:space:]]+" (rust-re-grab rust-re-ident))) | ||
| 168 | |||
| 169 | ;; TODO some of this does only make sense for `fn' (unsafe, extern...) | ||
| 170 | ;; and not other items | ||
| 171 | (defun rust-re-item-def-imenu (itype) | ||
| 172 | (concat "^[[:space:]]*" | ||
| 173 | (rust-re-shy (concat rust-re-vis "[[:space:]]+")) "?" | ||
| 174 | (rust-re-shy (concat (rust-re-word "default") "[[:space:]]+")) "?" | ||
| 175 | (rust-re-shy (concat (rust-re-shy rust-re-async-or-const) "[[:space:]]+")) "?" | ||
| 176 | (rust-re-shy (concat (rust-re-word rust-re-unsafe) "[[:space:]]+")) "?" | ||
| 177 | (rust-re-shy (concat (rust-re-word rust-re-extern) "[[:space:]]+" | ||
| 178 | (rust-re-shy "\"[^\"]+\"[[:space:]]+") "?")) "?" | ||
| 179 | (rust-re-item-def itype))) | ||
| 180 | |||
| 181 | (defvar rust-imenu-generic-expression | ||
| 182 | (append (mapcar #'(lambda (x) | ||
| 183 | (list (capitalize x) (rust-re-item-def-imenu x) 1)) | ||
| 184 | '("enum" "struct" "union" "type" "mod" "fn" "trait" "impl")) | ||
| 185 | `(("Macro" ,(rust-re-item-def-imenu "macro_rules!") 1))) | ||
| 186 | "Value for `imenu-generic-expression' in Rust mode. | ||
| 187 | |||
| 188 | Create a hierarchical index of the item definitions in a Rust file. | ||
| 189 | |||
| 190 | Imenu will show all the enums, structs, etc. in their own subheading. | ||
| 191 | Use idomenu (imenu with `ido-mode') for best mileage.") | ||
| 192 | |||
| 193 | (defvar rust-mode-syntax-table | ||
| 194 | (let ((table (make-syntax-table))) | ||
| 195 | |||
| 196 | ;; Operators | ||
| 197 | (dolist (i '(?+ ?- ?* ?/ ?% ?& ?| ?^ ?! ?< ?> ?~ ?@)) | ||
| 198 | (modify-syntax-entry i "." table)) | ||
| 199 | |||
| 200 | ;; Strings | ||
| 201 | (modify-syntax-entry ?\" "\"" table) | ||
| 202 | (modify-syntax-entry ?\\ "\\" table) | ||
| 203 | |||
| 204 | ;; Angle brackets. We suppress this with syntactic propertization | ||
| 205 | ;; when needed | ||
| 206 | (modify-syntax-entry ?< "(>" table) | ||
| 207 | (modify-syntax-entry ?> ")<" table) | ||
| 208 | |||
| 209 | ;; Comments | ||
| 210 | (modify-syntax-entry ?/ ". 124b" table) | ||
| 211 | (modify-syntax-entry ?* ". 23n" table) | ||
| 212 | (modify-syntax-entry ?\n "> b" table) | ||
| 213 | (modify-syntax-entry ?\^m "> b" table) | ||
| 214 | |||
| 215 | table) | ||
| 216 | "Syntax definitions and helpers.") | ||
| 217 | |||
| 218 | ;;; Prettify | ||
| 219 | |||
| 220 | (defun rust--prettify-symbols-compose-p (start end match) | ||
| 221 | "Return true iff the symbol MATCH should be composed. | ||
| 222 | See `prettify-symbols-compose-predicate'." | ||
| 223 | (and (fboundp 'prettify-symbols-default-compose-p) | ||
| 224 | (prettify-symbols-default-compose-p start end match) | ||
| 225 | ;; Make sure || is not a closure with 0 arguments and && is not | ||
| 226 | ;; a double reference. | ||
| 227 | (pcase match | ||
| 228 | ("||" (not (save-excursion | ||
| 229 | (goto-char start) | ||
| 230 | (looking-back "\\(?:\\<move\\|[[({:=,;]\\) *" | ||
| 231 | (line-beginning-position))))) | ||
| 232 | ("&&" (char-equal (char-after end) ?\s)) | ||
| 233 | (_ t)))) | ||
| 234 | |||
| 235 | ;;; Mode | 58 | ;;; Mode |
| 236 | 59 | ||
| 237 | (defvar rust-mode-map | 60 | (defvar rust-mode-map |
| @@ -248,1384 +71,16 @@ See `prettify-symbols-compose-predicate'." | |||
| 248 | map) | 71 | map) |
| 249 | "Keymap for Rust major mode.") | 72 | "Keymap for Rust major mode.") |
| 250 | 73 | ||
| 251 | ;;;###autoload | 74 | (if (and (version<= "29.1" emacs-version) rust-mode-treesitter-derive) |
| 252 | (define-derived-mode rust-mode prog-mode "Rust" | 75 | (require 'rust-mode-treesitter) |
| 253 | "Major mode for Rust code. | 76 | (require 'rust-prog-mode)) |
| 254 | |||
| 255 | \\{rust-mode-map}" | ||
| 256 | :group 'rust-mode | ||
| 257 | :syntax-table rust-mode-syntax-table | ||
| 258 | |||
| 259 | ;; Syntax | ||
| 260 | (setq-local syntax-propertize-function #'rust-syntax-propertize) | ||
| 261 | |||
| 262 | ;; Indentation | ||
| 263 | (setq-local indent-line-function 'rust-mode-indent-line) | ||
| 264 | |||
| 265 | ;; Fonts | ||
| 266 | (setq-local font-lock-defaults | ||
| 267 | '(rust-font-lock-keywords | ||
| 268 | nil nil nil nil | ||
| 269 | (font-lock-syntactic-face-function | ||
| 270 | . rust-mode-syntactic-face-function))) | ||
| 271 | |||
| 272 | ;; Misc | ||
| 273 | (setq-local comment-start "// ") | ||
| 274 | (setq-local comment-end "") | ||
| 275 | (setq-local open-paren-in-column-0-is-defun-start nil) | ||
| 276 | 77 | ||
| 277 | ;; Auto indent on } | 78 | ;;;###autoload |
| 278 | (setq-local electric-indent-chars | 79 | (autoload 'rust-mode "rust-mode" "Major mode for Rust code." t) |
| 279 | (cons ?} (and (boundp 'electric-indent-chars) | ||
| 280 | electric-indent-chars))) | ||
| 281 | |||
| 282 | ;; Allow paragraph fills for comments | ||
| 283 | (setq-local comment-start-skip "\\(?://[/!]*\\|/\\*[*!]?\\)[[:space:]]*") | ||
| 284 | (setq-local paragraph-start | ||
| 285 | (concat "[[:space:]]*\\(?:" | ||
| 286 | comment-start-skip | ||
| 287 | "\\|\\*/?[[:space:]]*\\|\\)$")) | ||
| 288 | (setq-local paragraph-separate paragraph-start) | ||
| 289 | (setq-local normal-auto-fill-function #'rust-do-auto-fill) | ||
| 290 | (setq-local fill-paragraph-function #'rust-fill-paragraph) | ||
| 291 | (setq-local fill-forward-paragraph-function #'rust-fill-forward-paragraph) | ||
| 292 | (setq-local adaptive-fill-function #'rust-find-fill-prefix) | ||
| 293 | (setq-local adaptive-fill-first-line-regexp "") | ||
| 294 | (setq-local comment-multi-line t) | ||
| 295 | (setq-local comment-line-break-function #'rust-comment-indent-new-line) | ||
| 296 | (setq-local imenu-generic-expression rust-imenu-generic-expression) | ||
| 297 | (setq-local imenu-syntax-alist '((?! . "w"))) ; For macro_rules! | ||
| 298 | (setq-local beginning-of-defun-function #'rust-beginning-of-defun) | ||
| 299 | (setq-local end-of-defun-function #'rust-end-of-defun) | ||
| 300 | (setq-local parse-sexp-lookup-properties t) | ||
| 301 | (setq-local electric-pair-inhibit-predicate | ||
| 302 | #'rust-electric-pair-inhibit-predicate-wrap) | ||
| 303 | (add-function :before-until (local 'electric-pair-skip-self) | ||
| 304 | #'rust-electric-pair-skip-self) | ||
| 305 | ;; Configure prettify | ||
| 306 | (setq prettify-symbols-alist rust-prettify-symbols-alist) | ||
| 307 | (setq prettify-symbols-compose-predicate #'rust--prettify-symbols-compose-p) | ||
| 308 | |||
| 309 | (add-hook 'before-save-hook rust-before-save-hook nil t) | ||
| 310 | (add-hook 'after-save-hook rust-after-save-hook nil t)) | ||
| 311 | 80 | ||
| 312 | ;;;###autoload | 81 | ;;;###autoload |
| 313 | (add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-mode)) | 82 | (add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-mode)) |
| 314 | 83 | ||
| 315 | (defvar rust-top-item-beg-re | ||
| 316 | (concat "\\s-*\\(?:priv\\|pub\\)?\\s-*" | ||
| 317 | ;; TODO some of this does only make sense for `fn' (unsafe, extern...) | ||
| 318 | ;; and not other items | ||
| 319 | (rust-re-shy (concat (rust-re-shy rust-re-vis) "[[:space:]]+")) "?" | ||
| 320 | (rust-re-shy (concat (rust-re-shy rust-re-async-or-const) "[[:space:]]+")) "?" | ||
| 321 | (rust-re-shy (concat (rust-re-shy rust-re-unsafe) "[[:space:]]+")) "?" | ||
| 322 | (regexp-opt | ||
| 323 | '("enum" "struct" "union" "type" "mod" "fn" "static" "impl" | ||
| 324 | "extern" "trait" "async")) | ||
| 325 | "\\_>") | ||
| 326 | "Start of a Rust item.") | ||
| 327 | |||
| 328 | (defconst rust-re-type-or-constructor | ||
| 329 | (rx symbol-start | ||
| 330 | (group upper (0+ (any word nonascii digit "_"))) | ||
| 331 | symbol-end)) | ||
| 332 | |||
| 333 | (defconst rust-keywords | ||
| 334 | '("as" "async" "await" | ||
| 335 | "box" "break" | ||
| 336 | "const" "continue" "crate" | ||
| 337 | "do" "dyn" | ||
| 338 | "else" "enum" "extern" "existential" | ||
| 339 | "false" "fn" "for" | ||
| 340 | "if" "impl" "in" | ||
| 341 | "let" "loop" | ||
| 342 | "match" "mod" "move" "mut" | ||
| 343 | "priv" "pub" | ||
| 344 | "ref" "return" | ||
| 345 | "self" "static" "struct" "super" | ||
| 346 | "true" "trait" "type" "try" | ||
| 347 | "use" | ||
| 348 | "virtual" | ||
| 349 | "where" "while" | ||
| 350 | "yield") | ||
| 351 | "Font-locking definitions and helpers.") | ||
| 352 | |||
| 353 | (defconst rust-special-types | ||
| 354 | '("u8" "i8" | ||
| 355 | "u16" "i16" | ||
| 356 | "u32" "i32" | ||
| 357 | "u64" "i64" | ||
| 358 | "u128" "i128" | ||
| 359 | |||
| 360 | "f32" "f64" | ||
| 361 | "isize" "usize" | ||
| 362 | "bool" | ||
| 363 | "str" "char")) | ||
| 364 | |||
| 365 | (defconst rust-number-with-type | ||
| 366 | (eval-when-compile | ||
| 367 | (concat | ||
| 368 | "\\_<\\(?:0[box]?\\|[1-9]\\)[[:digit:]a-fA-F_.]*\\(?:[eE][+-]?[[:digit:]_]\\)?" | ||
| 369 | (regexp-opt '("u8" "i8" "u16" "i16" "u32" "i32" "u64" "i64" | ||
| 370 | "u128" "i128" "usize" "isize" "f32" "f64") | ||
| 371 | t) | ||
| 372 | "\\_>")) | ||
| 373 | "Regular expression matching a number with a type suffix.") | ||
| 374 | |||
| 375 | (defvar rust-builtin-formatting-macros | ||
| 376 | '("eprint" | ||
| 377 | "eprintln" | ||
| 378 | "format" | ||
| 379 | "print" | ||
| 380 | "println") | ||
| 381 | "List of builtin Rust macros for string formatting. | ||
| 382 | This is used by `rust-font-lock-keywords'. | ||
| 383 | \(`write!' is handled separately).") | ||
| 384 | |||
| 385 | (defvar rust-formatting-macro-opening-re | ||
| 386 | "[[:space:]\n]*[({[][[:space:]\n]*" | ||
| 387 | "Regular expression to match the opening delimiter of a Rust formatting macro.") | ||
| 388 | |||
| 389 | (defvar rust-start-of-string-re | ||
| 390 | "\\(?:r#*\\)?\"" | ||
| 391 | "Regular expression to match the start of a Rust raw string.") | ||
| 392 | |||
| 393 | (defun rust-path-font-lock-matcher (re-ident) | ||
| 394 | "Match occurrences of RE-IDENT followed by a double-colon. | ||
| 395 | Examples include to match names like \"foo::\" or \"Foo::\". | ||
| 396 | Does not match type annotations of the form \"foo::<\"." | ||
| 397 | `(lambda (limit) | ||
| 398 | (catch 'rust-path-font-lock-matcher | ||
| 399 | (while t | ||
| 400 | (let* ((symbol-then-colons (rx-to-string '(seq (group (regexp ,re-ident)) "::"))) | ||
| 401 | (match (re-search-forward symbol-then-colons limit t))) | ||
| 402 | (cond | ||
| 403 | ;; If we didn't find a match, there are no more occurrences | ||
| 404 | ;; of foo::, so return. | ||
| 405 | ((null match) (throw 'rust-path-font-lock-matcher nil)) | ||
| 406 | ;; If this isn't a type annotation foo::<, we've found a | ||
| 407 | ;; match, so a return it! | ||
| 408 | ((not (looking-at (rx (0+ space) "<"))) | ||
| 409 | (throw 'rust-path-font-lock-matcher match)))))))) | ||
| 410 | |||
| 411 | (defvar rust-font-lock-keywords | ||
| 412 | (append | ||
| 413 | `( | ||
| 414 | ;; Keywords proper | ||
| 415 | (,(regexp-opt rust-keywords 'symbols) . font-lock-keyword-face) | ||
| 416 | |||
| 417 | ;; Contextual keywords | ||
| 418 | ("\\_<\\(default\\)[[:space:]]+fn\\_>" 1 font-lock-keyword-face) | ||
| 419 | (,rust-re-union 1 font-lock-keyword-face) | ||
| 420 | |||
| 421 | ;; Special types | ||
| 422 | (,(regexp-opt rust-special-types 'symbols) . font-lock-type-face) | ||
| 423 | |||
| 424 | ;; The unsafe keyword | ||
| 425 | ("\\_<unsafe\\_>" . 'rust-unsafe) | ||
| 426 | |||
| 427 | ;; Attributes like `#[bar(baz)]` or `#![bar(baz)]` or `#[bar = "baz"]` | ||
| 428 | (,(rust-re-grab (concat "#\\!?\\[" rust-re-ident "[^]]*\\]")) | ||
| 429 | 1 font-lock-preprocessor-face keep) | ||
| 430 | |||
| 431 | ;; Builtin formatting macros | ||
| 432 | (,(concat (rust-re-grab | ||
| 433 | (concat (rust-re-word (regexp-opt rust-builtin-formatting-macros)) | ||
| 434 | "!")) | ||
| 435 | rust-formatting-macro-opening-re | ||
| 436 | "\\(?:" rust-start-of-string-re "\\)?") | ||
| 437 | (1 'rust-builtin-formatting-macro) | ||
| 438 | (rust-string-interpolation-matcher | ||
| 439 | (rust-end-of-string) | ||
| 440 | nil | ||
| 441 | (0 'rust-string-interpolation t nil))) | ||
| 442 | |||
| 443 | ;; write! macro | ||
| 444 | (,(concat (rust-re-grab (concat (rust-re-word "write\\(ln\\)?") "!")) | ||
| 445 | rust-formatting-macro-opening-re | ||
| 446 | "[[:space:]]*[^\"]+,[[:space:]]*" | ||
| 447 | rust-start-of-string-re) | ||
| 448 | (1 'rust-builtin-formatting-macro) | ||
| 449 | (rust-string-interpolation-matcher | ||
| 450 | (rust-end-of-string) | ||
| 451 | nil | ||
| 452 | (0 'rust-string-interpolation t nil))) | ||
| 453 | |||
| 454 | ;; Syntax extension invocations like `foo!`, highlight including the ! | ||
| 455 | (,(concat (rust-re-grab (concat rust-re-ident "!")) "[({[:space:][]") | ||
| 456 | 1 font-lock-preprocessor-face) | ||
| 457 | |||
| 458 | ;; Field names like `foo:`, highlight excluding the : | ||
| 459 | (,(concat (rust-re-grab rust-re-ident) "[[:space:]]*:[^:]") | ||
| 460 | 1 font-lock-variable-name-face) | ||
| 461 | |||
| 462 | ;; CamelCase Means Type Or Constructor | ||
| 463 | (,rust-re-type-or-constructor 1 font-lock-type-face) | ||
| 464 | |||
| 465 | ;; Type-inferred binding | ||
| 466 | (,(concat "\\_<\\(?:let\\s-+ref\\|let\\|ref\\|for\\)\\s-+\\(?:mut\\s-+\\)?" | ||
| 467 | (rust-re-grab rust-re-ident) | ||
| 468 | "\\_>") | ||
| 469 | 1 font-lock-variable-name-face) | ||
| 470 | |||
| 471 | ;; Type names like `Foo::`, highlight excluding the :: | ||
| 472 | (,(rust-path-font-lock-matcher rust-re-uc-ident) 1 font-lock-type-face) | ||
| 473 | |||
| 474 | ;; Module names like `foo::`, highlight excluding the :: | ||
| 475 | (,(rust-path-font-lock-matcher rust-re-lc-ident) 1 font-lock-constant-face) | ||
| 476 | |||
| 477 | ;; Lifetimes like `'foo` | ||
| 478 | (,(concat "'" (rust-re-grab rust-re-ident) "[^']") 1 font-lock-variable-name-face) | ||
| 479 | |||
| 480 | ;; Question mark operator | ||
| 481 | ("\\?" . 'rust-question-mark) | ||
| 482 | ("\\(&+\\)\\(?:'\\(?:\\<\\|_\\)\\|\\<\\|[[({:*_|]\\)" | ||
| 483 | 1 'rust-ampersand-face) | ||
| 484 | ;; Numbers with type suffix | ||
| 485 | (,rust-number-with-type 1 font-lock-type-face) | ||
| 486 | ) | ||
| 487 | |||
| 488 | ;; Ensure we highlight `Foo` in `struct Foo` as a type. | ||
| 489 | (mapcar #'(lambda (x) | ||
| 490 | (list (rust-re-item-def (car x)) | ||
| 491 | 1 (cdr x))) | ||
| 492 | '(("enum" . font-lock-type-face) | ||
| 493 | ("struct" . font-lock-type-face) | ||
| 494 | ("union" . font-lock-type-face) | ||
| 495 | ("type" . font-lock-type-face) | ||
| 496 | ("mod" . font-lock-constant-face) | ||
| 497 | ("use" . font-lock-constant-face) | ||
| 498 | ("fn" . font-lock-function-name-face))))) | ||
| 499 | |||
| 500 | (defun rust-end-of-string () | ||
| 501 | "Skip to the end of the current string." | ||
| 502 | (save-excursion | ||
| 503 | (skip-syntax-forward "^\"|") | ||
| 504 | (skip-syntax-forward "\"|") | ||
| 505 | (point))) | ||
| 506 | |||
| 507 | (defun rust-looking-back-str (str) | ||
| 508 | "Return non-nil if there's a match on the text before point and STR. | ||
| 509 | Like `looking-back' but for fixed strings rather than regexps (so | ||
| 510 | that it's not so slow)." | ||
| 511 | (let ((len (length str))) | ||
| 512 | (and (> (point) len) | ||
| 513 | (equal str (buffer-substring-no-properties (- (point) len) (point)))))) | ||
| 514 | |||
| 515 | (defun rust-looking-back-symbols (symbols) | ||
| 516 | "Return non-nil if the point is after a member of SYMBOLS. | ||
| 517 | SYMBOLS is a list of strings that represent the respective | ||
| 518 | symbols." | ||
| 519 | (save-excursion | ||
| 520 | (let* ((pt-orig (point)) | ||
| 521 | (beg-of-symbol (progn (forward-thing 'symbol -1) (point))) | ||
| 522 | (end-of-symbol (progn (forward-thing 'symbol 1) (point)))) | ||
| 523 | (and | ||
| 524 | (= end-of-symbol pt-orig) | ||
| 525 | (member (buffer-substring-no-properties beg-of-symbol pt-orig) | ||
| 526 | symbols))))) | ||
| 527 | |||
| 528 | (defun rust-looking-back-ident () | ||
| 529 | "Non-nil if we are looking backwards at a valid rust identifier." | ||
| 530 | (let ((beg-of-symbol (save-excursion (forward-thing 'symbol -1) (point)))) | ||
| 531 | (looking-back rust-re-ident beg-of-symbol))) | ||
| 532 | |||
| 533 | (defun rust-looking-back-macro () | ||
| 534 | "Non-nil if looking back at an ident followed by a ! | ||
| 535 | |||
| 536 | This is stricter than rust syntax which allows a space between | ||
| 537 | the ident and the ! symbol. If this space is allowed, then we | ||
| 538 | would also need a keyword check to avoid `if !(condition)` being | ||
| 539 | seen as a macro." | ||
| 540 | (if (> (- (point) (point-min)) 1) | ||
| 541 | (save-excursion | ||
| 542 | (backward-char) | ||
| 543 | (and (= ?! (char-after)) | ||
| 544 | (rust-looking-back-ident))))) | ||
| 545 | |||
| 546 | ;;; Syntax definitions and helpers | ||
| 547 | |||
| 548 | (defun rust-paren-level () (nth 0 (syntax-ppss))) | ||
| 549 | (defun rust-in-str () (nth 3 (syntax-ppss))) | ||
| 550 | (defun rust-in-str-or-cmnt () (nth 8 (syntax-ppss))) | ||
| 551 | (defun rust-rewind-past-str-cmnt () (goto-char (nth 8 (syntax-ppss)))) | ||
| 552 | |||
| 553 | (defun rust-rewind-irrelevant () | ||
| 554 | (let ((continue t)) | ||
| 555 | (while continue | ||
| 556 | (let ((starting (point))) | ||
| 557 | (skip-chars-backward "[:space:]\n") | ||
| 558 | (when (rust-looking-back-str "*/") | ||
| 559 | (backward-char)) | ||
| 560 | (when (rust-in-str-or-cmnt) | ||
| 561 | (rust-rewind-past-str-cmnt)) | ||
| 562 | ;; Rewind until the point no longer moves | ||
| 563 | (setq continue (/= starting (point))))))) | ||
| 564 | |||
| 565 | (defvar-local rust-macro-scopes nil | ||
| 566 | "Cache for the scopes calculated by `rust-macro-scope'. | ||
| 567 | |||
| 568 | This variable can be `let' bound directly or indirectly around | ||
| 569 | `rust-macro-scope' as an optimization but should not be otherwise | ||
| 570 | set.") | ||
| 571 | |||
| 572 | (defun rust-macro-scope (start end) | ||
| 573 | "Return the scope of macros in the buffer. | ||
| 574 | |||
| 575 | The return value is a list of (START END) positions in the | ||
| 576 | buffer. | ||
| 577 | |||
| 578 | If set START and END are optimizations which limit the return | ||
| 579 | value to scopes which are approximately with this range." | ||
| 580 | (save-excursion | ||
| 581 | ;; need to special case macro_rules which has unique syntax | ||
| 582 | (let ((scope nil) | ||
| 583 | (start (or start (point-min))) | ||
| 584 | (end (or end (point-max)))) | ||
| 585 | (goto-char start) | ||
| 586 | ;; if there is a start move back to the previous top level, | ||
| 587 | ;; as any macros before that must have closed by this time. | ||
| 588 | (let ((top (syntax-ppss-toplevel-pos (syntax-ppss)))) | ||
| 589 | (when top | ||
| 590 | (goto-char top))) | ||
| 591 | (while | ||
| 592 | (and | ||
| 593 | ;; The movement below may have moved us passed end, in | ||
| 594 | ;; which case search-forward will error | ||
| 595 | (< (point) end) | ||
| 596 | (search-forward "!" end t)) | ||
| 597 | (let ((pt (point))) | ||
| 598 | (cond | ||
| 599 | ;; in a string or comment is boring, move straight on | ||
| 600 | ((rust-in-str-or-cmnt)) | ||
| 601 | ;; in a normal macro, | ||
| 602 | ((and (skip-chars-forward " \t\n\r") | ||
| 603 | (memq (char-after) | ||
| 604 | '(?\[ ?\( ?\{)) | ||
| 605 | ;; Check that we have a macro declaration after. | ||
| 606 | (rust-looking-back-macro)) | ||
| 607 | (let ((start (point))) | ||
| 608 | (ignore-errors (forward-list)) | ||
| 609 | (setq scope (cons (list start (point)) scope)))) | ||
| 610 | ;; macro_rules, why, why, why did you not use macro syntax?? | ||
| 611 | ((save-excursion | ||
| 612 | ;; yuck -- last test moves point, even if it fails | ||
| 613 | (goto-char (- pt 1)) | ||
| 614 | (skip-chars-backward " \t\n\r") | ||
| 615 | (rust-looking-back-str "macro_rules")) | ||
| 616 | (save-excursion | ||
| 617 | (when (re-search-forward "[[({]" nil t) | ||
| 618 | (backward-char) | ||
| 619 | (let ((start (point))) | ||
| 620 | (ignore-errors (forward-list)) | ||
| 621 | (setq scope (cons (list start (point)) scope))))))))) | ||
| 622 | ;; Return 'empty rather than nil, to indicate a buffer with no | ||
| 623 | ;; macros at all. | ||
| 624 | (or scope 'empty)))) | ||
| 625 | |||
| 626 | (defun rust-in-macro (&optional start end) | ||
| 627 | "Return non-nil when point is within the scope of a macro. | ||
| 628 | |||
| 629 | If START and END are set, minimize the buffer analysis to | ||
| 630 | approximately this location as an optimization. | ||
| 631 | |||
| 632 | Alternatively, if `rust-macro-scopes' is a list use the scope | ||
| 633 | information in this variable. This last is an optimization and | ||
| 634 | the caller is responsible for ensuring that the data in | ||
| 635 | `rust-macro-scopes' is up to date." | ||
| 636 | (when (> (rust-paren-level) 0) | ||
| 637 | (let ((scopes | ||
| 638 | (or | ||
| 639 | rust-macro-scopes | ||
| 640 | (rust-macro-scope start end)))) | ||
| 641 | ;; `rust-macro-scope' can return the symbol `empty' if the | ||
| 642 | ;; buffer has no macros at all. | ||
| 643 | (when (listp scopes) | ||
| 644 | (seq-some | ||
| 645 | (lambda (sc) | ||
| 646 | (and (>= (point) (car sc)) | ||
| 647 | (< (point) (cadr sc)))) | ||
| 648 | scopes))))) | ||
| 649 | |||
| 650 | (defun rust-looking-at-where () | ||
| 651 | "Return T when looking at the \"where\" keyword." | ||
| 652 | (and (looking-at-p "\\bwhere\\b") | ||
| 653 | (not (rust-in-str-or-cmnt)))) | ||
| 654 | |||
| 655 | (defun rust-rewind-to-where (&optional limit) | ||
| 656 | "Rewind the point to the closest occurrence of the \"where\" keyword. | ||
| 657 | Return T iff a where-clause was found. Does not rewind past | ||
| 658 | LIMIT when passed, otherwise only stops at the beginning of the | ||
| 659 | buffer." | ||
| 660 | (when (re-search-backward "\\bwhere\\b" limit t) | ||
| 661 | (if (rust-in-str-or-cmnt) | ||
| 662 | (rust-rewind-to-where limit) | ||
| 663 | t))) | ||
| 664 | |||
| 665 | (defconst rust-re-pre-expression-operators "[-=!%&*/:<>[{(|.^;}]") | ||
| 666 | |||
| 667 | (defconst rust-re-special-types (regexp-opt rust-special-types 'symbols)) | ||
| 668 | |||
| 669 | (defun rust-align-to-expr-after-brace () | ||
| 670 | (save-excursion | ||
| 671 | (forward-char) | ||
| 672 | ;; We don't want to indent out to the open bracket if the | ||
| 673 | ;; open bracket ends the line | ||
| 674 | (when (not (looking-at "[[:blank:]]*\\(?://.*\\)?$")) | ||
| 675 | (when (looking-at "[[:space:]]") | ||
| 676 | (forward-word 1) | ||
| 677 | (backward-word 1)) | ||
| 678 | (current-column)))) | ||
| 679 | |||
| 680 | (defun rust-rewind-to-beginning-of-current-level-expr () | ||
| 681 | (let ((current-level (rust-paren-level))) | ||
| 682 | (back-to-indentation) | ||
| 683 | (when (looking-at "->") | ||
| 684 | (rust-rewind-irrelevant) | ||
| 685 | (back-to-indentation)) | ||
| 686 | (while (> (rust-paren-level) current-level) | ||
| 687 | (backward-up-list) | ||
| 688 | (back-to-indentation)) | ||
| 689 | ;; When we're in the where clause, skip over it. First find out the start | ||
| 690 | ;; of the function and its paren level. | ||
| 691 | (let ((function-start nil) (function-level nil)) | ||
| 692 | (save-excursion | ||
| 693 | (rust-beginning-of-defun) | ||
| 694 | (back-to-indentation) | ||
| 695 | ;; Avoid using multiple-value-bind | ||
| 696 | (setq function-start (point) | ||
| 697 | function-level (rust-paren-level))) | ||
| 698 | ;; On a where clause | ||
| 699 | (when (or (rust-looking-at-where) | ||
| 700 | ;; or in one of the following lines, e.g. | ||
| 701 | ;; where A: Eq | ||
| 702 | ;; B: Hash <- on this line | ||
| 703 | (and (save-excursion | ||
| 704 | (rust-rewind-to-where function-start)) | ||
| 705 | (= current-level function-level))) | ||
| 706 | (goto-char function-start))))) | ||
| 707 | |||
| 708 | (defun rust-align-to-method-chain () | ||
| 709 | (save-excursion | ||
| 710 | ;; for method-chain alignment to apply, we must be looking at | ||
| 711 | ;; another method call or field access or something like | ||
| 712 | ;; that. This avoids rather "eager" jumps in situations like: | ||
| 713 | ;; | ||
| 714 | ;; { | ||
| 715 | ;; something.foo() | ||
| 716 | ;; <indent> | ||
| 717 | ;; | ||
| 718 | ;; Without this check, we would wind up with the cursor under the | ||
| 719 | ;; `.`. In an older version, I had the inverse of the current | ||
| 720 | ;; check, where we checked for situations that should NOT indent, | ||
| 721 | ;; vs checking for the one situation where we SHOULD. It should be | ||
| 722 | ;; clear that this is more robust, but also I find it mildly less | ||
| 723 | ;; annoying to have to press tab again to align to a method chain | ||
| 724 | ;; than to have an over-eager indent in all other cases which must | ||
| 725 | ;; be undone via tab. | ||
| 726 | |||
| 727 | (when (looking-at (concat "\s*\." rust-re-ident)) | ||
| 728 | (forward-line -1) | ||
| 729 | (end-of-line) | ||
| 730 | ;; Keep going up (looking for a line that could contain a method chain) | ||
| 731 | ;; while we're in a comment or on a blank line. Stop when the paren | ||
| 732 | ;; level changes. | ||
| 733 | (let ((level (rust-paren-level))) | ||
| 734 | (while (and (or (rust-in-str-or-cmnt) | ||
| 735 | ;; Only whitespace (or nothing) from the beginning to | ||
| 736 | ;; the end of the line. | ||
| 737 | (looking-back "^\s*" (line-beginning-position))) | ||
| 738 | (= (rust-paren-level) level)) | ||
| 739 | (forward-line -1) | ||
| 740 | (end-of-line))) | ||
| 741 | |||
| 742 | (let | ||
| 743 | ;; skip-dot-identifier is used to position the point at the | ||
| 744 | ;; `.` when looking at something like | ||
| 745 | ;; | ||
| 746 | ;; foo.bar | ||
| 747 | ;; ^ ^ | ||
| 748 | ;; | | | ||
| 749 | ;; | position of point | ||
| 750 | ;; returned offset | ||
| 751 | ;; | ||
| 752 | ((skip-dot-identifier | ||
| 753 | (lambda () | ||
| 754 | (when (and (rust-looking-back-ident) | ||
| 755 | (save-excursion | ||
| 756 | (forward-thing 'symbol -1) | ||
| 757 | (= ?. (char-before)))) | ||
| 758 | (forward-thing 'symbol -1) | ||
| 759 | (backward-char) | ||
| 760 | (- (current-column) rust-indent-offset))))) | ||
| 761 | (cond | ||
| 762 | ;; foo.bar(...) | ||
| 763 | ((looking-back "[)?]" (1- (point))) | ||
| 764 | (backward-list 1) | ||
| 765 | (funcall skip-dot-identifier)) | ||
| 766 | |||
| 767 | ;; foo.bar | ||
| 768 | (t (funcall skip-dot-identifier))))))) | ||
| 769 | |||
| 770 | (defun rust-mode-indent-line () | ||
| 771 | (interactive) | ||
| 772 | (let ((indent | ||
| 773 | (save-excursion | ||
| 774 | (back-to-indentation) | ||
| 775 | ;; Point is now at beginning of current line | ||
| 776 | (let* ((level (rust-paren-level)) | ||
| 777 | (baseline | ||
| 778 | ;; Our "baseline" is one level out from the | ||
| 779 | ;; indentation of the expression containing the | ||
| 780 | ;; innermost enclosing opening bracket. That way | ||
| 781 | ;; if we are within a block that has a different | ||
| 782 | ;; indentation than this mode would give it, we | ||
| 783 | ;; still indent the inside of it correctly relative | ||
| 784 | ;; to the outside. | ||
| 785 | (if (= 0 level) | ||
| 786 | 0 | ||
| 787 | (or | ||
| 788 | (when rust-indent-method-chain | ||
| 789 | (rust-align-to-method-chain)) | ||
| 790 | (save-excursion | ||
| 791 | (rust-rewind-irrelevant) | ||
| 792 | (backward-up-list) | ||
| 793 | (rust-rewind-to-beginning-of-current-level-expr) | ||
| 794 | (+ (current-column) rust-indent-offset)))))) | ||
| 795 | (cond | ||
| 796 | ;; Indent inside a non-raw string only if the previous line | ||
| 797 | ;; ends with a backslash that is inside the same string | ||
| 798 | ((nth 3 (syntax-ppss)) | ||
| 799 | (let* | ||
| 800 | ((string-begin-pos (nth 8 (syntax-ppss))) | ||
| 801 | (end-of-prev-line-pos | ||
| 802 | (and (not (rust--same-line-p (point) (point-min))) | ||
| 803 | (line-end-position 0)))) | ||
| 804 | (when | ||
| 805 | (and | ||
| 806 | ;; If the string begins with an "r" it's a raw string and | ||
| 807 | ;; we should not change the indentation | ||
| 808 | (/= ?r (char-after string-begin-pos)) | ||
| 809 | |||
| 810 | ;; If we're on the first line this will be nil and the | ||
| 811 | ;; rest does not apply | ||
| 812 | end-of-prev-line-pos | ||
| 813 | |||
| 814 | ;; The end of the previous line needs to be inside the | ||
| 815 | ;; current string... | ||
| 816 | (> end-of-prev-line-pos string-begin-pos) | ||
| 817 | |||
| 818 | ;; ...and end with a backslash | ||
| 819 | (= ?\\ (char-before end-of-prev-line-pos))) | ||
| 820 | |||
| 821 | ;; Indent to the same level as the previous line, or the | ||
| 822 | ;; start of the string if the previous line starts the string | ||
| 823 | (if (rust--same-line-p end-of-prev-line-pos string-begin-pos) | ||
| 824 | ;; The previous line is the start of the string. | ||
| 825 | ;; If the backslash is the only character after the | ||
| 826 | ;; string beginning, indent to the next indent | ||
| 827 | ;; level. Otherwise align with the start of the string. | ||
| 828 | (if (> (- end-of-prev-line-pos string-begin-pos) 2) | ||
| 829 | (save-excursion | ||
| 830 | (goto-char (+ 1 string-begin-pos)) | ||
| 831 | (current-column)) | ||
| 832 | baseline) | ||
| 833 | |||
| 834 | ;; The previous line is not the start of the string, so | ||
| 835 | ;; match its indentation. | ||
| 836 | (save-excursion | ||
| 837 | (goto-char end-of-prev-line-pos) | ||
| 838 | (back-to-indentation) | ||
| 839 | (current-column)))))) | ||
| 840 | |||
| 841 | ;; A function return type is indented to the corresponding | ||
| 842 | ;; function arguments, if -to-arguments is selected. | ||
| 843 | ((and rust-indent-return-type-to-arguments | ||
| 844 | (looking-at "->")) | ||
| 845 | (save-excursion | ||
| 846 | (backward-list) | ||
| 847 | (or (rust-align-to-expr-after-brace) | ||
| 848 | (+ baseline rust-indent-offset)))) | ||
| 849 | |||
| 850 | ;; A closing brace is 1 level unindented | ||
| 851 | ((looking-at "[]})]") (- baseline rust-indent-offset)) | ||
| 852 | |||
| 853 | ;; Doc comments in /** style with leading * indent to line up the *s | ||
| 854 | ((and (nth 4 (syntax-ppss)) (looking-at "*")) | ||
| 855 | (+ 1 baseline)) | ||
| 856 | |||
| 857 | ;; When the user chose not to indent the start of the where | ||
| 858 | ;; clause, put it on the baseline. | ||
| 859 | ((and (not rust-indent-where-clause) | ||
| 860 | (rust-looking-at-where)) | ||
| 861 | baseline) | ||
| 862 | |||
| 863 | ;; If we're in any other token-tree / sexp, then: | ||
| 864 | (t | ||
| 865 | (or | ||
| 866 | ;; If we are inside a pair of braces, with something after the | ||
| 867 | ;; open brace on the same line and ending with a comma, treat | ||
| 868 | ;; it as fields and align them. | ||
| 869 | (when (> level 0) | ||
| 870 | (save-excursion | ||
| 871 | (rust-rewind-irrelevant) | ||
| 872 | (backward-up-list) | ||
| 873 | ;; Point is now at the beginning of the containing set of braces | ||
| 874 | (rust-align-to-expr-after-brace))) | ||
| 875 | |||
| 876 | ;; When where-clauses are spread over multiple lines, clauses | ||
| 877 | ;; should be aligned on the type parameters. In this case we | ||
| 878 | ;; take care of the second and following clauses (the ones | ||
| 879 | ;; that don't start with "where ") | ||
| 880 | (save-excursion | ||
| 881 | ;; Find the start of the function, we'll use this to limit | ||
| 882 | ;; our search for "where ". | ||
| 883 | (let ((function-start nil) (function-level nil)) | ||
| 884 | (save-excursion | ||
| 885 | ;; If we're already at the start of a function, | ||
| 886 | ;; don't go back any farther. We can easily do | ||
| 887 | ;; this by moving to the end of the line first. | ||
| 888 | (end-of-line) | ||
| 889 | (rust-beginning-of-defun) | ||
| 890 | (back-to-indentation) | ||
| 891 | ;; Avoid using multiple-value-bind | ||
| 892 | (setq function-start (point) | ||
| 893 | function-level (rust-paren-level))) | ||
| 894 | ;; When we're not on a line starting with "where ", but | ||
| 895 | ;; still on a where-clause line, go to "where " | ||
| 896 | (when (and | ||
| 897 | (not (rust-looking-at-where)) | ||
| 898 | ;; We're looking at something like "F: ..." | ||
| 899 | (looking-at (concat rust-re-ident ":")) | ||
| 900 | ;; There is a "where " somewhere after the | ||
| 901 | ;; start of the function. | ||
| 902 | (rust-rewind-to-where function-start) | ||
| 903 | ;; Make sure we're not inside the function | ||
| 904 | ;; already (e.g. initializing a struct) by | ||
| 905 | ;; checking we are the same level. | ||
| 906 | (= function-level level)) | ||
| 907 | ;; skip over "where" | ||
| 908 | (forward-char 5) | ||
| 909 | ;; Unless "where" is at the end of the line | ||
| 910 | (if (eolp) | ||
| 911 | ;; in this case the type parameters bounds are just | ||
| 912 | ;; indented once | ||
| 913 | (+ baseline rust-indent-offset) | ||
| 914 | ;; otherwise, skip over whitespace, | ||
| 915 | (skip-chars-forward "[:space:]") | ||
| 916 | ;; get the column of the type parameter and use that | ||
| 917 | ;; as indentation offset | ||
| 918 | (current-column))))) | ||
| 919 | |||
| 920 | (progn | ||
| 921 | (back-to-indentation) | ||
| 922 | ;; Point is now at the beginning of the current line | ||
| 923 | (if (or | ||
| 924 | ;; If this line begins with "else" or "{", stay on the | ||
| 925 | ;; baseline as well (we are continuing an expression, | ||
| 926 | ;; but the "else" or "{" should align with the beginning | ||
| 927 | ;; of the expression it's in.) | ||
| 928 | ;; Or, if this line starts a comment, stay on the | ||
| 929 | ;; baseline as well. | ||
| 930 | (looking-at "\\<else\\>\\|{\\|/[/*]") | ||
| 931 | |||
| 932 | ;; If this is the start of a top-level item, | ||
| 933 | ;; stay on the baseline. | ||
| 934 | (looking-at rust-top-item-beg-re) | ||
| 935 | |||
| 936 | (save-excursion | ||
| 937 | (rust-rewind-irrelevant) | ||
| 938 | ;; Point is now at the end of the previous line | ||
| 939 | (or | ||
| 940 | ;; If we are at the start of the buffer, no | ||
| 941 | ;; indentation is needed, so stay at baseline... | ||
| 942 | (= (point) 1) | ||
| 943 | ;; ..or if the previous line ends with any of these: | ||
| 944 | ;; { ? : ( , ; [ } | ||
| 945 | ;; then we are at the beginning of an | ||
| 946 | ;; expression, so stay on the baseline... | ||
| 947 | (looking-back "[(,:;[{}]\\|[^|]|" (- (point) 2)) | ||
| 948 | ;; or if the previous line is the end of an | ||
| 949 | ;; attribute, stay at the baseline... | ||
| 950 | (progn (rust-rewind-to-beginning-of-current-level-expr) | ||
| 951 | (looking-at "#"))))) | ||
| 952 | baseline | ||
| 953 | |||
| 954 | ;; Otherwise, we are continuing the same expression from | ||
| 955 | ;; the previous line, so add one additional indent level | ||
| 956 | (+ baseline rust-indent-offset)))))))))) | ||
| 957 | |||
| 958 | (when indent | ||
| 959 | ;; If we're at the beginning of the line (before or at the current | ||
| 960 | ;; indentation), jump with the indentation change. Otherwise, save the | ||
| 961 | ;; excursion so that adding the indentations will leave us at the | ||
| 962 | ;; equivalent position within the line to where we were before. | ||
| 963 | (if (<= (current-column) (current-indentation)) | ||
| 964 | (indent-line-to indent) | ||
| 965 | (save-excursion (indent-line-to indent)))))) | ||
| 966 | |||
| 967 | (defun rust--same-line-p (pos1 pos2) | ||
| 968 | "Return non-nil if POS1 and POS2 are on the same line." | ||
| 969 | (save-excursion (= (progn (goto-char pos1) (line-end-position)) | ||
| 970 | (progn (goto-char pos2) (line-end-position))))) | ||
| 971 | |||
| 972 | ;;; Font-locking definitions and helpers | ||
| 973 | |||
| 974 | (defun rust-next-string-interpolation (limit) | ||
| 975 | "Search forward from point for next Rust interpolation marker before LIMIT. | ||
| 976 | Set point to the end of the occurrence found, and return match beginning | ||
| 977 | and end." | ||
| 978 | (catch 'match | ||
| 979 | (save-match-data | ||
| 980 | (save-excursion | ||
| 981 | (while (search-forward "{" limit t) | ||
| 982 | (if (eql (char-after (point)) ?{) | ||
| 983 | (forward-char) | ||
| 984 | (let ((start (match-beginning 0))) | ||
| 985 | ;; According to fmt_macros::Parser::next, an opening brace | ||
| 986 | ;; must be followed by an optional argument and/or format | ||
| 987 | ;; specifier, then a closing brace. A single closing brace | ||
| 988 | ;; without a corresponding unescaped opening brace is an | ||
| 989 | ;; error. We don't need to do anything special with | ||
| 990 | ;; arguments, specifiers, or errors, so we only search for | ||
| 991 | ;; the single closing brace. | ||
| 992 | (when (search-forward "}" limit t) | ||
| 993 | (throw 'match (list start (point))))))))))) | ||
| 994 | |||
| 995 | (defun rust-string-interpolation-matcher (limit) | ||
| 996 | "Match next Rust interpolation marker before LIMIT and set match data if found. | ||
| 997 | Returns nil if not within a Rust string." | ||
| 998 | (when (rust-in-str) | ||
| 999 | (let ((match (rust-next-string-interpolation limit))) | ||
| 1000 | (when match | ||
| 1001 | (set-match-data match) | ||
| 1002 | (goto-char (cadr match)) | ||
| 1003 | match)))) | ||
| 1004 | |||
| 1005 | (defun rust-syntax-class-before-point () | ||
| 1006 | (when (> (point) 1) | ||
| 1007 | (syntax-class (syntax-after (1- (point)))))) | ||
| 1008 | |||
| 1009 | (defun rust-rewind-qualified-ident () | ||
| 1010 | (while (rust-looking-back-ident) | ||
| 1011 | (backward-sexp) | ||
| 1012 | (when (save-excursion (rust-rewind-irrelevant) (rust-looking-back-str "::")) | ||
| 1013 | (rust-rewind-irrelevant) | ||
| 1014 | (backward-char 2) | ||
| 1015 | (rust-rewind-irrelevant)))) | ||
| 1016 | |||
| 1017 | (defun rust-rewind-type-param-list () | ||
| 1018 | (cond | ||
| 1019 | ((and (rust-looking-back-str ">") (equal 5 (rust-syntax-class-before-point))) | ||
| 1020 | (backward-sexp) | ||
| 1021 | (rust-rewind-irrelevant)) | ||
| 1022 | |||
| 1023 | ;; We need to be able to back up past the Fn(args) -> RT form as well. If | ||
| 1024 | ;; we're looking back at this, we want to end up just after "Fn". | ||
| 1025 | ((member (char-before) '(?\] ?\) )) | ||
| 1026 | (let* ((is-paren (rust-looking-back-str ")")) | ||
| 1027 | (dest (save-excursion | ||
| 1028 | (backward-sexp) | ||
| 1029 | (rust-rewind-irrelevant) | ||
| 1030 | (or | ||
| 1031 | (when (rust-looking-back-str "->") | ||
| 1032 | (backward-char 2) | ||
| 1033 | (rust-rewind-irrelevant) | ||
| 1034 | (when (rust-looking-back-str ")") | ||
| 1035 | (backward-sexp) | ||
| 1036 | (point))) | ||
| 1037 | (and is-paren (point)))))) | ||
| 1038 | (when dest | ||
| 1039 | (goto-char dest)))))) | ||
| 1040 | |||
| 1041 | (defun rust-rewind-to-decl-name () | ||
| 1042 | "Return the point at the beginning of the name in a declaration. | ||
| 1043 | I.e. if we are before an ident that is part of a declaration that | ||
| 1044 | can have a where clause, rewind back to just before the name of | ||
| 1045 | the subject of that where clause and return the new point. | ||
| 1046 | Otherwise return nil." | ||
| 1047 | (let* ((ident-pos (point)) | ||
| 1048 | (newpos (save-excursion | ||
| 1049 | (rust-rewind-irrelevant) | ||
| 1050 | (rust-rewind-type-param-list) | ||
| 1051 | (cond | ||
| 1052 | ((rust-looking-back-symbols | ||
| 1053 | '("fn" "trait" "enum" "struct" "union" "impl" "type")) | ||
| 1054 | ident-pos) | ||
| 1055 | |||
| 1056 | ((equal 5 (rust-syntax-class-before-point)) | ||
| 1057 | (backward-sexp) | ||
| 1058 | (rust-rewind-to-decl-name)) | ||
| 1059 | |||
| 1060 | ((looking-back "[:,'+=]" (1- (point))) | ||
| 1061 | (backward-char) | ||
| 1062 | (rust-rewind-to-decl-name)) | ||
| 1063 | |||
| 1064 | ((rust-looking-back-str "->") | ||
| 1065 | (backward-char 2) | ||
| 1066 | (rust-rewind-to-decl-name)) | ||
| 1067 | |||
| 1068 | ((rust-looking-back-ident) | ||
| 1069 | (rust-rewind-qualified-ident) | ||
| 1070 | (rust-rewind-to-decl-name)))))) | ||
| 1071 | (when newpos (goto-char newpos)) | ||
| 1072 | newpos)) | ||
| 1073 | |||
| 1074 | (defun rust-is-in-expression-context (token) | ||
| 1075 | "Return t if what comes right after the point is part of an | ||
| 1076 | expression (as opposed to starting a type) by looking at what | ||
| 1077 | comes before. Takes a symbol that roughly indicates what is | ||
| 1078 | after the point. | ||
| 1079 | |||
| 1080 | This function is used as part of `rust-is-lt-char-operator' as | ||
| 1081 | part of angle bracket matching, and is not intended to be used | ||
| 1082 | outside of this context." | ||
| 1083 | (save-excursion | ||
| 1084 | (let ((postchar (char-after))) | ||
| 1085 | (rust-rewind-irrelevant) | ||
| 1086 | ;; A type alias or ascription could have a type param list. Skip backwards past it. | ||
| 1087 | (when (member token '(ambiguous-operator open-brace)) | ||
| 1088 | (rust-rewind-type-param-list)) | ||
| 1089 | (cond | ||
| 1090 | |||
| 1091 | ;; Certain keywords always introduce expressions | ||
| 1092 | ((rust-looking-back-symbols '("if" "while" "match" "return" "box" "in")) t) | ||
| 1093 | |||
| 1094 | ;; "as" introduces a type | ||
| 1095 | ((rust-looking-back-symbols '("as")) nil) | ||
| 1096 | |||
| 1097 | ;; An open angle bracket never introduces expression context WITHIN the angle brackets | ||
| 1098 | ((and (equal token 'open-brace) (equal postchar ?<)) nil) | ||
| 1099 | |||
| 1100 | ;; An ident! followed by an open brace is a macro invocation. Consider | ||
| 1101 | ;; it to be an expression. | ||
| 1102 | ((and (equal token 'open-brace) (rust-looking-back-macro)) t) | ||
| 1103 | |||
| 1104 | ;; In a brace context a "]" introduces an expression. | ||
| 1105 | ((and (eq token 'open-brace) (rust-looking-back-str "]"))) | ||
| 1106 | |||
| 1107 | ;; An identifier is right after an ending paren, bracket, angle bracket | ||
| 1108 | ;; or curly brace. It's a type if the last sexp was a type. | ||
| 1109 | ((and (equal token 'ident) (equal 5 (rust-syntax-class-before-point))) | ||
| 1110 | (backward-sexp) | ||
| 1111 | (rust-is-in-expression-context 'open-brace)) | ||
| 1112 | |||
| 1113 | ;; If a "for" appears without a ; or { before it, it's part of an | ||
| 1114 | ;; "impl X for y", so the y is a type. Otherwise it's | ||
| 1115 | ;; introducing a loop, so the y is an expression | ||
| 1116 | ((and (equal token 'ident) (rust-looking-back-symbols '("for"))) | ||
| 1117 | (backward-sexp) | ||
| 1118 | (rust-rewind-irrelevant) | ||
| 1119 | (looking-back "[{;]" (1- (point)))) | ||
| 1120 | |||
| 1121 | ((rust-looking-back-ident) | ||
| 1122 | (rust-rewind-qualified-ident) | ||
| 1123 | (rust-rewind-irrelevant) | ||
| 1124 | (cond | ||
| 1125 | ((equal token 'open-brace) | ||
| 1126 | ;; We now know we have: | ||
| 1127 | ;; ident <maybe type params> [{([] | ||
| 1128 | ;; where [{([] denotes either a {, ( or [. | ||
| 1129 | ;; This character is bound as postchar. | ||
| 1130 | (cond | ||
| 1131 | ;; If postchar is a paren or square bracket, then if the | ||
| 1132 | ;; brace is a type if the identifier is one | ||
| 1133 | ((member postchar '(?\( ?\[ )) (rust-is-in-expression-context 'ident)) | ||
| 1134 | |||
| 1135 | ;; If postchar is a curly brace, the brace can only be a type if | ||
| 1136 | ;; ident2 is the name of an enum, struct or trait being declared. | ||
| 1137 | ;; Note that if there is a -> before the ident then the ident would | ||
| 1138 | ;; be a type but the { is not. | ||
| 1139 | ((equal ?{ postchar) | ||
| 1140 | (not (and (rust-rewind-to-decl-name) | ||
| 1141 | (progn | ||
| 1142 | (rust-rewind-irrelevant) | ||
| 1143 | (rust-looking-back-symbols | ||
| 1144 | '("enum" "struct" "union" "trait" "type")))))))) | ||
| 1145 | |||
| 1146 | ((equal token 'ambiguous-operator) | ||
| 1147 | (cond | ||
| 1148 | ;; An ampersand after an ident has to be an operator rather | ||
| 1149 | ;; than a & at the beginning of a ref type | ||
| 1150 | ((equal postchar ?&) t) | ||
| 1151 | |||
| 1152 | ;; A : followed by a type then an = introduces an | ||
| 1153 | ;; expression (unless it is part of a where clause of a | ||
| 1154 | ;; "type" declaration) | ||
| 1155 | ((and (equal postchar ?=) | ||
| 1156 | (looking-back "[^:]:" (- (point) 2)) | ||
| 1157 | (not (save-excursion | ||
| 1158 | (and (rust-rewind-to-decl-name) | ||
| 1159 | (progn (rust-rewind-irrelevant) | ||
| 1160 | (rust-looking-back-symbols '("type")))))))) | ||
| 1161 | |||
| 1162 | ;; "let ident =" introduces an expression--and so does "const" and "mut" | ||
| 1163 | ((and (equal postchar ?=) (rust-looking-back-symbols '("let" "const" "mut"))) t) | ||
| 1164 | |||
| 1165 | ;; As a specific special case, see if this is the = in this situation: | ||
| 1166 | ;; enum EnumName<type params> { Ident = | ||
| 1167 | ;; In this case, this is a c-like enum and despite Ident | ||
| 1168 | ;; representing a type, what comes after the = is an expression | ||
| 1169 | ((and | ||
| 1170 | (> (rust-paren-level) 0) | ||
| 1171 | (save-excursion | ||
| 1172 | (backward-up-list) | ||
| 1173 | (rust-rewind-irrelevant) | ||
| 1174 | (rust-rewind-type-param-list) | ||
| 1175 | (and | ||
| 1176 | (rust-looking-back-ident) | ||
| 1177 | (progn | ||
| 1178 | (rust-rewind-qualified-ident) | ||
| 1179 | (rust-rewind-irrelevant) | ||
| 1180 | (rust-looking-back-str "enum"))))) | ||
| 1181 | t) | ||
| 1182 | |||
| 1183 | ;; Otherwise the ambiguous operator is a type if the identifier is a type | ||
| 1184 | ((rust-is-in-expression-context 'ident) t))) | ||
| 1185 | |||
| 1186 | ((equal token 'colon) | ||
| 1187 | (cond | ||
| 1188 | ;; If we see a ident: not inside any braces/parens, we're at top level. | ||
| 1189 | ;; There are no allowed expressions after colons there, just types. | ||
| 1190 | ((<= (rust-paren-level) 0) nil) | ||
| 1191 | |||
| 1192 | ;; We see ident: inside a list | ||
| 1193 | ((looking-back "[{,]" (1- (point))) | ||
| 1194 | (backward-up-list) | ||
| 1195 | |||
| 1196 | ;; If a : appears whose surrounding paren/brackets/braces are | ||
| 1197 | ;; anything other than curly braces, it can't be a field | ||
| 1198 | ;; initializer and must be denoting a type. | ||
| 1199 | (when (looking-at "{") | ||
| 1200 | (rust-rewind-irrelevant) | ||
| 1201 | (rust-rewind-type-param-list) | ||
| 1202 | (when (rust-looking-back-ident) | ||
| 1203 | ;; We have a context that looks like this: | ||
| 1204 | ;; ident2 <maybe type params> { [maybe paren-balanced code ending in comma] ident1: | ||
| 1205 | ;; the point is sitting just after ident2, and we trying to | ||
| 1206 | ;; figure out if the colon introduces an expression or a type. | ||
| 1207 | ;; The answer is that ident1 is a field name, and what comes | ||
| 1208 | ;; after the colon is an expression, if ident2 is an | ||
| 1209 | ;; expression. | ||
| 1210 | (rust-rewind-qualified-ident) | ||
| 1211 | (rust-is-in-expression-context 'ident)))) | ||
| 1212 | |||
| 1213 | ;; Otherwise, if the ident: appeared with anything other than , or { | ||
| 1214 | ;; before it, it can't be part of a struct initializer and therefore | ||
| 1215 | ;; must be denoting a type. | ||
| 1216 | (t nil))))) | ||
| 1217 | |||
| 1218 | ;; An operator-like character after a string is indeed an operator | ||
| 1219 | ((and (equal token 'ambiguous-operator) | ||
| 1220 | (member (rust-syntax-class-before-point) '(5 7 15))) t) | ||
| 1221 | |||
| 1222 | ;; A colon that has something other than an identifier before it is a | ||
| 1223 | ;; type ascription | ||
| 1224 | ((equal token 'colon) nil) | ||
| 1225 | |||
| 1226 | ;; A :: introduces a type (or module, but not an expression in any case) | ||
| 1227 | ((rust-looking-back-str "::") nil) | ||
| 1228 | |||
| 1229 | ((rust-looking-back-str ":") | ||
| 1230 | (backward-char) | ||
| 1231 | (rust-is-in-expression-context 'colon)) | ||
| 1232 | |||
| 1233 | ;; A -> introduces a type | ||
| 1234 | ((rust-looking-back-str "->") nil) | ||
| 1235 | |||
| 1236 | ;; If we are up against the beginning of a list, or after a comma inside | ||
| 1237 | ;; of one, back up out of it and check what the list itself is | ||
| 1238 | ((or | ||
| 1239 | (equal 4 (rust-syntax-class-before-point)) | ||
| 1240 | (rust-looking-back-str ",")) | ||
| 1241 | (condition-case nil | ||
| 1242 | (progn | ||
| 1243 | (backward-up-list) | ||
| 1244 | (rust-is-in-expression-context 'open-brace)) | ||
| 1245 | (scan-error nil))) | ||
| 1246 | |||
| 1247 | ;; A => introduces an expression | ||
| 1248 | ((rust-looking-back-str "=>") t) | ||
| 1249 | |||
| 1250 | ;; A == introduces an expression | ||
| 1251 | ((rust-looking-back-str "==") t) | ||
| 1252 | |||
| 1253 | ;; These operators can introduce expressions or types | ||
| 1254 | ((looking-back "[-+=!?&*]" (1- (point))) | ||
| 1255 | (backward-char) | ||
| 1256 | (rust-is-in-expression-context 'ambiguous-operator)) | ||
| 1257 | |||
| 1258 | ;; These operators always introduce expressions. (Note that if this | ||
| 1259 | ;; regexp finds a < it must not be an angle bracket, or it'd | ||
| 1260 | ;; have been caught in the syntax-class check above instead of this.) | ||
| 1261 | ((looking-back rust-re-pre-expression-operators (1- (point))) t))))) | ||
| 1262 | |||
| 1263 | (defun rust-is-lt-char-operator () | ||
| 1264 | "Return non-nil if the `<' sign just after point is an operator. | ||
| 1265 | Otherwise, if it is an opening angle bracket, then return nil." | ||
| 1266 | (let ((case-fold-search nil)) | ||
| 1267 | (save-excursion | ||
| 1268 | (rust-rewind-irrelevant) | ||
| 1269 | ;; We are now just after the character syntactically before the <. | ||
| 1270 | (cond | ||
| 1271 | |||
| 1272 | ;; If we are looking back at a < that is not an angle bracket (but not | ||
| 1273 | ;; two of them) then this is the second < in a bit shift operator | ||
| 1274 | ((and (rust-looking-back-str "<") | ||
| 1275 | (not (equal 4 (rust-syntax-class-before-point))) | ||
| 1276 | (not (rust-looking-back-str "<<")))) | ||
| 1277 | |||
| 1278 | ;; On the other hand, if we are after a closing paren/brace/bracket it | ||
| 1279 | ;; can only be an operator, not an angle bracket. Likewise, if we are | ||
| 1280 | ;; after a string it's an operator. (The string case could actually be | ||
| 1281 | ;; valid in rust for character literals.) | ||
| 1282 | ((member (rust-syntax-class-before-point) '(5 7 15)) t) | ||
| 1283 | |||
| 1284 | ;; If we are looking back at an operator, we know that we are at | ||
| 1285 | ;; the beginning of an expression, and thus it has to be an angle | ||
| 1286 | ;; bracket (starting a "<Type as Trait>::" construct.) | ||
| 1287 | ((looking-back rust-re-pre-expression-operators (1- (point))) nil) | ||
| 1288 | |||
| 1289 | ;; If we are looking back at a keyword, it's an angle bracket | ||
| 1290 | ;; unless that keyword is "self", "true" or "false" | ||
| 1291 | ((rust-looking-back-symbols rust-keywords) | ||
| 1292 | (rust-looking-back-symbols '("self" "true" "false"))) | ||
| 1293 | |||
| 1294 | ((rust-looking-back-str "?") | ||
| 1295 | (rust-is-in-expression-context 'ambiguous-operator)) | ||
| 1296 | |||
| 1297 | ;; If we're looking back at an identifier, this depends on whether | ||
| 1298 | ;; the identifier is part of an expression or a type | ||
| 1299 | ((rust-looking-back-ident) | ||
| 1300 | (backward-sexp) | ||
| 1301 | (or | ||
| 1302 | ;; The special types can't take type param lists, so a < after one is | ||
| 1303 | ;; always an operator | ||
| 1304 | (looking-at rust-re-special-types) | ||
| 1305 | |||
| 1306 | (rust-is-in-expression-context 'ident))) | ||
| 1307 | |||
| 1308 | ;; Otherwise, assume it's an angle bracket | ||
| 1309 | )))) | ||
| 1310 | |||
| 1311 | (defun rust-electric-pair-inhibit-predicate-wrap (char) | ||
| 1312 | "Prevent \"matching\" with a `>' when CHAR is the less-than operator. | ||
| 1313 | This wraps the default defined by `electric-pair-inhibit-predicate'." | ||
| 1314 | (or | ||
| 1315 | (when (= ?< char) | ||
| 1316 | (save-excursion | ||
| 1317 | (backward-char) | ||
| 1318 | (rust-is-lt-char-operator))) | ||
| 1319 | (funcall (default-value 'electric-pair-inhibit-predicate) char))) | ||
| 1320 | |||
| 1321 | (defun rust-electric-pair-skip-self (char) | ||
| 1322 | "Skip CHAR instead of inserting a second closing character. | ||
| 1323 | This is added to the default skips defined by `electric-pair-skip-self'." | ||
| 1324 | (= ?> char)) | ||
| 1325 | |||
| 1326 | (defun rust-ordinary-lt-gt-p () | ||
| 1327 | "Test whether the `<' or `>' at point is an ordinary operator of some kind. | ||
| 1328 | |||
| 1329 | This returns t if the `<' or `>' is an ordinary operator (like | ||
| 1330 | less-than) or part of one (like `->'); and nil if the character | ||
| 1331 | should be considered a paired angle bracket." | ||
| 1332 | (cond | ||
| 1333 | ;; If matching is turned off suppress all of them | ||
| 1334 | ((not rust-match-angle-brackets) t) | ||
| 1335 | |||
| 1336 | ;; This is a cheap check so we do it early. | ||
| 1337 | ;; Don't treat the > in -> or => as an angle bracket | ||
| 1338 | ((and (= (following-char) ?>) (memq (preceding-char) '(?- ?=))) t) | ||
| 1339 | |||
| 1340 | ;; We don't take < or > in strings or comments to be angle brackets | ||
| 1341 | ((rust-in-str-or-cmnt) t) | ||
| 1342 | |||
| 1343 | ;; Inside a macro we don't really know the syntax. Any < or > may be an | ||
| 1344 | ;; angle bracket or it may not. But we know that the other braces have | ||
| 1345 | ;; to balance regardless of the < and >, so if we don't treat any < or > | ||
| 1346 | ;; as angle brackets it won't mess up any paren balancing. | ||
| 1347 | ((rust-in-macro) t) | ||
| 1348 | |||
| 1349 | ((= (following-char) ?<) | ||
| 1350 | (rust-is-lt-char-operator)) | ||
| 1351 | |||
| 1352 | ;; Since rust-ordinary-lt-gt-p is called only when either < or > are at the point, | ||
| 1353 | ;; we know that the following char must be > in the clauses below. | ||
| 1354 | |||
| 1355 | ;; If we are at top level and not in any list, it can't be a closing | ||
| 1356 | ;; angle bracket | ||
| 1357 | ((>= 0 (rust-paren-level)) t) | ||
| 1358 | |||
| 1359 | ;; Otherwise, treat the > as a closing angle bracket if it would | ||
| 1360 | ;; match an opening one | ||
| 1361 | ((save-excursion | ||
| 1362 | (backward-up-list) | ||
| 1363 | (/= (following-char) ?<))))) | ||
| 1364 | |||
| 1365 | (defun rust-mode-syntactic-face-function (state) | ||
| 1366 | "Return face that distinguishes doc and normal comments in given syntax STATE." | ||
| 1367 | (if (nth 3 state) | ||
| 1368 | 'font-lock-string-face | ||
| 1369 | (save-excursion | ||
| 1370 | (goto-char (nth 8 state)) | ||
| 1371 | (if (looking-at "/\\([*][*!][^*!]\\|/[/!][^/!]\\)") | ||
| 1372 | 'font-lock-doc-face | ||
| 1373 | 'font-lock-comment-face)))) | ||
| 1374 | |||
| 1375 | (eval-and-compile | ||
| 1376 | (defconst rust--char-literal-rx | ||
| 1377 | (rx (seq | ||
| 1378 | (group "'") | ||
| 1379 | (or | ||
| 1380 | (seq | ||
| 1381 | "\\" | ||
| 1382 | (or | ||
| 1383 | (: "u{" (** 1 6 xdigit) "}") | ||
| 1384 | (: "x" (= 2 xdigit)) | ||
| 1385 | (any "'nrt0\"\\"))) | ||
| 1386 | (not (any "'\\"))) | ||
| 1387 | (group "'"))) | ||
| 1388 | "A regular expression matching a character literal.")) | ||
| 1389 | |||
| 1390 | (defun rust--syntax-propertize-raw-string (str-start end) | ||
| 1391 | "A helper for rust-syntax-propertize. | ||
| 1392 | |||
| 1393 | This will apply the appropriate string syntax to the character | ||
| 1394 | from the STR-START up to the end of the raw string, or to END, | ||
| 1395 | whichever comes first." | ||
| 1396 | (when (save-excursion | ||
| 1397 | (goto-char str-start) | ||
| 1398 | (looking-at "r\\(#*\\)\\(\"\\)")) | ||
| 1399 | ;; In a raw string, so try to find the end. | ||
| 1400 | (let ((hashes (match-string 1))) | ||
| 1401 | ;; Match \ characters at the end of the string to suppress | ||
| 1402 | ;; their normal character-quote syntax. | ||
| 1403 | (when (re-search-forward (concat "\\(\\\\*\\)\\(\"" hashes "\\)") end t) | ||
| 1404 | (put-text-property (match-beginning 1) (match-end 1) | ||
| 1405 | 'syntax-table (string-to-syntax "_")) | ||
| 1406 | (put-text-property (1- (match-end 2)) (match-end 2) | ||
| 1407 | 'syntax-table (string-to-syntax "|")) | ||
| 1408 | (goto-char (match-end 0)))))) | ||
| 1409 | |||
| 1410 | ;;; Syntax Propertize | ||
| 1411 | |||
| 1412 | (defun rust-syntax-propertize (start end) | ||
| 1413 | "A `syntax-propertize-function' to apply properties from START to END." | ||
| 1414 | ;; Cache all macro scopes as an optimization. See issue #208 | ||
| 1415 | (let ((rust-macro-scopes (rust-macro-scope start end))) | ||
| 1416 | (goto-char start) | ||
| 1417 | (let ((str-start (rust-in-str-or-cmnt))) | ||
| 1418 | (when str-start | ||
| 1419 | (rust--syntax-propertize-raw-string str-start end))) | ||
| 1420 | (funcall | ||
| 1421 | (syntax-propertize-rules | ||
| 1422 | ;; Character literals. | ||
| 1423 | (rust--char-literal-rx (1 "\"") (2 "\"")) | ||
| 1424 | ;; Raw strings. | ||
| 1425 | ("\\(r\\)#*\"" | ||
| 1426 | (0 (ignore | ||
| 1427 | (goto-char (match-end 0)) | ||
| 1428 | (unless (save-excursion (nth 8 (syntax-ppss (match-beginning 0)))) | ||
| 1429 | (put-text-property (match-beginning 1) (match-end 1) | ||
| 1430 | 'syntax-table (string-to-syntax "|")) | ||
| 1431 | (rust--syntax-propertize-raw-string (match-beginning 0) end))))) | ||
| 1432 | ("[<>]" | ||
| 1433 | (0 (ignore | ||
| 1434 | (when (save-match-data | ||
| 1435 | (save-excursion | ||
| 1436 | (goto-char (match-beginning 0)) | ||
| 1437 | (rust-ordinary-lt-gt-p))) | ||
| 1438 | (put-text-property (match-beginning 0) (match-end 0) | ||
| 1439 | 'syntax-table (string-to-syntax ".")) | ||
| 1440 | (goto-char (match-end 0))))))) | ||
| 1441 | (point) end))) | ||
| 1442 | |||
| 1443 | (defun rust-fill-prefix-for-comment-start (line-start) | ||
| 1444 | "Determine what to use for `fill-prefix' based on the text at LINE-START." | ||
| 1445 | (let ((result | ||
| 1446 | ;; Replace /* with same number of spaces | ||
| 1447 | (replace-regexp-in-string | ||
| 1448 | "\\(?:/\\*+?\\)[!*]?" | ||
| 1449 | (lambda (s) | ||
| 1450 | ;; We want the * to line up with the first * of the | ||
| 1451 | ;; comment start | ||
| 1452 | (let ((offset (if (eq t | ||
| 1453 | (compare-strings "/*" nil nil | ||
| 1454 | s | ||
| 1455 | (- (length s) 2) | ||
| 1456 | (length s))) | ||
| 1457 | 1 2))) | ||
| 1458 | (concat (make-string (- (length s) offset) | ||
| 1459 | ?\x20) "*"))) | ||
| 1460 | line-start))) | ||
| 1461 | ;; Make sure we've got at least one space at the end | ||
| 1462 | (if (not (= (aref result (- (length result) 1)) ?\x20)) | ||
| 1463 | (setq result (concat result " "))) | ||
| 1464 | result)) | ||
| 1465 | |||
| 1466 | (defun rust-in-comment-paragraph (body) | ||
| 1467 | ;; We might move the point to fill the next comment, but we don't want it | ||
| 1468 | ;; seeming to jump around on the user | ||
| 1469 | (save-excursion | ||
| 1470 | ;; If we're outside of a comment, with only whitespace and then a comment | ||
| 1471 | ;; in front, jump to the comment and prepare to fill it. | ||
| 1472 | (when (not (nth 4 (syntax-ppss))) | ||
| 1473 | (beginning-of-line) | ||
| 1474 | (when (looking-at (concat "[[:space:]\n]*" comment-start-skip)) | ||
| 1475 | (goto-char (match-end 0)))) | ||
| 1476 | |||
| 1477 | ;; We need this when we're moving the point around and then checking syntax | ||
| 1478 | ;; while doing paragraph fills, because the cache it uses isn't always | ||
| 1479 | ;; invalidated during this. | ||
| 1480 | (syntax-ppss-flush-cache 1) | ||
| 1481 | ;; If we're at the beginning of a comment paragraph with nothing but | ||
| 1482 | ;; whitespace til the next line, jump to the next line so that we use the | ||
| 1483 | ;; existing prefix to figure out what the new prefix should be, rather than | ||
| 1484 | ;; inferring it from the comment start. | ||
| 1485 | (let ((next-bol (line-beginning-position 2))) | ||
| 1486 | (while (save-excursion | ||
| 1487 | (end-of-line) | ||
| 1488 | (syntax-ppss-flush-cache 1) | ||
| 1489 | (and (nth 4 (syntax-ppss)) | ||
| 1490 | (save-excursion | ||
| 1491 | (beginning-of-line) | ||
| 1492 | (looking-at paragraph-start)) | ||
| 1493 | (looking-at "[[:space:]]*$") | ||
| 1494 | (nth 4 (syntax-ppss next-bol)))) | ||
| 1495 | (goto-char next-bol))) | ||
| 1496 | |||
| 1497 | (syntax-ppss-flush-cache 1) | ||
| 1498 | ;; If we're on the last line of a multiline-style comment that started | ||
| 1499 | ;; above, back up one line so we don't mistake the * of the */ that ends | ||
| 1500 | ;; the comment for a prefix. | ||
| 1501 | (when (save-excursion | ||
| 1502 | (and (nth 4 (syntax-ppss (line-beginning-position 1))) | ||
| 1503 | (looking-at "[[:space:]]*\\*/"))) | ||
| 1504 | (goto-char (line-end-position 0))) | ||
| 1505 | (funcall body))) | ||
| 1506 | |||
| 1507 | (defun rust-with-comment-fill-prefix (body) | ||
| 1508 | (let* | ||
| 1509 | ((line-string (buffer-substring-no-properties | ||
| 1510 | (line-beginning-position) (line-end-position))) | ||
| 1511 | (line-comment-start | ||
| 1512 | (when (nth 4 (syntax-ppss)) | ||
| 1513 | (cond | ||
| 1514 | ;; If we're inside the comment and see a * prefix, use it | ||
| 1515 | ((string-match "^\\([[:space:]]*\\*+[[:space:]]*\\)" | ||
| 1516 | line-string) | ||
| 1517 | (match-string 1 line-string)) | ||
| 1518 | ;; If we're at the start of a comment, figure out what prefix | ||
| 1519 | ;; to use for the subsequent lines after it | ||
| 1520 | ((string-match (concat "[[:space:]]*" comment-start-skip) line-string) | ||
| 1521 | (rust-fill-prefix-for-comment-start | ||
| 1522 | (match-string 0 line-string)))))) | ||
| 1523 | (fill-prefix | ||
| 1524 | (or line-comment-start | ||
| 1525 | fill-prefix))) | ||
| 1526 | (funcall body))) | ||
| 1527 | |||
| 1528 | (defun rust-find-fill-prefix () | ||
| 1529 | (rust-in-comment-paragraph | ||
| 1530 | (lambda () | ||
| 1531 | (rust-with-comment-fill-prefix | ||
| 1532 | (lambda () | ||
| 1533 | fill-prefix))))) | ||
| 1534 | |||
| 1535 | (defun rust-fill-paragraph (&rest args) | ||
| 1536 | "Special wrapping for `fill-paragraph'. | ||
| 1537 | This handles multi-line comments with a * prefix on each line." | ||
| 1538 | (rust-in-comment-paragraph | ||
| 1539 | (lambda () | ||
| 1540 | (rust-with-comment-fill-prefix | ||
| 1541 | (lambda () | ||
| 1542 | (let | ||
| 1543 | ((fill-paragraph-function | ||
| 1544 | (if (not (eq fill-paragraph-function #'rust-fill-paragraph)) | ||
| 1545 | fill-paragraph-function)) | ||
| 1546 | (fill-paragraph-handle-comment t)) | ||
| 1547 | (apply #'fill-paragraph args) | ||
| 1548 | t)))))) | ||
| 1549 | |||
| 1550 | (defun rust-do-auto-fill (&rest args) | ||
| 1551 | "Special wrapping for `do-auto-fill'. | ||
| 1552 | This handles multi-line comments with a * prefix on each line." | ||
| 1553 | (rust-with-comment-fill-prefix | ||
| 1554 | (lambda () | ||
| 1555 | (apply #'do-auto-fill args) | ||
| 1556 | t))) | ||
| 1557 | |||
| 1558 | (defun rust-fill-forward-paragraph (arg) | ||
| 1559 | ;; This is to work around some funny behavior when a paragraph separator is | ||
| 1560 | ;; at the very top of the file and there is a fill prefix. | ||
| 1561 | (let ((fill-prefix nil)) (forward-paragraph arg))) | ||
| 1562 | |||
| 1563 | (defun rust-comment-indent-new-line (&optional arg) | ||
| 1564 | (rust-with-comment-fill-prefix | ||
| 1565 | (lambda () (comment-indent-new-line arg)))) | ||
| 1566 | |||
| 1567 | ;;; Defun Motions | ||
| 1568 | |||
| 1569 | (defun rust-beginning-of-defun (&optional arg) | ||
| 1570 | "Move backward to the beginning of the current defun. | ||
| 1571 | |||
| 1572 | With ARG, move backward multiple defuns. Negative ARG means | ||
| 1573 | move forward. | ||
| 1574 | |||
| 1575 | This is written mainly to be used as `beginning-of-defun-function' for Rust. | ||
| 1576 | Don't move to the beginning of the line. `beginning-of-defun', | ||
| 1577 | which calls this, does that afterwards." | ||
| 1578 | (interactive "p") | ||
| 1579 | (let* ((arg (or arg 1)) | ||
| 1580 | (magnitude (abs arg)) | ||
| 1581 | (sign (if (< arg 0) -1 1))) | ||
| 1582 | ;; If moving forward, don't find the defun we might currently be | ||
| 1583 | ;; on. | ||
| 1584 | (when (< sign 0) | ||
| 1585 | (end-of-line)) | ||
| 1586 | (catch 'done | ||
| 1587 | (dotimes (_ magnitude) | ||
| 1588 | ;; Search until we find a match that is not in a string or comment. | ||
| 1589 | (while (if (re-search-backward (concat "^\\(" rust-top-item-beg-re "\\)") | ||
| 1590 | nil 'move sign) | ||
| 1591 | (rust-in-str-or-cmnt) | ||
| 1592 | ;; Did not find it. | ||
| 1593 | (throw 'done nil))))) | ||
| 1594 | t)) | ||
| 1595 | |||
| 1596 | (defun rust-end-of-defun () | ||
| 1597 | "Move forward to the next end of defun. | ||
| 1598 | |||
| 1599 | With argument, do it that many times. | ||
| 1600 | Negative argument -N means move back to Nth preceding end of defun. | ||
| 1601 | |||
| 1602 | Assume that this is called after `beginning-of-defun'. So point is | ||
| 1603 | at the beginning of the defun body. | ||
| 1604 | |||
| 1605 | This is written mainly to be used as `end-of-defun-function' for Rust." | ||
| 1606 | (interactive) | ||
| 1607 | ;; Find the opening brace | ||
| 1608 | (if (re-search-forward "[{]" nil t) | ||
| 1609 | (progn | ||
| 1610 | (goto-char (match-beginning 0)) | ||
| 1611 | ;; Go to the closing brace | ||
| 1612 | (condition-case nil | ||
| 1613 | (forward-sexp) | ||
| 1614 | (scan-error | ||
| 1615 | ;; The parentheses are unbalanced; instead of being unable | ||
| 1616 | ;; to fontify, just jump to the end of the buffer | ||
| 1617 | (goto-char (point-max))))) | ||
| 1618 | ;; There is no opening brace, so consider the whole buffer to be one "defun" | ||
| 1619 | (goto-char (point-max)))) | ||
| 1620 | |||
| 1621 | ;;; _ | ||
| 1622 | |||
| 1623 | (defun rust-mode-reload () | ||
| 1624 | (interactive) | ||
| 1625 | (unload-feature 'rust-mode) | ||
| 1626 | (require 'rust-mode) | ||
| 1627 | (rust-mode)) | ||
| 1628 | |||
| 1629 | (provide 'rust-mode) | 84 | (provide 'rust-mode) |
| 1630 | (require 'rust-utils) | 85 | (require 'rust-utils) |
| 1631 | 86 | ||
