/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; import * as azdata from 'azdata'; import * as nls from 'vscode-nls'; import { ApiWrapper } from './apiWrapper'; import { TreeNode } from './treeNodes'; import * as utils from './utils'; import * as constants from './constants'; import { AppContext } from './appContext'; export interface ICommandContextParsingOptions { editor: boolean; uri: boolean; } export interface ICommandBaseContext { command: string; editor?: vscode.TextEditor; uri?: vscode.Uri; } export interface ICommandUnknownContext extends ICommandBaseContext { type: 'unknown'; } export interface ICommandUriContext extends ICommandBaseContext { type: 'uri'; } export interface ICommandViewContext extends ICommandBaseContext { type: 'view'; node: TreeNode; } export interface ICommandObjectExplorerContext extends ICommandBaseContext { type: 'objectexplorer'; explorerContext: azdata.ObjectExplorerContext; } export type CommandContext = ICommandObjectExplorerContext | ICommandViewContext | ICommandUriContext | ICommandUnknownContext; function isTextEditor(editor: any): editor is vscode.TextEditor { if (editor === undefined) { return false; } return editor.id !== undefined && ((editor as vscode.TextEditor).edit !== undefined || (editor as vscode.TextEditor).document !== undefined); } export abstract class Command extends vscode.Disposable { protected readonly contextParsingOptions: ICommandContextParsingOptions = { editor: false, uri: false }; private disposable: vscode.Disposable; constructor(command: string | string[], protected appContext: AppContext) { super(() => this.dispose()); if (typeof command === 'string') { this.disposable = this.apiWrapper.registerCommand(command, (...args: any[]) => this._execute(command, ...args), this); return; } const subscriptions = command.map(cmd => this.apiWrapper.registerCommand(cmd, (...args: any[]) => this._execute(cmd, ...args), this)); this.disposable = vscode.Disposable.from(...subscriptions); } dispose(): void { this.disposable && this.disposable.dispose(); } protected get apiWrapper(): ApiWrapper { return this.appContext.apiWrapper; } protected async preExecute(context: CommandContext, ...args: any[]): Promise { return this.execute(...args); } abstract execute(...args: any[]): any; protected _execute(command: string, ...args: any[]): any { // TODO consider using Telemetry.trackEvent(command); const [context, rest] = Command.parseContext(command, this.contextParsingOptions, ...args); return this.preExecute(context, ...rest); } private static parseContext(command: string, options: ICommandContextParsingOptions, ...args: any[]): [CommandContext, any[]] { let editor: vscode.TextEditor | undefined = undefined; let firstArg = args[0]; if (options.editor && (firstArg === undefined || isTextEditor(firstArg))) { editor = firstArg; args = args.slice(1); firstArg = args[0]; } if (options.uri && (firstArg === undefined || firstArg instanceof vscode.Uri)) { const [uri, ...rest] = args as [vscode.Uri, any]; return [{ command: command, type: 'uri', editor: editor, uri: uri }, rest]; } if (firstArg instanceof TreeNode) { const [node, ...rest] = args as [TreeNode, any]; return [{ command: command, type: constants.ViewType, node: node }, rest]; } if (firstArg && utils.isObjectExplorerContext(firstArg)) { const [explorerContext, ...rest] = args as [azdata.ObjectExplorerContext, any]; return [{ command: command, type: constants.ObjectExplorerService, explorerContext: explorerContext }, rest]; } return [{ command: command, type: 'unknown', editor: editor }, args]; } }