Skip to content

Commit 67644b9

Browse files
committed
fix(ruby) symbols, string interpolation, class names with underscores
Signed-off-by: jimtng <2554958+jimtng@users.noreply.github.com>
1 parent d301848 commit 67644b9

File tree

8 files changed

+220
-50
lines changed

8 files changed

+220
-50
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) symbols, string interpolation, class names with underscores
6+
7+
18
## Version 11.11.1
29

310
- Fixes regression with Rust grammar.

src/languages/ruby.js

+105-48
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 = [
@@ -120,19 +114,16 @@ export default function(hljs) {
120114
className: 'subst',
121115
begin: /#\{/,
122116
end: /\}/,
123-
keywords: RUBY_KEYWORDS
117+
keywords: RUBY_KEYWORDS,
118+
relevance: 10
124119
};
125-
const STRING = {
120+
const STRING_INTERPOLABLE = {
126121
className: 'string',
127122
contains: [
128123
hljs.BACKSLASH_ESCAPE,
129124
SUBST
130125
],
131126
variants: [
132-
{
133-
begin: /'/,
134-
end: /'/
135-
},
136127
{
137128
begin: /"/,
138129
end: /"/
@@ -142,45 +133,45 @@ export default function(hljs) {
142133
end: /`/
143134
},
144135
{
145-
begin: /%[qQwWx]?\(/,
146-
end: /\)/
136+
begin: /%[QWx]?\(/,
137+
end: /\)/,
138+
relevance: 2
147139
},
148140
{
149-
begin: /%[qQwWx]?\[/,
150-
end: /\]/
141+
begin: /%[QWx]?\[/,
142+
end: /\]/,
143+
relevance: 2
151144
},
152145
{
153-
begin: /%[qQwWx]?\{/,
154-
end: /\}/
146+
begin: /%[QWx]?\{/,
147+
end: /\}/,
148+
relevance: 2
155149
},
156150
{
157-
begin: /%[qQwWx]?</,
158-
end: />/
151+
begin: /%[QWx]?</,
152+
end: />/,
153+
relevance: 2
159154
},
160155
{
161-
begin: /%[qQwWx]?\//,
162-
end: /\//
156+
begin: /%[QWx]?\//,
157+
end: /\//,
158+
relevance: 2
163159
},
164160
{
165-
begin: /%[qQwWx]?%/,
166-
end: /%/
161+
begin: /%[QWx]?%/,
162+
end: /%/,
163+
relevance: 2
167164
},
168165
{
169-
begin: /%[qQwWx]?-/,
170-
end: /-/
166+
begin: /%[QWx]?-/,
167+
end: /-/,
168+
relevance: 2
171169
},
172170
{
173-
begin: /%[qQwWx]?\|/,
174-
end: /\|/
171+
begin: /%[QWx]?\|/,
172+
end: /\|/,
173+
relevance: 2
175174
},
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/ },
184175
// heredocs
185176
{
186177
// this guard makes sure that we have an entire heredoc and not a false
@@ -202,6 +193,63 @@ export default function(hljs) {
202193
}
203194
]
204195
};
196+
const STRING_NONINTERPOLABLE = {
197+
className: 'string',
198+
variants: [
199+
{
200+
begin: /'/,
201+
end: /'/
202+
},
203+
{
204+
begin: /%[qw]?\(/,
205+
end: /\)/,
206+
relevance: 2
207+
},
208+
{
209+
begin: /%[qw]?\[/,
210+
end: /\]/,
211+
relevance: 2
212+
},
213+
{
214+
begin: /%[qw]?\{/,
215+
end: /\}/,
216+
relevance: 2
217+
},
218+
{
219+
begin: /%[qw]?</,
220+
end: />/,
221+
relevance: 2
222+
},
223+
{
224+
begin: /%[qw]?\//,
225+
end: /\//,
226+
relevance: 2
227+
},
228+
{
229+
begin: /%[qw]?%/,
230+
end: /%/,
231+
relevance: 2
232+
},
233+
{
234+
begin: /%[qw]?-/,
235+
end: /-/,
236+
relevance: 2
237+
},
238+
{
239+
begin: /%[qw]?\|/,
240+
end: /\|/,
241+
relevance: 2
242+
},
243+
// in the following expressions, \B in the beginning suppresses recognition of ?-sequences
244+
// where ? is the last character of a preceding identifier, as in: `func?4`
245+
{ begin: /\B\?(\\\d{1,3})/ },
246+
{ begin: /\B\?(\\x[A-Fa-f0-9]{1,2})/ },
247+
{ begin: /\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/ },
248+
{ begin: /\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/ },
249+
{ begin: /\B\?\\(c|C-)[\x20-\x7e]/ },
250+
{ begin: /\B\?\\?\S/ }
251+
]
252+
};
205253

206254
// Ruby syntax is underdocumented, but this grammar seems to be accurate
207255
// as of version 2.7.2 (confirmed with (irb and `Ripper.sexp(...)`)
@@ -246,7 +294,7 @@ export default function(hljs) {
246294
const INCLUDE_EXTEND = {
247295
match: [
248296
/(include|extend)\s+/,
249-
CLASS_NAME_WITH_NAMESPACE_RE
297+
CLASS_NAME_RE
250298
],
251299
scope: {
252300
2: "title.class"
@@ -259,15 +307,15 @@ export default function(hljs) {
259307
{
260308
match: [
261309
/class\s+/,
262-
CLASS_NAME_WITH_NAMESPACE_RE,
310+
CLASS_NAME_RE,
263311
/\s+<\s+/,
264-
CLASS_NAME_WITH_NAMESPACE_RE
312+
CLASS_NAME_RE
265313
]
266314
},
267315
{
268316
match: [
269317
/\b(class|module)\s+/,
270-
CLASS_NAME_WITH_NAMESPACE_RE
318+
CLASS_NAME_RE
271319
]
272320
}
273321
],
@@ -301,7 +349,7 @@ export default function(hljs) {
301349
const OBJECT_CREATION = {
302350
relevance: 0,
303351
match: [
304-
CLASS_NAME_WITH_NAMESPACE_RE,
352+
CLASS_NAME_RE,
305353
/\.new[. (]/
306354
],
307355
scope: {
@@ -317,7 +365,8 @@ export default function(hljs) {
317365
};
318366

319367
const RUBY_DEFAULT_CONTAINS = [
320-
STRING,
368+
STRING_INTERPOLABLE,
369+
STRING_NONINTERPOLABLE,
321370
CLASS_DEFINITION,
322371
INCLUDE_EXTEND,
323372
OBJECT_CREATION,
@@ -326,20 +375,28 @@ export default function(hljs) {
326375
METHOD_DEFINITION,
327376
{
328377
// swallow namespace qualifiers before symbols
329-
begin: hljs.IDENT_RE + '::' },
378+
begin: hljs.IDENT_RE + '::'
379+
},
330380
{
331381
className: 'symbol',
332382
begin: hljs.UNDERSCORE_IDENT_RE + '(!|\\?)?:',
333383
relevance: 0
334384
},
335385
{
336386
className: 'symbol',
337-
begin: ':(?!\\s)',
387+
begin: '(?<!:):(?!\\s|:)',
338388
contains: [
339-
STRING,
340-
{ begin: RUBY_METHOD_RE }
389+
{ begin: /'/, end: /'/ },
390+
{
391+
begin: /"/, end: /"/,
392+
contains: [
393+
hljs.BACKSLASH_ESCAPE,
394+
SUBST
395+
]
396+
},
397+
{ begin: hljs.UNDERSCORE_IDENT_RE }
341398
],
342-
relevance: 0
399+
relevance: 1
343400
},
344401
NUMBER,
345402
{

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

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
::notsymbol
21+
22+
<span class="hljs-symbol">hash_symbol:</span> value

test/markup/ruby/symbols.txt

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
::notsymbol
21+
22+
hash_symbol: value

0 commit comments

Comments
 (0)