FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Support a "getCombinedCodeFix" service · Pull Request #20338 · microsoft/TypeScript · GitHub

Support a "getCombinedCodeFix" service - #20338

Merged
15 commits merged into
masterfrom
codeFixAll
Dec 7, 2017
Merged

Support a "getCombinedCodeFix" service#20338
15 commits merged into
masterfrom
codeFixAll

Conversation

Deleted user (ghost) commented Nov 29, 2017
edited by ghost
Loading

Copy link
Copy Markdown

Fixes #14549
Does not fix #20315
Each code fix may come with a groupId object. This groupId may then be passed to a getCombinedCodeFix services method which generates a code action combining all code fixes in that group.
This would be a good time to consider changes to the API, such as if we want fixes to be able to have multiple groupIds or if we want to give groupIds a human-readable description too.
Also, we might want to design the signature of getCombinedCodeFix to allow for other possible scopes in the future; such as taking { type: "file", fileName: "..." } instead of just a filename directly.

For the implementation: For the most part, services could be written so that their main body is a function taking a changeTracker, and we can then iterate over every error in the file and apply changes to the tracker. Some were more difficult due to creating strings directly instead of nodes (via checker.typeToString) -- it would probably be better to use checker.typeToTypeNode instead?

Comment thread src/server/protocol.ts Outdated
errorCodes?: number[];
}

export interface GetCombinedCodeFixRequestArgs extends FileRequestArgs {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Please add a GetCombinedCodeFixResponse definition

Comment thread src/server/protocol.ts Outdated
}

export interface GetCombinedCodeFixRequestArgs extends FileRequestArgs {
groupId: {};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Why would not this be string? i am assuming to allow for an array, but when would you need this? u have an example in mind?

Comment thread src/server/protocol.ts Outdated

export interface CodeActionAll {
changes: FileCodeEdits[];
commands: {}[] | undefined;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

why not optional instead?

Comment thread src/server/protocol.ts Outdated

export interface CodeFix extends CodeAction {
/** If present, one may call 'getAllCodeFixesInGroup' with this groupId. */
groupId: {} | undefined;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

why not optional instead.

Comment thread src/services/codeFixProvider.ts Outdated
getCodeActions(context: CodeFixContext): CodeAction[] | undefined;
getCodeActions(context: CodeFixContext): CodeFix[] | undefined;
groupIds: string[];
fixAllInGroup(context: CodeFixAllContext): CodeActionAll;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

this should be optional. exsiting users like tslint for instance do not supply this function.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

though this si not a public API.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Also a name that still include codeActions would be better.. something that maintains parity with getCodeActions

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

maybe getAllCodeActions or getAllCodeActionsInFile

Comment thread src/services/codeFixProvider.ts Outdated
errorCodes: number[];
getCodeActions(context: CodeFixContext): CodeAction[] | undefined;
getCodeActions(context: CodeFixContext): CodeFix[] | undefined;
groupIds: string[];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I think a better name is actionIds or actionCodes.

Comment thread src/services/codeFixProvider.ts Outdated
}

export interface CodeFixAllContext extends CodeFixContextBase {
groupId: {};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

i think we should just make this a string, unless we have a reason not to.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

If we use string we are limited to treating it like an enum. With an object we could support any data as part of an action id, such as the name of a class if we wanted to limit fixes to just that. That could be done with a string, but we would have to stringify and parse the data, which is silly because we're sending this through JSON already anyway.

newText: `// @ts-nocheck${newLineCharacter}`
}])],
// groupId unnecessary because adding `// @ts-nocheck` even once will ignore every error in the file.
groupId: undefined,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

would it be better to split this CodeFix into two, and make sure that a CodeFix is a union type, that either it has fillAllInGroup and returns groupId/groupIds or it does not.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I think it would be a pain to have to split up a codefix just to allow multiple (or optional) groupIds. If anything I think we should consolidate them more, like fixClassDoesntImplementInheritedAbstractClass and fixClassIncorrectlyImplementsInterface.

Andrew Casey (amcasey) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I'm not done reviewing but I wanted to indicate that I'm looking at this.

Comment thread src/server/protocol.ts Outdated
GetCodeFixesFull = "getCodeFixes-full",
GetCombinedCodeFix = "getCombinedCodeFix",
/* @internal */
GetCombinedCodeFixFull = "getCombinedCodeFix",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

"getCombinedCodeFix-full"?

Comment thread src/server/protocol.ts Outdated
commands?: {}[];
}

export interface CodeActionAll {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

CombinedCodeActions?

Comment thread src/services/codeFixProvider.ts Outdated
getCodeActions(context: CodeFixContext): CodeAction[] | undefined;
getCodeActions(context: CodeFixContext): CodeFix[] | undefined;
actionIds: string[];
getAllCodeActions(context: CodeFixAllContext): CodeActionAll;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

we should make this one optional

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Or make CodeFixRegisraction a union type

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

not all codeFixes need to provide a list of applicable actionIds, and not all of them need to provide a getAllCodeActions feature and not all of them need to return an actionId in their output.

Comment thread src/services/utilities.ts Outdated
}

/** Add a value to a set, and return true if it wasn't already present. */
export function addToSeenIds(seen: true[], key: number): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

why not jsut use a map for these, it is a sparse array anyways..

Copy link
Copy Markdown
Member

We've had some problems in the past when we've tried to make multiple changes to the same range in a single ChangeTracker. Do we have some reason to believe that won't occur when combining code fixes?

