emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/rust-mode ade453f95e 2/2: Merge pull request #515 from rus


From: ELPA Syncer
Subject: [nongnu] elpa/rust-mode ade453f95e 2/2: Merge pull request #515 from rust-lang/treesit-mode
Date: Sun, 25 Feb 2024 01:00:22 -0500 (EST)

branch: elpa/rust-mode
commit ade453f95e95a3fb6ae90204e51571008fd25d48
Merge: 25c2535a11 a73d8266a9
Author: Sibi Prabakaran <sibi@psibi.in>
Commit: GitHub <noreply@github.com>

    Merge pull request #515 from rust-lang/treesit-mode
    
    Fix rust-mode and tree sitter integration
---
 rust-common.el    |   18 +
 rust-mode.el      | 1450 +----------------------------------------------------
 rust-prog-mode.el | 1442 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 1460 insertions(+), 1450 deletions(-)

diff --git a/rust-common.el b/rust-common.el
new file mode 100644
index 0000000000..c216264561
--- /dev/null
+++ b/rust-common.el
@@ -0,0 +1,18 @@
+;;; rust-common.el --- Common code for both modes -*-lexical-binding: t-*-
+;;; Commentary:
+
+;; rust-common code for both prog-mode and tree-sitter one
+
+;;; Code:
+(defcustom rust-before-save-hook 'rust-before-save-method
+  "Function for formatting before save."
+  :type 'function
+  :group 'rust-mode)
+
+(defcustom rust-after-save-hook 'rust-after-save-method
+  "Default method to handle rustfmt invocation after save."
+  :type 'function
+  :group 'rust-mode)
+
+(provide 'rust-common)
+;;; rust-common.el ends here
diff --git a/rust-mode.el b/rust-mode.el
index 0c68c43ce7..70c2a2c067 100644
--- a/rust-mode.el
+++ b/rust-mode.el
@@ -14,6 +14,7 @@
 ;; This package implements a major-mode for editing Rust source code.
 
 ;;; Code:
+(require 'rust-common)
 
 (eval-when-compile
   (require 'rx)
@@ -29,26 +30,6 @@ This variable might soon be remove again.")
   (require 'rust-playpen)
   (require 'rust-rustfmt))
 
-(defvar electric-pair-inhibit-predicate)
-(defvar electric-pair-skip-self)
-(defvar electric-indent-chars)
-
-(defcustom rust-before-save-hook 'rust-before-save-method
-  "Function for formatting before save."
-  :type 'function
-  :group 'rust-mode)
-
-(defcustom rust-after-save-hook 'rust-after-save-method
-  "Default method to handle rustfmt invocation after save."
-  :type 'function
-  :group 'rust-mode)
-
-(defvar rust-prettify-symbols-alist
-  '(("&&" . ?∧) ("||" . ?∨)
-    ("<=" . ?≤)  (">=" . ?≥) ("!=" . ?≠)
-    ("INFINITY" . ?∞) ("->" . ?→) ("=>" . ?⇒))
-  "Alist of symbol prettifications used for `prettify-symbols-alist'.")
-
 ;;; Customization
 
 (defgroup rust-mode nil
@@ -56,38 +37,6 @@ This variable might soon be remove again.")
   :link '(url-link "https://www.rust-lang.org/";)
   :group 'languages)
 
-(defcustom rust-indent-offset 4
-  "Indent Rust code by this number of spaces."
-  :type 'integer
-  :group 'rust-mode
-  :safe #'integerp)
-
-(defcustom rust-indent-method-chain nil
-  "Indent Rust method chains, aligned by the `.' operators."
-  :type 'boolean
-  :group 'rust-mode
-  :safe #'booleanp)
-
-(defcustom rust-indent-where-clause nil
-  "Indent lines starting with the `where' keyword following a function or 
trait.
-When nil, `where' will be aligned with `fn' or `trait'."
-  :type 'boolean
-  :group 'rust-mode
-  :safe #'booleanp)
-
-(defcustom rust-match-angle-brackets t
-  "Whether to enable angle bracket (`<' and `>') matching where appropriate."
-  :type 'boolean
-  :safe #'booleanp
-  :group 'rust-mode)
-
-(defcustom rust-indent-return-type-to-arguments t
-  "Indent a line starting with the `->' (RArrow) following a function, aligning
-to the function arguments.  When nil, `->' will be indented one level."
-  :type 'boolean
-  :group 'rust-mode
-  :safe #'booleanp)
-
 (defcustom rust-mode-treesitter-derive nil
   "Whether rust-mode should derive from the new treesitter mode `rust-ts-mode'
 instead of `prog-mode'. This option requires emacs29+."
@@ -106,141 +55,6 @@ instead of `prog-mode'. This option requires emacs29+."
 (define-obsolete-face-alias 'rust-string-interpolation-face
   'rust-string-interpolation "0.6.0")
 
-(defface rust-unsafe
-  '((t :inherit font-lock-warning-face))
-  "Face for the `unsafe' keyword."
-  :group 'rust-mode)
-
-(defface rust-question-mark
-  '((t :weight bold :inherit font-lock-builtin-face))
-  "Face for the question mark operator."
-  :group 'rust-mode)
-
-(defface rust-ampersand-face
-  '((t :inherit default))
-  "Face for the ampersand reference mark."
-  :group 'rust-mode)
-
-(defface rust-builtin-formatting-macro
-  '((t :inherit font-lock-builtin-face))
-  "Face for builtin formatting macros (print! &c.)."
-  :group 'rust-mode)
-
-(defface rust-string-interpolation
-  '((t :slant italic :inherit font-lock-string-face))
-  "Face for interpolating braces in builtin formatting macro strings."
-  :group 'rust-mode)
-
-;;; Syntax
-
-(defun rust-re-word (inner) (concat "\\<" inner "\\>"))
-(defun rust-re-grab (inner) (concat "\\(" inner "\\)"))
-(defun rust-re-shy (inner) (concat "\\(?:" inner "\\)"))
-
-(defconst rust-re-ident 
"[[:word:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*")
-(defconst rust-re-lc-ident 
"[[:lower:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*")
-(defconst rust-re-uc-ident "[[:upper:]][[:word:][:multibyte:]_[:digit:]]*")
-(defvar rust-re-vis
-  ;; pub | pub ( crate ) | pub ( self ) | pub ( super ) | pub ( in SimplePath )
-  (concat
-   "pub"
-   (rust-re-shy
-    (concat
-     "[[:space:]]*([[:space:]]*"
-     (rust-re-shy
-      (concat "crate" "\\|"
-              "\\(?:s\\(?:elf\\|uper\\)\\)" "\\|"
-              ;;   in SimplePath
-              (rust-re-shy
-               (concat
-                "in[[:space:]]+"
-                rust-re-ident
-                (rust-re-shy (concat "::" rust-re-ident)) "*"))))
-     "[[:space:]]*)"))
-   "?"))
-(defconst rust-re-unsafe "unsafe")
-(defconst rust-re-extern "extern")
-(defconst rust-re-async-or-const "async\\|const")
-(defconst rust-re-generic
-  (concat "<[[:space:]]*'" rust-re-ident "[[:space:]]*>"))
-(defconst rust-re-union
-  (rx-to-string
-   `(seq
-     (or space line-start)
-     (group symbol-start "union" symbol-end)
-     (+ space) (regexp ,rust-re-ident))))
-
-(defun rust-re-item-def (itype)
-  (concat (rust-re-word itype)
-          (rust-re-shy rust-re-generic) "?"
-          "[[:space:]]+" (rust-re-grab rust-re-ident)))
-
-;; TODO some of this does only make sense for `fn' (unsafe, extern...)
-;; and not other items
-(defun rust-re-item-def-imenu (itype)
-  (concat "^[[:space:]]*"
-          (rust-re-shy (concat rust-re-vis "[[:space:]]+")) "?"
-          (rust-re-shy (concat (rust-re-word "default") "[[:space:]]+")) "?"
-          (rust-re-shy (concat (rust-re-shy rust-re-async-or-const) 
"[[:space:]]+")) "?"
-          (rust-re-shy (concat (rust-re-word rust-re-unsafe) "[[:space:]]+")) 
"?"
-          (rust-re-shy (concat (rust-re-word rust-re-extern) "[[:space:]]+"
-                               (rust-re-shy "\"[^\"]+\"[[:space:]]+") "?")) "?"
-                               (rust-re-item-def itype)))
-
-(defvar rust-imenu-generic-expression
-  (append (mapcar #'(lambda (x)
-                      (list (capitalize x) (rust-re-item-def-imenu x) 1))
-                  '("enum" "struct" "union" "type" "mod" "fn" "trait" "impl"))
-          `(("Macro" ,(rust-re-item-def-imenu "macro_rules!") 1)))
-  "Value for `imenu-generic-expression' in Rust mode.
-
-Create a hierarchical index of the item definitions in a Rust file.
-
-Imenu will show all the enums, structs, etc. in their own subheading.
-Use idomenu (imenu with `ido-mode') for best mileage.")
-
-(defvar rust-mode-syntax-table
-  (let ((table (make-syntax-table)))
-
-    ;; Operators
-    (dolist (i '(?+ ?- ?* ?/ ?% ?& ?| ?^ ?! ?< ?> ?~ ?@))
-      (modify-syntax-entry i "." table))
-
-    ;; Strings
-    (modify-syntax-entry ?\" "\"" table)
-    (modify-syntax-entry ?\\ "\\" table)
-
-    ;; Angle brackets.  We suppress this with syntactic propertization
-    ;; when needed
-    (modify-syntax-entry ?< "(>" table)
-    (modify-syntax-entry ?> ")<" table)
-
-    ;; Comments
-    (modify-syntax-entry ?/  ". 124b" table)
-    (modify-syntax-entry ?*  ". 23n"  table)
-    (modify-syntax-entry ?\n "> b"    table)
-    (modify-syntax-entry ?\^m "> b"   table)
-
-    table)
-  "Syntax definitions and helpers.")
-
-;;; Prettify
-
-(defun rust--prettify-symbols-compose-p (start end match)
-  "Return true iff the symbol MATCH should be composed.
-See `prettify-symbols-compose-predicate'."
-  (and (fboundp 'prettify-symbols-default-compose-p)
-       (prettify-symbols-default-compose-p start end match)
-       ;; Make sure || is not a closure with 0 arguments and && is not
-       ;; a double reference.
-       (pcase match
-         ("||" (not (save-excursion
-                      (goto-char start)
-                      (looking-back "\\(?:\\<move\\|[[({:=,;]\\) *"
-                                    (line-beginning-position)))))
-         ("&&" (char-equal (char-after end) ?\s))
-         (_ t))))
-
 ;;; Mode
 
 (defvar rust-mode-map
@@ -264,1268 +78,6 @@ See `prettify-symbols-compose-predicate'."
 ;;;###autoload
 (add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-mode))
 
-(defvar rust-top-item-beg-re
-  (concat "\\s-*\\(?:priv\\|pub\\)?\\s-*"
-          ;; TODO some of this does only make sense for `fn' (unsafe, 
extern...)
-          ;; and not other items
-          (rust-re-shy (concat (rust-re-shy rust-re-vis) "[[:space:]]+")) "?"
-          (rust-re-shy (concat (rust-re-shy rust-re-async-or-const) 
"[[:space:]]+")) "?"
-          (rust-re-shy (concat (rust-re-shy rust-re-unsafe) "[[:space:]]+")) 
"?"
-          (regexp-opt
-           '("enum" "struct" "union" "type" "mod" "fn" "static" "impl"
-             "extern" "trait" "async"))
-          "\\_>")
-  "Start of a Rust item.")
-
-(defconst rust-re-type-or-constructor
-  (rx symbol-start
-      (group upper (0+ (any word nonascii digit "_")))
-      symbol-end))
-
-(defconst rust-keywords
-  '("as" "async" "await"
-    "box" "break"
-    "const" "continue" "crate"
-    "do" "dyn"
-    "else" "enum" "extern" "existential"
-    "false" "fn" "for"
-    "if" "impl" "in"
-    "let" "loop"
-    "match" "mod" "move" "mut"
-    "priv" "pub"
-    "ref" "return"
-    "self" "static" "struct" "super"
-    "true" "trait" "type" "try"
-    "use"
-    "virtual"
-    "where" "while"
-    "yield")
-  "Font-locking definitions and helpers.")
-
-(defconst rust-special-types
-  '("u8" "i8"
-    "u16" "i16"
-    "u32" "i32"
-    "u64" "i64"
-    "u128" "i128"
-
-    "f32" "f64"
-    "isize" "usize"
-    "bool"
-    "str" "char"))
-
-(defconst rust-expression-introducers
-  '("if" "while" "match" "return" "box" "in")
-  "List of Rust keywords that are always followed by expressions.")
-
-(defconst rust-number-with-type
-  (eval-when-compile
-    (concat
-     
"\\_<\\(?:0[box]?\\|[1-9]\\)[[:digit:]a-fA-F_.]*\\(?:[eE][+-]?[[:digit:]_]\\)?"
-     (regexp-opt '("u8" "i8" "u16" "i16" "u32" "i32" "u64" "i64"
-                   "u128" "i128" "usize" "isize" "f32" "f64")
-                 t)
-     "\\_>"))
-  "Regular expression matching a number with a type suffix.")
-
-(defvar rust-builtin-formatting-macros
-  '("eprint"
-    "eprintln"
-    "format"
-    "print"
-    "println")
-  "List of builtin Rust macros for string formatting.
-This is used by `rust-font-lock-keywords'.
-\(`write!' is handled separately).")
-
-(defvar rust-formatting-macro-opening-re
-  "[[:space:]\n]*[({[][[:space:]\n]*"
-  "Regular expression to match the opening delimiter of a Rust formatting 
macro.")
-
-(defvar rust-start-of-string-re
-  "\\(?:r#*\\)?\""
-  "Regular expression to match the start of a Rust raw string.")
-
-(defun rust-path-font-lock-matcher (re-ident)
-  "Match occurrences of RE-IDENT followed by a double-colon.
-Examples include to match names like \"foo::\" or \"Foo::\".
-Does not match type annotations of the form \"foo::<\"."
-  `(lambda (limit)
-     (catch 'rust-path-font-lock-matcher
-       (while t
-         (let* ((symbol-then-colons (rx-to-string '(seq (group (regexp 
,re-ident)) "::")))
-                (match (re-search-forward symbol-then-colons limit t)))
-           (cond
-            ;; If we didn't find a match, there are no more occurrences
-            ;; of foo::, so return.
-            ((null match) (throw 'rust-path-font-lock-matcher nil))
-            ;; If this isn't a type annotation foo::<, we've found a
-            ;; match, so a return it!
-            ((not (looking-at (rx (0+ space) "<")))
-             (throw 'rust-path-font-lock-matcher match))))))))
-
-(defvar rust-font-lock-keywords
-  (append
-   `(
-     ;; Keywords proper
-     (,(regexp-opt rust-keywords 'symbols) . font-lock-keyword-face)
-
-     ;; Contextual keywords
-     ("\\_<\\(default\\)[[:space:]]+fn\\_>" 1 font-lock-keyword-face)
-     (,rust-re-union 1 font-lock-keyword-face)
-
-     ;; Special types
-     (,(regexp-opt rust-special-types 'symbols) . font-lock-type-face)
-
-     ;; The unsafe keyword
-     ("\\_<unsafe\\_>" . 'rust-unsafe)
-
-     ;; Attributes like `#[bar(baz)]` or `#![bar(baz)]` or `#[bar = "baz"]`
-     (,(rust-re-grab (concat "#\\!?\\[" rust-re-ident "[^]]*\\]"))
-      1 font-lock-preprocessor-face keep)
-
-     ;; Builtin formatting macros
-     (,(concat (rust-re-grab
-                (concat (rust-re-word (regexp-opt 
rust-builtin-formatting-macros))
-                        "!"))
-               rust-formatting-macro-opening-re
-               "\\(?:" rust-start-of-string-re "\\)?")
-      (1 'rust-builtin-formatting-macro)
-      (rust-string-interpolation-matcher
-       (rust-end-of-string)
-       nil
-       (0 'rust-string-interpolation t nil)))
-
-     ;; write! macro
-     (,(concat (rust-re-grab (concat (rust-re-word "write\\(ln\\)?") "!"))
-               rust-formatting-macro-opening-re
-               "[[:space:]]*[^\"]+,[[:space:]]*"
-               rust-start-of-string-re)
-      (1 'rust-builtin-formatting-macro)
-      (rust-string-interpolation-matcher
-       (rust-end-of-string)
-       nil
-       (0 'rust-string-interpolation t nil)))
-
-     ;; Syntax extension invocations like `foo!`, highlight including the !
-     (,(concat (rust-re-grab (concat rust-re-ident "!")) "[({[:space:][]")
-      1 font-lock-preprocessor-face)
-
-     ;; Field names like `foo:`, highlight excluding the :
-     (,(concat (rust-re-grab rust-re-ident) "[[:space:]]*:[^:]")
-      1 font-lock-variable-name-face)
-
-     ;; CamelCase Means Type Or Constructor
-     (,rust-re-type-or-constructor 1 font-lock-type-face)
-
-     ;; Type-inferred binding
-     (,(concat 
"\\_<\\(?:let\\s-+ref\\|let\\|ref\\|for\\)\\s-+\\(?:mut\\s-+\\)?"
-               (rust-re-grab rust-re-ident)
-               "\\_>")
-      1 font-lock-variable-name-face)
-
-     ;; Type names like `Foo::`, highlight excluding the ::
-     (,(rust-path-font-lock-matcher rust-re-uc-ident) 1 font-lock-type-face)
-
-     ;; Module names like `foo::`, highlight excluding the ::
-     (,(rust-path-font-lock-matcher rust-re-lc-ident) 1 
font-lock-constant-face)
-
-     ;; Lifetimes like `'foo`
-     (,(concat "'" (rust-re-grab rust-re-ident) "[^']") 1 
font-lock-variable-name-face)
-
-     ;; Question mark operator
-     ("\\?" . 'rust-question-mark)
-     ("\\(&+\\)\\(?:'\\(?:\\<\\|_\\)\\|\\<\\|[[({:*_|]\\)"
-      1 'rust-ampersand-face)
-     ;; Numbers with type suffix
-     (,rust-number-with-type 1 font-lock-type-face)
-     )
-
-   ;; Ensure we highlight `Foo` in `struct Foo` as a type.
-   (mapcar #'(lambda (x)
-               (list (rust-re-item-def (car x))
-                     1 (cdr x)))
-           '(("enum" . font-lock-type-face)
-             ("struct" . font-lock-type-face)
-             ("union" . font-lock-type-face)
-             ("type" . font-lock-type-face)
-             ("mod" . font-lock-constant-face)
-             ("use" . font-lock-constant-face)
-             ("fn" . font-lock-function-name-face)))))
-
-(defun rust-end-of-string ()
-  "Skip to the end of the current string."
-  (save-excursion
-    (skip-syntax-forward "^\"|")
-    (skip-syntax-forward "\"|")
-    (point)))
-
-(defun rust-looking-back-str (str)
-  "Return non-nil if there's a match on the text before point and STR.
-Like `looking-back' but for fixed strings rather than regexps (so
-that it's not so slow)."
-  (let ((len (length str)))
-    (and (> (point) len)
-         (equal str (buffer-substring-no-properties (- (point) len) 
(point))))))
-
-(defun rust-looking-back-symbols (symbols)
-  "Return non-nil if the point is after a member of SYMBOLS.
-SYMBOLS is a list of strings that represent the respective
-symbols."
-  (save-excursion
-    (let* ((pt-orig (point))
-           (beg-of-symbol (progn (forward-thing 'symbol -1) (point)))
-           (end-of-symbol (progn (forward-thing 'symbol 1) (point))))
-      (and
-       (= end-of-symbol pt-orig)
-       (member (buffer-substring-no-properties beg-of-symbol pt-orig)
-               symbols)))))
-
-(defun rust-looking-back-ident ()
-  "Non-nil if we are looking backwards at a valid rust identifier.
-If we are, regexp match 0 is the identifier."
-  (let ((outer-point (point)))
-    (save-excursion
-      (forward-thing 'symbol -1)
-      (and (looking-at rust-re-ident)
-           (eq (match-end 0) outer-point)))))
-
-(defun rust-looking-back-macro ()
-  "Non-nil if looking back at a potential macro name followed by a \"!\".
-If we are, regexp match 0 is the macro name."
-  (save-excursion
-    ;; Look past whitespace and line breaks.
-    ;; > is okay because we only use it for \n and \r, not "*/"
-    (skip-syntax-backward "->")
-    (when (eq (char-before) ?!)
-      (forward-char -1)
-      (skip-syntax-backward "->")
-      (when (rust-looking-back-ident)
-        (let ((ident (match-string 0)))
-          (not (member ident rust-expression-introducers)))))))
-
-(defun rust-looking-back-macro-rules ()
-  "Non-nil if looking back at \"macro_rules IDENT !\"."
-  (save-excursion
-    (skip-syntax-backward "->")
-    (let ((outer-point (point)))
-      (forward-thing 'symbol -2)
-      (and (looking-at (concat "macro_rules\\s-*!\\s-*" rust-re-ident))
-           (eq (match-end 0) outer-point)))))
-
-;;; Syntax definitions and helpers
-
-(defun rust-paren-level () (nth 0 (syntax-ppss)))
-(defun rust-in-str () (nth 3 (syntax-ppss)))
-(defun rust-in-str-or-cmnt () (nth 8 (syntax-ppss)))
-(defun rust-rewind-past-str-cmnt () (goto-char (nth 8 (syntax-ppss))))
-
-(defun rust-rewind-irrelevant ()
-  (let ((continue t))
-    (while continue
-      (let ((starting (point)))
-        (skip-chars-backward "[:space:]\n")
-        (when (rust-looking-back-str "*/")
-          (backward-char))
-        (when (rust-in-str-or-cmnt)
-          (rust-rewind-past-str-cmnt))
-        ;; Rewind until the point no longer moves
-        (setq continue (/= starting (point)))))))
-
-(defun rust-in-macro ()
-  "Return non-nil when point is within the scope of a macro.
-If we are, return the position of the opening bracket of the macro's 
arguments."
-  (let ((ppss (syntax-ppss)))
-    ;; If we're in a string or comment, we're definitely not on a token a macro
-    ;; will see.
-    (when (not (or (nth 3 ppss) (nth 4 ppss)))
-      ;; Walk outward to enclosing parens, looking for one preceded by "ident 
!"
-      ;; or "macro_rules! ident".
-      (let (result
-            (enclosing (reverse (nth 9 ppss))))
-        (save-excursion
-          (while enclosing
-            (goto-char (car enclosing))
-            (if (or (rust-looking-back-macro)
-                    (rust-looking-back-macro-rules))
-                (setq result (point) enclosing nil)
-              (setq enclosing (cdr enclosing)))))
-        result))))
-
-(defun rust-looking-at-where ()
-  "Return T when looking at the \"where\" keyword."
-  (and (looking-at-p "\\bwhere\\b")
-       (not (rust-in-str-or-cmnt))))
-
-(defun rust-rewind-to-where (&optional limit)
-  "Rewind the point to the closest occurrence of the \"where\" keyword.
-Return T iff a where-clause was found.  Does not rewind past
-LIMIT when passed, otherwise only stops at the beginning of the
-buffer."
-  (when (re-search-backward "\\bwhere\\b" limit t)
-    (if (rust-in-str-or-cmnt)
-        (rust-rewind-to-where limit)
-      t)))
-
-(defconst rust-re-pre-expression-operators "[-=!%&*/:<>[{(|.^;}]")
-
-(defconst rust-re-special-types (regexp-opt rust-special-types 'symbols))
-
-(defun rust-align-to-expr-after-brace ()
-  (save-excursion
-    (forward-char)
-    ;; We don't want to indent out to the open bracket if the
-    ;; open bracket ends the line
-    (when (not (looking-at "[[:blank:]]*\\(?://.*\\)?$"))
-      (when (looking-at "[[:space:]]")
-        (forward-word 1)
-        (backward-word 1))
-      (current-column))))
-
-(defun rust-rewind-to-beginning-of-current-level-expr ()
-  (let ((current-level (rust-paren-level)))
-    (back-to-indentation)
-    (when (looking-at "->")
-      (rust-rewind-irrelevant)
-      (back-to-indentation))
-    (while (> (rust-paren-level) current-level)
-      (backward-up-list)
-      (back-to-indentation))
-    ;; When we're in the where clause, skip over it.  First find out the start
-    ;; of the function and its paren level.
-    (let ((function-start nil) (function-level nil))
-      (save-excursion
-        (rust-beginning-of-defun)
-        (back-to-indentation)
-        ;; Avoid using multiple-value-bind
-        (setq function-start (point)
-              function-level (rust-paren-level)))
-      ;; On a where clause
-      (when (or (rust-looking-at-where)
-                ;; or in one of the following lines, e.g.
-                ;; where A: Eq
-                ;;       B: Hash <- on this line
-                (and (save-excursion
-                       (rust-rewind-to-where function-start))
-                     (= current-level function-level)))
-        (goto-char function-start)))))
-
-(defun rust-align-to-method-chain ()
-  (save-excursion
-    ;; for method-chain alignment to apply, we must be looking at
-    ;; another method call or field access or something like
-    ;; that. This avoids rather "eager" jumps in situations like:
-    ;;
-    ;; {
-    ;;     something.foo()
-    ;; <indent>
-    ;;
-    ;; Without this check, we would wind up with the cursor under the
-    ;; `.`. In an older version, I had the inverse of the current
-    ;; check, where we checked for situations that should NOT indent,
-    ;; vs checking for the one situation where we SHOULD. It should be
-    ;; clear that this is more robust, but also I find it mildly less
-    ;; annoying to have to press tab again to align to a method chain
-    ;; than to have an over-eager indent in all other cases which must
-    ;; be undone via tab.
-
-    (when (looking-at (concat "\s*\." rust-re-ident))
-      (forward-line -1)
-      (end-of-line)
-      ;; Keep going up (looking for a line that could contain a method chain)
-      ;; while we're in a comment or on a blank line. Stop when the paren
-      ;; level changes.
-      (let ((level (rust-paren-level)))
-        (while (and (or (rust-in-str-or-cmnt)
-                        ;; Only whitespace (or nothing) from the beginning to
-                        ;; the end of the line.
-                        (looking-back "^\s*" (line-beginning-position)))
-                    (= (rust-paren-level) level))
-          (forward-line -1)
-          (end-of-line)))
-
-      (let
-          ;; skip-dot-identifier is used to position the point at the
-          ;; `.` when looking at something like
-          ;;
-          ;;      foo.bar
-          ;;         ^   ^
-          ;;         |   |
-          ;;         |  position of point
-          ;;       returned offset
-          ;;
-          ((skip-dot-identifier
-            (lambda ()
-              (when (and (rust-looking-back-ident)
-                         (save-excursion
-                           (forward-thing 'symbol -1)
-                           (= ?. (char-before))))
-                (forward-thing 'symbol -1)
-                (backward-char)
-                (- (current-column) rust-indent-offset)))))
-        (cond
-         ;; foo.bar(...)
-         ((looking-back "[)?]" (1- (point)))
-          (backward-list 1)
-          (funcall skip-dot-identifier))
-
-         ;; foo.bar
-         (t (funcall skip-dot-identifier)))))))
-
-(defun rust-mode-indent-line ()
-  (interactive)
-  (let ((indent
-         (save-excursion
-           (back-to-indentation)
-           ;; Point is now at beginning of current line
-           (let* ((level (rust-paren-level))
-                  (baseline
-                   ;; Our "baseline" is one level out from the
-                   ;; indentation of the expression containing the
-                   ;; innermost enclosing opening bracket.  That way
-                   ;; if we are within a block that has a different
-                   ;; indentation than this mode would give it, we
-                   ;; still indent the inside of it correctly relative
-                   ;; to the outside.
-                   (if (= 0 level)
-                       0
-                     (or
-                      (when rust-indent-method-chain
-                        (rust-align-to-method-chain))
-                      (save-excursion
-                        (rust-rewind-irrelevant)
-                        (backward-up-list)
-                        (rust-rewind-to-beginning-of-current-level-expr)
-                        (+ (current-column) rust-indent-offset))))))
-             (cond
-              ;; Indent inside a non-raw string only if the previous line
-              ;; ends with a backslash that is inside the same string
-              ((nth 3 (syntax-ppss))
-               (let*
-                   ((string-begin-pos (nth 8 (syntax-ppss)))
-                    (end-of-prev-line-pos
-                     (and (not (rust--same-line-p (point) (point-min)))
-                          (line-end-position 0))))
-                 (when
-                     (and
-                      ;; If the string begins with an "r" it's a raw string and
-                      ;; we should not change the indentation
-                      (/= ?r (char-after string-begin-pos))
-
-                      ;; If we're on the first line this will be nil and the
-                      ;; rest does not apply
-                      end-of-prev-line-pos
-
-                      ;; The end of the previous line needs to be inside the
-                      ;; current string...
-                      (> end-of-prev-line-pos string-begin-pos)
-
-                      ;; ...and end with a backslash
-                      (= ?\\ (char-before end-of-prev-line-pos)))
-
-                   ;; Indent to the same level as the previous line, or the
-                   ;; start of the string if the previous line starts the 
string
-                   (if (rust--same-line-p end-of-prev-line-pos 
string-begin-pos)
-                       ;; The previous line is the start of the string.
-                       ;; If the backslash is the only character after the
-                       ;; string beginning, indent to the next indent
-                       ;; level.  Otherwise align with the start of the string.
-                       (if (> (- end-of-prev-line-pos string-begin-pos) 2)
-                           (save-excursion
-                             (goto-char (+ 1 string-begin-pos))
-                             (current-column))
-                         baseline)
-
-                     ;; The previous line is not the start of the string, so
-                     ;; match its indentation.
-                     (save-excursion
-                       (goto-char end-of-prev-line-pos)
-                       (back-to-indentation)
-                       (current-column))))))
-
-              ;; A function return type is indented to the corresponding
-              ;; function arguments, if -to-arguments is selected.
-              ((and rust-indent-return-type-to-arguments
-                    (looking-at "->"))
-               (save-excursion
-                 (backward-list)
-                 (or (rust-align-to-expr-after-brace)
-                     (+ baseline rust-indent-offset))))
-
-              ;; A closing brace is 1 level unindented
-              ((looking-at "[]})]") (- baseline rust-indent-offset))
-
-              ;; Doc comments in /** style with leading * indent to line up 
the *s
-              ((and (nth 4 (syntax-ppss)) (looking-at "*"))
-               (+ 1 baseline))
-
-              ;; When the user chose not to indent the start of the where
-              ;; clause, put it on the baseline.
-              ((and (not rust-indent-where-clause)
-                    (rust-looking-at-where))
-               baseline)
-
-              ;; If we're in any other token-tree / sexp, then:
-              (t
-               (or
-                ;; If we are inside a pair of braces, with something after the
-                ;; open brace on the same line and ending with a comma, treat
-                ;; it as fields and align them.
-                (when (> level 0)
-                  (save-excursion
-                    (rust-rewind-irrelevant)
-                    (backward-up-list)
-                    ;; Point is now at the beginning of the containing set of 
braces
-                    (rust-align-to-expr-after-brace)))
-
-                ;; When where-clauses are spread over multiple lines, clauses
-                ;; should be aligned on the type parameters.  In this case we
-                ;; take care of the second and following clauses (the ones
-                ;; that don't start with "where ")
-                (save-excursion
-                  ;; Find the start of the function, we'll use this to limit
-                  ;; our search for "where ".
-                  (let ((function-start nil) (function-level nil))
-                    (save-excursion
-                      ;; If we're already at the start of a function,
-                      ;; don't go back any farther.  We can easily do
-                      ;; this by moving to the end of the line first.
-                      (end-of-line)
-                      (rust-beginning-of-defun)
-                      (back-to-indentation)
-                      ;; Avoid using multiple-value-bind
-                      (setq function-start (point)
-                            function-level (rust-paren-level)))
-                    ;; When we're not on a line starting with "where ", but
-                    ;; still on a where-clause line, go to "where "
-                    (when (and
-                           (not (rust-looking-at-where))
-                           ;; We're looking at something like "F: ..."
-                           (looking-at (concat rust-re-ident ":"))
-                           ;; There is a "where " somewhere after the
-                           ;; start of the function.
-                           (rust-rewind-to-where function-start)
-                           ;; Make sure we're not inside the function
-                           ;; already (e.g. initializing a struct) by
-                           ;; checking we are the same level.
-                           (= function-level level))
-                      ;; skip over "where"
-                      (forward-char 5)
-                      ;; Unless "where" is at the end of the line
-                      (if (eolp)
-                          ;; in this case the type parameters bounds are just
-                          ;; indented once
-                          (+ baseline rust-indent-offset)
-                        ;; otherwise, skip over whitespace,
-                        (skip-chars-forward "[:space:]")
-                        ;; get the column of the type parameter and use that
-                        ;; as indentation offset
-                        (current-column)))))
-
-                (progn
-                  (back-to-indentation)
-                  ;; Point is now at the beginning of the current line
-                  (if (or
-                       ;; If this line begins with "else" or "{", stay on the
-                       ;; baseline as well (we are continuing an expression,
-                       ;; but the "else" or "{" should align with the beginning
-                       ;; of the expression it's in.)
-                       ;; Or, if this line starts a comment, stay on the
-                       ;; baseline as well.
-                       (looking-at "\\<else\\>\\|{\\|/[/*]")
-
-                       ;; If this is the start of a top-level item,
-                       ;; stay on the baseline.
-                       (looking-at rust-top-item-beg-re)
-
-                       (save-excursion
-                         (rust-rewind-irrelevant)
-                         ;; Point is now at the end of the previous line
-                         (or
-                          ;; If we are at the start of the buffer, no
-                          ;; indentation is needed, so stay at baseline...
-                          (= (point) 1)
-                          ;; ..or if the previous line ends with any of these:
-                          ;;     { ? : ( , ; [ }
-                          ;; then we are at the beginning of an
-                          ;; expression, so stay on the baseline...
-                          (looking-back "[(,:;[{}]\\|[^|]|" (- (point) 2))
-                          ;; or if the previous line is the end of an
-                          ;; attribute, stay at the baseline...
-                          (progn 
(rust-rewind-to-beginning-of-current-level-expr)
-                                 (looking-at "#")))))
-                      baseline
-
-                    ;; Otherwise, we are continuing the same expression from
-                    ;; the previous line, so add one additional indent level
-                    (+ baseline rust-indent-offset))))))))))
-
-    (when indent
-      ;; If we're at the beginning of the line (before or at the current
-      ;; indentation), jump with the indentation change.  Otherwise, save the
-      ;; excursion so that adding the indentations will leave us at the
-      ;; equivalent position within the line to where we were before.
-      (if (<= (current-column) (current-indentation))
-          (indent-line-to indent)
-        (save-excursion (indent-line-to indent))))))
-
-(defun rust--same-line-p (pos1 pos2)
-  "Return non-nil if POS1 and POS2 are on the same line."
-  (save-excursion (= (progn (goto-char pos1) (line-end-position))
-                     (progn (goto-char pos2) (line-end-position)))))
-
-;;; Font-locking definitions and helpers
-
-(defun rust-next-string-interpolation (limit)
-  "Search forward from point for next Rust interpolation marker before LIMIT.
-Set point to the end of the occurrence found, and return match beginning
-and end."
-  (catch 'match
-    (save-match-data
-      (save-excursion
-        (while (search-forward "{" limit t)
-          (if (eql (char-after (point)) ?{)
-              (forward-char)
-            (let ((start (match-beginning 0)))
-              ;; According to fmt_macros::Parser::next, an opening brace
-              ;; must be followed by an optional argument and/or format
-              ;; specifier, then a closing brace. A single closing brace
-              ;; without a corresponding unescaped opening brace is an
-              ;; error. We don't need to do anything special with
-              ;; arguments, specifiers, or errors, so we only search for
-              ;; the single closing brace.
-              (when (search-forward "}" limit t)
-                (throw 'match (list start (point)))))))))))
-
-(defun rust-string-interpolation-matcher (limit)
-  "Match next Rust interpolation marker before LIMIT and set match data if 
found.
-Returns nil if not within a Rust string."
-  (when-let (((rust-in-str))
-             (match (rust-next-string-interpolation limit)))
-    (set-match-data match)
-    (goto-char (cadr match))
-    match))
-
-(defun rust-syntax-class-before-point ()
-  (when (> (point) 1)
-    (syntax-class (syntax-after (1- (point))))))
-
-(defun rust-rewind-qualified-ident ()
-  (while (rust-looking-back-ident)
-    (backward-sexp)
-    (when (save-excursion (rust-rewind-irrelevant) (rust-looking-back-str 
"::"))
-      (rust-rewind-irrelevant)
-      (backward-char 2)
-      (rust-rewind-irrelevant))))
-
-(defun rust-rewind-type-param-list ()
-  (cond
-   ((and (rust-looking-back-str ">") (equal 5 
(rust-syntax-class-before-point)))
-    (backward-sexp)
-    (rust-rewind-irrelevant))
-
-   ;; We need to be able to back up past the Fn(args) -> RT form as well.  If
-   ;; we're looking back at this, we want to end up just after "Fn".
-   ((member (char-before) '(?\] ?\) ))
-    (let ((is-paren (rust-looking-back-str ")")))
-      (when-let ((dest (save-excursion
-                         (backward-sexp)
-                         (rust-rewind-irrelevant)
-                         (or
-                          (when (rust-looking-back-str "->")
-                            (backward-char 2)
-                            (rust-rewind-irrelevant)
-                            (when (rust-looking-back-str ")")
-                              (backward-sexp)
-                              (point)))
-                          (and is-paren (point))))))
-        (goto-char dest))))))
-
-(defun rust-rewind-to-decl-name ()
-  "Return the point at the beginning of the name in a declaration.
-I.e. if we are before an ident that is part of a declaration that
-can have a where clause, rewind back to just before the name of
-the subject of that where clause and return the new point.
-Otherwise return nil."
-  (let* ((ident-pos (point))
-         (newpos (save-excursion
-                   (rust-rewind-irrelevant)
-                   (rust-rewind-type-param-list)
-                   (cond
-                    ((rust-looking-back-symbols
-                      '("fn" "trait" "enum" "struct" "union" "impl" "type"))
-                     ident-pos)
-
-                    ((equal 5 (rust-syntax-class-before-point))
-                     (backward-sexp)
-                     (rust-rewind-to-decl-name))
-
-                    ((looking-back "[:,'+=]" (1- (point)))
-                     (backward-char)
-                     (rust-rewind-to-decl-name))
-
-                    ((rust-looking-back-str "->")
-                     (backward-char 2)
-                     (rust-rewind-to-decl-name))
-
-                    ((rust-looking-back-ident)
-                     (rust-rewind-qualified-ident)
-                     (rust-rewind-to-decl-name))))))
-    (when newpos (goto-char newpos))
-    newpos))
-
-(defun rust-is-in-expression-context (token)
-  "Return t if what comes right after the point is part of an
-expression (as opposed to starting a type) by looking at what
-comes before.  Takes a symbol that roughly indicates what is
-after the point.
-
-This function is used as part of `rust-is-lt-char-operator' as
-part of angle bracket matching, and is not intended to be used
-outside of this context."
-  (save-excursion
-    (let ((postchar (char-after)))
-      (rust-rewind-irrelevant)
-      ;; A type alias or ascription could have a type param list.  Skip 
backwards past it.
-      (when (member token '(ambiguous-operator open-brace))
-        (rust-rewind-type-param-list))
-      (cond
-
-       ;; Certain keywords always introduce expressions
-       ((rust-looking-back-symbols rust-expression-introducers) t)
-
-       ;; "as" introduces a type
-       ((rust-looking-back-symbols '("as")) nil)
-
-       ;; An open angle bracket never introduces expression context WITHIN the 
angle brackets
-       ((and (equal token 'open-brace) (equal postchar ?<)) nil)
-
-       ;; An ident! followed by an open brace is a macro invocation.  Consider
-       ;; it to be an expression.
-       ((and (equal token 'open-brace) (rust-looking-back-macro)) t)
-
-       ;; In a brace context a "]" introduces an expression.
-       ((and (eq token 'open-brace) (rust-looking-back-str "]")))
-
-       ;; An identifier is right after an ending paren, bracket, angle bracket
-       ;; or curly brace.  It's a type if the last sexp was a type.
-       ((and (equal token 'ident) (equal 5 (rust-syntax-class-before-point)))
-        (backward-sexp)
-        (rust-is-in-expression-context 'open-brace))
-
-       ;; If a "for" appears without a ; or { before it, it's part of an
-       ;; "impl X for y", so the y is a type.  Otherwise it's
-       ;; introducing a loop, so the y is an expression
-       ((and (equal token 'ident) (rust-looking-back-symbols '("for")))
-        (backward-sexp)
-        (rust-rewind-irrelevant)
-        (looking-back "[{;]" (1- (point))))
-
-       ((rust-looking-back-ident)
-        (rust-rewind-qualified-ident)
-        (rust-rewind-irrelevant)
-        (cond
-         ((equal token 'open-brace)
-          ;; We now know we have:
-          ;;   ident <maybe type params> [{([]
-          ;; where [{([] denotes either a {, ( or [.
-          ;; This character is bound as postchar.
-          (cond
-           ;; If postchar is a paren or square bracket, then if the
-           ;; brace is a type if the identifier is one
-           ((member postchar '(?\( ?\[ )) (rust-is-in-expression-context 
'ident))
-
-           ;; If postchar is a curly brace, the brace can only be a type if
-           ;; ident2 is the name of an enum, struct or trait being declared.
-           ;; Note that if there is a -> before the ident then the ident would
-           ;; be a type but the { is not.
-           ((equal ?{ postchar)
-            (not (and (rust-rewind-to-decl-name)
-                      (progn
-                        (rust-rewind-irrelevant)
-                        (rust-looking-back-symbols
-                         '("enum" "struct" "union" "trait" "type"))))))))
-
-         ((equal token 'ambiguous-operator)
-          (cond
-           ;; An ampersand after an ident has to be an operator rather
-           ;; than a & at the beginning of a ref type
-           ((equal postchar ?&) t)
-
-           ;; A : followed by a type then an = introduces an
-           ;; expression (unless it is part of a where clause of a
-           ;; "type" declaration)
-           ((and (equal postchar ?=)
-                 (looking-back "[^:]:" (- (point) 2))
-                 (not (save-excursion
-                        (and (rust-rewind-to-decl-name)
-                             (progn (rust-rewind-irrelevant)
-                                    (rust-looking-back-symbols '("type"))))))))
-
-           ;; "let ident =" introduces an expression--and so does "const" and 
"mut"
-           ((and (equal postchar ?=) (rust-looking-back-symbols '("let" 
"const" "mut"))) t)
-
-           ;; As a specific special case, see if this is the = in this 
situation:
-           ;;     enum EnumName<type params> { Ident =
-           ;; In this case, this is a c-like enum and despite Ident
-           ;; representing a type, what comes after the = is an expression
-           ((and
-             (> (rust-paren-level) 0)
-             (save-excursion
-               (backward-up-list)
-               (rust-rewind-irrelevant)
-               (rust-rewind-type-param-list)
-               (and
-                (rust-looking-back-ident)
-                (progn
-                  (rust-rewind-qualified-ident)
-                  (rust-rewind-irrelevant)
-                  (rust-looking-back-str "enum")))))
-            t)
-
-           ;; Otherwise the ambiguous operator is a type if the identifier is 
a type
-           ((rust-is-in-expression-context 'ident) t)))
-
-         ((equal token 'colon)
-          (cond
-           ;; If we see a ident: not inside any braces/parens, we're at top 
level.
-           ;; There are no allowed expressions after colons there, just types.
-           ((<= (rust-paren-level) 0) nil)
-
-           ;; We see ident: inside a list
-           ((looking-back "[{,]" (1- (point)))
-            (backward-up-list)
-
-            ;; If a : appears whose surrounding paren/brackets/braces are
-            ;; anything other than curly braces, it can't be a field
-            ;; initializer and must be denoting a type.
-            (when (looking-at "{")
-              (rust-rewind-irrelevant)
-              (rust-rewind-type-param-list)
-              (when (rust-looking-back-ident)
-                ;; We have a context that looks like this:
-                ;;    ident2 <maybe type params> { [maybe paren-balanced code 
ending in comma] ident1:
-                ;; the point is sitting just after ident2, and we trying to
-                ;; figure out if the colon introduces an expression or a type.
-                ;; The answer is that ident1 is a field name, and what comes
-                ;; after the colon is an expression, if ident2 is an
-                ;; expression.
-                (rust-rewind-qualified-ident)
-                (rust-is-in-expression-context 'ident))))
-
-           ;; Otherwise, if the ident: appeared with anything other than , or {
-           ;; before it, it can't be part of a struct initializer and therefore
-           ;; must be denoting a type.
-           (t nil)))))
-
-       ;; An operator-like character after a string is indeed an operator
-       ((and (equal token 'ambiguous-operator)
-             (member (rust-syntax-class-before-point) '(5 7 15))) t)
-
-       ;; A colon that has something other than an identifier before it is a
-       ;; type ascription
-       ((equal token 'colon) nil)
-
-       ;; A :: introduces a type (or module, but not an expression in any case)
-       ((rust-looking-back-str "::") nil)
-
-       ((rust-looking-back-str ":")
-        (backward-char)
-        (rust-is-in-expression-context 'colon))
-
-       ;; A -> introduces a type
-       ((rust-looking-back-str "->") nil)
-
-       ;; If we are up against the beginning of a list, or after a comma inside
-       ;; of one, back up out of it and check what the list itself is
-       ((or
-         (equal 4 (rust-syntax-class-before-point))
-         (rust-looking-back-str ","))
-        (condition-case nil
-            (progn
-              (backward-up-list)
-              (rust-is-in-expression-context 'open-brace))
-          (scan-error nil)))
-
-       ;; A => introduces an expression
-       ((rust-looking-back-str "=>") t)
-
-       ;; A == introduces an expression
-       ((rust-looking-back-str "==") t)
-
-       ;; These operators can introduce expressions or types
-       ((looking-back "[-+=!?&*]" (1- (point)))
-        (backward-char)
-        (rust-is-in-expression-context 'ambiguous-operator))
-
-       ;; These operators always introduce expressions.  (Note that if this
-       ;; regexp finds a < it must not be an angle bracket, or it'd
-       ;; have been caught in the syntax-class check above instead of this.)
-       ((looking-back rust-re-pre-expression-operators (1- (point))) t)))))
-
-(defun rust-is-lt-char-operator ()
-  "Return non-nil if the `<' sign just after point is an operator.
-Otherwise, if it is an opening angle bracket, then return nil."
-  (let ((case-fold-search nil))
-    (save-excursion
-      (rust-rewind-irrelevant)
-      ;; We are now just after the character syntactically before the <.
-      (cond
-
-       ;; If we are looking back at a < that is not an angle bracket (but not
-       ;; two of them) then this is the second < in a bit shift operator
-       ((and (rust-looking-back-str "<")
-             (not (equal 4 (rust-syntax-class-before-point)))
-             (not (rust-looking-back-str "<<"))))
-
-       ;; On the other hand, if we are after a closing paren/brace/bracket it
-       ;; can only be an operator, not an angle bracket.  Likewise, if we are
-       ;; after a string it's an operator.  (The string case could actually be
-       ;; valid in rust for character literals.)
-       ((member (rust-syntax-class-before-point) '(5 7 15)) t)
-
-       ;; If we are looking back at an operator, we know that we are at
-       ;; the beginning of an expression, and thus it has to be an angle
-       ;; bracket (starting a "<Type as Trait>::" construct.)
-       ((looking-back rust-re-pre-expression-operators (1- (point))) nil)
-
-       ;; If we are looking back at a keyword, it's an angle bracket
-       ;; unless that keyword is "self", "true" or "false"
-       ((rust-looking-back-symbols rust-keywords)
-        (rust-looking-back-symbols '("self" "true" "false")))
-
-       ((rust-looking-back-str "?")
-        (rust-is-in-expression-context 'ambiguous-operator))
-
-       ;; If we're looking back at an identifier, this depends on whether
-       ;; the identifier is part of an expression or a type
-       ((rust-looking-back-ident)
-        (backward-sexp)
-        (or
-         ;; The special types can't take type param lists, so a < after one is
-         ;; always an operator
-         (looking-at rust-re-special-types)
-
-         (rust-is-in-expression-context 'ident)))
-
-       ;; Otherwise, assume it's an angle bracket
-       ))))
-
-(defun rust-electric-pair-inhibit-predicate-wrap (char)
-  "Prevent \"matching\" with a `>' when CHAR is the less-than operator.
-This wraps the default defined by `electric-pair-inhibit-predicate'."
-  (or
-   (when (= ?< char)
-     (save-excursion
-       (backward-char)
-       (rust-is-lt-char-operator)))
-   (funcall (default-value 'electric-pair-inhibit-predicate) char)))
-
-(defun rust-electric-pair-skip-self (char)
-  "Skip CHAR instead of inserting a second closing character.
-This is added to the default skips defined by `electric-pair-skip-self'."
-  (= ?> char))
-
-(defun rust-ordinary-lt-gt-p ()
-  "Test whether the `<' or `>' at point is an ordinary operator of some kind.
-
-This returns t if the `<' or `>' is an ordinary operator (like
-less-than) or part of one (like `->'); and nil if the character
-should be considered a paired angle bracket."
-  (cond
-   ;; If matching is turned off suppress all of them
-   ((not rust-match-angle-brackets) t)
-
-   ;; This is a cheap check so we do it early.
-   ;; Don't treat the > in -> or => as an angle bracket
-   ((and (= (following-char) ?>) (memq (preceding-char) '(?- ?=))) t)
-
-   ;; We don't take < or > in strings or comments to be angle brackets
-   ((rust-in-str-or-cmnt) t)
-
-   ;; Inside a macro we don't really know the syntax.  Any < or > may be an
-   ;; angle bracket or it may not.  But we know that the other braces have
-   ;; to balance regardless of the < and >, so if we don't treat any < or >
-   ;; as angle brackets it won't mess up any paren balancing.
-   ((rust-in-macro) t)
-
-   ((= (following-char) ?<)
-    (rust-is-lt-char-operator))
-
-   ;; Since rust-ordinary-lt-gt-p is called only when either < or > are at the 
point,
-   ;; we know that the following char must be > in the clauses below.
-
-   ;; If we are at top level and not in any list, it can't be a closing
-   ;; angle bracket
-   ((>= 0 (rust-paren-level)) t)
-
-   ;; Otherwise, treat the > as a closing angle bracket if it would
-   ;; match an opening one
-   ((save-excursion
-      (backward-up-list)
-      (/= (following-char) ?<)))))
-
-(defun rust-mode-syntactic-face-function (state)
-  "Return face that distinguishes doc and normal comments in given syntax 
STATE."
-  (if (nth 3 state)
-      'font-lock-string-face
-    (save-excursion
-      (goto-char (nth 8 state))
-      (if (looking-at "/\\([*][*!][^*!]\\|/[/!][^/!]\\)")
-          'font-lock-doc-face
-        'font-lock-comment-face))))
-
-(eval-and-compile
-  (defconst rust--char-literal-rx
-    (rx (seq
-         (group "'")
-         (or
-          (seq
-           "\\"
-           (or
-            (: "u{" (** 1 6 xdigit) "}")
-            (: "x" (= 2 xdigit))
-            (any "'nrt0\"\\")))
-          (not (any "'\\")))
-         (group "'")))
-    "A regular expression matching a character literal."))
-
-(defun rust--syntax-propertize-raw-string (str-start end)
-  "A helper for rust-syntax-propertize.
-
-This will apply the appropriate string syntax to the character
-from the STR-START up to the end of the raw string, or to END,
-whichever comes first."
-  (when (save-excursion
-          (goto-char str-start)
-          (looking-at "r\\(#*\\)\\(\"\\)"))
-    ;; In a raw string, so try to find the end.
-    (let ((hashes (match-string 1)))
-      ;; Match \ characters at the end of the string to suppress
-      ;; their normal character-quote syntax.
-      (when (re-search-forward (concat "\\(\\\\*\\)\\(\"" hashes "\\)") end t)
-        (put-text-property (match-beginning 1) (match-end 1)
-                           'syntax-table (string-to-syntax "_"))
-        (put-text-property (1- (match-end 2)) (match-end 2)
-                           'syntax-table (string-to-syntax "|"))
-        (goto-char (match-end 0))))))
-
-;;; Syntax Propertize
-
-(defun rust-syntax-propertize (start end)
-  "A `syntax-propertize-function' to apply properties from START to END."
-  (goto-char start)
-  (when-let ((str-start (rust-in-str-or-cmnt)))
-    (rust--syntax-propertize-raw-string str-start end))
-  (funcall
-   (syntax-propertize-rules
-    ;; Character literals.
-    (rust--char-literal-rx (1 "\"") (2 "\""))
-    ;; Raw strings.
-    ("\\(r\\)#*\""
-     (0 (ignore
-         (goto-char (match-end 0))
-         (unless (save-excursion (nth 8 (syntax-ppss (match-beginning 0))))
-           (put-text-property (match-beginning 1) (match-end 1)
-                              'syntax-table (string-to-syntax "|"))
-           (rust--syntax-propertize-raw-string (match-beginning 0) end)))))
-    ("[<>]"
-     (0 (ignore
-         (when (save-match-data
-                 (save-excursion
-                   (goto-char (match-beginning 0))
-                   (rust-ordinary-lt-gt-p)))
-           (put-text-property (match-beginning 0) (match-end 0)
-                              'syntax-table (string-to-syntax "."))
-           (goto-char (match-end 0)))))))
-   (point) end))
-
-(defun rust-fill-prefix-for-comment-start (line-start)
-  "Determine what to use for `fill-prefix' based on the text at LINE-START."
-  (let ((result
-         ;; Replace /* with same number of spaces
-         (replace-regexp-in-string
-          "\\(?:/\\*+?\\)[!*]?"
-          (lambda (s)
-            ;; We want the * to line up with the first * of the
-            ;; comment start
-            (let ((offset (if (eq t
-                                  (compare-strings "/*" nil nil
-                                                   s
-                                                   (- (length s) 2)
-                                                   (length s)))
-                              1 2)))
-              (concat (make-string (- (length s) offset)
-                                   ?\x20) "*")))
-          line-start)))
-    ;; Make sure we've got at least one space at the end
-    (if (not (= (aref result (- (length result) 1)) ?\x20))
-        (setq result (concat result " ")))
-    result))
-
-(defun rust-in-comment-paragraph (body)
-  ;; We might move the point to fill the next comment, but we don't want it
-  ;; seeming to jump around on the user
-  (save-excursion
-    ;; If we're outside of a comment, with only whitespace and then a comment
-    ;; in front, jump to the comment and prepare to fill it.
-    (when (not (nth 4 (syntax-ppss)))
-      (beginning-of-line)
-      (when (looking-at (concat "[[:space:]\n]*" comment-start-skip))
-        (goto-char (match-end 0))))
-
-    ;; We need this when we're moving the point around and then checking syntax
-    ;; while doing paragraph fills, because the cache it uses isn't always
-    ;; invalidated during this.
-    (syntax-ppss-flush-cache 1)
-    ;; If we're at the beginning of a comment paragraph with nothing but
-    ;; whitespace til the next line, jump to the next line so that we use the
-    ;; existing prefix to figure out what the new prefix should be, rather than
-    ;; inferring it from the comment start.
-    (let ((next-bol (line-beginning-position 2)))
-      (while (save-excursion
-               (end-of-line)
-               (syntax-ppss-flush-cache 1)
-               (and (nth 4 (syntax-ppss))
-                    (save-excursion
-                      (beginning-of-line)
-                      (looking-at paragraph-start))
-                    (looking-at "[[:space:]]*$")
-                    (nth 4 (syntax-ppss next-bol))))
-        (goto-char next-bol)))
-
-    (syntax-ppss-flush-cache 1)
-    ;; If we're on the last line of a multiline-style comment that started
-    ;; above, back up one line so we don't mistake the * of the */ that ends
-    ;; the comment for a prefix.
-    (when (save-excursion
-            (and (nth 4 (syntax-ppss (line-beginning-position 1)))
-                 (looking-at "[[:space:]]*\\*/")))
-      (goto-char (line-end-position 0)))
-    (funcall body)))
-
-(defun rust-with-comment-fill-prefix (body)
-  (let*
-      ((line-string (buffer-substring-no-properties
-                     (line-beginning-position) (line-end-position)))
-       (line-comment-start
-        (when (nth 4 (syntax-ppss))
-          (cond
-           ;; If we're inside the comment and see a * prefix, use it
-           ((string-match "^\\([[:space:]]*\\*+[[:space:]]*\\)"
-                          line-string)
-            (match-string 1 line-string))
-           ;; If we're at the start of a comment, figure out what prefix
-           ;; to use for the subsequent lines after it
-           ((string-match (concat "[[:space:]]*" comment-start-skip) 
line-string)
-            (rust-fill-prefix-for-comment-start
-             (match-string 0 line-string))))))
-       (fill-prefix
-        (or line-comment-start
-            fill-prefix)))
-    (funcall body)))
-
-(defun rust-find-fill-prefix ()
-  (rust-in-comment-paragraph
-   (lambda ()
-     (rust-with-comment-fill-prefix
-      (lambda ()
-        fill-prefix)))))
-
-(defun rust-fill-paragraph (&rest args)
-  "Special wrapping for `fill-paragraph'.
-This handles multi-line comments with a * prefix on each line."
-  (rust-in-comment-paragraph
-   (lambda ()
-     (rust-with-comment-fill-prefix
-      (lambda ()
-        (let
-            ((fill-paragraph-function
-              (if (not (eq fill-paragraph-function #'rust-fill-paragraph))
-                  fill-paragraph-function))
-             (fill-paragraph-handle-comment t))
-          (apply #'fill-paragraph args)
-          t))))))
-
-(defun rust-do-auto-fill (&rest args)
-  "Special wrapping for `do-auto-fill'.
-This handles multi-line comments with a * prefix on each line."
-  (rust-with-comment-fill-prefix
-   (lambda ()
-     (apply #'do-auto-fill args)
-     t)))
-
-(defun rust-fill-forward-paragraph (arg)
-  ;; This is to work around some funny behavior when a paragraph separator is
-  ;; at the very top of the file and there is a fill prefix.
-  (let ((fill-prefix nil)) (forward-paragraph arg)))
-
-(defun rust-comment-indent-new-line (&optional arg)
-  (rust-with-comment-fill-prefix
-   (lambda () (comment-indent-new-line arg))))
-
-;;; Defun Motions
-
-(defun rust-beginning-of-defun (&optional arg)
-  "Move backward to the beginning of the current defun.
-
-With ARG, move backward multiple defuns.  Negative ARG means
-move forward.
-
-This is written mainly to be used as `beginning-of-defun-function' for Rust.
-Don't move to the beginning of the line. `beginning-of-defun',
-which calls this, does that afterwards."
-  (interactive "p")
-  (let* ((arg (or arg 1))
-         (magnitude (abs arg))
-         (sign (if (< arg 0) -1 1)))
-    ;; If moving forward, don't find the defun we might currently be
-    ;; on.
-    (when (< sign 0)
-      (end-of-line))
-    (catch 'done
-      (dotimes (_ magnitude)
-        ;; Search until we find a match that is not in a string or comment.
-        (while (if (re-search-backward (concat "^\\(" rust-top-item-beg-re 
"\\)")
-                                       nil 'move sign)
-                   (rust-in-str-or-cmnt)
-                 ;; Did not find it.
-                 (throw 'done nil)))))
-    t))
-
-(defun rust-end-of-defun ()
-  "Move forward to the next end of defun.
-
-With argument, do it that many times.
-Negative argument -N means move back to Nth preceding end of defun.
-
-Assume that this is called after `beginning-of-defun'.  So point is
-at the beginning of the defun body.
-
-This is written mainly to be used as `end-of-defun-function' for Rust."
-  (interactive)
-  ;; Find the opening brace
-  (if (re-search-forward "[{]" nil t)
-      (progn
-        (goto-char (match-beginning 0))
-        ;; Go to the closing brace
-        (condition-case nil
-            (forward-sexp)
-          (scan-error
-           ;; The parentheses are unbalanced; instead of being unable
-           ;; to fontify, just jump to the end of the buffer
-           (goto-char (point-max)))))
-    ;; There is no opening brace, so consider the whole buffer to be one 
"defun"
-    (goto-char (point-max))))
-
-;;; _
-
-(defun rust-mode-reload ()
-  (interactive)
-  (unload-feature 'rust-mode)
-  (require 'rust-mode)
-  (rust-mode))
-
 (provide 'rust-mode)
 (require 'rust-utils)
 
diff --git a/rust-prog-mode.el b/rust-prog-mode.el
index f443da86f4..92b475a7e9 100644
--- a/rust-prog-mode.el
+++ b/rust-prog-mode.el
@@ -4,6 +4,1446 @@
 ;; rust-mode code deriving from prog-mode instead of rust-ts-mode
 
 ;;; Code:
+(require 'rust-common)
+
+(defvar electric-pair-inhibit-predicate)
+(defvar electric-pair-skip-self)
+(defvar electric-indent-chars)
+
+(defvar rust-prettify-symbols-alist
+  '(("&&" . ?∧) ("||" . ?∨)
+    ("<=" . ?≤)  (">=" . ?≥) ("!=" . ?≠)
+    ("INFINITY" . ?∞) ("->" . ?→) ("=>" . ?⇒))
+  "Alist of symbol prettifications used for `prettify-symbols-alist'.")
+
+(defcustom rust-indent-offset 4
+  "Indent Rust code by this number of spaces."
+  :type 'integer
+  :group 'rust-mode
+  :safe #'integerp)
+
+(defcustom rust-indent-method-chain nil
+  "Indent Rust method chains, aligned by the `.' operators."
+  :type 'boolean
+  :group 'rust-mode
+  :safe #'booleanp)
+
+(defcustom rust-indent-where-clause nil
+  "Indent lines starting with the `where' keyword following a function or 
trait.
+When nil, `where' will be aligned with `fn' or `trait'."
+  :type 'boolean
+  :group 'rust-mode
+  :safe #'booleanp)
+
+(defcustom rust-match-angle-brackets t
+  "Whether to enable angle bracket (`<' and `>') matching where appropriate."
+  :type 'boolean
+  :safe #'booleanp
+  :group 'rust-mode)
+
+(defcustom rust-indent-return-type-to-arguments t
+  "Indent a line starting with the `->' (RArrow) following a function, aligning
+to the function arguments.  When nil, `->' will be indented one level."
+  :type 'boolean
+  :group 'rust-mode
+  :safe #'booleanp)
+
+(defface rust-unsafe
+  '((t :inherit font-lock-warning-face))
+  "Face for the `unsafe' keyword."
+  :group 'rust-mode)
+
+(defface rust-question-mark
+  '((t :weight bold :inherit font-lock-builtin-face))
+  "Face for the question mark operator."
+  :group 'rust-mode)
+
+(defface rust-ampersand-face
+  '((t :inherit default))
+  "Face for the ampersand reference mark."
+  :group 'rust-mode)
+
+(defface rust-builtin-formatting-macro
+  '((t :inherit font-lock-builtin-face))
+  "Face for builtin formatting macros (print! &c.)."
+  :group 'rust-mode)
+
+(defface rust-string-interpolation
+  '((t :slant italic :inherit font-lock-string-face))
+  "Face for interpolating braces in builtin formatting macro strings."
+  :group 'rust-mode)
+
+;;; Syntax
+
+(defun rust-re-word (inner) (concat "\\<" inner "\\>"))
+(defun rust-re-grab (inner) (concat "\\(" inner "\\)"))
+(defun rust-re-shy (inner) (concat "\\(?:" inner "\\)"))
+
+(defconst rust-re-ident 
"[[:word:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*")
+(defconst rust-re-lc-ident 
"[[:lower:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*")
+(defconst rust-re-uc-ident "[[:upper:]][[:word:][:multibyte:]_[:digit:]]*")
+(defvar rust-re-vis
+  ;; pub | pub ( crate ) | pub ( self ) | pub ( super ) | pub ( in SimplePath )
+  (concat
+   "pub"
+   (rust-re-shy
+    (concat
+     "[[:space:]]*([[:space:]]*"
+     (rust-re-shy
+      (concat "crate" "\\|"
+              "\\(?:s\\(?:elf\\|uper\\)\\)" "\\|"
+              ;;   in SimplePath
+              (rust-re-shy
+               (concat
+                "in[[:space:]]+"
+                rust-re-ident
+                (rust-re-shy (concat "::" rust-re-ident)) "*"))))
+     "[[:space:]]*)"))
+   "?"))
+(defconst rust-re-unsafe "unsafe")
+(defconst rust-re-extern "extern")
+(defconst rust-re-async-or-const "async\\|const")
+(defconst rust-re-generic
+  (concat "<[[:space:]]*'" rust-re-ident "[[:space:]]*>"))
+(defconst rust-re-union
+  (rx-to-string
+   `(seq
+     (or space line-start)
+     (group symbol-start "union" symbol-end)
+     (+ space) (regexp ,rust-re-ident))))
+
+(defun rust-re-item-def (itype)
+  (concat (rust-re-word itype)
+          (rust-re-shy rust-re-generic) "?"
+          "[[:space:]]+" (rust-re-grab rust-re-ident)))
+
+;; TODO some of this does only make sense for `fn' (unsafe, extern...)
+;; and not other items
+(defun rust-re-item-def-imenu (itype)
+  (concat "^[[:space:]]*"
+          (rust-re-shy (concat rust-re-vis "[[:space:]]+")) "?"
+          (rust-re-shy (concat (rust-re-word "default") "[[:space:]]+")) "?"
+          (rust-re-shy (concat (rust-re-shy rust-re-async-or-const) 
"[[:space:]]+")) "?"
+          (rust-re-shy (concat (rust-re-word rust-re-unsafe) "[[:space:]]+")) 
"?"
+          (rust-re-shy (concat (rust-re-word rust-re-extern) "[[:space:]]+"
+                               (rust-re-shy "\"[^\"]+\"[[:space:]]+") "?")) "?"
+                               (rust-re-item-def itype)))
+
+(defvar rust-imenu-generic-expression
+  (append (mapcar #'(lambda (x)
+                      (list (capitalize x) (rust-re-item-def-imenu x) 1))
+                  '("enum" "struct" "union" "type" "mod" "fn" "trait" "impl"))
+          `(("Macro" ,(rust-re-item-def-imenu "macro_rules!") 1)))
+  "Value for `imenu-generic-expression' in Rust mode.
+
+Create a hierarchical index of the item definitions in a Rust file.
+
+Imenu will show all the enums, structs, etc. in their own subheading.
+Use idomenu (imenu with `ido-mode') for best mileage.")
+
+;;; Prettify
+
+(defun rust--prettify-symbols-compose-p (start end match)
+  "Return true iff the symbol MATCH should be composed.
+See `prettify-symbols-compose-predicate'."
+  (and (fboundp 'prettify-symbols-default-compose-p)
+       (prettify-symbols-default-compose-p start end match)
+       ;; Make sure || is not a closure with 0 arguments and && is not
+       ;; a double reference.
+       (pcase match
+         ("||" (not (save-excursion
+                      (goto-char start)
+                      (looking-back "\\(?:\\<move\\|[[({:=,;]\\) *"
+                                    (line-beginning-position)))))
+         ("&&" (char-equal (char-after end) ?\s))
+         (_ t))))
+
+(defvar rust-top-item-beg-re
+  (concat "\\s-*\\(?:priv\\|pub\\)?\\s-*"
+          ;; TODO some of this does only make sense for `fn' (unsafe, 
extern...)
+          ;; and not other items
+          (rust-re-shy (concat (rust-re-shy rust-re-vis) "[[:space:]]+")) "?"
+          (rust-re-shy (concat (rust-re-shy rust-re-async-or-const) 
"[[:space:]]+")) "?"
+          (rust-re-shy (concat (rust-re-shy rust-re-unsafe) "[[:space:]]+")) 
"?"
+          (regexp-opt
+           '("enum" "struct" "union" "type" "mod" "fn" "static" "impl"
+             "extern" "trait" "async"))
+          "\\_>")
+  "Start of a Rust item.")
+
+(defconst rust-re-type-or-constructor
+  (rx symbol-start
+      (group upper (0+ (any word nonascii digit "_")))
+      symbol-end))
+
+(defconst rust-keywords
+  '("as" "async" "await"
+    "box" "break"
+    "const" "continue" "crate"
+    "do" "dyn"
+    "else" "enum" "extern" "existential"
+    "false" "fn" "for"
+    "if" "impl" "in"
+    "let" "loop"
+    "match" "mod" "move" "mut"
+    "priv" "pub"
+    "ref" "return"
+    "self" "static" "struct" "super"
+    "true" "trait" "type" "try"
+    "use"
+    "virtual"
+    "where" "while"
+    "yield")
+  "Font-locking definitions and helpers.")
+
+(defconst rust-special-types
+  '("u8" "i8"
+    "u16" "i16"
+    "u32" "i32"
+    "u64" "i64"
+    "u128" "i128"
+
+    "f32" "f64"
+    "isize" "usize"
+    "bool"
+    "str" "char"))
+
+(defconst rust-expression-introducers
+  '("if" "while" "match" "return" "box" "in")
+  "List of Rust keywords that are always followed by expressions.")
+
+(defconst rust-number-with-type
+  (eval-when-compile
+    (concat
+     
"\\_<\\(?:0[box]?\\|[1-9]\\)[[:digit:]a-fA-F_.]*\\(?:[eE][+-]?[[:digit:]_]\\)?"
+     (regexp-opt '("u8" "i8" "u16" "i16" "u32" "i32" "u64" "i64"
+                   "u128" "i128" "usize" "isize" "f32" "f64")
+                 t)
+     "\\_>"))
+  "Regular expression matching a number with a type suffix.")
+
+(defvar rust-builtin-formatting-macros
+  '("eprint"
+    "eprintln"
+    "format"
+    "print"
+    "println")
+  "List of builtin Rust macros for string formatting.
+This is used by `rust-font-lock-keywords'.
+\(`write!' is handled separately).")
+
+(defvar rust-formatting-macro-opening-re
+  "[[:space:]\n]*[({[][[:space:]\n]*"
+  "Regular expression to match the opening delimiter of a Rust formatting 
macro.")
+
+(defvar rust-start-of-string-re
+  "\\(?:r#*\\)?\""
+  "Regular expression to match the start of a Rust raw string.")
+
+(defun rust-path-font-lock-matcher (re-ident)
+  "Match occurrences of RE-IDENT followed by a double-colon.
+Examples include to match names like \"foo::\" or \"Foo::\".
+Does not match type annotations of the form \"foo::<\"."
+  `(lambda (limit)
+     (catch 'rust-path-font-lock-matcher
+       (while t
+         (let* ((symbol-then-colons (rx-to-string '(seq (group (regexp 
,re-ident)) "::")))
+                (match (re-search-forward symbol-then-colons limit t)))
+           (cond
+            ;; If we didn't find a match, there are no more occurrences
+            ;; of foo::, so return.
+            ((null match) (throw 'rust-path-font-lock-matcher nil))
+            ;; If this isn't a type annotation foo::<, we've found a
+            ;; match, so a return it!
+            ((not (looking-at (rx (0+ space) "<")))
+             (throw 'rust-path-font-lock-matcher match))))))))
+
+(defvar rust-font-lock-keywords
+  (append
+   `(
+     ;; Keywords proper
+     (,(regexp-opt rust-keywords 'symbols) . font-lock-keyword-face)
+
+     ;; Contextual keywords
+     ("\\_<\\(default\\)[[:space:]]+fn\\_>" 1 font-lock-keyword-face)
+     (,rust-re-union 1 font-lock-keyword-face)
+
+     ;; Special types
+     (,(regexp-opt rust-special-types 'symbols) . font-lock-type-face)
+
+     ;; The unsafe keyword
+     ("\\_<unsafe\\_>" . 'rust-unsafe)
+
+     ;; Attributes like `#[bar(baz)]` or `#![bar(baz)]` or `#[bar = "baz"]`
+     (,(rust-re-grab (concat "#\\!?\\[" rust-re-ident "[^]]*\\]"))
+      1 font-lock-preprocessor-face keep)
+
+     ;; Builtin formatting macros
+     (,(concat (rust-re-grab
+                (concat (rust-re-word (regexp-opt 
rust-builtin-formatting-macros))
+                        "!"))
+               rust-formatting-macro-opening-re
+               "\\(?:" rust-start-of-string-re "\\)?")
+      (1 'rust-builtin-formatting-macro)
+      (rust-string-interpolation-matcher
+       (rust-end-of-string)
+       nil
+       (0 'rust-string-interpolation t nil)))
+
+     ;; write! macro
+     (,(concat (rust-re-grab (concat (rust-re-word "write\\(ln\\)?") "!"))
+               rust-formatting-macro-opening-re
+               "[[:space:]]*[^\"]+,[[:space:]]*"
+               rust-start-of-string-re)
+      (1 'rust-builtin-formatting-macro)
+      (rust-string-interpolation-matcher
+       (rust-end-of-string)
+       nil
+       (0 'rust-string-interpolation t nil)))
+
+     ;; Syntax extension invocations like `foo!`, highlight including the !
+     (,(concat (rust-re-grab (concat rust-re-ident "!")) "[({[:space:][]")
+      1 font-lock-preprocessor-face)
+
+     ;; Field names like `foo:`, highlight excluding the :
+     (,(concat (rust-re-grab rust-re-ident) "[[:space:]]*:[^:]")
+      1 font-lock-variable-name-face)
+
+     ;; CamelCase Means Type Or Constructor
+     (,rust-re-type-or-constructor 1 font-lock-type-face)
+
+     ;; Type-inferred binding
+     (,(concat 
"\\_<\\(?:let\\s-+ref\\|let\\|ref\\|for\\)\\s-+\\(?:mut\\s-+\\)?"
+               (rust-re-grab rust-re-ident)
+               "\\_>")
+      1 font-lock-variable-name-face)
+
+     ;; Type names like `Foo::`, highlight excluding the ::
+     (,(rust-path-font-lock-matcher rust-re-uc-ident) 1 font-lock-type-face)
+
+     ;; Module names like `foo::`, highlight excluding the ::
+     (,(rust-path-font-lock-matcher rust-re-lc-ident) 1 
font-lock-constant-face)
+
+     ;; Lifetimes like `'foo`
+     (,(concat "'" (rust-re-grab rust-re-ident) "[^']") 1 
font-lock-variable-name-face)
+
+     ;; Question mark operator
+     ("\\?" . 'rust-question-mark)
+     ("\\(&+\\)\\(?:'\\(?:\\<\\|_\\)\\|\\<\\|[[({:*_|]\\)"
+      1 'rust-ampersand-face)
+     ;; Numbers with type suffix
+     (,rust-number-with-type 1 font-lock-type-face)
+     )
+
+   ;; Ensure we highlight `Foo` in `struct Foo` as a type.
+   (mapcar #'(lambda (x)
+               (list (rust-re-item-def (car x))
+                     1 (cdr x)))
+           '(("enum" . font-lock-type-face)
+             ("struct" . font-lock-type-face)
+             ("union" . font-lock-type-face)
+             ("type" . font-lock-type-face)
+             ("mod" . font-lock-constant-face)
+             ("use" . font-lock-constant-face)
+             ("fn" . font-lock-function-name-face)))))
+
+(defun rust-end-of-string ()
+  "Skip to the end of the current string."
+  (save-excursion
+    (skip-syntax-forward "^\"|")
+    (skip-syntax-forward "\"|")
+    (point)))
+
+(defun rust-looking-back-str (str)
+  "Return non-nil if there's a match on the text before point and STR.
+Like `looking-back' but for fixed strings rather than regexps (so
+that it's not so slow)."
+  (let ((len (length str)))
+    (and (> (point) len)
+         (equal str (buffer-substring-no-properties (- (point) len) 
(point))))))
+
+(defun rust-looking-back-symbols (symbols)
+  "Return non-nil if the point is after a member of SYMBOLS.
+SYMBOLS is a list of strings that represent the respective
+symbols."
+  (save-excursion
+    (let* ((pt-orig (point))
+           (beg-of-symbol (progn (forward-thing 'symbol -1) (point)))
+           (end-of-symbol (progn (forward-thing 'symbol 1) (point))))
+      (and
+       (= end-of-symbol pt-orig)
+       (member (buffer-substring-no-properties beg-of-symbol pt-orig)
+               symbols)))))
+
+(defun rust-looking-back-ident ()
+  "Non-nil if we are looking backwards at a valid rust identifier.
+If we are, regexp match 0 is the identifier."
+  (let ((outer-point (point)))
+    (save-excursion
+      (forward-thing 'symbol -1)
+      (and (looking-at rust-re-ident)
+           (eq (match-end 0) outer-point)))))
+
+(defun rust-looking-back-macro ()
+  "Non-nil if looking back at a potential macro name followed by a \"!\".
+If we are, regexp match 0 is the macro name."
+  (save-excursion
+    ;; Look past whitespace and line breaks.
+    ;; > is okay because we only use it for \n and \r, not "*/"
+    (skip-syntax-backward "->")
+    (when (eq (char-before) ?!)
+      (forward-char -1)
+      (skip-syntax-backward "->")
+      (when (rust-looking-back-ident)
+        (let ((ident (match-string 0)))
+          (not (member ident rust-expression-introducers)))))))
+
+(defun rust-looking-back-macro-rules ()
+  "Non-nil if looking back at \"macro_rules IDENT !\"."
+  (save-excursion
+    (skip-syntax-backward "->")
+    (let ((outer-point (point)))
+      (forward-thing 'symbol -2)
+      (and (looking-at (concat "macro_rules\\s-*!\\s-*" rust-re-ident))
+           (eq (match-end 0) outer-point)))))
+
+;;; Syntax definitions and helpers
+
+(defun rust-paren-level () (nth 0 (syntax-ppss)))
+(defun rust-in-str () (nth 3 (syntax-ppss)))
+(defun rust-in-str-or-cmnt () (nth 8 (syntax-ppss)))
+(defun rust-rewind-past-str-cmnt () (goto-char (nth 8 (syntax-ppss))))
+
+(defun rust-rewind-irrelevant ()
+  (let ((continue t))
+    (while continue
+      (let ((starting (point)))
+        (skip-chars-backward "[:space:]\n")
+        (when (rust-looking-back-str "*/")
+          (backward-char))
+        (when (rust-in-str-or-cmnt)
+          (rust-rewind-past-str-cmnt))
+        ;; Rewind until the point no longer moves
+        (setq continue (/= starting (point)))))))
+
+(defun rust-in-macro ()
+  "Return non-nil when point is within the scope of a macro.
+If we are, return the position of the opening bracket of the macro's 
arguments."
+  (let ((ppss (syntax-ppss)))
+    ;; If we're in a string or comment, we're definitely not on a token a macro
+    ;; will see.
+    (when (not (or (nth 3 ppss) (nth 4 ppss)))
+      ;; Walk outward to enclosing parens, looking for one preceded by "ident 
!"
+      ;; or "macro_rules! ident".
+      (let (result
+            (enclosing (reverse (nth 9 ppss))))
+        (save-excursion
+          (while enclosing
+            (goto-char (car enclosing))
+            (if (or (rust-looking-back-macro)
+                    (rust-looking-back-macro-rules))
+                (setq result (point) enclosing nil)
+              (setq enclosing (cdr enclosing)))))
+        result))))
+
+(defun rust-looking-at-where ()
+  "Return T when looking at the \"where\" keyword."
+  (and (looking-at-p "\\bwhere\\b")
+       (not (rust-in-str-or-cmnt))))
+
+(defun rust-rewind-to-where (&optional limit)
+  "Rewind the point to the closest occurrence of the \"where\" keyword.
+Return T iff a where-clause was found.  Does not rewind past
+LIMIT when passed, otherwise only stops at the beginning of the
+buffer."
+  (when (re-search-backward "\\bwhere\\b" limit t)
+    (if (rust-in-str-or-cmnt)
+        (rust-rewind-to-where limit)
+      t)))
+
+(defconst rust-re-pre-expression-operators "[-=!%&*/:<>[{(|.^;}]")
+
+(defconst rust-re-special-types (regexp-opt rust-special-types 'symbols))
+
+(defun rust-align-to-expr-after-brace ()
+  (save-excursion
+    (forward-char)
+    ;; We don't want to indent out to the open bracket if the
+    ;; open bracket ends the line
+    (when (not (looking-at "[[:blank:]]*\\(?://.*\\)?$"))
+      (when (looking-at "[[:space:]]")
+        (forward-word 1)
+        (backward-word 1))
+      (current-column))))
+
+(defun rust-rewind-to-beginning-of-current-level-expr ()
+  (let ((current-level (rust-paren-level)))
+    (back-to-indentation)
+    (when (looking-at "->")
+      (rust-rewind-irrelevant)
+      (back-to-indentation))
+    (while (> (rust-paren-level) current-level)
+      (backward-up-list)
+      (back-to-indentation))
+    ;; When we're in the where clause, skip over it.  First find out the start
+    ;; of the function and its paren level.
+    (let ((function-start nil) (function-level nil))
+      (save-excursion
+        (rust-beginning-of-defun)
+        (back-to-indentation)
+        ;; Avoid using multiple-value-bind
+        (setq function-start (point)
+              function-level (rust-paren-level)))
+      ;; On a where clause
+      (when (or (rust-looking-at-where)
+                ;; or in one of the following lines, e.g.
+                ;; where A: Eq
+                ;;       B: Hash <- on this line
+                (and (save-excursion
+                       (rust-rewind-to-where function-start))
+                     (= current-level function-level)))
+        (goto-char function-start)))))
+
+(defun rust-align-to-method-chain ()
+  (save-excursion
+    ;; for method-chain alignment to apply, we must be looking at
+    ;; another method call or field access or something like
+    ;; that. This avoids rather "eager" jumps in situations like:
+    ;;
+    ;; {
+    ;;     something.foo()
+    ;; <indent>
+    ;;
+    ;; Without this check, we would wind up with the cursor under the
+    ;; `.`. In an older version, I had the inverse of the current
+    ;; check, where we checked for situations that should NOT indent,
+    ;; vs checking for the one situation where we SHOULD. It should be
+    ;; clear that this is more robust, but also I find it mildly less
+    ;; annoying to have to press tab again to align to a method chain
+    ;; than to have an over-eager indent in all other cases which must
+    ;; be undone via tab.
+
+    (when (looking-at (concat "\s*\." rust-re-ident))
+      (forward-line -1)
+      (end-of-line)
+      ;; Keep going up (looking for a line that could contain a method chain)
+      ;; while we're in a comment or on a blank line. Stop when the paren
+      ;; level changes.
+      (let ((level (rust-paren-level)))
+        (while (and (or (rust-in-str-or-cmnt)
+                        ;; Only whitespace (or nothing) from the beginning to
+                        ;; the end of the line.
+                        (looking-back "^\s*" (line-beginning-position)))
+                    (= (rust-paren-level) level))
+          (forward-line -1)
+          (end-of-line)))
+
+      (let
+          ;; skip-dot-identifier is used to position the point at the
+          ;; `.` when looking at something like
+          ;;
+          ;;      foo.bar
+          ;;         ^   ^
+          ;;         |   |
+          ;;         |  position of point
+          ;;       returned offset
+          ;;
+          ((skip-dot-identifier
+            (lambda ()
+              (when (and (rust-looking-back-ident)
+                         (save-excursion
+                           (forward-thing 'symbol -1)
+                           (= ?. (char-before))))
+                (forward-thing 'symbol -1)
+                (backward-char)
+                (- (current-column) rust-indent-offset)))))
+        (cond
+         ;; foo.bar(...)
+         ((looking-back "[)?]" (1- (point)))
+          (backward-list 1)
+          (funcall skip-dot-identifier))
+
+         ;; foo.bar
+         (t (funcall skip-dot-identifier)))))))
+
+(defun rust-mode-indent-line ()
+  (interactive)
+  (let ((indent
+         (save-excursion
+           (back-to-indentation)
+           ;; Point is now at beginning of current line
+           (let* ((level (rust-paren-level))
+                  (baseline
+                   ;; Our "baseline" is one level out from the
+                   ;; indentation of the expression containing the
+                   ;; innermost enclosing opening bracket.  That way
+                   ;; if we are within a block that has a different
+                   ;; indentation than this mode would give it, we
+                   ;; still indent the inside of it correctly relative
+                   ;; to the outside.
+                   (if (= 0 level)
+                       0
+                     (or
+                      (when rust-indent-method-chain
+                        (rust-align-to-method-chain))
+                      (save-excursion
+                        (rust-rewind-irrelevant)
+                        (backward-up-list)
+                        (rust-rewind-to-beginning-of-current-level-expr)
+                        (+ (current-column) rust-indent-offset))))))
+             (cond
+              ;; Indent inside a non-raw string only if the previous line
+              ;; ends with a backslash that is inside the same string
+              ((nth 3 (syntax-ppss))
+               (let*
+                   ((string-begin-pos (nth 8 (syntax-ppss)))
+                    (end-of-prev-line-pos
+                     (and (not (rust--same-line-p (point) (point-min)))
+                          (line-end-position 0))))
+                 (when
+                     (and
+                      ;; If the string begins with an "r" it's a raw string and
+                      ;; we should not change the indentation
+                      (/= ?r (char-after string-begin-pos))
+
+                      ;; If we're on the first line this will be nil and the
+                      ;; rest does not apply
+                      end-of-prev-line-pos
+
+                      ;; The end of the previous line needs to be inside the
+                      ;; current string...
+                      (> end-of-prev-line-pos string-begin-pos)
+
+                      ;; ...and end with a backslash
+                      (= ?\\ (char-before end-of-prev-line-pos)))
+
+                   ;; Indent to the same level as the previous line, or the
+                   ;; start of the string if the previous line starts the 
string
+                   (if (rust--same-line-p end-of-prev-line-pos 
string-begin-pos)
+                       ;; The previous line is the start of the string.
+                       ;; If the backslash is the only character after the
+                       ;; string beginning, indent to the next indent
+                       ;; level.  Otherwise align with the start of the string.
+                       (if (> (- end-of-prev-line-pos string-begin-pos) 2)
+                           (save-excursion
+                             (goto-char (+ 1 string-begin-pos))
+                             (current-column))
+                         baseline)
+
+                     ;; The previous line is not the start of the string, so
+                     ;; match its indentation.
+                     (save-excursion
+                       (goto-char end-of-prev-line-pos)
+                       (back-to-indentation)
+                       (current-column))))))
+
+              ;; A function return type is indented to the corresponding
+              ;; function arguments, if -to-arguments is selected.
+              ((and rust-indent-return-type-to-arguments
+                    (looking-at "->"))
+               (save-excursion
+                 (backward-list)
+                 (or (rust-align-to-expr-after-brace)
+                     (+ baseline rust-indent-offset))))
+
+              ;; A closing brace is 1 level unindented
+              ((looking-at "[]})]") (- baseline rust-indent-offset))
+
+              ;; Doc comments in /** style with leading * indent to line up 
the *s
+              ((and (nth 4 (syntax-ppss)) (looking-at "*"))
+               (+ 1 baseline))
+
+              ;; When the user chose not to indent the start of the where
+              ;; clause, put it on the baseline.
+              ((and (not rust-indent-where-clause)
+                    (rust-looking-at-where))
+               baseline)
+
+              ;; If we're in any other token-tree / sexp, then:
+              (t
+               (or
+                ;; If we are inside a pair of braces, with something after the
+                ;; open brace on the same line and ending with a comma, treat
+                ;; it as fields and align them.
+                (when (> level 0)
+                  (save-excursion
+                    (rust-rewind-irrelevant)
+                    (backward-up-list)
+                    ;; Point is now at the beginning of the containing set of 
braces
+                    (rust-align-to-expr-after-brace)))
+
+                ;; When where-clauses are spread over multiple lines, clauses
+                ;; should be aligned on the type parameters.  In this case we
+                ;; take care of the second and following clauses (the ones
+                ;; that don't start with "where ")
+                (save-excursion
+                  ;; Find the start of the function, we'll use this to limit
+                  ;; our search for "where ".
+                  (let ((function-start nil) (function-level nil))
+                    (save-excursion
+                      ;; If we're already at the start of a function,
+                      ;; don't go back any farther.  We can easily do
+                      ;; this by moving to the end of the line first.
+                      (end-of-line)
+                      (rust-beginning-of-defun)
+                      (back-to-indentation)
+                      ;; Avoid using multiple-value-bind
+                      (setq function-start (point)
+                            function-level (rust-paren-level)))
+                    ;; When we're not on a line starting with "where ", but
+                    ;; still on a where-clause line, go to "where "
+                    (when (and
+                           (not (rust-looking-at-where))
+                           ;; We're looking at something like "F: ..."
+                           (looking-at (concat rust-re-ident ":"))
+                           ;; There is a "where " somewhere after the
+                           ;; start of the function.
+                           (rust-rewind-to-where function-start)
+                           ;; Make sure we're not inside the function
+                           ;; already (e.g. initializing a struct) by
+                           ;; checking we are the same level.
+                           (= function-level level))
+                      ;; skip over "where"
+                      (forward-char 5)
+                      ;; Unless "where" is at the end of the line
+                      (if (eolp)
+                          ;; in this case the type parameters bounds are just
+                          ;; indented once
+                          (+ baseline rust-indent-offset)
+                        ;; otherwise, skip over whitespace,
+                        (skip-chars-forward "[:space:]")
+                        ;; get the column of the type parameter and use that
+                        ;; as indentation offset
+                        (current-column)))))
+
+                (progn
+                  (back-to-indentation)
+                  ;; Point is now at the beginning of the current line
+                  (if (or
+                       ;; If this line begins with "else" or "{", stay on the
+                       ;; baseline as well (we are continuing an expression,
+                       ;; but the "else" or "{" should align with the beginning
+                       ;; of the expression it's in.)
+                       ;; Or, if this line starts a comment, stay on the
+                       ;; baseline as well.
+                       (looking-at "\\<else\\>\\|{\\|/[/*]")
+
+                       ;; If this is the start of a top-level item,
+                       ;; stay on the baseline.
+                       (looking-at rust-top-item-beg-re)
+
+                       (save-excursion
+                         (rust-rewind-irrelevant)
+                         ;; Point is now at the end of the previous line
+                         (or
+                          ;; If we are at the start of the buffer, no
+                          ;; indentation is needed, so stay at baseline...
+                          (= (point) 1)
+                          ;; ..or if the previous line ends with any of these:
+                          ;;     { ? : ( , ; [ }
+                          ;; then we are at the beginning of an
+                          ;; expression, so stay on the baseline...
+                          (looking-back "[(,:;[{}]\\|[^|]|" (- (point) 2))
+                          ;; or if the previous line is the end of an
+                          ;; attribute, stay at the baseline...
+                          (progn 
(rust-rewind-to-beginning-of-current-level-expr)
+                                 (looking-at "#")))))
+                      baseline
+
+                    ;; Otherwise, we are continuing the same expression from
+                    ;; the previous line, so add one additional indent level
+                    (+ baseline rust-indent-offset))))))))))
+
+    (when indent
+      ;; If we're at the beginning of the line (before or at the current
+      ;; indentation), jump with the indentation change.  Otherwise, save the
+      ;; excursion so that adding the indentations will leave us at the
+      ;; equivalent position within the line to where we were before.
+      (if (<= (current-column) (current-indentation))
+          (indent-line-to indent)
+        (save-excursion (indent-line-to indent))))))
+
+(defun rust--same-line-p (pos1 pos2)
+  "Return non-nil if POS1 and POS2 are on the same line."
+  (save-excursion (= (progn (goto-char pos1) (line-end-position))
+                     (progn (goto-char pos2) (line-end-position)))))
+
+;;; Font-locking definitions and helpers
+
+(defun rust-next-string-interpolation (limit)
+  "Search forward from point for next Rust interpolation marker before LIMIT.
+Set point to the end of the occurrence found, and return match beginning
+and end."
+  (catch 'match
+    (save-match-data
+      (save-excursion
+        (while (search-forward "{" limit t)
+          (if (eql (char-after (point)) ?{)
+              (forward-char)
+            (let ((start (match-beginning 0)))
+              ;; According to fmt_macros::Parser::next, an opening brace
+              ;; must be followed by an optional argument and/or format
+              ;; specifier, then a closing brace. A single closing brace
+              ;; without a corresponding unescaped opening brace is an
+              ;; error. We don't need to do anything special with
+              ;; arguments, specifiers, or errors, so we only search for
+              ;; the single closing brace.
+              (when (search-forward "}" limit t)
+                (throw 'match (list start (point)))))))))))
+
+(defun rust-string-interpolation-matcher (limit)
+  "Match next Rust interpolation marker before LIMIT and set match data if 
found.
+Returns nil if not within a Rust string."
+  (when-let (((rust-in-str))
+             (match (rust-next-string-interpolation limit)))
+    (set-match-data match)
+    (goto-char (cadr match))
+    match))
+
+(defun rust-syntax-class-before-point ()
+  (when (> (point) 1)
+    (syntax-class (syntax-after (1- (point))))))
+
+(defun rust-rewind-qualified-ident ()
+  (while (rust-looking-back-ident)
+    (backward-sexp)
+    (when (save-excursion (rust-rewind-irrelevant) (rust-looking-back-str 
"::"))
+      (rust-rewind-irrelevant)
+      (backward-char 2)
+      (rust-rewind-irrelevant))))
+
+(defun rust-rewind-type-param-list ()
+  (cond
+   ((and (rust-looking-back-str ">") (equal 5 
(rust-syntax-class-before-point)))
+    (backward-sexp)
+    (rust-rewind-irrelevant))
+
+   ;; We need to be able to back up past the Fn(args) -> RT form as well.  If
+   ;; we're looking back at this, we want to end up just after "Fn".
+   ((member (char-before) '(?\] ?\) ))
+    (let ((is-paren (rust-looking-back-str ")")))
+      (when-let ((dest (save-excursion
+                         (backward-sexp)
+                         (rust-rewind-irrelevant)
+                         (or
+                          (when (rust-looking-back-str "->")
+                            (backward-char 2)
+                            (rust-rewind-irrelevant)
+                            (when (rust-looking-back-str ")")
+                              (backward-sexp)
+                              (point)))
+                          (and is-paren (point))))))
+        (goto-char dest))))))
+
+(defun rust-rewind-to-decl-name ()
+  "Return the point at the beginning of the name in a declaration.
+I.e. if we are before an ident that is part of a declaration that
+can have a where clause, rewind back to just before the name of
+the subject of that where clause and return the new point.
+Otherwise return nil."
+  (let* ((ident-pos (point))
+         (newpos (save-excursion
+                   (rust-rewind-irrelevant)
+                   (rust-rewind-type-param-list)
+                   (cond
+                    ((rust-looking-back-symbols
+                      '("fn" "trait" "enum" "struct" "union" "impl" "type"))
+                     ident-pos)
+
+                    ((equal 5 (rust-syntax-class-before-point))
+                     (backward-sexp)
+                     (rust-rewind-to-decl-name))
+
+                    ((looking-back "[:,'+=]" (1- (point)))
+                     (backward-char)
+                     (rust-rewind-to-decl-name))
+
+                    ((rust-looking-back-str "->")
+                     (backward-char 2)
+                     (rust-rewind-to-decl-name))
+
+                    ((rust-looking-back-ident)
+                     (rust-rewind-qualified-ident)
+                     (rust-rewind-to-decl-name))))))
+    (when newpos (goto-char newpos))
+    newpos))
+
+(defun rust-is-in-expression-context (token)
+  "Return t if what comes right after the point is part of an
+expression (as opposed to starting a type) by looking at what
+comes before.  Takes a symbol that roughly indicates what is
+after the point.
+
+This function is used as part of `rust-is-lt-char-operator' as
+part of angle bracket matching, and is not intended to be used
+outside of this context."
+  (save-excursion
+    (let ((postchar (char-after)))
+      (rust-rewind-irrelevant)
+      ;; A type alias or ascription could have a type param list.  Skip 
backwards past it.
+      (when (member token '(ambiguous-operator open-brace))
+        (rust-rewind-type-param-list))
+      (cond
+
+       ;; Certain keywords always introduce expressions
+       ((rust-looking-back-symbols rust-expression-introducers) t)
+
+       ;; "as" introduces a type
+       ((rust-looking-back-symbols '("as")) nil)
+
+       ;; An open angle bracket never introduces expression context WITHIN the 
angle brackets
+       ((and (equal token 'open-brace) (equal postchar ?<)) nil)
+
+       ;; An ident! followed by an open brace is a macro invocation.  Consider
+       ;; it to be an expression.
+       ((and (equal token 'open-brace) (rust-looking-back-macro)) t)
+
+       ;; In a brace context a "]" introduces an expression.
+       ((and (eq token 'open-brace) (rust-looking-back-str "]")))
+
+       ;; An identifier is right after an ending paren, bracket, angle bracket
+       ;; or curly brace.  It's a type if the last sexp was a type.
+       ((and (equal token 'ident) (equal 5 (rust-syntax-class-before-point)))
+        (backward-sexp)
+        (rust-is-in-expression-context 'open-brace))
+
+       ;; If a "for" appears without a ; or { before it, it's part of an
+       ;; "impl X for y", so the y is a type.  Otherwise it's
+       ;; introducing a loop, so the y is an expression
+       ((and (equal token 'ident) (rust-looking-back-symbols '("for")))
+        (backward-sexp)
+        (rust-rewind-irrelevant)
+        (looking-back "[{;]" (1- (point))))
+
+       ((rust-looking-back-ident)
+        (rust-rewind-qualified-ident)
+        (rust-rewind-irrelevant)
+        (cond
+         ((equal token 'open-brace)
+          ;; We now know we have:
+          ;;   ident <maybe type params> [{([]
+          ;; where [{([] denotes either a {, ( or [.
+          ;; This character is bound as postchar.
+          (cond
+           ;; If postchar is a paren or square bracket, then if the
+           ;; brace is a type if the identifier is one
+           ((member postchar '(?\( ?\[ )) (rust-is-in-expression-context 
'ident))
+
+           ;; If postchar is a curly brace, the brace can only be a type if
+           ;; ident2 is the name of an enum, struct or trait being declared.
+           ;; Note that if there is a -> before the ident then the ident would
+           ;; be a type but the { is not.
+           ((equal ?{ postchar)
+            (not (and (rust-rewind-to-decl-name)
+                      (progn
+                        (rust-rewind-irrelevant)
+                        (rust-looking-back-symbols
+                         '("enum" "struct" "union" "trait" "type"))))))))
+
+         ((equal token 'ambiguous-operator)
+          (cond
+           ;; An ampersand after an ident has to be an operator rather
+           ;; than a & at the beginning of a ref type
+           ((equal postchar ?&) t)
+
+           ;; A : followed by a type then an = introduces an
+           ;; expression (unless it is part of a where clause of a
+           ;; "type" declaration)
+           ((and (equal postchar ?=)
+                 (looking-back "[^:]:" (- (point) 2))
+                 (not (save-excursion
+                        (and (rust-rewind-to-decl-name)
+                             (progn (rust-rewind-irrelevant)
+                                    (rust-looking-back-symbols '("type"))))))))
+
+           ;; "let ident =" introduces an expression--and so does "const" and 
"mut"
+           ((and (equal postchar ?=) (rust-looking-back-symbols '("let" 
"const" "mut"))) t)
+
+           ;; As a specific special case, see if this is the = in this 
situation:
+           ;;     enum EnumName<type params> { Ident =
+           ;; In this case, this is a c-like enum and despite Ident
+           ;; representing a type, what comes after the = is an expression
+           ((and
+             (> (rust-paren-level) 0)
+             (save-excursion
+               (backward-up-list)
+               (rust-rewind-irrelevant)
+               (rust-rewind-type-param-list)
+               (and
+                (rust-looking-back-ident)
+                (progn
+                  (rust-rewind-qualified-ident)
+                  (rust-rewind-irrelevant)
+                  (rust-looking-back-str "enum")))))
+            t)
+
+           ;; Otherwise the ambiguous operator is a type if the identifier is 
a type
+           ((rust-is-in-expression-context 'ident) t)))
+
+         ((equal token 'colon)
+          (cond
+           ;; If we see a ident: not inside any braces/parens, we're at top 
level.
+           ;; There are no allowed expressions after colons there, just types.
+           ((<= (rust-paren-level) 0) nil)
+
+           ;; We see ident: inside a list
+           ((looking-back "[{,]" (1- (point)))
+            (backward-up-list)
+
+            ;; If a : appears whose surrounding paren/brackets/braces are
+            ;; anything other than curly braces, it can't be a field
+            ;; initializer and must be denoting a type.
+            (when (looking-at "{")
+              (rust-rewind-irrelevant)
+              (rust-rewind-type-param-list)
+              (when (rust-looking-back-ident)
+                ;; We have a context that looks like this:
+                ;;    ident2 <maybe type params> { [maybe paren-balanced code 
ending in comma] ident1:
+                ;; the point is sitting just after ident2, and we trying to
+                ;; figure out if the colon introduces an expression or a type.
+                ;; The answer is that ident1 is a field name, and what comes
+                ;; after the colon is an expression, if ident2 is an
+                ;; expression.
+                (rust-rewind-qualified-ident)
+                (rust-is-in-expression-context 'ident))))
+
+           ;; Otherwise, if the ident: appeared with anything other than , or {
+           ;; before it, it can't be part of a struct initializer and therefore
+           ;; must be denoting a type.
+           (t nil)))))
+
+       ;; An operator-like character after a string is indeed an operator
+       ((and (equal token 'ambiguous-operator)
+             (member (rust-syntax-class-before-point) '(5 7 15))) t)
+
+       ;; A colon that has something other than an identifier before it is a
+       ;; type ascription
+       ((equal token 'colon) nil)
+
+       ;; A :: introduces a type (or module, but not an expression in any case)
+       ((rust-looking-back-str "::") nil)
+
+       ((rust-looking-back-str ":")
+        (backward-char)
+        (rust-is-in-expression-context 'colon))
+
+       ;; A -> introduces a type
+       ((rust-looking-back-str "->") nil)
+
+       ;; If we are up against the beginning of a list, or after a comma inside
+       ;; of one, back up out of it and check what the list itself is
+       ((or
+         (equal 4 (rust-syntax-class-before-point))
+         (rust-looking-back-str ","))
+        (condition-case nil
+            (progn
+              (backward-up-list)
+              (rust-is-in-expression-context 'open-brace))
+          (scan-error nil)))
+
+       ;; A => introduces an expression
+       ((rust-looking-back-str "=>") t)
+
+       ;; A == introduces an expression
+       ((rust-looking-back-str "==") t)
+
+       ;; These operators can introduce expressions or types
+       ((looking-back "[-+=!?&*]" (1- (point)))
+        (backward-char)
+        (rust-is-in-expression-context 'ambiguous-operator))
+
+       ;; These operators always introduce expressions.  (Note that if this
+       ;; regexp finds a < it must not be an angle bracket, or it'd
+       ;; have been caught in the syntax-class check above instead of this.)
+       ((looking-back rust-re-pre-expression-operators (1- (point))) t)))))
+
+(defun rust-is-lt-char-operator ()
+  "Return non-nil if the `<' sign just after point is an operator.
+Otherwise, if it is an opening angle bracket, then return nil."
+  (let ((case-fold-search nil))
+    (save-excursion
+      (rust-rewind-irrelevant)
+      ;; We are now just after the character syntactically before the <.
+      (cond
+
+       ;; If we are looking back at a < that is not an angle bracket (but not
+       ;; two of them) then this is the second < in a bit shift operator
+       ((and (rust-looking-back-str "<")
+             (not (equal 4 (rust-syntax-class-before-point)))
+             (not (rust-looking-back-str "<<"))))
+
+       ;; On the other hand, if we are after a closing paren/brace/bracket it
+       ;; can only be an operator, not an angle bracket.  Likewise, if we are
+       ;; after a string it's an operator.  (The string case could actually be
+       ;; valid in rust for character literals.)
+       ((member (rust-syntax-class-before-point) '(5 7 15)) t)
+
+       ;; If we are looking back at an operator, we know that we are at
+       ;; the beginning of an expression, and thus it has to be an angle
+       ;; bracket (starting a "<Type as Trait>::" construct.)
+       ((looking-back rust-re-pre-expression-operators (1- (point))) nil)
+
+       ;; If we are looking back at a keyword, it's an angle bracket
+       ;; unless that keyword is "self", "true" or "false"
+       ((rust-looking-back-symbols rust-keywords)
+        (rust-looking-back-symbols '("self" "true" "false")))
+
+       ((rust-looking-back-str "?")
+        (rust-is-in-expression-context 'ambiguous-operator))
+
+       ;; If we're looking back at an identifier, this depends on whether
+       ;; the identifier is part of an expression or a type
+       ((rust-looking-back-ident)
+        (backward-sexp)
+        (or
+         ;; The special types can't take type param lists, so a < after one is
+         ;; always an operator
+         (looking-at rust-re-special-types)
+
+         (rust-is-in-expression-context 'ident)))
+
+       ;; Otherwise, assume it's an angle bracket
+       ))))
+
+(defun rust-electric-pair-inhibit-predicate-wrap (char)
+  "Prevent \"matching\" with a `>' when CHAR is the less-than operator.
+This wraps the default defined by `electric-pair-inhibit-predicate'."
+  (or
+   (when (= ?< char)
+     (save-excursion
+       (backward-char)
+       (rust-is-lt-char-operator)))
+   (funcall (default-value 'electric-pair-inhibit-predicate) char)))
+
+(defun rust-electric-pair-skip-self (char)
+  "Skip CHAR instead of inserting a second closing character.
+This is added to the default skips defined by `electric-pair-skip-self'."
+  (= ?> char))
+
+(defun rust-ordinary-lt-gt-p ()
+  "Test whether the `<' or `>' at point is an ordinary operator of some kind.
+
+This returns t if the `<' or `>' is an ordinary operator (like
+less-than) or part of one (like `->'); and nil if the character
+should be considered a paired angle bracket."
+  (cond
+   ;; If matching is turned off suppress all of them
+   ((not rust-match-angle-brackets) t)
+
+   ;; This is a cheap check so we do it early.
+   ;; Don't treat the > in -> or => as an angle bracket
+   ((and (= (following-char) ?>) (memq (preceding-char) '(?- ?=))) t)
+
+   ;; We don't take < or > in strings or comments to be angle brackets
+   ((rust-in-str-or-cmnt) t)
+
+   ;; Inside a macro we don't really know the syntax.  Any < or > may be an
+   ;; angle bracket or it may not.  But we know that the other braces have
+   ;; to balance regardless of the < and >, so if we don't treat any < or >
+   ;; as angle brackets it won't mess up any paren balancing.
+   ((rust-in-macro) t)
+
+   ((= (following-char) ?<)
+    (rust-is-lt-char-operator))
+
+   ;; Since rust-ordinary-lt-gt-p is called only when either < or > are at the 
point,
+   ;; we know that the following char must be > in the clauses below.
+
+   ;; If we are at top level and not in any list, it can't be a closing
+   ;; angle bracket
+   ((>= 0 (rust-paren-level)) t)
+
+   ;; Otherwise, treat the > as a closing angle bracket if it would
+   ;; match an opening one
+   ((save-excursion
+      (backward-up-list)
+      (/= (following-char) ?<)))))
+
+(defun rust-mode-syntactic-face-function (state)
+  "Return face that distinguishes doc and normal comments in given syntax 
STATE."
+  (if (nth 3 state)
+      'font-lock-string-face
+    (save-excursion
+      (goto-char (nth 8 state))
+      (if (looking-at "/\\([*][*!][^*!]\\|/[/!][^/!]\\)")
+          'font-lock-doc-face
+        'font-lock-comment-face))))
+
+(eval-and-compile
+  (defconst rust--char-literal-rx
+    (rx (seq
+         (group "'")
+         (or
+          (seq
+           "\\"
+           (or
+            (: "u{" (** 1 6 xdigit) "}")
+            (: "x" (= 2 xdigit))
+            (any "'nrt0\"\\")))
+          (not (any "'\\")))
+         (group "'")))
+    "A regular expression matching a character literal."))
+
+(defun rust-fill-prefix-for-comment-start (line-start)
+  "Determine what to use for `fill-prefix' based on the text at LINE-START."
+  (let ((result
+         ;; Replace /* with same number of spaces
+         (replace-regexp-in-string
+          "\\(?:/\\*+?\\)[!*]?"
+          (lambda (s)
+            ;; We want the * to line up with the first * of the
+            ;; comment start
+            (let ((offset (if (eq t
+                                  (compare-strings "/*" nil nil
+                                                   s
+                                                   (- (length s) 2)
+                                                   (length s)))
+                              1 2)))
+              (concat (make-string (- (length s) offset)
+                                   ?\x20) "*")))
+          line-start)))
+    ;; Make sure we've got at least one space at the end
+    (if (not (= (aref result (- (length result) 1)) ?\x20))
+        (setq result (concat result " ")))
+    result))
+
+(defun rust-in-comment-paragraph (body)
+  ;; We might move the point to fill the next comment, but we don't want it
+  ;; seeming to jump around on the user
+  (save-excursion
+    ;; If we're outside of a comment, with only whitespace and then a comment
+    ;; in front, jump to the comment and prepare to fill it.
+    (when (not (nth 4 (syntax-ppss)))
+      (beginning-of-line)
+      (when (looking-at (concat "[[:space:]\n]*" comment-start-skip))
+        (goto-char (match-end 0))))
+
+    ;; We need this when we're moving the point around and then checking syntax
+    ;; while doing paragraph fills, because the cache it uses isn't always
+    ;; invalidated during this.
+    (syntax-ppss-flush-cache 1)
+    ;; If we're at the beginning of a comment paragraph with nothing but
+    ;; whitespace til the next line, jump to the next line so that we use the
+    ;; existing prefix to figure out what the new prefix should be, rather than
+    ;; inferring it from the comment start.
+    (let ((next-bol (line-beginning-position 2)))
+      (while (save-excursion
+               (end-of-line)
+               (syntax-ppss-flush-cache 1)
+               (and (nth 4 (syntax-ppss))
+                    (save-excursion
+                      (beginning-of-line)
+                      (looking-at paragraph-start))
+                    (looking-at "[[:space:]]*$")
+                    (nth 4 (syntax-ppss next-bol))))
+        (goto-char next-bol)))
+
+    (syntax-ppss-flush-cache 1)
+    ;; If we're on the last line of a multiline-style comment that started
+    ;; above, back up one line so we don't mistake the * of the */ that ends
+    ;; the comment for a prefix.
+    (when (save-excursion
+            (and (nth 4 (syntax-ppss (line-beginning-position 1)))
+                 (looking-at "[[:space:]]*\\*/")))
+      (goto-char (line-end-position 0)))
+    (funcall body)))
+
+(defun rust-with-comment-fill-prefix (body)
+  (let*
+      ((line-string (buffer-substring-no-properties
+                     (line-beginning-position) (line-end-position)))
+       (line-comment-start
+        (when (nth 4 (syntax-ppss))
+          (cond
+           ;; If we're inside the comment and see a * prefix, use it
+           ((string-match "^\\([[:space:]]*\\*+[[:space:]]*\\)"
+                          line-string)
+            (match-string 1 line-string))
+           ;; If we're at the start of a comment, figure out what prefix
+           ;; to use for the subsequent lines after it
+           ((string-match (concat "[[:space:]]*" comment-start-skip) 
line-string)
+            (rust-fill-prefix-for-comment-start
+             (match-string 0 line-string))))))
+       (fill-prefix
+        (or line-comment-start
+            fill-prefix)))
+    (funcall body)))
+
+(defun rust-find-fill-prefix ()
+  (rust-in-comment-paragraph
+   (lambda ()
+     (rust-with-comment-fill-prefix
+      (lambda ()
+        fill-prefix)))))
+
+(defun rust-fill-paragraph (&rest args)
+  "Special wrapping for `fill-paragraph'.
+This handles multi-line comments with a * prefix on each line."
+  (rust-in-comment-paragraph
+   (lambda ()
+     (rust-with-comment-fill-prefix
+      (lambda ()
+        (let
+            ((fill-paragraph-function
+              (if (not (eq fill-paragraph-function #'rust-fill-paragraph))
+                  fill-paragraph-function))
+             (fill-paragraph-handle-comment t))
+          (apply #'fill-paragraph args)
+          t))))))
+
+(defun rust-do-auto-fill (&rest args)
+  "Special wrapping for `do-auto-fill'.
+This handles multi-line comments with a * prefix on each line."
+  (rust-with-comment-fill-prefix
+   (lambda ()
+     (apply #'do-auto-fill args)
+     t)))
+
+(defun rust-fill-forward-paragraph (arg)
+  ;; This is to work around some funny behavior when a paragraph separator is
+  ;; at the very top of the file and there is a fill prefix.
+  (let ((fill-prefix nil)) (forward-paragraph arg)))
+
+(defun rust-comment-indent-new-line (&optional arg)
+  (rust-with-comment-fill-prefix
+   (lambda () (comment-indent-new-line arg))))
+
+;;; Defun Motions
+
+(defun rust-beginning-of-defun (&optional arg)
+  "Move backward to the beginning of the current defun.
+
+With ARG, move backward multiple defuns.  Negative ARG means
+move forward.
+
+This is written mainly to be used as `beginning-of-defun-function' for Rust.
+Don't move to the beginning of the line. `beginning-of-defun',
+which calls this, does that afterwards."
+  (interactive "p")
+  (let* ((arg (or arg 1))
+         (magnitude (abs arg))
+         (sign (if (< arg 0) -1 1)))
+    ;; If moving forward, don't find the defun we might currently be
+    ;; on.
+    (when (< sign 0)
+      (end-of-line))
+    (catch 'done
+      (dotimes (_ magnitude)
+        ;; Search until we find a match that is not in a string or comment.
+        (while (if (re-search-backward (concat "^\\(" rust-top-item-beg-re 
"\\)")
+                                       nil 'move sign)
+                   (rust-in-str-or-cmnt)
+                 ;; Did not find it.
+                 (throw 'done nil)))))
+    t))
+
+(defun rust-end-of-defun ()
+  "Move forward to the next end of defun.
+
+With argument, do it that many times.
+Negative argument -N means move back to Nth preceding end of defun.
+
+Assume that this is called after `beginning-of-defun'.  So point is
+at the beginning of the defun body.
+
+This is written mainly to be used as `end-of-defun-function' for Rust."
+  (interactive)
+  ;; Find the opening brace
+  (if (re-search-forward "[{]" nil t)
+      (progn
+        (goto-char (match-beginning 0))
+        ;; Go to the closing brace
+        (condition-case nil
+            (forward-sexp)
+          (scan-error
+           ;; The parentheses are unbalanced; instead of being unable
+           ;; to fontify, just jump to the end of the buffer
+           (goto-char (point-max)))))
+    ;; There is no opening brace, so consider the whole buffer to be one 
"defun"
+    (goto-char (point-max))))
+
+;;; _
+
+(defun rust-mode-reload ()
+  (interactive)
+  (unload-feature 'rust-mode)
+  (require 'rust-mode)
+  (rust-mode))
+
+(defvar rust-mode-syntax-table
+  (let ((table (make-syntax-table)))
+
+    ;; Operators
+    (dolist (i '(?+ ?- ?* ?/ ?% ?& ?| ?^ ?! ?< ?> ?~ ?@))
+      (modify-syntax-entry i "." table))
+
+    ;; Strings
+    (modify-syntax-entry ?\" "\"" table)
+    (modify-syntax-entry ?\\ "\\" table)
+
+    ;; Angle brackets.  We suppress this with syntactic propertization
+    ;; when needed
+    (modify-syntax-entry ?< "(>" table)
+    (modify-syntax-entry ?> ")<" table)
+
+    ;; Comments
+    (modify-syntax-entry ?/  ". 124b" table)
+    (modify-syntax-entry ?*  ". 23n"  table)
+    (modify-syntax-entry ?\n "> b"    table)
+    (modify-syntax-entry ?\^m "> b"   table)
+
+    table)
+  "Syntax definitions and helpers.")
+
+(defun rust--syntax-propertize-raw-string (str-start end)
+  "A helper for rust-syntax-propertize.
+
+This will apply the appropriate string syntax to the character
+from the STR-START up to the end of the raw string, or to END,
+whichever comes first."
+  (when (save-excursion
+          (goto-char str-start)
+          (looking-at "r\\(#*\\)\\(\"\\)"))
+    ;; In a raw string, so try to find the end.
+    (let ((hashes (match-string 1)))
+      ;; Match \ characters at the end of the string to suppress
+      ;; their normal character-quote syntax.
+      (when (re-search-forward (concat "\\(\\\\*\\)\\(\"" hashes "\\)") end t)
+        (put-text-property (match-beginning 1) (match-end 1)
+                           'syntax-table (string-to-syntax "_"))
+        (put-text-property (1- (match-end 2)) (match-end 2)
+                           'syntax-table (string-to-syntax "|"))
+        (goto-char (match-end 0))))))
+
+;;; Syntax Propertize
+
+(defun rust-syntax-propertize (start end)
+  "A `syntax-propertize-function' to apply properties from START to END."
+  (goto-char start)
+  (when-let ((str-start (rust-in-str-or-cmnt)))
+    (rust--syntax-propertize-raw-string str-start end))
+  (funcall
+   (syntax-propertize-rules
+    ;; Character literals.
+    (rust--char-literal-rx (1 "\"") (2 "\""))
+    ;; Raw strings.
+    ("\\(r\\)#*\""
+     (0 (ignore
+         (goto-char (match-end 0))
+         (unless (save-excursion (nth 8 (syntax-ppss (match-beginning 0))))
+           (put-text-property (match-beginning 1) (match-end 1)
+                              'syntax-table (string-to-syntax "|"))
+           (rust--syntax-propertize-raw-string (match-beginning 0) end)))))
+    ("[<>]"
+     (0 (ignore
+         (when (save-match-data
+                 (save-excursion
+                   (goto-char (match-beginning 0))
+                   (rust-ordinary-lt-gt-p)))
+           (put-text-property (match-beginning 0) (match-end 0)
+                              'syntax-table (string-to-syntax "."))
+           (goto-char (match-end 0)))))))
+   (point) end))
 
 ;;;###autoload
 (define-derived-mode rust-mode prog-mode "Rust"
@@ -67,4 +1507,4 @@
   (add-hook 'after-save-hook rust-after-save-hook nil t))
 
 (provide 'rust-prog-mode)
-;;; rust-prog-mode.el ends here
\ No newline at end of file
+;;; rust-prog-mode.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]