foldcode.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // the tagRangeFinder function is
  2. // Copyright (C) 2011 by Daniel Glazman <daniel@glazman.org>
  3. // released under the MIT license (../../LICENSE) like the rest of CodeMirror
  4. CodeMirror.tagRangeFinder = function(cm, line, hideEnd) {
  5. var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
  6. var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
  7. var xmlNAMERegExp = new RegExp("^[" + nameStartChar + "][" + nameChar + "]*");
  8. var lineText = cm.getLine(line);
  9. var found = false;
  10. var tag = null;
  11. var pos = 0;
  12. while (!found) {
  13. pos = lineText.indexOf("<", pos);
  14. if (-1 == pos) // no tag on line
  15. return;
  16. if (pos + 1 < lineText.length && lineText[pos + 1] == "/") { // closing tag
  17. pos++;
  18. continue;
  19. }
  20. // ok we weem to have a start tag
  21. if (!lineText.substr(pos + 1).match(xmlNAMERegExp)) { // not a tag name...
  22. pos++;
  23. continue;
  24. }
  25. var gtPos = lineText.indexOf(">", pos + 1);
  26. if (-1 == gtPos) { // end of start tag not in line
  27. var l = line + 1;
  28. var foundGt = false;
  29. var lastLine = cm.lineCount();
  30. while (l < lastLine && !foundGt) {
  31. var lt = cm.getLine(l);
  32. var gt = lt.indexOf(">");
  33. if (-1 != gt) { // found a >
  34. foundGt = true;
  35. var slash = lt.lastIndexOf("/", gt);
  36. if (-1 != slash && slash < gt) {
  37. var str = lineText.substr(slash, gt - slash + 1);
  38. if (!str.match( /\/\s*\>/ )) { // yep, that's the end of empty tag
  39. if (hideEnd === true) l++;
  40. return l;
  41. }
  42. }
  43. }
  44. l++;
  45. }
  46. found = true;
  47. }
  48. else {
  49. var slashPos = lineText.lastIndexOf("/", gtPos);
  50. if (-1 == slashPos) { // cannot be empty tag
  51. found = true;
  52. // don't continue
  53. }
  54. else { // empty tag?
  55. // check if really empty tag
  56. var str = lineText.substr(slashPos, gtPos - slashPos + 1);
  57. if (!str.match( /\/\s*\>/ )) { // finally not empty
  58. found = true;
  59. // don't continue
  60. }
  61. }
  62. }
  63. if (found) {
  64. var subLine = lineText.substr(pos + 1);
  65. tag = subLine.match(xmlNAMERegExp);
  66. if (tag) {
  67. // we have an element name, wooohooo !
  68. tag = tag[0];
  69. // do we have the close tag on same line ???
  70. if (-1 != lineText.indexOf("</" + tag + ">", pos)) // yep
  71. {
  72. found = false;
  73. }
  74. // we don't, so we have a candidate...
  75. }
  76. else
  77. found = false;
  78. }
  79. if (!found)
  80. pos++;
  81. }
  82. if (found) {
  83. var startTag = "(\\<\\/" + tag + "\\>)|(\\<" + tag + "\\>)|(\\<" + tag + "\\s)|(\\<" + tag + "$)";
  84. var startTagRegExp = new RegExp(startTag, "g");
  85. var endTag = "</" + tag + ">";
  86. var depth = 1;
  87. var l = line + 1;
  88. var lastLine = cm.lineCount();
  89. while (l < lastLine) {
  90. lineText = cm.getLine(l);
  91. var match = lineText.match(startTagRegExp);
  92. if (match) {
  93. for (var i = 0; i < match.length; i++) {
  94. if (match[i] == endTag)
  95. depth--;
  96. else
  97. depth++;
  98. if (!depth) {
  99. if (hideEnd === true) l++;
  100. return l;
  101. }
  102. }
  103. }
  104. l++;
  105. }
  106. return;
  107. }
  108. };
  109. CodeMirror.braceRangeFinder = function(cm, line, hideEnd) {
  110. var lineText = cm.getLine(line), at = lineText.length, startChar, tokenType;
  111. for (;;) {
  112. var found = lineText.lastIndexOf("{", at);
  113. if (found < 0) break;
  114. tokenType = cm.getTokenAt({line: line, ch: found}).className;
  115. if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; }
  116. at = found - 1;
  117. }
  118. if (startChar == null || lineText.lastIndexOf("}") > startChar) return;
  119. var count = 1, lastLine = cm.lineCount(), end;
  120. outer: for (var i = line + 1; i < lastLine; ++i) {
  121. var text = cm.getLine(i), pos = 0;
  122. for (;;) {
  123. var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos);
  124. if (nextOpen < 0) nextOpen = text.length;
  125. if (nextClose < 0) nextClose = text.length;
  126. pos = Math.min(nextOpen, nextClose);
  127. if (pos == text.length) break;
  128. if (cm.getTokenAt({line: i, ch: pos + 1}).className == tokenType) {
  129. if (pos == nextOpen) ++count;
  130. else if (!--count) { end = i; break outer; }
  131. }
  132. ++pos;
  133. }
  134. }
  135. if (end == null || end == line + 1) return;
  136. if (hideEnd === true) end++;
  137. return end;
  138. };
  139. CodeMirror.indentRangeFinder = function(cm, line) {
  140. var tabSize = cm.getOption("tabSize");
  141. var myIndent = cm.getLineHandle(line).indentation(tabSize), last;
  142. for (var i = line + 1, end = cm.lineCount(); i < end; ++i) {
  143. var handle = cm.getLineHandle(i);
  144. if (!/^\s*$/.test(handle.text)) {
  145. if (handle.indentation(tabSize) <= myIndent) break;
  146. last = i;
  147. }
  148. }
  149. if (!last) return null;
  150. return last + 1;
  151. };
  152. CodeMirror.newFoldFunction = function(rangeFinder, markText, hideEnd) {
  153. var folded = [];
  154. if (markText == null) markText = '<div style="position: absolute; left: 2px; color:#600">&#x25bc;</div>%N%';
  155. function isFolded(cm, n) {
  156. for (var i = 0; i < folded.length; ++i) {
  157. var start = cm.lineInfo(folded[i].start);
  158. if (!start) folded.splice(i--, 1);
  159. else if (start.line == n) return {pos: i, region: folded[i]};
  160. }
  161. }
  162. function expand(cm, region) {
  163. cm.clearMarker(region.start);
  164. for (var i = 0; i < region.hidden.length; ++i)
  165. cm.showLine(region.hidden[i]);
  166. }
  167. return function(cm, line) {
  168. cm.operation(function() {
  169. var known = isFolded(cm, line);
  170. if (known) {
  171. folded.splice(known.pos, 1);
  172. expand(cm, known.region);
  173. } else {
  174. var end = rangeFinder(cm, line, hideEnd);
  175. if (end == null) return;
  176. var hidden = [];
  177. for (var i = line + 1; i < end; ++i) {
  178. var handle = cm.hideLine(i);
  179. if (handle) hidden.push(handle);
  180. }
  181. var first = cm.setMarker(line, markText);
  182. var region = {start: first, hidden: hidden};
  183. cm.onDeleteLine(first, function() { expand(cm, region); });
  184. folded.push(region);
  185. }
  186. });
  187. };
  188. };