Merge pull request #151566 from justschen/justin/refactor-fix

Refactoring Preview option in Command Palette (part of #151140)
This commit is contained in:
Justin Chen
2022-06-09 11:36:49 -07:00
committed by GitHub
6 changed files with 67 additions and 36 deletions
@@ -22,6 +22,7 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat
export const codeActionCommandId = 'editor.action.codeAction';
export const refactorCommandId = 'editor.action.refactor';
export const refactorPreviewCommandId = 'editor.action.refactor.preview';
export const sourceActionCommandId = 'editor.action.sourceAction';
export const organizeImportsCommandId = 'editor.action.organizeImports';
export const fixAllCommandId = 'editor.action.fixAll';
@@ -18,7 +18,7 @@ import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { CodeActionTriggerType } from 'vs/editor/common/languages';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { codeActionCommandId, CodeActionItem, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/browser/codeAction';
import { codeActionCommandId, CodeActionItem, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, refactorPreviewCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/browser/codeAction';
import { CodeActionUi } from 'vs/editor/contrib/codeAction/browser/codeActionUi';
import { MessageController } from 'vs/editor/contrib/message/browser/messageController';
import * as nls from 'vs/nls';
@@ -27,8 +27,8 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IEditorProgressService } from 'vs/platform/progress/common/progress';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { CodeActionModel, CodeActionsState, SUPPORTED_CODE_ACTIONS } from './codeActionModel';
import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionFilter, CodeActionKind, CodeActionTrigger } from './types';
@@ -39,6 +39,26 @@ function contextKeyForSupportedActions(kind: CodeActionKind) {
new RegExp('(\\s|^)' + escapeRegExpCharacters(kind.value) + '\\b'));
}
function RefactorTrigger(editor: ICodeEditor, userArgs: any, preview: boolean) {
const args = CodeActionCommandArgs.fromUser(userArgs, {
kind: CodeActionKind.Refactor,
apply: CodeActionAutoApply.Never
});
return triggerCodeActionsForEditorSelection(editor,
typeof userArgs?.kind === 'string'
? args.preferred
? nls.localize('editor.action.refactor.noneMessage.preferred.kind', "No preferred refactorings for '{0}' available", userArgs.kind)
: nls.localize('editor.action.refactor.noneMessage.kind', "No refactorings for '{0}' available", userArgs.kind)
: args.preferred
? nls.localize('editor.action.refactor.noneMessage.preferred', "No preferred refactorings available")
: nls.localize('editor.action.refactor.noneMessage', "No refactorings available"),
{
include: CodeActionKind.Refactor.contains(args.kind) ? args.kind : CodeActionKind.None,
onlyIncludePreferredActions: args.preferred
},
args.apply, preview);
}
const argsSchema: IJSONSchema = {
type: 'object',
defaultSnippets: [{ body: { kind: '' } }],
@@ -92,11 +112,12 @@ export class QuickFixController extends Disposable implements IEditorContributio
this._model = this._register(new CodeActionModel(this._editor, languageFeaturesService.codeActionProvider, markerService, contextKeyService, progressService));
this._register(this._model.onDidChangeState(newState => this.update(newState)));
this._ui = new Lazy(() =>
this._register(new CodeActionUi(editor, QuickFixAction.Id, AutoFixAction.Id, {
applyCodeAction: async (action, retrigger) => {
applyCodeAction: async (action, retrigger, preview) => {
try {
await this._applyCodeAction(action);
await this._applyCodeAction(action, preview);
} finally {
if (retrigger) {
this._trigger({ type: CodeActionTriggerType.Auto, filter: {} });
@@ -118,7 +139,8 @@ export class QuickFixController extends Disposable implements IEditorContributio
public manualTriggerAtCurrentPosition(
notAvailableMessage: string,
filter?: CodeActionFilter,
autoApply?: CodeActionAutoApply
autoApply?: CodeActionAutoApply,
preview?: boolean
): void {
if (!this._editor.hasModel()) {
return;
@@ -126,22 +148,22 @@ export class QuickFixController extends Disposable implements IEditorContributio
MessageController.get(this._editor)?.closeMessage();
const triggerPosition = this._editor.getPosition();
this._trigger({ type: CodeActionTriggerType.Invoke, filter, autoApply, context: { notAvailableMessage, position: triggerPosition } });
this._trigger({ type: CodeActionTriggerType.Invoke, filter, autoApply, context: { notAvailableMessage, position: triggerPosition }, preview });
}
private _trigger(trigger: CodeActionTrigger) {
return this._model.trigger(trigger);
}
private _applyCodeAction(action: CodeActionItem): Promise<void> {
return this._instantiationService.invokeFunction(applyCodeAction, action, this._editor);
private _applyCodeAction(action: CodeActionItem, preview: boolean): Promise<void> {
return this._instantiationService.invokeFunction(applyCodeAction, action, { preview, editor: this._editor });
}
}
export async function applyCodeAction(
accessor: ServicesAccessor,
item: CodeActionItem,
editor?: ICodeEditor,
options?: { preview?: boolean; editor?: ICodeEditor }
): Promise<void> {
const bulkEditService = accessor.get(IBulkEditService);
const commandService = accessor.get(ICommandService);
@@ -171,11 +193,12 @@ export async function applyCodeAction(
if (item.action.edit) {
await bulkEditService.apply(ResourceEdit.convert(item.action.edit), {
editor,
editor: options?.editor,
label: item.action.title,
quotableLabel: item.action.title,
code: 'undoredo.codeAction',
respectAutoSaveConfig: true
respectAutoSaveConfig: true,
showPreview: options?.preview,
});
}
@@ -206,12 +229,13 @@ function triggerCodeActionsForEditorSelection(
editor: ICodeEditor,
notAvailableMessage: string,
filter: CodeActionFilter | undefined,
autoApply: CodeActionAutoApply | undefined
autoApply: CodeActionAutoApply | undefined,
preview: boolean = false
): void {
if (editor.hasModel()) {
const controller = QuickFixController.get(editor);
if (controller) {
controller.manualTriggerAtCurrentPosition(notAvailableMessage, filter, autoApply);
controller.manualTriggerAtCurrentPosition(notAvailableMessage, filter, autoApply, preview);
}
}
}
@@ -306,23 +330,27 @@ export class RefactorAction extends EditorAction {
}
public run(_accessor: ServicesAccessor, editor: ICodeEditor, userArgs: any): void {
const args = CodeActionCommandArgs.fromUser(userArgs, {
kind: CodeActionKind.Refactor,
apply: CodeActionAutoApply.Never
return RefactorTrigger(editor, userArgs, false);
}
}
export class RefactorPreview extends EditorAction {
constructor() {
super({
id: refactorPreviewCommandId,
label: nls.localize('refactor.preview.label', "Refactor with Preview..."),
alias: 'Refactor Preview...',
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider),
description: {
description: 'Refactor Preview...',
args: [{ name: 'args', schema: argsSchema }]
}
});
return triggerCodeActionsForEditorSelection(editor,
typeof userArgs?.kind === 'string'
? args.preferred
? nls.localize('editor.action.refactor.noneMessage.preferred.kind', "No preferred refactorings for '{0}' available", userArgs.kind)
: nls.localize('editor.action.refactor.noneMessage.kind', "No refactorings for '{0}' available", userArgs.kind)
: args.preferred
? nls.localize('editor.action.refactor.noneMessage.preferred', "No preferred refactorings available")
: nls.localize('editor.action.refactor.noneMessage', "No refactorings available"),
{
include: CodeActionKind.Refactor.contains(args.kind) ? args.kind : CodeActionKind.None,
onlyIncludePreferredActions: args.preferred,
},
args.apply);
}
public run(_accessor: ServicesAccessor, editor: ICodeEditor, userArgs: any): void {
return RefactorTrigger(editor, userArgs, true);
}
}
@@ -4,12 +4,13 @@
*--------------------------------------------------------------------------------------------*/
import { registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { AutoFixAction, CodeActionCommand, FixAllAction, OrganizeImportsAction, QuickFixAction, QuickFixController, RefactorAction, SourceAction } from 'vs/editor/contrib/codeAction/browser/codeActionCommands';
import { AutoFixAction, CodeActionCommand, FixAllAction, OrganizeImportsAction, QuickFixAction, QuickFixController, RefactorAction, RefactorPreview, SourceAction } from 'vs/editor/contrib/codeAction/browser/codeActionCommands';
registerEditorContribution(QuickFixController.ID, QuickFixController);
registerEditorAction(QuickFixAction);
registerEditorAction(RefactorAction);
registerEditorAction(RefactorPreview);
registerEditorAction(SourceAction);
registerEditorAction(OrganizeImportsAction);
registerEditorAction(AutoFixAction);
@@ -23,7 +23,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
interface CodeActionWidgetDelegate {
onSelectCodeAction: (action: CodeActionItem) => Promise<any>;
onSelectCodeAction: (action: CodeActionItem, trigger: CodeActionTrigger) => Promise<any>;
}
interface ResolveCodeActionKeybinding {
@@ -115,7 +115,7 @@ export class CodeActionMenu extends Disposable {
actionsToShow: readonly CodeActionItem[],
documentation: readonly Command[]
): IAction[] {
const toCodeActionAction = (item: CodeActionItem): CodeActionAction => new CodeActionAction(item.action, () => this._delegate.onSelectCodeAction(item));
const toCodeActionAction = (item: CodeActionItem): CodeActionAction => new CodeActionAction(item.action, () => this._delegate.onSelectCodeAction(item, trigger));
const result: IAction[] = actionsToShow
.map(toCodeActionAction);
@@ -31,7 +31,7 @@ export class CodeActionUi extends Disposable {
quickFixActionId: string,
preferredFixActionId: string,
private readonly delegate: {
applyCodeAction: (action: CodeActionItem, regtriggerAfterApply: boolean) => Promise<void>;
applyCodeAction: (action: CodeActionItem, regtriggerAfterApply: boolean, preview: boolean) => Promise<void>;
},
@IInstantiationService instantiationService: IInstantiationService,
) {
@@ -39,8 +39,8 @@ export class CodeActionUi extends Disposable {
this._codeActionWidget = new Lazy(() => {
return this._register(instantiationService.createInstance(CodeActionMenu, this._editor, {
onSelectCodeAction: async (action) => {
this.delegate.applyCodeAction(action, /* retrigger */ true);
onSelectCodeAction: async (action, trigger) => {
this.delegate.applyCodeAction(action, /* retrigger */ true, Boolean(trigger.preview));
}
}));
});
@@ -85,7 +85,7 @@ export class CodeActionUi extends Disposable {
if (validActionToApply) {
try {
this._lightBulbWidget.getValue().hide();
await this.delegate.applyCodeAction(validActionToApply, false);
await this.delegate.applyCodeAction(validActionToApply, false, false);
} finally {
actions.dispose();
}
@@ -122,6 +122,7 @@ export interface CodeActionTrigger {
readonly notAvailableMessage: string;
readonly position: Position;
};
readonly preview?: boolean;
}
export class CodeActionCommandArgs {