Skip to content

Commit 94ea825

Browse files
author
Paul van Brenk
committed
Api Changes and simple superfixes
1 parent 269b828 commit 94ea825

6 files changed

Lines changed: 194 additions & 3 deletions

File tree

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/* @internal */
2+
namespace ts {
3+
export interface CodeFix {
4+
name: string;
5+
errorCodes: string[];
6+
getCodeActions(context: CodeFixContext): CodeAction[];
7+
}
8+
9+
export interface CodeFixContext {
10+
errorCode: string;
11+
sourceFile: SourceFile;
12+
span: TextSpan;
13+
checker: TypeChecker;
14+
newLineCharacter: string;
15+
}
16+
17+
export namespace codeFix {
18+
const codeFixes: Map<CodeFix[]> = {};
19+
20+
export function registerCodeFix(action: CodeFix) {
21+
forEach(action.errorCodes, error => {
22+
let fixes = codeFixes[error];
23+
if (!fixes) {
24+
fixes = [];
25+
codeFixes[error] = fixes;
26+
}
27+
fixes.push(action);
28+
});
29+
}
30+
31+
export class CodeFixProvider {
32+
public static getSupportedErrorCodes() {
33+
return getKeys(codeFixes);
34+
}
35+
36+
public getFixes(context: CodeFixContext): CodeAction[] {
37+
const fixes = codeFixes[context.errorCode];
38+
let allActions: CodeAction[] = [];
39+
40+
Debug.assert(fixes && fixes.length > 0, "No fixes found for error: '${errorCode}'.");
41+
42+
forEach(fixes, f => {
43+
const actions = f.getCodeActions(context);
44+
if (actions && actions.length > 0) {
45+
allActions = allActions.concat(actions);
46+
}
47+
});
48+
49+
return allActions;
50+
}
51+
}
52+
}
53+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
///<reference path='..\services.ts' />
2+
///<reference path='codeFixProvider.ts' />
3+
///<reference path='superFixes.ts' />
4+
///<reference path='unusedIdentifierFixes.ts' />
5+
///<reference path='changeExtendsToImplementsFix.ts' />
6+
///<reference path='interfaceFixes.ts' />
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/* @internal */
2+
namespace ts.codeFix {
3+
function getOpenBraceEnd(constructor: ConstructorDeclaration, sourceFile: SourceFile) {
4+
// First token is the open curly, this is where we want to put the 'super' call.
5+
return constructor.body.getFirstToken(sourceFile).getEnd();
6+
}
7+
8+
registerCodeFix({
9+
name: "AddMissingSuperCallFix",
10+
errorCodes: ["TS2377"],
11+
getCodeActions: (context: CodeFixContext) => {
12+
const sourceFile = context.sourceFile;
13+
const token = getTokenAtPosition(sourceFile, context.span.start);
14+
Debug.assert(token.kind === SyntaxKind.ConstructorKeyword, "Failed to find the constructor.");
15+
16+
const newPosition = getOpenBraceEnd(<ConstructorDeclaration>token.parent, sourceFile);
17+
return [{
18+
description: getLocaleSpecificMessage(Diagnostics.Add_missing_super_call),
19+
changes: [{ fileName: sourceFile.fileName, textChanges: [{ newText: "super();", span: { start: newPosition, length: 0 } }] }]
20+
}];
21+
}
22+
});
23+
24+
registerCodeFix({
25+
name: "MakeSuperCallTheFirstStatementInTheConstructor",
26+
errorCodes: ["TS17009"],
27+
getCodeActions: (context: CodeFixContext) => {
28+
const sourceFile = context.sourceFile;
29+
30+
const token = getTokenAtPosition(sourceFile, context.span.start);
31+
const constructor = getContainingFunction(token);
32+
Debug.assert(constructor.kind === SyntaxKind.Constructor, "Failed to find the constructor.");
33+
34+
const superCall = findSuperCall((<ConstructorDeclaration>constructor).body);
35+
Debug.assert(!!superCall, "Failed to find super call.");
36+
37+
const newPosition = getOpenBraceEnd(<ConstructorDeclaration>constructor, sourceFile);
38+
const changes = [{
39+
fileName: sourceFile.fileName, textChanges: [{
40+
newText: superCall.getText(sourceFile),
41+
span: { start: newPosition, length: 0 }
42+
},
43+
{
44+
newText: "",
45+
span: { start: superCall.getStart(sourceFile), length: superCall.getWidth(sourceFile) }
46+
}]
47+
}];
48+
49+
return [{
50+
description: getLocaleSpecificMessage(Diagnostics.Make_super_call_the_first_statement_in_the_constructor),
51+
changes
52+
}];
53+
54+
function findSuperCall(n: Node): Node {
55+
if (n.kind === SyntaxKind.ExpressionStatement && isSuperCallExpression((<ExpressionStatement>n).expression)) {
56+
return n;
57+
}
58+
if (isFunctionLike(n)) {
59+
return undefined;
60+
}
61+
return forEachChild(n, findSuperCall);
62+
}
63+
}
64+
});
65+
}

src/services/services.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
/// <reference path='jsTyping.ts' />
1212
/// <reference path='formatting\formatting.ts' />
1313
/// <reference path='formatting\smartIndenter.ts' />
14+
/// <reference path='codefixes\references.ts' />
1415

1516
namespace ts {
1617
/** The version of the language service API */
@@ -1237,6 +1238,8 @@ namespace ts {
12371238

12381239
isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean;
12391240

1241+
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string[]): CodeAction[];
1242+
12401243
getEmitOutput(fileName: string): EmitOutput;
12411244

12421245
getProgram(): Program;
@@ -1283,6 +1286,18 @@ namespace ts {
12831286
newText: string;
12841287
}
12851288

1289+
export interface FileTextChanges {
1290+
fileName: string;
1291+
textChanges: TextChange[];
1292+
}
1293+
1294+
export interface CodeAction {
1295+
/** Description of the code action to display in the UI of the editor */
1296+
description: string;
1297+
/** Text changes to apply to each file as part of the code action */
1298+
changes: FileTextChanges[];
1299+
}
1300+
12861301
export interface TextInsertion {
12871302
newText: string;
12881303
/** The position in newText the caret should point to after the insertion. */
@@ -1886,9 +1901,13 @@ namespace ts {
18861901
};
18871902
}
18881903

1889-
// Cache host information about script should be refreshed
1904+
export function getSupportedCodeFixes() {
1905+
return codeFix.CodeFixProvider.getSupportedErrorCodes();
1906+
}
1907+
1908+
// Cache host information about script Should be refreshed
18901909
// at each language service public entry point, since we don't know when
1891-
// set of scripts handled by the host changes.
1910+
// the set of scripts handled by the host changes.
18921911
class HostCache {
18931912
private fileNameToEntry: FileMap<HostFileInformation>;
18941913
private _compilationSettings: CompilerOptions;
@@ -3022,6 +3041,7 @@ namespace ts {
30223041
documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory())): LanguageService {
30233042

30243043
const syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
3044+
const codeFixProvider: codeFix.CodeFixProvider = new codeFix.CodeFixProvider();
30253045
let ruleProvider: formatting.RulesProvider;
30263046
let program: Program;
30273047
let lastProjectVersion: string;
@@ -7832,6 +7852,30 @@ namespace ts {
78327852
return [];
78337853
}
78347854

7855+
function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string[]): CodeAction[] {
7856+
synchronizeHostData();
7857+
const sourceFile = getValidSourceFile(fileName);
7858+
const checker = program.getTypeChecker();
7859+
let allFixes: CodeAction[] = [];
7860+
7861+
forEach(errorCodes, error => {
7862+
const context = {
7863+
errorCode: error,
7864+
sourceFile: sourceFile,
7865+
span: { start, length: end - start },
7866+
checker: checker,
7867+
newLineCharacter: getNewLineOrDefaultFromHost(host)
7868+
};
7869+
7870+
const fixes = codeFixProvider.getFixes(context);
7871+
if (fixes) {
7872+
allFixes = allFixes.concat(fixes);
7873+
}
7874+
});
7875+
7876+
return allFixes;
7877+
}
7878+
78357879
/**
78367880
* Checks if position points to a valid position to add JSDoc comments, and if so,
78377881
* returns the appropriate template. Otherwise returns an empty string.
@@ -8302,6 +8346,7 @@ namespace ts {
83028346
getFormattingEditsAfterKeystroke,
83038347
getDocCommentTemplateAtPosition,
83048348
isValidBraceCompletionAtPosition,
8349+
getCodeFixesAtPosition,
83058350
getEmitOutput,
83068351
getNonBoundSourceFile,
83078352
getProgram

src/services/shims.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,8 @@ namespace ts {
240240
*/
241241
isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): string;
242242

243+
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string): string;
244+
243245
getEmitOutput(fileName: string): string;
244246
getEmitOutputObject(fileName: string): EmitOutput;
245247
}
@@ -255,6 +257,7 @@ namespace ts {
255257
getTSConfigFileInfo(fileName: string, sourceText: IScriptSnapshot): string;
256258
getDefaultCompilationSettings(): string;
257259
discoverTypings(discoverTypingsJson: string): string;
260+
getSupportedCodeFixes(): string;
258261
}
259262

