Isolate features (#6792)

* working; new query and scripting

* working; removing manage from menus and combining data explorer contributions

* consolidate dashboard contributions; move manage action to dashboard contributions; make groupings the same

* fix notebook actions

* fix tests
This commit is contained in:
Anthony Dresser
2019-08-21 09:51:02 -07:00
committed by GitHub
parent 3fde278ed2
commit 85bf22322f
30 changed files with 655 additions and 616 deletions

View File

@@ -14,7 +14,6 @@ import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ContextKeyExpr, ContextKeyEqualsExpr } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { isMacintosh } from 'vs/base/common/platform';
import { QueryEditor } from 'sql/workbench/parts/query/browser/queryEditor';
import { QueryResultsEditor } from 'sql/workbench/parts/query/browser/queryResultsEditor';
@@ -34,6 +33,9 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } fr
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { TimeElapsedStatusBarContributions, RowCountStatusBarContributions, QueryStatusStatusBarContributions } from 'sql/workbench/parts/query/browser/statusBarItems';
import { SqlFlavorStatusbarItem } from 'sql/workbench/parts/query/browser/flavorStatus';
import { NewQueryTask, OE_NEW_QUERY_ACTION_ID, DE_NEW_QUERY_COMMAND_ID } from 'sql/workbench/parts/query/browser/queryActions';
import { TreeNodeContextKey } from 'sql/workbench/parts/objectExplorer/common/treeNodeContextKey';
import { MssqlNodeContext } from 'sql/workbench/parts/dataExplorer/common/mssqlNodeContext';
const gridCommandsWeightBonus = 100; // give our commands a little bit more weight over other default list/tree commands
@@ -61,7 +63,30 @@ const queryEditorDescriptor = new EditorDescriptor(
Registry.as<IEditorRegistry>(EditorExtensions.Editors)
.registerEditor(queryEditorDescriptor, [new SyncDescriptor(QueryInput)]);
let actionRegistry = <IWorkbenchActionRegistry>Registry.as(Extensions.WorkbenchActions);
const actionRegistry = <IWorkbenchActionRegistry>Registry.as(Extensions.WorkbenchActions);
new NewQueryTask().registerTask();
MenuRegistry.appendMenuItem(MenuId.ObjectExplorerItemContext, {
group: '0_query',
order: 0,
command: {
id: OE_NEW_QUERY_ACTION_ID,
title: localize('newQuery', "New Query")
},
when: ContextKeyExpr.or(ContextKeyExpr.and(TreeNodeContextKey.Status.notEqualsTo('Unavailable'), TreeNodeContextKey.NodeType.isEqualTo('Server')), ContextKeyExpr.and(TreeNodeContextKey.Status.notEqualsTo('Unavailable'), TreeNodeContextKey.NodeType.isEqualTo('Database')))
});
// New Query
MenuRegistry.appendMenuItem(MenuId.DataExplorerContext, {
group: '0_query',
order: 0,
command: {
id: DE_NEW_QUERY_COMMAND_ID,
title: localize('newQuery', "New Query")
},
when: MssqlNodeContext.IsDatabaseOrServer
});
// Query Actions
actionRegistry.registerWorkbenchAction(
@@ -74,15 +99,12 @@ actionRegistry.registerWorkbenchAction(
RunQueryKeyboardAction.LABEL
);
// Touch Bar
if (isMacintosh) {
// Only show Run Query if the active editor is a query editor.
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
command: { id: RunQueryKeyboardAction.ID, title: RunQueryKeyboardAction.LABEL },
group: 'query',
when: ContextKeyEqualsExpr.create('activeEditor', 'workbench.editor.queryEditor')
});
}
// Only show Run Query if the active editor is a query editor.
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
command: { id: RunQueryKeyboardAction.ID, title: RunQueryKeyboardAction.LABEL },
group: 'query',
when: ContextKeyEqualsExpr.create('activeEditor', 'workbench.editor.queryEditor')
});
actionRegistry.registerWorkbenchAction(
new SyncActionDescriptor(
@@ -260,7 +282,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
});
// Intellisense and other configuration options
let registryProperties = {
const registryProperties = {
'sql.saveAsCsv.includeHeaders': {
'type': 'boolean',
'description': localize('sql.saveAsCsv.includeHeaders', "[Optional] When true, column headers are included when saving results as CSV"),
@@ -480,7 +502,7 @@ let registryProperties = {
};
// Setup keybindings
let initialShortcuts = [
const initialShortcuts = [
{ name: 'sp_help', primary: KeyMod.Alt + KeyCode.F2 },
// Note: using Ctrl+Shift+N since Ctrl+N is used for "open editor at index" by default. This means it's different from SSMS
{ name: 'sp_who', primary: KeyMod.WinCtrl + KeyMod.Shift + KeyCode.KEY_1 },
@@ -512,7 +534,7 @@ for (let i = 0; i < 9; i++) {
}
// Register the query-related configuration options
let configurationRegistry = <IConfigurationRegistry>Registry.as(ConfigExtensions.Configuration);
const configurationRegistry = <IConfigurationRegistry>Registry.as(ConfigExtensions.Configuration);
configurationRegistry.registerConfiguration({
'id': 'sqlEditor',
'title': 'SQL Editor',

View File

@@ -11,7 +11,6 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import Severity from 'vs/base/common/severity';
import { append, $ } from 'vs/base/browser/dom';
@@ -21,13 +20,27 @@ import {
IConnectionParams,
INewConnectionParams,
ConnectionType,
RunQueryOnConnectionMode
RunQueryOnConnectionMode,
IConnectionCompletionOptions,
IConnectableInput
} from 'sql/platform/connection/common/connectionManagement';
import { QueryEditor } from 'sql/workbench/parts/query/browser/queryEditor';
import { IQueryModelService } from 'sql/platform/query/common/queryModel';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { attachEditableDropdownStyler, attachSelectBoxStyler } from 'sql/platform/theme/common/styler';
import { Dropdown } from 'sql/base/parts/editableDropdown/browser/dropdown';
import { Task } from 'sql/platform/tasks/browser/tasksRegistry';
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/queryEditorService';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { getCurrentGlobalConnection } from 'sql/workbench/browser/taskUtilities';
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { OEAction } from 'sql/workbench/parts/objectExplorer/browser/objectExplorerActions';
import { TreeViewItemHandleArg } from 'sql/workbench/common/views';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { IQueryManagementService } from 'sql/platform/query/common/queryManagement';
/**
@@ -98,6 +111,83 @@ export abstract class QueryTaskbarAction extends Action {
}
}
export function openNewQuery(accessor: ServicesAccessor, profile?: IConnectionProfile, initalContent?: string, onConnection?: RunQueryOnConnectionMode): Promise<void> {
const editorService = accessor.get(IEditorService);
const queryEditorService = accessor.get(IQueryEditorService);
const objectExplorerService = accessor.get(IObjectExplorerService);
const connectionManagementService = accessor.get(IConnectionManagementService);
if (!profile) {
profile = getCurrentGlobalConnection(objectExplorerService, connectionManagementService, editorService);
}
return queryEditorService.newSqlEditor(initalContent).then((owner: IConnectableInput) => {
// Connect our editor to the input connection
let options: IConnectionCompletionOptions = {
params: { connectionType: ConnectionType.editor, runQueryOnCompletion: onConnection, input: owner },
saveTheConnection: false,
showDashboard: false,
showConnectionDialogOnError: true,
showFirewallRuleOnError: true
};
if (profile) {
return connectionManagementService.connect(profile, owner.uri, options).then();
}
return undefined;
});
}
// --- actions
export class NewQueryTask extends Task {
public static ID = 'newQuery';
public static LABEL = nls.localize('newQueryTask.newQuery', "New Query");
public static ICON = 'new-query';
constructor() {
super({
id: NewQueryTask.ID,
title: NewQueryTask.LABEL,
iconPath: undefined,
iconClass: NewQueryTask.ICON
});
}
public runTask(accessor: ServicesAccessor, profile: IConnectionProfile): Promise<void> {
return openNewQuery(accessor, profile);
}
}
export const OE_NEW_QUERY_ACTION_ID = 'objectExplorer.newQuery';
CommandsRegistry.registerCommand(OE_NEW_QUERY_ACTION_ID, (accessor: ServicesAccessor, actionContext: any) => {
const instantiationService = accessor.get(IInstantiationService);
return instantiationService.createInstance(OEAction, NewQueryTask.ID, NewQueryTask.LABEL).run(actionContext);
});
export const DE_NEW_QUERY_COMMAND_ID = 'dataExplorer.newQuery';
// New Query
CommandsRegistry.registerCommand({
id: DE_NEW_QUERY_COMMAND_ID,
handler: (accessor, args: TreeViewItemHandleArg) => {
if (args.$treeItem) {
const queryEditorService = accessor.get(IQueryEditorService);
const connectionService = accessor.get(IConnectionManagementService);
const capabilitiesService = accessor.get(ICapabilitiesService);
return queryEditorService.newSqlEditor().then((owner: IConnectableInput) => {
// Connect our editor to the input connection
let options: IConnectionCompletionOptions = {
params: { connectionType: ConnectionType.editor, input: owner },
saveTheConnection: false,
showDashboard: false,
showConnectionDialogOnError: true,
showFirewallRuleOnError: true
};
return connectionService.connect(new ConnectionProfile(capabilitiesService, args.$treeItem.payload), owner.uri, options);
});
}
return Promise.resolve(true);
}
});
/**
* Action class that runs a query in the active SQL text document.
*/
@@ -109,8 +199,7 @@ export class RunQueryAction extends QueryTaskbarAction {
constructor(
editor: QueryEditor,
@IQueryModelService protected readonly queryModelService: IQueryModelService,
@IConnectionManagementService connectionManagementService: IConnectionManagementService,
@IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService
@IConnectionManagementService connectionManagementService: IConnectionManagementService
) {
super(connectionManagementService, editor, RunQueryAction.ID, RunQueryAction.EnabledClass);
this.label = nls.localize('runQueryLabel', "Run");
@@ -480,7 +569,6 @@ export class ListDatabasesActionItem implements IActionViewItem {
public actionRunner: IActionRunner;
private _toDispose: IDisposable[];
private _context: any;
private _currentDatabaseName: string;
private _isConnected: boolean;
private _databaseListDropdown: HTMLElement;
@@ -537,7 +625,6 @@ export class ListDatabasesActionItem implements IActionViewItem {
}
public setActionContext(context: any): void {
this._context = context;
}
public isEnabled(): boolean {

View File

@@ -69,7 +69,7 @@ suite('SQL QueryAction Tests', () => {
test('setClass sets child CSS class correctly', (done) => {
// If I create a RunQueryAction
let queryAction: QueryTaskbarAction = new RunQueryAction(undefined, undefined, undefined, undefined);
let queryAction: QueryTaskbarAction = new RunQueryAction(undefined, undefined, undefined);
// "class should automatically get set to include the base class and the RunQueryAction class
let className = RunQueryAction.EnabledClass;
@@ -93,7 +93,7 @@ suite('SQL QueryAction Tests', () => {
editor.setup(x => x.input).returns(() => testQueryInput.object);
// If I create a QueryTaskbarAction and I pass a non-connected editor to _getConnectedQueryEditorUri
let queryAction: QueryTaskbarAction = new RunQueryAction(undefined, undefined, connectionManagementService.object, undefined);
let queryAction: QueryTaskbarAction = new RunQueryAction(undefined, undefined, connectionManagementService.object);
let connected: boolean = queryAction.isConnected(editor.object);
// I should get an unconnected state
@@ -136,7 +136,7 @@ suite('SQL QueryAction Tests', () => {
});
// If I call run on RunQueryAction when I am not connected
let queryAction: RunQueryAction = new RunQueryAction(editor.object, queryModelService.object, connectionManagementService.object, undefined);
let queryAction: RunQueryAction = new RunQueryAction(editor.object, queryModelService.object, connectionManagementService.object);
isConnected = false;
calledRunQueryOnInput = false;
queryAction.run();
@@ -195,7 +195,7 @@ suite('SQL QueryAction Tests', () => {
let queryModelService = TypeMoq.Mock.ofType(QueryModelService, TypeMoq.MockBehavior.Loose);
// If I call run on RunQueryAction when I have a non empty selection
let queryAction: RunQueryAction = new RunQueryAction(queryEditor.object, queryModelService.object, connectionManagementService.object, undefined);
let queryAction: RunQueryAction = new RunQueryAction(queryEditor.object, queryModelService.object, connectionManagementService.object);
isSelectionEmpty = false;
queryAction.run();
@@ -266,7 +266,7 @@ suite('SQL QueryAction Tests', () => {
/// End Setup Test ///
////// If I call run on RunQueryAction while disconnected and with an undefined selection
let queryAction: RunQueryAction = new RunQueryAction(queryEditor.object, undefined, connectionManagementService.object, undefined);
let queryAction: RunQueryAction = new RunQueryAction(queryEditor.object, undefined, connectionManagementService.object);
isConnected = false;
selectionToReturnInGetSelection = undefined;
queryAction.run();

View File

@@ -54,7 +54,7 @@ suite('SQL QueryEditor Tests', () => {
return new Promise((resolve) => resolve(mockEditor));
});
instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((input) => {
return new Promise((resolve) => resolve(new RunQueryAction(undefined, undefined, undefined, undefined)));
return new Promise((resolve) => resolve(new RunQueryAction(undefined, undefined, undefined)));
});
// Setup hook to capture calls to create the listDatabase action
instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((classDef, editor, action) => {
@@ -64,7 +64,7 @@ suite('SQL QueryEditor Tests', () => {
}
}
// Default
return new RunQueryAction(undefined, undefined, undefined, undefined);
return new RunQueryAction(undefined, undefined, undefined);
});
// Mock EditorDescriptorService to give us a mock editor description
@@ -269,7 +269,7 @@ suite('SQL QueryEditor Tests', () => {
queryActionInstantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((input) => {
// Default
return new RunQueryAction(undefined, undefined, undefined, undefined);
return new RunQueryAction(undefined, undefined, undefined);
});
// Setup hook to capture calls to create the listDatabase action
@@ -280,7 +280,7 @@ suite('SQL QueryEditor Tests', () => {
return item;
}
// Default
return new RunQueryAction(undefined, undefined, undefined, undefined);
return new RunQueryAction(undefined, undefined, undefined);
});
let fileInput = new UntitledEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService.object, undefined, undefined);