openrat-cms

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

sublime.js (25450B)


      1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
      2 // Distributed under an MIT license: http://codemirror.net/LICENSE
      3 
      4 // A rough approximation of Sublime Text's keybindings
      5 // Depends on addon/search/searchcursor.js and optionally addon/dialog/dialogs.js
      6 
      7 (function(mod) {
      8   if (typeof exports == "object" && typeof module == "object") // CommonJS
      9     mod(require("../lib/codemirror"), require("../addon/search/searchcursor"), require("../addon/edit/matchbrackets"));
     10   else if (typeof define == "function" && define.amd) // AMD
     11     define(["../lib/codemirror", "../addon/search/searchcursor", "../addon/edit/matchbrackets"], mod);
     12   else // Plain browser env
     13     mod(CodeMirror);
     14 })(function(CodeMirror) {
     15   "use strict";
     16 
     17   var cmds = CodeMirror.commands;
     18   var Pos = CodeMirror.Pos;
     19 
     20   // This is not exactly Sublime's algorithm. I couldn't make heads or tails of that.
     21   function findPosSubword(doc, start, dir) {
     22     if (dir < 0 && start.ch == 0) return doc.clipPos(Pos(start.line - 1));
     23     var line = doc.getLine(start.line);
     24     if (dir > 0 && start.ch >= line.length) return doc.clipPos(Pos(start.line + 1, 0));
     25     var state = "start", type;
     26     for (var pos = start.ch, e = dir < 0 ? 0 : line.length, i = 0; pos != e; pos += dir, i++) {
     27       var next = line.charAt(dir < 0 ? pos - 1 : pos);
     28       var cat = next != "_" && CodeMirror.isWordChar(next) ? "w" : "o";
     29       if (cat == "w" && next.toUpperCase() == next) cat = "W";
     30       if (state == "start") {
     31         if (cat != "o") { state = "in"; type = cat; }
     32       } else if (state == "in") {
     33         if (type != cat) {
     34           if (type == "w" && cat == "W" && dir < 0) pos--;
     35           if (type == "W" && cat == "w" && dir > 0) { type = "w"; continue; }
     36           break;
     37         }
     38       }
     39     }
     40     return Pos(start.line, pos);
     41   }
     42 
     43   function moveSubword(cm, dir) {
     44     cm.extendSelectionsBy(function(range) {
     45       if (cm.display.shift || cm.doc.extend || range.empty())
     46         return findPosSubword(cm.doc, range.head, dir);
     47       else
     48         return dir < 0 ? range.from() : range.to();
     49     });
     50   }
     51 
     52   cmds.goSubwordLeft = function(cm) { moveSubword(cm, -1); };
     53   cmds.goSubwordRight = function(cm) { moveSubword(cm, 1); };
     54 
     55   cmds.scrollLineUp = function(cm) {
     56     var info = cm.getScrollInfo();
     57     if (!cm.somethingSelected()) {
     58       var visibleBottomLine = cm.lineAtHeight(info.top + info.clientHeight, "local");
     59       if (cm.getCursor().line >= visibleBottomLine)
     60         cm.execCommand("goLineUp");
     61     }
     62     cm.scrollTo(null, info.top - cm.defaultTextHeight());
     63   };
     64   cmds.scrollLineDown = function(cm) {
     65     var info = cm.getScrollInfo();
     66     if (!cm.somethingSelected()) {
     67       var visibleTopLine = cm.lineAtHeight(info.top, "local")+1;
     68       if (cm.getCursor().line <= visibleTopLine)
     69         cm.execCommand("goLineDown");
     70     }
     71     cm.scrollTo(null, info.top + cm.defaultTextHeight());
     72   };
     73 
     74   cmds.splitSelectionByLine = function(cm) {
     75     var ranges = cm.listSelections(), lineRanges = [];
     76     for (var i = 0; i < ranges.length; i++) {
     77       var from = ranges[i].from(), to = ranges[i].to();
     78       for (var line = from.line; line <= to.line; ++line)
     79         if (!(to.line > from.line && line == to.line && to.ch == 0))
     80           lineRanges.push({anchor: line == from.line ? from : Pos(line, 0),
     81                            head: line == to.line ? to : Pos(line)});
     82     }
     83     cm.setSelections(lineRanges, 0);
     84   };
     85 
     86   cmds.singleSelectionTop = function(cm) {
     87     var range = cm.listSelections()[0];
     88     cm.setSelection(range.anchor, range.head, {scroll: false});
     89   };
     90 
     91   cmds.selectLine = function(cm) {
     92     var ranges = cm.listSelections(), extended = [];
     93     for (var i = 0; i < ranges.length; i++) {
     94       var range = ranges[i];
     95       extended.push({anchor: Pos(range.from().line, 0),
     96                      head: Pos(range.to().line + 1, 0)});
     97     }
     98     cm.setSelections(extended);
     99   };
    100 
    101   function insertLine(cm, above) {
    102     if (cm.isReadOnly()) return CodeMirror.Pass
    103     cm.operation(function() {
    104       var len = cm.listSelections().length, newSelection = [], last = -1;
    105       for (var i = 0; i < len; i++) {
    106         var head = cm.listSelections()[i].head;
    107         if (head.line <= last) continue;
    108         var at = Pos(head.line + (above ? 0 : 1), 0);
    109         cm.replaceRange("\n", at, null, "+insertLine");
    110         cm.indentLine(at.line, null, true);
    111         newSelection.push({head: at, anchor: at});
    112         last = head.line + 1;
    113       }
    114       cm.setSelections(newSelection);
    115     });
    116     cm.execCommand("indentAuto");
    117   }
    118 
    119   cmds.insertLineAfter = function(cm) { return insertLine(cm, false); };
    120 
    121   cmds.insertLineBefore = function(cm) { return insertLine(cm, true); };
    122 
    123   function wordAt(cm, pos) {
    124     var start = pos.ch, end = start, line = cm.getLine(pos.line);
    125     while (start && CodeMirror.isWordChar(line.charAt(start - 1))) --start;
    126     while (end < line.length && CodeMirror.isWordChar(line.charAt(end))) ++end;
    127     return {from: Pos(pos.line, start), to: Pos(pos.line, end), word: line.slice(start, end)};
    128   }
    129 
    130   cmds.selectNextOccurrence = function(cm) {
    131     var from = cm.getCursor("from"), to = cm.getCursor("to");
    132     var fullWord = cm.state.sublimeFindFullWord == cm.doc.sel;
    133     if (CodeMirror.cmpPos(from, to) == 0) {
    134       var word = wordAt(cm, from);
    135       if (!word.word) return;
    136       cm.setSelection(word.from, word.to);
    137       fullWord = true;
    138     } else {
    139       var text = cm.getRange(from, to);
    140       var query = fullWord ? new RegExp("\\b" + text + "\\b") : text;
    141       var cur = cm.getSearchCursor(query, to);
    142       var found = cur.findNext();
    143       if (!found) {
    144         cur = cm.getSearchCursor(query, Pos(cm.firstLine(), 0));
    145         found = cur.findNext();
    146       }
    147       if (!found || isSelectedRange(cm.listSelections(), cur.from(), cur.to()))
    148         return CodeMirror.Pass
    149       cm.addSelection(cur.from(), cur.to());
    150     }
    151     if (fullWord)
    152       cm.state.sublimeFindFullWord = cm.doc.sel;
    153   };
    154 
    155   function addCursorToSelection(cm, dir) {
    156     var ranges = cm.listSelections(), newRanges = [];
    157     for (var i = 0; i < ranges.length; i++) {
    158       var range = ranges[i];
    159       var newAnchor = cm.findPosV(range.anchor, dir, "line");
    160       var newHead = cm.findPosV(range.head, dir, "line");
    161       var newRange = {anchor: newAnchor, head: newHead};
    162       newRanges.push(range);
    163       newRanges.push(newRange);
    164     }
    165     cm.setSelections(newRanges);
    166   }
    167   cmds.addCursorToPrevLine = function(cm) { addCursorToSelection(cm, -1); };
    168   cmds.addCursorToNextLine = function(cm) { addCursorToSelection(cm, 1); };
    169 
    170   function isSelectedRange(ranges, from, to) {
    171     for (var i = 0; i < ranges.length; i++)
    172       if (ranges[i].from() == from && ranges[i].to() == to) return true
    173     return false
    174   }
    175 
    176   var mirror = "(){}[]";
    177   function selectBetweenBrackets(cm) {
    178     var ranges = cm.listSelections(), newRanges = []
    179     for (var i = 0; i < ranges.length; i++) {
    180       var range = ranges[i], pos = range.head, opening = cm.scanForBracket(pos, -1);
    181       if (!opening) return false;
    182       for (;;) {
    183         var closing = cm.scanForBracket(pos, 1);
    184         if (!closing) return false;
    185         if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) {
    186           newRanges.push({anchor: Pos(opening.pos.line, opening.pos.ch + 1),
    187                           head: closing.pos});
    188           break;
    189         }
    190         pos = Pos(closing.pos.line, closing.pos.ch + 1);
    191       }
    192     }
    193     cm.setSelections(newRanges);
    194     return true;
    195   }
    196 
    197   cmds.selectScope = function(cm) {
    198     selectBetweenBrackets(cm) || cm.execCommand("selectAll");
    199   };
    200   cmds.selectBetweenBrackets = function(cm) {
    201     if (!selectBetweenBrackets(cm)) return CodeMirror.Pass;
    202   };
    203 
    204   cmds.goToBracket = function(cm) {
    205     cm.extendSelectionsBy(function(range) {
    206       var next = cm.scanForBracket(range.head, 1);
    207       if (next && CodeMirror.cmpPos(next.pos, range.head) != 0) return next.pos;
    208       var prev = cm.scanForBracket(range.head, -1);
    209       return prev && Pos(prev.pos.line, prev.pos.ch + 1) || range.head;
    210     });
    211   };
    212 
    213   cmds.swapLineUp = function(cm) {
    214     if (cm.isReadOnly()) return CodeMirror.Pass
    215     var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1, newSels = [];
    216     for (var i = 0; i < ranges.length; i++) {
    217       var range = ranges[i], from = range.from().line - 1, to = range.to().line;
    218       newSels.push({anchor: Pos(range.anchor.line - 1, range.anchor.ch),
    219                     head: Pos(range.head.line - 1, range.head.ch)});
    220       if (range.to().ch == 0 && !range.empty()) --to;
    221       if (from > at) linesToMove.push(from, to);
    222       else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
    223       at = to;
    224     }
    225     cm.operation(function() {
    226       for (var i = 0; i < linesToMove.length; i += 2) {
    227         var from = linesToMove[i], to = linesToMove[i + 1];
    228         var line = cm.getLine(from);
    229         cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
    230         if (to > cm.lastLine())
    231           cm.replaceRange("\n" + line, Pos(cm.lastLine()), null, "+swapLine");
    232         else
    233           cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
    234       }
    235       cm.setSelections(newSels);
    236       cm.scrollIntoView();
    237     });
    238   };
    239 
    240   cmds.swapLineDown = function(cm) {
    241     if (cm.isReadOnly()) return CodeMirror.Pass
    242     var ranges = cm.listSelections(), linesToMove = [], at = cm.lastLine() + 1;
    243     for (var i = ranges.length - 1; i >= 0; i--) {
    244       var range = ranges[i], from = range.to().line + 1, to = range.from().line;
    245       if (range.to().ch == 0 && !range.empty()) from--;
    246       if (from < at) linesToMove.push(from, to);
    247       else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
    248       at = to;
    249     }
    250     cm.operation(function() {
    251       for (var i = linesToMove.length - 2; i >= 0; i -= 2) {
    252         var from = linesToMove[i], to = linesToMove[i + 1];
    253         var line = cm.getLine(from);
    254         if (from == cm.lastLine())
    255           cm.replaceRange("", Pos(from - 1), Pos(from), "+swapLine");
    256         else
    257           cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
    258         cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
    259       }
    260       cm.scrollIntoView();
    261     });
    262   };
    263 
    264   cmds.toggleCommentIndented = function(cm) {
    265     cm.toggleComment({ indent: true });
    266   }
    267 
    268   cmds.joinLines = function(cm) {
    269     var ranges = cm.listSelections(), joined = [];
    270     for (var i = 0; i < ranges.length; i++) {
    271       var range = ranges[i], from = range.from();
    272       var start = from.line, end = range.to().line;
    273       while (i < ranges.length - 1 && ranges[i + 1].from().line == end)
    274         end = ranges[++i].to().line;
    275       joined.push({start: start, end: end, anchor: !range.empty() && from});
    276     }
    277     cm.operation(function() {
    278       var offset = 0, ranges = [];
    279       for (var i = 0; i < joined.length; i++) {
    280         var obj = joined[i];
    281         var anchor = obj.anchor && Pos(obj.anchor.line - offset, obj.anchor.ch), head;
    282         for (var line = obj.start; line <= obj.end; line++) {
    283           var actual = line - offset;
    284           if (line == obj.end) head = Pos(actual, cm.getLine(actual).length + 1);
    285           if (actual < cm.lastLine()) {
    286             cm.replaceRange(" ", Pos(actual), Pos(actual + 1, /^\s*/.exec(cm.getLine(actual + 1))[0].length));
    287             ++offset;
    288           }
    289         }
    290         ranges.push({anchor: anchor || head, head: head});
    291       }
    292       cm.setSelections(ranges, 0);
    293     });
    294   };
    295 
    296   cmds.duplicateLine = function(cm) {
    297     cm.operation(function() {
    298       var rangeCount = cm.listSelections().length;
    299       for (var i = 0; i < rangeCount; i++) {
    300         var range = cm.listSelections()[i];
    301         if (range.empty())
    302           cm.replaceRange(cm.getLine(range.head.line) + "\n", Pos(range.head.line, 0));
    303         else
    304           cm.replaceRange(cm.getRange(range.from(), range.to()), range.from());
    305       }
    306       cm.scrollIntoView();
    307     });
    308   };
    309 
    310 
    311   function sortLines(cm, caseSensitive) {
    312     if (cm.isReadOnly()) return CodeMirror.Pass
    313     var ranges = cm.listSelections(), toSort = [], selected;
    314     for (var i = 0; i < ranges.length; i++) {
    315       var range = ranges[i];
    316       if (range.empty()) continue;
    317       var from = range.from().line, to = range.to().line;
    318       while (i < ranges.length - 1 && ranges[i + 1].from().line == to)
    319         to = ranges[++i].to().line;
    320       if (!ranges[i].to().ch) to--;
    321       toSort.push(from, to);
    322     }
    323     if (toSort.length) selected = true;
    324     else toSort.push(cm.firstLine(), cm.lastLine());
    325 
    326     cm.operation(function() {
    327       var ranges = [];
    328       for (var i = 0; i < toSort.length; i += 2) {
    329         var from = toSort[i], to = toSort[i + 1];
    330         var start = Pos(from, 0), end = Pos(to);
    331         var lines = cm.getRange(start, end, false);
    332         if (caseSensitive)
    333           lines.sort();
    334         else
    335           lines.sort(function(a, b) {
    336             var au = a.toUpperCase(), bu = b.toUpperCase();
    337             if (au != bu) { a = au; b = bu; }
    338             return a < b ? -1 : a == b ? 0 : 1;
    339           });
    340         cm.replaceRange(lines, start, end);
    341         if (selected) ranges.push({anchor: start, head: Pos(to + 1, 0)});
    342       }
    343       if (selected) cm.setSelections(ranges, 0);
    344     });
    345   }
    346 
    347   cmds.sortLines = function(cm) { sortLines(cm, true); };
    348   cmds.sortLinesInsensitive = function(cm) { sortLines(cm, false); };
    349 
    350   cmds.nextBookmark = function(cm) {
    351     var marks = cm.state.sublimeBookmarks;
    352     if (marks) while (marks.length) {
    353       var current = marks.shift();
    354       var found = current.find();
    355       if (found) {
    356         marks.push(current);
    357         return cm.setSelection(found.from, found.to);
    358       }
    359     }
    360   };
    361 
    362   cmds.prevBookmark = function(cm) {
    363     var marks = cm.state.sublimeBookmarks;
    364     if (marks) while (marks.length) {
    365       marks.unshift(marks.pop());
    366       var found = marks[marks.length - 1].find();
    367       if (!found)
    368         marks.pop();
    369       else
    370         return cm.setSelection(found.from, found.to);
    371     }
    372   };
    373 
    374   cmds.toggleBookmark = function(cm) {
    375     var ranges = cm.listSelections();
    376     var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []);
    377     for (var i = 0; i < ranges.length; i++) {
    378       var from = ranges[i].from(), to = ranges[i].to();
    379       var found = cm.findMarks(from, to);
    380       for (var j = 0; j < found.length; j++) {
    381         if (found[j].sublimeBookmark) {
    382           found[j].clear();
    383           for (var k = 0; k < marks.length; k++)
    384             if (marks[k] == found[j])
    385               marks.splice(k--, 1);
    386           break;
    387         }
    388       }
    389       if (j == found.length)
    390         marks.push(cm.markText(from, to, {sublimeBookmark: true, clearWhenEmpty: false}));
    391     }
    392   };
    393 
    394   cmds.clearBookmarks = function(cm) {
    395     var marks = cm.state.sublimeBookmarks;
    396     if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear();
    397     marks.length = 0;
    398   };
    399 
    400   cmds.selectBookmarks = function(cm) {
    401     var marks = cm.state.sublimeBookmarks, ranges = [];
    402     if (marks) for (var i = 0; i < marks.length; i++) {
    403       var found = marks[i].find();
    404       if (!found)
    405         marks.splice(i--, 0);
    406       else
    407         ranges.push({anchor: found.from, head: found.to});
    408     }
    409     if (ranges.length)
    410       cm.setSelections(ranges, 0);
    411   };
    412 
    413   function modifyWordOrSelection(cm, mod) {
    414     cm.operation(function() {
    415       var ranges = cm.listSelections(), indices = [], replacements = [];
    416       for (var i = 0; i < ranges.length; i++) {
    417         var range = ranges[i];
    418         if (range.empty()) { indices.push(i); replacements.push(""); }
    419         else replacements.push(mod(cm.getRange(range.from(), range.to())));
    420       }
    421       cm.replaceSelections(replacements, "around", "case");
    422       for (var i = indices.length - 1, at; i >= 0; i--) {
    423         var range = ranges[indices[i]];
    424         if (at && CodeMirror.cmpPos(range.head, at) > 0) continue;
    425         var word = wordAt(cm, range.head);
    426         at = word.from;
    427         cm.replaceRange(mod(word.word), word.from, word.to);
    428       }
    429     });
    430   }
    431 
    432   cmds.smartBackspace = function(cm) {
    433     if (cm.somethingSelected()) return CodeMirror.Pass;
    434 
    435     cm.operation(function() {
    436       var cursors = cm.listSelections();
    437       var indentUnit = cm.getOption("indentUnit");
    438 
    439       for (var i = cursors.length - 1; i >= 0; i--) {
    440         var cursor = cursors[i].head;
    441         var toStartOfLine = cm.getRange({line: cursor.line, ch: 0}, cursor);
    442         var column = CodeMirror.countColumn(toStartOfLine, null, cm.getOption("tabSize"));
    443 
    444         // Delete by one character by default
    445         var deletePos = cm.findPosH(cursor, -1, "char", false);
    446 
    447         if (toStartOfLine && !/\S/.test(toStartOfLine) && column % indentUnit == 0) {
    448           var prevIndent = new Pos(cursor.line,
    449             CodeMirror.findColumn(toStartOfLine, column - indentUnit, indentUnit));
    450 
    451           // Smart delete only if we found a valid prevIndent location
    452           if (prevIndent.ch != cursor.ch) deletePos = prevIndent;
    453         }
    454 
    455         cm.replaceRange("", deletePos, cursor, "+delete");
    456       }
    457     });
    458   };
    459 
    460   cmds.delLineRight = function(cm) {
    461     cm.operation(function() {
    462       var ranges = cm.listSelections();
    463       for (var i = ranges.length - 1; i >= 0; i--)
    464         cm.replaceRange("", ranges[i].anchor, Pos(ranges[i].to().line), "+delete");
    465       cm.scrollIntoView();
    466     });
    467   };
    468 
    469   cmds.upcaseAtCursor = function(cm) {
    470     modifyWordOrSelection(cm, function(str) { return str.toUpperCase(); });
    471   };
    472   cmds.downcaseAtCursor = function(cm) {
    473     modifyWordOrSelection(cm, function(str) { return str.toLowerCase(); });
    474   };
    475 
    476   cmds.setSublimeMark = function(cm) {
    477     if (cm.state.sublimeMark) cm.state.sublimeMark.clear();
    478     cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
    479   };
    480   cmds.selectToSublimeMark = function(cm) {
    481     var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
    482     if (found) cm.setSelection(cm.getCursor(), found);
    483   };
    484   cmds.deleteToSublimeMark = function(cm) {
    485     var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
    486     if (found) {
    487       var from = cm.getCursor(), to = found;
    488       if (CodeMirror.cmpPos(from, to) > 0) { var tmp = to; to = from; from = tmp; }
    489       cm.state.sublimeKilled = cm.getRange(from, to);
    490       cm.replaceRange("", from, to);
    491     }
    492   };
    493   cmds.swapWithSublimeMark = function(cm) {
    494     var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
    495     if (found) {
    496       cm.state.sublimeMark.clear();
    497       cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
    498       cm.setCursor(found);
    499     }
    500   };
    501   cmds.sublimeYank = function(cm) {
    502     if (cm.state.sublimeKilled != null)
    503       cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
    504   };
    505 
    506   cmds.showInCenter = function(cm) {
    507     var pos = cm.cursorCoords(null, "local");
    508     cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
    509   };
    510 
    511   cmds.selectLinesUpward = function(cm) {
    512     cm.operation(function() {
    513       var ranges = cm.listSelections();
    514       for (var i = 0; i < ranges.length; i++) {
    515         var range = ranges[i];
    516         if (range.head.line > cm.firstLine())
    517           cm.addSelection(Pos(range.head.line - 1, range.head.ch));
    518       }
    519     });
    520   };
    521   cmds.selectLinesDownward = function(cm) {
    522     cm.operation(function() {
    523       var ranges = cm.listSelections();
    524       for (var i = 0; i < ranges.length; i++) {
    525         var range = ranges[i];
    526         if (range.head.line < cm.lastLine())
    527           cm.addSelection(Pos(range.head.line + 1, range.head.ch));
    528       }
    529     });
    530   };
    531 
    532   function getTarget(cm) {
    533     var from = cm.getCursor("from"), to = cm.getCursor("to");
    534     if (CodeMirror.cmpPos(from, to) == 0) {
    535       var word = wordAt(cm, from);
    536       if (!word.word) return;
    537       from = word.from;
    538       to = word.to;
    539     }
    540     return {from: from, to: to, query: cm.getRange(from, to), word: word};
    541   }
    542 
    543   function findAndGoTo(cm, forward) {
    544     var target = getTarget(cm);
    545     if (!target) return;
    546     var query = target.query;
    547     var cur = cm.getSearchCursor(query, forward ? target.to : target.from);
    548 
    549     if (forward ? cur.findNext() : cur.findPrevious()) {
    550       cm.setSelection(cur.from(), cur.to());
    551     } else {
    552       cur = cm.getSearchCursor(query, forward ? Pos(cm.firstLine(), 0)
    553                                               : cm.clipPos(Pos(cm.lastLine())));
    554       if (forward ? cur.findNext() : cur.findPrevious())
    555         cm.setSelection(cur.from(), cur.to());
    556       else if (target.word)
    557         cm.setSelection(target.from, target.to);
    558     }
    559   };
    560   cmds.findUnder = function(cm) { findAndGoTo(cm, true); };
    561   cmds.findUnderPrevious = function(cm) { findAndGoTo(cm,false); };
    562   cmds.findAllUnder = function(cm) {
    563     var target = getTarget(cm);
    564     if (!target) return;
    565     var cur = cm.getSearchCursor(target.query);
    566     var matches = [];
    567     var primaryIndex = -1;
    568     while (cur.findNext()) {
    569       matches.push({anchor: cur.from(), head: cur.to()});
    570       if (cur.from().line <= target.from.line && cur.from().ch <= target.from.ch)
    571         primaryIndex++;
    572     }
    573     cm.setSelections(matches, primaryIndex);
    574   };
    575 
    576 
    577   var keyMap = CodeMirror.keyMap;
    578   keyMap.macSublime = {
    579     "Cmd-Left": "goLineStartSmart",
    580     "Shift-Tab": "indentLess",
    581     "Shift-Ctrl-K": "deleteLine",
    582     "Alt-Q": "wrapLines",
    583     "Ctrl-Left": "goSubwordLeft",
    584     "Ctrl-Right": "goSubwordRight",
    585     "Ctrl-Alt-Up": "scrollLineUp",
    586     "Ctrl-Alt-Down": "scrollLineDown",
    587     "Cmd-L": "selectLine",
    588     "Shift-Cmd-L": "splitSelectionByLine",
    589     "Esc": "singleSelectionTop",
    590     "Cmd-Enter": "insertLineAfter",
    591     "Shift-Cmd-Enter": "insertLineBefore",
    592     "Cmd-D": "selectNextOccurrence",
    593     "Shift-Cmd-Up": "addCursorToPrevLine",
    594     "Shift-Cmd-Down": "addCursorToNextLine",
    595     "Shift-Cmd-Space": "selectScope",
    596     "Shift-Cmd-M": "selectBetweenBrackets",
    597     "Cmd-M": "goToBracket",
    598     "Cmd-Ctrl-Up": "swapLineUp",
    599     "Cmd-Ctrl-Down": "swapLineDown",
    600     "Cmd-/": "toggleCommentIndented",
    601     "Cmd-J": "joinLines",
    602     "Shift-Cmd-D": "duplicateLine",
    603     "F9": "sortLines",
    604     "Cmd-F9": "sortLinesInsensitive",
    605     "F2": "nextBookmark",
    606     "Shift-F2": "prevBookmark",
    607     "Cmd-F2": "toggleBookmark",
    608     "Shift-Cmd-F2": "clearBookmarks",
    609     "Alt-F2": "selectBookmarks",
    610     "Backspace": "smartBackspace",
    611     "Cmd-K Cmd-K": "delLineRight",
    612     "Cmd-K Cmd-U": "upcaseAtCursor",
    613     "Cmd-K Cmd-L": "downcaseAtCursor",
    614     "Cmd-K Cmd-Space": "setSublimeMark",
    615     "Cmd-K Cmd-A": "selectToSublimeMark",
    616     "Cmd-K Cmd-W": "deleteToSublimeMark",
    617     "Cmd-K Cmd-X": "swapWithSublimeMark",
    618     "Cmd-K Cmd-Y": "sublimeYank",
    619     "Cmd-K Cmd-C": "showInCenter",
    620     "Cmd-K Cmd-G": "clearBookmarks",
    621     "Cmd-K Cmd-Backspace": "delLineLeft",
    622     "Cmd-K Cmd-0": "unfoldAll",
    623     "Cmd-K Cmd-J": "unfoldAll",
    624     "Ctrl-Shift-Up": "selectLinesUpward",
    625     "Ctrl-Shift-Down": "selectLinesDownward",
    626     "Cmd-F3": "findUnder",
    627     "Shift-Cmd-F3": "findUnderPrevious",
    628     "Alt-F3": "findAllUnder",
    629     "Shift-Cmd-[": "fold",
    630     "Shift-Cmd-]": "unfold",
    631     "Cmd-I": "findIncremental",
    632     "Shift-Cmd-I": "findIncrementalReverse",
    633     "Cmd-H": "replace",
    634     "F3": "findNext",
    635     "Shift-F3": "findPrev",
    636     "fallthrough": "macDefault"
    637   };
    638   CodeMirror.normalizeKeyMap(keyMap.macSublime);
    639 
    640   keyMap.pcSublime = {
    641     "Shift-Tab": "indentLess",
    642     "Shift-Ctrl-K": "deleteLine",
    643     "Alt-Q": "wrapLines",
    644     "Ctrl-T": "transposeChars",
    645     "Alt-Left": "goSubwordLeft",
    646     "Alt-Right": "goSubwordRight",
    647     "Ctrl-Up": "scrollLineUp",
    648     "Ctrl-Down": "scrollLineDown",
    649     "Ctrl-L": "selectLine",
    650     "Shift-Ctrl-L": "splitSelectionByLine",
    651     "Esc": "singleSelectionTop",
    652     "Ctrl-Enter": "insertLineAfter",
    653     "Shift-Ctrl-Enter": "insertLineBefore",
    654     "Ctrl-D": "selectNextOccurrence",
    655     "Alt-CtrlUp": "addCursorToPrevLine",
    656     "Alt-CtrlDown": "addCursorToNextLine",
    657     "Shift-Ctrl-Space": "selectScope",
    658     "Shift-Ctrl-M": "selectBetweenBrackets",
    659     "Ctrl-M": "goToBracket",
    660     "Shift-Ctrl-Up": "swapLineUp",
    661     "Shift-Ctrl-Down": "swapLineDown",
    662     "Ctrl-/": "toggleCommentIndented",
    663     "Ctrl-J": "joinLines",
    664     "Shift-Ctrl-D": "duplicateLine",
    665     "F9": "sortLines",
    666     "Ctrl-F9": "sortLinesInsensitive",
    667     "F2": "nextBookmark",
    668     "Shift-F2": "prevBookmark",
    669     "Ctrl-F2": "toggleBookmark",
    670     "Shift-Ctrl-F2": "clearBookmarks",
    671     "Alt-F2": "selectBookmarks",
    672     "Backspace": "smartBackspace",
    673     "Ctrl-K Ctrl-K": "delLineRight",
    674     "Ctrl-K Ctrl-U": "upcaseAtCursor",
    675     "Ctrl-K Ctrl-L": "downcaseAtCursor",
    676     "Ctrl-K Ctrl-Space": "setSublimeMark",
    677     "Ctrl-K Ctrl-A": "selectToSublimeMark",
    678     "Ctrl-K Ctrl-W": "deleteToSublimeMark",
    679     "Ctrl-K Ctrl-X": "swapWithSublimeMark",
    680     "Ctrl-K Ctrl-Y": "sublimeYank",
    681     "Ctrl-K Ctrl-C": "showInCenter",
    682     "Ctrl-K Ctrl-G": "clearBookmarks",
    683     "Ctrl-K Ctrl-Backspace": "delLineLeft",
    684     "Ctrl-K Ctrl-0": "unfoldAll",
    685     "Ctrl-K Ctrl-J": "unfoldAll",
    686     "Ctrl-Alt-Up": "selectLinesUpward",
    687     "Ctrl-Alt-Down": "selectLinesDownward",
    688     "Ctrl-F3": "findUnder",
    689     "Shift-Ctrl-F3": "findUnderPrevious",
    690     "Alt-F3": "findAllUnder",
    691     "Shift-Ctrl-[": "fold",
    692     "Shift-Ctrl-]": "unfold",
    693     "Ctrl-I": "findIncremental",
    694     "Shift-Ctrl-I": "findIncrementalReverse",
    695     "Ctrl-H": "replace",
    696     "F3": "findNext",
    697     "Shift-F3": "findPrev",
    698     "fallthrough": "pcDefault"
    699   };
    700   CodeMirror.normalizeKeyMap(keyMap.pcSublime);
    701 
    702   var mac = keyMap.default == keyMap.macDefault;
    703   keyMap.sublime = mac ? keyMap.macSublime : keyMap.pcSublime;
    704 });