Completed
Push — master ( 2177e8...5b06fe )
by Felipe
12s
created

js/codemirror/addon/lint/lint.js   F

Complexity

Total Complexity 87
Complexity/F 2.9

Size

Lines of Code 236
Function Count 30

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 0
nc 16384
dl 0
loc 236
rs 3.12
c 0
b 0
f 0
wmc 87
mnd 3
bc 43
fnc 30
bpm 1.4333
cpm 2.9
noi 4

How to fix   Complexity   

Complexity

Complex classes like js/codemirror/addon/lint/lint.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
// CodeMirror, copyright (c) by Marijn Haverbeke and others
2
// Distributed under an MIT license: http://codemirror.net/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
15
  function showTooltip(e, content) {
16
    var tt = document.createElement("div");
17
    tt.className = "CodeMirror-lint-tooltip";
18
    tt.appendChild(content.cloneNode(true));
19
    document.body.appendChild(tt);
20
21
    function position(e) {
22
      if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position);
23
      tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px";
24
      tt.style.left = (e.clientX + 5) + "px";
25
    }
26
    CodeMirror.on(document, "mousemove", position);
27
    position(e);
28
    if (tt.style.opacity != null) tt.style.opacity = 1;
29
    return tt;
30
  }
31
  function rm(elt) {
32
    if (elt.parentNode) elt.parentNode.removeChild(elt);
33
  }
34
  function hideTooltip(tt) {
35
    if (!tt.parentNode) return;
36
    if (tt.style.opacity == null) rm(tt);
37
    tt.style.opacity = 0;
38
    setTimeout(function() { rm(tt); }, 600);
39
  }
40
41
  function showTooltipFor(e, content, node) {
42
    var tooltip = showTooltip(e, content);
43
    function hide() {
44
      CodeMirror.off(node, "mouseout", hide);
45
      if (tooltip) { hideTooltip(tooltip); tooltip = null; }
46
    }
47
    var poll = setInterval(function() {
48
      if (tooltip) for (var n = node;; n = n.parentNode) {
49
        if (n && n.nodeType == 11) n = n.host;
50
        if (n == document.body) return;
51
        if (!n) { hide(); break; }
52
      }
53
      if (!tooltip) return clearInterval(poll);
54
    }, 400);
55
    CodeMirror.on(node, "mouseout", hide);
56
  }
57
58
  function LintState(cm, options, hasGutter) {
59
    this.marked = [];
60
    this.options = options;
61
    this.timeout = null;
62
    this.hasGutter = hasGutter;
63
    this.onMouseOver = function(e) { onMouseOver(cm, e); };
64
    this.waitingFor = 0
65
  }
66
67
  function parseOptions(_cm, options) {
68
    if (options instanceof Function) return {getAnnotations: options};
69
    if (!options || options === true) options = {};
70
    return options;
71
  }
72
73
  function clearMarks(cm) {
74
    var state = cm.state.lint;
75
    if (state.hasGutter) cm.clearGutter(GUTTER_ID);
76
    for (var i = 0; i < state.marked.length; ++i)
77
      state.marked[i].clear();
78
    state.marked.length = 0;
79
  }
80
81
  function makeMarker(labels, severity, multiple, tooltips) {
82
    var marker = document.createElement("div"), inner = marker;
83
    marker.className = "CodeMirror-lint-marker-" + severity;
84
    if (multiple) {
85
      inner = marker.appendChild(document.createElement("div"));
86
      inner.className = "CodeMirror-lint-marker-multiple";
87
    }
88
89
    if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) {
90
      showTooltipFor(e, labels, inner);
91
    });
92
93
    return marker;
94
  }
95
96
  function getMaxSeverity(a, b) {
97
    if (a == "error") return a;
98
    else return b;
99
  }
100
101
  function groupByLine(annotations) {
102
    var lines = [];
103
    for (var i = 0; i < annotations.length; ++i) {
104
      var ann = annotations[i], line = ann.from.line;
105
      (lines[line] || (lines[line] = [])).push(ann);
106
    }
107
    return lines;
108
  }
109
110
  function annotationTooltip(ann) {
111
    var severity = ann.severity;
112
    if (!severity) severity = "error";
113
    var tip = document.createElement("div");
114
    tip.className = "CodeMirror-lint-message-" + severity;
115
    tip.appendChild(document.createTextNode(ann.message));
116
    return tip;
117
  }
