github.com/elliott5/community@v0.14.1-0.20160709191136-823126fb026a/app/public/codemirror/addon/edit/closebrackets.js (about) 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)); 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, next; 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 });