mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-09 01:32:34 -05:00
* Merge from vscode 64980ea1f3f532c82bb6c28d27bba9ef2c5b4463 * fix config changes * fix strictnull checks
478 lines
15 KiB
TypeScript
478 lines
15 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
|
|
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
|
import { Disposable } 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 { IPosition } from 'vs/editor/common/core/position';
|
|
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
|
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
|
import { CodeAction } from 'vs/editor/common/modes';
|
|
import { CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction';
|
|
import { CodeActionUi } from 'vs/editor/contrib/codeAction/codeActionUi';
|
|
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 { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
|
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
|
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 { CodeActionModel, CodeActionsState, SUPPORTED_CODE_ACTIONS } from './codeActionModel';
|
|
import { CodeActionAutoApply, CodeActionFilter, CodeActionKind, CodeActionTrigger } from './codeActionTrigger';
|
|
|
|
function contextKeyForSupportedActions(kind: CodeActionKind) {
|
|
return ContextKeyExpr.regex(
|
|
SUPPORTED_CODE_ACTIONS.keys()[0],
|
|
new RegExp('(\\s|^)' + escapeRegExpCharacters(kind.value) + '\\b'));
|
|
}
|
|
|
|
|
|
export class QuickFixController extends Disposable implements IEditorContribution {
|
|
|
|
private static readonly ID = 'editor.contrib.quickFixController';
|
|
|
|
public static get(editor: ICodeEditor): QuickFixController {
|
|
return editor.getContribution<QuickFixController>(QuickFixController.ID);
|
|
}
|
|
|
|
private readonly _editor: ICodeEditor;
|
|
private readonly _model: CodeActionModel;
|
|
private readonly _ui: CodeActionUi;
|
|
|
|
constructor(
|
|
editor: ICodeEditor,
|
|
@IMarkerService markerService: IMarkerService,
|
|
@IContextKeyService contextKeyService: IContextKeyService,
|
|
@IEditorProgressService progressService: IEditorProgressService,
|
|
@IContextMenuService contextMenuService: IContextMenuService,
|
|
@IKeybindingService keybindingService: IKeybindingService,
|
|
@ICommandService private readonly _commandService: ICommandService,
|
|
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
|
|
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
|
) {
|
|
super();
|
|
|
|
this._editor = editor;
|
|
this._model = this._register(new CodeActionModel(this._editor, markerService, contextKeyService, progressService));
|
|
this._register(this._model.onDidChangeState((newState) => this.update(newState)));
|
|
|
|
this._ui = this._register(new CodeActionUi(editor, QuickFixAction.Id, {
|
|
applyCodeAction: async (action, retrigger) => {
|
|
try {
|
|
await this._applyCodeAction(action);
|
|
} finally {
|
|
if (retrigger) {
|
|
this._trigger({ type: 'auto', filter: {} });
|
|
}
|
|
}
|
|
}
|
|
}, contextMenuService, keybindingService));
|
|
}
|
|
|
|
private update(newState: CodeActionsState.State): void {
|
|
this._ui.update(newState);
|
|
}
|
|
|
|
public showCodeActions(actions: CodeActionSet, at: IAnchor | IPosition) {
|
|
return this._ui.showCodeActionList(actions, at);
|
|
}
|
|
|
|
public getId(): string {
|
|
return QuickFixController.ID;
|
|
}
|
|
|
|
public manualTriggerAtCurrentPosition(
|
|
notAvailableMessage: string,
|
|
filter?: CodeActionFilter,
|
|
autoApply?: CodeActionAutoApply
|
|
): void {
|
|
if (!this._editor.hasModel()) {
|
|
return;
|
|
}
|
|
|
|
MessageController.get(this._editor).closeMessage();
|
|
const triggerPosition = this._editor.getPosition();
|
|
this._trigger({ type: 'manual', filter, autoApply, context: { notAvailableMessage, position: triggerPosition } });
|
|
}
|
|
|
|
private _trigger(trigger: CodeActionTrigger) {
|
|
return this._model.trigger(trigger);
|
|
}
|
|
|
|
private _applyCodeAction(action: CodeAction): Promise<void> {
|
|
return this._instantiationService.invokeFunction(applyCodeAction, action, this._bulkEditService, this._commandService, this._editor);
|
|
}
|
|
}
|
|
|
|
export async function applyCodeAction(
|
|
accessor: ServicesAccessor,
|
|
action: CodeAction,
|
|
bulkEditService: IBulkEditService,
|
|
commandService: ICommandService,
|
|
editor?: ICodeEditor,
|
|
): Promise<void> {
|
|
const notificationService = accessor.get(INotificationService);
|
|
if (action.edit) {
|
|
await bulkEditService.apply(action.edit, { editor });
|
|
}
|
|
if (action.command) {
|
|
try {
|
|
await commandService.executeCommand(action.command.id, ...(action.command.arguments || []));
|
|
} catch (err) {
|
|
const message = asMessage(err);
|
|
notificationService.error(
|
|
typeof message === 'string'
|
|
? message
|
|
: nls.localize('applyCodeActionFailed', "An unknown error occurred while applying the code action"));
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
function asMessage(err: any): string | undefined {
|
|
if (typeof err === 'string') {
|
|
return err;
|
|
} else if (err instanceof Error && typeof err.message === 'string') {
|
|
return err.message;
|
|
} else {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
function triggerCodeActionsForEditorSelection(
|
|
editor: ICodeEditor,
|
|
notAvailableMessage: string,
|
|
filter: CodeActionFilter | undefined,
|
|
autoApply: CodeActionAutoApply | undefined
|
|
): void {
|
|
if (editor.hasModel()) {
|
|
const controller = QuickFixController.get(editor);
|
|
if (controller) {
|
|
controller.manualTriggerAtCurrentPosition(notAvailableMessage, filter, autoApply);
|
|
}
|
|
}
|
|
}
|
|
|
|
export class QuickFixAction extends EditorAction {
|
|
|
|
static readonly Id = 'editor.action.quickFix';
|
|
|
|
constructor() {
|
|
super({
|
|
id: QuickFixAction.Id,
|
|
label: nls.localize('quickfix.trigger.label', "Quick Fix..."),
|
|
alias: 'Quick Fix...',
|
|
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider),
|
|
kbOpts: {
|
|
kbExpr: EditorContextKeys.editorTextFocus,
|
|
primary: KeyMod.CtrlCmd | KeyCode.US_DOT,
|
|
weight: KeybindingWeight.EditorContrib
|
|
}
|
|
});
|
|
}
|
|
|
|
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
|
|
return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available"), undefined, undefined);
|
|
}
|
|
}
|
|
|
|
|
|
class CodeActionCommandArgs {
|
|
public static fromUser(arg: any, defaults: { kind: CodeActionKind, apply: CodeActionAutoApply }): CodeActionCommandArgs {
|
|
if (!arg || typeof arg !== 'object') {
|
|
return new CodeActionCommandArgs(defaults.kind, defaults.apply, false);
|
|
}
|
|
return new CodeActionCommandArgs(
|
|
CodeActionCommandArgs.getKindFromUser(arg, defaults.kind),
|
|
CodeActionCommandArgs.getApplyFromUser(arg, defaults.apply),
|
|
CodeActionCommandArgs.getPreferredUser(arg));
|
|
}
|
|
|
|
private static getApplyFromUser(arg: any, defaultAutoApply: CodeActionAutoApply) {
|
|
switch (typeof arg.apply === 'string' ? arg.apply.toLowerCase() : '') {
|
|
case 'first': return CodeActionAutoApply.First;
|
|
case 'never': return CodeActionAutoApply.Never;
|
|
case 'ifsingle': return CodeActionAutoApply.IfSingle;
|
|
default: return defaultAutoApply;
|
|
}
|
|
}
|
|
|
|
private static getKindFromUser(arg: any, defaultKind: CodeActionKind) {
|
|
return typeof arg.kind === 'string'
|
|
? new CodeActionKind(arg.kind)
|
|
: defaultKind;
|
|
}
|
|
|
|
private static getPreferredUser(arg: any): boolean {
|
|
return typeof arg.preferred === 'boolean'
|
|
? arg.preferred
|
|
: false;
|
|
}
|
|
|
|
private constructor(
|
|
public readonly kind: CodeActionKind,
|
|
public readonly apply: CodeActionAutoApply,
|
|
public readonly preferred: boolean,
|
|
) { }
|
|
}
|
|
|
|
export class CodeActionCommand extends EditorCommand {
|
|
|
|
static readonly Id = 'editor.action.codeAction';
|
|
|
|
constructor() {
|
|
super({
|
|
id: CodeActionCommand.Id,
|
|
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider),
|
|
description: {
|
|
description: `Trigger a code action`,
|
|
args: [{
|
|
name: 'args',
|
|
schema: {
|
|
'type': 'object',
|
|
'required': ['kind'],
|
|
'properties': {
|
|
'kind': {
|
|
'type': 'string'
|
|
},
|
|
'apply': {
|
|
'type': 'string',
|
|
'default': 'ifSingle',
|
|
'enum': ['first', 'ifSingle', 'never']
|
|
}
|
|
}
|
|
}
|
|
}]
|
|
}
|
|
});
|
|
}
|
|
|
|
public runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, userArg: any) {
|
|
const args = CodeActionCommandArgs.fromUser(userArg, {
|
|
kind: CodeActionKind.Empty,
|
|
apply: CodeActionAutoApply.IfSingle,
|
|
});
|
|
return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available"),
|
|
{
|
|
kind: args.kind,
|
|
includeSourceActions: true,
|
|
onlyIncludePreferredActions: args.preferred,
|
|
},
|
|
args.apply);
|
|
}
|
|
}
|
|
|
|
|
|
export class RefactorAction extends EditorAction {
|
|
|
|
static readonly Id = 'editor.action.refactor';
|
|
|
|
constructor() {
|
|
super({
|
|
id: RefactorAction.Id,
|
|
label: nls.localize('refactor.label', "Refactor..."),
|
|
alias: 'Refactor...',
|
|
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider),
|
|
kbOpts: {
|
|
kbExpr: EditorContextKeys.editorTextFocus,
|
|
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_R,
|
|
mac: {
|
|
primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_R
|
|
},
|
|
weight: KeybindingWeight.EditorContrib
|
|
},
|
|
menuOpts: {
|
|
group: '1_modification',
|
|
order: 2,
|
|
when: ContextKeyExpr.and(
|
|
EditorContextKeys.writable,
|
|
contextKeyForSupportedActions(CodeActionKind.Refactor)),
|
|
},
|
|
description: {
|
|
description: 'Refactor...',
|
|
args: [{
|
|
name: 'args',
|
|
schema: {
|
|
'type': 'object',
|
|
'properties': {
|
|
'kind': {
|
|
'type': 'string'
|
|
},
|
|
'apply': {
|
|
'type': 'string',
|
|
'default': 'never',
|
|
'enum': ['first', 'ifSingle', 'never']
|
|
}
|
|
}
|
|
}
|
|
}]
|
|
}
|
|
});
|
|
}
|
|
|
|
public run(_accessor: ServicesAccessor, editor: ICodeEditor, userArg: any): void {
|
|
const args = CodeActionCommandArgs.fromUser(userArg, {
|
|
kind: CodeActionKind.Refactor,
|
|
apply: CodeActionAutoApply.Never
|
|
});
|
|
return triggerCodeActionsForEditorSelection(editor,
|
|
nls.localize('editor.action.refactor.noneMessage', "No refactorings available"),
|
|
{
|
|
kind: CodeActionKind.Refactor.contains(args.kind) ? args.kind : CodeActionKind.Empty,
|
|
onlyIncludePreferredActions: args.preferred,
|
|
},
|
|
args.apply);
|
|
}
|
|
}
|
|
|
|
|
|
export class SourceAction extends EditorAction {
|
|
|
|
static readonly Id = 'editor.action.sourceAction';
|
|
|
|
constructor() {
|
|
super({
|
|
id: SourceAction.Id,
|
|
label: nls.localize('source.label', "Source Action..."),
|
|
alias: 'Source Action...',
|
|
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider),
|
|
menuOpts: {
|
|
group: '1_modification',
|
|
order: 2.1,
|
|
when: ContextKeyExpr.and(
|
|
EditorContextKeys.writable,
|
|
contextKeyForSupportedActions(CodeActionKind.Source)),
|
|
},
|
|
description: {
|
|
description: 'Source Action...',
|
|
args: [{
|
|
name: 'args',
|
|
schema: {
|
|
'type': 'object',
|
|
'properties': {
|
|
'kind': {
|
|
'type': 'string'
|
|
},
|
|
'apply': {
|
|
'type': 'string',
|
|
'default': 'never',
|
|
'enum': ['first', 'ifSingle', 'never']
|
|
}
|
|
}
|
|
}
|
|
}]
|
|
}
|
|
});
|
|
}
|
|
|
|
public run(_accessor: ServicesAccessor, editor: ICodeEditor, userArg: any): void {
|
|
const args = CodeActionCommandArgs.fromUser(userArg, {
|
|
kind: CodeActionKind.Source,
|
|
apply: CodeActionAutoApply.Never
|
|
});
|
|
return triggerCodeActionsForEditorSelection(editor,
|
|
nls.localize('editor.action.source.noneMessage', "No source actions available"),
|
|
{
|
|
kind: CodeActionKind.Source.contains(args.kind) ? args.kind : CodeActionKind.Empty,
|
|
includeSourceActions: true,
|
|
onlyIncludePreferredActions: args.preferred,
|
|
},
|
|
args.apply);
|
|
}
|
|
}
|
|
|
|
export class OrganizeImportsAction extends EditorAction {
|
|
|
|
static readonly Id = 'editor.action.organizeImports';
|
|
|
|
constructor() {
|
|
super({
|
|
id: OrganizeImportsAction.Id,
|
|
label: nls.localize('organizeImports.label', "Organize Imports"),
|
|
alias: 'Organize Imports',
|
|
precondition: ContextKeyExpr.and(
|
|
EditorContextKeys.writable,
|
|
contextKeyForSupportedActions(CodeActionKind.SourceOrganizeImports)),
|
|
kbOpts: {
|
|
kbExpr: EditorContextKeys.editorTextFocus,
|
|
primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_O,
|
|
weight: KeybindingWeight.EditorContrib
|
|
}
|
|
});
|
|
}
|
|
|
|
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
|
|
return triggerCodeActionsForEditorSelection(editor,
|
|
nls.localize('editor.action.organize.noneMessage', "No organize imports action available"),
|
|
{ kind: CodeActionKind.SourceOrganizeImports, includeSourceActions: true },
|
|
CodeActionAutoApply.IfSingle);
|
|
}
|
|
}
|
|
|
|
export class FixAllAction extends EditorAction {
|
|
|
|
static readonly Id = 'editor.action.fixAll';
|
|
|
|
constructor() {
|
|
super({
|
|
id: FixAllAction.Id,
|
|
label: nls.localize('fixAll.label', "Fix All"),
|
|
alias: 'Fix All',
|
|
precondition: ContextKeyExpr.and(
|
|
EditorContextKeys.writable,
|
|
contextKeyForSupportedActions(CodeActionKind.SourceFixAll))
|
|
});
|
|
}
|
|
|
|
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
|
|
return triggerCodeActionsForEditorSelection(editor,
|
|
nls.localize('fixAll.noneMessage', "No fix all action available"),
|
|
{ kind: CodeActionKind.SourceFixAll, includeSourceActions: true },
|
|
CodeActionAutoApply.IfSingle);
|
|
}
|
|
}
|
|
|
|
export class AutoFixAction extends EditorAction {
|
|
|
|
static readonly Id = 'editor.action.autoFix';
|
|
|
|
constructor() {
|
|
super({
|
|
id: AutoFixAction.Id,
|
|
label: nls.localize('autoFix.label', "Auto Fix..."),
|
|
alias: 'Auto Fix...',
|
|
precondition: ContextKeyExpr.and(
|
|
EditorContextKeys.writable,
|
|
contextKeyForSupportedActions(CodeActionKind.QuickFix)),
|
|
kbOpts: {
|
|
kbExpr: EditorContextKeys.editorTextFocus,
|
|
primary: KeyMod.Alt | KeyMod.Shift | KeyCode.US_DOT,
|
|
mac: {
|
|
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_DOT
|
|
},
|
|
weight: KeybindingWeight.EditorContrib
|
|
}
|
|
});
|
|
}
|
|
|
|
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
|
|
return triggerCodeActionsForEditorSelection(editor,
|
|
nls.localize('editor.action.autoFix.noneMessage', "No auto fixes available"),
|
|
{
|
|
kind: CodeActionKind.QuickFix,
|
|
onlyIncludePreferredActions: true
|
|
},
|
|
CodeActionAutoApply.IfSingle);
|
|
}
|
|
}
|