diff --git a/src/sql/parts/common/customInputConverter.ts b/src/sql/parts/common/customInputConverter.ts index 51b73f95a2..ed08fff511 100644 --- a/src/sql/parts/common/customInputConverter.ts +++ b/src/sql/parts/common/customInputConverter.ts @@ -38,13 +38,13 @@ export function convertEditorInput(input: EditorInput, options: IQueryEditorOpti let uri: URI = getQueryEditorFileUri(input); if (uri) { const queryResultsInput: QueryResultsInput = instantiationService.createInstance(QueryResultsInput, uri.toString()); - let queryInput: QueryInput = instantiationService.createInstance(QueryInput, input.getName(), '', input, queryResultsInput, undefined); + let queryInput: QueryInput = instantiationService.createInstance(QueryInput, '', input, queryResultsInput, undefined); return queryInput; } //QueryPlanInput uri = getQueryPlanEditorUri(input); - if(uri) { + if (uri) { let queryPlanXml: string = fs.readFileSync(uri.fsPath); let queryPlanInput: QueryPlanInput = instantiationService.createInstance(QueryPlanInput, queryPlanXml, 'aaa', undefined); return queryPlanInput; @@ -60,14 +60,14 @@ export function convertEditorInput(input: EditorInput, options: IQueryEditorOpti */ export function getSupportedInputResource(input: IEditorInput): URI { if (input instanceof UntitledEditorInput) { - let untitledCast: UntitledEditorInput = input; + let untitledCast: UntitledEditorInput = input; if (untitledCast) { return untitledCast.getResource(); } } if (input instanceof FileEditorInput) { - let fileCast: FileEditorInput = input; + let fileCast: FileEditorInput = input; if (fileCast) { return fileCast.getResource(); } @@ -99,7 +99,7 @@ function getQueryEditorFileUri(input: EditorInput): URI { if (uri) { let isValidUri: boolean = !!uri && !!uri.toString; - if (isValidUri && (hasFileExtension(sqlFileTypes, input, true) || hasSqlFileMode(input)) ) { + if (isValidUri && (hasFileExtension(sqlFileTypes, input, true) || hasSqlFileMode(input))) { return uri; } } @@ -120,7 +120,7 @@ function getQueryPlanEditorUri(input: EditorInput): URI { // If this editor is not already of type queryinput if (!(input instanceof QueryPlanInput)) { let uri: URI = getSupportedInputResource(input); - if(uri) { + if (uri) { if (hasFileExtension(sqlPlanFileTypes, input, false)) { return uri; } @@ -136,7 +136,7 @@ function getQueryPlanEditorUri(input: EditorInput): URI { */ function hasSqlFileMode(input: EditorInput): boolean { if (input instanceof UntitledEditorInput) { - let untitledCast: UntitledEditorInput = input; + let untitledCast: UntitledEditorInput = input; return untitledCast && (untitledCast.getModeId() === undefined || untitledCast.getModeId() === sqlModeId); } diff --git a/src/sql/parts/query/common/query.contribution.ts b/src/sql/parts/query/common/query.contribution.ts index 2eb3b40cce..52485bfe27 100644 --- a/src/sql/parts/query/common/query.contribution.ts +++ b/src/sql/parts/query/common/query.contribution.ts @@ -306,6 +306,11 @@ let registryProperties = { 'default': Constants.tabColorModeOff, 'description': localize('tabColorMode', "Controls how to color tabs based on the server group of their active connection") }, + 'sql.showConnectionInfoInTitle': { + 'type': 'boolean', + 'description': localize('showConnectionInfoInTitle', "Controls whether to show the connection info for a tab in the title."), + 'default': false + }, 'mssql.intelliSense.enableIntelliSense': { 'type': 'boolean', 'default': true, diff --git a/src/sql/parts/query/common/queryInput.ts b/src/sql/parts/query/common/queryInput.ts index b65f959a14..3483e87f36 100644 --- a/src/sql/parts/query/common/queryInput.ts +++ b/src/sql/parts/query/common/queryInput.ts @@ -4,17 +4,35 @@ *--------------------------------------------------------------------------------------------*/ import { TPromise } from 'vs/base/common/winjs.base'; -import { EditorInput, EditorModel, ConfirmResult, EncodingMode, IEncodingSupport } from 'vs/workbench/common/editor'; -import { IConnectionManagementService, IConnectableInput, INewConnectionParams, RunQueryOnConnectionMode } from 'sql/parts/connection/common/connectionManagement'; -import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; -import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput'; -import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; +import { localize } from 'vs/nls'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import URI from 'vs/base/common/uri'; -import { ISelectionData, ExecutionPlanOptions } from 'sqlops'; +import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; +import { EditorInput, EditorModel, ConfirmResult, EncodingMode, IEncodingSupport } from 'vs/workbench/common/editor'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +import { IConnectionManagementService, IConnectableInput, INewConnectionParams, RunQueryOnConnectionMode } from 'sql/parts/connection/common/connectionManagement'; +import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput'; +import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; +import { ISelectionData, ExecutionPlanOptions } from 'sqlops'; + +const MAX_SIZE = 13; + +function trimTitle(title: string): string { + const length = title.length; + const diff = length - MAX_SIZE; + + if (Math.sign(diff) <= 0) { + return title; + } else { + const start = (length / 2) - (diff / 2); + return title.slice(0, start) + '...' + title.slice(start + diff, length); + } +} + /** * Input for the QueryEditor. This input is simply a wrapper around a QueryResultsInput for the QueryResultsEditor * and a UntitledEditorInput for the SQL File Editor. @@ -39,17 +57,16 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec private _currentEventCallbacks: IDisposable[]; constructor( - private _name: string, private _description: string, private _sql: UntitledEditorInput, private _results: QueryResultsInput, private _connectionProviderName: string, @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, @IQueryModelService private _queryModelService: IQueryModelService, - @IQueryEditorService private _queryEditorService: IQueryEditorService + @IQueryEditorService private _queryEditorService: IQueryEditorService, + @IConfigurationService private _configurationService: IConfigurationService ) { super(); - let self = this; this._updateTaskbar = new Emitter(); this._showQueryResultsEditor = new Emitter(); this._updateSelection = new Emitter(); @@ -66,36 +83,44 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec // Register callbacks for the Actions this._toDispose.push( this._queryModelService.onRunQueryStart(uri => { - if (self.uri === uri) { - self.onRunQuery(); + if (this.uri === uri) { + this.onRunQuery(); } }) ); this._toDispose.push( this._queryModelService.onRunQueryComplete(uri => { - if (self.uri === uri) { - self.onQueryComplete(); + if (this.uri === uri) { + this.onQueryComplete(); } }) ); } if (this._connectionManagementService) { - this._toDispose.push(self._connectionManagementService.onDisconnect(result => { - if (result.connectionUri === self.uri) { - self.onDisconnect(); + this._toDispose.push(this._connectionManagementService.onDisconnect(result => { + if (result.connectionUri === this.uri) { + this.onDisconnect(); } })); - if (self.uri) { + if (this.uri) { if (this._connectionProviderName) { - this._connectionManagementService.doChangeLanguageFlavor(self.uri, 'sql', this._connectionProviderName); + this._connectionManagementService.doChangeLanguageFlavor(this.uri, 'sql', this._connectionProviderName); } else { - this._connectionManagementService.ensureDefaultLanguageFlavor(self.uri); + this._connectionManagementService.ensureDefaultLanguageFlavor(this.uri); } } } + if (this._configurationService) { + this._configurationService.onDidChangeConfiguration(e => { + if (e.affectedKeys.includes('sql.showConnectionInfoInTitle')) { + this._onDidChangeLabel.fire(); + } + }); + } + this.onDisconnect(); this.onQueryComplete(); } @@ -136,11 +161,31 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec public resolve(refresh?: boolean): TPromise { return this._sql.resolve(); } public save(): TPromise { return this._sql.save(); } public isDirty(): boolean { return this._sql.isDirty(); } - public confirmSave(): TPromise { return this._sql.confirmSave(); } + public confirmSave(): TPromise { return this._sql.confirmSave(); } public getResource(): URI { return this._sql.getResource(); } public getEncoding(): string { return this._sql.getEncoding(); } public suggestFileName(): string { return this._sql.suggestFileName(); } - public getName(): string { return this._sql.getName(); } + + public getName(): string { + if (this._configurationService.getValue('sql.showConnectionInfoInTitle')) { + let profile = this._connectionManagementService.getConnectionProfile(this.uri); + let title = ''; + if (profile) { + if (profile.userName) { + title = `${profile.serverName}.${profile.databaseName} (${profile.userName})`; + } else { + title = `${profile.serverName}.${profile.databaseName} (${profile.authenticationType})`; + } + } else { + title = localize('disconnected', 'disconnected'); + } + + return this._sql.getName() + ` - ${trimTitle(title)}`; + } else { + return this._sql.getName(); + } + } + public get hasAssociatedFilePath(): boolean { return this._sql.hasAssociatedFilePath; } public setEncoding(encoding: string, mode: EncodingMode /* ignored, we only have Encode */): void { diff --git a/src/sql/parts/query/services/queryEditorService.ts b/src/sql/parts/query/services/queryEditorService.ts index 42298b0dcc..d8198ed497 100644 --- a/src/sql/parts/query/services/queryEditorService.ts +++ b/src/sql/parts/query/services/queryEditorService.ts @@ -93,7 +93,7 @@ export class QueryEditorService implements IQueryEditorService { //input.resolve().then(model => this.backupFileService.backupResource(resource, model.getValue(), model.getVersionId())).done(null, errors.onUnexpectedError); const queryResultsInput: QueryResultsInput = this._instantiationService.createInstance(QueryResultsInput, docUri.toString()); - let queryInput: QueryInput = this._instantiationService.createInstance(QueryInput, fileInput.getName(), '', fileInput, queryResultsInput, connectionProviderName); + let queryInput: QueryInput = this._instantiationService.createInstance(QueryInput, '', fileInput, queryResultsInput, connectionProviderName); this._editorService.openEditor(queryInput, { pinned: true }) .then((editor) => { @@ -113,7 +113,7 @@ export class QueryEditorService implements IQueryEditorService { const self = this; return new Promise((resolve, reject) => { let queryPlanInput: QueryPlanInput = self._instantiationService.createInstance(QueryPlanInput, xmlShowPlan, 'aaa', undefined); - self._editorService.openEditor(queryPlanInput, { pinned: true }, false); + self._editorService.openEditor(queryPlanInput, { pinned: true }, false); resolve(true); }); } @@ -222,8 +222,7 @@ export class QueryEditorService implements IQueryEditorService { } let uri: URI = QueryEditorService._getEditorChangeUri(editor.input, changingToSql); - if(uri.scheme === Schemas.untitled && (editor.input instanceof QueryInput || editor.input instanceof EditDataInput)) - { + if (uri.scheme === Schemas.untitled && (editor.input instanceof QueryInput || editor.input instanceof EditDataInput)) { QueryEditorService.notificationService.notify({ severity: Severity.Error, message: QueryEditorService.CHANGE_UNSUPPORTED_ERROR_MESSAGE @@ -258,15 +257,15 @@ export class QueryEditorService implements IQueryEditorService { } // Close the current editor - QueryEditorService.editorService.closeEditor(position, editor.input).then(() => { + QueryEditorService.editorService.closeEditor(position, editor.input).then(() => { // Reopen a new editor in the same position/index - QueryEditorService.editorService.openEditor(newEditorInput, options, position).then((editor) => { + QueryEditorService.editorService.openEditor(newEditorInput, options, position).then((editor) => { resolve(QueryEditorService._onEditorOpened(editor, uri.toString(), position, options.pinned)); }, - (error) => { - reject(error); - }); + (error) => { + reject(error); + }); }); }); } @@ -331,10 +330,10 @@ export class QueryEditorService implements IQueryEditorService { let newEditorInput: IEditorInput = undefined; if (changingToSql) { const queryResultsInput: QueryResultsInput = QueryEditorService.instantiationService.createInstance(QueryResultsInput, uri.toString()); - let queryInput: QueryInput = QueryEditorService.instantiationService.createInstance(QueryInput, input.getName(), '', input, queryResultsInput, undefined); + let queryInput: QueryInput = QueryEditorService.instantiationService.createInstance(QueryInput, '', input, queryResultsInput, undefined); newEditorInput = queryInput; } else { - let uriCopy: URI = URI.from( { scheme: uri.scheme, authority: uri.authority, path: uri.path, query: uri.query, fragment: uri.fragment } ); + let uriCopy: URI = URI.from({ scheme: uri.scheme, authority: uri.authority, path: uri.path, query: uri.query, fragment: uri.fragment }); newEditorInput = QueryEditorService.instantiationService.createInstance(FileEditorInput, uriCopy, undefined); } @@ -350,7 +349,7 @@ export class QueryEditorService implements IQueryEditorService { // It is assumed that if we got here, !changingToSql is logically equivalent to changingFromSql let changingFromSql = !changingToSql; if (input instanceof QueryInput && changingFromSql) { - let queryInput: QueryInput = input; + let queryInput: QueryInput = input; uriSource = queryInput.sql; } return getSupportedInputResource(uriSource); @@ -378,7 +377,7 @@ export class QueryEditorService implements IQueryEditorService { // Grab and returns the IModel that will be used to resolve the sqlLanguageModeCheck promise. let control = editor.getControl(); - let codeEditor: CodeEditor = control; + let codeEditor: CodeEditor = control; let newModel = codeEditor ? codeEditor.getModel() : undefined; return newModel; } diff --git a/src/sqltest/parts/query/editor/queryEditor.test.ts b/src/sqltest/parts/query/editor/queryEditor.test.ts index 28fa89e84e..098f06262e 100644 --- a/src/sqltest/parts/query/editor/queryEditor.test.ts +++ b/src/sqltest/parts/query/editor/queryEditor.test.ts @@ -114,14 +114,14 @@ suite('SQL QueryEditor Tests', () => { let uri: URI = URI.parse(filePath); let fileInput = new UntitledEditorInput(uri, false, '', '', '', instantiationService.object, undefined, undefined, undefined, undefined); let queryResultsInput: QueryResultsInput = new QueryResultsInput(uri.fsPath); - queryInput = new QueryInput('first', 'first', fileInput, queryResultsInput, undefined, undefined, undefined, undefined); + queryInput = new QueryInput('first', fileInput, queryResultsInput, undefined, undefined, undefined, undefined, undefined); // Create a QueryInput to compare to the previous one let filePath2 = 'someFile2.sql'; let uri2: URI = URI.parse(filePath2); let fileInput2 = new UntitledEditorInput(uri2, false, '', '', '', instantiationService.object, undefined, undefined, undefined, undefined); let queryResultsInput2: QueryResultsInput = new QueryResultsInput(uri2.fsPath); - queryInput2 = new QueryInput('second', 'second', fileInput2, queryResultsInput2, undefined, undefined, undefined, undefined); + queryInput2 = new QueryInput('second', fileInput2, queryResultsInput2, undefined, undefined, undefined, undefined, undefined); // Mock IMessageService notificationService = TypeMoq.Mock.ofType(TestNotificationService, TypeMoq.MockBehavior.Loose); @@ -346,13 +346,13 @@ suite('SQL QueryEditor Tests', () => { queryModelService = TypeMoq.Mock.ofType(QueryModelService, TypeMoq.MockBehavior.Loose, undefined, undefined); queryModelService.callBase = true; queryInput = new QueryInput( - 'testUri', '', fileInput, undefined, undefined, undefined, queryModelService.object, + undefined, undefined ); }); diff --git a/src/sqltest/workbench/common/taskUtilities.test.ts b/src/sqltest/workbench/common/taskUtilities.test.ts index f8f316de9e..584cb547e9 100644 --- a/src/sqltest/workbench/common/taskUtilities.test.ts +++ b/src/sqltest/workbench/common/taskUtilities.test.ts @@ -17,10 +17,10 @@ import URI from 'vs/base/common/uri'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { QueryInput } from 'sql/parts/query/common/queryInput'; -suite('TaskUtilities', function() { +suite('TaskUtilities', function () { test('getCurrentGlobalConnection returns the selected OE server if a server or one of its children is selected', () => { let connectionProfile = { databaseName: 'test_database', id: 'test_id', authenticationType: 'SQL Login', password: 'test_password', serverName: 'test_server', userName: 'test_user' } as IConnectionProfile; - let mockObjectExplorerService = TypeMoq.Mock.ofInstance({isFocused: () => undefined, getSelectedProfileAndDatabase: () => undefined } as IObjectExplorerService); + let mockObjectExplorerService = TypeMoq.Mock.ofInstance({ isFocused: () => undefined, getSelectedProfileAndDatabase: () => undefined } as IObjectExplorerService); let mockConnectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService); let mockWorkbenchEditorService = TypeMoq.Mock.ofType(WorkbenchEditorTestService); let expectedProfile = new ConnectionProfile(undefined, connectionProfile); @@ -38,7 +38,7 @@ suite('TaskUtilities', function() { test('getCurrentGlobalConnection returns the selected OE database if a database or its children is selected', () => { let connectionProfile = { databaseName: 'test_database', id: 'test_id', authenticationType: 'SQL Login', password: 'test_password', serverName: 'test_server', userName: 'test_user' } as IConnectionProfile; - let mockObjectExplorerService = TypeMoq.Mock.ofInstance({isFocused: () => undefined, getSelectedProfileAndDatabase: () => undefined } as IObjectExplorerService); + let mockObjectExplorerService = TypeMoq.Mock.ofInstance({ isFocused: () => undefined, getSelectedProfileAndDatabase: () => undefined } as IObjectExplorerService); let mockConnectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService); let mockWorkbenchEditorService = TypeMoq.Mock.ofType(WorkbenchEditorTestService); let serverProfile = new ConnectionProfile(undefined, connectionProfile); @@ -63,7 +63,7 @@ suite('TaskUtilities', function() { test('getCurrentGlobalConnection returns the connection from the active tab, if there is one and OE is not focused', () => { let connectionProfile = { databaseName: 'test_database', id: 'test_id', authenticationType: 'SQL Login', password: 'test_password', serverName: 'test_server', userName: 'test_user' } as IConnectionProfile; - let mockObjectExplorerService = TypeMoq.Mock.ofInstance({isFocused: () => undefined, getSelectedProfileAndDatabase: () => undefined } as IObjectExplorerService); + let mockObjectExplorerService = TypeMoq.Mock.ofInstance({ isFocused: () => undefined, getSelectedProfileAndDatabase: () => undefined } as IObjectExplorerService); let mockConnectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService); let mockWorkbenchEditorService = TypeMoq.Mock.ofType(WorkbenchEditorTestService); let oeProfile = new ConnectionProfile(undefined, connectionProfile); @@ -80,7 +80,7 @@ suite('TaskUtilities', function() { // Mock the workbench service to return the active tab connection let tabConnectionUri = 'test_uri'; let editorInput = new UntitledEditorInput(URI.parse(tabConnectionUri), false, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); - let queryInput = new QueryInput(undefined, undefined, editorInput, undefined, undefined, undefined, undefined, undefined); + let queryInput = new QueryInput(undefined, editorInput, undefined, undefined, undefined, undefined, undefined, undefined); mockWorkbenchEditorService.setup(x => x.getActiveEditorInput()).returns(() => queryInput); mockConnectionManagementService.setup(x => x.getConnectionProfile(tabConnectionUri)).returns(() => tabProfile); @@ -95,7 +95,7 @@ suite('TaskUtilities', function() { test('getCurrentGlobalConnection returns the connection from OE if there is no active tab, even if OE is not focused', () => { let connectionProfile = { databaseName: 'test_database', id: 'test_id', authenticationType: 'SQL Login', password: 'test_password', serverName: 'test_server', userName: 'test_user' } as IConnectionProfile; - let mockObjectExplorerService = TypeMoq.Mock.ofInstance({isFocused: () => undefined, getSelectedProfileAndDatabase: () => undefined } as IObjectExplorerService); + let mockObjectExplorerService = TypeMoq.Mock.ofInstance({ isFocused: () => undefined, getSelectedProfileAndDatabase: () => undefined } as IObjectExplorerService); let mockConnectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService); let mockWorkbenchEditorService = TypeMoq.Mock.ofType(WorkbenchEditorTestService); let oeProfile = new ConnectionProfile(undefined, connectionProfile);