Support a "getCombinedCodeFix" service (#20338)

* Support a "getCombinedCodeFix" service

* Rename things

* Code review

* Rename things

* Update API baselines

* CodeActionAll -> CombinedCodeActions

* Take a `scope` parameter instead of `fileName` for flexibility

* Renames and bugfixes

* Make API changes internal

* Code review

* Update comment
This commit is contained in:
Andy
2017-12-07 12:14:18 -08:00
committed by GitHub
parent 4902d85479
commit 19ea189b42
119 changed files with 2942 additions and 1789 deletions

View File

@@ -200,7 +200,7 @@ namespace ts.server {
const response = this.processResponse<protocol.CompletionDetailsResponse>(request);
Debug.assert(response.body.length === 1, "Unexpected length of completion details response body.");
const convertedCodeActions = map(response.body[0].codeActions, codeAction => this.convertCodeActions(codeAction, fileName));
const convertedCodeActions = map(response.body[0].codeActions, ({ description, changes }) => ({ description, changes: this.convertChanges(changes, fileName) }));
return { ...response.body[0], codeActions: convertedCodeActions };
}
@@ -553,15 +553,18 @@ namespace ts.server {
return notImplemented();
}
getCodeFixesAtPosition(file: string, start: number, end: number, errorCodes: number[]): CodeAction[] {
getCodeFixesAtPosition(file: string, start: number, end: number, errorCodes: ReadonlyArray<number>): ReadonlyArray<CodeFixAction> {
const args: protocol.CodeFixRequestArgs = { ...this.createFileRangeRequestArgs(file, start, end), errorCodes };
const request = this.processRequest<protocol.CodeFixRequest>(CommandNames.GetCodeFixes, args);
const response = this.processResponse<protocol.CodeFixResponse>(request);
return response.body.map(entry => this.convertCodeActions(entry, file));
// TODO: GH#20538 shouldn't need cast
return (response.body as ReadonlyArray<protocol.CodeFixAction>).map(({ description, changes, fixId }) => ({ description, changes: this.convertChanges(changes, file), fixId }));
}
getCombinedCodeFix = notImplemented;
applyCodeActionCommand = notImplemented;
private createFileLocationOrRangeRequestArgs(positionOrRange: number | TextRange, fileName: string): protocol.FileLocationOrRangeRequestArgs {
@@ -638,14 +641,11 @@ namespace ts.server {
});
}
convertCodeActions(entry: protocol.CodeAction, fileName: string): CodeAction {
return {
description: entry.description,
changes: entry.changes.map(change => ({
fileName: change.fileName,
textChanges: change.textChanges.map(textChange => this.convertTextChangeToCodeEdit(textChange, fileName))
}))
};
private convertChanges(changes: protocol.FileCodeEdits[], fileName: string): FileTextChanges[] {
return changes.map(change => ({
fileName: change.fileName,
textChanges: change.textChanges.map(textChange => this.convertTextChangeToCodeEdit(textChange, fileName))
}));
}
convertTextChangeToCodeEdit(change: protocol.CodeEdit, fileName: string): ts.TextChange {

View File

@@ -100,9 +100,14 @@ namespace ts.server.protocol {
BreakpointStatement = "breakpointStatement",
CompilerOptionsForInferredProjects = "compilerOptionsForInferredProjects",
GetCodeFixes = "getCodeFixes",
ApplyCodeActionCommand = "applyCodeActionCommand",
/* @internal */
GetCodeFixesFull = "getCodeFixes-full",
// TODO: GH#20538
/* @internal */
GetCombinedCodeFix = "getCombinedCodeFix",
/* @internal */
GetCombinedCodeFixFull = "getCombinedCodeFix-full",
ApplyCodeActionCommand = "applyCodeActionCommand",
GetSupportedCodeFixes = "getSupportedCodeFixes",
GetApplicableRefactors = "getApplicableRefactors",
@@ -552,6 +557,19 @@ namespace ts.server.protocol {
arguments: CodeFixRequestArgs;
}
// TODO: GH#20538
/* @internal */
export interface GetCombinedCodeFixRequest extends Request {
command: CommandTypes.GetCombinedCodeFix;
arguments: GetCombinedCodeFixRequestArgs;
}
// TODO: GH#20538
/* @internal */
export interface GetCombinedCodeFixResponse extends Response {
body: CombinedCodeActions;
}
export interface ApplyCodeActionCommandRequest extends Request {
command: CommandTypes.ApplyCodeActionCommand;
arguments: ApplyCodeActionCommandRequestArgs;
@@ -601,7 +619,21 @@ namespace ts.server.protocol {
/**
* Errorcodes we want to get the fixes for.
*/
errorCodes?: number[];
errorCodes?: ReadonlyArray<number>;
}
// TODO: GH#20538
/* @internal */
export interface GetCombinedCodeFixRequestArgs {
scope: GetCombinedCodeFixScope;
fixId: {};
}
// TODO: GH#20538
/* @internal */
export interface GetCombinedCodeFixScope {
type: "file";
args: FileRequestArgs;
}
export interface ApplyCodeActionCommandRequestArgs {
@@ -1587,7 +1619,7 @@ namespace ts.server.protocol {
export interface CodeFixResponse extends Response {
/** The code actions that are available */
body?: CodeAction[];
body?: CodeAction[]; // TODO: GH#20538 CodeFixAction[]
}
export interface CodeAction {
@@ -1599,6 +1631,23 @@ namespace ts.server.protocol {
commands?: {}[];
}
// TODO: GH#20538
/* @internal */
export interface CombinedCodeActions {
changes: ReadonlyArray<FileCodeEdits>;
commands?: ReadonlyArray<{}>;
}
// TODO: GH#20538
/* @internal */
export interface CodeFixAction extends CodeAction {
/**
* If present, one may call 'getCombinedCodeFix' with this fixId.
* This may be omitted to indicate that the code fix can't be applied in a group.
*/
fixId?: {};
}
/**
* Format and format on key response message.
*/

View File

@@ -1538,18 +1538,14 @@ namespace ts.server {
const oldText = snapshot.getText(0, snapshot.getLength());
mappedRenameLocation = getLocationInNewDocument(oldText, renameFilename, renameLocation, edits);
}
return {
renameLocation: mappedRenameLocation,
renameFilename,
edits: edits.map(change => this.mapTextChangesToCodeEdits(project, change))
};
return { renameLocation: mappedRenameLocation, renameFilename, edits: this.mapTextChangesToCodeEdits(project, edits) };
}
else {
return result;
}
}
private getCodeFixes(args: protocol.CodeFixRequestArgs, simplifiedResult: boolean): protocol.CodeAction[] | CodeAction[] {
private getCodeFixes(args: protocol.CodeFixRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.CodeAction> | ReadonlyArray<CodeAction> {
if (args.errorCodes.length === 0) {
return undefined;
}
@@ -1571,6 +1567,19 @@ namespace ts.server {
}
}
private getCombinedCodeFix({ scope, fixId }: protocol.GetCombinedCodeFixRequestArgs, simplifiedResult: boolean): protocol.CombinedCodeActions | CombinedCodeActions {
Debug.assert(scope.type === "file");
const { file, project } = this.getFileAndProject(scope.args);
const formatOptions = this.projectService.getFormatCodeOptions(file);
const res = project.getLanguageService().getCombinedCodeFix({ type: "file", fileName: file }, fixId, formatOptions);
if (simplifiedResult) {
return { changes: this.mapTextChangesToCodeEdits(project, res.changes), commands: res.commands };
}
else {
return res;
}
}
private applyCodeActionCommand(args: protocol.ApplyCodeActionCommandRequestArgs): {} {
const commands = args.command as CodeActionCommand | CodeActionCommand[]; // They should be sending back the command we sent them.
for (const command of toArray(commands)) {
@@ -1605,15 +1614,15 @@ namespace ts.server {
}
private mapCodeAction({ description, changes: unmappedChanges, commands }: CodeAction, scriptInfo: ScriptInfo): protocol.CodeAction {
const changes = unmappedChanges.map(change => ({
fileName: change.fileName,
textChanges: change.textChanges.map(textChange => this.convertTextChangeToCodeEdit(textChange, scriptInfo))
}));
const changes = unmappedChanges.map(change => this.mapTextChangesToCodeEditsUsingScriptinfo(change, scriptInfo));
return { description, changes, commands };
}
private mapTextChangesToCodeEdits(project: Project, textChanges: FileTextChanges): protocol.FileCodeEdits {
const scriptInfo = project.getScriptInfoForNormalizedPath(toNormalizedPath(textChanges.fileName));
private mapTextChangesToCodeEdits(project: Project, textChanges: ReadonlyArray<FileTextChanges>): protocol.FileCodeEdits[] {
return textChanges.map(change => this.mapTextChangesToCodeEditsUsingScriptinfo(change, project.getScriptInfoForNormalizedPath(toNormalizedPath(change.fileName))));
}
private mapTextChangesToCodeEditsUsingScriptinfo(textChanges: FileTextChanges, scriptInfo: ScriptInfo): protocol.FileCodeEdits {
return {
fileName: textChanges.fileName,
textChanges: textChanges.textChanges.map(textChange => this.convertTextChangeToCodeEdit(textChange, scriptInfo))
@@ -1960,6 +1969,12 @@ namespace ts.server {
[CommandNames.GetCodeFixesFull]: (request: protocol.CodeFixRequest) => {
return this.requiredResponse(this.getCodeFixes(request.arguments, /*simplifiedResult*/ false));
},
[CommandNames.GetCombinedCodeFix]: (request: protocol.GetCombinedCodeFixRequest) => {
return this.requiredResponse(this.getCombinedCodeFix(request.arguments, /*simplifiedResult*/ true));
},
[CommandNames.GetCombinedCodeFixFull]: (request: protocol.GetCombinedCodeFixRequest) => {
return this.requiredResponse(this.getCombinedCodeFix(request.arguments, /*simplifiedResult*/ false));
},
[CommandNames.ApplyCodeActionCommand]: (request: protocol.ApplyCodeActionCommandRequest) => {
return this.requiredResponse(this.applyCodeActionCommand(request.arguments));
},