xml-hint.min.js (4735B)
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 16 function getHints(cm, options) { 17 var tags = options && options.schemaInfo; 18 var quote = (options && options.quoteChar) || '"'; 19 if (!tags) return; 20 var cur = cm.getCursor(), token = cm.getTokenAt(cur); 21 if (token.end > cur.ch) { 22 token.end = cur.ch; 23 token.string = token.string.slice(0, cur.ch - token.start); 24 } 25 var inner = CodeMirror.innerMode(cm.getMode(), token.state); 26 if (inner.mode.name != "xml") return; 27 var result = [], replaceToken = false, prefix; 28 var tag = /\btag\b/.test(token.type) && !/>$/.test(token.string); 29 var tagName = tag && /^\w/.test(token.string), tagStart; 30 31 if (tagName) { 32 var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start); 33 var tagType = /<\/$/.test(before) ? "close" : /<$/.test(before) ? "open" : null; 34 if (tagType) tagStart = token.start - (tagType == "close" ? 2 : 1); 35 } else if (tag && token.string == "<") { 36 tagType = "open"; 37 } else if (tag && token.string == "</") { 38 tagType = "close"; 39 } 40 41 if (!tag && !inner.state.tagName || tagType) { 42 if (tagName) 43 prefix = token.string; 44 replaceToken = tagType; 45 var cx = inner.state.context, curTag = cx && tags[cx.tagName]; 46 var childList = cx ? curTag && curTag.children : tags["!top"]; 47 if (childList && tagType != "close") { 48 for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].lastIndexOf(prefix, 0) == 0) 49 result.push("<" + childList[i]); 50 } else if (tagType != "close") { 51 for (var name in tags) 52 if (tags.hasOwnProperty(name) && name != "!top" && name != "!attrs" && (!prefix || name.lastIndexOf(prefix, 0) == 0)) 53 result.push("<" + name); 54 } 55 if (cx && (!prefix || tagType == "close" && cx.tagName.lastIndexOf(prefix, 0) == 0)) 56 result.push("</" + cx.tagName + ">"); 57 } else { 58 // Attribute completion 59 var curTag = tags[inner.state.tagName], attrs = curTag && curTag.attrs; 60 var globalAttrs = tags["!attrs"]; 61 if (!attrs && !globalAttrs) return; 62 if (!attrs) { 63 attrs = globalAttrs; 64 } else if (globalAttrs) { // Combine tag-local and global attributes 65 var set = {}; 66 for (var nm in globalAttrs) if (globalAttrs.hasOwnProperty(nm)) set[nm] = globalAttrs[nm]; 67 for (var nm in attrs) if (attrs.hasOwnProperty(nm)) set[nm] = attrs[nm]; 68 attrs = set; 69 } 70 if (token.type == "string" || token.string == "=") { // A value 71 var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)), 72 Pos(cur.line, token.type == "string" ? token.start : token.end)); 73 var atName = before.match(/([^\s\u00a0=<>\"\']+)=$/), atValues; 74 if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return; 75 if (typeof atValues == 'function') atValues = atValues.call(this, cm); // Functions can be used to supply values for autocomplete widget 76 if (token.type == "string") { 77 prefix = token.string; 78 var n = 0; 79 if (/['"]/.test(token.string.charAt(0))) { 80 quote = token.string.charAt(0); 81 prefix = token.string.slice(1); 82 n++; 83 } 84 var len = token.string.length; 85 if (/['"]/.test(token.string.charAt(len - 1))) { 86 quote = token.string.charAt(len - 1); 87 prefix = token.string.substr(n, len - 2); 88 } 89 replaceToken = true; 90 } 91 for (var i = 0; i < atValues.length; ++i) if (!prefix || atValues[i].lastIndexOf(prefix, 0) == 0) 92 result.push(quote + atValues[i] + quote); 93 } else { // An attribute name 94 if (token.type == "attribute") { 95 prefix = token.string; 96 replaceToken = true; 97 } 98 for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || attr.lastIndexOf(prefix, 0) == 0)) 99 result.push(attr); 100 } 101 } 102 return { 103 list: result, 104 from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur, 105 to: replaceToken ? Pos(cur.line, token.end) : cur 106 }; 107 } 108 109 CodeMirror.registerHelper("hint", "xml", getHints); 110 });