Skip to content

Commit 0afd0d3

Browse files
authored
fix(javascript/typescript): lambda with parens in parameters fails (#2502)
* fix(javascript/typescript): lambda with parens in parameters fails - Fixes both JavaScript and TypeScript grammars Fixes samples like: const bad = ((a, b) => [...a, b]); sides.every((length,width=(3+2+(4/5))) => length > 0 ); This is done by counting parens in the regex that finds arrows functions. Currently we can only handle 2 levels of nesting as shown in the second example above. * allow much richer highlighting inside params * improve highlighting inside arguments on typescript
1 parent 7502e42 commit 0afd0d3

File tree

8 files changed

+102
-44
lines changed

8 files changed

+102
-44
lines changed

CHANGES.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
## Version 10.1.0 (in progress)
22

3-
Language improvements:
3+
Language Improvements:
44

5+
- fix(javascript) `=>` function with nested `()` in params now works (#2502) [Josh Goebel][]
6+
- fix(typescript) `=>` function with nested `()` in params now works (#2502) [Josh Goebel][]
57
- fix(yaml) Fix tags to include non-word characters (#2486) [Peter Plantinga][]
68

9+
[Josh Goebel]: https://github.com/yyyc514
710
[Peter Plantinga]: https://github.com/pplantinga
811

912

src/languages/javascript.js

+16-2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ export default function(hljs) {
9090
hljs.REGEXP_MODE
9191
];
9292
var PARAMS_CONTAINS = SUBST.contains.concat([
93+
// eat recursive parens in sub expressions
94+
{ begin: /\(/, end: /\)/,
95+
contains: ["self"].concat(SUBST.contains, [hljs.C_BLOCK_COMMENT_MODE, hljs.C_LINE_COMMENT_MODE])
96+
},
9397
hljs.C_BLOCK_COMMENT_MODE,
9498
hljs.C_LINE_COMMENT_MODE
9599
]);
@@ -175,17 +179,27 @@ export default function(hljs) {
175179
hljs.REGEXP_MODE,
176180
{
177181
className: 'function',
178-
begin: '(\\(.*?\\)|' + IDENT_RE + ')\\s*=>', returnBegin: true,
182+
// we have to count the parens to make sure we actually have the
183+
// correct bounding ( ) before the =>. There could be any number of
184+
// sub-expressions inside also surrounded by parens.
185+
begin: '(\\([^(]*' +
186+
'(\\([^(]*' +
187+
'(\\([^(]*' +
188+
'\\))?' +
189+
'\\))?' +
190+
'\\)|' + hljs.UNDERSCORE_IDENT_RE + ')\\s*=>', returnBegin: true,
179191
end: '\\s*=>',
180192
contains: [
181193
{
182194
className: 'params',
183195
variants: [
184196
{
185-
begin: IDENT_RE
197+
begin: hljs.UNDERSCORE_IDENT_RE
186198
},
187199
{
200+
className: null,
188201
begin: /\(\s*\)/,
202+
skip: true
189203
},
190204
{
191205
begin: /\(/, end: /\)/,

src/languages/typescript.js

+39-38
Original file line numberDiff line numberDiff line change
@@ -27,38 +27,10 @@ export default function(hljs) {
2727
'Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require ' +
2828
'module console window document any number boolean string void Promise'
2929
};
30-
3130
var DECORATOR = {
3231
className: 'meta',
3332
begin: '@' + JS_IDENT_RE,
3433
};
35-
36-
var ARGS =
37-
{
38-
begin: '\\(',
39-
end: /\)/,
40-
keywords: KEYWORDS,
41-
contains: [
42-
'self',
43-
hljs.QUOTE_STRING_MODE,
44-
hljs.APOS_STRING_MODE,
45-
hljs.NUMBER_MODE
46-
]
47-
};
48-
49-
var PARAMS = {
50-
className: 'params',
51-
begin: /\(/, end: /\)/,
52-
excludeBegin: true,
53-
excludeEnd: true,
54-
keywords: KEYWORDS,
55-
contains: [
56-
hljs.C_LINE_COMMENT_MODE,
57-
hljs.C_BLOCK_COMMENT_MODE,
58-
DECORATOR,
59-
ARGS
60-
]
61-
};
6234
var NUMBER = {
6335
className: 'number',
6436
variants: [
@@ -113,8 +85,31 @@ export default function(hljs) {
11385
NUMBER,
11486
hljs.REGEXP_MODE
11587
];
116-
117-
88+
var ARGUMENTS =
89+
{
90+
begin: '\\(',
91+
end: /\)/,
92+
keywords: KEYWORDS,
93+
contains: [
94+
'self',
95+
hljs.QUOTE_STRING_MODE,
96+
hljs.APOS_STRING_MODE,
97+
hljs.NUMBER_MODE
98+
]
99+
};
100+
var PARAMS = {
101+
className: 'params',
102+
begin: /\(/, end: /\)/,
103+
excludeBegin: true,
104+
excludeEnd: true,
105+
keywords: KEYWORDS,
106+
contains: [
107+
hljs.C_LINE_COMMENT_MODE,
108+
hljs.C_BLOCK_COMMENT_MODE,
109+
DECORATOR,
110+
ARGUMENTS
111+
]
112+
};
118113

119114
return {
120115
name: 'TypeScript',
@@ -142,27 +137,33 @@ export default function(hljs) {
142137
hljs.REGEXP_MODE,
143138
{
144139
className: 'function',
145-
begin: '(\\(.*?\\)|' + hljs.IDENT_RE + ')\\s*=>', returnBegin: true,
140+
// we have to count the parens to make sure we actually have the
141+
// correct bounding ( ) before the =>. There could be any number of
142+
// sub-expressions inside also surrounded by parens.
143+
begin: '(\\([^(]*' +
144+
'(\\([^(]*' +
145+
'(\\([^(]*' +
146+
'\\))?' +
147+
'\\))?' +
148+
'\\)|' + hljs.UNDERSCORE_IDENT_RE + ')\\s*=>', returnBegin: true,
146149
end: '\\s*=>',
147150
contains: [
148151
{
149152
className: 'params',
150153
variants: [
151154
{
152-
begin: hljs.IDENT_RE
155+
begin: hljs.UNDERSCORE_IDENT_RE
153156
},
154157
{
158+
className: null,
155159
begin: /\(\s*\)/,
160+
skip: true
156161
},
157162
{
158163
begin: /\(/, end: /\)/,
159164
excludeBegin: true, excludeEnd: true,
160165
keywords: KEYWORDS,
161-
contains: [
162-
'self',
163-
hljs.C_LINE_COMMENT_MODE,
164-
hljs.C_BLOCK_COMMENT_MODE
165-
]
166+
contains: ARGUMENTS.contains
166167
}
167168
]
168169
}
@@ -209,7 +210,7 @@ export default function(hljs) {
209210
begin: '\\.' + hljs.IDENT_RE, relevance: 0 // hack: prevents detection of keywords after dots
210211
},
211212
DECORATOR,
212-
ARGS
213+
ARGUMENTS
213214
]
214215
};
215216
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
<span class="hljs-keyword">var</span> f = <span class="hljs-function"><span class="hljs-params">x</span> =&gt;</span> x;
22
f(<span class="hljs-function"><span class="hljs-params">x</span> =&gt;</span> x + <span class="hljs-function">(<span class="hljs-params">y=<span class="hljs-number">2</span>, z=<span class="hljs-literal">undefined</span>, ...rest</span>) =&gt;</span> y);
3-
<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> <span class="hljs-literal">null</span>;
3+
<span class="hljs-function">() =&gt;</span> <span class="hljs-literal">null</span>;
44
<span class="hljs-keyword">const</span> FC = <span class="hljs-function"><span class="hljs-params">props</span> =&gt;</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>functional component<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>;
5+
6+
<span class="hljs-keyword">const</span> good = <span class="hljs-function">() =&gt;</span> <span class="hljs-number">0</span>;
7+
<span class="hljs-keyword">const</span> good = <span class="hljs-function">(<span class="hljs-params">x</span>) =&gt;</span> <span class="hljs-number">0</span>;
8+
<span class="hljs-keyword">const</span> bad = (<span class="hljs-function"><span class="hljs-params">a</span> =&gt;</span> [...a, b]);
9+
<span class="hljs-keyword">const</span> bad = (<span class="hljs-function"><span class="hljs-params">_</span> =&gt;</span> doSomething());
10+
<span class="hljs-keyword">const</span> bad = (<span class="hljs-function">() =&gt;</span> <span class="hljs-number">0</span>);
11+
<span class="hljs-keyword">const</span> bad = (<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> [...a, b]);
12+
<span class="hljs-keyword">const</span> array = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>].reduce(<span class="hljs-function">(<span class="hljs-params">acc, next</span>) =&gt;</span> [...acc, next], []);
13+
sides.every(<span class="hljs-function">(<span class="hljs-params">length,width=(<span class="hljs-number">3</span>+<span class="hljs-number">2</span>+(<span class="hljs-number">4</span>/<span class="hljs-number">5</span>))</span>) =&gt;</span> length &gt; <span class="hljs-number">0</span> );

test/markup/javascript/arrow-function.txt

+10
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,13 @@ var f = x => x;
22
f(x => x + (y=2, z=undefined, ...rest) => y);
33
() => null;
44
const FC = props => <p>functional component</p>;
5+
6+
const good = () => 0;
7+
const good = (x) => 0;
8+
const bad = (a => [...a, b]);
9+
const bad = (_ => doSomething());
10+
const bad = (() => 0);
11+
const bad = ((a, b) => [...a, b]);
12+
const array = [1, 2, 3].reduce((acc, next) => [...acc, next], []);
13+
sides.every((length,width=(3+2+(4/5))) => length > 0 );
14+

test/markup/javascript/jsx.expect.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
<span class="hljs-keyword">return</span> (<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">node</span> <span class="hljs-attr">attr</span>=<span class="hljs-string">"value"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">node</span>&gt;</span></span>);
1010

11-
<span class="hljs-keyword">const</span> n = <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">X</span> /&gt;</span></span>
12-
<span class="hljs-keyword">const</span> m = <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">X</span> <span class="hljs-attr">x</span>=<span class="hljs-string">""</span> /&gt;</span></span>
11+
<span class="hljs-keyword">const</span> n = <span class="hljs-function">() =&gt;</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">X</span> /&gt;</span></span>
12+
<span class="hljs-keyword">const</span> m = <span class="hljs-function">() =&gt;</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">X</span> <span class="hljs-attr">x</span>=<span class="hljs-string">""</span> /&gt;</span></span>
1313

1414
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">App</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span> </span>{
1515
render() {

test/markup/typescript/functions.expect.txt

+10
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,13 @@
1313
<span class="hljs-keyword">type</span> Foo = {
1414
functionInFoo(): <span class="hljs-built_in">void</span>;
1515
};
16+
17+
<span class="hljs-keyword">const</span> good = <span class="hljs-function">() =&gt;</span> <span class="hljs-number">0</span>;
18+
<span class="hljs-keyword">const</span> good = <span class="hljs-function">(<span class="hljs-params">x</span>) =&gt;</span> <span class="hljs-number">0</span>;
19+
<span class="hljs-keyword">const</span> bad = (<span class="hljs-function"><span class="hljs-params">a</span> =&gt;</span> [...a, b]);
20+
<span class="hljs-keyword">const</span> bad = (<span class="hljs-function"><span class="hljs-params">_</span> =&gt;</span> doSomething());
21+
<span class="hljs-keyword">const</span> bad = (<span class="hljs-function">() =&gt;</span> <span class="hljs-number">0</span>);
22+
<span class="hljs-keyword">const</span> bad = (<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> [...a, b]);
23+
<span class="hljs-keyword">const</span> array = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>].reduce&lt;<span class="hljs-built_in">number</span>[]&gt;(<span class="hljs-function">(<span class="hljs-params">acc, next</span>) =&gt;</span> [...acc, next], []);
24+
<span class="hljs-keyword">const</span> bad = (<span class="hljs-function">(<span class="hljs-params">a=<span class="hljs-number">2</span>, b=<span class="hljs-number">5</span></span>) =&gt;</span> [...a, b]);
25+
sides.every(<span class="hljs-function">(<span class="hljs-params">length,width=(<span class="hljs-params"><span class="hljs-number">3</span>+<span class="hljs-number">2</span>+(<span class="hljs-params"><span class="hljs-number">4</span>/<span class="hljs-number">5</span></span>)</span>)</span>) =&gt;</span> length &gt; <span class="hljs-number">0</span> );

test/markup/typescript/functions.txt

+11
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,14 @@ function getArray(): number[] {
1313
type Foo = {
1414
functionInFoo(): void;
1515
};
16+
17+
const good = () => 0;
18+
const good = (x) => 0;
19+
const bad = (a => [...a, b]);
20+
const bad = (_ => doSomething());
21+
const bad = (() => 0);
22+
const bad = ((a, b) => [...a, b]);
23+
const array = [1, 2, 3].reduce<number[]>((acc, next) => [...acc, next], []);
24+
const bad = ((a=2, b=5) => [...a, b]);
25+
sides.every((length,width=(3+2+(4/5))) => length > 0 );
26+

0 commit comments

Comments
 (0)