         (end-of-line)
 
         ;; Optimized for speed - checks only the last character.
-        (when (some (lambda (char)
-                        (= (char-before) char))
-                      coffee-indenters-eol)
-          (setd indenter-at-eol t)))
+        (let ((indenters coffee-indenters-eol))
+          (while indenters
+            (if (and (char-before) (/= (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 ()
-  "Returns `t' if the previous line is a CoffeeScript 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 ()
-  "Returns `t' if the current line is a CoffeeScript comment."
+  "Return t if the current line is a CoffeeScript comment."
   (save-excursion
     (backward-to-indentation 0)
     (= (char-after) (string-to-char "#"))))
 
+
+;; (defun coffee-quote-syntax (n)
+;;   "Put `syntax-table' property correctly on triple quote.
+;; Used for syntactic keywords.  N is the match number (1, 2 or 3)."
+;;   ;; From python-mode...
+;;   ;;
+;;   ;; Given a triple quote, we have to check the context to know
+;;   ;; whether this is an opening or closing triple or whether it's
+;;   ;; quoted anyhow, and should be ignored.  (For that we need to do
+;;   ;; the same job as `syntax-ppss' to be correct and it seems to be OK
+;;   ;; to use it here despite initial worries.)  We also have to sort
+;;   ;; out a possible prefix -- well, we don't _have_ to, but I think it
+;;   ;; should be treated as part of the string.
+
+;;   ;; Test cases:
+;;   ;;  ur"""ar""" x='"' # """
+;;   ;; x = ''' """ ' a
+;;   ;; '''
+;;   ;; x '"""' x """ \"""" x
+;;   (save-excursion
+;;     (goto-char (match-beginning 0))
+;;     (cond
+;;      ;; Consider property for the last char if in a fenced string.
+;;      ((= n 3)
+;;       (let* ((font-lock-syntactic-keywords nil)
+;; 	     (syntax (syntax-ppss)))
+;; 	(when (eq t (nth 3 syntax))	; after unclosed fence
+;; 	  (goto-char (nth 8 syntax))	; fence position
+;; 	  ;; (skip-chars-forward "uUrR")	; skip any prefix
+;; 	  ;; Is it a matching sequence?
+;; 	  (if (eq (char-after) (char-after (match-beginning 2)))
+;; 	      (eval-when-compile (string-to-syntax "|"))))))
+;;      ;; Consider property for initial char, accounting for prefixes.
+;;      ((or (and (= n 2)			; leading quote (not prefix)
+;; 	       (not (match-end 1)))     ; prefix is null
+;; 	  (and (= n 1)			; prefix
+;; 	       (match-end 1)))          ; non-empty
+;;       (let ((font-lock-syntactic-keywords nil))
+;; 	(unless (eq 'string (syntax-ppss-context (syntax-ppss)))
+;; 	  (eval-when-compile (string-to-syntax "|")))))
+;;      ;; Otherwise (we're in a non-matching string) the property is
+;;      ;; nil, which is OK.
+;;      )))
+
+(defun coffee-indent-shift-amount (start end dir)
+  "Compute distance to the closest increment of `coffee-tab-width'."
+  (let ((min most-positive-fixnum) rem)
+    (save-excursion
+      (goto-char start)
+      (while (< (point) end)
+        (let ((current (current-indentation)))
+          (when (< current min) (setq min current)))
+        (forward-line))
+      (setq rem (% min coffee-tab-width))
+      (if (zerop rem)
+          coffee-tab-width
+        (cond ((eq dir 'left) rem)
+              ((eq dir 'right) (- coffee-tab-width rem))
+              (t 0))))))
+
+(defun coffee-indent-shift-left (start end &optional count)
+  "Shift lines contained in region START END by COUNT columns to the left.
+If COUNT is not given, indents to the closest increment of
+`coffee-tab-width'. If region isn't active, the current line is
+shifted. The shifted region includes the lines in which START and
+END lie. An error is signaled if any lines in the region are
+indented less than COUNT columns."
+  (interactive
+   (if mark-active
+       (list (region-beginning) (region-end) current-prefix-arg)
+     (list (line-beginning-position) (line-end-position) current-prefix-arg)))
+  (let ((amount (if count (prefix-numeric-value count)
+                  (coffee-indent-shift-amount start end 'left))))
+    (when (> amount 0)
+      (let (deactivate-mark)
+        (save-excursion
+          (goto-char start)
+          ;; Check that all lines can be shifted enough
+          (while (< (point) end)
+            (if (and (< (current-indentation) amount)
+                     (not (looking-at "[ \t]*$")))
+                (error "Can't shift all lines enough"))
+            (forward-line))
+          (indent-rigidly start end (- amount)))))))
+
+(add-to-list 'debug-ignored-errors "^Can't shift all lines enough")
+
+(defun coffee-indent-shift-right (start end &optional count)
+  "Shift lines contained in region START END by COUNT columns to the right.
+if COUNT is not given, indents to the closest increment of
+`coffee-tab-width'. If region isn't active, the current line is
+shifted. The shifted region includes the lines in which START and
+END lie."
+  (interactive
+   (if mark-active
+       (list (region-beginning) (region-end) current-prefix-arg)
+     (list (line-beginning-position) (line-end-position) current-prefix-arg)))
+  (let (deactivate-mark
+        (amount (if count (prefix-numeric-value count)
+                  (coffee-indent-shift-amount start end 'right))))
+    (indent-rigidly start end amount)))
+
 ;;
 ;; Define Major Mode
 ;;
+
+(defun coffee-block-comment-delimiter (match)
+  (progn
+    (goto-char match)
+    (beginning-of-line)
+    (add-text-properties (point) (+ (point) 1) `(syntax-table (14 . nil)))))
+
+;; support coffescript block comments
+;; examples:
+;;   at indent level 0
+;;   ###
+;;        foobar
+;;   ###
+;;   at indent level 0 with text following it
+;;   ### foobar
+;;     moretext
+;;   ###
+;;   at indent level > 0
+;;     ###
+;;       foobar
+;;     ###
+;; examples of non-block comments:
+;;   #### foobar
+(defun coffee-propertize-function (start end)
+  ;; return if we don't have anything to parse
+  (unless (>= start end)
+    (save-excursion
+      (progn
+        (goto-char start)
+        (let ((match (re-search-forward "^[[:space:]]*###\\([[:space:]]+.*\\)?$" end t)))
+          (if match
+              (progn
+                (coffee-block-comment-delimiter match)
+                (goto-char match)
+                (next-line)
+                (coffee-propertize-function (point) end))))))))
 
 ;;;###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)))
+
+  ;; treat "_" as part of a word
+  (modify-syntax-entry ?_ "w" coffee-mode-syntax-table)
 
   ;; perl style comment: "# ..."
   (modify-syntax-entry ?# "< b" coffee-mode-syntax-table)

      ;; 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))
        (setd 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


;; Copyright (C) 2010 Chris Wanstrath

;; Version: 0.4.1
;; Keywords: CoffeeScript major mode
;; Author: Chris Wanstrath <chris@ozmm.org>
;; URL: http://github.com/defunkt/coffee-mode

;; This file is not part of GNU Emacs.

