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