codemirror-rails 3.13 → 3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/lib/codemirror/rails/version.rb +2 -2
  3. data/vendor/assets/javascripts/codemirror.js +328 -250
  4. data/vendor/assets/javascripts/codemirror/addons/comment/comment.js +7 -6
  5. data/vendor/assets/javascripts/codemirror/addons/edit/closebrackets.js +33 -7
  6. data/vendor/assets/javascripts/codemirror/addons/edit/matchbrackets.js +14 -10
  7. data/vendor/assets/javascripts/codemirror/addons/edit/trailingspace.js +15 -0
  8. data/vendor/assets/javascripts/codemirror/addons/fold/brace-fold.js +70 -17
  9. data/vendor/assets/javascripts/codemirror/addons/fold/foldcode.js +56 -20
  10. data/vendor/assets/javascripts/codemirror/addons/fold/xml-fold.js +135 -39
  11. data/vendor/assets/javascripts/codemirror/addons/hint/html-hint.js +324 -571
  12. data/vendor/assets/javascripts/codemirror/addons/hint/show-hint.js +199 -109
  13. data/vendor/assets/javascripts/codemirror/addons/hint/xml-hint.js +60 -113
  14. data/vendor/assets/javascripts/codemirror/addons/lint/coffeescript-lint.js +24 -0
  15. data/vendor/assets/javascripts/codemirror/addons/merge/merge.js +431 -0
  16. data/vendor/assets/javascripts/codemirror/addons/mode/multiplex.js +7 -1
  17. data/vendor/assets/javascripts/codemirror/addons/search/match-highlighter.js +46 -20
  18. data/vendor/assets/javascripts/codemirror/addons/search/search.js +1 -1
  19. data/vendor/assets/javascripts/codemirror/keymaps/emacs.js +370 -13
  20. data/vendor/assets/javascripts/codemirror/keymaps/extra.js +43 -0
  21. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +535 -214
  22. data/vendor/assets/javascripts/codemirror/modes/clike.js +56 -0
  23. data/vendor/assets/javascripts/codemirror/modes/javascript.js +19 -14
  24. data/vendor/assets/javascripts/codemirror/modes/markdown.js +2 -2
  25. data/vendor/assets/javascripts/codemirror/modes/ruby.js +67 -16
  26. data/vendor/assets/javascripts/codemirror/modes/smarty.js +167 -110
  27. data/vendor/assets/javascripts/codemirror/modes/sql.js +97 -15
  28. data/vendor/assets/javascripts/codemirror/modes/xml.js +14 -18
  29. data/vendor/assets/stylesheets/codemirror.css +0 -1
  30. data/vendor/assets/stylesheets/codemirror/addons/merge/merge.css +82 -0
  31. metadata +7 -2
@@ -30,7 +30,7 @@
30
30
  if (firstLine == null) return;
31
31
  var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1);
32
32
  var pad = options.padding == null ? " " : options.padding;
33
- var blankLines = options.commentBlankLines;
33
+ var blankLines = options.commentBlankLines || from.line == to.line;
34
34
 