118
119
  function lintAsync(cm, getAnnotations, passOptions) {
120
    var state = cm.state.lint
121
    var id = ++state.waitingFor
122
    function abort() {
123
      id = -1
124
      cm.off("change", abort)
125
    }
126
    cm.on("change", abort)
127
    getAnnotations(cm.getValue(), function(annotations, arg2) {
128
      cm.off("change", abort)
129
      if (state.waitingFor != id) return
130
      if (arg2 && annotations instanceof CodeMirror) annotations = arg2
131
      updateLinting(cm, annotations)
132
    }, passOptions, cm);
133
  }
134
135
  function startLinting(cm) {
136
    var state = cm.state.lint, options = state.options;
137
    var passOptions = options.options || options; // Support deprecated passing of `options` property in options
138
    var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint");
139
    if (!getAnnotations) return;
140
    if (options.async || getAnnotations.async) {
141
      lintAsync(cm, getAnnotations, passOptions)
142
    } else {
143
      updateLinting(cm, getAnnotations(cm.getValue(), passOptions, cm));
144
    }
145
  }
146
147
  function updateLinting(cm, annotationsNotSorted) {
148
    clearMarks(cm);
149
    var state = cm.state.lint, options = state.options;
150
151
    var annotations = groupByLine(annotationsNotSorted);
152
153
    for (var line = 0; line < annotations.length; ++line) {
154
      var anns = annotations[line];
155
      if (!anns) continue;
156
157
      var maxSeverity = null;
158
      var tipLabel = state.hasGutter && document.createDocumentFragment();
159
160
      for (var i = 0; i < anns.length; ++i) {
161
        var ann = anns[i];
162
        var severity = ann.severity;
163
        if (!severity) severity = "error";
164
        maxSeverity = getMaxSeverity(maxSeverity, severity);
165
166
        if (options.formatAnnotation) ann = options.formatAnnotation(ann);
167
        if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann));
168
169
        if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, {
170
          className: "CodeMirror-lint-mark-" + severity,
171
          __annotation: ann
172
        }));
173
      }
174
175
      if (state.hasGutter)
176
        cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1,
177
                                                       state.options.tooltips));
178
    }
179
    if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
180
  }
181
182
  function onChange(cm) {
183
    var state = cm.state.lint;
184
    if (!state) return;
185
    clearTimeout(state.timeout);
186
    state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
187
  }
188
189
  function popupTooltips(annotations, e) {
190
    var target = e.target || e.srcElement;
191
    var tooltip = document.createDocumentFragment();
192
    for (var i = 0; i < annotations.length; i++) {
193
      var ann = annotations[i];
194
      tooltip.appendChild(annotationTooltip(ann));
195
    }
196
    showTooltipFor(e, tooltip, target);
197
  }
198
199
  function onMouseOver(cm, e) {
200
    var target = e.target || e.srcElement;
201
    if (!/\bCodeMirror-lint-mark-/.test(target.className)) return;
202
    var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2;
203
    var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client"));
204
205
    var annotations = [];
206
    for (var i = 0; i < spans.length; ++i) {
207
      var ann = spans[i].__annotation;
208
      if (ann) annotations.push(ann);
209
    }
210
    if (annotations.length) popupTooltips(annotations, e);
211
  }
212
213
  CodeMirror.defineOption("lint", false, function(cm, val, old) {
214
    if (old && old != CodeMirror.Init) {
215
      clearMarks(cm);
216
      if (cm.state.lint.options.lintOnChange !== false)
217
        cm.off("change", onChange);
218
      CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver);
219
      clearTimeout(cm.state.lint.timeout);
220
      delete cm.state.lint;
221
    }
222
223
    if (val) {
224
      var gutters = cm.getOption("gutters"), hasLintGutter = false;
225
      for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true;
226
      var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter);
227
      if (state.options.lintOnChange !== false)
228
        cm.on("change", onChange);
229
      if (state.options.tooltips != false)
230
        CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver);
231
232
      startLinting(cm);
233
    }
234
  });
235
236
  CodeMirror.defineExtension("performLint", function() {
237
    if (this.state.lint) startLinting(this);
238
  });
239
});
240