@@ -288,7 +288,7 @@ namespace ts.Completions {
288288
289289 switch ( completionData . kind ) {
290290 case CompletionDataKind . Data :
291- const response = completionInfoFromData ( sourceFile , host , program , compilerOptions , log , completionData , preferences , formatContext ) ;
291+ const response = completionInfoFromData ( sourceFile , host , program , compilerOptions , log , completionData , preferences , formatContext , position ) ;
292292 if ( response ?. isIncomplete ) {
293293 incompleteCompletionsCache ?. set ( response ) ;
294294 }
@@ -452,6 +452,7 @@ namespace ts.Completions {
452452 completionData : CompletionData ,
453453 preferences : UserPreferences ,
454454 formatContext : formatting . FormatContext | undefined ,
455+ position : number
455456 ) : CompletionInfo | undefined {
456457 const {
457458 symbols,
@@ -512,7 +513,7 @@ namespace ts.Completions {
512513 isJsxIdentifierExpected ,
513514 isRightOfOpenTag ,
514515 ) ;
515- getJSCompletionEntries ( sourceFile , location . pos , uniqueNames , getEmitScriptTarget ( compilerOptions ) , entries ) ; // TODO: GH#18217
516+ getJSCompletionEntries ( sourceFile , location . pos , uniqueNames , getEmitScriptTarget ( compilerOptions ) , entries ) ;
516517 }
517518 else {
518519 if ( ! isNewIdentifierLocation && ( ! symbols || symbols . length === 0 ) && keywordFilters === KeywordCompletionFilters . None ) {
@@ -556,6 +557,13 @@ namespace ts.Completions {
556557 }
557558 }
558559
560+ const entryNames = new Set ( entries . map ( e => e . name ) ) ;
561+ for ( const keywordEntry of getContextualKeywords ( contextToken , position ) ) {
562+ if ( ! entryNames . has ( keywordEntry . name ) ) {
563+ insertSorted ( entries , keywordEntry , compareCompletionEntries , /*allowDuplicates*/ true ) ;
564+ }
565+ }
566+
559567 for ( const literal of literals ) {
560568 insertSorted ( entries , createCompletionEntryForLiteral ( sourceFile , preferences , literal ) , compareCompletionEntries , /*allowDuplicates*/ true ) ;
561569 }
@@ -3630,6 +3638,38 @@ namespace ts.Completions {
36303638 return isIdentifier ( node ) ? node . originalKeywordKind || SyntaxKind . Unknown : node . kind ;
36313639 }
36323640
3641+ function getContextualKeywords (
3642+ contextToken : Node | undefined ,
3643+ position : number ,
3644+ ) : readonly CompletionEntry [ ] {
3645+ const entries = [ ] ;
3646+ /**
3647+ * An `AssertClause` can come after an import declaration:
3648+ * import * from "foo" |
3649+ * import "foo" |
3650+ * or after a re-export declaration that has a module specifier:
3651+ * export { foo } from "foo" |
3652+ * Source: https://tc39.es/proposal-import-assertions/
3653+ */
3654+ if ( contextToken ) {
3655+ const file = contextToken . getSourceFile ( ) ;
3656+ const parent = contextToken . parent ;
3657+ const tokenLine = file . getLineAndCharacterOfPosition ( contextToken . end ) . line ;
3658+ const currentLine = file . getLineAndCharacterOfPosition ( position ) . line ;
3659+ if ( ( isImportDeclaration ( parent ) || isExportDeclaration ( parent ) && parent . moduleSpecifier )
3660+ && contextToken === parent . moduleSpecifier
3661+ && tokenLine === currentLine ) {
3662+ entries . push ( {
3663+ name : tokenToString ( SyntaxKind . AssertKeyword ) ! ,
3664+ kind : ScriptElementKind . keyword ,
3665+ kindModifiers : ScriptElementKindModifier . none ,
3666+ sortText : SortText . GlobalsOrKeywords ,
3667+ } ) ;
3668+ }
3669+ }
3670+ return entries ;
3671+ }
3672+
36333673 /** Get the corresponding JSDocTag node if the position is in a jsDoc comment */
36343674 function getJsDocTagAtPosition ( node : Node , position : number ) : JSDocTag | undefined {
36353675 return findAncestor ( node , n =>
0 commit comments