Skip to content

Commit 9028051

Browse files
authored
Add assert keyword in completions (microsoft#47644)
* add contextual keyword assert in completions * clean up
1 parent f2c51c1 commit 9028051

File tree

2 files changed

+96
-2
lines changed

2 files changed

+96
-2
lines changed

src/services/completions.ts

+42-2
Original file line numberDiff line numberDiff line change
@@ -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 =>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @allowJs: true
4+
// @Filename: a.ts
5+
//// const f = {
6+
//// a: 1
7+
////};
8+
//// import * as thing from "thing" /*0*/
9+
//// export { foo } from "foo" /*1*/
10+
//// import "foo" as /*2*/
11+
//// import "foo" a/*3*/
12+
//// import * as that from "that"
13+
//// /*4*/
14+
//// import * /*5*/ as those from "those"
15+
16+
// @Filename: b.js
17+
//// import * as thing from "thing" /*js*/;
18+
19+
const assertEntry = {
20+
name: "assert",
21+
kind: "keyword",
22+
sortText: completion.SortText.GlobalsOrKeywords,
23+
};
24+
25+
verify.completions(
26+
{
27+
marker: "0",
28+
includes: [assertEntry],
29+
},
30+
{
31+
marker: "1",
32+
includes: [assertEntry],
33+
},
34+
{
35+
marker: "2",
36+
excludes: ["assert"],
37+
},
38+
{
39+
marker: "3",
40+
includes: [assertEntry],
41+
},
42+
{
43+
marker: "4",
44+
excludes: ["assert"],
45+
},
46+
{
47+
marker: "5",
48+
excludes: ["assert"],
49+
},
50+
{
51+
marker: "js",
52+
includes: [assertEntry],
53+
},
54+
);

0 commit comments

Comments
 (0)