Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 616 lines (494 sloc) 19.604 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
89203e8 @rrthomas Make version consistent and bump it to 0.4.1 (Fixes #61).
rrthomas authored
5 ;; Version: 0.4.1
4a11c70 @defunkt documentation
authored
6 ;; Keywords: CoffeeScript major mode
7 ;; Author: Chris Wanstrath <chris@ozmm.org>
1d2aed3 @defunkt Use correct URL. Fixes #71
authored
8 ;; URL: http://github.com/defunkt/coffee-mode
4a11c70 @defunkt documentation
authored
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
89203e8 @rrthomas Make version consistent and bump it to 0.4.1 (Fixes #61).
rrthomas authored
69 (defconst coffee-mode-version "0.4.1"
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
70 "The version of `coffee-mode'.")
5f14029 @defunkt Menu
authored
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-tab-width tab-width
77 "The tab width to use when indenting."
78 :type 'integer
79 :group 'coffee)
80
81 (defcustom coffee-command "coffee"
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
82 "The CoffeeScript command used for evaluating code."
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
83 :type 'string
84 :group 'coffee)
7557530 @defunkt compilation commands
authored
85
2c204ef @mizchi add coffee-js2coffee-replace-region
mizchi authored
86 (defcustom js2coffee-command "js2coffee"
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
87 "The js2coffee command used for evaluating code."
2c204ef @mizchi add coffee-js2coffee-replace-region
mizchi authored
88 :type 'string
89 :group 'coffee)
90
3b0962d @defunkt Rename `args` variables to be more consistent.
authored
91 (defcustom coffee-args-repl '("-i")
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
92 "The arguments to pass to `coffee-command' to start a REPL."
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
93 :type 'list
94 :group 'coffee)
28485fa @defunkt `coffee-repl'
authored
95
227a360 @orlandohill Customize args for compiling files and display the first compiler error
orlandohill authored
96 (defcustom coffee-args-compile '("-c")
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
97 "The arguments to pass to `coffee-command' to compile a file."
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
98 :type 'list
99 :group 'coffee)
5f14029 @defunkt Menu
authored
100
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
101 (defcustom coffee-compiled-buffer-name "*coffee-compiled*"
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
102 "The name of the scratch buffer used for compiled CoffeeScript."
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
103 :type 'string
104 :group 'coffee)
7557530 @defunkt compilation commands
authored
105
14a0d56 @errge Added coffee-compile-jump-to-error.
errge authored
106 (defcustom coffee-compile-jump-to-error t
107 "Whether to jump to the first error if compilation fails.
108 Please note that the coffee compiler doesn't always give a line
109 number for the issue and in that case it is not possible to jump
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
110 to the error."
14a0d56 @errge Added coffee-compile-jump-to-error.
errge authored
111 :type 'boolean
112 :group 'coffee)
113
0b06b86 @semperos Add support for coffee --watch, and in process also add function for …
semperos authored
114 (defcustom coffee-watch-buffer-name "*coffee-watch*"
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
115 "The name of the scratch buffer used when using the --watch flag
116 with CoffeeScript."
0b06b86 @semperos Add support for coffee --watch, and in process also add function for …
semperos authored
117 :type 'string
118 :group 'coffee)
119
6d1d92d @rrthomas Make coffee-mode-hook customizable.
rrthomas authored
120 (defcustom coffee-mode-hook nil
121 "Hook called by `coffee-mode'."
122 :type 'hook
123 :group 'coffee)
1b887a5 @defunkt clean whitespace on save
authored
124
e8b2ab6 don't force key redefinition in major-mode hook
Le Wang authored
125 (defvar coffee-mode-map (let ((map (make-sparse-keymap)))
126 ;; key bindings
127 (define-key map (kbd "A-r") 'coffee-compile-buffer)
128 (define-key map (kbd "A-R") 'coffee-compile-region)
129 (define-key map (kbd "A-M-r") 'coffee-repl)
130 (define-key map [remap comment-dwim] 'coffee-comment-dwim)
131 (define-key map "\C-m" 'coffee-newline-and-indent)
132 (define-key map "\C-c\C-o\C-s" 'coffee-cos-mode)
133 map)
eee29e0 @defunkt defvar the keymap
authored
134 "Keymap for CoffeeScript major mode.")
135
38ce8da @defunkt small tweaks
authored
136 ;;
137 ;; Commands
138 ;;
139
28485fa @defunkt `coffee-repl'
authored
140 (defun coffee-repl ()
141 "Launch a CoffeeScript REPL using `coffee-command' as an inferior mode."
142 (interactive)
143
17150cf @defunkt docs and starting to implement indenters
authored
144 (unless (comint-check-proc "*CoffeeREPL*")
28485fa @defunkt `coffee-repl'
authored
145 (set-buffer
17150cf @defunkt docs and starting to implement indenters
authored
146 (apply 'make-comint "CoffeeREPL"
3b0962d @defunkt Rename `args` variables to be more consistent.
authored
147 coffee-command nil coffee-args-repl)))
28485fa @defunkt `coffee-repl'
authored
148
8a3d6f5 Updating coffee-repl to pop-to-buffer using the name '*CoffeeREPL*' i…
Nick Parker authored
149 (pop-to-buffer "*CoffeeREPL*"))
38ce8da @defunkt small tweaks
authored
150
62c84fb @knu Add a function coffee-compiled-file-name for ease of scripting.
knu authored
151 (defun coffee-compiled-file-name (&optional filename)
152 "Returns the name of the JavaScript file compiled from a CoffeeScript file.
153 If FILENAME is omitted, the current buffer's file name is used."
154 (concat (file-name-sans-extension (or filename (buffer-file-name))) ".js"))
155
cbd31f9 @defunkt coffee-compile-file
authored
156 (defun coffee-compile-file ()
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
157 "Compiles and saves the current file to disk."
cbd31f9 @defunkt coffee-compile-file
authored
158 (interactive)
227a360 @orlandohill Customize args for compiling files and display the first compiler error
orlandohill authored
159 (let ((compiler-output (shell-command-to-string (coffee-command-compile (buffer-file-name)))))
160 (if (string= compiler-output "")
62c84fb @knu Add a function coffee-compiled-file-name for ease of scripting.
knu authored
161 (message "Compiled and saved %s" (coffee-compiled-file-name))
14a0d56 @errge Added coffee-compile-jump-to-error.
errge authored
162 (let* ((msg (car (split-string compiler-output "[\n\r]+")))
66c326b @rrthomas Remove custom debug code; should use the standard Emacs debugging too…
rrthomas authored
163 (line (and (string-match "on line \\([0-9]+\\)" msg)
164 (string-to-number (match-string 1 msg)))))
165 (message msg)
166 (when (and coffee-compile-jump-to-error line (> line 0))
167 (goto-char (point-min))
168 (forward-line (1- line)))))))
cbd31f9 @defunkt coffee-compile-file
authored
169
7557530 @defunkt compilation commands
authored
170 (defun coffee-compile-buffer ()
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
171 "Compiles the current buffer and displays the JavaScript in a buffer
172 called `coffee-compiled-buffer-name'."
7557530 @defunkt compilation commands
authored
173 (interactive)
174 (save-excursion
175 (coffee-compile-region (point-min) (point-max))))
176
177 (defun coffee-compile-region (start end)
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
178 "Compiles a region and displays the JavaScript in a buffer called
179 `coffee-compiled-buffer-name'."
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
b4270de @semperos Allow existing `coffee-args-compile` to be used for `coffee-compile-r…
semperos authored
186 (apply (apply-partially 'call-process-region start end coffee-command nil
187 (get-buffer-create coffee-compiled-buffer-name)
188 nil)
189 (append coffee-args-compile (list "-s" "-p")))
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))
eb912a1 @bretthoerner Fix let paren placement in let form.
bretthoerner authored
191 (let ((buffer-file-name "tmp.js")) (set-auto-mode))
6639c36 @mkhl Silence byte-compiler warnings.
mkhl authored
192 (goto-char (point-min)))
3a47b28 @defunkt docs
authored
193
2c204ef @mizchi add coffee-js2coffee-replace-region
mizchi authored
194 (defun coffee-js2coffee-replace-region (start end)
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
195 "Convert JavaScript in the region into CoffeeScript."
2c204ef @mizchi add coffee-js2coffee-replace-region
mizchi authored
196 (interactive "r")
197
198 (let ((buffer (get-buffer coffee-compiled-buffer-name)))
199 (when buffer
200 (kill-buffer buffer)))
201
66c326b @rrthomas Remove custom debug code; should use the standard Emacs debugging too…
rrthomas authored
202 (call-process-region start end
2c204ef @mizchi add coffee-js2coffee-replace-region
mizchi authored
203 js2coffee-command nil
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
204 (current-buffer))
205 (delete-region start end))
2c204ef @mizchi add coffee-js2coffee-replace-region
mizchi authored
206
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
207 (defun coffee-version ()
208 "Show the `coffee-mode' version in the echo area."
5f14029 @defunkt Menu
authored
209 (interactive)
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
210 (message (concat "coffee-mode version " coffee-mode-version)))
5f14029 @defunkt Menu
authored
211
0b06b86 @semperos Add support for coffee --watch, and in process also add function for …
semperos authored
212 (defun coffee-watch (dir-or-file)
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
213 "Run `coffee-run-cmd' with the --watch flag on a directory or file."
0b06b86 @semperos Add support for coffee --watch, and in process also add function for …
semperos authored
214 (interactive "fDirectory or File: ")
215 (let ((coffee-compiled-buffer-name coffee-watch-buffer-name)
66c326b @rrthomas Remove custom debug code; should use the standard Emacs debugging too…
rrthomas authored
216 (args (mapconcat 'identity (append coffee-args-compile (list "--watch" (expand-file-name dir-or-file))) " ")))
0b06b86 @semperos Add support for coffee --watch, and in process also add function for …
semperos authored
217 (coffee-run-cmd args)))
218
5f14029 @defunkt Menu
authored
219 ;;
220 ;; Menubar
221 ;;
222
223 (easy-menu-define coffee-mode-menu coffee-mode-map
224 "Menu for CoffeeScript mode"
225 '("CoffeeScript"
cbd31f9 @defunkt coffee-compile-file
authored
226 ["Compile File" coffee-compile-file]
5f14029 @defunkt Menu
authored
227 ["Compile Buffer" coffee-compile-buffer]
228 ["Compile Region" coffee-compile-region]
5256afd @defunkt tweaks
authored
229 ["REPL" coffee-repl]
5f14029 @defunkt Menu
authored
230 "---"
231 ["Version" coffee-show-version]
232 ))
233
3a47b28 @defunkt docs
authored
234 ;;
235 ;; Define Language Syntax
236 ;;
237
c5831bb @sstephenson Fix for string literals with escaped quotes
sstephenson authored
238 ;; String literals
239 (defvar coffee-string-regexp "\"\\([^\\]\\|\\\\.\\)*?\"\\|'\\([^\\]\\|\\\\.\\)*?'")
240
3a47b28 @defunkt docs
authored
241 ;; Instance variables (implicit this)
2bb69be Regex for "this" variable includes underscore.
Ryan Koopmans authored
242 (defvar coffee-this-regexp "@\\(\\w\\|_\\)*\\|this")
3a47b28 @defunkt docs
authored
243
dc85f42 @tav Added support for the shorthand::syntax for .prototype.
tav authored
244 ;; Prototype::access
245 (defvar coffee-prototype-regexp "\\(\\(\\w\\|\\.\\|_\\| \\|$\\)+?\\)::\\(\\(\\w\\|\\.\\|_\\| \\|$\\)+?\\):")
246
88e7b95 @defunkt better regexps, @blah: and {blah: true, blah2:true}
authored
247 ;; Assignment
37b481e @defunkt Better assignment highlighting for `:`
authored
248 (defvar coffee-assign-regexp "\\(\\(\\w\\|\\.\\|_\\|$\\)+?\s*\\):")
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
249
250 ;; Lambda
00c197b @defunkt elisp regexps are fun
authored
251 (defvar coffee-lambda-regexp "\\((.+)\\)?\\s *\\(->\\|=>\\)")
88e7b95 @defunkt better regexps, @blah: and {blah: true, blah2:true}
authored
252
76ed341 @defunkt class support in imenu
authored
253 ;; Namespaces
254 (defvar coffee-namespace-regexp "\\b\\(class\\s +\\(\\S +\\)\\)\\b")
255
af34f91 @defunkt Booleans, better colors
authored
256 ;; Booleans
3900a8c @Wilfred adding undefined to reserved words as part of coffee-boolean-regexp (…
Wilfred authored
257 (defvar coffee-boolean-regexp "\\b\\(true\\|false\\|yes\\|no\\|on\\|off\\|null\\|undefined\\)\\b")
3a47b28 @defunkt docs
authored
258
dbb4ec5 @defunkt comment tweaks
authored
259 ;; Regular Expressions
88e1c8c @mooz Fix for regexp literals with slashes in brackets.
mooz authored
260 (defvar coffee-regexp-regexp "\\/\\(\\\\.\\|\\[\\(\\\\.\\|.\\)+?\\]\\|[^/]\\)+?\\/")
3a47b28 @defunkt docs
authored
261
cf028f8 @defunkt basics
authored
262 ;; JavaScript Keywords
d6b78ed @defunkt defvars and basic indentation
authored
263 (defvar coffee-js-keywords
af34f91 @defunkt Booleans, better colors
authored
264 '("if" "else" "new" "return" "try" "catch"
cf028f8 @defunkt basics
authored
265 "finally" "throw" "break" "continue" "for" "in" "while"
266 "delete" "instanceof" "typeof" "switch" "super" "extends"
7803688 Until and loop are keywords.
Ryan Koopmans authored
267 "class" "until" "loop"))
cf028f8 @defunkt basics
authored
268
269 ;; Reserved keywords either by JS or CS.
d6b78ed @defunkt defvars and basic indentation
authored
270 (defvar coffee-js-reserved
cf028f8 @defunkt basics
authored
271 '("case" "default" "do" "function" "var" "void" "with"
272 "const" "let" "debugger" "enum" "export" "import" "native"
273 "__extends" "__hasProp"))
274
275 ;; CoffeeScript keywords.
d6b78ed @defunkt defvars and basic indentation
authored
276 (defvar coffee-cs-keywords
af34f91 @defunkt Booleans, better colors
authored
277 '("then" "unless" "and" "or" "is"
cf028f8 @defunkt basics
authored
278 "isnt" "not" "of" "by" "where" "when"))
279
80ebae6 Added support for iced coffeescript await and defer keywords.
Marius Kjeldahl authored
280 ;; Iced CoffeeScript keywords
281 (defvar iced-coffee-cs-keywords
282 '("await" "defer"))
283
0480381 @defunkt mini docs
authored
284 ;; Regular expression combining the above three lists.
d6b78ed @defunkt defvars and basic indentation
authored
285 (defvar coffee-keywords-regexp (regexp-opt
cf028f8 @defunkt basics
authored
286 (append
287 coffee-js-reserved
288 coffee-js-keywords
80ebae6 Added support for iced coffeescript await and defer keywords.
Marius Kjeldahl authored
289 coffee-cs-keywords
290 iced-coffee-cs-keywords) 'words))
cf028f8 @defunkt basics
authored
291
292
ff61700 @defunkt that was bothering me
authored
293 ;; Create the list for font-lock. Each class of keyword is given a
294 ;; particular face.
5256afd @defunkt tweaks
authored
295 (defvar coffee-font-lock-keywords
ff61700 @defunkt that was bothering me
authored
296 ;; *Note*: order below matters. `coffee-keywords-regexp' goes last
297 ;; because otherwise the keyword "state" in the function
298 ;; "state_entry" would be highlighted.
c5831bb @sstephenson Fix for string literals with escaped quotes
sstephenson authored
299 `((,coffee-string-regexp . font-lock-string-face)
300 (,coffee-this-regexp . font-lock-variable-name-face)
dc85f42 @tav Added support for the shorthand::syntax for .prototype.
tav authored
301 (,coffee-prototype-regexp . font-lock-variable-name-face)
ff61700 @defunkt that was bothering me
authored
302 (,coffee-assign-regexp . font-lock-type-face)
303 (,coffee-regexp-regexp . font-lock-constant-face)
304 (,coffee-boolean-regexp . font-lock-constant-face)
305 (,coffee-keywords-regexp . font-lock-keyword-face)))
cf028f8 @defunkt basics
authored
306
3a47b28 @defunkt docs
authored
307 ;;
308 ;; Helper Functions
309 ;;
310
9ff612a @defunkt single quotes
authored
311 (defun coffee-comment-dwim (arg)
312 "Comment or uncomment current line or region in a smart way.
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
313 For details, see `comment-dwim'."
9ff612a @defunkt single quotes
authored
314 (interactive "*P")
315 (require 'newcomment)
316 (let ((deactivate-mark nil) (comment-start "#") (comment-end ""))
317 (comment-dwim arg)))
84ab684 @defunkt comments
authored
318
227a360 @orlandohill Customize args for compiling files and display the first compiler error
orlandohill authored
319 (defun coffee-command-compile (file-name)
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
320 "Run `coffee-command' to compile FILE."
66c326b @rrthomas Remove custom debug code; should use the standard Emacs debugging too…
rrthomas authored
321 (let ((full-file-name (expand-file-name file-name)))
2d489a8 @semperos Add support for Cygwin-style absolute paths, which makes using the fi…
semperos authored
322 (mapconcat 'identity (append (list coffee-command) coffee-args-compile (list full-file-name)) " ")))
5b00ae2 @defunkt more todo
authored
323
0b06b86 @semperos Add support for coffee --watch, and in process also add function for …
semperos authored
324 (defun coffee-run-cmd (args)
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
325 "Run `coffee-command' with the given arguments, and display the
326 output in a compilation buffer."
0b06b86 @semperos Add support for coffee --watch, and in process also add function for …
semperos authored
327 (interactive "sArguments: ")
328 (let ((compilation-buffer-name-function (lambda (this-mode)
329 (generate-new-buffer-name coffee-compiled-buffer-name))))
330 (compile (concat coffee-command " " args))))
331
2504ebe @defunkt explain indentation plan
authored
332 ;;
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
333 ;; imenu support
334 ;;
335
336 ;; This is a pretty naive but workable way of doing it. First we look
337 ;; for any lines that starting with `coffee-assign-regexp' that include
338 ;; `coffee-lambda-regexp' then add those tokens to the list.
339 ;;
340 ;; Should cover cases like these:
341 ;;
342 ;; minus: (x, y) -> x - y
343 ;; String::length: -> 10
344 ;; block: ->
345 ;; print('potion')
346 ;;
347 ;; Next we look for any line that starts with `class' or
348 ;; `coffee-assign-regexp' followed by `{` and drop into a
349 ;; namespace. This means we search one indentation level deeper for
350 ;; more assignments and add them to the alist prefixed with the
351 ;; namespace name.
352 ;;
353 ;; Should cover cases like these:
354 ;;
355 ;; class Person
356 ;; print: ->
357 ;; print 'My name is ' + this.name + '.'
358 ;;
359 ;; class Policeman extends Person
360 ;; constructor: (rank) ->
361 ;; @rank: rank
362 ;; print: ->
363 ;; print 'My name is ' + this.name + " and I'm a " + this.rank + '.'
364 ;;
76ed341 @defunkt class support in imenu
authored
365 ;; TODO:
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
366 ;; app = {
367 ;; window: {width: 200, height: 200}
368 ;; para: -> 'Welcome.'
369 ;; button: -> 'OK'
370 ;; }
371
372 (defun coffee-imenu-create-index ()
373 "Create an imenu index of all methods in the buffer."
374 (interactive)
375
376 ;; This function is called within a `save-excursion' so we're safe.
6639c36 @mkhl Silence byte-compiler warnings.
mkhl authored
377 (goto-char (point-min))
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
378
76ed341 @defunkt class support in imenu
authored
379 (let ((index-alist '()) assign pos indent ns-name ns-indent)
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
380 ;; Go through every assignment that includes -> or => on the same
c6562af @defunkt Make `coffee-debug' a macro
authored
381 ;; line or starts with `class'.
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
382 (while (re-search-forward
76ed341 @defunkt class support in imenu
authored
383 (concat "^\\(\\s *\\)"
384 "\\("
385 coffee-assign-regexp
386 ".+?"
387 coffee-lambda-regexp
388 "\\|"
389 coffee-namespace-regexp
390 "\\)")
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
391 (point-max)
392 t)
393
76ed341 @defunkt class support in imenu
authored
394 ;; If this is the start of a new namespace, save the namespace's
395 ;; indentation level and name.
7f5e1b3 @defunkt Bugfix: Clear old namespace when a new one is found
authored
396 (when (match-string 8)
397 ;; Set the name.
398 (setq ns-name (match-string 8))
399
76ed341 @defunkt class support in imenu
authored
400 ;; If this is a class declaration, add :: to the namespace.
401 (setq ns-name (concat ns-name "::"))
402
403 ;; Save the indentation level.
66c326b @rrthomas Remove custom debug code; should use the standard Emacs debugging too…
rrthomas authored
404 (setq ns-indent (length (match-string 1))))
76ed341 @defunkt class support in imenu
authored
405
406 ;; If this is an assignment, save the token being
407 ;; assigned. `Please.print:` will be `Please.print`, `block:`
408 ;; will be `block`, etc.
409 (when (setq assign (match-string 3))
410 ;; The position of the match in the buffer.
411 (setq pos (match-beginning 3))
412
413 ;; The indent level of this match
414 (setq indent (length (match-string 1)))
415
416 ;; If we're within the context of a namespace, add that to the
417 ;; front of the assign, e.g.
418 ;; constructor: => Policeman::constructor
419 (when (and ns-name (> indent ns-indent))
420 (setq assign (concat ns-name assign)))
421
422 ;; Clear the namespace if we're no longer indented deeper
423 ;; than it.
424 (when (and ns-name (<= indent ns-indent))
425 (setq ns-name nil)
426 (setq ns-indent nil))
427
428 ;; Add this to the alist. Done.
429 (push (cons assign pos) index-alist)))
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
430
431 ;; Return the alist.
432 index-alist))
433
434 ;;
d6b78ed @defunkt defvars and basic indentation
authored
435 ;; Indentation
2504ebe @defunkt explain indentation plan
authored
436 ;;
437
dbb4ec5 @defunkt comment tweaks
authored
438 ;;; The theory is explained in the README.
97a1001 @defunkt basic indentation with TAB
authored
439
d6b78ed @defunkt defvars and basic indentation
authored
440 (defun coffee-indent-line ()
7b03a71 @defunkt indentation theory
authored
441 "Indent current line as CoffeeScript."
d6b78ed @defunkt defvars and basic indentation
authored
442 (interactive)
443
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
444 (if (= (point) (point-at-bol))
445 (insert-tab)
446 (save-excursion
3f4271f @rrthomas Simplify coffee-indent-line and coffee-previous-indent.
rrthomas authored
447 (let ((prev-indent (coffee-previous-indent))
448 (cur-indent (current-indentation)))
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
449 ;; Shift one column to the left
450 (beginning-of-line)
451 (insert-tab)
452
453 (when (= (point-at-bol) (point))
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
454 (forward-char coffee-tab-width))
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
455
456 ;; We're too far, remove all indentation.
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
457 (when (> (- (current-indentation) prev-indent) coffee-tab-width)
df7ae3b @defunkt Bugfix: Indenting blank lines with TAB
authored
458 (backward-to-indentation 0)
459 (delete-region (point-at-bol) (point)))))))
b7ee8b8 @defunkt start cleaning up indentation code
authored
460
461 (defun coffee-previous-indent ()
462 "Return the indentation level of the previous non-blank line."
463 (save-excursion
464 (forward-line -1)
d063614 @superbobry Fix hanging of `coffee-previous-indent` at start of buffer.
superbobry authored
465 (if (bobp)
466 0
467 (progn
3f4271f @rrthomas Simplify coffee-indent-line and coffee-previous-indent.
rrthomas authored
468 (while (and (looking-at "^[ \t]*$") (not (bobp))) (forward-line -1))
d063614 @superbobry Fix hanging of `coffee-previous-indent` at start of buffer.
superbobry authored
469 (current-indentation)))))
b7ee8b8 @defunkt start cleaning up indentation code
authored
470
17150cf @defunkt docs and starting to implement indenters
authored
471 (defun coffee-newline-and-indent ()
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
472 "Insert a newline and indent it to the same level as the previous line."
17150cf @defunkt docs and starting to implement indenters
authored
473 (interactive)
474
475 ;; Remember the current line indentation level,
476 ;; insert a newline, and indent the newline to the same
477 ;; level as the previous line.
478 (let ((prev-indent (current-indentation)) (indent-next nil))
74a9135 @piranha do not leave trailing whitespaces on newline
piranha authored
479 (delete-horizontal-space t)
17150cf @defunkt docs and starting to implement indenters
authored
480 (newline)
7976bc4 @defunkt Make config options available from `customize-group`, document in README
authored
481 (insert-tab (/ prev-indent coffee-tab-width))
3a47b28 @defunkt docs
authored
482
17150cf @defunkt docs and starting to implement indenters
authored
483 ;; We need to insert an additional tab because the last line was special.
484 (when (coffee-line-wants-indent)
485 (insert-tab)))
486
487 ;; Last line was a comment so this one should probably be,
488 ;; too. Makes it easy to write multi-line comments (like the one I'm
489 ;; writing right now).
490 (when (coffee-previous-line-is-comment)
491 (insert "# ")))
492
493 ;; Indenters help determine whether the current line should be
494 ;; indented further based on the content of the previous line. If a
495 ;; line starts with `class', for instance, you're probably going to
496 ;; want to indent the next line.
497
498 (defvar coffee-indenters-bol '("class" "for" "if" "try")
7d338d1 @defunkt coffee-indenters
authored
499 "Keywords or syntax whose presence at the start of a line means the
500 next line should probably be indented.")
501
502 (defun coffee-indenters-bol-regexp ()
503 "Builds a regexp out of `coffee-indenters-bol' words."
27df14a @defunkt Fix coffee-indenters-bol-regexp. Closes #36
authored
504 (regexp-opt coffee-indenters-bol 'words))
7d338d1 @defunkt coffee-indenters
authored
505
5e170be @defunkt get it working (with a fake function though)
authored
506 (defvar coffee-indenters-eol '(?> ?{ ?\[)
507 "Single characters at the end of a line that mean the next line
508 should probably be indented.")
7d338d1 @defunkt coffee-indenters
authored
509
17150cf @defunkt docs and starting to implement indenters
authored
510 (defun coffee-line-wants-indent ()
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
511 "Return t if the current line should be indented relative to the
512 previous line."
8bfb7ed @defunkt Keep same indentation of last line when creating a new line
authored
513 (interactive)
514
17150cf @defunkt docs and starting to implement indenters
authored
515 (save-excursion
516 (let ((indenter-at-bol) (indenter-at-eol))
517 ;; Go back a line and to the first character.
518 (forward-line -1)
519 (backward-to-indentation 0)
8bfb7ed @defunkt Keep same indentation of last line when creating a new line
authored
520
17150cf @defunkt docs and starting to implement indenters
authored
521 ;; If the next few characters match one of our magic indenter
522 ;; keywords, we want to indent the line we were on originally.
523 (when (looking-at (coffee-indenters-bol-regexp))
66c326b @rrthomas Remove custom debug code; should use the standard Emacs debugging too…
rrthomas authored
524 (setq indenter-at-bol t))
17150cf @defunkt docs and starting to implement indenters
authored
525
526 ;; 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
527 ;; see if the last character matches one of our indenter
528 ;; characters.
17150cf @defunkt docs and starting to implement indenters
authored
529 (when (not indenter-at-bol)
530 (end-of-line)
531
5e170be @defunkt get it working (with a fake function though)
authored
532 ;; Optimized for speed - checks only the last character.
65797fa @jaeyeom Fix missing function `some' issue when loading compiled elisp.
jaeyeom authored
533 (let ((indenters coffee-indenters-eol))
534 (while indenters
535 (if (/= (char-before) (car indenters))
536 (setq indenters (cdr indenters))
537 (setq indenter-at-eol t)
538 (setq indenters nil)))))
17150cf @defunkt docs and starting to implement indenters
authored
539
540 ;; If we found an indenter, return `t'.
541 (or indenter-at-bol indenter-at-eol))))
542
8bfb7ed @defunkt Keep same indentation of last line when creating a new line
authored
543 (defun coffee-previous-line-is-comment ()
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
544 "Return t if the previous line is a CoffeeScript comment."
8bfb7ed @defunkt Keep same indentation of last line when creating a new line
authored
545 (save-excursion
546 (forward-line -1)
547 (coffee-line-is-comment)))
548
549 (defun coffee-line-is-comment ()
579e270 @rrthomas Overhaul the docstrings.
rrthomas authored
550 "Return t if the current line is a CoffeeScript comment."
8bfb7ed @defunkt Keep same indentation of last line when creating a new line
authored
551 (save-excursion
552 (backward-to-indentation 0)
553 (= (char-after) (string-to-char "#"))))
554
3a47b28 @defunkt docs
authored
555 ;;
556 ;; Define Major Mode
557 ;;
558
af021a5 @josh magic autoload comments
josh authored
559 ;;;###autoload
cf028f8 @defunkt basics
authored
560 (define-derived-mode coffee-mode fundamental-mode
4631aa1 Use "Coffee" as mode name rather than the idiosynchratic "coffee-mode".
Daniel Brockman authored
561 "Coffee"
562 "Major mode for editing CoffeeScript."
cf028f8 @defunkt basics
authored
563
564 ;; code for syntax highlighting
565 (setq font-lock-defaults '((coffee-font-lock-keywords)))
566
84ab684 @defunkt comments
authored
567 ;; perl style comment: "# ..."
568 (modify-syntax-entry ?# "< b" coffee-mode-syntax-table)
569 (modify-syntax-entry ?\n "> b" coffee-mode-syntax-table)
40a5bf1 @defunkt comment-start is buffer local. fixes #10
authored
570 (make-local-variable 'comment-start)
0480381 @defunkt mini docs
authored
571 (setq comment-start "#")
84ab684 @defunkt comments
authored
572
9ff612a @defunkt single quotes
authored
573 ;; single quote strings
574 (modify-syntax-entry ?' "\"" coffee-mode-syntax-table)
575
d6b78ed @defunkt defvars and basic indentation
authored
576 ;; indentation
577 (make-local-variable 'indent-line-function)
578 (setq indent-line-function 'coffee-indent-line)
b4b2a53 @kelleyk Small patch to fix indentation behavior; before, the global tab-width…
kelleyk authored
579 (set (make-local-variable 'tab-width) coffee-tab-width)
d6b78ed @defunkt defvars and basic indentation
authored
580
30d2af5 @defunkt Basic imenu support - makes things like `textmate-goto-symbol' work!
authored
581 ;; imenu
582 (make-local-variable 'imenu-create-index-function)
583 (setq imenu-create-index-function 'coffee-imenu-create-index)
584
d6b78ed @defunkt defvars and basic indentation
authored
585 ;; no tabs
be2305f @rrthomas Remove support for removing trailing whitespace: whitepace-mode does …
rrthomas authored
586 (setq indent-tabs-mode nil))
eaf28ab @defunkt register as .coffee handler
authored
587
6eb6d99 @knu Introduce a minor mode `coffee-cos-mode'.
knu authored
588 ;;
589 ;; Compile-on-Save minor mode
590 ;;
591
592 (defvar coffee-cos-mode-line " CoS")
593 (make-variable-buffer-local 'coffee-cos-mode-line)
594
595 (define-minor-mode coffee-cos-mode
596 "Toggle compile-on-save for coffee-mode."
597 :group 'coffee-cos :lighter coffee-cos-mode-line
598 (cond
599 (coffee-cos-mode
600 (add-hook 'after-save-hook 'coffee-compile-file nil t))
601 (t
602 (remove-hook 'after-save-hook 'coffee-compile-file t))))
603
27ffada @defunkt provide something
authored
604 (provide 'coffee-mode)
605
eaf28ab @defunkt register as .coffee handler
authored
606 ;;
607 ;; On Load
608 ;;
609
610 ;; Run coffee-mode for files ending in .coffee.
af021a5 @josh magic autoload comments
josh authored
611 ;;;###autoload
eaf28ab @defunkt register as .coffee handler
authored
612 (add-to-list 'auto-mode-alist '("\\.coffee$" . coffee-mode))
af021a5 @josh magic autoload comments
josh authored
613 ;;;###autoload
605a330 @defunkt highlight Cakefile too
authored
614 (add-to-list 'auto-mode-alist '("Cakefile" . coffee-mode))
a9c8f48 Formatting changes for complians with elp/Marmalade
Scott Barron authored
615 ;;; coffee-mode.el ends here
Something went wrong with that request. Please try again.