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