changeset 189:4bb68b30325b

rust-mode: new package
author Jordi Gutiérrez Hermoso <jordigh@octave.org>
date Wed, 13 Feb 2019 15:38:59 -0500
parents cf476cceb7b2
children 9328263cb84e
files dotemacs.el elpa/rust-mode-0.3.0/rust-mode-autoloads.el elpa/rust-mode-0.3.0/rust-mode-pkg.el elpa/rust-mode-0.3.0/rust-mode.el
diffstat 4 files changed, 1554 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/dotemacs.el
+++ b/dotemacs.el
@@ -426,7 +426,7 @@
      ("gnu" . "http://elpa.gnu.org/packages/"))))
  '(package-selected-packages
    (quote
-    (mastodon systemd fountain-mode markdown-mode magit js2-mode yaml-mode web-mode undo-tree puppet-mode nginx-mode json-mode jade-mode idomenu haml-mode goto-last-change flymake-haml elpy dockerfile-mode)))
+    (mastodon rust-mode systemd fountain-mode markdown-mode magit js2-mode yaml-mode web-mode undo-tree puppet-mode nginx-mode json-mode jade-mode idomenu haml-mode goto-last-change flymake-haml elpy dockerfile-mode)))
  '(safe-local-variable-values
    (quote
     ((encoding . utf-8)
new file mode 100644
--- /dev/null
+++ b/elpa/rust-mode-0.3.0/rust-mode-autoloads.el
@@ -0,0 +1,26 @@
+;;; rust-mode-autoloads.el --- automatically extracted autoloads
+;;
+;;; Code:
+(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path))))
+
+;;;### (autoloads nil "rust-mode" "rust-mode.el" (23554 43461 606463
+;;;;;;  237000))
+;;; Generated autoloads from rust-mode.el
+
+(autoload 'rust-mode "rust-mode" "\
+Major mode for Rust code.
+
+\\{rust-mode-map}
+
+\(fn)" t nil)
+
+(add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-mode))
+
+;;;***
+
+;; Local Variables:
+;; version-control: never
+;; no-byte-compile: t
+;; no-update-autoloads: t
+;; End:
+;;; rust-mode-autoloads.el ends here
new file mode 100644
--- /dev/null
+++ b/elpa/rust-mode-0.3.0/rust-mode-pkg.el
@@ -0,0 +1,2 @@
+;;; -*- no-byte-compile: t -*-
+(define-package "rust-mode" "0.3.0" "A major emacs mode for editing Rust source code" '((emacs "24.0")) :commit "e32765893ce2efb2db6662f507fb9d33d5c1b61b" :keywords '("languages") :authors '(("Mozilla")) :maintainer '("Mozilla") :url "https://github.com/rust-lang/rust-mode")
new file mode 100644
--- /dev/null
+++ b/elpa/rust-mode-0.3.0/rust-mode.el
@@ -0,0 +1,1525 @@
+;;; rust-mode.el --- A major emacs mode for editing Rust source code -*-lexical-binding: t-*-
+
+;; Version: 0.3.0
+;; Package-Version: 0.3.0
+;; Author: Mozilla
+;; Url: https://github.com/rust-lang/rust-mode
+;; Keywords: languages
+;; Package-Requires: ((emacs "24.0"))
+
+;; This file is distributed under the terms of both the MIT license and the
+;; Apache License (version 2.0).
+
+;;; Commentary:
+;;
+
+;;; Code:
+
+(eval-when-compile (require 'rx)
+                   (require 'compile)
+                   (require 'url-vars))
+
+(defvar electric-pair-inhibit-predicate)
+(defvar electric-indent-chars)
+
+;; for GNU Emacs < 24.3
+(eval-when-compile
+  (unless (fboundp 'setq-local)
+    (defmacro setq-local (var val)
+      "Set variable VAR to value VAL in current buffer."
+      (list 'set (list 'make-local-variable (list 'quote var)) val))))
+
+(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:]]*")
+(defconst rust-re-vis "pub")
+
+(defconst rust-re-non-standard-string
+  (rx
+   (or
+    ;; Raw string: if it matches, it ends up with the starting character
+    ;; of the string as group 1, any ending backslashes as group 4, and
+    ;; the ending character as either group 5 or group 6.
+    (seq
+     ;; The "r" starts the raw string.  Capture it as group 1 to mark it as such syntactically:
+     (group "r")
+
+     ;; Then either:
+     (or
+      ;; a sequence at least one "#" (followed by quote).  Capture all
+      ;; but the last "#" as group 2 for this case.
+      (seq (group (* "#")) "#\"")
+
+      ;; ...or a quote without any "#".  Capture it as group 3. This is
+      ;; used later to match the opposite quote only if this capture
+      ;; occurred
+      (group "\""))
+
+     ;; The contents of the string:
+     (*? anything)
+
+     ;; If there are any backslashes at the end of the string, capture
+     ;; them as group 4 so we can suppress the normal escape syntax
+     ;; parsing:
+     (group (* "\\"))
+
+     ;; Then the end of the string--the backreferences ensure that we
+     ;; only match the kind of ending that corresponds to the beginning
+     ;; we had:
+     (or
+      ;; There were "#"s - capture the last one as group 5 to mark it as
+      ;; the end of the string:
+      (seq "\"" (backref 2) (group "#"))
+
+      ;; No "#"s - capture the ending quote (using a backref to group 3,
+      ;; so that we can't match a quote if we had "#"s) as group 6
+      (group (backref 3))
+
+      ;; If the raw string wasn't actually closed, go all the way to the end
+      string-end))
+
+    ;; Character literal: match the beginning ' of a character literal
+    ;; as group 7, and the ending one as group 8
+    (seq
+     (group "'")
+     (or
+      (seq
+       "\\"
+       (or
+        (: "u{" (** 1 6 xdigit) "}")
+        (: "x" (= 2 xdigit))
+        (any "'nrt0\"\\")))
+      (not (any "'\\"))
+      )
+     (group "'"))
+    )
+   ))
+
+(defun rust-looking-back-str (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 (SYMS)
+  "Return non-nil if the point is just after a complete symbol that is a member of the list of strings SYMS"
+  (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) SYMS)))))
+
+(defun rust-looking-back-ident ()
+  "Non-nil if we are looking backwards at a valid rust identifier"
+  (let ((beg-of-symbol (save-excursion (forward-thing 'symbol -1) (point))))
+    (looking-back rust-re-ident beg-of-symbol)))
+
+(defun rust-looking-back-macro ()
+  "Non-nil if looking back at an ident followed by a !"
+  (save-excursion (backward-char) (and (= ?! (char-after)) (rust-looking-back-ident))))
+
+;; Syntax definitions and helpers
+(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 fontification 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))
+
+(defgroup rust-mode nil
+  "Support for Rust code."
+  :link '(url-link "http://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 t
+  "Indent the line 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-playpen-url-format "https://play.rust-lang.org/?code=%s"
+  "Format string to use when submitting code to the playpen"
+  :type 'string
+  :group 'rust-mode)
+(defcustom rust-shortener-url-format "http://is.gd/create.php?format=simple&url=%s"
+  "Format string to use for creating the shortened link of a playpen submission"
+  :type 'string
+  :group 'rust-mode)
+
+(defcustom rust-match-angle-brackets t
+  "Enable angle bracket matching.  Attempt to match `<' and `>' where
+  appropriate."
+  :type 'boolean
+  :safe #'booleanp
+  :group 'rust-mode)
+
+(defcustom rust-format-on-save nil
+  "Format future rust buffers before saving using rustfmt."
+  :type 'boolean
+  :safe #'booleanp)
+
+(defcustom rust-rustfmt-bin "rustfmt"
+  "Path to rustfmt executable."
+  :type 'string)
+
+(defface rust-unsafe-face
+  '((t :inherit font-lock-warning-face))
+  "Face for the `unsafe' keyword."
+  :group 'rust-mode)
+
+(defun rust-paren-level () (nth 0 (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 ()
+  (save-excursion
+    (when (> (rust-paren-level) 0)
+      (backward-up-list)
+      (rust-rewind-irrelevant)
+      (or (rust-looking-back-macro)
+          (and (rust-looking-back-ident) (save-excursion (backward-sexp) (rust-rewind-irrelevant) (rust-looking-back-str "macro_rules!")))
+          (rust-in-macro))
+      )))
+
+(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)))
+
+(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*" (point-at-bol)))
+                    (= (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(...)
+         ((rust-looking-back-str ")")
+          (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 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 (when (> (line-number-at-pos) 1)
+                                            (save-excursion
+                                              (forward-line -1)
+                                              (end-of-line)
+                                              (point)))))
+                 (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 (= (line-number-at-pos end-of-prev-line-pos) (line-number-at-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
+              ((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
+                      (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\\>\\|{\\|/[/*]")
+
+                       (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))))))
+
+
+;; Font-locking definitions and helpers
+(defconst rust-mode-keywords
+  '("as"
+    "box" "break"
+    "const" "continue" "crate"
+    "do"
+    "else" "enum" "extern"
+    "false" "fn" "for"
+    "if" "impl" "in"
+    "let" "loop"
+    "match" "mod" "move" "mut"
+    "priv" "pub"
+    "ref" "return"
+    "self" "static" "struct" "super"
+    "true" "trait" "type"
+    "use"
+    "virtual"
+    "where" "while"))
+
+(defconst rust-special-types
+  '("u8" "i8"
+    "u16" "i16"
+    "u32" "i32"
+    "u64" "i64"
+
+    "f32" "f64"
+    "float" "int" "uint" "isize" "usize"
+    "bool"
+    "str" "char"))
+
+(defconst rust-re-type-or-constructor
+  (rx symbol-start
+      (group upper (0+ (any word nonascii digit "_")))
+      symbol-end))
+
+(defconst rust-re-pre-expression-operators "[-=!%&*/:<>[{(|.^;}]")
+(defun rust-re-word (inner) (concat "\\<" inner "\\>"))
+(defun rust-re-grab (inner) (concat "\\(" inner "\\)"))
+(defun rust-re-shy (inner) (concat "\\(?:" inner "\\)"))
+(defun rust-re-item-def (itype)
+  (concat (rust-re-word itype) "[[:space:]]+" (rust-re-grab rust-re-ident)))
+(defun rust-re-item-def-imenu (itype)
+  (concat "^[[:space:]]*"
+          (rust-re-shy (concat (rust-re-word rust-re-vis) "[[:space:]]+")) "?"
+          (rust-re-item-def itype)))
+
+(defconst rust-re-special-types (regexp-opt rust-special-types 'symbols))
+
+
+(defun rust-path-font-lock-matcher (re-ident)
+  "Matches names like \"foo::\" or \"Foo::\" (depending on RE-IDENT, which should match
+the desired identifiers), but does not match type annotations \"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-mode-font-lock-keywords
+  (append
+   `(
+     ;; Keywords proper
+     (,(regexp-opt rust-mode-keywords 'symbols) . font-lock-keyword-face)
+
+     ;; Special types
+     (,(regexp-opt rust-special-types 'symbols) . font-lock-type-face)
+
+     ;; The unsafe keyword
+     ("\\_<unsafe\\_>" . 'rust-unsafe-face)
+
+     ;; Attributes like `#[bar(baz)]` or `#![bar(baz)]` or `#[bar = "baz"]`
+     (,(rust-re-grab (concat "#\\!?\\[" rust-re-ident "[^]]*\\]"))
+      1 font-lock-preprocessor-face keep)
+
+     ;; 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) ":[^:]") 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)
+
+     ;; CamelCase Means Type Or Constructor
+     (,rust-re-type-or-constructor 1 font-lock-type-face)
+     )
+
+   ;; Item definitions
+   (mapcar #'(lambda (x)
+               (list (rust-re-item-def (car x))
+                     1 (cdr x)))
+           '(("enum" . font-lock-type-face)
+             ("struct" . 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)))))
+
+(defvar font-lock-beg)
+(defvar font-lock-end)
+
+(defun rust-font-lock-extend-region ()
+  "Extend the region given by `font-lock-beg' and `font-lock-end'
+  to include the beginning of a string or comment if it includes
+  part of it.  Adjusts to include the r[#] of a raw string as
+  well."
+
+  (save-excursion
+    (let ((orig-beg font-lock-beg)
+          (orig-end font-lock-end))
+
+      (let*
+          ;; It's safe to call `syntax-ppss' here on positions that are
+          ;; already syntactically fontified
+          ((beg-ppss (syntax-ppss font-lock-beg))
+           (beg-in-cmnt (and beg-ppss (nth 4 beg-ppss) (nth 8 beg-ppss)))
+           (beg-in-str (and beg-ppss (nth 3 beg-ppss) (nth 8 beg-ppss))))
+
+        (when (and beg-in-str (>= font-lock-beg beg-in-str))
+          (setq font-lock-beg (nth 8 beg-ppss))
+          (while (equal ?# (char-before font-lock-beg))
+            (setq font-lock-beg (1- font-lock-beg)))
+          (when (equal ?r (char-before font-lock-beg))
+            (setq font-lock-beg (1- font-lock-beg))))
+
+        (when (and beg-in-cmnt (> font-lock-beg beg-in-cmnt))
+          (setq font-lock-beg beg-in-cmnt)))
+
+      ;; We need to make sure that if the region ends inside a raw string, we
+      ;; extend it out past the end of it.  But we can't use `syntax-ppss' to
+      ;; detect that, becaue that depends on font-lock already being done, and we
+      ;; are trying to figure out how much to font-lock before that.  So we use
+      ;; the regexp directly.
+      (save-match-data
+        (goto-char font-lock-beg)
+        (while (and (< (point) font-lock-end)
+                    (re-search-forward rust-re-non-standard-string (buffer-end 1) t)
+                    (<= (match-beginning 0) font-lock-end))
+          (setq font-lock-end (max font-lock-end (match-end 0)))
+          (goto-char (1+ (match-beginning 0)))))
+
+      (or (/= font-lock-beg orig-beg)
+          (/= font-lock-end orig-end))
+      )))
+
+(defun rust-conditional-re-search-forward (regexp bound condition)
+  ;; Search forward for regexp (with bound).  If found, call condition and return the found
+  ;; match only if it returns true.
+  (let* (found
+         found-ret-list
+         (ret-list (save-excursion
+                     (while (and (not found) (re-search-forward regexp bound t))
+                       (setq
+                        found-ret-list (list (point) (match-data))
+                        found (save-match-data (save-excursion (ignore-errors (funcall condition)))))
+                       ;; If the condition filters out a match, need to search
+                       ;; again just after its beginning.  This will allow
+                       ;; cases such as:
+                       ;;    "bar" r"foo"
+                       ;; where the filtered out search (r" r") should not
+                       ;; prevent finding another one that begins in the middle
+                       ;; of it (r"foo")
+                       (when (not found)
+                         (goto-char (1+ (match-beginning 0))))
+                       )
+                     (when found found-ret-list))))
+    (when ret-list
+      (goto-char (nth 0 ret-list))
+      (set-match-data (nth 1 ret-list))
+      (nth 0 ret-list))))
+
+(defun rust-look-for-non-standard-string (bound)
+  ;; Find a raw string or character literal, but only if it's not in the middle
+  ;; of another string or a comment.
+
+  (rust-conditional-re-search-forward
+   rust-re-non-standard-string
+   bound
+   (lambda ()
+     (let ((pstate (syntax-ppss (match-beginning 0))))
+       (not
+        (or
+         (nth 4 pstate) ;; Skip if in a comment
+         (and (nth 3 pstate) (wholenump (nth 8 pstate)) (< (nth 8 pstate) (match-beginning 0))) ;; Skip if in a string that isn't starting here
+         ))))))
+
+(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 ")"))
+           (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))))))
+      (when dest
+        (goto-char dest))))))
+
+(defun rust-rewind-to-decl-name ()
+  "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" "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 '("if" "while" "match" "return" "box" "in")) 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)
+       
+       ;; 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" "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 ","))
+        (backward-up-list)
+        (rust-is-in-expression-context 'open-brace))
+
+       ;; 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 t if the < sign just after point is an operator rather
+  than an opening angle bracket, otherwise 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-mode-keywords)
+        (rust-looking-back-symbols '("self" "true" "false")))
+
+       ;; 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)
+  "Wraps the default `electric-pair-inhibit-predicate' to prevent
+  inserting a \"matching\" > after a < that would be treated as a
+  less than sign rather than as an opening angle bracket."
+  (or
+   (when (= ?< char)
+     (save-excursion
+       (backward-char)
+       (rust-is-lt-char-operator)))
+   (funcall (default-value 'electric-pair-inhibit-predicate) char)))
+
+(defun rust-look-for-non-angle-bracket-lt-gt (bound)
+  "Find an angle bracket (\"<\" or \">\") that should be part of
+  a matched pair Relies on the fact that when it finds a < or >,
+  we have already decided which previous ones are angle brackets
+  and which ones are not.  So this only really works as a
+  font-lock-syntactic-keywords matcher--it won't work at
+  arbitrary positions without the earlier parts of the buffer
+  having already been covered."
+
+  (rust-conditional-re-search-forward
+   "[<>]" bound
+   (lambda ()
+     (goto-char (match-beginning 0))
+     (cond
+      ;; If matching is turned off suppress all of them
+      ((not rust-match-angle-brackets) 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)
+      
+      ((looking-at "<")
+       (rust-is-lt-char-operator))
+
+      ((looking-at ">")
+       (cond
+        ;; Don't treat the > in -> or => as an angle bracket
+        ((member (char-before (point)) '(?- ?=)) t)
+
+        ;; 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)
+           (not (looking-at "<"))))))))))
+
+(defvar rust-mode-font-lock-syntactic-keywords
+  (append
+   ;; Handle raw strings and character literals:
+   `((rust-look-for-non-standard-string (1 "|" nil t) (4 "_" nil t) (5 "|" nil t) (6 "|" nil t) (7 "\"" nil t) (8 "\"" nil t)))
+   ;; Find where < and > characters represent operators rather than angle brackets:
+   '((rust-look-for-non-angle-bracket-lt-gt (0 "." t)))))
+
+(defun rust-mode-syntactic-face-function (state)
+  "Syntactic face function to distinguish doc comments from other comments."
+  (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
+    ))))
+
+(defun rust-fill-prefix-for-comment-start (line-start)
+  "Determine what to use for `fill-prefix' based on what is at the beginning of a line."
+  (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' to handle 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' to handle 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))))
+
+;;; Imenu support
+(defvar rust-imenu-generic-expression
+  (append (mapcar #'(lambda (x)
+                      (list (capitalize x) (rust-re-item-def-imenu x) 1))
+                  '("enum" "struct" "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.")
+
+;;; Defun Motions
+
+;;; Start of a Rust item
+(defvar rust-top-item-beg-re
+  (concat "^\\s-*\\(?:priv\\|pub\\)?\\s-*"
+          (regexp-opt
+           '("enum" "struct" "type" "mod" "use" "fn" "static" "impl"
+             "extern" "trait"))))
+
+(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")
+  (re-search-backward (concat "^\\(" rust-top-item-beg-re "\\)\\_>")
+                      nil 'move (or arg 1)))
+
+(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))))
+
+;; Formatting using rustfmt
+(defun rust--format-call (buf)
+  "Format BUF using rustfmt."
+  (with-current-buffer (get-buffer-create "*rustfmt*")
+    (erase-buffer)
+    (insert-buffer-substring buf)
+    (if (zerop (call-process-region (point-min) (point-max) rust-rustfmt-bin t t nil))
+        (progn
+          (if (not (string= (buffer-string) (with-current-buffer buf (buffer-string))))
+              (copy-to-buffer buf (point-min) (point-max)))
+          (kill-buffer))
+      (error "Rustfmt failed, see *rustfmt* buffer for details"))))
+
+(defun rust-format-buffer ()
+  "Format the current buffer using rustfmt."
+  (interactive)
+  (unless (executable-find rust-rustfmt-bin)
+    (error "Could not locate executable \"%s\"" rust-rustfmt-bin))
+
+  (let ((cur-line (line-number-at-pos))
+        (cur-column (current-column))
+        (cur-win-start (window-start)))
+    (rust--format-call (current-buffer))
+    ;; Move to the same line and column as before.  This is best
+    ;; effort: if rustfmt inserted lines before point, we end up in
+    ;; the wrong place. See issue #162.
+    (goto-char (point-min))
+    (forward-line (1- cur-line))
+    (forward-char cur-column)
+    (set-window-start (selected-window) cur-win-start))
+
+  ;; Issue #127: Running this on a buffer acts like a revert, and could cause
+  ;; the fontification to get out of sync.  Call the same hook to ensure it is
+  ;; restored.
+  (rust--after-revert-hook)
+
+  (message "Formatted buffer with rustfmt."))
+
+(defun rust-enable-format-on-save ()
+  "Enable formatting using rustfmt when saving buffer."
+  (interactive)
+  (setq-local rust-format-on-save t))
+
+(defun rust-disable-format-on-save ()
+  "Disable formatting using rustfmt when saving buffer."
+  (interactive)
+  (setq-local rust-format-on-save nil))
+
+(defvar rust-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map (kbd "C-c C-f") 'rust-format-buffer)
+    map)
+  "Keymap for Rust major mode.")
+
+;;;###autoload
+(define-derived-mode rust-mode prog-mode "Rust"
+  "Major mode for Rust code.
+
+\\{rust-mode-map}"
+  :group 'rust-mode
+  :syntax-table rust-mode-syntax-table
+
+  ;; Indentation
+  (setq-local indent-line-function 'rust-mode-indent-line)
+
+  ;; Fonts
+  (add-to-list 'font-lock-extend-region-functions 'rust-font-lock-extend-region)
+  (setq-local font-lock-defaults '(rust-mode-font-lock-keywords
+                                   nil nil nil nil
+                                   (font-lock-syntactic-keywords . rust-mode-font-lock-syntactic-keywords)
+                                   (font-lock-syntactic-face-function . rust-mode-syntactic-face-function)
+                                   ))
+
+  ;; Misc
+  (setq-local comment-start "// ")
+  (setq-local comment-end   "")
+  (setq-local indent-tabs-mode nil)
+
+  ;; Auto indent on }
+  (setq-local
+   electric-indent-chars (cons ?} (and (boundp 'electric-indent-chars)
+                                       electric-indent-chars)))
+
+  ;; Allow paragraph fills for comments
+  (setq-local comment-start-skip "\\(?://[/!]*\\|/\\*[*!]?\\)[[:space:]]*")
+  (setq-local paragraph-start
+       (concat "[[:space:]]*\\(?:" comment-start-skip "\\|\\*/?[[:space:]]*\\|\\)$"))
+  (setq-local paragraph-separate paragraph-start)
+  (setq-local normal-auto-fill-function 'rust-do-auto-fill)
+  (setq-local fill-paragraph-function 'rust-fill-paragraph)
+  (setq-local fill-forward-paragraph-function 'rust-fill-forward-paragraph)
+  (setq-local adaptive-fill-function 'rust-find-fill-prefix)
+  (setq-local adaptive-fill-first-line-regexp "")
+  (setq-local comment-multi-line t)
+  (setq-local comment-line-break-function 'rust-comment-indent-new-line)
+  (setq-local imenu-generic-expression rust-imenu-generic-expression)
+  (setq-local imenu-syntax-alist '((?! . "w"))) ; For macro_rules!
+  (setq-local beginning-of-defun-function 'rust-beginning-of-defun)
+  (setq-local end-of-defun-function 'rust-end-of-defun)
+  (setq-local parse-sexp-lookup-properties t)
+  (setq-local electric-pair-inhibit-predicate 'rust-electric-pair-inhibit-predicate-wrap)
+  (add-hook 'after-revert-hook 'rust--after-revert-hook nil t)
+  (add-hook 'before-save-hook 'rust--before-save-hook nil t))
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-mode))
+
+(defun rust-mode-reload ()
+  (interactive)
+  (unload-feature 'rust-mode)
+  (require 'rust-mode)
+  (rust-mode))
+
+;; Issue #104: When reverting the buffer, make sure all fontification is redone
+;; so that we don't end up missing a non-angle-bracket '<' or '>' character.
+(defun rust--after-revert-hook ()
+  ;; In Emacs 25 and later, the preferred method to force fontification is
+  ;; to use `font-lock-ensure', which doesn't exist in Emacs 24 and earlier.
+  ;; If it's not available, fall back to calling `font-lock-fontify-region'
+  ;; on the whole buffer.
+  (save-excursion
+    (if (fboundp 'font-lock-ensure)
+        (font-lock-ensure)
+      (font-lock-fontify-region (point-min) (point-max)))))
+
+(defun rust--before-save-hook ()
+  (when rust-format-on-save (rust-format-buffer)))
+
+;; Issue #6887: Rather than inheriting the 'gnu compilation error
+;; regexp (which is broken on a few edge cases), add our own 'rust
+;; compilation error regexp and use it instead.
+(defvar rustc-compilation-regexps
+  (let ((file "\\([^\n]+\\)")
+        (start-line "\\([0-9]+\\)")
+        (start-col  "\\([0-9]+\\)")
+        (end-line   "\\([0-9]+\\)")
+        (end-col    "\\([0-9]+\\)")
+        (msg-type   "\\(?:[Ee]rror\\|\\([Ww]arning\\)\\|\\([Nn]ote\\|[Hh]elp\\)\\)"))
+    (let ((re (concat "^" file ":" start-line ":" start-col
+                      ": " end-line ":" end-col
+                      " " msg-type ":")))
+      (cons re '(1 (2 . 4) (3 . 5) (6 . 7)))))
+  "Specifications for matching errors in rustc invocations.
+See `compilation-error-regexp-alist' for help on their format.")
+
+(defvar rustc-new-compilation-regexps
+  (let ((file "\\([^\n]+\\)")
+        (start-line "\\([0-9]+\\)")
+        (start-col  "\\([0-9]+\\)"))
+    (let ((re (concat "^ *--> " file ":" start-line ":" start-col ; --> 1:2:3
+                      )))
+      (cons re '(1 2 3))))
+  "Specifications for matching errors in rustc invocations (new style).
+See `compilation-error-regexp-alist' for help on their format.")
+
+;; Match test run failures and panics during compilation as
+;; compilation warnings
+(defvar cargo-compilation-regexps
+  '("^\\s-+thread '[^']+' panicked at \\('[^']+', \\([^:]+\\):\\([0-9]+\\)\\)" 2 3 nil nil 1)
+  "Specifications for matching panics in cargo test invocations.
+See `compilation-error-regexp-alist' for help on their format.")
+
+(defun rustc-scroll-down-after-next-error ()
+  "In the new style error messages, the regular expression
+   matches on the file name (which appears after `-->`), but the
+   start of the error appears a few lines earlier. This hook runs
+   after `M-x next-error`; it simply scrolls down a few lines in
+   the compilation window until the top of the error is visible."
+  (save-selected-window
+    (when (eq major-mode 'rust-mode)
+      (select-window (get-buffer-window next-error-last-buffer))
+      (when (save-excursion
+              (beginning-of-line)
+              (looking-at " *-->"))
+        (let ((start-of-error
+               (save-excursion
+                 (beginning-of-line)
+                 (while (not (looking-at "^[a-z]+:\\|^[a-z]+\\[E[0-9]+\\]:"))
+                   (forward-line -1))
+                 (point))))
+          (set-window-start (selected-window) start-of-error))))))
+
+(eval-after-load 'compile
+  '(progn
+     (add-to-list 'compilation-error-regexp-alist-alist
+                  (cons 'rustc-new rustc-new-compilation-regexps))
+     (add-to-list 'compilation-error-regexp-alist 'rustc-new)
+     (add-hook 'next-error-hook 'rustc-scroll-down-after-next-error)
+     (add-to-list 'compilation-error-regexp-alist-alist
+                  (cons 'rustc rustc-compilation-regexps))
+     (add-to-list 'compilation-error-regexp-alist 'rustc)
+     (add-to-list 'compilation-error-regexp-alist-alist
+                  (cons 'cargo cargo-compilation-regexps))
+     (add-to-list 'compilation-error-regexp-alist 'cargo)))
+
+;;; Functions to submit (parts of) buffers to the rust playpen, for
+;;; sharing.
+(defun rust-playpen-region (begin end)
+  "Create a sharable URL for the contents of the current region
+   on the Rust playpen."
+  (interactive "r")
+  (let* ((data (buffer-substring begin end))
+         (escaped-data (url-hexify-string data))
+         (escaped-playpen-url (url-hexify-string (format rust-playpen-url-format escaped-data))))
+    (if (> (length escaped-playpen-url) 5000)
+        (error "encoded playpen data exceeds 5000 character limit (length %s)"
+               (length escaped-playpen-url))
+      (let ((shortener-url (format rust-shortener-url-format escaped-playpen-url))
+            (url-request-method "POST"))
+        (url-retrieve shortener-url
+                      (lambda (state)
+                        ; filter out the headers etc. included at the
+                        ; start of the buffer: the relevant text
+                        ; (shortened url or error message) is exactly
+                        ; the last line.
+                        (goto-char (point-max))
+                        (let ((last-line (thing-at-point 'line t))
+                              (err (plist-get state :error)))
+                          (kill-buffer)
+                          (if err
+                              (error "failed to shorten playpen url: %s" last-line)
+                            (message "%s" last-line)))))))))
+
+(defun rust-playpen-buffer ()
+  "Create a sharable URL for the contents of the current buffer
+   on the Rust playpen."
+  (interactive)
+  (rust-playpen-region (point-min) (point-max)))
+
+(defun rust-promote-module-into-dir ()
+  "Promote the module file visited by the current buffer into its own directory.
+
+For example, if the current buffer is visiting the file `foo.rs',
+then this function creates the directory `foo' and renames the
+file to `foo/mod.rs'.  The current buffer will be updated to
+visit the new file."
+  (interactive)
+  (let ((filename (buffer-file-name)))
+    (if (not filename)
+        (message "Buffer is not visiting a file.")
+      (if (string-equal (file-name-nondirectory filename) "mod.rs")
+          (message "Won't promote a module file already named mod.rs.")
+        (let* ((basename (file-name-sans-extension
+                          (file-name-nondirectory filename)))
+               (mod-dir (file-name-as-directory
+                         (concat (file-name-directory filename) basename)))
+               (new-name (concat mod-dir "mod.rs")))
+          (mkdir mod-dir t)
+          (rename-file filename new-name 1)
+          (set-visited-file-name new-name))))))
+
+(provide 'rust-mode)
+
+;;; rust-mode.el ends here