foldcode.js
196 lines
| 6.2 KiB
| application/javascript
|
JavascriptLexer
Brian Granger
|
r5970 | // the tagRangeFinder function is | ||
// Copyright (C) 2011 by Daniel Glazman <daniel@glazman.org> | ||||
// released under the MIT license (../../LICENSE) like the rest of CodeMirror | ||||
Matthias BUSSONNIER
|
r8053 | CodeMirror.tagRangeFinder = function(cm, line, hideEnd) { | ||
Brian Granger
|
r5970 | 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"; | ||
Matthias BUSSONNIER
|
r8053 | var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; | ||
Brian Granger
|
r5970 | 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); | ||||
Matthias BUSSONNIER
|
r8053 | if (!str.match( /\/\s*\>/ )) { // yep, that's the end of empty tag | ||
if (hideEnd === true) l++; | ||||
return l; | ||||
} | ||||
Brian Granger
|
r5970 | } | ||
} | ||||
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) { | ||||
Matthias BUSSONNIER
|
r8053 | var startTag = "(\\<\\/" + tag + "\\>)|(\\<" + tag + "\\>)|(\\<" + tag + "\\s)|(\\<" + tag + "$)"; | ||
Brian Granger
|
r5970 | 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++; | ||||
Matthias BUSSONNIER
|
r8053 | if (!depth) { | ||
if (hideEnd === true) l++; | ||||
return l; | ||||
} | ||||
Brian Granger
|
r5970 | } | ||
} | ||||
l++; | ||||
} | ||||
return; | ||||
} | ||||
}; | ||||
Matthias BUSSONNIER
|
r8053 | 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; | ||||
Brian Granger
|
r5941 | 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; | ||||
Matthias BUSSONNIER
|
r8053 | if (hideEnd === true) end++; | ||
Brian Granger
|
r5941 | return end; | ||
}; | ||||
Matthias BUSSONNIER
|
r8053 | 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; | ||||
}; | ||||
Brian Granger
|
r5941 | |||
Matthias BUSSONNIER
|
r8053 | CodeMirror.newFoldFunction = function(rangeFinder, markText, hideEnd) { | ||
Brian Granger
|
r5941 | 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 { | ||||
Matthias BUSSONNIER
|
r8053 | var end = rangeFinder(cm, line, hideEnd); | ||
Brian Granger
|
r5941 | 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); | ||||
} | ||||
}); | ||||
}; | ||||
}; | ||||