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 + "]*");
9
var lineText = cm.getLine(line);
14
pos = lineText.indexOf("<", pos);
15
if (-1 == pos) // no tag on line
17
if (pos + 1 < lineText.length && lineText[pos + 1] == "/") { // closing tag
21
// ok we weem to have a start tag
22
if (!lineText.substr(pos + 1).match(xmlNAMERegExp)) { // not a tag name...
26
var gtPos = lineText.indexOf(">", pos + 1);
27
if (-1 == gtPos) { // end of start tag not in line
30
var lastLine = cm.lineCount();
31
while (l < lastLine && !foundGt) {
32
var lt = cm.getLine(l);
33
var gt = lt.indexOf(">");
34
if (-1 != gt) { // found a >
36
var slash = lt.lastIndexOf("/", gt);
37
if (-1 != slash && slash < gt) {
38
var str = lineText.substr(slash, gt - slash + 1);
39
if (!str.match( /\/\s*\>/ )) { // yep, that's the end of empty tag
40
if (hideEnd === true) l++;
50
var slashPos = lineText.lastIndexOf("/", gtPos);
51
if (-1 == slashPos) { // cannot be empty tag
56
// check if really empty tag
57
var str = lineText.substr(slashPos, gtPos - slashPos + 1);
58
if (!str.match( /\/\s*\>/ )) { // finally not empty
65
var subLine = lineText.substr(pos + 1);
66
tag = subLine.match(xmlNAMERegExp);
68
// we have an element name, wooohooo !
70
// do we have the close tag on same line ???
71
if (-1 != lineText.indexOf("</" + tag + ">", pos)) // yep
75
// we don't, so we have a candidate...
85
var startTag = "(\\<\\/" + tag + "\\>)|(\\<" + tag + "\\>)|(\\<" + tag + "\\s)|(\\<" + tag + "$)";
86
var startTagRegExp = new RegExp(startTag, "g");
87
var endTag = "</" + tag + ">";
90
var lastLine = cm.lineCount();
91
while (l < lastLine) {
92
lineText = cm.getLine(l);
93
var match = lineText.match(startTagRegExp);
95
for (var i = 0; i < match.length; i++) {
96
if (match[i] == endTag)
101
if (hideEnd === true) l++;
112
CodeMirror.braceRangeFinder = function(cm, line, hideEnd) {
113
var lineText = cm.getLine(line), at = lineText.length, startChar, tokenType;
115
var found = lineText.lastIndexOf("{", at);
116
if (found < 0) break;
117
tokenType = cm.getTokenAt({line: line, ch: found}).className;
118
if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; }
121
if (startChar == null || lineText.lastIndexOf("}") > startChar) return;
122
var count = 1, lastLine = cm.lineCount(), end;
123
outer: for (var i = line + 1; i < lastLine; ++i) {
124
var text = cm.getLine(i), pos = 0;
126
var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos);
127
if (nextOpen < 0) nextOpen = text.length;
128
if (nextClose < 0) nextClose = text.length;
129
pos = Math.min(nextOpen, nextClose);
130
if (pos == text.length) break;
131
if (cm.getTokenAt({line: i, ch: pos + 1}).className == tokenType) {
132
if (pos == nextOpen) ++count;
133
else if (!--count) { end = i; break outer; }
138
if (end == null || end == line + 1) return;
139
if (hideEnd === true) end++;
143
CodeMirror.indentRangeFinder = function(cm, line) {
144
var tabSize = cm.getOption("tabSize");
145
var myIndent = cm.getLineHandle(line).indentation(tabSize), last;
146
for (var i = line + 1, end = cm.lineCount(); i < end; ++i) {
147
var handle = cm.getLineHandle(i);
148
if (!/^\s*$/.test(handle.text)) {
149
if (handle.indentation(tabSize) <= myIndent) break;
153
if (!last) return null;
157
CodeMirror.newFoldFunction = function(rangeFinder, markText, hideEnd) {
159
if (markText == null) markText = '<div style="position: absolute; left: 2px; color:#600">▼</div>%N%';
161
function isFolded(cm, n) {
162
for (var i = 0; i < folded.length; ++i) {
163
var start = cm.lineInfo(folded[i].start);
164
if (!start) folded.splice(i--, 1);
165
else if (start.line == n) return {pos: i, region: folded[i]};
169
function expand(cm, region) {
170
cm.clearMarker(region.start);
171
for (var i = 0; i < region.hidden.length; ++i)
172
cm.showLine(region.hidden[i]);
175
return function(cm, line) {
176
cm.operation(function() {
177
var known = isFolded(cm, line);
179
folded.splice(known.pos, 1);
180
expand(cm, known.region);
182
var end = rangeFinder(cm, line, hideEnd);
183
if (end == null) return;
185
for (var i = line + 1; i < end; ++i) {
186
var handle = cm.hideLine(i);
187
if (handle) hidden.push(handle);
189
var first = cm.setMarker(line, markText);
190
var region = {start: first, hidden: hidden};
191
cm.onDeleteLine(first, function() { expand(cm, region); });