Permalink
Browse files

Add rust-mode.

  • Loading branch information...
1 parent cdca59e commit f6a4a7c9884cec514598043c25fbfb7faec62ba1 @bretthoerner committed Apr 5, 2013
Showing with 639 additions and 0 deletions.
  1. +4 −0 init.el
  2. +14 −0 rust/Makefile
  3. +100 −0 rust/README.md
  4. +194 −0 rust/cm-mode.el
  5. +327 −0 rust/rust-mode.el
View
@@ -629,6 +629,10 @@ makes)."
(add-to-list 'load-path (concat dotfiles-dir "ruby"))
(add-to-list 'auto-mode-alist '("\\.rake$" . ruby-mode))
+;; rust
+(add-to-list 'load-path (concat dotfiles-dir "rust"))
+(require 'rust-mode)
+
;; savehist
(savehist-mode 1)
View
@@ -0,0 +1,14 @@
+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
@@ -0,0 +1,100 @@
+rust-mode: A major emacs mode for editing Rust source code
+==========================================================
+
+`rust-mode` makes editing [Rust](http://rust-lang.org) code with emacs
+enjoyable.
+
+
+### Manual Installation
+
+To install manually, check out this repository and add this to your .emacs
+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`.
+
+### package.el installation via Marmalade or MELPA
+
+It can be more convenient to use Emacs's package manager to handle
+installation for you if you use many elisp libraries. If you have
+package.el but haven't added Marmalade or MELPA, the community package source,
+yet, add this to ~/.emacs.d/init.el:
+
+Using Marmalade:
+
+```lisp
+(require 'package)
+(add-to-list 'package-archives
+ '("marmalade" . "http://marmalade-repo.org/packages/"))
+(package-initialize)
+```
+
+Using MELPA:
+
+```lisp
+(require 'package)
+(add-to-list 'package-archives
+ '("melpa" . "http://melpa.milkbox.net/packages/") t)
+(package-initialize)
+```
+
+Then do this to load the package listing:
+
+* <kbd>M-x eval-buffer</kbd>
+* <kbd>M-x package-refresh-contents</kbd>
+
+If you use a version of Emacs prior to 24 that doesn't include
+package.el, you can get it from http://bit.ly/pkg-el23.
+
+If you have an older ELPA package.el installed from tromey.com, you
+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
+them from a list:
+
+* <kbd>M-x package-list-packages</kbd>
+
+Now, to install packages, move your cursor to them and press i. This
+will mark the packages for installation. When you're done with
+marking, press x, and ELPA will install the packages for you (under
+~/.emacs.d/elpa/).
+
+* or using <kbd>M-x package-install rust-mode
View
@@ -0,0 +1,194 @@
+;;; 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
Oops, something went wrong.

0 comments on commit f6a4a7c

Please sign in to comment.