1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | function ruby(hljs) {
|
11 | const regex = hljs.regex;
|
12 | const RUBY_METHOD_RE = '([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)';
|
13 | const RUBY_KEYWORDS = {
|
14 | keyword:
|
15 | 'and then defined module in return redo if BEGIN retry end for self when ' +
|
16 | 'next until do begin unless END rescue else break undef not super class case ' +
|
17 | 'require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor ' +
|
18 | '__FILE__',
|
19 | built_in: 'proc lambda',
|
20 | literal:
|
21 | 'true false nil'
|
22 | };
|
23 | const YARDOCTAG = {
|
24 | className: 'doctag',
|
25 | begin: '@[A-Za-z]+'
|
26 | };
|
27 | const IRB_OBJECT = {
|
28 | begin: '#<',
|
29 | end: '>'
|
30 | };
|
31 | const COMMENT_MODES = [
|
32 | hljs.COMMENT(
|
33 | '#',
|
34 | '$',
|
35 | {
|
36 | contains: [ YARDOCTAG ]
|
37 | }
|
38 | ),
|
39 | hljs.COMMENT(
|
40 | '^=begin',
|
41 | '^=end',
|
42 | {
|
43 | contains: [ YARDOCTAG ],
|
44 | relevance: 10
|
45 | }
|
46 | ),
|
47 | hljs.COMMENT('^__END__', '\\n$')
|
48 | ];
|
49 | const SUBST = {
|
50 | className: 'subst',
|
51 | begin: /#\{/,
|
52 | end: /\}/,
|
53 | keywords: RUBY_KEYWORDS
|
54 | };
|
55 | const STRING = {
|
56 | className: 'string',
|
57 | contains: [
|
58 | hljs.BACKSLASH_ESCAPE,
|
59 | SUBST
|
60 | ],
|
61 | variants: [
|
62 | {
|
63 | begin: /'/,
|
64 | end: /'/
|
65 | },
|
66 | {
|
67 | begin: /"/,
|
68 | end: /"/
|
69 | },
|
70 | {
|
71 | begin: /`/,
|
72 | end: /`/
|
73 | },
|
74 | {
|
75 | begin: /%[qQwWx]?\(/,
|
76 | end: /\)/
|
77 | },
|
78 | {
|
79 | begin: /%[qQwWx]?\[/,
|
80 | end: /\]/
|
81 | },
|
82 | {
|
83 | begin: /%[qQwWx]?\{/,
|
84 | end: /\}/
|
85 | },
|
86 | {
|
87 | begin: /%[qQwWx]?</,
|
88 | end: />/
|
89 | },
|
90 | {
|
91 | begin: /%[qQwWx]?\//,
|
92 | end: /\//
|
93 | },
|
94 | {
|
95 | begin: /%[qQwWx]?%/,
|
96 | end: /%/
|
97 | },
|
98 | {
|
99 | begin: /%[qQwWx]?-/,
|
100 | end: /-/
|
101 | },
|
102 | {
|
103 | begin: /%[qQwWx]?\|/,
|
104 | end: /\|/
|
105 | },
|
106 |
|
107 |
|
108 | {
|
109 | begin: /\B\?(\\\d{1,3})/
|
110 | },
|
111 | {
|
112 | begin: /\B\?(\\x[A-Fa-f0-9]{1,2})/
|
113 | },
|
114 | {
|
115 | begin: /\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/
|
116 | },
|
117 | {
|
118 | begin: /\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/
|
119 | },
|
120 | {
|
121 | begin: /\B\?\\(c|C-)[\x20-\x7e]/
|
122 | },
|
123 | {
|
124 | begin: /\B\?\\?\S/
|
125 | },
|
126 |
|
127 | {
|
128 |
|
129 |
|
130 | begin: regex.concat(
|
131 | /<<[-~]?'?/,
|
132 | regex.lookahead(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)
|
133 | ),
|
134 | contains: [
|
135 | hljs.END_SAME_AS_BEGIN({
|
136 | begin: /(\w+)/,
|
137 | end: /(\w+)/,
|
138 | contains: [
|
139 | hljs.BACKSLASH_ESCAPE,
|
140 | SUBST
|
141 | ]
|
142 | })
|
143 | ]
|
144 | }
|
145 | ]
|
146 | };
|
147 |
|
148 | // Ruby syntax is underdocumented, but this grammar seems to be accurate
|
149 | // as of version 2.7.2 (confirmed with (irb and `Ripper.sexp(...)`)
|
150 | // https://docs.ruby-lang.org/en/2.7.0/doc/syntax/literals_rdoc.html#label-Numbers
|
151 | const decimal = '[1-9](_?[0-9])*|0';
|
152 | const digits = '[0-9](_?[0-9])*';
|
153 | const NUMBER = {
|
154 | className: 'number',
|
155 | relevance: 0,
|
156 | variants: [
|
157 | // decimal integer/float, optionally exponential or rational, optionally imaginary
|
158 | {
|
159 | begin: `\\b(${decimal})(\\.(${digits}))?([eE][+-]?(${digits})|r)?i?\\b`
|
160 | },
|
161 |
|
162 | // explicit decimal/binary/octal/hexadecimal integer,
|
163 | // optionally rational and/or imaginary
|
164 | {
|
165 | begin: "\\b0[dD][0-9](_?[0-9])*r?i?\\b"
|
166 | },
|
167 | {
|
168 | begin: "\\b0[bB][0-1](_?[0-1])*r?i?\\b"
|
169 | },
|
170 | {
|
171 | begin: "\\b0[oO][0-7](_?[0-7])*r?i?\\b"
|
172 | },
|
173 | {
|
174 | begin: "\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"
|
175 | },
|
176 |
|
177 | // 0-prefixed implicit octal integer, optionally rational and/or imaginary
|
178 | {
|
179 | begin: "\\b0(_?[0-7])+r?i?\\b"
|
180 | }
|
181 | ]
|
182 | };
|
183 |
|
184 | const PARAMS = {
|
185 | className: 'params',
|
186 | begin: '\\(',
|
187 | end: '\\)',
|
188 | endsParent: true,
|
189 | keywords: RUBY_KEYWORDS
|
190 | };
|
191 |
|
192 | const RUBY_DEFAULT_CONTAINS = [
|
193 | STRING,
|
194 | {
|
195 | className: 'class',
|
196 | beginKeywords: 'class module',
|
197 | end: '$|;',
|
198 | illegal: /=/,
|
199 | contains: [
|
200 | hljs.inherit(hljs.TITLE_MODE, {
|
201 | begin: '[A-Za-z_]\\w*(::\\w+)*(\\?|!)?'
|
202 | }),
|
203 | {
|
204 | begin: '<\\s*',
|
205 | contains: [
|
206 | {
|
207 | begin: '(' + hljs.IDENT_RE + '::)?' + hljs.IDENT_RE,
|
208 | // we already get points for <, we don't need poitns
|
209 | // for the name also
|
210 | relevance: 0
|
211 | }
|
212 | ]
|
213 | }
|
214 | ].concat(COMMENT_MODES)
|
215 | },
|
216 | {
|
217 | className: 'function',
|
218 | // def method_name(
|
219 | // def method_name;
|
220 | // def method_name (end of line)
|
221 | begin: regex.concat(/def\s+/, regex.lookahead(RUBY_METHOD_RE + "\\s*(\\(|;|$)")),
|
222 | relevance: 0, // relevance comes from kewords
|
223 | keywords: "def",
|
224 | end: '$|;',
|
225 | contains: [
|
226 | hljs.inherit(hljs.TITLE_MODE, {
|
227 | begin: RUBY_METHOD_RE
|
228 | }),
|
229 | PARAMS
|
230 | ].concat(COMMENT_MODES)
|
231 | },
|
232 | {
|
233 | // swallow namespace qualifiers before symbols
|
234 | begin: hljs.IDENT_RE + '::'
|
235 | },
|
236 | {
|
237 | className: 'symbol',
|
238 | begin: hljs.UNDERSCORE_IDENT_RE + '(!|\\?)?:',
|
239 | relevance: 0
|
240 | },
|
241 | {
|
242 | className: 'symbol',
|
243 | begin: ':(?!\\s)',
|
244 | contains: [
|
245 | STRING,
|
246 | {
|
247 | begin: RUBY_METHOD_RE
|
248 | }
|
249 | ],
|
250 | relevance: 0
|
251 | },
|
252 | NUMBER,
|
253 | {
|
254 | // negative-look forward attempts to prevent false matches like:
|
255 | // @ident@ or $ident$ that might indicate this is not ruby at all
|
256 | className: "variable",
|
257 | begin: '(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])' + `(?![A-Za-z])(?![@$?'])`
|
258 | },
|
259 | {
|
260 | className: 'params',
|
261 | begin: /\|/,
|
262 | end: /\|/,
|
263 | relevance: 0, // this could be a lot of things (in other languages) other than params
|
264 | keywords: RUBY_KEYWORDS
|
265 | },
|
266 | { // regexp container
|
267 | begin: '(' + hljs.RE_STARTERS_RE + '|unless)\\s*',
|
268 | keywords: 'unless',
|
269 | contains: [
|
270 | {
|
271 | className: 'regexp',
|
272 | contains: [
|
273 | hljs.BACKSLASH_ESCAPE,
|
274 | SUBST
|
275 | ],
|
276 | illegal: /\n/,
|
277 | variants: [
|
278 | {
|
279 | begin: '/',
|
280 | end: '/[a-z]*'
|
281 | },
|
282 | {
|
283 | begin: /%r\{/,
|
284 | end: /\}[a-z]*/
|
285 | },
|
286 | {
|
287 | begin: '%r\\(',
|
288 | end: '\\)[a-z]*'
|
289 | },
|
290 | {
|
291 | begin: '%r!',
|
292 | end: '![a-z]*'
|
293 | },
|
294 | {
|
295 | begin: '%r\\[',
|
296 | end: '\\][a-z]*'
|
297 | }
|
298 | ]
|
299 | }
|
300 | ].concat(IRB_OBJECT, COMMENT_MODES),
|
301 | relevance: 0
|
302 | }
|
303 | ].concat(IRB_OBJECT, COMMENT_MODES);
|
304 |
|
305 | SUBST.contains = RUBY_DEFAULT_CONTAINS;
|
306 | PARAMS.contains = RUBY_DEFAULT_CONTAINS;
|
307 |
|
308 | // >>
|
309 | // ?>
|
310 | const SIMPLE_PROMPT = "[>?]>";
|
311 | // irb(main):001:0>
|
312 | const DEFAULT_PROMPT = "[\\w#]+\\(\\w+\\):\\d+:\\d+>";
|
313 | const RVM_PROMPT = "(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>";
|
314 |
|
315 | const IRB_DEFAULT = [
|
316 | {
|
317 | begin: /^\s*=>/,
|
318 | starts: {
|
319 | end: '$',
|
320 | contains: RUBY_DEFAULT_CONTAINS
|
321 | }
|
322 | },
|
323 | {
|
324 | className: 'meta',
|
325 | begin: '^(' + SIMPLE_PROMPT + "|" + DEFAULT_PROMPT + '|' + RVM_PROMPT + ')(?=[ ])',
|
326 | starts: {
|
327 | end: '$',
|
328 | contains: RUBY_DEFAULT_CONTAINS
|
329 | }
|
330 | }
|
331 | ];
|
332 |
|
333 | COMMENT_MODES.unshift(IRB_OBJECT);
|
334 |
|
335 | return {
|
336 | name: 'Ruby',
|
337 | aliases: [
|
338 | 'rb',
|
339 | 'gemspec',
|
340 | 'podspec',
|
341 | 'thor',
|
342 | 'irb'
|
343 | ],
|
344 | keywords: RUBY_KEYWORDS,
|
345 | illegal: /\/\*/,
|
346 | contains: [
|
347 | hljs.SHEBANG({
|
348 | binary: "ruby"
|
349 | })
|
350 | ]
|
351 | .concat(IRB_DEFAULT)
|
352 | .concat(COMMENT_MODES)
|
353 | .concat(RUBY_DEFAULT_CONTAINS)
|
354 | };
|
355 | }
|
356 |
|
357 | export { ruby as default };
|