CodeMirror.braceRangeFinder = function(cm, line) { var lineText = cm.getLine(line); var startChar = lineText.lastIndexOf("{"); if (startChar < 0 || lineText.lastIndexOf("}") > startChar) return; var tokenType = cm.getTokenAt({line: line, ch: startChar}).className; var count = 1, lastLine = cm.lineCount(), end; outer: for (var i = line + 1; i < lastLine; ++i) { var text = cm.getLine(i), pos = 0; for (;;) { var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos); if (nextOpen < 0) nextOpen = text.length; if (nextClose < 0) nextClose = text.length; pos = Math.min(nextOpen, nextClose); if (pos == text.length) break; if (cm.getTokenAt({line: i, ch: pos + 1}).className == tokenType) { if (pos == nextOpen) ++count; else if (!--count) { end = i; break outer; } } ++pos; } } if (end == null || end == line + 1) return; return end; }; CodeMirror.newFoldFunction = function(rangeFinder, markText) { var folded = []; if (markText == null) markText = '<div style="position: absolute; left: 2px; color:#600">▼</div>%N%'; function isFolded(cm, n) { for (var i = 0; i < folded.length; ++i) { var start = cm.lineInfo(folded[i].start); if (!start) folded.splice(i--, 1); else if (start.line == n) return {pos: i, region: folded[i]}; } } function expand(cm, region) { cm.clearMarker(region.start); for (var i = 0; i < region.hidden.length; ++i) cm.showLine(region.hidden[i]); } return function(cm, line) { cm.operation(function() { var known = isFolded(cm, line); if (known) { folded.splice(known.pos, 1); expand(cm, known.region); } else { var end = rangeFinder(cm, line); if (end == null) return; var hidden = []; for (var i = line + 1; i < end; ++i) { var handle = cm.hideLine(i); if (handle) hidden.push(handle); } var first = cm.setMarker(line, markText); var region = {start: first, hidden: hidden}; cm.onDeleteLine(first, function() { expand(cm, region); }); folded.push(region); } }); }; };