Skip to content

Commit baf8ac2

Browse files
committed
import attributes completions
1 parent cc6c2d5 commit baf8ac2

5 files changed

Lines changed: 305 additions & 1 deletion

File tree

src/compiler/checker.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ import {
325325
getModeForUsageLocation,
326326
getModifiers,
327327
getModuleInstanceState,
328+
getNameFromImportAttribute,
328329
getNameFromIndexInfo,
329330
getNameOfDeclaration,
330331
getNameOfExpando,
@@ -553,6 +554,7 @@ import {
553554
isIdentifierTypePredicate,
554555
isIdentifierTypeReference,
555556
isIfStatement,
557+
isImportAttributes,
556558
isImportCall,
557559
isImportClause,
558560
isImportDeclaration,
@@ -11541,7 +11543,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1154111543
const symbol = createSymbol(SymbolFlags.ObjectLiteral, InternalSymbolName.ImportAttributes);
1154211544
const members = createSymbolTable();
1154311545
forEach(node.elements, attr => {
11544-
const member = createSymbol(SymbolFlags.Property, isIdentifier(attr.name) ? attr.name.escapedText : escapeLeadingUnderscores(attr.name.text));
11546+
const member = createSymbol(SymbolFlags.Property, getNameFromImportAttribute(attr));
1154511547
member.parent = symbol;
1154611548
member.links.type = checkImportAttribute(attr);
1154711549
member.links.target = member;
@@ -30765,6 +30767,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3076530767
case SyntaxKind.JsxOpeningElement:
3076630768
case SyntaxKind.JsxSelfClosingElement:
3076730769
return getContextualJsxElementAttributesType(parent as JsxOpeningLikeElement, contextFlags);
30770+
case SyntaxKind.ImportAttribute:
30771+
return getContextualImportAttributeType(parent as ImportAttribute);
3076830772
}
3076930773
return undefined;
3077030774
}
@@ -30811,6 +30815,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3081130815
}
3081230816
}
3081330817

30818+
function getContextualImportAttributeType(node: ImportAttribute) {
30819+
return getTypeOfPropertyOfContextualType(getGlobalImportAttributesType(/*reportErrors*/ false), getNameFromImportAttribute(node));
30820+
}
30821+
3081430822
function getContextualJsxElementAttributesType(node: JsxOpeningLikeElement, contextFlags: ContextFlags | undefined) {
3081530823
if (isJsxOpeningElement(node) && contextFlags !== ContextFlags.Completions) {
3081630824
const index = findContextualNode(node.parent, /*includeCaches*/ !contextFlags);
@@ -47570,6 +47578,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4757047578
return checkMetaPropertyKeyword(node.parent);
4757147579
}
4757247580

47581+
if (isImportAttributes(node)) {
47582+
return getGlobalImportAttributesType(/*reportErrors*/ false);
47583+
}
47584+
4757347585
return errorType;
4757447586
}
4757547587

