One of my favorite things about emacs is its javascript modes. While there are a million options, I'm only going to talk about how I edit javascript with emacs.
Which Mode do I Use?
I use js2-mode. It provides syntax highlighting and underlines errors as you type. This mode doesn't come with emacs, it was built by Steve Yegge. However it is installable through the ELPA.
One problem I found with this mode is that it has some weird ideas
about indentation. I personally prefer the indentation from the
built-in js-mode
in emacs. After some googling I came across a
blogpost
on fixing indentation in js2-mode
. Starting with that I then made my
own modifications and have a js2-mode
that works exactly the way I
want it (code below).
(add-to-list 'auto-mode-alist '("\\.js$" . js2-mode)) | |
(add-to-list 'auto-mode-alist '("\\.json$" . js2-mode)) | |
;; Use js-mode indentation in js2-mode, I don't like js2-mode's indentation | |
;; | |
;; thanks http://mihai.bazon.net/projects/editing-javascript-with-emacs-js2-mode | |
;; with my own modifications | |
;; | |
(defun my-js2-indent-function () | |
(interactive) | |
(save-restriction | |
(widen) | |
(let* ((inhibit-point-motion-hooks t) | |
(parse-status (save-excursion (syntax-ppss (point-at-bol)))) | |
(offset (- (current-column) (current-indentation))) | |
(indentation (js--proper-indentation parse-status)) | |
node) | |
(save-excursion | |
(back-to-indentation) | |
;; consecutive declarations in a var statement are nice if | |
;; properly aligned, i.e: | |
;; | |
;; var foo = "bar", | |
;; bar = "foo"; | |
(setq node (js2-node-at-point)) | |
(when (and node | |
(= js2-NAME (js2-node-type node)) | |
(= js2-VAR (js2-node-type (js2-node-parent node)))) | |
(setq indentation ( 4 indentation)))) | |
(indent-line-to indentation) | |
(when (> offset 0) (forward-char offset))))) | |
(defun my-indent-sexp () | |
(interactive) | |
(save-restriction | |
(save-excursion | |
(widen) | |
(let* ((inhibit-point-motion-hooks t) | |
(parse-status (syntax-ppss (point))) | |
(beg (nth 1 parse-status)) | |
(end-marker (make-marker)) | |
(end (progn (goto-char beg) (forward-list) (point))) | |
(ovl (make-overlay beg end))) | |
(set-marker end-marker end) | |
(overlay-put ovl 'face 'highlight) | |
(goto-char beg) | |
(while (< (point) (marker-position end-marker)) | |
;; don't reindent blank lines so we don't set the "buffer | |
;; modified" property for nothing | |
(beginning-of-line) | |
(unless (looking-at "\\s-*$") | |
(indent-according-to-mode)) | |
(forward-line)) | |
(run-with-timer 0.5 nil '(lambda(ovl) | |
(delete-overlay ovl)) ovl))))) | |
(defun my-js2-mode-hook () | |
(require 'js) | |
(setq js-indent-level 2 | |
indent-tabs-mode nil | |
c-basic-offset 2) | |
(c-toggle-auto-state 0) | |
(c-toggle-hungry-state 1) | |
(set (make-local-variable 'indent-line-function) 'my-js2-indent-function) | |
(define-key js2-mode-map [(meta control |)] 'cperl-lineup) | |
(define-key js2-mode-map [(meta control \;)] | |
'(lambda() | |
(interactive) | |
(insert "/* -----[ ") | |
(save-excursion | |
(insert " ]----- */")) | |
)) | |
(define-key js2-mode-map [(return)] 'newline-and-indent) | |
(define-key js2-mode-map [(backspace)] 'c-electric-backspace) | |
(define-key js2-mode-map [(control d)] 'c-electric-delete-forward) | |
(define-key js2-mode-map [(control meta q)] 'my-indent-sexp) | |
(if (featurep 'js2-highlight-vars) | |
(js2-highlight-vars-mode)) | |
(message "My JS2 hook")) | |
(add-hook 'js2-mode-hook 'my-js2-mode-hook) | |
Editing Javascript in HTML
In my experience I'm not always editing pure .js
files. Sometimes
I'm editing javascript inside a .haml
, .erb
, or .html
file. The
haml-mode
I am using is supposed to have easy js-mode
integration.
However I tried to integrate js2-mode
, and it was not simple.
Furthermore, the built-in javascript highlighting seems to break on a
regular basis. Troubleshooting that problem looked like it was going
to take more time than I had. Instead I wrote some elisp to allow me
to quickly create a javascript scratch buffer.
Javascript Scratch Buffer
I use this to write javascript and copy / paste between buffers until
I'm happy with the code. It's a very simple solution to the problem.
It creates a buffer named scratch-js with js2-mode
. It's not unlike
the elisp scratch buffer that comes up on emacs start up. I don't
write javascript all the time, so this solution works pretty well for me.
(defun scratch-js () | |
"Create or switch to a javascript mode scratch buffer" | |
(interactive) | |
(if (not (eq nil (get-buffer "scratch-js"))) | |
(switch-to-buffer "scratch-js") | |
(set-buffer (get-buffer-create "scratch-js")) | |
(js2-mode) | |
(switch-to-buffer "scratch-js"))) |
This is admittedly a stopgap solution. I would prefer to have
js2-mode
integrated tightly with haml-mode
. If anyone else has
actually done this, I would love to see the code.