Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 637 lines (502 sloc) 19.672 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
62c84fb @knu Add a function coffee-compiled-file-name for ease of scripting.
knu authored
159 (defun coffee-compiled-file-name (&optional filename)
160 "Returns the name of the JavaScript file compiled from a CoffeeScript file.
161 If FILENAME is omitted, the current buffer's file name is used."
162 (concat (file-name-sans-extension (or filename (buffer-file-name))) ".js"))
163
cbd31f9 @defunkt coffee-compile-file
authored
164 (defun coffee-compile-file ()
165 "Compiles and saves the current file to disk. Doesn't open in a buffer.."
166 (interactive)
227a360 @orlandohill Customize args for compiling files and display the first compiler error
orlandohill authored
167 (let ((compiler-output (shell-command-to-string (coffee-command-compile (buffer-file-name)))))
168 (if (string= compiler-output "")
62c84fb @knu Add a function coffee-compiled-file-name for ease of scripting.
knu authored
169 (message "Compiled and saved %s" (coffee-compiled-file-name))
227a360 @orlandohill Customize args for compiling files and display the first compiler error
orlandohill authored
170 (message (car (split-string compiler-output "[\n\r]+"))))))
cbd31f9 @defunkt coffee-compile-file
authored
171
7557530 @defunkt compilation commands
authored
172 (defun coffee-compile-buffer ()
17150cf @defunkt docs and starting to implement indenters
authored
173 "Compiles the current buffer and displays the JS in another buffer."
7557530 @defunkt compilation commands
authored
174 (interactive)
175 (save-excursion
176 (coffee-compile-region (point-min) (point-max))))
177
178 (defun coffee-compile-region (start end)
17150cf @defunkt docs and starting to implement indenters
authored
179 "Compiles a region and displays the JS in another buffer."
7557530 @defunkt compilation commands
authored
180 (interactive "r")
181
182 (let ((buffer (get-buffer coffee-compiled-buffer-name)))
183 (when buffer
184 (kill-buffer buffer)))
185
186 (call-process-region start end coffee-command nil
187 (get-buffer-create coffee-compiled-buffer-name)
188 nil
b874bf4 @defunkt --no-wrap is deprecated; please use --bare instead.
authored
189 "-s" "-p" "--bare")
e3c21f8 @tav Change so that the compiled buffer doesn't open in a new frame.
tav authored
190 (switch-to-buffer (get-buffer coffee-compiled-buffer-name))
7557530 @defunkt compilation commands
authored
191 (funcall coffee-js-mode)
6639c36 @mkhl Silence byte-compiler warnings.
mkhl authored
192 (goto-char (point-min)))
3a47b28 @defunkt docs
authored
193
5f14029 @defunkt Menu
authored
194 (defun coffee-show-version ()
195 "Prints the `coffee-mode' version."
196 (interactive)
197 (message (concat "coffee-mode v" coffee-mode-version)))
198
fd314cc @defunkt menu tweaks
authored
199 (defun coffee-open-reference ()
200 "Open browser to CoffeeScript reference."
201 (interactive)
202 (browse-url "http://jashkenas.github.com/coffee-script/"))
203
48c36c1 @defunkt open node.js api in browser
authored
204 (defun coffee-open-node-reference ()
1f9bc1a @markhellewell nodejs.org docs changed location.
markhellewell authored
205 "Open browser to node.js documentation."
48c36c1 @defunkt open node.js api in browser
authored
206 (interactive)
1f9bc1a @markhellewell nodejs.org docs changed location.
markhellewell authored
207 (browse-url "http://nodejs.org/docs/"))
48c36c1 @defunkt open node.js api in browser
authored
208
5f14029 @defunkt Menu
authored
209 (defun coffee-open-github ()
fd314cc @defunkt menu tweaks
authored
210 "Open browser to `coffee-mode' project on GithHub."
5f14029 @defunkt Menu
authored
211 (interactive)
212 (browse-url "http://github.com/defunkt/coffee-mode"))
213
214 ;;
215 ;; Menubar
216 ;;
217
218 (easy-menu-define coffee-mode-menu coffee-mode-map
219 "Menu for CoffeeScript mode"
220 '("CoffeeScript"
cbd31f9 @defunkt coffee-compile-file
authored
221 ["Compile File" coffee-compile-file]
5f14029 @defunkt Menu
authored
222 ["Compile Buffer" coffee-compile-buffer]
223 ["Compile Region" coffee-compile-region]
5256afd @defunkt tweaks
authored
224 ["REPL" coffee-repl]
5f14029 @defunkt Menu
authored
225 "---"
48c36c1 @defunkt open node.js api in browser
authored
226 ["CoffeeScript Reference" coffee-open-reference]
227 ["node.js Reference" coffee-open-node-reference]
fd314cc @defunkt menu tweaks
authored
228 ["coffee-mode on GitHub" coffee-open-github]
5f14029 @defunkt Menu
authored
229 ["Version" coffee-show-version]
230 ))
231
3a47b28 @defunkt docs
authored
232 ;;
233 ;; Define Language Syntax
234 ;;
235
c5831bb @sstephenson Fix for string literals with escaped quotes
sstephenson authored
236 ;; String literals
237 (defvar coffee-string-regexp "\"\\([^\\]\\|\\\\.\\)*?\"\\|'\\([^\\]\\|\\\\.\\)*?'")
238
3a47b28 @defunkt docs
authored
239 ;; Instance variables (implicit this)
2bb69be Regex for "this" variable includes underscore.
Ryan Koopmans authored
240 (defvar coffee-this-regexp "@\\(\\w\\|_\\)*\\|this")
3a47b28 @defunkt docs
authored
241
dc85f42 @tav Added support for the shorthand::syntax for .prototype.
tav authored
242 ;; Prototype::access
243 (defvar coffee-prototype-regexp "\\(\\(\\w\\|\\.\\|_\\| \\|$\\)+?\\)::\\(\\(\\w\\|\\.\\|_\\| \\|$\\)+?\\):")
244
88e7b95 @defunkt better regexps, @blah: and {blah: true, blah2:true}
authored
245 ;; Assignment
37b481e @defunkt Better assignment highlighting for `:`
authored
246 (defvar coffee-assign-regexp "\\(\\(\\w\\|\\.\\|_\\|$\\)+?\s*\\):")
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
247
248 ;; Lambda
00c197b @defunkt elisp regexps are fun
authored
249 (defvar coffee-lambda-regexp "\\((.+)\\)?\\s *\\(->\\|=>\\)")
88e7b95 @defunkt better regexps, @blah: and {blah: true, blah2:true}
authored
250
76ed341 @defunkt class support in imenu
authored
251 ;; Namespaces
252 (defvar coffee-namespace-regexp "\\b\\(class\\s +\\(\\S +\\)\\)\\b")
253
af34f91 @defunkt Booleans, better colors
authored
254 ;; Booleans
af81c6a @defunkt Highlight null. closes #4
authored
255 (defvar coffee-boolean-regexp "\\b\\(true\\|false\\|yes\\|no\\|on\\|off\\|null\\)\\b")
3a47b28 @defunkt docs
authored
256
dbb4ec5 @defunkt comment tweaks
authored
257 ;; Regular Expressions
88e1c8c @mooz Fix for regexp literals with slashes in brackets.
mooz authored
258 (defvar coffee-regexp-regexp "\\/\\(\\\\.\\|\\[\\(\\\\.\\|.\\)+?\\]\\|[^/]\\)+?\\/")
3a47b28 @defunkt docs
authored
259
cf028f8 @defunkt basics
authored
260 ;; JavaScript Keywords
d6b78ed @defunkt defvars and basic indentation
authored
261 (defvar coffee-js-keywords
af34f91 @defunkt Booleans, better colors
authored
262 '("if" "else" "new" "return" "try" "catch"
cf028f8 @defunkt basics
authored
263 "finally" "throw" "break" "continue" "for" "in" "while"
264 "delete" "instanceof" "typeof" "switch" "super" "extends"
7803688 Until and loop are keywords.
Ryan Koopmans authored
265 "class" "until" "loop"))
cf028f8 @defunkt basics
authored
266
267 ;; Reserved keywords either by JS or CS.
d6b78ed @defunkt defvars and basic indentation
authored
268 (defvar coffee-js-reserved
cf028f8 @defunkt basics
authored
269 '("case" "default" "do" "function" "var" "void" "with"
270 "const" "let" "debugger" "enum" "export" "import" "native"
271 "__extends" "__hasProp"))
272
273 ;; CoffeeScript keywords.
d6b78ed @defunkt defvars and basic indentation
authored
274 (defvar coffee-cs-keywords
af34f91 @defunkt Booleans, better colors
authored
275 '("then" "unless" "and" "or" "is"
cf028f8 @defunkt basics
authored
276 "isnt" "not" "of" "by" "where" "when"))
277
0480381 @defunkt mini docs
authored
278 ;; Regular expression combining the above three lists.
d6b78ed @defunkt defvars and basic indentation
authored
279 (defvar coffee-keywords-regexp (regexp-opt
cf028f8 @defunkt basics
authored
280 (append
281 coffee-js-reserved
282 coffee-js-keywords
283 coffee-cs-keywords) 'words))
284
285
ff61700 @defunkt that was bothering me
authored
286 ;; Create the list for font-lock. Each class of keyword is given a
287 ;; particular face.
5256afd @defunkt tweaks
authored
288 (defvar coffee-font-lock-keywords
ff61700 @defunkt that was bothering me
authored
289 ;; *Note*: order below matters. `coffee-keywords-regexp' goes last
290 ;; because otherwise the keyword "state" in the function
291 ;; "state_entry" would be highlighted.
c5831bb @sstephenson Fix for string literals with escaped quotes
sstephenson authored
292 `((,coffee-string-regexp . font-lock-string-face)
293 (,coffee-this-regexp . font-lock-variable-name-face)
dc85f42 @tav Added support for the shorthand::syntax for .prototype.
tav authored
294 (,coffee-prototype-regexp . font-lock-variable-name-face)
ff61700 @defunkt that was bothering me
authored
295 (,coffee-assign-regexp . font-lock-type-face)
296 (,coffee-regexp-regexp . font-lock-constant-face)
297 (,coffee-boolean-regexp . font-lock-constant-face)
298 (,coffee-keywords-regexp . font-lock-keyword-face)))
cf028f8 @defunkt basics
authored
299
3a47b28 @defunkt docs
authored
300 ;;
301 ;; Helper Functions
302 ;;
303
1b887a5 @defunkt clean whitespace on save
authored
304 (defun coffee-before-save ()
305 "Hook run before file is saved. Deletes whitespace if
306 `coffee-cleanup-whitespace' is non-nil."
307 (when coffee-cleanup-whitespace
308 (delete-trailing-whitespace)))
309
9ff612a @defunkt single quotes
authored
310 (defun coffee-comment-dwim (arg)
311 "Comment or uncomment current line or region in a smart way.
312 For detail, see `comment-dwim'."
313 (interactive "*P")
314 (require 'newcomment)
315 (let ((deactivate-mark nil) (comment-start "#") (comment-end ""))
316 (comment-dwim arg)))
84ab684 @defunkt comments
authored
317
227a360 @orlandohill Customize args for compiling files and display the first compiler error
orlandohill authored
318 (defun coffee-command-compile (file-name)
319 "The `coffee-command' with args to compile a file."
320 (mapconcat 'identity (append (list coffee-command) coffee-args-compile (list file-name)) " "))
5b00ae2 @defunkt more todo
authored
321
2504ebe @defunkt explain indentation plan
authored
322 ;;
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
323 ;; imenu support
324 ;;
325
326 ;; This is a pretty naive but workable way of doing it. First we look
327 ;; for any lines that starting with `coffee-assign-regexp' that include
328 ;; `coffee-lambda-regexp' then add those tokens to the list.
329 ;;
330 ;; Should cover cases like these:
331 ;;
332 ;; minus: (x, y) -> x - y
333 ;; String::length: -> 10
334 ;; block: ->
335 ;; print('potion')
336 ;;
337 ;; Next we look for any line that starts with `class' or
338 ;; `coffee-assign-regexp' followed by `{` and drop into a
339 ;; namespace. This means we search one indentation level deeper for
340 ;; more assignments and add them to the alist prefixed with the
341 ;; namespace name.
342 ;;
343 ;; Should cover cases like these:
344 ;;
345 ;; class Person
346 ;; print: ->
347 ;; print 'My name is ' + this.name + '.'
348 ;;
349 ;; class Policeman extends Person
350 ;; constructor: (rank) ->
351 ;; @rank: rank
352 ;; print: ->
353 ;; print 'My name is ' + this.name + " and I'm a " + this.rank + '.'
354 ;;
76ed341 @defunkt class support in imenu
authored
355 ;; TODO:
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
356 ;; app = {
357 ;; window: {width: 200, height: 200}
358 ;; para: -> 'Welcome.'
359 ;; button: -> 'OK'
360 ;; }
361
362 (defun coffee-imenu-create-index ()
363 "Create an imenu index of all methods in the buffer."
364 (interactive)
365
366 ;; This function is called within a `save-excursion' so we're safe.
6639c36 @mkhl Silence byte-compiler warnings.
mkhl authored
367 (goto-char (point-min))
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
368
76ed341 @defunkt class support in imenu
authored
369 (let ((index-alist '()) assign pos indent ns-name ns-indent)
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
370 ;; Go through every assignment that includes -> or => on the same
c6562af @defunkt Make `coffee-debug' a macro
authored
371 ;; line or starts with `class'.
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
372 (while (re-search-forward
76ed341 @defunkt class support in imenu
authored
373 (concat "^\\(\\s *\\)"
374 "\\("
375 coffee-assign-regexp
376 ".+?"
377 coffee-lambda-regexp
378 "\\|"
379 coffee-namespace-regexp
380 "\\)")
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
381 (point-max)
382 t)
383
e1b9c83 @defunkt print full match to debug
authored
384 (coffee-debug "Match: %s" (match-string 0))
385
76ed341 @defunkt class support in imenu
authored
386 ;; If this is the start of a new namespace, save the namespace's
387 ;; indentation level and name.
7f5e1b3 @defunkt Bugfix: Clear old namespace when a new one is found
authored
388 (when (match-string 8)
389 ;; Set the name.
390 (setq ns-name (match-string 8))
391
76ed341 @defunkt class support in imenu
authored
392 ;; If this is a class declaration, add :: to the namespace.
393 (setq ns-name (concat ns-name "::"))
394
395 ;; Save the indentation level.
396 (setq ns-indent (length (match-string 1)))
397
398 ;; Debug
399 (coffee-debug "ns: Found %s with indent %s" ns-name ns-indent))
400
401 ;; If this is an assignment, save the token being
402 ;; assigned. `Please.print:` will be `Please.print`, `block:`
403 ;; will be `block`, etc.
404 (when (setq assign (match-string 3))
405 ;; The position of the match in the buffer.
406 (setq pos (match-beginning 3))
407
408 ;; The indent level of this match
409 (setq indent (length (match-string 1)))
410
411 ;; If we're within the context of a namespace, add that to the
412 ;; front of the assign, e.g.
413 ;; constructor: => Policeman::constructor
414 (when (and ns-name (> indent ns-indent))
415 (setq assign (concat ns-name assign)))
416
417 (coffee-debug "=: Found %s with indent %s" assign indent)
418
419 ;; Clear the namespace if we're no longer indented deeper
420 ;; than it.
421 (when (and ns-name (<= indent ns-indent))
422 (coffee-debug "ns: Clearing %s" ns-name)
423 (setq ns-name nil)
424 (setq ns-indent nil))
425
426 ;; Add this to the alist. Done.
427 (push (cons assign pos) index-alist)))
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
428
429 ;; Return the alist.
430 index-alist))
431
432 ;;
d6b78ed @defunkt defvars and basic indentation
authored
433 ;; Indentation
2504ebe @defunkt explain indentation plan
authored
434 ;;
435
dbb4ec5 @defunkt comment tweaks
authored
436 ;;; The theory is explained in the README.
97a1001 @defunkt basic indentation with TAB
authored
437
d6b78ed @defunkt defvars and basic indentation
authored
438 (defun coffee-indent-line ()
7b03a71 @defunkt indentation theory
authored
439 "Indent current line as CoffeeScript."
d6b78ed @defunkt defvars and basic indentation
authored
440 (interactive)
441
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
442 (if (= (point) (point-at-bol))
443 (insert-tab)
444 (save-excursion
445 (let ((prev-indent 0) (cur-indent 0))
446 ;; Figure out the indentation of the previous line
447 (setd prev-indent (coffee-previous-indent))
97ef92b @defunkt Bugfix: Indentation works when point is at beginning of the line
authored
448
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
449 ;; Figure out the current line's indentation
450 (setd cur-indent (current-indentation))
97ef92b @defunkt Bugfix: Indentation works when point is at beginning of the line
authored
451
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
452 ;; Shift one column to the left
453 (beginning-of-line)
454 (insert-tab)
455
456 (coffee-debug "point: %s" (point))
457 (coffee-debug "point-at-bol: %s" (point-at-bol))
458
459 (when (= (point-at-bol) (point))
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
460 (forward-char coffee-tab-width))
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
461
462 (coffee-debug "New indent: %s" (current-indentation))
b7ee8b8 @defunkt start cleaning up indentation code
authored
463
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
464 ;; We're too far, remove all indentation.
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
465 (when (> (- (current-indentation) prev-indent) coffee-tab-width)
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
466 (backward-to-indentation 0)
467 (delete-region (point-at-bol) (point)))))))
b7ee8b8 @defunkt start cleaning up indentation code
authored
468
469 (defun coffee-previous-indent ()
470 "Return the indentation level of the previous non-blank line."
97ef92b @defunkt Bugfix: Indentation works when point is at beginning of the line
authored
471
b7ee8b8 @defunkt start cleaning up indentation code
authored
472 (save-excursion
473 (forward-line -1)
d063614 @superbobry Fix hanging of `coffee-previous-indent` at start of buffer.
superbobry authored
474 (if (bobp)
475 0
476 (progn
f4597a0 @typester Fixed inf loop in coffee-previous-indent
typester authored
477 (while (and (coffee-line-empty-p) (not (bobp))) (forward-line -1))
d063614 @superbobry Fix hanging of `coffee-previous-indent` at start of buffer.
superbobry authored
478 (current-indentation)))))
b7ee8b8 @defunkt start cleaning up indentation code
authored
479
480 (defun coffee-line-empty-p ()
481 "Is this line empty? Returns non-nil if so, nil if not."
482 (or (bobp)
483 (string-match "^\\s *$" (coffee-line-as-string))))
df08186 @defunkt indenters
authored
484
17150cf @defunkt docs and starting to implement indenters
authored
485 (defun coffee-newline-and-indent ()
486 "Inserts a newline and indents it to the same level as the previous line."
487 (interactive)
488
489 ;; Remember the current line indentation level,
490 ;; insert a newline, and indent the newline to the same
491 ;; level as the previous line.
492 (let ((prev-indent (current-indentation)) (indent-next nil))
74a9135 @piranha do not leave trailing whitespaces on newline
piranha authored
493 (delete-horizontal-space t)
17150cf @defunkt docs and starting to implement indenters
authored
494 (newline)
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
495 (insert-tab (/ prev-indent coffee-tab-width))
3a47b28 @defunkt docs
authored
496
17150cf @defunkt docs and starting to implement indenters
authored
497 ;; We need to insert an additional tab because the last line was special.
498 (when (coffee-line-wants-indent)
499 (insert-tab)))
500
501 ;; Last line was a comment so this one should probably be,
502 ;; too. Makes it easy to write multi-line comments (like the one I'm
503 ;; writing right now).
504 (when (coffee-previous-line-is-comment)
505 (insert "# ")))
506
507 ;; Indenters help determine whether the current line should be
508 ;; indented further based on the content of the previous line. If a
509 ;; line starts with `class', for instance, you're probably going to
510 ;; want to indent the next line.
511
512 (defvar coffee-indenters-bol '("class" "for" "if" "try")
7d338d1 @defunkt coffee-indenters
authored
513 "Keywords or syntax whose presence at the start of a line means the
514 next line should probably be indented.")
515
516 (defun coffee-indenters-bol-regexp ()
517 "Builds a regexp out of `coffee-indenters-bol' words."
518 (concat "^" (regexp-opt coffee-indenters-bol 'words)))
519
5e170be @defunkt get it working (with a fake function though)
authored
520 (defvar coffee-indenters-eol '(?> ?{ ?\[)
521 "Single characters at the end of a line that mean the next line
522 should probably be indented.")
7d338d1 @defunkt coffee-indenters
authored
523
17150cf @defunkt docs and starting to implement indenters
authored
524 (defun coffee-line-wants-indent ()
525 "Does the current line want to be indented deeper than the previous
df08186 @defunkt indenters
authored
526 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
527 (interactive)
528
17150cf @defunkt docs and starting to implement indenters
authored
529 (save-excursion
530 (let ((indenter-at-bol) (indenter-at-eol))
531 ;; Go back a line and to the first character.
532 (forward-line -1)
533 (backward-to-indentation 0)
8bfb7ed @defunkt Keep same indentation of last line when creating a new line
authored
534
17150cf @defunkt docs and starting to implement indenters
authored
535 ;; If the next few characters match one of our magic indenter
536 ;; keywords, we want to indent the line we were on originally.
537 (when (looking-at (coffee-indenters-bol-regexp))
c5ffe04 @defunkt setd macro for logging variable values when setting them
authored
538 (setd indenter-at-bol t))
17150cf @defunkt docs and starting to implement indenters
authored
539
540 ;; 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
541 ;; see if the last character matches one of our indenter
542 ;; characters.
17150cf @defunkt docs and starting to implement indenters
authored
543 (when (not indenter-at-bol)
544 (end-of-line)
545
5e170be @defunkt get it working (with a fake function though)
authored
546 ;; Optimized for speed - checks only the last character.
14ab5a0 @defunkt Use Common Lisp's `some` despite its inferior API.
authored
547 (when (some (lambda (char)
548 (= (char-before) char))
549 coffee-indenters-eol)
c5ffe04 @defunkt setd macro for logging variable values when setting them
authored
550 (setd indenter-at-eol t)))
17150cf @defunkt docs and starting to implement indenters
authored
551
552 ;; If we found an indenter, return `t'.
553 (or indenter-at-bol indenter-at-eol))))
554
8bfb7ed @defunkt Keep same indentation of last line when creating a new line
authored
555 (defun coffee-previous-line-is-comment ()
556 "Returns `t' if the previous line is a CoffeeScript comment."
557 (save-excursion
558 (forward-line -1)
559 (coffee-line-is-comment)))
560
561 (defun coffee-line-is-comment ()
562 "Returns `t' if the current line is a CoffeeScript comment."
563 (save-excursion
564 (backward-to-indentation 0)
565 (= (char-after) (string-to-char "#"))))
566
3a47b28 @defunkt docs
authored
567 ;;
568 ;; Define Major Mode
569 ;;
570
af021a5 @josh magic autoload comments
josh authored
571 ;;;###autoload
cf028f8 @defunkt basics
authored
572 (define-derived-mode coffee-mode fundamental-mode
4631aa1 Use "Coffee" as mode name rather than the idiosynchratic "coffee-mode".
Daniel Brockman authored
573 "Coffee"
574 "Major mode for editing CoffeeScript."
cf028f8 @defunkt basics
authored
575
1b887a5 @defunkt clean whitespace on save
authored
576 ;; key bindings
7557530 @defunkt compilation commands
authored
577 (define-key coffee-mode-map (kbd "A-r") 'coffee-compile-buffer)
79333bf @defunkt Bugfix: Bad binding
authored
578 (define-key coffee-mode-map (kbd "A-R") 'coffee-compile-region)
eee29e0 @defunkt defvar the keymap
authored
579 (define-key coffee-mode-map (kbd "A-M-r") 'coffee-repl)
7557530 @defunkt compilation commands
authored
580 (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
581 (define-key coffee-mode-map "\C-m" 'coffee-newline-and-indent)
6eb6d99 @knu Introduce a minor mode `coffee-cos-mode'.
knu authored
582 (define-key coffee-mode-map "\C-c\C-o\C-s" 'coffee-cos-mode)
7557530 @defunkt compilation commands
authored
583
cf028f8 @defunkt basics
authored
584 ;; code for syntax highlighting
585 (setq font-lock-defaults '((coffee-font-lock-keywords)))
586
84ab684 @defunkt comments
authored
587 ;; perl style comment: "# ..."
588 (modify-syntax-entry ?# "< b" coffee-mode-syntax-table)
589 (modify-syntax-entry ?\n "> b" coffee-mode-syntax-table)
40a5bf1 @defunkt comment-start is buffer local. fixes #10
authored
590 (make-local-variable 'comment-start)
0480381 @defunkt mini docs
authored
591 (setq comment-start "#")
84ab684 @defunkt comments
authored
592
9ff612a @defunkt single quotes
authored
593 ;; single quote strings
594 (modify-syntax-entry ?' "\"" coffee-mode-syntax-table)
595
d6b78ed @defunkt defvars and basic indentation
authored
596 ;; indentation
597 (make-local-variable 'indent-line-function)
598 (setq indent-line-function 'coffee-indent-line)
599
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
600 ;; imenu
601 (make-local-variable 'imenu-create-index-function)
602 (setq imenu-create-index-function 'coffee-imenu-create-index)
603
d6b78ed @defunkt defvars and basic indentation
authored
604 ;; no tabs
605 (setq indent-tabs-mode nil)
606
1b887a5 @defunkt clean whitespace on save
authored
607 ;; hooks
adf9654 @defunkt blah
authored
608 (set (make-local-variable 'before-save-hook) 'coffee-before-save))
eaf28ab @defunkt register as .coffee handler
authored
609
6eb6d99 @knu Introduce a minor mode `coffee-cos-mode'.
knu authored
610 ;;
611 ;; Compile-on-Save minor mode
612 ;;
613
614 (defvar coffee-cos-mode-line " CoS")
615 (make-variable-buffer-local 'coffee-cos-mode-line)
616
617 (define-minor-mode coffee-cos-mode
618 "Toggle compile-on-save for coffee-mode."
619 :group 'coffee-cos :lighter coffee-cos-mode-line
620 (cond
621 (coffee-cos-mode
622 (add-hook 'after-save-hook 'coffee-compile-file nil t))
623 (t
624 (remove-hook 'after-save-hook 'coffee-compile-file t))))
625
27ffada @defunkt provide something
authored
626 (provide 'coffee-mode)
627
eaf28ab @defunkt register as .coffee handler
authored
628 ;;
629 ;; On Load
630 ;;
631
632 ;; Run coffee-mode for files ending in .coffee.
af021a5 @josh magic autoload comments
josh authored
633 ;;;###autoload
eaf28ab @defunkt register as .coffee handler
authored
634 (add-to-list 'auto-mode-alist '("\\.coffee$" . coffee-mode))
af021a5 @josh magic autoload comments
josh authored
635 ;;;###autoload
605a330 @defunkt highlight Cakefile too
authored
636 (add-to-list 'auto-mode-alist '("Cakefile" . coffee-mode))
Something went wrong with that request. Please try again.