Api Changes and simple superfixes

This commit is contained in:
Paul van Brenk 2016-08-05 16:20:16 -07:00
parent 269b828538
commit 94ea825f95
6 changed files with 194 additions and 3 deletions

View File

@ -0,0 +1,53 @@
/* @internal */
namespace ts {
export interface CodeFix {
name: string;
errorCodes: string[];
getCodeActions(context: CodeFixContext): CodeAction[];
}
export interface CodeFixContext {
errorCode: string;
sourceFile: SourceFile;
span: TextSpan;
checker: TypeChecker;
newLineCharacter: string;
}
export namespace codeFix {
const codeFixes: Map<CodeFix[]> = {};
export function registerCodeFix(action: CodeFix) {
forEach(action.errorCodes, error => {
let fixes = codeFixes[error];
if (!fixes) {
fixes = [];
codeFixes[error] = fixes;
}
fixes.push(action);
});
}
export class CodeFixProvider {
public static getSupportedErrorCodes() {
return getKeys(codeFixes);
}
public getFixes(context: CodeFixContext): CodeAction[] {
const fixes = codeFixes[context.errorCode];
let allActions: CodeAction[] = [];
Debug.assert(fixes && fixes.length > 0, "No fixes found for error: '${errorCode}'.");
forEach(fixes, f => {
const actions = f.getCodeActions(context);
if (actions && actions.length > 0) {
allActions = allActions.concat(actions);
}
});
return allActions;
}
}
}
}

View File

@ -0,0 +1,6 @@
///<reference path='..\services.ts' />
///<reference path='codeFixProvider.ts' />
///<reference path='superFixes.ts' />
///<reference path='unusedIdentifierFixes.ts' />
///<reference path='changeExtendsToImplementsFix.ts' />
///<reference path='interfaceFixes.ts' />

View File

@ -0,0 +1,65 @@
/* @internal */
namespace ts.codeFix {
function getOpenBraceEnd(constructor: ConstructorDeclaration, sourceFile: SourceFile) {
// First token is the open curly, this is where we want to put the 'super' call.
return constructor.body.getFirstToken(sourceFile).getEnd();
}
registerCodeFix({
name: "AddMissingSuperCallFix",
errorCodes: ["TS2377"],
getCodeActions: (context: CodeFixContext) => {
const sourceFile = context.sourceFile;
const token = getTokenAtPosition(sourceFile, context.span.start);
Debug.assert(token.kind === SyntaxKind.ConstructorKeyword, "Failed to find the constructor.");
const newPosition = getOpenBraceEnd(<ConstructorDeclaration>token.parent, sourceFile);
return [{
description: getLocaleSpecificMessage(Diagnostics.Add_missing_super_call),
changes: [{ fileName: sourceFile.fileName, textChanges: [{ newText: "super();", span: { start: newPosition, length: 0 } }] }]
}];
}
});
registerCodeFix({
name: "MakeSuperCallTheFirstStatementInTheConstructor",
errorCodes: ["TS17009"],
getCodeActions: (context: CodeFixContext) => {
const sourceFile = context.sourceFile;
const token = getTokenAtPosition(sourceFile, context.span.start);
const constructor = getContainingFunction(token);
Debug.assert(constructor.kind === SyntaxKind.Constructor, "Failed to find the constructor.");
const superCall = findSuperCall((<ConstructorDeclaration>constructor).body);
Debug.assert(!!superCall, "Failed to find super call.");
const newPosition = getOpenBraceEnd(<ConstructorDeclaration>constructor, sourceFile);
const changes = [{
fileName: sourceFile.fileName, textChanges: [{
newText: superCall.getText(sourceFile),
span: { start: newPosition, length: 0 }
},
{
newText: "",
span: { start: superCall.getStart(sourceFile), length: superCall.getWidth(sourceFile) }
}]
}];
return [{
description: getLocaleSpecificMessage(Diagnostics.Make_super_call_the_first_statement_in_the_constructor),
changes
}];
function findSuperCall(n: Node): Node {
if (n.kind === SyntaxKind.ExpressionStatement && isSuperCallExpression((<ExpressionStatement>n).expression)) {
return n;
}
if (isFunctionLike(n)) {
return undefined;
}
return forEachChild(n, findSuperCall);
}
}
});
}

View File

