@@ -57,7 +57,9 @@ namespace ts.Completions {
5757 */
5858 export enum CompletionSource {
5959 /** Completions that require `this.` insertion text */
60- ThisProperty = "ThisProperty/"
60+ ThisProperty = "ThisProperty/" ,
61+ /** Auto-import that comes attached to a class member snippet */
62+ ClassMemberSnippet = "ClassMemberSnippet/" ,
6163 }
6264
6365 const enum SymbolOriginInfoKind {
@@ -641,6 +643,7 @@ namespace ts.Completions {
641643 let replacementSpan = getReplacementSpanForContextToken ( replacementToken ) ;
642644 let data : CompletionEntryData | undefined ;
643645 let isSnippet : true | undefined ;
646+ let source = getSourceFromOrigin ( origin ) ;
644647 let sourceDisplay ;
645648 let hasAction ;
646649
@@ -702,7 +705,12 @@ namespace ts.Completions {
702705 preferences . includeCompletionsWithInsertText &&
703706 completionKind === CompletionKind . MemberLike &&
704707 isClassLikeMemberCompletion ( symbol , location ) ) {
705- ( { insertText, isSnippet } = getEntryForMemberCompletion ( host , program , options , preferences , name , symbol , location , contextToken ) ) ;
708+ let importAdder ;
709+ ( { insertText, isSnippet, importAdder } = getEntryForMemberCompletion ( host , program , options , preferences , name , symbol , location , contextToken ) ) ;
710+ if ( importAdder ?. hasFixes ( ) ) {
711+ hasAction = true ;
712+ source = CompletionSource . ClassMemberSnippet ;
713+ }
706714 }
707715
708716 const kind = SymbolDisplay . getSymbolKind ( typeChecker , symbol , location ) ;
@@ -758,7 +766,7 @@ namespace ts.Completions {
758766 kind,
759767 kindModifiers : SymbolDisplay . getSymbolModifiers ( typeChecker , symbol ) ,
760768 sortText,
761- source : getSourceFromOrigin ( origin ) ,
769+ source,
762770 hasAction : hasAction ? true : undefined ,
763771 isRecommended : isRecommendedCompletionMatch ( symbol , recommendedCompletion , typeChecker ) || undefined ,
764772 insertText,
@@ -828,7 +836,7 @@ namespace ts.Completions {
828836 symbol : Symbol ,
829837 location : Node ,
830838 contextToken : Node | undefined ,
831- ) : { insertText : string , isSnippet ?: true } {
839+ ) : { insertText : string , isSnippet ?: true , importAdder ?: codefix . ImportAdder } {
832840 const classLikeDeclaration = findAncestor ( location , isClassLike ) ;
833841 if ( ! classLikeDeclaration ) {
834842 return { insertText : name } ;
@@ -921,7 +929,7 @@ namespace ts.Completions {
921929 insertText = printer . printSnippetList ( ListFormat . MultiLine , factory . createNodeArray ( completionNodes ) , sourceFile ) ;
922930 }
923931
924- return { insertText, isSnippet } ;
932+ return { insertText, isSnippet, importAdder } ;
925933 }
926934
927935 function getPresentModifiers ( contextToken : Node ) : ModifierFlags {
@@ -1297,6 +1305,7 @@ namespace ts.Completions {
12971305 location : Node ;
12981306 origin : SymbolOriginInfo | SymbolOriginInfoExport | SymbolOriginInfoResolvedExport | undefined ;
12991307 previousToken : Node | undefined ;
1308+ contextToken : Node | undefined ;
13001309 readonly isJsxInitializer : IsJsxInitializer ;
13011310 readonly isTypeOnlyLocation : boolean ;
13021311 }
@@ -1312,11 +1321,13 @@ namespace ts.Completions {
13121321 if ( entryId . data ) {
13131322 const autoImport = getAutoImportSymbolFromCompletionEntryData ( entryId . name , entryId . data , program , host ) ;
13141323 if ( autoImport ) {
1324+ const { contextToken, previousToken } = getRelevantTokens ( position , sourceFile ) ;
13151325 return {
13161326 type : "symbol" ,
13171327 symbol : autoImport . symbol ,
13181328 location : getTouchingPropertyName ( sourceFile , position ) ,
1319- previousToken : findPrecedingToken ( position , sourceFile , /*startNode*/ undefined ) ! ,
1329+ previousToken,
1330+ contextToken,
13201331 isJsxInitializer : false ,
13211332 isTypeOnlyLocation : false ,
13221333 origin : autoImport . origin ,
@@ -1333,7 +1344,7 @@ namespace ts.Completions {
13331344 return { type : "request" , request : completionData } ;
13341345 }
13351346
1336- const { symbols, literals, location, completionKind, symbolToOriginInfoMap, previousToken, isJsxInitializer, isTypeOnlyLocation } = completionData ;
1347+ const { symbols, literals, location, completionKind, symbolToOriginInfoMap, contextToken , previousToken, isJsxInitializer, isTypeOnlyLocation } = completionData ;
13371348
13381349 const literal = find ( literals , l => completionNameForLiteral ( sourceFile , preferences , l ) === entryId . name ) ;
13391350 if ( literal !== undefined ) return { type : "literal" , literal } ;
@@ -1345,8 +1356,8 @@ namespace ts.Completions {
13451356 return firstDefined ( symbols , ( symbol , index ) : SymbolCompletion | undefined => {
13461357 const origin = symbolToOriginInfoMap [ index ] ;
13471358 const info = getCompletionEntryDisplayNameForSymbol ( symbol , getEmitScriptTarget ( compilerOptions ) , origin , completionKind , completionData . isJsxIdentifierExpected ) ;
1348- return info && info . name === entryId . name && getSourceFromOrigin ( origin ) === entryId . source
1349- ? { type : "symbol" as const , symbol, location, origin, previousToken, isJsxInitializer, isTypeOnlyLocation }
1359+ return info && info . name === entryId . name && ( entryId . source === CompletionSource . ClassMemberSnippet && symbol . flags & SymbolFlags . ClassMember || getSourceFromOrigin ( origin ) === entryId . source )
1360+ ? { type : "symbol" as const , symbol, location, origin, contextToken , previousToken, isJsxInitializer, isTypeOnlyLocation }
13501361 : undefined ;
13511362 } ) || { type : "none" } ;
13521363 }
@@ -1370,7 +1381,7 @@ namespace ts.Completions {
13701381 ) : CompletionEntryDetails | undefined {
13711382 const typeChecker = program . getTypeChecker ( ) ;
13721383 const compilerOptions = program . getCompilerOptions ( ) ;
1373- const { name } = entryId ;
1384+ const { name, source , data } = entryId ;
13741385
13751386 const contextToken = findPrecedingToken ( position , sourceFile ) ;
13761387 if ( isInString ( sourceFile , position , contextToken ) ) {
@@ -1396,8 +1407,8 @@ namespace ts.Completions {
13961407 }
13971408 }
13981409 case "symbol" : {
1399- const { symbol, location, origin, previousToken } = symbolCompletion ;
1400- const { codeActions, sourceDisplay } = getCompletionEntryCodeActionsAndSourceDisplay ( origin , symbol , program , host , compilerOptions , sourceFile , position , previousToken , formatContext , preferences , entryId . data ) ;
1410+ const { symbol, location, contextToken , origin, previousToken } = symbolCompletion ;
1411+ const { codeActions, sourceDisplay } = getCompletionEntryCodeActionsAndSourceDisplay ( name , location , contextToken , origin , symbol , program , host , compilerOptions , sourceFile , position , previousToken , formatContext , preferences , data , source ) ;
14011412 return createCompletionDetailsForSymbol ( symbol , typeChecker , sourceFile , location , cancellationToken , codeActions , sourceDisplay ) ; // TODO: GH#18217
14021413 }
14031414 case "literal" : {
@@ -1433,6 +1444,9 @@ namespace ts.Completions {
14331444 readonly sourceDisplay : SymbolDisplayPart [ ] | undefined ;
14341445 }
14351446 function getCompletionEntryCodeActionsAndSourceDisplay (
1447+ name : string ,
1448+ location : Node ,
1449+ contextToken : Node | undefined ,
14361450 origin : SymbolOriginInfo | SymbolOriginInfoExport | SymbolOriginInfoResolvedExport | undefined ,
14371451 symbol : Symbol ,
14381452 program : Program ,
@@ -1444,6 +1458,7 @@ namespace ts.Completions {
14441458 formatContext : formatting . FormatContext ,
14451459 preferences : UserPreferences ,
14461460 data : CompletionEntryData | undefined ,
1461+ source : string | undefined ,
14471462 ) : CodeActionsAndSourceDisplay {
14481463 if ( data ?. moduleSpecifier ) {
14491464 const { contextToken, previousToken } = getRelevantTokens ( position , sourceFile ) ;
@@ -1453,6 +1468,30 @@ namespace ts.Completions {
14531468 }
14541469 }
14551470
1471+ if ( source === CompletionSource . ClassMemberSnippet ) {
1472+ const { importAdder } = getEntryForMemberCompletion (
1473+ host ,
1474+ program ,
1475+ compilerOptions ,
1476+ preferences ,
1477+ name ,
1478+ symbol ,
1479+ location ,
1480+ contextToken ) ;
1481+ if ( importAdder ) {
1482+ const changes = textChanges . ChangeTracker . with (
1483+ { host, formatContext, preferences } ,
1484+ importAdder . writeFixes ) ;
1485+ return {
1486+ sourceDisplay : undefined ,
1487+ codeActions : [ {
1488+ changes,
1489+ description : diagnosticToString ( [ Diagnostics . Includes_imports_of_types_referenced_by_0 , name ] ) ,
1490+ } ] ,
1491+ } ;
1492+ }
1493+ }
1494+
14561495 if ( ! origin || ! ( originIsExport ( origin ) || originIsResolvedExport ( origin ) ) ) {
14571496 return { codeActions : undefined , sourceDisplay : undefined } ;
14581497 }
0 commit comments