Skip to content
Newer
Older
100644 732 lines (582 sloc) 22.7 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
244d4b3 @defunkt Update version. Fixes #59
authored
5 ;; Version: 0.4.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
2c204ef @mizchi add coffee-js2coffee-replace-region
mizchi authored
102 (defcustom js2coffee-command "js2coffee"
103 "The js2coffee command used for evaluating code. Must be in your
104 path."
105 :type 'string
106 :group 'coffee)
107
108
3b0962d @defunkt Rename `args` variables to be more consistent.
authored
109 (defcustom coffee-args-repl '("-i")
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
110 "The command line arguments to pass to `coffee-command' to start a REPL."
111 :type 'list
112 :group 'coffee)
28485fa @defunkt `coffee-repl'
authored
113
227a360 @orlandohill Customize args for compiling files and display the first compiler error
orlandohill authored
114 (defcustom coffee-args-compile '("-c")
115 "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
116 :type 'list
117 :group 'coffee)
5f14029 @defunkt Menu
authored
118
2d489a8 @semperos Add support for Cygwin-style absolute paths, which makes using the fi…
semperos authored
119 (defcustom coffee-cygwin-mode t
120 "For Windows systems, add support for Cygwin-style absolute paths."
121 :type 'boolean
122 :group 'coffee)
123
124 (defcustom coffee-cygwin-prefix "/cygdrive/C"
125 "The prefix with which to replace the drive-letter for your Windows partition, e.g. 'C:' would be replaced by '/c/cygdrive'."
126 :type 'string
127 :group 'coffee)
128
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
129 (defcustom coffee-compiled-buffer-name "*coffee-compiled*"
130 "The name of the scratch buffer used when compiling CoffeeScript."
131 :type 'string
132 :group 'coffee)
7557530 @defunkt compilation commands
authored
133
14a0d56 @errge Added coffee-compile-jump-to-error.
errge authored
134 (defcustom coffee-compile-jump-to-error t
135 "Whether to jump to the first error if compilation fails.
136 Please note that the coffee compiler doesn't always give a line
137 number for the issue and in that case it is not possible to jump
138 to the error, of course."
139 :type 'boolean
140 :group 'coffee)
141
0b06b86 @semperos Add support for coffee --watch, and in process also add function for …
semperos authored
142 (defcustom coffee-watch-buffer-name "*coffee-watch*"
143 "The name of the scratch buffer used when using the --watch flag with CoffeeScript."
144 :type 'string
145 :group 'coffee)
146
1b887a5 @defunkt clean whitespace on save
authored
147 (defvar coffee-mode-hook nil
148 "A hook for you to run your own code when the mode is loaded.")
149
eee29e0 @defunkt defvar the keymap
authored
150 (defvar coffee-mode-map (make-keymap)
151 "Keymap for CoffeeScript major mode.")
152
38ce8da @defunkt small tweaks
authored
153 ;;
266849c @defunkt add apply-partially defun
authored
154 ;; Compat
155 ;;
156
157 (unless (fboundp 'apply-partially)
158 (defun apply-partially (fun &rest args)
159 "Return a function that is a partial application of FUN to ARGS.
160 ARGS is a list of the first N arguments to pass to FUN.
161 The result is a new function which does the same as FUN, except that
162 the first N arguments are fixed at the values with which this function
163 was called."
164 (lexical-let ((fun fun) (args1 args))
165 (lambda (&rest args2) (apply fun (append args1 args2))))))
166
167 ;;
c5ffe04 @defunkt setd macro for logging variable values when setting them
authored
168 ;; Macros
5b00ae2 @defunkt more todo
authored
169 ;;
170
c5ffe04 @defunkt setd macro for logging variable values when setting them
authored
171 (defmacro setd (var val)
172 "Like setq but optionally logs the variable's value using `coffee-debug'."
cd06e4e @mkhl Fix compiling `coffee-mode' without loading it.
mkhl authored
173 (if (and (boundp 'coffee-debug-mode) coffee-debug-mode)
c6562af @defunkt Make `coffee-debug' a macro
authored
174 `(progn
175 (coffee-debug "%s: %s" ',var ,val)
176 (setq ,var ,val))
177 `(setq ,var ,val)))
178
2799609 @defunkt back out that macro change for now, something is funny
authored
179 (defun coffee-debug (string &rest args)
c6562af @defunkt Make `coffee-debug' a macro
authored
180 "Print a message when in debug mode."
181 (when coffee-debug-mode
2799609 @defunkt back out that macro change for now, something is funny
authored
182 (apply 'message (append (list string) args))))
c5ffe04 @defunkt setd macro for logging variable values when setting them
authored
183
b7ee8b8 @defunkt start cleaning up indentation code
authored
184 (defmacro coffee-line-as-string ()
185 "Returns the current line as a string."
186 `(buffer-substring (point-at-bol) (point-at-eol)))
187
5b00ae2 @defunkt more todo
authored
188 ;;
38ce8da @defunkt small tweaks
authored
189 ;; Commands
190 ;;
191
28485fa @defunkt `coffee-repl'
authored
192 (defun coffee-repl ()
193 "Launch a CoffeeScript REPL using `coffee-command' as an inferior mode."
194 (interactive)
195
17150cf @defunkt docs and starting to implement indenters
authored
196 (unless (comint-check-proc "*CoffeeREPL*")
28485fa @defunkt `coffee-repl'
authored
197 (set-buffer
17150cf @defunkt docs and starting to implement indenters
authored
198 (apply 'make-comint "CoffeeREPL"
3b0962d @defunkt Rename `args` variables to be more consistent.
authored
199 coffee-command nil coffee-args-repl)))
28485fa @defunkt `coffee-repl'
authored
200
8a3d6f5 Updating coffee-repl to pop-to-buffer using the name '*CoffeeREPL*' i…
Nick Parker authored
201 (pop-to-buffer "*CoffeeREPL*"))
38ce8da @defunkt small tweaks
authored
202
62c84fb @knu Add a function coffee-compiled-file-name for ease of scripting.
knu authored
203 (defun coffee-compiled-file-name (&optional filename)
204 "Returns the name of the JavaScript file compiled from a CoffeeScript file.
205 If FILENAME is omitted, the current buffer's file name is used."
206 (concat (file-name-sans-extension (or filename (buffer-file-name))) ".js"))
207
cbd31f9 @defunkt coffee-compile-file
authored
208 (defun coffee-compile-file ()
209 "Compiles and saves the current file to disk. Doesn't open in a buffer.."
210 (interactive)
227a360 @orlandohill Customize args for compiling files and display the first compiler error
orlandohill authored
211 (let ((compiler-output (shell-command-to-string (coffee-command-compile (buffer-file-name)))))
212 (if (string= compiler-output "")
62c84fb @knu Add a function coffee-compiled-file-name for ease of scripting.
knu authored
213 (message "Compiled and saved %s" (coffee-compiled-file-name))
14a0d56 @errge Added coffee-compile-jump-to-error.
errge authored
214 (let* ((msg (car (split-string compiler-output "[\n\r]+")))
215 (line (and (string-match "on line \\([0-9]+\\)" msg)
216 (string-to-number (match-string 1 msg)))))
217 (message msg)
218 (when (and coffee-compile-jump-to-error line (> line 0))
219 (goto-char (point-min))
220 (forward-line (1- line)))))))
cbd31f9 @defunkt coffee-compile-file
authored
221
7557530 @defunkt compilation commands
authored
222 (defun coffee-compile-buffer ()
17150cf @defunkt docs and starting to implement indenters
authored
223 "Compiles the current buffer and displays the JS in another buffer."
7557530 @defunkt compilation commands
authored
224 (interactive)
225 (save-excursion
226 (coffee-compile-region (point-min) (point-max))))
227
228 (defun coffee-compile-region (start end)
17150cf @defunkt docs and starting to implement indenters
authored
229 "Compiles a region and displays the JS in another buffer."
7557530 @defunkt compilation commands
authored
230 (interactive "r")
231
232 (let ((buffer (get-buffer coffee-compiled-buffer-name)))
233 (when buffer
234 (kill-buffer buffer)))
235
b4270de @semperos Allow existing `coffee-args-compile` to be used for `coffee-compile-r…
semperos authored
236 (apply (apply-partially 'call-process-region start end coffee-command nil
237 (get-buffer-create coffee-compiled-buffer-name)
238 nil)
239 (append coffee-args-compile (list "-s" "-p")))
e3c21f8 @tav Change so that the compiled buffer doesn't open in a new frame.
tav authored
240 (switch-to-buffer (get-buffer coffee-compiled-buffer-name))
7557530 @defunkt compilation commands
authored
241 (funcall coffee-js-mode)
6639c36 @mkhl Silence byte-compiler warnings.
mkhl authored
242 (goto-char (point-min)))
3a47b28 @defunkt docs
authored
243
2c204ef @mizchi add coffee-js2coffee-replace-region
mizchi authored
244 (defun coffee-js2coffee-replace-region (start end)
245 "Replace JS to coffee in current buffer."
246 (interactive "r")
247
248 (let ((buffer (get-buffer coffee-compiled-buffer-name)))
249 (when buffer
250 (kill-buffer buffer)))
251
252 (call-process-region start end
253 js2coffee-command nil
254 (current-buffer)
255 )
256 (delete-region start end)
257 )
258
5f14029 @defunkt Menu
authored
259 (defun coffee-show-version ()
260 "Prints the `coffee-mode' version."
261 (interactive)
262 (message (concat "coffee-mode v" coffee-mode-version)))
263
fd314cc @defunkt menu tweaks
authored
264 (defun coffee-open-reference ()
265 "Open browser to CoffeeScript reference."
266 (interactive)
267 (browse-url "http://jashkenas.github.com/coffee-script/"))
268
48c36c1 @defunkt open node.js api in browser
authored
269 (defun coffee-open-node-reference ()
1f9bc1a @markhellewell nodejs.org docs changed location.
markhellewell authored
270 "Open browser to node.js documentation."
48c36c1 @defunkt open node.js api in browser
authored
271 (interactive)
1f9bc1a @markhellewell nodejs.org docs changed location.
markhellewell authored
272 (browse-url "http://nodejs.org/docs/"))
48c36c1 @defunkt open node.js api in browser
authored
273
5f14029 @defunkt Menu
authored
274 (defun coffee-open-github ()
fd314cc @defunkt menu tweaks
authored
275 "Open browser to `coffee-mode' project on GithHub."
5f14029 @defunkt Menu
authored
276 (interactive)
277 (browse-url "http://github.com/defunkt/coffee-mode"))
278
0b06b86 @semperos Add support for coffee --watch, and in process also add function for …
semperos authored
279 (defun coffee-watch (dir-or-file)
280 "Run `coffee-run-cmd' with the --watch flag enabled for a directory or file"
281 (interactive "fDirectory or File: ")
282 (let ((coffee-compiled-buffer-name coffee-watch-buffer-name)
c8e7fb7 @semperos Use coffee-universal-path for coffee-watch, to properly handle paths …
semperos authored
283 (args (mapconcat 'identity (append coffee-args-compile (list "--watch" (coffee-universal-path dir-or-file))) " ")))
0b06b86 @semperos Add support for coffee --watch, and in process also add function for …
semperos authored
284 (coffee-run-cmd args)))
285
5f14029 @defunkt Menu
authored
286 ;;
287 ;; Menubar
288 ;;
289
290 (easy-menu-define coffee-mode-menu coffee-mode-map
291 "Menu for CoffeeScript mode"
292 '("CoffeeScript"
cbd31f9 @defunkt coffee-compile-file
authored
293 ["Compile File" coffee-compile-file]
5f14029 @defunkt Menu
authored
294 ["Compile Buffer" coffee-compile-buffer]
295 ["Compile Region" coffee-compile-region]
5256afd @defunkt tweaks
authored
296 ["REPL" coffee-repl]
5f14029 @defunkt Menu
authored
297 "---"
48c36c1 @defunkt open node.js api in browser
authored
298 ["CoffeeScript Reference" coffee-open-reference]
299 ["node.js Reference" coffee-open-node-reference]
fd314cc @defunkt menu tweaks
authored
300 ["coffee-mode on GitHub" coffee-open-github]
5f14029 @defunkt Menu
authored
301 ["Version" coffee-show-version]
302 ))
303
3a47b28 @defunkt docs
authored
304 ;;
305 ;; Define Language Syntax
306 ;;
307
c5831bb @sstephenson Fix for string literals with escaped quotes
sstephenson authored
308 ;; String literals
309 (defvar coffee-string-regexp "\"\\([^\\]\\|\\\\.\\)*?\"\\|'\\([^\\]\\|\\\\.\\)*?'")
310
3a47b28 @defunkt docs
authored
311 ;; Instance variables (implicit this)
2bb69be Regex for "this" variable includes underscore.
Ryan Koopmans authored
312 (defvar coffee-this-regexp "@\\(\\w\\|_\\)*\\|this")
3a47b28 @defunkt docs
authored
313
dc85f42 @tav Added support for the shorthand::syntax for .prototype.
tav authored
314 ;; Prototype::access
315 (defvar coffee-prototype-regexp "\\(\\(\\w\\|\\.\\|_\\| \\|$\\)+?\\)::\\(\\(\\w\\|\\.\\|_\\| \\|$\\)+?\\):")
316
88e7b95 @defunkt better regexps, @blah: and {blah: true, blah2:true}
authored
317 ;; Assignment
37b481e @defunkt Better assignment highlighting for `:`
authored
318 (defvar coffee-assign-regexp "\\(\\(\\w\\|\\.\\|_\\|$\\)+?\s*\\):")
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
319
320 ;; Lambda
00c197b @defunkt elisp regexps are fun
authored
321 (defvar coffee-lambda-regexp "\\((.+)\\)?\\s *\\(->\\|=>\\)")
88e7b95 @defunkt better regexps, @blah: and {blah: true, blah2:true}
authored
322
76ed341 @defunkt class support in imenu
authored
323 ;; Namespaces
324 (defvar coffee-namespace-regexp "\\b\\(class\\s +\\(\\S +\\)\\)\\b")
325
af34f91 @defunkt Booleans, better colors
authored
326 ;; Booleans
3900a8c @Wilfred adding undefined to reserved words as part of coffee-boolean-regexp (…
Wilfred authored
327 (defvar coffee-boolean-regexp "\\b\\(true\\|false\\|yes\\|no\\|on\\|off\\|null\\|undefined\\)\\b")
3a47b28 @defunkt docs
authored
328
dbb4ec5 @defunkt comment tweaks
authored
329 ;; Regular Expressions
1fb4956 @levskaya small fix of coffee-regexp-regexp so it doesn't cause massive hangs w…
levskaya authored
330 (defvar coffee-regexp-regexp "\\/\\(\\\\.\\|\\[\\(\\\\.\\|.\\)+?\\]\\|[^/
331 ]\\)+?\\/")
3a47b28 @defunkt docs
authored
332
cf028f8 @defunkt basics
authored
333 ;; JavaScript Keywords
d6b78ed @defunkt defvars and basic indentation
authored
334 (defvar coffee-js-keywords
af34f91 @defunkt Booleans, better colors
authored
335 '("if" "else" "new" "return" "try" "catch"
cf028f8 @defunkt basics
authored
336 "finally" "throw" "break" "continue" "for" "in" "while"
337 "delete" "instanceof" "typeof" "switch" "super" "extends"
7803688 Until and loop are keywords.
Ryan Koopmans authored
338 "class" "until" "loop"))
cf028f8 @defunkt basics
authored
339
340 ;; Reserved keywords either by JS or CS.
d6b78ed @defunkt defvars and basic indentation
authored
341 (defvar coffee-js-reserved
cf028f8 @defunkt basics
authored
342 '("case" "default" "do" "function" "var" "void" "with"
343 "const" "let" "debugger" "enum" "export" "import" "native"
344 "__extends" "__hasProp"))
345
346 ;; CoffeeScript keywords.
d6b78ed @defunkt defvars and basic indentation
authored
347 (defvar coffee-cs-keywords
af34f91 @defunkt Booleans, better colors
authored
348 '("then" "unless" "and" "or" "is"
cf028f8 @defunkt basics
authored
349 "isnt" "not" "of" "by" "where" "when"))
350
0480381 @defunkt mini docs
authored
351 ;; Regular expression combining the above three lists.
d6b78ed @defunkt defvars and basic indentation
authored
352 (defvar coffee-keywords-regexp (regexp-opt
cf028f8 @defunkt basics
authored
353 (append
354 coffee-js-reserved
355 coffee-js-keywords
356 coffee-cs-keywords) 'words))
357
358
ff61700 @defunkt that was bothering me
authored
359 ;; Create the list for font-lock. Each class of keyword is given a
360 ;; particular face.
5256afd @defunkt tweaks
authored
361 (defvar coffee-font-lock-keywords
ff61700 @defunkt that was bothering me
authored
362 ;; *Note*: order below matters. `coffee-keywords-regexp' goes last
363 ;; because otherwise the keyword "state" in the function
364 ;; "state_entry" would be highlighted.
c5831bb @sstephenson Fix for string literals with escaped quotes
sstephenson authored
365 `((,coffee-string-regexp . font-lock-string-face)
366 (,coffee-this-regexp . font-lock-variable-name-face)
dc85f42 @tav Added support for the shorthand::syntax for .prototype.
tav authored
367 (,coffee-prototype-regexp . font-lock-variable-name-face)
ff61700 @defunkt that was bothering me
authored
368 (,coffee-assign-regexp . font-lock-type-face)
369 (,coffee-regexp-regexp . font-lock-constant-face)
370 (,coffee-boolean-regexp . font-lock-constant-face)
371 (,coffee-keywords-regexp . font-lock-keyword-face)))
cf028f8 @defunkt basics
authored
372
3a47b28 @defunkt docs
authored
373 ;;
374 ;; Helper Functions
375 ;;
376
1b887a5 @defunkt clean whitespace on save
authored
377 (defun coffee-before-save ()
378 "Hook run before file is saved. Deletes whitespace if
379 `coffee-cleanup-whitespace' is non-nil."
380 (when coffee-cleanup-whitespace
381 (delete-trailing-whitespace)))
382
9ff612a @defunkt single quotes
authored
383 (defun coffee-comment-dwim (arg)
384 "Comment or uncomment current line or region in a smart way.
385 For detail, see `comment-dwim'."
386 (interactive "*P")
387 (require 'newcomment)
388 (let ((deactivate-mark nil) (comment-start "#") (comment-end ""))
389 (comment-dwim arg)))
84ab684 @defunkt comments
authored
390
2d489a8 @semperos Add support for Cygwin-style absolute paths, which makes using the fi…
semperos authored
391 (defun coffee-cygwin-path (expanded-file-name)
392 "Given an expanded file name, derive the absolute Cygwin path based on `coffee-cygwin-prefix'."
393 (replace-regexp-in-string "^[a-zA-Z]:" coffee-cygwin-prefix expanded-file-name t))
394
e8fd539 @semperos Create function to encapsulate logic of supporting Cygwin-based Windo…
semperos authored
395 (defun coffee-universal-path (file-name)
396 "Handle different paths for different OS configurations for CoffeeScript"
397 (let ((full-file-name (expand-file-name file-name)))
398 (if (and (equal system-type 'windows-nt)
399 coffee-cygwin-mode)
400 (coffee-cygwin-path full-file-name)
401 full-file-name)))
402
227a360 @orlandohill Customize args for compiling files and display the first compiler error
orlandohill authored
403 (defun coffee-command-compile (file-name)
404 "The `coffee-command' with args to compile a file."
e8fd539 @semperos Create function to encapsulate logic of supporting Cygwin-based Windo…
semperos authored
405 (let ((full-file-name (coffee-universal-path file-name)))
2d489a8 @semperos Add support for Cygwin-style absolute paths, which makes using the fi…
semperos authored
406 (mapconcat 'identity (append (list coffee-command) coffee-args-compile (list full-file-name)) " ")))
5b00ae2 @defunkt more todo
authored
407
0b06b86 @semperos Add support for coffee --watch, and in process also add function for …
semperos authored
408 (defun coffee-run-cmd (args)
409 "Given an arbitrary set of arguments for the `coffee-command', compile the command and show output in a custom compilation buffer."
410 (interactive "sArguments: ")
411 (let ((compilation-buffer-name-function (lambda (this-mode)
412 (generate-new-buffer-name coffee-compiled-buffer-name))))
413 (compile (concat coffee-command " " args))))
414
2504ebe @defunkt explain indentation plan
authored
415 ;;
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
416 ;; imenu support
417 ;;
418
419 ;; This is a pretty naive but workable way of doing it. First we look
420 ;; for any lines that starting with `coffee-assign-regexp' that include
421 ;; `coffee-lambda-regexp' then add those tokens to the list.
422 ;;
423 ;; Should cover cases like these:
424 ;;
425 ;; minus: (x, y) -> x - y
426 ;; String::length: -> 10
427 ;; block: ->
428 ;; print('potion')
429 ;;
430 ;; Next we look for any line that starts with `class' or
431 ;; `coffee-assign-regexp' followed by `{` and drop into a
432 ;; namespace. This means we search one indentation level deeper for
433 ;; more assignments and add them to the alist prefixed with the
434 ;; namespace name.
435 ;;
436 ;; Should cover cases like these:
437 ;;
438 ;; class Person
439 ;; print: ->
440 ;; print 'My name is ' + this.name + '.'
441 ;;
442 ;; class Policeman extends Person
443 ;; constructor: (rank) ->
444 ;; @rank: rank
445 ;; print: ->
446 ;; print 'My name is ' + this.name + " and I'm a " + this.rank + '.'
447 ;;
76ed341 @defunkt class support in imenu
authored
448 ;; TODO:
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
449 ;; app = {
450 ;; window: {width: 200, height: 200}
451 ;; para: -> 'Welcome.'
452 ;; button: -> 'OK'
453 ;; }
454
455 (defun coffee-imenu-create-index ()
456 "Create an imenu index of all methods in the buffer."
457 (interactive)
458
459 ;; This function is called within a `save-excursion' so we're safe.
6639c36 @mkhl Silence byte-compiler warnings.
mkhl authored
460 (goto-char (point-min))
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
461
76ed341 @defunkt class support in imenu
authored
462 (let ((index-alist '()) assign pos indent ns-name ns-indent)
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
463 ;; Go through every assignment that includes -> or => on the same
c6562af @defunkt Make `coffee-debug' a macro
authored
464 ;; line or starts with `class'.
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
465 (while (re-search-forward
76ed341 @defunkt class support in imenu
authored
466 (concat "^\\(\\s *\\)"
467 "\\("
468 coffee-assign-regexp
469 ".+?"
470 coffee-lambda-regexp
471 "\\|"
472 coffee-namespace-regexp
473 "\\)")
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
474 (point-max)
475 t)
476
e1b9c83 @defunkt print full match to debug
authored
477 (coffee-debug "Match: %s" (match-string 0))
478
76ed341 @defunkt class support in imenu
authored
479 ;; If this is the start of a new namespace, save the namespace's
480 ;; indentation level and name.
7f5e1b3 @defunkt Bugfix: Clear old namespace when a new one is found
authored
481 (when (match-string 8)
482 ;; Set the name.
483 (setq ns-name (match-string 8))
484
76ed341 @defunkt class support in imenu
authored
485 ;; If this is a class declaration, add :: to the namespace.
486 (setq ns-name (concat ns-name "::"))
487
488 ;; Save the indentation level.
489 (setq ns-indent (length (match-string 1)))
490
491 ;; Debug
492 (coffee-debug "ns: Found %s with indent %s" ns-name ns-indent))
493
494 ;; If this is an assignment, save the token being
495 ;; assigned. `Please.print:` will be `Please.print`, `block:`
496 ;; will be `block`, etc.
497 (when (setq assign (match-string 3))
498 ;; The position of the match in the buffer.
499 (setq pos (match-beginning 3))
500
501 ;; The indent level of this match
502 (setq indent (length (match-string 1)))
503
504 ;; If we're within the context of a namespace, add that to the
505 ;; front of the assign, e.g.
506 ;; constructor: => Policeman::constructor
507 (when (and ns-name (> indent ns-indent))
508 (setq assign (concat ns-name assign)))
509
510 (coffee-debug "=: Found %s with indent %s" assign indent)
511
512 ;; Clear the namespace if we're no longer indented deeper
513 ;; than it.
514 (when (and ns-name (<= indent ns-indent))
515 (coffee-debug "ns: Clearing %s" ns-name)
516 (setq ns-name nil)
517 (setq ns-indent nil))
518
519 ;; Add this to the alist. Done.
520 (push (cons assign pos) index-alist)))
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
521
522 ;; Return the alist.
523 index-alist))
524
525 ;;
d6b78ed @defunkt defvars and basic indentation
authored
526 ;; Indentation
2504ebe @defunkt explain indentation plan
authored
527 ;;
528
dbb4ec5 @defunkt comment tweaks
authored
529 ;;; The theory is explained in the README.
97a1001 @defunkt basic indentation with TAB
authored
530
d6b78ed @defunkt defvars and basic indentation
authored
531 (defun coffee-indent-line ()
7b03a71 @defunkt indentation theory
authored
532 "Indent current line as CoffeeScript."
d6b78ed @defunkt defvars and basic indentation
authored
533 (interactive)
534
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
535 (if (= (point) (point-at-bol))
536 (insert-tab)
537 (save-excursion
538 (let ((prev-indent 0) (cur-indent 0))
539 ;; Figure out the indentation of the previous line
540 (setd prev-indent (coffee-previous-indent))
97ef92b @defunkt Bugfix: Indentation works when point is at beginning of the line
authored
541
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
542 ;; Figure out the current line's indentation
543 (setd cur-indent (current-indentation))
97ef92b @defunkt Bugfix: Indentation works when point is at beginning of the line
authored
544
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
545 ;; Shift one column to the left
546 (beginning-of-line)
547 (insert-tab)
548
549 (coffee-debug "point: %s" (point))
550 (coffee-debug "point-at-bol: %s" (point-at-bol))
551
552 (when (= (point-at-bol) (point))
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
553 (forward-char coffee-tab-width))
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
554
555 (coffee-debug "New indent: %s" (current-indentation))
b7ee8b8 @defunkt start cleaning up indentation code
authored
556
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
557 ;; We're too far, remove all indentation.
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
558 (when (> (- (current-indentation) prev-indent) coffee-tab-width)
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
559 (backward-to-indentation 0)
560 (delete-region (point-at-bol) (point)))))))
b7ee8b8 @defunkt start cleaning up indentation code
authored
561
562 (defun coffee-previous-indent ()
563 "Return the indentation level of the previous non-blank line."
97ef92b @defunkt Bugfix: Indentation works when point is at beginning of the line
authored
564
b7ee8b8 @defunkt start cleaning up indentation code
authored
565 (save-excursion
566 (forward-line -1)
d063614 @superbobry Fix hanging of `coffee-previous-indent` at start of buffer.
superbobry authored
567 (if (bobp)
568 0
569 (progn
f4597a0 @typester Fixed inf loop in coffee-previous-indent
typester authored
570 (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
571 (current-indentation)))))
b7ee8b8 @defunkt start cleaning up indentation code
authored
572
573 (defun coffee-line-empty-p ()
574 "Is this line empty? Returns non-nil if so, nil if not."
575 (or (bobp)
576 (string-match "^\\s *$" (coffee-line-as-string))))
df08186 @defunkt indenters
authored
577
17150cf @defunkt docs and starting to implement indenters
authored
578 (defun coffee-newline-and-indent ()
579 "Inserts a newline and indents it to the same level as the previous line."
580 (interactive)
581
582 ;; Remember the current line indentation level,
583 ;; insert a newline, and indent the newline to the same
584 ;; level as the previous line.
585 (let ((prev-indent (current-indentation)) (indent-next nil))
74a9135 @piranha do not leave trailing whitespaces on newline
piranha authored
586 (delete-horizontal-space t)
17150cf @defunkt docs and starting to implement indenters
authored
587 (newline)
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
588 (insert-tab (/ prev-indent coffee-tab-width))
3a47b28 @defunkt docs
authored
589
17150cf @defunkt docs and starting to implement indenters
authored
590 ;; We need to insert an additional tab because the last line was special.
591 (when (coffee-line-wants-indent)
592 (insert-tab)))
593
594 ;; Last line was a comment so this one should probably be,
595 ;; too. Makes it easy to write multi-line comments (like the one I'm
596 ;; writing right now).
597 (when (coffee-previous-line-is-comment)
598 (insert "# ")))
599
600 ;; Indenters help determine whether the current line should be
601 ;; indented further based on the content of the previous line. If a
602 ;; line starts with `class', for instance, you're probably going to
603 ;; want to indent the next line.
604
605 (defvar coffee-indenters-bol '("class" "for" "if" "try")
7d338d1 @defunkt coffee-indenters
authored
606 "Keywords or syntax whose presence at the start of a line means the
607 next line should probably be indented.")
608
609 (defun coffee-indenters-bol-regexp ()
610 "Builds a regexp out of `coffee-indenters-bol' words."
27df14a @defunkt Fix coffee-indenters-bol-regexp. Closes #36
authored
611 (regexp-opt coffee-indenters-bol 'words))
7d338d1 @defunkt coffee-indenters
authored
612
5e170be @defunkt get it working (with a fake function though)
authored
613 (defvar coffee-indenters-eol '(?> ?{ ?\[)
614 "Single characters at the end of a line that mean the next line
615 should probably be indented.")
7d338d1 @defunkt coffee-indenters
authored
616
17150cf @defunkt docs and starting to implement indenters
authored
617 (defun coffee-line-wants-indent ()
618 "Does the current line want to be indented deeper than the previous
df08186 @defunkt indenters
authored
619 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
620 (interactive)
621
17150cf @defunkt docs and starting to implement indenters
authored
622 (save-excursion
623 (let ((indenter-at-bol) (indenter-at-eol))
624 ;; Go back a line and to the first character.
625 (forward-line -1)
626 (backward-to-indentation 0)
8bfb7ed @defunkt Keep same indentation of last line when creating a new line
authored
627
17150cf @defunkt docs and starting to implement indenters
authored
628 ;; If the next few characters match one of our magic indenter
629 ;; keywords, we want to indent the line we were on originally.
630 (when (looking-at (coffee-indenters-bol-regexp))
c5ffe04 @defunkt setd macro for logging variable values when setting them
authored
631 (setd indenter-at-bol t))
17150cf @defunkt docs and starting to implement indenters
authored
632
633 ;; 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
634 ;; see if the last character matches one of our indenter
635 ;; characters.
17150cf @defunkt docs and starting to implement indenters
authored
636 (when (not indenter-at-bol)
637 (end-of-line)
638
5e170be @defunkt get it working (with a fake function though)
authored
639 ;; Optimized for speed - checks only the last character.
14ab5a0 @defunkt Use Common Lisp's `some` despite its inferior API.
authored
640 (when (some (lambda (char)
641 (= (char-before) char))
642 coffee-indenters-eol)
c5ffe04 @defunkt setd macro for logging variable values when setting them
authored
643 (setd indenter-at-eol t)))
17150cf @defunkt docs and starting to implement indenters
authored
644
645 ;; If we found an indenter, return `t'.
646 (or indenter-at-bol indenter-at-eol))))
647
8bfb7ed @defunkt Keep same indentation of last line when creating a new line
authored
648 (defun coffee-previous-line-is-comment ()
649 "Returns `t' if the previous line is a CoffeeScript comment."
650 (save-excursion
651 (forward-line -1)
652 (coffee-line-is-comment)))
653
654 (defun coffee-line-is-comment ()
655 "Returns `t' if the current line is a CoffeeScript comment."
656 (save-excursion
657 (backward-to-indentation 0)
658 (= (char-after) (string-to-char "#"))))
659
3a47b28 @defunkt docs
authored
660 ;;
661 ;; Define Major Mode
662 ;;
663
af021a5 @josh magic autoload comments
josh authored
664 ;;;###autoload
cf028f8 @defunkt basics
authored
665 (define-derived-mode coffee-mode fundamental-mode
4631aa1 Use "Coffee" as mode name rather than the idiosynchratic "coffee-mode".
Daniel Brockman authored
666 "Coffee"
667 "Major mode for editing CoffeeScript."
cf028f8 @defunkt basics
authored
668
1b887a5 @defunkt clean whitespace on save
authored
669 ;; key bindings
7557530 @defunkt compilation commands
authored
670 (define-key coffee-mode-map (kbd "A-r") 'coffee-compile-buffer)
79333bf @defunkt Bugfix: Bad binding
authored
671 (define-key coffee-mode-map (kbd "A-R") 'coffee-compile-region)
eee29e0 @defunkt defvar the keymap
authored
672 (define-key coffee-mode-map (kbd "A-M-r") 'coffee-repl)
7557530 @defunkt compilation commands
authored
673 (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
674 (define-key coffee-mode-map "\C-m" 'coffee-newline-and-indent)
6eb6d99 @knu Introduce a minor mode `coffee-cos-mode'.
knu authored
675 (define-key coffee-mode-map "\C-c\C-o\C-s" 'coffee-cos-mode)
7557530 @defunkt compilation commands
authored
676
cf028f8 @defunkt basics
authored
677 ;; code for syntax highlighting
678 (setq font-lock-defaults '((coffee-font-lock-keywords)))
679
84ab684 @defunkt comments
authored
680 ;; perl style comment: "# ..."
681 (modify-syntax-entry ?# "< b" coffee-mode-syntax-table)
682 (modify-syntax-entry ?\n "> b" coffee-mode-syntax-table)
40a5bf1 @defunkt comment-start is buffer local. fixes #10
authored
683 (make-local-variable 'comment-start)
0480381 @defunkt mini docs
authored
684 (setq comment-start "#")
84ab684 @defunkt comments
authored
685
9ff612a @defunkt single quotes
authored
686 ;; single quote strings
687 (modify-syntax-entry ?' "\"" coffee-mode-syntax-table)
688
d6b78ed @defunkt defvars and basic indentation
authored
689 ;; indentation
690 (make-local-variable 'indent-line-function)
691 (setq indent-line-function 'coffee-indent-line)
b4b2a53 @kelleyk Small patch to fix indentation behavior; before, the global tab-width…
kelleyk authored
692 (set (make-local-variable 'tab-width) coffee-tab-width)
d6b78ed @defunkt defvars and basic indentation
authored
693
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
694 ;; imenu
695 (make-local-variable 'imenu-create-index-function)
696 (setq imenu-create-index-function 'coffee-imenu-create-index)
697
d6b78ed @defunkt defvars and basic indentation
authored
698 ;; no tabs
699 (setq indent-tabs-mode nil)
700
1b887a5 @defunkt clean whitespace on save
authored
701 ;; hooks
adf9654 @defunkt blah
authored
702 (set (make-local-variable 'before-save-hook) 'coffee-before-save))
eaf28ab @defunkt register as .coffee handler
authored
703
6eb6d99 @knu Introduce a minor mode `coffee-cos-mode'.
knu authored
704 ;;
705 ;; Compile-on-Save minor mode
706 ;;
707
708 (defvar coffee-cos-mode-line " CoS")
709 (make-variable-buffer-local 'coffee-cos-mode-line)
710
711 (define-minor-mode coffee-cos-mode
712 "Toggle compile-on-save for coffee-mode."
713 :group 'coffee-cos :lighter coffee-cos-mode-line
714 (cond
715 (coffee-cos-mode
716 (add-hook 'after-save-hook 'coffee-compile-file nil t))
717 (t
718 (remove-hook 'after-save-hook 'coffee-compile-file t))))
719
27ffada @defunkt provide something
authored
720 (provide 'coffee-mode)
721
eaf28ab @defunkt register as .coffee handler
authored
722 ;;
723 ;; On Load
724 ;;
725
726 ;; Run coffee-mode for files ending in .coffee.
af021a5 @josh magic autoload comments
josh authored
727 ;;;###autoload
eaf28ab @defunkt register as .coffee handler
authored
728 (add-to-list 'auto-mode-alist '("\\.coffee$" . coffee-mode))
af021a5 @josh magic autoload comments
josh authored
729 ;;;###autoload
605a330 @defunkt highlight Cakefile too
authored
730 (add-to-list 'auto-mode-alist '("Cakefile" . coffee-mode))
a9c8f48 Formatting changes for complians with elp/Marmalade
Scott Barron authored
731 ;;; coffee-mode.el ends here
Something went wrong with that request. Please try again.