From bedeb5a02d808fd4fd81bad6a1ac940f50564cb5 Mon Sep 17 00:00:00 2001 From: Lewis Sanchez Date: Fri, 25 Aug 2023 23:09:40 -0700 Subject: [PATCH 01/13] Send server contextualization to Copilot extension --- extensions/mssql/src/contracts.ts | 4 ++ extensions/mssql/src/features.ts | 7 +++- src/sql/azdata.proposed.d.ts | 24 +++++++++++- .../api/browser/mainThreadDataProtocol.ts | 4 ++ .../api/common/extHostDataProtocol.ts | 7 ++++ .../api/common/sqlExtHost.api.impl.ts | 4 ++ .../api/common/sqlExtHost.protocol.ts | 5 +++ .../contextualization/common/interfaces.ts | 4 ++ .../common/serverContextualizationService.ts | 39 ++++++++++++++++++- 9 files changed, 94 insertions(+), 4 deletions(-) diff --git a/extensions/mssql/src/contracts.ts b/extensions/mssql/src/contracts.ts index 99cc33d9c1da..867c5fa3d7fe 100644 --- a/extensions/mssql/src/contracts.ts +++ b/extensions/mssql/src/contracts.ts @@ -1574,6 +1574,10 @@ export namespace GetServerContextualizationRequest { export const type = new RequestType('metadata/getServerContext'); } +export namespace GenerateServerContextualizationCompleteNotification { + export const type = new NotificationType('metadata/generateServerContextComplete'); +} + // ------------------------------- < Database Server Contextualization API > ------------------------------------ // ------------------------------- < Object Management > ------------------------------------ diff --git a/extensions/mssql/src/features.ts b/extensions/mssql/src/features.ts index 9d79b97e1b4d..42b2a05c34af 100644 --- a/extensions/mssql/src/features.ts +++ b/extensions/mssql/src/features.ts @@ -1352,10 +1352,15 @@ export class ServerContextualizationServiceFeature extends SqlOpsFeature any): void => { + client.onNotification(contracts.GenerateServerContextualizationCompleteNotification.type, handler); + }; + return azdata.dataprotocol.registerServerContextualizationProvider({ providerId: client.providerId, generateServerContextualization: generateServerContextualization, - getServerContextualization: getServerContextualization + getServerContextualization: getServerContextualization, + registerOnGenerateContextualizationComplete: registerOnGenerateContextualizationComplete }); } } diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts index 4f4928ede4ba..0b4fd9cd0a97 100644 --- a/src/sql/azdata.proposed.d.ts +++ b/src/sql/azdata.proposed.d.ts @@ -901,7 +901,7 @@ declare module 'azdata' { * Copilot for improved suggestions. * @param provider The provider to register */ - export function registerServerContextualizationProvider(provider: contextualization.ServerContextualizationProvider): vscode.Disposable + export function registerServerContextualizationProvider(provider: contextualization.ServerContextualizationProvider): vscode.Disposable; } export namespace designers { @@ -1790,6 +1790,22 @@ declare module 'azdata' { context: string[]; } + export interface GenerateServerContextualizationCompleteParams { + /** + * The URI identifying the owner of the connection + */ + ownerUri: string; + /** + * Indicates if generating context completed successfully + */ + completedGeneratingContext: boolean; + /** + * Holds an error message, if errors were encountered while + * generating context + */ + errorMessage?: string | undefined; + } + export interface ServerContextualizationProvider extends DataProvider { /** * Generates server context. @@ -1802,6 +1818,12 @@ declare module 'azdata' { * @param ownerUri The URI of the connection to get context for. */ getServerContextualization(ownerUri: string): Thenable; + + /** + * Registers a handler for the GenerateServerContextualizationComplete notification. + * @param handler The handler to be registered + */ + registerOnGenerateContextualizationComplete(handler: (contextualizationCompleteParams: GenerateServerContextualizationCompleteParams) => any): void; } } diff --git a/src/sql/workbench/api/browser/mainThreadDataProtocol.ts b/src/sql/workbench/api/browser/mainThreadDataProtocol.ts index d0de82ca4fff..915962ea27ea 100644 --- a/src/sql/workbench/api/browser/mainThreadDataProtocol.ts +++ b/src/sql/workbench/api/browser/mainThreadDataProtocol.ts @@ -581,6 +581,10 @@ export class MainThreadDataProtocol extends Disposable implements MainThreadData }); } + public $onGenerateServerContextualizationComplete(handle: number, serverContextualizationCompleteParams: azdata.contextualization.GenerateServerContextualizationCompleteParams) { + this._serverContextualizationService.onGenerateServerContextualizationComplete(handle, serverContextualizationCompleteParams); + } + // Connection Management handlers public $onConnectionComplete(handle: number, connectionInfoSummary: azdata.ConnectionInfoSummary): void { this._connectionManagementService.onConnectionComplete(handle, connectionInfoSummary); diff --git a/src/sql/workbench/api/common/extHostDataProtocol.ts b/src/sql/workbench/api/common/extHostDataProtocol.ts index 98a13bf62029..ba2987aca070 100644 --- a/src/sql/workbench/api/common/extHostDataProtocol.ts +++ b/src/sql/workbench/api/common/extHostDataProtocol.ts @@ -216,6 +216,13 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape { return rt; } + override $onGenerateServerContextualizationComplete(handle: number, generateContextualizationCompleteParams: azdata.contextualization.GenerateServerContextualizationCompleteParams): void { + if (this.uriTransformer) { + generateContextualizationCompleteParams.ownerUri = this._getTransformedUri(generateContextualizationCompleteParams.ownerUri, this.uriTransformer.transformOutgoing); + } + this._proxy.$onGenerateServerContextualizationComplete(handle, generateContextualizationCompleteParams); + } + // Capabilities Discovery handlers override $getServerCapabilities(handle: number, client: azdata.DataProtocolClientCapabilities): Thenable { return this._resolveProvider(handle).getServerCapabilities(client); diff --git a/src/sql/workbench/api/common/sqlExtHost.api.impl.ts b/src/sql/workbench/api/common/sqlExtHost.api.impl.ts index e88ee30d44f9..206351aebb2b 100644 --- a/src/sql/workbench/api/common/sqlExtHost.api.impl.ts +++ b/src/sql/workbench/api/common/sqlExtHost.api.impl.ts @@ -409,6 +409,10 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp }; let registerServerContextualizationProvider = (provider: azdata.contextualization.ServerContextualizationProvider): vscode.Disposable => { + provider.registerOnGenerateContextualizationComplete((contextualizationCompleteParams: azdata.contextualization.GenerateServerContextualizationCompleteParams) => { + extHostDataProvider.$onGenerateServerContextualizationComplete(provider.handle, contextualizationCompleteParams); + }); + return extHostDataProvider.$registerServerContextualizationProvider(provider); }; diff --git a/src/sql/workbench/api/common/sqlExtHost.protocol.ts b/src/sql/workbench/api/common/sqlExtHost.protocol.ts index 6e700cfeda4c..dd302ccba392 100644 --- a/src/sql/workbench/api/common/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/common/sqlExtHost.protocol.ts @@ -603,6 +603,10 @@ export abstract class ExtHostDataProtocolShape { * Gets server context. */ $getServerContextualization(handle: number, ownerUri: string): Thenable { throw ni(); } + /** + * Handles onGenerateServerContextualizationComplete events. + */ + $onGenerateServerContextualizationComplete(handle: number, generateContextualizationCompleteParams: azdata.contextualization.GenerateServerContextualizationCompleteParams): void { throw ni(); } } /** @@ -696,6 +700,7 @@ export interface MainThreadDataProtocolShape extends IDisposable { $registerTableDesignerProvider(providerId: string, handle: number): Promise; $registerExecutionPlanProvider(providerId: string, handle: number): void; $registerServerContextualizationProvider(providerId: string, handle: number): void; + $onGenerateServerContextualizationComplete(handle: number, serverContextualizationCompleteParams: azdata.contextualization.GenerateServerContextualizationCompleteParams): void; $unregisterProvider(handle: number): Promise; $onConnectionComplete(handle: number, connectionInfoSummary: azdata.ConnectionInfoSummary): void; $onIntelliSenseCacheComplete(handle: number, connectionUri: string): void; diff --git a/src/sql/workbench/services/contextualization/common/interfaces.ts b/src/sql/workbench/services/contextualization/common/interfaces.ts index b53dc15f1477..dd38fd269f2b 100644 --- a/src/sql/workbench/services/contextualization/common/interfaces.ts +++ b/src/sql/workbench/services/contextualization/common/interfaces.ts @@ -40,4 +40,8 @@ export interface IServerContextualizationService { * @param ownerUri The URI of the connection to get context for. */ getServerContextualization(ownerUri: string): Promise; + /** + * Handles onGenerateServerContextualizationComplete events + */ + onGenerateServerContextualizationComplete(handle: number, serverContextualizationCompleteParams: azdata.contextualization.GenerateServerContextualizationCompleteParams): void } diff --git a/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts b/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts index a9980d4b756c..864b44538116 100644 --- a/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts +++ b/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts @@ -7,9 +7,12 @@ import * as azdata from 'azdata'; import { invalidProvider } from 'sql/base/common/errors'; import { IConnectionManagementService, IConnectionParams } from 'sql/platform/connection/common/connectionManagement'; import { IQueryEditorConfiguration } from 'sql/platform/query/common/query'; +import { QueryEditorInput } from 'sql/workbench/common/editor/query/queryEditorInput'; import { IServerContextualizationService } from 'sql/workbench/services/contextualization/common/interfaces'; import { Disposable } from 'vs/base/common/lifecycle'; +import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; export class ServerContextualizationService extends Disposable implements IServerContextualizationService { @@ -19,7 +22,9 @@ export class ServerContextualizationService extends Disposable implements IServe constructor( @IConnectionManagementService private readonly _connectionManagementService: IConnectionManagementService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IExtensionService private readonly _extensionService: IExtensionService + @IExtensionService private readonly _extensionService: IExtensionService, + @ICommandService private readonly _commandService: ICommandService, + @IEditorService private readonly _editorService: IEditorService, ) { super(); @@ -28,7 +33,15 @@ export class ServerContextualizationService extends Disposable implements IServe if (copilotExt && this._configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled) { const ownerUri = e.connectionUri; - await this.generateServerContextualization(ownerUri); + this.generateServerContextualization(ownerUri); + } + })); + + this._register(this._editorService.onDidActiveEditorChange(async () => { + const queryEditorInput = this._editorService.activeEditorPane.input as QueryEditorInput; + const uri = queryEditorInput?.uri; + if (uri) { + await this.sendServerContextualizationToCopilot(uri); } })); } @@ -91,4 +104,26 @@ export class ServerContextualizationService extends Disposable implements IServe }); } } + + public async onGenerateServerContextualizationComplete(handle: number, serverContextualizationCompleteParams: azdata.contextualization.GenerateServerContextualizationCompleteParams): Promise { + if (serverContextualizationCompleteParams.completedGeneratingContext) { + await this.sendServerContextualizationToCopilot(serverContextualizationCompleteParams.ownerUri); + } + } + + private async sendServerContextualizationToCopilot(ownerUri: string): Promise { + if (this._configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled) { + const result = await this.getServerContextualization(ownerUri); + + // Compressing scripts down to just create statements. + const createsOnly = result.context.filter(c => c.includes('CREATE')); + createsOnly.forEach((c, index, myArray) => myArray[index] = myArray[index].replace('\t', '')); // LEWISSANCHEZ TODO: Remove tabs completely as scripts don't need any formatting to be understood by Copilot. + const conjoinedCreateScript = createsOnly.join('\n'); + + // LEWISSANCHEZ TODO: Find way to set context on untitled query editor files. Need to save first for Copilot status to say "Has Context" + await this._commandService.executeCommand('github.copilot.provideContext', '**/*.sql', { + value: conjoinedCreateScript + }); + } + } } From 906b1c81c644dda943430985a419e8e6cf1eb274 Mon Sep 17 00:00:00 2001 From: Lewis Sanchez Date: Mon, 28 Aug 2023 10:49:16 -0700 Subject: [PATCH 02/13] Keep context in editor input --- src/sql/azdata.proposed.d.ts | 4 +-- .../common/editor/query/queryEditorInput.ts | 25 ++++---------- .../common/serverContextualizationService.ts | 34 ++++++++++++------- 3 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts index 0b4fd9cd0a97..4d51bd4c471f 100644 --- a/src/sql/azdata.proposed.d.ts +++ b/src/sql/azdata.proposed.d.ts @@ -1796,9 +1796,9 @@ declare module 'azdata' { */ ownerUri: string; /** - * Indicates if generating context completed successfully + * An array containing the generated server context. */ - completedGeneratingContext: boolean; + context: string[] | undefined; /** * Holds an error message, if errors were encountered while * generating context diff --git a/src/sql/workbench/common/editor/query/queryEditorInput.ts b/src/sql/workbench/common/editor/query/queryEditorInput.ts index eff674a0ec4e..7c358c440d03 100644 --- a/src/sql/workbench/common/editor/query/queryEditorInput.ts +++ b/src/sql/workbench/common/editor/query/queryEditorInput.ts @@ -144,7 +144,7 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab private _state = this._register(new QueryEditorState()); public get state(): QueryEditorState { return this._state; } - private _serverContext: string[]; + private _serverContext: string[] | undefined; constructor( private _description: string | undefined, @@ -241,25 +241,12 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab public override isDirty(): boolean { return this._text.isDirty(); } public get resource(): URI { return this._text.resource; } - public async getServerContext(): Promise { - const copilotExt = await this.extensionService.getExtension('github.copilot'); - - if (copilotExt && this.configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled) { - if (!this._serverContext) { - const result = await this.serverContextualizationService.getServerContextualization(this.uri); - // TODO lewissanchez - Remove this from here once Copilot starts pulling context. That isn't implemented yet, so - // getting scripts this way for now. - this._serverContext = result.context; + public set serverContext(context: string[]) { + this._serverContext = context; + } - return this._serverContext; - } - else { - return this._serverContext; - } - } - else { - return Promise.resolve([]); - } + public get serverContext(): string[] | undefined { + return this._serverContext; } public override getName(longForm?: boolean): string { diff --git a/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts b/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts index 864b44538116..8c1b6a775ebc 100644 --- a/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts +++ b/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts @@ -38,10 +38,10 @@ export class ServerContextualizationService extends Disposable implements IServe })); this._register(this._editorService.onDidActiveEditorChange(async () => { - const queryEditorInput = this._editorService.activeEditorPane.input as QueryEditorInput; - const uri = queryEditorInput?.uri; + const activeQueryEditorInput = this._editorService.activeEditorPane.input as QueryEditorInput; + const uri = activeQueryEditorInput?.uri; if (uri) { - await this.sendServerContextualizationToCopilot(uri); + await this.sendServerContextualizationToCopilot(uri, activeQueryEditorInput.serverContext); } })); } @@ -106,23 +106,33 @@ export class ServerContextualizationService extends Disposable implements IServe } public async onGenerateServerContextualizationComplete(handle: number, serverContextualizationCompleteParams: azdata.contextualization.GenerateServerContextualizationCompleteParams): Promise { - if (serverContextualizationCompleteParams.completedGeneratingContext) { - await this.sendServerContextualizationToCopilot(serverContextualizationCompleteParams.ownerUri); + if (serverContextualizationCompleteParams.context) { + const activeQueryEditorInput = this._editorService.activeEditorPane.input as QueryEditorInput; + if (activeQueryEditorInput) { + activeQueryEditorInput.serverContext = serverContextualizationCompleteParams.context; + } + + await this.sendServerContextualizationToCopilot(serverContextualizationCompleteParams.ownerUri, serverContextualizationCompleteParams.context); } } - private async sendServerContextualizationToCopilot(ownerUri: string): Promise { + private async sendServerContextualizationToCopilot(ownerUri: string, serverContext: string[] | undefined): Promise { if (this._configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled) { - const result = await this.getServerContextualization(ownerUri); + var flattenedServerContext = ''; - // Compressing scripts down to just create statements. - const createsOnly = result.context.filter(c => c.includes('CREATE')); - createsOnly.forEach((c, index, myArray) => myArray[index] = myArray[index].replace('\t', '')); // LEWISSANCHEZ TODO: Remove tabs completely as scripts don't need any formatting to be understood by Copilot. - const conjoinedCreateScript = createsOnly.join('\n'); + if (serverContext) { + // Flattening scripts from string[] to just a string + flattenedServerContext = serverContext.filter(c => c.includes('CREATE')).join('\n'); + } + else { + const result = await this.getServerContextualization(ownerUri); + // Flattening scripts from string[] to just a string + flattenedServerContext = result.context.filter(c => c.includes('CREATE')).join('\n'); + } // LEWISSANCHEZ TODO: Find way to set context on untitled query editor files. Need to save first for Copilot status to say "Has Context" await this._commandService.executeCommand('github.copilot.provideContext', '**/*.sql', { - value: conjoinedCreateScript + value: flattenedServerContext // value is expecting a string and not a string[] }); } } From c85563879475e64defc2a00aa849203560878ee3 Mon Sep 17 00:00:00 2001 From: Lewis Sanchez Date: Mon, 28 Aug 2023 10:54:17 -0700 Subject: [PATCH 03/13] Remove unnecessary server context and extension service --- .../browser/editor/query/fileQueryEditorInput.ts | 8 ++------ .../browser/editor/query/untitledQueryEditorInput.ts | 8 ++------ src/sql/workbench/common/editor/query/queryEditorInput.ts | 6 +----- .../commandLine/test/electron-browser/commandLine.test.ts | 2 +- .../contrib/query/test/browser/queryEditor.test.ts | 2 -- 5 files changed, 6 insertions(+), 20 deletions(-) diff --git a/src/sql/workbench/browser/editor/query/fileQueryEditorInput.ts b/src/sql/workbench/browser/editor/query/fileQueryEditorInput.ts index aeb05d5fe150..380a758c010d 100644 --- a/src/sql/workbench/browser/editor/query/fileQueryEditorInput.ts +++ b/src/sql/workbench/browser/editor/query/fileQueryEditorInput.ts @@ -18,8 +18,6 @@ import { FILE_QUERY_EDITOR_TYPEID } from 'sql/workbench/common/constants'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; -import { IServerContextualizationService } from 'sql/workbench/services/contextualization/common/interfaces'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; export class FileQueryEditorInput extends QueryEditorInput { @@ -32,11 +30,9 @@ export class FileQueryEditorInput extends QueryEditorInput { @IConnectionManagementService connectionManagementService: IConnectionManagementService, @IQueryModelService queryModelService: IQueryModelService, @IConfigurationService configurationService: IConfigurationService, - @IInstantiationService instantiationService: IInstantiationService, - @IServerContextualizationService serverContextualizationService: IServerContextualizationService, - @IExtensionService extensionService: IExtensionService + @IInstantiationService instantiationService: IInstantiationService ) { - super(description, text, results, connectionManagementService, queryModelService, configurationService, instantiationService, serverContextualizationService, extensionService); + super(description, text, results, connectionManagementService, queryModelService, configurationService, instantiationService); } public override resolve(): Promise { diff --git a/src/sql/workbench/browser/editor/query/untitledQueryEditorInput.ts b/src/sql/workbench/browser/editor/query/untitledQueryEditorInput.ts index 3d6f80236081..ad0bfa39c0db 100644 --- a/src/sql/workbench/browser/editor/query/untitledQueryEditorInput.ts +++ b/src/sql/workbench/browser/editor/query/untitledQueryEditorInput.ts @@ -22,8 +22,6 @@ import { IUntitledQueryEditorInput } from 'sql/workbench/common/editor/query/unt import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService'; import { Uri } from 'vscode'; import { ILogService } from 'vs/platform/log/common/log'; -import { IServerContextualizationService } from 'sql/workbench/services/contextualization/common/interfaces'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; export class UntitledQueryEditorInput extends QueryEditorInput implements IUntitledQueryEditorInput { @@ -38,11 +36,9 @@ export class UntitledQueryEditorInput extends QueryEditorInput implements IUntit @IConfigurationService configurationService: IConfigurationService, @IInstantiationService instantiationService: IInstantiationService, @ILogService private readonly logService: ILogService, - @IEditorResolverService private readonly editorResolverService: IEditorResolverService, - @IServerContextualizationService serverContextualizationService: IServerContextualizationService, - @IExtensionService extensionService: IExtensionService + @IEditorResolverService private readonly editorResolverService: IEditorResolverService ) { - super(description, text, results, connectionManagementService, queryModelService, configurationService, instantiationService, serverContextualizationService, extensionService); + super(description, text, results, connectionManagementService, queryModelService, configurationService, instantiationService); // Set the mode explicitely to stop the auto language detection service from changing the mode unexpectedly. // the auto language detection service won't do the language change only if the mode is explicitely set. // if the mode (e.g. kusto, sql) do not exist for whatever reason, we will default it to sql. diff --git a/src/sql/workbench/common/editor/query/queryEditorInput.ts b/src/sql/workbench/common/editor/query/queryEditorInput.ts index 7c358c440d03..d0ba027ae458 100644 --- a/src/sql/workbench/common/editor/query/queryEditorInput.ts +++ b/src/sql/workbench/common/editor/query/queryEditorInput.ts @@ -20,8 +20,6 @@ import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/text import { IQueryEditorConfiguration } from 'sql/platform/query/common/query'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IServerContextualizationService } from 'sql/workbench/services/contextualization/common/interfaces'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; const MAX_SIZE = 13; @@ -153,9 +151,7 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab @IConnectionManagementService private readonly connectionManagementService: IConnectionManagementService, @IQueryModelService private readonly queryModelService: IQueryModelService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IInstantiationService protected readonly instantiationService: IInstantiationService, - @IServerContextualizationService private readonly serverContextualizationService: IServerContextualizationService, - @IExtensionService private readonly extensionService: IExtensionService + @IInstantiationService protected readonly instantiationService: IInstantiationService ) { super(); diff --git a/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts b/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts index cce62f977c3f..a07bd46bddc3 100644 --- a/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts +++ b/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts @@ -467,7 +467,7 @@ suite('commandLineService tests', () => { let uri = URI.file(args._[0]); const workbenchinstantiationService = workbenchInstantiationService(); const editorInput = workbenchinstantiationService.createInstance(FileEditorInput, uri, undefined, undefined, undefined, undefined, undefined, undefined); - const queryInput = new FileQueryEditorInput(undefined, editorInput, undefined, connectionManagementService.object, querymodelService.object, configurationService.object, workbenchinstantiationService, undefined, undefined); + const queryInput = new FileQueryEditorInput(undefined, editorInput, undefined, connectionManagementService.object, querymodelService.object, configurationService.object, workbenchinstantiationService); queryInput.state.connected = true; const editorService: TypeMoq.Mock = TypeMoq.Mock.ofType(TestEditorService, TypeMoq.MockBehavior.Strict); editorService.setup(e => e.editors).returns(() => [queryInput]); diff --git a/src/sql/workbench/contrib/query/test/browser/queryEditor.test.ts b/src/sql/workbench/contrib/query/test/browser/queryEditor.test.ts index 56fbef5cebd2..1078b2e7ac29 100644 --- a/src/sql/workbench/contrib/query/test/browser/queryEditor.test.ts +++ b/src/sql/workbench/contrib/query/test/browser/queryEditor.test.ts @@ -316,8 +316,6 @@ suite('SQL QueryEditor Tests', () => { configurationService.object, testinstantiationService, undefined, - undefined, - undefined, undefined ); }); From bd3b9c848a95fcecd1c84ae6312e393da1927f14 Mon Sep 17 00:00:00 2001 From: Lewis Sanchez Date: Mon, 28 Aug 2023 14:53:56 -0700 Subject: [PATCH 04/13] Send context when connecting from open editor --- .../common/serverContextualizationService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts b/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts index 8c1b6a775ebc..bb7443968977 100644 --- a/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts +++ b/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts @@ -111,9 +111,9 @@ export class ServerContextualizationService extends Disposable implements IServe if (activeQueryEditorInput) { activeQueryEditorInput.serverContext = serverContextualizationCompleteParams.context; } - - await this.sendServerContextualizationToCopilot(serverContextualizationCompleteParams.ownerUri, serverContextualizationCompleteParams.context); } + + await this.sendServerContextualizationToCopilot(serverContextualizationCompleteParams.ownerUri, serverContextualizationCompleteParams.context); } private async sendServerContextualizationToCopilot(ownerUri: string, serverContext: string[] | undefined): Promise { From cf6c99b1e5193525fca989a072e0a6a6d55e54fb Mon Sep 17 00:00:00 2001 From: Lewis Sanchez Date: Tue, 29 Aug 2023 17:00:50 -0700 Subject: [PATCH 05/13] Remove contextualization complete event --- extensions/mssql/src/contracts.ts | 4 ---- extensions/mssql/src/features.ts | 7 +----- src/sql/azdata.proposed.d.ts | 22 ------------------- .../api/browser/mainThreadDataProtocol.ts | 4 ---- .../api/common/extHostDataProtocol.ts | 7 ------ .../api/common/sqlExtHost.api.impl.ts | 4 ---- .../api/common/sqlExtHost.protocol.ts | 5 ----- .../contextualization/common/interfaces.ts | 4 ---- .../common/serverContextualizationService.ts | 11 ---------- 9 files changed, 1 insertion(+), 67 deletions(-) diff --git a/extensions/mssql/src/contracts.ts b/extensions/mssql/src/contracts.ts index 867c5fa3d7fe..99cc33d9c1da 100644 --- a/extensions/mssql/src/contracts.ts +++ b/extensions/mssql/src/contracts.ts @@ -1574,10 +1574,6 @@ export namespace GetServerContextualizationRequest { export const type = new RequestType('metadata/getServerContext'); } -export namespace GenerateServerContextualizationCompleteNotification { - export const type = new NotificationType('metadata/generateServerContextComplete'); -} - // ------------------------------- < Database Server Contextualization API > ------------------------------------ // ------------------------------- < Object Management > ------------------------------------ diff --git a/extensions/mssql/src/features.ts b/extensions/mssql/src/features.ts index 42b2a05c34af..9d79b97e1b4d 100644 --- a/extensions/mssql/src/features.ts +++ b/extensions/mssql/src/features.ts @@ -1352,15 +1352,10 @@ export class ServerContextualizationServiceFeature extends SqlOpsFeature any): void => { - client.onNotification(contracts.GenerateServerContextualizationCompleteNotification.type, handler); - }; - return azdata.dataprotocol.registerServerContextualizationProvider({ providerId: client.providerId, generateServerContextualization: generateServerContextualization, - getServerContextualization: getServerContextualization, - registerOnGenerateContextualizationComplete: registerOnGenerateContextualizationComplete + getServerContextualization: getServerContextualization }); } } diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts index 4d51bd4c471f..9961685bed5c 100644 --- a/src/sql/azdata.proposed.d.ts +++ b/src/sql/azdata.proposed.d.ts @@ -1790,22 +1790,6 @@ declare module 'azdata' { context: string[]; } - export interface GenerateServerContextualizationCompleteParams { - /** - * The URI identifying the owner of the connection - */ - ownerUri: string; - /** - * An array containing the generated server context. - */ - context: string[] | undefined; - /** - * Holds an error message, if errors were encountered while - * generating context - */ - errorMessage?: string | undefined; - } - export interface ServerContextualizationProvider extends DataProvider { /** * Generates server context. @@ -1818,12 +1802,6 @@ declare module 'azdata' { * @param ownerUri The URI of the connection to get context for. */ getServerContextualization(ownerUri: string): Thenable; - - /** - * Registers a handler for the GenerateServerContextualizationComplete notification. - * @param handler The handler to be registered - */ - registerOnGenerateContextualizationComplete(handler: (contextualizationCompleteParams: GenerateServerContextualizationCompleteParams) => any): void; } } diff --git a/src/sql/workbench/api/browser/mainThreadDataProtocol.ts b/src/sql/workbench/api/browser/mainThreadDataProtocol.ts index 915962ea27ea..d0de82ca4fff 100644 --- a/src/sql/workbench/api/browser/mainThreadDataProtocol.ts +++ b/src/sql/workbench/api/browser/mainThreadDataProtocol.ts @@ -581,10 +581,6 @@ export class MainThreadDataProtocol extends Disposable implements MainThreadData }); } - public $onGenerateServerContextualizationComplete(handle: number, serverContextualizationCompleteParams: azdata.contextualization.GenerateServerContextualizationCompleteParams) { - this._serverContextualizationService.onGenerateServerContextualizationComplete(handle, serverContextualizationCompleteParams); - } - // Connection Management handlers public $onConnectionComplete(handle: number, connectionInfoSummary: azdata.ConnectionInfoSummary): void { this._connectionManagementService.onConnectionComplete(handle, connectionInfoSummary); diff --git a/src/sql/workbench/api/common/extHostDataProtocol.ts b/src/sql/workbench/api/common/extHostDataProtocol.ts index ba2987aca070..98a13bf62029 100644 --- a/src/sql/workbench/api/common/extHostDataProtocol.ts +++ b/src/sql/workbench/api/common/extHostDataProtocol.ts @@ -216,13 +216,6 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape { return rt; } - override $onGenerateServerContextualizationComplete(handle: number, generateContextualizationCompleteParams: azdata.contextualization.GenerateServerContextualizationCompleteParams): void { - if (this.uriTransformer) { - generateContextualizationCompleteParams.ownerUri = this._getTransformedUri(generateContextualizationCompleteParams.ownerUri, this.uriTransformer.transformOutgoing); - } - this._proxy.$onGenerateServerContextualizationComplete(handle, generateContextualizationCompleteParams); - } - // Capabilities Discovery handlers override $getServerCapabilities(handle: number, client: azdata.DataProtocolClientCapabilities): Thenable { return this._resolveProvider(handle).getServerCapabilities(client); diff --git a/src/sql/workbench/api/common/sqlExtHost.api.impl.ts b/src/sql/workbench/api/common/sqlExtHost.api.impl.ts index 206351aebb2b..e88ee30d44f9 100644 --- a/src/sql/workbench/api/common/sqlExtHost.api.impl.ts +++ b/src/sql/workbench/api/common/sqlExtHost.api.impl.ts @@ -409,10 +409,6 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp }; let registerServerContextualizationProvider = (provider: azdata.contextualization.ServerContextualizationProvider): vscode.Disposable => { - provider.registerOnGenerateContextualizationComplete((contextualizationCompleteParams: azdata.contextualization.GenerateServerContextualizationCompleteParams) => { - extHostDataProvider.$onGenerateServerContextualizationComplete(provider.handle, contextualizationCompleteParams); - }); - return extHostDataProvider.$registerServerContextualizationProvider(provider); }; diff --git a/src/sql/workbench/api/common/sqlExtHost.protocol.ts b/src/sql/workbench/api/common/sqlExtHost.protocol.ts index dd302ccba392..6e700cfeda4c 100644 --- a/src/sql/workbench/api/common/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/common/sqlExtHost.protocol.ts @@ -603,10 +603,6 @@ export abstract class ExtHostDataProtocolShape { * Gets server context. */ $getServerContextualization(handle: number, ownerUri: string): Thenable { throw ni(); } - /** - * Handles onGenerateServerContextualizationComplete events. - */ - $onGenerateServerContextualizationComplete(handle: number, generateContextualizationCompleteParams: azdata.contextualization.GenerateServerContextualizationCompleteParams): void { throw ni(); } } /** @@ -700,7 +696,6 @@ export interface MainThreadDataProtocolShape extends IDisposable { $registerTableDesignerProvider(providerId: string, handle: number): Promise; $registerExecutionPlanProvider(providerId: string, handle: number): void; $registerServerContextualizationProvider(providerId: string, handle: number): void; - $onGenerateServerContextualizationComplete(handle: number, serverContextualizationCompleteParams: azdata.contextualization.GenerateServerContextualizationCompleteParams): void; $unregisterProvider(handle: number): Promise; $onConnectionComplete(handle: number, connectionInfoSummary: azdata.ConnectionInfoSummary): void; $onIntelliSenseCacheComplete(handle: number, connectionUri: string): void; diff --git a/src/sql/workbench/services/contextualization/common/interfaces.ts b/src/sql/workbench/services/contextualization/common/interfaces.ts index dd38fd269f2b..b53dc15f1477 100644 --- a/src/sql/workbench/services/contextualization/common/interfaces.ts +++ b/src/sql/workbench/services/contextualization/common/interfaces.ts @@ -40,8 +40,4 @@ export interface IServerContextualizationService { * @param ownerUri The URI of the connection to get context for. */ getServerContextualization(ownerUri: string): Promise; - /** - * Handles onGenerateServerContextualizationComplete events - */ - onGenerateServerContextualizationComplete(handle: number, serverContextualizationCompleteParams: azdata.contextualization.GenerateServerContextualizationCompleteParams): void } diff --git a/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts b/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts index bb7443968977..424f130d8634 100644 --- a/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts +++ b/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts @@ -105,17 +105,6 @@ export class ServerContextualizationService extends Disposable implements IServe } } - public async onGenerateServerContextualizationComplete(handle: number, serverContextualizationCompleteParams: azdata.contextualization.GenerateServerContextualizationCompleteParams): Promise { - if (serverContextualizationCompleteParams.context) { - const activeQueryEditorInput = this._editorService.activeEditorPane.input as QueryEditorInput; - if (activeQueryEditorInput) { - activeQueryEditorInput.serverContext = serverContextualizationCompleteParams.context; - } - } - - await this.sendServerContextualizationToCopilot(serverContextualizationCompleteParams.ownerUri, serverContextualizationCompleteParams.context); - } - private async sendServerContextualizationToCopilot(ownerUri: string, serverContext: string[] | undefined): Promise { if (this._configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled) { var flattenedServerContext = ''; From b75b287adeaac8143333e12792b8a311a7a3a218 Mon Sep 17 00:00:00 2001 From: Lewis Sanchez Date: Wed, 30 Aug 2023 16:13:11 -0700 Subject: [PATCH 06/13] Contextualize editor after connection success --- extensions/mssql/src/contracts.ts | 4 +- extensions/mssql/src/features.ts | 12 +++- src/sql/azdata.proposed.d.ts | 13 +++- .../api/common/extHostDataProtocol.ts | 4 +- .../api/common/sqlExtHost.protocol.ts | 2 +- .../editor/query/fileQueryEditorInput.ts | 6 +- .../editor/query/untitledQueryEditorInput.ts | 6 +- .../common/editor/query/queryEditorInput.ts | 32 ++++++++-- .../test/electron-browser/commandLine.test.ts | 2 +- .../query/test/browser/queryEditor.test.ts | 1 + .../contextualization/common/interfaces.ts | 8 ++- .../common/serverContextualizationService.ts | 64 +++++++------------ 12 files changed, 91 insertions(+), 63 deletions(-) diff --git a/extensions/mssql/src/contracts.ts b/extensions/mssql/src/contracts.ts index 99cc33d9c1da..50fa6d7ff47c 100644 --- a/extensions/mssql/src/contracts.ts +++ b/extensions/mssql/src/contracts.ts @@ -1566,8 +1566,8 @@ export interface ServerContextualizationParams { ownerUri: string; } -export namespace GenerateServerContextualizationNotification { - export const type = new NotificationType('metadata/generateServerContext'); +export namespace GenerateServerContextualizationRequest { + export const type = new RequestType('metadata/generateServerContext'); } export namespace GetServerContextualizationRequest { diff --git a/extensions/mssql/src/features.ts b/extensions/mssql/src/features.ts index 9d79b97e1b4d..ea2e8f4755a4 100644 --- a/extensions/mssql/src/features.ts +++ b/extensions/mssql/src/features.ts @@ -1310,7 +1310,7 @@ export class ExecutionPlanServiceFeature extends SqlOpsFeature { */ export class ServerContextualizationServiceFeature extends SqlOpsFeature { private static readonly messagesTypes: RPCMessageType[] = [ - contracts.GenerateServerContextualizationNotification.type + contracts.GenerateServerContextualizationRequest.type ]; constructor(client: SqlOpsDataClient) { @@ -1330,12 +1330,18 @@ export class ServerContextualizationServiceFeature extends SqlOpsFeature { + const generateServerContextualization = (ownerUri: string): Thenable => { const params: contracts.ServerContextualizationParams = { ownerUri: ownerUri }; - return client.sendNotification(contracts.GenerateServerContextualizationNotification.type, params); + return client.sendRequest(contracts.GenerateServerContextualizationRequest.type, params).then( + r => r, + e => { + client.logFailedRequest(contracts.GenerateServerContextualizationRequest.type, e); + return Promise.reject(e); + } + ); }; const getServerContextualization = (ownerUri: string): Thenable => { diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts index 9961685bed5c..2cd5d3aae9ba 100644 --- a/src/sql/azdata.proposed.d.ts +++ b/src/sql/azdata.proposed.d.ts @@ -1783,11 +1783,18 @@ declare module 'azdata' { } export namespace contextualization { + export interface GenerateServerContextualizationResult { + /** + * The generated server context. + */ + context: string | undefined; + } + export interface GetServerContextualizationResult { /** - * An array containing the generated server context. + * The retrieved server context. */ - context: string[]; + context: string | undefined; } export interface ServerContextualizationProvider extends DataProvider { @@ -1795,7 +1802,7 @@ declare module 'azdata' { * Generates server context. * @param ownerUri The URI of the connection to generate context for. */ - generateServerContextualization(ownerUri: string): void; + generateServerContextualization(ownerUri: string): Thenable; /** * Gets server context, which can be in the form of create scripts but is left up each provider. diff --git a/src/sql/workbench/api/common/extHostDataProtocol.ts b/src/sql/workbench/api/common/extHostDataProtocol.ts index 98a13bf62029..8a102b176620 100644 --- a/src/sql/workbench/api/common/extHostDataProtocol.ts +++ b/src/sql/workbench/api/common/extHostDataProtocol.ts @@ -972,8 +972,8 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape { // Database Server Contextualization API - public override $generateServerContextualization(handle: number, ownerUri: string): void { - this._resolveProvider(handle).generateServerContextualization(ownerUri); + public override $generateServerContextualization(handle: number, ownerUri: string): Thenable { + return this._resolveProvider(handle).generateServerContextualization(ownerUri); } public override $getServerContextualization(handle: number, ownerUri: string): Thenable { diff --git a/src/sql/workbench/api/common/sqlExtHost.protocol.ts b/src/sql/workbench/api/common/sqlExtHost.protocol.ts index 6e700cfeda4c..8c65c8725778 100644 --- a/src/sql/workbench/api/common/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/common/sqlExtHost.protocol.ts @@ -598,7 +598,7 @@ export abstract class ExtHostDataProtocolShape { /** * Generates server context. */ - $generateServerContextualization(handle: number, ownerUri: string): void { throw ni(); } + $generateServerContextualization(handle: number, ownerUri: string): Thenable { throw ni(); } /** * Gets server context. */ diff --git a/src/sql/workbench/browser/editor/query/fileQueryEditorInput.ts b/src/sql/workbench/browser/editor/query/fileQueryEditorInput.ts index 380a758c010d..bc4ea8b69098 100644 --- a/src/sql/workbench/browser/editor/query/fileQueryEditorInput.ts +++ b/src/sql/workbench/browser/editor/query/fileQueryEditorInput.ts @@ -18,6 +18,7 @@ import { FILE_QUERY_EDITOR_TYPEID } from 'sql/workbench/common/constants'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; +import { IServerContextualizationService } from 'sql/workbench/services/contextualization/common/interfaces'; export class FileQueryEditorInput extends QueryEditorInput { @@ -30,9 +31,10 @@ export class FileQueryEditorInput extends QueryEditorInput { @IConnectionManagementService connectionManagementService: IConnectionManagementService, @IQueryModelService queryModelService: IQueryModelService, @IConfigurationService configurationService: IConfigurationService, - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @IServerContextualizationService serverContextualizationService: IServerContextualizationService ) { - super(description, text, results, connectionManagementService, queryModelService, configurationService, instantiationService); + super(description, text, results, connectionManagementService, queryModelService, configurationService, instantiationService, serverContextualizationService); } public override resolve(): Promise { diff --git a/src/sql/workbench/browser/editor/query/untitledQueryEditorInput.ts b/src/sql/workbench/browser/editor/query/untitledQueryEditorInput.ts index ad0bfa39c0db..78fc3591094c 100644 --- a/src/sql/workbench/browser/editor/query/untitledQueryEditorInput.ts +++ b/src/sql/workbench/browser/editor/query/untitledQueryEditorInput.ts @@ -22,6 +22,7 @@ import { IUntitledQueryEditorInput } from 'sql/workbench/common/editor/query/unt import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService'; import { Uri } from 'vscode'; import { ILogService } from 'vs/platform/log/common/log'; +import { IServerContextualizationService } from 'sql/workbench/services/contextualization/common/interfaces'; export class UntitledQueryEditorInput extends QueryEditorInput implements IUntitledQueryEditorInput { @@ -36,9 +37,10 @@ export class UntitledQueryEditorInput extends QueryEditorInput implements IUntit @IConfigurationService configurationService: IConfigurationService, @IInstantiationService instantiationService: IInstantiationService, @ILogService private readonly logService: ILogService, - @IEditorResolverService private readonly editorResolverService: IEditorResolverService + @IEditorResolverService private readonly editorResolverService: IEditorResolverService, + @IServerContextualizationService serverContextualizationService: IServerContextualizationService ) { - super(description, text, results, connectionManagementService, queryModelService, configurationService, instantiationService); + super(description, text, results, connectionManagementService, queryModelService, configurationService, instantiationService, serverContextualizationService); // Set the mode explicitely to stop the auto language detection service from changing the mode unexpectedly. // the auto language detection service won't do the language change only if the mode is explicitely set. // if the mode (e.g. kusto, sql) do not exist for whatever reason, we will default it to sql. diff --git a/src/sql/workbench/common/editor/query/queryEditorInput.ts b/src/sql/workbench/common/editor/query/queryEditorInput.ts index d0ba027ae458..f0d3b84e7a8b 100644 --- a/src/sql/workbench/common/editor/query/queryEditorInput.ts +++ b/src/sql/workbench/common/editor/query/queryEditorInput.ts @@ -20,6 +20,7 @@ import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/text import { IQueryEditorConfiguration } from 'sql/platform/query/common/query'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IServerContextualizationService } from 'sql/workbench/services/contextualization/common/interfaces'; const MAX_SIZE = 13; @@ -142,7 +143,7 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab private _state = this._register(new QueryEditorState()); public get state(): QueryEditorState { return this._state; } - private _serverContext: string[] | undefined; + private _serverContext: string | undefined; constructor( private _description: string | undefined, @@ -151,7 +152,8 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab @IConnectionManagementService private readonly connectionManagementService: IConnectionManagementService, @IQueryModelService private readonly queryModelService: IQueryModelService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IInstantiationService protected readonly instantiationService: IInstantiationService + @IInstantiationService protected readonly instantiationService: IInstantiationService, + @IServerContextualizationService private readonly serverContextualizationService: IServerContextualizationService ) { super(); @@ -237,11 +239,11 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab public override isDirty(): boolean { return this._text.isDirty(); } public get resource(): URI { return this._text.resource; } - public set serverContext(context: string[]) { + public set serverContext(context: string) { this._serverContext = context; } - public get serverContext(): string[] | undefined { + public get serverContext(): string | undefined { return this._serverContext; } @@ -329,6 +331,28 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab } } this._onDidChangeLabel.fire(); + + this.contextualizeEditorForCopilot(); + } + + private contextualizeEditorForCopilot(): void { + // Don't need to take any actions if contextualization is not enabled and can return + if (!this.configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled) { + return; + } + + this.serverContextualizationService.getServerContextualization(this.uri) + .then(async getServerContextualizationResult => { + if (getServerContextualizationResult.context) { + await this.serverContextualizationService.sendServerContextualizationToCopilot(getServerContextualizationResult.context); + } + else { + const generateServerContextualizationResult = await this.serverContextualizationService.generateServerContextualization(this.uri); + if (generateServerContextualizationResult.context) { + await this.serverContextualizationService.sendServerContextualizationToCopilot(generateServerContextualizationResult.context); + } + } + }); } public onDisconnect(): void { diff --git a/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts b/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts index a07bd46bddc3..ccfbeb03bc67 100644 --- a/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts +++ b/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts @@ -467,7 +467,7 @@ suite('commandLineService tests', () => { let uri = URI.file(args._[0]); const workbenchinstantiationService = workbenchInstantiationService(); const editorInput = workbenchinstantiationService.createInstance(FileEditorInput, uri, undefined, undefined, undefined, undefined, undefined, undefined); - const queryInput = new FileQueryEditorInput(undefined, editorInput, undefined, connectionManagementService.object, querymodelService.object, configurationService.object, workbenchinstantiationService); + const queryInput = new FileQueryEditorInput(undefined, editorInput, undefined, connectionManagementService.object, querymodelService.object, configurationService.object, workbenchinstantiationService, undefined); queryInput.state.connected = true; const editorService: TypeMoq.Mock = TypeMoq.Mock.ofType(TestEditorService, TypeMoq.MockBehavior.Strict); editorService.setup(e => e.editors).returns(() => [queryInput]); diff --git a/src/sql/workbench/contrib/query/test/browser/queryEditor.test.ts b/src/sql/workbench/contrib/query/test/browser/queryEditor.test.ts index 1078b2e7ac29..0722ccf1f7db 100644 --- a/src/sql/workbench/contrib/query/test/browser/queryEditor.test.ts +++ b/src/sql/workbench/contrib/query/test/browser/queryEditor.test.ts @@ -316,6 +316,7 @@ suite('SQL QueryEditor Tests', () => { configurationService.object, testinstantiationService, undefined, + undefined, undefined ); }); diff --git a/src/sql/workbench/services/contextualization/common/interfaces.ts b/src/sql/workbench/services/contextualization/common/interfaces.ts index b53dc15f1477..8d082fbfef38 100644 --- a/src/sql/workbench/services/contextualization/common/interfaces.ts +++ b/src/sql/workbench/services/contextualization/common/interfaces.ts @@ -33,11 +33,17 @@ export interface IServerContextualizationService { * Generates server context * @param ownerUri The URI of the connection to generate context for. */ - generateServerContextualization(ownerUri: string): void; + generateServerContextualization(ownerUri: string): Promise; /** * Gets all database context. * @param ownerUri The URI of the connection to get context for. */ getServerContextualization(ownerUri: string): Promise; + + /** + * Sends server context to the Copilot extension, so it can be used to generate improved suggestions. + * @param serverContext The server context to be sent to Copilot. + */ + sendServerContextualizationToCopilot(serverContext: string | undefined): Promise } diff --git a/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts b/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts index 424f130d8634..e96645adeb27 100644 --- a/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts +++ b/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts @@ -5,14 +5,12 @@ import * as azdata from 'azdata'; import { invalidProvider } from 'sql/base/common/errors'; -import { IConnectionManagementService, IConnectionParams } from 'sql/platform/connection/common/connectionManagement'; +import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; import { IQueryEditorConfiguration } from 'sql/platform/query/common/query'; -import { QueryEditorInput } from 'sql/workbench/common/editor/query/queryEditorInput'; import { IServerContextualizationService } from 'sql/workbench/services/contextualization/common/interfaces'; import { Disposable } from 'vs/base/common/lifecycle'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; export class ServerContextualizationService extends Disposable implements IServerContextualizationService { @@ -23,27 +21,9 @@ export class ServerContextualizationService extends Disposable implements IServe @IConnectionManagementService private readonly _connectionManagementService: IConnectionManagementService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IExtensionService private readonly _extensionService: IExtensionService, - @ICommandService private readonly _commandService: ICommandService, - @IEditorService private readonly _editorService: IEditorService, + @ICommandService private readonly _commandService: ICommandService ) { super(); - - this._register(this._connectionManagementService.onConnect(async (e: IConnectionParams) => { - const copilotExt = await this._extensionService.getExtension('github.copilot'); - - if (copilotExt && this._configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled) { - const ownerUri = e.connectionUri; - this.generateServerContextualization(ownerUri); - } - })); - - this._register(this._editorService.onDidActiveEditorChange(async () => { - const activeQueryEditorInput = this._editorService.activeEditorPane.input as QueryEditorInput; - const uri = activeQueryEditorInput?.uri; - if (uri) { - await this.sendServerContextualizationToCopilot(uri, activeQueryEditorInput.serverContext); - } - })); } /** @@ -80,11 +60,19 @@ export class ServerContextualizationService extends Disposable implements IServe * Generates server context * @param ownerUri The URI of the connection to generate context for. */ - public generateServerContextualization(ownerUri: string): void { + public async generateServerContextualization(ownerUri: string): Promise { + const copilotExt = await this._extensionService.getExtension('github.copilot'); + const isContextualizationEnabled = this._configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled; + const providerName = this._connectionManagementService.getProviderIdFromUri(ownerUri); const handler = this.getProvider(providerName); - if (handler) { - handler.generateServerContextualization(ownerUri); + if (copilotExt && isContextualizationEnabled && handler) { + return await handler.generateServerContextualization(ownerUri); + } + else { + return Promise.resolve({ + context: undefined + }); } } @@ -93,35 +81,27 @@ export class ServerContextualizationService extends Disposable implements IServe * @param ownerUri The URI of the connection to get context for. */ public async getServerContextualization(ownerUri: string): Promise { + const copilotExt = await this._extensionService.getExtension('github.copilot'); + const isContextualizationEnabled = this._configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled; + const providerName = this._connectionManagementService.getProviderIdFromUri(ownerUri); const handler = this.getProvider(providerName); - if (handler) { + if (copilotExt && isContextualizationEnabled && handler) { return await handler.getServerContextualization(ownerUri); } else { return Promise.resolve({ - context: [] + context: undefined }); } } - private async sendServerContextualizationToCopilot(ownerUri: string, serverContext: string[] | undefined): Promise { - if (this._configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled) { - var flattenedServerContext = ''; - - if (serverContext) { - // Flattening scripts from string[] to just a string - flattenedServerContext = serverContext.filter(c => c.includes('CREATE')).join('\n'); - } - else { - const result = await this.getServerContextualization(ownerUri); - // Flattening scripts from string[] to just a string - flattenedServerContext = result.context.filter(c => c.includes('CREATE')).join('\n'); - } - + public async sendServerContextualizationToCopilot(serverContext: string | undefined): Promise { + const copilotExt = await this._extensionService.getExtension('github.copilot'); + if (copilotExt && serverContext && this._configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled) { // LEWISSANCHEZ TODO: Find way to set context on untitled query editor files. Need to save first for Copilot status to say "Has Context" await this._commandService.executeCommand('github.copilot.provideContext', '**/*.sql', { - value: flattenedServerContext // value is expecting a string and not a string[] + value: serverContext }); } } From 994e598f3161ba3b9886f94848f81cfa442f96f9 Mon Sep 17 00:00:00 2001 From: Lewis Sanchez Date: Wed, 30 Aug 2023 16:15:07 -0700 Subject: [PATCH 07/13] Minor clean up --- .../workbench/common/editor/query/queryEditorInput.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/sql/workbench/common/editor/query/queryEditorInput.ts b/src/sql/workbench/common/editor/query/queryEditorInput.ts index f0d3b84e7a8b..d636396c56e1 100644 --- a/src/sql/workbench/common/editor/query/queryEditorInput.ts +++ b/src/sql/workbench/common/editor/query/queryEditorInput.ts @@ -143,8 +143,6 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab private _state = this._register(new QueryEditorState()); public get state(): QueryEditorState { return this._state; } - private _serverContext: string | undefined; - constructor( private _description: string | undefined, protected _text: AbstractTextResourceEditorInput, @@ -239,14 +237,6 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab public override isDirty(): boolean { return this._text.isDirty(); } public get resource(): URI { return this._text.resource; } - public set serverContext(context: string) { - this._serverContext = context; - } - - public get serverContext(): string | undefined { - return this._serverContext; - } - public override getName(longForm?: boolean): string { if (this.configurationService.getValue('queryEditor').showConnectionInfoInTitle) { let profile = this.connectionManagementService.getConnectionProfile(this.uri); From 5fdab8c016a00cb623f9f6e00d0b764f067c5e1a Mon Sep 17 00:00:00 2001 From: Lewis Sanchez Date: Thu, 31 Aug 2023 13:31:39 -0700 Subject: [PATCH 08/13] Remove nested then and use async/await --- .../common/editor/query/queryEditorInput.ts | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/sql/workbench/common/editor/query/queryEditorInput.ts b/src/sql/workbench/common/editor/query/queryEditorInput.ts index d636396c56e1..072d08ba716f 100644 --- a/src/sql/workbench/common/editor/query/queryEditorInput.ts +++ b/src/sql/workbench/common/editor/query/queryEditorInput.ts @@ -303,7 +303,7 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab this.state.connecting = false; } - public onConnectSuccess(params?: INewConnectionParams): void { + public async onConnectSuccess(params?: INewConnectionParams): Promise { this.state.connected = true; this.state.connecting = false; @@ -325,24 +325,22 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab this.contextualizeEditorForCopilot(); } - private contextualizeEditorForCopilot(): void { + private async contextualizeEditorForCopilot(): Promise { // Don't need to take any actions if contextualization is not enabled and can return if (!this.configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled) { return; } - this.serverContextualizationService.getServerContextualization(this.uri) - .then(async getServerContextualizationResult => { - if (getServerContextualizationResult.context) { - await this.serverContextualizationService.sendServerContextualizationToCopilot(getServerContextualizationResult.context); - } - else { - const generateServerContextualizationResult = await this.serverContextualizationService.generateServerContextualization(this.uri); - if (generateServerContextualizationResult.context) { - await this.serverContextualizationService.sendServerContextualizationToCopilot(generateServerContextualizationResult.context); - } - } - }); + const getServerContextualizationResult = await this.serverContextualizationService.getServerContextualization(this.uri); + if (getServerContextualizationResult.context) { + await this.serverContextualizationService.sendServerContextualizationToCopilot(getServerContextualizationResult.context); + } + else { + const generateServerContextualizationResult = await this.serverContextualizationService.generateServerContextualization(this.uri); + if (generateServerContextualizationResult.context) { + await this.serverContextualizationService.sendServerContextualizationToCopilot(generateServerContextualizationResult.context); + } + } } public onDisconnect(): void { From e1b4566e99c1555a366e8800a327f7c227f3c386 Mon Sep 17 00:00:00 2001 From: Lewis Sanchez Date: Thu, 31 Aug 2023 13:32:54 -0700 Subject: [PATCH 09/13] Create helper function --- .../common/serverContextualizationService.ts | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts b/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts index e96645adeb27..5145da468326 100644 --- a/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts +++ b/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts @@ -61,12 +61,10 @@ export class ServerContextualizationService extends Disposable implements IServe * @param ownerUri The URI of the connection to generate context for. */ public async generateServerContextualization(ownerUri: string): Promise { - const copilotExt = await this._extensionService.getExtension('github.copilot'); - const isContextualizationEnabled = this._configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled; - + const isContextualizationNeeded = await this.isContextualizationNeeded(); const providerName = this._connectionManagementService.getProviderIdFromUri(ownerUri); const handler = this.getProvider(providerName); - if (copilotExt && isContextualizationEnabled && handler) { + if (isContextualizationNeeded && handler) { return await handler.generateServerContextualization(ownerUri); } else { @@ -81,12 +79,10 @@ export class ServerContextualizationService extends Disposable implements IServe * @param ownerUri The URI of the connection to get context for. */ public async getServerContextualization(ownerUri: string): Promise { - const copilotExt = await this._extensionService.getExtension('github.copilot'); - const isContextualizationEnabled = this._configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled; - + const isContextualizationNeeded = await this.isContextualizationNeeded(); const providerName = this._connectionManagementService.getProviderIdFromUri(ownerUri); const handler = this.getProvider(providerName); - if (copilotExt && isContextualizationEnabled && handler) { + if (isContextualizationNeeded && handler) { return await handler.getServerContextualization(ownerUri); } else { @@ -96,13 +92,29 @@ export class ServerContextualizationService extends Disposable implements IServe } } + /** + * Sends the provided context over to copilot, so that it can be used to generate improved suggestions. + * @param serverContext The context to be sent over to Copilot + */ public async sendServerContextualizationToCopilot(serverContext: string | undefined): Promise { - const copilotExt = await this._extensionService.getExtension('github.copilot'); - if (copilotExt && serverContext && this._configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled) { + const isContextualizationNeeded = await this.isContextualizationNeeded(); + if (isContextualizationNeeded && serverContext) { // LEWISSANCHEZ TODO: Find way to set context on untitled query editor files. Need to save first for Copilot status to say "Has Context" await this._commandService.executeCommand('github.copilot.provideContext', '**/*.sql', { value: serverContext }); } } + + /** + * Checks if contextualization is needed. This is based on whether the Copilot extension is installed and the GitHub Copilot + * contextualization setting is enabled. + * @returns A promise that resolves to true if contextualization is needed, false otherwise. + */ + private async isContextualizationNeeded(): Promise { + const copilotExt = await this._extensionService.getExtension('github.copilot'); + const isContextualizationEnabled = this._configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled + + return (copilotExt && isContextualizationEnabled); + } } From d589fb1b2fdeceadca96d4da7037a7e8a0924402 Mon Sep 17 00:00:00 2001 From: Lewis Sanchez Date: Thu, 31 Aug 2023 15:08:26 -0700 Subject: [PATCH 10/13] Remove unneeded async and add comment --- src/sql/workbench/common/editor/query/queryEditorInput.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sql/workbench/common/editor/query/queryEditorInput.ts b/src/sql/workbench/common/editor/query/queryEditorInput.ts index 072d08ba716f..a3599cc23732 100644 --- a/src/sql/workbench/common/editor/query/queryEditorInput.ts +++ b/src/sql/workbench/common/editor/query/queryEditorInput.ts @@ -303,7 +303,7 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab this.state.connecting = false; } - public async onConnectSuccess(params?: INewConnectionParams): Promise { + public onConnectSuccess(params?: INewConnectionParams): void { this.state.connected = true; this.state.connecting = false; @@ -322,6 +322,7 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab } this._onDidChangeLabel.fire(); + // Intentionally not awaiting, so that contextualization can happen in the background this.contextualizeEditorForCopilot(); } From fc3f4ad29a26458694649a2c1de15608a24a4a1d Mon Sep 17 00:00:00 2001 From: Lewis Sanchez Date: Thu, 31 Aug 2023 15:28:08 -0700 Subject: [PATCH 11/13] Encapsulate all context logic in service --- .../common/editor/query/queryEditorInput.ts | 20 +--------- .../contextualization/common/interfaces.ts | 19 ++------- .../common/serverContextualizationService.ts | 39 ++++++++++++++----- 3 files changed, 35 insertions(+), 43 deletions(-) diff --git a/src/sql/workbench/common/editor/query/queryEditorInput.ts b/src/sql/workbench/common/editor/query/queryEditorInput.ts index a3599cc23732..1ec7fd947e85 100644 --- a/src/sql/workbench/common/editor/query/queryEditorInput.ts +++ b/src/sql/workbench/common/editor/query/queryEditorInput.ts @@ -323,25 +323,7 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab this._onDidChangeLabel.fire(); // Intentionally not awaiting, so that contextualization can happen in the background - this.contextualizeEditorForCopilot(); - } - - private async contextualizeEditorForCopilot(): Promise { - // Don't need to take any actions if contextualization is not enabled and can return - if (!this.configurationService.getValue('queryEditor').githubCopilotContextualizationEnabled) { - return; - } - - const getServerContextualizationResult = await this.serverContextualizationService.getServerContextualization(this.uri); - if (getServerContextualizationResult.context) { - await this.serverContextualizationService.sendServerContextualizationToCopilot(getServerContextualizationResult.context); - } - else { - const generateServerContextualizationResult = await this.serverContextualizationService.generateServerContextualization(this.uri); - if (generateServerContextualizationResult.context) { - await this.serverContextualizationService.sendServerContextualizationToCopilot(generateServerContextualizationResult.context); - } - } + this.serverContextualizationService.contextualizeUriForCopilot(this.uri); } public onDisconnect(): void { diff --git a/src/sql/workbench/services/contextualization/common/interfaces.ts b/src/sql/workbench/services/contextualization/common/interfaces.ts index 8d082fbfef38..3e07e6a7d279 100644 --- a/src/sql/workbench/services/contextualization/common/interfaces.ts +++ b/src/sql/workbench/services/contextualization/common/interfaces.ts @@ -30,20 +30,9 @@ export interface IServerContextualizationService { getProvider(providerId: string): azdata.contextualization.ServerContextualizationProvider; /** - * Generates server context - * @param ownerUri The URI of the connection to generate context for. + * Contextualizes the provided URI for GitHub Copilot. + * @param uri The URI to contextualize for Copilot + * @returns A promise that doesn't need to be awaited since contextualization is done in the background. */ - generateServerContextualization(ownerUri: string): Promise; - - /** - * Gets all database context. - * @param ownerUri The URI of the connection to get context for. - */ - getServerContextualization(ownerUri: string): Promise; - - /** - * Sends server context to the Copilot extension, so it can be used to generate improved suggestions. - * @param serverContext The server context to be sent to Copilot. - */ - sendServerContextualizationToCopilot(serverContext: string | undefined): Promise + contextualizeUriForCopilot(uri: string): Promise; } diff --git a/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts b/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts index 5145da468326..7c8b388c27bf 100644 --- a/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts +++ b/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts @@ -56,15 +56,38 @@ export class ServerContextualizationService extends Disposable implements IServe throw invalidProvider(providerId); } + /** + * Contextualizes the provided URI for GitHub Copilot. + * @param uri The URI to contextualize for Copilot + * @returns A promise that doesn't need to be awaited since contextualization is done in the background. + */ + public async contextualizeUriForCopilot(uri: string): Promise { + // Don't need to take any actions if contextualization is not enabled and can return + const isContextualizationNeeded = await this.isContextualizationNeeded(); + if (!isContextualizationNeeded) { + return; + } + + const getServerContextualizationResult = await this.getServerContextualization(uri); + if (getServerContextualizationResult.context) { + await this.sendServerContextualizationToCopilot(getServerContextualizationResult.context); + } + else { + const generateServerContextualizationResult = await this.generateServerContextualization(uri); + if (generateServerContextualizationResult.context) { + await this.sendServerContextualizationToCopilot(generateServerContextualizationResult.context); + } + } + } + /** * Generates server context * @param ownerUri The URI of the connection to generate context for. */ - public async generateServerContextualization(ownerUri: string): Promise { - const isContextualizationNeeded = await this.isContextualizationNeeded(); + private async generateServerContextualization(ownerUri: string): Promise { const providerName = this._connectionManagementService.getProviderIdFromUri(ownerUri); const handler = this.getProvider(providerName); - if (isContextualizationNeeded && handler) { + if (handler) { return await handler.generateServerContextualization(ownerUri); } else { @@ -78,11 +101,10 @@ export class ServerContextualizationService extends Disposable implements IServe * Gets all database context. * @param ownerUri The URI of the connection to get context for. */ - public async getServerContextualization(ownerUri: string): Promise { - const isContextualizationNeeded = await this.isContextualizationNeeded(); + private async getServerContextualization(ownerUri: string): Promise { const providerName = this._connectionManagementService.getProviderIdFromUri(ownerUri); const handler = this.getProvider(providerName); - if (isContextualizationNeeded && handler) { + if (handler) { return await handler.getServerContextualization(ownerUri); } else { @@ -96,9 +118,8 @@ export class ServerContextualizationService extends Disposable implements IServe * Sends the provided context over to copilot, so that it can be used to generate improved suggestions. * @param serverContext The context to be sent over to Copilot */ - public async sendServerContextualizationToCopilot(serverContext: string | undefined): Promise { - const isContextualizationNeeded = await this.isContextualizationNeeded(); - if (isContextualizationNeeded && serverContext) { + private async sendServerContextualizationToCopilot(serverContext: string | undefined): Promise { + if (serverContext) { // LEWISSANCHEZ TODO: Find way to set context on untitled query editor files. Need to save first for Copilot status to say "Has Context" await this._commandService.executeCommand('github.copilot.provideContext', '**/*.sql', { value: serverContext From 5b27ad17a026584e7e129f67196c2153d80ad6f6 Mon Sep 17 00:00:00 2001 From: Lewis Sanchez Date: Thu, 31 Aug 2023 15:35:53 -0700 Subject: [PATCH 12/13] Use void operator to fix floating promise --- src/sql/workbench/common/editor/query/queryEditorInput.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sql/workbench/common/editor/query/queryEditorInput.ts b/src/sql/workbench/common/editor/query/queryEditorInput.ts index 1ec7fd947e85..0f7be018165c 100644 --- a/src/sql/workbench/common/editor/query/queryEditorInput.ts +++ b/src/sql/workbench/common/editor/query/queryEditorInput.ts @@ -323,7 +323,7 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab this._onDidChangeLabel.fire(); // Intentionally not awaiting, so that contextualization can happen in the background - this.serverContextualizationService.contextualizeUriForCopilot(this.uri); + void this.serverContextualizationService.contextualizeUriForCopilot(this.uri); } public onDisconnect(): void { From 5d36e9663ec58190a950473f6e1e39afa5635e7a Mon Sep 17 00:00:00 2001 From: Lewis Sanchez Date: Thu, 31 Aug 2023 15:53:06 -0700 Subject: [PATCH 13/13] Correct return comment --- src/sql/workbench/common/editor/query/queryEditorInput.ts | 2 +- .../workbench/services/contextualization/common/interfaces.ts | 4 ++-- .../common/serverContextualizationService.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sql/workbench/common/editor/query/queryEditorInput.ts b/src/sql/workbench/common/editor/query/queryEditorInput.ts index 0f7be018165c..c521a9ba13d7 100644 --- a/src/sql/workbench/common/editor/query/queryEditorInput.ts +++ b/src/sql/workbench/common/editor/query/queryEditorInput.ts @@ -323,7 +323,7 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab this._onDidChangeLabel.fire(); // Intentionally not awaiting, so that contextualization can happen in the background - void this.serverContextualizationService.contextualizeUriForCopilot(this.uri); + void this.serverContextualizationService?.contextualizeUriForCopilot(this.uri); } public onDisconnect(): void { diff --git a/src/sql/workbench/services/contextualization/common/interfaces.ts b/src/sql/workbench/services/contextualization/common/interfaces.ts index 3e07e6a7d279..508cd7e2f287 100644 --- a/src/sql/workbench/services/contextualization/common/interfaces.ts +++ b/src/sql/workbench/services/contextualization/common/interfaces.ts @@ -31,8 +31,8 @@ export interface IServerContextualizationService { /** * Contextualizes the provided URI for GitHub Copilot. - * @param uri The URI to contextualize for Copilot - * @returns A promise that doesn't need to be awaited since contextualization is done in the background. + * @param uri The URI to contextualize for Copilot. + * @returns Copilot will have the URI contextualized when the promise completes. */ contextualizeUriForCopilot(uri: string): Promise; } diff --git a/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts b/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts index 7c8b388c27bf..098aa35756dc 100644 --- a/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts +++ b/src/sql/workbench/services/contextualization/common/serverContextualizationService.ts @@ -58,8 +58,8 @@ export class ServerContextualizationService extends Disposable implements IServe /** * Contextualizes the provided URI for GitHub Copilot. - * @param uri The URI to contextualize for Copilot - * @returns A promise that doesn't need to be awaited since contextualization is done in the background. + * @param uri The URI to contextualize for Copilot. + * @returns Copilot will have the URI contextualized when the promise completes. */ public async contextualizeUriForCopilot(uri: string): Promise { // Don't need to take any actions if contextualization is not enabled and can return