Here’s a topic that has been done over and over, but I wanted to share this method of using the popular JSLint javascript validation tool within my favourite text editor, Vim. I had tried a few methods previously that didn’t satisfy me because the interface from Vim to JSLint required the use of Vim’s make command and it was just sloppy.
My key requirements were:
- Some form of persistent visual line differentiation on lines with error(s)
- Error message text displayed within Vim command line when line under cursor contains error(s)
- Error lines are navigable using Vim custom hotkey commands
The solution that finally worked for me was to use an adapted implementation of a method implemented by Honza Porkorny. The method requires use of node.js (the node executable must be on your $PATH) and Vim plugin syntastic.
Directory structure of files:
$HOME
.vim
syntax_checkers
javascript.vim
bin
runjslint.js
jslint.js
My version of the method alters
~/.vim/syntax_checkers/javascript.vim
(note: location of file may be different on your system) as such:
Replace:
With:
if !executable("runjslint.js")
runjslint.js is an executable (via node.js) javascript file that is somewhere on your $PATH.
Then replace:
function! SyntaxCheckers_javascript_GetLocList()
let makeprg = "jsl -nologo -nofilelisting -nosummary -nocontext -process ".shellescape(expand('%'))
let errorformat='%W%f(%l): lint warning: %m,%-Z%p^,%W%f(%l): warning: %m,%-Z%p^,%E%f(%l): SyntaxError: %m,%-Z%p^,%-G'
return SyntasticMake({ 'makeprg': makeprg, 'errorformat': errorformat })
endfunction
With:
function! SyntaxCheckers_javascript_GetLocList()
let makeprg = "runjslint.js ".shellescape(expand('%'))
let errorformat='%W%f(%l:%c): lint warning: %m,%-Z%p^,%W%f(%l:%c): warning: %m,%-Z%p^,%E%f(%l:%c): SyntaxError: %m,%-Z%p^,%-G'
return SyntasticMake({ 'makeprg': makeprg, 'errorformat': errorformat })
endfunction
The contents of the runjslint.js file I mentioned earlier are a bit involved in order to string this whole method together.
(function () {
// Node-specific stuff
// Notice that JSLINT is defined here as well
var fs, sys, filename, input, errors, warning, JSLINT;
fs = require('fs');
sys = require('sys');
// JSLINT object included here
JSLINT = require('./jslint').JSLINT;
// capture the command line argument
filename = process.argv[2];
// bail if no argument was specified
if (!filename) {
sys.puts('Usage: node runjslint.js file.js');
process.exit(1);
}
// read the file specified
input = fs.readFileSync(filename);
input = input.toString("utf-8");
// run jslint on the file
JSLINT(input, {
evil: true,
onevar: true,
undef: true,
devel: true,
browser: true,
indent: 4
});
errors = JSLINT.errors;
// if we have errors, print them out
if (errors.length > 0) {
for (var i = 0; i < errors.length; i++) {
warning = errors[i];
if (warning) {
// format: filename.js(3): lint warning: reason
// e.g.: main.js(12): lint warning: Weird assignment
var r = filename + "(" + warning.line + ":";
r += warning.character+"):";
r += "SyntaxError: " + warning.reason;
sys.puts(r);
}
}
}
}());
Now if you followed Hanzo Porkorny’s method, you’ll notice that he added similar code above to the bottom of the jslint file, which is fine, but I didn’t want to have to do too much work to refit JSLint with the runjslint.js contents every time a new version of JSLint was introduced.
Here is the only addition I made to the jslint file (at the very bottom):
// for running with node.js
exports.JSLINT = JSLINT;
This allows for the inclusion of the JSLINT object into the executable runjslint.js file. Now, when a new version of JSLint is released, I only need to replace one line of code at the very bottom of the file. Easy.
So far this method satisfies the first base requirement of mine, persistent visual line differentiation on error lines, but not the second or third. Well, syntastic does allow navigation using location list hopping, but this was not intuitive for me since the location list would always pop up and I’d have to close it again. So I rolled my own function for navigation, building off of syntastics error storage within Vim’s location list.
Unfortunately, the Google pretty print plugin does not understand Vim script syntax so the code listing is sans colour. The Vim script code is as follows (I just stuck it in my .vimrc file for now):
" navigate syntastic errors without opening the location list
nnoremap <leader>n :call GotoError(1)
nnoremap <leader>p :call GotoError(-1)
fun! GotoError(dir)
" if current file is javascript
if matchstr(expand('%'),'\.js$') == '.js'
" if syntastic location list is not active, bug out!
if !exists('b:syntastic_loclist')
return
endif
" if location list is empty
if len(b:syntastic_loclist) upperBound
let b:cur_error = upperBound
endif
" move cursor to line AND column of error
call cursor(loclist[b:cur_error]['lnum'], loclist[b:cur_error]['col'])
" display error text in Vim command line
redraw
echo loclist[b:cur_error]['text']
else
redraw
echo 'Current file is not a javascript file. JSLint validation disabled.'
endif
endfun
Now you can use whatever custom key bindings you want to bind to the function GotoError(). Just pass 1 as the argument to cycle forward through the errors and -1 to cycle backwards.
That about does it. Let me know if this post was useful to you. Would love to hear feedback… well, constructive feedback anyway.