Update lint
This commit is contained in:
parent
6508e2af98
commit
d3e27a76c8
@ -1335,6 +1335,274 @@
|
||||
});
|
||||
});
|
||||
|
||||
// ========= close-bracket.js ========= //
|
||||
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
var defaults = {
|
||||
pairs: "()[]{}''\"\"",
|
||||
closeBefore: ")]}'\":;>",
|
||||
triples: "",
|
||||
explode: "[]{}"
|
||||
};
|
||||
|
||||
var Pos = CodeMirror.Pos;
|
||||
|
||||
CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
|
||||
if (old && old != CodeMirror.Init) {
|
||||
cm.removeKeyMap(keyMap);
|
||||
cm.state.closeBrackets = null;
|
||||
}
|
||||
if (val) {
|
||||
ensureBound(getOption(val, "pairs"))
|
||||
cm.state.closeBrackets = val;
|
||||
cm.addKeyMap(keyMap);
|
||||
}
|
||||
});
|
||||
|
||||
function getOption(conf, name) {
|
||||
if (name == "pairs" && typeof conf == "string") return conf;
|
||||
if (typeof conf == "object" && conf[name] != null) return conf[name];
|
||||
return defaults[name];
|
||||
}
|
||||
|
||||
var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
|
||||
function ensureBound(chars) {
|
||||
for (var i = 0; i < chars.length; i++) {
|
||||
var ch = chars.charAt(i), key = "'" + ch + "'"
|
||||
if (!keyMap[key]) keyMap[key] = handler(ch)
|
||||
}
|
||||
}
|
||||
ensureBound(defaults.pairs + "`")
|
||||
|
||||
function handler(ch) {
|
||||
return function(cm) { return handleChar(cm, ch); };
|
||||
}
|
||||
|
||||
function getConfig(cm) {
|
||||
var deflt = cm.state.closeBrackets;
|
||||
if (!deflt || deflt.override) return deflt;
|
||||
var mode = cm.getModeAt(cm.getCursor());
|
||||
return mode.closeBrackets || deflt;
|
||||
}
|
||||
|
||||
function handleBackspace(cm) {
|
||||
var conf = getConfig(cm);
|
||||
if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||
|
||||
var pairs = getOption(conf, "pairs");
|
||||
var ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
if (!ranges[i].empty()) return CodeMirror.Pass;
|
||||
var around = charsAround(cm, ranges[i].head);
|
||||
if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
|
||||
}
|
||||
for (var i = ranges.length - 1; i >= 0; i--) {
|
||||
var cur = ranges[i].head;
|
||||
cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), "+delete");
|
||||
}
|
||||
}
|
||||
|
||||
function handleEnter(cm) {
|
||||
var conf = getConfig(cm);
|
||||
var explode = conf && getOption(conf, "explode");
|
||||
if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||
|
||||
var ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
if (!ranges[i].empty()) return CodeMirror.Pass;
|
||||
var around = charsAround(cm, ranges[i].head);
|
||||
if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
|
||||
}
|
||||
cm.operation(function() {
|
||||
var linesep = cm.lineSeparator() || "\n";
|
||||
cm.replaceSelection(linesep + linesep, null);
|
||||
cm.execCommand("goCharLeft");
|
||||
ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var line = ranges[i].head.line;
|
||||
cm.indentLine(line, null, true);
|
||||
cm.indentLine(line + 1, null, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function contractSelection(sel) {
|
||||
var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0;
|
||||
return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)),
|
||||
head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1))};
|
||||
}
|
||||
|
||||
function handleChar(cm, ch) {
|
||||
var conf = getConfig(cm);
|
||||
if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||
|
||||
var pairs = getOption(conf, "pairs");
|
||||
var pos = pairs.indexOf(ch);
|
||||
if (pos == -1) return CodeMirror.Pass;
|
||||
|
||||
var closeBefore = getOption(conf,"closeBefore");
|
||||
|
||||
var triples = getOption(conf, "triples");
|
||||
|
||||
var identical = pairs.charAt(pos + 1) == ch;
|
||||
var ranges = cm.listSelections();
|
||||
var opening = pos % 2 == 0;
|
||||
|
||||
var type;
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i], cur = range.head, curType;
|
||||
var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
|
||||
if (opening && !range.empty()) {
|
||||
curType = "surround";
|
||||
} else if ((identical || !opening) && next == ch) {
|
||||
if (identical && stringStartsAfter(cm, cur))
|
||||
curType = "both";
|
||||
else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
|
||||
curType = "skipThree";
|
||||
else
|
||||
curType = "skip";
|
||||
} else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
|
||||
cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
|
||||
if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
|
||||
curType = "addFour";
|
||||
} else if (identical) {
|
||||
var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
|
||||
if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
|
||||
else return CodeMirror.Pass;
|
||||
} else if (opening && (next.length === 0 || /\s/.test(next) || closeBefore.indexOf(next) > -1)) {
|
||||
curType = "both";
|
||||
} else {
|
||||
return CodeMirror.Pass;
|
||||
}
|
||||
if (!type) type = curType;
|
||||
else if (type != curType) return CodeMirror.Pass;
|
||||
}
|
||||
|
||||
var left = pos % 2 ? pairs.charAt(pos - 1) : ch;
|
||||
var right = pos % 2 ? ch : pairs.charAt(pos + 1);
|
||||
cm.operation(function() {
|
||||
if (type == "skip") {
|
||||
cm.execCommand("goCharRight");
|
||||
} else if (type == "skipThree") {
|
||||
for (var i = 0; i < 3; i++)
|
||||
cm.execCommand("goCharRight");
|
||||
} else if (type == "surround") {
|
||||
var sels = cm.getSelections();
|
||||
for (var i = 0; i < sels.length; i++)
|
||||
sels[i] = left + sels[i] + right;
|
||||
cm.replaceSelections(sels, "around");
|
||||
sels = cm.listSelections().slice();
|
||||
for (var i = 0; i < sels.length; i++)
|
||||
sels[i] = contractSelection(sels[i]);
|
||||
cm.setSelections(sels);
|
||||
} else if (type == "both") {
|
||||
cm.replaceSelection(left + right, null);
|
||||
cm.triggerElectric(left + right);
|
||||
cm.execCommand("goCharLeft");
|
||||
} else if (type == "addFour") {
|
||||
cm.replaceSelection(left + left + left + left, "before");
|
||||
cm.execCommand("goCharRight");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function charsAround(cm, pos) {
|
||||
var str = cm.getRange(Pos(pos.line, pos.ch - 1),
|
||||
Pos(pos.line, pos.ch + 1));
|
||||
return str.length == 2 ? str : null;
|
||||
}
|
||||
|
||||
function stringStartsAfter(cm, pos) {
|
||||
var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
|
||||
return /\bstring/.test(token.type) && token.start == pos.ch &&
|
||||
(pos.ch == 0 || !/\bstring/.test(cm.getTokenTypeAt(pos)))
|
||||
}
|
||||
});
|
||||
|
||||
// ========= active-line.js ========= //
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
var WRAP_CLASS = "CodeMirror-activeline";
|
||||
var BACK_CLASS = "CodeMirror-activeline-background";
|
||||
var GUTT_CLASS = "CodeMirror-activeline-gutter";
|
||||
|
||||
CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
|
||||
var prev = old == CodeMirror.Init ? false : old;
|
||||
if (val == prev) return
|
||||
if (prev) {
|
||||
cm.off("beforeSelectionChange", selectionChange);
|
||||
clearActiveLines(cm);
|
||||
delete cm.state.activeLines;
|
||||
}
|
||||
if (val) {
|
||||
cm.state.activeLines = [];
|
||||
updateActiveLines(cm, cm.listSelections());
|
||||
cm.on("beforeSelectionChange", selectionChange);
|
||||
}
|
||||
});
|
||||
|
||||
function clearActiveLines(cm) {
|
||||
for (var i = 0; i < cm.state.activeLines.length; i++) {
|
||||
cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS);
|
||||
cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS);
|
||||
cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS);
|
||||
}
|
||||
}
|
||||
|
||||
function sameArray(a, b) {
|
||||
if (a.length != b.length) return false;
|
||||
for (var i = 0; i < a.length; i++)
|
||||
if (a[i] != b[i]) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function updateActiveLines(cm, ranges) {
|
||||
var active = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i];
|
||||
var option = cm.getOption("styleActiveLine");
|
||||
if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty())
|
||||
continue
|
||||
var line = cm.getLineHandleVisualStart(range.head.line);
|
||||
if (active[active.length - 1] != line) active.push(line);
|
||||
}
|
||||
if (sameArray(cm.state.activeLines, active)) return;
|
||||
cm.operation(function() {
|
||||
clearActiveLines(cm);
|
||||
for (var i = 0; i < active.length; i++) {
|
||||
cm.addLineClass(active[i], "wrap", WRAP_CLASS);
|
||||
cm.addLineClass(active[i], "background", BACK_CLASS);
|
||||
cm.addLineClass(active[i], "gutter", GUTT_CLASS);
|
||||
}
|
||||
cm.state.activeLines = active;
|
||||
});
|
||||
}
|
||||
|
||||
function selectionChange(cm, sel) {
|
||||
updateActiveLines(cm, sel.ranges);
|
||||
}
|
||||
});
|
||||
|
||||
// ========= tern.js ========= //
|
||||
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
@ -2056,3 +2324,327 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========= lint.js ========= //
|
||||
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
var GUTTER_ID = "CodeMirror-lint-markers";
|
||||
|
||||
function showTooltip(cm, e, content) {
|
||||
var tt = document.createElement("div");
|
||||
tt.className = "CodeMirror-lint-tooltip cm-s-" + cm.options.theme;
|
||||
tt.appendChild(content.cloneNode(true));
|
||||
if (cm.state.lint.options.selfContain)
|
||||
cm.getWrapperElement().appendChild(tt);
|
||||
else
|
||||
document.body.appendChild(tt);
|
||||
|
||||
function position(e) {
|
||||
if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position);
|
||||
tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px";
|
||||
tt.style.left = (e.clientX + 5) + "px";
|
||||
}
|
||||
CodeMirror.on(document, "mousemove", position);
|
||||
position(e);
|
||||
if (tt.style.opacity != null) tt.style.opacity = 1;
|
||||
return tt;
|
||||
}
|
||||
function rm(elt) {
|
||||
if (elt.parentNode) elt.parentNode.removeChild(elt);
|
||||
}
|
||||
function hideTooltip(tt) {
|
||||
if (!tt.parentNode) return;
|
||||
if (tt.style.opacity == null) rm(tt);
|
||||
tt.style.opacity = 0;
|
||||
setTimeout(function() { rm(tt); }, 600);
|
||||
}
|
||||
|
||||
function showTooltipFor(cm, e, content, node) {
|
||||
var tooltip = showTooltip(cm, e, content);
|
||||
function hide() {
|
||||
CodeMirror.off(node, "mouseout", hide);
|
||||
if (tooltip) { hideTooltip(tooltip); tooltip = null; }
|
||||
}
|
||||
var poll = setInterval(function() {
|
||||
if (tooltip) for (var n = node;; n = n.parentNode) {
|
||||
if (n && n.nodeType == 11) n = n.host;
|
||||
if (n == document.body) return;
|
||||
if (!n) { hide(); break; }
|
||||
}
|
||||
if (!tooltip) return clearInterval(poll);
|
||||
}, 400);
|
||||
CodeMirror.on(node, "mouseout", hide);
|
||||
}
|
||||
|
||||
function LintState(cm, options, hasGutter) {
|
||||
this.marked = [];
|
||||
this.options = options;
|
||||
this.timeout = null;
|
||||
this.hasGutter = hasGutter;
|
||||
this.onMouseOver = function(e) { onMouseOver(cm, e); };
|
||||
this.waitingFor = 0
|
||||
}
|
||||
|
||||
function parseOptions(_cm, options) {
|
||||
if (options instanceof Function) return {getAnnotations: options};
|
||||
if (!options || options === true) options = {};
|
||||
return options;
|
||||
}
|
||||
|
||||
function clearMarks(cm) {
|
||||
var state = cm.state.lint;
|
||||
if (state.hasGutter) cm.clearGutter(GUTTER_ID);
|
||||
for (var i = 0; i < state.marked.length; ++i)
|
||||
state.marked[i].clear();
|
||||
state.marked.length = 0;
|
||||
}
|
||||
|
||||
function makeMarker(cm, labels, severity, multiple, tooltips) {
|
||||
var marker = document.createElement("div"), inner = marker;
|
||||
marker.className = "CodeMirror-lint-marker-" + severity;
|
||||
if (multiple) {
|
||||
inner = marker.appendChild(document.createElement("div"));
|
||||
inner.className = "CodeMirror-lint-marker-multiple";
|
||||
}
|
||||
|
||||
if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) {
|
||||
showTooltipFor(cm, e, labels, inner);
|
||||
});
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
function getMaxSeverity(a, b) {
|
||||
if (a == "error") return a;
|
||||
else return b;
|
||||
}
|
||||
|
||||
function groupByLine(annotations) {
|
||||
var lines = [];
|
||||
for (var i = 0; i < annotations.length; ++i) {
|
||||
var ann = annotations[i], line = ann.from.line;
|
||||
(lines[line] || (lines[line] = [])).push(ann);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
function annotationTooltip(ann) {
|
||||
var severity = ann.severity;
|
||||
if (!severity) severity = "error";
|
||||
var tip = document.createElement("div");
|
||||
tip.className = "CodeMirror-lint-message-" + severity;
|
||||
if (typeof ann.messageHTML != 'undefined') {
|
||||
tip.innerHTML = ann.messageHTML;
|
||||
} else {
|
||||
tip.appendChild(document.createTextNode(ann.message));
|
||||
}
|
||||
return tip;
|
||||
}
|
||||
|
||||
function lintAsync(cm, getAnnotations, passOptions) {
|
||||
var state = cm.state.lint
|
||||
var id = ++state.waitingFor
|
||||
function abort() {
|
||||
id = -1
|
||||
cm.off("change", abort)
|
||||
}
|
||||
cm.on("change", abort)
|
||||
getAnnotations(cm.getValue(), function(annotations, arg2) {
|
||||
cm.off("change", abort)
|
||||
if (state.waitingFor != id) return
|
||||
if (arg2 && annotations instanceof CodeMirror) annotations = arg2
|
||||
cm.operation(function() {updateLinting(cm, annotations)})
|
||||
}, passOptions, cm);
|
||||
}
|
||||
|
||||
function startLinting(cm) {
|
||||
var state = cm.state.lint, options = state.options;
|
||||
/*
|
||||
* Passing rules in `options` property prevents JSHint (and other linters) from complaining
|
||||
* about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc.
|
||||
*/
|
||||
var passOptions = options.options || options;
|
||||
var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint");
|
||||
if (!getAnnotations) return;
|
||||
if (options.async || getAnnotations.async) {
|
||||
lintAsync(cm, getAnnotations, passOptions)
|
||||
} else {
|
||||
var annotations = getAnnotations(cm.getValue(), passOptions, cm);
|
||||
if (!annotations) return;
|
||||
if (annotations.then) annotations.then(function(issues) {
|
||||
cm.operation(function() {updateLinting(cm, issues)})
|
||||
});
|
||||
else cm.operation(function() {updateLinting(cm, annotations)})
|
||||
}
|
||||
}
|
||||
|
||||
function updateLinting(cm, annotationsNotSorted) {
|
||||
clearMarks(cm);
|
||||
var state = cm.state.lint, options = state.options;
|
||||
|
||||
var annotations = groupByLine(annotationsNotSorted);
|
||||
|
||||
for (var line = 0; line < annotations.length; ++line) {
|
||||
var anns = annotations[line];
|
||||
if (!anns) continue;
|
||||
|
||||
var maxSeverity = null;
|
||||
var tipLabel = state.hasGutter && document.createDocumentFragment();
|
||||
|
||||
for (var i = 0; i < anns.length; ++i) {
|
||||
var ann = anns[i];
|
||||
var severity = ann.severity;
|
||||
if (!severity) severity = "error";
|
||||
maxSeverity = getMaxSeverity(maxSeverity, severity);
|
||||
|
||||
if (options.formatAnnotation) ann = options.formatAnnotation(ann);
|
||||
if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann));
|
||||
|
||||
if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, {
|
||||
className: "CodeMirror-lint-mark-" + severity,
|
||||
__annotation: ann
|
||||
}));
|
||||
}
|
||||
|
||||
if (state.hasGutter)
|
||||
cm.setGutterMarker(line, GUTTER_ID, makeMarker(cm, tipLabel, maxSeverity, anns.length > 1,
|
||||
state.options.tooltips));
|
||||
}
|
||||
if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
|
||||
}
|
||||
|
||||
function onChange(cm) {
|
||||
var state = cm.state.lint;
|
||||
if (!state) return;
|
||||
clearTimeout(state.timeout);
|
||||
state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
|
||||
}
|
||||
|
||||
function popupTooltips(cm, annotations, e) {
|
||||
var target = e.target || e.srcElement;
|
||||
var tooltip = document.createDocumentFragment();
|
||||
for (var i = 0; i < annotations.length; i++) {
|
||||
var ann = annotations[i];
|
||||
tooltip.appendChild(annotationTooltip(ann));
|
||||
}
|
||||
showTooltipFor(cm, e, tooltip, target);
|
||||
}
|
||||
|
||||
function onMouseOver(cm, e) {
|
||||
var target = e.target || e.srcElement;
|
||||
if (!/\bCodeMirror-lint-mark-/.test(target.className)) return;
|
||||
var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2;
|
||||
var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client"));
|
||||
|
||||
var annotations = [];
|
||||
for (var i = 0; i < spans.length; ++i) {
|
||||
var ann = spans[i].__annotation;
|
||||
if (ann) annotations.push(ann);
|
||||
}
|
||||
if (annotations.length) popupTooltips(cm, annotations, e);
|
||||
}
|
||||
|
||||
CodeMirror.defineOption("lint", false, function(cm, val, old) {
|
||||
if (old && old != CodeMirror.Init) {
|
||||
clearMarks(cm);
|
||||
if (cm.state.lint.options.lintOnChange !== false)
|
||||
cm.off("change", onChange);
|
||||
CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver);
|
||||
clearTimeout(cm.state.lint.timeout);
|
||||
delete cm.state.lint;
|
||||
}
|
||||
|
||||
if (val) {
|
||||
var gutters = cm.getOption("gutters"), hasLintGutter = false;
|
||||
for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true;
|
||||
var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter);
|
||||
if (state.options.lintOnChange !== false)
|
||||
cm.on("change", onChange);
|
||||
if (state.options.tooltips != false && state.options.tooltips != "gutter")
|
||||
CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver);
|
||||
|
||||
startLinting(cm);
|
||||
}
|
||||
});
|
||||
|
||||
CodeMirror.defineExtension("performLint", function() {
|
||||
if (this.state.lint) startLinting(this);
|
||||
});
|
||||
});
|
||||
|
||||
// ========= lint ========= //
|
||||
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
// declare global: JSHINT
|
||||
|
||||
function validator(text, options) {
|
||||
if (!window.JSHINT) {
|
||||
if (window.console) {
|
||||
window.console.error("Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run.");
|
||||
}
|
||||
return [];
|
||||
}
|
||||
if (!options.indent) // JSHint error.character actually is a column index, this fixes underlining on lines using tabs for indentation
|
||||
options.indent = 1; // JSHint default value is 4
|
||||
JSHINT(text, options, options.globals);
|
||||
var errors = JSHINT.data().errors, result = [];
|
||||
if (errors) parseErrors(errors, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
CodeMirror.registerHelper("lint", "javascript", validator);
|
||||
|
||||
function parseErrors(errors, output) {
|
||||
for ( var i = 0; i < errors.length; i++) {
|
||||
var error = errors[i];
|
||||
if (error) {
|
||||
if (error.line <= 0) {
|
||||
if (window.console) {
|
||||
window.console.warn("Cannot display JSHint error (invalid line " + error.line + ")", error);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
var start = error.character - 1, end = start + 1;
|
||||
if (error.evidence) {
|
||||
var index = error.evidence.substring(start).search(/.\b/);
|
||||
if (index > -1) {
|
||||
end += index;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to format expected by validation service
|
||||
var hint = {
|
||||
message: error.reason,
|
||||
severity: error.code ? (error.code.startsWith('W') ? "warning" : "error") : "error",
|
||||
from: CodeMirror.Pos(error.line - 1, start),
|
||||
to: CodeMirror.Pos(error.line - 1, end)
|
||||
};
|
||||
|
||||
output.push(hint);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -539,7 +539,7 @@ span.CodeMirror-selectedtext {
|
||||
|
||||
.CodeMirror-hints {
|
||||
position: absolute;
|
||||
z-index: 301;
|
||||
z-index: 340;
|
||||
overflow: hidden;
|
||||
list-style: none;
|
||||
|
||||
@ -787,7 +787,7 @@ li.CodeMirror-hint-active {
|
||||
|
||||
max-width: 40em;
|
||||
position: absolute;
|
||||
z-index: 330;
|
||||
z-index: 320;
|
||||
-webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
||||
-moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
||||
box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
||||
|
||||
1
_server/CodeMirror/jshint.min.js
vendored
Normal file
1
_server/CodeMirror/jshint.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -4889,3 +4889,604 @@
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
(function(root, mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
return mod(exports, require("tern/lib/infer"), require("tern/lib/tern"), require("acorn/dist/walk"));
|
||||
if (typeof define == "function" && define.amd) // AMD
|
||||
return define(["exports", "tern/lib/infer", "tern/lib/tern", "acorn/dist/walk"], mod);
|
||||
mod(root.tern || (root.tern = {}), tern, tern, acorn.walk);
|
||||
})(this, function(exports, infer, tern, walk) {
|
||||
"use strict";
|
||||
|
||||
var defaultRules = {
|
||||
"UnknownProperty" : {"severity" : "warning"},
|
||||
"UnknownIdentifier" : {"severity" : "warning"},
|
||||
"NotAFunction" : {"severity" : "error"},
|
||||
"InvalidArgument" : {"severity" : "error"},
|
||||
"UnusedVariable" : {"severity" : "warning"},
|
||||
"UnknownModule" : {"severity" : "error"},
|
||||
"MixedReturnTypes": {"severity" : "warning"},
|
||||
"ObjectLiteral": {"severity" : "error"},
|
||||
"TypeMismatch": {"severity" : "warning"},
|
||||
"Array": {"severity" : "error"},
|
||||
"ES6Modules": {"severity" : "error"}
|
||||
};
|
||||
|
||||
function makeVisitors(server, query, file, messages) {
|
||||
|
||||
function addMessage(node, msg, severity) {
|
||||
var error = makeError(node, msg, severity);
|
||||
messages.push(error);
|
||||
}
|
||||
|
||||
function makeError(node, msg, severity) {
|
||||
var error = {};
|
||||
var pos = getPosition(node);
|
||||
|
||||
error.message = msg;
|
||||
error.from = tern.outputPos(query, file, pos.start);
|
||||
error.to = tern.outputPos(query, file, pos.end);
|
||||
error.severity = severity;
|
||||
|
||||
if (query.lineNumber) {
|
||||
error.lineNumber = query.lineCharPositions
|
||||
? error.from.line
|
||||
: tern.outputPos(
|
||||
Object.extend({}, query, { lineCharPositions: true }),
|
||||
file,
|
||||
pos.start
|
||||
).line;
|
||||
}
|
||||
|
||||
if (!query.groupByFiles) error.file = file.name;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
function getNodeName(node) {
|
||||
if (node.callee) {
|
||||
// This is a CallExpression node.
|
||||
// We get the position of the function name.
|
||||
return getNodeName(node.callee);
|
||||
} else if (node.property) {
|
||||
// This is a MemberExpression node.
|
||||
// We get the name of the property.
|
||||
return node.property.name;
|
||||
} else {
|
||||
return node.name;
|
||||
}
|
||||
}
|
||||
|
||||
function getNodeValue(node) {
|
||||
if (node.callee) {
|
||||
// This is a CallExpression node.
|
||||
// We get the position of the function name.
|
||||
return getNodeValue(node.callee);
|
||||
} else if (node.property) {
|
||||
// This is a MemberExpression node.
|
||||
// We get the value of the property.
|
||||
return node.property.value;
|
||||
} else {
|
||||
if (node.type === "Identifier") {
|
||||
var query = {
|
||||
type: "definition",
|
||||
start: node.start,
|
||||
end: node.end
|
||||
};
|
||||
var expr = tern.findQueryExpr(file, query);
|
||||
var type = infer.expressionType(expr);
|
||||
var objExpr = type.getType();
|
||||
if (objExpr && objExpr.originNode) return getNodeValue(objExpr.originNode);
|
||||
return null;
|
||||
}
|
||||
return node.value;
|
||||
}
|
||||
}
|
||||
|
||||
function getPosition(node) {
|
||||
if(node.callee) {
|
||||
// This is a CallExpression node.
|
||||
// We get the position of the function name.
|
||||
return getPosition(node.callee);
|
||||
}
|
||||
if(node.property) {
|
||||
// This is a MemberExpression node.
|
||||
// We get the position of the property.
|
||||
return node.property;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
function getTypeName(type) {
|
||||
if (!type) return "Unknown type";
|
||||
if (type.types) {
|
||||
// multiple types
|
||||
var types = type.types, s = "";
|
||||
for (var i = 0; i < types.length; i++) {
|
||||
if (i > 0) s +="|";
|
||||
var t = getTypeName(types[i]);
|
||||
if (t != "Unknown type") s+= t;
|
||||
}
|
||||
return s == "" ? "Unknown type" : s;
|
||||
}
|
||||
if (type.name) {
|
||||
return type.name;
|
||||
}
|
||||
return (type.proto) ? type.proto.name : "Unknown type";
|
||||
}
|
||||
|
||||
function hasProto(expectedType, name) {
|
||||
if (!expectedType) return false;
|
||||
if(!expectedType.proto) return false;
|
||||
return expectedType.proto.name === name;
|
||||
}
|
||||
|
||||
function isRegexExpected(expectedType) {
|
||||
return hasProto(expectedType, 'RegExp.prototype');
|
||||
}
|
||||
|
||||
function isEmptyType(val) {
|
||||
return (!val || (val.types && val.types.length == 0));
|
||||
}
|
||||
|
||||
function compareType(expected, actual) {
|
||||
if (isEmptyType(expected) || isEmptyType(actual)) return true;
|
||||
if (expected.types) {
|
||||
for (var i = 0; i < expected.types.length; i++) {
|
||||
if (actual.types) {
|
||||
for (var j = 0; j < actual.types.length; j++) {
|
||||
if (compareType(expected.types[i], actual.types[j])) return true;
|
||||
}
|
||||
} else {
|
||||
if (compareType(expected.types[i], actual.getType())) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else if (actual.types) {
|
||||
for (var i = 0; i < actual.types.length; i++) {
|
||||
if (compareType(expected.getType(), actual.types[i])) return true;
|
||||
}
|
||||
}
|
||||
var expectedType = expected.getType(), actualType = actual.getType();
|
||||
if (!expectedType || !actualType) return true;
|
||||
var currentProto = actualType.proto;
|
||||
while(currentProto) {
|
||||
if (expectedType.proto && expectedType.proto.name === currentProto.name) return true;
|
||||
currentProto = currentProto.proto;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkPropsInObject(node, expectedArg, actualObj, invalidArgument) {
|
||||
var properties = node.properties, expectedObj = expectedArg.getType();
|
||||
for (var i = 0; i < properties.length; i++) {
|
||||
var property = properties[i], key = property.key, prop = key && key.name, value = property.value;
|
||||
if (prop) {
|
||||
var expectedType = expectedObj.hasProp(prop);
|
||||
if (!expectedType) {
|
||||
// key doesn't exists
|
||||
addMessage(key, "Invalid property at " + (i+1) + ": " + prop + " is not a property in " + getTypeName(expectedArg), invalidArgument.severity);
|
||||
} else {
|
||||
// test that each object literal prop is the correct type
|
||||
var actualType = actualObj.props[prop];
|
||||
if (!compareType(expectedType, actualType)) {
|
||||
addMessage(value, "Invalid property at " + (i+1) + ": cannot convert from " + getTypeName(actualType) + " to " + getTypeName(expectedType), invalidArgument.severity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkItemInArray(node, expectedArg, state, invalidArgument) {
|
||||
var elements = node.elements, expectedType = expectedArg.hasProp("<i>");
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var elt = elements[i], actualType = infer.expressionType({node: elt, state: state});
|
||||
if (!compareType(expectedType, actualType)) {
|
||||
addMessage(elt, "Invalid item at " + (i+1) + ": cannot convert from " + getTypeName(actualType) + " to " + getTypeName(expectedType), invalidArgument.severity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isObjectLiteral(type) {
|
||||
var objType = type.getObjType();
|
||||
return objType && objType.proto && objType.proto.name == "Object.prototype";
|
||||
}
|
||||
|
||||
function getFunctionLint(fnType) {
|
||||
if (fnType.lint) return fnType.lint;
|
||||
if (fnType.metaData) {
|
||||
fnType.lint = getLint(fnType.metaData["!lint"]);
|
||||
return fnType.lint;
|
||||
};
|
||||
}
|
||||
|
||||
function isFunctionType(type) {
|
||||
if (type.types) {
|
||||
for (var i = 0; i < type.types.length; i++) {
|
||||
if (isFunctionType(type.types[i])) return true;
|
||||
}
|
||||
}
|
||||
return type.proto && type.proto.name == "Function.prototype";
|
||||
}
|
||||
|
||||
function validateCallExpression(node, state, c) {
|
||||
var notAFunctionRule = getRule("NotAFunction"), invalidArgument = getRule("InvalidArgument");
|
||||
if (!notAFunctionRule && !invalidArgument) return;
|
||||
var type = infer.expressionType({node: node.callee, state: state});
|
||||
if(type && !type.isEmpty()) {
|
||||
// If type.isEmpty(), it is handled by MemberExpression/Identifier already.
|
||||
|
||||
// An expression can have multiple possible (guessed) types.
|
||||
// If one of them is a function, type.getFunctionType() will return it.
|
||||
var fnType = type.getFunctionType();
|
||||
if(fnType == null) {
|
||||
if (notAFunctionRule && !isFunctionType(type)) addMessage(node, "'" + getNodeName(node) + "' is not a function", notAFunctionRule.severity);
|
||||
return;
|
||||
}
|
||||
var fnLint = getFunctionLint(fnType);
|
||||
var continueLint = fnLint ? fnLint(node, addMessage, getRule) : true;
|
||||
if (continueLint && fnType.args) {
|
||||
// validate parameters of the function
|
||||
if (!invalidArgument) return;
|
||||
var actualArgs = node.arguments;
|
||||
if (!actualArgs) return;
|
||||
var expectedArgs = fnType.args;
|
||||
for (var i = 0; i < expectedArgs.length; i++) {
|
||||
var expectedArg = expectedArgs[i];
|
||||
if (actualArgs.length > i) {
|
||||
var actualNode = actualArgs[i];
|
||||
if (isRegexExpected(expectedArg.getType())) {
|
||||
var value = getNodeValue(actualNode);
|
||||
if (value) {
|
||||
try {
|
||||
var regex = new RegExp(value);
|
||||
}
|
||||
catch(e) {
|
||||
addMessage(actualNode, "Invalid argument at " + (i+1) + ": " + e, invalidArgument.severity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var actualArg = infer.expressionType({node: actualNode, state: state});
|
||||
// if actual type is an Object literal and expected type is an object, we ignore
|
||||
// the comparison type since object literal properties validation is done inside "ObjectExpression".
|
||||
if (!(expectedArg.getObjType() && isObjectLiteral(actualArg))) {
|
||||
if (!compareType(expectedArg, actualArg)) {
|
||||
addMessage(actualNode, "Invalid argument at " + (i+1) + ": cannot convert from " + getTypeName(actualArg) + " to " + getTypeName(expectedArg), invalidArgument.severity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateAssignement(nodeLeft, nodeRight, rule, state) {
|
||||
if (!nodeLeft || !nodeRight) return;
|
||||
if (!rule) return;
|
||||
var leftType = infer.expressionType({node: nodeLeft, state: state}),
|
||||
rightType = infer.expressionType({node: nodeRight, state: state});
|
||||
if (!compareType(leftType, rightType)) {
|
||||
addMessage(nodeRight, "Type mismatch: cannot convert from " + getTypeName(leftType) + " to " + getTypeName(rightType), rule.severity);
|
||||
}
|
||||
}
|
||||
|
||||
function validateDeclaration(node, state, c) {
|
||||
|
||||
function isUsedVariable(varNode, varState, file, srv) {
|
||||
var name = varNode.name;
|
||||
|
||||
for (var scope = varState; scope && !(name in scope.props); scope = scope.prev) {}
|
||||
if (!scope) return false;
|
||||
|
||||
var hasRef = false;
|
||||
function searchRef(file) {
|
||||
return function(node, scopeHere) {
|
||||
if (node != varNode) {
|
||||
hasRef = true;
|
||||
throw new Error(); // throw an error to stop the search.
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
if (scope.node) {
|
||||
// local scope
|
||||
infer.findRefs(scope.node, scope, name, scope, searchRef(file));
|
||||
} else {
|
||||
// global scope
|
||||
infer.findRefs(file.ast, file.scope, name, scope, searchRef(file));
|
||||
for (var i = 0; i < srv.files.length && !hasRef; ++i) {
|
||||
var cur = srv.files[i];
|
||||
if (cur != file) infer.findRefs(cur.ast, cur.scope, name, scope, searchRef(cur));
|
||||
}
|
||||
}
|
||||
} catch(e) {};
|
||||
return hasRef;
|
||||
}
|
||||
|
||||
var unusedRule = getRule("UnusedVariable"), mismatchRule = getRule("TypeMismatch");
|
||||
if (!unusedRule && !mismatchRule) return;
|
||||
switch(node.type) {
|
||||
case "VariableDeclaration":
|
||||
for (var i = 0; i < node.declarations.length; ++i) {
|
||||
var decl = node.declarations[i], varNode = decl.id;
|
||||
if (varNode.name != "✖") {
|
||||
// unused variable
|
||||
if (unusedRule && !isUsedVariable(varNode, state, file, server)) addMessage(varNode, "Unused variable '" + getNodeName(varNode) + "'", unusedRule.severity);
|
||||
// type mismatch?
|
||||
if (mismatchRule) validateAssignement(varNode, decl.init, mismatchRule, state);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "FunctionDeclaration":
|
||||
if (unusedRule) {
|
||||
var varNode = node.id;
|
||||
if (varNode.name != "✖" && !isUsedVariable(varNode, state, file, server)) addMessage(varNode, "Unused function '" + getNodeName(varNode) + "'", unusedRule.severity);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function getArrType(type) {
|
||||
if (type instanceof infer.Arr) {
|
||||
return type.getObjType();
|
||||
} else if (type.types) {
|
||||
for (var i = 0; i < type.types.length; i++) {
|
||||
if (getArrType(type.types[i])) return type.types[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var visitors = {
|
||||
VariableDeclaration: validateDeclaration,
|
||||
FunctionDeclaration: validateDeclaration,
|
||||
ReturnStatement: function(node, state, c) {
|
||||
if (!node.argument) return;
|
||||
var rule = getRule("MixedReturnTypes");
|
||||
if (!rule) return;
|
||||
if (state.fnType && state.fnType.retval) {
|
||||
var actualType = infer.expressionType({node: node.argument, state: state}), expectedType = state.fnType.retval;
|
||||
if (!compareType(expectedType, actualType)) {
|
||||
addMessage(node, "Invalid return type : cannot convert from " + getTypeName(actualType) + " to " + getTypeName(expectedType), rule.severity);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Detects expressions of the form `object.property`
|
||||
MemberExpression: function(node, state, c) {
|
||||
var rule = getRule("UnknownProperty");
|
||||
if (!rule) return;
|
||||
var prop = node.property && node.property.name;
|
||||
if (!prop || prop == "✖") return;
|
||||
var type = infer.expressionType({node: node, state: state});
|
||||
var parentType = infer.expressionType({node: node.object, state: state});
|
||||
|
||||
if(node.computed) {
|
||||
// Bracket notation.
|
||||
// Until we figure out how to handle these properly, we ignore these nodes.
|
||||
return;
|
||||
}
|
||||
|
||||
if(!parentType.isEmpty() && type.isEmpty()) {
|
||||
// The type of the property cannot be determined, which means
|
||||
// that the property probably doesn't exist.
|
||||
|
||||
// We only do this check if the parent type is known,
|
||||
// otherwise we will generate errors for an entire chain of unknown
|
||||
// properties.
|
||||
|
||||
// Also, the expression may be valid even if the parent type is unknown,
|
||||
// since the inference engine cannot detect the type in all cases.
|
||||
|
||||
var propertyDefined = false;
|
||||
|
||||
// In some cases the type is unknown, even if the property is defined
|
||||
if(parentType.types) {
|
||||
// We cannot use parentType.hasProp or parentType.props - in the case of an AVal,
|
||||
// this may contain properties that are not really defined.
|
||||
parentType.types.forEach(function(potentialType) {
|
||||
// Obj#hasProp checks the prototype as well
|
||||
if(typeof potentialType.hasProp == 'function' && potentialType.hasProp(prop, true)) {
|
||||
propertyDefined = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(!propertyDefined) {
|
||||
addMessage(node, "Unknown property '" + getNodeName(node) + "'", rule.severity);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Detects top-level identifiers, e.g. the object in
|
||||
// `object.property` or just `object`.
|
||||
Identifier: function(node, state, c) {
|
||||
var rule = getRule("UnknownIdentifier");
|
||||
if (!rule) return;
|
||||
var type = infer.expressionType({
|
||||
node: node,
|
||||
state: state,
|
||||
});
|
||||
|
||||
if (type.originNode != null || type.origin != null) {
|
||||
// The node is defined somewhere (could be this node),
|
||||
// regardless of whether or not the type is known.
|
||||
} else if (type.isEmpty()) {
|
||||
// The type of the identifier cannot be determined,
|
||||
// and the origin is unknown.
|
||||
addMessage(node, "Unknown identifier '" + getNodeName(node) + "'", rule.severity);
|
||||
} else {
|
||||
// Even though the origin node is unknown, the type is known.
|
||||
// This is typically the case for built-in identifiers (e.g. window or document).
|
||||
}
|
||||
},
|
||||
// Detects function calls.
|
||||
// `node.callee` is the expression (Identifier or MemberExpression)
|
||||
// the is called as a function.
|
||||
NewExpression: validateCallExpression,
|
||||
CallExpression: validateCallExpression,
|
||||
AssignmentExpression: function(node, state, c) {
|
||||
var rule = getRule("TypeMismatch");
|
||||
validateAssignement(node.left, node.right, rule, state);
|
||||
},
|
||||
ObjectExpression: function(node, state, c) {
|
||||
// validate properties of the object literal
|
||||
var rule = getRule("ObjectLiteral");
|
||||
if (!rule) return;
|
||||
var actualType = node.objType;
|
||||
var ctxType = infer.typeFromContext(file.ast, {node: node, state: state}), expectedType = null;
|
||||
if (ctxType instanceof infer.Obj) {
|
||||
expectedType = ctxType.getObjType();
|
||||
} else if (ctxType && ctxType.makeupType) {
|
||||
var objType = ctxType.makeupType();
|
||||
if (objType && objType.getObjType()) {
|
||||
expectedType = objType.getObjType();
|
||||
}
|
||||
}
|
||||
if (expectedType && expectedType != actualType) {
|
||||
// expected type is known. Ex: config object of RequireJS
|
||||
checkPropsInObject(node, expectedType, actualType, rule);
|
||||
}
|
||||
},
|
||||
ArrayExpression: function(node, state, c) {
|
||||
// validate elements of the Arrray
|
||||
var rule = getRule("Array");
|
||||
if (!rule) return;
|
||||
//var actualType = infer.expressionType({node: node, state: state});
|
||||
var ctxType = infer.typeFromContext(file.ast, {node: node, state: state}), expectedType = getArrType(ctxType);
|
||||
if (expectedType /*&& expectedType != actualType*/) {
|
||||
// expected type is known. Ex: config object of RequireJS
|
||||
checkItemInArray(node, expectedType, state, rule);
|
||||
}
|
||||
},
|
||||
ImportDeclaration: function(node, state, c) {
|
||||
// Validate ES6 modules from + specifiers
|
||||
var rule = getRule("ES6Modules");
|
||||
if (!rule) return;
|
||||
var me = infer.cx().parent.mod.modules;
|
||||
if (!me) return; // tern plugin modules.js is not loaded
|
||||
var source = node.source;
|
||||
if (!source) return;
|
||||
// Validate ES6 modules "from"
|
||||
var modType = me.getModType(source);
|
||||
if (!modType || modType == infer.ANull) {
|
||||
addMessage(source, "Invalid modules from '" + source.value + "'", rule.severity);
|
||||
return;
|
||||
}
|
||||
// Validate ES6 modules "specifiers"
|
||||
var specifiers = node.specifiers, specifier;
|
||||
if (!specifiers) return;
|
||||
for (var i = 0; i < specifiers.length; i++) {
|
||||
var specifier = specifiers[i], imported = specifier.imported;
|
||||
if (imported) {
|
||||
var name = imported.name, type = modType.getType();
|
||||
if (type && !type.hasProp(name)) addMessage(imported, "Invalid modules specifier '" + getNodeName(imported) + "'", rule.severity);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return visitors;
|
||||
}
|
||||
|
||||
// Adapted from infer.searchVisitor.
|
||||
// Record the scope and pass it through in the state.
|
||||
// VariableDeclaration in infer.searchVisitor breaks things for us.
|
||||
var scopeVisitor = walk.make({
|
||||
Function: function(node, _st, c) {
|
||||
var scope = node.scope;
|
||||
if (node.id) c(node.id, scope);
|
||||
for (var i = 0; i < node.params.length; ++i)
|
||||
c(node.params[i], scope);
|
||||
c(node.body, scope, "ScopeBody");
|
||||
},
|
||||
Statement: function(node, st, c) {
|
||||
c(node, node.scope || st)
|
||||
}
|
||||
});
|
||||
|
||||
// Validate one file
|
||||
|
||||
var validateFile = exports.validateFile = function(server, query, file) {
|
||||
try {
|
||||
var messages = [];
|
||||
var ast = file.ast;
|
||||
var state = file.scope;
|
||||
var visitors = makeVisitors(server, query, file, messages);
|
||||
walk.simple(ast, visitors, infer.searchVisitor, state);
|
||||
return { messages: messages };
|
||||
} catch(err) {
|
||||
console.error(err.stack);
|
||||
return { messages: [] };
|
||||
}
|
||||
}
|
||||
|
||||
tern.defineQueryType("lint", {
|
||||
takesFile: true,
|
||||
run: function(server, query, file) {
|
||||
return validateFile(server, query, file);
|
||||
}
|
||||
});
|
||||
|
||||
// Validate the whole files of the server
|
||||
|
||||
var validateFiles = exports.validateFiles = function(server, query) {
|
||||
try {
|
||||
var messages = [], files = server.files, groupByFiles = query.groupByFiles == true;
|
||||
for (var i = 0; i < files.length; ++i) {
|
||||
var messagesFile = groupByFiles ? [] : messages, file = files[i], ast = file.ast, state = file.scope;
|
||||
var visitors = makeVisitors(server, query, file, messagesFile);
|
||||
walk.simple(ast, visitors, infer.searchVisitor, state);
|
||||
if (groupByFiles) messages.push({file:file.name, messages: messagesFile});
|
||||
}
|
||||
return {messages: messages};
|
||||
} catch(err) {
|
||||
console.error(err.stack);
|
||||
return {messages: []};
|
||||
}
|
||||
}
|
||||
|
||||
tern.defineQueryType("lint-full", {
|
||||
run: function(server, query) {
|
||||
return validateFiles(server, query);
|
||||
}
|
||||
});
|
||||
|
||||
var lints = Object.create(null);
|
||||
tern.registerLint = function(name, lint) {
|
||||
lints[name] = lint;
|
||||
};
|
||||
|
||||
var getLint = tern.getLint = function(name) {
|
||||
if (!name) return null;
|
||||
return lints[name];
|
||||
}
|
||||
|
||||
tern.registerPlugin("lint", function(server, options) {
|
||||
server._lint = {
|
||||
rules: getRules(options)
|
||||
};
|
||||
return {
|
||||
passes: {},
|
||||
loadFirst: true
|
||||
};
|
||||
});
|
||||
|
||||
function getRules(options) {
|
||||
var rules = {};
|
||||
for(var ruleName in defaultRules) {
|
||||
if (options && options.rules && options.rules[ruleName] && options.rules[ruleName].severity) {
|
||||
if (options.rules[ruleName].severity != 'none') rules[ruleName] = options.rules[ruleName];
|
||||
} else {
|
||||
rules[ruleName] = defaultRules[ruleName];
|
||||
}
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
|
||||
function getRule(ruleName) {
|
||||
var cx = infer.cx(), server = cx.parent, rules = server._lint.rules;
|
||||
return rules[ruleName];
|
||||
}
|
||||
});
|
||||
@ -18,6 +18,7 @@ editor_multi = function () {
|
||||
lint: true,
|
||||
autocomplete: true,
|
||||
autoCloseBrackets: true,
|
||||
styleActiveLine: true,
|
||||
highlightSelectionMatches: { showToken: /\w/, annotateScrollbar: true }
|
||||
});
|
||||
|
||||
|
||||
@ -452,6 +452,7 @@
|
||||
<!-- codemirror -->
|
||||
<script src="_server/CodeMirror/codeMirror.bundle.min.js"></script>
|
||||
<script src="_server/CodeMirror/beautify.min.js"></script>
|
||||
<script src="_server/CodeMirror/jshint.min.js"></script>
|
||||
<script src="_server/CodeMirror/codeMirror.plugin.js"></script>
|
||||
<script src="_server/CodeMirror/acorn.min.js"></script>
|
||||
<script src="_server/CodeMirror/defs.js"></script>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user