Skip to content

Commit 4f404ad

Browse files
author
Paul van Brenk
committed
Implement codefixes in tsserver
1 parent 20dea29 commit 4f404ad

3 files changed

Lines changed: 148 additions & 3 deletions

File tree

src/server/protocol.d.ts

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,53 @@ declare namespace ts.server.protocol {
203203
position?: number;
204204
}
205205

206+
/**
207+
* Request for the available codefixed at a specific position.
208+
*/
209+
export interface CodeFixRequest extends Request {
210+
arguments: CodeFixRequestArgs;
211+
}
212+
213+
/**
214+
* Instances of this interface specify errorcodes on a specific location in a sourcefile.
215+
*/
216+
export interface CodeFixRequestArgs extends FileRequestArgs {
217+
/**
218+
* The line number for the request (1-based).
219+
*/
220+
startLine?: number;
221+
222+
/**
223+
* The character offset (on the line) for the request (1-based).
224+
*/
225+
startOffset?: number;
226+
227+
/**
228+
* Position (can be specified instead of line/offset pair)
229+
*/
230+
startPosition?: number;
231+
232+
/**
233+
* The line number for the request (1-based).
234+
*/
235+
endLine?: number;
236+
237+
/**
238+
* The character offset (on the line) for the request (1-based).
239+
*/
240+
endOffset?: number;
241+
242+
/**
243+
* Position (can be specified instead of line/offset pair)
244+
*/
245+
endPosition?: number;
246+
247+
/**
248+
* Errorcodes we want to get the fixes for.
249+
*/
250+
errorCodes?: string[]
251+
}
252+
206253
/**
207254
* A request whose arguments specify a file location (file, line, col).
208255
*/
@@ -428,7 +475,6 @@ declare namespace ts.server.protocol {
428475
findInStrings?: boolean;
429476
}
430477

431-
432478
/**
433479
* Rename request; value of command field is "rename". Return
434480
* response giving the file locations that reference the symbol
@@ -873,6 +919,18 @@ declare namespace ts.server.protocol {
873919
newText: string;
874920
}
875921

922+
export interface FileCodeEdits {
923+
fileName: string;
924+
textChanges: CodeEdit[];
925+
}
926+
927+
export interface CodeActionResponse extends Response {
928+
/** Description of the code action to display in the UI of the editor */
929+
description: string;
930+
/** Text changes to apply to each file as part of the code action */
931+
changes: FileCodeEdits[];
932+
}
933+
876934
/**
877935
* Format and format on key response message.
878936
*/
@@ -1518,4 +1576,40 @@ declare namespace ts.server.protocol {
15181576
export interface NavBarResponse extends Response {
15191577
body?: NavigationBarItem[];
15201578
}
1579+
1580+
export interface CodeAction {
1581+
/**
1582+
* Description of the code action to display in the UI of the editor.
1583+
*/
1584+
description: string;
1585+
1586+
/**
1587+
* Changes to apply to each file as part of the code action.
1588+
*/
1589+
changes: FileTextChanges[]
1590+
}
1591+
1592+
export interface FileTextChanges {
1593+
/**
1594+
* File to apply the change to.
1595+
*/
1596+
fileName: string;
1597+
1598+
/**
1599+
* Changes to apply to the file.
1600+
*/
1601+
textChanges: TextChange[];
1602+
}
1603+
1604+
export class TextChange {
1605+
/**
1606+
* The span for the text change.
1607+
*/
1608+
span: TextSpan;
1609+
1610+
/**
1611+
* New text for the span, can be an empty string if we want to delete text.
1612+
*/
1613+
newText: string;
1614+
}
15211615
}

src/server/session.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ namespace ts.server {
134134
export const NameOrDottedNameSpan = "nameOrDottedNameSpan";
135135
export const BreakpointStatement = "breakpointStatement";
136136
export const CompilerOptionsForInferredProjects = "compilerOptionsForInferredProjects";
137+
export const GetCodeFixes = "getCodeFixes";
138+
export const GetCodeFixesFull = "getCodeFixes-full";
137139
}
138140

