4
4
namespace ts . Completions {
5
5
export type Log = ( message : string ) => void ;
6
6
7
- interface SymbolOriginInfo {
7
+ type SymbolOriginInfo = { type : "this-type" } | SymbolOriginInfoExport ;
8
+ interface SymbolOriginInfoExport {
9
+ type : "export" ;
8
10
moduleSymbol : Symbol ;
9
11
isDefaultExport : boolean ;
10
12
}
@@ -170,11 +172,21 @@ namespace ts.Completions {
170
172
return undefined ;
171
173
}
172
174
const { name, needsConvertPropertyAccess } = info ;
173
- Debug . assert ( ! ( needsConvertPropertyAccess && ! propertyAccessToConvert ) ) ;
174
175
if ( needsConvertPropertyAccess && ! includeInsertTextCompletions ) {
175
176
return undefined ;
176
177
}
177
178
179
+ let insertText : string | undefined ;
180
+ let replacementSpan : TextSpan | undefined ;
181
+ if ( kind === CompletionKind . Global && origin && origin . type === "this-type" ) {
182
+ insertText = needsConvertPropertyAccess ? `this["${ name } "]` : `this.${ name } ` ;
183
+ }
184
+ else if ( needsConvertPropertyAccess ) {
185
+ // TODO: GH#20619 Use configured quote style
186
+ insertText = `["${ name } "]` ;
187
+ replacementSpan = createTextSpanFromBounds ( findChildOfKind ( propertyAccessToConvert ! , SyntaxKind . DotToken , sourceFile ) ! . getStart ( sourceFile ) , propertyAccessToConvert ! . name . end ) ;
188
+ }
189
+
178
190
// TODO(drosen): Right now we just permit *all* semantic meanings when calling
179
191
// 'getSymbolKind' which is permissible given that it is backwards compatible; but
180
192
// really we should consider passing the meaning for the node so that we don't report
@@ -189,13 +201,10 @@ namespace ts.Completions {
189
201
kindModifiers : SymbolDisplay . getSymbolModifiers ( symbol ) ,
190
202
sortText : "0" ,
191
203
source : getSourceFromOrigin ( origin ) ,
192
- // TODO: GH#20619 Use configured quote style
193
- insertText : needsConvertPropertyAccess ? `["${ name } "]` : undefined ,
194
- replacementSpan : needsConvertPropertyAccess
195
- ? createTextSpanFromBounds ( findChildOfKind ( propertyAccessToConvert , SyntaxKind . DotToken , sourceFile ) ! . getStart ( sourceFile ) , propertyAccessToConvert . name . end )
196
- : undefined ,
197
- hasAction : trueOrUndefined ( needsConvertPropertyAccess || origin !== undefined ) ,
204
+ hasAction : trueOrUndefined ( ! ! origin && origin . type === "export" ) ,
198
205
isRecommended : trueOrUndefined ( isRecommendedCompletionMatch ( symbol , recommendedCompletion , typeChecker ) ) ,
206
+ insertText,
207
+ replacementSpan,
199
208
} ;
200
209
}
201
210
@@ -210,7 +219,7 @@ namespace ts.Completions {
210
219
}
211
220
212
221
function getSourceFromOrigin ( origin : SymbolOriginInfo | undefined ) : string | undefined {
213
- return origin && stripQuotes ( origin . moduleSymbol . name ) ;
222
+ return origin && origin . type === "export" ? stripQuotes ( origin . moduleSymbol . name ) : undefined ;
214
223
}
215
224
216
225
function getCompletionEntriesFromSymbols (
@@ -504,7 +513,7 @@ namespace ts.Completions {
504
513
}
505
514
506
515
function getSymbolName ( symbol : Symbol , origin : SymbolOriginInfo | undefined , target : ScriptTarget ) : string {
507
- return origin && origin . isDefaultExport && symbol . escapedName === InternalSymbolName . Default
516
+ return origin && origin . type === "export" && origin . isDefaultExport && symbol . escapedName === InternalSymbolName . Default
508
517
// Name of "export default foo;" is "foo". Name of "export default 0" is the filename converted to camelCase.
509
518
? firstDefined ( symbol . declarations , d => isExportAssignment ( d ) && isIdentifier ( d . expression ) ? d . expression . text : undefined )
510
519
|| codefix . moduleSymbolToValidIdentifier ( origin . moduleSymbol , target )
@@ -590,13 +599,13 @@ namespace ts.Completions {
590
599
allSourceFiles : ReadonlyArray < SourceFile > ,
591
600
) : CodeActionsAndSourceDisplay {
592
601
const symbolOriginInfo = symbolToOriginInfoMap [ getSymbolId ( symbol ) ] ;
593
- return symbolOriginInfo
602
+ return symbolOriginInfo && symbolOriginInfo . type === "export"
594
603
? getCodeActionsAndSourceDisplayForImport ( symbolOriginInfo , symbol , program , checker , host , compilerOptions , sourceFile , previousToken , formatContext , getCanonicalFileName , allSourceFiles )
595
604
: { codeActions : undefined , sourceDisplay : undefined } ;
596
605
}
597
606
598
607
function getCodeActionsAndSourceDisplayForImport (
599
- symbolOriginInfo : SymbolOriginInfo ,
608
+ symbolOriginInfo : SymbolOriginInfoExport ,
600
609
symbol : Symbol ,
601
610
program : Program ,
602
611
checker : TypeChecker ,
@@ -1117,6 +1126,18 @@ namespace ts.Completions {
1117
1126
const symbolMeanings = SymbolFlags . Type | SymbolFlags . Value | SymbolFlags . Namespace | SymbolFlags . Alias ;
1118
1127
1119
1128
symbols = typeChecker . getSymbolsInScope ( scopeNode , symbolMeanings ) ;
1129
+
1130
+ // Need to insert 'this.' before properties of `this` type, so only do that if `includeInsertTextCompletions`
1131
+ if ( options . includeInsertTextCompletions && scopeNode . kind !== SyntaxKind . SourceFile ) {
1132
+ const thisType = typeChecker . tryGetThisTypeAt ( scopeNode ) ;
1133
+ if ( thisType ) {
1134
+ for ( const symbol of getPropertiesForCompletion ( thisType , typeChecker , /*isForAccess*/ true ) ) {
1135
+ symbolToOriginInfoMap [ getSymbolId ( symbol ) ] = { type : "this-type" } ;
1136
+ symbols . push ( symbol ) ;
1137
+ }
1138
+ }
1139
+ }
1140
+
1120
1141
if ( options . includeExternalModuleExports ) {
1121
1142
getSymbolsFromOtherSourceFileExports ( symbols , previousToken && isIdentifier ( previousToken ) ? previousToken . text : "" , target ) ;
1122
1143
}
@@ -1230,10 +1251,10 @@ namespace ts.Completions {
1230
1251
symbol = getLocalSymbolForExportDefault ( symbol ) || symbol ;
1231
1252
}
1232
1253
1233
- const origin : SymbolOriginInfo = { moduleSymbol, isDefaultExport } ;
1254
+ const origin : SymbolOriginInfo = { type : "export" , moduleSymbol, isDefaultExport } ;
1234
1255
if ( stringContainsCharactersInOrder ( getSymbolName ( symbol , origin , target ) . toLowerCase ( ) , tokenTextLowerCase ) ) {
1235
1256
symbols . push ( symbol ) ;
1236
- symbolToOriginInfoMap [ getSymbolId ( symbol ) ] = { moduleSymbol , isDefaultExport } ;
1257
+ symbolToOriginInfoMap [ getSymbolId ( symbol ) ] = origin ;
1237
1258
}
1238
1259
}
1239
1260
} ) ;
@@ -2072,13 +2093,13 @@ namespace ts.Completions {
2072
2093
if ( isIdentifierText ( name , target ) ) return validIdentiferResult ;
2073
2094
switch ( kind ) {
2074
2095
case CompletionKind . None :
2075
- case CompletionKind . Global :
2076
2096
case CompletionKind . MemberLike :
2077
2097
return undefined ;
2078
2098
case CompletionKind . ObjectPropertyDeclaration :
2079
2099
// TODO: GH#18169
2080
2100
return { name : JSON . stringify ( name ) , needsConvertPropertyAccess : false } ;
2081
2101
case CompletionKind . PropertyAccess :
2102
+ case CompletionKind . Global :
2082
2103
// Don't add a completion for a name starting with a space. See https://github.com/Microsoft/TypeScript/pull/20547
2083
2104
return name . charCodeAt ( 0 ) === CharacterCodes . space ? undefined : { name, needsConvertPropertyAccess : true } ;
2084
2105
case CompletionKind . String :
0 commit comments