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