Created
May 15, 2014 13:27
-
-
Save rkistner/598f25e523a83252f4ae to your computer and use it in GitHub Desktop.
tern.lint + jshint
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Set the linter on the codemirror instance | |
options.lint = { getAnnotations: asyncValidator, async: true } | |
function asyncValidator(cm, updateLinting, options) { | |
// jshint | |
var jshint = performJSHint(cm.getValue()); | |
// tern.lint, adapted from tern-lint.js | |
var query = { | |
type : "lint", | |
file : "#0", | |
lineCharPositions : true | |
}; | |
var files = []; | |
files.push({ | |
type : "full", | |
name : "[doc]", | |
text : cm.getValue() | |
}); | |
var doc = { | |
query : query, | |
files : files | |
}; | |
ternServer.server.request(doc, function(error, response) { | |
if (error) { | |
updateLinting(cm, jshint); | |
} else { | |
var messages = response.messages; | |
updateLinting(cm, jshint.concat(messages)); | |
} | |
}); | |
} | |
// This is my own JSHint integration for CodeMirror. | |
// There may be a standard/better one by now - I haven't checked recently. | |
function performJSHint(text) { | |
var options = {}; | |
var globals = {}; | |
JSHINT(text, options, globals); | |
var report = JSHINT.data(); | |
var errors = report.errors; | |
var result = []; | |
if (errors) { | |
parseErrors(errors, result); | |
} | |
return result; | |
} | |
// Convert JSHint errors into CodeMirror errors | |
function parseErrors(errors, output) { | |
for ( var i = 0; i < errors.length; i++) { | |
var error = errors[i]; | |
if (error) { | |
var linetabpositions, index; | |
linetabpositions = []; | |
// This next block is to fix a problem in jshint. Jshint | |
// replaces | |
// all tabs with spaces then performs some checks. The error | |
// positions (character/space) are then reported incorrectly, | |
// not taking the replacement step into account. Here we look | |
// at the evidence line and try to adjust the character position | |
// to the correct value. | |
if (error.evidence) { | |
// Tab positions are computed once per line and cached | |
var tabpositions = linetabpositions[error.line]; | |
if (!tabpositions) { | |
var evidence = error.evidence; | |
tabpositions = []; | |
// ugggh phantomjs does not like this | |
// forEachChar(evidence, function(item, index) { | |
Array.prototype.forEach.call(evidence, function(item, | |
index) { | |
if (item === '\t') { | |
// First col is 1 (not 0) to match error | |
// positions | |
tabpositions.push(index + 1); | |
} | |
}); | |
linetabpositions[error.line] = tabpositions; | |
} | |
if (tabpositions.length > 0) { | |
var pos = error.character; | |
tabpositions.forEach(function(tabposition) { | |
if (pos > tabposition) pos -= 1; | |
}); | |
error.character = pos; | |
} | |
} | |
var start = error.character - 1, end = start + 1; | |
if (error.evidence) { | |
index = error.evidence.substring(start).search(/.\b/); | |
if (index > -1) { | |
end += index; | |
} | |
} | |
// Convert to format expected by validation service | |
error.description = error.reason; | |
error.start = error.character; | |
error.end = end; | |
error = cleanup(error); | |
if (error) | |
output.push({message: error.description, | |
severity: error.severity, | |
from: CodeMirror.Pos(error.line - 1, start), | |
to: CodeMirror.Pos(error.line - 1, end)}); | |
} | |
} | |
} | |
var bogus = [ "Dangerous comment", "Use '===' to compare with '0'", "Mixed spaces and tabs." ]; | |
var warnings = [ [ "Expected '{'", | |
"Statement body should be inside '{ }' braces." ] ]; | |
var errors = [ "Missing semicolon", "Extra comma", "Missing property name", | |
"Unmatched ", " and instead saw", " is not defined", | |
"Unclosed string", "Stopping, unable to continue", | |
"Redefining global variable"]; | |
function cleanup(error) { | |
// All problems are warnings by default | |
fixWith(error, warnings, "warning", true); | |
fixWith(error, errors, "error"); | |
return isBogus(error) ? null : error; | |
} | |
function fixWith(error, fixes, severity, force) { | |
var description, fix, find, replace, found; | |
description = error.description; | |
for ( var i = 0; i < fixes.length; i++) { | |
fix = fixes[i]; | |
find = (typeof fix === "string" ? fix : fix[0]); | |
replace = (typeof fix === "string" ? null : fix[1]); | |
found = description.indexOf(find) !== -1; | |
if (force || found) { | |
error.severity = severity; | |
} | |
if (found && replace) { | |
error.description = replace; | |
} | |
} | |
} | |
function isBogus(error) { | |
var description = error.description; | |
for ( var i = 0; i < bogus.length; i++) { | |
if (description.indexOf(bogus[i]) !== -1) { | |
return true; | |
} | |
} | |
return false; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment