Skip to content

Pvb/refactor/jsdoc #11951

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Jakefile.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@ var servicesSources = [
"formatting/rulesMap.ts",
"formatting/rulesProvider.ts",
"formatting/smartIndenter.ts",
"formatting/tokenRange.ts"
"formatting/tokenRange.ts",
"coderefactorings/codeRefactoringProvider.ts",
"coderefactorings/coderefactorings.ts",
].map(function (f) {
return path.join(servicesDirectory, f);
}));
Expand Down
12 changes: 12 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3118,5 +3118,17 @@
"Implement inherited abstract class": {
"category": "Message",
"code": 90007
},
"Inline temporary variable": {
"category": "Message",
"code": 91001
},
"Add JSDoc Comments to Class": {
"category": "Message",
"code": 91002
},
"Add JSDoc Comments to Module": {
"category": "Message",
"code": 91003
}
}
59 changes: 59 additions & 0 deletions src/harness/fourslash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ namespace FourSlash {
end: number;
}

export interface ExpectedFileChange {
fileName: string;
expectedText: string;
}

export import IndentStyle = ts.IndentStyle;

const entityMap = ts.createMap({
Expand Down Expand Up @@ -2060,6 +2065,56 @@ namespace FourSlash {
}
}

public verifyRefactoringAtPosition(expectedChanges: ExpectedFileChange[]) {
// file the refactoring is triggered from
const sourceFileName = this.activeFile.fileName;

const markers = this.getMarkers();
if (markers.length < 1 || markers.length > 2) {
this.raiseError(`Expected 1 or 2 markers, actually found: ${markers.length}`);
}
const start = markers[0].position;
const end = markers[1] ? markers[1].position : markers[0].position;

const actualRefactorings = this.languageService.getCodeRefactoringsAtPosition(sourceFileName, start, end, this.languageService);
if (!actualRefactorings || actualRefactorings.length == 0) {
this.raiseError("No code refactorings found.");
}

// for now only assume a single result for each refactoring
if (actualRefactorings.length > 1) {
this.raiseError("More than 1 refactoring returned.");
}

// for each file:
// * optionally apply the changes
// * check if the new contents match the expected contents
// * if we applied changes, but don't check the content raise an error
ts.forEach(this.testData.files, file => {
const refactorForFile = ts.find(actualRefactorings[0].changes, change => {
return change.fileName == file.fileName;
});

if (refactorForFile) {
this.applyEdits(file.fileName, refactorForFile.textChanges, /*isFormattingEdit*/ false);
}

const expectedFile = ts.find(expectedChanges, expected => {
const name = expected.fileName;
const fullName = name.indexOf("/") === -1 ? (this.basePath + "/" + name) : name;
return fullName === file.fileName;
});

if (refactorForFile && !expectedFile) {
this.raiseError(`Applied changes to '${file.fileName}' which was not expected.`);
}
const actualText = this.getFileContent(file.fileName);
if (this.removeWhitespace(expectedFile.expectedText) !== this.removeWhitespace(actualText)) {
this.raiseError(`Actual text doesn't match expected text. Actual: '${actualText}' Expected: '${expectedFile.expectedText}'`);
}
});
}

public verifyDocCommentTemplate(expected?: ts.TextInsertion) {
const name = "verifyDocCommentTemplate";
const actual = this.languageService.getDocCommentTemplateAtPosition(this.activeFile.fileName, this.currentCaretPosition);
Expand Down Expand Up @@ -3329,6 +3384,10 @@ namespace FourSlashInterface {
this.state.verifyCodeFixAtPosition(expectedText, errorCode);
}

public refactoringsAtPostion(expectedChanges: FourSlash.ExpectedFileChange[]): void {
this.state.verifyRefactoringAtPosition(expectedChanges);
}

public navigationBar(json: any) {
this.state.verifyNavigationBar(json);
}
Expand Down
6 changes: 4 additions & 2 deletions src/harness/harnessLanguageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,6 @@ namespace Harness.LanguageService {
getNavigationTree(fileName: string): ts.NavigationTree {
return unwrapJSONCallResult(this.shim.getNavigationTree(fileName));
}

getOutliningSpans(fileName: string): ts.OutliningSpan[] {
return unwrapJSONCallResult(this.shim.getOutliningSpans(fileName));
}
Expand Down Expand Up @@ -487,7 +486,10 @@ namespace Harness.LanguageService {
return unwrapJSONCallResult(this.shim.isValidBraceCompletionAtPosition(fileName, position, openingBrace));
}
getCodeFixesAtPosition(): ts.CodeAction[] {
throw new Error("Not supported on the shim.");
throw new Error("getCodeFixesAtPosition not supported on the shim.");
}
getCodeRefactoringsAtPosition(): ts.CodeAction[] {
throw new Error("getCodeRefactoringsAtPosition not supported on the shim.");
}
getEmitOutput(fileName: string): ts.EmitOutput {
return unwrapJSONCallResult(this.shim.getEmitOutput(fileName));
Expand Down
18 changes: 18 additions & 0 deletions src/server/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,24 @@ namespace ts.server {
return response.body.map(entry => this.convertCodeActions(entry, fileName));
}

getCodeRefactoringsAtPosition(fileName: string, start: number, end: number): CodeAction[] {
const startLineOffset = this.positionToOneBasedLineOffset(fileName, start);
const endLineOffset = this.positionToOneBasedLineOffset(fileName, end);

const args: protocol.CodeRefactoringRequestArgs = {
file: fileName,
startLine: startLineOffset.line,
startOffset: startLineOffset.offset,
endLine: endLineOffset.line,
endOffset: endLineOffset.offset,
};

const request = this.processRequest<protocol.CodeRefactoringRequest>(CommandNames.GetCodeRefactorings, args);
const response = this.processResponse<protocol.CodeRefactoringResponse>(request);

return response.body.map(entry => this.convertCodeActions(entry, fileName));
}

convertCodeActions(entry: protocol.CodeAction, fileName: string): CodeAction {
return {
description: entry.description,
Expand Down
111 changes: 69 additions & 42 deletions src/server/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ namespace ts.server.protocol {
/* @internal */
export type GetCodeFixesFull = "getCodeFixes-full";
export type GetSupportedCodeFixes = "getSupportedCodeFixes";
export type GetCodeRefactorings = "getCodeRefactorings";
/* @internal */
export type GetCodeRefactoringsFull = "getCodeRefactorings-full";
}

/**
Expand Down Expand Up @@ -394,54 +397,83 @@ namespace ts.server.protocol {
position?: number;
}

export interface CodeChangeRequestArgs extends FileRequestArgs {
/**
* The line number for the request (1-based).
*/
startLine: number;

/**
* The character offset (on the line) for the request (1-based).
*/
startOffset: number;

/**
* Position (can be specified instead of line/offset pair)
*/
/* @internal */
startPosition?: number;

/**
* The line number for the request (1-based).
*/
endLine: number;

/**
* The character offset (on the line) for the request (1-based).
*/
endOffset: number;

/**
* Position (can be specified instead of line/offset pair)
*/
/* @internal */
endPosition?: number;
}

/**
* Request for the available codefixes at a specific position.
* Request for the available code refactorings at a specific position.
*/
export interface CodeFixRequest extends Request {
command: CommandTypes.GetCodeFixes;
arguments: CodeFixRequestArgs;
export interface CodeRefactoringRequest extends Request {
command: CommandTypes.GetCodeRefactorings;
arguments: CodeRefactoringRequestArgs;
}

/**
* Instances of this interface specify errorcodes on a specific location in a sourcefile.
* Response for GetCoderefactorings request.
*/
export interface CodeFixRequestArgs extends FileRequestArgs {
/**
* The line number for the request (1-based).
*/
startLine: number;

/**
* The character offset (on the line) for the request (1-based).
*/
startOffset: number;
export interface CodeRefactoringResponse extends Response {
body?: CodeAction[];
}

/**
* Position (can be specified instead of line/offset pair)
*/
/* @internal */
startPosition?: number;
/**
* Instances of this interface request the available refactorings for a specific location in a sourcefile.
*/
export interface CodeRefactoringRequestArgs extends CodeChangeRequestArgs {

/**
* The line number for the request (1-based).
*/
endLine: number;
}

/**
* The character offset (on the line) for the request (1-based).
*/
endOffset: number;
/**
* Request for the available codefixes at a specific position.
*/
export interface CodeFixRequest extends Request {
command: CommandTypes.GetCodeFixes;
arguments: CodeFixRequestArgs;
}

/**
* Position (can be specified instead of line/offset pair)
*/
/* @internal */
endPosition?: number;
export interface CodeFixResponse extends Response {
/** The code actions that are available */
body?: CodeAction[];
}

/**
* Errorcodes we want to get the fixes for.
*/
errorCodes?: number[];
/**
* Instances of this interface specify errorcodes for a specific location in a sourcefile.
*/
export interface CodeFixRequestArgs extends CodeChangeRequestArgs {
/**
* Errorcodes we want to get the fixes for.
*/
errorCodes?: number[];
}

/**
Expand Down Expand Up @@ -1367,11 +1399,6 @@ namespace ts.server.protocol {
textChanges: CodeEdit[];
}

export interface CodeFixResponse extends Response {
/** The code actions that are available */
body?: CodeAction[];
}

export interface CodeAction {
/** Description of the code action to display in the UI of the editor */
description: string;
Expand Down
50 changes: 43 additions & 7 deletions src/server/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ namespace ts.server {
export const GetCodeFixes: protocol.CommandTypes.GetCodeFixes = "getCodeFixes";
export const GetCodeFixesFull: protocol.CommandTypes.GetCodeFixesFull = "getCodeFixes-full";
export const GetSupportedCodeFixes: protocol.CommandTypes.GetSupportedCodeFixes = "getSupportedCodeFixes";
export const GetCodeRefactorings: protocol.CommandTypes.GetCodeRefactorings = "getCodeRefactorings";
export const GetCodeRefactoringsFull: protocol.CommandTypes.GetCodeRefactoringsFull = "getCodeRefactorings-full";
}

export function formatMessage<T extends protocol.Message>(msg: T, logger: server.Logger, byteLength: (s: string, encoding: string) => number, newLine: string): string {
Expand Down Expand Up @@ -1119,8 +1121,8 @@ namespace ts.server {
return !items
? undefined
: simplifiedResult
? this.decorateNavigationBarItems(items, project.getScriptInfoForNormalizedPath(file))
: items;
? this.decorateNavigationBarItems(items, project.getScriptInfoForNormalizedPath(file))
: items;
}

private decorateNavigationTree(tree: ts.NavigationTree, scriptInfo: ScriptInfo): protocol.NavigationTree {
Expand All @@ -1146,8 +1148,8 @@ namespace ts.server {
return !tree
? undefined
: simplifiedResult
? this.decorateNavigationTree(tree, project.getScriptInfoForNormalizedPath(file))
: tree;
? this.decorateNavigationTree(tree, project.getScriptInfoForNormalizedPath(file))
: tree;
}

private getNavigateToItems(args: protocol.NavtoRequestArgs, simplifiedResult: boolean): protocol.NavtoItem[] | NavigateToItem[] {
Expand Down Expand Up @@ -1261,6 +1263,34 @@ namespace ts.server {
}
}

private getCodeRefactorings(args: protocol.CodeRefactoringRequestArgs, simplifiedResult: boolean): protocol.CodeAction[] | CodeAction[] {
const { file, project } = this.getFileAndProjectWithoutRefreshingInferredProjects(args);

const scriptInfo = project.getScriptInfoForNormalizedPath(file);
const startPosition = getStartPosition();
const endPosition = getEndPosition();

const languageService = project.getLanguageService();
const codeActions = languageService.getCodeRefactoringsAtPosition(file, startPosition, endPosition, languageService);
if (!codeActions) {
return undefined;
}
if (simplifiedResult) {
return codeActions.map(codeAction => this.mapCodeAction(codeAction, scriptInfo));
}
else {
return codeActions;
}

function getStartPosition() {
return args.startPosition !== undefined ? args.startPosition : scriptInfo.lineOffsetToPosition(args.startLine, args.startOffset);
}

function getEndPosition() {
return args.endPosition !== undefined ? args.endPosition : scriptInfo.lineOffsetToPosition(args.endLine, args.endOffset);
}
}

private mapCodeAction(codeAction: CodeAction, scriptInfo: ScriptInfo): protocol.CodeAction {
return {
description: codeAction.description,
Expand Down Expand Up @@ -1289,8 +1319,8 @@ namespace ts.server {
return !spans
? undefined
: simplifiedResult
? spans.map(span => this.decorateSpan(span, scriptInfo))
: spans;
? spans.map(span => this.decorateSpan(span, scriptInfo))
: spans;
}

getDiagnosticsForProject(delay: number, fileName: string) {
Expand Down Expand Up @@ -1593,7 +1623,13 @@ namespace ts.server {
},
[CommandNames.GetSupportedCodeFixes]: () => {
return this.requiredResponse(this.getSupportedCodeFixes());
}
},
[CommandNames.GetCodeRefactorings]: (request: protocol.CodeRefactoringRequest) => {
return this.requiredResponse(this.getCodeRefactorings(request.arguments, /*simplifiedResult*/ true));
},
[CommandNames.GetCodeRefactoringsFull]: (request: protocol.CodeRefactoringRequest) => {
return this.requiredResponse(this.getCodeRefactorings(request.arguments, /*simplifiedResult*/ false));
},
});

public addProtocolHandler(command: string, handler: (request: protocol.Request) => { response?: any, responseRequired: boolean }) {
Expand Down
3 changes: 2 additions & 1 deletion src/server/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@ namespace ts.server {
getCompletionEntrySymbol: throwLanguageServiceIsDisabledError,
getImplementationAtPosition: throwLanguageServiceIsDisabledError,
getSourceFile: throwLanguageServiceIsDisabledError,
getCodeFixesAtPosition: throwLanguageServiceIsDisabledError
getCodeFixesAtPosition: throwLanguageServiceIsDisabledError,
getCodeRefactoringsAtPosition: throwLanguageServiceIsDisabledError,
};

export interface ServerLanguageServiceHost {
Expand Down
Loading