closebrackets.js 4.7 KB

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