-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Refactoring support #15569
Conversation
Anyone want to take a look? The VS side is about to merge |
src/compiler/types.ts
Outdated
@@ -3368,6 +3368,7 @@ namespace ts { | |||
Warning, | |||
Error, | |||
Message, | |||
CodeFix |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
src/server/protocol.ts
Outdated
export type GetApplicableRefactorsRequestArgs = FileLocationOrRangeRequestArgs; | ||
|
||
export interface ApplicableRefactorInfo { | ||
refactorName: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just name
src/server/protocol.ts
Outdated
@@ -95,6 +95,10 @@ namespace ts.server.protocol { | |||
/* @internal */ | |||
export type GetCodeFixesFull = "getCodeFixes-full"; | |||
export type GetSupportedCodeFixes = "getSupportedCodeFixes"; | |||
export type GetCodeFixDiagnostics = "getCodeFixDiagnostics"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getSuggestionDiagnostics
There was a problem hiding this comment.
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)) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Codefix feature removed
src/services/codeFixProvider.ts
Outdated
} | ||
|
||
export function getSupportedErrorCodes() { | ||
return Object.keys(codeFixes); | ||
} | ||
|
||
export function getCodeFixDiagnosticsForNode(context: CodeFixDiagnoseContext, node: Node): Diagnostic[] | undefined { | ||
let result: Diagnostic[]; |
There was a problem hiding this comment.
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)) { |
There was a problem hiding this comment.
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.
src/services/codeFixProvider.ts
Outdated
@@ -3,6 +3,14 @@ namespace ts { | |||
export interface CodeFix { | |||
errorCodes: number[]; | |||
getCodeActions(context: CodeFixContext): CodeAction[] | undefined; | |||
createCodeFixDiagnosticIfApplicable?(node: Node, context: CodeFixDiagnoseContext): Diagnostic | undefined; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
src/services/refactorProvider.ts
Outdated
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; |
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
src/services/refactorProvider.ts
Outdated
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; |
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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"?...
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
src/services/services.ts
Outdated
positionOrRange: number | TextRange, | ||
refactorName: string): CodeAction[] | undefined { | ||
|
||
const context: RefactorContext = { |
There was a problem hiding this comment.
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.
src/services/refactorProvider.ts
Outdated
positionOrRange: number | TextRange): ApplicableRefactorInfo[] | undefined { | ||
|
||
let results: ApplicableRefactorInfo[]; | ||
refactors.forEach(refactor => { |
There was a problem hiding this comment.
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.
src/services/refactorProvider.ts
Outdated
program: Program; | ||
} | ||
|
||
export interface RefactorContext extends QueryRefactorContext { |
There was a problem hiding this comment.
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?
src/services/refactorProvider.ts
Outdated
|
||
export interface RefactorContext extends QueryRefactorContext { | ||
newLineCharacter: string; | ||
rulesProvider: formatting.RulesProvider; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about cancellation token?
There was a problem hiding this comment.
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.
326ba1c
to
17b1eb2
Compare
17b1eb2
to
f6fbe61
Compare
|
||
export type GetApplicableRefactors = "getApplicableRefactors"; | ||
export type GetRefactorCodeActions = "getRefactorCodeActions"; | ||
export type GetRefactorCodeActionsFull = "getRefactorCodeActions-full"; |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this optional?
Fixes microsoft#25739, from microsoft/TypeScript#15569 Prototype of refactoring support for ts 2.4
* 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
Picks up where #14624 left off.