Refresh master with initial release/0.24 snapshot (#332)

* Initial port of release/0.24 source code

* Fix additional headers

* Fix a typo in launch.json
This commit is contained in:
Karl Burtram
2017-12-15 15:38:57 -08:00
committed by GitHub
parent 271b3a0b82
commit 6ad0df0e3e
7118 changed files with 107999 additions and 56466 deletions

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IExtensionGalleryService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor';
import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions';
import { IConfigurationRegistry, Extensions as ConfigExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
@@ -12,16 +12,16 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
import { DashboardEditor } from 'sql/parts/dashboard/dashboardEditor';
import { DashboardInput } from 'sql/parts/dashboard/dashboardInput';
import { AddServerGroupAction, AddServerAction } from 'sql/parts/registeredServer/viewlet/connectionTreeAction';
import { ClearRecentConnectionsAction } from 'sql/parts/connection/common/connectionActions';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorDescriptor } from 'vs/workbench/browser/editor';
import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService';
import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { localize } from 'vs/nls';
import { AddServerGroupAction, AddServerAction } from 'sql/parts/registeredServer/viewlet/connectionTreeAction';
// Singletons
registerSingleton(IExtensionGalleryService, ExtensionGalleryService);
@@ -30,10 +30,9 @@ registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService);
// Connection Dashboard registration
const dashboardEditorDescriptor = new EditorDescriptor(
DashboardEditor,
DashboardEditor.ID,
'Dashboard',
'sql/parts/dashboard/dashboardEditor',
'DashboardEditor'
'Dashboard'
);
Registry.as<IEditorRegistry>(EditorExtensions.Editors)
@@ -50,7 +49,6 @@ actionRegistry.registerWorkbenchAction(
),
ClearRecentConnectionsAction.LABEL
);
actionRegistry.registerWorkbenchAction(
new SyncActionDescriptor(
AddServerGroupAction,
@@ -82,8 +80,8 @@ configurationRegistry.registerConfiguration({
},
'sql.defaultEngine': {
'type': 'string',
'description': localize('sql.defaultEngineDescription', 'Default SQL Engine to use. This drives default language provider in .sql files and the default to use when creating a new connection.'),
'description': localize('sql.defaultEngineDescription', 'Default SQL Engine to use. This drives default language provider in .sql files and the default to use when creating a new connection. Valid option is currently MSSQL'),
'default': 'MSSQL'
},
}
});
});

View File

