diff --git a/CHANGES.md b/CHANGES.md index f77c7baadb..9c7eda553b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,9 +1,12 @@ ## Version 10.1.0 (in progress) -Language improvements: +Language Improvements: +- fix(javascript) `=>` function with nested `()` in params now works (#2502) [Josh Goebel][] +- fix(typescript) `=>` function with nested `()` in params now works (#2502) [Josh Goebel][] - fix(yaml) Fix tags to include non-word characters (#2486) [Peter Plantinga][] +[Josh Goebel]: https://github.com/yyyc514 [Peter Plantinga]: https://github.com/pplantinga diff --git a/src/languages/javascript.js b/src/languages/javascript.js index 9636f508da..0ad9b3f18f 100644 --- a/src/languages/javascript.js +++ b/src/languages/javascript.js @@ -90,6 +90,10 @@ export default function(hljs) { hljs.REGEXP_MODE ]; var PARAMS_CONTAINS = SUBST.contains.concat([ + // eat recursive parens in sub expressions + { begin: /\(/, end: /\)/, + contains: ["self"].concat(SUBST.contains, [hljs.C_BLOCK_COMMENT_MODE, hljs.C_LINE_COMMENT_MODE]) + }, hljs.C_BLOCK_COMMENT_MODE, hljs.C_LINE_COMMENT_MODE ]); @@ -175,17 +179,27 @@ export default function(hljs) { hljs.REGEXP_MODE, { className: 'function', - begin: '(\\(.*?\\)|' + IDENT_RE + ')\\s*=>', returnBegin: true, + // we have to count the parens to make sure we actually have the + // correct bounding ( ) before the =>. There could be any number of + // sub-expressions inside also surrounded by parens. + begin: '(\\([^(]*' + + '(\\([^(]*' + + '(\\([^(]*' + + '\\))?' + + '\\))?' + + '\\)|' + hljs.UNDERSCORE_IDENT_RE + ')\\s*=>', returnBegin: true, end: '\\s*=>', contains: [ { className: 'params', variants: [ { - begin: IDENT_RE + begin: hljs.UNDERSCORE_IDENT_RE }, { + className: null, begin: /\(\s*\)/, + skip: true }, { begin: /\(/, end: /\)/, diff --git a/src/languages/typescript.js b/src/languages/typescript.js index 23d3fb34a4..9266b55e6b 100644 --- a/src/languages/typescript.js +++ b/src/languages/typescript.js @@ -27,38 +27,10 @@ export default function(hljs) { 'Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require ' + 'module console window document any number boolean string void Promise' }; - var DECORATOR = { className: 'meta', begin: '@' + JS_IDENT_RE, }; - - var ARGS = - { - begin: '\\(', - end: /\)/, - keywords: KEYWORDS, - contains: [ - 'self', - hljs.QUOTE_STRING_MODE, - hljs.APOS_STRING_MODE, - hljs.NUMBER_MODE - ] - }; - - var PARAMS = { - className: 'params', - begin: /\(/, end: /\)/, - excludeBegin: true, - excludeEnd: true, - keywords: KEYWORDS, - contains: [ - hljs.C_LINE_COMMENT_MODE, - hljs.C_BLOCK_COMMENT_MODE, - DECORATOR, - ARGS - ] - }; var NUMBER = { className: 'number', variants: [ @@ -113,8 +85,31 @@ export default function(hljs) { NUMBER, hljs.REGEXP_MODE ]; - - + var ARGUMENTS = + { + begin: '\\(', + end: /\)/, + keywords: KEYWORDS, + contains: [ + 'self', + hljs.QUOTE_STRING_MODE, + hljs.APOS_STRING_MODE, + hljs.NUMBER_MODE + ] + }; + var PARAMS = { + className: 'params', + begin: /\(/, end: /\)/, + excludeBegin: true, + excludeEnd: true, + keywords: KEYWORDS, + contains: [ + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE, + DECORATOR, + ARGUMENTS + ] + }; return { name: 'TypeScript', @@ -142,27 +137,33 @@ export default function(hljs) { hljs.REGEXP_MODE, { className: 'function', - begin: '(\\(.*?\\)|' + hljs.IDENT_RE + ')\\s*=>', returnBegin: true, + // we have to count the parens to make sure we actually have the + // correct bounding ( ) before the =>. There could be any number of + // sub-expressions inside also surrounded by parens. + begin: '(\\([^(]*' + + '(\\([^(]*' + + '(\\([^(]*' + + '\\))?' + + '\\))?' + + '\\)|' + hljs.UNDERSCORE_IDENT_RE + ')\\s*=>', returnBegin: true, end: '\\s*=>', contains: [ { className: 'params', variants: [ { - begin: hljs.IDENT_RE + begin: hljs.UNDERSCORE_IDENT_RE }, { + className: null, begin: /\(\s*\)/, + skip: true }, { begin: /\(/, end: /\)/, excludeBegin: true, excludeEnd: true, keywords: KEYWORDS, - contains: [ - 'self', - hljs.C_LINE_COMMENT_MODE, - hljs.C_BLOCK_COMMENT_MODE - ] + contains: ARGUMENTS.contains } ] } @@ -209,7 +210,7 @@ export default function(hljs) { begin: '\\.' + hljs.IDENT_RE, relevance: 0 // hack: prevents detection of keywords after dots }, DECORATOR, - ARGS + ARGUMENTS ] }; } diff --git a/test/markup/javascript/arrow-function.expect.txt b/test/markup/javascript/arrow-function.expect.txt index c7b648e5bc..9683eb3982 100644 --- a/test/markup/javascript/arrow-function.expect.txt +++ b/test/markup/javascript/arrow-function.expect.txt @@ -1,4 +1,13 @@ var f = x => x; f(x => x + (y=2, z=undefined, ...rest) => y); -() => null; +() => null; const FC = props => <p>functional component</p>; + +const good = () => 0; +const good = (x) => 0; +const bad = (a => [...a, b]); +const bad = (_ => doSomething()); +const bad = (() => 0); +const bad = ((a, b) => [...a, b]); +const array = [1, 2, 3].reduce((acc, next) => [...acc, next], []); +sides.every((length,width=(3+2+(4/5))) => length > 0 ); diff --git a/test/markup/javascript/arrow-function.txt b/test/markup/javascript/arrow-function.txt index 4e49e3405f..607c18eb51 100644 --- a/test/markup/javascript/arrow-function.txt +++ b/test/markup/javascript/arrow-function.txt @@ -2,3 +2,13 @@ var f = x => x; f(x => x + (y=2, z=undefined, ...rest) => y); () => null; const FC = props =>
functional component
; + +const good = () => 0; +const good = (x) => 0; +const bad = (a => [...a, b]); +const bad = (_ => doSomething()); +const bad = (() => 0); +const bad = ((a, b) => [...a, b]); +const array = [1, 2, 3].reduce((acc, next) => [...acc, next], []); +sides.every((length,width=(3+2+(4/5))) => length > 0 ); + diff --git a/test/markup/javascript/jsx.expect.txt b/test/markup/javascript/jsx.expect.txt index a000fe75a7..0f17aa4dd0 100644 --- a/test/markup/javascript/jsx.expect.txt +++ b/test/markup/javascript/jsx.expect.txt @@ -8,8 +8,8 @@ return (<node attr="value"></node>); -const n = () => <X /> -const m = () => <X x="" /> +const n = () => <X /> +const m = () => <X x="" /> class App extends Component { render() { diff --git a/test/markup/typescript/functions.expect.txt b/test/markup/typescript/functions.expect.txt index 3addcb3036..d805a30c87 100644 --- a/test/markup/typescript/functions.expect.txt +++ b/test/markup/typescript/functions.expect.txt @@ -13,3 +13,13 @@ type Foo = { functionInFoo(): void; }; + +const good = () => 0; +const good = (x) => 0; +const bad = (a => [...a, b]); +const bad = (_ => doSomething()); +const bad = (() => 0); +const bad = ((a, b) => [...a, b]); +const array = [1, 2, 3].reduce<number[]>((acc, next) => [...acc, next], []); +const bad = ((a=2, b=5) => [...a, b]); +sides.every((length,width=(3+2+(4/5))) => length > 0 ); diff --git a/test/markup/typescript/functions.txt b/test/markup/typescript/functions.txt index 4985bdbfb9..22d72f54ab 100644 --- a/test/markup/typescript/functions.txt +++ b/test/markup/typescript/functions.txt @@ -13,3 +13,14 @@ function getArray(): number[] { type Foo = { functionInFoo(): void; }; + +const good = () => 0; +const good = (x) => 0; +const bad = (a => [...a, b]); +const bad = (_ => doSomething()); +const bad = (() => 0); +const bad = ((a, b) => [...a, b]); +const array = [1, 2, 3].reduce