vendor tsgo
This commit is contained in:
927
tools/tsgo/internal/ls/inlay_hints.go
Normal file
927
tools/tsgo/internal/ls/inlay_hints.go
Normal file
@@ -0,0 +1,927 @@
|
||||
package ls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/astnav"
|
||||
"github.com/microsoft/typescript-go/internal/checker"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/debug"
|
||||
"github.com/microsoft/typescript-go/internal/evaluator"
|
||||
"github.com/microsoft/typescript-go/internal/ls/lsconv"
|
||||
"github.com/microsoft/typescript-go/internal/ls/lsutil"
|
||||
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
|
||||
"github.com/microsoft/typescript-go/internal/nodebuilder"
|
||||
"github.com/microsoft/typescript-go/internal/printer"
|
||||
"github.com/microsoft/typescript-go/internal/scanner"
|
||||
"github.com/microsoft/typescript-go/internal/stringutil"
|
||||
)
|
||||
|
||||
func (l *LanguageService) ProvideInlayHint(
|
||||
ctx context.Context,
|
||||
params *lsproto.InlayHintParams,
|
||||
) (lsproto.InlayHintResponse, error) {
|
||||
userPreferences := l.UserPreferences()
|
||||
inlayHintPreferences := userPreferences.InlayHints
|
||||
if !isAnyInlayHintEnabled(inlayHintPreferences) {
|
||||
return lsproto.InlayHintsOrNull{InlayHints: nil}, nil
|
||||
}
|
||||
|
||||
program, file := l.getProgramAndFile(params.TextDocument.Uri)
|
||||
quotePreference := lsutil.GetQuotePreference(file, userPreferences)
|
||||
|
||||
checker, done := program.GetTypeCheckerForFile(ctx, file)
|
||||
defer done()
|
||||
inlayHintState := &inlayHintState{
|
||||
ctx: ctx,
|
||||
span: l.converters.FromLSPRange(file, params.Range),
|
||||
preferences: inlayHintPreferences,
|
||||
quotePreference: quotePreference,
|
||||
file: file,
|
||||
checker: checker,
|
||||
converters: l.converters,
|
||||
}
|
||||
inlayHintState.visit(file.AsNode())
|
||||
return lsproto.InlayHintsOrNull{InlayHints: &inlayHintState.result}, nil
|
||||
}
|
||||
|
||||
type inlayHintState struct {
|
||||
ctx context.Context
|
||||
span core.TextRange
|
||||
preferences lsutil.InlayHintsPreferences
|
||||
quotePreference lsutil.QuotePreference
|
||||
file *ast.SourceFile
|
||||
checker *checker.Checker
|
||||
converters *lsconv.Converters
|
||||
result []*lsproto.InlayHint
|
||||
}
|
||||
|
||||
func (s *inlayHintState) visit(node *ast.Node) bool {
|
||||
if node == nil || node.End()-node.Pos() == 0 || node.Flags&ast.NodeFlagsReparsed != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch node.Kind {
|
||||
case ast.KindModuleDeclaration, ast.KindClassDeclaration, ast.KindInterfaceDeclaration,
|
||||
ast.KindFunctionDeclaration, ast.KindClassExpression, ast.KindFunctionExpression,
|
||||
ast.KindMethodDeclaration, ast.KindArrowFunction:
|
||||
if s.ctx.Err() != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if !s.span.Intersects(node.Loc) {
|
||||
return false
|
||||
}
|
||||
|
||||
if ast.IsTypeNode(node) && !ast.IsExpressionWithTypeArguments(node) {
|
||||
return false
|
||||
}
|
||||
|
||||
if s.preferences.IncludeInlayVariableTypeHints.IsTrue() && ast.IsVariableDeclaration(node) {
|
||||
s.visitVariableLikeDeclaration(node)
|
||||
} else if s.preferences.IncludeInlayPropertyDeclarationTypeHints.IsTrue() && ast.IsPropertyDeclaration(node) {
|
||||
s.visitVariableLikeDeclaration(node)
|
||||
} else if s.preferences.IncludeInlayEnumMemberValueHints.IsTrue() && ast.IsEnumMember(node) {
|
||||
s.visitEnumMember(node)
|
||||
} else if shouldShowParameterNameHints(s.preferences) && (ast.IsCallExpression(node) || ast.IsNewExpression(node)) {
|
||||
s.visitCallOrNewExpression(node)
|
||||
} else {
|
||||
if s.preferences.IncludeInlayFunctionParameterTypeHints.IsTrue() &&
|
||||
ast.IsFunctionLikeDeclaration(node) &&
|
||||
ast.HasContextSensitiveParameters(node) {
|
||||
s.visitFunctionLikeForParameterType(node)
|
||||
}
|
||||
if s.preferences.IncludeInlayFunctionLikeReturnTypeHints.IsTrue() &&
|
||||
isSignatureSupportingReturnAnnotation(node) {
|
||||
s.visitFunctionDeclarationLikeForReturnType(node)
|
||||
}
|
||||
}
|
||||
return node.ForEachChild(s.visit)
|
||||
}
|
||||
|
||||
// FunctionDeclaration | MethodDeclaration | GetAccessor | FunctionExpression | ArrowFunction
|
||||
func (s *inlayHintState) visitFunctionDeclarationLikeForReturnType(decl *ast.FunctionLikeDeclaration) {
|
||||
if ast.IsArrowFunction(decl) {
|
||||
if astnav.FindChildOfKind(decl, ast.KindOpenParenToken, s.file) == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
typeAnnotation := decl.Type()
|
||||
if typeAnnotation != nil || decl.Body() == nil {
|
||||
return
|
||||
}
|
||||
|
||||
signature := s.checker.GetSignatureFromDeclaration(decl)
|
||||
if signature == nil {
|
||||
return
|
||||
}
|
||||
|
||||
typePredicate := s.checker.GetTypePredicateOfSignature(signature)
|
||||
|
||||
if typePredicate != nil && typePredicate.Type() != nil {
|
||||
hintParts := s.typePredicateToInlayHintParts(typePredicate)
|
||||
s.addTypeHints(hintParts, s.getTypeAnnotationPosition(decl))
|
||||
return
|
||||
}
|
||||
|
||||
returnType := s.checker.GetReturnTypeOfSignature(signature)
|
||||
if isModuleReferenceType(returnType) {
|
||||
return
|
||||
}
|
||||
|
||||
hintParts := s.typeToInlayHintParts(returnType)
|
||||
s.addTypeHints(hintParts, s.getTypeAnnotationPosition(decl))
|
||||
}
|
||||
|
||||
func (s *inlayHintState) visitCallOrNewExpression(expr *ast.CallOrNewExpression) {
|
||||
args := expr.Arguments()
|
||||
if len(args) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
signature := s.checker.GetResolvedSignature(expr)
|
||||
if signature == nil {
|
||||
return
|
||||
}
|
||||
|
||||
signatureParamPos := 0
|
||||
for _, originalArg := range args {
|
||||
arg := ast.SkipParentheses(originalArg)
|
||||
if shouldShowLiteralParameterNameHintsOnly(s.preferences) && !isHintableLiteral(arg) {
|
||||
signatureParamPos++
|
||||
continue
|
||||
}
|
||||
|
||||
spreadArgs := 0
|
||||
if ast.IsSpreadElement(arg) {
|
||||
spreadType := s.checker.GetTypeAtLocation(arg.Expression())
|
||||
if spreadType.IsTupleType() {
|
||||
elementFlags := spreadType.Target().AsTupleType().ElementFlags()
|
||||
fixedLength := spreadType.Target().AsTupleType().FixedLength()
|
||||
if fixedLength == 0 {
|
||||
continue
|
||||
}
|
||||
firstOptionalIndex := slices.IndexFunc(elementFlags, func(f checker.ElementFlags) bool {
|
||||
return f&checker.ElementFlagsRequired == 0
|
||||
})
|
||||
requiredArgs := core.IfElse(firstOptionalIndex < 0, fixedLength, firstOptionalIndex)
|
||||
if requiredArgs > 0 {
|
||||
spreadArgs = requiredArgs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
identifierInfo := s.getParameterIdentifierInfoAtPosition(signature, signatureParamPos)
|
||||
signatureParamPos = signatureParamPos + core.IfElse(spreadArgs > 0, spreadArgs, 1)
|
||||
if identifierInfo == nil {
|
||||
return
|
||||
}
|
||||
|
||||
parameter := identifierInfo.parameter
|
||||
parameterName := identifierInfo.name
|
||||
isFirstVariadicArgument := identifierInfo.isRestParameter
|
||||
parameterNameNotSameAsArgument := s.preferences.IncludeInlayParameterNameHintsWhenArgumentMatchesName.IsTrue() ||
|
||||
!identifierOrAccessExpressionPostfixMatchesParameterName(arg, parameterName)
|
||||
if !parameterNameNotSameAsArgument && !isFirstVariadicArgument {
|
||||
continue
|
||||
}
|
||||
|
||||
if s.leadingCommentsContainsParameterName(arg, parameterName) {
|
||||
continue
|
||||
}
|
||||
|
||||
s.addParameterHints(
|
||||
parameterName,
|
||||
parameter,
|
||||
astnav.GetStartOfNode(originalArg, s.file, false /*includeJSDoc*/),
|
||||
isFirstVariadicArgument,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *inlayHintState) visitEnumMember(member *ast.EnumMemberNode) {
|
||||
if member.Initializer() != nil {
|
||||
return
|
||||
}
|
||||
|
||||
enumValue := s.checker.GetConstantValue(member)
|
||||
if enumValue != nil {
|
||||
s.addEnumMemberValueHints(evaluator.AnyToString(enumValue), member.End())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *inlayHintState) visitVariableLikeDeclaration(decl *ast.VariableOrPropertyDeclaration) {
|
||||
if decl.Initializer() == nil &&
|
||||
!(ast.IsPropertyDeclaration(decl) && s.checker.GetTypeAtLocation(decl).Flags()&checker.TypeFlagsAny == 0) ||
|
||||
ast.IsBindingPattern(decl.Name()) || (ast.IsVariableDeclaration(decl) && !isHintableDeclaration(decl)) {
|
||||
return
|
||||
}
|
||||
|
||||
typeAnnotation := decl.Type()
|
||||
if typeAnnotation != nil {
|
||||
return
|
||||
}
|
||||
|
||||
declarationType := s.checker.GetTypeAtLocation(decl)
|
||||
if isModuleReferenceType(declarationType) {
|
||||
return
|
||||
}
|
||||
|
||||
hintParts := s.typeToInlayHintParts(declarationType)
|
||||
var hintText string
|
||||
if hintParts.String != nil {
|
||||
hintText = *hintParts.String
|
||||
} else if hintParts.InlayHintLabelParts != nil {
|
||||
var b strings.Builder
|
||||
for _, part := range *hintParts.InlayHintLabelParts {
|
||||
b.WriteString(part.Value)
|
||||
}
|
||||
hintText = b.String()
|
||||
}
|
||||
if !s.preferences.IncludeInlayVariableTypeHintsWhenTypeMatchesName.IsTrue() &&
|
||||
!ast.IsComputedPropertyName(decl.Name()) &&
|
||||
stringutil.EquateStringCaseInsensitive(decl.Name().Text(), hintText) {
|
||||
return
|
||||
}
|
||||
s.addTypeHints(hintParts, decl.Name().End())
|
||||
}
|
||||
|
||||
func (s *inlayHintState) visitFunctionLikeForParameterType(node *ast.FunctionLikeDeclaration) {
|
||||
signature := s.checker.GetSignatureFromDeclaration(node)
|
||||
if signature == nil {
|
||||
return
|
||||
}
|
||||
|
||||
pos := 0
|
||||
for _, param := range node.Parameters() {
|
||||
if isHintableDeclaration(param) {
|
||||
var symbol *ast.Symbol
|
||||
if ast.IsThisParameter(param) {
|
||||
symbol = signature.ThisParameter()
|
||||
} else {
|
||||
symbol = signature.Parameters()[pos]
|
||||
}
|
||||
s.addParameterTypeHint(param, symbol)
|
||||
}
|
||||
if ast.IsThisParameter(param) {
|
||||
continue
|
||||
}
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
func (s *inlayHintState) addParameterTypeHint(node *ast.ParameterDeclarationNode, symbol *ast.Symbol) {
|
||||
typeAnnotation := node.Type()
|
||||
if typeAnnotation != nil || symbol == nil {
|
||||
return
|
||||
}
|
||||
typeHints := s.getParameterDeclarationTypeHints(symbol)
|
||||
if typeHints == nil {
|
||||
return
|
||||
}
|
||||
var pos int
|
||||
if node.QuestionToken() != nil {
|
||||
pos = node.QuestionToken().End()
|
||||
} else {
|
||||
pos = node.Name().End()
|
||||
}
|
||||
s.addTypeHints(*typeHints, pos)
|
||||
}
|
||||
|
||||
func (s *inlayHintState) getParameterDeclarationTypeHints(symbol *ast.Symbol) *lsproto.StringOrInlayHintLabelParts {
|
||||
valueDeclaration := symbol.ValueDeclaration
|
||||
if valueDeclaration == nil || !ast.IsParameterDeclaration(valueDeclaration) {
|
||||
return nil
|
||||
}
|
||||
|
||||
signatureParamType := s.checker.GetTypeOfSymbolAtLocation(symbol, valueDeclaration)
|
||||
if isModuleReferenceType(signatureParamType) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return new(s.typeToInlayHintParts(signatureParamType))
|
||||
}
|
||||
|
||||
func (s *inlayHintState) typeToInlayHintParts(t *checker.Type) lsproto.StringOrInlayHintLabelParts {
|
||||
flags := nodebuilder.FlagsIgnoreErrors | nodebuilder.FlagsAllowUniqueESSymbolType |
|
||||
nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope
|
||||
idToSymbol := make(map[*ast.IdentifierNode]*ast.Symbol)
|
||||
// !!! Avoid type node reuse so we collect identifier symbols.
|
||||
typeNode := s.checker.TypeToTypeNode(t, nil /*enclosingDeclaration*/, flags, idToSymbol)
|
||||
debug.Assert(typeNode != nil, "should always get typenode")
|
||||
return lsproto.StringOrInlayHintLabelParts{
|
||||
InlayHintLabelParts: new(s.getInlayHintLabelParts(typeNode, idToSymbol)),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *inlayHintState) typePredicateToInlayHintParts(typePredicate *checker.TypePredicate) lsproto.StringOrInlayHintLabelParts {
|
||||
flags := nodebuilder.FlagsIgnoreErrors | nodebuilder.FlagsAllowUniqueESSymbolType |
|
||||
nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope
|
||||
idToSymbol := make(map[*ast.IdentifierNode]*ast.Symbol)
|
||||
// !!! Avoid type node reuse so we collect identifier symbols.
|
||||
typeNode := s.checker.TypePredicateToTypePredicateNode(typePredicate, nil /*enclosingDeclaration*/, flags, idToSymbol)
|
||||
debug.Assert(typeNode != nil, "should always get typePredicateNode")
|
||||
return lsproto.StringOrInlayHintLabelParts{
|
||||
InlayHintLabelParts: new(s.getInlayHintLabelParts(typeNode, idToSymbol)),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *inlayHintState) addTypeHints(hint lsproto.StringOrInlayHintLabelParts, position int) {
|
||||
if hint.String != nil {
|
||||
hint.String = new(": " + *hint.String)
|
||||
} else {
|
||||
hint.InlayHintLabelParts = new(append([]*lsproto.InlayHintLabelPart{{Value: ": "}}, *hint.InlayHintLabelParts...))
|
||||
}
|
||||
s.result = append(s.result, &lsproto.InlayHint{
|
||||
Label: hint,
|
||||
Position: s.converters.PositionToLineAndCharacter(s.file, core.TextPos(position)),
|
||||
Kind: new(lsproto.InlayHintKindType),
|
||||
PaddingLeft: new(true),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *inlayHintState) addEnumMemberValueHints(text string, position int) {
|
||||
s.result = append(s.result, &lsproto.InlayHint{
|
||||
Label: lsproto.StringOrInlayHintLabelParts{
|
||||
String: new("= " + text),
|
||||
},
|
||||
Position: s.converters.PositionToLineAndCharacter(s.file, core.TextPos(position)),
|
||||
PaddingLeft: new(true),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *inlayHintState) addParameterHints(text string, parameter *ast.IdentifierNode, position int, isFirstVariadicArgument bool) {
|
||||
hintText := core.IfElse(isFirstVariadicArgument, "...", "") + text
|
||||
displayParts := []*lsproto.InlayHintLabelPart{
|
||||
s.getNodeDisplayPart(hintText, parameter),
|
||||
{
|
||||
Value: ":",
|
||||
},
|
||||
}
|
||||
labelParts := lsproto.StringOrInlayHintLabelParts{InlayHintLabelParts: &displayParts}
|
||||
|
||||
s.result = append(s.result, &lsproto.InlayHint{
|
||||
Label: labelParts,
|
||||
Position: s.converters.PositionToLineAndCharacter(s.file, core.TextPos(position)),
|
||||
Kind: new(lsproto.InlayHintKindParameter),
|
||||
PaddingRight: new(true),
|
||||
})
|
||||
}
|
||||
|
||||
func shouldShowParameterNameHints(preferences lsutil.InlayHintsPreferences) bool {
|
||||
return (preferences.IncludeInlayParameterNameHints == lsutil.IncludeInlayParameterNameHintsLiterals ||
|
||||
preferences.IncludeInlayParameterNameHints == lsutil.IncludeInlayParameterNameHintsAll)
|
||||
}
|
||||
|
||||
func shouldShowLiteralParameterNameHintsOnly(preferences lsutil.InlayHintsPreferences) bool {
|
||||
return preferences.IncludeInlayParameterNameHints == lsutil.IncludeInlayParameterNameHintsLiterals
|
||||
}
|
||||
|
||||
// node is FunctionDeclaration | ArrowFunction | FunctionExpression | MethodDeclaration | GetAccessor
|
||||
func isSignatureSupportingReturnAnnotation(node *ast.Node) bool {
|
||||
return ast.IsArrowFunction(node) || ast.IsFunctionExpression(node) || ast.IsFunctionDeclaration(node) ||
|
||||
ast.IsMethodDeclaration(node) || ast.IsGetAccessorDeclaration(node)
|
||||
}
|
||||
|
||||
func isHintableDeclaration(node *ast.VariableOrParameterDeclaration) bool {
|
||||
if (ast.IsPartOfParameterDeclaration(node) || ast.IsVariableDeclaration(node) && ast.IsVarConst(node)) &&
|
||||
node.Initializer() != nil {
|
||||
initializer := ast.SkipParentheses(node.Initializer())
|
||||
return !(isHintableLiteral(initializer) || ast.IsNewExpression(initializer) ||
|
||||
ast.IsObjectLiteralExpression(initializer) || ast.IsAssertionExpression(initializer))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isHintableLiteral(node *ast.Node) bool {
|
||||
switch node.Kind {
|
||||
case ast.KindPrefixUnaryExpression:
|
||||
operand := node.AsPrefixUnaryExpression().Operand
|
||||
return ast.IsLiteralExpression(operand) || ast.IsIdentifier(operand) && ast.IsInfinityOrNaNString(operand.Text())
|
||||
case ast.KindTrueKeyword, ast.KindFalseKeyword, ast.KindNullKeyword,
|
||||
ast.KindNoSubstitutionTemplateLiteral, ast.KindTemplateExpression:
|
||||
return true
|
||||
case ast.KindIdentifier:
|
||||
name := node.Text()
|
||||
return name == "undefined" || ast.IsInfinityOrNaNString(name)
|
||||
}
|
||||
return ast.IsLiteralExpression(node)
|
||||
}
|
||||
|
||||
func isModuleReferenceType(t *checker.Type) bool {
|
||||
symbol := t.Symbol()
|
||||
return symbol != nil && symbol.Flags&ast.SymbolFlagsModule != 0
|
||||
}
|
||||
|
||||
func (s *inlayHintState) getInlayHintLabelParts(node *ast.Node, idToSymbol map[*ast.IdentifierNode]*ast.Symbol) []*lsproto.InlayHintLabelPart {
|
||||
var parts []*lsproto.InlayHintLabelPart
|
||||
|
||||
var visitForDisplayParts func(node *ast.Node)
|
||||
var visitDisplayPartList func(nodes []*ast.Node, separator string)
|
||||
var visitParametersAndTypeParameters func(node *ast.SignatureDeclaration)
|
||||
|
||||
visitForDisplayParts = func(node *ast.Node) {
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
|
||||
tokenString := scanner.TokenToString(node.Kind)
|
||||
if tokenString != "" {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: tokenString})
|
||||
return
|
||||
}
|
||||
|
||||
if ast.IsLiteralExpression(node) {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: s.getLiteralText(node)})
|
||||
return
|
||||
}
|
||||
|
||||
switch node.Kind {
|
||||
case ast.KindIdentifier:
|
||||
identifierText := node.Text()
|
||||
var name *ast.Node
|
||||
if symbol := idToSymbol[node]; symbol != nil && len(symbol.Declarations) != 0 {
|
||||
name = ast.GetNameOfDeclaration(symbol.Declarations[0])
|
||||
}
|
||||
if name != nil {
|
||||
parts = append(parts, s.getNodeDisplayPart(identifierText, name))
|
||||
} else {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: identifierText})
|
||||
}
|
||||
case ast.KindQualifiedName:
|
||||
visitForDisplayParts(node.AsQualifiedName().Left)
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "."})
|
||||
visitForDisplayParts(node.AsQualifiedName().Right)
|
||||
case ast.KindTypePredicate:
|
||||
if node.AsTypePredicateNode().AssertsModifier != nil {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "asserts "})
|
||||
}
|
||||
visitForDisplayParts(node.AsTypePredicateNode().ParameterName)
|
||||
if node.Type() != nil {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: " is "})
|
||||
visitForDisplayParts(node.Type())
|
||||
}
|
||||
case ast.KindTypeReference:
|
||||
visitForDisplayParts(node.AsTypeReferenceNode().TypeName)
|
||||
if len(node.TypeArguments()) > 0 {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "<"})
|
||||
visitDisplayPartList(node.TypeArguments(), ",")
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: ">"})
|
||||
}
|
||||
case ast.KindTypeParameter:
|
||||
if len(node.ModifierNodes()) > 0 {
|
||||
visitDisplayPartList(node.ModifierNodes(), "")
|
||||
}
|
||||
visitForDisplayParts(node.Name())
|
||||
if node.AsTypeParameterDeclaration().Constraint != nil {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: " extends "})
|
||||
visitForDisplayParts(node.AsTypeParameterDeclaration().Constraint)
|
||||
}
|
||||
if node.AsTypeParameterDeclaration().DefaultType != nil {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: " = "})
|
||||
visitForDisplayParts(node.AsTypeParameterDeclaration().DefaultType)
|
||||
}
|
||||
case ast.KindParameter:
|
||||
if len(node.ModifierNodes()) > 0 {
|
||||
visitDisplayPartList(node.ModifierNodes(), " ")
|
||||
}
|
||||
if node.AsParameterDeclaration().DotDotDotToken != nil {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "..."})
|
||||
}
|
||||
visitForDisplayParts(node.Name())
|
||||
if node.QuestionToken() != nil {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "?"})
|
||||
}
|
||||
if node.Type() != nil {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: ": "})
|
||||
visitForDisplayParts(node.Type())
|
||||
}
|
||||
case ast.KindConstructorType:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "new "})
|
||||
visitParametersAndTypeParameters(node)
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: " => "})
|
||||
visitForDisplayParts(node.Type())
|
||||
case ast.KindTypeQuery:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "typeof "})
|
||||
visitForDisplayParts(node.AsTypeQueryNode().ExprName)
|
||||
if len(node.TypeArguments()) > 0 {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "<"})
|
||||
visitDisplayPartList(node.TypeArguments(), ", ")
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: ">"})
|
||||
}
|
||||
case ast.KindTypeLiteral:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "{"})
|
||||
if len(node.Members()) > 0 {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: " "})
|
||||
visitDisplayPartList(node.Members(), "; ")
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: " "})
|
||||
}
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "}"})
|
||||
case ast.KindArrayType:
|
||||
visitForDisplayParts(node.AsArrayTypeNode().ElementType)
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "[]"})
|
||||
case ast.KindTupleType:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "["})
|
||||
visitDisplayPartList(node.Elements(), ", ")
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "]"})
|
||||
case ast.KindNamedTupleMember:
|
||||
if node.AsNamedTupleMember().DotDotDotToken != nil {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "..."})
|
||||
}
|
||||
visitForDisplayParts(node.Name())
|
||||
if node.QuestionToken() != nil {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "?"})
|
||||
}
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: ": "})
|
||||
visitForDisplayParts(node.Type())
|
||||
case ast.KindOptionalType:
|
||||
visitForDisplayParts(node.Type())
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "?"})
|
||||
case ast.KindRestType:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "..."})
|
||||
visitForDisplayParts(node.Type())
|
||||
case ast.KindUnionType:
|
||||
if node.AsUnionTypeNode().Types != nil {
|
||||
visitDisplayPartList(node.AsUnionTypeNode().Types.Nodes, " | ")
|
||||
}
|
||||
case ast.KindIntersectionType:
|
||||
if node.AsIntersectionTypeNode().Types != nil {
|
||||
visitDisplayPartList(node.AsIntersectionTypeNode().Types.Nodes, " & ")
|
||||
}
|
||||
case ast.KindConditionalType:
|
||||
visitForDisplayParts(node.AsConditionalTypeNode().CheckType)
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: " extends "})
|
||||
visitForDisplayParts(node.AsConditionalTypeNode().ExtendsType)
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: " ? "})
|
||||
visitForDisplayParts(node.AsConditionalTypeNode().TrueType)
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: " : "})
|
||||
visitForDisplayParts(node.AsConditionalTypeNode().FalseType)
|
||||
case ast.KindInferType:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "infer "})
|
||||
visitForDisplayParts(node.AsInferTypeNode().TypeParameter)
|
||||
case ast.KindParenthesizedType:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "("})
|
||||
visitForDisplayParts(node.Type())
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: ")"})
|
||||
case ast.KindTypeOperator:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: scanner.TokenToString(node.AsTypeOperatorNode().Operator)})
|
||||
visitForDisplayParts(node.Type())
|
||||
case ast.KindIndexedAccessType:
|
||||
visitForDisplayParts(node.AsIndexedAccessTypeNode().ObjectType)
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "["})
|
||||
visitForDisplayParts(node.AsIndexedAccessTypeNode().IndexType)
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "]"})
|
||||
case ast.KindMappedType:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "{ "})
|
||||
if node.AsMappedTypeNode().ReadonlyToken != nil {
|
||||
if node.AsMappedTypeNode().ReadonlyToken.Kind == ast.KindPlusToken {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "+"})
|
||||
} else if node.AsMappedTypeNode().ReadonlyToken.Kind == ast.KindMinusToken {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "-"})
|
||||
}
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "readonly "})
|
||||
}
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "["})
|
||||
visitForDisplayParts(node.AsMappedTypeNode().TypeParameter)
|
||||
if node.AsMappedTypeNode().NameType != nil {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: " as "})
|
||||
visitForDisplayParts(node.AsMappedTypeNode().NameType)
|
||||
}
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "]"})
|
||||
if node.QuestionToken() != nil {
|
||||
if node.QuestionToken().Kind == ast.KindPlusToken {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "+"})
|
||||
} else if node.QuestionToken().Kind == ast.KindMinusToken {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "-"})
|
||||
}
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "?"})
|
||||
}
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: ": "})
|
||||
if node.Type() != nil {
|
||||
visitForDisplayParts(node.Type())
|
||||
}
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "; }"})
|
||||
case ast.KindLiteralType:
|
||||
visitForDisplayParts(node.AsLiteralTypeNode().Literal)
|
||||
case ast.KindFunctionType:
|
||||
visitParametersAndTypeParameters(node)
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: " => "})
|
||||
visitForDisplayParts(node.Type())
|
||||
case ast.KindImportType:
|
||||
if node.AsImportTypeNode().IsTypeOf {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "typeof "})
|
||||
}
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "import("})
|
||||
visitForDisplayParts(node.AsImportTypeNode().Argument)
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: ")"})
|
||||
if node.AsImportTypeNode().Qualifier != nil {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "."})
|
||||
visitForDisplayParts(node.AsImportTypeNode().Qualifier)
|
||||
}
|
||||
if len(node.TypeArguments()) > 0 {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "<"})
|
||||
visitDisplayPartList(node.TypeArguments(), ", ")
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: ">"})
|
||||
}
|
||||
case ast.KindPropertySignature:
|
||||
if len(node.ModifierNodes()) > 0 {
|
||||
visitDisplayPartList(node.ModifierNodes(), " ")
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: " "})
|
||||
}
|
||||
visitForDisplayParts(node.Name())
|
||||
if node.PostfixToken() != nil {
|
||||
parts = append(
|
||||
parts,
|
||||
&lsproto.InlayHintLabelPart{
|
||||
Value: scanner.TokenToString(node.PostfixToken().Kind),
|
||||
},
|
||||
)
|
||||
}
|
||||
if node.Type() != nil {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: ": "})
|
||||
visitForDisplayParts(node.Type())
|
||||
}
|
||||
case ast.KindIndexSignature:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "["})
|
||||
visitDisplayPartList(node.Parameters(), ", ")
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "]"})
|
||||
if node.Type() != nil {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: ": "})
|
||||
visitForDisplayParts(node.Type())
|
||||
}
|
||||
case ast.KindMethodSignature:
|
||||
if len(node.ModifierNodes()) > 0 {
|
||||
visitDisplayPartList(node.ModifierNodes(), " ")
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: " "})
|
||||
}
|
||||
visitForDisplayParts(node.Name())
|
||||
if node.PostfixToken() != nil {
|
||||
parts = append(
|
||||
parts,
|
||||
&lsproto.InlayHintLabelPart{
|
||||
Value: scanner.TokenToString(node.PostfixToken().Kind),
|
||||
},
|
||||
)
|
||||
}
|
||||
visitParametersAndTypeParameters(node)
|
||||
if node.Type() != nil {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: ": "})
|
||||
visitForDisplayParts(node.Type())
|
||||
}
|
||||
case ast.KindCallSignature:
|
||||
visitParametersAndTypeParameters(node)
|
||||
if node.Type() != nil {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: ": "})
|
||||
visitForDisplayParts(node.Type())
|
||||
}
|
||||
case ast.KindConstructSignature:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "new "})
|
||||
visitParametersAndTypeParameters(node)
|
||||
if node.Type() != nil {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: ": "})
|
||||
visitForDisplayParts(node.Type())
|
||||
}
|
||||
case ast.KindArrayBindingPattern:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "["})
|
||||
visitDisplayPartList(node.Elements(), ", ")
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "]"})
|
||||
case ast.KindObjectBindingPattern:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "{"})
|
||||
if len(node.Elements()) > 0 {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: " "})
|
||||
visitDisplayPartList(node.Elements(), ", ")
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: " "})
|
||||
}
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "}"})
|
||||
case ast.KindBindingElement:
|
||||
visitForDisplayParts(node.Name())
|
||||
case ast.KindPrefixUnaryExpression:
|
||||
parts = append(
|
||||
parts,
|
||||
&lsproto.InlayHintLabelPart{
|
||||
Value: scanner.TokenToString(node.AsPrefixUnaryExpression().Operator),
|
||||
},
|
||||
)
|
||||
visitForDisplayParts(node.AsPrefixUnaryExpression().Operand)
|
||||
case ast.KindTemplateLiteralType:
|
||||
visitForDisplayParts(node.AsTemplateLiteralTypeNode().Head)
|
||||
for _, span := range node.AsTemplateLiteralTypeNode().TemplateSpans.Nodes {
|
||||
visitForDisplayParts(span)
|
||||
}
|
||||
case ast.KindTemplateHead:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: s.getLiteralText(node)})
|
||||
case ast.KindTemplateLiteralTypeSpan:
|
||||
visitForDisplayParts(node.Type())
|
||||
visitForDisplayParts(node.AsTemplateLiteralTypeSpan().Literal)
|
||||
case ast.KindTemplateMiddle, ast.KindTemplateTail:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: s.getLiteralText(node)})
|
||||
case ast.KindThisType:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "this"})
|
||||
case ast.KindComputedPropertyName:
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "["})
|
||||
visitForDisplayParts(node.Expression())
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "]"})
|
||||
case ast.KindPropertyAccessExpression:
|
||||
visitForDisplayParts(node.Expression())
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "."})
|
||||
visitForDisplayParts(node.Name())
|
||||
case ast.KindElementAccessExpression:
|
||||
visitForDisplayParts(node.Expression())
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "["})
|
||||
visitForDisplayParts(node.AsElementAccessExpression().ArgumentExpression)
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "]"})
|
||||
default:
|
||||
debug.FailBadSyntaxKind(node)
|
||||
}
|
||||
}
|
||||
|
||||
visitDisplayPartList = func(nodes []*ast.Node, separator string) {
|
||||
for i, n := range nodes {
|
||||
if i > 0 {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: separator})
|
||||
}
|
||||
visitForDisplayParts(n)
|
||||
}
|
||||
}
|
||||
|
||||
visitParametersAndTypeParameters = func(node *ast.SignatureDeclaration) {
|
||||
if len(node.TypeParameters()) > 0 {
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "<"})
|
||||
visitDisplayPartList(node.TypeParameters(), ", ")
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: ">"})
|
||||
}
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: "("})
|
||||
visitDisplayPartList(node.Parameters(), ", ")
|
||||
parts = append(parts, &lsproto.InlayHintLabelPart{Value: ")"})
|
||||
}
|
||||
|
||||
visitForDisplayParts(node)
|
||||
return parts
|
||||
}
|
||||
|
||||
func (s *inlayHintState) getNodeDisplayPart(text string, node *ast.Node) *lsproto.InlayHintLabelPart {
|
||||
file := ast.GetSourceFileOfNode(node)
|
||||
pos := astnav.GetStartOfNode(node, file, false /*includeJSDoc*/)
|
||||
end := node.End()
|
||||
return &lsproto.InlayHintLabelPart{
|
||||
Value: text,
|
||||
Location: &lsproto.Location{
|
||||
Uri: lsconv.FileNameToDocumentURI(file.FileName()),
|
||||
Range: s.converters.ToLSPRange(file, core.NewTextRange(pos, end)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *inlayHintState) getLiteralText(node *ast.LiteralLikeNode) string {
|
||||
switch node.Kind {
|
||||
case ast.KindStringLiteral:
|
||||
if s.quotePreference == lsutil.QuotePreferenceSingle {
|
||||
return `'` + printer.EscapeString(node.Text(), printer.QuoteCharSingleQuote) + `'`
|
||||
}
|
||||
return `"` + printer.EscapeString(node.Text(), printer.QuoteCharDoubleQuote) + `"`
|
||||
case ast.KindTemplateHead, ast.KindTemplateMiddle, ast.KindTemplateTail:
|
||||
rawText := node.RawText()
|
||||
if rawText == "" {
|
||||
rawText = printer.EscapeString(node.Text(), printer.QuoteCharBacktick)
|
||||
}
|
||||
switch node.Kind {
|
||||
case ast.KindTemplateHead:
|
||||
return "`" + rawText + "${"
|
||||
case ast.KindTemplateMiddle:
|
||||
return "}" + rawText + "${"
|
||||
case ast.KindTemplateTail:
|
||||
return "}" + rawText + "`"
|
||||
}
|
||||
}
|
||||
return node.Text()
|
||||
}
|
||||
|
||||
type parameterInfo struct {
|
||||
parameter *ast.IdentifierNode
|
||||
name string
|
||||
isRestParameter bool
|
||||
}
|
||||
|
||||
func (s *inlayHintState) getParameterIdentifierInfoAtPosition(signature *checker.Signature, pos int) *parameterInfo {
|
||||
parameters := signature.Parameters()
|
||||
paramCount := len(parameters) - core.IfElse(signature.HasRestParameter(), 1, 0)
|
||||
if pos < paramCount {
|
||||
param := parameters[pos]
|
||||
paramId := getParameterDeclarationIdentifier(param)
|
||||
if paramId == nil {
|
||||
return nil
|
||||
}
|
||||
return ¶meterInfo{
|
||||
parameter: paramId,
|
||||
name: paramId.Text(),
|
||||
isRestParameter: false,
|
||||
}
|
||||
}
|
||||
|
||||
var restParameter *ast.Symbol
|
||||
var restId *ast.IdentifierNode
|
||||
if paramCount < len(parameters) {
|
||||
restParameter = parameters[paramCount]
|
||||
restId = getParameterDeclarationIdentifier(restParameter)
|
||||
}
|
||||
if restId == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
restType := s.checker.GetTypeOfSymbol(restParameter)
|
||||
if restType.IsTupleType() {
|
||||
associatedNames := make([]*ast.Node, 0, len(restType.Target().AsTupleType().ElementInfos()))
|
||||
for _, elementInfo := range restType.Target().AsTupleType().ElementInfos() {
|
||||
labeledElement := elementInfo.LabeledDeclaration()
|
||||
associatedNames = append(associatedNames, labeledElement)
|
||||
}
|
||||
index := pos - paramCount
|
||||
if index < len(associatedNames) {
|
||||
associatedName := associatedNames[index]
|
||||
if associatedName != nil {
|
||||
debug.Assert(ast.IsIdentifier(associatedName.Name()))
|
||||
var isRestTupleElement bool
|
||||
if ast.IsNamedTupleMember(associatedName) {
|
||||
isRestTupleElement = associatedName.AsNamedTupleMember().DotDotDotToken != nil
|
||||
} else {
|
||||
isRestTupleElement = associatedName.AsParameterDeclaration().DotDotDotToken != nil
|
||||
}
|
||||
return ¶meterInfo{
|
||||
parameter: associatedName.Name(),
|
||||
name: associatedName.Name().Text(),
|
||||
isRestParameter: isRestTupleElement,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if pos == paramCount {
|
||||
return ¶meterInfo{
|
||||
parameter: restId,
|
||||
name: restParameter.Name,
|
||||
isRestParameter: true,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getParameterDeclarationIdentifier(symbol *ast.Symbol) *ast.IdentifierNode {
|
||||
if symbol.ValueDeclaration != nil && ast.IsParameterDeclaration(symbol.ValueDeclaration) && ast.IsIdentifier(symbol.ValueDeclaration.Name()) {
|
||||
return symbol.ValueDeclaration.Name()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func identifierOrAccessExpressionPostfixMatchesParameterName(expr *ast.Expression, parameterName string) bool {
|
||||
if ast.IsIdentifier(expr) {
|
||||
return expr.Text() == parameterName
|
||||
}
|
||||
if ast.IsPropertyAccessExpression(expr) {
|
||||
return expr.Name().Text() == parameterName
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *inlayHintState) leadingCommentsContainsParameterName(node *ast.Node, name string) bool {
|
||||
if !scanner.IsIdentifierText(name, s.file.LanguageVariant) {
|
||||
return false
|
||||
}
|
||||
|
||||
ranges := getLeadingCommentRangesOfNode(node, s.file)
|
||||
fileText := s.file.Text()
|
||||
for r := range ranges {
|
||||
commentText := strings.TrimFunc(fileText[r.Pos():r.End()], func(r rune) bool {
|
||||
return unicode.IsSpace(r) || r == '/' || r == '*'
|
||||
})
|
||||
if commentText == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *inlayHintState) getTypeAnnotationPosition(decl *ast.FunctionLikeDeclaration) int {
|
||||
closeParenToken := astnav.FindChildOfKind(decl, ast.KindCloseParenToken, s.file)
|
||||
if closeParenToken != nil {
|
||||
return closeParenToken.End()
|
||||
}
|
||||
return decl.ParameterList().End()
|
||||
}
|
||||
|
||||
func isAnyInlayHintEnabled(preferences lsutil.InlayHintsPreferences) bool {
|
||||
return preferences.IncludeInlayParameterNameHints != lsutil.IncludeInlayParameterNameHintsNone ||
|
||||
preferences.IncludeInlayFunctionParameterTypeHints.IsTrue() ||
|
||||
preferences.IncludeInlayVariableTypeHints.IsTrue() ||
|
||||
preferences.IncludeInlayPropertyDeclarationTypeHints.IsTrue() ||
|
||||
preferences.IncludeInlayFunctionLikeReturnTypeHints.IsTrue() ||
|
||||
preferences.IncludeInlayEnumMemberValueHints.IsTrue()
|
||||
}
|
||||
Reference in New Issue
Block a user