src/compiler/utilities.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ import {
220220
idText,
221221
IfStatement,
222222
ignoredPaths,
223+
ImportAttribute,
223224
ImportCall,
224225
ImportClause,
225226
ImportDeclaration,
@@ -10655,3 +10656,8 @@ export function replaceFirstStar(s: string, replacement: string): string {
1065510656
// Attempt to defeat this analysis by indirectly calling the method.
1065610657
return stringReplace.call(s, "*", replacement);
1065710658
}
10659+
10660+
/** @internal */
10661+
export function getNameFromImportAttribute(node: ImportAttribute) {
10662+
return isIdentifier(node.name) ? node.name.escapedText : escapeLeadingUnderscores(node.name.text);
10663+
}

src/services/completions.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ import {
9595
getLineStartPositionForPosition,
9696
getLocalSymbolForExportDefault,
9797
getModifiers,
98+
getNameFromImportAttribute,
9899
getNameOfDeclaration,
99100
getNameTable,
100101
getNewLineCharacter,
@@ -169,6 +170,7 @@ import {
169170
isIdentifier,
170171
isIdentifierText,
171172
isImportableFile,
173+
isImportAttributes,
172174
isImportDeclaration,
173175
isImportEqualsDeclaration,
174176
isImportKeyword,
@@ -3768,6 +3770,7 @@ function getCompletionData(
37683770
|| tryGetObjectLikeCompletionSymbols()
37693771
|| tryGetImportCompletionSymbols()
37703772
|| tryGetImportOrExportClauseCompletionSymbols()
3773+
|| tryGetImportAttributesCompletionSymbols()
37713774
|| tryGetLocalNamedExportCompletionSymbols()
37723775
|| tryGetConstructorCompletion()
37733776
|| tryGetClassLikeCompletionSymbols()
@@ -4455,6 +4458,22 @@ function getCompletionData(
44554458
return GlobalsSearch.Success;
44564459
}
44574460

4461+
/**
4462+
* import { x } from "foo" with { | }
4463+
*/
4464+
function tryGetImportAttributesCompletionSymbols(): GlobalsSearch {
4465+
if (contextToken === undefined) return GlobalsSearch.Continue;
4466+
4467+
const importAttributes = contextToken.kind === SyntaxKind.OpenBraceToken || contextToken.kind === SyntaxKind.CommaToken
4468+
? tryCast(contextToken.parent, isImportAttributes)
4469+
: undefined;
4470+
if (importAttributes === undefined) return GlobalsSearch.Continue;
4471+
4472+
const existing = new Set(importAttributes.elements.map(getNameFromImportAttribute));
4473+
symbols = filter(typeChecker.getTypeAtLocation(importAttributes).getApparentProperties(), attr => !existing.has(attr.escapedName));
4474+
return GlobalsSearch.Success;
4475+
}
4476+
44584477
/**
44594478
* Adds local declarations for completions in named exports:
44604479
*
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
// === Completions ===
2+
=== /tests/cases/fourslash/./b.ts ===
3+
// declare global {
4+
// interface ImportAttributes {
5+
// type: "json",
6+
// "resolution-mode": "import"
7+
// }
8+
// }
9+
//
10+
// import * as t1 from "./a" with { };
11+
// ^
12+
// | ----------------------------------------------------------------------
13+
// | (property) ImportAttributes["resolution-mode"]: "import"
14+
// | (property) ImportAttributes.type: "json"
15+
// | ----------------------------------------------------------------------
16+
// import * as t2 from "./a" with { type: "" };
17+
//
18+
// | ----------------------------------------------------------------------
19+
// | json
20+
// | ----------------------------------------------------------------------
21+
// import * as t3 from "./a" with { type: "json", };
22+
// ^
23+
// | ----------------------------------------------------------------------
24+
// | (property) ImportAttributes["resolution-mode"]: "import"
25+
// | ----------------------------------------------------------------------
26+
27+
[
28+
{
29+
"marker": {
30+
"fileName": "/tests/cases/fourslash/./b.ts",
31+
"position": 150,
32+
"name": "1"
33+
},
34+
"item": {
35+
"flags": 0,
36+
"isGlobalCompletion": false,
37+
"isMemberCompletion": false,
38+
"isNewIdentifierLocation": false,
39+
"entries": [
40+
{
41+
"name": "resolution-mode",
42+
"kind": "property",
43+
"kindModifiers": "declare",
44+
"sortText": "11",
45+
"displayParts": [
46+
{
47+
"text": "(",
48+
"kind": "punctuation"
49+
},
50+
{
51+
"text": "property",
52+
"kind": "text"
53+
},
54+
{
55+
"text": ")",
56+
"kind": "punctuation"
57+
},
58+
{
59+
"text": " ",
60+
"kind": "space"
61+
},
62+
{
63+
"text": "ImportAttributes",
64+
"kind": "interfaceName"
65+
},
66+
{
67+
"text": "[",
68+
"kind": "punctuation"
69+
},
70+
{
71+
"text": "\"resolution-mode\"",
72+
"kind": "stringLiteral"
73+
},
74+
{
75+
"text": "]",
76+
"kind": "punctuation"
77+
},
78+
{
79+
"text": ":",
80+
"kind": "punctuation"
81+
},
82+
{
83+
"text": " ",
84+
"kind": "space"
85+
},
86+
{
87+
"text": "\"import\"",
88+
"kind": "stringLiteral"
89+
}
90+
],
91+
"documentation": []
92+
},
93+
{
94+
"name": "type",
95+
"kind": "property",
96+
"kindModifiers": "declare",
97+
"sortText": "11",
98+
"displayParts": [
99+
{
100+
"text": "(",
101+
"kind": "punctuation"
102+
},
103+
{
104+
"text": "property",
105+
"kind": "text"
106+
},
107+
{
108+
"text": ")",
109+
"kind": "punctuation"
110+
},
111+
{
112+
"text": " ",
113+
"kind": "space"
114+
},
115+
{
116+
"text": "ImportAttributes",
117+
"kind": "interfaceName"
118+
},
119+
{
120+
"text": ".",
121+
"kind": "punctuation"
122+
},
123+
{
124+
"text": "type",
125+
"kind": "propertyName"
126+
},
127+
{
128+
"text": ":",
129+
"kind": "punctuation"
130+
},
131+
{
132+
"text": " ",
133+
"kind": "space"
134+
},
135+
{
136+
"text": "\"json\"",
137+
"kind": "stringLiteral"
138+
}
139+
],
140+
"documentation": []
141+
}
142+
]
143+
}
144+
},
145+
{
146+
"marker": {
147+
"fileName": "/tests/cases/fourslash/./b.ts",
148+
"position": 194,
149+
"name": "2"
150+
},
151+
"item": {
152+
"isGlobalCompletion": false,
153+
"isMemberCompletion": false,
154+
"isNewIdentifierLocation": false,
155+
"optionalReplacementSpan": {
156+
"start": 194,
157+
"length": 0
158+
},
159+
"entries": [
160+
{
161+
"name": "json",
162+
"kindModifiers": "",
163+
"kind": "string",
164+
"sortText": "11",
165+
"replacementSpan": {
166+
"start": 194,
167+
"length": 0
168+
},
169+
"displayParts": [
170+
{
171+
"text": "json",
172+
"kind": "text"
173+
}
174+
]
175+
}
176+
]
177+
}
178+
},
179+
{
180+
"marker": {
181+
"fileName": "/tests/cases/fourslash/./b.ts",
182+
"position": 246,
183+
"name": "3"
184+
},
185+
"item": {
186+
"flags": 0,
187+
"isGlobalCompletion": false,
188+
"isMemberCompletion": false,
189+
"isNewIdentifierLocation": false,
190+
"entries": [
191+
{
192+
"name": "resolution-mode",
193+
"kind": "property",
194+
"kindModifiers": "declare",
195+
"sortText": "11",
196+
"displayParts": [
197+
{
198+
"text": "(",
199+
"kind": "punctuation"
200+
},
201+
{
202+
"text": "property",
203+
"kind": "text"
204+
},
205+
{
206+
"text": ")",
207+
"kind": "punctuation"
208+
},
209+
{
210+
"text": " ",
211+
"kind": "space"
212+
},
213+
{
214+
"text": "ImportAttributes",
215+
"kind": "interfaceName"
216+
},
217+
{
218+
"text": "[",
219+
"kind": "punctuation"
220+
},
221+
{
222+
"text": "\"resolution-mode\"",
223+
"kind": "stringLiteral"
224+
},
225+
{
226+
"text": "]",
227+
"kind": "punctuation"
228+
},
229+
{
230+
"text": ":",
231+
"kind": "punctuation"
232+
},
233+
{
234+
"text": " ",
235+
"kind": "space"
236+
},
237+
{
238+
"text": "\"import\"",
239+
"kind": "stringLiteral"
240+
}
241+
],
242+
"documentation": []
243+
}
244+
]
245+
}
246+
}
247+
]
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
///<reference path="fourslash.ts" />
2+
3+
// @module: esnext
4+
// @target: esnext
5+
// @filename: ./a.ts
6+
////export default {};
7+
8+
// @filename: ./b.ts
9+
////declare global {
10+
//// interface ImportAttributes {
11+
//// type: "json",
12+
//// "resolution-mode": "import"
13+
//// }
14+
////}
15+
////
16+
////import * as t1 from "./a" with { /*1*/ };
17+
////import * as t2 from "./a" with { type: "/*2*/" };
18+
////import * as t3 from "./a" with { type: "json", /*3*/ };
19+
20+
verify.baselineCompletions();

0 commit comments

Comments
 (0)