@@ -3,57 +3,84 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import nls = require('vs/nls');
import { Action } from 'vs/base/common/actions';
import { TPromise } from 'vs/base/common/winjs.base';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import Event, { Emitter } from 'vs/base/common/event';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
/**
* Locates the active editor and calls runQuery() on the editor if it is a QueryEditor.
* Workbench action to clear the recent connnections list
*/
export class ClearRecentConnectionsAction extends Action {
public static ID = 'clearRecentConnectionsAction';
public static LABEL = nls.localize('ClearRecentlyUsedLabel', 'Clear Recent Connections List');
public static ID = 'clearRecentConnectionsAction';
public static LABEL = nls.localize('ClearRecentlyUsedLabel', 'Clear Recent Connections List');
constructor(
id: string,
label: string,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IMessageService private _messageService: IMessageService,
@IQuickOpenService private _quickOpenService: IQuickOpenService
) {
super(id, label);
this.enabled = true;
}
public run(): TPromise<void> {
let self = this;
return self.promptToClearRecentConnectionsList().then(result => {
if (result) {
self._connectionManagementService.clearRecentConnectionsList();
self._messageService.show(Severity.Info, nls.localize('ClearedRecentConnections', 'Recent connections list cleared'));
}
});
}
private promptToClearRecentConnectionsList(): TPromise<boolean> {
const self = this;
return new TPromise<boolean>((resolve, reject) => {
let choices: { key, value }[] = [
{ key: nls.localize('yes', 'Yes'), value: true },
{ key: nls.localize('no', 'No'), value: false }
];
self._quickOpenService.pick(choices.map(x => x.key), { placeHolder: nls.localize('ClearRecentlyUsedLabel', 'Clear Recent Connections List'), ignoreFocusLost: true }).then((choice) => {
let confirm = choices.find(x => x.key === choice);
resolve(confirm && confirm.value);
});
});
}
}
/**
* Action to delete one recently used connection from the MRU
*/
export class ClearSingleRecentConnectionAction extends Action {
public static ID = 'clearSingleRecentConnectionAction';
public static LABEL = nls.localize('delete', 'Delete');
private _onRecentConnectionRemoved = new Emitter<void>();
public onRecentConnectionRemoved: Event<void> = this._onRecentConnectionRemoved.event;
constructor(
id: string,
label: string,
@IInstantiationService private _instantiationService: IInstantiationService,
private _connectionProfile: IConnectionProfile,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IMessageService private _messageService: IMessageService,
@IQuickOpenService private _quickOpenService: IQuickOpenService
) {
super(id, label);
this.enabled = true;
}
public run(): TPromise<void> {
let self = this;
return self.promptToClearRecentConnectionsList().then(result => {
if (result) {
self._connectionManagementService.clearRecentConnectionsList();
self._messageService.show(Severity.Info, nls.localize('ClearedRecentConnections', 'Recent connections list cleared'));
}
return new TPromise<void>((resolve, reject) => {
resolve(this._connectionManagementService.clearRecentConnection(this._connectionProfile));
this._onRecentConnectionRemoved.fire();
});
}
private promptToClearRecentConnectionsList(): TPromise<boolean> {
const self = this;
return new TPromise<boolean>((resolve, reject) => {
let choices: { key, value }[] = [
{ key: nls.localize('yes', 'Yes'), value: true },
{ key: nls.localize('no', 'No'), value: false }
];
self._quickOpenService.pick(choices.map(x => x.key), { placeHolder: nls.localize('ClearRecentlyUsedLabel', 'Clear Recent Connections List'), ignoreFocusLost: true }).then((choice) => {
let confirm = choices.find(x => x.key === choice);
resolve(confirm && confirm.value);
});
});
}
}
}

View File

@@ -9,9 +9,9 @@ import * as Utils from './utils';
import { IConnectionProfile, IConnectionProfileStore } from './interfaces';
import { IConnectionConfig } from './iconnectionConfig';
import { ConnectionProfileGroup, IConnectionProfileGroup } from './connectionProfileGroup';
import { IConfigurationEditingService, ConfigurationTarget, IConfigurationValue } from 'vs/workbench/services/configuration/common/configurationEditing';
import { ConfigurationEditingService, IConfigurationValue, IConfigurationValue as TConfigurationValue } from 'vs/workbench/services/configuration/node/configurationEditingService';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { IConfigurationValue as TConfigurationValue } from 'vs/platform/configuration/common/configuration';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { ConnectionProfile } from './connectionProfile';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import * as data from 'data';
@@ -30,22 +30,25 @@ export interface ISaveGroupResult {
export class ConnectionConfig implements IConnectionConfig {
private _providerCapabilitiesMap: { [providerName: string]: data.DataProtocolServerCapabilities };
private _providerCachedCapabilitiesMap: { [providerName: string]: data.DataProtocolServerCapabilities };
/**
* Constructor.
*/
public constructor(
private _configurationEditService: IConfigurationEditingService,
private _configurationEditService: ConfigurationEditingService,
private _workspaceConfigurationService: IWorkspaceConfigurationService,
private _capabilitiesService: ICapabilitiesService,
private _cachedMetadata?: data.DataProtocolServerCapabilities[]
cachedMetadata?: data.DataProtocolServerCapabilities[]
) {
this._providerCapabilitiesMap = {};
this._providerCachedCapabilitiesMap = {};
this.setCachedMetadata(cachedMetadata);
}
public setCachedMetadata(cachedMetaData: data.DataProtocolServerCapabilities[]): void {
this._cachedMetadata = cachedMetaData;
public setCachedMetadata(cachedMetadata: data.DataProtocolServerCapabilities[]): void {
if (cachedMetadata) {
cachedMetadata.forEach(item => {
this.updateCapabilitiesCache(item.providerName, item);
});
}
}
/**
@@ -58,7 +61,6 @@ export class ConnectionConfig implements IConnectionConfig {
let workspaceGroups = this.getConfiguration(Constants.connectionGroupsArrayName).workspace as IConnectionProfileGroup[];
if (userGroups) {
if (workspaceGroups) {
userGroups = userGroups.filter(x => workspaceGroups.find(f => this.isSameGroupName(f, x)) === undefined);
allGroups = allGroups.concat(workspaceGroups);
@@ -74,43 +76,44 @@ export class ConnectionConfig implements IConnectionConfig {
return allGroups;
}
private updateCapabilitiesCache(providerName: string, providerCapabilities: data.DataProtocolServerCapabilities): void {
if (providerName && providerCapabilities) {
this._providerCapabilitiesMap[providerName] = providerCapabilities;
}
}
private getCapabilitiesFromCache(providerName: string): data.DataProtocolServerCapabilities {
if (providerName in this._providerCapabilitiesMap) {
return this._providerCapabilitiesMap[providerName];
}
return undefined;
}
/**
* Returns the capabilities for given provider name. First tries to get it from capabilitiesService and if it's not registered yet,
* Gets the data from the metadata stored in the config
* @param providerName Provider Name
*/
public getCapabilities(providerName: string): data.DataProtocolServerCapabilities {
let result: data.DataProtocolServerCapabilities;
if (providerName in this._providerCapabilitiesMap) {
result = this._providerCapabilitiesMap[providerName];
let result: data.DataProtocolServerCapabilities = this.getCapabilitiesFromCache(providerName);
if (result) {
return result;
} else {
let capabilities = this._capabilitiesService.getCapabilities();
if (capabilities) {
let providerCapabilities = capabilities.find(c => c.providerName === providerName);
if (providerCapabilities) {
this._providerCapabilitiesMap[providerName] = providerCapabilities;
result = providerCapabilities;
this.updateCapabilitiesCache(providerName, providerCapabilities);
return providerCapabilities;
} else {
return undefined;
}
}
}
if (!result && this._cachedMetadata) {
if (providerName in this._providerCachedCapabilitiesMap) {
result = this._providerCachedCapabilitiesMap[providerName];
} else {
let metaDataFromConfig = this._cachedMetadata;
if (metaDataFromConfig) {
let providerCapabilities = metaDataFromConfig.find(m => m.providerName === providerName);
this._providerCachedCapabilitiesMap[providerName] = providerCapabilities;
result = providerCapabilities;
}
return undefined;
}
}
return result;
}
/**
* Add a new connection to the connection config.
*/
@@ -118,13 +121,13 @@ export class ConnectionConfig implements IConnectionConfig {
return new Promise<IConnectionProfile>((resolve, reject) => {
if (profile.saveProfile) {
this.addGroupFromProfile(profile).then(groupId => {
let profiles = this._workspaceConfigurationService.lookup<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
let profiles = this._workspaceConfigurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
if (!profiles) {
profiles = [];
}
let providerCapabilities = this.getCapabilities(profile.providerName);
let connectionProfile = this.getConnectionProfileInstance(profile, groupId);
let connectionProfile = this.getConnectionProfileInstance(profile, groupId, providerCapabilities);
let newProfile = ConnectionProfile.convertToProfileStore(providerCapabilities, connectionProfile);
// Remove the profile if already set
@@ -151,9 +154,8 @@ export class ConnectionConfig implements IConnectionConfig {
});
}
private getConnectionProfileInstance(profile: IConnectionProfile, groupId: string): ConnectionProfile {
private getConnectionProfileInstance(profile: IConnectionProfile, groupId: string, providerCapabilities: data.DataProtocolServerCapabilities): ConnectionProfile {
let connectionProfile = profile as ConnectionProfile;
let providerCapabilities = this.getCapabilities(profile.providerName);
if (connectionProfile === undefined) {
connectionProfile = new ConnectionProfile(providerCapabilities, profile);
}
@@ -170,7 +172,7 @@ export class ConnectionConfig implements IConnectionConfig {
if (profile.groupId && profile.groupId !== Utils.defaultGroupId) {
resolve(profile.groupId);
} else {
let groups = this._workspaceConfigurationService.lookup<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
let groups = this._workspaceConfigurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
let result = this.saveGroup(groups, profile.groupFullName, undefined, undefined);
groups = result.groups;
@@ -192,7 +194,7 @@ export class ConnectionConfig implements IConnectionConfig {
if (profileGroup.id) {
resolve(profileGroup.id);
} else {
let groups = this._workspaceConfigurationService.lookup<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
let groups = this._workspaceConfigurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
let sameNameGroup = groups ? groups.find(group => group.name === profileGroup.name) : undefined;
if (sameNameGroup) {
let errMessage: string = nls.localize('invalidServerName', "A server group with the same name already exists.");
@@ -221,7 +223,7 @@ export class ConnectionConfig implements IConnectionConfig {
profiles = <IConnectionProfileStore[]>configs.workspace;
}
if (profiles) {
if(this.fixConnectionIds(profiles)) {
if (this.fixConnectionIds(profiles)) {
this.writeConfiguration(Constants.connectionsArrayName, profiles, configTarget);
}
} else {
@@ -245,9 +247,9 @@ export class ConnectionConfig implements IConnectionConfig {
profile.id = generateUuid();
changed = true;
}
if (profile.id in idsCache) {
profile.id = generateUuid();
changed = true;
if (profile.id in idsCache) {
profile.id = generateUuid();
changed = true;
}
idsCache[profile.id] = true;
}
@@ -298,7 +300,7 @@ export class ConnectionConfig implements IConnectionConfig {
*/
public deleteConnection(profile: ConnectionProfile): Promise<void> {
// Get all connections in the settings
let profiles = this._workspaceConfigurationService.lookup<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
let profiles = this._workspaceConfigurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
// Remove the profile from the connections
profiles = profiles.filter(value => {
let providerCapabilities = this.getCapabilities(value.providerName);
@@ -320,7 +322,7 @@ export class ConnectionConfig implements IConnectionConfig {
// Add selected group to subgroups list
subgroups.push(group);
// Get all connections in the settings
let profiles = this._workspaceConfigurationService.lookup<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
let profiles = this._workspaceConfigurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
// Remove the profiles from the connections
profiles = profiles.filter(value => {
let providerCapabilities = this.getCapabilities(value.providerName);
@@ -329,7 +331,7 @@ export class ConnectionConfig implements IConnectionConfig {
});
// Get all groups in the settings
let groups = this._workspaceConfigurationService.lookup<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
let groups = this._workspaceConfigurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
// Remove subgroups in the settings
groups = groups.filter((grp) => {
return !subgroups.some((item) => item.id === grp.id);
@@ -347,7 +349,7 @@ export class ConnectionConfig implements IConnectionConfig {
* Moves the source group under the target group.
*/
public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise<void> {
let groups = this._workspaceConfigurationService.lookup<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
let groups = this._workspaceConfigurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
groups = groups.map(g => {
if (g.id === source.id) {
g.parentId = target.id;
@@ -372,8 +374,8 @@ export class ConnectionConfig implements IConnectionConfig {
*/
private changeGroupIdForConnectionInSettings(profile: ConnectionProfile, newGroupID: string, target: ConfigurationTarget = ConfigurationTarget.USER): Promise<void> {
return new Promise<void>((resolve, reject) => {
let profiles = target === ConfigurationTarget.USER ? this._workspaceConfigurationService.lookup<IConnectionProfileStore[]>(Constants.connectionsArrayName).user :
this._workspaceConfigurationService.lookup<IConnectionProfileStore[]>(Constants.connectionsArrayName).workspace;
let profiles = target === ConfigurationTarget.USER ? this._workspaceConfigurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user :
this._workspaceConfigurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).workspace;
if (profiles) {
let providerCapabilities = this.getCapabilities(profile.providerName);
if (profile.parent && profile.parent.id === Constants.unsavedGroupId) {
@@ -429,7 +431,7 @@ export class ConnectionConfig implements IConnectionConfig {
}
public editGroup(source: ConnectionProfileGroup): Promise<void> {
let groups = this._workspaceConfigurationService.lookup<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
let groups = this._workspaceConfigurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
let sameNameGroup = groups ? groups.find(group => group.name === source.name && group.id !== source.id) : undefined;
if (sameNameGroup) {
let errMessage: string = nls.localize('invalidServerName', "A server group with the same name already exists.");
@@ -510,10 +512,9 @@ export class ConnectionConfig implements IConnectionConfig {
* @param parsedSettingsFile an object representing the parsed contents of the settings file.
* @returns the set of connection profiles found in the parsed settings file.
*/
private getConfiguration(key: string): TConfigurationValue<IConnectionProfileStore[] | IConnectionProfileGroup[] | data.DataProtocolServerCapabilities[]> {
let configs: TConfigurationValue<IConnectionProfileStore[] | IConnectionProfileGroup[] | data.DataProtocolServerCapabilities[]>;
configs = this._workspaceConfigurationService.lookup<IConnectionProfileStore[] | IConnectionProfileGroup[] | data.DataProtocolServerCapabilities[]>(key);
private getConfiguration(key: string): any {
let configs: any;
configs = this._workspaceConfigurationService.inspect<IConnectionProfileStore[] | IConnectionProfileGroup[] | data.DataProtocolServerCapabilities[]>(key);
return configs;
}
@@ -532,7 +533,7 @@ export class ConnectionConfig implements IConnectionConfig {
value: profiles
};
this._configurationEditService.writeConfiguration(target, configValue).then(result => {
this._workspaceConfigurationService.reloadConfiguration().then(() => {
this._workspaceConfigurationService.reloadConfiguration().then(() => {
resolve();
});
}, (error => {

View File

@@ -9,12 +9,12 @@ import { IViewlet } from 'vs/workbench/common/viewlet';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { TPromise } from 'vs/base/common/winjs.base';
import Event from 'vs/base/common/event';
import { IAction } from 'vs/base/common/actions';
import Severity from 'vs/base/common/severity';
import data = require('data');
import { IConnectionProfileGroup, ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import Severity from 'vs/base/common/severity';
import { ISelectionData } from 'data';
import { ConnectionManagementInfo } from './connectionManagementInfo';
export const VIEWLET_ID = 'workbench.view.connections';
@@ -57,6 +57,7 @@ export interface IConnectionResult {
connected: boolean;
errorMessage: string;
errorCode: number;
callStack: string;
errorHandled?: boolean;
}
@@ -85,7 +86,7 @@ export interface IConnectionManagementService {
/**
* Opens the connection dialog to create new connection
*/
showConnectionDialog(params?: INewConnectionParams, model?: IConnectionProfile, error?: string): Promise<void>;
showConnectionDialog(params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): Promise<void>;
/**
* Opens the add server group dialog
@@ -135,6 +136,8 @@ export interface IConnectionManagementService {
clearRecentConnectionsList(): void;
clearRecentConnection(connectionProfile: IConnectionProfile) : void;
getActiveConnections(): ConnectionProfile[];
saveProfileGroup(profile: IConnectionProfileGroup): Promise<string>;
@@ -253,7 +256,7 @@ export interface IConnectionManagementService {
export const IConnectionDialogService = createDecorator<IConnectionDialogService>('connectionDialogService');
export interface IConnectionDialogService {
_serviceBrand: any;
showDialog(connectionManagementService: IConnectionManagementService, params: INewConnectionParams, model: IConnectionProfile, error?: string): Thenable<void>;
showDialog(connectionManagementService: IConnectionManagementService, params: INewConnectionParams, model: IConnectionProfile, connectionResult?: IConnectionResult): Thenable<void>;
}
export interface IServerGroupDialogCallbacks {
@@ -270,7 +273,7 @@ export interface IServerGroupController {
export const IErrorMessageService = createDecorator<IErrorMessageService>('errorMessageService');
export interface IErrorMessageService {
_serviceBrand: any;
showDialog(severity: Severity, headerTitle: string, message: string): void;
showDialog(severity: Severity, headerTitle: string, message: string, messageDetails?: string, actions?: IAction[]): void;
}
export enum ServiceOptionType {
@@ -295,14 +298,15 @@ export enum RunQueryOnConnectionMode {
none = 0,
executeQuery = 1,
executeCurrentQuery = 2,
estimatedQueryPlan = 3
estimatedQueryPlan = 3,
actualQueryPlan = 4
}
export interface INewConnectionParams {
connectionType: ConnectionType;
input?: IConnectableInput;
runQueryOnCompletion?: RunQueryOnConnectionMode;
querySelection?: ISelectionData;
querySelection?: data.ISelectionData;
showDashboard?: boolean;
}

View File

@@ -29,7 +29,7 @@ export class ConnectionManagementInfo {
/**
* Callback for when a connection notification is received.
*/
public connectHandler: (result: boolean, errorMessage?: string, errorCode?: number) => void;
public connectHandler: (result: boolean, errorMessage?: string, errorCode?: number, callStack?: string) => void;
/**
* Information about the SQL Server instance.

View File

@@ -43,7 +43,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ConnectionProfileGroup, IConnectionProfileGroup } from './connectionProfileGroup';
import { IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing';
import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import Event, { Emitter } from 'vs/base/common/event';
@@ -78,6 +78,8 @@ export class ConnectionManagementService implements IConnectionManagementService
private _connectionGlobalStatus: ConnectionGlobalStatus;
private _configurationEditService: ConfigurationEditingService;
constructor(
private _connectionMemento: Memento,
private _connectionStore: ConnectionStore,
@@ -89,7 +91,6 @@ export class ConnectionManagementService implements IConnectionManagementService
@IWorkspaceContextService private _contextService: IWorkspaceContextService,
@IStorageService private _storageService: IStorageService,
@ITelemetryService private _telemetryService: ITelemetryService,
@IConfigurationEditingService private _configurationEditService: IConfigurationEditingService,
@IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService,
@ICredentialsService private _credentialsService: ICredentialsService,
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
@@ -100,13 +101,17 @@ export class ConnectionManagementService implements IConnectionManagementService
@IViewletService private _viewletService: IViewletService,
@IAngularEventingService private _angularEventing: IAngularEventingService
) {
if (this._instantiationService) {
this._configurationEditService = this._instantiationService.createInstance(ConfigurationEditingService);
}
// _connectionMemento and _connectionStore are in constructor to enable this class to be more testable
if (!this._connectionMemento) {
this._connectionMemento = new Memento('ConnectionManagement');
}
if (!this._connectionStore) {
this._connectionStore = new ConnectionStore(_storageService, this._connectionMemento,
_configurationEditService, this._workspaceConfigurationService, this._credentialsService, this._capabilitiesService);
this._configurationEditService, this._workspaceConfigurationService, this._credentialsService, this._capabilitiesService);
}
this._connectionStatusManager = new ConnectionStatusManager(this._capabilitiesService);
@@ -196,7 +201,7 @@ export class ConnectionManagementService implements IConnectionManagementService
// show the Registered Server viewlet
let startupConfig = this._workspaceConfigurationService.getConfiguration('startup');
if (startupConfig) {
let showServerViewlet = <boolean>startupConfig['alwaysShowServersView'];
let showServerViewlet = <boolean>startupConfig['alwaysShowServersView'];
if (showServerViewlet) {
// only show the Servers viewlet if there isn't another active viewlet
if (!this._viewletService.getActiveViewlet()) {
@@ -212,7 +217,7 @@ export class ConnectionManagementService implements IConnectionManagementService
* @param params Include the uri, type of connection
* @param model the existing connection profile to create a new one from
*/
public showConnectionDialog(params?: INewConnectionParams, model?: IConnectionProfile, error?: string): Promise<void> {
public showConnectionDialog(params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): Promise<void> {
let self = this;
return new Promise<void>((resolve, reject) => {
if (!params) {
@@ -221,7 +226,7 @@ export class ConnectionManagementService implements IConnectionManagementService
if (!model && params.input && params.input.uri) {
model = this._connectionStatusManager.getConnectionProfile(params.input.uri);
}
self._connectionDialogService.showDialog(self, params, model, error).then(() => {
self._connectionDialogService.showDialog(self, params, model, connectionResult).then(() => {
resolve();
}, dialogError => {
warn('failed to open the connection dialog. error: ' + dialogError);
@@ -301,7 +306,7 @@ export class ConnectionManagementService implements IConnectionManagementService
}
// If the password is required and still not loaded show the dialog
if (!foundPassword && this._connectionStore.isPasswordRequired(newConnection) && !newConnection.password) {
resolve(this.showConnectionDialogOnError(connection, owner, { connected: false, errorMessage: undefined, errorCode: undefined }, options));
resolve(this.showConnectionDialogOnError(connection, owner, { connected: false, errorMessage: undefined, callStack: undefined, errorCode: undefined }, options));
} else {
// Try to connect
this.connectWithOptions(newConnection, owner.uri, options, owner).then(connectionResult => {
@@ -340,7 +345,7 @@ export class ConnectionManagementService implements IConnectionManagementService
runQueryOnCompletion: RunQueryOnConnectionMode.none,
showDashboard: options.showDashboard
};
this.showConnectionDialog(params, connection, connectionResult.errorMessage).then(() => {
this.showConnectionDialog(params, connection, connectionResult).then(() => {
resolve(connectionResult);
}).catch(err => {
reject(err);
@@ -643,6 +648,10 @@ export class ConnectionManagementService implements IConnectionManagementService
return this._connectionStore.clearRecentlyUsed();
}
public clearRecentConnection(connectionProfile: IConnectionProfile) : void {
this._connectionStore.removeConnectionToMemento(connectionProfile, Constants.recentConnections);
}
public getActiveConnections(): ConnectionProfile[] {
return this._connectionStore.getActiveConnections();
}
@@ -825,6 +834,9 @@ export class ConnectionManagementService implements IConnectionManagementService
return new Promise<data.ListDatabasesResult>((resolve, reject) => {
let provider = this._providers[providerId];
provider.listDatabases(uri).then(result => {
if (result && result.databaseNames) {
result.databaseNames.sort();
}
resolve(result);
}, error => {
reject(error);
@@ -845,9 +857,9 @@ export class ConnectionManagementService implements IConnectionManagementService
/**
* Add a connection to the active connections list.
*/
private tryAddActiveConnection(connectionManagementInfo: ConnectionManagementInfo, newConnection: IConnectionProfile): void {
private tryAddActiveConnection(connectionManagementInfo: ConnectionManagementInfo, newConnection: IConnectionProfile, isConnectionToDefaultDb: boolean): void {
if (newConnection) {
this._connectionStore.addActiveConnection(newConnection)
this._connectionStore.addActiveConnection(newConnection, isConnectionToDefaultDb)
.then(() => {
connectionManagementInfo.connectHandler(true);
}, err => {
@@ -881,6 +893,10 @@ export class ConnectionManagementService implements IConnectionManagementService
let connection = this._connectionStatusManager.onConnectionComplete(info);
if (info.connectionId) {
let isConnectionToDefaultDb = false;
if (connection.connectionProfile && (!connection.connectionProfile.databaseName || connection.connectionProfile.databaseName.trim() === '')) {
isConnectionToDefaultDb = true;
}
if (info.connectionSummary && info.connectionSummary.databaseName) {
this._connectionStatusManager.updateDatabaseName(info);
}
@@ -889,14 +905,14 @@ export class ConnectionManagementService implements IConnectionManagementService
connection.connectHandler(true);
let activeConnection = connection.connectionProfile;
self.tryAddActiveConnection(connection, activeConnection);
self.tryAddActiveConnection(connection, activeConnection, isConnectionToDefaultDb);
self.addTelemetryForConnection(connection);
} else {
connection.connectHandler(false, info.messages, info.errorNumber);
}
if (this._connectionStatusManager.isDefaultTypeUri(info.ownerUri)) {
this._connectionGlobalStatus.setStatusToConnected(info.connectionSummary);
if (self._connectionStatusManager.isDefaultTypeUri(info.ownerUri)) {
self._connectionGlobalStatus.setStatusToConnected(info.connectionSummary);
}
} else {
connection.connectHandler(false, info.errorMessage, info.errorNumber, info.messages);
}
}
@@ -1004,18 +1020,18 @@ export class ConnectionManagementService implements IConnectionManagementService
this._capabilitiesService.onCapabilitiesReady().then(() => {
let connectionInfo = this._connectionStatusManager.addConnection(connection, uri);
// Setup the handler for the connection complete notification to call
connectionInfo.connectHandler = ((connectResult, errorMessage, errorCode) => {
connectionInfo.connectHandler = ((connectResult, errorMessage, errorCode, callStack) => {
let connectionMngInfo = this._connectionStatusManager.findConnection(uri);
if (connectionMngInfo && connectionMngInfo.deleted) {
this._connectionStatusManager.deleteConnection(uri);
resolve({ connected: connectResult, errorMessage: undefined, errorCode: undefined, errorHandled: true });
resolve({ connected: connectResult, errorMessage: undefined, errorCode: undefined, callStack: undefined, errorHandled: true });
} else {
if (errorMessage) {
// Connection to the server failed
this._connectionStatusManager.deleteConnection(uri);
resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode });
resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode, callStack: callStack });
} else {
resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode });
resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode, callStack: callStack });
}
}
});

View File

@@ -79,6 +79,26 @@ export class ConnectionProfileGroup implements IConnectionProfileGroup {
return false;
}
public get hasValidConnections(): boolean {
if (this.connections) {
let invalidConnections = this.connections.find(c => c.serverCapabilities === undefined);
if (invalidConnections !== undefined) {
return false;
} else {
let childrenAreValid: boolean = true;
this.children.forEach(element => {
let isChildValid = element.hasValidConnections;
if (!isChildValid) {
childrenAreValid = false;
}
});
return childrenAreValid;
}
} else {
return true;
}
}
public getChildren(): any {
let allChildren = [];

View File

@@ -155,9 +155,9 @@ export class ConnectionStatusManager {
}
/**
* Tries to find an existing connection that's mapped with given the ownerUri
* Tries to find an existing connection that's mapped with the given ownerUri
* The purpose for this method is to find the connection given the ownerUri and find the original uri assigned to it. most of the times should be the same.
* Only if the db name in the original uri is different than when connection is complete, we need to use the original uri
* Only if the db name in the original uri is different when connection is complete, we need to use the original uri
* Returns the generated ownerUri for the connection profile if not existing connection found
* @param ownerUri connection owner uri to find an existing connection
* @param purpose purpose for the connection

View File

@@ -15,11 +15,11 @@ import { ConnectionConfig } from './connectionConfig';
import { Memento, Scope as MementoScope } from 'vs/workbench/common/memento';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ConnectionProfileGroup, IConnectionProfileGroup } from './connectionProfileGroup';
import { IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing';
import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { equalsIgnoreCase } from 'vs/base/common/strings';
import * as data from 'data';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
const MAX_CONNECTIONS_DEFAULT = 25;
@@ -37,24 +37,21 @@ export class ConnectionStore {
constructor(
private _storageService: IStorageService,
private _context: Memento,
private _configurationEditService: IConfigurationEditingService,
private _configurationEditService: ConfigurationEditingService,
private _workspaceConfigurationService: IWorkspaceConfigurationService,
private _credentialService: ICredentialsService,
private _capabilitiesService: ICapabilitiesService,
private _connectionConfig?: IConnectionConfig
) {
if (_context) {
this._memento = this._context.getMemento(this._storageService, MementoScope.GLOBAL);
}
this._groupIdToFullNameMap = {};
this._groupFullNameToIdMap = {};
if (!this._connectionConfig) {
let cachedServerCapabilities = this.getCachedServerCapabilities();
this._connectionConfig = new ConnectionConfig(this._configurationEditService,
this._workspaceConfigurationService, this._capabilitiesService, cachedServerCapabilities);
this._connectionConfig.setCachedMetadata(cachedServerCapabilities);
}
}
@@ -115,17 +112,6 @@ export class ConnectionStore {
}
}
/**
* Gets all connection profiles stored in the user settings
* Profiles from workspace will be included if getWorkspaceProfiles is passed as true
* Note: connections will not include password value
*
* @returns {IConnectionProfile[]}
*/
public getProfiles(getWorkspaceProfiles: boolean): IConnectionProfile[] {
return this.loadProfiles(getWorkspaceProfiles);
}
public addSavedPassword(credentialsItem: IConnectionProfile): Promise<{ profile: IConnectionProfile, savedCred: boolean }> {
let self = this;
return new Promise<{ profile: IConnectionProfile, savedCred: boolean }>((resolve, reject) => {
@@ -314,12 +300,15 @@ export class ConnectionStore {
* @param {IConnectionCredentials} conn the connection to add
* @returns {Promise<void>} a Promise that returns when the connection was saved
*/
public addActiveConnection(conn: IConnectionProfile): Promise<void> {
if(this.getActiveConnections().some(existingConn => existingConn.id === conn.id)) {
public addActiveConnection(conn: IConnectionProfile, isConnectionToDefaultDb: boolean = false): Promise<void> {
if (this.getActiveConnections().some(existingConn => existingConn.id === conn.id)) {
return Promise.resolve(undefined);
} else {
return this.addConnectionToMemento(conn, Constants.activeConnections, undefined, conn.savePassword).then(() => {
let maxConnections = this.getMaxRecentConnectionsCount();
if (isConnectionToDefaultDb) {
conn.databaseName = '';
}
return this.addConnectionToMemento(conn, Constants.recentConnections, maxConnections);
});
}
@@ -330,8 +319,7 @@ export class ConnectionStore {
return new Promise<void>((resolve, reject) => {
// Get all profiles
let configValues = self.getConnectionsFromMemento(mementoKey);
let configToSave: IConnectionProfile[] = this.addToConnectionList(conn, configValues, mementoKey === Constants.recentConnections);
let configToSave = this.addToConnectionList(conn, configValues);
if (maxConnections) {
// Remove last element if needed
if (configToSave.length > maxConnections) {
@@ -349,24 +337,6 @@ export class ConnectionStore {
});
}
private isSameConnectionProfileNoGroup(profile1: IConnectionProfile, profile2: IConnectionProfile): boolean {
// both are undefined
if (!profile1 && !profile2) {
return true;
}
// only one is undefined
if (!profile1 || !profile2) {
return false;
}
// compare all the connection's "identity" properties
return equalsIgnoreCase(profile1.serverName, profile2.serverName) &&
equalsIgnoreCase(profile1.databaseName, profile2.databaseName) &&
equalsIgnoreCase(profile1.userName, profile2.userName) &&
profile1.authenticationType === profile2.authenticationType &&
profile1.providerName === profile2.providerName;
}
public removeConnectionToMemento(conn: IConnectionProfile, mementoKey: string): Promise<void> {
const self = this;
return new Promise<void>((resolve, reject) => {
@@ -388,25 +358,18 @@ export class ConnectionStore {
return this.convertConfigValuesToConnectionProfiles(configValues);
}
private addToConnectionList(conn: IConnectionProfile, list: ConnectionProfile[], isRecentConnections: boolean): IConnectionProfile[] {
private addToConnectionList(conn: IConnectionProfile, list: ConnectionProfile[]): IConnectionProfile[] {
let savedProfile: ConnectionProfile = this.getProfileWithoutPassword(conn);
// Remove the connection from the list if it already exists
if (isRecentConnections) {
// recent connections should use a different comparison the server viewlet for managing connection list
list = list.filter(value => {
return !(this.isSameConnectionProfileNoGroup(value, savedProfile));
});
} else {
list = list.filter(value => {
let equal = value && value.getConnectionInfoId() === savedProfile.getConnectionInfoId();
if (equal && savedProfile.saveProfile) {
equal = value.groupId === savedProfile.groupId ||
ConnectionProfileGroup.sameGroupName(value.groupFullName, savedProfile.groupFullName);
}
return !equal;
});
}
list = list.filter(value => {
let equal = value && value.getConnectionInfoId() === savedProfile.getConnectionInfoId();
if (equal && savedProfile.saveProfile) {
equal = value.groupId === savedProfile.groupId ||
ConnectionProfileGroup.sameGroupName(value.groupFullName, savedProfile.groupFullName);
}
return !equal;
});
list.unshift(savedProfile);
@@ -527,11 +490,6 @@ export class ConnectionStore {
return result;
}
private loadProfiles(loadWorkspaceProfiles: boolean): IConnectionProfile[] {
let connections: IConnectionProfile[] = this._connectionConfig.getConnections(loadWorkspaceProfiles);
return connections;
}
private getMaxRecentConnectionsCount(): number {
let config = this._workspaceConfigurationService.getConfiguration(Constants.sqlConfigSectionName);

View File

@@ -25,3 +25,10 @@ export const mssqlProviderName = 'MSSQL';
export const applicationName = 'sqlops';
export const defaultEngine = 'defaultEngine';
export const passwordChars = '***************';
/* authentication types */
export const sqlLogin = 'SqlLogin';
export const integrated = 'Integrated';

View File

@@ -6,7 +6,7 @@
'use strict';
import { IConnectionManagementService, ConnectionOptionSpecialType } from 'sql/parts/connection/common/connectionManagement';
import { IConnectionComponentCallbacks, IConnectionComponentController, IConnectionResult } from 'sql/parts/connection/connectionDialog/connectionDialogService';
import { IConnectionComponentCallbacks, IConnectionComponentController, IConnectionValidateResult } from 'sql/parts/connection/connectionDialog/connectionDialogService';
import { ConnectionWidget } from 'sql/parts/connection/connectionDialog/connectionWidget';
import { AdvancedPropertiesController } from 'sql/parts/connection/connectionDialog/advancedPropertiesController';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
@@ -119,7 +119,7 @@ export class ConnectionController implements IConnectionComponentController {
this._connectionWidget.focusOnOpen();
}
public validateConnection(): IConnectionResult {
public validateConnection(): IConnectionValidateResult {
return { isValid: this._connectionWidget.connect(this._model), connection: this._model };
}

View File

@@ -7,7 +7,7 @@
import {
IConnectionDialogService, IConnectionManagementService, IErrorMessageService,
ConnectionType, INewConnectionParams, IConnectionCompletionOptions
ConnectionType, INewConnectionParams, IConnectionCompletionOptions, IConnectionResult
} from 'sql/parts/connection/common/connectionManagement';
import { ConnectionDialogWidget, OnShowUIResponse } from 'sql/parts/connection/connectionDialog/connectionDialogWidget';
import { ConnectionController } from 'sql/parts/connection/connectionDialog/connectionController';
@@ -24,10 +24,16 @@ import { IPartService } from 'vs/workbench/services/part/common/partService';
import { withElementById } from 'vs/base/browser/builder';
import { TPromise } from 'vs/base/common/winjs.base';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import * as platform from 'vs/base/common/platform';
import Severity from 'vs/base/common/severity';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { Action, IAction } from 'vs/base/common/actions';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import * as types from 'vs/base/common/types';
export interface IConnectionResult {
export interface IConnectionValidateResult {
isValid: boolean;
connection: IConnectionProfile;
}
@@ -42,7 +48,7 @@ export interface IConnectionComponentCallbacks {
export interface IConnectionComponentController {
showUiComponent(container: HTMLElement): void;
initDialog(model: IConnectionProfile): void;
validateConnection(): IConnectionResult;
validateConnection(): IConnectionValidateResult;
fillInConnectionInputs(connectionInfo: IConnectionProfile): void;
handleOnConnecting(): void;
handleResetConnection(): void;
@@ -65,14 +71,17 @@ export class ConnectionDialogService implements IConnectionDialogService {
private _providerTypes: string[];
private _currentProviderType: string = 'Microsoft SQL Server';
private _connecting: boolean = false;
private _connectionErrorTitle = localize('connectionError', 'Connection Error');
private _connectionErrorTitle = localize('connectionError', 'Connection error');
constructor(
@IPartService private _partService: IPartService,
@IInstantiationService private _instantiationService: IInstantiationService,
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
@IErrorMessageService private _errorMessageService: IErrorMessageService,
@IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService
@IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService,
@IWindowsService private _windowsService: IWindowsService,
@IClipboardService private _clipboardService: IClipboardService,
@ICommandService private _commandService: ICommandService
) {
this._capabilitiesMaps = {};
this._providerNameToDisplayNameMap = {};
@@ -172,13 +181,13 @@ export class ConnectionDialogService implements IConnectionDialogService {
} else if (connectionResult && connectionResult.errorHandled) {
this._connectionDialog.resetConnection();
} else {
this._errorMessageService.showDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage);
this._connectionDialog.resetConnection();
this.showErrorDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage, connectionResult.callStack);
}
}).catch(err => {
this._connecting = false;
this._errorMessageService.showDialog(Severity.Error, this._connectionErrorTitle, err);
this._connectionDialog.resetConnection();
this.showErrorDialog(Severity.Error, this._connectionErrorTitle, err);
});
}
@@ -277,7 +286,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
connectionManagementService: IConnectionManagementService,
params: INewConnectionParams,
model?: IConnectionProfile,
error?: string): Thenable<void> {
connectionResult?: IConnectionResult): Thenable<void> {
this._connectionManagementService = connectionManagementService;
this._params = params;
@@ -303,8 +312,8 @@ export class ConnectionDialogService implements IConnectionDialogService {
}
resolve(this.showDialogWithModel().then(() => {
if (error && error !== '') {
this._errorMessageService.showDialog(Severity.Error, this._connectionErrorTitle, error);
if (connectionResult && connectionResult.errorMessage) {
this.showErrorDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage, connectionResult.callStack);
}
}));
}, err => reject(err));
@@ -333,6 +342,33 @@ export class ConnectionDialogService implements IConnectionDialogService {
}
private getCurrentProviderName(): string {
return 'MSSQL';
return Object.keys(this._providerNameToDisplayNameMap).find(providerName => {
return this._currentProviderType === this._providerNameToDisplayNameMap[providerName];
});
}
private showErrorDialog(severity: Severity, headerTitle: string, message: string, messageDetails?: string): void {
// Kerberos errors are currently very hard to understand, so adding handling of these to solve the common scenario
// note that ideally we would have an extensible service to handle errors by error code and provider, but for now
// this solves the most common "hard error" that we've noticed
const helpLink = 'https://aka.ms/sqlopskerberos';
let actions: IAction[] = [];
if (!platform.isWindows && types.isString(message) && message.toLowerCase().includes('kerberos') && message.toLowerCase().includes('kinit')) {
message = [
localize('kerberosErrorStart', "Connection failed due to Kerberos error."),
localize('kerberosHelpLink', "&nbsp;Help configuring Kerberos is available at ") + helpLink,
localize('kerberosKinit', "&nbsp;If you have previously connected you may need to re-run kinit.")
].join('<br/>');
actions.push(new Action('Kinit', 'Run kinit', null, true, () => {
this._connectionDialog.close();
this._clipboardService.writeText('kinit\r');
this._commandService.executeCommand('workbench.action.terminal.focus').then(resolve => {
return this._commandService.executeCommand('workbench.action.terminal.paste');
}).then(resolve => null, reject => null);
return null;
}));
}
this._errorMessageService.showDialog(severity, headerTitle, message, messageDetails, actions);
}
}

View File

@@ -4,7 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/connectionDialog';
import { attachModalDialogStyler } from 'sql/common/theme/styler';
import nls = require('vs/nls');
import { Button } from 'sql/base/browser/ui/button/button';
import { attachModalDialogStyler, attachButtonStyler } from 'sql/common/theme/styler';
import { TPromise } from 'vs/base/common/winjs.base';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { Modal } from 'sql/base/browser/ui/modal/modal';
@@ -14,51 +17,37 @@ import { TreeCreationUtils } from 'sql/parts/registeredServer/viewlet/treeCreati
import { TreeUpdateUtils } from 'sql/parts/registeredServer/viewlet/treeUpdateUtils';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import * as styler from 'vs/platform/theme/common/styler';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import Event, { Emitter } from 'vs/base/common/event';
import { Builder, $ } from 'vs/base/browser/builder';
import { Button } from 'vs/base/browser/ui/button/button';
import { DefaultController, ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults';
import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import * as TelemetryKeys from 'sql/common/telemetryKeys';
import { localize } from 'vs/nls';
import * as DOM from 'vs/base/browser/dom';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { RecentConnectionTreeController, RecentConnectionActionsProvider } from 'sql/parts/connection/connectionDialog/recentConnectionTreeController';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IMessageService, IConfirmation } from 'vs/platform/message/common/message';
export interface OnShowUIResponse {
selectedProviderType: string;
container: HTMLElement;
}
class TreeController extends DefaultController {
constructor(private clickcb: (element: any, eventish: ICancelableEvent, origin: string) => void) {
super();
}
protected onLeftClick(tree: ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean {
this.clickcb(element, eventish, origin);
return super.onLeftClick(tree, element, eventish, origin);
}
protected onEnter(tree: ITree, event: IKeyboardEvent): boolean {
super.onEnter(tree, event);
this.clickcb(tree.getSelection()[0], event, 'keyboard');
return true;
}
}
export class ConnectionDialogWidget extends Modal {
private _bodyBuilder: Builder;
private _recentConnectionBuilder: Builder;
private _noRecentConnectionBuilder: Builder;
private _dividerBuilder: Builder;
private _connectButton: Button;
private _closeButton: Button;
private _providerTypeSelectBox: SelectBox;
private _newConnectionParams: INewConnectionParams;
private _recentConnectionTree: ITree;
private $connectionUIContainer: Builder;
@@ -89,16 +78,20 @@ export class ConnectionDialogWidget extends Modal {
@IWorkbenchThemeService private _themeService: IWorkbenchThemeService,
@IPartService _partService: IPartService,
@ITelemetryService telemetryService: ITelemetryService,
@IContextKeyService contextKeyService: IContextKeyService
@IContextKeyService contextKeyService: IContextKeyService,
@IContextMenuService private _contextMenuService: IContextMenuService,
@IMessageService private _messageService: IMessageService
) {
super(localize('connection', 'Connection'), TelemetryKeys.Connection, _partService, telemetryService, contextKeyService, { hasSpinner: true, hasErrors: true });
}
protected renderBody(container: HTMLElement): void {
this._bodyBuilder = new Builder(container);
this._providerTypeSelectBox = new SelectBox(this.providerTypeOptions, this.selectedProviderType);
this._bodyBuilder.div({ class: 'connection-recent', id: 'recentConnection' }, (builder) => {
this._recentConnectionBuilder = new Builder(builder.getHTMLElement());
this._noRecentConnectionBuilder = new Builder(builder.getHTMLElement());
this.createRecentConnections();
this._recentConnectionBuilder.hide();
});
@@ -108,16 +101,10 @@ export class ConnectionDialogWidget extends Modal {
});
this._bodyBuilder.div({ class: 'connection-type' }, (modelTableContent) => {
// add SQL Server label to Connection Dialog until we support multiple connection providers
let sqlServerName = localize('microsoftSqlServer', "Microsoft SQL Server");
modelTableContent.div({ class: 'server-name-label' }, (nameLabel) => {
nameLabel.innerHtml(sqlServerName);
});
//let connectTypeLabel = localize('connectType', 'Connection type');
let connectTypeLabel = localize('connectType', 'Connection type');
modelTableContent.element('table', { class: 'connection-table-content' }, (tableContainer) => {
// DialogHelper.appendInputSelectBox(
// DialogHelper.appendRow(tableContainer, connectTypeLabel, 'connection-label', 'connection-input'), this._providerTypeSelectBox);
DialogHelper.appendInputSelectBox(
DialogHelper.appendRow(tableContainer, connectTypeLabel, 'connection-label', 'connection-input'), this._providerTypeSelectBox);
});
});
@@ -141,7 +128,7 @@ export class ConnectionDialogWidget extends Modal {
this._connectButton.enabled = false;
this._closeButton = this.addFooterButton(cancelLabel, () => this.cancel());
this.registerListeners();
this.onProviderTypeSelected('MSSQL');
this.onProviderTypeSelected(this._providerTypeSelectBox.value);
}
// Update theming that is specific to connection flyout body
@@ -156,9 +143,14 @@ export class ConnectionDialogWidget extends Modal {
}
private registerListeners(): void {
this._register(styler.attachButtonStyler(this._connectButton, this._themeService));
this._register(styler.attachButtonStyler(this._closeButton, this._themeService));
// Theme styler
this._register(styler.attachSelectBoxStyler(this._providerTypeSelectBox, this._themeService));
this._register(attachButtonStyler(this._connectButton, this._themeService));
this._register(attachButtonStyler(this._closeButton, this._themeService));
this._register(this._providerTypeSelectBox.onDidSelect(selectedProviderType => {
this.onProviderTypeSelected(selectedProviderType.selected);
}));
}
private onProviderTypeSelected(selectedProviderType: string) {
@@ -171,6 +163,7 @@ export class ConnectionDialogWidget extends Modal {
private connect(element?: IConnectionProfile): void {
if (this._connectButton.enabled) {
this._connectButton.enabled = false;
this._providerTypeSelectBox.disable();
this.showSpinner();
this._onConnect.fire(element);
}
@@ -198,13 +191,37 @@ export class ConnectionDialogWidget extends Modal {
this.hide();
}
private createRecentConnections() {
private clearRecentConnectionList(): TPromise<boolean> {
let confirm: IConfirmation = {
message: nls.localize('clearRecentConnectionMessage', 'Are you sure you want to delete all the connections from the list?'),
primaryButton: localize('yes', 'Yes'),
secondaryButton: localize('no', 'No'),
type: 'question'
};
return this._messageService.confirm(confirm).then(confirmation => {
if (!confirmation.confirmed) {
return TPromise.as(false);
} else {
this._connectionManagementService.clearRecentConnectionsList();
this.open(false);
return TPromise.as(true);
}
});
}
private createRecentConnectionList(): void {
this._recentConnectionBuilder.div({ class: 'connection-recent-content' }, (recentConnectionContainer) => {
let recentHistoryLabel = localize('recentHistory', 'Recent history');
recentConnectionContainer.div({ class: 'connection-history-label' }, (recentTitle) => {
recentTitle.innerHtml(recentHistoryLabel);
recentConnectionContainer.div({ class: 'recent-titles-container'}, (container) => {
container.div({ class: 'connection-history-label' }, (recentTitle) => {
recentTitle.innerHtml(recentHistoryLabel);
});
container.div({ class: 'search-action clear-search-results'}, (clearSearchIcon) => {
clearSearchIcon.on('click', () => this.clearRecentConnectionList());
});
});
recentConnectionContainer.div({ class: 'server-explorer-viewlet' }, (divContainer: Builder) => {
divContainer.div({ class: 'explorer-servers' }, (treeContainer: Builder) => {
let leftClick = (element: any, eventish: ICancelableEvent, origin: string) => {
@@ -212,9 +229,16 @@ export class ConnectionDialogWidget extends Modal {
if (element instanceof ConnectionProfile) {
this.onRecentConnectionClick({ payload: { origin: origin, originalEvent: eventish } }, element);
}
};
let controller = new TreeController(leftClick);
let actionProvider = this._instantiationService.createInstance(RecentConnectionActionsProvider, this._instantiationService, this._connectionManagementService,
this._messageService);
let controller = new RecentConnectionTreeController(leftClick, actionProvider, this._connectionManagementService, this._contextMenuService);
actionProvider.onRecentConnectionRemoved(() => {
this.open(this._connectionManagementService.getRecentConnections().length > 0);
});
controller.onRecentConnectionRemoved(() => {
this.open(this._connectionManagementService.getRecentConnections().length > 0);
})
this._recentConnectionTree = TreeCreationUtils.createConnectionTree(treeContainer.getHTMLElement(), this._instantiationService, controller);
// Theme styler
@@ -225,6 +249,20 @@ export class ConnectionDialogWidget extends Modal {
});
}
private createRecentConnections() {
this.createRecentConnectionList();
this._noRecentConnectionBuilder.div({ class: 'connection-recent-content' }, (noRecentConnectionContainer) => {
let recentHistoryLabel = localize('recentHistory', 'Recent history');
noRecentConnectionContainer.div({ class: 'connection-history-label' }, (recentTitle) => {
recentTitle.innerHtml(recentHistoryLabel);
});
let noRecentHistoryLabel = localize('noRecentConnections', 'No Recent Connections');
noRecentConnectionContainer.div({ class: 'no-recent-connections' }, (noRecentTitle) => {
noRecentTitle.innerHtml(noRecentHistoryLabel);
});
});
}
private onRecentConnectionClick(event: any, element: IConnectionProfile) {
let isMouseOrigin = event.payload && (event.payload.origin === 'mouse');
let isDoubleClick = isMouseOrigin && event.payload.originalEvent && event.payload.originalEvent.detail === 2;
@@ -244,14 +282,15 @@ export class ConnectionDialogWidget extends Modal {
public open(recentConnections: boolean) {
this.show();
if (recentConnections) {
this._noRecentConnectionBuilder.hide();
this._recentConnectionBuilder.show();
TreeUpdateUtils.structuralTreeUpdate(this._recentConnectionTree, 'recent', this._connectionManagementService);
// call layout with view height
this.layout();
} else {
this._recentConnectionBuilder.hide();
this._noRecentConnectionBuilder.show();
}
TreeUpdateUtils.structuralTreeUpdate(this._recentConnectionTree, 'recent', this._connectionManagementService);
// call layout with view height
this.layout();
this.initDialog();
}
@@ -284,6 +323,7 @@ export class ConnectionDialogWidget extends Modal {
public resetConnection(): void {
this.hideSpinner();
this._connectButton.enabled = true;
this._providerTypeSelectBox.enable();
this._onResetConnection.fire();
}
@@ -296,6 +336,7 @@ export class ConnectionDialogWidget extends Modal {
}
public updateProvider(displayName: string) {
this.onProviderTypeSelected('MSSQL');
this._providerTypeSelectBox.selectWithOptionName(displayName);
this.onProviderTypeSelected(displayName);
}
}

View File

@@ -7,10 +7,10 @@
import 'vs/css!./media/sqlConnection';
import { Builder, $ } from 'vs/base/browser/builder';
import { Button } from 'vs/base/browser/ui/button/button';
import { Button } from 'sql/base/browser/ui/button/button';
import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { Checkbox } from 'sql/base/browser/ui/checkbox/defaultCheckbox';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
import * as DialogHelper from 'sql/base/browser/ui/modal/dialogHelper';
import { IConnectionComponentCallbacks } from 'sql/parts/connection/connectionDialog/connectionDialogService';
@@ -21,11 +21,12 @@ import * as Constants from 'sql/parts/connection/common/constants';
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import * as styler from 'vs/platform/theme/common/styler';
import { attachInputBoxStyler } from 'sql/common/theme/styler';
import { attachInputBoxStyler, attachButtonStyler } from 'sql/common/theme/styler';
import * as DOM from 'vs/base/browser/dom';
import data = require('data');
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { localize } from 'vs/nls';
import { OS, OperatingSystem } from 'vs/base/common/platform';
export class ConnectionWidget {
private _builder: Builder;
@@ -36,6 +37,7 @@ export class ConnectionWidget {
private _databaseNameInputBox: InputBox;
private _userNameInputBox: InputBox;
private _passwordInputBox: InputBox;
private _password: string;
private _rememberPasswordCheckBox: Checkbox;
private _advancedButton: Button;
private _callbacks: IConnectionComponentCallbacks;
@@ -43,9 +45,10 @@ export class ConnectionWidget {
private _toDispose: lifecycle.IDisposable[];
private _optionsMaps: { [optionType: number]: data.ConnectionOption };
private _tableContainer: Builder;
private _focusedBeforeHandleOnConnection: HTMLElement;
private _providerName: string;
private _authTypeMap: { [providerName: string]: AuthenticationType[] } = {
[Constants.mssqlProviderName]: [new AuthenticationType('Integrated', false), new AuthenticationType('SqlLogin', true)]
[Constants.mssqlProviderName]: [new AuthenticationType(Constants.integrated, false), new AuthenticationType(Constants.sqlLogin, true)]
};
private _saveProfile: boolean;
public DefaultServerGroup: IConnectionProfileGroup = {
@@ -84,7 +87,14 @@ export class ConnectionWidget {
}
var authTypeOption = this._optionsMaps[ConnectionOptionSpecialType.authType];
this._authTypeSelectBox = authTypeOption ? new SelectBox(authTypeOption.categoryValues.map(c => c.displayName), authTypeOption.defaultValue) : undefined;
if(authTypeOption) {
if (OS === OperatingSystem.Windows) {
authTypeOption.defaultValue = this.getAuthTypeDisplayName(Constants.integrated);
} else {
authTypeOption.defaultValue = this.getAuthTypeDisplayName(Constants.sqlLogin);
}
this._authTypeSelectBox = new SelectBox(authTypeOption.categoryValues.map(c => c.displayName), authTypeOption.defaultValue);
}
this._providerName = providerName;
}
@@ -134,6 +144,7 @@ export class ConnectionWidget {
let passwordBuilder = DialogHelper.appendRow(this._tableContainer, passwordOption.displayName, 'connection-label', 'connection-input');
this._passwordInputBox = new InputBox(passwordBuilder.getHTMLElement(), this._contextViewService);
this._passwordInputBox.inputElement.type = 'password';
this._password = '';
let rememberPasswordLabel = localize('rememberPassword', 'Remember password');
this._rememberPasswordCheckBox = this.appendCheckbox(this._tableContainer, rememberPasswordLabel, 'connection-checkbox', 'connection-input', false);
@@ -201,13 +212,14 @@ export class ConnectionWidget {
this._toDispose.push(attachInputBoxStyler(this._userNameInputBox, this._themeService));
this._toDispose.push(attachInputBoxStyler(this._passwordInputBox, this._themeService));
this._toDispose.push(styler.attachSelectBoxStyler(this._serverGroupSelectBox, this._themeService));
this._toDispose.push(styler.attachButtonStyler(this._advancedButton, this._themeService));
this._toDispose.push(attachButtonStyler(this._advancedButton, this._themeService));
if (this._authTypeSelectBox) {
// Theme styler
this._toDispose.push(styler.attachSelectBoxStyler(this._authTypeSelectBox, this._themeService));
this._toDispose.push(this._authTypeSelectBox.onDidSelect(selectedAuthType => {
this.onAuthTypeSelected(selectedAuthType.selected);
this.setConnectButton();
}));
}
@@ -218,6 +230,14 @@ export class ConnectionWidget {
this._toDispose.push(this._serverNameInputBox.onDidChange(serverName => {
this.serverNameChanged(serverName);
}));
this._toDispose.push(this._userNameInputBox.onDidChange(userName => {
this.setConnectButton();
}));
this._toDispose.push(this._passwordInputBox.onDidChange(passwordInput => {
this._password = passwordInput;
}));
}
private onGroupSelected(selectedGroup: string) {
@@ -230,6 +250,17 @@ export class ConnectionWidget {
}
}
private setConnectButton() : void {
let authDisplayName: string = this.getAuthTypeDisplayName(this.authenticationType);
let authType: AuthenticationType = this.getMatchingAuthType(authDisplayName);
let showUsernameAndPassword: boolean = true;
if(authType) {
showUsernameAndPassword = authType.showUsernameAndPassword;
}
showUsernameAndPassword ? this._callbacks.onSetConnectButton(!!this.serverName && !!this.userName) :
this._callbacks.onSetConnectButton(!!this.serverName);
}
private onAuthTypeSelected(selectedAuthType: string) {
let currentAuthType = this.getMatchingAuthType(selectedAuthType);
if (!currentAuthType.showUsernameAndPassword) {
@@ -239,6 +270,7 @@ export class ConnectionWidget {
this._passwordInputBox.hideMessage();
this._userNameInputBox.value = '';
this._passwordInputBox.value = '';
this._password = '';
this._rememberPasswordCheckBox.checked = false;
this._rememberPasswordCheckBox.enabled = false;
@@ -250,7 +282,7 @@ export class ConnectionWidget {
}
private serverNameChanged(serverName: string) {
this._callbacks.onSetConnectButton(!!serverName);
this.setConnectButton();
if (serverName.toLocaleLowerCase().includes('database.windows.net')) {
this._callbacks.onSetAzureTimeOut();
}
@@ -289,10 +321,10 @@ export class ConnectionWidget {
public fillInConnectionInputs(connectionInfo: IConnectionProfile) {
if (connectionInfo) {
this._serverNameInputBox.value = this.getModelValue(connectionInfo.serverName);
this._callbacks.onSetConnectButton(!!connectionInfo.serverName);
this._databaseNameInputBox.value = this.getModelValue(connectionInfo.databaseName);
this._userNameInputBox.value = this.getModelValue(connectionInfo.userName);
this._passwordInputBox.value = this.getModelValue(connectionInfo.password);
this._passwordInputBox.value = connectionInfo.password ? Constants.passwordChars : '';
this._password = this.getModelValue(connectionInfo.password);
this._saveProfile = connectionInfo.saveProfile;
let groupName: string;
if (this._saveProfile) {
@@ -321,18 +353,26 @@ export class ConnectionWidget {
if (this._authTypeSelectBox) {
this.onAuthTypeSelected(this._authTypeSelectBox.value);
}
// Disable connect button if -
// 1. Authentication type is SQL Login and no username is provided
// 2. No server name is provided
this.setConnectButton();
}
}
private getAuthTypeDisplayName(authTypeName: string) {
var displayName: string;
var authTypeOption = this._optionsMaps[ConnectionOptionSpecialType.authType];
authTypeOption.categoryValues.forEach(c => {
if (c.name === authTypeName) {
displayName = c.displayName;
}
});
if(authTypeOption) {
authTypeOption.categoryValues.forEach(c => {
if (c.name === authTypeName) {
displayName = c.displayName;
}
});
}
return displayName;
}
@@ -348,6 +388,7 @@ export class ConnectionWidget {
}
public handleOnConnecting(): void {
this._focusedBeforeHandleOnConnection = <HTMLElement>document.activeElement;
this._advancedButton.enabled = false;
this._serverGroupSelectBox.disable();
@@ -378,6 +419,10 @@ export class ConnectionWidget {
this._passwordInputBox.enable();
this._rememberPasswordCheckBox.enabled = true;
}
if (this._focusedBeforeHandleOnConnection) {
this._focusedBeforeHandleOnConnection.focus();
}
}
public get serverName(): string {
@@ -393,7 +438,7 @@ export class ConnectionWidget {
}
public get password(): string {
return this._passwordInputBox.value;
return this._password;
}
public get authenticationType(): string {
@@ -467,7 +512,8 @@ export class ConnectionWidget {
}
private getMatchingAuthType(displayName: string): AuthenticationType {
return this._authTypeMap[this._providerName].find(authType => this.getAuthTypeDisplayName(authType.name) === displayName);
const authType = this._authTypeMap[this._providerName];
return authType ? authType.find(authType => this.getAuthTypeDisplayName(authType.name) === displayName) : undefined;
}
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#1E1E1E" d="M4.222 0h-2.222v.479c-.526.648-.557 1.57-.043 2.269l.043.059v3.203l-.4.296-.053.053c-.353.352-.547.822-.547 1.321s.194.967.549 1.32c.134.134.288.236.451.322v6.678h14v-16h-11.778z"/><path fill="#E8E8E8" d="M10.798 7l-1.83-2h6.032v2h-4.202zm-2.292-6h-3.207l1.337 1.52 1.87-1.52zm-5.506 8.531v1.469h12v-2h-10.813l-.024.021c-.3.299-.716.479-1.163.51zm0 5.469h12v-2h-12v2zm3.323-8h.631l-.347-.266-.284.266zm8.677-4v-2h-3.289l-1.743 2h5.032z"/><path fill="#F48771" d="M7.246 4.6l2.856-3.277-.405-.002-3.176 2.581-2.607-2.962c-.336-.221-.786-.2-1.082.096-.308.306-.319.779-.069 1.12l2.83 2.444-3.339 2.466c-.339.338-.339.887 0 1.225.339.337.888.337 1.226 0l3.063-2.867 3.33 2.555h.466l-3.093-3.379z"/></svg>

After

Width:  |  Height:  |  Size: 787 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#fff" d="M4.222 0h-2.222v.479c-.526.648-.557 1.57-.043 2.269l.043.059v3.203l-.4.296-.053.053c-.353.352-.547.822-.547 1.321s.194.967.549 1.32c.134.134.288.236.451.322v6.678h14v-16h-11.778z"/><path fill="#424242" d="M10.798 7l-1.83-2h6.032v2h-4.202zm-2.292-6h-3.207l1.337 1.52 1.87-1.52zm-5.506 8.531v1.469h12v-2h-10.813l-.024.021c-.3.299-.716.479-1.163.51zm0 5.469h12v-2h-12v2zm3.323-8h.631l-.347-.266-.284.266zm8.677-4v-2h-3.289l-1.743 2h5.032z"/><path fill="#A1260D" d="M7.246 4.6l2.856-3.277-.405-.002-3.176 2.581-2.607-2.962c-.336-.221-.786-.2-1.082.096-.308.306-.319.779-.069 1.12l2.83 2.444-3.339 2.466c-.339.338-.339.887 0 1.225.339.337.888.337 1.226 0l3.063-2.867 3.33 2.555h.466l-3.093-3.379z"/></svg>

After

Width:  |  Height:  |  Size: 784 B

View File

@@ -20,13 +20,22 @@
overflow-y: auto;
}
.connection-history-label {
font-size: 15px;
.no-recent-connections {
font-size: 12px;
text-align: left;
display: block;
padding-top: 5px;
}
.server-name-label {
.connection-history-label {
font-size: 15px;
margin-bottom: 15px;
display: inline;
text-align: left;
}
.recent-titles-container {
display: flex;
justify-content: space-between;
}
.connection-provider-info {
@@ -46,4 +55,17 @@
.connection-type {
margin: 15px;
overflow-y: hidden;
}
.search-action.clear-search-results {
background: url('clear-search-results.svg') center right no-repeat;
width: 10%;
cursor: pointer;
}
.vs-dark .search-action.clear-search-results,
.hc-black .search-action.clear-search-results {
background: url('clear-search-results-dark.svg') center right no-repeat;
width: 10%;
cursor: pointer;
}

View File

@@ -0,0 +1,142 @@
/*---------------------------------------------------------------------------------------------
* 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 { TPromise } from 'vs/base/common/winjs.base';
import { DefaultController, ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ClearSingleRecentConnectionAction } from 'sql/parts/connection/common/connectionActions';
import { ContributableActionProvider } from 'vs/workbench/browser/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import { IAction } from 'vs/base/common/actions';
import Event, { Emitter } from 'vs/base/common/event';
import { IMessageService } from 'vs/platform/message/common/message';
import mouse = require('vs/base/browser/mouseEvent');
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
export class RecentConnectionActionsProvider extends ContributableActionProvider {
private _onRecentConnectionRemoved = new Emitter<void>();
public onRecentConnectionRemoved: Event<void> = this._onRecentConnectionRemoved.event;
constructor(
@IInstantiationService private _instantiationService: IInstantiationService,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IMessageService private _messageService: IMessageService,
) {
super();
}
private getRecentConnectionActions(tree: ITree, element: any): IAction[] {
let actions: IAction[] = [];
let clearSingleConnectionAction = this._instantiationService.createInstance(ClearSingleRecentConnectionAction, ClearSingleRecentConnectionAction.ID,
ClearSingleRecentConnectionAction.LABEL,<IConnectionProfile>element);
clearSingleConnectionAction.onRecentConnectionRemoved(() => this._onRecentConnectionRemoved.fire());
actions.push(clearSingleConnectionAction);
return actions;
}
public hasActions(tree: ITree, element: any): boolean {
return element instanceof ConnectionProfile;
}
/**
* Return actions given an element in the tree
*/
public getActions(tree: ITree, element: any): TPromise<IAction[]> {
if (element instanceof ConnectionProfile) {
return TPromise.as(this.getRecentConnectionActions(tree, element));
}
return TPromise.as([]);
}
}
export class RecentConnectionsActionsContext {
public connectionProfile: ConnectionProfile;
public container: HTMLElement;
public tree: ITree;
}
export class RecentConnectionTreeController extends DefaultController {
private _onRecentConnectionRemoved = new Emitter<void>();
public onRecentConnectionRemoved: Event<void> = this._onRecentConnectionRemoved.event;
constructor(
private clickcb: (element: any, eventish: ICancelableEvent, origin: string) => void,
private actionProvider: RecentConnectionActionsProvider,
private _connectionManagementService: IConnectionManagementService,
@IContextMenuService private _contextMenuService: IContextMenuService
) {
super();
}
protected onLeftClick(tree: ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean {
this.clickcb(element, eventish, origin);
return super.onLeftClick(tree, element, eventish, origin);
}
protected onEnter(tree: ITree, event: IKeyboardEvent): boolean {
super.onEnter(tree, event);
this.clickcb(tree.getSelection()[0], event, 'keyboard');
return true;
}
protected onRightClick(tree: ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean {
this.clickcb(element, eventish, origin);
this.onContextMenu(tree, element, event);
return true;
}
public onMouseDown(tree: ITree, element: any, event: mouse.IMouseEvent, origin: string = 'mouse'): boolean {
if (event.leftButton || event.middleButton) {
return this.onLeftClick(tree, element, event, origin);
} else {
return this.onRightClick(tree, element, event);
}
}
public onKeyDown(tree: ITree, event: IKeyboardEvent): boolean {
if (event.keyCode === 20) {
let element = tree.getFocus();
if (element instanceof ConnectionProfile) {
this._connectionManagementService.clearRecentConnection(element);
this._onRecentConnectionRemoved.fire();
return true;
}
}
return super.onKeyDown(tree, event);
}
public onContextMenu(tree: ITree, element: any, event: any): boolean {
var actionContext: any;
if (element instanceof ConnectionProfile) {
actionContext = new RecentConnectionsActionsContext();
actionContext.container = event.target;
actionContext.connectionProfile = <ConnectionProfile>element;
actionContext.tree = tree;
} else {
actionContext = element;
}
let anchor = { x: event.x + 1, y: event.y };
this._contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => this.actionProvider.getActions(tree, element),
onHide: (wasCancelled?: boolean) => {
if (wasCancelled) {
tree.DOMFocus();
}
},
getActionsContext: () => (actionContext)
});
return true;
}
}