closebrackets.js 6.3 KB

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