@@ -132,6 +132,46 @@ namespace ts {
132
132
let scanner : Scanner = createScanner ( ScriptTarget . Latest , /*skipTrivia*/ true ) ;
133
133
134
134
let emptyArray : any [ ] = [ ] ;
135
+
136
+ const jsDocTagNames = [
137
+ "augments" ,
138
+ "author" ,
139
+ "argument" ,
140
+ "borrows" ,
141
+ "class" ,
142
+ "constant" ,
143
+ "constructor" ,
144
+ "constructs" ,
145
+ "default" ,
146
+ "deprecated" ,
147
+ "description" ,
148
+ "event" ,
149
+ "example" ,
150
+ "extends" ,
151
+ "field" ,
152
+ "fileOverview" ,
153
+ "function" ,
154
+ "ignore" ,
155
+ "inner" ,
156
+ "lends" ,
157
+ "link" ,
158
+ "memberOf" ,
159
+ "name" ,
160
+ "namespace" ,
161
+ "param" ,
162
+ "private" ,
163
+ "property" ,
164
+ "public" ,
165
+ "requires" ,
166
+ "returns" ,
167
+ "see" ,
168
+ "since" ,
169
+ "static" ,
170
+ "throws" ,
171
+ "type" ,
172
+ "version"
173
+ ] ;
174
+ let jsDocCompletionEntries : CompletionEntry [ ] ;
135
175
136
176
function createNode ( kind : SyntaxKind , pos : number , end : number , flags : NodeFlags , parent ?: Node ) : NodeObject {
137
177
let node = < NodeObject > new ( getNodeConstructor ( kind ) ) ( ) ;
@@ -2971,6 +3011,8 @@ namespace ts {
2971
3011
let sourceFile = getValidSourceFile ( fileName ) ;
2972
3012
let isJavaScriptFile = isJavaScript ( fileName ) ;
2973
3013
3014
+ let isJsDocTagName = false ;
3015
+
2974
3016
let start = new Date ( ) . getTime ( ) ;
2975
3017
let currentToken = getTokenAtPosition ( sourceFile , position ) ;
2976
3018
log ( "getCompletionData: Get current token: " + ( new Date ( ) . getTime ( ) - start ) ) ;
@@ -2981,8 +3023,44 @@ namespace ts {
2981
3023
log ( "getCompletionData: Is inside comment: " + ( new Date ( ) . getTime ( ) - start ) ) ;
2982
3024
2983
3025
if ( insideComment ) {
2984
- log ( "Returning an empty list because completion was inside a comment." ) ;
2985
- return undefined ;
3026
+ // The current position is next to the '@' sign, when no tag name being provided yet.
3027
+ // Provide a full list of tag names
3028
+ if ( hasDocComment ( sourceFile , position ) && sourceFile . text . charCodeAt ( position - 1 ) === CharacterCodes . at ) {
3029
+ isJsDocTagName = true ;
3030
+ }
3031
+
3032
+ // Completion should work inside certain JsDoc tags. For example:
3033
+ // /** @type {number | string } */
3034
+ // Completion should work in the brackets
3035
+ let insideJsDocTagExpression = false ;
3036
+ let tag = getJsDocTagAtPosition ( sourceFile , position ) ;
3037
+ if ( tag ) {
3038
+ if ( tag . tagName . pos <= position && position <= tag . tagName . end ) {
3039
+ isJsDocTagName = true ;
3040
+ }
3041
+
3042
+ switch ( tag . kind ) {
3043
+ case SyntaxKind . JSDocTypeTag :
3044
+ case SyntaxKind . JSDocParameterTag :
3045
+ case SyntaxKind . JSDocReturnTag :
3046
+ let tagWithExpression = < JSDocTypeTag | JSDocParameterTag | JSDocReturnTag > tag ;
3047
+ if ( tagWithExpression . typeExpression ) {
3048
+ insideJsDocTagExpression = tagWithExpression . typeExpression . pos < position && position < tagWithExpression . typeExpression . end ;
3049
+ }
3050
+ break ;
3051
+ }
3052
+ }
3053
+
3054
+ if ( isJsDocTagName ) {
3055
+ return { symbols : undefined , isMemberCompletion : false , isNewIdentifierLocation : false , location : undefined , isRightOfDot : false , isJsDocTagName } ;
3056
+ }
3057
+
3058
+ if ( ! insideJsDocTagExpression ) {
3059
+ // Proceed if the current position is in jsDoc tag expression; otherwise it is a normal
3060
+ // comment or the plain text part of a jsDoc comment, so no completion should be available
3061
+ log ( "Returning an empty list because completion was inside a regular comment or plain text part of a JsDoc comment." ) ;
3062
+ return undefined ;
3063
+ }
2986
3064
}
2987
3065
2988
3066
start = new Date ( ) . getTime ( ) ;
@@ -3068,7 +3146,7 @@ namespace ts {
3068
3146
3069
3147
log ( "getCompletionData: Semantic work: " + ( new Date ( ) . getTime ( ) - semanticStart ) ) ;
3070
3148
3071
- return { symbols, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot : ( isRightOfDot || isRightOfOpenTag ) } ;
3149
+ return { symbols, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot : ( isRightOfDot || isRightOfOpenTag ) , isJsDocTagName } ;
3072
3150
3073
3151
function getTypeScriptMemberSymbols ( ) : void {
3074
3152
// Right of dot member completion list
@@ -3708,9 +3786,14 @@ namespace ts {
3708
3786
return undefined ;
3709
3787
}
3710
3788
3711
- let { symbols, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot } = completionData ;
3789
+ let { symbols, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot, isJsDocTagName } = completionData ;
3712
3790
3713
3791
let entries : CompletionEntry [ ] ;
3792
+ if ( isJsDocTagName ) {
3793
+ // If the current position is a jsDoc tag name, only tag names should be provided for completion
3794
+ return { isMemberCompletion : false , isNewIdentifierLocation : false , entries : getAllJsDocCompletionEntries ( ) } ;
3795
+ }
3796
+
3714
3797
if ( isRightOfDot && isJavaScript ( fileName ) ) {
3715
3798
entries = getCompletionEntriesFromSymbols ( symbols ) ;
3716
3799
addRange ( entries , getJavaScriptCompletionEntries ( ) ) ;
@@ -3724,7 +3807,7 @@ namespace ts {
3724
3807
}
3725
3808
3726
3809
// Add keywords if this is not a member completion list
3727
- if ( ! isMemberCompletion ) {
3810
+ if ( ! isMemberCompletion && ! isJsDocTagName ) {
3728
3811
addRange ( entries , keywordCompletions ) ;
3729
3812
}
3730
3813
@@ -3757,6 +3840,17 @@ namespace ts {
3757
3840
return entries ;
3758
3841
}
3759
3842
3843
+ function getAllJsDocCompletionEntries ( ) : CompletionEntry [ ] {
3844
+ return jsDocCompletionEntries || ( jsDocCompletionEntries = ts . map ( jsDocTagNames , tagName => {
3845
+ return {
3846
+ name : tagName ,
3847
+ kind : ScriptElementKind . keyword ,
3848
+ kindModifiers : "" ,
3849
+ sortText : "0" ,
3850
+ }
3851
+ } ) ) ;
3852
+ }
3853
+
3760
3854
function createCompletionEntry ( symbol : Symbol , location : Node ) : CompletionEntry {
3761
3855
// Try to get a valid display name for this symbol, if we could not find one, then ignore it.
3762
3856
// We would like to only show things that can be added after a dot, so for instance numeric properties can
0 commit comments