260263
function logInternalError(logger: Logger, err: Error) {
@@ -901,6 +904,16 @@ namespace ts {
901904
);
902905
}
903906

907+
public getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string): string {
908+
return this.forwardJSONCall(
909+
`getCodeFixesAtPosition( '${fileName}', ${start}, ${end}, ${errorCodes}')`,
910+
() => {
911+
const localErrors: string[] = JSON.parse(errorCodes);
912+
return this.languageService.getCodeFixesAtPosition(fileName, start, end, localErrors);
913+
}
914+
);
915+
}
916+
904917
/// NAVIGATE TO
905918

906919
/** Return a list of symbols that are interesting to navigate to */
@@ -1109,6 +1122,12 @@ namespace ts {
11091122
info.compilerOptions);
11101123
});
11111124
}
1125+
1126+
public getSupportedCodeFixes(): string {
1127+
return this.forwardJSONCall("getSupportedCodeFixes()",
1128+
() => getSupportedCodeFixes()
1129+
);
1130+
}
11121131
}
11131132

11141133
export class TypeScriptServicesFactory implements ShimFactory {

src/services/tsconfig.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
"formatting/rulesMap.ts",
5353
"formatting/rulesProvider.ts",
5454
"formatting/smartIndenter.ts",
55-
"formatting/tokenRange.ts"
55+
"formatting/tokenRange.ts",
56+
"codeFixes/codeFixProvider.ts",
57+
"codeFixes/references.ts",
58+
"codeFixes/superFixes.ts"
5659
]
5760
}

0 commit comments

Comments
 (0)