openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

closebrackets.js (6899B)


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