diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index 8ade0e22464..a08593c70ff 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -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 { + 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)) { diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 4c5ee4309ca..afab76dcb83 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -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.ID); - } - - private _editor: ICodeEditor; - private _model: CodeActionModel; - private _codeActionContextMenu: CodeActionContextMenu; - private _lightBulbWidget: LightBulbWidget; - private _disposables: IDisposable[] = []; - - private _activeRequest: CancelablePromise | 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 { - 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 { - return applyCodeAction(action, this._bulkEditService, this._commandService, this._editor); - } -} - -export async function applyCodeAction( - action: CodeAction, - bulkEditService: IBulkEditService, - commandService: ICommandService, - editor?: ICodeEditor, -): Promise { - 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; } diff --git a/src/vs/editor/contrib/codeAction/codeActionContributions.ts b/src/vs/editor/contrib/codeAction/codeActionContributions.ts index 7a022b495b4..69940fb9fc6 100644 --- a/src/vs/editor/contrib/codeAction/codeActionContributions.ts +++ b/src/vs/editor/contrib/codeAction/codeActionContributions.ts @@ -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); diff --git a/src/vs/editor/contrib/codeAction/codeActionController.ts b/src/vs/editor/contrib/codeAction/codeActionController.ts new file mode 100644 index 00000000000..8284aa26510 --- /dev/null +++ b/src/vs/editor/contrib/codeAction/codeActionController.ts @@ -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.ID); + } + + private _editor: ICodeEditor; + private _model: CodeActionModel; + private _codeActionContextMenu: CodeActionContextMenu; + private _lightBulbWidget: LightBulbWidget; + private _disposables: IDisposable[] = []; + + private _activeRequest: CancelablePromise | 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 { + 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 { + return applyCodeAction(action, this._bulkEditService, this._commandService, this._editor); + } +} \ No newline at end of file diff --git a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts index eb965620574..55986976584 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts @@ -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'; diff --git a/src/vs/workbench/contrib/markers/electron-browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/electron-browser/markersTreeViewer.ts index 0a8d4a38d84..992e9cb5caa 100644 --- a/src/vs/workbench/contrib/markers/electron-browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/electron-browser/markersTreeViewer.ts @@ -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;