2 if (typeof exports
== "object" && typeof module
== "object") // CommonJS
3 mod(require("../../lib/codemirror"));
4 else if (typeof define
== "function" && define
.amd
) // AMD
5 define(["../../lib/codemirror"], mod
);
6 else // Plain browser env
8 })(function(CodeMirror
) {
10 var GUTTER_ID
= "CodeMirror-lint-markers";
11 var SEVERITIES
= /^(?:error|warning)$/;
13 function showTooltip(e
, content
) {
14 var tt
= document
.createElement("div");
15 tt
.className
= "CodeMirror-lint-tooltip";
16 tt
.appendChild(content
.cloneNode(true));
17 document
.body
.appendChild(tt
);
19 function position(e
) {
20 if (!tt
.parentNode
) return CodeMirror
.off(document
, "mousemove", position
);
21 tt
.style
.top
= Math
.max(0, e
.clientY
- tt
.offsetHeight
- 5) + "px";
22 tt
.style
.left
= (e
.clientX
+ 5) + "px";
24 CodeMirror
.on(document
, "mousemove", position
);
26 if (tt
.style
.opacity
!= null) tt
.style
.opacity
= 1;
30 if (elt
.parentNode
) elt
.parentNode
.removeChild(elt
);
32 function hideTooltip(tt
) {
33 if (!tt
.parentNode
) return;
34 if (tt
.style
.opacity
== null) rm(tt
);
36 setTimeout(function() { rm(tt
); }, 600);
39 function showTooltipFor(e
, content
, node
) {
40 var tooltip
= showTooltip(e
, content
);
42 CodeMirror
.off(node
, "mouseout", hide
);
43 if (tooltip
) { hideTooltip(tooltip
); tooltip
= null; }
45 var poll
= setInterval(function() {
46 if (tooltip
) for (var n
= node
;; n
= n
.parentNode
) {
47 if (n
== document
.body
) return;
48 if (!n
) { hide(); break; }
50 if (!tooltip
) return clearInterval(poll
);
52 CodeMirror
.on(node
, "mouseout", hide
);
55 function LintState(cm
, options
, hasGutter
) {
57 this.options
= options
;
59 this.hasGutter
= hasGutter
;
60 this.onMouseOver = function(e
) { onMouseOver(cm
, e
); };
63 function parseOptions(cm
, options
) {
64 if (options
instanceof Function
) return {getAnnotations
: options
};
65 if (!options
|| options
=== true) options
= {};
66 if (!options
.getAnnotations
) options
.getAnnotations
= cm
.getHelper(CodeMirror
.Pos(0, 0), "lint");
67 if (!options
.getAnnotations
) throw new Error("Required option 'getAnnotations' missing (lint addon)");
71 function clearMarks(cm
) {
72 var state
= cm
.state
.lint
;
73 if (state
.hasGutter
) cm
.clearGutter(GUTTER_ID
);
74 for (var i
= 0; i
< state
.marked
.length
; ++i
)
75 state
.marked
[i
].clear();
76 state
.marked
.length
= 0;
79 function makeMarker(labels
, severity
, multiple
, tooltips
) {
80 var marker
= document
.createElement("div"), inner
= marker
;
81 marker
.className
= "CodeMirror-lint-marker-" + severity
;
83 inner
= marker
.appendChild(document
.createElement("div"));
84 inner
.className
= "CodeMirror-lint-marker-multiple";
87 if (tooltips
!= false) CodeMirror
.on(inner
, "mouseover", function(e
) {
88 showTooltipFor(e
, labels
, inner
);
94 function getMaxSeverity(a
, b
) {
95 if (a
== "error") return a
;
99 function groupByLine(annotations
) {
101 for (var i
= 0; i
< annotations
.length
; ++i
) {
102 var ann
= annotations
[i
], line
= ann
.from.line
;
103 (lines
[line
] || (lines
[line
] = [])).push(ann
);
108 function annotationTooltip(ann
) {
109 var severity
= ann
.severity
;
110 if (!SEVERITIES
.test(severity
)) severity
= "error";
111 var tip
= document
.createElement("div");
112 tip
.className
= "CodeMirror-lint-message-" + severity
;
113 tip
.appendChild(document
.createTextNode(ann
.message
));
117 function startLinting(cm
) {
118 var state
= cm
.state
.lint
, options
= state
.options
;
120 options
.getAnnotations(cm
, updateLinting
, options
);
122 updateLinting(cm
, options
.getAnnotations(cm
.getValue(), options
.options
));
125 function updateLinting(cm
, annotationsNotSorted
) {
127 var state
= cm
.state
.lint
, options
= state
.options
;
129 var annotations
= groupByLine(annotationsNotSorted
);
131 for (var line
= 0; line
< annotations
.length
; ++line
) {
132 var anns
= annotations
[line
];
135 var maxSeverity
= null;
136 var tipLabel
= state
.hasGutter
&& document
.createDocumentFragment();
138 for (var i
= 0; i
< anns
.length
; ++i
) {
140 var severity
= ann
.severity
;
141 if (!SEVERITIES
.test(severity
)) severity
= "error";
142 maxSeverity
= getMaxSeverity(maxSeverity
, severity
);
144 if (options
.formatAnnotation
) ann
= options
.formatAnnotation(ann
);
145 if (state
.hasGutter
) tipLabel
.appendChild(annotationTooltip(ann
));
147 if (ann
.to
) state
.marked
.push(cm
.markText(ann
.from, ann
.to
, {
148 className
: "CodeMirror-lint-mark-" + severity
,
154 cm
.setGutterMarker(line
, GUTTER_ID
, makeMarker(tipLabel
, maxSeverity
, anns
.length
> 1,
155 state
.options
.tooltips
));
157 if (options
.onUpdateLinting
) options
.onUpdateLinting(annotationsNotSorted
, annotations
, cm
);
160 function onChange(cm
) {
161 var state
= cm
.state
.lint
;
162 clearTimeout(state
.timeout
);
163 state
.timeout
= setTimeout(function(){startLinting(cm
);}, state
.options
.delay
|| 500);
166 function popupSpanTooltip(ann
, e
) {
167 var target
= e
.target
|| e
.srcElement
;
168 showTooltipFor(e
, annotationTooltip(ann
), target
);
171 // When the mouseover fires, the cursor might not actually be over
172 // the character itself yet. These pairs of x,y offsets are used to
173 // probe a few nearby points when no suitable marked range is found.
174 var nearby
= [0, 0, 0, 5, 0, -5, 5, 0, -5, 0];
176 function onMouseOver(cm
, e
) {
177 if (!/\bCodeMirror-lint-mark-/.test((e
.target
|| e
.srcElement
).className
)) return;
178 for (var i
= 0; i
< nearby
.length
; i
+= 2) {
179 var spans
= cm
.findMarksAt(cm
.coordsChar({left
: e
.clientX
+ nearby
[i
],
180 top
: e
.clientY
+ nearby
[i
+ 1]}, "client"));
181 for (var j
= 0; j
< spans
.length
; ++j
) {
182 var span
= spans
[j
], ann
= span
.__annotation
;
183 if (ann
) return popupSpanTooltip(ann
, e
);
188 CodeMirror
.defineOption("lint", false, function(cm
, val
, old
) {
189 if (old
&& old
!= CodeMirror
.Init
) {
191 cm
.off("change", onChange
);
192 CodeMirror
.off(cm
.getWrapperElement(), "mouseover", cm
.state
.lint
.onMouseOver
);
193 delete cm
.state
.lint
;
197 var gutters
= cm
.getOption("gutters"), hasLintGutter
= false;
198 for (var i
= 0; i
< gutters
.length
; ++i
) if (gutters
[i
] == GUTTER_ID
) hasLintGutter
= true;
199 var state
= cm
.state
.lint
= new LintState(cm
, parseOptions(cm
, val
), hasLintGutter
);
200 cm
.on("change", onChange
);
201 if (state
.options
.tooltips
!= false)
202 CodeMirror
.on(cm
.getWrapperElement(), "mouseover", state
.onMouseOver
);