Mercurial
comparison .cms/lib/codemirror/addon/hint/xml-hint.js @ 0:78edf6b517a0 draft
24.10
author | Coffee CMS <info@coffee-cms.ru> |
---|---|
date | Fri, 11 Oct 2024 22:40:23 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:78edf6b517a0 |
---|---|
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others | |
2 // Distributed under an MIT license: https://codemirror.net/5/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 matches(hint, typed, matchInMiddle) { | |
17 if (matchInMiddle) return hint.indexOf(typed) >= 0; | |
18 else return hint.lastIndexOf(typed, 0) == 0; | |
19 } | |
20 | |
21 function getHints(cm, options) { | |
22 var tags = options && options.schemaInfo; | |
23 var quote = (options && options.quoteChar) || '"'; | |
24 var matchInMiddle = options && options.matchInMiddle; | |
25 if (!tags) return; | |
26 var cur = cm.getCursor(), token = cm.getTokenAt(cur); | |
27 if (token.end > cur.ch) { | |
28 token.end = cur.ch; | |
29 token.string = token.string.slice(0, cur.ch - token.start); | |
30 } | |
31 var inner = CodeMirror.innerMode(cm.getMode(), token.state); | |
32 if (!inner.mode.xmlCurrentTag) return | |
33 var result = [], replaceToken = false, prefix; | |
34 var tag = /\btag\b/.test(token.type) && !/>$/.test(token.string); | |
35 var tagName = tag && /^\w/.test(token.string), tagStart; | |
36 | |
37 if (tagName) { | |
38 var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start); | |
39 var tagType = /<\/$/.test(before) ? "close" : /<$/.test(before) ? "open" : null; | |
40 if (tagType) tagStart = token.start - (tagType == "close" ? 2 : 1); | |
41 } else if (tag && token.string == "<") { | |
42 tagType = "open"; | |
43 } else if (tag && token.string == "</") { | |
44 tagType = "close"; | |
45 } | |
46 | |
47 var tagInfo = inner.mode.xmlCurrentTag(inner.state) | |
48 if (!tag && !tagInfo || tagType) { | |
49 if (tagName) | |
50 prefix = token.string; | |
51 replaceToken = tagType; | |
52 var context = inner.mode.xmlCurrentContext ? inner.mode.xmlCurrentContext(inner.state) : [] | |
53 var inner = context.length && context[context.length - 1] | |
54 var curTag = inner && tags[inner] | |
55 var childList = inner ? curTag && curTag.children : tags["!top"]; | |
56 if (childList && tagType != "close") { | |
57 for (var i = 0; i < childList.length; ++i) if (!prefix || matches(childList[i], prefix, matchInMiddle)) | |
58 result.push("<" + childList[i]); | |
59 } else if (tagType != "close") { | |
60 for (var name in tags) | |
61 if (tags.hasOwnProperty(name) && name != "!top" && name != "!attrs" && (!prefix || matches(name, prefix, matchInMiddle))) | |
62 result.push("<" + name); | |
63 } | |
64 if (inner && (!prefix || tagType == "close" && matches(inner, prefix, matchInMiddle))) | |
65 result.push("</" + inner + ">"); | |
66 } else { | |
67 // Attribute completion | |
68 var curTag = tagInfo && tags[tagInfo.name], attrs = curTag && curTag.attrs; | |
69 var globalAttrs = tags["!attrs"]; | |
70 if (!attrs && !globalAttrs) return; | |
71 if (!attrs) { | |
72 attrs = globalAttrs; | |
73 } else if (globalAttrs) { // Combine tag-local and global attributes | |
74 var set = {}; | |
75 for (var nm in globalAttrs) if (globalAttrs.hasOwnProperty(nm)) set[nm] = globalAttrs[nm]; | |
76 for (var nm in attrs) if (attrs.hasOwnProperty(nm)) set[nm] = attrs[nm]; | |
77 attrs = set; | |
78 } | |
79 if (token.type == "string" || token.string == "=") { // A value | |
80 var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)), | |
81 Pos(cur.line, token.type == "string" ? token.start : token.end)); | |
82 var atName = before.match(/([^\s\u00a0=<>\"\']+)=$/), atValues; | |
83 if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return; | |
84 if (typeof atValues == 'function') atValues = atValues.call(this, cm); // Functions can be used to supply values for autocomplete widget | |
85 if (token.type == "string") { | |
86 prefix = token.string; | |
87 var n = 0; | |
88 if (/['"]/.test(token.string.charAt(0))) { | |
89 quote = token.string.charAt(0); | |
90 prefix = token.string.slice(1); | |
91 n++; | |
92 } | |
93 var len = token.string.length; | |
94 if (/['"]/.test(token.string.charAt(len - 1))) { | |
95 quote = token.string.charAt(len - 1); | |
96 prefix = token.string.substr(n, len - 2); | |
97 } | |
98 if (n) { // an opening quote | |
99 var line = cm.getLine(cur.line); | |
100 if (line.length > token.end && line.charAt(token.end) == quote) token.end++; // include a closing quote | |
101 } | |
102 replaceToken = true; | |
103 } | |
104 var returnHintsFromAtValues = function(atValues) { | |
105 if (atValues) | |
106 for (var i = 0; i < atValues.length; ++i) if (!prefix || matches(atValues[i], prefix, matchInMiddle)) | |
107 result.push(quote + atValues[i] + quote); | |
108 return returnHints(); | |
109 }; | |
110 if (atValues && atValues.then) return atValues.then(returnHintsFromAtValues); | |
111 return returnHintsFromAtValues(atValues); | |
112 } else { // An attribute name | |
113 if (token.type == "attribute") { | |
114 prefix = token.string; | |
115 replaceToken = true; | |
116 } | |
117 for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || matches(attr, prefix, matchInMiddle))) | |
118 result.push(attr); | |
119 } | |
120 } | |
121 function returnHints() { | |
122 return { | |
123 list: result, | |
124 from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur, | |
125 to: replaceToken ? Pos(cur.line, token.end) : cur | |
126 }; | |
127 } | |
128 return returnHints(); | |
129 } | |
130 | |
131 CodeMirror.registerHelper("hint", "xml", getHints); | |
132 }); |