github.com/elliott5/community@v0.14.1-0.20160709191136-823126fb026a/app/public/codemirror/addon/edit/closebrackets.js (about)

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