diff --git a/src/lsptoolshost/copilot.ts b/src/lsptoolshost/copilot.ts new file mode 100644 index 000000000..53a67912b --- /dev/null +++ b/src/lsptoolshost/copilot.ts @@ -0,0 +1,92 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { CSharpExtensionId } from '../constants/csharpExtensionId'; +import { CopilotRelatedDocumentsReport, CopilotRelatedDocumentsRequest } from './roslynProtocol'; +import { RoslynLanguageServer } from './roslynLanguageServer'; +import { UriConverter } from './uriConverter'; +import { TextDocumentIdentifier } from 'vscode-languageserver-protocol'; +import { languageServerOptions } from '../shared/options'; + +interface CopilotRelatedFilesProviderRegistration { + registerRelatedFilesProvider( + providerId: { extensionId: string; languageId: string }, + callback: (uri: vscode.Uri) => Promise<{ entries: vscode.Uri[]; traits?: { name: string; value: string }[] }> + ): void; +} + +export async function registerCopilotExtensionAsync( + languageServer: RoslynLanguageServer, + channel: vscode.OutputChannel +) { + const isTraceLogLevel = + languageServerOptions.logLevel && + (languageServerOptions.logLevel === 'Trace' || languageServerOptions.logLevel === 'Debug'); + + const ext = vscode.extensions.getExtension('github.copilot'); + if (!ext) { + if (isTraceLogLevel) { + channel.appendLine( + 'GitHub Copilot extension not installed. Skip registeration of C# related files provider.' + ); + } + return; + } + await ext.activate(); + const relatedAPI = ext.exports as CopilotRelatedFilesProviderRegistration | undefined; + if (!relatedAPI) { + if (isTraceLogLevel) { + channel.appendLine( + 'Incompatible GitHub Copilot extension installed. Skip registeration of C# related files provider.' + ); + } + return; + } + + if (isTraceLogLevel) { + channel.appendLine('registeration of C# related files provider for GitHub Copilot extension succeeded.'); + } + + const id = { + extensionId: CSharpExtensionId, + languageId: 'csharp', + }; + + relatedAPI.registerRelatedFilesProvider(id, async (uri) => { + const buildResult = (reports: CopilotRelatedDocumentsReport[], builder?: vscode.Uri[]) => { + if (reports) { + for (const report of reports) { + if (report._vs_file_paths) { + for (const filePath of report._vs_file_paths) { + builder?.push(vscode.Uri.file(filePath)); + } + } + } + } + }; + const relatedFiles: vscode.Uri[] = []; + const uriString = UriConverter.serialize(uri); + const textDocument = TextDocumentIdentifier.create(uriString); + try { + await languageServer.sendRequestWithProgress( + CopilotRelatedDocumentsRequest.type, + { + _vs_textDocument: textDocument, + position: { + line: 0, + character: 0, + }, + }, + async (r) => buildResult(r, relatedFiles) + ); + } catch (e) { + if (e instanceof Error) { + channel.appendLine(e.message); + } + } + return { entries: relatedFiles }; + }); +} diff --git a/src/lsptoolshost/roslynLanguageServer.ts b/src/lsptoolshost/roslynLanguageServer.ts index b46507392..e525eec57 100644 --- a/src/lsptoolshost/roslynLanguageServer.ts +++ b/src/lsptoolshost/roslynLanguageServer.ts @@ -68,6 +68,7 @@ import { registerLanguageStatusItems } from './languageStatusBar'; import { ProjectContextService } from './services/projectContextService'; import { ProvideDynamicFileResponse } from '../razor/src/dynamicFile/provideDynamicFileResponse'; import { ProvideDynamicFileParams } from '../razor/src/dynamicFile/provideDynamicFileParams'; +import { registerCopilotExtensionAsync } from './copilot'; let _channel: vscode.OutputChannel; let _traceChannel: vscode.OutputChannel; @@ -1036,6 +1037,7 @@ export async function activateRoslynLanguageServer( ); registerLanguageStatusItems(context, languageServer, languageServerEvents); + await registerCopilotExtensionAsync(languageServer, _channel); // Register any commands that need to be handled by the extension. registerCommands(context, languageServer, hostExecutableResolver, _channel); diff --git a/src/lsptoolshost/roslynProtocol.ts b/src/lsptoolshost/roslynProtocol.ts index 4e5850770..9c612502b 100644 --- a/src/lsptoolshost/roslynProtocol.ts +++ b/src/lsptoolshost/roslynProtocol.ts @@ -221,6 +221,15 @@ export interface ProjectNeedsRestoreName { projectFilePaths: string[]; } +export interface CopilotRelatedDocumentsParams extends lsp.WorkDoneProgressParams, lsp.PartialResultParams { + _vs_textDocument: lsp.TextDocumentIdentifier; + position: lsp.Position; +} + +export interface CopilotRelatedDocumentsReport { + _vs_file_paths?: string[]; +} + export namespace WorkspaceDebugConfigurationRequest { export const method = 'workspace/debugConfiguration'; export const messageDirection: lsp.MessageDirection = lsp.MessageDirection.clientToServer; @@ -330,3 +339,15 @@ export namespace ProjectNeedsRestoreRequest { export const messageDirection: lsp.MessageDirection = lsp.MessageDirection.serverToClient; export const type = new lsp.RequestType(method); } + +export namespace CopilotRelatedDocumentsRequest { + export const method = 'copilot/_related_documents'; + export const messageDirection: lsp.MessageDirection = lsp.MessageDirection.clientToServer; + export const type = new lsp.ProtocolRequestType< + CopilotRelatedDocumentsParams, + CopilotRelatedDocumentsReport[], + CopilotRelatedDocumentsReport[], + void, + void + >(method); +}