Skip to content
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

Refactoring support #15569

Merged
merged 1 commit into from
May 19, 2017
Merged

Conversation

RyanCavanaugh
Copy link
Member

Picks up where #14624 left off.

@RyanCavanaugh
Copy link
Member Author

Anyone want to take a look? The VS side is about to merge

@@ -3368,6 +3368,7 @@ namespace ts {
Warning,
Error,
Message,
CodeFix
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would call this Suggestion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

export type GetApplicableRefactorsRequestArgs = FileLocationOrRangeRequestArgs;

export interface ApplicableRefactorInfo {
refactorName: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just name

@@ -95,6 +95,10 @@ namespace ts.server.protocol {
/* @internal */
export type GetCodeFixesFull = "getCodeFixes-full";
export type GetSupportedCodeFixes = "getSupportedCodeFixes";
export type GetCodeFixDiagnostics = "getCodeFixDiagnostics";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getSuggestionDiagnostics

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

});

function createCodeFixDiagnosticIfApplicable(node: Node, context: CodeFixDiagnoseContext): Diagnostic | undefined {
if (!isSourceFileJavaScript(context.boundSourceFile)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not going to scale as we add code fixes. we can not call every code fix on every node on the tree, we need a more efficient way.
We could make codeFix register a SyntaxKind it is interested in, this will save us the dispatch on every node at least.
Alternatively, we can have the code fix return a Boolean whether to keep walking or not, this way we avoid walking expressions that we do not care about.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codefix feature removed

}

export function getSupportedErrorCodes() {
return Object.keys(codeFixes);
}

export function getCodeFixDiagnosticsForNode(context: CodeFixDiagnoseContext, node: Node): Diagnostic[] | undefined {
let result: Diagnostic[];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we need to optimize this function and getCodeFixDiagnostics.


const checker = context.program.getTypeChecker();
const symbol = checker.getSymbolAtLocation(node);
if (isClassLikeSymbol(symbol)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would say this should be limited to the declaration, not on every reference.

I would also say you do not need the checker here.

@@ -3,6 +3,14 @@ namespace ts {
export interface CodeFix {
errorCodes: number[];
getCodeActions(context: CodeFixContext): CodeAction[] | undefined;
createCodeFixDiagnosticIfApplicable?(node: Node, context: CodeFixDiagnoseContext): Diagnostic | undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we want this to be syntactic in nature, passing in the program allows for getting the checker. this can be expensive if the codeFix author do not know what they are doing, consider not passing in the program, but just a sourceFile and options.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also want to use the same mechanism for refactorings. a refactoring should have isApplicable, and getCodeActions. I would recommend switching this into isApplicable, and an optional diagnostic message.

getCodeActions(context: RefactorContext, positionOrRange: number | TextRange): CodeAction[];

/** A fast syntactic check to see if the refactor is applicable at given position. */
isApplicableForPositionOrRange(context: LightRefactorContext, positionOrRange: number | TextRange): boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider unifying this with the codeFix isApplicable. ideally we can have one implementation that serves as both a refactor and a codefix + suggestion provider.


const asyncSuffixRefactor: Refactor = {
name: "Add Async suffix",
description: "Add an 'Async' suffix to async function declarations",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be a diagnostics message for localizability.

const token = getTokenAtPosition(nonBoundSourceFile, tokenPos);

let node = token;
while (node) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is kind of random that on any expression i can refactor the containing function to include async. i would say this has to be on function/method declaration at least.

getCodeActions(context: RefactorContext, positionOrRange: number | TextRange): CodeAction[];

/** A fast syntactic check to see if the refactor is applicable at given position. */
isApplicableForPositionOrRange(context: QueryRefactorContext, positionOrRange: number | TextRange): boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just isApplicable, the positionOrRange is implied from the arguments.

/* @internal */
namespace ts.refactor {
const asyncSuffixRefactor: Refactor = {
name: "Add Async suffix",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be in diagnostics

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this refactor

namespace ts.refactor {
const asyncSuffixRefactor: Refactor = {
name: "Add Async suffix",
description: "Add an 'Async' suffix to async function declarations",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be in diagnostics to ensure localizatability.

}];
}

function isApplicableForPositionOrRange(context: RefactorContext, positionOrRange: number | TextRange): boolean {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kinda silly comment, but can we have (startPosition, endPoisition) instead of positionOrRange? every refactor author has to write the same check typeof positionOrRange === "number"?...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually why not just put these on the RefactorContext? we put the errorCode on the codeFix context.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense


let node = token;
while (node) {
if (node.kind === SyntaxKind.FunctionDeclaration) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would use isNameOfFunctionDeclaration or something similar.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would check if token is one of identifier, function keyword, public, private, protected, or static , and their parent is a FunctionDeclatation or MethodDeclaration.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File removed

}

// all static members are stored in the "exports" array of symbol
if (symbol.exports) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this intended for a .ts or a .js file?

I think it is a .js file, since the binding for the special properties do not work in .ts files any way.

If my understanding is correct, then for static members we can not just add declarations for them, this will result in invalid .js class.

this also applies to the property declarations and assignments..

If we want this to work in a .ts file, then we need to do something with the inference to make it work first.

I would also recommend adding a check at the top of isApplicable to return if !inJavaScriptFile(node)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also for member initialization, I wounder if we can even do these correctellly...

var c = function() { this.list.push(1); }
c.prototype.list = [];

is not the same as:

class c {
    constructor() { this.list.push(1); }
    list = [];

let a side that it is not valid JS strictly speaking.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed these up. Non-func-expr things remain as assignments following the class body

positionOrRange: number | TextRange,
refactorName: string): CodeAction[] | undefined {

const context: RefactorContext = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider in-lining the const in the call and using the contextual type instead.

positionOrRange: number | TextRange): ApplicableRefactorInfo[] | undefined {

let results: ApplicableRefactorInfo[];
refactors.forEach(refactor => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would check the cancellation token here.

program: Program;
}

export interface RefactorContext extends QueryRefactorContext {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the value of the split here?


export interface RefactorContext extends QueryRefactorContext {
newLineCharacter: string;
rulesProvider: formatting.RulesProvider;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about cancellation token?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider exposing CodeFixContext minus errorCode.

@RyanCavanaugh RyanCavanaugh merged commit f489f5a into microsoft:master May 19, 2017
@RyanCavanaugh RyanCavanaugh deleted the new_refactor branch May 19, 2017 18:23

export type GetApplicableRefactors = "getApplicableRefactors";
export type GetRefactorCodeActions = "getRefactorCodeActions";
export type GetRefactorCodeActionsFull = "getRefactorCodeActions-full";
Copy link
Contributor

@mhegazy mhegazy May 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be marked as /* @internal */


export type RefactorCodeActions = {
actions: protocol.CodeAction[];
renameLocation?: number
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment here would be good.

program: Program;
newLineCharacter: string;
rulesProvider?: formatting.RulesProvider;
cancellationToken?: CancellationToken;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this optional?

endPosition?: number;
program: Program;
newLineCharacter: string;
rulesProvider?: formatting.RulesProvider;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this optional?

@mhegazy mhegazy mentioned this pull request May 22, 2017
@mhegazy mhegazy mentioned this pull request May 24, 2017
mjbvz added a commit to mjbvz/vscode that referenced this pull request Jun 16, 2017
Fixes microsoft#25739, from microsoft/TypeScript#15569

Prototype of refactoring support for ts 2.4
mjbvz added a commit to microsoft/vscode that referenced this pull request Jun 16, 2017
* Prototype TS/JS Refactoring Provider

Fixes #25739, from microsoft/TypeScript#15569

Prototype of refactoring support for ts 2.4

* Adding error reporting

* Updating for new API

* show quick pick for non-inlinable refactrings
ananthakumaran added a commit to ananthakumaran/tide that referenced this pull request Jul 8, 2017
ananthakumaran added a commit to ananthakumaran/tide that referenced this pull request Jul 30, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants