1 | /**
|
---|
2 | * @param {string} value
|
---|
3 | * @returns {RegExp}
|
---|
4 | * */
|
---|
5 |
|
---|
6 | /**
|
---|
7 | * @param {RegExp | string } re
|
---|
8 | * @returns {string}
|
---|
9 | */
|
---|
10 | function source(re) {
|
---|
11 | if (!re) return null;
|
---|
12 | if (typeof re === "string") return re;
|
---|
13 |
|
---|
14 | return re.source;
|
---|
15 | }
|
---|
16 |
|
---|
17 | /**
|
---|
18 | * @param {RegExp | string } re
|
---|
19 | * @returns {string}
|
---|
20 | */
|
---|
21 | function lookahead(re) {
|
---|
22 | return concat('(?=', re, ')');
|
---|
23 | }
|
---|
24 |
|
---|
25 | /**
|
---|
26 | * @param {RegExp | string } re
|
---|
27 | * @returns {string}
|
---|
28 | */
|
---|
29 | function optional(re) {
|
---|
30 | return concat('(', re, ')?');
|
---|
31 | }
|
---|
32 |
|
---|
33 | /**
|
---|
34 | * @param {...(RegExp | string) } args
|
---|
35 | * @returns {string}
|
---|
36 | */
|
---|
37 | function concat(...args) {
|
---|
38 | const joined = args.map((x) => source(x)).join("");
|
---|
39 | return joined;
|
---|
40 | }
|
---|
41 |
|
---|
42 | /**
|
---|
43 | * Any of the passed expresssions may match
|
---|
44 | *
|
---|
45 | * Creates a huge this | this | that | that match
|
---|
46 | * @param {(RegExp | string)[] } args
|
---|
47 | * @returns {string}
|
---|
48 | */
|
---|
49 | function either(...args) {
|
---|
50 | const joined = '(' + args.map((x) => source(x)).join("|") + ")";
|
---|
51 | return joined;
|
---|
52 | }
|
---|
53 |
|
---|
54 | /*
|
---|
55 | Language: HTML, XML
|
---|
56 | Website: https://www.w3.org/XML/
|
---|
57 | Category: common
|
---|
58 | Audit: 2020
|
---|
59 | */
|
---|
60 |
|
---|
61 | /** @type LanguageFn */
|
---|
62 | function xml(hljs) {
|
---|
63 | // Element names can contain letters, digits, hyphens, underscores, and periods
|
---|
64 | const TAG_NAME_RE = concat(/[A-Z_]/, optional(/[A-Z0-9_.-]*:/), /[A-Z0-9_.-]*/);
|
---|
65 | const XML_IDENT_RE = /[A-Za-z0-9._:-]+/;
|
---|
66 | const XML_ENTITIES = {
|
---|
67 | className: 'symbol',
|
---|
68 | begin: /&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/
|
---|
69 | };
|
---|
70 | const XML_META_KEYWORDS = {
|
---|
71 | begin: /\s/,
|
---|
72 | contains: [
|
---|
73 | {
|
---|
74 | className: 'meta-keyword',
|
---|
75 | begin: /#?[a-z_][a-z1-9_-]+/,
|
---|
76 | illegal: /\n/
|
---|
77 | }
|
---|
78 | ]
|
---|
79 | };
|
---|
80 | const XML_META_PAR_KEYWORDS = hljs.inherit(XML_META_KEYWORDS, {
|
---|
81 | begin: /\(/,
|
---|
82 | end: /\)/
|
---|
83 | });
|
---|
84 | const APOS_META_STRING_MODE = hljs.inherit(hljs.APOS_STRING_MODE, {
|
---|
85 | className: 'meta-string'
|
---|
86 | });
|
---|
87 | const QUOTE_META_STRING_MODE = hljs.inherit(hljs.QUOTE_STRING_MODE, {
|
---|
88 | className: 'meta-string'
|
---|
89 | });
|
---|
90 | const TAG_INTERNALS = {
|
---|
91 | endsWithParent: true,
|
---|
92 | illegal: /</,
|
---|
93 | relevance: 0,
|
---|
94 | contains: [
|
---|
95 | {
|
---|
96 | className: 'attr',
|
---|
97 | begin: XML_IDENT_RE,
|
---|
98 | relevance: 0
|
---|
99 | },
|
---|
100 | {
|
---|
101 | begin: /=\s*/,
|
---|
102 | relevance: 0,
|
---|
103 | contains: [
|
---|
104 | {
|
---|
105 | className: 'string',
|
---|
106 | endsParent: true,
|
---|
107 | variants: [
|
---|
108 | {
|
---|
109 | begin: /"/,
|
---|
110 | end: /"/,
|
---|
111 | contains: [ XML_ENTITIES ]
|
---|
112 | },
|
---|
113 | {
|
---|
114 | begin: /'/,
|
---|
115 | end: /'/,
|
---|
116 | contains: [ XML_ENTITIES ]
|
---|
117 | },
|
---|
118 | {
|
---|
119 | begin: /[^\s"'=<>`]+/
|
---|
120 | }
|
---|
121 | ]
|
---|
122 | }
|
---|
123 | ]
|
---|
124 | }
|
---|
125 | ]
|
---|
126 | };
|
---|
127 | return {
|
---|
128 | name: 'HTML, XML',
|
---|
129 | aliases: [
|
---|
130 | 'html',
|
---|
131 | 'xhtml',
|
---|
132 | 'rss',
|
---|
133 | 'atom',
|
---|
134 | 'xjb',
|
---|
135 | 'xsd',
|
---|
136 | 'xsl',
|
---|
137 | 'plist',
|
---|
138 | 'wsf',
|
---|
139 | 'svg'
|
---|
140 | ],
|
---|
141 | case_insensitive: true,
|
---|
142 | contains: [
|
---|
143 | {
|
---|
144 | className: 'meta',
|
---|
145 | begin: /<![a-z]/,
|
---|
146 | end: />/,
|
---|
147 | relevance: 10,
|
---|
148 | contains: [
|
---|
149 | XML_META_KEYWORDS,
|
---|
150 | QUOTE_META_STRING_MODE,
|
---|
151 | APOS_META_STRING_MODE,
|
---|
152 | XML_META_PAR_KEYWORDS,
|
---|
153 | {
|
---|
154 | begin: /\[/,
|
---|
155 | end: /\]/,
|
---|
156 | contains: [
|
---|
157 | {
|
---|
158 | className: 'meta',
|
---|
159 | begin: /<![a-z]/,
|
---|
160 | end: />/,
|
---|
161 | contains: [
|
---|
162 | XML_META_KEYWORDS,
|
---|
163 | XML_META_PAR_KEYWORDS,
|
---|
164 | QUOTE_META_STRING_MODE,
|
---|
165 | APOS_META_STRING_MODE
|
---|
166 | ]
|
---|
167 | }
|
---|
168 | ]
|
---|
169 | }
|
---|
170 | ]
|
---|
171 | },
|
---|
172 | hljs.COMMENT(
|
---|
173 | /<!--/,
|
---|
174 | /-->/,
|
---|
175 | {
|
---|
176 | relevance: 10
|
---|
177 | }
|
---|
178 | ),
|
---|
179 | {
|
---|
180 | begin: /<!\[CDATA\[/,
|
---|
181 | end: /\]\]>/,
|
---|
182 | relevance: 10
|
---|
183 | },
|
---|
184 | XML_ENTITIES,
|
---|
185 | {
|
---|
186 | className: 'meta',
|
---|
187 | begin: /<\?xml/,
|
---|
188 | end: /\?>/,
|
---|
189 | relevance: 10
|
---|
190 | },
|
---|
191 | {
|
---|
192 | className: 'tag',
|
---|
193 | /*
|
---|
194 | The lookahead pattern (?=...) ensures that 'begin' only matches
|
---|
195 | '<style' as a single word, followed by a whitespace or an
|
---|
196 | ending braket. The '$' is needed for the lexeme to be recognized
|
---|
197 | by hljs.subMode() that tests lexemes outside the stream.
|
---|
198 | */
|
---|
199 | begin: /<style(?=\s|>)/,
|
---|
200 | end: />/,
|
---|
201 | keywords: {
|
---|
202 | name: 'style'
|
---|
203 | },
|
---|
204 | contains: [ TAG_INTERNALS ],
|
---|
205 | starts: {
|
---|
206 | end: /<\/style>/,
|
---|
207 | returnEnd: true,
|
---|
208 | subLanguage: [
|
---|
209 | 'css',
|
---|
210 | 'xml'
|
---|
211 | ]
|
---|
212 | }
|
---|
213 | },
|
---|
214 | {
|
---|
215 | className: 'tag',
|
---|
216 | // See the comment in the <style tag about the lookahead pattern
|
---|
217 | begin: /<script(?=\s|>)/,
|
---|
218 | end: />/,
|
---|
219 | keywords: {
|
---|
220 | name: 'script'
|
---|
221 | },
|
---|
222 | contains: [ TAG_INTERNALS ],
|
---|
223 | starts: {
|
---|
224 | end: /<\/script>/,
|
---|
225 | returnEnd: true,
|
---|
226 | subLanguage: [
|
---|
227 | 'javascript',
|
---|
228 | 'handlebars',
|
---|
229 | 'xml'
|
---|
230 | ]
|
---|
231 | }
|
---|
232 | },
|
---|
233 | // we need this for now for jSX
|
---|
234 | {
|
---|
235 | className: 'tag',
|
---|
236 | begin: /<>|<\/>/
|
---|
237 | },
|
---|
238 | // open tag
|
---|
239 | {
|
---|
240 | className: 'tag',
|
---|
241 | begin: concat(
|
---|
242 | /</,
|
---|
243 | lookahead(concat(
|
---|
244 | TAG_NAME_RE,
|
---|
245 | // <tag/>
|
---|
246 | // <tag>
|
---|
247 | // <tag ...
|
---|
248 | either(/\/>/, />/, /\s/)
|
---|
249 | ))
|
---|
250 | ),
|
---|
251 | end: /\/?>/,
|
---|
252 | contains: [
|
---|
253 | {
|
---|
254 | className: 'name',
|
---|
255 | begin: TAG_NAME_RE,
|
---|
256 | relevance: 0,
|
---|
257 | starts: TAG_INTERNALS
|
---|
258 | }
|
---|
259 | ]
|
---|
260 | },
|
---|
261 | // close tag
|
---|
262 | {
|
---|
263 | className: 'tag',
|
---|
264 | begin: concat(
|
---|
265 | /<\//,
|
---|
266 | lookahead(concat(
|
---|
267 | TAG_NAME_RE, />/
|
---|
268 | ))
|
---|
269 | ),
|
---|
270 | contains: [
|
---|
271 | {
|
---|
272 | className: 'name',
|
---|
273 | begin: TAG_NAME_RE,
|
---|
274 | relevance: 0
|
---|
275 | },
|
---|
276 | {
|
---|
277 | begin: />/,
|
---|
278 | relevance: 0,
|
---|
279 | endsParent: true
|
---|
280 | }
|
---|
281 | ]
|
---|
282 | }
|
---|
283 | ]
|
---|
284 | };
|
---|
285 | }
|
---|
286 |
|
---|
287 | module.exports = xml;
|
---|