mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
Add tab coloring by server group (#383)
This commit is contained in:
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@@ -63,7 +63,7 @@
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "attach",
|
||||
@@ -95,7 +95,7 @@
|
||||
"name": "Unit Tests",
|
||||
"protocol": "inspector",
|
||||
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
|
||||
"runtimeExecutable": "${workspaceFolder}/.build/electron/sqlops.app/Contents/MacOS/Electron",
|
||||
"runtimeExecutable": "${workspaceFolder}/.build/electron/SQL Operations Studio.app/Contents/MacOS/Electron",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceFolder}/.build/electron/sqlops.exe"
|
||||
},
|
||||
|
||||
@@ -220,6 +220,8 @@ export interface IConnectionManagementService {
|
||||
|
||||
canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean;
|
||||
|
||||
getTabColorForUri(uri: string): string;
|
||||
|
||||
/**
|
||||
* Sends a notification that the language flavor for a given URI has changed.
|
||||
* For SQL, this would be the specific SQL implementation being used.
|
||||
|
||||
@@ -148,6 +148,11 @@ export class ConnectionManagementService implements IConnectionManagementService
|
||||
|
||||
this.disposables.push(this._onAddConnectionProfile);
|
||||
this.disposables.push(this._onDeleteConnectionProfile);
|
||||
|
||||
// Refresh editor titles when connections start/end/change to ensure tabs are colored correctly
|
||||
this.onConnectionChanged(() => this.refreshEditorTitles());
|
||||
this.onConnect(() => this.refreshEditorTitles());
|
||||
this.onDisconnect(() => this.refreshEditorTitles());
|
||||
}
|
||||
|
||||
// Event Emitters
|
||||
@@ -1219,6 +1224,7 @@ export class ConnectionManagementService implements IConnectionManagementService
|
||||
public editGroup(group: ConnectionProfileGroup): Promise<any> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
this._connectionStore.editGroup(group).then(groupId => {
|
||||
this.refreshEditorTitles();
|
||||
this._onAddConnectionProfile.fire();
|
||||
resolve(null);
|
||||
}).catch(err => {
|
||||
@@ -1323,4 +1329,25 @@ export class ConnectionManagementService implements IConnectionManagementService
|
||||
}
|
||||
return Promise.reject('The given URI is not currently connected');
|
||||
}
|
||||
|
||||
public getTabColorForUri(uri: string): string {
|
||||
if (!WorkbenchUtils.getSqlConfigValue<string>(this._workspaceConfigurationService, 'enableTabColors')) {
|
||||
return undefined;
|
||||
}
|
||||
let connectionProfile = this.getConnectionProfile(uri);
|
||||
if (!connectionProfile) {
|
||||
return undefined;
|
||||
}
|
||||
let matchingGroup = this._connectionStore.getGroupFromId(connectionProfile.groupId);
|
||||
if (!matchingGroup) {
|
||||
return undefined;
|
||||
}
|
||||
return matchingGroup.color;
|
||||
}
|
||||
|
||||
private refreshEditorTitles(): void {
|
||||
if (this._editorGroupService instanceof EditorPart) {
|
||||
this._editorGroupService.refreshEditorTitles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import { ConfigurationEditingService } from 'vs/workbench/services/configuration
|
||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
|
||||
import * as data from 'data';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
const MAX_CONNECTIONS_DEFAULT = 25;
|
||||
|
||||
@@ -490,6 +489,11 @@ export class ConnectionStore {
|
||||
return result;
|
||||
}
|
||||
|
||||
public getGroupFromId(groupId: string): IConnectionProfileGroup {
|
||||
let groups = this._connectionConfig.getAllGroups();
|
||||
return groups.find(group => group.id === groupId);
|
||||
}
|
||||
|
||||
private getMaxRecentConnectionsCount(): number {
|
||||
let config = this._workspaceConfigurationService.getConfiguration(Constants.sqlConfigSectionName);
|
||||
|
||||
|
||||
@@ -167,4 +167,8 @@ export class DashboardInput extends EditorInput {
|
||||
&& profile1.authenticationType === profile2.authenticationType
|
||||
&& profile1.groupFullName === profile2.groupFullName;
|
||||
}
|
||||
|
||||
public get tabColor(): string {
|
||||
return this._connectionService.getTabColorForUri(this.uri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,4 +180,8 @@ export class EditDataInput extends EditorInput implements IConnectableInput {
|
||||
super.close();
|
||||
});
|
||||
}
|
||||
|
||||
public get tabColor(): string {
|
||||
return this._connectionManagementService.getTabColorForUri(this.uri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,6 +240,11 @@ let registryProperties = {
|
||||
'description': localize('sql.showBatchTime', '[Optional] Should execution time be shown for individual batches'),
|
||||
'default': false
|
||||
},
|
||||
'sql.enableTabColors': {
|
||||
'type': 'boolean',
|
||||
'description': localize('sql.enableTabColors', 'True to color tabs based on the server group of their active connection, false otherwise'),
|
||||
'default': true
|
||||
},
|
||||
'mssql.intelliSense.enableIntelliSense': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
|
||||
@@ -252,4 +252,11 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec
|
||||
this._currentEventCallbacks = dispose(this._currentEventCallbacks);
|
||||
this._currentEventCallbacks = callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the color that should be displayed
|
||||
*/
|
||||
public get tabColor(): string {
|
||||
return this._connectionManagementService.getTabColorForUri(this.uri);
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import { WorkspaceConfigurationTestService } from 'sqltest/stubs/workspaceConfig
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { IConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup';
|
||||
|
||||
suite('SQL ConnectionManagementService tests', () => {
|
||||
|
||||
@@ -210,7 +211,7 @@ suite('SQL ConnectionManagementService tests', () => {
|
||||
let connectionToUse = connection ? connection : connectionProfile;
|
||||
return new Promise<IConnectionResult>((resolve, reject) => {
|
||||
let id = connectionToUse.getOptionsKey();
|
||||
let defaultUri = 'connection://' + (id ? id : connection.serverName + ':' + connection.databaseName);
|
||||
let defaultUri = 'connection://' + (id ? id : connectionToUse.serverName + ':' + connectionToUse.databaseName);
|
||||
connectionManagementService.onConnectionRequestSent(() => {
|
||||
let info: data.ConnectionInfoSummary = {
|
||||
connectionId: error ? undefined : 'id',
|
||||
@@ -290,7 +291,7 @@ suite('SQL ConnectionManagementService tests', () => {
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
}, err => done(err));
|
||||
});
|
||||
|
||||
test('connect should save profile given options with saveProfile set to true', done => {
|
||||
@@ -764,4 +765,29 @@ suite('SQL ConnectionManagementService tests', () => {
|
||||
}
|
||||
}, err => done(err));
|
||||
});
|
||||
|
||||
test('getTabColorForUri returns undefined when there is no connection for the given URI', () => {
|
||||
let connectionManagementService = createConnectionManagementService();
|
||||
let color = connectionManagementService.getTabColorForUri('invalidUri');
|
||||
assert.equal(color, undefined);
|
||||
});
|
||||
|
||||
test('getTabColorForUri returns the group color corresponding to the connection for a URI', done => {
|
||||
// Set up the connection store to give back a group for the expected connection profile
|
||||
configResult['enableTabColors'] = true;
|
||||
let expectedColor = 'red';
|
||||
connectionStore.setup(x => x.getGroupFromId(connectionProfile.groupId)).returns(() => <IConnectionProfileGroup> {
|
||||
color: expectedColor
|
||||
});
|
||||
let uri = 'testUri';
|
||||
connect(uri).then(() => {
|
||||
try {
|
||||
let tabColor = connectionManagementService.getTabColorForUri(uri);
|
||||
assert.equal(tabColor, expectedColor);
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
}, err => done(err));
|
||||
});
|
||||
});
|
||||
@@ -18,7 +18,7 @@ import { CapabilitiesService } from 'sql/services/capabilities/capabilitiesServi
|
||||
import * as data from 'data';
|
||||
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup';
|
||||
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup';
|
||||
|
||||
suite('SQL ConnectionStore tests', () => {
|
||||
let defaultNamedProfile: IConnectionProfile;
|
||||
@@ -93,7 +93,7 @@ suite('SQL ConnectionStore tests', () => {
|
||||
getInstalled: () => {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
capabilitiesService = TypeMoq.Mock.ofType(CapabilitiesService, TypeMoq.MockBehavior.Loose, extensionManagementServiceMock, {});
|
||||
let capabilities: data.DataProtocolServerCapabilities[] = [];
|
||||
@@ -462,4 +462,33 @@ suite('SQL ConnectionStore tests', () => {
|
||||
currentList = connectionStore.getConnectionsFromMemento(mementoKey);
|
||||
assert.equal(currentList.length, 3, 'Adding same connection with group /');
|
||||
});
|
||||
|
||||
test('getGroupFromId returns undefined when there is no group with the given ID', () => {
|
||||
let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object,
|
||||
credentialStore.object, capabilitiesService.object, connectionConfig.object);
|
||||
let group = connectionStore.getGroupFromId('invalidId');
|
||||
assert.equal(group, undefined, 'Returned group was not undefined when there was no group with the given ID');
|
||||
});
|
||||
|
||||
test('getGroupFromId returns the group that has the given ID', () => {
|
||||
// Set up the server groups with an additional group that contains a child group
|
||||
let groups: IConnectionProfileGroup[] = connectionConfig.object.getAllGroups();
|
||||
let parentGroupId = 'parentGroup';
|
||||
let childGroupId = 'childGroup';
|
||||
let parentGroup = new ConnectionProfileGroup(parentGroupId, undefined, parentGroupId, '', '');
|
||||
let childGroup = new ConnectionProfileGroup(childGroupId, parentGroup, childGroupId, '', '');
|
||||
groups.push(parentGroup, childGroup);
|
||||
let newConnectionConfig = TypeMoq.Mock.ofType(ConnectionConfig);
|
||||
newConnectionConfig.setup(x => x.getAllGroups()).returns(() => groups);
|
||||
let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object,
|
||||
credentialStore.object, capabilitiesService.object, newConnectionConfig.object);
|
||||
|
||||
// If I look up the parent group using its ID, then I get back the correct group
|
||||
let actualGroup = connectionStore.getGroupFromId(parentGroupId);
|
||||
assert.equal(actualGroup.id, parentGroupId, 'Did not get the parent group when looking it up with its ID');
|
||||
|
||||
// If I look up the child group using its ID, then I get back the correct group
|
||||
actualGroup = connectionStore.getGroupFromId(childGroupId);
|
||||
assert.equal(actualGroup.id, childGroupId, 'Did not get the child group when looking it up with its ID');
|
||||
});
|
||||
});
|
||||
@@ -237,4 +237,8 @@ export class TestConnectionManagementService implements IConnectionManagementSer
|
||||
rebuildIntelliSenseCache(uri: string): Thenable<void> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getTabColorForUri(uri: string): string {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,8 @@ export interface IEditorGroupsControl {
|
||||
|
||||
getRatio(): number[];
|
||||
|
||||
// {{SQL CARBON EDIT}} -- Allow editor titles to be refreshed to support tab coloring
|
||||
refreshTitles(): void;
|
||||
|
||||
dispose(): void;
|
||||
}
|
||||
@@ -2126,6 +2128,14 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
}
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}} -- Allow editor titles to be refreshed to support tab coloring
|
||||
public refreshTitles(): void {
|
||||
POSITIONS.forEach(position => {
|
||||
let titleControl = this.getTitleAreaControl(position);
|
||||
titleControl.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
|
||||
@@ -1359,6 +1359,11 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
|
||||
return sizes;
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}} -- Allow editor titles to be refreshed to support tab coloring
|
||||
public refreshEditorTitles(): void {
|
||||
this.editorGroupsControl.refreshTitles();
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
|
||||
// Persist UI State
|
||||
|
||||
@@ -39,12 +39,15 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { extractResources } from 'vs/base/browser/dnd';
|
||||
import { getOrSet } from 'vs/base/common/map';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
|
||||
import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_INACTIVE_FOREGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND, TAB_UNFOCUSED_INACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER } from 'vs/workbench/common/theme';
|
||||
import { activeContrastBorder, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
|
||||
|
||||
// {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
interface IEditorInputLabel {
|
||||
name: string;
|
||||
description?: string;
|
||||
@@ -330,6 +333,20 @@ export class TabsTitleControl extends TitleControl {
|
||||
} else {
|
||||
DOM.removeClass(tabContainer, 'dirty');
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||
let sqlEditor = editor as any;
|
||||
if (sqlEditor.tabColor && this.themeService.getTheme().type !== HIGH_CONTRAST) {
|
||||
tabContainer.style.borderTopColor = sqlEditor.tabColor;
|
||||
tabContainer.style.borderTopWidth = isTabActive ? '2px' : '1px';
|
||||
let backgroundColor = Color.Format.CSS.parseHex(sqlEditor.tabColor);
|
||||
if (backgroundColor) {
|
||||
tabContainer.style.backgroundColor = backgroundColor.transparent(isTabActive ? 0.3 : 0.2).toString();
|
||||
}
|
||||
} else {
|
||||
tabContainer.style.borderTopColor = '';
|
||||
tabContainer.style.borderTopWidth = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user