139141
export function formatMessage<T extends protocol.Message>(msg: T, logger: server.Logger, byteLength: (s: string, encoding: string) => number, newLine: string): string {
@@ -751,7 +753,7 @@ namespace ts.server {
751753
return this.getFileAndProjectWorker(args.file, args.projectFileName, /*refreshInferredProjects*/ false, errorOnMissingProject);
752754
}
753755

754-
private getFileAndProjectWorker(uncheckedFileName: string, projectFileName: string, refreshInferredProjects: boolean, errorOnMissingProject: boolean) {
756+
private getFileAndProjectWorker(uncheckedFileName: string, projectFileName: string, refreshInferredProjects: boolean, errorOnMissingProject: boolean) {
755757
const file = toNormalizedPath(uncheckedFileName);
756758
const project: Project = this.getProject(projectFileName) || this.projectService.getDefaultProjectForFile(file, refreshInferredProjects);
757759
if (!project && errorOnMissingProject) {
@@ -1199,6 +1201,48 @@ namespace ts.server {
11991201
}
12001202
}
12011203

1204+
private getCodeFixes(args: protocol.CodeFixRequestArgs, simplifiedResult: boolean): protocol.CodeAction[] | CodeAction[] {
1205+
const { file, project } = this.getFileAndProjectWithoutRefreshingInferredProjects(args);
1206+
1207+
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
1208+
const startPosition = getStartPosition();
1209+
const endPosition = getEndPosition();
1210+
1211+
const codeActions = project.getLanguageService().getCodeFixesAtPosition(file, startPosition, endPosition, args.errorCodes);
1212+
if (!codeActions) {
1213+
return undefined;
1214+
}
1215+
if (simplifiedResult) {
1216+
return codeActions.map(mapCodeAction);
1217+
} else {
1218+
return codeActions;
1219+
}
1220+
1221+
function mapCodeAction(source: CodeAction): protocol.CodeAction {
1222+
return {
1223+
description: source.description,
1224+
changes: source.changes.map(change => ({
1225+
fileName: change.fileName,
1226+
textChanges: change.textChanges.map(textChange => ({
1227+
span: {
1228+
start: scriptInfo.positionToLineOffset(textChange.span.start),
1229+
end: scriptInfo.positionToLineOffset(textChange.span.start + textChange.span.length)
1230+
},
1231+
newText: textChange.newText
1232+
}))
1233+
}))
1234+
};
1235+
}
1236+
1237+
function getStartPosition() {
1238+
return args.startPosition !== undefined ? args.startPosition : scriptInfo.lineOffsetToPosition(args.startLine, args.startOffset);
1239+
}
1240+
1241+
function getEndPosition() {
1242+
return args.endPosition !== undefined ? args.endPosition : scriptInfo.lineOffsetToPosition(args.endLine, args.endOffset);
1243+
}
1244+
}
1245+
12021246
private getBraceMatching(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.TextSpan[] | TextSpan[] {
12031247
const { file, project } = this.getFileAndProjectWithoutRefreshingInferredProjects(args);
12041248

@@ -1521,6 +1565,12 @@ namespace ts.server {
15211565
[CommandNames.ReloadProjects]: (request: protocol.ReloadProjectsRequest) => {
15221566
this.projectService.reloadProjects();
15231567
return this.notRequired();
1568+
},
1569+
[CommandNames.GetCodeFixes]: (request: protocol.CodeFixRequest) => {
1570+
return this.requiredResponse(this.getCodeFixes(request.arguments, /*simplifiedResult*/ true));
1571+
},
1572+
[CommandNames.GetCodeFixesFull]: (request: protocol.CodeFixRequest) => {
1573+
return this.requiredResponse(this.getCodeFixes(request.arguments, /*simplifiedResult*/ false));
15241574
}
15251575
});
15261576

src/server/utilities.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ namespace ts.server {
201201
dispose: (): any => throwLanguageServiceIsDisabledError(),
202202
getCompletionEntrySymbol: (): any => throwLanguageServiceIsDisabledError(),
203203
getImplementationAtPosition: (): any => throwLanguageServiceIsDisabledError(),
204-
getSourceFile: (): any => throwLanguageServiceIsDisabledError()
204+
getSourceFile: (): any => throwLanguageServiceIsDisabledError(),
205+
getCodeFixesAtPosition: (): any => throwLanguageServiceIsDisabledError()
205206
};
206207

207208
export interface ServerLanguageServiceHost {

0 commit comments

Comments
 (0)