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

js/codemirror/addon/edit/closebrackets.js   F

Complexity

Total Complexity 85
Complexity/F 5.31

Size

Lines of Code 192
Function Count 16

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 0
nc 226492416
dl 0
loc 192
rs 1.9603
c 0
b 0
f 0
wmc 85
mnd 6
bc 35
fnc 16
bpm 2.1875
cpm 5.3125
noi 9

How to fix   Complexity   

Complexity

Complex classes like js/codemirror/addon/edit/closebrackets.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
  var defaults = {
13
    pairs: "()[]{}''\"\"",
14
    triples: "",
15
    explode: "[]{}"
16
  };
17
18
  var Pos = CodeMirror.Pos;
19
20
  CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
21
    if (old && old != CodeMirror.Init) {
22
      cm.removeKeyMap(keyMap);
23
      cm.state.closeBrackets = null;
24
    }
25
    if (val) {
26
      cm.state.closeBrackets = val;
27
      cm.addKeyMap(keyMap);
28
    }
29
  });
30
31
  function getOption(conf, name) {
32
    if (name == "pairs" && typeof conf == "string") return conf;
33
    if (typeof conf == "object" && conf[name] != null) return conf[name];
34
    return defaults[name];
35
  }
36
37
  var bind = defaults.pairs + "`";
38
  var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
39
  for (var i = 0; i < bind.length; i++)
40
    keyMap["'" + bind.charAt(i) + "'"] = handler(bind.charAt(i));
41
42
  function handler(ch) {
43
    return function(cm) { return handleChar(cm, ch); };
44
  }
45
46
  function getConfig(cm) {
47
    var deflt = cm.state.closeBrackets;
48
    if (!deflt) return null;
49
    var mode = cm.getModeAt(cm.getCursor());
50
    return mode.closeBrackets || deflt;
51
  }
52
53
  function handleBackspace(cm) {
54
    var conf = getConfig(cm);
55
    if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
56
57
    var pairs = getOption(conf, "pairs");
58
    var ranges = cm.listSelections();
59
    for (var i = 0; i < ranges.length; i++) {
60
      if (!ranges[i].empty()) return CodeMirror.Pass;
61
      var around = charsAround(cm, ranges[i].head);
62
      if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
63
    }
64
    for (var i = ranges.length - 1; i >= 0; i--) {
65
      var cur = ranges[i].head;
66
      cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), "+delete");
67
    }
68
  }
69
70
  function handleEnter(cm) {
71
    var conf = getConfig(cm);
72
    var explode = conf && getOption(conf, "explode");
73
    if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass;
74
75
    var ranges = cm.listSelections();
76
    for (var i = 0; i < ranges.length; i++) {
77
      if (!ranges[i].empty()) return CodeMirror.Pass;
78
      var around = charsAround(cm, ranges[i].head);
79
      if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
80
    }
81
    cm.operation(function() {
82
      cm.replaceSelection("\n\n", null);
83
      cm.execCommand("goCharLeft");
84
      ranges = cm.listSelections();
85
      for (var i = 0; i < ranges.length; i++) {
86
        var line = ranges[i].head.line;
87
        cm.indentLine(line, null, true);
88
        cm.indentLine(line + 1, null, true);
89
      }
90
    });
91
  }
92
93
  function contractSelection(sel) {
94
    var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0;
95
    return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)),
96
            head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1))};
97
  }
98
99
  function handleChar(cm, ch) {
100
    var conf = getConfig(cm);
101
    if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
102
103
    var pairs = getOption(conf, "pairs");
104
    var pos = pairs.indexOf(ch);
105
    if (pos == -1) return CodeMirror.Pass;
106
    var triples = getOption(conf, "triples");
107
108
    var identical = pairs.charAt(pos + 1) == ch;
109
    var ranges = cm.listSelections();
110
    var opening = pos % 2 == 0;
111
112
    var type;
113
    for (var i = 0; i < ranges.length; i++) {
114
      var range = ranges[i], cur = range.head, curType;
115
      var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
116
      if (opening && !range.empty()) {
117
        curType = "surround";
118
      } else if ((identical || !opening) && next == ch) {
119
        if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
120
          curType = "skipThree";
121
        else
122
          curType = "skip";
123
      } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
124
                 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch &&
125
                 (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != ch)) {
126
        curType = "addFour";
127
      } else if (identical) {
128
        if (!CodeMirror.isWordChar(next) && enteringString(cm, cur, ch)) curType = "both";
129
        else return CodeMirror.Pass;
130
      } else if (opening && (cm.getLine(cur.line).length == cur.ch ||
131
                             isClosingBracket(next, pairs) ||
132
                             /\s/.test(next))) {
133
        curType = "both";
134
      } else {
135
        return CodeMirror.Pass;
136
      }
137
      if (!type) type = curType;
138
      else if (type != curType) return CodeMirror.Pass;
139
    }
140
141
    var left = pos % 2 ? pairs.charAt(pos - 1) : ch;
142
    var right = pos % 2 ? ch : pairs.charAt(pos + 1);
143
    cm.operation(function() {
144
      if (type == "skip") {
145
        cm.execCommand("goCharRight");
146
      } else if (type == "skipThree") {
147
        for (var i = 0; i < 3; i++)
148
          cm.execCommand("goCharRight");
149
      } else if (type == "surround") {
150
        var sels = cm.getSelections();
151
        for (var i = 0; i < sels.length; i++)
152
          sels[i] = left + sels[i] + right;
153
        cm.replaceSelections(sels, "around");
154
        sels = cm.listSelections().slice();
155
        for (var i = 0; i < sels.length; i++)
156
          sels[i] = contractSelection(sels[i]);
157
        cm.setSelections(sels);
158
      } else if (type == "both") {
159
        cm.replaceSelection(left + right, null);
160
        cm.triggerElectric(left + right);
161
        cm.execCommand("goCharLeft");
162
      } else if (type == "addFour") {
163
        cm.replaceSelection(left + left + left + left, "before");
164
        cm.execCommand("goCharRight");
165
      }
166
    });
167
  }
168
169
  function isClosingBracket(ch, pairs) {
170
    var pos = pairs.lastIndexOf(ch);
171
    return pos > -1 && pos % 2 == 1;
172
  }
173
174
  function charsAround(cm, pos) {
175
    var str = cm.getRange(Pos(pos.line, pos.ch - 1),
176
                          Pos(pos.line, pos.ch + 1));
177
    return str.length == 2 ? str : null;
178
  }
179
180
  // Project the token type that will exists after the given char is
181
  // typed, and use it to determine whether it would cause the start
182
  // of a string token.
183
  function enteringString(cm, pos, ch) {
184
    var line = cm.getLine(pos.line);
185
    var token = cm.getTokenAt(pos);
186
    if (/\bstring2?\b/.test(token.type)) return false;
187
    var stream = new CodeMirror.StringStream(line.slice(0, pos.ch) + ch + line.slice(pos.ch), 4);
188
    stream.pos = stream.start = token.start;
189
    for (;;) {
190
      var type1 = cm.getMode().token(stream, token.state);
191
      if (stream.pos >= pos.ch + 1) return /\bstring2?\b/.test(type1);
192
      stream.start = stream.pos;
193
    }
194
  }
195
});
196