35
35
  self.operation(function() {
36
36
  if (options.indent) {
@@ -90,14 +90,14 @@
90
90
 
91
91
  // Try finding line comments
92
92
  var lineString = options.lineComment || mode.lineComment, lines = [];
93
- var pad = options.padding == null ? " " : options.padding;
94
- lineComment: for(;;) {
95
- if (!lineString) break;
93
+ var pad = options.padding == null ? " " : options.padding, didSomething;
94
+ lineComment: {
95
+ if (!lineString) break lineComment;
96
96
  for (var i = start; i <= end; ++i) {
97
97
  var line = self.getLine(i);
98
98
  var found = line.indexOf(lineString);
99
99
  if (found == -1 && (i != end || i == start) && nonWS.test(line)) break lineComment;
100
- if (i != start && nonWS.test(line.slice(0, found))) break lineComment;
100
+ if (i != start && found > -1 && nonWS.test(line.slice(0, found))) break lineComment;
101
101
  lines.push(line);
102
102
  }
103
103
  self.operation(function() {
@@ -106,10 +106,11 @@
106
106
  var pos = line.indexOf(lineString), endPos = pos + lineString.length;
107
107
  if (pos < 0) continue;
108
108
  if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;
109
+ didSomething = true;
109
110
  self.replaceRange("", Pos(i, pos), Pos(i, endPos));
110
111
  }
111
112
  });
112
- return true;
113
+ if (didSomething) return true;
113
114
  }
114
115
 
115
116
  // Try block comments
@@ -1,23 +1,36 @@
1
1
  (function() {
2
2
  var DEFAULT_BRACKETS = "()[]{}''\"\"";
3
+ var DEFAULT_EXPLODE_ON_ENTER = "[]{}";
3
4
  var SPACE_CHAR_REGEX = /\s/;
4
5
 
5
6
  CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
6
- var wasOn = old && old != CodeMirror.Init;
7
- if (val && !wasOn)
8
- cm.addKeyMap(buildKeymap(typeof val == "string" ? val : DEFAULT_BRACKETS));
9
- else if (!val && wasOn)
7
+ if (old != CodeMirror.Init && old)
10
8
  cm.removeKeyMap("autoCloseBrackets");
9
+ if (!val) return;
10
+ var pairs = DEFAULT_BRACKETS, explode = DEFAULT_EXPLODE_ON_ENTER;
11
+ if (typeof val == "string") pairs = val;
12
+ else if (typeof val == "object") {
13
+ if (val.pairs != null) pairs = val.pairs;
14
+ if (val.explode != null) explode = val.explode;
15
+ }
16
+ var map = buildKeymap(pairs);
17
+ if (explode) map.Enter = buildExplodeHandler(explode);
18
+ cm.addKeyMap(map);
11
19
  });
12
20
 
21
+ function charsAround(cm, pos) {
22
+ var str = cm.getRange(CodeMirror.Pos(pos.line, pos.ch - 1),
23
+ CodeMirror.Pos(pos.line, pos.ch + 1));
24
+ return str.length == 2 ? str : null;
25
+ }
26
+
13
27
  function buildKeymap(pairs) {
14
28
  var map = {
15
29
  name : "autoCloseBrackets",
16
30
  Backspace: function(cm) {
17
31
  if (cm.somethingSelected()) return CodeMirror.Pass;
18
- var cur = cm.getCursor(), line = cm.getLine(cur.line);
19
- if (cur.ch && cur.ch < line.length &&
20
- pairs.indexOf(line.slice(cur.ch - 1, cur.ch + 1)) % 2 == 0)
32
+ var cur = cm.getCursor(), around = charsAround(cm, cur);
33
+ if (around && pairs.indexOf(around) % 2 == 0)
21
34
  cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1));
22
35
  else
23
36
  return CodeMirror.Pass;
@@ -51,4 +64,17 @@
51
64
  })(pairs.charAt(i), pairs.charAt(i + 1));
52
65
  return map;
53
66
  }
67
+
68
+ function buildExplodeHandler(pairs) {
69
+ return function(cm) {
70
+ var cur = cm.getCursor(), around = charsAround(cm, cur);
71
+ if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
72
+ cm.operation(function() {
73
+ var newPos = CodeMirror.Pos(cur.line + 1, 0);
74
+ cm.replaceSelection("\n\n", {anchor: newPos, head: newPos}, "+input");
75
+ cm.indentLine(cur.line + 1, null, true);
76
+ cm.indentLine(cur.line + 2, null, true);
77
+ });
78
+ };
79
+ }
54
80
  })();
@@ -5,25 +5,26 @@
5
5
  var Pos = CodeMirror.Pos;
6
6
 
7
7
  var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
