1// Because sometimes you need to mark the selected *text*. 2// 3// Adds an option 'styleSelectedText' which, when enabled, gives 4// selected text the CSS class given as option value, or 5// "CodeMirror-selectedtext" when the value is not a string. 6 7(function() { 8 "use strict"; 9 10 CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) { 11 var prev = old && old != CodeMirror.Init; 12 if (val && !prev) { 13 cm.state.markedSelection = []; 14 cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext"; 15 reset(cm); 16 cm.on("cursorActivity", onCursorActivity); 17 cm.on("change", onChange); 18 } else if (!val && prev) { 19 cm.off("cursorActivity", onCursorActivity); 20 cm.off("change", onChange); 21 clear(cm); 22 cm.state.markedSelection = cm.state.markedSelectionStyle = null; 23 } 24 }); 25 26 function onCursorActivity(cm) { 27 cm.operation(function() { update(cm); }); 28 } 29 30 function onChange(cm) { 31 if (cm.state.markedSelection.length) 32 cm.operation(function() { clear(cm); }); 33 } 34 35 var CHUNK_SIZE = 8; 36 var Pos = CodeMirror.Pos; 37 38 function cmp(pos1, pos2) { 39 return pos1.line - pos2.line || pos1.ch - pos2.ch; 40 } 41 42 function coverRange(cm, from, to, addAt) { 43 if (cmp(from, to) == 0) return; 44 var array = cm.state.markedSelection; 45 var cls = cm.state.markedSelectionStyle; 46 for (var line = from.line;;) { 47 var start = line == from.line ? from : Pos(line, 0); 48 var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line; 49 var end = atEnd ? to : Pos(endLine, 0); 50 var mark = cm.markText(start, end, {className: cls}); 51 if (addAt == null) array.push(mark); 52 else array.splice(addAt++, 0, mark); 53 if (atEnd) break; 54 line = endLine; 55 } 56 } 57 58 function clear(cm) { 59 var array = cm.state.markedSelection; 60 for (var i = 0; i < array.length; ++i) array[i].clear(); 61 array.length = 0; 62 } 63 64 function reset(cm) { 65 clear(cm); 66 var from = cm.getCursor("start"), to = cm.getCursor("end"); 67 coverRange(cm, from, to); 68 } 69 70 function update(cm) { 71 var from = cm.getCursor("start"), to = cm.getCursor("end"); 72 if (cmp(from, to) == 0) return clear(cm); 73 74 var array = cm.state.markedSelection; 75 if (!array.length) return coverRange(cm, from, to); 76 77 var coverStart = array[0].find(), coverEnd = array[array.length - 1].find(); 78 if (!coverStart || !coverEnd || to.line - from.line < CHUNK_SIZE || 79 cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0) 80 return reset(cm); 81 82 while (cmp(from, coverStart.from) > 0) { 83 array.shift().clear(); 84 coverStart = array[0].find(); 85 } 86 if (cmp(from, coverStart.from) < 0) { 87 if (coverStart.to.line - from.line < CHUNK_SIZE) { 88 array.shift().clear(); 89 coverRange(cm, from, coverStart.to, 0); 90 } else { 91 coverRange(cm, from, coverStart.from, 0); 92 } 93 } 94 95 while (cmp(to, coverEnd.to) < 0) { 96 array.pop().clear(); 97 coverEnd = array[array.length - 1].find(); 98 } 99 if (cmp(to, coverEnd.to) > 0) { 100 if (to.line - coverEnd.from.line < CHUNK_SIZE) { 101 array.pop().clear(); 102 coverRange(cm, coverEnd.from, to); 103 } else { 104 coverRange(cm, coverEnd.to, to); 105 } 106 } 107 } 108})(); 109