@ -11,6 +11,7 @@
/// <reference path='jsTyping.ts' />
/// <reference path='formatting\formatting.ts' />
/// <reference path='formatting\smartIndenter.ts' />
/// <reference path='codefixes\references.ts' />
namespace ts {
/** The version of the language service API */
@ -1237,6 +1238,8 @@ namespace ts {
isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean;
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string[]): CodeAction[];
getEmitOutput(fileName: string): EmitOutput;
getProgram(): Program;
@ -1283,6 +1286,18 @@ namespace ts {
newText: string;
}
export interface FileTextChanges {
fileName: string;
textChanges: TextChange[];
}
export interface CodeAction {
/** Description of the code action to display in the UI of the editor */
description: string;
/** Text changes to apply to each file as part of the code action */
changes: FileTextChanges[];
}
export interface TextInsertion {
newText: string;
/** The position in newText the caret should point to after the insertion. */
@ -1886,9 +1901,13 @@ namespace ts {
};
}
// Cache host information about script should be refreshed
export function getSupportedCodeFixes() {
return codeFix.CodeFixProvider.getSupportedErrorCodes();
}
// Cache host information about script Should be refreshed
// at each language service public entry point, since we don't know when
// set of scripts handled by the host changes.
// the set of scripts handled by the host changes.
class HostCache {
private fileNameToEntry: FileMap<HostFileInformation>;
private _compilationSettings: CompilerOptions;
@ -3022,6 +3041,7 @@ namespace ts {
documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory())): LanguageService {
const syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
const codeFixProvider: codeFix.CodeFixProvider = new codeFix.CodeFixProvider();
let ruleProvider: formatting.RulesProvider;
let program: Program;
let lastProjectVersion: string;
@ -7832,6 +7852,30 @@ namespace ts {
return [];
}
function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string[]): CodeAction[] {
synchronizeHostData();
const sourceFile = getValidSourceFile(fileName);
const checker = program.getTypeChecker();
let allFixes: CodeAction[] = [];
forEach(errorCodes, error => {
const context = {
errorCode: error,
sourceFile: sourceFile,
span: { start, length: end - start },
checker: checker,
newLineCharacter: getNewLineOrDefaultFromHost(host)
};
const fixes = codeFixProvider.getFixes(context);
if (fixes) {
allFixes = allFixes.concat(fixes);
}
});
return allFixes;
}
/**
* Checks if position points to a valid position to add JSDoc comments, and if so,
* returns the appropriate template. Otherwise returns an empty string.
@ -8302,6 +8346,7 @@ namespace ts {
getFormattingEditsAfterKeystroke,
getDocCommentTemplateAtPosition,
isValidBraceCompletionAtPosition,
getCodeFixesAtPosition,
getEmitOutput,
getNonBoundSourceFile,
getProgram

View File

@ -240,6 +240,8 @@ namespace ts {
*/
isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): string;
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string): string;
getEmitOutput(fileName: string): string;
getEmitOutputObject(fileName: string): EmitOutput;
}
@ -255,6 +257,7 @@ namespace ts {
getTSConfigFileInfo(fileName: string, sourceText: IScriptSnapshot): string;
getDefaultCompilationSettings(): string;
discoverTypings(discoverTypingsJson: string): string;
getSupportedCodeFixes(): string;
}
function logInternalError(logger: Logger, err: Error) {
@ -901,6 +904,16 @@ namespace ts {
);
}
public getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string): string {
return this.forwardJSONCall(
`getCodeFixesAtPosition( '${fileName}', ${start}, ${end}, ${errorCodes}')`,
() => {
const localErrors: string[] = JSON.parse(errorCodes);
return this.languageService.getCodeFixesAtPosition(fileName, start, end, localErrors);
}
);
}
/// NAVIGATE TO
/** Return a list of symbols that are interesting to navigate to */
@ -1109,6 +1122,12 @@ namespace ts {
info.compilerOptions);
});
}
public getSupportedCodeFixes(): string {
return this.forwardJSONCall("getSupportedCodeFixes()",
() => getSupportedCodeFixes()
);
}
}
export class TypeScriptServicesFactory implements ShimFactory {

View File

@ -52,6 +52,9 @@
"formatting/rulesMap.ts",
"formatting/rulesProvider.ts",
"formatting/smartIndenter.ts",
"formatting/tokenRange.ts"
"formatting/tokenRange.ts",
"codeFixes/codeFixProvider.ts",
"codeFixes/references.ts",
"codeFixes/superFixes.ts"
]
}