@@ -36,15 +36,15 @@ namespace ts.Completions {
3636 ) : CompletionInfo | undefined {
3737 if ( isInReferenceComment ( sourceFile , position ) ) {
3838 const entries = PathCompletions . getTripleSlashReferenceCompletion ( sourceFile , position , compilerOptions , host ) ;
39- return entries && pathCompletionsInfo ( entries ) ;
39+ return entries && convertPathCompletions ( entries ) ;
4040 }
4141
4242 const contextToken = findPrecedingToken ( position , sourceFile ) ;
4343
4444 if ( isInString ( sourceFile , position , contextToken ) ) {
4545 return ! contextToken || ! isStringLiteral ( contextToken ) && ! isNoSubstitutionTemplateLiteral ( contextToken )
4646 ? undefined
47- : getStringLiteralCompletionEntries ( sourceFile , contextToken , position , typeChecker , compilerOptions , host , log ) ;
47+ : convertStringLiteralCompletions ( getStringLiteralCompletionEntries ( sourceFile , contextToken , position , typeChecker , compilerOptions , host ) , sourceFile , typeChecker , log ) ;
4848 }
4949
5050 if ( contextToken && isBreakOrContinueStatement ( contextToken . parent )
@@ -73,6 +73,34 @@ namespace ts.Completions {
7373 }
7474 }
7575
76+ function convertStringLiteralCompletions ( completion : StringLiteralCompletion | undefined , sourceFile : SourceFile , checker : TypeChecker , log : Log ) : CompletionInfo | undefined {
77+ if ( completion === undefined ) {
78+ return undefined ;
79+ }
80+ switch ( completion . kind ) {
81+ case StringLiteralCompletionKind . Paths :
82+ return convertPathCompletions ( completion . paths ) ;
83+ case StringLiteralCompletionKind . Properties : {
84+ const entries : CompletionEntry [ ] = [ ] ;
85+ getCompletionEntriesFromSymbols ( completion . symbols , entries , sourceFile , sourceFile , checker , ScriptTarget . ESNext , log , CompletionKind . String ) ; // Target will not be used, so arbitrary
86+ return { isGlobalCompletion : false , isMemberCompletion : true , isNewIdentifierLocation : true , entries } ;
87+ }
88+ case StringLiteralCompletionKind . Types : {
89+ const entries = completion . types . map ( type => ( { name : type . value , kindModifiers : ScriptElementKindModifier . none , kind : ScriptElementKind . variableElement , sortText : "0" } ) ) ;
90+ return { isGlobalCompletion : false , isMemberCompletion : false , isNewIdentifierLocation : true , entries } ;
91+ }
92+ default :
93+ return Debug . assertNever ( completion ) ;
94+ }
95+ }
96+
97+ function convertPathCompletions ( pathCompletions : ReadonlyArray < PathCompletions . PathCompletion > ) : CompletionInfo {
98+ const isGlobalCompletion = false ; // We don't want the editor to offer any other completions, such as snippets, inside a comment.
99+ const isNewIdentifierLocation = true ; // The user may type in a path that doesn't yet exist, creating a "new identifier" with respect to the collection of identifiers the server is aware of.
100+ const entries = pathCompletions . map ( ( { name, kind, span } ) => ( { name, kind, kindModifiers : ScriptElementKindModifier . none , sortText : "0" , replacementSpan : span } ) ) ;
101+ return { isGlobalCompletion, isMemberCompletion : false , isNewIdentifierLocation, entries } ;
102+ }
103+
76104 function jsdocCompletionInfo ( entries : CompletionEntry [ ] ) : CompletionInfo {
77105 return { isGlobalCompletion : false , isMemberCompletion : false , isNewIdentifierLocation : false , entries } ;
78106 }
@@ -294,7 +322,38 @@ namespace ts.Completions {
294322 }
295323 }
296324
297- function getStringLiteralCompletionEntries ( sourceFile : SourceFile , node : StringLiteralLike , position : number , typeChecker : TypeChecker , compilerOptions : CompilerOptions , host : LanguageServiceHost , log : Log ) : CompletionInfo | undefined {
325+ function getLabelStatementCompletions ( node : Node ) : CompletionEntry [ ] {
326+ const entries : CompletionEntry [ ] = [ ] ;
327+ const uniques = createMap < true > ( ) ;
328+ let current = node ;
329+
330+ while ( current ) {
331+ if ( isFunctionLike ( current ) ) {
332+ break ;
333+ }
334+ if ( isLabeledStatement ( current ) ) {
335+ const name = current . label . text ;
336+ if ( ! uniques . has ( name ) ) {
337+ uniques . set ( name , true ) ;
338+ entries . push ( {
339+ name,
340+ kindModifiers : ScriptElementKindModifier . none ,
341+ kind : ScriptElementKind . label ,
342+ sortText : "0"
343+ } ) ;
344+ }
345+ }
346+ current = current . parent ;
347+ }
348+ return entries ;
349+ }
350+
351+ const enum StringLiteralCompletionKind { Paths , Properties , Types }
352+ type StringLiteralCompletion =
353+ | { readonly kind : StringLiteralCompletionKind . Paths , readonly paths : ReadonlyArray < PathCompletions . PathCompletion > }
354+ | { readonly kind : StringLiteralCompletionKind . Properties , readonly symbols : ReadonlyArray < Symbol > }
355+ | { readonly kind : StringLiteralCompletionKind . Types , readonly types : ReadonlyArray < StringLiteralType > } ;
356+ function getStringLiteralCompletionEntries ( sourceFile : SourceFile , node : StringLiteralLike , position : number , typeChecker : TypeChecker , compilerOptions : CompilerOptions , host : LanguageServiceHost ) : StringLiteralCompletion | undefined {
298357 switch ( node . parent . kind ) {
299358 case SyntaxKind . LiteralType :
300359 switch ( node . parent . parent . kind ) {
@@ -308,15 +367,13 @@ namespace ts.Completions {
308367 // bar: string;
309368 // }
310369 // let x: Foo["/*completion position*/"]
311- const type = typeChecker . getTypeFromTypeNode ( ( node . parent . parent as IndexedAccessTypeNode ) . objectType ) ;
312- return getStringLiteralCompletionEntriesFromElementAccessOrIndexedAccess ( node , sourceFile , type , typeChecker , compilerOptions . target , log ) ;
370+ return { kind : StringLiteralCompletionKind . Properties , symbols : typeChecker . getTypeFromTypeNode ( ( node . parent . parent as IndexedAccessTypeNode ) . objectType ) . getApparentProperties ( ) } ;
313371 default :
314372 return undefined ;
315373 }
316374
317375 case SyntaxKind . PropertyAssignment :
318- if ( node . parent . parent . kind === SyntaxKind . ObjectLiteralExpression &&
319- ( < PropertyAssignment > node . parent ) . name === node ) {
376+ if ( isObjectLiteralExpression ( node . parent . parent ) && ( < PropertyAssignment > node . parent ) . name === node ) {
320377 // Get quoted name of properties of the object literal expression
321378 // i.e. interface ConfigFiles {
322379 // 'jspm:dev': string
@@ -329,7 +386,8 @@ namespace ts.Completions {
329386 // foo({
330387 // '/*completion position*/'
331388 // });
332- return getStringLiteralCompletionEntriesFromPropertyAssignment ( < PropertyAssignment > node . parent , sourceFile , typeChecker , compilerOptions . target , log ) ;
389+ const type = typeChecker . getContextualType ( node . parent . parent ) ;
390+ return { kind : StringLiteralCompletionKind . Properties , symbols : type && type . getApparentProperties ( ) } ;
333391 }
334392 return fromContextualType ( ) ;
335393
@@ -342,10 +400,9 @@ namespace ts.Completions {
342400 // }
343401 // let a: A;
344402 // a['/*completion position*/']
345- const type = typeChecker . getTypeAtLocation ( expression ) ;
346- return getStringLiteralCompletionEntriesFromElementAccessOrIndexedAccess ( node , sourceFile , type , typeChecker , compilerOptions . target , log ) ;
403+ return { kind : StringLiteralCompletionKind . Properties , symbols : typeChecker . getTypeAtLocation ( expression ) . getApparentProperties ( ) } ;
347404 }
348- break ;
405+ return undefined ;
349406 }
350407
351408 case SyntaxKind . CallExpression :
@@ -355,9 +412,15 @@ namespace ts.Completions {
355412 // Get string literal completions from specialized signatures of the target
356413 // i.e. declare function f(a: 'A');
357414 // f("/*completion position*/")
358- return argumentInfo ? getStringLiteralCompletionEntriesFromCallExpression ( argumentInfo , typeChecker ) : fromContextualType ( ) ;
415+ if ( argumentInfo ) {
416+ const candidates : Signature [ ] = [ ] ;
417+ typeChecker . getResolvedSignature ( argumentInfo . invocation , candidates , argumentInfo . argumentCount ) ;
418+ const uniques = createMap < true > ( ) ;
419+ return { kind : StringLiteralCompletionKind . Types , types : flatMap ( candidates , candidate => getStringLiteralTypes ( typeChecker . getParameterType ( candidate , argumentInfo . argumentIndex ) , typeChecker , uniques ) ) } ;
420+ }
421+ return fromContextualType ( ) ;
359422 }
360- // falls through
423+ // falls through (is `require("")` or `import("")`)
361424
362425 case SyntaxKind . ImportDeclaration :
363426 case SyntaxKind . ExportDeclaration :
@@ -368,132 +431,28 @@ namespace ts.Completions {
368431 // import x = require("/*completion position*/");
369432 // var y = require("/*completion position*/");
370433 // export * from "/*completion position*/";
371- return pathCompletionsInfo ( PathCompletions . getStringLiteralCompletionsFromModuleNames ( sourceFile , node , compilerOptions , host , typeChecker ) ) ;
434+ return { kind : StringLiteralCompletionKind . Paths , paths : PathCompletions . getStringLiteralCompletionsFromModuleNames ( sourceFile , node , compilerOptions , host , typeChecker ) } ;
372435
373436 default :
374437 return fromContextualType ( ) ;
375438 }
376439
377- function fromContextualType ( ) : CompletionInfo {
440+ function fromContextualType ( ) : StringLiteralCompletion {
378441 // Get completion for string literal from string literal type
379442 // i.e. var x: "hi" | "hello" = "/*completion position*/"
380- return getStringLiteralCompletionEntriesFromType ( getContextualTypeFromParent ( node , typeChecker ) , typeChecker ) ;
443+ return { kind : StringLiteralCompletionKind . Types , types : getStringLiteralTypes ( getContextualTypeFromParent ( node , typeChecker ) , typeChecker ) } ;
381444 }
382445 }
383446
384- function pathCompletionsInfo ( entries : CompletionEntry [ ] ) : CompletionInfo {
385- return {
386- // We don't want the editor to offer any other completions, such as snippets, inside a comment.
387- isGlobalCompletion : false ,
388- isMemberCompletion : false ,
389- // The user may type in a path that doesn't yet exist, creating a "new identifier"
390- // with respect to the collection of identifiers the server is aware of.
391- isNewIdentifierLocation : true ,
392- entries,
393- } ;
394- }
395-
396- function getStringLiteralCompletionEntriesFromPropertyAssignment ( element : ObjectLiteralElement , sourceFile : SourceFile , typeChecker : TypeChecker , target : ScriptTarget , log : Log ) : CompletionInfo | undefined {
397- const type = typeChecker . getContextualType ( ( < ObjectLiteralExpression > element . parent ) ) ;
398- const entries : CompletionEntry [ ] = [ ] ;
399- if ( type ) {
400- getCompletionEntriesFromSymbols ( type . getApparentProperties ( ) , entries , element , sourceFile , typeChecker , target , log , CompletionKind . String ) ;
401- if ( entries . length ) {
402- return { isGlobalCompletion : false , isMemberCompletion : true , isNewIdentifierLocation : true , entries } ;
403- }
404- }
405- }
406-
407- function getStringLiteralCompletionEntriesFromCallExpression ( argumentInfo : SignatureHelp . ArgumentListInfo , typeChecker : TypeChecker ) : CompletionInfo | undefined {
408- const candidates : Signature [ ] = [ ] ;
409- const entries : CompletionEntry [ ] = [ ] ;
410- const uniques = createMap < true > ( ) ;
411-
412- typeChecker . getResolvedSignature ( argumentInfo . invocation , candidates , argumentInfo . argumentCount ) ;
413-
414- for ( const candidate of candidates ) {
415- addStringLiteralCompletionsFromType ( typeChecker . getParameterType ( candidate , argumentInfo . argumentIndex ) , entries , typeChecker , uniques ) ;
416- }
417-
418- if ( entries . length ) {
419- return { isGlobalCompletion : false , isMemberCompletion : false , isNewIdentifierLocation : true , entries } ;
420- }
421-
422- return undefined ;
423- }
424-
425- function getStringLiteralCompletionEntriesFromElementAccessOrIndexedAccess ( stringLiteralNode : StringLiteral | NoSubstitutionTemplateLiteral , sourceFile : SourceFile , type : Type , typeChecker : TypeChecker , target : ScriptTarget , log : Log ) : CompletionInfo | undefined {
426- const entries : CompletionEntry [ ] = [ ] ;
427- if ( type ) {
428- getCompletionEntriesFromSymbols ( type . getApparentProperties ( ) , entries , stringLiteralNode , sourceFile , typeChecker , target , log , CompletionKind . String ) ;
429- if ( entries . length ) {
430- return { isGlobalCompletion : false , isMemberCompletion : true , isNewIdentifierLocation : true , entries } ;
431- }
432- }
433- return undefined ;
434- }
435-
436- function getStringLiteralCompletionEntriesFromType ( type : Type , typeChecker : TypeChecker ) : CompletionInfo | undefined {
437- if ( type ) {
438- const entries : CompletionEntry [ ] = [ ] ;
439- addStringLiteralCompletionsFromType ( type , entries , typeChecker ) ;
440- if ( entries . length ) {
441- return { isGlobalCompletion : false , isMemberCompletion : false , isNewIdentifierLocation : false , entries } ;
442- }
443- }
444- return undefined ;
445- }
446-
447- function getLabelStatementCompletions ( node : Node ) : CompletionEntry [ ] {
448- const entries : CompletionEntry [ ] = [ ] ;
449- const uniques = createMap < true > ( ) ;
450- let current = node ;
451-
452- while ( current ) {
453- if ( isFunctionLike ( current ) ) {
454- break ;
455- }
456- if ( isLabeledStatement ( current ) ) {
457- const name = current . label . text ;
458- if ( ! uniques . has ( name ) ) {
459- uniques . set ( name , true ) ;
460- entries . push ( {
461- name,
462- kindModifiers : ScriptElementKindModifier . none ,
463- kind : ScriptElementKind . label ,
464- sortText : "0"
465- } ) ;
466- }
467- }
468- current = current . parent ;
469- }
470- return entries ;
471- }
472-
473- function addStringLiteralCompletionsFromType ( type : Type , result : Push < CompletionEntry > , typeChecker : TypeChecker , uniques = createMap < true > ( ) ) : void {
447+ function getStringLiteralTypes ( type : Type , typeChecker : TypeChecker , uniques = createMap < true > ( ) ) : ReadonlyArray < StringLiteralType > {
474448 if ( type && type . flags & TypeFlags . TypeParameter ) {
475- type = typeChecker . getBaseConstraintOfType ( type ) ;
476- }
477- if ( ! type ) {
478- return ;
479- }
480- if ( type . flags & TypeFlags . Union ) {
481- for ( const t of ( < UnionType > type ) . types ) {
482- addStringLiteralCompletionsFromType ( t , result , typeChecker , uniques ) ;
483- }
484- }
485- else if ( type . flags & TypeFlags . StringLiteral && ! ( type . flags & TypeFlags . EnumLiteral ) ) {
486- const name = ( < StringLiteralType > type ) . value ;
487- if ( ! uniques . has ( name ) ) {
488- uniques . set ( name , true ) ;
489- result . push ( {
490- name,
491- kindModifiers : ScriptElementKindModifier . none ,
492- kind : ScriptElementKind . variableElement ,
493- sortText : "0"
494- } ) ;
495- }
449+ type = type . getConstraint ( ) ;
496450 }
451+ return type && type . flags & TypeFlags . Union
452+ ? flatMap ( ( < UnionType > type ) . types , t => getStringLiteralTypes ( t , typeChecker , uniques ) )
453+ : type && type . flags & TypeFlags . StringLiteral && ! ( type . flags & TypeFlags . EnumLiteral ) && addToSeen ( uniques , ( type as StringLiteralType ) . value )
454+ ? [ type as StringLiteralType ]
455+ : emptyArray ;
497456 }
498457
499458 interface SymbolCompletion {
0 commit comments