Split codeActonController to own file

This commit is contained in:
Matt Bierner 2019-02-20 13:58:08 -08:00
parent fe01a19db1
commit 4eb65f974d
6 changed files with 164 additions and 150 deletions

View file

@ -14,6 +14,9 @@ import { ITextModel } from 'vs/editor/common/model';
import { CodeAction, CodeActionContext, CodeActionProviderRegistry, CodeActionTrigger as CodeActionTriggerKind } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind, CodeActionFilter } from './codeActionTrigger';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
export function getCodeActions(
model: ITextModel,
@ -78,6 +81,21 @@ function codeActionsComparator(a: CodeAction, b: CodeAction): number {
}
}
export async function applyCodeAction(
action: CodeAction,
bulkEditService: IBulkEditService,
commandService: ICommandService,
editor?: ICodeEditor,
): Promise<void> {
if (action.edit) {
await bulkEditService.apply(action.edit, { editor });
}
if (action.command) {
await commandService.executeCommand(action.command.id, ...(action.command.arguments || []));
}
}
registerLanguageCommand('_executeCodeActionProvider', function (accessor, args) {
const { resource, range, kind } = args;
if (!(resource instanceof URI) || !Range.isIRange(range)) {

View file

@ -3,30 +3,18 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancelablePromise } from 'vs/base/common/async';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, EditorCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { CodeAction } from 'vs/editor/common/modes';
import { MessageController } from 'vs/editor/contrib/message/messageController';
import * as nls from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { CodeActionModel, SUPPORTED_CODE_ACTIONS, CodeActionsState } from './codeActionModel';
import { CodeActionAutoApply, CodeActionFilter, CodeActionKind } from './codeActionTrigger';
import { CodeActionContextMenu } from './codeActionWidget';
import { LightBulbWidget } from './lightBulbWidget';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { onUnexpectedError } from 'vs/base/common/errors';
import { CodeActionController } from './codeActionController';
import { SUPPORTED_CODE_ACTIONS } from './codeActionModel';
import { CodeActionAutoApply, CodeActionFilter, CodeActionKind } from './codeActionTrigger';
function contextKeyForSupportedActions(kind: CodeActionKind) {
return ContextKeyExpr.regex(
@ -34,132 +22,6 @@ function contextKeyForSupportedActions(kind: CodeActionKind) {
new RegExp('(\\s|^)' + escapeRegExpCharacters(kind.value) + '\\b'));
}
export class QuickFixController implements IEditorContribution {
private static readonly ID = 'editor.contrib.quickFixController';
public static get(editor: ICodeEditor): QuickFixController {
return editor.getContribution<QuickFixController>(QuickFixController.ID);
}
private _editor: ICodeEditor;
private _model: CodeActionModel;
private _codeActionContextMenu: CodeActionContextMenu;
private _lightBulbWidget: LightBulbWidget;
private _disposables: IDisposable[] = [];
private _activeRequest: CancelablePromise<CodeAction[]> | undefined;
constructor(editor: ICodeEditor,
@IMarkerService markerService: IMarkerService,
@IContextKeyService contextKeyService: IContextKeyService,
@IProgressService progressService: IProgressService,
@IContextMenuService contextMenuService: IContextMenuService,
@ICommandService private readonly _commandService: ICommandService,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
) {
this._editor = editor;
this._model = new CodeActionModel(this._editor, markerService, contextKeyService, progressService);
this._codeActionContextMenu = new CodeActionContextMenu(editor, contextMenuService, action => this._onApplyCodeAction(action));
this._lightBulbWidget = new LightBulbWidget(editor);
this._updateLightBulbTitle();
this._disposables.push(
this._codeActionContextMenu.onDidExecuteCodeAction(_ => this._model.trigger({ type: 'auto', filter: {} })),
this._lightBulbWidget.onClick(this._handleLightBulbSelect, this),
this._model.onDidChangeState(e => this._onDidChangeCodeActionsState(e)),
this._keybindingService.onDidUpdateKeybindings(this._updateLightBulbTitle, this)
);
}
public dispose(): void {
this._model.dispose();
dispose(this._disposables);
}
private _onDidChangeCodeActionsState(newState: CodeActionsState.State): void {
if (this._activeRequest) {
this._activeRequest.cancel();
this._activeRequest = undefined;
}
if (newState.type === CodeActionsState.Type.Triggered) {
this._activeRequest = newState.actions;
if (newState.trigger.filter && newState.trigger.filter.kind) {
// Triggered for specific scope
newState.actions.then(fixes => {
if (fixes.length > 0) {
// Apply if we only have one action or requested autoApply
if (newState.trigger.autoApply === CodeActionAutoApply.First || (newState.trigger.autoApply === CodeActionAutoApply.IfSingle && fixes.length === 1)) {
this._onApplyCodeAction(fixes[0]);
return;
}
}
this._codeActionContextMenu.show(newState.actions, newState.position);
}).catch(onUnexpectedError);
} else if (newState.trigger.type === 'manual') {
this._codeActionContextMenu.show(newState.actions, newState.position);
} else {
// auto magically triggered
// * update an existing list of code actions
// * manage light bulb
if (this._codeActionContextMenu.isVisible) {
this._codeActionContextMenu.show(newState.actions, newState.position);
} else {
this._lightBulbWidget.tryShow(newState);
}
}
} else {
this._lightBulbWidget.hide();
}
}
public getId(): string {
return QuickFixController.ID;
}
private _handleLightBulbSelect(e: { x: number, y: number, state: CodeActionsState.Triggered }): void {
this._codeActionContextMenu.show(e.state.actions, e);
}
public triggerFromEditorSelection(filter?: CodeActionFilter, autoApply?: CodeActionAutoApply): Promise<CodeAction[] | undefined> {
return this._model.trigger({ type: 'manual', filter, autoApply });
}
private _updateLightBulbTitle(): void {
const kb = this._keybindingService.lookupKeybinding(QuickFixAction.Id);
let title: string;
if (kb) {
title = nls.localize('quickFixWithKb', "Show Fixes ({0})", kb.getLabel());
} else {
title = nls.localize('quickFix', "Show Fixes");
}
this._lightBulbWidget.title = title;
}
private _onApplyCodeAction(action: CodeAction): Promise<void> {
return applyCodeAction(action, this._bulkEditService, this._commandService, this._editor);
}
}
export async function applyCodeAction(
action: CodeAction,
bulkEditService: IBulkEditService,
commandService: ICommandService,
editor?: ICodeEditor,
): Promise<void> {
if (action.edit) {
await bulkEditService.apply(action.edit, { editor });
}
if (action.command) {
await commandService.executeCommand(action.command.id, ...(action.command.arguments || []));
}
}
function showCodeActionsForEditorSelection(
editor: ICodeEditor,
notAvailableMessage: string,
@ -170,7 +32,7 @@ function showCodeActionsForEditorSelection(
return;
}
const controller = QuickFixController.get(editor);
const controller = CodeActionController.get(editor);
if (!controller) {
return;
}

View file

@ -4,10 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import { registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { CodeActionCommand, OrganizeImportsAction, QuickFixAction, QuickFixController, RefactorAction, SourceAction, AutoFixAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { CodeActionCommand, OrganizeImportsAction, QuickFixAction, RefactorAction, SourceAction, AutoFixAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { CodeActionController } from 'vs/editor/contrib/codeAction/codeActionController';
registerEditorContribution(QuickFixController);
registerEditorContribution(CodeActionController);
registerEditorAction(QuickFixAction);
registerEditorAction(RefactorAction);
registerEditorAction(SourceAction);

View file

@ -0,0 +1,136 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancelablePromise } from 'vs/base/common/async';
import { onUnexpectedError } from 'vs/base/common/errors';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { CodeAction } from 'vs/editor/common/modes';
import * as nls from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { QuickFixAction } from './codeActionCommands';
import { CodeActionModel, CodeActionsState } from './codeActionModel';
import { CodeActionAutoApply, CodeActionFilter } from './codeActionTrigger';
import { CodeActionContextMenu } from './codeActionWidget';
import { LightBulbWidget } from './lightBulbWidget';
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeAction';
export class CodeActionController implements IEditorContribution {
private static readonly ID = 'editor.contrib.quickFixController';
public static get(editor: ICodeEditor): CodeActionController {
return editor.getContribution<CodeActionController>(CodeActionController.ID);
}
private _editor: ICodeEditor;
private _model: CodeActionModel;
private _codeActionContextMenu: CodeActionContextMenu;
private _lightBulbWidget: LightBulbWidget;
private _disposables: IDisposable[] = [];
private _activeRequest: CancelablePromise<CodeAction[]> | undefined;
constructor(editor: ICodeEditor,
@IMarkerService markerService: IMarkerService,
@IContextKeyService contextKeyService: IContextKeyService,
@IProgressService progressService: IProgressService,
@IContextMenuService contextMenuService: IContextMenuService,
@ICommandService private readonly _commandService: ICommandService,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
) {
this._editor = editor;
this._model = new CodeActionModel(this._editor, markerService, contextKeyService, progressService);
this._codeActionContextMenu = new CodeActionContextMenu(editor, contextMenuService, action => this._onApplyCodeAction(action));
this._lightBulbWidget = new LightBulbWidget(editor);
this._updateLightBulbTitle();
this._disposables.push(
this._codeActionContextMenu.onDidExecuteCodeAction(_ => this._model.trigger({ type: 'auto', filter: {} })),
this._lightBulbWidget.onClick(this._handleLightBulbSelect, this),
this._model.onDidChangeState(e => this._onDidChangeCodeActionsState(e)),
this._keybindingService.onDidUpdateKeybindings(this._updateLightBulbTitle, this)
);
}
public dispose(): void {
this._model.dispose();
dispose(this._disposables);
}
private _onDidChangeCodeActionsState(newState: CodeActionsState.State): void {
if (this._activeRequest) {
this._activeRequest.cancel();
this._activeRequest = undefined;
}
if (newState.type === CodeActionsState.Type.Triggered) {
this._activeRequest = newState.actions;
if (newState.trigger.filter && newState.trigger.filter.kind) {
// Triggered for specific scope
newState.actions.then(fixes => {
if (fixes.length > 0) {
// Apply if we only have one action or requested autoApply
if (newState.trigger.autoApply === CodeActionAutoApply.First || (newState.trigger.autoApply === CodeActionAutoApply.IfSingle && fixes.length === 1)) {
this._onApplyCodeAction(fixes[0]);
return;
}
}
this._codeActionContextMenu.show(newState.actions, newState.position);
}).catch(onUnexpectedError);
} else if (newState.trigger.type === 'manual') {
this._codeActionContextMenu.show(newState.actions, newState.position);
} else {
// auto magically triggered
// * update an existing list of code actions
// * manage light bulb
if (this._codeActionContextMenu.isVisible) {
this._codeActionContextMenu.show(newState.actions, newState.position);
} else {
this._lightBulbWidget.tryShow(newState);
}
}
} else {
this._lightBulbWidget.hide();
}
}
public getId(): string {
return CodeActionController.ID;
}
private _handleLightBulbSelect(e: { x: number, y: number, state: CodeActionsState.Triggered }): void {
this._codeActionContextMenu.show(e.state.actions, e);
}
public triggerFromEditorSelection(filter?: CodeActionFilter, autoApply?: CodeActionAutoApply): Promise<CodeAction[] | undefined> {
return this._model.trigger({ type: 'manual', filter, autoApply });
}
private _updateLightBulbTitle(): void {
const kb = this._keybindingService.lookupKeybinding(QuickFixAction.Id);
let title: string;
if (kb) {
title = nls.localize('quickFixWithKb', "Show Fixes ({0})", kb.getLabel());
} else {
title = nls.localize('quickFix', "Show Fixes");
}
this._lightBulbWidget.title = title;
}
private _onApplyCodeAction(action: CodeAction): Promise<void> {
return applyCodeAction(action, this._bulkEditService, this._commandService, this._editor);
}
}

View file

@ -20,8 +20,7 @@ import { IIdentifiedSingleEditOperation, ISingleEditOperation, ITextModel } from
import { CodeAction } from 'vs/editor/common/modes';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService';
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { getCodeActions, applyCodeAction } from 'vs/editor/contrib/codeAction/codeAction';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';
import { getDocumentFormattingEdits, FormatMode } from 'vs/editor/contrib/format/format';
import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit';

View file

@ -36,14 +36,13 @@ import { fillResourceDataTransfers } from 'vs/workbench/browser/dnd';
import { CancelablePromise, createCancelablePromise, Delayer } from 'vs/base/common/async';
import { IModelService } from 'vs/editor/common/services/modelService';
import { Range } from 'vs/editor/common/core/range';
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
import { getCodeActions, applyCodeAction } from 'vs/editor/contrib/codeAction/codeAction';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';
import { ITextModel } from 'vs/editor/common/model';
import { CodeAction } from 'vs/editor/common/modes';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
export type TreeElement = ResourceMarkers | Marker | RelatedInformation;