Skip to content

Commit cdf4c60

Browse files
authored
Merge pull request #1144 from kyasbal-1994/master
Improve import quickfix
2 parents 8db97ee + dae3038 commit cdf4c60

File tree

9 files changed

+167
-13
lines changed

9 files changed

+167
-13
lines changed

dist/main/lang/fixmyts/quickFixRegistry.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use strict";
22
var addClassMember_1 = require("./quickFixes/addClassMember");
33
var addClassMethod_1 = require("./quickFixes/addClassMethod");
4+
var addImportDefaultStatement_1 = require("./quickFixes/addImportDefaultStatement");
45
var addImportFromStatement_1 = require("./quickFixes/addImportFromStatement");
56
var addImportStatement_1 = require("./quickFixes/addImportStatement");
67
var equalsToEquals_1 = require("./quickFixes/equalsToEquals");
@@ -16,6 +17,7 @@ var singleLineCommentToJsdoc_1 = require("./quickFixes/singleLineCommentToJsdoc"
1617
exports.allQuickFixes = [
1718
new addClassMethod_1.AddClassMethod(),
1819
new addClassMember_1.AddClassMember(),
20+
new addImportDefaultStatement_1.AddImportDefaultStatement(),
1921
new addImportFromStatement_1.AddImportFromStatement(),
2022
new addImportStatement_1.AddImportStatement(),
2123
new wrapInProperty_1.WrapInProperty(),
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"use strict";
2+
var os_1 = require("os");
3+
var displayPartsToString = ts.displayPartsToString, typeToDisplayParts = ts.typeToDisplayParts;
4+
var getPathCompletions_1 = require("../../modules/getPathCompletions");
5+
function getIdentifierAndFileNames(error, project) {
6+
var errorText = error.messageText;
7+
if (typeof errorText !== 'string') {
8+
return undefined;
9+
}
10+
;
11+
var match = errorText.match(/Cannot find name \'(\w+)\'./);
12+
if (!match)
13+
return;
14+
var identifierName = match[1];
15+
var files = getPathCompletions_1.getPathCompletions({
16+
project: project,
17+
filePath: error.file.fileName,
18+
prefix: identifierName,
19+
includeExternalModules: false
20+
}).files;
21+
var file = files.length > 0 ? files[0].relativePath : undefined;
22+
var basename = files.length > 0 ? files[0].name : undefined;
23+
return { identifierName: identifierName, file: file, basename: basename };
24+
}
25+
var AddImportDefaultStatement = (function () {
26+
function AddImportDefaultStatement() {
27+
this.key = AddImportDefaultStatement.name;
28+
}
29+
AddImportDefaultStatement.prototype.canProvideFix = function (info) {
30+
var relevantError = info.positionErrors.filter(function (x) { return x.code == 2304; })[0];
31+
if (!relevantError)
32+
return;
33+
if (info.positionNode.kind !== ts.SyntaxKind.Identifier)
34+
return;
35+
var matches = getIdentifierAndFileNames(relevantError, info.project);
36+
if (!matches)
37+
return;
38+
var identifierName = matches.identifierName, file = matches.file;
39+
return file ? { display: "import " + identifierName + " from \"" + file + "\"" } : undefined;
40+
};
41+
AddImportDefaultStatement.prototype.provideFix = function (info) {
42+
var relevantError = info.positionErrors.filter(function (x) { return x.code == 2304; })[0];
43+
var identifier = info.positionNode;
44+
var identifierName = identifier.text;
45+
var fileNameforFix = getIdentifierAndFileNames(relevantError, info.project);
46+
var refactorings = [{
47+
span: {
48+
start: 0,
49+
length: 0
50+
},
51+
newText: "import " + identifierName + " from \"" + fileNameforFix.file + "\";" + os_1.EOL,
52+
filePath: info.sourceFile.fileName
53+
}];
54+
return refactorings;
55+
};
56+
return AddImportDefaultStatement;
57+
}());
58+
exports.AddImportDefaultStatement = AddImportDefaultStatement;

dist/main/lang/modules/getPathCompletions.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ function getExternalModuleNames(program) {
1414
});
1515
return entries;
1616
}
17+
function formatImportPath(sourcePath) {
18+
sourcePath = sourcePath.replace(/\.d$/, "");
19+
sourcePath = sourcePath.replace(/.*\/node_modules\//, "");
20+
return sourcePath;
21+
}
1722
function getPathCompletions(query) {
1823
var project = query.project;
1924
var sourceDir = path.dirname(query.filePath);
@@ -30,7 +35,7 @@ function getPathCompletions(query) {
3035
filePaths.forEach(function (p) {
3136
files.push({
3237
name: path.basename(p, '.ts'),
33-
relativePath: tsconfig.removeExt(tsconfig.makeRelativePath(sourceDir, p)),
38+
relativePath: formatImportPath(tsconfig.removeExt(tsconfig.makeRelativePath(sourceDir, p))),
3439
fullPath: p
3540
});
3641
});

lib/main/lang/fixmyts/quickFixRegistry.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {QuickFix} from "./quickFix";
44
*/
55
import {AddClassMember} from "./quickFixes/addClassMember";
66
import {AddClassMethod} from "./quickFixes/addClassMethod";
7+
import {AddImportDefaultStatement} from "./quickFixes/addImportDefaultStatement";
78
import {AddImportFromStatement} from "./quickFixes/addImportFromStatement";
89
import {AddImportStatement} from "./quickFixes/addImportStatement";
910
import {EqualsToEquals} from "./quickFixes/equalsToEquals";
@@ -19,6 +20,7 @@ import {SingleLineCommentToJsdoc} from "./quickFixes/singleLineCommentToJsdoc";
1920
export var allQuickFixes: QuickFix[] = [
2021
new AddClassMethod(),
2122
new AddClassMember(),
23+
new AddImportDefaultStatement(),
2224
new AddImportFromStatement(),
2325
new AddImportStatement(),
2426
new WrapInProperty(),
@@ -31,4 +33,4 @@ export var allQuickFixes: QuickFix[] = [
3133
new TypeAssertPropertyAccessToType(),
3234
new ImplementInterface(),
3335
new SingleLineCommentToJsdoc()
34-
];
36+
];
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import {QuickFix, QuickFixQueryInformation, Refactoring, CanProvideFixResponse} from "../quickFix";
2+
import * as ast from "../astUtils";
3+
import {EOL } from "os";
4+
var { displayPartsToString, typeToDisplayParts } = ts;
5+
import path = require('path');
6+
import {Project} from "../../core/project";
7+
8+
import {getPathCompletions} from "../../modules/getPathCompletions";
9+
10+
function getIdentifierAndFileNames(error: ts.Diagnostic, project: Project) {
11+
12+
var errorText: string = <any>error.messageText;
13+
14+
// We don't support error chains yet
15+
if (typeof errorText !== 'string') {
16+
return undefined;
17+
};
18+
19+
var match = errorText.match(/Cannot find name \'(\w+)\'./);
20+
21+
// If for whatever reason the error message doesn't match
22+
if (!match) return;
23+
24+
var [, identifierName] = match;
25+
var {files} = getPathCompletions({
26+
project,
27+
filePath: error.file.fileName,
28+
prefix: identifierName,
29+
includeExternalModules: false
30+
});
31+
var file = files.length > 0 ? files[0].relativePath : undefined;
32+
var basename = files.length > 0 ? files[0].name : undefined;
33+
return { identifierName, file, basename };
34+
}
35+
36+
export class AddImportDefaultStatement implements QuickFix {
37+
key = AddImportDefaultStatement.name;
38+
39+
canProvideFix(info: QuickFixQueryInformation): CanProvideFixResponse {
40+
var relevantError = info.positionErrors.filter(x => x.code == 2304)[0];
41+
if (!relevantError) return;
42+
if (info.positionNode.kind !== ts.SyntaxKind.Identifier) return;
43+
var matches = getIdentifierAndFileNames(relevantError, info.project);
44+
if (!matches) return;
45+
var { identifierName, file} = matches;
46+
return file ? { display: `import ${identifierName} from \"${file}\"` } : undefined;
47+
}
48+
49+
provideFix(info: QuickFixQueryInformation): Refactoring[] {
50+
var relevantError = info.positionErrors.filter(x => x.code == 2304)[0];
51+
var identifier = <ts.Identifier>info.positionNode;
52+
53+
var identifierName = identifier.text;
54+
var fileNameforFix = getIdentifierAndFileNames(relevantError, info.project);
55+
// Add stuff at the top of the file
56+
let refactorings: Refactoring[] = [{
57+
span: {
58+
start: 0,
59+
length: 0
60+
},
61+
newText: `import ${identifierName} from \"${fileNameforFix.file}\";${EOL}`,
62+
filePath: info.sourceFile.fileName
63+
}];
64+
65+
// Also refactor the variable name to match the file name
66+
// TODO: the following code only takes into account location
67+
// There may be other locations where this is used.
68+
// Better that they trigger a *rename* explicitly later if they want to rename the variable
69+
// if (identifierName !== fileNameforFix.basename) {
70+
// refactorings.push({
71+
// span: {
72+
// start: identifier.getStart(),
73+
// length: identifier.end - identifier.getStart()
74+
// },
75+
// newText: fileNameforFix.basename,
76+
// filePath: info.srcFile.fileName
77+
// })
78+
// }
79+
80+
return refactorings;
81+
}
82+
}

lib/main/lang/fixmyts/quickFixes/addImportFromStatement.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,21 @@ export class AddImportFromStatement implements QuickFix {
3737
key = AddImportFromStatement.name;
3838

3939
canProvideFix(info: QuickFixQueryInformation): CanProvideFixResponse {
40-
var relevantError = info.positionErrors.filter(x=> x.code == 2304)[0];
40+
var relevantError = info.positionErrors.filter(x => x.code == 2304)[0];
4141
if (!relevantError) return;
4242
if (info.positionNode.kind !== ts.SyntaxKind.Identifier) return;
4343
var matches = getIdentifierAndFileNames(relevantError, info.project);
4444
if (!matches) return;
45-
4645
var { identifierName, file} = matches;
4746
return file ? { display: `import {${identifierName}} from \"${file}\"` } : undefined;
4847
}
4948

5049
provideFix(info: QuickFixQueryInformation): Refactoring[] {
51-
var relevantError = info.positionErrors.filter(x=> x.code == 2304)[0];
50+
var relevantError = info.positionErrors.filter(x => x.code == 2304)[0];
5251
var identifier = <ts.Identifier>info.positionNode;
5352

5453
var identifierName = identifier.text;
5554
var fileNameforFix = getIdentifierAndFileNames(relevantError, info.project);
56-
5755
// Add stuff at the top of the file
5856
let refactorings: Refactoring[] = [{
5957
span: {

lib/main/lang/fixmyts/quickFixes/addImportStatement.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class AddImportStatement implements QuickFix {
3737
key = AddImportStatement.name;
3838

3939
canProvideFix(info: QuickFixQueryInformation): CanProvideFixResponse {
40-
var relevantError = info.positionErrors.filter(x=> x.code == 2304)[0];
40+
var relevantError = info.positionErrors.filter(x => x.code == 2304)[0];
4141
if (!relevantError) return;
4242
if (info.positionNode.kind !== ts.SyntaxKind.Identifier) return;
4343
var matches = getIdentifierAndFileNames(relevantError, info.project);
@@ -48,7 +48,7 @@ export class AddImportStatement implements QuickFix {
4848
}
4949

5050
provideFix(info: QuickFixQueryInformation): Refactoring[] {
51-
var relevantError = info.positionErrors.filter(x=> x.code == 2304)[0];
51+
var relevantError = info.positionErrors.filter(x => x.code == 2304)[0];
5252
var identifier = <ts.Identifier>info.positionNode;
5353

5454
var identifierName = identifier.text;

lib/main/lang/modules/getPathCompletions.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ function getExternalModuleNames(program: ts.Program): string[] {
2121
return entries;
2222
}
2323

24+
function formatImportPath(sourcePath: string): string {
25+
sourcePath = sourcePath.replace(/\.d$/, ""); // tsconfig.removeExt can convert d.ts file path into the filepath end with `.d`;
26+
sourcePath = sourcePath.replace(/.*\/node_modules\//, "");
27+
return sourcePath;
28+
}
29+
2430
export interface GetRelativePathsInProjectResponse {
2531
files: {
2632
name: string;
@@ -40,7 +46,7 @@ export interface GetPathCompletions {
4046
export function getPathCompletions(query: GetPathCompletions): GetRelativePathsInProjectResponse {
4147
var project = query.project;
4248
var sourceDir = path.dirname(query.filePath);
43-
var filePaths = project.projectFile.project.files.filter(p=> p !== query.filePath);
49+
var filePaths = project.projectFile.project.files.filter(p => p !== query.filePath);
4450
var files: {
4551
name: string;
4652
relativePath: string;
@@ -49,17 +55,17 @@ export function getPathCompletions(query: GetPathCompletions): GetRelativePathsI
4955

5056
if (query.includeExternalModules) {
5157
var externalModules = getExternalModuleNames(project.languageService.getProgram());
52-
externalModules.forEach(e=> files.push({
58+
externalModules.forEach(e => files.push({
5359
name: `${e}`,
5460
relativePath: e,
5561
fullPath: e
5662
}));
5763
}
5864

59-
filePaths.forEach(p=> {
65+
filePaths.forEach(p => {
6066
files.push({
6167
name: path.basename(p, '.ts'),
62-
relativePath: tsconfig.removeExt(tsconfig.makeRelativePath(sourceDir, p)),
68+
relativePath: formatImportPath(tsconfig.removeExt(tsconfig.makeRelativePath(sourceDir, p))),
6369
fullPath: p
6470
});
6571
});
@@ -75,4 +81,4 @@ export function getPathCompletions(query: GetPathCompletions): GetRelativePathsI
7581
};
7682

7783
return response;
78-
}
84+
}

lib/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
"./main/lang/fixmyts/quickFix.ts",
7474
"./main/lang/fixmyts/quickFixes/addClassMember.ts",
7575
"./main/lang/fixmyts/quickFixes/addClassMethod.ts",
76+
"./main/lang/fixmyts/quickFixes/addImportDefaultStatement.ts",
7677
"./main/lang/fixmyts/quickFixes/addImportFromStatement.ts",
7778
"./main/lang/fixmyts/quickFixes/addImportStatement.ts",
7879
"./main/lang/fixmyts/quickFixes/equalsToEquals.ts",

0 commit comments

Comments
 (0)