]> code.delx.au - gnu-emacs-elpa/blobdiff - coffee-mode.el
Small patch to fix indentation behavior; before, the global tab-width setting is...
[gnu-emacs-elpa] / coffee-mode.el
index 83fa633b89ad5b55d70d8bc963fdae23bb889f6a..27a2e6e256e8916868d9aca88f86ba5c98bb6654 100644 (file)
 
 ;;; Code:
 
+(require 'comint)
 (require 'easymenu)
 (require 'font-lock)
-(require 'cl)
+
+(eval-when-compile
+  (require 'cl))
 
 ;;
 ;; Customizable Variables
@@ -96,14 +99,13 @@ path."
   :type 'string
   :group 'coffee)
 
-(defcustom coffee-repl-args '("-i")
+(defcustom coffee-args-repl '("-i")
   "The command line arguments to pass to `coffee-command' to start a REPL."
   :type 'list
   :group 'coffee)
 
-(defcustom coffee-command-args '("-s" "-p" "--no-wrap")
-  "The command line arguments to pass to `coffee-command' to get it to
-print the compiled JavaScript."
+(defcustom coffee-args-compile '("-c")
+  "The command line arguments to pass to `coffee-command' when compiling a file."
   :type 'list
   :group 'coffee)
 
@@ -124,7 +126,7 @@ print the compiled JavaScript."
 
 (defmacro setd (var val)
   "Like setq but optionally logs the variable's value using `coffee-debug'."
-  (if coffee-debug-mode
+  (if (and (boundp 'coffee-debug-mode) coffee-debug-mode)
       `(progn
          (coffee-debug "%s: %s" ',var ,val)
          (setq ,var ,val))
@@ -150,18 +152,22 @@ print the compiled JavaScript."
   (unless (comint-check-proc "*CoffeeREPL*")
     (set-buffer
      (apply 'make-comint "CoffeeREPL"
-            coffee-command nil coffee-repl-args)))
+            coffee-command nil coffee-args-repl)))
+
+  (pop-to-buffer "*CoffeeREPL*"))
 
-  (pop-to-buffer "*CoffeeScript*"))
+(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. Doesn't open in a buffer.."
   (interactive)
-  (shell-command (concat coffee-command " -c " (buffer-file-name)))
-  (message "Compiled and saved %s"
-           (concat
-            (substring (buffer-file-name) 0 -6)
-            "js")))
+  (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))
+      (message (car (split-string compiler-output "[\n\r]+"))))))
 
 (defun coffee-compile-buffer ()
   "Compiles the current buffer and displays the JS in another buffer."
@@ -180,10 +186,10 @@ print the compiled JavaScript."
   (call-process-region start end coffee-command nil
                        (get-buffer-create coffee-compiled-buffer-name)
                        nil
-                       "-s" "-p" "--no-wrap")
+                       "-s" "-p" "--bare")
   (switch-to-buffer (get-buffer coffee-compiled-buffer-name))
   (funcall coffee-js-mode)
-  (beginning-of-buffer))
+  (goto-char (point-min)))
 
 (defun coffee-show-version ()
   "Prints the `coffee-mode' version."
@@ -196,9 +202,9 @@ print the compiled JavaScript."
   (browse-url "http://jashkenas.github.com/coffee-script/"))
 
 (defun coffee-open-node-reference ()
-  "Open browser to node.js reference."
+  "Open browser to node.js documentation."
   (interactive)
-  (browse-url "http://nodejs.org/api.html"))
+  (browse-url "http://nodejs.org/docs/"))
 
 (defun coffee-open-github ()
   "Open browser to `coffee-mode' project on GithHub."
@@ -227,11 +233,17 @@ print the compiled JavaScript."
 ;; Define Language Syntax
 ;;
 
+;; String literals
+(defvar coffee-string-regexp "\"\\([^\\]\\|\\\\.\\)*?\"\\|'\\([^\\]\\|\\\\.\\)*?'")
+
 ;; Instance variables (implicit this)
-(defvar coffee-this-regexp "@\\w*\\|this")
+(defvar coffee-this-regexp "@\\(\\w\\|_\\)*\\|this")
+
+;; Prototype::access
+(defvar coffee-prototype-regexp "\\(\\(\\w\\|\\.\\|_\\| \\|$\\)+?\\)::\\(\\(\\w\\|\\.\\|_\\| \\|$\\)+?\\):")
 
 ;; Assignment
-(defvar coffee-assign-regexp "\\(\\(\\w\\|\\.\\|_\\| \\|$\\)+?\\):")
+(defvar coffee-assign-regexp "\\(\\(\\w\\|\\.\\|_\\|$\\)+?\s*\\):")
 
 ;; Lambda
 (defvar coffee-lambda-regexp "\\((.+)\\)?\\s *\\(->\\|=>\\)")
@@ -240,17 +252,17 @@ print the compiled JavaScript."
 (defvar coffee-namespace-regexp "\\b\\(class\\s +\\(\\S +\\)\\)\\b")
 
 ;; Booleans
-(defvar coffee-boolean-regexp "\\b\\(true\\|false\\|yes\\|no\\|on\\|off\\)\\b")
+(defvar coffee-boolean-regexp "\\b\\(true\\|false\\|yes\\|no\\|on\\|off\\|null\\)\\b")
 
 ;; Regular Expressions
-(defvar coffee-regexp-regexp "\\/.+?\\/")
+(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"))
+        "class" "until" "loop"))
 
 ;; Reserved keywords either by JS or CS.
 (defvar coffee-js-reserved
@@ -277,7 +289,9 @@ print the compiled JavaScript."
   ;; *Note*: order below matters. `coffee-keywords-regexp' goes last
   ;; because otherwise the keyword "state" in the function
   ;; "state_entry" would be highlighted.
-  `((,coffee-this-regexp . font-lock-variable-name-face)
+  `((,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)
@@ -301,9 +315,9 @@ For detail, see `comment-dwim'."
   (let ((deactivate-mark nil) (comment-start "#") (comment-end ""))
     (comment-dwim arg)))
 
-(defun coffee-command-full ()
-  "The full `coffee-command' complete with args."
-  (mapconcat 'identity (append (list coffee-command) coffee-command-args) " "))
+(defun coffee-command-compile (file-name)
+  "The `coffee-command' with args to compile a file."
+  (mapconcat 'identity (append (list coffee-command) coffee-args-compile (list file-name)) " "))
 
 ;;
 ;; imenu support
@@ -350,7 +364,7 @@ For detail, see `comment-dwim'."
   (interactive)
 
   ;; This function is called within a `save-excursion' so we're safe.
-  (beginning-of-buffer)
+  (goto-char (point-min))
 
   (let ((index-alist '()) assign pos indent ns-name ns-indent)
     ;; Go through every assignment that includes -> or => on the same
@@ -457,8 +471,11 @@ For detail, see `comment-dwim'."
 
   (save-excursion
     (forward-line -1)
-    (while (coffee-line-empty-p) (forward-line -1))
-    (current-indentation)))
+    (if (bobp)
+        0
+      (progn
+        (while (and (coffee-line-empty-p) (not (bobp))) (forward-line -1))
+        (current-indentation)))))
 
 (defun coffee-line-empty-p ()
   "Is this line empty? Returns non-nil if so, nil if not."
@@ -473,6 +490,7 @@ For detail, see `comment-dwim'."
   ;; 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))
 
@@ -497,7 +515,7 @@ next line should probably be indented.")
 
 (defun coffee-indenters-bol-regexp ()
   "Builds a regexp out of `coffee-indenters-bol' words."
-  (concat "^" (regexp-opt 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
@@ -552,8 +570,8 @@ line? Returns `t' or `nil'. See the README for more details."
 
 ;;;###autoload
 (define-derived-mode coffee-mode fundamental-mode
-  "coffee-mode"
-  "Major mode for editing CoffeeScript..."
+  "Coffee"
+  "Major mode for editing CoffeeScript."
 
   ;; key bindings
   (define-key coffee-mode-map (kbd "A-r") 'coffee-compile-buffer)
@@ -561,6 +579,7 @@ line? Returns `t' or `nil'. See the README for more details."
   (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)))
@@ -568,16 +587,16 @@ line? Returns `t' or `nil'. See the README for more details."
   ;; 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)
-  (modify-syntax-entry ?' "\"" coffee-mode-syntax-table)
 
   ;; indentation
   (make-local-variable 'indent-line-function)
   (setq indent-line-function 'coffee-indent-line)
-  (setq coffee-tab-width tab-width) ;; Just in case...
+  (set (make-local-variable 'tab-width) coffee-tab-width)
 
   ;; imenu
   (make-local-variable 'imenu-create-index-function)
@@ -589,6 +608,22 @@ line? Returns `t' or `nil'. See the README for more details."
   ;; hooks
   (set (make-local-variable 'before-save-hook) 'coffee-before-save))
 
+;;
+;; 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)
 
 ;;