@@ -29,9 +29,12 @@ namespace ts.SignatureHelp {
2929 return undefined ;
3030 }
3131
32- if ( shouldCarefullyCheckContext ( triggerReason ) ) {
33- // In the middle of a string, don't provide signature help unless the user explicitly requested it.
34- if ( isInString ( sourceFile , position , startingToken ) ) {
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 ) {
37+ if ( isInString ( sourceFile , position , startingToken ) || isInComment ( sourceFile , position ) ) {
3538 return undefined ;
3639 }
3740 }
@@ -41,8 +44,8 @@ namespace ts.SignatureHelp {
4144
4245 cancellationToken . throwIfCancellationRequested ( ) ;
4346
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 ) ;
4649 cancellationToken . throwIfCancellationRequested ( ) ;
4750
4851 if ( ! candidateInfo ) {
@@ -57,24 +60,57 @@ namespace ts.SignatureHelp {
5760 return typeChecker . runWithCancellationToken ( cancellationToken , typeChecker => createSignatureHelpItems ( candidateInfo . candidates , candidateInfo . resolvedSignature , argumentInfo , sourceFile , typeChecker ) ) ;
5861 }
5962
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 {
6466
65- function getCandidateInfo ( argumentInfo : ArgumentListInfo , checker : TypeChecker ) : { readonly candidates : ReadonlyArray < Signature > , readonly resolvedSignature : Signature } | undefined {
6667 const { invocation } = argumentInfo ;
6768 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+
6898 const candidates : Signature [ ] = [ ] ;
6999 const resolvedSignature = checker . getResolvedSignature ( invocation . node , candidates , argumentInfo . argumentCount ) ! ; // TODO: GH#18217
70100 return candidates . length === 0 ? undefined : { candidates, resolvedSignature } ;
71101 }
72- else {
102+ else if ( invocation . kind === InvocationKind . TypeArgs ) {
103+ if ( onlyUseSyntacticOwners && ! lessThanFollowsCalledExpression ( startingToken , sourceFile , invocation . called ) ) {
104+ return undefined ;
105+ }
73106 const type = checker . getTypeAtLocation ( invocation . called ) ! ; // TODO: GH#18217
74107 const signatures = isNewExpression ( invocation . called . parent ) ? type . getConstructSignatures ( ) : type . getCallSignatures ( ) ;
75108 const candidates = signatures . filter ( candidate => ! ! candidate . typeParameters && candidate . typeParameters . length >= argumentInfo . argumentCount ) ;
76109 return candidates . length === 0 ? undefined : { candidates, resolvedSignature : first ( candidates ) } ;
77110 }
111+ else {
112+ Debug . assertNever ( invocation ) ;
113+ }
78114 }
79115
80116 function createJavaScriptSignatureHelpItems ( argumentInfo : ArgumentListInfo , program : Program , cancellationToken : CancellationToken ) : SignatureHelpItems | undefined {
@@ -107,6 +143,14 @@ namespace ts.SignatureHelp {
107143 }
108144 }
109145
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+
110154 export interface ArgumentInfoForCompletions {
111155 readonly invocation : CallLikeExpression ;
112156 readonly argumentIndex : number ;
0 commit comments