Comment thread src/services/codeFixProvider.ts Outdated

export function registerCodeFix(codeFix: CodeFix) {
forEach(codeFix.errorCodes, error => {
export function registerCodeFix(codeFix: CodeFixRegistration) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Now that there's a CodeFix type, codeFix is a confusing name for a CodeFixRegistration.

Andrew Casey (amcasey) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Is this based on Roslyn's implementation? Using theirs as a model might help us avoid making the same mistakes.

Comment thread src/services/types.ts Outdated

export interface CodeFix extends CodeAction {
/** If present, one may call 'getCombinedCodeFix' with this actionId. */
actionId?: {} | undefined;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

This is on a separate type for back compat reasons? Making it optional isn't enough?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

A CodeFix includes both a CodeAction and an optional action id. A CodeAction will only have an actionId if returned from a code fix, not if from a completion.

Comment thread src/services/codeFixProvider.ts Outdated
});
}
if (codeFix.actionIds) {
for (const gid of codeFix.actionIds) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

gid is out of date

Copy link
Copy Markdown
Author

We've had some problems in the past when we've tried to make multiple changes to the same range in a single ChangeTracker. Do we have some reason to believe that won't occur when combining code fixes?

We know that ChangeTracker can't handle all possible sequences of changes because some would be nonsensical -- so it's a codefix's responsibility to ensure it's doing the possible and not e.g. making any changes twice (see the many addToSeen calls).
Since ChangeTracker already has the responsibility of making multiple changes, I think it's best to make all changes through there and make improvements to ChangeTracker if any problems come up. (
There was one problem that came up when deleting the last and next-to-last items in a list, fixed in this PR.) That should be easier than trying to combine raw text changes.

Copy link
Copy Markdown
Member

I don't want to bikeshed on naming, but it seems strange that a CodeFix is a CodeAction with an (optional) actionId. If the ID is for the action, why isn't it on the action? Maybe a CodeFixAction is a CodeAction with a fixId? Mohamed Hegazy (@mhegazy)?

Copy link
Copy Markdown
Member

I'm not quite clear on the difference between a CombinedCodeAction and a CodeActionAll. Do we just use different names in different layers?

Mohamed Hegazy (mhegazy) commented Dec 2, 2017
edited
Loading

Copy link
Copy Markdown
Contributor

This is the extra ID I was thinking of:
http://source.roslyn.io/#Microsoft.CodeAnalysis.Workspaces/CodeActions/CodeAction.cs,48

so we should generate that from all the inputs to the codefix to make sure they are unique?

but what to do if combining the fixes is not just applying them in sequence?

Copy link
Copy Markdown
Member

Mohamed Hegazy (@mhegazy) I think it's more or less equivalent to what we're calling action/fix ID and we have the option of incorporating the code in the future if that turns out to be interesting.

Copy link
Copy Markdown
Member

Does VS offer all levels of repetition for all code fixes or do code fixes somehow indicate that they can be repeated? If they indicate that they can be repeated, do they somehow indicate which choices would be sensible? (e.g. don't offer 'project' if all occurrences are in 'file')

Andrew Casey (amcasey) commented Dec 6, 2017
edited
Loading

Copy link
Copy Markdown
Member

Roslyn appears to offer all scopes when repetition is offered, but not always offer repetition (e.g. add missing member), so we might need to add a bit to CodeFixAction. Or would you just omit the fixId in that case?

ghost mentioned this pull request Dec 7, 2017
ghost force-pushed the codeFixAll branch from fce0913 to 8a61761 Compare December 7, 2017 15:23

Copy link
Copy Markdown
Author

Andrew Casey (@amcasey) Mohamed Hegazy (@mhegazy) I've made the API changes internal for now (and marked with #20538). I'd like to get this PR in soon since it touches a lot of files.

Comment thread src/server/client.ts Outdated
}

getCodeFixesAtPosition(file: string, start: number, end: number, errorCodes: number[]): CodeAction[] {
getCodeFixesAtPosition(file: string, start: number, end: number, errorCodes: number[]): CodeFixAction[] {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Can the return type be ReadonlyArray ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

👍

Comment thread src/harness/fourslash.ts
const { changes, commands } = this.languageService.getCombinedCodeFix({ type: "file", fileName: this.activeFile.fileName }, fixId, this.formatCodeSettings);
assert.deepEqual(commands, options.commands);
this.applyChanges(changes);
this.verifyCurrentFileContent(newFileContent);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

will this always be currentFile. Api call returns TextChangeRange which has file name per change.. So wouldnt it need to verify all those files?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Added an assertion that all changes are for the current file and a TODO for if we need to test changes affecting multiple files.

Copy link
Copy Markdown
Member

Andy (Andrewkraft) (@Andy-MS) Do we have a way to signal that some code fixes can't be repeated? Or, at least, can we add that later without affecting back-compat? Otherwise, LGTM.

ghost force-pushed the codeFixAll branch from 4df09b0 to 87d30c0 Compare December 7, 2017 18:26

Copy link
Copy Markdown
Author

I think we can do that by just not supplying a fixId.

Copy link
Copy Markdown
Member

Can you please add a comment to that effect on the declaration? I'm worried that future implementers (i.e. me) might forget that that's an option and try to add a flag.

This pull request was closed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
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.

Fix all in file for import fix Proposal: 'Fix All In File' for CodeFixes

3 participants


Back | FazBrowse Home | New Git URL