Skip to content

Commit 5e38774

Browse files
committed
fix(ruby) class names with underscores, symbols, string interpolation
Signed-off-by: jimtng <[email protected]>
1 parent d301848 commit 5e38774

File tree

8 files changed

+190
-41
lines changed

8 files changed

+190
-41
lines changed

CHANGES.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## Version 11.11.2
2+
3+
Core Grammars:
4+
5+
- fix(ruby) fix class, symbols, string interpolation
6+
7+
18
## Version 11.11.1
29

310
- Fixes regression with Rust grammar.

src/languages/ruby.js

+79-39
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,7 @@ export default function(hljs) {
1111
const regex = hljs.regex;
1212
const RUBY_METHOD_RE = '([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)';
1313
// TODO: move concepts like CAMEL_CASE into `modes.js`
14-
const CLASS_NAME_RE = regex.either(
15-
/\b([A-Z]+[a-z0-9]+)+/,
16-
// ends in caps
17-
/\b([A-Z]+[a-z0-9]+)+[A-Z]+/,
18-
)
19-
;
20-
const CLASS_NAME_WITH_NAMESPACE_RE = regex.concat(CLASS_NAME_RE, /(::\w+)*/)
14+
const CLASS_NAME_RE = /\b([A-Z]+[a-z0-9_]+)+[A-Z]*/;
2115
// very popular ruby built-ins that one might even assume
2216
// are actual keywords (despite that not being the case)
2317
const PSEUDO_KWS = [
@@ -122,17 +116,13 @@ export default function(hljs) {
122116
end: /\}/,
123117
keywords: RUBY_KEYWORDS
124118
};
125-
const STRING = {
119+
const STRING_INTERPOLABLE = {
126120
className: 'string',
127121
contains: [
128122
hljs.BACKSLASH_ESCAPE,
129123
SUBST
130124
],
131125
variants: [
132-
{
133-
begin: /'/,
134-
end: /'/
135-
},
136126
{
137127
begin: /"/,
138128
end: /"/
@@ -142,45 +132,37 @@ export default function(hljs) {
142132
end: /`/
143133
},
144134
{
145-
begin: /%[qQwWx]?\(/,
135+
begin: /%[QWx]?\(/,
146136
end: /\)/
147137
},
148138
{
149-
begin: /%[qQwWx]?\[/,
139+
begin: /%[QWx]?\[/,
150140
end: /\]/
151141
},
152142
{
153-
begin: /%[qQwWx]?\{/,
143+
begin: /%[QWx]?\{/,
154144
end: /\}/
155145
},
156146
{
157-
begin: /%[qQwWx]?</,
147+
begin: /%[QWx]?</,
158148
end: />/
159149
},
160150
{
161-
begin: /%[qQwWx]?\//,
151+
begin: /%[QWx]?\//,
162152
end: /\//
163153
},
164154
{
165-
begin: /%[qQwWx]?%/,
155+
begin: /%[QWx]?%/,
166156
end: /%/
167157
},
168158
{
169-
begin: /%[qQwWx]?-/,
159+
begin: /%[QWx]?-/,
170160
end: /-/
171161
},
172162
{
173-
begin: /%[qQwWx]?\|/,
163+
begin: /%[QWx]?\|/,
174164
end: /\|/
175165
},
176-
// in the following expressions, \B in the beginning suppresses recognition of ?-sequences
177-
// where ? is the last character of a preceding identifier, as in: `func?4`
178-
{ begin: /\B\?(\\\d{1,3})/ },
179-
{ begin: /\B\?(\\x[A-Fa-f0-9]{1,2})/ },
180-
{ begin: /\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/ },
181-
{ begin: /\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/ },
182-
{ begin: /\B\?\\(c|C-)[\x20-\x7e]/ },
183-
{ begin: /\B\?\\?\S/ },
184166
// heredocs
185167
{
186168
// this guard makes sure that we have an entire heredoc and not a false
@@ -202,6 +184,55 @@ export default function(hljs) {
202184
}
203185
]
204186
};
187+
const STRING_NONINTERPOLABLE = {
188+
className: 'string',
189+
variants: [
190+
{
191+
begin: /'/,
192+
end: /'/
193+
},
194+
{
195+
begin: /%[qw]?\(/,
196+
end: /\)/
197+
},
198+
{
199+
begin: /%[qw]?\[/,
200+
end: /\]/
201+
},
202+
{
203+
begin: /%[qw]?\{/,
204+
end: /\}/
205+
},
206+
{
207+
begin: /%[qw]?</,
208+
end: />/
209+
},
210+
{
211+
begin: /%[qw]?\//,
212+
end: /\//
213+
},
214+
{
215+
begin: /%[qw]?%/,
216+
end: /%/
217+
},
218+
{
219+
begin: /%[qw]?-/,
220+
end: /-/
221+
},
222+
{
223+
begin: /%[qw]?\|/,
224+
end: /\|/
225+
},
226+
// in the following expressions, \B in the beginning suppresses recognition of ?-sequences
227+
// where ? is the last character of a preceding identifier, as in: `func?4`
228+
{ begin: /\B\?(\\\d{1,3})/ },
229+
{ begin: /\B\?(\\x[A-Fa-f0-9]{1,2})/ },
230+
{ begin: /\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/ },
231+
{ begin: /\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/ },
232+
{ begin: /\B\?\\(c|C-)[\x20-\x7e]/ },
233+
{ begin: /\B\?\\?\S/ }
234+
]
235+
};
205236

206237
// Ruby syntax is underdocumented, but this grammar seems to be accurate
207238
// as of version 2.7.2 (confirmed with (irb and `Ripper.sexp(...)`)
@@ -246,7 +277,7 @@ export default function(hljs) {
246277
const INCLUDE_EXTEND = {
247278
match: [
248279
/(include|extend)\s+/,
249-
CLASS_NAME_WITH_NAMESPACE_RE
280+
CLASS_NAME_RE
250281
],
251282
scope: {
252283
2: "title.class"
@@ -259,15 +290,15 @@ export default function(hljs) {
259290
{
260291
match: [
261292
/class\s+/,
262-
CLASS_NAME_WITH_NAMESPACE_RE,
293+
CLASS_NAME_RE,
263294
/\s+<\s+/,
264-
CLASS_NAME_WITH_NAMESPACE_RE
295+
CLASS_NAME_RE
265296
]
266297
},
267298
{
268299
match: [
269300
/\b(class|module)\s+/,
270-
CLASS_NAME_WITH_NAMESPACE_RE
301+
CLASS_NAME_RE
271302
]
272303
}
273304
],
@@ -301,7 +332,7 @@ export default function(hljs) {
301332
const OBJECT_CREATION = {
302333
relevance: 0,
303334
match: [
304-
CLASS_NAME_WITH_NAMESPACE_RE,
335+
CLASS_NAME_RE,
305336
/\.new[. (]/
306337
],
307338
scope: {
@@ -317,7 +348,8 @@ export default function(hljs) {
317348
};
318349

319350
const RUBY_DEFAULT_CONTAINS = [
320-
STRING,
351+
STRING_INTERPOLABLE,
352+
STRING_NONINTERPOLABLE,
321353
CLASS_DEFINITION,
322354
INCLUDE_EXTEND,
323355
OBJECT_CREATION,
@@ -326,20 +358,28 @@ export default function(hljs) {
326358
METHOD_DEFINITION,
327359
{
328360
// swallow namespace qualifiers before symbols
329-
begin: hljs.IDENT_RE + '::' },
361+
begin: hljs.IDENT_RE + '::'
362+
},
330363
{
331364
className: 'symbol',
332365
begin: hljs.UNDERSCORE_IDENT_RE + '(!|\\?)?:',
333366
relevance: 0
334367
},
335368
{
336369
className: 'symbol',
337-
begin: ':(?!\\s)',
370+
begin: '(?<!:):(?!\\s|:)',
338371
contains: [
339-
STRING,
340-
{ begin: RUBY_METHOD_RE }
372+
{ begin: /'/, end: /'/ },
373+
{
374+
begin: /"/, end: /"/,
375+
contains: [
376+
hljs.BACKSLASH_ESCAPE,
377+
SUBST
378+
]
379+
},
380+
{ begin: hljs.UNDERSCORE_IDENT_RE }
341381
],
342-
relevance: 0
382+
relevance: 1
343383
},
344384
NUMBER,
345385
{

test/markup/ruby/classes.expect.txt

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<span class="hljs-title class_">Class</span>
2+
<span class="hljs-title class_">ClassName</span>
3+
<span class="hljs-title class_">Class_Name</span>
4+
<span class="hljs-title class_">ClassNAME</span>
5+
<span class="hljs-title class_">ClassName</span>::<span class="hljs-title class_">With</span>::<span class="hljs-title class_">Namespace</span>
6+
<span class="hljs-title class_">ClassName</span>::<span class="hljs-title class_">With</span>.method
7+
::<span class="hljs-title class_">TopLevel</span>::<span class="hljs-title class_">Class</span>

test/markup/ruby/classes.txt

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Class
2+
ClassName
3+
Class_Name
4+
ClassNAME
5+
ClassName::With::Namespace
6+
ClassName::With.method
7+
::TopLevel::Class

test/markup/ruby/strings.expect.txt

+25-1
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,28 @@ c = <span class="hljs-string">?\u{00AF09}</span>
2727
c = <span class="hljs-string">?\u{0AF09}</span>
2828
c = <span class="hljs-string">?\u{AF9}</span>
2929
c = <span class="hljs-string">?\u{F9}</span>
30-
c = <span class="hljs-string">?\u{F}</span>
30+
c = <span class="hljs-string">?\u{F}</span>
31+
32+
<span class="hljs-comment"># Interpolable Strings</span>
33+
<span class="hljs-string">&quot;string&quot;</span>
34+
<span class="hljs-string">&quot;string <span class="hljs-subst">#{var}</span>&quot;</span>
35+
<span class="hljs-string">`string`</span>
36+
<span class="hljs-string">`string <span class="hljs-subst">#{var}</span>`</span>
37+
<span class="hljs-string">%W[foo bar]</span>
38+
<span class="hljs-string">%W[foo bar <span class="hljs-subst">#{var}</span>]</span>
39+
<span class="hljs-string">%Q[foo bar]</span>
40+
<span class="hljs-string">%Q[foo bar <span class="hljs-subst">#{var}</span>]</span>
41+
<span class="hljs-string">%x[foo]</span>
42+
<span class="hljs-string">%x[foo <span class="hljs-subst">#{var}</span>]</span>
43+
<span class="hljs-string">&lt;&lt;~DOC
44+
Multiline heredoc
45+
Text <span class="hljs-subst">#{var}</span>
46+
DOC</span>
47+
48+
<span class="hljs-comment"># Non-interpolable Strings</span>
49+
<span class="hljs-string">&#x27;string&#x27;</span>
50+
<span class="hljs-string">&#x27;string #{var}&#x27;</span>
51+
<span class="hljs-string">%q[foo]</span>
52+
<span class="hljs-string">%q[foo #{var}]</span>
53+
<span class="hljs-string">%w[foo]</span>
54+
<span class="hljs-string">%w[foo #{var}]</span>

test/markup/ruby/strings.txt

+25-1
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,28 @@ c = ?\u{00AF09}
2727
c = ?\u{0AF09}
2828
c = ?\u{AF9}
2929
c = ?\u{F9}
30-
c = ?\u{F}
30+
c = ?\u{F}
31+
32+
# Interpolable Strings
33+
"string"
34+
"string #{var}"
35+
`string`
36+
`string #{var}`
37+
%W[foo bar]
38+
%W[foo bar #{var}]
39+
%Q[foo bar]
40+
%Q[foo bar #{var}]
41+
%x[foo]
42+
%x[foo #{var}]
43+
<<~DOC
44+
Multiline heredoc
45+
Text #{var}
46+
DOC
47+
48+
# Non-interpolable Strings
49+
'string'
50+
'string #{var}'
51+
%q[foo]
52+
%q[foo #{var}]
53+
%w[foo]
54+
%w[foo #{var}]

test/markup/ruby/symbols.expect.txt

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<span class="hljs-symbol">:symbol</span>
2+
<span class="hljs-symbol">:Symbol</span>
3+
<span class="hljs-symbol">:_leading</span>
4+
<span class="hljs-symbol">:trailing_</span>
5+
<span class="hljs-symbol">:contains_underscore</span>
6+
<span class="hljs-symbol">:symbol_CAPS</span>
7+
<span class="hljs-symbol">:&quot;string symbol&quot;</span>
8+
<span class="hljs-symbol">:&quot;interpolated <span class="hljs-subst">#{test}</span>&quot;</span>
9+
<span class="hljs-symbol">:&#x27;string symbol&#x27;</span>
10+
<span class="hljs-symbol">:&#x27;not interpolated #{test}&#x27;</span>
11+
method <span class="hljs-symbol">:symbol</span>
12+
method(<span class="hljs-symbol">:symbol</span>)
13+
method(&amp;<span class="hljs-symbol">:symbol</span>)
14+
assign=<span class="hljs-symbol">:symbol</span>
15+
assign = <span class="hljs-symbol">:symbol</span>
16+
<span class="hljs-symbol">:symbol</span>, others
17+
<span class="hljs-symbol">:</span>1notasymbol
18+
<span class="hljs-symbol">:</span><span class="hljs-string">%q[notasymbol]</span>
19+
20+
<span class="hljs-symbol">hash_symbol:</span> value

test/markup/ruby/symbols.txt

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
:symbol
2+
:Symbol
3+
:_leading
4+
:trailing_
5+
:contains_underscore
6+
:symbol_CAPS
7+
:"string symbol"
8+
:"interpolated #{test}"
9+
:'string symbol'
10+
:'not interpolated #{test}'
11+
method :symbol
12+
method(:symbol)
13+
method(&:symbol)
14+
assign=:symbol
15+
assign = :symbol
16+
:symbol, others
17+
:1notasymbol
18+
:%q[notasymbol]
19+
20+
hash_symbol: value

0 commit comments

Comments
 (0)