diff --git a/src/sql/parts/connection/common/interfaces.ts b/src/sql/parts/connection/common/interfaces.ts index 99d10d5c88..3361fe5282 100644 --- a/src/sql/parts/connection/common/interfaces.ts +++ b/src/sql/parts/connection/common/interfaces.ts @@ -6,22 +6,9 @@ 'use strict'; import * as sqlops from 'sqlops'; -// A Connection Profile contains all the properties of connection credentials, with additional -// optional name and details on whether password should be saved -export interface IConnectionProfile extends sqlops.ConnectionInfo { - serverName: string; - databaseName: string; - userName: string; - password: string; - authenticationType: string; - savePassword: boolean; - groupFullName: string; - groupId: string; +export interface IConnectionProfile extends sqlops.IConnectionProfile { getOptionsKey(): string; - matches(profile: IConnectionProfile): boolean; - providerName: string; - saveProfile: boolean; - id: string; + matches(profile: sqlops.IConnectionProfile): boolean; } export interface IConnectionProfileStore { diff --git a/src/sql/parts/dashboard/services/dashboardServiceInterface.service.ts b/src/sql/parts/dashboard/services/dashboardServiceInterface.service.ts index f23a400a27..80bbd30130 100644 --- a/src/sql/parts/dashboard/services/dashboardServiceInterface.service.ts +++ b/src/sql/parts/dashboard/services/dashboardServiceInterface.service.ts @@ -41,6 +41,7 @@ import Severity from 'vs/base/common/severity'; import * as nls from 'vs/nls'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { deepClone } from 'vs/base/common/objects'; +import { ICommandService } from 'vs/platform/commands/common/commands'; import { IDashboardWebviewService } from 'sql/services/dashboardWebview/common/dashboardWebviewService'; @@ -133,6 +134,7 @@ export class DashboardServiceInterface implements OnDestroy { private _storageService: IStorageService; private _capabilitiesService: ICapabilitiesService; private _configurationEditingService: ConfigurationEditingService; + private _commandService: ICommandService; private _dashboardWebviewService: IDashboardWebviewService; private _partService: IPartService; @@ -166,6 +168,7 @@ export class DashboardServiceInterface implements OnDestroy { this._storageService = this._bootstrapService.storageService; this._capabilitiesService = this._bootstrapService.capabilitiesService; this._configurationEditingService = this._bootstrapService.configurationEditorService; + this._commandService = this._bootstrapService.commandService; this._dashboardWebviewService = this._bootstrapService.dashboardWebviewService; this._partService = this._bootstrapService.partService; } @@ -190,6 +193,10 @@ export class DashboardServiceInterface implements OnDestroy { return this._connectionManagementService; } + public get commandService(): ICommandService { + return this._commandService; + } + public get themeService(): IWorkbenchThemeService { return this._themeService; } diff --git a/src/sql/parts/dashboard/widgets/explorer/explorerTree.ts b/src/sql/parts/dashboard/widgets/explorer/explorerTree.ts index 91cbe353fd..ed80dbe244 100644 --- a/src/sql/parts/dashboard/widgets/explorer/explorerTree.ts +++ b/src/sql/parts/dashboard/widgets/explorer/explorerTree.ts @@ -27,6 +27,7 @@ import { IAction } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { generateUuid } from 'vs/base/common/uuid'; import { $ } from 'vs/base/browser/dom'; +import { OEAction } from 'sql/parts/registeredServer/viewlet/objectExplorerActions'; export class ObjectMetadataWrapper implements ObjectMetadata { public metadataType: MetadataType; @@ -370,14 +371,14 @@ function GetExplorerActions(element: TreeResource, instantiationService: IInstan actions.push(instantiationService.createInstance(ScriptAlterAction, ScriptAlterAction.ID, ScriptAlterAction.LABEL)); } } else { - actions.push(instantiationService.createInstance(NewQueryAction, NewQueryAction.ID, NewQueryAction.LABEL, NewQueryAction.ICON)); + actions.push(instantiationService.createInstance(OEAction, NewQueryAction.ID, NewQueryAction.LABEL)); - let action: IAction = instantiationService.createInstance(RestoreAction, RestoreAction.ID, RestoreAction.LABEL, RestoreAction.ICON); + let action: IAction = instantiationService.createInstance(OEAction, RestoreAction.ID, RestoreAction.LABEL); if (capabilitiesService.isFeatureAvailable(action, info)) { actions.push(action); } - action = instantiationService.createInstance(BackupAction, BackupAction.ID, BackupAction.LABEL, BackupAction.ICON); + action = instantiationService.createInstance(OEAction, BackupAction.ID, BackupAction.LABEL); if (capabilitiesService.isFeatureAvailable(action, info)) { actions.push(action); } diff --git a/src/sql/parts/dashboard/widgets/tasks/tasksWidget.component.ts b/src/sql/parts/dashboard/widgets/tasks/tasksWidget.component.ts index 10cb632593..dcce7455d2 100644 --- a/src/sql/parts/dashboard/widgets/tasks/tasksWidget.component.ts +++ b/src/sql/parts/dashboard/widgets/tasks/tasksWidget.component.ts @@ -12,7 +12,7 @@ import { DomSanitizer } from '@angular/platform-browser'; /* SQL imports */ import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/parts/dashboard/common/dashboardWidget'; import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service'; -import { ITaskRegistry, Extensions, TaskAction } from 'sql/platform/tasks/taskRegistry'; +import { TaskRegistry } from 'sql/platform/tasks/common/tasks'; import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; import { BaseActionContext } from 'sql/workbench/common/actions'; @@ -20,7 +20,6 @@ import { BaseActionContext } from 'sql/workbench/common/actions'; import * as themeColors from 'vs/workbench/common/theme'; import * as colors from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant, ICssStyleCollector, ITheme } from 'vs/platform/theme/common/themeService'; -import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; import Severity from 'vs/base/common/severity'; import * as nls from 'vs/nls'; @@ -29,6 +28,8 @@ import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElemen import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { $, Builder } from 'vs/base/browser/builder'; import * as DOM from 'vs/base/browser/dom'; +import { CommandsRegistry, ICommand } from 'vs/platform/commands/common/commands'; +import { MenuRegistry, ICommandAction } from 'vs/platform/actions/common/actions'; interface IConfig { tasks: Array; @@ -40,7 +41,7 @@ interface IConfig { }) export class TasksWidget extends DashboardWidget implements IDashboardWidget, OnInit { private _size: number = 98; - private _tasks: Array = []; + private _tasks: Array = []; private _profile: IConnectionProfile; private _scrollableElement: ScrollableElement; private $container: Builder; @@ -55,30 +56,14 @@ export class TasksWidget extends DashboardWidget implements IDashboardWidget, On ) { super(); this._profile = this._bootstrap.connectionManagementService.connectionInfo.connectionProfile; - let registry = Registry.as(Extensions.TaskContribution); let tasksConfig = Object.values(this._config.widget)[0]; - let taskIds: Array; + let tasks = TaskRegistry.getTasks(); if (tasksConfig.tasks) { - taskIds = Object.keys(tasksConfig.tasks); - } else { - taskIds = registry.ids; + tasks = Object.keys(tasksConfig.tasks).filter(i => tasks.includes(i)); } - let ctorMap = registry.idToCtorMap; - this._tasks = taskIds.map(id => { - let ctor = ctorMap[id]; - if (ctor) { - let action = this._bootstrap.instantiationService.createInstance(ctor, ctor.ID, ctor.LABEL, ctor.ICON); - if (this._bootstrap.capabilitiesService.isFeatureAvailable(action, this._bootstrap.connectionManagementService.connectionInfo)) { - return action; - } - } else { - this._bootstrap.messageService.show(Severity.Warning, nls.localize('missingTask', 'Could not find task {0}; are you missing an extension?', id)); - } - - return undefined; - }).filter(a => !types.isUndefinedOrNull(a)); + this._tasks = tasks.map(i => MenuRegistry.getCommand(i)); } ngOnInit() { @@ -120,11 +105,15 @@ export class TasksWidget extends DashboardWidget implements IDashboardWidget, On this.$container.style('height', height + 'px').style('width', width + 'px'); } - private _createTile(action: TaskAction): HTMLElement { - let label = $('div').safeInnerHtml(action.label); - let icon = $('span.icon').addClass(action.icon); - let innerTile = $('div').append(icon).append(label); + private _createTile(action: ICommandAction): HTMLElement { + let label = $('div').safeInnerHtml(types.isString(action.title) ? action.title : action.title.value); let tile = $('div.task-tile').style('height', this._size + 'px').style('width', this._size + 'px'); + let innerTile = $('div'); + if (action) { + let icon = $('span.icon').addClass(action.iconClass); + innerTile.append(icon); + } + innerTile.append(label); tile.append(innerTile); tile.on(DOM.EventType.CLICK, () => this.runTask(action)); return tile.getHTMLElement(); @@ -142,11 +131,9 @@ export class TasksWidget extends DashboardWidget implements IDashboardWidget, On } } - public runTask(task: Action) { - let context: BaseActionContext = { - profile: this._profile - }; - task.run(context); + public runTask(task: ICommandAction) { + let serverInfo = this._bootstrap.connectionManagementService.connectionInfo.serverInfo; + this._bootstrap.commandService.executeCommand(task.id, this._profile); } public layout(): void { diff --git a/src/sql/parts/dashboard/widgets/tasks/tasksWidget.contribution.ts b/src/sql/parts/dashboard/widgets/tasks/tasksWidget.contribution.ts index 3d0d3d4427..bd48db6026 100644 --- a/src/sql/parts/dashboard/widgets/tasks/tasksWidget.contribution.ts +++ b/src/sql/parts/dashboard/widgets/tasks/tasksWidget.contribution.ts @@ -4,17 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { registerDashboardWidget } from 'sql/platform/dashboard/common/widgetRegistry'; -import { Extensions as TaskExtensions, ITaskRegistry } from 'sql/platform/tasks/taskRegistry'; -import { Registry } from 'vs/platform/registry/common/platform'; - -let taskRegistry = Registry.as(TaskExtensions.TaskContribution); +import { TaskRegistry } from 'sql/platform/tasks/common/tasks'; let tasksSchema: IJSONSchema = { type: 'object', properties: { tasks: { - type: 'object', - properties: taskRegistry.taskSchemas + type: 'object' } } }; diff --git a/src/sql/parts/insights/browser/insightsDialogView.ts b/src/sql/parts/insights/browser/insightsDialogView.ts index ee6ad6e3bd..7a0168d79f 100644 --- a/src/sql/parts/insights/browser/insightsDialogView.ts +++ b/src/sql/parts/insights/browser/insightsDialogView.ts @@ -9,7 +9,7 @@ import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; import { Modal } from 'sql/base/browser/ui/modal/modal'; import { IInsightsConfigDetails } from 'sql/parts/dashboard/widgets/insights/interfaces'; import { attachButtonStyler, attachModalDialogStyler, attachTableStyler } from 'sql/common/theme/styler'; -import { ITaskRegistry, Extensions as TaskExtensions } from 'sql/platform/tasks/taskRegistry'; +import { TaskRegistry } from 'sql/platform/tasks/common/tasks'; import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; import * as TelemetryKeys from 'sql/common/telemetryKeys'; import { IInsightsDialogModel, ListResource, IInsightDialogActionContext, insertValueRegex } from 'sql/parts/insights/common/interfaces'; @@ -29,7 +29,6 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IListService } from 'vs/platform/list/browser/listService'; import * as nls from 'vs/nls'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { Registry } from 'vs/platform/registry/common/platform'; import { IAction } from 'vs/base/common/actions'; import { TPromise } from 'vs/base/common/winjs.base'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -37,6 +36,8 @@ import * as types from 'vs/base/common/types'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { MenuRegistry, ExecuteCommandAction } from 'vs/platform/actions/common/actions'; const labelDisplay = nls.localize("item", "Item"); const valueDisplay = nls.localize("value", "Value"); @@ -127,7 +128,8 @@ export class InsightsDialogView extends Modal { @IPartService partService: IPartService, @IContextMenuService private _contextMenuService: IContextMenuService, @ITelemetryService telemetryService: ITelemetryService, - @IContextKeyService contextKeyService: IContextKeyService + @IContextKeyService contextKeyService: IContextKeyService, + @ICommandService private _commandService: ICommandService ) { super(nls.localize("InsightsDialogTitle", "Insights"), TelemetryKeys.Insights, partService, telemetryService, contextKeyService); this._model.onDataChange(e => this.build()); @@ -284,12 +286,12 @@ export class InsightsDialogView extends Modal { this._topTableData.clear(); this._topTableData.push(inputArray); if (this._insight.actions && this._insight.actions.types) { - const taskRegistry = Registry.as(TaskExtensions.TaskContribution); - let tasks = taskRegistry.idToCtorMap; + let tasks = TaskRegistry.getTasks(); for (let action of this._insight.actions.types) { - let ctor = tasks[action]; - if (ctor) { - let button = this.addFooterButton(ctor.LABEL, () => { + let task = tasks.includes(action); + let commandAction = MenuRegistry.getCommand(action); + if (task) { + let button = this.addFooterButton(types.isString(commandAction.title) ? commandAction.title : commandAction.title.value, () => { let element = this._topTable.getSelectedRows(); let resource: ListResource; if (element && element.length > 0) { @@ -297,7 +299,7 @@ export class InsightsDialogView extends Modal { } else { return; } - this._instantiationService.createInstance(ctor, ctor.ID, ctor.LABEL, ctor.ICON).run(this.topInsightContext(resource)); + this._commandService.executeCommand(action, this._connectionProfile); }, 'left'); button.enabled = false; this._taskButtonDisposables.push(button); @@ -333,14 +335,14 @@ export class InsightsDialogView extends Modal { } private get insightActions(): TPromise { - const taskRegistry = Registry.as(TaskExtensions.TaskContribution); - let tasks = taskRegistry.idToCtorMap; + let tasks = TaskRegistry.getTasks(); let actions = this._insight.actions.types; let returnActions: IAction[] = []; for (let action of actions) { - let ctor = tasks[action]; - if (ctor) { - returnActions.push(this._instantiationService.createInstance(ctor, ctor.ID, ctor.LABEL, ctor.ICON)); + let task = tasks.includes(action); + let commandAction = MenuRegistry.getCommand(action); + if (task) { + returnActions.push(this._instantiationService.createInstance(ExecuteCommandAction, commandAction.title, commandAction.iconClass)); } } return TPromise.as(returnActions); @@ -413,4 +415,4 @@ export class InsightsDialogView extends Modal { } } } -} \ No newline at end of file +} diff --git a/src/sql/parts/profiler/contrib/profilerActions.contribution.ts b/src/sql/parts/profiler/contrib/profilerActions.contribution.ts index cbb85b9840..9f16666a61 100644 --- a/src/sql/parts/profiler/contrib/profilerActions.contribution.ts +++ b/src/sql/parts/profiler/contrib/profilerActions.contribution.ts @@ -6,7 +6,7 @@ import { GlobalNewProfilerAction } from './profilerWorkbenchActions'; -import { registerTask } from 'sql/platform/tasks/taskRegistry'; +import { TaskRegistry } from 'sql/platform/tasks/common/tasks'; import { NewProfilerAction } from './profilerActions'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -27,6 +27,5 @@ const newProfilerSchema: IJSONSchema = { if (process.env['VSCODE_DEV']) { const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalNewProfilerAction, GlobalNewProfilerAction.ID, GlobalNewProfilerAction.LABEL), 'Profiler: New Profiler', category); - - registerTask('new-profiler', '', newProfilerSchema, NewProfilerAction); + new NewProfilerAction().registerTask(); } diff --git a/src/sql/parts/profiler/contrib/profilerActions.ts b/src/sql/parts/profiler/contrib/profilerActions.ts index 7f3a66fcb5..3798ffc0a9 100644 --- a/src/sql/parts/profiler/contrib/profilerActions.ts +++ b/src/sql/parts/profiler/contrib/profilerActions.ts @@ -9,17 +9,18 @@ import { IProfilerService } from 'sql/parts/profiler/service/interfaces'; import { IProfilerController } from 'sql/parts/profiler/editor/controller/interfaces'; import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput'; import { BaseActionContext } from 'sql/workbench/common/actions'; -import { TaskAction } from 'sql/platform/tasks/taskRegistry'; +import { Task } from 'sql/platform/tasks/common/tasks'; +import { ObjectExplorerActionsContext } from 'sql/parts/registeredServer/viewlet/objectExplorerActions'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { IConnectionManagementService, IConnectionCompletionOptions, ConnectionType } from 'sql/parts/connection/common/connectionManagement'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; import { TPromise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; import { IEditorAction } from 'vs/editor/common/editorCommon'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ObjectExplorerActionsContext } from 'sql/parts/registeredServer/viewlet/objectExplorerActions'; -import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; -import { IConnectionManagementService, IConnectionCompletionOptions, ConnectionType } from 'sql/parts/connection/common/connectionManagement'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; export class ProfilerConnect extends Action { public static ID = 'profiler.connect'; @@ -226,29 +227,20 @@ export class ProfilerFindPrevious implements IEditorAction { } } -export class NewProfilerAction extends TaskAction { - public static ID = 'newProfiler'; - public static LABEL = nls.localize('newProfiler', 'New Profiler'); - public static ICON = 'profile'; +export class NewProfilerAction extends Task { + public static readonly ID = 'newProfiler'; + public static readonly LABEL = nls.localize('newProfiler', 'New Profiler'); + public static readonly ICON = 'profile'; private _connectionProfile: ConnectionProfile; - constructor( - id: string, label: string, icon: string, - @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, - @IConnectionManagementService private _connectionService: IConnectionManagementService, - @IInstantiationService private _instantiationService: IInstantiationService - ) { - super(id, label, icon); + constructor() { + super({ id: NewProfilerAction.ID, title: NewProfilerAction.LABEL, iconClass: NewProfilerAction.ICON }); } - run(actionContext: BaseActionContext): TPromise { - if (actionContext instanceof ObjectExplorerActionsContext) { - this._connectionProfile = actionContext.connectionProfile; - } - - let profilerInput = this._instantiationService.createInstance(ProfilerInput, actionContext.profile); - return this._editorService.openEditor(profilerInput, { pinned: true }, false).then(() => { + public runTask(accessor: ServicesAccessor, profile: IConnectionProfile): TPromise { + let profilerInput = accessor.get(IInstantiationService).createInstance(ProfilerInput, profile); + return accessor.get(IWorkbenchEditorService).openEditor(profilerInput, { pinned: true }, false).then(() => { let options: IConnectionCompletionOptions = { params: undefined, saveTheConnection: false, @@ -256,11 +248,9 @@ export class NewProfilerAction extends TaskAction { showDashboard: false, showFirewallRuleOnError: true }; - this._connectionService.connect(this._connectionProfile, profilerInput.id, options).then(() => { - TPromise.as(true); - }); + accessor.get(IConnectionManagementService).connect(this._connectionProfile, profilerInput.id, options); - return TPromise.as(true); + return TPromise.as(void 0); }); } } diff --git a/src/sql/parts/registeredServer/viewlet/objectExplorerActions.ts b/src/sql/parts/registeredServer/viewlet/objectExplorerActions.ts index 1aee95b6f2..5d42b1a049 100644 --- a/src/sql/parts/registeredServer/viewlet/objectExplorerActions.ts +++ b/src/sql/parts/registeredServer/viewlet/objectExplorerActions.ts @@ -11,8 +11,10 @@ import { ITree } from 'vs/base/parts/tree/browser/tree'; import { IConnectionManagementService, IConnectionCompletionOptions, IErrorMessageService } from 'sql/parts/connection/common/connectionManagement'; import { TreeNode } from 'sql/parts/registeredServer/common/treeNode'; import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; -import { NewQueryAction, ScriptSelectAction, EditDataAction, ScriptCreateAction, - ScriptExecuteAction, ScriptDeleteAction, ScriptAlterAction } from 'sql/workbench/common/actions'; +import { + NewQueryAction, ScriptSelectAction, EditDataAction, ScriptCreateAction, + ScriptExecuteAction, ScriptDeleteAction, ScriptAlterAction +} from 'sql/workbench/common/actions'; import { NodeType } from 'sql/parts/registeredServer/common/nodeType'; import { TreeUpdateUtils } from 'sql/parts/registeredServer/viewlet/treeUpdateUtils'; import { TreeSelectionHandler } from 'sql/parts/registeredServer/viewlet/treeSelectionHandler'; @@ -22,6 +24,9 @@ import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; import { IObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService'; import * as Constants from 'sql/parts/connection/common/constants'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ExecuteCommandAction } from 'vs/platform/actions/common/actions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; export class ObjectExplorerActionsContext { public treeNode: TreeNode; @@ -30,34 +35,33 @@ export class ObjectExplorerActionsContext { public tree: ITree; } -export class OENewQueryAction extends NewQueryAction { - public static ID = 'objectExplorer.' + NewQueryAction.ID; +export class OEAction extends ExecuteCommandAction { private _objectExplorerTreeNode: TreeNode; private _container: HTMLElement; private _treeSelectionHandler: TreeSelectionHandler; constructor( - id: string, label: string, icon: string, - @IQueryEditorService protected _queryEditorService: IQueryEditorService, - @IConnectionManagementService protected _connectionManagementService: IConnectionManagementService, + id: string, label: string, @IInstantiationService private _instantiationService: IInstantiationService, - @IObjectExplorerService protected _objectExplorerService: IObjectExplorerService, - @IWorkbenchEditorService protected _workbenchEditorService: IWorkbenchEditorService + @ICommandService commandService: ICommandService, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService ) { - super(id, label, icon, _queryEditorService, _connectionManagementService, _objectExplorerService, _workbenchEditorService); + super(id, label, commandService); } public run(actionContext: any): TPromise { this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler); - if (actionContext instanceof ObjectExplorerActionsContext) { - //set objectExplorerTreeNode for context menu clicks - this._objectExplorerTreeNode = actionContext.treeNode; - this._container = actionContext.container; + + + let profile: IConnectionProfile; + if (actionContext.connectionProfile) { + profile = actionContext.connectionProfile; + } else { + profile = TreeUpdateUtils.getConnectionProfile(actionContext.treeNode); } this._treeSelectionHandler.onTreeActionStateChange(true); - var connectionProfile = TreeUpdateUtils.getConnectionProfile(this._objectExplorerTreeNode); - return super.run({ profile: connectionProfile }).then(() => { + return super.run(profile).then(() => { this._treeSelectionHandler.onTreeActionStateChange(false); return true; }); @@ -443,13 +447,13 @@ export class ObjectExplorerActionUtilities { let basicScripting = [OEScriptCreateAction, OEScriptDeleteAction]; let storedProcedureScripting = isMssqlProvider ? [OEScriptCreateAction, OEScriptAlterAction, OEScriptDeleteAction, OEScriptExecuteAction] : - basicScripting; + basicScripting; let viewScripting = isMssqlProvider ? [OEScriptSelectAction, OEScriptCreateAction, OEScriptAlterAction, OEScriptDeleteAction] : - [OEScriptSelectAction, OEScriptCreateAction, OEScriptDeleteAction]; + [OEScriptSelectAction, OEScriptCreateAction, OEScriptDeleteAction]; let functionScripting = isMssqlProvider ? [OEScriptCreateAction, OEScriptAlterAction, OEScriptDeleteAction] : - basicScripting; + basicScripting; scriptMap.set(NodeType.AggregateFunction, functionScripting); scriptMap.set(NodeType.PartitionFunction, functionScripting); scriptMap.set(NodeType.ScalarValuedFunction, functionScripting); diff --git a/src/sql/parts/registeredServer/viewlet/serverTreeActionProvider.ts b/src/sql/parts/registeredServer/viewlet/serverTreeActionProvider.ts index 0061a6af12..9c5ebccb47 100644 --- a/src/sql/parts/registeredServer/viewlet/serverTreeActionProvider.ts +++ b/src/sql/parts/registeredServer/viewlet/serverTreeActionProvider.ts @@ -11,13 +11,14 @@ import { IAction } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { - DisconnectConnectionAction, AddServerAction, NewQueryAction, + DisconnectConnectionAction, AddServerAction, DeleteConnectionAction, RefreshAction, EditServerGroupAction } from 'sql/parts/registeredServer/viewlet/connectionTreeAction'; import { - OENewQueryAction, DisconnectAction, ObjectExplorerActionUtilities, - ManageConnectionAction + DisconnectAction, ObjectExplorerActionUtilities, + ManageConnectionAction, + OEAction } from 'sql/parts/registeredServer/viewlet/objectExplorerActions'; import { TreeNode } from 'sql/parts/registeredServer/common/treeNode'; import { NodeType } from 'sql/parts/registeredServer/common/nodeType'; @@ -26,6 +27,8 @@ import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile import { NewProfilerAction } from 'sql/parts/profiler/contrib/profilerActions'; import { TreeUpdateUtils } from 'sql/parts/registeredServer/viewlet/treeUpdateUtils'; import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { ExecuteCommandAction } from 'vs/platform/actions/common/actions'; +import { NewQueryAction } from 'sql/workbench/common/actions'; /** * Provides actions for the server tree elements @@ -75,7 +78,7 @@ export class ServerTreeActionProvider extends ContributableActionProvider { public getConnectionActions(tree: ITree, element: ConnectionProfile): IAction[] { let actions: IAction[] = []; actions.push(this._instantiationService.createInstance(ManageConnectionAction, ManageConnectionAction.ID, ManageConnectionAction.LABEL)); - actions.push(this._instantiationService.createInstance(NewQueryAction, NewQueryAction.ID, NewQueryAction.LABEL)); + actions.push(this._instantiationService.createInstance(OEAction, NewQueryAction.ID, NewQueryAction.LABEL)); if (this._connectionManagementService.isProfileConnected(element)) { actions.push(this._instantiationService.createInstance(DisconnectConnectionAction, DisconnectConnectionAction.ID, DisconnectConnectionAction.LABEL)); } @@ -83,7 +86,7 @@ export class ServerTreeActionProvider extends ContributableActionProvider { actions.push(this._instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL, tree, element)); if (process.env['VSCODE_DEV']) { - actions.push(this._instantiationService.createInstance(NewProfilerAction, NewProfilerAction.ID, NewProfilerAction.LABEL, NewProfilerAction.ICON)); + actions.push(this._instantiationService.createInstance(OEAction, NewProfilerAction.ID, NewProfilerAction.LABEL)); } return actions; @@ -112,7 +115,7 @@ export class ServerTreeActionProvider extends ContributableActionProvider { return actions; } } - actions.push(this._instantiationService.createInstance(OENewQueryAction, OENewQueryAction.ID, OENewQueryAction.LABEL, OENewQueryAction.ICON)); + actions.push(this._instantiationService.createInstance(OEAction, NewQueryAction.ID, NewQueryAction.LABEL)); let scriptMap: Map = ObjectExplorerActionUtilities.getScriptMap(treeNode); let supportedActions = scriptMap.get(treeNode.nodeTypeId); let self = this; @@ -131,4 +134,4 @@ export class ServerTreeActionProvider extends ContributableActionProvider { return actions; } -} \ No newline at end of file +} diff --git a/src/sql/platform/tasks/common/tasks.ts b/src/sql/platform/tasks/common/tasks.ts new file mode 100644 index 0000000000..478b49b72f --- /dev/null +++ b/src/sql/platform/tasks/common/tasks.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as types from 'vs/base/common/types'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as platform from 'vs/platform/registry/common/platform'; +import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; +import { Action } from 'vs/base/common/actions'; +import { IConstructorSignature3, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import * as nls from 'vs/nls'; +import { ILocalizedString, IMenuItem, MenuRegistry, ICommandAction } from 'vs/platform/actions/common/actions'; +import Event from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { LinkedList } from 'vs/base/common/linkedList'; + +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; + +export interface ITaskOptions { + id: string; + title: string; + iconClass: string; + description?: ITaskHandlerDescription; +} + +export abstract class Task { + public readonly id: string; + public readonly title: string; + public readonly iconClass: string; + private readonly _description: ITaskHandlerDescription; + + constructor(opts: ITaskOptions) { + this.id = opts.id; + this.title = opts.title; + this.iconClass = opts.iconClass; + this._description = opts.description; + } + + private toITask(): ITask { + return { + id: this.id, + handler: (accessor, profile, args) => this.runTask(accessor, profile, args), + description: this._description + }; + } + + private toCommandAction(): ICommandAction { + return { + iconClass: this.iconClass, + id: this.id, + title: this.title + }; + } + + public registerTask(): IDisposable { + MenuRegistry.addCommand(this.toCommandAction()); + return TaskRegistry.registerTask(this.toITask()); + } + + public abstract runTask(accessor: ServicesAccessor, profile: IConnectionProfile, args: any): void | TPromise; +} + +export interface ITaskHandlerDescription { + description: string; + args: { name: string; description?: string; constraint?: types.TypeConstraint; }[]; + returns?: string; +} + +export interface ITaskEvent { + taskId: string; +} + +export interface ITaskAction { + id: string; + title: string | ILocalizedString; + category?: string | ILocalizedString; + iconClass?: string; + iconPath?: string; +} + +export interface ITaskHandler { + (accessor: ServicesAccessor, profile: IConnectionProfile, ...args: any[]): void; +} + +export interface ITask { + id: string; + handler: ITaskHandler; + precondition?: ContextKeyExpr; + description?: ITaskHandlerDescription; +} + +export interface ITaskRegistry { + registerTask(id: string, command: ITaskHandler): IDisposable; + registerTask(command: ITask): IDisposable; + getTasks(): string[]; +} + +export const TaskRegistry: ITaskRegistry = new class implements ITaskRegistry { + + private _tasks = new Array(); + + registerTask(idOrTask: string | ITask, handler?: ITaskHandler): IDisposable { + let disposable: IDisposable; + let id: string; + if (types.isString(idOrTask)) { + disposable = CommandsRegistry.registerCommand(idOrTask, handler); + id = idOrTask; + } else { + disposable = CommandsRegistry.registerCommand(idOrTask); + id = idOrTask.id; + } + + this._tasks.push(id); + + return { + dispose: () => { + let index = this._tasks.indexOf(id); + if (index >= 0) { + this._tasks = this._tasks.splice(index, 1); + } + disposable.dispose(); + } + }; + } + + getTasks(): string[] { + return this._tasks; + } +}; diff --git a/src/sql/platform/tasks/taskRegistry.ts b/src/sql/platform/tasks/taskRegistry.ts deleted file mode 100644 index f22383148b..0000000000 --- a/src/sql/platform/tasks/taskRegistry.ts +++ /dev/null @@ -1,95 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as platform from 'vs/platform/registry/common/platform'; -import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; -import { Action } from 'vs/base/common/actions'; -import { IConstructorSignature3 } from 'vs/platform/instantiation/common/instantiation'; -import * as nls from 'vs/nls'; - -export type TaskIdentifier = string; - -export interface ActionICtor extends IConstructorSignature3 { - ID: string; - LABEL: string; - ICON: string; -} - -export class TaskAction extends Action { - constructor(id: string, label: string, private _icon: string) { - super(id, label); - } - - get icon(): string { - return this._icon; - } -} - -export const Extensions = { - TaskContribution: 'workbench.contributions.tasks' -}; - -export interface ITaskRegistry { - /** - * Returns a map of action ids to their contructors; - */ - idToCtorMap: { [id: string]: ActionICtor }; - - /** - * Returns array of registered ids - */ - ids: Array; - - /** - * Schemas of the tasks registered - */ - taskSchemas: IJSONSchemaMap; - - /** - * Registers an action as a task which can be ran given the schema as an input - * @param id id of the task - * @param description desciption of the task - * @param schema schema of expected input - * @param ctor contructor of the action - */ - registerTask(id: string, description: string, schema: IJSONSchema, ctor: ActionICtor): TaskIdentifier; -} - -class TaskRegistry implements ITaskRegistry { - private _idCtorMap: { [id: string]: ActionICtor } = {}; - private _taskSchema: IJSONSchema = { type: 'object', description: nls.localize('schema.taskSchema', 'Task actions specific for sql'), properties: {}, additionalProperties: false }; - - get idToCtorMap(): { [id: string]: ActionICtor } { - return this._idCtorMap; - } - - get ids(): Array { - return Object.keys(this._idCtorMap); - } - - get taskSchemas(): IJSONSchemaMap { - return this._taskSchema.properties; - } - - /** - * Registers an action as a task which can be ran given the schema as an input - * @param id id of the task - * @param description desciption of the task - * @param schema schema of expected input - * @param ctor contructor of the action - */ - registerTask(id: string, description: string, schema: IJSONSchema, ctor: ActionICtor): TaskIdentifier { - this._idCtorMap[id] = ctor; - this._taskSchema.properties[id] = schema; - return id; - } -} - -const taskRegistry = new TaskRegistry(); -platform.Registry.add(Extensions.TaskContribution, taskRegistry); - -export function registerTask(id: string, description: string, schema: IJSONSchema, ctor: ActionICtor): TaskIdentifier { - return taskRegistry.registerTask(id, description, schema, ctor); -} \ No newline at end of file diff --git a/src/sql/services/bootstrap/bootstrapService.ts b/src/sql/services/bootstrap/bootstrapService.ts index 66a43f3bf6..11a9810746 100644 --- a/src/sql/services/bootstrap/bootstrapService.ts +++ b/src/sql/services/bootstrap/bootstrapService.ts @@ -40,6 +40,7 @@ import { IWindowsService, IWindowService } from 'vs/platform/windows/common/wind import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService'; +import { ICommandService } from 'vs/platform/commands/common/commands'; export const BOOTSTRAP_SERVICE_ID = 'bootstrapService'; export const IBootstrapService = createDecorator(BOOTSTRAP_SERVICE_ID); @@ -89,6 +90,7 @@ export interface IBootstrapService { clipboardService: IClipboardService; capabilitiesService: ICapabilitiesService; configurationEditorService: ConfigurationEditingService; + commandService: ICommandService; dashboardWebviewService: IDashboardWebviewService; /* diff --git a/src/sql/services/bootstrap/bootstrapServiceImpl.ts b/src/sql/services/bootstrap/bootstrapServiceImpl.ts index e592d56461..5c44546889 100644 --- a/src/sql/services/bootstrap/bootstrapServiceImpl.ts +++ b/src/sql/services/bootstrap/bootstrapServiceImpl.ts @@ -24,6 +24,7 @@ import { ISqlOAuthService } from 'sql/common/sqlOAuthService'; import { IFileBrowserService, IFileBrowserDialogController } from 'sql/parts/fileBrowser/common/interfaces'; import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService'; import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import { IDashboardWebviewService } from 'sql/services/dashboardWebview/common/dashboardWebviewService'; import { $ } from 'vs/base/browser/dom'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -43,7 +44,7 @@ import { IWindowsService, IWindowService } from 'vs/platform/windows/common/wind import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService'; -import { IDashboardWebviewService } from 'sql/services/dashboardWebview/common/dashboardWebviewService'; +import { ICommandService } from 'vs/platform/commands/common/commands'; export class BootstrapService implements IBootstrapService { @@ -98,6 +99,7 @@ export class BootstrapService implements IBootstrapService { @IStorageService public storageService: IStorageService, @IClipboardService public clipboardService: IClipboardService, @ICapabilitiesService public capabilitiesService: ICapabilitiesService, + @ICommandService public commandService: ICommandService, @IDashboardWebviewService public dashboardWebviewService: IDashboardWebviewService ) { this.configurationEditorService = this.instantiationService.createInstance(ConfigurationEditingService); diff --git a/src/sql/sqlops.d.ts b/src/sql/sqlops.d.ts index 62356022c5..fe4eccc480 100644 --- a/src/sql/sqlops.d.ts +++ b/src/sql/sqlops.d.ts @@ -114,6 +114,20 @@ declare module 'sqlops' { options: { [name: string]: any }; } + export interface IConnectionProfile extends ConnectionInfo { + serverName: string; + databaseName: string; + userName: string; + password: string; + authenticationType: string; + savePassword: boolean; + groupFullName: string; + groupId: string; + providerName: string; + saveProfile: boolean; + id: string; + } + export interface ConnectionInfoSummary { /** @@ -1492,4 +1506,25 @@ declare module 'sqlops' { title: string ): ModalDialog; } + + export namespace tasks { + + export interface ITaskHandler { + (profile: IConnectionProfile, ...args: any[]): any; + } + + /** + * Registers a task that can be invoked via a keyboard shortcut, + * a menu item, an action, or directly. + * + * Registering a task with an existing task identifier twice + * will cause an error. + * + * @param task A unique identifier for the task. + * @param callback A task handler function. + * @param thisArg The `this` context used when invoking the handler function. + * @return Disposable which unregisters this task on disposal. + */ + export function registerTask(task: string, callback: ITaskHandler, thisArg?: any): vscode.Disposable; + } } diff --git a/src/sql/workbench/api/electron-browser/mainThreadTasks.ts b/src/sql/workbench/api/electron-browser/mainThreadTasks.ts new file mode 100644 index 0000000000..961430a87e --- /dev/null +++ b/src/sql/workbench/api/electron-browser/mainThreadTasks.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; +import { TaskRegistry, ITaskHandlerDescription } from 'sql/platform/tasks/common/tasks'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; + +import { + ExtHostAccountManagementShape, + MainThreadAccountManagementShape, + SqlExtHostContext, + SqlMainContext, + ExtHostTasksShape, + MainThreadTasksShape +} from 'sql/workbench/api/node/sqlExtHost.protocol'; + +import { IConnectionProfile } from 'sqlops'; + +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; + +@extHostNamedCustomer(SqlMainContext.MainThreadTasks) +export class MainThreadTasks implements MainThreadTasksShape { + + private readonly _disposables = new Map(); + private readonly _generateCommandsDocumentationRegistration: IDisposable; + private readonly _proxy: ExtHostTasksShape; + + constructor( + extHostContext: IExtHostContext + ) { + this._proxy = extHostContext.get(SqlExtHostContext.ExtHostTasks); + } + + dispose() { + this._disposables.forEach(value => value.dispose()); + this._disposables.clear(); + + this._generateCommandsDocumentationRegistration.dispose(); + } + + $registerTask(id: string): TPromise { + this._disposables.set( + id, + TaskRegistry.registerTask(id, (accessor, profile: IConnectionProfile, ...args) => { + if (profile instanceof ConnectionProfile) { + profile = profile.toIConnectionProfile(); + } + this._proxy.$executeContributedTask(id, profile, ...args); + }) + ); + return undefined; + } + + $unregisterTask(id: string): TPromise { + if (this._disposables.has(id)) { + this._disposables.get(id).dispose(); + this._disposables.delete(id); + } + return undefined; + } +} diff --git a/src/sql/workbench/api/node/extHostTasks.ts b/src/sql/workbench/api/node/extHostTasks.ts new file mode 100644 index 0000000000..b89c01bd3f --- /dev/null +++ b/src/sql/workbench/api/node/extHostTasks.ts @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { validateConstraint } from 'vs/base/common/types'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IMainContext } from 'vs/workbench/api/node/extHost.protocol'; +import * as extHostTypes from 'vs/workbench/api/node/extHostTypes'; +import { TPromise } from 'vs/base/common/winjs.base'; + +import * as sqlops from 'sqlops'; + +import { ITaskHandlerDescription } from 'sql/platform/tasks/common/tasks'; +import { SqlMainContext, MainThreadTasksShape, ExtHostTasksShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; + +interface TaskHandler { + callback: Function; + thisArg: any; + description: ITaskHandlerDescription; +} + +export class ExtHostTasks implements ExtHostTasksShape { + private _proxy: MainThreadTasksShape; + private _tasks = new Map(); + + constructor( + mainContext: IMainContext, + private logService: ILogService + ) { + this._proxy = mainContext.get(SqlMainContext.MainThreadTasks); + } + + registerTask(id: string, callback: sqlops.tasks.ITaskHandler, thisArg?: any, description?: ITaskHandlerDescription): extHostTypes.Disposable { + this.logService.trace('ExtHostTasks#registerTask', id); + + if (!id.trim().length) { + throw new Error('invalid id'); + } + + if (this._tasks.has(id)) { + throw new Error(`task '${id}' already exists`); + } + + this._tasks.set(id, { callback, thisArg, description }); + this._proxy.$registerTask(id); + + return new extHostTypes.Disposable(() => { + if (this._tasks.delete(id)) { + this._proxy.$unregisterTask(id); + } + }); + } + + $executeContributedTask(id: string, ...args: any[]): Thenable { + let command = this._tasks.get(id); + if (!command) { + return TPromise.wrapError(new Error(`Contributed task '${id}' does not exist.`)); + } + + let { callback, thisArg, description } = command; + + if (description) { + for (let i = 0; i < description.args.length; i++) { + try { + validateConstraint(args[i], description.args[i].constraint); + } catch (err) { + return TPromise.wrapError(new Error(`Running the contributed task:'${id}' failed. Illegal argument '${description.args[i].name}' - ${description.args[i].description}`)); + } + } + } + + try { + let result = callback.apply(thisArg, args); + return TPromise.as(result); + } catch (err) { + // console.log(err); + // try { + // console.log(toErrorMessage(err)); + // } catch (err) { + // // + // } + return TPromise.wrapError(new Error(`Running the contributed task:'${id}' failed.`)); + } + } + + $getContributedTaskHandlerDescriptions(): TPromise<{ [id: string]: any; }> { + throw new Error('Method not implemented.'); + } +} diff --git a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts index 5b8d0541c7..f0fe0a4e23 100644 --- a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts +++ b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts @@ -26,6 +26,7 @@ import * as sqlExtHostTypes from 'sql/workbench/api/common/sqlExtHostTypes'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; import { ExtHostModalDialogs } from 'sql/workbench/api/node/extHostModalDialog'; +import { ExtHostTasks } from 'sql/workbench/api/node/extHostTasks'; import { ILogService } from 'vs/platform/log/common/log'; import { IExtensionApiFactory } from 'vs/workbench/api/node/extHost.api.impl'; import { ExtHostDashboardWebviews } from 'sql/workbench/api/node/extHostDashboardWebview'; @@ -57,6 +58,7 @@ export function createApiFactory( const extHostSerializationProvider = threadService.set(SqlExtHostContext.ExtHostSerializationProvider, new ExtHostSerializationProvider(threadService)); const extHostResourceProvider = threadService.set(SqlExtHostContext.ExtHostResourceProvider, new ExtHostResourceProvider(threadService)); const extHostModalDialogs = threadService.set(SqlExtHostContext.ExtHostModalDialogs, new ExtHostModalDialogs(threadService)); + const extHostTasks = threadService.set(SqlExtHostContext.ExtHostTasks, new ExtHostTasks(threadService, logService)); const extHostWebviewWidgets = threadService.set(SqlExtHostContext.ExtHostDashboardWebviews, new ExtHostDashboardWebviews(threadService)); return { @@ -255,12 +257,18 @@ export function createApiFactory( } }; - const window = { + const window: typeof sqlops.window = { createDialog(name: string) { return extHostModalDialogs.createDialog(name); } }; + const tasks: typeof sqlops.tasks = { + registerTask(id: string, task: (...args: any[]) => any, thisArgs?: any): vscode.Disposable { + return extHostTasks.registerTask(id, task, thisArgs); + } + }; + const dashboard = { registerWebviewProvider(widgetId: string, handler: (webview: sqlops.DashboardWebview) => void) { extHostWebviewWidgets.$registerProvider(widgetId, handler); @@ -282,6 +290,7 @@ export function createApiFactory( TaskExecutionMode: sqlExtHostTypes.TaskExecutionMode, ScriptOperation: sqlExtHostTypes.ScriptOperation, window, + tasks, dashboard }; } diff --git a/src/sql/workbench/api/node/sqlExtHost.contribution.ts b/src/sql/workbench/api/node/sqlExtHost.contribution.ts index f9cef6acf5..9b7a1552a4 100644 --- a/src/sql/workbench/api/node/sqlExtHost.contribution.ts +++ b/src/sql/workbench/api/node/sqlExtHost.contribution.ts @@ -15,6 +15,7 @@ import 'sql/workbench/api/node/mainThreadCredentialManagement'; import 'sql/workbench/api/node/mainThreadDataProtocol'; import 'sql/workbench/api/node/mainThreadSerializationProvider'; import 'sql/workbench/api/node/mainThreadResourceProvider'; +import 'sql/workbench/api/electron-browser/mainThreadTasks'; import 'sql/workbench/api/node/mainThreadDashboardWebview'; import './mainThreadAccountManagement'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; diff --git a/src/sql/workbench/api/node/sqlExtHost.protocol.ts b/src/sql/workbench/api/node/sqlExtHost.protocol.ts index 2f5ab489c0..8e2bf0fca7 100644 --- a/src/sql/workbench/api/node/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/node/sqlExtHost.protocol.ts @@ -9,11 +9,12 @@ import { createExtHostContextProxyIdentifier as createExtId, ProxyIdentifier } from 'vs/workbench/services/thread/common/threadService'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IDisposable } from 'vs/base/common/lifecycle'; import * as sqlops from 'sqlops'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { ITaskHandlerDescription } from 'sql/platform/tasks/common/tasks'; export abstract class ExtHostAccountManagementShape { $autoOAuthCancelled(handle: number): Thenable { throw ni(); } @@ -421,6 +422,7 @@ export const SqlMainContext = { MainThreadSerializationProvider: createMainId('MainThreadSerializationProvider'), MainThreadResourceProvider: createMainId('MainThreadResourceProvider'), MainThreadModalDialog: createMainId('MainThreadModalDialog'), + MainThreadTasks: createMainId('MainThreadTasks'), MainThreadDashboardWebview: createMainId('MainThreadDashboardWebview') }; @@ -432,6 +434,7 @@ export const SqlExtHostContext = { ExtHostSerializationProvider: createExtId('ExtHostSerializationProvider'), ExtHostResourceProvider: createExtId('ExtHostResourceProvider'), ExtHostModalDialogs: createExtId('ExtHostModalDialogs'), + ExtHostTasks: createExtId('ExtHostTasks'), ExtHostDashboardWebviews: createExtId('ExtHostDashboardWebviews') }; @@ -449,6 +452,16 @@ export interface ExtHostModalDialogsShape { $onClosed(handle: number): void; } +export interface ExtHostTasksShape { + $executeContributedTask(id: string, ...args: any[]): Thenable; + $getContributedTaskHandlerDescriptions(): TPromise<{ [id: string]: string | ITaskHandlerDescription }>; +} + +export interface MainThreadTasksShape extends IDisposable { + $registerTask(id: string): TPromise; + $unregisterTask(id: string): TPromise; +} + export interface ExtHostDashboardWebviewsShape { $registerProvider(widgetId: string, handler: (webview: sqlops.DashboardWebview) => void): void; $onMessage(handle: number, message: any): void; diff --git a/src/sql/workbench/common/actions.contribution.ts b/src/sql/workbench/common/actions.contribution.ts index c229366a79..ce6708b0d2 100644 --- a/src/sql/workbench/common/actions.contribution.ts +++ b/src/sql/workbench/common/actions.contribution.ts @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerTask } from 'sql/platform/tasks/taskRegistry'; +import { TaskRegistry } from 'sql/platform/tasks/common/tasks'; import * as Actions from './actions'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; @@ -15,39 +15,14 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } fr import { ShowCurrentReleaseNotesAction, ProductContribution } from 'sql/workbench/update/releaseNotes'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -const backupSchema: IJSONSchema = { - description: nls.localize('carbon.actions.back', 'Open up backup dialog'), - type: 'null', - default: null -}; - -const restoreSchema: IJSONSchema = { - description: nls.localize('carbon.actions.restore', 'Open up restore dialog'), - type: 'null', - default: null -}; - -const newQuerySchema: IJSONSchema = { - description: nls.localize('carbon.actions.newQuery', 'Open a new query window'), - type: 'null', - default: null -}; - -const configureDashboardSchema: IJSONSchema = { - description: nls.localize('carbon.actions.configureDashboard', 'Configure the Management Dashboard'), - type: 'null', - default: null -}; - -registerTask('backup', '', backupSchema, Actions.BackupAction); -registerTask('restore', '', restoreSchema, Actions.RestoreAction); -registerTask('new-query', '', newQuerySchema, Actions.NewQueryAction); - -registerTask('configure-dashboard', '', configureDashboardSchema, Actions.ConfigureDashboardAction); +new Actions.BackupAction().registerTask(); +new Actions.RestoreAction().registerTask(); +new Actions.NewQueryAction().registerTask(); +new Actions.ConfigureDashboardAction().registerTask(); // add product update and release notes contributions Registry.as(WorkbenchExtensions.Workbench) -.registerWorkbenchContribution(ProductContribution, LifecyclePhase.Running); + .registerWorkbenchContribution(ProductContribution, LifecyclePhase.Running); Registry.as(ActionExtensions.WorkbenchActions) .registerWorkbenchAction(new SyncActionDescriptor(ShowCurrentReleaseNotesAction, ShowCurrentReleaseNotesAction.ID, ShowCurrentReleaseNotesAction.LABEL), 'Show Getting Started'); diff --git a/src/sql/workbench/common/actions.ts b/src/sql/workbench/common/actions.ts index 2ab4dd2c4a..b6e93268bc 100644 --- a/src/sql/workbench/common/actions.ts +++ b/src/sql/workbench/common/actions.ts @@ -15,17 +15,18 @@ import { IAngularEventingService, AngularEventType } from 'sql/services/angularE import { IInsightsDialogService } from 'sql/parts/insights/common/interfaces'; import { IAdminService } from 'sql/parts/admin/common/adminService'; import * as Constants from 'sql/common/constants'; -import { ObjectMetadata } from 'sqlops'; import { ScriptOperation } from 'sql/workbench/common/taskUtilities'; -import { TaskAction } from 'sql/platform/tasks/taskRegistry'; +import { Task } from 'sql/platform/tasks/common/tasks'; +import { IObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService'; + +import { ObjectMetadata } from 'sqlops'; import { TPromise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; import { IWindowsService } from 'vs/platform/windows/common/windows'; - import * as nls from 'vs/nls'; -import { IObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; export interface BaseActionContext { object?: ObjectMetadata; @@ -41,35 +42,29 @@ export interface ManageActionContext extends BaseActionContext { } // --- actions -export class NewQueryAction extends TaskAction { +export class NewQueryAction extends Task { public static ID = 'newQuery'; public static LABEL = nls.localize('newQuery', 'New Query'); public static ICON = 'new-query'; - constructor( - id: string, label: string, icon: string, - @IQueryEditorService protected _queryEditorService: IQueryEditorService, - @IConnectionManagementService protected _connectionManagementService: IConnectionManagementService, - @IObjectExplorerService protected _objectExplorerService: IObjectExplorerService, - @IWorkbenchEditorService protected _workbenchEditorService: IWorkbenchEditorService - ) { - super(id, label, icon); + constructor() { + super({ id: NewQueryAction.ID, title: NewQueryAction.LABEL, iconClass: NewQueryAction.ICON }); } - public run(actionContext: BaseActionContext): TPromise { - return new TPromise((resolve, reject) => { + public runTask(accessor: ServicesAccessor, profile: IConnectionProfile): TPromise { + return new TPromise((resolve, reject) => { TaskUtilities.newQuery( - actionContext.profile, - this._connectionManagementService, - this._queryEditorService, - this._objectExplorerService, - this._workbenchEditorService + profile, + accessor.get(IConnectionManagementService), + accessor.get(IQueryEditorService), + accessor.get(IObjectExplorerService), + accessor.get(IWorkbenchEditorService) ).then( result => { - resolve(true); + resolve(void 0); }, error => { - resolve(false); + resolve(void 0); } ); }); @@ -284,58 +279,52 @@ export class ScriptDeleteAction extends Action { } } -export class BackupAction extends TaskAction { - public static ID = Constants.BackupFeatureName; - public static LABEL = nls.localize('backup', 'Backup'); - public static ICON = Constants.BackupFeatureName; +export class BackupAction extends Task { + public static readonly ID = Constants.BackupFeatureName; + public static readonly LABEL = nls.localize('backup', 'Backup'); + public static readonly ICON = Constants.BackupFeatureName; - constructor( - id: string, label: string, icon: string, - @IBackupUiService protected _backupUiService: IBackupUiService - ) { - super(id, label, icon); + constructor() { + super({ id: BackupAction.ID, title: BackupAction.LABEL, iconClass: BackupAction.ICON }); } - run(actionContext: BaseActionContext): TPromise { - return new TPromise((resolve, reject) => { + runTask(accessor: ServicesAccessor, profile: IConnectionProfile): TPromise { + return new TPromise((resolve, reject) => { TaskUtilities.showBackup( - actionContext.profile, - this._backupUiService, + profile, + accessor.get(IBackupUiService) ).then( result => { - resolve(true); + resolve(void 0); }, error => { - resolve(false); + resolve(void 0); } ); }); } } -export class RestoreAction extends TaskAction { - public static ID = Constants.RestoreFeatureName; - public static LABEL = nls.localize('restore', 'Restore'); - public static ICON = Constants.RestoreFeatureName; +export class RestoreAction extends Task { + public static readonly ID = Constants.RestoreFeatureName; + public static readonly LABEL = nls.localize('restore', 'Restore'); + public static readonly ICON = Constants.RestoreFeatureName; - constructor( - id: string, label: string, icon: string, - @IRestoreDialogController protected _restoreService: IRestoreDialogController - ) { - super(id, label, icon); + constructor() { + super({ id: RestoreAction.ID, title: RestoreAction.LABEL, iconClass: RestoreAction.ICON }); } - run(actionContext: BaseActionContext): TPromise { - return new TPromise((resolve, reject) => { + runTask(accessor: ServicesAccessor, profile: IConnectionProfile): TPromise { + return new TPromise((resolve, reject) => { TaskUtilities.showRestore( - actionContext.profile, - this._restoreService + profile, + accessor.get(IRestoreDialogController) ).then( result => { - resolve(true); + resolve(void 0); }, error => { - resolve(false); + resolve(void 0); } ); }); @@ -390,7 +379,7 @@ export class InsightAction extends Action { } } -export class NewDatabaseAction extends TaskAction { +export class NewDatabaseAction extends Action { public static ID = 'newDatabase'; public static LABEL = nls.localize('newDatabase', 'New Database'); public static ICON = 'new-database'; @@ -410,24 +399,22 @@ export class NewDatabaseAction extends TaskAction { } } -export class ConfigureDashboardAction extends TaskAction { - public static ID = 'configureDashboard'; - public static LABEL = nls.localize('configureDashboard', 'Configure'); - public static ICON = 'configure-dashboard'; +export class ConfigureDashboardAction extends Task { + public static readonly ID = 'configureDashboard'; + public static readonly LABEL = nls.localize('configureDashboard', 'Configure'); + public static readonly ICON = 'configure-dashboard'; private static readonly configHelpUri = 'https://aka.ms/sqldashboardconfig'; - constructor( - id: string, label: string, icon: string, - @IWindowsService private _windowsService - ) { - super(id, label, icon); + + constructor() { + super({ id: ConfigureDashboardAction.ID, title: ConfigureDashboardAction.LABEL, iconClass: ConfigureDashboardAction.ICON }); } - run(actionContext: BaseActionContext): TPromise { - return new TPromise((resolve, reject) => { - this._windowsService.openExternal(ConfigureDashboardAction.configHelpUri).then((result) => { - resolve(result); + runTask(accessor: ServicesAccessor): TPromise { + return new TPromise((resolve, reject) => { + accessor.get(IWindowsService).openExternal(ConfigureDashboardAction.configHelpUri).then((result) => { + resolve(void 0); }, err => { - resolve(err); + resolve(void 0); }); }); } diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index a8ebc9e003..d40e4e1dc9 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -30,12 +30,14 @@ export interface ICommandKeybindingsOptions extends IKeybindings { kbExpr?: ContextKeyExpr; weight?: number; } + export interface ICommandOptions { id: string; precondition: ContextKeyExpr; kbOpts?: ICommandKeybindingsOptions; description?: ICommandHandlerDescription; } + export abstract class Command { public readonly id: string; public readonly precondition: ContextKeyExpr;