1 ;;; rust-mode.el --- A major emacs mode for editing Rust source code
5 ;; Url: https://github.com/mozilla/rust
7 (eval-when-compile (require 'cl))
9 ;; Syntax definitions and helpers
10 (defvar rust-mode-syntax-table
11 (let ((table (make-syntax-table)))
14 (loop for i in '(?+ ?- ?* ?/ ?& ?| ?^ ?! ?< ?> ?~ ?@)
15 do (modify-syntax-entry i "." table))
18 (modify-syntax-entry ?\" "\"" table)
19 (modify-syntax-entry ?\\ "\\" table)
22 (modify-syntax-entry ?_ "w" table)
25 (modify-syntax-entry ?/ ". 124b" table)
26 (modify-syntax-entry ?* ". 23" table)
27 (modify-syntax-entry ?\n "> b" table)
28 (modify-syntax-entry ?\^m "> b" table)
32 (defcustom rust-indent-offset 4
33 "*Indent Rust code by this number of spaces.")
35 (defun rust-paren-level () (nth 0 (syntax-ppss)))
36 (defun rust-in-str-or-cmnt () (nth 8 (syntax-ppss)))
37 (defun rust-rewind-past-str-cmnt () (goto-char (nth 8 (syntax-ppss))))
38 (defun rust-rewind-irrelevant ()
39 (let ((starting (point)))
40 (skip-chars-backward "[:space:]\n")
41 (if (looking-back "\\*/") (backward-char))
42 (if (rust-in-str-or-cmnt)
43 (rust-rewind-past-str-cmnt))
44 (if (/= starting (point))
45 (rust-rewind-irrelevant))))
47 (defun rust-mode-indent-line ()
52 (let ((level (rust-paren-level)))
54 ;; A function return type is 1 level indented
55 ((looking-at "->") (* rust-indent-offset (+ level 1)))
57 ;; A closing brace is 1 level unindended
58 ((looking-at "}") (* rust-indent-offset (- level 1)))
60 ;; If we're in any other token-tree / sexp, then:
61 ;; - [ or ( means line up with the opening token
62 ;; - { means indent to either nesting-level * rust-indent-offset,
63 ;; or one further indent from that if either current line
64 ;; begins with 'else', or previous line didn't end in
65 ;; semi, comma or brace, and wasn't an attribute. PHEW.
68 (rust-rewind-irrelevant)
70 (if (looking-at "[[(]")
71 (+ 1 (current-column))
75 (if (looking-at "\\<else\\>")
76 (* rust-indent-offset (+ 1 level))
80 (rust-rewind-irrelevant)
82 (if (looking-back "[{};,]")
83 (* rust-indent-offset level)
86 (* rust-indent-offset level)
87 (* rust-indent-offset (+ 1 level))))))))))
89 ;; Otherwise we're in a column-zero definition
92 ;; If we're to the left of the indentation, reindent and jump to it.
93 ((<= (current-column) indent)
94 (indent-line-to indent))
96 ;; We're to the right; if it needs indent, do so but save excursion.
97 ((not (eq (current-indentation) indent))
98 (save-excursion (indent-line-to indent))))))
101 ;; Font-locking definitions and helpers
102 (defconst rust-mode-keywords
106 "else" "enum" "extern"
113 "self" "static" "struct" "super"
114 "true" "trait" "type"
118 (defconst rust-special-types
129 (defconst rust-re-ident "[[:word:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*")
130 (defconst rust-re-CamelCase "[[:upper:]][[:word:][:multibyte:]_[:digit:]]*")
131 (defun rust-re-word (inner) (concat "\\<" inner "\\>"))
132 (defun rust-re-grab (inner) (concat "\\(" inner "\\)"))
133 (defun rust-re-grabword (inner) (rust-re-grab (rust-re-word inner)))
134 (defun rust-re-item-def (itype)
135 (concat (rust-re-word itype) "[[:space:]]+" (rust-re-grab rust-re-ident)))
137 (defvar rust-mode-font-lock-keywords
141 (,(regexp-opt rust-mode-keywords 'words) . font-lock-keyword-face)
144 (,(regexp-opt rust-special-types 'words) . font-lock-type-face)
146 ;; Attributes like `#[bar(baz)]`
147 (,(rust-re-grab (concat "#\\[" rust-re-ident "[^]]*\\]"))
148 1 font-lock-preprocessor-face)
150 ;; Syntax extension invocations like `foo!`, highlight including the !
151 (,(concat (rust-re-grab (concat rust-re-ident "!")) "[({[:space:]]")
152 1 font-lock-preprocessor-face)
154 ;; Field names like `foo:`, highlight excluding the :
155 (,(concat (rust-re-grab rust-re-ident) ":[^:]") 1 font-lock-variable-name-face)
157 ;; Module names like `foo::`, highlight including the ::
158 (,(rust-re-grab (concat rust-re-ident "::")) 1 font-lock-type-face)
160 ;; Lifetimes like `'foo`
161 (,(concat "'" (rust-re-grab rust-re-ident) "[^']") 1 font-lock-variable-name-face)
163 ;; Character constants, since they're not treated as strings
164 ;; in order to have sufficient leeway to parse 'lifetime above.
165 (,(rust-re-grab "'[^']'") 1 font-lock-string-face)
166 (,(rust-re-grab "'\\\\[nrt]'") 1 font-lock-string-face)
167 (,(rust-re-grab "'\\\\x[[:xdigit:]]\\{2\\}'") 1 font-lock-string-face)
168 (,(rust-re-grab "'\\\\u[[:xdigit:]]\\{4\\}'") 1 font-lock-string-face)
169 (,(rust-re-grab "'\\\\U[[:xdigit:]]\\{8\\}'") 1 font-lock-string-face)
171 ;; CamelCase Means Type Or Constructor
172 (,(rust-re-grabword rust-re-CamelCase) 1 font-lock-type-face)
176 (loop for (item . face) in
178 '(("enum" . font-lock-type-face)
179 ("struct" . font-lock-type-face)
180 ("type" . font-lock-type-face)
181 ("mod" . font-lock-type-face)
182 ("use" . font-lock-type-face)
183 ("fn" . font-lock-function-name-face)
184 ("static" . font-lock-constant-face))
186 collect `(,(rust-re-item-def item) 1 ,face))))
189 ;; For compatibility with Emacs < 24, derive conditionally
190 (defalias 'rust-parent-mode
191 (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode))
195 (define-derived-mode rust-mode rust-parent-mode "Rust"
196 "Major mode for Rust code."
199 (set-syntax-table rust-mode-syntax-table)
202 (set (make-local-variable 'indent-line-function)
203 'rust-mode-indent-line)
206 (set (make-local-variable 'font-lock-defaults)
207 '(rust-mode-font-lock-keywords nil nil nil nil))
210 (set (make-local-variable 'comment-start) "// ")
211 (set (make-local-variable 'comment-end) "")
212 (set (make-local-variable 'indent-tabs-mode) nil))
216 (add-to-list 'auto-mode-alist '("\\.rs$" . rust-mode))
218 (defun rust-mode-reload ()
220 (unload-feature 'rust-mode)
226 ;; Issue #6887: Rather than inheriting the 'gnu compilation error
227 ;; regexp (which is broken on a few edge cases), add our own 'rust
228 ;; compilation error regexp and use it instead.
229 (defvar rustc-compilation-regexps
230 (let ((file "\\([^\n]+\\)")
231 (start-line "\\([0-9]+\\)")
232 (start-col "\\([0-9]+\\)")
233 (end-line "\\([0-9]+\\)")
234 (end-col "\\([0-9]+\\)")
235 (error-or-warning "\\(?:[Ee]rror\\|\\([Ww]arning\\)\\)"))
236 (let ((re (concat "^" file ":" start-line ":" start-col
237 ": " end-line ":" end-col
238 " \\(?:[Ee]rror\\|\\([Ww]arning\\)\\):")))
239 (cons re '(1 (2 . 4) (3 . 5) (6)))))
240 "Specifications for matching errors in rustc invocations.
241 See `compilation-error-regexp-alist for help on their format.")
243 (eval-after-load 'compile
245 (add-to-list 'compilation-error-regexp-alist-alist
246 (cons 'rustc rustc-compilation-regexps))
247 (add-to-list 'compilation-error-regexp-alist 'rustc)))
249 ;;; rust-mode.el ends here