Implement CSS `display: flow-root` (modern clearfix)
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / External / CodeMirror / closebrackets.js
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 (identical && stringStartsAfter(cm, cur))
120           curType = "both";
121         else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
122           curType = "skipThree";
123         else
124           curType = "skip";
125       } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
126                  cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch &&
127                  (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != ch)) {
128         curType = "addFour";
129       } else if (identical) {
130         if (!CodeMirror.isWordChar(next) && enteringString(cm, cur, ch)) curType = "both";
131         else return CodeMirror.Pass;
132       } else if (opening && (cm.getLine(cur.line).length == cur.ch ||
133                              isClosingBracket(next, pairs) ||
134                              /\s/.test(next))) {
135         curType = "both";
136       } else {
137         return CodeMirror.Pass;
138       }
139       if (!type) type = curType;
140       else if (type != curType) return CodeMirror.Pass;
141     }
142
143     var left = pos % 2 ? pairs.charAt(pos - 1) : ch;
144     var right = pos % 2 ? ch : pairs.charAt(pos + 1);
145     cm.operation(function() {
146       if (type == "skip") {
147         cm.execCommand("goCharRight");
148       } else if (type == "skipThree") {
149         for (var i = 0; i < 3; i++)
150           cm.execCommand("goCharRight");
151       } else if (type == "surround") {
152         var sels = cm.getSelections();
153         for (var i = 0; i < sels.length; i++)
154           sels[i] = left + sels[i] + right;
155         cm.replaceSelections(sels, "around");
156         sels = cm.listSelections().slice();
157         for (var i = 0; i < sels.length; i++)
158           sels[i] = contractSelection(sels[i]);
159         cm.setSelections(sels);
160       } else if (type == "both") {
161         cm.replaceSelection(left + right, null);
162         cm.triggerElectric(left + right);
163         cm.execCommand("goCharLeft");
164       } else if (type == "addFour") {
165         cm.replaceSelection(left + left + left + left, "before");
166         cm.execCommand("goCharRight");
167       }
168     });
169   }
170
171   function isClosingBracket(ch, pairs) {
172     var pos = pairs.lastIndexOf(ch);
173     return pos > -1 && pos % 2 == 1;
174   }
175
176   function charsAround(cm, pos) {
177     var str = cm.getRange(Pos(pos.line, pos.ch - 1),
178                           Pos(pos.line, pos.ch + 1));
179     return str.length == 2 ? str : null;
180   }
181
182   // Project the token type that will exists after the given char is
183   // typed, and use it to determine whether it would cause the start
184   // of a string token.
185   function enteringString(cm, pos, ch) {
186     var line = cm.getLine(pos.line);
187     var token = cm.getTokenAt(pos);
188     if (/\bstring2?\b/.test(token.type)) return false;
189     var stream = new CodeMirror.StringStream(line.slice(0, pos.ch) + ch + line.slice(pos.ch), 4);
190     stream.pos = stream.start = token.start;
191     for (;;) {
192       var type1 = cm.getMode().token(stream, token.state);
193       if (stream.pos >= pos.ch + 1) return /\bstring2?\b/.test(type1);
194       stream.start = stream.pos;
195     }
196   }
197
198   function stringStartsAfter(cm, pos) {
199     var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
200     return /\bstring/.test(token.type) && token.start == pos.ch
201   }
202 });