Mercurial
comparison .cms/lib/codemirror/addon/lint/lint.js @ 0:78edf6b517a0 draft
24.10
author | Coffee CMS <info@coffee-cms.ru> |
---|---|
date | Fri, 11 Oct 2024 22:40:23 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:78edf6b517a0 |
---|---|
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others | |
2 // Distributed under an MIT license: https://codemirror.net/5/LICENSE | |
3 | |
4 (function(mod) { | |
5 if (typeof exports == "object" && typeof module == "object") // CommonJS | |
6 mod(require("../../lib/codemirror")); | |
7 else if (typeof define == "function" && define.amd) // AMD | |
8 define(["../../lib/codemirror"], mod); | |
9 else // Plain browser env | |
10 mod(CodeMirror); | |
11 })(function(CodeMirror) { | |
12 "use strict"; | |
13 var GUTTER_ID = "CodeMirror-lint-markers"; | |
14 var LINT_LINE_ID = "CodeMirror-lint-line-"; | |
15 | |
16 function showTooltip(cm, e, content) { | |
17 var tt = document.createElement("div"); | |
18 tt.className = "CodeMirror-lint-tooltip cm-s-" + cm.options.theme; | |
19 tt.appendChild(content.cloneNode(true)); | |
20 if (cm.state.lint.options.selfContain) | |
21 cm.getWrapperElement().appendChild(tt); | |
22 else | |
23 document.body.appendChild(tt); | |
24 | |
25 function position(e) { | |
26 if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position); | |
27 var top = Math.max(0, e.clientY - tt.offsetHeight - 5); | |
28 var left = Math.max(0, Math.min(e.clientX + 5, tt.ownerDocument.defaultView.innerWidth - tt.offsetWidth)); | |
29 tt.style.top = top + "px" | |
30 tt.style.left = left + "px"; | |
31 } | |
32 CodeMirror.on(document, "mousemove", position); | |
33 position(e); | |
34 if (tt.style.opacity != null) tt.style.opacity = 1; | |
35 return tt; | |
36 } | |
37 function rm(elt) { | |
38 if (elt.parentNode) elt.parentNode.removeChild(elt); | |
39 } | |
40 function hideTooltip(tt) { | |
41 if (!tt.parentNode) return; | |
42 if (tt.style.opacity == null) rm(tt); | |
43 tt.style.opacity = 0; | |
44 setTimeout(function() { rm(tt); }, 600); | |
45 } | |
46 | |
47 function showTooltipFor(cm, e, content, node) { | |
48 var tooltip = showTooltip(cm, e, content); | |
49 function hide() { | |
50 CodeMirror.off(node, "mouseout", hide); | |
51 if (tooltip) { hideTooltip(tooltip); tooltip = null; } | |
52 } | |
53 var poll = setInterval(function() { | |
54 if (tooltip) for (var n = node;; n = n.parentNode) { | |
55 if (n && n.nodeType == 11) n = n.host; | |
56 if (n == document.body) return; | |
57 if (!n) { hide(); break; } | |
58 } | |
59 if (!tooltip) return clearInterval(poll); | |
60 }, 400); | |
61 CodeMirror.on(node, "mouseout", hide); | |
62 } | |
63 | |
64 function LintState(cm, conf, hasGutter) { | |
65 this.marked = []; | |
66 if (conf instanceof Function) conf = {getAnnotations: conf}; | |
67 if (!conf || conf === true) conf = {}; | |
68 this.options = {}; | |
69 this.linterOptions = conf.options || {}; | |
70 for (var prop in defaults) this.options[prop] = defaults[prop]; | |
71 for (var prop in conf) { | |
72 if (defaults.hasOwnProperty(prop)) { | |
73 if (conf[prop] != null) this.options[prop] = conf[prop]; | |
74 } else if (!conf.options) { | |
75 this.linterOptions[prop] = conf[prop]; | |
76 } | |
77 } | |
78 this.timeout = null; | |
79 this.hasGutter = hasGutter; | |
80 this.onMouseOver = function(e) { onMouseOver(cm, e); }; | |
81 this.waitingFor = 0 | |
82 } | |
83 | |
84 var defaults = { | |
85 highlightLines: false, | |
86 tooltips: true, | |
87 delay: 500, | |
88 lintOnChange: true, | |
89 getAnnotations: null, | |
90 async: false, | |
91 selfContain: null, | |
92 formatAnnotation: null, | |
93 onUpdateLinting: null | |
94 } | |
95 | |
96 function clearMarks(cm) { | |
97 var state = cm.state.lint; | |
98 if (state.hasGutter) cm.clearGutter(GUTTER_ID); | |
99 if (state.options.highlightLines) clearErrorLines(cm); | |
100 for (var i = 0; i < state.marked.length; ++i) | |
101 state.marked[i].clear(); | |
102 state.marked.length = 0; | |
103 } | |
104 | |
105 function clearErrorLines(cm) { | |
106 cm.eachLine(function(line) { | |
107 var has = line.wrapClass && /\bCodeMirror-lint-line-\w+\b/.exec(line.wrapClass); | |
108 if (has) cm.removeLineClass(line, "wrap", has[0]); | |
109 }) | |
110 } | |
111 | |
112 function makeMarker(cm, labels, severity, multiple, tooltips) { | |
113 var marker = document.createElement("div"), inner = marker; | |
114 marker.className = "CodeMirror-lint-marker CodeMirror-lint-marker-" + severity; | |
115 if (multiple) { | |
116 inner = marker.appendChild(document.createElement("div")); | |
117 inner.className = "CodeMirror-lint-marker CodeMirror-lint-marker-multiple"; | |
118 } | |
119 | |
120 if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) { | |
121 showTooltipFor(cm, e, labels, inner); | |
122 }); | |
123 | |
124 return marker; | |
125 } | |
126 | |
127 function getMaxSeverity(a, b) { | |
128 if (a == "error") return a; | |
129 else return b; | |
130 } | |
131 | |
132 function groupByLine(annotations) { | |
133 var lines = []; | |
134 for (var i = 0; i < annotations.length; ++i) { | |
135 var ann = annotations[i], line = ann.from.line; | |
136 (lines[line] || (lines[line] = [])).push(ann); | |
137 } | |
138 return lines; | |
139 } | |
140 | |
141 function annotationTooltip(ann) { | |
142 var severity = ann.severity; | |
143 if (!severity) severity = "error"; | |
144 var tip = document.createElement("div"); | |
145 tip.className = "CodeMirror-lint-message CodeMirror-lint-message-" + severity; | |
146 if (typeof ann.messageHTML != 'undefined') { | |
147 tip.innerHTML = ann.messageHTML; | |
148 } else { | |
149 tip.appendChild(document.createTextNode(ann.message)); | |
150 } | |
151 return tip; | |
152 } | |
153 | |
154 function lintAsync(cm, getAnnotations) { | |
155 var state = cm.state.lint | |
156 var id = ++state.waitingFor | |
157 function abort() { | |
158 id = -1 | |
159 cm.off("change", abort) | |
160 } | |
161 cm.on("change", abort) | |
162 getAnnotations(cm.getValue(), function(annotations, arg2) { | |
163 cm.off("change", abort) | |
164 if (state.waitingFor != id) return | |
165 if (arg2 && annotations instanceof CodeMirror) annotations = arg2 | |
166 cm.operation(function() {updateLinting(cm, annotations)}) | |
167 }, state.linterOptions, cm); | |
168 } | |
169 | |
170 function startLinting(cm) { | |
171 var state = cm.state.lint; | |
172 if (!state) return; | |
173 var options = state.options; | |
174 /* | |
175 * Passing rules in `options` property prevents JSHint (and other linters) from complaining | |
176 * about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc. | |
177 */ | |
178 var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint"); | |
179 if (!getAnnotations) return; | |
180 if (options.async || getAnnotations.async) { | |
181 lintAsync(cm, getAnnotations) | |
182 } else { | |
183 var annotations = getAnnotations(cm.getValue(), state.linterOptions, cm); | |
184 if (!annotations) return; | |
185 if (annotations.then) annotations.then(function(issues) { | |
186 cm.operation(function() {updateLinting(cm, issues)}) | |
187 }); | |
188 else cm.operation(function() {updateLinting(cm, annotations)}) | |
189 } | |
190 } | |
191 | |
192 function updateLinting(cm, annotationsNotSorted) { | |
193 var state = cm.state.lint; | |
194 if (!state) return; | |
195 var options = state.options; | |
196 clearMarks(cm); | |
197 | |
198 var annotations = groupByLine(annotationsNotSorted); | |
199 | |
200 for (var line = 0; line < annotations.length; ++line) { | |
201 var anns = annotations[line]; | |
202 if (!anns) continue; | |
203 | |
204 var maxSeverity = null; | |
205 var tipLabel = state.hasGutter && document.createDocumentFragment(); | |
206 | |
207 for (var i = 0; i < anns.length; ++i) { | |
208 var ann = anns[i]; | |
209 var severity = ann.severity; | |
210 if (!severity) severity = "error"; | |
211 maxSeverity = getMaxSeverity(maxSeverity, severity); | |
212 | |
213 if (options.formatAnnotation) ann = options.formatAnnotation(ann); | |
214 if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann)); | |
215 | |
216 if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, { | |
217 className: "CodeMirror-lint-mark CodeMirror-lint-mark-" + severity, | |
218 __annotation: ann | |
219 })); | |
220 } | |
221 if (state.hasGutter) | |
222 cm.setGutterMarker(line, GUTTER_ID, makeMarker(cm, tipLabel, maxSeverity, anns.length > 1, | |
223 options.tooltips)); | |
224 | |
225 if (options.highlightLines) | |
226 cm.addLineClass(line, "wrap", LINT_LINE_ID + maxSeverity); | |
227 } | |
228 if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); | |
229 } | |
230 | |
231 function onChange(cm) { | |
232 var state = cm.state.lint; | |
233 if (!state) return; | |
234 clearTimeout(state.timeout); | |
235 state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay); | |
236 } | |
237 | |
238 function popupTooltips(cm, annotations, e) { | |
239 var target = e.target || e.srcElement; | |
240 var tooltip = document.createDocumentFragment(); | |
241 for (var i = 0; i < annotations.length; i++) { | |
242 var ann = annotations[i]; | |
243 tooltip.appendChild(annotationTooltip(ann)); | |
244 } | |
245 showTooltipFor(cm, e, tooltip, target); | |
246 } | |
247 | |
248 function onMouseOver(cm, e) { | |
249 var target = e.target || e.srcElement; | |
250 if (!/\bCodeMirror-lint-mark-/.test(target.className)) return; | |
251 var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2; | |
252 var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client")); | |
253 | |
254 var annotations = []; | |
255 for (var i = 0; i < spans.length; ++i) { | |
256 var ann = spans[i].__annotation; | |
257 if (ann) annotations.push(ann); | |
258 } | |
259 if (annotations.length) popupTooltips(cm, annotations, e); | |
260 } | |
261 | |
262 CodeMirror.defineOption("lint", false, function(cm, val, old) { | |
263 if (old && old != CodeMirror.Init) { | |
264 clearMarks(cm); | |
265 if (cm.state.lint.options.lintOnChange !== false) | |
266 cm.off("change", onChange); | |
267 CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver); | |
268 clearTimeout(cm.state.lint.timeout); | |
269 delete cm.state.lint; | |
270 } | |
271 | |
272 if (val) { | |
273 var gutters = cm.getOption("gutters"), hasLintGutter = false; | |
274 for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; | |
275 var state = cm.state.lint = new LintState(cm, val, hasLintGutter); | |
276 if (state.options.lintOnChange) | |
277 cm.on("change", onChange); | |
278 if (state.options.tooltips != false && state.options.tooltips != "gutter") | |
279 CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); | |
280 | |
281 startLinting(cm); | |
282 } | |
283 }); | |
284 | |
285 CodeMirror.defineExtension("performLint", function() { | |
286 startLinting(this); | |
287 }); | |
288 }); |