@@ -366,16 +366,16 @@ namespace ts.Completions {
366
366
let request : Request | undefined ;
367
367
368
368
let start = timestamp ( ) ;
369
- const currentToken = getTokenAtPosition ( sourceFile , position , /*includeJsDocComment*/ false ) ;
369
+ let currentToken = getTokenAtPosition ( sourceFile , position , /*includeJsDocComment*/ false ) ;
370
370
// We will check for jsdoc comments with insideComment and getJsDocTagAtPosition. (TODO: that seems rather inefficient to check the same thing so many times.)
371
-
372
371
log ( "getCompletionData: Get current token: " + ( timestamp ( ) - start ) ) ;
373
372
374
373
start = timestamp ( ) ;
375
374
// Completion not allowed inside comments, bail out if this is the case
376
375
const insideComment = isInComment ( sourceFile , position , currentToken ) ;
377
376
log ( "getCompletionData: Is inside comment: " + ( timestamp ( ) - start ) ) ;
378
377
378
+ let insideJsDocTagTypeExpression = false ;
379
379
if ( insideComment ) {
380
380
if ( hasDocComment ( sourceFile , position ) ) {
381
381
if ( sourceFile . text . charCodeAt ( position - 1 ) === CharacterCodes . at ) {
@@ -410,33 +410,31 @@ namespace ts.Completions {
410
410
// Completion should work inside certain JsDoc tags. For example:
411
411
// /** @type {number | string } */
412
412
// Completion should work in the brackets
413
- let insideJsDocTagExpression = false ;
414
413
const tag = getJsDocTagAtPosition ( currentToken , position ) ;
415
414
if ( tag ) {
416
415
if ( tag . tagName . pos <= position && position <= tag . tagName . end ) {
417
416
request = { kind : "JsDocTagName" } ;
418
417
}
419
-
420
- switch ( tag . kind ) {
421
- case SyntaxKind . JSDocTypeTag :
422
- case SyntaxKind . JSDocParameterTag :
423
- case SyntaxKind . JSDocReturnTag :
424
- const tagWithExpression = < JSDocTypeTag | JSDocParameterTag | JSDocReturnTag > tag ;
425
- if ( tagWithExpression . typeExpression && tagWithExpression . typeExpression . pos < position && position < tagWithExpression . typeExpression . end ) {
426
- insideJsDocTagExpression = true ;
427
- }
428
- else if ( isJSDocParameterTag ( tag ) && ( nodeIsMissing ( tag . name ) || tag . name . pos <= position && position <= tag . name . end ) ) {
429
- request = { kind : "JsDocParameterName" , tag } ;
430
- }
431
- break ;
418
+ if ( isTagWithTypeExpression ( tag ) && tag . typeExpression ) {
419
+ currentToken = getTokenAtPosition ( sourceFile , position , /*includeJsDocComment*/ true ) ;
420
+ if ( ! currentToken ||
421
+ ( ! isDeclarationName ( currentToken ) &&
422
+ ( currentToken . parent . kind !== SyntaxKind . JSDocPropertyTag ||
423
+ ( < JSDocPropertyTag > currentToken . parent ) . name !== currentToken ) ) ) {
424
+ // Use as type location if inside tag's type expression
425
+ insideJsDocTagTypeExpression = isCurrentlyEditingNode ( tag . typeExpression ) ;
426
+ }
427
+ }
428
+ if ( isJSDocParameterTag ( tag ) && ( nodeIsMissing ( tag . name ) || tag . name . pos <= position && position <= tag . name . end ) ) {
429
+ request = { kind : "JsDocParameterName" , tag } ;
432
430
}
433
431
}
434
432
435
433
if ( request ) {
436
434
return { symbols : undefined , isGlobalCompletion : false , isMemberCompletion : false , isNewIdentifierLocation : false , location : undefined , isRightOfDot : false , request, hasFilteredClassMemberKeywords : false } ;
437
435
}
438
436
439
- if ( ! insideJsDocTagExpression ) {
437
+ if ( ! insideJsDocTagTypeExpression ) {
440
438
// Proceed if the current position is in jsDoc tag expression; otherwise it is a normal
441
439
// comment or the plain text part of a jsDoc comment, so no completion should be available
442
440
log ( "Returning an empty list because completion was inside a regular comment or plain text part of a JsDoc comment." ) ;
@@ -445,7 +443,7 @@ namespace ts.Completions {
445
443
}
446
444
447
445
start = timestamp ( ) ;
448
- const previousToken = findPrecedingToken ( position , sourceFile , /*startNode*/ undefined , /*includeJsDoc*/ true ) ;
446
+ const previousToken = findPrecedingToken ( position , sourceFile , /*startNode*/ undefined , insideJsDocTagTypeExpression ) ;
449
447
log ( "getCompletionData: Get previous token 1: " + ( timestamp ( ) - start ) ) ;
450
448
451
449
// The decision to provide completion depends on the contextToken, which is determined through the previousToken.
@@ -456,7 +454,7 @@ namespace ts.Completions {
456
454
// Skip this partial identifier and adjust the contextToken to the token that precedes it.
457
455
if ( contextToken && position <= contextToken . end && isWord ( contextToken . kind ) ) {
458
456
const start = timestamp ( ) ;
459
- contextToken = findPrecedingToken ( contextToken . getFullStart ( ) , sourceFile , /*startNode*/ undefined , /*includeJsDoc*/ true ) ;
457
+ contextToken = findPrecedingToken ( contextToken . getFullStart ( ) , sourceFile , /*startNode*/ undefined , insideJsDocTagTypeExpression ) ;
460
458
log ( "getCompletionData: Get previous token 2: " + ( timestamp ( ) - start ) ) ;
461
459
}
462
460
@@ -468,7 +466,7 @@ namespace ts.Completions {
468
466
let isRightOfOpenTag = false ;
469
467
let isStartingCloseTag = false ;
470
468
471
- let location = getTouchingPropertyName ( sourceFile , position , /*includeJsDocComment*/ false ) ; // TODO: GH#15853
469
+ let location = getTouchingPropertyName ( sourceFile , position , insideJsDocTagTypeExpression ) ; // TODO: GH#15853
472
470
if ( contextToken ) {
473
471
// Bail out if this is a known invalid completion location
474
472
if ( isCompletionListBlocker ( contextToken ) ) {
@@ -572,14 +570,28 @@ namespace ts.Completions {
572
570
573
571
return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot : ( isRightOfDot || isRightOfOpenTag ) , request, hasFilteredClassMemberKeywords } ;
574
572
573
+ type JSDocTagWithTypeExpression = JSDocAugmentsTag | JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag ;
574
+
575
+ function isTagWithTypeExpression ( tag : JSDocTag ) : tag is JSDocTagWithTypeExpression {
576
+ switch ( tag . kind ) {
577
+ case SyntaxKind . JSDocAugmentsTag :
578
+ case SyntaxKind . JSDocParameterTag :
579
+ case SyntaxKind . JSDocPropertyTag :
580
+ case SyntaxKind . JSDocReturnTag :
581
+ case SyntaxKind . JSDocTypeTag :
582
+ case SyntaxKind . JSDocTypedefTag :
583
+ return true ;
584
+ }
585
+ }
586
+
575
587
function getTypeScriptMemberSymbols ( ) : void {
576
588
// Right of dot member completion list
577
589
isGlobalCompletion = false ;
578
590
isMemberCompletion = true ;
579
591
isNewIdentifierLocation = false ;
580
592
581
593
// Since this is qualified name check its a type node location
582
- const isTypeLocation = isPartOfTypeNode ( node . parent ) ;
594
+ const isTypeLocation = isPartOfTypeNode ( node . parent ) || insideJsDocTagTypeExpression ;
583
595
const isRhsOfImportDeclaration = isInRightSideOfInternalImportEqualsDeclaration ( node ) ;
584
596
if ( node . kind === SyntaxKind . Identifier || node . kind === SyntaxKind . QualifiedName || node . kind === SyntaxKind . PropertyAccessExpression ) {
585
597
let symbol = typeChecker . getSymbolAtLocation ( node ) ;
@@ -741,8 +753,9 @@ namespace ts.Completions {
741
753
return ! ! ( symbol . flags & SymbolFlags . Namespace ) ;
742
754
}
743
755
744
- if ( ! isContextTokenValueLocation ( contextToken ) &&
745
- ( isPartOfTypeNode ( location ) || isContextTokenTypeLocation ( contextToken ) ) ) {
756
+ if ( insideJsDocTagTypeExpression ||
757
+ ( ! isContextTokenValueLocation ( contextToken ) &&
758
+ ( isPartOfTypeNode ( location ) || isContextTokenTypeLocation ( contextToken ) ) ) ) {
746
759
// Its a type, but you can reach it by namespace.type as well
747
760
return symbolCanbeReferencedAtTypeLocation ( symbol ) ;
748
761
}
@@ -789,14 +802,16 @@ namespace ts.Completions {
789
802
symbol = typeChecker . getAliasedSymbol ( symbol ) ;
790
803
}
791
804
805
+ if ( symbol . flags & SymbolFlags . Type ) {
806
+ return true ;
807
+ }
808
+
792
809
if ( symbol . flags & ( SymbolFlags . ValueModule | SymbolFlags . NamespaceModule ) ) {
793
810
const exportedSymbols = typeChecker . getExportsOfModule ( symbol ) ;
794
811
// If the exported symbols contains type,
795
812
// symbol can be referenced at locations where type is allowed
796
813
return forEach ( exportedSymbols , symbolCanbeReferencedAtTypeLocation ) ;
797
814
}
798
-
799
- return ! ! ( symbol . flags & ( SymbolFlags . NamespaceModule | SymbolFlags . Type ) ) ;
800
815
}
801
816
802
817
/**
0 commit comments