foldgutter.js (5368B) - View raw
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163// CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("./foldcode")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror", "./foldcode"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; CodeMirror.defineOption("foldGutter", false, function(cm, val, old) { if (old && old != CodeMirror.Init) { cm.clearGutter(cm.state.foldGutter.options.gutter); cm.state.foldGutter = null; cm.off("gutterClick", onGutterClick); cm.off("changes", onChange); cm.off("viewportChange", onViewportChange); cm.off("fold", onFold); cm.off("unfold", onFold); cm.off("swapDoc", onChange); } if (val) { cm.state.foldGutter = new State(parseOptions(val)); updateInViewport(cm); cm.on("gutterClick", onGutterClick); cm.on("changes", onChange); cm.on("viewportChange", onViewportChange); cm.on("fold", onFold); cm.on("unfold", onFold); cm.on("swapDoc", onChange); } }); var Pos = CodeMirror.Pos; function State(options) { this.options = options; this.from = this.to = 0; } function parseOptions(opts) { if (opts === true) opts = {}; if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter"; if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open"; if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded"; return opts; } function isFolded(cm, line) { var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0)); for (var i = 0; i < marks.length; ++i) { if (marks[i].__isFold) { var fromPos = marks[i].find(-1); if (fromPos && fromPos.line === line) return marks[i]; } } } function marker(spec) { if (typeof spec == "string") { var elt = document.createElement("div"); elt.className = spec + " CodeMirror-guttermarker-subtle"; return elt; } else { return spec.cloneNode(true); } } function updateFoldInfo(cm, from, to) { var opts = cm.state.foldGutter.options, cur = from - 1; var minSize = cm.foldOption(opts, "minFoldSize"); var func = cm.foldOption(opts, "rangeFinder"); // we can reuse the built-in indicator element if its className matches the new state var clsFolded = typeof opts.indicatorFolded == "string" && classTest(opts.indicatorFolded); var clsOpen = typeof opts.indicatorOpen == "string" && classTest(opts.indicatorOpen); cm.eachLine(from, to, function(line) { ++cur; var mark = null; var old = line.gutterMarkers; if (old) old = old[opts.gutter]; if (isFolded(cm, cur)) { if (clsFolded && old && clsFolded.test(old.className)) return; mark = marker(opts.indicatorFolded); } else { var pos = Pos(cur, 0); var range = func && func(cm, pos); if (range && range.to.line - range.from.line >= minSize) { if (clsOpen && old && clsOpen.test(old.className)) return; mark = marker(opts.indicatorOpen); } } if (!mark && !old) return; cm.setGutterMarker(line, opts.gutter, mark); }); } // copied from CodeMirror/src/util/dom.js function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } function updateInViewport(cm) { var vp = cm.getViewport(), state = cm.state.foldGutter; if (!state) return; cm.operation(function() { updateFoldInfo(cm, vp.from, vp.to); }); state.from = vp.from; state.to = vp.to; } function onGutterClick(cm, line, gutter) { var state = cm.state.foldGutter; if (!state) return; var opts = state.options; if (gutter != opts.gutter) return; var folded = isFolded(cm, line); if (folded) folded.clear(); else cm.foldCode(Pos(line, 0), opts); } function onChange(cm) { var state = cm.state.foldGutter; if (!state) return; var opts = state.options; state.from = state.to = 0; clearTimeout(state.changeUpdate); state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600); } function onViewportChange(cm) { var state = cm.state.foldGutter; if (!state) return; var opts = state.options; clearTimeout(state.changeUpdate); state.changeUpdate = setTimeout(function() { var vp = cm.getViewport(); if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) { updateInViewport(cm); } else { cm.operation(function() { if (vp.from < state.from) { updateFoldInfo(cm, vp.from, state.from); state.from = vp.from; } if (vp.to > state.to) { updateFoldInfo(cm, state.to, vp.to); state.to = vp.to; } }); } }, opts.updateViewportTimeSpan || 400); } function onFold(cm, from) { var state = cm.state.foldGutter; if (!state) return; var line = from.line; if (line >= state.from && line < state.to) updateFoldInfo(cm, line, line + 1); } });