123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- // the tagRangeFinder function is
- // Copyright (C) 2011 by Daniel Glazman <daniel@glazman.org>
- // released under the MIT license (../../LICENSE) like the rest of CodeMirror
- CodeMirror.tagRangeFinder = function(cm, line, hideEnd) {
- 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";
- var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
- var xmlNAMERegExp = new RegExp("^[" + nameStartChar + "][" + nameChar + "]*");
- var lineText = cm.getLine(line);
- var found = false;
- var tag = null;
- var pos = 0;
- while (!found) {
- pos = lineText.indexOf("<", pos);
- if (-1 == pos) // no tag on line
- return;
- if (pos + 1 < lineText.length && lineText[pos + 1] == "/") { // closing tag
- pos++;
- continue;
- }
- // ok we weem to have a start tag
- if (!lineText.substr(pos + 1).match(xmlNAMERegExp)) { // not a tag name...
- pos++;
- continue;
- }
- var gtPos = lineText.indexOf(">", pos + 1);
- if (-1 == gtPos) { // end of start tag not in line
- var l = line + 1;
- var foundGt = false;
- var lastLine = cm.lineCount();
- while (l < lastLine && !foundGt) {
- var lt = cm.getLine(l);
- var gt = lt.indexOf(">");
- if (-1 != gt) { // found a >
- foundGt = true;
- var slash = lt.lastIndexOf("/", gt);
- if (-1 != slash && slash < gt) {
- var str = lineText.substr(slash, gt - slash + 1);
- if (!str.match( /\/\s*\>/ )) { // yep, that's the end of empty tag
- if (hideEnd === true) l++;
- return l;
- }
- }
- }
- l++;
- }
- found = true;
- }
- else {
- var slashPos = lineText.lastIndexOf("/", gtPos);
- if (-1 == slashPos) { // cannot be empty tag
- found = true;
- // don't continue
- }
- else { // empty tag?
- // check if really empty tag
- var str = lineText.substr(slashPos, gtPos - slashPos + 1);
- if (!str.match( /\/\s*\>/ )) { // finally not empty
- found = true;
- // don't continue
- }
- }
- }
- if (found) {
- var subLine = lineText.substr(pos + 1);
- tag = subLine.match(xmlNAMERegExp);
- if (tag) {
- // we have an element name, wooohooo !
- tag = tag[0];
- // do we have the close tag on same line ???
- if (-1 != lineText.indexOf("</" + tag + ">", pos)) // yep
- {
- found = false;
- }
- // we don't, so we have a candidate...
- }
- else
- found = false;
- }
- if (!found)
- pos++;
- }
- if (found) {
- var startTag = "(\\<\\/" + tag + "\\>)|(\\<" + tag + "\\>)|(\\<" + tag + "\\s)|(\\<" + tag + "$)";
- var startTagRegExp = new RegExp(startTag, "g");
- var endTag = "</" + tag + ">";
- var depth = 1;
- var l = line + 1;
- var lastLine = cm.lineCount();
- while (l < lastLine) {
- lineText = cm.getLine(l);
- var match = lineText.match(startTagRegExp);
- if (match) {
- for (var i = 0; i < match.length; i++) {
- if (match[i] == endTag)
- depth--;
- else
- depth++;
- if (!depth) {
- if (hideEnd === true) l++;
- return l;
- }
- }
- }
- l++;
- }
- return;
- }
- };
- CodeMirror.braceRangeFinder = function(cm, line, hideEnd) {
- var lineText = cm.getLine(line), at = lineText.length, startChar, tokenType;
- for (;;) {
- var found = lineText.lastIndexOf("{", at);
- if (found < 0) break;
- tokenType = cm.getTokenAt({line: line, ch: found}).className;
- if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; }
- at = found - 1;
- }
- if (startChar == null || lineText.lastIndexOf("}") > startChar) return;
- var count = 1, lastLine = cm.lineCount(), end;
- outer: for (var i = line + 1; i < lastLine; ++i) {
- var text = cm.getLine(i), pos = 0;
- for (;;) {
- var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos);
- if (nextOpen < 0) nextOpen = text.length;
- if (nextClose < 0) nextClose = text.length;
- pos = Math.min(nextOpen, nextClose);
- if (pos == text.length) break;
- if (cm.getTokenAt({line: i, ch: pos + 1}).className == tokenType) {
- if (pos == nextOpen) ++count;
- else if (!--count) { end = i; break outer; }
- }
- ++pos;
- }
- }
- if (end == null || end == line + 1) return;
- if (hideEnd === true) end++;
- return end;
- };
- CodeMirror.indentRangeFinder = function(cm, line) {
- var tabSize = cm.getOption("tabSize");
- var myIndent = cm.getLineHandle(line).indentation(tabSize), last;
- for (var i = line + 1, end = cm.lineCount(); i < end; ++i) {
- var handle = cm.getLineHandle(i);
- if (!/^\s*$/.test(handle.text)) {
- if (handle.indentation(tabSize) <= myIndent) break;
- last = i;
- }
- }
- if (!last) return null;
- return last + 1;
- };
- CodeMirror.newFoldFunction = function(rangeFinder, markText, hideEnd) {
- var folded = [];
- if (markText == null) markText = '<div style="position: absolute; left: 2px; color:#600">▼</div>%N%';
- function isFolded(cm, n) {
- for (var i = 0; i < folded.length; ++i) {
- var start = cm.lineInfo(folded[i].start);
- if (!start) folded.splice(i--, 1);
- else if (start.line == n) return {pos: i, region: folded[i]};
- }
- }
- function expand(cm, region) {
- cm.clearMarker(region.start);
- for (var i = 0; i < region.hidden.length; ++i)
- cm.showLine(region.hidden[i]);
- }
- return function(cm, line) {
- cm.operation(function() {
- var known = isFolded(cm, line);
- if (known) {
- folded.splice(known.pos, 1);
- expand(cm, known.region);
- } else {
- var end = rangeFinder(cm, line, hideEnd);
- if (end == null) return;
- var hidden = [];
- for (var i = line + 1; i < end; ++i) {
- var handle = cm.hideLine(i);
- if (handle) hidden.push(handle);
- }
- var first = cm.setMarker(line, markText);
- var region = {start: first, hidden: hidden};
- cm.onDeleteLine(first, function() { expand(cm, region); });
- folded.push(region);
- }
- });
- };
- };
|