On-the-fly jslint validation in emacs
In the hope that someone will come across this trying not to waste time figuring things out, here’s how I managed to have emacs run jslint in my JavaScript buffers in real time. It’s not perfact, but it works where no other method I found online did.
We’ll use the jslint package from node.js1:
1. Install node.js. I use the homebrew package manager on OSX, so I just had to type brew install node.
2. Install the node package manager: curl http://npmjs.org/install.sh | sh on OSX (see details)
2. Install the jslint module of node.js: npm -g install jslint
After this you should have a jslint command which you can test in a terminal (eg, jslint test.js) to see something that looks like:
test.js
#1 Missing space between 'input' and '='.
var input= "", line = ""; // Line 1, Pos 10
#2 'li' was used before it was defined.
while (li ne) { // Line 3, Pos 8
The fact that each error is reported on two lines is is going to be a problem, as emacs’ flymake mode doesn’t handle multi-line errors2. To solve that we can modify jslint:
4. find jslint’s reporter.js file (see it on github). Here it’s in /usr/local/lib/node_modules/jslint/lib. Comment out the following lines, and add the log("Error:... statement:
if (!lint.ok) {
log(fileMessage);
len = lint.errors.length;
for (i = 0; i < len; i += 1) {
// pad = "#" + String(i + 1);
// while (pad.length < 3) {
// pad = ' ' + pad;
// }
e = lint.errors[i];
if (e) {
// line = ' // Line ' + e.line + ', Pos ' + e.character;
// log(pad + ' ' + ((colorize) ? color.yellow(e.reason) : e.reason));
// log(' ' + (e.evidence || '').replace(/^\s+|\s+$/, "") +
// ((colorize) ? color.grey(line) : line));
log("Error:" + e.line + ":" + e.character + ": " + e.reason);
}
}
Now, jslint will output something nicer, eg:
Error:1:10: Missing space between 'input' and '='. Error:3:8: 'li' was used before it was defined.
5. Add the following to your .emacs file:
(when (load "flymake" t)
(defun flymake-jslint-init ()
(let* ((temp-file (flymake-init-create-temp-buffer-copy
'flymake-create-temp-inplace))
(local-file (file-relative-name
temp-file
(file-name-directory buffer-file-name))))
(list "jslint" (list local-file))))
(setq flymake-err-line-patterns
(cons '("Error:\\([[:digit:]]+\\):\\([[:digit:]]+\\):\\(.*\\)$"
nil 1 2 3)
flymake-err-line-patterns))
(add-to-list 'flymake-allowed-file-name-masks
'("\\.js\\'" flymake-jslint-init))
(require 'flymake-cursor)
)
(add-hook 'js2-mode-hook
(lambda ()
(flymake-mode 1)
(define-key js2-mode-map "\C-c\C-n" 'flymake-goto-next-error)))
flymake-cursor replaces the default behaviour of popping up a menu with the error message by just putting it in the minibuffer.
Emacs should of course be set up so that it knows where jslint is, in order to invoke it on the javascript buffers3.
The main drawback is that when the jsnode package is updated, the changes you made to reporter.js will be removed. The best way to solve that would be to convince the author to change his format, or at least to add an option to output errors on one line. I’ll be asking him that.
Notes:
1. I also tried jslint4java but it turned out to be too slow for real-time invocation with flymake. Other Java-based methods (using Rhino) are, unsurprisingly, reported to be equally slow, and I failed to get spidermonkey installed.
2. There are three options at that point: either pipe jslint’s output into sed in order to put each error on one line, fix flymake so that it handle multiline errors, or change jslint’s error output format. It’s almost philosophical which is the right one to choose: is jslint wrong to not follow the decades-old rule of one-line-per-error, or is flyspell wrong to assume all parsers follow it, or is it down to the user to make them compatible with a script?
3. In OS X, applications get their environment variables (like PATH, here) from the environment.plist in ~/.MacOSX. See this emacswiki page for instructions.








