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