@@ -354,14 +354,15 @@ namespace ts.Completions {
354
354
let requestJsDocTag = false ;
355
355
356
356
let start = timestamp ( ) ;
357
- const currentToken = getTokenAtPosition ( sourceFile , position , /*includeJsDocComment*/ false ) ; // TODO: GH#15853
357
+ let currentToken = getTokenAtPosition ( sourceFile , position , /*includeJsDocComment*/ false ) ; // TODO: GH#15853
358
358
log ( "getCompletionData: Get current token: " + ( timestamp ( ) - start ) ) ;
359
359
360
360
start = timestamp ( ) ;
361
361
// Completion not allowed inside comments, bail out if this is the case
362
362
const insideComment = isInComment ( sourceFile , position , currentToken ) ;
363
363
log ( "getCompletionData: Is inside comment: " + ( timestamp ( ) - start ) ) ;
364
364
365
+ let insideJsDocTagTypeExpression = false ;
365
366
if ( insideComment ) {
366
367
if ( hasDocComment ( sourceFile , position ) ) {
367
368
// The current position is next to the '@' sign, when no tag name being provided yet.
@@ -394,30 +395,28 @@ namespace ts.Completions {
394
395
// Completion should work inside certain JsDoc tags. For example:
395
396
// /** @type {number | string } */
396
397
// Completion should work in the brackets
397
- let insideJsDocTagExpression = false ;
398
398
const tag = getJsDocTagAtPosition ( sourceFile , position ) ;
399
399
if ( tag ) {
400
400
if ( tag . tagName . pos <= position && position <= tag . tagName . end ) {
401
401
requestJsDocTagName = true ;
402
402
}
403
-
404
- switch ( tag . kind ) {
405
- case SyntaxKind . JSDocTypeTag :
406
- case SyntaxKind . JSDocParameterTag :
407
- case SyntaxKind . JSDocReturnTag :
408
- const tagWithExpression = < JSDocTypeTag | JSDocParameterTag | JSDocReturnTag > tag ;
409
- if ( tagWithExpression . typeExpression ) {
410
- insideJsDocTagExpression = tagWithExpression . typeExpression . pos < position && position < tagWithExpression . typeExpression . end ;
411
- }
412
- break ;
403
+ if ( isTagWithTypeExpression ( tag ) && tag . typeExpression ) {
404
+ currentToken = getTokenAtPosition ( sourceFile , position , /*includeJsDocComment*/ true ) ;
405
+ if ( ! currentToken ||
406
+ ( ! isDeclarationName ( currentToken ) &&
407
+ ( currentToken . parent . kind !== SyntaxKind . JSDocPropertyTag ||
408
+ ( < JSDocPropertyTag > currentToken . parent ) . name !== currentToken ) ) ) {
409
+ // Use as type location if inside tag's type expression
410
+ insideJsDocTagTypeExpression = isCurrentlyEditingNode ( tag . typeExpression ) ;
411
+ }
413
412
}
414
413
}
415
414
416
415
if ( requestJsDocTagName || requestJsDocTag ) {
417
416
return { symbols : undefined , isGlobalCompletion : false , isMemberCompletion : false , isNewIdentifierLocation : false , location : undefined , isRightOfDot : false , requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords : false } ;
418
417
}
419
418
420
- if ( ! insideJsDocTagExpression ) {
419
+ if ( ! insideJsDocTagTypeExpression ) {
421
420
// Proceed if the current position is in jsDoc tag expression; otherwise it is a normal
422
421
// comment or the plain text part of a jsDoc comment, so no completion should be available
423
422
log ( "Returning an empty list because completion was inside a regular comment or plain text part of a JsDoc comment." ) ;
@@ -426,7 +425,7 @@ namespace ts.Completions {
426
425
}
427
426
428
427
start = timestamp ( ) ;
429
- const previousToken = findPrecedingToken ( position , sourceFile ) ;
428
+ const previousToken = findPrecedingToken ( position , sourceFile , /*startNode*/ undefined , insideJsDocTagTypeExpression ) ;
430
429
log ( "getCompletionData: Get previous token 1: " + ( timestamp ( ) - start ) ) ;
431
430
432
431
// The decision to provide completion depends on the contextToken, which is determined through the previousToken.
@@ -437,7 +436,7 @@ namespace ts.Completions {
437
436
// Skip this partial identifier and adjust the contextToken to the token that precedes it.
438
437
if ( contextToken && position <= contextToken . end && isWord ( contextToken . kind ) ) {
439
438
const start = timestamp ( ) ;
440
- contextToken = findPrecedingToken ( contextToken . getFullStart ( ) , sourceFile ) ;
439
+ contextToken = findPrecedingToken ( contextToken . getFullStart ( ) , sourceFile , /*startNode*/ undefined , insideJsDocTagTypeExpression ) ;
441
440
log ( "getCompletionData: Get previous token 2: " + ( timestamp ( ) - start ) ) ;
442
441
}
443
442
@@ -449,7 +448,7 @@ namespace ts.Completions {
449
448
let isRightOfOpenTag = false ;
450
449
let isStartingCloseTag = false ;
451
450
452
- let location = getTouchingPropertyName ( sourceFile , position , /*includeJsDocComment*/ false ) ; // TODO: GH#15853
451
+ let location = getTouchingPropertyName ( sourceFile , position , insideJsDocTagTypeExpression ) ; // TODO: GH#15853
453
452
if ( contextToken ) {
454
453
// Bail out if this is a known invalid completion location
455
454
if ( isCompletionListBlocker ( contextToken ) ) {
@@ -553,14 +552,28 @@ namespace ts.Completions {
553
552
554
553
return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot : ( isRightOfDot || isRightOfOpenTag ) , requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords } ;
555
554
555
+ type JSDocTagWithTypeExpression = JSDocAugmentsTag | JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag ;
556
+
557
+ function isTagWithTypeExpression ( tag : JSDocTag ) : tag is JSDocTagWithTypeExpression {
558
+ switch ( tag . kind ) {
559
+ case SyntaxKind . JSDocAugmentsTag :
560
+ case SyntaxKind . JSDocParameterTag :
561
+ case SyntaxKind . JSDocPropertyTag :
562
+ case SyntaxKind . JSDocReturnTag :
563
+ case SyntaxKind . JSDocTypeTag :
564
+ case SyntaxKind . JSDocTypedefTag :
565
+ return true ;
566
+ }
567
+ }
568
+
556
569
function getTypeScriptMemberSymbols ( ) : void {
557
570
// Right of dot member completion list
558
571
isGlobalCompletion = false ;
559
572
isMemberCompletion = true ;
560
573
isNewIdentifierLocation = false ;
561
574
562
575
// Since this is qualified name check its a type node location
563
- const isTypeLocation = isPartOfTypeNode ( node . parent ) ;
576
+ const isTypeLocation = isPartOfTypeNode ( node . parent ) || insideJsDocTagTypeExpression ;
564
577
const isRhsOfImportDeclaration = isInRightSideOfInternalImportEqualsDeclaration ( node ) ;
565
578
if ( node . kind === SyntaxKind . Identifier || node . kind === SyntaxKind . QualifiedName || node . kind === SyntaxKind . PropertyAccessExpression ) {
566
579
let symbol = typeChecker . getSymbolAtLocation ( node ) ;
@@ -722,8 +735,9 @@ namespace ts.Completions {
722
735
return ! ! ( symbol . flags & SymbolFlags . Namespace ) ;
723
736
}
724
737
725
- if ( ! isContextTokenValueLocation ( contextToken ) &&
726
- ( isPartOfTypeNode ( location ) || isContextTokenTypeLocation ( contextToken ) ) ) {
738
+ if ( insideJsDocTagTypeExpression ||
739
+ ( ! isContextTokenValueLocation ( contextToken ) &&
740
+ ( isPartOfTypeNode ( location ) || isContextTokenTypeLocation ( contextToken ) ) ) ) {
727
741
// Its a type, but you can reach it by namespace.type as well
728
742
return symbolCanbeReferencedAtTypeLocation ( symbol ) ;
729
743
}
@@ -770,14 +784,16 @@ namespace ts.Completions {
770
784
symbol = typeChecker . getAliasedSymbol ( symbol ) ;
771
785
}
772
786
787
+ if ( symbol . flags & SymbolFlags . Type ) {
788
+ return true ;
789
+ }
790
+
773
791
if ( symbol . flags & ( SymbolFlags . ValueModule | SymbolFlags . NamespaceModule ) ) {
774
792
const exportedSymbols = typeChecker . getExportsOfModule ( symbol ) ;
775
793
// If the exported symbols contains type,
776
794
// symbol can be referenced at locations where type is allowed
777
795
return forEach ( exportedSymbols , symbolCanbeReferencedAtTypeLocation ) ;
778
796
}
779
-
780
- return ! ! ( symbol . flags & ( SymbolFlags . NamespaceModule | SymbolFlags . Type ) ) ;
781
797
}
782
798
783
799
/**
0 commit comments