closebrackets.js 5.4 KB

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