michael@0: (function() { michael@0: "use strict"; michael@0: michael@0: function doFold(cm, pos, options, force) { michael@0: var finder = options && (options.call ? options : options.rangeFinder); michael@0: if (!finder) finder = CodeMirror.fold.auto; michael@0: if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0); michael@0: var minSize = options && options.minFoldSize || 0; michael@0: michael@0: function getRange(allowFolded) { michael@0: var range = finder(cm, pos); michael@0: if (!range || range.to.line - range.from.line < minSize) return null; michael@0: var marks = cm.findMarksAt(range.from); michael@0: for (var i = 0; i < marks.length; ++i) { michael@0: if (marks[i].__isFold && force !== "fold") { michael@0: if (!allowFolded) return null; michael@0: range.cleared = true; michael@0: marks[i].clear(); michael@0: } michael@0: } michael@0: return range; michael@0: } michael@0: michael@0: var range = getRange(true); michael@0: if (options && options.scanUp) while (!range && pos.line > cm.firstLine()) { michael@0: pos = CodeMirror.Pos(pos.line - 1, 0); michael@0: range = getRange(false); michael@0: } michael@0: if (!range || range.cleared || force === "unfold") return; michael@0: michael@0: var myWidget = makeWidget(options); michael@0: CodeMirror.on(myWidget, "mousedown", function() { myRange.clear(); }); michael@0: var myRange = cm.markText(range.from, range.to, { michael@0: replacedWith: myWidget, michael@0: clearOnEnter: true, michael@0: __isFold: true michael@0: }); michael@0: myRange.on("clear", function(from, to) { michael@0: CodeMirror.signal(cm, "unfold", cm, from, to); michael@0: }); michael@0: CodeMirror.signal(cm, "fold", cm, range.from, range.to); michael@0: } michael@0: michael@0: function makeWidget(options) { michael@0: var widget = (options && options.widget) || "\u2194"; michael@0: if (typeof widget == "string") { michael@0: var text = document.createTextNode(widget); michael@0: widget = document.createElement("span"); michael@0: widget.appendChild(text); michael@0: widget.className = "CodeMirror-foldmarker"; michael@0: } michael@0: return widget; michael@0: } michael@0: michael@0: // Clumsy backwards-compatible interface michael@0: CodeMirror.newFoldFunction = function(rangeFinder, widget) { michael@0: return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); }; michael@0: }; michael@0: michael@0: // New-style interface michael@0: CodeMirror.defineExtension("foldCode", function(pos, options, force) { michael@0: doFold(this, pos, options, force); michael@0: }); michael@0: michael@0: CodeMirror.commands.fold = function(cm) { michael@0: cm.foldCode(cm.getCursor()); michael@0: }; michael@0: michael@0: CodeMirror.registerHelper("fold", "combine", function() { michael@0: var funcs = Array.prototype.slice.call(arguments, 0); michael@0: return function(cm, start) { michael@0: for (var i = 0; i < funcs.length; ++i) { michael@0: var found = funcs[i](cm, start); michael@0: if (found) return found; michael@0: } michael@0: }; michael@0: }); michael@0: michael@0: CodeMirror.registerHelper("fold", "auto", function(cm, start) { michael@0: var helpers = cm.getHelpers(start, "fold"); michael@0: for (var i = 0; i < helpers.length; i++) { michael@0: var cur = helpers[i](cm, start); michael@0: if (cur) return cur; michael@0: } michael@0: }); michael@0: })();