mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-19 01:25:36 -05:00
Extensibility: Context menu support in Object Explorer (#1883)
- Fixes #1867 context menu should be extensible - Added context keys to support "when" conditions on the new extensions - Fixes issue where actions like New Query, scripting show up even if these are not valid for the provider type or object type - Fixed node expansion bug where rapid connect / expand / disconnect could break the app (fix in ObjectExplorerService.onNodeExpanded) - Major change to how internal actions work. These cannot assume the context has non-serializable objects. Opened up some APIs to make this easier to handle. - Fixed a number of existing bugs in internal actions. - Notably, DisconnectAction was adding a listener on each right-click on an active connection and never getting it disposed. This wasn't needed at all due to design changes. - Another bug fix is that the Manage action now correctly navigates to the DB dashboard for database-level connections. Before this it went to the server-level dashboard. * Define API for context info
This commit is contained in:
@@ -9,6 +9,8 @@ import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { ContributableActionProvider } from 'vs/workbench/browser/actions';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
|
||||
import {
|
||||
DisconnectConnectionAction, AddServerAction,
|
||||
@@ -16,9 +18,7 @@ import {
|
||||
}
|
||||
from 'sql/parts/objectExplorer/viewlet/connectionTreeAction';
|
||||
import {
|
||||
DisconnectAction, ObjectExplorerActionUtilities,
|
||||
ManageConnectionAction,
|
||||
OEAction
|
||||
ObjectExplorerActionUtilities, ManageConnectionAction, OEAction
|
||||
} from 'sql/parts/objectExplorer/viewlet/objectExplorerActions';
|
||||
import { TreeNode } from 'sql/parts/objectExplorer/common/treeNode';
|
||||
import { NodeType } from 'sql/parts/objectExplorer/common/nodeType';
|
||||
@@ -27,8 +27,14 @@ import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile
|
||||
import { NewProfilerAction } from 'sql/parts/profiler/contrib/profilerActions';
|
||||
import { TreeUpdateUtils } from 'sql/parts/objectExplorer/viewlet/treeUpdateUtils';
|
||||
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { ExecuteCommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { MenuId, IMenuService } from 'vs/platform/actions/common/actions';
|
||||
import { NewQueryAction } from 'sql/workbench/common/actions';
|
||||
import { ConnectionContextKey } from 'sql/parts/connection/common/connectionContextKey';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { TreeNodeContextKey } from './treeNodeContextKey';
|
||||
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
|
||||
import { IScriptingService } from 'sql/services/scripting/scriptingService';
|
||||
import * as constants from 'sql/common/constants';
|
||||
|
||||
/**
|
||||
* Provides actions for the server tree elements
|
||||
@@ -37,7 +43,12 @@ export class ServerTreeActionProvider extends ContributableActionProvider {
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
||||
@IQueryManagementService private _queryManagementService: IQueryManagementService,
|
||||
@IScriptingService private _scriptingService: IScriptingService,
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IMenuService private menuService: IMenuService,
|
||||
@IContextKeyService private _contextKeyService: IContextKeyService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -57,8 +68,11 @@ export class ServerTreeActionProvider extends ContributableActionProvider {
|
||||
return TPromise.as(this.getConnectionProfileGroupActions(tree, element));
|
||||
}
|
||||
if (element instanceof TreeNode) {
|
||||
var treeNode = <TreeNode>element;
|
||||
return TPromise.as(this.getObjectExplorerNodeActions(tree, treeNode));
|
||||
return TPromise.as(this.getObjectExplorerNodeActions({
|
||||
tree: tree,
|
||||
profile: element.getConnectionProfile(),
|
||||
treeNode: element
|
||||
}));
|
||||
}
|
||||
|
||||
return TPromise.as([]);
|
||||
@@ -75,23 +89,58 @@ export class ServerTreeActionProvider extends ContributableActionProvider {
|
||||
/**
|
||||
* Return actions for connection elements
|
||||
*/
|
||||
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(OEAction, NewQueryAction.ID, NewQueryAction.LABEL));
|
||||
if (this._connectionManagementService.isProfileConnected(element)) {
|
||||
actions.push(this._instantiationService.createInstance(DisconnectConnectionAction, DisconnectConnectionAction.ID, DisconnectConnectionAction.LABEL));
|
||||
}
|
||||
actions.push(this._instantiationService.createInstance(DeleteConnectionAction, DeleteConnectionAction.ID, DeleteConnectionAction.DELETE_CONNECTION_LABEL, element));
|
||||
actions.push(this._instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL, tree, element));
|
||||
public getConnectionActions(tree: ITree, profile: ConnectionProfile): IAction[] {
|
||||
return this.getAllActions({
|
||||
tree: tree,
|
||||
profile: profile
|
||||
}, (context) => this.getBuiltinConnectionActions(context));
|
||||
}
|
||||
|
||||
if (process.env['VSCODE_DEV']) {
|
||||
private getAllActions(context: ObjectExplorerContext, getDefaultActions: (ObjectExplorerContext) => IAction[]) {
|
||||
// Create metadata needed to get a useful set of actions
|
||||
let scopedContextService = this.getContextKeyService(context);
|
||||
let menu = this.menuService.createMenu(MenuId.ObjectExplorerItemContext, scopedContextService);
|
||||
|
||||
// Fill in all actions
|
||||
let actions = getDefaultActions(context);
|
||||
fillInActions(menu, { arg: undefined, shouldForwardArgs: true }, actions, this.contextMenuService);
|
||||
|
||||
// Cleanup
|
||||
scopedContextService.dispose();
|
||||
menu.dispose();
|
||||
return actions;
|
||||
|
||||
}
|
||||
|
||||
private getBuiltinConnectionActions(context: ObjectExplorerContext): IAction[] {
|
||||
let actions: IAction[] = [];
|
||||
actions.push(this._instantiationService.createInstance(ManageConnectionAction, ManageConnectionAction.ID, ManageConnectionAction.LABEL, context.tree));
|
||||
this.addNewQueryAction(context, actions);
|
||||
|
||||
if (this._connectionManagementService.isProfileConnected(context.profile)) {
|
||||
actions.push(this._instantiationService.createInstance(DisconnectConnectionAction, DisconnectConnectionAction.ID, DisconnectConnectionAction.LABEL, context.profile));
|
||||
}
|
||||
actions.push(this._instantiationService.createInstance(DeleteConnectionAction, DeleteConnectionAction.ID, DeleteConnectionAction.DELETE_CONNECTION_LABEL, context.profile));
|
||||
actions.push(this._instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL, context.tree, context.profile));
|
||||
|
||||
if (process.env['VSCODE_DEV'] && constants.MssqlProviderId === context.profile.providerName) {
|
||||
actions.push(this._instantiationService.createInstance(OEAction, NewProfilerAction.ID, NewProfilerAction.LABEL));
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private getContextKeyService(context: ObjectExplorerContext): IContextKeyService {
|
||||
let scopedContextService = this._contextKeyService.createScoped();
|
||||
let connectionContextKey = new ConnectionContextKey(scopedContextService);
|
||||
connectionContextKey.set(context.profile);
|
||||
let treeNodeContextKey = new TreeNodeContextKey(scopedContextService);
|
||||
if (context.treeNode) {
|
||||
treeNodeContextKey.set(context.treeNode);
|
||||
}
|
||||
return scopedContextService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return actions for connection group elements
|
||||
*/
|
||||
@@ -106,32 +155,50 @@ export class ServerTreeActionProvider extends ContributableActionProvider {
|
||||
/**
|
||||
* Return actions for OE elements
|
||||
*/
|
||||
public getObjectExplorerNodeActions(tree: ITree, treeNode: TreeNode): IAction[] {
|
||||
let actions = [];
|
||||
private getObjectExplorerNodeActions(context: ObjectExplorerContext): IAction[] {
|
||||
return this.getAllActions(context, (context) => this.getBuiltInNodeActions(context));
|
||||
}
|
||||
|
||||
private getBuiltInNodeActions(context: ObjectExplorerContext): IAction[] {
|
||||
let actions: IAction[] = [];
|
||||
let treeNode = context.treeNode;
|
||||
if (TreeUpdateUtils.isDatabaseNode(treeNode)) {
|
||||
if (TreeUpdateUtils.isAvailableDatabaseNode(treeNode)) {
|
||||
actions.push(this._instantiationService.createInstance(ManageConnectionAction, ManageConnectionAction.ID, ManageConnectionAction.LABEL));
|
||||
actions.push(this._instantiationService.createInstance(ManageConnectionAction, ManageConnectionAction.ID, ManageConnectionAction.LABEL, context.tree));
|
||||
} else {
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
actions.push(this._instantiationService.createInstance(OEAction, NewQueryAction.ID, NewQueryAction.LABEL));
|
||||
let scriptMap: Map<NodeType, any[]> = ObjectExplorerActionUtilities.getScriptMap(treeNode);
|
||||
let supportedActions = scriptMap.get(treeNode.nodeTypeId);
|
||||
let self = this;
|
||||
|
||||
if (supportedActions !== null && supportedActions !== undefined) {
|
||||
supportedActions.forEach(action => {
|
||||
actions.push(self._instantiationService.createInstance(action, action.ID, action.LABEL));
|
||||
});
|
||||
}
|
||||
actions.push(this._instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL, tree, treeNode));
|
||||
|
||||
|
||||
if (treeNode.isTopLevel()) {
|
||||
actions.push(this._instantiationService.createInstance(DisconnectAction, DisconnectAction.ID, DisconnectAction.LABEL));
|
||||
}
|
||||
this.addNewQueryAction(context, actions);
|
||||
this.addScriptingActions(context, actions);
|
||||
actions.push(this._instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL, context.tree, treeNode));
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private addNewQueryAction(context: ObjectExplorerContext, actions: IAction[]): void {
|
||||
if (this._queryManagementService.isProviderRegistered(context.profile.providerName)) {
|
||||
actions.push(this._instantiationService.createInstance(OEAction, NewQueryAction.ID, NewQueryAction.LABEL));
|
||||
}
|
||||
}
|
||||
|
||||
private addScriptingActions(context: ObjectExplorerContext, actions: IAction[]): void {
|
||||
if (this._scriptingService.isProviderRegistered(context.profile.providerName)) {
|
||||
let scriptMap: Map<NodeType, any[]> = ObjectExplorerActionUtilities.getScriptMap(context.treeNode);
|
||||
let supportedActions = scriptMap.get(context.treeNode.nodeTypeId);
|
||||
let self = this;
|
||||
if (supportedActions !== null && supportedActions !== undefined) {
|
||||
supportedActions.forEach(action => {
|
||||
actions.push(self._instantiationService.createInstance(action, action.ID, action.LABEL));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ObjectExplorerContext {
|
||||
tree: ITree;
|
||||
profile: ConnectionProfile;
|
||||
treeNode?: TreeNode;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user