rewrite rust-mode to use font-lock-mode and emacs builtin syntax analysis

This commit is contained in:
Graydon Hoare 2013-07-24 17:11:04 -07:00
parent 330378d1a1
commit 581b3db3b3
4 changed files with 190 additions and 538 deletions

View File

@ -1,14 +0,0 @@
E=@echo
TEMP=temp.el
EMACS ?= emacs
all: $(TEMP)
$(EMACS) -batch -q -no-site-file -l ./$(TEMP) -f rustmode-compile
rm -f $(TEMP)
$(TEMP):
$(E) '(setq load-path (cons "." load-path))' >> $(TEMP)
$(E) '(defun rustmode-compile () (mapcar (lambda (x) (byte-compile-file x))' >> $(TEMP)
$(E) ' (list "cm-mode.el" "rust-mode.el")))' >> $(TEMP)
clean:
rm -f *.elc $(TEMP)

View File

@ -13,21 +13,8 @@ file:
(add-to-list 'load-path "/path/to/rust-mode/")
(require 'rust-mode)
Make sure you byte-compile the .el files first, or the mode will be
painfully slow. There is an included `Makefile` which will do it for
you, so in the simplest case you can just run `make` and everything
should Just Work.
If for some reason that doesn't work, you can byte compile manually,
by pasting this in your `*scratch*` buffer, moving the cursor below
it, and pressing `C-j`:
(progn
(byte-compile-file "/path/to/rust-mode/cm-mode.el" t)
(byte-compile-file "/path/to/rust-mode/rust-mode.el" t))
Rust mode will automatically be associated with .rs and .rc files. To
enable it explicitly, do `M-x rust-mode`.
Rust mode will automatically be associated with .rs files. To enable it
explicitly, do `M-x rust-mode`.
### package.el installation via Marmalade or MELPA
@ -67,24 +54,6 @@ should upgrade in order to support installation from multiple sources.
The ELPA archive is deprecated and no longer accepting new packages,
so the version there (1.7.1) is very outdated.
#### Important
In order to have cm-mode properly initialized after compilation prior
to rust-mode.el compilation you will need to add these `advices` to
your init file or if you are a melpa user install the `melpa` package.
```lisp
(defadvice package-download-tar
(after package-download-tar-initialize activate compile)
"initialize the package after compilation"
(package-initialize))
(defadvice package-download-single
(after package-download-single-initialize activate compile)
"initialize the package after compilation"
(package-initialize))
```
#### Install rust-mode
From there you can install rust-mode or any other modes by choosing

View File

@ -1,194 +0,0 @@
;;; cm-mode.el --- Wrapper for CodeMirror-style Emacs modes
;; Version: 0.1.0
;; Author: Mozilla
;; Url: https://github.com/mozilla/rust
;; Highlighting is done by running a stateful parser (with first-class
;; state object) over the buffer, line by line, using the output to
;; add 'face properties, and storing the parser state at the end of
;; each line. Indentation is done based on the parser state at the
;; start of the line.
(eval-when-compile (require 'cl))
;; Mode data structure
(defun make-cm-mode (token &optional start-state copy-state
compare-state indent)
(vector token
(or start-state (lambda () 'null))
(or copy-state 'cm-default-copy-state)
(or compare-state 'eq)
indent))
(defmacro cm-mode-token (x) `(aref ,x 0))
(defmacro cm-mode-start-state (x) `(aref ,x 1))
(defmacro cm-mode-copy-state (x) `(aref ,x 2))
(defmacro cm-mode-compare-state (x) `(aref ,x 3))
(defmacro cm-mode-indent (x) `(aref ,x 4))
(defvar cm-cur-mode nil)
(defvar cm-worklist nil)
(defun cm-default-copy-state (state)
(if (consp state) (copy-sequence state) state))
(defun cm-clear-work-items (from to)
(let ((prev-cons nil)
(rem cm-worklist))
(while rem
(let ((pos (marker-position (car rem))))
(cond ((or (< pos from) (> pos to)) (setf prev-cons rem))
(prev-cons (setf (cdr prev-cons) (cdr rem)))
(t (setf cm-worklist (cdr rem))))
(setf rem (cdr rem))))))
(defun cm-min-worklist-item ()
(let ((rest cm-worklist) (min most-positive-fixnum))
(while rest
(let ((pos (marker-position (car rest))))
(when (< pos min) (setf min pos)))
(setf rest (cdr rest)))
min))
;; Indentation
(defun cm-indent ()
(let (indent-pos)
(save-excursion
(beginning-of-line)
(let* ((buf (current-buffer))
(state (cm-preserve-state buf 'cm-state-for-point))
(old-indent (current-indentation)))
(back-to-indentation)
(setf indent-pos (point))
(let ((new-indent (funcall (cm-mode-indent cm-cur-mode) state)))
(unless (= old-indent new-indent)
(indent-line-to new-indent)
(setf indent-pos (point))
(beginning-of-line)
(cm-preserve-state buf
(lambda ()
(cm-highlight-line state)
(when (< (point) (point-max))
(put-text-property (point) (+ (point) 1) 'cm-parse-state state))))))))
(when (< (point) indent-pos)
(goto-char indent-pos))))
(defun cm-backtrack-to-state ()
(let ((backtracked 0)
(min-indent most-positive-fixnum)
min-indented)
(loop
(when (= (point) (point-min))
(return (funcall (cm-mode-start-state cm-cur-mode))))
(let ((st (get-text-property (- (point) 1) 'cm-parse-state)))
(when (and st (save-excursion
(backward-char)
(beginning-of-line)
(not (looking-at "[ ]*$"))))
(return (funcall (cm-mode-copy-state cm-cur-mode) st))))
(let ((i (current-indentation)))
(when (< i min-indent)
(setf min-indent i min-indented (point))))
(when (> (incf backtracked) 30)
(goto-char min-indented)
(return (funcall (cm-mode-start-state cm-cur-mode))))
(forward-line -1))))
(defun cm-state-for-point ()
(let ((pos (point))
(state (cm-backtrack-to-state)))
(while (< (point) pos)
(cm-highlight-line state)
(put-text-property (point) (+ (point) 1) 'cm-parse-state
(funcall (cm-mode-copy-state cm-cur-mode) state))
(forward-char))
state))
;; Highlighting
(defun cm-highlight-line (state)
(let ((eol (point-at-eol)))
(remove-text-properties (point) eol '(face))
(loop
(let ((p (point)))
(when (= p eol) (return))
(let ((style (funcall (cm-mode-token cm-cur-mode) state)))
(when (= p (point)) (print (point)) (error "Nothing consumed."))
(when (> p eol) (error "Parser moved past EOL"))
(when style
(put-text-property p (point) 'face style)))))))
(defun cm-find-state-before-point ()
(loop
(beginning-of-line)
(when (= (point) 1)
(return (funcall (cm-mode-start-state cm-cur-mode))))
(let ((cur (get-text-property (- (point) 1) 'cm-parse-state)))
(when cur (return (funcall (cm-mode-copy-state cm-cur-mode) cur))))
(backward-char)))
(defun cm-schedule-work (delay)
(run-with-idle-timer delay nil 'cm-preserve-state (current-buffer) 'cm-do-some-work))
(defun cm-preserve-state (buffer f &rest args)
(with-current-buffer buffer
(let ((modified (buffer-modified-p))
(buffer-undo-list t)
(inhibit-read-only t)
(inhibit-point-motion-hooks t)
(inhibit-modification-hooks t))
(unwind-protect (apply f args)
(unless modified
(restore-buffer-modified-p nil))))))
(defun cm-do-some-work-inner ()
(let ((end-time (time-add (current-time) (list 0 0 500)))
(quitting nil))
(while (and (not quitting) cm-worklist)
(goto-char (cm-min-worklist-item))
(let ((state (cm-find-state-before-point))
(startpos (point))
(timer-idle-list nil))
(loop
(cm-highlight-line state)
(when (= (point) (point-max)) (return))
(let ((old (get-text-property (point) 'cm-parse-state)))
(when (and old (funcall (cm-mode-compare-state cm-cur-mode) state old))
(return))
(put-text-property (point) (+ (point) 1) 'cm-parse-state
(funcall (cm-mode-copy-state cm-cur-mode) state)))
(when (or (let ((timer-idle-list nil)) (input-pending-p))
(time-less-p end-time (current-time)))
(setf quitting t) (return))
(forward-char))
(cm-clear-work-items startpos (point)))
(when quitting
(push (copy-marker (+ (point) 1)) cm-worklist)
(cm-schedule-work 0.05)))))
(defun cm-do-some-work ()
(save-excursion
(condition-case cnd (cm-do-some-work-inner)
(error (print cnd) (error cnd)))))
(defun cm-after-change-function (from to oldlen)
(cm-preserve-state (current-buffer) 'remove-text-properties from to '(cm-parse-state))
(push (copy-marker from) cm-worklist)
(cm-schedule-work 0.2))
;; Entry function
;;;###autoload
(defun cm-mode (mode)
(set (make-local-variable 'cm-cur-mode) mode)
(set (make-local-variable 'cm-worklist) (list (copy-marker 1)))
(when (cm-mode-indent mode)
(set (make-local-variable 'indent-line-function) 'cm-indent))
(add-hook 'after-change-functions 'cm-after-change-function t t)
(add-hook 'after-revert-hook (lambda () (cm-after-change-function 1 (point-max) nil)) t t)
(cm-schedule-work 0.05))
(provide 'cm-mode)
;;; cm-mode.el ends here

View File

@ -1,331 +1,222 @@
;;; rust-mode.el --- A major emacs mode for editing Rust source code
;; Version: 0.1.0
;; Version: 0.2.0
;; Author: Mozilla
;; Package-Requires: ((cm-mode "0.1.0"))
;; Url: https://github.com/mozilla/rust
(require 'cm-mode)
(require 'cc-mode)
(eval-when-compile (require 'cl))
(defun rust-electric-brace (arg)
(interactive "*P")
(self-insert-command (prefix-numeric-value arg))
(when (and c-electric-flag
(not (member (get-text-property (point) 'face)
'(font-lock-comment-face font-lock-string-face))))
(cm-indent)))
;; Syntax definitions and helpers
(defvar rust-mode-syntax-table
(let ((table (make-syntax-table)))
(defcustom rust-capitalized-idents-are-types t
"If non-nil, capitalized identifiers will be treated as types for the purposes of font-lock mode"
:type 'boolean
:require 'rust-mode
:group 'rust-mode)
;; Operators
(loop for i in '(?+ ?- ?* ?/ ?& ?| ?^ ?! ?< ?> ?~ ?@)
do (modify-syntax-entry i "." table))
(defcustom rust-indent-unit 4
"Amount of offset per level of indentation"
:type 'integer
:require 'rust-mode
:group 'rust-mode)
;; Strings
(modify-syntax-entry ?\" "\"" table)
(modify-syntax-entry ?\\ "\\" table)
(defvar rust-syntax-table (let ((table (make-syntax-table)))
(c-populate-syntax-table table)
table))
;; _ is a word-char
(modify-syntax-entry ?_ "w" table)
(defun make-rust-state ()
(vector 'rust-token-base
(list (vector 'top (- rust-indent-unit) nil nil nil))
0
nil))
(defmacro rust-state-tokenize (x) `(aref ,x 0))
(defmacro rust-state-context (x) `(aref ,x 1))
(defmacro rust-state-indent (x) `(aref ,x 2))
(defmacro rust-state-last-token (x) `(aref ,x 3))
;; Comments
(modify-syntax-entry ?/ ". 124b" table)
(modify-syntax-entry ?* ". 23" table)
(modify-syntax-entry ?\n "> b" table)
(modify-syntax-entry ?\^m "> b" table)
(defmacro rust-context-type (x) `(aref ,x 0))
(defmacro rust-context-indent (x) `(aref ,x 1))
(defmacro rust-context-column (x) `(aref ,x 2))
(defmacro rust-context-align (x) `(aref ,x 3))
(defmacro rust-context-info (x) `(aref ,x 4))
(defun rust-push-context (st type &optional align-column auto-align)
(let ((ctx (vector type (rust-state-indent st) align-column
(if align-column (if auto-align t 'unset) nil) nil)))
(push ctx (rust-state-context st))
ctx))
(defun rust-pop-context (st)
(let ((old (pop (rust-state-context st))))
(setf (rust-state-indent st) (rust-context-indent old))
old))
(defun rust-dup-context (st)
(let* ((list (rust-state-context st))
(dup (copy-sequence (car list))))
(setf (rust-state-context st) (cons dup (cdr list)))
dup))
(defvar rust-operator-chars "-+/%=<>!*&|@~^")
(defvar rust-punc-chars "()[].,{}:;")
(defvar rust-value-keywords
(let ((table (make-hash-table :test 'equal)))
(dolist (word '("mod" "const" "class" "type"
"trait" "struct" "fn" "enum"
"impl"))
(puthash word 'def table))
(dolist (word '("as" "break"
"copy" "do" "drop" "else"
"extern" "for" "if" "let" "log"
"loop" "once" "priv" "pub" "pure"
"ref" "return" "static" "unsafe" "use"
"while" "while"
"assert"
"mut"))
(puthash word t table))
(puthash "match" 'alt table)
(dolist (word '("self" "true" "false")) (puthash word 'atom table))
table))
;; FIXME type-context keywords
(defvar rust-tcat nil "Kludge for multiple returns without consing")
(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 ((starting (point)))
(skip-chars-backward "[:space:]\n")
(if (looking-back "\\*/") (backward-char))
(if (rust-in-str-or-cmnt)
(rust-rewind-past-str-cmnt))
(if (/= starting (point))
(rust-rewind-irrelevant))))
(defmacro rust-eat-re (re)
`(when (looking-at ,re) (goto-char (match-end 0)) t))
(defun rust-mode-indent-line ()
(interactive)
(let ((indent
(save-excursion
(back-to-indentation)
(let ((level (rust-paren-level)))
(cond
;; A function return type is 1 level indented
((looking-at "->") (* default-tab-width (+ level 1)))
(defvar rust-char-table
(let ((table (make-char-table 'syntax-table)))
(macrolet ((def (range &rest body)
`(let ((--b (lambda (st) ,@body)))
,@(mapcar (lambda (elt)
(if (consp elt)
`(loop for ch from ,(car elt) to ,(cdr elt) collect
(set-char-table-range table ch --b))
`(set-char-table-range table ',elt --b)))
(if (consp range) range (list range))))))
(def t (forward-char) nil)
(def (32 ?\t) (skip-chars-forward " \t") nil)
(def ?\" (forward-char)
(rust-push-context st 'string (current-column) t)
(setf (rust-state-tokenize st) 'rust-token-string)
(rust-token-string st))
(def ?\' (rust-single-quote))
(def ?/ (forward-char)
(case (char-after)
(?/ (end-of-line) 'font-lock-comment-face)
(?* (forward-char)
(rust-push-context st 'comment)
(setf (rust-state-tokenize st) 'rust-token-comment)
(rust-token-comment st))
(t (skip-chars-forward rust-operator-chars) (setf rust-tcat 'op) nil)))
(def ?# (forward-char)
(cond ((eq (char-after) ?\[) (forward-char) (setf rust-tcat 'open-attr))
((rust-eat-re "[a-z_]+") (setf rust-tcat 'macro)))
'font-lock-preprocessor-face)
(def ((?a . ?z) (?A . ?Z) ?_)
(rust-token-identifier))
(def ((?0 . ?9))
(rust-eat-re "0x[0-9a-fA-F_]+\\|0b[01_]+\\|[0-9_]+\\(\\.[0-9_]+\\)?\\(e[+\\-]?[0-9_]+\\)?")
(setf rust-tcat 'atom)
(rust-eat-re "[iuf][0-9_]*")
'font-lock-constant-face)
(def ?. (forward-char)
(cond ((rust-eat-re "[0-9]+\\(e[+\\-]?[0-9]+\\)?")
(setf rust-tcat 'atom)
(rust-eat-re "f[0-9]+")
'font-lock-constant-face)
(t (setf rust-tcat (char-before)) nil)))
(def (?\( ?\) ?\[ ?\] ?\{ ?\} ?: ?\; ?,)
(forward-char)
(setf rust-tcat (char-before)) nil)
(def ?|
(skip-chars-forward rust-operator-chars)
(setf rust-tcat 'pipe) nil)
(def (?+ ?- ?% ?= ?< ?> ?! ?* ?& ?@ ?~)
(skip-chars-forward rust-operator-chars)
(setf rust-tcat 'op) nil)
table)))
;; A closing brace is 1 level unindended
((looking-at "}") (* default-tab-width (- level 1)))
(defun rust-token-identifier ()
(rust-eat-re "[a-zA-Z_][a-zA-Z0-9_]*")
(setf rust-tcat 'ident)
(if (and (eq (char-after) ?:) (eq (char-after (+ (point) 1)) ?:)
(not (eq (char-after (+ (point) 2)) ?:)))
(progn (forward-char 2) 'font-lock-builtin-face)
(match-string 0)))
;; If we're in any other token-tree / sexp, then:
;; - [ or ( means line up with the opening token
;; - { means indent to either nesting-level * tab width,
;; or one further indent from that if either current line
;; begins with 'else', or previous line didn't end in
;; semi, comma or brace, and wasn't an attribute. PHEW.
((> level 0)
(let ((pt (point)))
(rust-rewind-irrelevant)
(backward-up-list)
(if (looking-at "[[(]")
(+ 1 (current-column))
(progn
(goto-char pt)
(back-to-indentation)
(if (looking-at "\\<else\\>")
(* default-tab-width (+ 1 level))
(progn
(goto-char pt)
(beginning-of-line)
(rust-rewind-irrelevant)
(end-of-line)
(if (looking-back "[{};,]")
(* default-tab-width level)
(back-to-indentation)
(if (looking-at "#")
(* default-tab-width level)
(* default-tab-width (+ 1 level))))))))))
(defun rust-single-quote ()
(forward-char)
(setf rust-tcat 'atom)
; Is this a lifetime?
(if (or (looking-at "[a-zA-Z_]$")
(looking-at "[a-zA-Z_][^']"))
; If what we see is 'abc, use font-lock-builtin-face:
(progn (rust-eat-re "[a-zA-Z_][a-zA-Z_0-9]*")
'font-lock-builtin-face)
; Otherwise, handle as a character constant:
(let ((is-escape (eq (char-after) ?\\))
(start (point)))
(if (not (rust-eat-until-unescaped ?\'))
'font-lock-warning-face
(if (or is-escape (= (point) (+ start 2)))
'font-lock-string-face 'font-lock-warning-face)))))
;; Otherwise we're in a column-zero definition
(t 0))))))
(cond
;; If we're to the left of the indentation, reindent and jump to it.
((<= (current-column) indent)
(indent-line-to indent))
(defun rust-token-base (st)
(funcall (char-table-range rust-char-table (char-after)) st))
;; We're to the right; if it needs indent, do so but save excursion.
((not (eq (current-indentation) indent))
(save-excursion (indent-line-to indent))))))
(defun rust-eat-until-unescaped (ch)
(let (escaped)
(loop
(let ((cur (char-after)))
(when (or (eq cur ?\n) (not cur)) (return nil))
(forward-char)
(when (and (eq cur ch) (not escaped)) (return t))
(setf escaped (and (not escaped) (eq cur ?\\)))))))
(defun rust-token-string (st)
(setf rust-tcat 'atom)
(cond ((rust-eat-until-unescaped ?\")
(setf (rust-state-tokenize st) 'rust-token-base)
(rust-pop-context st))
(t (let ((align (eq (char-before) ?\\)))
(unless (eq align (rust-context-align (car (rust-state-context st))))
(setf (rust-context-align (rust-dup-context st)) align)))))
'font-lock-string-face)
;; Font-locking definitions and helpers
(defconst rust-mode-keywords
'("as"
"break"
"do"
"else" "enum" "extern"
"false" "fn" "for"
"if" "impl"
"let" "loop"
"match" "mod" "mut"
"priv" "pub"
"ref" "return"
"self" "static" "struct" "super"
"true" "trait" "type"
"unsafe" "use"
"while"))
(defun rust-token-comment (st)
(let ((eol (point-at-eol)))
(loop
(unless (re-search-forward "\\(/\\*\\)\\|\\(\\*/\\)" eol t)
(goto-char eol)
(return))
(if (match-beginning 1)
(push (car (rust-state-context st)) (rust-state-context st))
(rust-pop-context st)
(unless (eq (rust-context-type (car (rust-state-context st))) 'comment)
(setf (rust-state-tokenize st) 'rust-token-base)
(return))))
'font-lock-comment-face))
(defconst rust-special-types
'("u8" "i8"
"u16" "i16"
"u32" "i32"
"u64" "i64"
(defun rust-next-block-info (st)
(dolist (cx (rust-state-context st))
(when (eq (rust-context-type cx) ?\}) (return (rust-context-info cx)))))
"f32" "f64"
"float" "int" "uint"
"bool"
"str" "char"))
(defun rust-is-capitalized (string)
(let ((case-fold-search nil))
(string-match-p "[A-Z]" string)))
(defconst rust-re-ident "[[:word:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*")
(defconst rust-re-CamelCase "[[:upper:]][[:word:][:multibyte:]_[:digit:]]*")
(defun rust-re-word (inner) (concat "\\<" inner "\\>"))
(defun rust-re-grab (inner) (concat "\\(" inner "\\)"))
(defun rust-re-grabword (inner) (rust-re-grab (rust-re-word inner)))
(defun rust-re-item-def (itype)
(concat (rust-re-word itype) "[[:space:]]+" (rust-re-grab rust-re-ident)))
(defun rust-token (st)
(let ((cx (car (rust-state-context st))))
(when (bolp)
(setf (rust-state-indent st) (current-indentation))
(when (eq (rust-context-align cx) 'unset)
(setf (rust-context-align cx) nil)))
(setf rust-tcat nil)
(let* ((tok (funcall (rust-state-tokenize st) st))
(tok-id (or tok rust-tcat))
(cur-cx (rust-context-type cx))
(cx-info (rust-context-info cx)))
(when (stringp tok)
(setf tok-id (gethash tok rust-value-keywords nil))
(setf tok (cond ((eq tok-id 'atom) 'font-lock-constant-face)
(tok-id 'font-lock-keyword-face)
((equal (rust-state-last-token st) 'def) 'font-lock-function-name-face)
((and rust-capitalized-idents-are-types
(rust-is-capitalized tok)) 'font-lock-type-face)
(t nil))))
(when rust-tcat
(when (eq (rust-context-align cx) 'unset)
(setf (rust-context-align cx) t))
(when (eq cx-info 'alt-1)
(setf cx (rust-dup-context st))
(setf (rust-context-info cx) 'alt-2))
(when (and (eq rust-tcat 'pipe) (eq (rust-state-last-token st) ?{))
(setf cx (rust-dup-context st))
(setf (rust-context-info cx) 'block))
(case rust-tcat
((?\; ?,) (when (eq cur-cx 'statement) (rust-pop-context st)))
(?\{
(when (and (eq cur-cx 'statement) (not (member cx-info '(alt-1 alt-2))))
(rust-pop-context st))
(when (eq cx-info 'alt-2)
(setf cx (rust-dup-context st))
(setf (rust-context-info cx) nil))
(let ((next-info (rust-next-block-info st))
(newcx (rust-push-context st ?\} (current-column))))
(cond ((eq cx-info 'alt-2) (setf (rust-context-info newcx) 'alt-outer))
((eq next-info 'alt-outer) (setf (rust-context-info newcx) 'alt-inner)))))
((?\[ open-attr)
(let ((newcx (rust-push-context st ?\] (current-column))))
(when (eq rust-tcat 'open-attr)
(setf (rust-context-info newcx) 'attr))))
(?\( (rust-push-context st ?\) (current-column))
(when (eq (rust-context-info cx) 'attr)
(setf (rust-context-info (car (rust-state-context st))) 'attr)))
(?\} (when (eq cur-cx 'statement) (rust-pop-context st))
(when (eq (rust-context-type (car (rust-state-context st))) ?})
(rust-pop-context st))
(setf cx (car (rust-state-context st)))
(when (and (eq (rust-context-type cx) 'statement)
(not (eq (rust-context-info cx) 'alt-2)))
(rust-pop-context st)))
(t (cond ((eq cur-cx rust-tcat)
(when (eq (rust-context-info (rust-pop-context st)) 'attr)
(setf tok 'font-lock-preprocessor-face)
(when (eq (rust-context-type (car (rust-state-context st))) 'statement)
(rust-pop-context st))))
((or (and (eq cur-cx ?\}) (not (eq (rust-context-info cx) 'alt-outer)))
(eq cur-cx 'top))
(rust-push-context st 'statement)))))
(setf (rust-state-last-token st) tok-id))
(setf cx (car (rust-state-context st)))
(when (and (eq tok-id 'alt) (eq (rust-context-type cx) 'statement))
(setf (rust-context-info cx) 'alt-1))
(when (and (eq (rust-state-last-token st) 'pipe)
(eq (rust-next-block-info st) 'block) (eolp))
(when (eq (rust-context-type cx) 'statement) (rust-pop-context st))
(setf cx (rust-dup-context st)
(rust-context-info cx) nil
(rust-context-align cx) nil))
(if (eq (rust-context-info cx) 'attr)
'font-lock-preprocessor-face
tok))))
(defvar rust-mode-font-lock-keywords
(append
`(
;; Keywords proper
(,(regexp-opt rust-mode-keywords 'words) . font-lock-keyword-face)
;; Special types
(,(regexp-opt rust-special-types 'words) . font-lock-type-face)
;; Attributes like `#[bar(baz)]`
(,(rust-re-grab (concat "#\\[" rust-re-ident "[^]]*\\]"))
1 font-lock-preprocessor-face)
;; 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)
;; Module names like `foo::`, highlight including the ::
(,(rust-re-grab (concat rust-re-ident "::")) 1 font-lock-type-face)
;; Lifetimes like `'foo`
(,(concat "'" (rust-re-grab rust-re-ident) "[^']") 1 font-lock-variable-name-face)
;; Character constants, since they're not treated as strings
;; in order to have sufficient leeway to parse 'lifetime above.
(,(rust-re-grab "'[^']'") 1 font-lock-string-face)
(,(rust-re-grab "'\\\\[nrt]'") 1 font-lock-string-face)
(,(rust-re-grab "'\\\\x[[:xdigit:]]\\{2\\}'") 1 font-lock-string-face)
(,(rust-re-grab "'\\\\u[[:xdigit:]]\\{4\\}'") 1 font-lock-string-face)
(,(rust-re-grab "'\\\\U[[:xdigit:]]\\{8\\}'") 1 font-lock-string-face)
;; CamelCase Means Type Or Constructor
(,(rust-re-grabword rust-re-CamelCase) 1 font-lock-type-face)
)
;; Item definitions
(loop for (item . face) in
'(("enum" . font-lock-type-face)
("struct" . font-lock-type-face)
("type" . font-lock-type-face)
("mod" . font-lock-type-face)
("use" . font-lock-type-face)
("fn" . font-lock-function-name-face)
("static" . font-lock-constant-face))
collect `(,(rust-re-item-def item) 1 ,face))))
;; For compatibility with Emacs < 24, derive conditionally
(defalias 'rust-parent-mode
(if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode))
(defun rust-indent (st)
(let ((cx (car (rust-state-context st)))
(parent (cadr (rust-state-context st))))
(when (and (eq (rust-context-type cx) 'statement)
(or (eq (char-after) ?\}) (looking-at "with \\|{[ ]*$")))
(setf cx parent parent (caddr (rust-state-context st))))
(let* ((tp (rust-context-type cx))
(closing (eq tp (char-after)))
(unit rust-indent-unit)
(base (if (and (eq tp 'statement) parent (rust-context-align parent))
(rust-context-column parent) (rust-context-indent cx))))
(cond ((eq tp 'comment) base)
((eq tp 'string) (if (rust-context-align cx) (rust-context-column cx) 0))
((eq tp 'statement) (+ base (if (eq (char-after) ?\}) 0 unit)))
((eq (rust-context-align cx) t) (+ (rust-context-column cx) (if closing -1 0)))
(t (+ base (if closing 0 unit)))))))
;;;###autoload
(define-derived-mode rust-mode fundamental-mode "Rust"
"Major mode for editing Rust source files."
(set-syntax-table rust-syntax-table)
(setq major-mode 'rust-mode mode-name "Rust")
(run-hooks 'rust-mode-hook)
(set (make-local-variable 'indent-tabs-mode) nil)
(let ((par "[ ]*\\(//+\\|\\**\\)[ ]*$"))
(set (make-local-variable 'paragraph-start) par)
(set (make-local-variable 'paragraph-separate) par))
(set (make-local-variable 'comment-start) "//")
(cm-mode (make-cm-mode 'rust-token 'make-rust-state 'copy-sequence 'equal 'rust-indent)))
(define-derived-mode rust-mode rust-parent-mode "Rust"
"Major mode for Rust code."
;; Basic syntax
(set-syntax-table rust-mode-syntax-table)
;; Indentation
(set (make-local-variable 'indent-line-function)
'rust-mode-indent-line)
;; Fonts
(set (make-local-variable 'font-lock-defaults)
'(rust-mode-font-lock-keywords nil nil nil nil))
;; Misc
(set (make-local-variable 'comment-start) "// ")
(set (make-local-variable 'comment-end) "")
(set (make-local-variable 'indent-tabs-mode) nil))
(define-key rust-mode-map "}" 'rust-electric-brace)
(define-key rust-mode-map "{" 'rust-electric-brace)
;;;###autoload
(progn
(add-to-list 'auto-mode-alist '("\\.rs$" . rust-mode))
(add-to-list 'auto-mode-alist '("\\.rc$" . rust-mode)))
(add-to-list 'auto-mode-alist '("\\.rs$" . rust-mode))
(defun rust-mode-reload ()
(interactive)
(unload-feature 'rust-mode)
(require 'rust-mode)
(rust-mode))
(provide 'rust-mode)