Add refactoring to use default import (#19659)
* Add refactoring to use default import * Add localizable description
This commit is contained in:
parent
cc2a2a79b5
commit
d54ad4b01a
|
@ -3793,5 +3793,9 @@
|
||||||
"Infer parameter types from usage.": {
|
"Infer parameter types from usage.": {
|
||||||
"category": "Message",
|
"category": "Message",
|
||||||
"code": 95012
|
"code": 95012
|
||||||
|
},
|
||||||
|
"Convert to default import": {
|
||||||
|
"category": "Message",
|
||||||
|
"code": 95013
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -470,6 +470,10 @@ namespace FourSlash {
|
||||||
|
|
||||||
public select(startMarker: string, endMarker: string) {
|
public select(startMarker: string, endMarker: string) {
|
||||||
const start = this.getMarkerByName(startMarker), end = this.getMarkerByName(endMarker);
|
const start = this.getMarkerByName(startMarker), end = this.getMarkerByName(endMarker);
|
||||||
|
ts.Debug.assert(start.fileName === end.fileName);
|
||||||
|
if (this.activeFile.fileName !== start.fileName) {
|
||||||
|
this.openFile(start.fileName);
|
||||||
|
}
|
||||||
this.goToPosition(start.position);
|
this.goToPosition(start.position);
|
||||||
this.selectionEnd = end.position;
|
this.selectionEnd = end.position;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
/// <reference path="convertFunctionToEs6Class.ts" />
|
/// <reference path="convertFunctionToEs6Class.ts" />
|
||||||
/// <reference path="extractSymbol.ts" />
|
/// <reference path="extractSymbol.ts" />
|
||||||
/// <reference path="installTypesForPackage.ts" />
|
/// <reference path="installTypesForPackage.ts" />
|
||||||
|
/// <reference path="useDefaultImport.ts" />
|
||||||
|
|
96
src/services/refactors/useDefaultImport.ts
Normal file
96
src/services/refactors/useDefaultImport.ts
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
/* @internal */
|
||||||
|
namespace ts.refactor.installTypesForPackage {
|
||||||
|
const actionName = "Convert to default import";
|
||||||
|
|
||||||
|
const useDefaultImport: Refactor = {
|
||||||
|
name: actionName,
|
||||||
|
description: getLocaleSpecificMessage(Diagnostics.Convert_to_default_import),
|
||||||
|
getEditsForAction,
|
||||||
|
getAvailableActions,
|
||||||
|
};
|
||||||
|
|
||||||
|
registerRefactor(useDefaultImport);
|
||||||
|
|
||||||
|
function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined {
|
||||||
|
const { file, startPosition, program } = context;
|
||||||
|
|
||||||
|
if (!program.getCompilerOptions().allowSyntheticDefaultImports) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const importInfo = getConvertibleImportAtPosition(file, startPosition);
|
||||||
|
if (!importInfo) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const module = ts.getResolvedModule(file, importInfo.moduleSpecifier.text);
|
||||||
|
const resolvedFile = program.getSourceFile(module.resolvedFileName);
|
||||||
|
if (!(resolvedFile.externalModuleIndicator && isExportAssignment(resolvedFile.externalModuleIndicator) && resolvedFile.externalModuleIndicator.isExportEquals)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: useDefaultImport.name,
|
||||||
|
description: useDefaultImport.description,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
description: useDefaultImport.description,
|
||||||
|
name: actionName,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEditsForAction(context: RefactorContext, _actionName: string): RefactorEditInfo | undefined {
|
||||||
|
const { file, startPosition } = context;
|
||||||
|
Debug.assertEqual(actionName, _actionName);
|
||||||
|
const importInfo = getConvertibleImportAtPosition(file, startPosition);
|
||||||
|
if (!importInfo) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const { importStatement, name, moduleSpecifier } = importInfo;
|
||||||
|
const newImportClause = createImportClause(name, /*namedBindings*/ undefined);
|
||||||
|
const newImportStatement = ts.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, newImportClause, moduleSpecifier);
|
||||||
|
return {
|
||||||
|
edits: textChanges.ChangeTracker.with(context, t => t.replaceNode(file, importStatement, newImportStatement)),
|
||||||
|
renameFilename: undefined,
|
||||||
|
renameLocation: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getConvertibleImportAtPosition(
|
||||||
|
file: SourceFile,
|
||||||
|
startPosition: number,
|
||||||
|
): { importStatement: AnyImportSyntax, name: Identifier, moduleSpecifier: StringLiteral } | undefined {
|
||||||
|
let node = getTokenAtPosition(file, startPosition, /*includeJsDocComment*/ false);
|
||||||
|
while (true) {
|
||||||
|
switch (node.kind) {
|
||||||
|
case SyntaxKind.ImportEqualsDeclaration:
|
||||||
|
const eq = node as ImportEqualsDeclaration;
|
||||||
|
const { moduleReference } = eq;
|
||||||
|
return moduleReference.kind === SyntaxKind.ExternalModuleReference && isStringLiteral(moduleReference.expression)
|
||||||
|
? { importStatement: eq, name: eq.name, moduleSpecifier: moduleReference.expression }
|
||||||
|
: undefined;
|
||||||
|
case SyntaxKind.ImportDeclaration:
|
||||||
|
const d = node as ImportDeclaration;
|
||||||
|
const { importClause } = d;
|
||||||
|
return !importClause.name && importClause.namedBindings.kind === SyntaxKind.NamespaceImport && isStringLiteral(d.moduleSpecifier)
|
||||||
|
? { importStatement: d, name: importClause.namedBindings.name, moduleSpecifier: d.moduleSpecifier }
|
||||||
|
: undefined;
|
||||||
|
// For known child node kinds of convertible imports, try again with parent node.
|
||||||
|
case SyntaxKind.NamespaceImport:
|
||||||
|
case SyntaxKind.ExternalModuleReference:
|
||||||
|
case SyntaxKind.ImportKeyword:
|
||||||
|
case SyntaxKind.Identifier:
|
||||||
|
case SyntaxKind.StringLiteral:
|
||||||
|
case SyntaxKind.AsteriskToken:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
node = node.parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
tests/cases/fourslash/refactorUseDefaultImport.ts
Normal file
29
tests/cases/fourslash/refactorUseDefaultImport.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
// @allowSyntheticDefaultImports: true
|
||||||
|
|
||||||
|
// @Filename: /a.d.ts
|
||||||
|
////declare const x: number;
|
||||||
|
////export = x;
|
||||||
|
|
||||||
|
// @Filename: /b.ts
|
||||||
|
/////*b0*/import * as a from "./a";/*b1*/
|
||||||
|
|
||||||
|
// @Filename: /c.ts
|
||||||
|
/////*c0*/import a = require("./a");/*c1*/
|
||||||
|
|
||||||
|
goTo.select("b0", "b1");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to default import",
|
||||||
|
actionName: "Convert to default import",
|
||||||
|
actionDescription: "Convert to default import",
|
||||||
|
newContent: 'import a from "./a";',
|
||||||
|
});
|
||||||
|
|
||||||
|
goTo.select("c0", "c1");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to default import",
|
||||||
|
actionName: "Convert to default import",
|
||||||
|
actionDescription: "Convert to default import",
|
||||||
|
newContent: 'import a from "./a";',
|
||||||
|
});
|
Loading…
Reference in a new issue