foldgutter.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. (function() {
  2. "use strict";
  3. CodeMirror.defineOption("foldGutter", false, function(cm, val, old) {
  4. if (old && old != CodeMirror.Init) {
  5. cm.clearGutter(cm.state.foldGutter.options.gutter);
  6. cm.state.foldGutter = null;
  7. cm.off("gutterClick", onGutterClick);
  8. cm.off("change", onChange);
  9. cm.off("viewportChange", onViewportChange);
  10. cm.off("fold", onFold);
  11. cm.off("unfold", onFold);
  12. cm.off("swapDoc", updateInViewport);
  13. }
  14. if (val) {
  15. cm.state.foldGutter = new State(parseOptions(val));
  16. updateInViewport(cm);
  17. cm.on("gutterClick", onGutterClick);
  18. cm.on("change", onChange);
  19. cm.on("viewportChange", onViewportChange);
  20. cm.on("fold", onFold);
  21. cm.on("unfold", onFold);
  22. cm.on("swapDoc", updateInViewport);
  23. }
  24. });
  25. var Pos = CodeMirror.Pos;
  26. function State(options) {
  27. this.options = options;
  28. this.from = this.to = 0;
  29. }
  30. function parseOptions(opts) {
  31. if (opts === true) opts = {};
  32. if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter";
  33. if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open";
  34. if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded";
  35. return opts;
  36. }
  37. function isFolded(cm, line) {
  38. var marks = cm.findMarksAt(Pos(line));
  39. for (var i = 0; i < marks.length; ++i)
  40. if (marks[i].__isFold && marks[i].find().from.line == line) return true;
  41. }
  42. function marker(spec) {
  43. if (typeof spec == "string") {
  44. var elt = document.createElement("div");
  45. elt.className = spec;
  46. return elt;
  47. } else {
  48. return spec.cloneNode(true);
  49. }
  50. }
  51. function updateFoldInfo(cm, from, to) {
  52. var opts = cm.state.foldGutter.options, cur = from;
  53. cm.eachLine(from, to, function(line) {
  54. var mark = null;
  55. if (isFolded(cm, cur)) {
  56. mark = marker(opts.indicatorFolded);
  57. } else {
  58. var pos = Pos(cur, 0), func = opts.rangeFinder || CodeMirror.fold.auto;
  59. var range = func && func(cm, pos);
  60. if (range && range.from.line + 1 < range.to.line)
  61. mark = marker(opts.indicatorOpen);
  62. }
  63. cm.setGutterMarker(line, opts.gutter, mark);
  64. ++cur;
  65. });
  66. }
  67. function updateInViewport(cm) {
  68. var vp = cm.getViewport(), state = cm.state.foldGutter;
  69. if (!state) return;
  70. cm.operation(function() {
  71. updateFoldInfo(cm, vp.from, vp.to);
  72. });
  73. state.from = vp.from; state.to = vp.to;
  74. }
  75. function onGutterClick(cm, line, gutter) {
  76. var opts = cm.state.foldGutter.options;
  77. if (gutter != opts.gutter) return;
  78. cm.foldCode(Pos(line, 0), opts.rangeFinder);
  79. }
  80. function onChange(cm) {
  81. var state = cm.state.foldGutter, opts = cm.state.foldGutter.options;
  82. state.from = state.to = 0;
  83. clearTimeout(state.changeUpdate);
  84. state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600);
  85. }
  86. function onViewportChange(cm) {
  87. var state = cm.state.foldGutter, opts = cm.state.foldGutter.options;
  88. clearTimeout(state.changeUpdate);
  89. state.changeUpdate = setTimeout(function() {
  90. var vp = cm.getViewport();
  91. if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
  92. updateInViewport(cm);
  93. } else {
  94. cm.operation(function() {
  95. if (vp.from < state.from) {
  96. updateFoldInfo(cm, vp.from, state.from);
  97. state.from = vp.from;
  98. }
  99. if (vp.to > state.to) {
  100. updateFoldInfo(cm, state.to, vp.to);
  101. state.to = vp.to;
  102. }
  103. });
  104. }
  105. }, opts.updateViewportTimeSpan || 400);
  106. }
  107. function onFold(cm, from) {
  108. var state = cm.state.foldGutter, line = from.line;
  109. if (line >= state.from && line < state.to)
  110. updateFoldInfo(cm, line, line + 1);
  111. }
  112. })();