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

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

[ELPA-diffs] /srv/bzr/emacs/elpa r239: Add coffee-mode.


From: Stefan Monnier
Subject: [ELPA-diffs] /srv/bzr/emacs/elpa r239: Add coffee-mode.
Date: Sun, 15 Jul 2012 18:30:24 -0400
User-agent: Bazaar (2.5.0)

------------------------------------------------------------
revno: 239 [merge]
committer: Stefan Monnier <address@hidden>
branch nick: elpa
timestamp: Sun 2012-07-15 18:30:24 -0400
message:
  Add coffee-mode.
added:
  packages/coffee-mode/
  packages/coffee-mode/README.md
  packages/coffee-mode/coffee-mode-pkg.el
  packages/coffee-mode/coffee-mode.el
  packages/coffee-mode/examples/
  packages/coffee-mode/examples/basic.coffee
  packages/coffee-mode/examples/edge.coffee
  packages/coffee-mode/examples/imenu.coffee
=== added directory 'packages/coffee-mode'
=== added file 'packages/coffee-mode/README.md'
--- a/packages/coffee-mode/README.md    1970-01-01 00:00:00 +0000
+++ b/packages/coffee-mode/README.md    2012-07-08 23:33:43 +0000
@@ -0,0 +1,284 @@
+CoffeeScript Major Mode
+=======================
+
+An Emacs major mode for [CoffeeScript][cs], unfancy JavaScript.
+
+Provides syntax highlighting, indentation support, imenu support,
+a menu bar, and a few cute commands.
+
+![Screenshot](http://img.skitch.com/20100308-fcr622c95ibey4m474d5m1m1qt.png)
+
+## Installation
+
+In your shell:
+
+    $ cd ~/.emacs.d/vendor
+    $ git clone git://github.com/defunkt/coffee-mode.git
+
+In your emacs config:
+
+    (add-to-list 'load-path "~/.emacs.d/vendor/coffee-mode")
+    (require 'coffee-mode)
+
+If `coffee-mode` is not enabled automatically for any files ending in
+".coffee" or named "Cakefile", add this to your emacs config as well:
+
+    (add-to-list 'auto-mode-alist '("\\.coffee$" . coffee-mode))
+    (add-to-list 'auto-mode-alist '("Cakefile" . coffee-mode))
+
+[coffee-mode used to offer automatic deletion of trailing whitespace.
+This is now left to whitespace-mode. See its documentation for full
+details, but as a hint, configure:
+
+    (setq whitespace-action '(auto-cleanup)) ;; automatically clean up bad 
whitespace
+    (setq whitespace-style '(trailing space-before-tab indentation empty 
space-after-tab)) ;; only show bad whitespace
+
+Then turn on whitespace-mode, or global-whitespace-mode.]
+
+## Indentation
+
+### TAB Theory
+
+It goes like this: when you press `TAB`, we indent the line unless
+doing so would make the current line more than two indentation levels
+deepers than the previous line. If that's the case, remove all
+indentation.
+
+Consider this code, with point at the position indicated by the
+caret:
+
+    line1()
+      line2()
+      line3()
+         ^
+
+Pressing `TAB` will produce the following code:
+
+    line1()
+      line2()
+        line3()
+           ^
+
+Pressing `TAB` again will produce this code:
+
+    line1()
+      line2()
+    line3()
+       ^
+
+And so on. I think this is a pretty good way of getting decent
+indentation with a whitespace-sensitive language.
+
+### Newline and Indent
+
+We all love hitting `RET` and having the next line indented
+properly. Given this code and cursor position:
+
+    line1()
+      line2()
+      line3()
+            ^
+
+Pressing `RET` would insert a newline and place our cursor at the
+following position:
+
+    line1()
+      line2()
+      line3()
+
+      ^
+
+In other words, the level of indentation is maintained. This
+applies to comments as well. Combined with the `TAB` you should be
+able to get things where you want them pretty easily.
+
+### Indenters
+
+`class`, `for`, `if`, and possibly other keywords cause the next line
+to be indented a level deeper automatically.
+
+For example, given this code and cursor position::
+
+    class Animal
+                ^
+
+Pressing enter would produce the following:
+
+    class Animal
+
+      ^
+
+That is, indented a column deeper.
+
+This also applies to lines ending in `->`, `=>`, `{`, `[`, and
+possibly more characters.
+
+So this code and cursor position:
+
+    $('#demo').click ->
+                       ^
+
+On enter would produce this:
+
+    $('#demo').click ->
+
+      ^
+
+Pretty slick.
+
+## imenu
+
+If you're using imenu, `coffee-mode` should work just fine. This
+means users of [textmate.el][tm] will find that `⇧⌘T`
+(`textmate-go-to-symbol`) mostly works as expected.
+
+If you're not using imenu check out [this page][im] or textmate.el for
+a really awesome way to jump quickly to a function's definition in a
+file.
+
+## Commands
+
+If you have `easymenu` you can get to any of these commands from the
+menu bar:
+
+![coffee-mode menu 
bar](http://img.skitch.com/20100308-tt5yn51h2jww2pmjqaawed6eq8.png)
+
+### coffee-compile-file
+
+Compiles the current file as a JavaScript file. Doesn't open it or
+anything special for you.
+
+Operating on "basic.coffee" and running this command will save a
+"basic.js" in the same directory. Subsequent runs will overwrite the
+file.
+
+If there are compilation errors and we the compiler have returned a
+line number to us for the first error, the point is moved to that
+line, so you can investigate.  If this annoys you, you can set
+`coffee-compile-jump-to-error` to `nil`.
+
+### coffee-compile-buffer
+
+Compiles the current buffer to JavaScript using the command specified
+by the `coffee-command` variable and opens the contents in a new
+buffer using the mode configured for ".js" files.
+
+Bind it:
+
+    (define-key coffee-mode-map [(meta r)] 'coffee-compile-buffer)
+
+### coffee-compile-region
+
+Compiles the selected region to JavaScript using the same
+configuration variables as `coffee-compile-buffer`.
+
+Bind it:
+
+    (define-key coffee-mode-map [(meta R)] 'coffee-compile-region)
+
+### Compile-on-save
+
+Hitting the key sequence `C-c C-o C-s` turns on (toggles) the
+compile-on-save minor mode in `coffee-mode`.  To enable it by default:
+
+    (add-hook 'coffee-mode-hook '(lambda () (coffee-cos-mode t)))
+
+### coffee-repl
+
+Starts a repl in a new buffer using `coffee-command`.
+
+## Hooks
+
+### coffee-mode-hook
+
+Naturally. Example:
+
+    (defun coffee-custom ()
+      "coffee-mode-hook"
+
+      ;; CoffeeScript uses two spaces.
+      (make-local-variable 'tab-width)
+      (set 'tab-width 2)
+
+      ;; If you don't want your compiled files to be wrapped
+      (setq coffee-args-compile '("-c" "--bare"))
+
+      ;; Emacs key binding
+      (define-key coffee-mode-map [(meta r)] 'coffee-compile-buffer)
+
+      ;; Riding edge.
+      (setq coffee-command "~/dev/coffee")
+
+      ;; Compile '.coffee' files on every save
+      (and (file-exists-p (buffer-file-name))
+           (file-exists-p (coffee-compiled-file-name))
+           (coffee-cos-mode t)))
+
+    (add-hook 'coffee-mode-hook 'coffee-custom)
+
+## Configuration
+
+You can customize any of the following options using `M-x
+customize-group` with "coffee" as the group.
+
+You can also customize then with `coffee-mode-hook`, as demonstrated
+above.
+
+### coffee-tab-width
+
+The tab width to use when indenting.
+
+Default: `tab-width`
+
+### coffee-command
+
+The CoffeeScript command used for evaluating code. Must be in your
+path.
+
+Default: `"coffee"`
+
+### coffee-args-repl
+
+The command line arguments to pass to `coffee-command' to start a
+REPL.
+
+Default: `'("-i")`
+
+### coffee-args-compile
+
+The command line arguments to pass to `coffee-command' when compiling a file.
+
+Default: `'("-c")`
+
+### coffee-compiled-buffer-name
+
+The name of the scratch buffer used when compiling CoffeeScript.
+
+Default: `"*coffee-compiled*"`
+
+### coffee-compile-jump-to-error
+
+Whether to jump to the first error if compilation fails.  Please note
+that the coffee compiler doesn't always give a line number for the
+issue and in that case it is not possible to jump to the error, of
+course.
+
+Default: `t`
+
+## Thanks
+
+* Jeremy Ashkenas for CoffeeScript
+* <http://xahlee.org/emacs/elisp_syntax_coloring.html> for instructions.
+* Jason Blevins for the guidance his markdown-mode.el gave.
+* Steve Yegge for js2
+
+## Bugs
+
+Prototype accessor assignments like `String::length: -> 10` don't look
+great.
+
+Please file bugs at <http://github.com/defunkt/coffee-mode/issues>
+
+[cs]: http://jashkenas.github.com/coffee-script/
+[tm]: http://github.com/defunkt/textmate.el
+[im]: http://chopmo.blogspot.com/2008/09/quickly-jumping-to-symbols.html

=== added file 'packages/coffee-mode/coffee-mode-pkg.el'
--- a/packages/coffee-mode/coffee-mode-pkg.el   1970-01-01 00:00:00 +0000
+++ b/packages/coffee-mode/coffee-mode-pkg.el   2012-07-08 23:33:43 +0000
@@ -0,0 +1,1 @@
+(define-package "coffee-mode" "0.4.1" "Major mode to edit CoffeeScript files 
in Emacs.")

=== added file 'packages/coffee-mode/coffee-mode.el'
--- a/packages/coffee-mode/coffee-mode.el       1970-01-01 00:00:00 +0000
+++ b/packages/coffee-mode/coffee-mode.el       2012-07-08 23:33:43 +0000
@@ -0,0 +1,610 @@
+;;; coffee-mode.el --- Major mode to edit CoffeeScript files in Emacs
+
+;; Copyright (C) 2010 Chris Wanstrath
+
+;; Version: 0.4.1
+;; Keywords: CoffeeScript major mode
+;; Author: Chris Wanstrath <address@hidden>
+;; URL: http://github.com/defunkt/coffee-mode
+
+;; This file is not part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program; if not, write to the Free Software
+;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;; Commentary
+
+;; For commentary please see the README.md or
+;; http://github.com/defunkt/coffee-mode#readme
+
+;;; Installation
+
+;; In your shell:
+
+;;     $ cd ~/.emacs.d/vendor
+;;     $ git clone git://github.com/defunkt/coffee-mode.git
+
+;; In your emacs config:
+
+;;     (add-to-list 'load-path "~/.emacs.d/vendor/coffee-mode")
+;;     (require 'coffee-mode)
+
+;;; Thanks
+
+;; Major thanks to http://xahlee.org/emacs/elisp_syntax_coloring.html
+;; the instructions.
+
+;; Also thanks to Jason Blevins's markdown-mode.el and Steve Yegge's
+;; js2-mode for guidance.
+
+;; TODO:
+;; - Execute {buffer,region,line} and show output in new buffer
+;; - Make prototype accessor assignments like `String::length: -> 10` pretty.
+;; - mirror-mode - close brackets and parens automatically
+
+;;; Code:
+
+(require 'comint)
+(require 'easymenu)
+(require 'font-lock)
+
+(eval-when-compile
+  (require 'cl))
+
+;;
+;; Customizable Variables
+;;
+
+(defconst coffee-mode-version "0.4.1"
+  "The version of `coffee-mode'.")
+
+(defgroup coffee nil
+  "A CoffeeScript major mode."
+  :group 'languages)
+
+(defcustom coffee-tab-width tab-width
+  "The tab width to use when indenting."
+  :type 'integer
+  :group 'coffee)
+
+(defcustom coffee-command "coffee"
+  "The CoffeeScript command used for evaluating code."
+  :type 'string
+  :group 'coffee)
+
+(defcustom js2coffee-command "js2coffee"
+  "The js2coffee command used for evaluating code."
+  :type 'string
+  :group 'coffee)
+
+(defcustom coffee-args-repl '("-i")
+  "The arguments to pass to `coffee-command' to start a REPL."
+  :type 'list
+  :group 'coffee)
+
+(defcustom coffee-args-compile '("-c")
+  "The arguments to pass to `coffee-command' to compile a file."
+  :type 'list
+  :group 'coffee)
+
+(defcustom coffee-compiled-buffer-name "*coffee-compiled*"
+  "The name of the scratch buffer used for compiled CoffeeScript."
+  :type 'string
+  :group 'coffee)
+
+(defcustom coffee-compile-jump-to-error t
+  "Whether to jump to the first error if compilation fails.
+Please note that the coffee compiler doesn't always give a line
+number for the issue and in that case it is not possible to jump
+to the error."
+  :type 'boolean
+  :group 'coffee)
+
+(defcustom coffee-watch-buffer-name "*coffee-watch*"
+  "The name of the scratch buffer used when using the --watch flag
+with CoffeeScript."
+  :type 'string
+  :group 'coffee)
+
+(defcustom coffee-mode-hook nil
+  "Hook called by `coffee-mode'."
+  :type 'hook
+  :group 'coffee)
+
+(defvar coffee-mode-map (make-keymap)
+  "Keymap for CoffeeScript major mode.")
+
+;;
+;; Commands
+;;
+
+(defun coffee-repl ()
+  "Launch a CoffeeScript REPL using `coffee-command' as an inferior mode."
+  (interactive)
+
+  (unless (comint-check-proc "*CoffeeREPL*")
+    (set-buffer
+     (apply 'make-comint "CoffeeREPL"
+            coffee-command nil coffee-args-repl)))
+
+  (pop-to-buffer "*CoffeeREPL*"))
+
+(defun coffee-compiled-file-name (&optional filename)
+  "Returns the name of the JavaScript file compiled from a CoffeeScript file.
+If FILENAME is omitted, the current buffer's file name is used."
+  (concat (file-name-sans-extension (or filename (buffer-file-name))) ".js"))
+
+(defun coffee-compile-file ()
+  "Compiles and saves the current file to disk."
+  (interactive)
+  (let ((compiler-output (shell-command-to-string (coffee-command-compile 
(buffer-file-name)))))
+    (if (string= compiler-output "")
+        (message "Compiled and saved %s" (coffee-compiled-file-name))
+      (let* ((msg (car (split-string compiler-output "[\n\r]+")))
+             (line (and (string-match "on line \\([0-9]+\\)" msg)
+                        (string-to-number (match-string 1 msg)))))
+        (message msg)
+        (when (and coffee-compile-jump-to-error line (> line 0))
+          (goto-char (point-min))
+          (forward-line (1- line)))))))
+
+(defun coffee-compile-buffer ()
+  "Compiles the current buffer and displays the JavaScript in a buffer
+called `coffee-compiled-buffer-name'."
+  (interactive)
+  (save-excursion
+    (coffee-compile-region (point-min) (point-max))))
+
+(defun coffee-compile-region (start end)
+  "Compiles a region and displays the JavaScript in a buffer called
+`coffee-compiled-buffer-name'."
+  (interactive "r")
+
+  (let ((buffer (get-buffer coffee-compiled-buffer-name)))
+    (when buffer
+      (kill-buffer buffer)))
+
+  (apply (apply-partially 'call-process-region start end coffee-command nil
+                          (get-buffer-create coffee-compiled-buffer-name)
+                          nil)
+         (append coffee-args-compile (list "-s" "-p")))
+  (switch-to-buffer (get-buffer coffee-compiled-buffer-name))
+  (let ((buffer-file-name "tmp.js")) (set-auto-mode))
+  (goto-char (point-min)))
+
+(defun coffee-js2coffee-replace-region (start end)
+  "Convert JavaScript in the region into CoffeeScript."
+  (interactive "r")
+
+  (let ((buffer (get-buffer coffee-compiled-buffer-name)))
+    (when buffer
+      (kill-buffer buffer)))
+
+  (call-process-region start end
+                       js2coffee-command nil
+                       (current-buffer))
+  (delete-region start end))
+
+(defun coffee-version ()
+  "Show the `coffee-mode' version in the echo area."
+  (interactive)
+  (message (concat "coffee-mode version " coffee-mode-version)))
+
+(defun coffee-watch (dir-or-file)
+  "Run `coffee-run-cmd' with the --watch flag on a directory or file."
+  (interactive "fDirectory or File: ")
+  (let ((coffee-compiled-buffer-name coffee-watch-buffer-name)
+        (args (mapconcat 'identity (append coffee-args-compile (list "--watch" 
(expand-file-name dir-or-file))) " ")))
+    (coffee-run-cmd args)))
+
+;;
+;; Menubar
+;;
+
+(easy-menu-define coffee-mode-menu coffee-mode-map
+  "Menu for CoffeeScript mode"
+  '("CoffeeScript"
+    ["Compile File" coffee-compile-file]
+    ["Compile Buffer" coffee-compile-buffer]
+    ["Compile Region" coffee-compile-region]
+    ["REPL" coffee-repl]
+    "---"
+    ["Version" coffee-show-version]
+    ))
+
+;;
+;; Define Language Syntax
+;;
+
+;; String literals
+(defvar coffee-string-regexp 
"\"\\([^\\]\\|\\\\.\\)*?\"\\|'\\([^\\]\\|\\\\.\\)*?'")
+
+;; Instance variables (implicit this)
+(defvar coffee-this-regexp "@\\(\\w\\|_\\)*\\|this")
+
+;; Prototype::access
+(defvar coffee-prototype-regexp "\\(\\(\\w\\|\\.\\|_\\| 
\\|$\\)+?\\)::\\(\\(\\w\\|\\.\\|_\\| \\|$\\)+?\\):")
+
+;; Assignment
+(defvar coffee-assign-regexp "\\(\\(\\w\\|\\.\\|_\\|$\\)+?\s*\\):")
+
+;; Lambda
+(defvar coffee-lambda-regexp "\\((.+)\\)?\\s *\\(->\\|=>\\)")
+
+;; Namespaces
+(defvar coffee-namespace-regexp "\\b\\(class\\s +\\(\\S +\\)\\)\\b")
+
+;; Booleans
+(defvar coffee-boolean-regexp 
"\\b\\(true\\|false\\|yes\\|no\\|on\\|off\\|null\\|undefined\\)\\b")
+
+;; Regular Expressions
+(defvar coffee-regexp-regexp 
"\\/\\(\\\\.\\|\\[\\(\\\\.\\|.\\)+?\\]\\|[^/]\\)+?\\/")
+
+;; JavaScript Keywords
+(defvar coffee-js-keywords
+      '("if" "else" "new" "return" "try" "catch"
+        "finally" "throw" "break" "continue" "for" "in" "while"
+        "delete" "instanceof" "typeof" "switch" "super" "extends"
+        "class" "until" "loop"))
+
+;; Reserved keywords either by JS or CS.
+(defvar coffee-js-reserved
+      '("case" "default" "do" "function" "var" "void" "with"
+        "const" "let" "debugger" "enum" "export" "import" "native"
+        "__extends" "__hasProp"))
+
+;; CoffeeScript keywords.
+(defvar coffee-cs-keywords
+      '("then" "unless" "and" "or" "is"
+        "isnt" "not" "of" "by" "where" "when"))
+
+;; Regular expression combining the above three lists.
+(defvar coffee-keywords-regexp (regexp-opt
+                                (append
+                                 coffee-js-reserved
+                                 coffee-js-keywords
+                                 coffee-cs-keywords) 'words))
+
+
+;; Create the list for font-lock. Each class of keyword is given a
+;; particular face.
+(defvar coffee-font-lock-keywords
+  ;; *Note*: order below matters. `coffee-keywords-regexp' goes last
+  ;; because otherwise the keyword "state" in the function
+  ;; "state_entry" would be highlighted.
+  `((,coffee-string-regexp . font-lock-string-face)
+    (,coffee-this-regexp . font-lock-variable-name-face)
+    (,coffee-prototype-regexp . font-lock-variable-name-face)
+    (,coffee-assign-regexp . font-lock-type-face)
+    (,coffee-regexp-regexp . font-lock-constant-face)
+    (,coffee-boolean-regexp . font-lock-constant-face)
+    (,coffee-keywords-regexp . font-lock-keyword-face)))
+
+;;
+;; Helper Functions
+;;
+
+(defun coffee-comment-dwim (arg)
+  "Comment or uncomment current line or region in a smart way.
+For details, see `comment-dwim'."
+  (interactive "*P")
+  (require 'newcomment)
+  (let ((deactivate-mark nil) (comment-start "#") (comment-end ""))
+    (comment-dwim arg)))
+
+(defun coffee-command-compile (file-name)
+  "Run `coffee-command' to compile FILE."
+  (let ((full-file-name (expand-file-name file-name)))
+    (mapconcat 'identity (append (list coffee-command) coffee-args-compile 
(list full-file-name)) " ")))
+
+(defun coffee-run-cmd (args)
+  "Run `coffee-command' with the given arguments, and display the
+output in a compilation buffer."
+  (interactive "sArguments: ")
+  (let ((compilation-buffer-name-function (lambda (this-mode)
+                                            (generate-new-buffer-name 
coffee-compiled-buffer-name))))
+    (compile (concat coffee-command " " args))))
+
+;;
+;; imenu support
+;;
+
+;; This is a pretty naive but workable way of doing it. First we look
+;; for any lines that starting with `coffee-assign-regexp' that include
+;; `coffee-lambda-regexp' then add those tokens to the list.
+;;
+;; Should cover cases like these:
+;;
+;; minus: (x, y) -> x - y
+;; String::length: -> 10
+;; block: ->
+;;   print('potion')
+;;
+;; Next we look for any line that starts with `class' or
+;; `coffee-assign-regexp' followed by `{` and drop into a
+;; namespace. This means we search one indentation level deeper for
+;; more assignments and add them to the alist prefixed with the
+;; namespace name.
+;;
+;; Should cover cases like these:
+;;
+;; class Person
+;;   print: ->
+;;     print 'My name is ' + this.name + '.'
+;;
+;; class Policeman extends Person
+;;   constructor: (rank) ->
+;;     @rank: rank
+;;   print: ->
+;;     print 'My name is ' + this.name + " and I'm a " + this.rank + '.'
+;;
+;; TODO:
+;; app = {
+;;   window:  {width: 200, height: 200}
+;;   para:    -> 'Welcome.'
+;;   button:  -> 'OK'
+;; }
+
+(defun coffee-imenu-create-index ()
+  "Create an imenu index of all methods in the buffer."
+  (interactive)
+
+  ;; This function is called within a `save-excursion' so we're safe.
+  (goto-char (point-min))
+
+  (let ((index-alist '()) assign pos indent ns-name ns-indent)
+    ;; Go through every assignment that includes -> or => on the same
+    ;; line or starts with `class'.
+    (while (re-search-forward
+            (concat "^\\(\\s *\\)"
+                    "\\("
+                      coffee-assign-regexp
+                      ".+?"
+                      coffee-lambda-regexp
+                    "\\|"
+                      coffee-namespace-regexp
+                    "\\)")
+            (point-max)
+            t)
+
+      ;; If this is the start of a new namespace, save the namespace's
+      ;; indentation level and name.
+      (when (match-string 8)
+        ;; Set the name.
+        (setq ns-name (match-string 8))
+
+        ;; If this is a class declaration, add :: to the namespace.
+        (setq ns-name (concat ns-name "::"))
+
+        ;; Save the indentation level.
+        (setq ns-indent (length (match-string 1))))
+
+      ;; If this is an assignment, save the token being
+      ;; assigned. `Please.print:` will be `Please.print`, `block:`
+      ;; will be `block`, etc.
+      (when (setq assign (match-string 3))
+          ;; The position of the match in the buffer.
+          (setq pos (match-beginning 3))
+
+          ;; The indent level of this match
+          (setq indent (length (match-string 1)))
+
+          ;; If we're within the context of a namespace, add that to the
+          ;; front of the assign, e.g.
+          ;; constructor: => Policeman::constructor
+          (when (and ns-name (> indent ns-indent))
+            (setq assign (concat ns-name assign)))
+
+          ;; Clear the namespace if we're no longer indented deeper
+          ;; than it.
+          (when (and ns-name (<= indent ns-indent))
+            (setq ns-name nil)
+            (setq ns-indent nil))
+
+          ;; Add this to the alist. Done.
+          (push (cons assign pos) index-alist)))
+
+    ;; Return the alist.
+    index-alist))
+
+;;
+;; Indentation
+;;
+
+;;; The theory is explained in the README.
+
+(defun coffee-indent-line ()
+  "Indent current line as CoffeeScript."
+  (interactive)
+
+  (if (= (point) (point-at-bol))
+      (insert-tab)
+    (save-excursion
+      (let ((prev-indent (coffee-previous-indent))
+            (cur-indent (current-indentation)))
+        ;; Shift one column to the left
+        (beginning-of-line)
+        (insert-tab)
+
+        (when (= (point-at-bol) (point))
+          (forward-char coffee-tab-width))
+
+        ;; We're too far, remove all indentation.
+        (when (> (- (current-indentation) prev-indent) coffee-tab-width)
+          (backward-to-indentation 0)
+          (delete-region (point-at-bol) (point)))))))
+
+(defun coffee-previous-indent ()
+  "Return the indentation level of the previous non-blank line."
+  (save-excursion
+    (forward-line -1)
+    (if (bobp)
+        0
+      (progn
+        (while (and (looking-at "^[ \t]*$") (not (bobp))) (forward-line -1))
+        (current-indentation)))))
+
+(defun coffee-newline-and-indent ()
+  "Insert a newline and indent it to the same level as the previous line."
+  (interactive)
+
+  ;; Remember the current line indentation level,
+  ;; insert a newline, and indent the newline to the same
+  ;; level as the previous line.
+  (let ((prev-indent (current-indentation)) (indent-next nil))
+    (delete-horizontal-space t)
+    (newline)
+    (insert-tab (/ prev-indent coffee-tab-width))
+
+    ;; We need to insert an additional tab because the last line was special.
+    (when (coffee-line-wants-indent)
+      (insert-tab)))
+
+  ;; Last line was a comment so this one should probably be,
+  ;; too. Makes it easy to write multi-line comments (like the one I'm
+  ;; writing right now).
+  (when (coffee-previous-line-is-comment)
+    (insert "# ")))
+
+;; Indenters help determine whether the current line should be
+;; indented further based on the content of the previous line. If a
+;; line starts with `class', for instance, you're probably going to
+;; want to indent the next line.
+
+(defvar coffee-indenters-bol '("class" "for" "if" "try")
+  "Keywords or syntax whose presence at the start of a line means the
+next line should probably be indented.")
+
+(defun coffee-indenters-bol-regexp ()
+  "Builds a regexp out of `coffee-indenters-bol' words."
+  (regexp-opt coffee-indenters-bol 'words))
+
+(defvar coffee-indenters-eol '(?> ?{ ?\[)
+  "Single characters at the end of a line that mean the next line
+should probably be indented.")
+
+(defun coffee-line-wants-indent ()
+  "Return t if the current line should be indented relative to the
+previous line."
+  (interactive)
+
+  (save-excursion
+    (let ((indenter-at-bol) (indenter-at-eol))
+      ;; Go back a line and to the first character.
+      (forward-line -1)
+      (backward-to-indentation 0)
+
+      ;; If the next few characters match one of our magic indenter
+      ;; keywords, we want to indent the line we were on originally.
+      (when (looking-at (coffee-indenters-bol-regexp))
+        (setq indenter-at-bol t))
+
+      ;; If that didn't match, go to the back of the line and check to
+      ;; see if the last character matches one of our indenter
+      ;; characters.
+      (when (not indenter-at-bol)
+        (end-of-line)
+
+        ;; Optimized for speed - checks only the last character.
+        (let ((indenters coffee-indenters-eol))
+          (while indenters
+            (if (/= (char-before) (car indenters))
+                (setq indenters (cdr indenters))
+              (setq indenter-at-eol t)
+              (setq indenters nil)))))
+
+      ;; If we found an indenter, return `t'.
+      (or indenter-at-bol indenter-at-eol))))
+
+(defun coffee-previous-line-is-comment ()
+  "Return t if the previous line is a CoffeeScript comment."
+  (save-excursion
+    (forward-line -1)
+    (coffee-line-is-comment)))
+
+(defun coffee-line-is-comment ()
+  "Return t if the current line is a CoffeeScript comment."
+  (save-excursion
+    (backward-to-indentation 0)
+    (= (char-after) (string-to-char "#"))))
+
+;;
+;; Define Major Mode
+;;
+
+;;;###autoload
+(define-derived-mode coffee-mode fundamental-mode
+  "Coffee"
+  "Major mode for editing CoffeeScript."
+
+  ;; key bindings
+  (define-key coffee-mode-map (kbd "A-r") 'coffee-compile-buffer)
+  (define-key coffee-mode-map (kbd "A-R") 'coffee-compile-region)
+  (define-key coffee-mode-map (kbd "A-M-r") 'coffee-repl)
+  (define-key coffee-mode-map [remap comment-dwim] 'coffee-comment-dwim)
+  (define-key coffee-mode-map "\C-m" 'coffee-newline-and-indent)
+  (define-key coffee-mode-map "\C-c\C-o\C-s" 'coffee-cos-mode)
+
+  ;; code for syntax highlighting
+  (setq font-lock-defaults '((coffee-font-lock-keywords)))
+
+  ;; perl style comment: "# ..."
+  (modify-syntax-entry ?# "< b" coffee-mode-syntax-table)
+  (modify-syntax-entry ?\n "> b" coffee-mode-syntax-table)
+  (make-local-variable 'comment-start)
+  (setq comment-start "#")
+
+  ;; single quote strings
+  (modify-syntax-entry ?' "\"" coffee-mode-syntax-table)
+
+  ;; indentation
+  (make-local-variable 'indent-line-function)
+  (setq indent-line-function 'coffee-indent-line)
+  (set (make-local-variable 'tab-width) coffee-tab-width)
+
+  ;; imenu
+  (make-local-variable 'imenu-create-index-function)
+  (setq imenu-create-index-function 'coffee-imenu-create-index)
+
+  ;; no tabs
+  (setq indent-tabs-mode nil))
+
+;;
+;; Compile-on-Save minor mode
+;;
+
+(defvar coffee-cos-mode-line " CoS")
+(make-variable-buffer-local 'coffee-cos-mode-line)
+
+(define-minor-mode coffee-cos-mode
+  "Toggle compile-on-save for coffee-mode."
+  :group 'coffee-cos :lighter coffee-cos-mode-line
+  (cond
+   (coffee-cos-mode
+    (add-hook 'after-save-hook 'coffee-compile-file nil t))
+   (t
+    (remove-hook 'after-save-hook 'coffee-compile-file t))))
+
+(provide 'coffee-mode)
+
+;;
+;; On Load
+;;
+
+;; Run coffee-mode for files ending in .coffee.
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.coffee$" . coffee-mode))
+;;;###autoload
+(add-to-list 'auto-mode-alist '("Cakefile" . coffee-mode))
+;;; coffee-mode.el ends here

=== added directory 'packages/coffee-mode/examples'
=== added file 'packages/coffee-mode/examples/basic.coffee'
--- a/packages/coffee-mode/examples/basic.coffee        1970-01-01 00:00:00 
+0000
+++ b/packages/coffee-mode/examples/basic.coffee        2010-09-25 19:25:17 
+0000
@@ -0,0 +1,118 @@
+# These examples are taken from
+# http://jashkenas.github.com/coffee-script/
+
+song = ["do", "re", "mi", "fa", "so"]
+
+ages = {
+  max: 10
+  ida: 9
+  tim: 11
+}
+
+matrix = [
+  1, 0, 1
+  0, 0, 1
+  1, 1, 0
+]
+
+eldest = if 24 > 21 then "Liz" else "Ike"
+
+six = (one = 1) + (two = 2) + (three = 3)
+
+My.mood = greatly_improved if true
+
+# Unfancy JavaScript
+if happy and knows_it
+  cha_cha_cha()
+  false
+
+Account = (customer, cart) ->
+  @customer: customer
+  @cart: cart
+
+  $('.shopping_cart').bind 'click', (event) =>
+    @customer.purchase @cart
+
+class Animal
+  move: (meters) ->
+    alert @name + " moved " + meters + "m."
+
+  randomify: ->
+    @name.replace(/^[\w_-]*$/g, "-")
+
+class Snake extends Animal
+  constructor: (name) ->
+    @name: name
+
+  move: ->
+    alert "Slithering..."
+    super 5
+
+class Horse extends Animal
+  constructor: (name) ->
+    @name: name
+
+  move: ->
+    alert "Galloping..."
+    super 45
+
+sam = new Snake "Sammy the Python"
+tom = new Horse "Tommy the Palomino"
+
+sam.move()
+tom.move()
+if car.speed < speed_limit then accelerate()
+
+print "My name is " + @name
+
+gold = silver = the_field = "unknown"
+
+award_medals = (first, second, rest...) ->
+  gold:       first
+  silver:     second
+  the_field:  rest
+
+contenders = [
+  "Michael Phelps"
+  "Liu Xiang"
+]
+
+award_medals contenders...
+
+alert "Gold: " + gold
+alert "Silver: " + silver
+alert "The Field: " + the_field
+
+# Eat lunch.
+# what up
+# love it.
+lunch = eat food for food in ['toast', 'cheese', 'wine']
+
+$('#demo').click ->
+  asd
+# sup
+  # asd
+  # asdasd
+blah = true
+
+okay
+
+
+# Naive collision detection.
+for roid in asteroids
+  for roid2 in asteroids when roid isnt roid2
+    roid.explode() if roid.overlaps roid2
+
+years_old = max: 10, ida: 9, tim: 11
+
+ages = for child, age of years_old
+  child + " is " + age
+
+grade = (student) ->
+  if student.excellent_work
+    "A+"
+  else if student.okay_stuff
+    if student.tried_hard then "B" else "B-"
+  else
+    "C"
+

=== added file 'packages/coffee-mode/examples/edge.coffee'
--- a/packages/coffee-mode/examples/edge.coffee 1970-01-01 00:00:00 +0000
+++ b/packages/coffee-mode/examples/edge.coffee 2010-09-25 22:53:53 +0000
@@ -0,0 +1,16 @@
+# Edge cases
+
+if string.match /\// or string.match /\x1b/ or string.match /a\/b/
+  console.log "matched"
+
+string = "Something with a \"double quote"
+console.log string
+
+string = 'Something with a \'single quote'
+console.log string
+
+# TODO
+heredoc = """
+  Heredoc with a " double quote
+"""
+console.log heredoc

=== added file 'packages/coffee-mode/examples/imenu.coffee'
--- a/packages/coffee-mode/examples/imenu.coffee        1970-01-01 00:00:00 
+0000
+++ b/packages/coffee-mode/examples/imenu.coffee        2010-09-25 19:25:17 
+0000
@@ -0,0 +1,33 @@
+# Testing imenu
+regexp = /asdas/
+two = 4 / 2
+
+minus = (x, y) -> x - y
+
+String::length = -> 10
+
+class Person
+  print: ->
+    print 'My name is ' + this.name + '.'
+
+app =
+  window:  {width: 200, height: 200}
+  para:    'Welcome.'
+  button:  'OK'
+
+block = ->
+  print('potion')
+
+Please = {}
+Please.print = (word) ->
+  print(word)
+
+HomePage::get = (url) ->
+  session: url.query.session if url.query?
+
+class Policeman extends Person
+  constructor: (rank) ->
+    @rank: rank
+
+  print: ->
+    print 'My name is ' + this.name + " and I'm a " + this.rank + '.'


reply via email to

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