UNPKG

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