fix inlay hints for template literal type (#56309)

This commit is contained in:
Zzzen 2023-11-29 05:34:33 +08:00 committed by GitHub
parent e98ae5a5c4
commit c250aed310
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 181 additions and 6 deletions

View File

@ -5823,7 +5823,8 @@ export function createDiagnosticCollection(): DiagnosticCollection {
}
const templateSubstitutionRegExp = /\$\{/g;
function escapeTemplateSubstitution(str: string): string {
/** @internal */
export function escapeTemplateSubstitution(str: string): string {
return str.replace(templateSubstitutionRegExp, "\\${");
}

View File

@ -11,6 +11,7 @@ import {
EnumMember,
equateStringsCaseInsensitive,
escapeString,
escapeTemplateSubstitution,
Expression,
findChildOfKind,
findIndex,
@ -76,7 +77,11 @@ import {
isQualifiedName,
isRestTypeNode,
isSpreadElement,
isStringLiteral,
isTemplateHead,
isTemplateLiteralTypeNode,
isTemplateLiteralTypeSpan,
isTemplateMiddle,
isTemplateTail,
isThisTypeNode,
isTupleTypeNode,
isTypeLiteralNode,
@ -89,7 +94,7 @@ import {
isUnionTypeNode,
isVarConst,
isVariableDeclaration,
LiteralExpression,
LiteralLikeNode,
MethodDeclaration,
NewExpression,
Node,
@ -106,6 +111,7 @@ import {
Symbol,
SymbolFlags,
SyntaxKind,
TemplateLiteralLikeNode,
textSpanIntersectsWith,
tokenToString,
TupleTypeReference,
@ -793,6 +799,28 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
parts.push({ text: tokenToString(node.operator) });
visitForDisplayParts(node.operand);
break;
case SyntaxKind.TemplateLiteralType:
Debug.assertNode(node, isTemplateLiteralTypeNode);
visitForDisplayParts(node.head);
node.templateSpans.forEach(visitForDisplayParts);
break;
case SyntaxKind.TemplateHead:
Debug.assertNode(node, isTemplateHead);
parts.push({ text: getLiteralText(node) });
break;
case SyntaxKind.TemplateLiteralTypeSpan:
Debug.assertNode(node, isTemplateLiteralTypeSpan);
visitForDisplayParts(node.type);
visitForDisplayParts(node.literal);
break;
case SyntaxKind.TemplateMiddle:
Debug.assertNode(node, isTemplateMiddle);
parts.push({ text: getLiteralText(node) });
break;
case SyntaxKind.TemplateTail:
Debug.assertNode(node, isTemplateTail);
parts.push({ text: getLiteralText(node) });
break;
case SyntaxKind.ThisType:
Debug.assertNode(node, isThisTypeNode);
parts.push({ text: "this" });
@ -828,9 +856,23 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
});
}
function getLiteralText(node: LiteralExpression) {
if (isStringLiteral(node)) {
return quotePreference === QuotePreference.Single ? `'${escapeString(node.text, CharacterCodes.singleQuote)}'` : `"${escapeString(node.text, CharacterCodes.doubleQuote)}"`;
function getLiteralText(node: LiteralLikeNode) {
switch (node.kind) {
case SyntaxKind.StringLiteral:
return quotePreference === QuotePreference.Single ? `'${escapeString(node.text, CharacterCodes.singleQuote)}'` : `"${escapeString(node.text, CharacterCodes.doubleQuote)}"`;
case SyntaxKind.TemplateHead:
case SyntaxKind.TemplateMiddle:
case SyntaxKind.TemplateTail: {
const rawText = (node as TemplateLiteralLikeNode).rawText ?? escapeTemplateSubstitution(escapeString(node.text, CharacterCodes.backtick));
switch (node.kind) {
case SyntaxKind.TemplateHead:
return "`" + rawText + "${";
case SyntaxKind.TemplateMiddle:
return "}" + rawText + "${";
case SyntaxKind.TemplateTail:
return "}" + rawText + "`";
}
}
}
return node.text;
}

View File

@ -0,0 +1,116 @@
// === Inlay Hints ===
const lit1 = getTemplateLiteral1();
^
{
"text": "",
"displayParts": [
{
"text": ": "
},
{
"text": "`${"
},
{
"text": "string"
},
{
"text": "},${"
},
{
"text": "string"
},
{
"text": "}`"
}
],
"position": 73,
"kind": "Type",
"whitespaceBefore": true
}
const lit2 = getTemplateLiteral2();
^
{
"text": "",
"displayParts": [
{
"text": ": "
},
{
"text": "`\\${${"
},
{
"text": "string"
},
{
"text": "},${"
},
{
"text": "string"
},
{
"text": "}`"
}
],
"position": 175,
"kind": "Type",
"whitespaceBefore": true
}
const lit3 = getTemplateLiteral3();
^
{
"text": "",
"displayParts": [
{
"text": ": "
},
{
"text": "`start${"
},
{
"text": "string"
},
{
"text": "}\\${,$${"
},
{
"text": "string"
},
{
"text": "}end`"
}
],
"position": 286,
"kind": "Type",
"whitespaceBefore": true
}
const lit4 = getTemplateLiteral4();
^
{
"text": "",
"displayParts": [
{
"text": ": "
},
{
"text": "`${"
},
{
"text": "string"
},
{
"text": "}\\`,${"
},
{
"text": "string"
},
{
"text": "}`"
}
],
"position": 387,
"kind": "Type",
"whitespaceBefore": true
}

View File

@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
//// declare function getTemplateLiteral1(): `${string},${string}`;
//// const lit1 = getTemplateLiteral1();
//// declare function getTemplateLiteral2(): `\${${string},${string}`;
//// const lit2 = getTemplateLiteral2();
//// declare function getTemplateLiteral3(): `start${string}\${,$${string}end`;
//// const lit3 = getTemplateLiteral3();
//// declare function getTemplateLiteral4(): `${string}\`,${string}`;
//// const lit4 = getTemplateLiteral4();
verify.baselineInlayHints(undefined, {
includeInlayVariableTypeHints: true,
interactiveInlayHints: true
});