not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
7.1 KiB

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