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 jslint module of node.js: npm install jslint

After this you should have a jslint command which you can test in a terminal (eg, jslint --terse test.js) to see something that looks like:

test.js
18n.js(2):Expected 'en' at column 9, not column 3.
i18n.js(3):Expected 'record' at column 13, not column 5.
...

By default (without --terse) the output is more verbose, but having one line per error is easier in emacs.

3. 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 "--terse" local-file))))

  (setq flymake-err-line-patterns
	(cons '("^\\(.*\\)(\\([[:digit:]]+\\)):\\(.*\\)$"
		1 2 nil 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.

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.

3. In OS X, applications get their environment variables (like PATH, here) from the environment.plist in ~/.MacOSX. See this emacswiki page for instructions.

[updates 9 Oct 2012]:

  • Step 2 is no longer necessary, as npm is now included in homebrew’s node package
  • At step 3, removed -g
  • With the --terse option, it is no longer necessary to modify files inside the jslint module.
  • The lisp code has been updated to include --terse and match the new jslint error syntax

Thanks to the people who commented, leading to the update.

This entry was posted in General. Bookmark the permalink.

8 Responses to On-the-fly jslint validation in emacs

  1. Brad Wright says:

    I made some changes to the node-jslint code to support additional types of output. I’ve submitted this as a pull request:

    https://github.com/reid/node-jslint/pull/27

  2. site admin says:

    Great. Let’s hope your changes are pulled soon.

  3. snowfix says:

    Did you see that node-jslint is now living in another GitHub location? It might be worth to re-submit the pull-request here:

    https://github.com/rickeyski/node-jslint

  4. site admin says:

    Thanks snowfix. I’ll resubmit it there.

  5. AxxL says:

    The actual jslint.js has a –terse parameter to support the single line output.

    jslint –terse myfile.js

  6. Axel says:

    With the “terse” parameter, jslint is changing the output to a single line.

    jslint --terse C:/temp/tester01.js

    The output looks like this:

    C:/temp/tester.js(2):Expected ':' and instead saw '='.

    Unfortunetaly your regexp doesn’t work anymore. It should be now something like:

    file-name(linenumber):Message.

    Do you want to adjust it?

  7. Axel says:

    Hmm… Is the regexp something like this?

    "^\\(.*\\)\\(([[:digit:]]+)\\):\\(.*\\)$"

    for

    C:/temp/tester.js(2):Expected ':' and instead saw '='.

  8. site admin says:

    Thanks for the comments. I have updated the post with, first, the fact that npm is now in brew’s node package and, second, the new --terse which alleviates the need to change jslint code, but requires slight modifications to the lisp code (including the regexp suggested by Axel, which works for me).

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>