foldcode.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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, start) {
  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(start.line);
  9. var found = false;
  10. var tag = null;
  11. var pos = start.ch;
  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 seem 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 = start.line + 1;
  28. var foundGt = false;
  29. var lastLine = cm.lineCount();
  30. while (l < lastLine && !foundGt) {
  31. var lt = cm.getLine(l);
  32. gtPos = lt.indexOf(">");
  33. if (-1 != gtPos) { // found a >
  34. foundGt = true;
  35. var slash = lt.lastIndexOf("/", gtPos);
  36. if (-1 != slash && slash < gtPos) {
  37. var str = lineText.substr(slash, gtPos - slash + 1);
  38. if (!str.match( /\/\s*\>/ )) // yep, that's the end of empty tag
  39. return;
  40. }
  41. }
  42. l++;
  43. }
  44. found = true;
  45. }
  46. else {
  47. var slashPos = lineText.lastIndexOf("/", gtPos);
  48. if (-1 == slashPos) { // cannot be empty tag
  49. found = true;
  50. // don't continue
  51. }
  52. else { // empty tag?
  53. // check if really empty tag
  54. var str = lineText.substr(slashPos, gtPos - slashPos + 1);
  55. if (!str.match( /\/\s*\>/ )) { // finally not empty
  56. found = true;
  57. // don't continue
  58. }
  59. }
  60. }
  61. if (found) {
  62. var subLine = lineText.substr(pos + 1);
  63. tag = subLine.match(xmlNAMERegExp);
  64. if (tag) {
  65. // we have an element name, wooohooo !
  66. tag = tag[0];
  67. // do we have the close tag on same line ???
  68. if (-1 != lineText.indexOf("</" + tag + ">", pos)) // yep
  69. {
  70. found = false;
  71. }
  72. // we don't, so we have a candidate...
  73. }
  74. else
  75. found = false;
  76. }
  77. if (!found)
  78. pos++;
  79. }
  80. if (found) {
  81. var startTag = "(\\<\\/" + tag + "\\>)|(\\<" + tag + "\\>)|(\\<" + tag + "\\s)|(\\<" + tag + "$)";
  82. var startTagRegExp = new RegExp(startTag);
  83. var endTag = "</" + tag + ">";
  84. var depth = 1;
  85. var l = start.line + 1;
  86. var lastLine = cm.lineCount();
  87. while (l < lastLine) {
  88. lineText = cm.getLine(l);
  89. var match = lineText.match(startTagRegExp);
  90. if (match) {
  91. for (var i = 0; i < match.length; i++) {
  92. if (match[i] == endTag)
  93. depth--;
  94. else
  95. depth++;
  96. if (!depth) return {from: {line: start.line, ch: gtPos + 1},
  97. to: {line: l, ch: match.index}};
  98. }
  99. }
  100. l++;
  101. }
  102. return;
  103. }
  104. };
  105. CodeMirror.braceRangeFinder = function(cm, start) {
  106. var line = start.line, lineText = cm.getLine(line);
  107. var at = lineText.length, startChar, tokenType;
  108. for (;;) {
  109. var found = lineText.lastIndexOf("{", at);
  110. if (found < start.ch) break;
  111. tokenType = cm.getTokenAt({line: line, ch: found}).type;
  112. if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; }
  113. at = found - 1;
  114. }
  115. if (startChar == null || lineText.lastIndexOf("}") > startChar) return;
  116. var count = 1, lastLine = cm.lineCount(), end, endCh;
  117. outer: for (var i = line + 1; i < lastLine; ++i) {
  118. var text = cm.getLine(i), pos = 0;
  119. for (;;) {
  120. var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos);
  121. if (nextOpen < 0) nextOpen = text.length;
  122. if (nextClose < 0) nextClose = text.length;
  123. pos = Math.min(nextOpen, nextClose);
  124. if (pos == text.length) break;
  125. if (cm.getTokenAt({line: i, ch: pos + 1}).type == tokenType) {
  126. if (pos == nextOpen) ++count;
  127. else if (!--count) { end = i; endCh = pos; break outer; }
  128. }
  129. ++pos;
  130. }
  131. }
  132. if (end == null || end == line + 1) return;
  133. return {from: {line: line, ch: startChar + 1},
  134. to: {line: end, ch: endCh}};
  135. };
  136. CodeMirror.indentRangeFinder = function(cm, start) {
  137. var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line);
  138. var myIndent = CodeMirror.countColumn(firstLine, null, tabSize);
  139. for (var i = start.line + 1, end = cm.lineCount(); i < end; ++i) {
  140. var curLine = cm.getLine(i);
  141. if (CodeMirror.countColumn(curLine, null, tabSize) < myIndent)
  142. return {from: {line: start.line, ch: firstLine.length},
  143. to: {line: i, ch: curLine.length}};
  144. }
  145. };
  146. CodeMirror.newFoldFunction = function(rangeFinder, widget) {
  147. if (widget == null) widget = "\u2194";
  148. if (typeof widget == "string") {
  149. var text = document.createTextNode(widget);
  150. widget = document.createElement("span");
  151. widget.appendChild(text);
  152. widget.className = "CodeMirror-foldmarker";
  153. }
  154. return function(cm, pos) {
  155. if (typeof pos == "number") pos = {line: pos, ch: 0};
  156. var range = rangeFinder(cm, pos);
  157. if (!range) return;
  158. var present = cm.findMarksAt(range.from), cleared = 0;
  159. for (var i = 0; i < present.length; ++i) {
  160. if (present[i].__isFold) {
  161. ++cleared;
  162. present[i].clear();
  163. }
  164. }
  165. if (cleared) return;
  166. var myWidget = widget.cloneNode(true);
  167. CodeMirror.on(myWidget, "mousedown", function() {myRange.clear();});
  168. var myRange = cm.markText(range.from, range.to, {
  169. replacedWith: myWidget,
  170. clearOnEnter: true,
  171. __isFold: true
  172. });
  173. };
  174. };