Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 614 lines (483 sloc) 18.878 kB
4a11c70 @defunkt documentation
authored
1 ;;; coffee-mode.el --- Major mode to edit CoffeeScript files in Emacs
2
3 ;; Copyright (C) 2010 Chris Wanstrath
4
03f75e3 @defunkt 0.3.0
authored
5 ;; Version 0.3.0
4a11c70 @defunkt documentation
authored
6 ;; Keywords: CoffeeScript major mode
7 ;; Author: Chris Wanstrath <chris@ozmm.org>
8 ;; URL: http://github.com/defunkt/coffee-script
9
10 ;; This file is not part of GNU Emacs.
11
12 ;; This program is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; any later version.
16
17 ;; This program is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with this program; if not, write to the Free Software
24 ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25
26 ;;; Commentary
27
28 ;; For commentary please see the README.md or
29 ;; http://github.com/defunkt/coffee-mode#readme
30
31 ;;; Installation
32
33 ;; In your shell:
34
35 ;; $ cd ~/.emacs.d/vendor
36 ;; $ git clone git://github.com/defunkt/coffee-mode.git
37
38 ;; In your emacs config:
39
40 ;; (add-to-list 'load-path "~/.emacs.d/vendor/coffee-mode")
41 ;; (require 'coffee-mode)
42
43 ;;; Thanks
44
0480381 @defunkt mini docs
authored
45 ;; Major thanks to http://xahlee.org/emacs/elisp_syntax_coloring.html
46 ;; the instructions.
4a11c70 @defunkt documentation
authored
47
a6c6911 @defunkt tweaks
authored
48 ;; Also thanks to Jason Blevins's markdown-mode.el and Steve Yegge's
49 ;; js2-mode for guidance.
38ce8da @defunkt small tweaks
authored
50
12bd5d0 @defunkt todo
authored
51 ;; TODO:
a6c6911 @defunkt tweaks
authored
52 ;; - Execute {buffer,region,line} and show output in new buffer
12bd5d0 @defunkt todo
authored
53 ;; - Make prototype accessor assignments like `String::length: -> 10` pretty.
5b00ae2 @defunkt more todo
authored
54 ;; - mirror-mode - close brackets and parens automatically
12bd5d0 @defunkt todo
authored
55
56 ;;; Code:
57
6639c36 @mkhl Silence byte-compiler warnings.
mkhl authored
58 (require 'comint)
38ce8da @defunkt small tweaks
authored
59 (require 'easymenu)
60 (require 'font-lock)
6639c36 @mkhl Silence byte-compiler warnings.
mkhl authored
61
62 (eval-when-compile
63 (require 'cl))
0480381 @defunkt mini docs
authored
64
7557530 @defunkt compilation commands
authored
65 ;;
38ce8da @defunkt small tweaks
authored
66 ;; Customizable Variables
7557530 @defunkt compilation commands
authored
67 ;;
68
03f75e3 @defunkt 0.3.0
authored
69 (defconst coffee-mode-version "0.3.0"
5f14029 @defunkt Menu
authored
70 "The version of this `coffee-mode'.")
71
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
72 (defgroup coffee nil
73 "A CoffeeScript major mode."
74 :group 'languages)
75
76 (defcustom coffee-debug-mode nil
77 "Whether to run in debug mode or not. Logs to `*Messages*'."
78 :type 'boolean
79 :group 'coffee-mode)
80
81 (defcustom coffee-js-mode 'js2-mode
82 "The mode to use when viewing compiled JavaScript."
83 :type 'string
84 :group 'coffee)
85
86 (defcustom coffee-cleanup-whitespace t
87 "Should we `delete-trailing-whitespace' on save? Probably."
88 :type 'boolean
89 :group 'coffee)
90
91 (defcustom coffee-tab-width tab-width
92 "The tab width to use when indenting."
93 :type 'integer
94 :group 'coffee)
95
96 (defcustom coffee-command "coffee"
7557530 @defunkt compilation commands
authored
97 "The CoffeeScript command used for evaluating code. Must be in your
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
98 path."
99 :type 'string
100 :group 'coffee)
7557530 @defunkt compilation commands
authored
101
3b0962d @defunkt Rename `args` variables to be more consistent.
authored
102 (defcustom coffee-args-repl '("-i")
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
103 "The command line arguments to pass to `coffee-command' to start a REPL."
104 :type 'list
105 :group 'coffee)
28485fa @defunkt `coffee-repl'
authored
106
227a360 @orlandohill Customize args for compiling files and display the first compiler error
orlandohill authored
107 (defcustom coffee-args-compile '("-c")
108 "The command line arguments to pass to `coffee-command' when compiling a file."
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
109 :type 'list
110 :group 'coffee)
5f14029 @defunkt Menu
authored
111
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
112 (defcustom coffee-compiled-buffer-name "*coffee-compiled*"
113 "The name of the scratch buffer used when compiling CoffeeScript."
114 :type 'string
115 :group 'coffee)
7557530 @defunkt compilation commands
authored
116
1b887a5 @defunkt clean whitespace on save
authored
117 (defvar coffee-mode-hook nil
118 "A hook for you to run your own code when the mode is loaded.")
119
eee29e0 @defunkt defvar the keymap
authored
120 (defvar coffee-mode-map (make-keymap)
121 "Keymap for CoffeeScript major mode.")
122
38ce8da @defunkt small tweaks
authored
123 ;;
c5ffe04 @defunkt setd macro for logging variable values when setting them
authored
124 ;; Macros
5b00ae2 @defunkt more todo
authored
125 ;;
126
c5ffe04 @defunkt setd macro for logging variable values when setting them
authored
127 (defmacro setd (var val)
128 "Like setq but optionally logs the variable's value using `coffee-debug'."
cd06e4e @mkhl Fix compiling `coffee-mode' without loading it.
mkhl authored
129 (if (and (boundp 'coffee-debug-mode) coffee-debug-mode)
c6562af @defunkt Make `coffee-debug' a macro
authored
130 `(progn
131 (coffee-debug "%s: %s" ',var ,val)
132 (setq ,var ,val))
133 `(setq ,var ,val)))
134
2799609 @defunkt back out that macro change for now, something is funny
authored
135 (defun coffee-debug (string &rest args)
c6562af @defunkt Make `coffee-debug' a macro
authored
136 "Print a message when in debug mode."
137 (when coffee-debug-mode
2799609 @defunkt back out that macro change for now, something is funny
authored
138 (apply 'message (append (list string) args))))
c5ffe04 @defunkt setd macro for logging variable values when setting them
authored
139
b7ee8b8 @defunkt start cleaning up indentation code
authored
140 (defmacro coffee-line-as-string ()
141 "Returns the current line as a string."
142 `(buffer-substring (point-at-bol) (point-at-eol)))
143
5b00ae2 @defunkt more todo
authored
144 ;;
38ce8da @defunkt small tweaks
authored
145 ;; Commands
146 ;;
147
28485fa @defunkt `coffee-repl'
authored
148 (defun coffee-repl ()
149 "Launch a CoffeeScript REPL using `coffee-command' as an inferior mode."
150 (interactive)
151
17150cf @defunkt docs and starting to implement indenters
authored
152 (unless (comint-check-proc "*CoffeeREPL*")
28485fa @defunkt `coffee-repl'
authored
153 (set-buffer
17150cf @defunkt docs and starting to implement indenters
authored
154 (apply 'make-comint "CoffeeREPL"
3b0962d @defunkt Rename `args` variables to be more consistent.
authored
155 coffee-command nil coffee-args-repl)))
28485fa @defunkt `coffee-repl'
authored
156
8a3d6f5 Updating coffee-repl to pop-to-buffer using the name '*CoffeeREPL*' i…
Nick Parker authored
157 (pop-to-buffer "*CoffeeREPL*"))
38ce8da @defunkt small tweaks
authored
158
cbd31f9 @defunkt coffee-compile-file
authored
159 (defun coffee-compile-file ()
160 "Compiles and saves the current file to disk. Doesn't open in a buffer.."
161 (interactive)
227a360 @orlandohill Customize args for compiling files and display the first compiler error
orlandohill authored
162 (let ((compiler-output (shell-command-to-string (coffee-command-compile (buffer-file-name)))))
163 (if (string= compiler-output "")
164 (message "Compiled and saved %s" (concat (substring (buffer-file-name) 0 -6) "js"))
165 (message (car (split-string compiler-output "[\n\r]+"))))))
cbd31f9 @defunkt coffee-compile-file
authored
166
7557530 @defunkt compilation commands
authored
167 (defun coffee-compile-buffer ()
17150cf @defunkt docs and starting to implement indenters
authored
168 "Compiles the current buffer and displays the JS in another buffer."
7557530 @defunkt compilation commands
authored
169 (interactive)
170 (save-excursion
171 (coffee-compile-region (point-min) (point-max))))
172
173 (defun coffee-compile-region (start end)
17150cf @defunkt docs and starting to implement indenters
authored
174 "Compiles a region and displays the JS in another buffer."
7557530 @defunkt compilation commands
authored
175 (interactive "r")
176
177 (let ((buffer (get-buffer coffee-compiled-buffer-name)))
178 (when buffer
179 (kill-buffer buffer)))
180
181 (call-process-region start end coffee-command nil
182 (get-buffer-create coffee-compiled-buffer-name)
183 nil
b874bf4 @defunkt --no-wrap is deprecated; please use --bare instead.
authored
184 "-s" "-p" "--bare")
e3c21f8 @tav Change so that the compiled buffer doesn't open in a new frame.
tav authored
185 (switch-to-buffer (get-buffer coffee-compiled-buffer-name))
7557530 @defunkt compilation commands
authored
186 (funcall coffee-js-mode)
6639c36 @mkhl Silence byte-compiler warnings.
mkhl authored
187 (goto-char (point-min)))
3a47b28 @defunkt docs
authored
188
5f14029 @defunkt Menu
authored
189 (defun coffee-show-version ()
190 "Prints the `coffee-mode' version."
191 (interactive)
192 (message (concat "coffee-mode v" coffee-mode-version)))
193
fd314cc @defunkt menu tweaks
authored
194 (defun coffee-open-reference ()
195 "Open browser to CoffeeScript reference."
196 (interactive)
197 (browse-url "http://jashkenas.github.com/coffee-script/"))
198
48c36c1 @defunkt open node.js api in browser
authored
199 (defun coffee-open-node-reference ()
200 "Open browser to node.js reference."
201 (interactive)
202 (browse-url "http://nodejs.org/api.html"))
203
5f14029 @defunkt Menu
authored
204 (defun coffee-open-github ()
fd314cc @defunkt menu tweaks
authored
205 "Open browser to `coffee-mode' project on GithHub."
5f14029 @defunkt Menu
authored
206 (interactive)
207 (browse-url "http://github.com/defunkt/coffee-mode"))
208
209 ;;
210 ;; Menubar
211 ;;
212
213 (easy-menu-define coffee-mode-menu coffee-mode-map
214 "Menu for CoffeeScript mode"
215 '("CoffeeScript"
cbd31f9 @defunkt coffee-compile-file
authored
216 ["Compile File" coffee-compile-file]
5f14029 @defunkt Menu
authored
217 ["Compile Buffer" coffee-compile-buffer]
218 ["Compile Region" coffee-compile-region]
5256afd @defunkt tweaks
authored
219 ["REPL" coffee-repl]
5f14029 @defunkt Menu
authored
220 "---"
48c36c1 @defunkt open node.js api in browser
authored
221 ["CoffeeScript Reference" coffee-open-reference]
222 ["node.js Reference" coffee-open-node-reference]
fd314cc @defunkt menu tweaks
authored
223 ["coffee-mode on GitHub" coffee-open-github]
5f14029 @defunkt Menu
authored
224 ["Version" coffee-show-version]
225 ))
226
3a47b28 @defunkt docs
authored
227 ;;
228 ;; Define Language Syntax
229 ;;
230
c5831bb @sstephenson Fix for string literals with escaped quotes
sstephenson authored
231 ;; String literals
232 (defvar coffee-string-regexp "\"\\([^\\]\\|\\\\.\\)*?\"\\|'\\([^\\]\\|\\\\.\\)*?'")
233
3a47b28 @defunkt docs
authored
234 ;; Instance variables (implicit this)
2bb69be Regex for "this" variable includes underscore.
Ryan Koopmans authored
235 (defvar coffee-this-regexp "@\\(\\w\\|_\\)*\\|this")
3a47b28 @defunkt docs
authored
236
dc85f42 @tav Added support for the shorthand::syntax for .prototype.
tav authored
237 ;; Prototype::access
238 (defvar coffee-prototype-regexp "\\(\\(\\w\\|\\.\\|_\\| \\|$\\)+?\\)::\\(\\(\\w\\|\\.\\|_\\| \\|$\\)+?\\):")
239
88e7b95 @defunkt better regexps, @blah: and {blah: true, blah2:true}
authored
240 ;; Assignment
5c5a414 @sstephenson Recognize 0.9.0 assignment syntax
sstephenson authored
241 (defvar coffee-assign-regexp "\\(\\(\\w\\|\\.\\|_\\| \\|$\\)+?\\)[:=]")
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
242
243 ;; Lambda
00c197b @defunkt elisp regexps are fun
authored
244 (defvar coffee-lambda-regexp "\\((.+)\\)?\\s *\\(->\\|=>\\)")
88e7b95 @defunkt better regexps, @blah: and {blah: true, blah2:true}
authored
245
76ed341 @defunkt class support in imenu
authored
246 ;; Namespaces
247 (defvar coffee-namespace-regexp "\\b\\(class\\s +\\(\\S +\\)\\)\\b")
248
af34f91 @defunkt Booleans, better colors
authored
249 ;; Booleans
af81c6a @defunkt Highlight null. closes #4
authored
250 (defvar coffee-boolean-regexp "\\b\\(true\\|false\\|yes\\|no\\|on\\|off\\|null\\)\\b")
3a47b28 @defunkt docs
authored
251
dbb4ec5 @defunkt comment tweaks
authored
252 ;; Regular Expressions
095e65e @sstephenson Fix for regex literals with escaped slashes
sstephenson authored
253 (defvar coffee-regexp-regexp "\\/\\([^\\]\\|\\\\.\\)+?\\/")
3a47b28 @defunkt docs
authored
254
cf028f8 @defunkt basics
authored
255 ;; JavaScript Keywords
d6b78ed @defunkt defvars and basic indentation
authored
256 (defvar coffee-js-keywords
af34f91 @defunkt Booleans, better colors
authored
257 '("if" "else" "new" "return" "try" "catch"
cf028f8 @defunkt basics
authored
258 "finally" "throw" "break" "continue" "for" "in" "while"
259 "delete" "instanceof" "typeof" "switch" "super" "extends"
260 "class"))
261
262 ;; Reserved keywords either by JS or CS.
d6b78ed @defunkt defvars and basic indentation
authored
263 (defvar coffee-js-reserved
cf028f8 @defunkt basics
authored
264 '("case" "default" "do" "function" "var" "void" "with"
265 "const" "let" "debugger" "enum" "export" "import" "native"
266 "__extends" "__hasProp"))
267
268 ;; CoffeeScript keywords.
d6b78ed @defunkt defvars and basic indentation
authored
269 (defvar coffee-cs-keywords
af34f91 @defunkt Booleans, better colors
authored
270 '("then" "unless" "and" "or" "is"
cf028f8 @defunkt basics
authored
271 "isnt" "not" "of" "by" "where" "when"))
272
0480381 @defunkt mini docs
authored
273 ;; Regular expression combining the above three lists.
d6b78ed @defunkt defvars and basic indentation
authored
274 (defvar coffee-keywords-regexp (regexp-opt
cf028f8 @defunkt basics
authored
275 (append
276 coffee-js-reserved
277 coffee-js-keywords
278 coffee-cs-keywords) 'words))
279
280
ff61700 @defunkt that was bothering me
authored
281 ;; Create the list for font-lock. Each class of keyword is given a
282 ;; particular face.
5256afd @defunkt tweaks
authored
283 (defvar coffee-font-lock-keywords
ff61700 @defunkt that was bothering me
authored
284 ;; *Note*: order below matters. `coffee-keywords-regexp' goes last
285 ;; because otherwise the keyword "state" in the function
286 ;; "state_entry" would be highlighted.
c5831bb @sstephenson Fix for string literals with escaped quotes
sstephenson authored
287 `((,coffee-string-regexp . font-lock-string-face)
288 (,coffee-this-regexp . font-lock-variable-name-face)
dc85f42 @tav Added support for the shorthand::syntax for .prototype.
tav authored
289 (,coffee-prototype-regexp . font-lock-variable-name-face)
ff61700 @defunkt that was bothering me
authored
290 (,coffee-assign-regexp . font-lock-type-face)
291 (,coffee-regexp-regexp . font-lock-constant-face)
292 (,coffee-boolean-regexp . font-lock-constant-face)
293 (,coffee-keywords-regexp . font-lock-keyword-face)))
cf028f8 @defunkt basics
authored
294
3a47b28 @defunkt docs
authored
295 ;;
296 ;; Helper Functions
297 ;;
298
1b887a5 @defunkt clean whitespace on save
authored
299 (defun coffee-before-save ()
300 "Hook run before file is saved. Deletes whitespace if
301 `coffee-cleanup-whitespace' is non-nil."
302 (when coffee-cleanup-whitespace
303 (delete-trailing-whitespace)))
304
9ff612a @defunkt single quotes
authored
305 (defun coffee-comment-dwim (arg)
306 "Comment or uncomment current line or region in a smart way.
307 For detail, see `comment-dwim'."
308 (interactive "*P")
309 (require 'newcomment)
310 (let ((deactivate-mark nil) (comment-start "#") (comment-end ""))
311 (comment-dwim arg)))
84ab684 @defunkt comments
authored
312
227a360 @orlandohill Customize args for compiling files and display the first compiler error
orlandohill authored
313 (defun coffee-command-compile (file-name)
314 "The `coffee-command' with args to compile a file."
315 (mapconcat 'identity (append (list coffee-command) coffee-args-compile (list file-name)) " "))
5b00ae2 @defunkt more todo
authored
316
2504ebe @defunkt explain indentation plan
authored
317 ;;
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
318 ;; imenu support
319 ;;
320
321 ;; This is a pretty naive but workable way of doing it. First we look
322 ;; for any lines that starting with `coffee-assign-regexp' that include
323 ;; `coffee-lambda-regexp' then add those tokens to the list.
324 ;;
325 ;; Should cover cases like these:
326 ;;
327 ;; minus: (x, y) -> x - y
328 ;; String::length: -> 10
329 ;; block: ->
330 ;; print('potion')
331 ;;
332 ;; Next we look for any line that starts with `class' or
333 ;; `coffee-assign-regexp' followed by `{` and drop into a
334 ;; namespace. This means we search one indentation level deeper for
335 ;; more assignments and add them to the alist prefixed with the
336 ;; namespace name.
337 ;;
338 ;; Should cover cases like these:
339 ;;
340 ;; class Person
341 ;; print: ->
342 ;; print 'My name is ' + this.name + '.'
343 ;;
344 ;; class Policeman extends Person
345 ;; constructor: (rank) ->
346 ;; @rank: rank
347 ;; print: ->
348 ;; print 'My name is ' + this.name + " and I'm a " + this.rank + '.'
349 ;;
76ed341 @defunkt class support in imenu
authored
350 ;; TODO:
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
351 ;; app = {
352 ;; window: {width: 200, height: 200}
353 ;; para: -> 'Welcome.'
354 ;; button: -> 'OK'
355 ;; }
356
357 (defun coffee-imenu-create-index ()
358 "Create an imenu index of all methods in the buffer."
359 (interactive)
360
361 ;; This function is called within a `save-excursion' so we're safe.
6639c36 @mkhl Silence byte-compiler warnings.
mkhl authored
362 (goto-char (point-min))
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
363
76ed341 @defunkt class support in imenu
authored
364 (let ((index-alist '()) assign pos indent ns-name ns-indent)
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
365 ;; Go through every assignment that includes -> or => on the same
c6562af @defunkt Make `coffee-debug' a macro
authored
366 ;; line or starts with `class'.
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
367 (while (re-search-forward
76ed341 @defunkt class support in imenu
authored
368 (concat "^\\(\\s *\\)"
369 "\\("
370 coffee-assign-regexp
371 ".+?"
372 coffee-lambda-regexp
373 "\\|"
374 coffee-namespace-regexp
375 "\\)")
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
376 (point-max)
377 t)
378
e1b9c83 @defunkt print full match to debug
authored
379 (coffee-debug "Match: %s" (match-string 0))
380
76ed341 @defunkt class support in imenu
authored
381 ;; If this is the start of a new namespace, save the namespace's
382 ;; indentation level and name.
7f5e1b3 @defunkt Bugfix: Clear old namespace when a new one is found
authored
383 (when (match-string 8)
384 ;; Set the name.
385 (setq ns-name (match-string 8))
386
76ed341 @defunkt class support in imenu
authored
387 ;; If this is a class declaration, add :: to the namespace.
388 (setq ns-name (concat ns-name "::"))
389
390 ;; Save the indentation level.
391 (setq ns-indent (length (match-string 1)))
392
393 ;; Debug
394 (coffee-debug "ns: Found %s with indent %s" ns-name ns-indent))
395
396 ;; If this is an assignment, save the token being
397 ;; assigned. `Please.print:` will be `Please.print`, `block:`
398 ;; will be `block`, etc.
399 (when (setq assign (match-string 3))
400 ;; The position of the match in the buffer.
401 (setq pos (match-beginning 3))
402
403 ;; The indent level of this match
404 (setq indent (length (match-string 1)))
405
406 ;; If we're within the context of a namespace, add that to the
407 ;; front of the assign, e.g.
408 ;; constructor: => Policeman::constructor
409 (when (and ns-name (> indent ns-indent))
410 (setq assign (concat ns-name assign)))
411
412 (coffee-debug "=: Found %s with indent %s" assign indent)
413
414 ;; Clear the namespace if we're no longer indented deeper
415 ;; than it.
416 (when (and ns-name (<= indent ns-indent))
417 (coffee-debug "ns: Clearing %s" ns-name)
418 (setq ns-name nil)
419 (setq ns-indent nil))
420
421 ;; Add this to the alist. Done.
422 (push (cons assign pos) index-alist)))
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
423
424 ;; Return the alist.
425 index-alist))
426
427 ;;
d6b78ed @defunkt defvars and basic indentation
authored
428 ;; Indentation
2504ebe @defunkt explain indentation plan
authored
429 ;;
430
dbb4ec5 @defunkt comment tweaks
authored
431 ;;; The theory is explained in the README.
97a1001 @defunkt basic indentation with TAB
authored
432
d6b78ed @defunkt defvars and basic indentation
authored
433 (defun coffee-indent-line ()
7b03a71 @defunkt indentation theory
authored
434 "Indent current line as CoffeeScript."
d6b78ed @defunkt defvars and basic indentation
authored
435 (interactive)
436
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
437 (if (= (point) (point-at-bol))
438 (insert-tab)
439 (save-excursion
440 (let ((prev-indent 0) (cur-indent 0))
441 ;; Figure out the indentation of the previous line
442 (setd prev-indent (coffee-previous-indent))
97ef92b @defunkt Bugfix: Indentation works when point is at beginning of the line
authored
443
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
444 ;; Figure out the current line's indentation
445 (setd cur-indent (current-indentation))
97ef92b @defunkt Bugfix: Indentation works when point is at beginning of the line
authored
446
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
447 ;; Shift one column to the left
448 (beginning-of-line)
449 (insert-tab)
450
451 (coffee-debug "point: %s" (point))
452 (coffee-debug "point-at-bol: %s" (point-at-bol))
453
454 (when (= (point-at-bol) (point))
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
455 (forward-char coffee-tab-width))
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
456
457 (coffee-debug "New indent: %s" (current-indentation))
b7ee8b8 @defunkt start cleaning up indentation code
authored
458
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
459 ;; We're too far, remove all indentation.
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
460 (when (> (- (current-indentation) prev-indent) coffee-tab-width)
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
461 (backward-to-indentation 0)
462 (delete-region (point-at-bol) (point)))))))
b7ee8b8 @defunkt start cleaning up indentation code
authored
463
464 (defun coffee-previous-indent ()
465 "Return the indentation level of the previous non-blank line."
97ef92b @defunkt Bugfix: Indentation works when point is at beginning of the line
authored
466
b7ee8b8 @defunkt start cleaning up indentation code
authored
467 (save-excursion
468 (forward-line -1)
d063614 @superbobry Fix hanging of `coffee-previous-indent` at start of buffer.
superbobry authored
469 (if (bobp)
470 0
471 (progn
472 (while (coffee-line-empty-p) (forward-line -1))
473 (current-indentation)))))
b7ee8b8 @defunkt start cleaning up indentation code
authored
474
475 (defun coffee-line-empty-p ()
476 "Is this line empty? Returns non-nil if so, nil if not."
477 (or (bobp)
478 (string-match "^\\s *$" (coffee-line-as-string))))
df08186 @defunkt indenters
authored
479
17150cf @defunkt docs and starting to implement indenters
authored
480 (defun coffee-newline-and-indent ()
481 "Inserts a newline and indents it to the same level as the previous line."
482 (interactive)
483
484 ;; Remember the current line indentation level,
485 ;; insert a newline, and indent the newline to the same
486 ;; level as the previous line.
487 (let ((prev-indent (current-indentation)) (indent-next nil))
488 (newline)
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
489 (insert-tab (/ prev-indent coffee-tab-width))
3a47b28 @defunkt docs
authored
490
17150cf @defunkt docs and starting to implement indenters
authored
491 ;; We need to insert an additional tab because the last line was special.
492 (when (coffee-line-wants-indent)
493 (insert-tab)))
494
495 ;; Last line was a comment so this one should probably be,
496 ;; too. Makes it easy to write multi-line comments (like the one I'm
497 ;; writing right now).
498 (when (coffee-previous-line-is-comment)
499 (insert "# ")))
500
501 ;; Indenters help determine whether the current line should be
502 ;; indented further based on the content of the previous line. If a
503 ;; line starts with `class', for instance, you're probably going to
504 ;; want to indent the next line.
505
506 (defvar coffee-indenters-bol '("class" "for" "if" "try")
7d338d1 @defunkt coffee-indenters
authored
507 "Keywords or syntax whose presence at the start of a line means the
508 next line should probably be indented.")
509
510 (defun coffee-indenters-bol-regexp ()
511 "Builds a regexp out of `coffee-indenters-bol' words."
512 (concat "^" (regexp-opt coffee-indenters-bol 'words)))
513
5e170be @defunkt get it working (with a fake function though)
authored
514 (defvar coffee-indenters-eol '(?> ?{ ?\[)
515 "Single characters at the end of a line that mean the next line
516 should probably be indented.")
7d338d1 @defunkt coffee-indenters
authored
517
17150cf @defunkt docs and starting to implement indenters
authored
518 (defun coffee-line-wants-indent ()
519 "Does the current line want to be indented deeper than the previous
df08186 @defunkt indenters
authored
520 line? Returns `t' or `nil'. See the README for more details."
8bfb7ed @defunkt Keep same indentation of last line when creating a new line
authored
521 (interactive)
522
17150cf @defunkt docs and starting to implement indenters
authored
523 (save-excursion
524 (let ((indenter-at-bol) (indenter-at-eol))
525 ;; Go back a line and to the first character.
526 (forward-line -1)
527 (backward-to-indentation 0)
8bfb7ed @defunkt Keep same indentation of last line when creating a new line
authored
528
17150cf @defunkt docs and starting to implement indenters
authored
529 ;; If the next few characters match one of our magic indenter
530 ;; keywords, we want to indent the line we were on originally.
531 (when (looking-at (coffee-indenters-bol-regexp))
c5ffe04 @defunkt setd macro for logging variable values when setting them
authored
532 (setd indenter-at-bol t))
17150cf @defunkt docs and starting to implement indenters
authored
533
534 ;; If that didn't match, go to the back of the line and check to
5e170be @defunkt get it working (with a fake function though)
authored
535 ;; see if the last character matches one of our indenter
536 ;; characters.
17150cf @defunkt docs and starting to implement indenters
authored
537 (when (not indenter-at-bol)
538 (end-of-line)
539
5e170be @defunkt get it working (with a fake function though)
authored
540 ;; Optimized for speed - checks only the last character.
14ab5a0 @defunkt Use Common Lisp's `some` despite its inferior API.
authored
541 (when (some (lambda (char)
542 (= (char-before) char))
543 coffee-indenters-eol)
c5ffe04 @defunkt setd macro for logging variable values when setting them
authored
544 (setd indenter-at-eol t)))
17150cf @defunkt docs and starting to implement indenters
authored
545
546 ;; If we found an indenter, return `t'.
547 (or indenter-at-bol indenter-at-eol))))
548
8bfb7ed @defunkt Keep same indentation of last line when creating a new line
authored
549 (defun coffee-previous-line-is-comment ()
550 "Returns `t' if the previous line is a CoffeeScript comment."
551 (save-excursion
552 (forward-line -1)
553 (coffee-line-is-comment)))
554
555 (defun coffee-line-is-comment ()
556 "Returns `t' if the current line is a CoffeeScript comment."
557 (save-excursion
558 (backward-to-indentation 0)
559 (= (char-after) (string-to-char "#"))))
560
3a47b28 @defunkt docs
authored
561 ;;
562 ;; Define Major Mode
563 ;;
564
af021a5 @josh magic autoload comments
josh authored
565 ;;;###autoload
cf028f8 @defunkt basics
authored
566 (define-derived-mode coffee-mode fundamental-mode
567 "coffee-mode"
568 "Major mode for editing CoffeeScript..."
569
1b887a5 @defunkt clean whitespace on save
authored
570 ;; key bindings
7557530 @defunkt compilation commands
authored
571 (define-key coffee-mode-map (kbd "A-r") 'coffee-compile-buffer)
79333bf @defunkt Bugfix: Bad binding
authored
572 (define-key coffee-mode-map (kbd "A-R") 'coffee-compile-region)
eee29e0 @defunkt defvar the keymap
authored
573 (define-key coffee-mode-map (kbd "A-M-r") 'coffee-repl)
7557530 @defunkt compilation commands
authored
574 (define-key coffee-mode-map [remap comment-dwim] 'coffee-comment-dwim)
8bfb7ed @defunkt Keep same indentation of last line when creating a new line
authored
575 (define-key coffee-mode-map "\C-m" 'coffee-newline-and-indent)
7557530 @defunkt compilation commands
authored
576
cf028f8 @defunkt basics
authored
577 ;; code for syntax highlighting
578 (setq font-lock-defaults '((coffee-font-lock-keywords)))
579
84ab684 @defunkt comments
authored
580 ;; perl style comment: "# ..."
581 (modify-syntax-entry ?# "< b" coffee-mode-syntax-table)
582 (modify-syntax-entry ?\n "> b" coffee-mode-syntax-table)
0480381 @defunkt mini docs
authored
583 (setq comment-start "#")
84ab684 @defunkt comments
authored
584
9ff612a @defunkt single quotes
authored
585 ;; single quote strings
586 (modify-syntax-entry ?' "\"" coffee-mode-syntax-table)
587
d6b78ed @defunkt defvars and basic indentation
authored
588 ;; indentation
589 (make-local-variable 'indent-line-function)
590 (setq indent-line-function 'coffee-indent-line)
f7de20f @defunkt Just in case
authored
591 (setq coffee-tab-width tab-width) ;; Just in case...
d6b78ed @defunkt defvars and basic indentation
authored
592
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
593 ;; imenu
594 (make-local-variable 'imenu-create-index-function)
595 (setq imenu-create-index-function 'coffee-imenu-create-index)
596
d6b78ed @defunkt defvars and basic indentation
authored
597 ;; no tabs
598 (setq indent-tabs-mode nil)
599
1b887a5 @defunkt clean whitespace on save
authored
600 ;; hooks
adf9654 @defunkt blah
authored
601 (set (make-local-variable 'before-save-hook) 'coffee-before-save))
eaf28ab @defunkt register as .coffee handler
authored
602
27ffada @defunkt provide something
authored
603 (provide 'coffee-mode)
604
eaf28ab @defunkt register as .coffee handler
authored
605 ;;
606 ;; On Load
607 ;;
608
609 ;; Run coffee-mode for files ending in .coffee.
af021a5 @josh magic autoload comments
josh authored
610 ;;;###autoload
eaf28ab @defunkt register as .coffee handler
authored
611 (add-to-list 'auto-mode-alist '("\\.coffee$" . coffee-mode))
af021a5 @josh magic autoload comments
josh authored
612 ;;;###autoload
605a330 @defunkt highlight Cakefile too
authored
613 (add-to-list 'auto-mode-alist '("Cakefile" . coffee-mode))
Something went wrong with that request. Please try again.