@@ -29,8 +29,11 @@ namespace ts.SignatureHelp {
29
29
return undefined ;
30
30
}
31
31
32
- if ( shouldCarefullyCheckContext ( triggerReason ) ) {
33
- // In the middle of a string, don't provide signature help unless the user explicitly requested it.
32
+ // Only need to be careful if the user typed a character and signature help wasn't showing.
33
+ const shouldCarefullyCheckContext = ! ! triggerReason && triggerReason . kind === "characterTyped" ;
34
+
35
+ // Bail out quickly in the middle of a string or comment, don't provide signature help unless the user explicitly requested it.
36
+ if ( shouldCarefullyCheckContext ) {
34
37
if ( isInString ( sourceFile , position , startingToken ) || isInComment ( sourceFile , position ) ) {
35
38
return undefined ;
36
39
}
@@ -41,8 +44,8 @@ namespace ts.SignatureHelp {
41
44
42
45
cancellationToken . throwIfCancellationRequested ( ) ;
43
46
44
- // Semantic filtering of signature help
45
- const candidateInfo = getCandidateInfo ( argumentInfo , typeChecker ) ;
47
+ // Extra syntactic and semantic filtering of signature help
48
+ const candidateInfo = getCandidateInfo ( argumentInfo , typeChecker , sourceFile , startingToken , shouldCarefullyCheckContext ) ;
46
49
cancellationToken . throwIfCancellationRequested ( ) ;
47
50
48
51
if ( ! candidateInfo ) {
@@ -57,24 +60,57 @@ namespace ts.SignatureHelp {
57
60
return typeChecker . runWithCancellationToken ( cancellationToken , typeChecker => createSignatureHelpItems ( candidateInfo . candidates , candidateInfo . resolvedSignature , argumentInfo , sourceFile , typeChecker ) ) ;
58
61
}
59
62
60
- function shouldCarefullyCheckContext ( reason : SignatureHelpTriggerReason | undefined ) {
61
- // Only need to be careful if the user typed a character and signature help wasn't showing.
62
- return ! ! reason && reason . kind === "characterTyped" ;
63
- }
63
+ function getCandidateInfo (
64
+ argumentInfo : ArgumentListInfo , checker : TypeChecker , sourceFile : SourceFile , startingToken : Node , onlyUseSyntacticOwners : boolean ) :
65
+ { readonly candidates : ReadonlyArray < Signature > , readonly resolvedSignature : Signature } | undefined {
64
66
65
- function getCandidateInfo ( argumentInfo : ArgumentListInfo , checker : TypeChecker ) : { readonly candidates : ReadonlyArray < Signature > , readonly resolvedSignature : Signature } | undefined {
66
67
const { invocation } = argumentInfo ;
67
68
if ( invocation . kind === InvocationKind . Call ) {
69
+ if ( onlyUseSyntacticOwners ) {
70
+ if ( isCallOrNewExpression ( invocation . node ) ) {
71
+ const invocationChildren = invocation . node . getChildren ( sourceFile ) ;
72
+ switch ( startingToken . kind ) {
73
+ case SyntaxKind . OpenParenToken :
74
+ if ( ! contains ( invocationChildren , startingToken ) ) {
75
+ return undefined ;
76
+ }
77
+ break ;
78
+ case SyntaxKind . CommaToken :
79
+ const containingList = findContainingList ( startingToken ) ;
80
+ if ( ! containingList || ! contains ( invocationChildren , findContainingList ( startingToken ) ) ) {
81
+ return undefined ;
82
+ }
83
+ break ;
84
+ case SyntaxKind . LessThanToken :
85
+ if ( ! lessThanFollowsCalledExpression ( startingToken , sourceFile , invocation . node . expression ) ) {
86
+ return undefined ;
87
+ }
88
+ break ;
89
+ default :
90
+ return undefined ;
91
+ }
92
+ }
93
+ else {
94
+ return undefined ;
95
+ }
96
+ }
97
+
68
98
const candidates : Signature [ ] = [ ] ;
69
99
const resolvedSignature = checker . getResolvedSignature ( invocation . node , candidates , argumentInfo . argumentCount ) ! ; // TODO: GH#18217
70
100
return candidates . length === 0 ? undefined : { candidates, resolvedSignature } ;
71
101
}
72
- else {
102
+ else if ( invocation . kind === InvocationKind . TypeArgs ) {
103
+ if ( onlyUseSyntacticOwners && ! lessThanFollowsCalledExpression ( startingToken , sourceFile , invocation . called ) ) {
104
+ return undefined ;
105
+ }
73
106
const type = checker . getTypeAtLocation ( invocation . called ) ! ; // TODO: GH#18217
74
107
const signatures = isNewExpression ( invocation . called . parent ) ? type . getConstructSignatures ( ) : type . getCallSignatures ( ) ;
75
108
const candidates = signatures . filter ( candidate => ! ! candidate . typeParameters && candidate . typeParameters . length >= argumentInfo . argumentCount ) ;
76
109
return candidates . length === 0 ? undefined : { candidates, resolvedSignature : first ( candidates ) } ;
77
110
}
111
+ else {
112
+ Debug . assertNever ( invocation ) ;
113
+ }
78
114
}
79
115
80
116
function createJavaScriptSignatureHelpItems ( argumentInfo : ArgumentListInfo , program : Program , cancellationToken : CancellationToken ) : SignatureHelpItems | undefined {
@@ -107,6 +143,14 @@ namespace ts.SignatureHelp {
107
143
}
108
144
}
109
145
146
+ function lessThanFollowsCalledExpression ( startingToken : Node , sourceFile : SourceFile , calledExpression : Expression ) {
147
+ const precedingToken = Debug . assertDefined (
148
+ findPrecedingToken ( startingToken . getFullStart ( ) , sourceFile , startingToken . parent , /*excludeJsdoc*/ true )
149
+ ) ;
150
+
151
+ return rangeContainsRange ( calledExpression , precedingToken ) ;
152
+ }
153
+
110
154
export interface ArgumentInfoForCompletions {
111
155
readonly invocation : CallLikeExpression ;
112
156
readonly argumentIndex : number ;
0 commit comments