Skip to content

Commit 91c2844

Browse files
committed
Fill up the visitor switch
Handling a bunch of other easy cases (part 2)
1 parent a63050c commit 91c2844

File tree

1 file changed

+191
-7
lines changed

1 file changed

+191
-7
lines changed

src/services/inlayHints.ts

Lines changed: 191 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import {
22
__String,
3+
ArrayTypeNode,
34
ArrowFunction,
45
CallExpression,
6+
ConditionalTypeNode,
57
createPrinterWithRemoveComments,
68
createTextSpanFromNode,
79
Debug,
@@ -23,10 +25,14 @@ import {
2325
getLeadingCommentRanges,
2426
hasContextSensitiveParameters,
2527
Identifier,
28+
idText,
29+
ImportTypeNode,
30+
IndexedAccessTypeNode,
2631
InlayHint,
2732
InlayHintDisplayPart,
2833
InlayHintKind,
2934
InlayHintsContext,
35+
IntersectionTypeNode,
3036
isArrowFunction,
3137
isAssertionExpression,
3238
isBindingPattern,
@@ -53,13 +59,18 @@ import {
5359
isVarConst,
5460
isVariableDeclaration,
5561
MethodDeclaration,
62+
NamedTupleMember,
5663
NewExpression,
5764
Node,
65+
NodeArray,
5866
NodeBuilderFlags,
67+
OptionalTypeNode,
5968
ParameterDeclaration,
6069
ParenthesizedTypeNode,
6170
PrefixUnaryExpression,
6271
PropertyDeclaration,
72+
QualifiedName,
73+
RestTypeNode,
6374
Signature,
6475
skipParentheses,
6576
some,
@@ -69,11 +80,17 @@ import {
6980
SyntaxKind,
7081
textSpanIntersectsWith,
7182
tokenToString,
83+
TupleTypeNode,
7284
TupleTypeReference,
7385
Type,
7486
TypeFormatFlags,
7587
TypeNode,
88+
TypeOperatorNode,
89+
TypePredicateNode,
90+
TypeQueryNode,
91+
TypeReferenceNode,
7692
unescapeLeadingUnderscores,
93+
UnionTypeNode,
7794
UserPreferences,
7895
usingSingleLineStringWriter,
7996
VariableDeclaration,
@@ -162,7 +179,7 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
162179
function addParameterHints(text: string, parameter: Identifier, position: number, isFirstVariadicArgument: boolean, sourceFile: SourceFile | undefined) {
163180
let hintText: string | InlayHintDisplayPart[] = `${isFirstVariadicArgument ? "..." : ""}${text}`;
164181
if (shouldUseInteractiveInlayHints(preferences)) {
165-
hintText = [getNodeDisplayPart(hintText, parameter, sourceFile!), { text: ":" }];
182+
hintText = [getNodeDisplayPart(hintText, parameter, sourceFile), { text: ":" }];
166183
}
167184
else {
168185
hintText += ":";
@@ -446,13 +463,12 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
446463

447464
const parts: InlayHintDisplayPart[] = [];
448465
visitor(typeNode);
449-
function visitor(node: TypeNode): true | undefined {
466+
function visitor(node: Node) {
450467
if (!node) {
451468
return;
452469
}
453470

454471
switch (node.kind) {
455-
// Keyword types:
456472
case SyntaxKind.AnyKeyword:
457473
case SyntaxKind.BigIntKeyword:
458474
case SyntaxKind.BooleanKeyword:
@@ -465,18 +481,186 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
465481
case SyntaxKind.UndefinedKeyword:
466482
case SyntaxKind.UnknownKeyword:
467483
case SyntaxKind.VoidKeyword:
468-
parts.push({ text: tokenToString(node.kind) });
484+
case SyntaxKind.ThisType:
485+
parts.push({ text: tokenToString(node.kind)! });
486+
break;
487+
case SyntaxKind.Identifier:
488+
const identifier = node as Identifier;
489+
parts.push(getNodeDisplayPart(idText(identifier), identifier));
490+
break;
491+
case SyntaxKind.QualifiedName:
492+
const qualifiedName = node as QualifiedName;
493+
visitor(qualifiedName.left);
494+
parts.push({ text: "." });
495+
visitor(qualifiedName.right);
496+
break;
497+
case SyntaxKind.TypePredicate:
498+
const predicate = node as TypePredicateNode;
499+
if (predicate.assertsModifier) {
500+
parts.push({ text: "asserts " });
501+
}
502+
visitor(predicate.parameterName);
503+
if (predicate.type) {
504+
parts.push({ text: " is " });
505+
visitor(predicate.type);
506+
}
507+
break;
508+
case SyntaxKind.TypeReference:
509+
const typeReference = node as TypeReferenceNode;
510+
visitor(typeReference.typeName);
511+
if (typeReference.typeArguments) {
512+
parts.push({ text: "<" });
513+
visitList(typeReference.typeArguments, ",");
514+
parts.push({ text: ">" });
515+
}
516+
break;
517+
case SyntaxKind.FunctionType:
518+
// TODO: Handle this case.
519+
break;
520+
case SyntaxKind.ConstructorType:
521+
// TODO: Handle this case.
522+
break;
523+
case SyntaxKind.TypeQuery:
524+
const typeQuery = node as TypeQueryNode;
525+
parts.push({ text: "typeof " });
526+
visitor(typeQuery.exprName);
527+
if (typeQuery.typeArguments) {
528+
parts.push({ text: "<" });
529+
visitList(typeQuery.typeArguments, ",");
530+
parts.push({ text: ">" });
531+
}
532+
break;
533+
case SyntaxKind.TypeLiteral:
534+
// TODO: Handle this case.
535+
break;
536+
case SyntaxKind.ArrayType:
537+
visitor((node as ArrayTypeNode).elementType);
538+
parts.push({ text: "[]" });
539+
break;
540+
case SyntaxKind.TupleType:
541+
parts.push({ text: "[" });
542+
visitList((node as TupleTypeNode).elements, ",");
543+
parts.push({ text: "]" });
544+
break;
545+
case SyntaxKind.NamedTupleMember:
546+
const member = node as NamedTupleMember;
547+
if (member.dotDotDotToken) {
548+
parts.push({ text: "..." });
549+
}
550+
visitor(member.name);
551+
if (member.questionToken) {
552+
parts.push({ text: "?" });
553+
}
554+
parts.push({ text: ": " });
555+
visitor(member.type);
556+
break;
557+
case SyntaxKind.OptionalType:
558+
visitor((node as OptionalTypeNode).type);
559+
parts.push({ text: "?" });
560+
break;
561+
case SyntaxKind.RestType:
562+
parts.push({ text: "..." });
563+
visitor((node as RestTypeNode).type);
564+
break;
565+
case SyntaxKind.UnionType:
566+
visitList((node as UnionTypeNode).types, "|");
567+
break;
568+
case SyntaxKind.IntersectionType:
569+
visitList((node as IntersectionTypeNode).types, "&");
570+
break;
571+
case SyntaxKind.ConditionalType:
572+
const conditionalType = node as ConditionalTypeNode;
573+
visitor(conditionalType.checkType);
574+
parts.push({ text: " extends " });
575+
visitor(conditionalType.extendsType);
576+
parts.push({ text: " ? " });
577+
visitor(conditionalType.trueType);
578+
parts.push({ text: " : " });
579+
visitor(conditionalType.falseType);
580+
break;
581+
case SyntaxKind.InferType:
582+
// TODO: Handle this case.
469583
break;
470584
case SyntaxKind.ParenthesizedType:
471585
parts.push({ text: "(" });
472586
visitor((node as ParenthesizedTypeNode).type);
473587
parts.push({ text: ")" });
474588
break;
589+
case SyntaxKind.TypeOperator:
590+
const typeOperator = node as TypeOperatorNode;
591+
parts.push({ text: `${tokenToString(typeOperator.operator)} ` });
592+
visitor(typeOperator.type);
593+
break;
594+
case SyntaxKind.IndexedAccessType:
595+
const indexedAccess = node as IndexedAccessTypeNode;
596+
visitor(indexedAccess.objectType);
597+
parts.push({ text: "[" });
598+
visitor(indexedAccess.indexType);
599+
parts.push({ text: "]" });
600+
break;
601+
case SyntaxKind.MappedType:
602+
// TODO: Handle this case.
603+
break;
604+
case SyntaxKind.LiteralType:
605+
// TODO: Handle this case.
606+
break;
607+
case SyntaxKind.TemplateLiteralType:
608+
// TODO: Handle this case.
609+
break;
610+
case SyntaxKind.TemplateLiteralTypeSpan:
611+
// TODO: Handle this case.
612+
break;
613+
case SyntaxKind.ImportType:
614+
const importType = node as ImportTypeNode;
615+
if (importType.isTypeOf) {
616+
parts.push({ text: "typeof " });
617+
}
618+
parts.push({ text: "import(" });
619+
visitor(importType.argument);
620+
if (importType.assertions) {
621+
parts.push({ text: ", { assert: " });
622+
// TODO: Visit assert clause entries.
623+
parts.push({ text: " }" });
624+
}
625+
parts.push({ text: ")" });
626+
if (importType.qualifier) {
627+
parts.push({ text: "." });
628+
visitor(importType.qualifier);
629+
}
630+
if (importType.typeArguments) {
631+
parts.push({ text: "<" });
632+
visitList(importType.typeArguments, ",");
633+
parts.push({ text: ">" });
634+
}
635+
break;
636+
case SyntaxKind.ExpressionWithTypeArguments:
637+
// TODO: Handle this case.
638+
break;
639+
// TODO: I _think_ that we don't display inlay hints in JSDocs,
640+
// so I shouldn't worry about these cases (?).
641+
// case SyntaxKind.JSDocTypeExpression:
642+
// case SyntaxKind.JSDocAllType:
643+
// case SyntaxKind.JSDocUnknownType:
644+
// case SyntaxKind.JSDocNonNullableType:
645+
// case SyntaxKind.JSDocNullableType:
646+
// case SyntaxKind.JSDocOptionalType:
647+
// case SyntaxKind.JSDocFunctionType:
648+
// case SyntaxKind.JSDocVariadicType:
649+
// case SyntaxKind.JSDocNamepathType:
650+
// case SyntaxKind.JSDocSignature:
651+
// case SyntaxKind.JSDocTypeLiteral:
475652
default:
476-
// TODO: Make this unreachable when I consider all cases.
477-
return undefined;
653+
Debug.fail("Type node does not support inlay hints.");
478654
}
479655
}
656+
function visitList(nodes: NodeArray<TypeNode>, separator: string) {
657+
nodes.forEach((node, index) => {
658+
if (index > 0) {
659+
parts.push({ text: `${separator} ` });
660+
}
661+
visitor(node);
662+
});
663+
}
480664

481665
return parts;
482666
}
@@ -493,7 +677,7 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
493677
return true;
494678
}
495679

496-
function getNodeDisplayPart(text: string, node: Node, sourceFile: SourceFile): InlayHintDisplayPart {
680+
function getNodeDisplayPart(text: string, node: Node, sourceFile: SourceFile = node.getSourceFile()): InlayHintDisplayPart {
497681
return {
498682
text,
499683
span: createTextSpanFromNode(node, sourceFile),

0 commit comments

Comments
 (0)