Emacs JavaScript mode
Download javascript.elHere's my update to an old javascript-mode (authors Steven Champeon and Ville Skyttä). In essence, I was unhappy with indentation, but I also removed some stuff that I don't need and fixed various small things, such as highlighting literal regexps.
The indentation is based on CC Mode, which is almost Good Enough for JavaScript; the original javascript-mode.el (note that I'm talking about the version I had back in 2004; they might have fixed it in between, but I don't think so) had some indentation problems, for examples in cases like this:
obj.method({ foo: "bar", test: "asdf", qwer: "qwer", f: function() { return true; }});
It looks quite ugly, and due to CC Mode automatic indentation it was even hard to fix manually. ;-) One day I started looking into how to make my JS mode not suck, and at the end of the next day I came up with a modified version that Does What I Mean. To install, download the file in a directory in your LISP path and add the following to your .emacs:
(autoload 'javascript-mode "javascript") (add-to-list 'auto-mode-alist '("\\.js$" . javascript-mode))
Indentation style
Here are some examples of how this mode indents JS with my preferred settings (c-basic-offset is 8 characters, case-label is "*" (4 characters)). The sample above would indent like this:
obj.method({ foo: "bar", test: "asdf", qwer: "qwer", f: function() { return true; }});
However, if I put the "foo:" on a new line, it would indent like this:
obj.method({ foo: "bar", test: "asdf", qwer: "qwer", f: function() { return true; } });
Other samples:
var a = { lorem : "ipsum", foo : "bar" }; var a = { "lorem" : "ipsum", check : "foo bar", bar : 1234, method : function() { return { foo : "bar", test : 123 } } }; switch (what) { case "foo": do_foo(); break; case "bar": do_bar(); break; }
This is my javascript-mode-hook:
(defun my-js-mode-hook () (require 'cperl-mode) (setq tab-width 8 indent-tabs-mode nil c-basic-offset 8) (c-toggle-auto-state 0) (c-toggle-hungry-state 1) (define-key javascript-mode-map [(meta control |)] 'cperl-lineup))
The last keybinding allows me to quickly lineup colons in hashes by placing the caret before the first colon, then selecting the whole hash and pressing M-C-|.
How does it work
This mode uses the comprehensive indentation engine from CC Mode, which means that you should be able to customize it heavily as described in the CC Mode Manual.
Since the C syntax is quite different from JavaScript, I needed to make some fixes (C doesn't support literal hashes, nor does it know about inner anonymous functions, etc.). In order to add these fixes I advised the c-guess-basic-syntax function from cc-engine.el. "Function advices" are a cool ELisp concept that allows you to have some code executed along with a function that you can't modify. The original c-guess-basic-syntax will run first, then our "advice" kicks in and determines if we're in a JS situation that's likely to be misunderstood by CC-Mode. If that is the case, it will run some analysis and return proper information so that the CC indentation engine will do the Right Thing.
I found that for literal hashes it's best to work with the “arglist-*” classes, that is, it will return:
- “arglist-intro” if the caret is on a line that starts a hash and the bracket is on the previous line
- “arglist-cont” if the caret is on a line that continues a hash and the bracket is alone on the first line
- “arglist-cont-nonempty” if the caret is on a line that continues a hash and the first element starts on the same line as the bracket
- “arglist-close” if the caret is on a line that ends a hash (contains the ending bracket).
It will also properly detect functions:
- “defun-open” if the caret is on a line that contains the bracket starting a function, but not the function declaration
- “defun-block-intro” if the caret is on the first line in a function block (doesn't contain the bracket)
- “defun-close” if the caret is on a line containing the bracket that ends a function
I also declared the following in javascript.el:
(c-set-offset 'arglist-close '(c-lineup-close-paren)) (c-set-offset 'arglist-cont 0) (c-set-offset 'arglist-cont-nonempty '(js-lineup-arglist)) (c-set-offset 'arglist-intro '+) (setq c-special-indent-hook nil)
(but you can override them in javascript-mode-hook if you wish). Coupled with the declarations above and the advice to c-guess-basic-syntax, I ended up with a mode that Does What I Mean. I'm happy, end of story. :-)
Other JavaScript modes on the Planet
For a long time I was working with a modified version of Karl Landström's JavaScript mode. Indentation wasn't as good as I wanted, but it was still the best available for my lazy ass.
I recently discovered js2-mode by Steve Yegge. That's an amazing piece of work because it contains a full JavaScript parser and can inform you about a lot of potential problems in your code, such as missing semicolons (I simply love this one) or missing the "var" to declare locals, using undeclared variables, etc. etc. It will set a new standard for editing JavaScript (it seems that it will be included in the official GNU Emacs distribution!). My main problem with it is that the indentation engine is based on Karl's mode which is not good enough for me... I'd love it if I could integrate it with this simple advice to CC Mode.
Comments — add your comment
.DlHighlight {
background-color: #f8f8f8;
border-left: 2px solid #0c0;
font: 10pt monospace;
padding: 10px;
margin-left: 3em;
color:black;/* this is must be...*/
}
On Kubuntu using XEmacs 21.4, so line-number-at-pos was missing. Hacked a fix via Google searches for compatibility snippets (lifted from Python doctest) -- to use place this at the top of javascript.el and search/replace line-number-at-pos with javascript-line-number (except within the snippet of course)... didn't need to search/replace looking-back yet, so didn't, but left snippets here:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Emacs Compatibility Functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Define compatible versions of functions that are defined
;; for some versions of emacs but not others.
;; Backwards compatibility: looking-back
(cond ((fboundp 'looking-back) ;; Emacs 22.x
(defalias 'javascript-looking-back 'looking-back))
(t
(defun javascript-looking-back (regexp)
"Return true if text before point matches REGEXP."
(save-excursion
(let ((orig-pos (point)))
;; Search backwards for the regexp.
(if (re-search-backward regexp nil t)
;; Check if it ends at the original point.
(= orig-pos (match-end 0))))))))
;; Backwards compatibility: replace-regexp-in-string
(cond ((fboundp 'replace-regexp-in-string)
(defalias 'javascript-replace-regexp-in-string 'replace-regexp-in-string))
(t ;; XEmacs 21.x or Emacs 20.x
(defun javascript-replace-regexp-in-string
(regexp rep string &optional fixedcase literal)
"Replace all matches for REGEXP with REP in STRING."
(let ((start 0))
(while (and (< start (length string))
(string-match regexp string start))
(setq start (+ (match-end 0) 1))
(let ((newtext (if (functionp rep)
(save-match-data
(funcall rep (match-string 0 string)))
rep)))
(setq string (replace-match newtext fixedcase
literal string)))))
string)))
;; Backwards compatibility: line-number
(cond ((fboundp 'line-number) ;; XEmacs
(defalias 'javascript-line-number 'line-number))
((fboundp 'line-number-at-pos) ;; Emacs 22.x
(defalias 'javascript-line-number 'line-number-at-pos))
(t ;; Emacs 21.x
(defun javascript-line-number (&optional pos)
"Return the line number of POS (default=point)."
(1+ (count-lines 1
(save-excursion (progn (beginning-of-line)
(or pos (point)))))))))
;; Backwards compatibility: process-live-p
(cond ((fboundp 'process-live-p) ;; XEmacs
(defalias 'javascript-process-live-p 'process-live-p))
(t ;; Emacs
(defun javascript-process-live-p (process)
(and (processp process)
(equal (process-status process) 'run)))))