Skip to content

Commit

Permalink
Fill up the visitor switch
Browse files Browse the repository at this point in the history
Handling a bunch of other easy cases (part 2)
  • Loading branch information
MariaSolOs committed Aug 3, 2023
1 parent a63050c commit 91c2844
Showing 1 changed file with 191 additions and 7 deletions.
198 changes: 191 additions & 7 deletions src/services/inlayHints.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {
__String,
ArrayTypeNode,
ArrowFunction,
CallExpression,
ConditionalTypeNode,
createPrinterWithRemoveComments,
createTextSpanFromNode,
Debug,
Expand All @@ -23,10 +25,14 @@ import {
getLeadingCommentRanges,
hasContextSensitiveParameters,
Identifier,
idText,
ImportTypeNode,
IndexedAccessTypeNode,
InlayHint,
InlayHintDisplayPart,
InlayHintKind,
InlayHintsContext,
IntersectionTypeNode,
isArrowFunction,
isAssertionExpression,
isBindingPattern,
Expand All @@ -53,13 +59,18 @@ import {
isVarConst,
isVariableDeclaration,
MethodDeclaration,
NamedTupleMember,
NewExpression,
Node,
NodeArray,
NodeBuilderFlags,
OptionalTypeNode,
ParameterDeclaration,
ParenthesizedTypeNode,
PrefixUnaryExpression,
PropertyDeclaration,
QualifiedName,
RestTypeNode,
Signature,
skipParentheses,
some,
Expand All @@ -69,11 +80,17 @@ import {
SyntaxKind,
textSpanIntersectsWith,
tokenToString,
TupleTypeNode,
TupleTypeReference,
Type,
TypeFormatFlags,
TypeNode,
TypeOperatorNode,
TypePredicateNode,
TypeQueryNode,
TypeReferenceNode,
unescapeLeadingUnderscores,
UnionTypeNode,
UserPreferences,
usingSingleLineStringWriter,
VariableDeclaration,
Expand Down Expand Up @@ -162,7 +179,7 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
function addParameterHints(text: string, parameter: Identifier, position: number, isFirstVariadicArgument: boolean, sourceFile: SourceFile | undefined) {
let hintText: string | InlayHintDisplayPart[] = `${isFirstVariadicArgument ? "..." : ""}${text}`;
if (shouldUseInteractiveInlayHints(preferences)) {
hintText = [getNodeDisplayPart(hintText, parameter, sourceFile!), { text: ":" }];
hintText = [getNodeDisplayPart(hintText, parameter, sourceFile), { text: ":" }];
}
else {
hintText += ":";
Expand Down Expand Up @@ -446,13 +463,12 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {

const parts: InlayHintDisplayPart[] = [];
visitor(typeNode);
function visitor(node: TypeNode): true | undefined {
function visitor(node: Node) {
if (!node) {
return;
}

switch (node.kind) {
// Keyword types:
case SyntaxKind.AnyKeyword:
case SyntaxKind.BigIntKeyword:
case SyntaxKind.BooleanKeyword:
Expand All @@ -465,18 +481,186 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
case SyntaxKind.UndefinedKeyword:
case SyntaxKind.UnknownKeyword:
case SyntaxKind.VoidKeyword:
parts.push({ text: tokenToString(node.kind) });
case SyntaxKind.ThisType:
parts.push({ text: tokenToString(node.kind)! });
break;
case SyntaxKind.Identifier:
const identifier = node as Identifier;
parts.push(getNodeDisplayPart(idText(identifier), identifier));
break;
case SyntaxKind.QualifiedName:
const qualifiedName = node as QualifiedName;
visitor(qualifiedName.left);
parts.push({ text: "." });
visitor(qualifiedName.right);
break;
case SyntaxKind.TypePredicate:
const predicate = node as TypePredicateNode;
if (predicate.assertsModifier) {
parts.push({ text: "asserts " });
}
visitor(predicate.parameterName);
if (predicate.type) {
parts.push({ text: " is " });
visitor(predicate.type);
}
break;
case SyntaxKind.TypeReference:
const typeReference = node as TypeReferenceNode;
visitor(typeReference.typeName);
if (typeReference.typeArguments) {
parts.push({ text: "<" });
visitList(typeReference.typeArguments, ",");
parts.push({ text: ">" });
}
break;
case SyntaxKind.FunctionType:
// TODO: Handle this case.
break;
case SyntaxKind.ConstructorType:
// TODO: Handle this case.
break;
case SyntaxKind.TypeQuery:
const typeQuery = node as TypeQueryNode;
parts.push({ text: "typeof " });
visitor(typeQuery.exprName);
if (typeQuery.typeArguments) {
parts.push({ text: "<" });
visitList(typeQuery.typeArguments, ",");
parts.push({ text: ">" });
}
break;
case SyntaxKind.TypeLiteral:
// TODO: Handle this case.
break;
case SyntaxKind.ArrayType:
visitor((node as ArrayTypeNode).elementType);
parts.push({ text: "[]" });
break;
case SyntaxKind.TupleType:
parts.push({ text: "[" });
visitList((node as TupleTypeNode).elements, ",");
parts.push({ text: "]" });
break;
case SyntaxKind.NamedTupleMember:
const member = node as NamedTupleMember;
if (member.dotDotDotToken) {
parts.push({ text: "..." });
}
visitor(member.name);
if (member.questionToken) {
parts.push({ text: "?" });
}
parts.push({ text: ": " });
visitor(member.type);
break;
case SyntaxKind.OptionalType:
visitor((node as OptionalTypeNode).type);
parts.push({ text: "?" });
break;
case SyntaxKind.RestType:
parts.push({ text: "..." });
visitor((node as RestTypeNode).type);
break;
case SyntaxKind.UnionType:
visitList((node as UnionTypeNode).types, "|");
break;
case SyntaxKind.IntersectionType:
visitList((node as IntersectionTypeNode).types, "&");
break;
case SyntaxKind.ConditionalType:
const conditionalType = node as ConditionalTypeNode;
visitor(conditionalType.checkType);
parts.push({ text: " extends " });
visitor(conditionalType.extendsType);
parts.push({ text: " ? " });
visitor(conditionalType.trueType);
parts.push({ text: " : " });
visitor(conditionalType.falseType);
break;
case SyntaxKind.InferType:
// TODO: Handle this case.
break;
case SyntaxKind.ParenthesizedType:
parts.push({ text: "(" });
visitor((node as ParenthesizedTypeNode).type);
parts.push({ text: ")" });
break;
case SyntaxKind.TypeOperator:
const typeOperator = node as TypeOperatorNode;
parts.push({ text: `${tokenToString(typeOperator.operator)} ` });
visitor(typeOperator.type);
break;
case SyntaxKind.IndexedAccessType:
const indexedAccess = node as IndexedAccessTypeNode;
visitor(indexedAccess.objectType);
parts.push({ text: "[" });
visitor(indexedAccess.indexType);
parts.push({ text: "]" });
break;
case SyntaxKind.MappedType:
// TODO: Handle this case.
break;
case SyntaxKind.LiteralType:
// TODO: Handle this case.
break;
case SyntaxKind.TemplateLiteralType:
// TODO: Handle this case.
break;
case SyntaxKind.TemplateLiteralTypeSpan:
// TODO: Handle this case.
break;
case SyntaxKind.ImportType:
const importType = node as ImportTypeNode;
if (importType.isTypeOf) {
parts.push({ text: "typeof " });
}
parts.push({ text: "import(" });
visitor(importType.argument);
if (importType.assertions) {
parts.push({ text: ", { assert: " });
// TODO: Visit assert clause entries.
parts.push({ text: " }" });
}
parts.push({ text: ")" });
if (importType.qualifier) {
parts.push({ text: "." });
visitor(importType.qualifier);
}
if (importType.typeArguments) {
parts.push({ text: "<" });
visitList(importType.typeArguments, ",");
parts.push({ text: ">" });
}
break;
case SyntaxKind.ExpressionWithTypeArguments:
// TODO: Handle this case.
break;
// TODO: I _think_ that we don't display inlay hints in JSDocs,
// so I shouldn't worry about these cases (?).
// case SyntaxKind.JSDocTypeExpression:
// case SyntaxKind.JSDocAllType:
// case SyntaxKind.JSDocUnknownType:
// case SyntaxKind.JSDocNonNullableType:
// case SyntaxKind.JSDocNullableType:
// case SyntaxKind.JSDocOptionalType:
// case SyntaxKind.JSDocFunctionType:
// case SyntaxKind.JSDocVariadicType:
// case SyntaxKind.JSDocNamepathType:
// case SyntaxKind.JSDocSignature:
// case SyntaxKind.JSDocTypeLiteral:
default:
// TODO: Make this unreachable when I consider all cases.
return undefined;
Debug.fail("Type node does not support inlay hints.");
}
}
function visitList(nodes: NodeArray<TypeNode>, separator: string) {
nodes.forEach((node, index) => {
if (index > 0) {
parts.push({ text: `${separator} ` });
}
visitor(node);
});
}

return parts;
}
Expand All @@ -493,7 +677,7 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
return true;
}

function getNodeDisplayPart(text: string, node: Node, sourceFile: SourceFile): InlayHintDisplayPart {
function getNodeDisplayPart(text: string, node: Node, sourceFile: SourceFile = node.getSourceFile()): InlayHintDisplayPart {
return {
text,
span: createTextSpanFromNode(node, sourceFile),
Expand Down

0 comments on commit 91c2844

Please sign in to comment.