Skip to content

Commit

Permalink
fix(typescript) improved highlighting of optionals (#4114)
Browse files Browse the repository at this point in the history
Co-authored-by: Josh Goebel <me@joshgoebel.com>
  • Loading branch information
Dxuian and joshgoebel authored Sep 29, 2024
1 parent a9756e3 commit 0af0968
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Core Grammars:
- enh(lua) add 'pluto' as an alias [Sainan]
- enh(bash) add reserved keywords `time` and `coproc` [Álvaro Mondéjar][]
- fix(c) - Fixed hex numbers with decimals [Dxuian]
- fix(typescript) - Fixedoptional property not highlighted correctly [Dxuian]
- fix(ruby) - fix `|=` operator false positives (as block arguments) [Aboobacker MK]

New Grammars:
Expand Down
6 changes: 3 additions & 3 deletions src/languages/javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ export default function(hljs) {
const PARAMS = {
className: 'params',
// convert this to negative lookbehind in v12
begin: /(\s*)\(/, // to match the parms with
begin: /(\s*)\(/, // to match the parms with
end: /\)/,
excludeBegin: true,
excludeEnd: true,
Expand Down Expand Up @@ -476,8 +476,8 @@ export default function(hljs) {
NUMBER,
CLASS_REFERENCE,
{
className: 'attr',
begin: IDENT_RE + regex.lookahead(':'),
scope: 'attr',
match: IDENT_RE + regex.lookahead(':'),
relevance: 0
},
FUNCTION_VARIABLE,
Expand Down
16 changes: 13 additions & 3 deletions src/languages/typescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import javascript from "./javascript.js";

/** @type LanguageFn */
export default function(hljs) {
const regex = hljs.regex;
const tsLanguage = javascript(hljs);

const IDENT_RE = ECMAScript.IDENT_RE;
Expand Down Expand Up @@ -68,20 +69,19 @@ export default function(hljs) {
"override",
"satisfies"
];

/*
namespace is a TS keyword but it's fine to use it as a variable name too.
const message = 'foo';
const namespace = 'bar';
*/

const KEYWORDS = {
$pattern: ECMAScript.IDENT_RE,
keyword: ECMAScript.KEYWORDS.concat(TS_SPECIFIC_KEYWORDS),
literal: ECMAScript.LITERALS,
built_in: ECMAScript.BUILT_INS.concat(TYPES),
"variable.language": ECMAScript.BUILT_IN_VARIABLES
};

const DECORATOR = {
className: 'meta',
begin: '@' + IDENT_RE,
Expand All @@ -102,15 +102,25 @@ export default function(hljs) {
tsLanguage.exports.PARAMS_CONTAINS.push(DECORATOR);

// highlight the function params
const ATTRIBUTE_HIGHLIGHT = tsLanguage.contains.find(c => c.className === "attr");
const ATTRIBUTE_HIGHLIGHT = tsLanguage.contains.find(c => c.scope === "attr");

// take default attr rule and extend it to support optionals
const OPTIONAL_KEY_OR_ARGUMENT = Object.assign({},
ATTRIBUTE_HIGHLIGHT,
{ match: regex.concat(IDENT_RE, regex.lookahead(/\s*\?:/)) }
);
tsLanguage.exports.PARAMS_CONTAINS.push([
tsLanguage.exports.CLASS_REFERENCE, // class reference for highlighting the params types
ATTRIBUTE_HIGHLIGHT, // highlight the params key
OPTIONAL_KEY_OR_ARGUMENT, // Added for optional property assignment highlighting
]);

// Add the optional property assignment highlighting for objects or classes
tsLanguage.contains = tsLanguage.contains.concat([
DECORATOR,
NAMESPACE,
INTERFACE,
OPTIONAL_KEY_OR_ARGUMENT, // Added for optional property assignment highlighting
]);

// TS gets a simpler shebang rule than JS
Expand Down
67 changes: 63 additions & 4 deletions test/markup/typescript/generics.expect.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,69 @@
<span class="hljs-keyword">import</span> { useWeb3React <span class="hljs-keyword">as</span> useWeb3React_ } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;@web3-react/core&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-attr">useWeb3React</span>: &lt;T = <span class="hljs-built_in">any</span>&gt;<span class="hljs-function">(<span class="hljs-params">key?: <span class="hljs-built_in">string</span></span>) =&gt;</span> <span class="hljs-title class_">Modify</span>&lt;
<span class="hljs-comment">// Updated function with both optional and non-optional parameters</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-attr">useWeb3React</span>: &lt;T = <span class="hljs-built_in">any</span>&gt;<span class="hljs-function">(<span class="hljs-params"><span class="hljs-attr">key</span>?: <span class="hljs-built_in">string</span>, <span class="hljs-attr">version</span>: <span class="hljs-built_in">number</span></span>) =&gt;</span> <span class="hljs-title class_">Modify</span>&lt;
<span class="hljs-title class_">ReturnType</span>&lt;<span class="hljs-keyword">typeof</span> useWeb3React_&lt;T&gt;&gt;,
{ <span class="hljs-attr">chainId</span>: <span class="hljs-title class_">SupportedChainIds</span> }
{ <span class="hljs-attr">chainId</span>: <span class="hljs-title class_">SupportedChainIds</span>, <span class="hljs-attr">network</span>: <span class="hljs-built_in">string</span> }
&gt; = useWeb3React_ <span class="hljs-keyword">as</span> <span class="hljs-built_in">any</span>

<span class="hljs-keyword">declare</span> <span class="hljs-variable language_">global</span> {
<span class="hljs-keyword">type</span> <span class="hljs-title class_">SupportedChainIds</span> = <span class="hljs-number">1</span> | <span class="hljs-number">4</span>
}

<span class="hljs-comment">// Optional and non-optional properties in an object type</span>
<span class="hljs-keyword">type</span> <span class="hljs-title class_">Options</span> = {
<span class="hljs-attr">srcdir</span>: <span class="hljs-built_in">string</span>,
<span class="hljs-attr">outdir</span>: <span class="hljs-built_in">string</span>,
<span class="hljs-attr">minify</span>?: <span class="hljs-built_in">boolean</span>,
<span class="hljs-attr">sourcemap</span>: <span class="hljs-built_in">boolean</span>
}

<span class="hljs-comment">// Function with both optional and non-optional parameters</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">multiply</span>(<span class="hljs-params"><span class="hljs-attr">a</span>: <span class="hljs-built_in">number</span>, <span class="hljs-attr">b</span>: <span class="hljs-built_in">number</span>, <span class="hljs-attr">c</span>?: <span class="hljs-built_in">number</span>, <span class="hljs-attr">d</span>: <span class="hljs-built_in">number</span></span>): <span class="hljs-built_in">number</span> {
<span class="hljs-keyword">return</span> a * b * d;
}

<span class="hljs-comment">// Optional Function Parameters</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">greet</span>(<span class="hljs-params"><span class="hljs-attr">name</span>?: <span class="hljs-built_in">string</span>, <span class="hljs-attr">age</span>?: <span class="hljs-built_in">number</span>, <span class="hljs-attr">city</span>: <span class="hljs-built_in">string</span></span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Hello, <span class="hljs-subst">${name ?? <span class="hljs-string">&quot;Guest&quot;</span>}</span> from <span class="hljs-subst">${city}</span>!`</span>);
}

<span class="hljs-comment">// Optional and non-optional properties in an interface</span>
<span class="hljs-keyword">interface</span> <span class="hljs-title class_">Person</span> {
<span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;
<span class="hljs-attr">age</span>?: <span class="hljs-built_in">number</span>;
<span class="hljs-attr">address</span>?: <span class="hljs-built_in">string</span>;
<span class="hljs-attr">occupation</span>: <span class="hljs-built_in">string</span>;
}

<span class="hljs-comment">// Optional Class Members and Constructor Parameters</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">Car</span> {
<span class="hljs-attr">make</span>: <span class="hljs-built_in">string</span>;
<span class="hljs-attr">model</span>?: <span class="hljs-built_in">string</span>;
<span class="hljs-attr">year</span>: <span class="hljs-built_in">number</span>;

<span class="hljs-title function_">constructor</span>(<span class="hljs-params"><span class="hljs-attr">make</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">year</span>: <span class="hljs-built_in">number</span>, <span class="hljs-attr">model</span>?: <span class="hljs-built_in">string</span></span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">make</span> = make;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">year</span> = year;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">model</span> = model;
}
}


<span class="hljs-comment">// Optional Constructor Parameters</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span> {
<span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;
<span class="hljs-attr">age</span>?: <span class="hljs-built_in">number</span>;
<span class="hljs-attr">city</span>: <span class="hljs-built_in">string</span>;

<span class="hljs-title function_">constructor</span>(<span class="hljs-params"><span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">city</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">age</span>?: <span class="hljs-built_in">number</span></span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">name</span> = name;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">city</span> = city;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">age</span> = age;
}
}

<span class="hljs-comment">// Optional in Function Type Signatures</span>
<span class="hljs-keyword">type</span> <span class="hljs-title class_">PrintMessage</span> = <span class="hljs-function">(<span class="hljs-params"><span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">sender</span>?: <span class="hljs-built_in">string</span>, <span class="hljs-attr">timestamp</span>: <span class="hljs-built_in">number</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;

<span class="hljs-keyword">const</span> <span class="hljs-attr">print</span>: <span class="hljs-title class_">PrintMessage</span> = <span class="hljs-function">(<span class="hljs-params">message, sender, timestamp</span>) =&gt;</span> {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Message: <span class="hljs-subst">${message}</span>, from: <span class="hljs-subst">${sender ?? <span class="hljs-string">&quot;Anonymous&quot;</span>}</span> at <span class="hljs-subst">${timestamp}</span>`</span>);
};
68 changes: 64 additions & 4 deletions test/markup/typescript/generics.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,70 @@
import { useWeb3React as useWeb3React_ } from '@web3-react/core'

export const useWeb3React: <T = any>(key?: string) => Modify<
// Updated function with both optional and non-optional parameters
export const useWeb3React: <T = any>(key?: string, version: number) => Modify<
ReturnType<typeof useWeb3React_<T>>,
{ chainId: SupportedChainIds }
{ chainId: SupportedChainIds, network: string }
> = useWeb3React_ as any

declare global {
type SupportedChainIds = 1 | 4
}

// Optional and non-optional properties in an object type
type Options = {
srcdir: string,
outdir: string,
minify?: boolean,
sourcemap: boolean
}

// Function with both optional and non-optional parameters
function multiply(a: number, b: number, c?: number, d: number): number {
return a * b * d;
}

// Optional Function Parameters
function greet(name?: string, age?: number, city: string) {
console.log(`Hello, ${name ?? "Guest"} from ${city}!`);
}

// Optional and non-optional properties in an interface
interface Person {
name: string;
age?: number;
address?: string;
occupation: string;
}

// Optional Class Members and Constructor Parameters
class Car {
make: string;
model?: string;
year: number;

constructor(make: string, year: number, model?: string) {
this.make = make;
this.year = year;
this.model = model;
}
}


// Optional Constructor Parameters
class User {
name: string;
age?: number;
city: string;

constructor(name: string, city: string, age?: number) {
this.name = name;
this.city = city;
this.age = age;
}
}

// Optional in Function Type Signatures
type PrintMessage = (message: string, sender?: string, timestamp: number) => void;

const print: PrintMessage = (message, sender, timestamp) => {
console.log(`Message: ${message}, from: ${sender ?? "Anonymous"} at ${timestamp}`);
};

0 comments on commit 0af0968

Please sign in to comment.