Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(typescript) better highlighting of optionals #4114

Merged
merged 5 commits into from
Sep 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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}`);
};