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