@@ -47,6 +47,7 @@ import {
47
47
diagnosticToString ,
48
48
displayPart ,
49
49
DotDotDotToken ,
50
+ EmitFlags ,
50
51
EmitHint ,
51
52
EmitTextWriter ,
52
53
EntityName ,
@@ -312,6 +313,7 @@ import {
312
313
ScriptElementKindModifier ,
313
314
ScriptTarget ,
314
315
SemanticMeaning ,
316
+ setEmitFlags ,
315
317
setSnippetElement ,
316
318
shouldUseUriStyleNodeCoreModules ,
317
319
SignatureHelp ,
@@ -673,9 +675,10 @@ export function getCompletionsAtPosition(
673
675
674
676
}
675
677
678
+ const compilerOptions = program . getCompilerOptions ( ) ;
679
+ const checker = program . getTypeChecker ( ) ;
676
680
// If the request is a continuation of an earlier `isIncomplete` response,
677
681
// we can continue it from the cached previous response.
678
- const compilerOptions = program . getCompilerOptions ( ) ;
679
682
const incompleteCompletionsCache = preferences . allowIncompleteCompletions ? host . getIncompleteCompletionsCache ?.( ) : undefined ;
680
683
if ( incompleteCompletionsCache && completionKind === CompletionTriggerKind . TriggerForIncompleteCompletions && previousToken && isIdentifier ( previousToken ) ) {
681
684
const incompleteContinuation = continuePreviousIncompleteResponse ( incompleteCompletionsCache , sourceFile , previousToken , program , host , preferences , cancellationToken , position ) ;
@@ -713,12 +716,24 @@ export function getCompletionsAtPosition(
713
716
// If the current position is a jsDoc tag name, only tag names should be provided for completion
714
717
return jsdocCompletionInfo ( [
715
718
...JsDoc . getJSDocTagNameCompletions ( ) ,
716
- ...getJSDocParameterCompletions ( sourceFile , position , preferences , compilerOptions , /*tagNameOnly*/ true ) ] ) ;
719
+ ...getJSDocParameterCompletions (
720
+ sourceFile ,
721
+ position ,
722
+ checker ,
723
+ compilerOptions ,
724
+ preferences ,
725
+ /*tagNameOnly*/ true ) ] ) ;
717
726
case CompletionDataKind . JsDocTag :
718
727
// If the current position is a jsDoc tag, only tags should be provided for completion
719
728
return jsdocCompletionInfo ( [
720
729
...JsDoc . getJSDocTagCompletions ( ) ,
721
- ...getJSDocParameterCompletions ( sourceFile , position , preferences , compilerOptions , /*tagNameOnly*/ false ) ] ) ;
730
+ ...getJSDocParameterCompletions (
731
+ sourceFile ,
732
+ position ,
733
+ checker ,
734
+ compilerOptions ,
735
+ preferences ,
736
+ /*tagNameOnly*/ false ) ] ) ;
722
737
case CompletionDataKind . JsDocParameterName :
723
738
return jsdocCompletionInfo ( JsDoc . getJSDocParameterNameCompletions ( completionData . tag ) ) ;
724
739
case CompletionDataKind . Keywords :
@@ -838,8 +853,9 @@ function jsdocCompletionInfo(entries: CompletionEntry[]): CompletionInfo {
838
853
function getJSDocParameterCompletions (
839
854
sourceFile : SourceFile ,
840
855
position : number ,
841
- preferences : UserPreferences ,
856
+ checker : TypeChecker ,
842
857
options : CompilerOptions ,
858
+ preferences : UserPreferences ,
843
859
tagNameOnly : boolean ) : CompletionEntry [ ] {
844
860
const currentToken = getTokenAtPosition ( sourceFile , position ) ;
845
861
if ( ! isJSDocTag ( currentToken ) && ! isJSDoc ( currentToken ) ) {
@@ -864,8 +880,29 @@ function getJSDocParameterCompletions(
864
880
if ( isIdentifier ( param . name ) ) { // Named parameter
865
881
const tabstopCounter = { tabstop : 1 } ;
866
882
const paramName = param . name . text ;
867
- let snippetText = getJSDocParamAnnotation ( paramName , param . initializer , param . dotDotDotToken , isJs , /*isObject*/ false , /*isSnippet*/ true , tabstopCounter ) ;
868
- let insertText = getJSDocParamAnnotation ( paramName , param . initializer , param . dotDotDotToken , isJs , /*isObject*/ false , /*isSnippet*/ false ) ;
883
+ let snippetText =
884
+ getJSDocParamAnnotation (
885
+ paramName ,
886
+ param . initializer ,
887
+ param . dotDotDotToken ,
888
+ isJs ,
889
+ /*isObject*/ false ,
890
+ /*isSnippet*/ true ,
891
+ checker ,
892
+ options ,
893
+ preferences ,
894
+ tabstopCounter ) ;
895
+ let insertText =
896
+ getJSDocParamAnnotation (
897
+ paramName ,
898
+ param . initializer ,
899
+ param . dotDotDotToken ,
900
+ isJs ,
901
+ /*isObject*/ false ,
902
+ /*isSnippet*/ false ,
903
+ checker ,
904
+ options ,
905
+ preferences ) ;
869
906
if ( tagNameOnly ) { // Remove `@`
870
907
insertText = insertText . slice ( 1 ) ;
871
908
snippetText = snippetText . slice ( 1 ) ;
@@ -881,9 +918,27 @@ function getJSDocParameterCompletions(
881
918
else if ( param . parent . parameters . indexOf ( param ) === paramTagCount ) { // Destructuring parameter; do it positionally
882
919
const paramPath = `param${ paramTagCount } ` ;
883
920
const insertTextResult =
884
- generateJSDocParamTagsForDestructuring ( paramPath , param . name , param . initializer , param . dotDotDotToken , isJs , /*isSnippet*/ false ) ;
921
+ generateJSDocParamTagsForDestructuring (
922
+ paramPath ,
923
+ param . name ,
924
+ param . initializer ,
925
+ param . dotDotDotToken ,
926
+ isJs ,
927
+ /*isSnippet*/ false ,
928
+ checker ,
929
+ options ,
930
+ preferences , ) ;
885
931
const snippetTextResult =
886
- generateJSDocParamTagsForDestructuring ( paramPath , param . name , param . initializer , param . dotDotDotToken , isJs , /*isSnippet*/ true ) ;
932
+ generateJSDocParamTagsForDestructuring (
933
+ paramPath ,
934
+ param . name ,
935
+ param . initializer ,
936
+ param . dotDotDotToken ,
937
+ isJs ,
938
+ /*isSnippet*/ true ,
939
+ checker ,
940
+ options ,
941
+ preferences , ) ;
887
942
let insertText = insertTextResult . join ( getNewLineCharacter ( options ) + "* " ) ;
888
943
let snippetText = snippetTextResult . join ( getNewLineCharacter ( options ) + "* " ) ;
889
944
if ( tagNameOnly ) { // Remove `@`
@@ -907,9 +962,24 @@ function generateJSDocParamTagsForDestructuring(
907
962
initializer : Expression | undefined ,
908
963
dotDotDotToken : DotDotDotToken | undefined ,
909
964
isJs : boolean ,
910
- isSnippet : boolean ) : string [ ] {
965
+ isSnippet : boolean ,
966
+ checker : TypeChecker ,
967
+ options : CompilerOptions ,
968
+ preferences : UserPreferences ) : string [ ] {
911
969
if ( ! isJs ) {
912
- return [ getJSDocParamAnnotation ( path , initializer , dotDotDotToken , isJs , /*isObject*/ false , isSnippet , { tabstop : 1 } ) ] ;
970
+ return [
971
+ getJSDocParamAnnotation (
972
+ path ,
973
+ initializer ,
974
+ dotDotDotToken ,
975
+ isJs ,
976
+ /*isObject*/ false ,
977
+ isSnippet ,
978
+ checker ,
979
+ options ,
980
+ preferences ,
981
+ { tabstop : 1 } )
982
+ ] ;
913
983
}
914
984
return patternWorker ( path , pattern , initializer , dotDotDotToken , { tabstop : 1 } ) ;
915
985
@@ -922,7 +992,18 @@ function generateJSDocParamTagsForDestructuring(
922
992
if ( isObjectBindingPattern ( pattern ) && ! dotDotDotToken ) {
923
993
const oldTabstop = counter . tabstop ;
924
994
const childCounter = { tabstop : oldTabstop } ;
925
- const rootParam = getJSDocParamAnnotation ( path , initializer , dotDotDotToken , isJs , /*isObject*/ true , isSnippet , childCounter ) ;
995
+ const rootParam =
996
+ getJSDocParamAnnotation (
997
+ path ,
998
+ initializer ,
999
+ dotDotDotToken ,
1000
+ isJs ,
1001
+ /*isObject*/ true ,
1002
+ isSnippet ,
1003
+ checker ,
1004
+ options ,
1005
+ preferences ,
1006
+ childCounter ) ;
926
1007
let childTags : string [ ] | undefined = [ ] ;
927
1008
for ( const element of pattern . elements ) {
928
1009
const elementTags = elementWorker ( path , element , childCounter ) ;
@@ -939,7 +1020,19 @@ function generateJSDocParamTagsForDestructuring(
939
1020
return [ rootParam , ...childTags ] ;
940
1021
}
941
1022
}
942
- return [ getJSDocParamAnnotation ( path , initializer , dotDotDotToken , isJs , /*isObject*/ false , isSnippet , counter ) ] ;
1023
+ return [
1024
+ getJSDocParamAnnotation (
1025
+ path ,
1026
+ initializer ,
1027
+ dotDotDotToken ,
1028
+ isJs ,
1029
+ /*isObject*/ false ,
1030
+ isSnippet ,
1031
+ checker ,
1032
+ options ,
1033
+ preferences ,
1034
+ counter )
1035
+ ] ;
943
1036
}
944
1037
945
1038
// Assumes binding element is inside object binding pattern.
@@ -951,7 +1044,18 @@ function generateJSDocParamTagsForDestructuring(
951
1044
return undefined ;
952
1045
}
953
1046
const paramName = `${ path } .${ propertyName } ` ;
954
- return [ getJSDocParamAnnotation ( paramName , element . initializer , element . dotDotDotToken , isJs , /*isObject*/ false , isSnippet , counter ) ] ;
1047
+ return [
1048
+ getJSDocParamAnnotation (
1049
+ paramName ,
1050
+ element . initializer ,
1051
+ element . dotDotDotToken ,
1052
+ isJs ,
1053
+ /*isObject*/ false ,
1054
+ isSnippet ,
1055
+ checker ,
1056
+ options ,
1057
+ preferences ,
1058
+ counter ) ] ;
955
1059
}
956
1060
else if ( element . propertyName ) { // `{ b: {...} }` or `{ b: [...] }`
957
1061
const propertyName = tryGetTextOfPropertyName ( element . propertyName ) ;
@@ -973,6 +1077,9 @@ function getJSDocParamAnnotation(
973
1077
isJs : boolean ,
974
1078
isObject : boolean ,
975
1079
isSnippet : boolean ,
1080
+ checker : TypeChecker ,
1081
+ options : CompilerOptions ,
1082
+ preferences : UserPreferences ,
976
1083
tabstopCounter ?: TabStopCounter ) {
977
1084
if ( isSnippet ) {
978
1085
Debug . assertIsDefined ( tabstopCounter ) ;
@@ -984,17 +1091,38 @@ function getJSDocParamAnnotation(
984
1091
paramName = escapeSnippetText ( paramName ) ;
985
1092
}
986
1093
if ( isJs ) {
987
- let type : string ;
1094
+ let type = "*" ;
988
1095
if ( isObject ) {
989
1096
Debug . assert ( ! dotDotDotToken , `Cannot annotate a rest parameter with type 'Object'.` ) ;
990
1097
type = "Object" ;
991
1098
}
992
1099
else {
993
- if ( isSnippet ) {
994
- type = `\${${ tabstopCounter ! . tabstop ++ } :*}` ;
1100
+ if ( initializer ) {
1101
+ const inferredType = checker . getTypeAtLocation ( initializer . parent ) ;
1102
+ if ( ! ( inferredType . flags & TypeFlags . Any ) ) {
1103
+ const sourceFile = initializer . getSourceFile ( ) ;
1104
+ const quotePreference = getQuotePreference ( sourceFile , preferences ) ;
1105
+ const builderFlags = ( quotePreference === QuotePreference . Single ? NodeBuilderFlags . UseSingleQuotesForStringLiteralType : NodeBuilderFlags . None ) ;
1106
+ const typeNode = checker . typeToTypeNode ( inferredType , findAncestor ( initializer , isFunctionLike ) , builderFlags ) ;
1107
+ if ( typeNode ) {
1108
+ const printer = isSnippet
1109
+ ? createSnippetPrinter ( {
1110
+ removeComments : true ,
1111
+ module : options . module ,
1112
+ target : options . target ,
1113
+ } )
1114
+ : createPrinter ( {
1115
+ removeComments : true ,
1116
+ module : options . module ,
1117
+ target : options . target
1118
+ } ) ;
1119
+ setEmitFlags ( typeNode , EmitFlags . SingleLine ) ;
1120
+ type = printer . printNode ( EmitHint . Unspecified , typeNode , sourceFile ) ;
1121
+ }
1122
+ }
995
1123
}
996
- else {
997
- type = "*" ;
1124
+ if ( isSnippet ) {
1125
+ type = `\${ ${ tabstopCounter ! . tabstop ++ } : ${ type } }` ;
998
1126
}
999
1127
}
1000
1128
const dotDotDot = ! isObject && dotDotDotToken ? "..." : "" ;
0 commit comments