Skip to content

Commit c6f2995

Browse files
authored
fix(groovy) strings are not allowed inside ternary clauses (#2565)
* fix(groovy) strings are not allowed inside ternary clauses * whitespace can also include tabs
1 parent 02bdae3 commit c6f2995

File tree

6 files changed

+206
-58
lines changed

6 files changed

+206
-58
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Deprecations:
2121

2222
Language Improvements:
2323

24+
- fix(groovy) strings are not allowed inside ternary clauses (#2217) [Josh Goebel][]
2425
- fix(typescript) add `readonly` keyword (#2562) [Martin (Lhoerion)][]
2526
- fix(javascript) fix regex inside parens after a non-regex (#2530) [Josh Goebel][]
2627
- enh(typescript) use identifier to match potential keywords, preventing false positivites (#2519) [Josh Goebel][]

src/languages/groovy.js

+83-58
Original file line numberDiff line numberDiff line change
@@ -5,69 +5,84 @@
55
Website: https://groovy-lang.org
66
*/
77

8+
import * as regex from "../lib/regex";
9+
10+
function variants(variants, obj = {}) {
11+
obj.variants = variants;
12+
return obj;
13+
}
14+
815
export default function(hljs) {
16+
const IDENT_RE = '[A-Za-z0-9_$]+';
17+
const COMMENT = variants([
18+
hljs.C_LINE_COMMENT_MODE,
19+
hljs.C_BLOCK_COMMENT_MODE,
20+
hljs.COMMENT(
21+
'/\\*\\*',
22+
'\\*/',
23+
{
24+
relevance : 0,
25+
contains : [
26+
{
27+
// eat up @'s in emails to prevent them to be recognized as doctags
28+
begin: /\w+@/, relevance: 0
29+
}, {
30+
className : 'doctag',
31+
begin : '@[A-Za-z]+'
32+
}
33+
]
34+
}
35+
)
36+
]);
37+
const REGEXP = {
38+
className: 'regexp',
39+
begin: /~?\/[^\/\n]+\//,
40+
contains: [
41+
hljs.BACKSLASH_ESCAPE
42+
]
43+
};
44+
const NUMBER = variants([
45+
hljs.BINARY_NUMBER_MODE,
46+
hljs.C_NUMBER_MODE,
47+
]);
48+
const STRING = variants([
49+
{
50+
begin: /"""/,
51+
end: /"""/
52+
}, {
53+
begin: /'''/,
54+
end: /'''/
55+
}, {
56+
begin: "\\$/",
57+
end: "/\\$",
58+
relevance: 10
59+
},
60+
hljs.APOS_STRING_MODE,
61+
hljs.QUOTE_STRING_MODE,
62+
],
63+
{ className: "string" }
64+
);
65+
966
return {
1067
name: 'Groovy',
1168
keywords: {
12-
literal : 'true false null',
69+
built_in: 'this super',
70+
literal: 'true false null',
1371
keyword:
1472
'byte short char int long boolean float double void ' +
1573
// groovy specific keywords
1674
'def as in assert trait ' +
1775
// common keywords with Java
18-
'super this abstract static volatile transient public private protected synchronized final ' +
76+
'abstract static volatile transient public private protected synchronized final ' +
1977
'class interface enum if else for while switch case break default continue ' +
2078
'throw throws try catch finally implements extends new import package return instanceof'
2179
},
22-
2380
contains: [
24-
hljs.COMMENT(
25-
'/\\*\\*',
26-
'\\*/',
27-
{
28-
relevance : 0,
29-
contains : [
30-
{
31-
// eat up @'s in emails to prevent them to be recognized as doctags
32-
begin: /\w+@/, relevance: 0
33-
},
34-
{
35-
className : 'doctag',
36-
begin : '@[A-Za-z]+'
37-
}
38-
]
39-
}
40-
),
41-
hljs.C_LINE_COMMENT_MODE,
42-
hljs.C_BLOCK_COMMENT_MODE,
43-
{
44-
className: 'string',
45-
begin: '"""', end: '"""'
46-
},
47-
{
48-
className: 'string',
49-
begin: "'''", end: "'''"
50-
},
51-
{
52-
className: 'string',
53-
begin: "\\$/", end: "/\\$",
54-
relevance: 10
55-
},
56-
hljs.APOS_STRING_MODE,
57-
{
58-
className: 'regexp',
59-
begin: /~?\/[^\/\n]+\//,
60-
contains: [
61-
hljs.BACKSLASH_ESCAPE
62-
]
63-
},
64-
hljs.QUOTE_STRING_MODE,
65-
{
66-
className: 'meta',
67-
begin: "^#!/usr/bin/env", end: '$',
68-
illegal: '\n'
69-
},
70-
hljs.BINARY_NUMBER_MODE,
81+
hljs.SHEBANG(),
82+
COMMENT,
83+
STRING,
84+
REGEXP,
85+
NUMBER,
7186
{
7287
className: 'class',
7388
beginKeywords: 'class interface trait enum', end: '{',
@@ -77,25 +92,35 @@ export default function(hljs) {
7792
hljs.UNDERSCORE_TITLE_MODE
7893
]
7994
},
80-
hljs.C_NUMBER_MODE,
8195
{
8296
className: 'meta', begin: '@[A-Za-z]+'
8397
},
8498
{
85-
// highlight map keys and named parameters as strings
86-
className: 'string', begin: /[^\?]{0}[A-Za-z0-9_$]+ *:/
99+
// highlight map keys and named parameters as attrs
100+
className: 'attr', begin: IDENT_RE + '[ \t]*:'
87101
},
88102
{
89-
// catch middle element of the ternary operator
90-
// to avoid highlight it as a label, named parameter, or map key
91-
begin: /\?/, end: /\:/
103+
// catch middle element of the ternary operator
104+
// to avoid highlight it as a label, named parameter, or map key
105+
begin: /\?/,
106+
end: /:/,
107+
contains: [
108+
COMMENT,
109+
STRING,
110+
REGEXP,
111+
NUMBER,
112+
'self'
113+
]
92114
},
93115
{
94116
// highlight labeled statements
95-
className: 'symbol', begin: '^\\s*[A-Za-z0-9_$]+:',
117+
className: 'symbol',
118+
begin: '^[ \t]*' + regex.lookahead(IDENT_RE + ':'),
119+
excludeBegin: true,
120+
end: IDENT_RE + ':',
96121
relevance: 0
97122
}
98123
],
99124
illegal: /#|<\//
100-
}
125+
};
101126
}

test/markup/groovy/default.expect.txt

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<span class="hljs-meta">#!/usr/bin/env groovy</span>
2+
<span class="hljs-keyword">package</span> model
3+
4+
<span class="hljs-keyword">import</span> groovy.transform.CompileStatic
5+
<span class="hljs-keyword">import</span> java.util.List <span class="hljs-keyword">as</span> MyList
6+
7+
<span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Distributable</span> {</span>
8+
<span class="hljs-keyword">void</span> distribute(String version) {}
9+
}
10+
11+
<span class="hljs-meta">@CompileStatic</span>
12+
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Distribution</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Distributable</span> {</span>
13+
<span class="hljs-keyword">double</span> number = <span class="hljs-number">1234.234</span> / <span class="hljs-number">567</span>
14+
<span class="hljs-keyword">def</span> otherNumber = <span class="hljs-number">3</span> / <span class="hljs-number">4</span>
15+
<span class="hljs-keyword">boolean</span> archivable = condition ?: <span class="hljs-literal">true</span>
16+
<span class="hljs-keyword">def</span> ternary = a ? b : c
17+
String name = <span class="hljs-string">"Guillaume"</span>
18+
Closure description = <span class="hljs-literal">null</span>
19+
List&lt;DownloadPackage&gt; packages = []
20+
String regex = <span class="hljs-regexp">~/.*foo.*/</span>
21+
String multi = <span class="hljs-string">'''
22+
multi line string
23+
'''</span> + <span class="hljs-string">"""
24+
now with double quotes and ${gstring}
25+
"""</span> + <span class="hljs-string">$/
26+
even with dollar slashy strings
27+
/$</span>
28+
29+
<span class="hljs-comment">/**
30+
* description method
31+
* @param cl the closure
32+
*/</span>
33+
<span class="hljs-keyword">void</span> description(Closure cl) { <span class="hljs-built_in">this</span>.description = cl }
34+
35+
<span class="hljs-keyword">void</span> version(String name, Closure versionSpec) {
36+
<span class="hljs-keyword">def</span> closure = { println <span class="hljs-string">"hi"</span> } <span class="hljs-keyword">as</span> Runnable
37+
38+
MyList ml = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, [<span class="hljs-attr">a:</span> <span class="hljs-number">1</span>, <span class="hljs-attr">b:</span><span class="hljs-number">2</span>,<span class="hljs-attr">c :</span><span class="hljs-number">3</span>]]
39+
<span class="hljs-keyword">for</span> (ch <span class="hljs-keyword">in</span> <span class="hljs-string">"name"</span>) {}
40+
41+
<span class="hljs-comment">// single line comment</span>
42+
DownloadPackage pkg = <span class="hljs-keyword">new</span> DownloadPackage(<span class="hljs-attr">version:</span> name)
43+
44+
check <span class="hljs-attr">that:</span> <span class="hljs-literal">true</span>
45+
46+
<span class="hljs-symbol">label:</span>
47+
<span class="hljs-comment">// This is purposely tabbed</span>
48+
<span class="hljs-symbol">tabbed_label:</span>
49+
<span class="hljs-keyword">def</span> clone = versionSpec.rehydrate(pkg, pkg, pkg)
50+
<span class="hljs-comment">/*
51+
now clone() in a multiline comment
52+
*/</span>
53+
clone()
54+
packages.add(pkg)
55+
56+
<span class="hljs-keyword">assert</span> <span class="hljs-number">4</span> / <span class="hljs-number">2</span> == <span class="hljs-number">2</span>
57+
}
58+
}

test/markup/groovy/default.txt

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/env groovy
2+
package model
3+
4+
import groovy.transform.CompileStatic
5+
import java.util.List as MyList
6+
7+
trait Distributable {
8+
void distribute(String version) {}
9+
}
10+
11+
@CompileStatic
12+
class Distribution implements Distributable {
13+
double number = 1234.234 / 567
14+
def otherNumber = 3 / 4
15+
boolean archivable = condition ?: true
16+
def ternary = a ? b : c
17+
String name = "Guillaume"
18+
Closure description = null
19+
List<DownloadPackage> packages = []
20+
String regex = ~/.*foo.*/
21+
String multi = '''
22+
multi line string
23+
''' + """
24+
now with double quotes and ${gstring}
25+
""" + $/
26+
even with dollar slashy strings
27+
/$
28+
29+
/**
30+
* description method
31+
* @param cl the closure
32+
*/
33+
void description(Closure cl) { this.description = cl }
34+
35+
void version(String name, Closure versionSpec) {
36+
def closure = { println "hi" } as Runnable
37+
38+
MyList ml = [1, 2, [a: 1, b:2,c :3]]
39+
for (ch in "name") {}
40+
41+
// single line comment
42+
DownloadPackage pkg = new DownloadPackage(version: name)
43+
44+
check that: true
45+
46+
label:
47+
// This is purposely tabbed
48+
tabbed_label:
49+
def clone = versionSpec.rehydrate(pkg, pkg, pkg)
50+
/*
51+
now clone() in a multiline comment
52+
*/
53+
clone()
54+
packages.add(pkg)
55+
56+
assert 4 / 2 == 2
57+
}
58+
}

test/markup/groovy/oneoffs.expect.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<span class="hljs-comment">// ternary can include quotes</span>
2+
<span class="hljs-keyword">def</span> formattingMsg = label &lt; <span class="hljs-number">0</span> ? (<span class="hljs-string">'The following files need formatting:\n '</span> +
3+
codeStyleFiles.join(<span class="hljs-string">'\n '</span>)) : <span class="hljs-string">'All files are correctly formatted'</span>

test/markup/groovy/oneoffs.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// ternary can include quotes
2+
def formattingMsg = label < 0 ? ('The following files need formatting:\n ' +
3+
codeStyleFiles.join('\n ')) : 'All files are correctly formatted'

0 commit comments

Comments
 (0)