diff --git a/CodeMirror-3.14/addon/lint/javascript-lint.js b/CodeMirror-3.14/addon/lint/javascript-lint.js new file mode 100644 index 0000000..b9ef0d1 --- /dev/null +++ b/CodeMirror-3.14/addon/lint/javascript-lint.js @@ -0,0 +1,127 @@ +(function() { + + var bogus = [ "Dangerous comment" ]; + + 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" ]; + + function validator(options, text) { + JSHINT(text, options); + var errors = JSHINT.data().errors, result = []; + if (errors) parseErrors(errors, result); + return result; + } + + CodeMirror.javascriptValidatorWithOptions = function(options) { + return function(text) { return validator(options, text); }; + }; + + CodeMirror.javascriptValidator = CodeMirror.javascriptValidatorWithOptions(null); + + 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; + } + + 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;// + "(jshint)"; + 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)}); + } + } + } +})(); \ No newline at end of file diff --git a/CodeMirror-3.14/addon/lint/lint.css b/CodeMirror-3.14/addon/lint/lint.css new file mode 100644 index 0000000..160c0ca --- /dev/null +++ b/CodeMirror-3.14/addon/lint/lint.css @@ -0,0 +1,72 @@ +/* The lint marker gutter */ +.CodeMirror-lint-markers { + width: 12px; margin-left: 2px; +} + +.CodeMirror-lint-tooltip { + background-color: infobackground; + border: 1px solid black; + border-radius: 4px 4px 4px 4px; + color: infotext; + font-family: monospace; + font-size: 10pt; + overflow: hidden; + padding: 2px 5px; + position: fixed; + white-space: pre; + z-index: 100; + max-width: 600px; + opacity: 0; + transition: opacity .4s; + -moz-transition: opacity .4s; + -webkit-transition: opacity .4s; + -o-transition: opacity .4s; + -ms-transition: opacity .4s; +} + +.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning { + background-position: left bottom; + background-repeat: repeat-x; +} + +.CodeMirror-lint-mark-error { + background-image: + url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==") + ; +} + +.CodeMirror-lint-mark-warning { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); +} + +.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning { + background-position: center center; + background-repeat: no-repeat; + cursor: help; + display: inline-block; + height: 16px; + width: 16px; + vertical-align: middle; + position: relative; +} + +.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning { + padding-left: 18px; + background-position: top left; + background-repeat: no-repeat; +} + +.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { + background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPVvcvWHiPVucvRuc+ttcfV6f91KVN5LU99PV/FZY/JhaM4oN84pONE4Rd1ATfJLWutVYPRgbdxpcsgWKMgZKs4lNfE/UvE/U+artcpdSc5uXveimslHPuBhW/eJhfV5efaCgO2CgP+/v+PExP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAQABAAAAZ+wJJwSCwaScgkySgkjTQZTkYzWhadnE5oE+pwqkSshwQqkzxfa4kkQXxEpA9J9EFI1KQGQQBAigYCBA14ExEWF0gXihETeA0QD3AkD5QQg0NsDnAJmwkOd5gYFSQKpXAFDBhqaxgLBwQBBAapq00YEg0UDRKqTGtKSL7Cw8JBADs="); +} + +.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning { + background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP7bc//egf/ij/7ijv/jl/7kl//mnv7lnv/uwf7CTP7DTf7DT/7IW//Na/7Na//NbP7QdP/dmbltAIJNAF03AMSAJMSCLKqASa2DS6uBSquCSrGHTq6ETbCHT7WKUrKIUcCVXL+UXMOYX8GWXsSZYMiib6+ETbOIUcOXX86uhd3Muf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACsALAAAAAAQABAAAAZowJVwSCwaj0ihikRSJYcoBEL0XKlGkcjImQQhJBREKFnyICoThKeE/AAW6AXgdPyUAgrLJBEo0YsbAQyDhAEdRRwDDw8OaA4NDQImRBgFEJdglxAEGEQZKQcHBqOkKRpFF6mqq1WtrUEAOw=="); +} + +.CodeMirror-lint-marker-multiple { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII="); + background-repeat: no-repeat; + background-position: right bottom; + width: 100%; height: 100%; +} \ No newline at end of file diff --git a/CodeMirror-3.14/addon/lint/lint.js b/CodeMirror-3.14/addon/lint/lint.js new file mode 100644 index 0000000..dadabe1 --- /dev/null +++ b/CodeMirror-3.14/addon/lint/lint.js @@ -0,0 +1,197 @@ +CodeMirror.validate = (function() { + var GUTTER_ID = "CodeMirror-lint-markers"; + var SEVERITIES = /^(?:error|warning)$/; + + function showTooltip(e, content) { + var tt = document.createElement("div"); + tt.className = "CodeMirror-lint-tooltip"; + tt.appendChild(content.cloneNode(true)); + 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(e, content, node) { + var tooltip = showTooltip(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 == 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); }; + } + + function parseOptions(options) { + if (options instanceof Function) return {getAnnotations: options}; + else if (!options || !options.getAnnotations) throw new Error("Required option 'getAnnotations' missing (lint addon)"); + 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(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(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 (!SEVERITIES.test(severity)) severity = "error"; + var tip = document.createElement("div"); + tip.className = "CodeMirror-lint-message-" + severity; + tip.appendChild(document.createTextNode(ann.message)); + return tip; + } + + function startLinting(cm) { + var state = cm.state.lint, options = state.options; + if (options.async) + options.getAnnotations(cm, updateLinting, options); + else + updateLinting(cm, options.getAnnotations(cm.getValue())); + } + + 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 (!SEVERITIES.test(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(tipLabel, maxSeverity, anns.length > 1, + state.options.tooltips)); + } + if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); + } + + function onChange(cm) { + var state = cm.state.lint; + clearTimeout(state.timeout); + state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500); + } + + function popupSpanTooltip(ann, e) { + var target = e.target || e.srcElement; + showTooltipFor(e, annotationTooltip(ann), target); + } + + // When the mouseover fires, the cursor might not actually be over + // the character itself yet. These pairs of x,y offsets are used to + // probe a few nearby points when no suitable marked range is found. + var nearby = [0, 0, 0, 5, 0, -5, 5, 0, -5, 0]; + + function onMouseOver(cm, e) { + if (!/\bCodeMirror-lint-mark-/.test((e.target || e.srcElement).className)) return; + for (var i = 0; i < nearby.length; i += 2) { + var spans = cm.findMarksAt(cm.coordsChar({left: e.clientX + nearby[i], + top: e.clientY + nearby[i + 1]})); + for (var j = 0; j < spans.length; ++j) { + var span = spans[j], ann = span.__annotation; + if (ann) return popupSpanTooltip(ann, e); + } + } + } + + CodeMirror.defineOption("lintWith", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + clearMarks(cm); + cm.off("change", onChange); + CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver); + 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(val), hasLintGutter); + cm.on("change", onChange); + if (state.options.tooltips != false) + CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); + + startLinting(cm); + } + }); +})(); \ No newline at end of file