]> luflow.net public git repositories - flow-web.git/blob - static/highlight/languages/ruby.js
Initial commit.
[flow-web.git] / static / highlight / languages / ruby.js
1 /*! `ruby` grammar compiled for Highlight.js 11.11.1 */
2 (function(){
3 var hljsGrammar = (function () {
4 'use strict';
5
6 /*
7 Language: Ruby
8 Description: Ruby is a dynamic, open source programming language with a focus on simplicity and productivity.
9 Website: https://www.ruby-lang.org/
10 Author: Anton Kovalyov <anton@kovalyov.net>
11 Contributors: Peter Leonov <gojpeg@yandex.ru>, Vasily Polovnyov <vast@whiteants.net>, Loren Segal <lsegal@soen.ca>, Pascal Hurni <phi@ruby-reactive.org>, Cedric Sohrauer <sohrauer@googlemail.com>
12 Category: common, scripting
13 */
14
15 function ruby(hljs) {
16 const regex = hljs.regex;
17 const RUBY_METHOD_RE = '([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)';
18 // TODO: move concepts like CAMEL_CASE into `modes.js`
19 const CLASS_NAME_RE = regex.either(
20 /\b([A-Z]+[a-z0-9]+)+/,
21 // ends in caps
22 /\b([A-Z]+[a-z0-9]+)+[A-Z]+/,
23 )
24 ;
25 const CLASS_NAME_WITH_NAMESPACE_RE = regex.concat(CLASS_NAME_RE, /(::\w+)*/);
26 // very popular ruby built-ins that one might even assume
27 // are actual keywords (despite that not being the case)
28 const PSEUDO_KWS = [
29 "include",
30 "extend",
31 "prepend",
32 "public",
33 "private",
34 "protected",
35 "raise",
36 "throw"
37 ];
38 const RUBY_KEYWORDS = {
39 "variable.constant": [
40 "__FILE__",
41 "__LINE__",
42 "__ENCODING__"
43 ],
44 "variable.language": [
45 "self",
46 "super",
47 ],
48 keyword: [
49 "alias",
50 "and",
51 "begin",
52 "BEGIN",
53 "break",
54 "case",
55 "class",
56 "defined",
57 "do",
58 "else",
59 "elsif",
60 "end",
61 "END",
62 "ensure",
63 "for",
64 "if",
65 "in",
66 "module",
67 "next",
68 "not",
69 "or",
70 "redo",
71 "require",
72 "rescue",
73 "retry",
74 "return",
75 "then",
76 "undef",
77 "unless",
78 "until",
79 "when",
80 "while",
81 "yield",
82 ...PSEUDO_KWS
83 ],
84 built_in: [
85 "proc",
86 "lambda",
87 "attr_accessor",
88 "attr_reader",
89 "attr_writer",
90 "define_method",
91 "private_constant",
92 "module_function"
93 ],
94 literal: [
95 "true",
96 "false",
97 "nil"
98 ]
99 };
100 const YARDOCTAG = {
101 className: 'doctag',
102 begin: '@[A-Za-z]+'
103 };
104 const IRB_OBJECT = {
105 begin: '#<',
106 end: '>'
107 };
108 const COMMENT_MODES = [
109 hljs.COMMENT(
110 '#',
111 '$',
112 { contains: [ YARDOCTAG ] }
113 ),
114 hljs.COMMENT(
115 '^=begin',
116 '^=end',
117 {
118 contains: [ YARDOCTAG ],
119 relevance: 10
120 }
121 ),
122 hljs.COMMENT('^__END__', hljs.MATCH_NOTHING_RE)
123 ];
124 const SUBST = {
125 className: 'subst',
126 begin: /#\{/,
127 end: /\}/,
128 keywords: RUBY_KEYWORDS
129 };
130 const STRING = {
131 className: 'string',
132 contains: [
133 hljs.BACKSLASH_ESCAPE,
134 SUBST
135 ],
136 variants: [
137 {
138 begin: /'/,
139 end: /'/
140 },
141 {
142 begin: /"/,
143 end: /"/
144 },
145 {
146 begin: /`/,
147 end: /`/
148 },
149 {
150 begin: /%[qQwWx]?\(/,
151 end: /\)/
152 },
153 {
154 begin: /%[qQwWx]?\[/,
155 end: /\]/
156 },
157 {
158 begin: /%[qQwWx]?\{/,
159 end: /\}/
160 },
161 {
162 begin: /%[qQwWx]?</,
163 end: />/
164 },
165 {
166 begin: /%[qQwWx]?\//,
167 end: /\//
168 },
169 {
170 begin: /%[qQwWx]?%/,
171 end: /%/
172 },
173 {
174 begin: /%[qQwWx]?-/,
175 end: /-/
176 },
177 {
178 begin: /%[qQwWx]?\|/,
179 end: /\|/
180 },
181 // in the following expressions, \B in the beginning suppresses recognition of ?-sequences
182 // where ? is the last character of a preceding identifier, as in: `func?4`
183 { begin: /\B\?(\\\d{1,3})/ },
184 { begin: /\B\?(\\x[A-Fa-f0-9]{1,2})/ },
185 { begin: /\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/ },
186 { begin: /\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/ },
187 { begin: /\B\?\\(c|C-)[\x20-\x7e]/ },
188 { begin: /\B\?\\?\S/ },
189 // heredocs
190 {
191 // this guard makes sure that we have an entire heredoc and not a false
192 // positive (auto-detect, etc.)
193 begin: regex.concat(
194 /<<[-~]?'?/,
195 regex.lookahead(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)
196 ),
197 contains: [
198 hljs.END_SAME_AS_BEGIN({
199 begin: /(\w+)/,
200 end: /(\w+)/,
201 contains: [
202 hljs.BACKSLASH_ESCAPE,
203 SUBST
204 ]
205 })
206 ]
207 }
208 ]
209 };
210
211 // Ruby syntax is underdocumented, but this grammar seems to be accurate
212 // as of version 2.7.2 (confirmed with (irb and `Ripper.sexp(...)`)
213 // https://docs.ruby-lang.org/en/2.7.0/doc/syntax/literals_rdoc.html#label-Numbers
214 const decimal = '[1-9](_?[0-9])*|0';
215 const digits = '[0-9](_?[0-9])*';
216 const NUMBER = {
217 className: 'number',
218 relevance: 0,
219 variants: [
220 // decimal integer/float, optionally exponential or rational, optionally imaginary
221 { begin: `\\b(${decimal})(\\.(${digits}))?([eE][+-]?(${digits})|r)?i?\\b` },
222
223 // explicit decimal/binary/octal/hexadecimal integer,
224 // optionally rational and/or imaginary
225 { begin: "\\b0[dD][0-9](_?[0-9])*r?i?\\b" },
226 { begin: "\\b0[bB][0-1](_?[0-1])*r?i?\\b" },
227 { begin: "\\b0[oO][0-7](_?[0-7])*r?i?\\b" },
228 { begin: "\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b" },
229
230 // 0-prefixed implicit octal integer, optionally rational and/or imaginary
231 { begin: "\\b0(_?[0-7])+r?i?\\b" }
232 ]
233 };
234
235 const PARAMS = {
236 variants: [
237 {
238 match: /\(\)/,
239 },
240 {
241 className: 'params',
242 begin: /\(/,
243 end: /(?=\))/,
244 excludeBegin: true,
245 endsParent: true,
246 keywords: RUBY_KEYWORDS,
247 }
248 ]
249 };
250
251 const INCLUDE_EXTEND = {
252 match: [
253 /(include|extend)\s+/,
254 CLASS_NAME_WITH_NAMESPACE_RE
255 ],
256 scope: {
257 2: "title.class"
258 },
259 keywords: RUBY_KEYWORDS
260 };
261
262 const CLASS_DEFINITION = {
263 variants: [
264 {
265 match: [
266 /class\s+/,
267 CLASS_NAME_WITH_NAMESPACE_RE,
268 /\s+<\s+/,
269 CLASS_NAME_WITH_NAMESPACE_RE
270 ]
271 },
272 {
273 match: [
274 /\b(class|module)\s+/,
275 CLASS_NAME_WITH_NAMESPACE_RE
276 ]
277 }
278 ],
279 scope: {
280 2: "title.class",
281 4: "title.class.inherited"
282 },
283 keywords: RUBY_KEYWORDS
284 };
285
286 const UPPER_CASE_CONSTANT = {
287 relevance: 0,
288 match: /\b[A-Z][A-Z_0-9]+\b/,
289 className: "variable.constant"
290 };
291
292 const METHOD_DEFINITION = {
293 match: [
294 /def/, /\s+/,
295 RUBY_METHOD_RE
296 ],
297 scope: {
298 1: "keyword",
299 3: "title.function"
300 },
301 contains: [
302 PARAMS
303 ]
304 };
305
306 const OBJECT_CREATION = {
307 relevance: 0,
308 match: [
309 CLASS_NAME_WITH_NAMESPACE_RE,
310 /\.new[. (]/
311 ],
312 scope: {
313 1: "title.class"
314 }
315 };
316
317 // CamelCase
318 const CLASS_REFERENCE = {
319 relevance: 0,
320 match: CLASS_NAME_RE,
321 scope: "title.class"
322 };
323
324 const RUBY_DEFAULT_CONTAINS = [
325 STRING,
326 CLASS_DEFINITION,
327 INCLUDE_EXTEND,
328 OBJECT_CREATION,
329 UPPER_CASE_CONSTANT,
330 CLASS_REFERENCE,
331 METHOD_DEFINITION,
332 {
333 // swallow namespace qualifiers before symbols
334 begin: hljs.IDENT_RE + '::' },
335 {
336 className: 'symbol',
337 begin: hljs.UNDERSCORE_IDENT_RE + '(!|\\?)?:',
338 relevance: 0
339 },
340 {
341 className: 'symbol',
342 begin: ':(?!\\s)',
343 contains: [
344 STRING,
345 { begin: RUBY_METHOD_RE }
346 ],
347 relevance: 0
348 },
349 NUMBER,
350 {
351 // negative-look forward attempts to prevent false matches like:
352 // @ident@ or $ident$ that might indicate this is not ruby at all
353 className: "variable",
354 begin: '(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])' + `(?![A-Za-z])(?![@$?'])`
355 },
356 {
357 className: 'params',
358 begin: /\|(?!=)/,
359 end: /\|/,
360 excludeBegin: true,
361 excludeEnd: true,
362 relevance: 0, // this could be a lot of things (in other languages) other than params
363 keywords: RUBY_KEYWORDS
364 },
365 { // regexp container
366 begin: '(' + hljs.RE_STARTERS_RE + '|unless)\\s*',
367 keywords: 'unless',
368 contains: [
369 {
370 className: 'regexp',
371 contains: [
372 hljs.BACKSLASH_ESCAPE,
373 SUBST
374 ],
375 illegal: /\n/,
376 variants: [
377 {
378 begin: '/',
379 end: '/[a-z]*'
380 },
381 {
382 begin: /%r\{/,
383 end: /\}[a-z]*/
384 },
385 {
386 begin: '%r\\(',
387 end: '\\)[a-z]*'
388 },
389 {
390 begin: '%r!',
391 end: '![a-z]*'
392 },
393 {
394 begin: '%r\\[',
395 end: '\\][a-z]*'
396 }
397 ]
398 }
399 ].concat(IRB_OBJECT, COMMENT_MODES),
400 relevance: 0
401 }
402 ].concat(IRB_OBJECT, COMMENT_MODES);
403
404 SUBST.contains = RUBY_DEFAULT_CONTAINS;
405 PARAMS.contains = RUBY_DEFAULT_CONTAINS;
406
407 // >>
408 // ?>
409 const SIMPLE_PROMPT = "[>?]>";
410 // irb(main):001:0>
411 const DEFAULT_PROMPT = "[\\w#]+\\(\\w+\\):\\d+:\\d+[>*]";
412 const RVM_PROMPT = "(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>";
413
414 const IRB_DEFAULT = [
415 {
416 begin: /^\s*=>/,
417 starts: {
418 end: '$',
419 contains: RUBY_DEFAULT_CONTAINS
420 }
421 },
422 {
423 className: 'meta.prompt',
424 begin: '^(' + SIMPLE_PROMPT + "|" + DEFAULT_PROMPT + '|' + RVM_PROMPT + ')(?=[ ])',
425 starts: {
426 end: '$',
427 keywords: RUBY_KEYWORDS,
428 contains: RUBY_DEFAULT_CONTAINS
429 }
430 }
431 ];
432
433 COMMENT_MODES.unshift(IRB_OBJECT);
434
435 return {
436 name: 'Ruby',
437 aliases: [
438 'rb',
439 'gemspec',
440 'podspec',
441 'thor',
442 'irb'
443 ],
444 keywords: RUBY_KEYWORDS,
445 illegal: /\/\*/,
446 contains: [ hljs.SHEBANG({ binary: "ruby" }) ]
447 .concat(IRB_DEFAULT)
448 .concat(COMMENT_MODES)
449 .concat(RUBY_DEFAULT_CONTAINS)
450 };
451 }
452
453 return ruby;
454
455 })();
456
457 hljs.registerLanguage('ruby', hljsGrammar);
458 })();