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

Prototyping new CodeAction API #36316

Merged
merged 6 commits into from Nov 9, 2017
Merged

Prototyping new CodeAction API #36316

merged 6 commits into from Nov 9, 2017

Conversation

mjbvz
Copy link
Contributor

@mjbvz mjbvz commented Oct 16, 2017

Adds skeleton of a new CodeAction type and allows codeActionProviders to return either Commands or CodeActions

@mjbvz mjbvz requested a review from jrieken October 16, 2017 03:24
@mjbvz mjbvz force-pushed the code-action branch 2 times, most recently from 93ee798 to 305d6a6 Compare October 16, 2017 21:32
@mjbvz mjbvz changed the title Add CodeAction skeleton Prototyping new CodeAction API Oct 17, 2017
@mjbvz
Copy link
Contributor Author

mjbvz commented Oct 17, 2017

@jrieken Kai and I were talking today and wanted to try out a different API design than in #34664. The current change introduces two separate interfaces QuickFix and Refactoring instead of a single interface where quick fixes and refactorings are identified by a type field.

The implementation still need lots of work but please take a look at vscode.proposed.d.ts to see what you think of this proposal

@jrieken
Copy link
Member

jrieken commented Oct 17, 2017

I like the general direction but not the change 🙊. I think the Refactoring-type is just a complicated way of expressing the same Command does, a bit of UI (label, tooltip), the actual command id, and some arguments. For the Quickfix I would use some composition, maybe

class QuickFix {
 command: Command
 addresses: Diagnostics[]
}

alternatively, in case we make add support for text edits, add a title

class QuickFix {
  title: string;
  fix: Command | WorkspaceEdit | TextEdit
  addresses: Diagnostics
}

or making it even more compact, with only one type

class CodeAction {
 // ... all from above

 // when set treated as quick fix, otherwise as refactoring
 addresses?: Diagnostic[]
}

@jrieken
Copy link
Member

jrieken commented Oct 17, 2017

Some inspiration from roslyn: http://source.roslyn.io/#Microsoft.CodeAnalysis.Workspaces/CodeFixes/CodeFix.cs,7161867d561915e6

There seems to be a QuickFix type which is more or less a code action with a reference to the diagnostic it fixes.

@jrieken
Copy link
Member

jrieken commented Oct 17, 2017

/cc @DustinCampbell for some background how roslyn handles refactorings vs fixes.

@DustinCampbell
Copy link
Member

In Roslyn...

  • CodeAction is a named edit that can be invoked by the user. It contains a series of CodeActionOperations that can represent a text edit, a workspace edit, or something else. Often that "something else" is a dialog that allows the user to configure the CodeAction before invoking it. A great example of a CodeAction that requires a dialog is something like Change Signature.
  • A CodeFix encapsulates a set of CodeAction objects with a Diagnostic.
  • A CodeRefactoring encapsulates a set of CodeAction objects with a span in the editor, which could be collapsed as a caret position.

Does that help?

@mjbvz
Copy link
Contributor Author

mjbvz commented Oct 18, 2017

Thanks @DustinCampbell! Very helpful design to review. Do you happen to know how VS sets up keybindings for specific refactoring, such as ctrl+r, m for extract method? Is this done per-language or is there a general API for hooking these up?

@mjbvz
Copy link
Contributor Author

mjbvz commented Oct 18, 2017

@jrieken Today @kieferrm and I talked through the design again and cut it back to focus on address three problems:

  1. No way to associate a quick fix with the diagnostic it addresses
  2. No way to preview a quick fix's change
  3. Refactoring actions are always calculated and shown with the lightbulb when just moving around in a file

The first two are addressed by adding a new CodeAction class.

For the third, one thought is to keep refactoring as code actions for now, but give code action providers additional context so that they can decide which actions to return. This latest change enables this by adding a quickFixesOnly flag on the CodeActionContext. When this flag is set, the refactoring provider would not return any code actions. The flag would always be set when just moving the cursor through code but unset if you manually request code actions with cmd+..

@DustinCampbell
Copy link
Member

DustinCampbell commented Oct 18, 2017

Do you happen to know how VS sets up keybindings for specific refactoring, such as ctrl+r, m for extract method? Is this done per-language or is there a general API for hooking these up?

There's no general API for this. It's done on a case-by-case basis. 😞

@@ -39,13 +46,15 @@ export class QuickFixController implements IEditorContribution {
constructor(editor: ICodeEditor,
@IMarkerService markerService: IMarkerService,
@IContextKeyService contextKeyService: IContextKeyService,
@ICommandService commandService: ICommandService,
@ICommandService private readonly commandService: ICommandService,
Copy link
Member

Choose a reason for hiding this comment

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

_ for privates please

@@ -284,7 +292,7 @@ export interface CodeActionProvider {
/**
* Provide commands for the given document and range.
*/
provideCodeActions(model: editorCommon.IReadOnlyModel, range: Range, token: CancellationToken): Command[] | Thenable<Command[]>;
provideCodeActions(model: editorCommon.IReadOnlyModel, range: Range, token: CancellationToken): (Command | CodeAction)[] | Thenable<(Command | CodeAction)[]>;
Copy link
Member

Choose a reason for hiding this comment

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

In this land we can break the API so I'd suggest we don't carry along the Command | CodeAction notion but just make it a CodeAction. I'd also do that on the protocol layer and do the Command becomes CodeAction thing in the code action adapter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm concerned this will break the vscode.executeCodeActionProvider command. I believe this should still return commands instead of code actions for old providers and so I'm going to revert the change to only use code actions for now


const allResults: Command[] = [];
const allResults: (Command | CodeAction)[] = [];
Copy link
Member

Choose a reason for hiding this comment

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

CodeAction ftw

if ('id' in action) {
// must be a command
const command = action as Command;
return { title: command.title, command: command } as CodeAction;
Copy link
Member

Choose a reason for hiding this comment

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

CodeAction ftw, lets do the conversion in the ext host

constructor(
private readonly editor: ICodeEditor,
private readonly contextMenuService: IContextMenuService,
private readonly onApplyCodeAction: (action: CodeAction) => TPromise<any>
Copy link
Member

Choose a reason for hiding this comment

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

unnecessary change, _ for privates

return {
title: codeAction.title,
command: codeAction.command ? this._commands.toInternal(codeAction.command) : undefined,
edits: codeAction.edits
Copy link
Member

Choose a reason for hiding this comment

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

For the sake of simplicity we can also make edits always a WorkspaceEdit. We'd treat the TextEdit as sugar and translate it into a workspace edit... Makes the internal CodeAction simpler and easier to handle.

@jrieken
Copy link
Member

jrieken commented Nov 8, 2017

I would have gone ahead but there are some merge-conflicts...

Adds skeleton on a new CodeActionType and allows codeActionProvider to return either `Command`s or `CodeAction`s

Move proposed CodeAction API to proposed and try using it in TS

Split CodeAction into quickfix and refactoring classes

Update proposed interface

Update for new API

Adding basic docs
…nd` in modes since this will break `vscode.executeCodeActionProvider`
@mjbvz mjbvz added this to the November 2017 milestone Nov 9, 2017
@mjbvz mjbvz merged commit be88547 into microsoft:master Nov 9, 2017
@github-actions github-actions bot locked and limited conversation to collaborators Mar 29, 2020
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.

None yet

3 participants