closebrackets.js 6.2 KB

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