UNPKG

7.37 kBJavaScriptView Raw
1/*
2Language: Crystal
3Author: TSUYUSATO Kitsune <make.just.on@gmail.com>
4Website: https://crystal-lang.org
5*/
6
7/** @type LanguageFn */
8function crystal(hljs) {
9 const INT_SUFFIX = '(_?[ui](8|16|32|64|128))?';
10 const FLOAT_SUFFIX = '(_?f(32|64))?';
11 const CRYSTAL_IDENT_RE = '[a-zA-Z_]\\w*[!?=]?';
12 const CRYSTAL_METHOD_RE = '[a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|[=!]~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~|]|//|//=|&[-+*]=?|&\\*\\*|\\[\\][=?]?';
13 const CRYSTAL_PATH_RE = '[A-Za-z_]\\w*(::\\w+)*(\\?|!)?';
14 const CRYSTAL_KEYWORDS = {
15 $pattern: CRYSTAL_IDENT_RE,
16 keyword:
17 'abstract alias annotation as as? asm begin break case class def do else elsif end ensure enum extend for fun if ' +
18 'include instance_sizeof is_a? lib macro module next nil? of out pointerof private protected rescue responds_to? ' +
19 'return require select self sizeof struct super then type typeof union uninitialized unless until verbatim when while with yield ' +
20 '__DIR__ __END_LINE__ __FILE__ __LINE__',
21 literal: 'false nil true'
22 };
23 const SUBST = {
24 className: 'subst',
25 begin: /#\{/,
26 end: /\}/,
27 keywords: CRYSTAL_KEYWORDS
28 };
29 // borrowed from Ruby
30 const VARIABLE = {
31 // negative-look forward attemps to prevent false matches like:
32 // @ident@ or $ident$ that might indicate this is not ruby at all
33 className: "variable",
34 begin: '(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])' + `(?![A-Za-z])(?![@$?'])`
35 };
36 const EXPANSION = {
37 className: 'template-variable',
38 variants: [
39 {
40 begin: '\\{\\{',
41 end: '\\}\\}'
42 },
43 {
44 begin: '\\{%',
45 end: '%\\}'
46 }
47 ],
48 keywords: CRYSTAL_KEYWORDS
49 };
50
51 function recursiveParen(begin, end) {
52 const
53 contains = [
54 {
55 begin: begin,
56 end: end
57 }
58 ];
59 contains[0].contains = contains;
60 return contains;
61 }
62 const STRING = {
63 className: 'string',
64 contains: [
65 hljs.BACKSLASH_ESCAPE,
66 SUBST
67 ],
68 variants: [
69 {
70 begin: /'/,
71 end: /'/
72 },
73 {
74 begin: /"/,
75 end: /"/
76 },
77 {
78 begin: /`/,
79 end: /`/
80 },
81 {
82 begin: '%[Qwi]?\\(',
83 end: '\\)',
84 contains: recursiveParen('\\(', '\\)')
85 },
86 {
87 begin: '%[Qwi]?\\[',
88 end: '\\]',
89 contains: recursiveParen('\\[', '\\]')
90 },
91 {
92 begin: '%[Qwi]?\\{',
93 end: /\}/,
94 contains: recursiveParen(/\{/, /\}/)
95 },
96 {
97 begin: '%[Qwi]?<',
98 end: '>',
99 contains: recursiveParen('<', '>')
100 },
101 {
102 begin: '%[Qwi]?\\|',
103 end: '\\|'
104 },
105 {
106 begin: /<<-\w+$/,
107 end: /^\s*\w+$/
108 }
109 ],
110 relevance: 0
111 };
112 const Q_STRING = {
113 className: 'string',
114 variants: [
115 {
116 begin: '%q\\(',
117 end: '\\)',
118 contains: recursiveParen('\\(', '\\)')
119 },
120 {
121 begin: '%q\\[',
122 end: '\\]',
123 contains: recursiveParen('\\[', '\\]')
124 },
125 {
126 begin: '%q\\{',
127 end: /\}/,
128 contains: recursiveParen(/\{/, /\}/)
129 },
130 {
131 begin: '%q<',
132 end: '>',
133 contains: recursiveParen('<', '>')
134 },
135 {
136 begin: '%q\\|',
137 end: '\\|'
138 },
139 {
140 begin: /<<-'\w+'$/,
141 end: /^\s*\w+$/
142 }
143 ],
144 relevance: 0
145 };
146 const REGEXP = {
147 begin: '(?!%\\})(' + hljs.RE_STARTERS_RE + '|\\n|\\b(case|if|select|unless|until|when|while)\\b)\\s*',
148 keywords: 'case if select unless until when while',
149 contains: [
150 {
151 className: 'regexp',
152 contains: [
153 hljs.BACKSLASH_ESCAPE,
154 SUBST
155 ],
156 variants: [
157 {
158 begin: '//[a-z]*',
159 relevance: 0
160 },
161 {
162 begin: '/(?!\\/)',
163 end: '/[a-z]*'
164 }
165 ]
166 }
167 ],
168 relevance: 0
169 };
170 const REGEXP2 = {
171 className: 'regexp',
172 contains: [
173 hljs.BACKSLASH_ESCAPE,
174 SUBST
175 ],
176 variants: [
177 {
178 begin: '%r\\(',
179 end: '\\)',
180 contains: recursiveParen('\\(', '\\)')
181 },
182 {
183 begin: '%r\\[',
184 end: '\\]',
185 contains: recursiveParen('\\[', '\\]')
186 },
187 {
188 begin: '%r\\{',
189 end: /\}/,
190 contains: recursiveParen(/\{/, /\}/)
191 },
192 {
193 begin: '%r<',
194 end: '>',
195 contains: recursiveParen('<', '>')
196 },
197 {
198 begin: '%r\\|',
199 end: '\\|'
200 }
201 ],
202 relevance: 0
203 };
204 const ATTRIBUTE = {
205 className: 'meta',
206 begin: '@\\[',
207 end: '\\]',
208 contains: [
209 hljs.inherit(hljs.QUOTE_STRING_MODE, {
210 className: 'string'
211 })
212 ]
213 };
214 const CRYSTAL_DEFAULT_CONTAINS = [
215 EXPANSION,
216 STRING,
217 Q_STRING,
218 REGEXP2,
219 REGEXP,
220 ATTRIBUTE,
221 VARIABLE,
222 hljs.HASH_COMMENT_MODE,
223 {
224 className: 'class',
225 beginKeywords: 'class module struct',
226 end: '$|;',
227 illegal: /=/,
228 contains: [
229 hljs.HASH_COMMENT_MODE,
230 hljs.inherit(hljs.TITLE_MODE, {
231 begin: CRYSTAL_PATH_RE
232 }),
233 { // relevance booster for inheritance
234 begin: '<'
235 }
236 ]
237 },
238 {
239 className: 'class',
240 beginKeywords: 'lib enum union',
241 end: '$|;',
242 illegal: /=/,
243 contains: [
244 hljs.HASH_COMMENT_MODE,
245 hljs.inherit(hljs.TITLE_MODE, {
246 begin: CRYSTAL_PATH_RE
247 })
248 ]
249 },
250 {
251 beginKeywords: 'annotation',
252 end: '$|;',
253 illegal: /=/,
254 contains: [
255 hljs.HASH_COMMENT_MODE,
256 hljs.inherit(hljs.TITLE_MODE, {
257 begin: CRYSTAL_PATH_RE
258 })
259 ],
260 relevance: 2
261 },
262 {
263 className: 'function',
264 beginKeywords: 'def',
265 end: /\B\b/,
266 contains: [
267 hljs.inherit(hljs.TITLE_MODE, {
268 begin: CRYSTAL_METHOD_RE,
269 endsParent: true
270 })
271 ]
272 },
273 {
274 className: 'function',
275 beginKeywords: 'fun macro',
276 end: /\B\b/,
277 contains: [
278 hljs.inherit(hljs.TITLE_MODE, {
279 begin: CRYSTAL_METHOD_RE,
280 endsParent: true
281 })
282 ],
283 relevance: 2
284 },
285 {
286 className: 'symbol',
287 begin: hljs.UNDERSCORE_IDENT_RE + '(!|\\?)?:',
288 relevance: 0
289 },
290 {
291 className: 'symbol',
292 begin: ':',
293 contains: [
294 STRING,
295 {
296 begin: CRYSTAL_METHOD_RE
297 }
298 ],
299 relevance: 0
300 },
301 {
302 className: 'number',
303 variants: [
304 {
305 begin: '\\b0b([01_]+)' + INT_SUFFIX
306 },
307 {
308 begin: '\\b0o([0-7_]+)' + INT_SUFFIX
309 },
310 {
311 begin: '\\b0x([A-Fa-f0-9_]+)' + INT_SUFFIX
312 },
313 {
314 begin: '\\b([1-9][0-9_]*[0-9]|[0-9])(\\.[0-9][0-9_]*)?([eE]_?[-+]?[0-9_]*)?' + FLOAT_SUFFIX + '(?!_)'
315 },
316 {
317 begin: '\\b([1-9][0-9_]*|0)' + INT_SUFFIX
318 }
319 ],
320 relevance: 0
321 }
322 ];
323 SUBST.contains = CRYSTAL_DEFAULT_CONTAINS;
324 EXPANSION.contains = CRYSTAL_DEFAULT_CONTAINS.slice(1); // without EXPANSION
325
326 return {
327 name: 'Crystal',
328 aliases: [ 'cr' ],
329 keywords: CRYSTAL_KEYWORDS,
330 contains: CRYSTAL_DEFAULT_CONTAINS
331 };
332}
333
334export { crystal as default };