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 DEFAULT_BRACKETS = "()[]{}''\"\""; |
---|
13 | var DEFAULT_TRIPLES = "'\""; |
---|
14 | var DEFAULT_EXPLODE_ON_ENTER = "[]{}"; |
---|
15 | var SPACE_CHAR_REGEX = /\s/; |
---|
16 | |
---|
17 | var Pos = CodeMirror.Pos; |
---|
18 | |
---|
19 | CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) { |
---|
20 | if (old != CodeMirror.Init && old) |
---|
21 | cm.removeKeyMap("autoCloseBrackets"); |
---|
22 | if (!val) return; |
---|
23 | var pairs = DEFAULT_BRACKETS, triples = DEFAULT_TRIPLES, explode = DEFAULT_EXPLODE_ON_ENTER; |
---|
24 | if (typeof val == "string") pairs = val; |
---|
25 | else if (typeof val == "object") { |
---|
26 | if (val.pairs != null) pairs = val.pairs; |
---|
27 | if (val.triples != null) triples = val.triples; |
---|
28 | if (val.explode != null) explode = val.explode; |
---|
29 | } |
---|
30 | var map = buildKeymap(pairs, triples); |
---|
31 | if (explode) map.Enter = buildExplodeHandler(explode); |
---|
32 | cm.addKeyMap(map); |
---|
33 | }); |
---|
34 | |
---|
35 | function charsAround(cm, pos) { |
---|
36 | var str = cm.getRange(Pos(pos.line, pos.ch - 1), |
---|
37 | Pos(pos.line, pos.ch + 1)); |
---|
38 | return str.length == 2 ? str : null; |
---|
39 | } |
---|
40 | |
---|
41 | // Project the token type that will exists after the given char is |
---|
42 | // typed, and use it to determine whether it would cause the start |
---|
43 | // of a string token. |
---|
44 | function enteringString(cm, pos, ch) { |
---|
45 | var line = cm.getLine(pos.line); |
---|
46 | var token = cm.getTokenAt(pos); |
---|
47 | if (/\bstring2?\b/.test(token.type)) return false; |
---|
48 | var stream = new CodeMirror.StringStream(line.slice(0, pos.ch) + ch + line.slice(pos.ch), 4); |
---|
49 | stream.pos = stream.start = token.start; |
---|
50 | for (;;) { |
---|
51 | var type1 = cm.getMode().token(stream, token.state); |
---|
52 | if (stream.pos >= pos.ch + 1) return /\bstring2?\b/.test(type1); |
---|
53 | stream.start = stream.pos; |
---|
54 | } |
---|
55 | } |
---|
56 | |
---|
57 | function buildKeymap(pairs, triples) { |
---|
58 | var map = { |
---|
59 | name : "autoCloseBrackets", |
---|
60 | Backspace: function(cm) { |
---|
61 | if (cm.getOption("disableInput")) return CodeMirror.Pass; |
---|
62 | var ranges = cm.listSelections(); |
---|
63 | for (var i = 0; i < ranges.length; i++) { |
---|
64 | if (!ranges[i].empty()) return CodeMirror.Pass; |
---|
65 | var around = charsAround(cm, ranges[i].head); |
---|
66 | if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; |
---|
67 | } |
---|
68 | for (var i = ranges.length - 1; i >= 0; i--) { |
---|
69 | var cur = ranges[i].head; |
---|
70 | cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1)); |
---|
71 | } |
---|
72 | } |
---|
73 | }; |
---|
74 | var closingBrackets = ""; |
---|
75 | for (var i = 0; i < pairs.length; i += 2) (function(left, right) { |
---|
76 | closingBrackets += right; |
---|
77 | map["'" + left + "'"] = function(cm) { |
---|
78 | if (cm.getOption("disableInput")) return CodeMirror.Pass; |
---|
79 | var ranges = cm.listSelections(), type, next; |
---|
80 | for (var i = 0; i < ranges.length; i++) { |
---|
81 | var range = ranges[i], cur = range.head, curType; |
---|
82 | var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1)); |
---|
83 | if (!range.empty()) { |
---|
84 | curType = "surround"; |
---|
85 | } else if (left == right && next == right) { |
---|
86 | if (cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == left + left + left) |
---|
87 | curType = "skipThree"; |
---|
88 | else |
---|
89 | curType = "skip"; |
---|
90 | } else if (left == right && cur.ch > 1 && triples.indexOf(left) >= 0 && |
---|
91 | cm.getRange(Pos(cur.line, cur.ch - 2), cur) == left + left && |
---|
92 | (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != left)) { |
---|
93 | curType = "addFour"; |
---|
94 | } else if (left == '"' || left == "'") { |
---|
95 | if (!CodeMirror.isWordChar(next) && enteringString(cm, cur, left)) curType = "both"; |
---|
96 | else return CodeMirror.Pass; |
---|
97 | } else if (cm.getLine(cur.line).length == cur.ch || closingBrackets.indexOf(next) >= 0 || SPACE_CHAR_REGEX.test(next)) { |
---|
98 | curType = "both"; |
---|
99 | } else { |
---|
100 | return CodeMirror.Pass; |
---|
101 | } |
---|
102 | if (!type) type = curType; |
---|
103 | else if (type != curType) return CodeMirror.Pass; |
---|
104 | } |
---|
105 | |
---|
106 | cm.operation(function() { |
---|
107 | if (type == "skip") { |
---|
108 | cm.execCommand("goCharRight"); |
---|
109 | } else if (type == "skipThree") { |
---|
110 | for (var i = 0; i < 3; i++) |
---|
111 | cm.execCommand("goCharRight"); |
---|
112 | } else if (type == "surround") { |
---|
113 | var sels = cm.getSelections(); |
---|
114 | for (var i = 0; i < sels.length; i++) |
---|
115 | sels[i] = left + sels[i] + right; |
---|
116 | cm.replaceSelections(sels, "around"); |
---|
117 | } else if (type == "both") { |
---|
118 | cm.replaceSelection(left + right, null); |
---|
119 | cm.execCommand("goCharLeft"); |
---|
120 | } else if (type == "addFour") { |
---|
121 | cm.replaceSelection(left + left + left + left, "before"); |
---|
122 | cm.execCommand("goCharRight"); |
---|
123 | } |
---|
124 | }); |
---|
125 | }; |
---|
126 | if (left != right) map["'" + right + "'"] = function(cm) { |
---|
127 | var ranges = cm.listSelections(); |
---|
128 | for (var i = 0; i < ranges.length; i++) { |
---|
129 | var range = ranges[i]; |
---|
130 | if (!range.empty() || |
---|
131 | cm.getRange(range.head, Pos(range.head.line, range.head.ch + 1)) != right) |
---|
132 | return CodeMirror.Pass; |
---|
133 | } |
---|
134 | cm.execCommand("goCharRight"); |
---|
135 | }; |
---|
136 | })(pairs.charAt(i), pairs.charAt(i + 1)); |
---|
137 | return map; |
---|
138 | } |
---|
139 | |
---|
140 | function buildExplodeHandler(pairs) { |
---|
141 | return function(cm) { |
---|
142 | if (cm.getOption("disableInput")) return CodeMirror.Pass; |
---|
143 | var ranges = cm.listSelections(); |
---|
144 | for (var i = 0; i < ranges.length; i++) { |
---|
145 | if (!ranges[i].empty()) return CodeMirror.Pass; |
---|
146 | var around = charsAround(cm, ranges[i].head); |
---|
147 | if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; |
---|
148 | } |
---|
149 | cm.operation(function() { |
---|
150 | cm.replaceSelection("\n\n", null); |
---|
151 | cm.execCommand("goCharLeft"); |
---|
152 | ranges = cm.listSelections(); |
---|
153 | for (var i = 0; i < ranges.length; i++) { |
---|
154 | var line = ranges[i].head.line; |
---|
155 | cm.indentLine(line, null, true); |
---|
156 | cm.indentLine(line + 1, null, true); |
---|
157 | } |
---|
158 | }); |
---|
159 | }; |
---|
160 | } |
---|
161 | }); |
---|