mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-15 21:36:50 -05:00
fix(45607): add snippet for type only import statements (#45873)
This commit is contained in:
@@ -602,6 +602,7 @@ namespace ts.Completions {
|
||||
propertyAccessToConvert: PropertyAccessExpression | undefined,
|
||||
isJsxInitializer: IsJsxInitializer | undefined,
|
||||
importCompletionNode: Node | undefined,
|
||||
isTypeOnlyImport: boolean,
|
||||
useSemicolons: boolean,
|
||||
options: CompilerOptions,
|
||||
preferences: UserPreferences,
|
||||
@@ -661,7 +662,7 @@ namespace ts.Completions {
|
||||
if (originIsResolvedExport(origin)) {
|
||||
sourceDisplay = [textPart(origin.moduleSpecifier)];
|
||||
if (importCompletionNode) {
|
||||
({ insertText, replacementSpan } = getInsertTextAndReplacementSpanForImportCompletion(name, importCompletionNode, origin, useSemicolons, options, preferences));
|
||||
({ insertText, replacementSpan } = getInsertTextAndReplacementSpanForImportCompletion(name, importCompletionNode, isTypeOnlyImport, origin, useSemicolons, options, preferences));
|
||||
isSnippet = preferences.includeCompletionsWithSnippetText ? true : undefined;
|
||||
}
|
||||
}
|
||||
@@ -746,7 +747,7 @@ namespace ts.Completions {
|
||||
};
|
||||
}
|
||||
|
||||
function getInsertTextAndReplacementSpanForImportCompletion(name: string, importCompletionNode: Node, origin: SymbolOriginInfoResolvedExport, useSemicolons: boolean, options: CompilerOptions, preferences: UserPreferences) {
|
||||
function getInsertTextAndReplacementSpanForImportCompletion(name: string, importCompletionNode: Node, isTypeOnly: boolean | undefined, origin: SymbolOriginInfoResolvedExport, useSemicolons: boolean, options: CompilerOptions, preferences: UserPreferences) {
|
||||
const sourceFile = importCompletionNode.getSourceFile();
|
||||
const replacementSpan = createTextSpanFromNode(importCompletionNode, sourceFile);
|
||||
const quotedModuleSpecifier = quote(sourceFile, preferences, origin.moduleSpecifier);
|
||||
@@ -756,12 +757,13 @@ namespace ts.Completions {
|
||||
ExportKind.Named;
|
||||
const tabStop = preferences.includeCompletionsWithSnippetText ? "$1" : "";
|
||||
const importKind = codefix.getImportKind(sourceFile, exportKind, options, /*forceImportKeyword*/ true);
|
||||
const typeOnlyPrefix = isTypeOnly ? ` ${tokenToString(SyntaxKind.TypeKeyword)} ` : " ";
|
||||
const suffix = useSemicolons ? ";" : "";
|
||||
switch (importKind) {
|
||||
case ImportKind.CommonJS: return { replacementSpan, insertText: `import ${escapeSnippetText(name)}${tabStop} = require(${quotedModuleSpecifier})${suffix}` };
|
||||
case ImportKind.Default: return { replacementSpan, insertText: `import ${escapeSnippetText(name)}${tabStop} from ${quotedModuleSpecifier}${suffix}` };
|
||||
case ImportKind.Namespace: return { replacementSpan, insertText: `import * as ${escapeSnippetText(name)} from ${quotedModuleSpecifier}${suffix}` };
|
||||
case ImportKind.Named: return { replacementSpan, insertText: `import { ${escapeSnippetText(name)}${tabStop} } from ${quotedModuleSpecifier}${suffix}` };
|
||||
case ImportKind.CommonJS: return { replacementSpan, insertText: `import${typeOnlyPrefix}${escapeSnippetText(name)}${tabStop} = require(${quotedModuleSpecifier})${suffix}` };
|
||||
case ImportKind.Default: return { replacementSpan, insertText: `import${typeOnlyPrefix}${escapeSnippetText(name)}${tabStop} from ${quotedModuleSpecifier}${suffix}` };
|
||||
case ImportKind.Namespace: return { replacementSpan, insertText: `import${typeOnlyPrefix}* as ${escapeSnippetText(name)} from ${quotedModuleSpecifier}${suffix}` };
|
||||
case ImportKind.Named: return { replacementSpan, insertText: `import${typeOnlyPrefix}{ ${escapeSnippetText(name)}${tabStop} } from ${quotedModuleSpecifier}${suffix}` };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -814,6 +816,7 @@ namespace ts.Completions {
|
||||
const start = timestamp();
|
||||
const variableDeclaration = getVariableDeclaration(location);
|
||||
const useSemicolons = probablyUsesSemicolons(sourceFile);
|
||||
const isTypeOnlyImport = !!importCompletionNode && isTypeOnlyImportOrExportDeclaration(location.parent);
|
||||
// Tracks unique names.
|
||||
// Value is set to false for global variables or completions from external module exports, because we can have multiple of those;
|
||||
// true otherwise. Based on the order we add things we will always see locals first, then globals, then module exports.
|
||||
@@ -844,6 +847,7 @@ namespace ts.Completions {
|
||||
propertyAccessToConvert,
|
||||
isJsxInitializer,
|
||||
importCompletionNode,
|
||||
isTypeOnlyImport,
|
||||
useSemicolons,
|
||||
compilerOptions,
|
||||
preferences
|
||||
@@ -1916,6 +1920,7 @@ namespace ts.Completions {
|
||||
|
||||
function isTypeOnlyCompletion(): boolean {
|
||||
return insideJsDocTagTypeExpression
|
||||
|| !!importCompletionNode && isTypeOnlyImportOrExportDeclaration(location.parent)
|
||||
|| !isContextTokenValueLocation(contextToken) &&
|
||||
(isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker)
|
||||
|| isPartOfTypeNode(location)
|
||||
|
||||
26
tests/cases/fourslash/importTypeCompletions1.ts
Normal file
26
tests/cases/fourslash/importTypeCompletions1.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
// @target: esnext
|
||||
|
||||
// @filename: /foo.ts
|
||||
////export interface Foo {}
|
||||
|
||||
// @filename: /bar.ts
|
||||
////[|import type F/**/|]
|
||||
|
||||
goTo.file("/bar.ts")
|
||||
verify.completions({
|
||||
marker: "",
|
||||
exact: [{
|
||||
name: "Foo",
|
||||
sourceDisplay: "./foo",
|
||||
source: "./foo",
|
||||
insertText: "import type { Foo } from \"./foo\";",
|
||||
replacementSpan: test.ranges()[0]
|
||||
}],
|
||||
isNewIdentifierLocation: true,
|
||||
preferences: {
|
||||
includeCompletionsForModuleExports: true,
|
||||
includeCompletionsForImportStatements: true,
|
||||
includeCompletionsWithInsertText: true
|
||||
}
|
||||
});
|
||||
20
tests/cases/fourslash/importTypeCompletions2.ts
Normal file
20
tests/cases/fourslash/importTypeCompletions2.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
// @target: esnext
|
||||
|
||||
// @filename: /foo.ts
|
||||
////export const Foo = {};
|
||||
|
||||
// @filename: /bar.ts
|
||||
////[|import type F/**/|]
|
||||
|
||||
goTo.file("/bar.ts")
|
||||
verify.completions({
|
||||
marker: "",
|
||||
exact: [],
|
||||
isNewIdentifierLocation: true,
|
||||
preferences: {
|
||||
includeCompletionsForModuleExports: true,
|
||||
includeCompletionsForImportStatements: true,
|
||||
includeCompletionsWithInsertText: true
|
||||
}
|
||||
});
|
||||
26
tests/cases/fourslash/importTypeCompletions3.ts
Normal file
26
tests/cases/fourslash/importTypeCompletions3.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
// @target: esnext
|
||||
|
||||
// @filename: /foo.ts
|
||||
////export interface Foo {}
|
||||
|
||||
// @filename: /bar.ts
|
||||
////[|import type { F/**/ }|]
|
||||
|
||||
goTo.file("/bar.ts")
|
||||
verify.completions({
|
||||
marker: "",
|
||||
exact: [{
|
||||
name: "Foo",
|
||||
sourceDisplay: "./foo",
|
||||
source: "./foo",
|
||||
insertText: "import type { Foo } from \"./foo\";",
|
||||
replacementSpan: test.ranges()[0]
|
||||
}],
|
||||
isNewIdentifierLocation: true,
|
||||
preferences: {
|
||||
includeCompletionsForModuleExports: true,
|
||||
includeCompletionsForImportStatements: true,
|
||||
includeCompletionsWithInsertText: true
|
||||
}
|
||||
});
|
||||
27
tests/cases/fourslash/importTypeCompletions4.ts
Normal file
27
tests/cases/fourslash/importTypeCompletions4.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
// @esModuleInterop: true
|
||||
|
||||
// @Filename: /foo.ts
|
||||
////interface Foo { };
|
||||
////export = Foo;
|
||||
|
||||
// @Filename: /bar.ts
|
||||
//// [|import type f/**/|]
|
||||
|
||||
goTo.file("/bar.ts")
|
||||
verify.completions({
|
||||
marker: "",
|
||||
exact: [{
|
||||
name: "Foo",
|
||||
sourceDisplay: "./foo",
|
||||
source: "./foo",
|
||||
insertText: "import type Foo from \"./foo\";",
|
||||
replacementSpan: test.ranges()[0]
|
||||
}],
|
||||
isNewIdentifierLocation: true,
|
||||
preferences: {
|
||||
includeCompletionsForModuleExports: true,
|
||||
includeCompletionsForImportStatements: true,
|
||||
includeCompletionsWithInsertText: true
|
||||
}
|
||||
});
|
||||
28
tests/cases/fourslash/importTypeCompletions5.ts
Normal file
28
tests/cases/fourslash/importTypeCompletions5.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @esModuleInterop: false
|
||||
|
||||
// @Filename: /foo.ts
|
||||
////interface Foo { };
|
||||
////export = Foo;
|
||||
|
||||
// @Filename: /bar.ts
|
||||
//// [|import type f/**/|]
|
||||
|
||||
goTo.file("/bar.ts")
|
||||
verify.completions({
|
||||
marker: "",
|
||||
exact: [{
|
||||
name: "Foo",
|
||||
sourceDisplay: "./foo",
|
||||
source: "./foo",
|
||||
insertText: "import type Foo = require(\"./foo\");",
|
||||
replacementSpan: test.ranges()[0]
|
||||
}],
|
||||
isNewIdentifierLocation: true,
|
||||
preferences: {
|
||||
includeCompletionsForModuleExports: true,
|
||||
includeCompletionsForImportStatements: true,
|
||||
includeCompletionsWithInsertText: true
|
||||
}
|
||||
});
|
||||
28
tests/cases/fourslash/importTypeCompletions6.ts
Normal file
28
tests/cases/fourslash/importTypeCompletions6.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @module: esnext
|
||||
|
||||
// @Filename: /foo.ts
|
||||
////export const foo = { };
|
||||
////export interface Foo { };
|
||||
|
||||
// @Filename: /bar.ts
|
||||
//// [|import type * as f/**/|]
|
||||
|
||||
goTo.file("/bar.ts")
|
||||
verify.completions({
|
||||
marker: "",
|
||||
exact: [{
|
||||
name: "Foo",
|
||||
sourceDisplay: "./foo",
|
||||
source: "./foo",
|
||||
insertText: "import type { Foo } from \"./foo\";",
|
||||
replacementSpan: test.ranges()[0]
|
||||
}],
|
||||
isNewIdentifierLocation: true,
|
||||
preferences: {
|
||||
includeCompletionsForModuleExports: true,
|
||||
includeCompletionsForImportStatements: true,
|
||||
includeCompletionsWithInsertText: true
|
||||
}
|
||||
});
|
||||
29
tests/cases/fourslash/importTypeCompletions7.ts
Normal file
29
tests/cases/fourslash/importTypeCompletions7.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @target: es2020
|
||||
// @module: esnext
|
||||
|
||||
// @Filename: /foo.d.ts
|
||||
//// declare namespace Foo {}
|
||||
//// export = Foo;
|
||||
|
||||
// @Filename: /test.ts
|
||||
//// [|import F/**/|]
|
||||
|
||||
goTo.file("/test.ts")
|
||||
verify.completions({
|
||||
marker: "",
|
||||
exact: [{
|
||||
name: "Foo",
|
||||
sourceDisplay: "./foo",
|
||||
source: "./foo",
|
||||
insertText: "import * as Foo from \"./foo\";",
|
||||
replacementSpan: test.ranges()[0]
|
||||
}],
|
||||
isNewIdentifierLocation: true,
|
||||
preferences: {
|
||||
includeCompletionsForModuleExports: true,
|
||||
includeCompletionsForImportStatements: true,
|
||||
includeCompletionsWithInsertText: true
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user