@@ -24,7 +24,7 @@ namespace ts.Completions {
24
24
return undefined ;
25
25
}
26
26
27
- const { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, request, keywordFilters } = completionData ;
27
+ const { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral , isNewIdentifierLocation, location, request, keywordFilters } = completionData ;
28
28
29
29
if ( sourceFile . languageVariant === LanguageVariant . JSX &&
30
30
location && location . parent && location . parent . kind === SyntaxKind . JsxClosingElement ) {
@@ -56,15 +56,15 @@ namespace ts.Completions {
56
56
const entries : CompletionEntry [ ] = [ ] ;
57
57
58
58
if ( isSourceFileJavaScript ( sourceFile ) ) {
59
- const uniqueNames = getCompletionEntriesFromSymbols ( symbols , entries , location , /*performCharacterChecks*/ true , typeChecker , compilerOptions . target , log ) ;
59
+ const uniqueNames = getCompletionEntriesFromSymbols ( symbols , entries , location , /*performCharacterChecks*/ true , typeChecker , compilerOptions . target , log , allowStringLiteral ) ;
60
60
getJavaScriptCompletionEntries ( sourceFile , location . pos , uniqueNames , compilerOptions . target , entries ) ;
61
61
}
62
62
else {
63
63
if ( ( ! symbols || symbols . length === 0 ) && keywordFilters === KeywordCompletionFilters . None ) {
64
64
return undefined ;
65
65
}
66
66
67
- getCompletionEntriesFromSymbols ( symbols , entries , location , /*performCharacterChecks*/ true , typeChecker , compilerOptions . target , log ) ;
67
+ getCompletionEntriesFromSymbols ( symbols , entries , location , /*performCharacterChecks*/ true , typeChecker , compilerOptions . target , log , allowStringLiteral ) ;
68
68
}
69
69
70
70
// TODO add filter for keyword based on type/value/namespace and also location
@@ -97,7 +97,7 @@ namespace ts.Completions {
97
97
}
98
98
99
99
uniqueNames . set ( realName , true ) ;
100
- const displayName = getCompletionEntryDisplayName ( realName , target , /*performCharacterChecks*/ true ) ;
100
+ const displayName = getCompletionEntryDisplayName ( realName , target , /*performCharacterChecks*/ true , /*allowStringLiteral*/ false ) ;
101
101
if ( displayName ) {
102
102
entries . push ( {
103
103
name : displayName ,
@@ -109,11 +109,11 @@ namespace ts.Completions {
109
109
} ) ;
110
110
}
111
111
112
- function createCompletionEntry ( symbol : Symbol , location : Node , performCharacterChecks : boolean , typeChecker : TypeChecker , target : ScriptTarget ) : CompletionEntry {
112
+ function createCompletionEntry ( symbol : Symbol , location : Node , performCharacterChecks : boolean , typeChecker : TypeChecker , target : ScriptTarget , allowStringLiteral : boolean ) : CompletionEntry {
113
113
// Try to get a valid display name for this symbol, if we could not find one, then ignore it.
114
114
// We would like to only show things that can be added after a dot, so for instance numeric properties can
115
115
// not be accessed with a dot (a.1 <- invalid)
116
- const displayName = getCompletionEntryDisplayNameForSymbol ( symbol , target , performCharacterChecks ) ;
116
+ const displayName = getCompletionEntryDisplayNameForSymbol ( symbol , target , performCharacterChecks , allowStringLiteral ) ;
117
117
if ( ! displayName ) {
118
118
return undefined ;
119
119
}
@@ -134,12 +134,12 @@ namespace ts.Completions {
134
134
} ;
135
135
}
136
136
137
- function getCompletionEntriesFromSymbols ( symbols : Symbol [ ] , entries : Push < CompletionEntry > , location : Node , performCharacterChecks : boolean , typeChecker : TypeChecker , target : ScriptTarget , log : Log ) : Map < true > {
137
+ function getCompletionEntriesFromSymbols ( symbols : Symbol [ ] , entries : Push < CompletionEntry > , location : Node , performCharacterChecks : boolean , typeChecker : TypeChecker , target : ScriptTarget , log : Log , allowStringLiteral : boolean ) : Map < true > {
138
138
const start = timestamp ( ) ;
139
139
const uniqueNames = createMap < true > ( ) ;
140
140
if ( symbols ) {
141
141
for ( const symbol of symbols ) {
142
- const entry = createCompletionEntry ( symbol , location , performCharacterChecks , typeChecker , target ) ;
142
+ const entry = createCompletionEntry ( symbol , location , performCharacterChecks , typeChecker , target , allowStringLiteral ) ;
143
143
if ( entry ) {
144
144
const id = entry . name ;
145
145
if ( ! uniqueNames . has ( id ) ) {
@@ -224,7 +224,7 @@ namespace ts.Completions {
224
224
const type = typeChecker . getContextualType ( ( < ObjectLiteralExpression > element . parent ) ) ;
225
225
const entries : CompletionEntry [ ] = [ ] ;
226
226
if ( type ) {
227
- getCompletionEntriesFromSymbols ( type . getApparentProperties ( ) , entries , element , /*performCharacterChecks*/ false , typeChecker , target , log ) ;
227
+ getCompletionEntriesFromSymbols ( type . getApparentProperties ( ) , entries , element , /*performCharacterChecks*/ false , typeChecker , target , log , /*allowStringLiteral*/ true ) ;
228
228
if ( entries . length ) {
229
229
return { isGlobalCompletion : false , isMemberCompletion : true , isNewIdentifierLocation : true , entries } ;
230
230
}
@@ -253,7 +253,7 @@ namespace ts.Completions {
253
253
const type = typeChecker . getTypeAtLocation ( node . expression ) ;
254
254
const entries : CompletionEntry [ ] = [ ] ;
255
255
if ( type ) {
256
- getCompletionEntriesFromSymbols ( type . getApparentProperties ( ) , entries , node , /*performCharacterChecks*/ false , typeChecker , target , log ) ;
256
+ getCompletionEntriesFromSymbols ( type . getApparentProperties ( ) , entries , node , /*performCharacterChecks*/ false , typeChecker , target , log , /*allowStringLiteral*/ true ) ;
257
257
if ( entries . length ) {
258
258
return { isGlobalCompletion : false , isMemberCompletion : true , isNewIdentifierLocation : true , entries } ;
259
259
}
@@ -302,13 +302,13 @@ namespace ts.Completions {
302
302
// Compute all the completion symbols again.
303
303
const completionData = getCompletionData ( typeChecker , log , sourceFile , position ) ;
304
304
if ( completionData ) {
305
- const { symbols, location } = completionData ;
305
+ const { symbols, location, allowStringLiteral } = completionData ;
306
306
307
307
// Find the symbol with the matching entry name.
308
308
// We don't need to perform character checks here because we're only comparing the
309
309
// name against 'entryName' (which is known to be good), not building a new
310
310
// completion entry.
311
- const symbol = forEach ( symbols , s => getCompletionEntryDisplayNameForSymbol ( s , compilerOptions . target , /*performCharacterChecks*/ false ) === entryName ? s : undefined ) ;
311
+ const symbol = forEach ( symbols , s => getCompletionEntryDisplayNameForSymbol ( s , compilerOptions . target , /*performCharacterChecks*/ false , allowStringLiteral ) === entryName ? s : undefined ) ;
312
312
313
313
if ( symbol ) {
314
314
const { displayParts, documentation, symbolKind, tags } = SymbolDisplay . getSymbolDisplayPartsDocumentationAndSymbolKind ( typeChecker , symbol , sourceFile , location , location , SemanticMeaning . All ) ;
@@ -345,17 +345,22 @@ namespace ts.Completions {
345
345
export function getCompletionEntrySymbol ( typeChecker : TypeChecker , log : ( message : string ) => void , compilerOptions : CompilerOptions , sourceFile : SourceFile , position : number , entryName : string ) : Symbol | undefined {
346
346
// Compute all the completion symbols again.
347
347
const completionData = getCompletionData ( typeChecker , log , sourceFile , position ) ;
348
+ if ( ! completionData ) {
349
+ return undefined ;
350
+ }
351
+ const { symbols, allowStringLiteral } = completionData ;
348
352
// Find the symbol with the matching entry name.
349
353
// We don't need to perform character checks here because we're only comparing the
350
354
// name against 'entryName' (which is known to be good), not building a new
351
355
// completion entry.
352
- return completionData && forEach ( completionData . symbols , s => getCompletionEntryDisplayNameForSymbol ( s , compilerOptions . target , /*performCharacterChecks*/ false ) === entryName ? s : undefined ) ;
356
+ return forEach ( symbols , s => getCompletionEntryDisplayNameForSymbol ( s , compilerOptions . target , /*performCharacterChecks*/ false , allowStringLiteral ) === entryName ? s : undefined ) ;
353
357
}
354
358
355
359
interface CompletionData {
356
360
symbols : Symbol [ ] ;
357
361
isGlobalCompletion : boolean ;
358
362
isMemberCompletion : boolean ;
363
+ allowStringLiteral : boolean ;
359
364
isNewIdentifierLocation : boolean ;
360
365
location : Node ;
361
366
isRightOfDot : boolean ;
@@ -436,7 +441,7 @@ namespace ts.Completions {
436
441
}
437
442
438
443
if ( request ) {
439
- return { symbols : undefined , isGlobalCompletion : false , isMemberCompletion : false , isNewIdentifierLocation : false , location : undefined , isRightOfDot : false , request, keywordFilters : KeywordCompletionFilters . None } ;
444
+ return { symbols : undefined , isGlobalCompletion : false , isMemberCompletion : false , allowStringLiteral : false , isNewIdentifierLocation : false , location : undefined , isRightOfDot : false , request, keywordFilters : KeywordCompletionFilters . None } ;
440
445
}
441
446
442
447
if ( ! insideJsDocTagTypeExpression ) {
@@ -534,6 +539,7 @@ namespace ts.Completions {
534
539
const semanticStart = timestamp ( ) ;
535
540
let isGlobalCompletion = false ;
536
541
let isMemberCompletion : boolean ;
542
+ let allowStringLiteral = false ;
537
543
let isNewIdentifierLocation : boolean ;
538
544
let keywordFilters = KeywordCompletionFilters . None ;
539
545
let symbols : Symbol [ ] = [ ] ;
@@ -573,7 +579,7 @@ namespace ts.Completions {
573
579
574
580
log ( "getCompletionData: Semantic work: " + ( timestamp ( ) - semanticStart ) ) ;
575
581
576
- return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot : ( isRightOfDot || isRightOfOpenTag ) , request, keywordFilters } ;
582
+ return { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral , isNewIdentifierLocation, location, isRightOfDot : ( isRightOfDot || isRightOfOpenTag ) , request, keywordFilters } ;
577
583
578
584
type JSDocTagWithTypeExpression = JSDocAugmentsTag | JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag ;
579
585
@@ -961,6 +967,7 @@ namespace ts.Completions {
961
967
function tryGetObjectLikeCompletionSymbols ( objectLikeContainer : ObjectLiteralExpression | ObjectBindingPattern ) : boolean {
962
968
// We're looking up possible property names from contextual/inferred/declared type.
963
969
isMemberCompletion = true ;
970
+ allowStringLiteral = true ;
964
971
965
972
let typeMembers : Symbol [ ] ;
966
973
let existingMembers : ReadonlyArray < Declaration > ;
@@ -1609,7 +1616,7 @@ namespace ts.Completions {
1609
1616
*
1610
1617
* @return undefined if the name is of external module
1611
1618
*/
1612
- function getCompletionEntryDisplayNameForSymbol ( symbol : Symbol , target : ScriptTarget , performCharacterChecks : boolean ) : string | undefined {
1619
+ function getCompletionEntryDisplayNameForSymbol ( symbol : Symbol , target : ScriptTarget , performCharacterChecks : boolean , allowStringLiteral : boolean ) : string | undefined {
1613
1620
const name = symbol . name ;
1614
1621
if ( ! name ) return undefined ;
1615
1622
@@ -1623,20 +1630,21 @@ namespace ts.Completions {
1623
1630
}
1624
1631
}
1625
1632
1626
- return getCompletionEntryDisplayName ( name , target , performCharacterChecks ) ;
1633
+ return getCompletionEntryDisplayName ( name , target , performCharacterChecks , allowStringLiteral ) ;
1627
1634
}
1628
1635
1629
1636
/**
1630
1637
* Get a displayName from a given for completion list, performing any necessary quotes stripping
1631
1638
* and checking whether the name is valid identifier name.
1632
1639
*/
1633
- function getCompletionEntryDisplayName ( name : string , target : ScriptTarget , performCharacterChecks : boolean ) : string {
1640
+ function getCompletionEntryDisplayName ( name : string , target : ScriptTarget , performCharacterChecks : boolean , allowStringLiteral : boolean ) : string {
1634
1641
// If the user entered name for the symbol was quoted, removing the quotes is not enough, as the name could be an
1635
1642
// invalid identifier name. We need to check if whatever was inside the quotes is actually a valid identifier name.
1636
1643
// e.g "b a" is valid quoted name but when we strip off the quotes, it is invalid.
1637
1644
// We, thus, need to check if whatever was inside the quotes is actually a valid identifier name.
1638
1645
if ( performCharacterChecks && ! isIdentifierText ( name , target ) ) {
1639
- return undefined ;
1646
+ // TODO: GH#18169
1647
+ return allowStringLiteral ? JSON . stringify ( name ) : undefined ;
1640
1648
}
1641
1649
1642
1650
return name ;
0 commit comments