8
- function findMatchingBracket(cm) {
9
- var maxScanLen = cm.state._matchBrackets.maxScanLineLength || 10000;
8
+ function findMatchingBracket(cm, where, strict) {
9
+ var state = cm.state.matchBrackets;
10
+ var maxScanLen = (state && state.maxScanLineLength) || 10000;
10
11
 
11
- var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
12
+ var cur = where || cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
12
13
  var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
13
14
  if (!match) return null;
14
15
  var forward = match.charAt(1) == ">", d = forward ? 1 : -1;
15
- var style = cm.getTokenAt(Pos(cur.line, pos + 1)).type;
16
+ if (strict && forward != (pos == cur.ch)) return null;
17
+ var style = cm.getTokenTypeAt(Pos(cur.line, pos + 1));
16
18
 
17
19
  var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
18
20
  function scan(line, lineNo, start) {
19
21
  if (!line.text) return;
20
22
  var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1;
21
23
  if (line.text.length > maxScanLen) return null;
22
- var checkTokenStyles = line.text.length < 1000;
23
24
  if (start != null) pos = start + d;
24
25
  for (; pos != end; pos += d) {
25
26
  var ch = line.text.charAt(pos);
26
- if (re.test(ch) && (!checkTokenStyles || cm.getTokenAt(Pos(lineNo, pos + 1)).type == style)) {
27
+ if (re.test(ch) && cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style) {
27
28
  var match = matching[ch];
28
29
  if (match.charAt(1) == ">" == forward) stack.push(ch);
29
30
  else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
@@ -36,12 +37,13 @@
36
37
  else found = scan(cm.getLineHandle(i), i);
37
38
  if (found) break;
38
39
  }
39
- return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos), match: found && found.match};
40
+ return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos),
41
+ match: found && found.match, forward: forward};
40
42
  }
41
43
 
42
44
  function matchBrackets(cm, autoclear) {
43
45
  // Disable brace matching in long lines, since it'll cause hugely slow updates
44
- var maxHighlightLen = cm.state._matchBrackets.maxHighlightLineLength || 1000;
46
+ var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
45
47
  var found = findMatchingBracket(cm);
46
48
  if (!found || cm.getLine(found.from.line).length > maxHighlightLen ||
47
49
  found.to && cm.getLine(found.to.line).length > maxHighlightLen)
@@ -72,11 +74,13 @@
72
74
  if (old && old != CodeMirror.Init)
73
75
  cm.off("cursorActivity", doMatchBrackets);
74
76
  if (val) {
75
- cm.state._matchBrackets = typeof val == "object" ? val : {};
77
+ cm.state.matchBrackets = typeof val == "object" ? val : {};
76
78
  cm.on("cursorActivity", doMatchBrackets);
77
79
  }
78
80
  });
79
81
 
80
82
  CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
81
- CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);});
83
+ CodeMirror.defineExtension("findMatchingBracket", function(pos, strict){
84
+ return findMatchingBracket(this, pos, strict);
85
+ });
82
86
  })();
