browser/devtools/sourceeditor/codemirror/fold/xml-fold.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 (function() {
michael@0 2 "use strict";
michael@0 3
michael@0 4 var Pos = CodeMirror.Pos;
michael@0 5 function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
michael@0 6
michael@0 7 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";
michael@0 8 var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
michael@0 9 var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
michael@0 10
michael@0 11 function Iter(cm, line, ch, range) {
michael@0 12 this.line = line; this.ch = ch;
michael@0 13 this.cm = cm; this.text = cm.getLine(line);
michael@0 14 this.min = range ? range.from : cm.firstLine();
michael@0 15 this.max = range ? range.to - 1 : cm.lastLine();
michael@0 16 }
michael@0 17
michael@0 18 function tagAt(iter, ch) {
michael@0 19 var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
michael@0 20 return type && /\btag\b/.test(type);
michael@0 21 }
michael@0 22
michael@0 23 function nextLine(iter) {
michael@0 24 if (iter.line >= iter.max) return;
michael@0 25 iter.ch = 0;
michael@0 26 iter.text = iter.cm.getLine(++iter.line);
michael@0 27 return true;
michael@0 28 }
michael@0 29 function prevLine(iter) {
michael@0 30 if (iter.line <= iter.min) return;
michael@0 31 iter.text = iter.cm.getLine(--iter.line);
michael@0 32 iter.ch = iter.text.length;
michael@0 33 return true;
michael@0 34 }
michael@0 35
michael@0 36 function toTagEnd(iter) {
michael@0 37 for (;;) {
michael@0 38 var gt = iter.text.indexOf(">", iter.ch);
michael@0 39 if (gt == -1) { if (nextLine(iter)) continue; else return; }
michael@0 40 if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
michael@0 41 var lastSlash = iter.text.lastIndexOf("/", gt);
michael@0 42 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
michael@0 43 iter.ch = gt + 1;
michael@0 44 return selfClose ? "selfClose" : "regular";
michael@0 45 }
michael@0 46 }
michael@0 47 function toTagStart(iter) {
michael@0 48 for (;;) {
michael@0 49 var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
michael@0 50 if (lt == -1) { if (prevLine(iter)) continue; else return; }
michael@0 51 if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
michael@0 52 xmlTagStart.lastIndex = lt;
michael@0 53 iter.ch = lt;
michael@0 54 var match = xmlTagStart.exec(iter.text);
michael@0 55 if (match && match.index == lt) return match;
michael@0 56 }
michael@0 57 }
michael@0 58
michael@0 59 function toNextTag(iter) {
michael@0 60 for (;;) {
michael@0 61 xmlTagStart.lastIndex = iter.ch;
michael@0 62 var found = xmlTagStart.exec(iter.text);
michael@0 63 if (!found) { if (nextLine(iter)) continue; else return; }
michael@0 64 if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
michael@0 65 iter.ch = found.index + found[0].length;
michael@0 66 return found;
michael@0 67 }
michael@0 68 }
michael@0 69 function toPrevTag(iter) {
michael@0 70 for (;;) {
michael@0 71 var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
michael@0 72 if (gt == -1) { if (prevLine(iter)) continue; else return; }
michael@0 73 if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
michael@0 74 var lastSlash = iter.text.lastIndexOf("/", gt);
michael@0 75 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
michael@0 76 iter.ch = gt + 1;
michael@0 77 return selfClose ? "selfClose" : "regular";
michael@0 78 }
michael@0 79 }
michael@0 80
michael@0 81 function findMatchingClose(iter, tag) {
michael@0 82 var stack = [];
michael@0 83 for (;;) {
michael@0 84 var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
michael@0 85 if (!next || !(end = toTagEnd(iter))) return;
michael@0 86 if (end == "selfClose") continue;
michael@0 87 if (next[1]) { // closing tag
michael@0 88 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
michael@0 89 stack.length = i;
michael@0 90 break;
michael@0 91 }
michael@0 92 if (i < 0 && (!tag || tag == next[2])) return {
michael@0 93 tag: next[2],
michael@0 94 from: Pos(startLine, startCh),
michael@0 95 to: Pos(iter.line, iter.ch)
michael@0 96 };
michael@0 97 } else { // opening tag
michael@0 98 stack.push(next[2]);
michael@0 99 }
michael@0 100 }
michael@0 101 }
michael@0 102 function findMatchingOpen(iter, tag) {
michael@0 103 var stack = [];
michael@0 104 for (;;) {
michael@0 105 var prev = toPrevTag(iter);
michael@0 106 if (!prev) return;
michael@0 107 if (prev == "selfClose") { toTagStart(iter); continue; }
michael@0 108 var endLine = iter.line, endCh = iter.ch;
michael@0 109 var start = toTagStart(iter);
michael@0 110 if (!start) return;
michael@0 111 if (start[1]) { // closing tag
michael@0 112 stack.push(start[2]);
michael@0 113 } else { // opening tag
michael@0 114 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
michael@0 115 stack.length = i;
michael@0 116 break;
michael@0 117 }
michael@0 118 if (i < 0 && (!tag || tag == start[2])) return {
michael@0 119 tag: start[2],
michael@0 120 from: Pos(iter.line, iter.ch),
michael@0 121 to: Pos(endLine, endCh)
michael@0 122 };
michael@0 123 }
michael@0 124 }
michael@0 125 }
michael@0 126
michael@0 127 CodeMirror.registerHelper("fold", "xml", function(cm, start) {
michael@0 128 var iter = new Iter(cm, start.line, 0);
michael@0 129 for (;;) {
michael@0 130 var openTag = toNextTag(iter), end;
michael@0 131 if (!openTag || iter.line != start.line || !(end = toTagEnd(iter))) return;
michael@0 132 if (!openTag[1] && end != "selfClose") {
michael@0 133 var start = Pos(iter.line, iter.ch);
michael@0 134 var close = findMatchingClose(iter, openTag[2]);
michael@0 135 return close && {from: start, to: close.from};
michael@0 136 }
michael@0 137 }
michael@0 138 });
michael@0 139 CodeMirror.tagRangeFinder = CodeMirror.fold.xml; // deprecated
michael@0 140
michael@0 141 CodeMirror.findMatchingTag = function(cm, pos, range) {
michael@0 142 var iter = new Iter(cm, pos.line, pos.ch, range);
michael@0 143 if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
michael@0 144 var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
michael@0 145 var start = end && toTagStart(iter);
michael@0 146 if (!end || end == "selfClose" || !start || cmp(iter, pos) > 0) return;
michael@0 147 var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
michael@0 148
michael@0 149 if (start[1]) { // closing tag
michael@0 150 return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
michael@0 151 } else { // opening tag
michael@0 152 iter = new Iter(cm, to.line, to.ch, range);
michael@0 153 return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
michael@0 154 }
michael@0 155 };
michael@0 156
michael@0 157 CodeMirror.findEnclosingTag = function(cm, pos, range) {
michael@0 158 var iter = new Iter(cm, pos.line, pos.ch, range);
michael@0 159 for (;;) {
michael@0 160 var open = findMatchingOpen(iter);
michael@0 161 if (!open) break;
michael@0 162 var forward = new Iter(cm, pos.line, pos.ch, range);
michael@0 163 var close = findMatchingClose(forward, open.tag);
michael@0 164 if (close) return {open: open, close: close};
michael@0 165 }
michael@0 166 };
michael@0 167
michael@0 168 // Used by addon/edit/closetag.js
michael@0 169 CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
michael@0 170 var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
michael@0 171 return !!findMatchingClose(iter, name);
michael@0 172 };
michael@0 173 })();

mercurial