@@ -0,0 +1,15 @@
1
+ CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) {
2
+ if (prev == CodeMirror.Init) prev = false;
3
+ if (prev && !val)
4
+ cm.removeOverlay("trailingspace");
5
+ else if (!prev && val)
6
+ cm.addOverlay({
7
+ token: function(stream) {
8
+ for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {}
9
+ if (i > stream.pos) { stream.pos = i; return null; }
10
+ stream.pos = l;
11
+ return "trailingspace";
12
+ },
13
+ name: "trailingspace"
14
+ });
15
+ });
@@ -1,23 +1,33 @@
1
1
  CodeMirror.braceRangeFinder = function(cm, start) {
2
2
  var line = start.line, lineText = cm.getLine(line);
3
- var at = lineText.length, startChar, tokenType;
4
- for (; at > 0;) {
5
- var found = lineText.lastIndexOf("{", at);
6
- var startToken = '{', endToken = '}';
7
- if (found < start.ch) {
8
- found = lineText.lastIndexOf("[", at);
9
- if (found < start.ch) break;
10
- startToken = '['; endToken = ']';
3
+ var startCh, tokenType;
4
+
5
+ function findOpening(openCh) {
6
+ for (var at = start.ch, pass = 0;;) {
7
+ var found = lineText.lastIndexOf(openCh, at - 1);
8
+ if (found == -1) {
9
+ if (pass == 1) break;
10
+ pass = 1;
11
+ at = lineText.length;
12
+ continue;
13
+ }
14
+ if (pass == 1 && found < start.ch) break;
15
+ tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type;
16
+ if (!/^(comment|string)/.test(tokenType)) return found + 1;
17
+ at = found - 1;
11
18
  }
19
+ }
12
20
 
13
- tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type;
14
- if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; }
15
- at = found - 1;
21
+ var startToken = "{", endToken = "}", startCh = findOpening("{");
22
+ if (startCh == null) {
23
+ startToken = "[", endToken = "]";
24
+ startCh = findOpening("[");
16
25
  }
17
- if (startChar == null || lineText.lastIndexOf(startToken) > startChar) return;
18
- var count = 1, lastLine = cm.lineCount(), end, endCh;
19
- outer: for (var i = line + 1; i < lastLine; ++i) {
20
- var text = cm.getLine(i), pos = 0;
26
+
27
+ if (startCh == null) return;
28
+ var count = 1, lastLine = cm.lastLine(), end, endCh;
29
+ outer: for (var i = line; i <= lastLine; ++i) {
30
+ var text = cm.getLine(i), pos = i == line ? startCh : 0;
21
31
  for (;;) {
22
32
  var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
23
33
  if (nextOpen < 0) nextOpen = text.length;
@@ -31,7 +41,50 @@ CodeMirror.braceRangeFinder = function(cm, start) {
31
41
  ++pos;
32
42
  }
33
43
  }
34
- if (end == null || end == line + 1) return;
35
- return {from: CodeMirror.Pos(line, startChar + 1),
44
+ if (end == null || line == end && endCh == startCh) return;
45
+ return {from: CodeMirror.Pos(line, startCh),
36
46
  to: CodeMirror.Pos(end, endCh)};
37
47
  };
48
+
49
+ CodeMirror.importRangeFinder = function(cm, start) {
50
+ function hasImport(line) {
51
+ if (line < cm.firstLine() || line > cm.lastLine()) return null;
52
+ var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
53
+ if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
54
+ if (start.type != "keyword" || start.string != "import") return null;
55
+ // Now find closing semicolon, return its position
56
+ for (var i = line, e = Math.min(cm.lastLine(), line + 10); i <= e; ++i) {
57
+ var text = cm.getLine(i), semi = text.indexOf(";");
58
+ if (semi != -1) return {startCh: start.end, end: CodeMirror.Pos(i, semi)};
59
+ }
60
+ }
61
+
62
+ var start = start.line, has = hasImport(start), prev;
63
+ if (!has || hasImport(start - 1) || ((prev = hasImport(start - 2)) && prev.end.line == start - 1))
64
+ return null;
65
+ for (var end = has.end;;) {
66
+ var next = hasImport(end.line + 1);
67
+ if (next == null) break;
68
+ end = next.end;
69
+ }
70
+ return {from: cm.clipPos(CodeMirror.Pos(start, has.startCh + 1)), to: end};
71
+ };
72
+
73
+ CodeMirror.includeRangeFinder = function(cm, start) {
74
+ function hasInclude(line) {
75
+ if (line < cm.firstLine() || line > cm.lastLine()) return null;
76
+ var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
77
+ if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
78
+ if (start.type == "meta" && start.string.slice(0, 8) == "#include") return start.start + 8;
79
+ }
80
+
81
+ var start = start.line, has = hasInclude(start);
82
+ if (has == null || hasInclude(start - 1) != null) return null;
83
+ for (var end = start;;) {
84
+ var next = hasInclude(end + 1);
85
+ if (next == null) break;
86
+ ++end;
87
+ }
88
+ return {from: CodeMirror.Pos(start, has + 1),
89
+ to: cm.clipPos(CodeMirror.Pos(end))};
90
+ };
@@ -1,32 +1,68 @@
1
- CodeMirror.newFoldFunction = function(rangeFinder, widget) {
2
- if (widget == null) widget = "\u2194";
3
- if (typeof widget == "string") {
4
- var text = document.createTextNode(widget);
5
- widget = document.createElement("span");
6
- widget.appendChild(text);
7
- widget.className = "CodeMirror-foldmarker";
8
- }
1
+ (function() {
2
+ "use strict";
9
3
 
10
- return function(cm, pos) {
4
+ function doFold(cm, pos, options) {
5
+ var finder = options.call ? options : (options && options.rangeFinder);
6
+ if (!finder) return;
11
7
  if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0);
12
- var range = rangeFinder(cm, pos);
13
- if (!range) return;
14
-
15
- var present = cm.findMarksAt(range.from), cleared = 0;
16
- for (var i = 0; i < present.length; ++i) {
17
- if (present[i].__isFold) {
18
- ++cleared;
19
- present[i].clear();
8
+ var minSize = options && options.minFoldSize || 0;
9
+
10
+ function getRange(allowFolded) {
11
+ var range = finder(cm, pos);
12
+ if (!range || range.to.line - range.from.line < minSize) return null;
13
+ var marks = cm.findMarksAt(range.from);
14
+ for (var i = 0; i < marks.length; ++i) {
15
+ if (marks[i].__isFold) {
16
+ if (!allowFolded) return null;
17
+ range.cleared = true;
18
+ marks[i].clear();
19
+ }
20
20
  }
21
+ return range;
22
+ }
23
+
24
+ var range = getRange(true);
25
+ if (options && options.scanUp) while (!range && pos.line > cm.firstLine()) {
26
+ pos = CodeMirror.Pos(pos.line - 1, 0);
27
+ range = getRange(false);
21
28
  }
22
- if (cleared) return;
29
+ if (!range || range.cleared) return;
23
30
 
24
- var myWidget = widget.cloneNode(true);
31
+ var myWidget = makeWidget(options);
25
32
  CodeMirror.on(myWidget, "mousedown", function() {myRange.clear();});
26
33
  var myRange = cm.markText(range.from, range.to, {
27
34
  replacedWith: myWidget,
28
35
  clearOnEnter: true,
29
36
  __isFold: true
30
37
  });
38
+ }
39
+
40
+ function makeWidget(options) {
41
+ var widget = (options && options.widget) || "\u2194";
42
+ if (typeof widget == "string") {
43
+ var text = document.createTextNode(widget);
44
+ widget = document.createElement("span");
45
+ widget.appendChild(text);
46
+ widget.className = "CodeMirror-foldmarker";
47
+ }
48
+ return widget;
49
+ }
50
+
51
+ // Clumsy backwards-compatible interface
52
+ CodeMirror.newFoldFunction = function(rangeFinder, widget) {
53
+ return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); };
54
+ };
55
+
56
+ // New-style interface
57
+ CodeMirror.defineExtension("foldCode", function(pos, options) { doFold(this, pos, options); });
58
+
59
+ CodeMirror.combineRangeFinders = function() {
60
+ var funcs = Array.prototype.slice.call(arguments, 0);
61
+ return function(cm, start) {
62
+ for (var i = 0; i < funcs.length; ++i) {
63
+ var found = funcs[i](cm, start);
64
+ if (found) return found;
65
+ }
66
+ };
31
67
  };
32
- };
68
+ })();
@@ -1,64 +1,160 @@
1
- CodeMirror.tagRangeFinder = (function() {
1
+ (function() {
2
+ "use strict";
3
+
4
+ var Pos = CodeMirror.Pos;
5
+
2
6
  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";
3
7
  var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
4
8
  var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
5
9
 
6
- return function(cm, start) {
7
- var line = start.line, ch = start.ch, lineText = cm.getLine(line);
10
+ function Iter(cm, line, ch) {
11
+ this.line = line; this.ch = ch;
12
+ this.cm = cm; this.text = cm.getLine(line);
13
+ }
8
14
 
9
- function nextLine() {
10
- if (line >= cm.lastLine()) return;
11
- ch = 0;
12
- lineText = cm.getLine(++line);
13
- return true;
14
- }
15
- function toTagEnd() {
16
- for (;;) {
17
- var gt = lineText.indexOf(">", ch);
18
- if (gt == -1) { if (nextLine()) continue; else return; }
19
- var lastSlash = lineText.lastIndexOf("/", gt);
20
- var selfClose = lastSlash > -1 && /^\s*$/.test(lineText.slice(lastSlash + 1, gt));
21
- ch = gt + 1;
22
- return selfClose ? "selfClose" : "regular";
23
- }
15
+ function tagAt(iter, ch) {
16
+ var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
17
+ return type && /\btag\b/.test(type);
18
+ }
19
+
20
+ function nextLine(iter) {
21
+ if (iter.line >= iter.cm.lastLine()) return;
22
+ iter.ch = 0;
23
+ iter.text = iter.cm.getLine(++iter.line);
24
+ return true;
25
+ }
26
+ function prevLine(iter) {
27
+ if (iter.line <= iter.cm.firstLine()) return;
28
+ iter.text = iter.cm.getLine(--iter.line);
29
+ iter.ch = iter.text.length;
30
+ return true;
31
+ }
32
+
33
+ function toTagEnd(iter) {
34
+ for (;;) {
35
+ var gt = iter.text.indexOf(">", iter.ch);
36
+ if (gt == -1) { if (nextLine(iter)) continue; else return; }
37
+ if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
38
+ var lastSlash = iter.text.lastIndexOf("/", gt);
39
+ var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
40
+ iter.ch = gt + 1;
41
+ return selfClose ? "selfClose" : "regular";
24
42
  }
25
- function toNextTag() {
26
- for (;;) {
27
- xmlTagStart.lastIndex = ch;
28
- var found = xmlTagStart.exec(lineText);
29
- if (!found) { if (nextLine()) continue; else return; }
30
- ch = found.index + found[0].length;
31
- return found;
32
- }
43
+ }
44
+ function toTagStart(iter) {
45
+ for (;;) {
46
+ var lt = iter.text.lastIndexOf("<", iter.ch - 1);
47
+ if (lt == -1) { if (prevLine(iter)) continue; else return; }
48
+ if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
49
+ xmlTagStart.lastIndex = lt;
50
+ iter.ch = lt;
51
+ var match = xmlTagStart.exec(iter.text);
52
+ if (match && match.index == lt) return match;
33
53
  }
54
+ }
34
55
 
35
- var stack = [], startCh;
56
+ function toNextTag(iter) {
36
57
  for (;;) {
37
- var openTag = toNextTag(), end;
38
- if (!openTag || line != start.line || !(end = toTagEnd())) return;
39
- if (!openTag[1] && end != "selfClose") {
40
- stack.push(openTag[2]);
41
- startCh = ch;
42
- break;
43
- }
58
+ xmlTagStart.lastIndex = iter.ch;
59
+ var found = xmlTagStart.exec(iter.text);
60
+ if (!found) { if (nextLine(iter)) continue; else return; }
61
+ if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
62
+ iter.ch = found.index + found[0].length;
63
+ return found;
64
+ }
65
+ }
66
+ function toPrevTag(iter) {
67
+ for (;;) {
68
+ var gt = iter.text.lastIndexOf(">", iter.ch - 1);
69
+ if (gt == -1) { if (prevLine(iter)) continue; else return; }
70
+ if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
71
+ var lastSlash = iter.text.lastIndexOf("/", gt);
72
+ var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
73
+ iter.ch = gt + 1;
74
+ return selfClose ? "selfClose" : "regular";
44
75
  }
76
+ }
45
77
 
78
+ function findMatchingClose(iter, tag) {
79
+ var stack = [];
46
80
  for (;;) {
47
- var next = toNextTag(), end, tagLine = line, tagCh = ch - (next ? next[0].length : 0);
48
- if (!next || !(end = toTagEnd())) return;
81
+ var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
82
+ if (!next || !(end = toTagEnd(iter))) return;
49
83
  if (end == "selfClose") continue;
50
84
  if (next[1]) { // closing tag
51
85
  for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
52
86
  stack.length = i;
53
87
  break;
54
88
  }
55
- if (!stack.length) return {
56
- from: CodeMirror.Pos(start.line, startCh),
57
- to: CodeMirror.Pos(tagLine, tagCh)
89
+ if (i < 0 && (!tag || tag == next[2])) return {
90
+ tag: next[2],
91
+ from: Pos(startLine, startCh),
92
+ to: Pos(iter.line, iter.ch)
58
93
  };
59
94
  } else { // opening tag
60
95
  stack.push(next[2]);
61
96
  }
62
97
  }
98
+ }
99
+ function findMatchingOpen(iter, tag) {
100
+ var stack = [];
101
+ for (;;) {
102
+ var prev = toPrevTag(iter);
103
+ if (!prev) return;
104
+ if (prev == "selfClose") { toTagStart(iter); continue; }
105
+ var endLine = iter.line, endCh = iter.ch;
106
+ var start = toTagStart(iter);
107
+ if (!start) return;
108
+ if (start[1]) { // closing tag
109
+ stack.push(start[2]);
110
+ } else { // opening tag
111
+ for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
112
+ stack.length = i;
113
+ break;
114
+ }
115
+ if (i < 0 && (!tag || tag == start[2])) return {
116
+ tag: start[2],
117
+ from: Pos(iter.line, iter.ch),
118
+ to: Pos(endLine, endCh)
119
+ };
120
+ }
121
+ }
122
+ }
123
+
124
+ CodeMirror.tagRangeFinder = function(cm, start) {
125
+ var iter = new Iter(cm, start.line, 0);
126
+ for (;;) {
127
+ var openTag = toNextTag(iter), end;
128
+ if (!openTag || iter.line != start.line || !(end = toTagEnd(iter))) return;
129
+ if (!openTag[1] && end != "selfClose") {
130
+ var start = Pos(iter.line, iter.ch);
131
+ var close = findMatchingClose(iter, openTag[2]);
132
+ return close && {from: start, to: close.from};
133
+ }
134
+ }
135
+ };
136
+
137
+ CodeMirror.findMatchingTag = function(cm, pos) {
138
+ var iter = new Iter(cm, pos.line, pos.ch);
139
+ var end = toTagEnd(iter), start = toTagStart(iter);
140
+ if (!end || end == "selfClose" || !start) return;
141
+
142
+ if (start[1]) { // closing tag
143
+ return findMatchingOpen(iter, start[2]);
144
+ } else { // opening tag
145
+ toTagEnd(iter);
146
+ return findMatchingClose(iter, start[2]);
147
+ }
148
+ };
149
+
150
+ CodeMirror.findEnclosingTag = function(cm, pos) {
151
+ var iter = new Iter(cm, pos.line, pos.ch);
152
+ for (;;) {
153
+ var open = findMatchingOpen(iter);
154
+ if (!open) break;
155
+ var forward = new Iter(cm, pos.line, pos.ch);
156
+ var close = findMatchingClose(forward, open.tag);
157
+ if (close) return {open: open, close: close};
158
+ }
63
159
  };
64
160
  })();