From 4369bdb0d260430e21049087e6373d1e940d7f95 Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Mon, 16 Sep 2024 17:55:31 -0700 Subject: [PATCH 1/3] Register relatedFilesProvider for C# --- src/lsptoolshost/copilot.ts | 89 ++++++++++++++++++++++++ src/lsptoolshost/roslynLanguageServer.ts | 2 + src/lsptoolshost/roslynProtocol.ts | 22 ++++++ 3 files changed, 113 insertions(+) create mode 100644 src/lsptoolshost/copilot.ts diff --git a/src/lsptoolshost/copilot.ts b/src/lsptoolshost/copilot.ts new file mode 100644 index 000000000..9df291455 --- /dev/null +++ b/src/lsptoolshost/copilot.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; + +export async function registerCopilotExtension(languageServer: RoslynLanguageServer, channel: vscode.OutputChannel) { + const ext = vscode.extensions.getExtension('github.copilot'); + if (!ext) { + channel.appendLine('GitHub Copilot extension not installed. Skipping call to `registerRelatedFilesProvider`'); + return; + } + await ext.activate(); + const relatedAPI = ext.exports as + | { + registerRelatedFilesProvider( + providerId: { extensionId: string; languageId: string }, + callback: ( + uri: vscode.Uri + ) => Promise<{ entries: vscode.Uri[]; traits?: { name: string; value: string }[] }> + ): void; + } + | undefined; + if (!relatedAPI) { + channel.appendLine( + 'Incompatible GitHub Copilot extension installed. Skipping call to `registerRelatedFilesProvider`' + ); + return; + } + channel.appendLine('registerRelatedFilesProvider succeeded.'); + + const id = { + extensionId: CSharpExtensionId, + languageId: 'csharp', + }; + + relatedAPI.registerRelatedFilesProvider(id, async (uri) => { + const writeOutput = (output: CopilotRelatedDocumentsReport[], builder: vscode.Uri[] | null) => { + if (output) { + for (const report of output) { + if (report._vs_file_paths) { + for (const filePath of report._vs_file_paths) { + channel.appendLine('found related file: ' + filePath); + builder?.push(vscode.Uri.file(filePath)); + } + } + } + } + }; + + const relatedFiles: vscode.Uri[] = []; + const uriString = UriConverter.serialize(uri); + const textDocument = TextDocumentIdentifier.create(uriString); + const responsePromise = languageServer.sendRequestWithProgress( + CopilotRelatedDocumentsRequest.type, + { + _vs_textDocument: textDocument, + position: { + line: 0, + character: 0, + }, + }, + async (p) => { + writeOutput(p, relatedFiles); + } + ); + + await responsePromise.then( + (result) => { + writeOutput(result, null); + return; + }, + (err) => { + channel.appendLine(err); + return; + } + ); + + return { + entries: relatedFiles, + }; + }); +} diff --git a/src/lsptoolshost/roslynLanguageServer.ts b/src/lsptoolshost/roslynLanguageServer.ts index b46507392..105d0e9f0 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 { registerCopilotExtension } from './copilot'; let _channel: vscode.OutputChannel; let _traceChannel: vscode.OutputChannel; @@ -1036,6 +1037,7 @@ export async function activateRoslynLanguageServer( ); registerLanguageStatusItems(context, languageServer, languageServerEvents); + await registerCopilotExtension(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..942212c18 100644 --- a/src/lsptoolshost/roslynProtocol.ts +++ b/src/lsptoolshost/roslynProtocol.ts @@ -221,6 +221,16 @@ export interface ProjectNeedsRestoreName { projectFilePaths: string[]; } +export interface CopilotRelatedDocumentsParams extends lsp.WorkDoneProgressParams, lsp.PartialResultParams { + _vs_textDocument: lsp.TextDocumentIdentifier; + position: lsp.Position; +} + +export interface CopilotRelatedDocumentsReport { + _vs_resultId: string | null; + _vs_file_paths: string[] | null; +} + export namespace WorkspaceDebugConfigurationRequest { export const method = 'workspace/debugConfiguration'; export const messageDirection: lsp.MessageDirection = lsp.MessageDirection.clientToServer; @@ -330,3 +340,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); +} From b70b82c76289a5ddfc1b05cf5d00e348eb168504 Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Thu, 19 Sep 2024 13:13:33 -0700 Subject: [PATCH 2/3] Address review comments --- src/lsptoolshost/copilot.ts | 65 +++++++++++------------- src/lsptoolshost/roslynLanguageServer.ts | 4 +- src/lsptoolshost/roslynProtocol.ts | 3 +- 3 files changed, 32 insertions(+), 40 deletions(-) diff --git a/src/lsptoolshost/copilot.ts b/src/lsptoolshost/copilot.ts index 9df291455..935bda00d 100644 --- a/src/lsptoolshost/copilot.ts +++ b/src/lsptoolshost/copilot.ts @@ -10,30 +10,33 @@ import { RoslynLanguageServer } from './roslynLanguageServer'; import { UriConverter } from './uriConverter'; import { TextDocumentIdentifier } from 'vscode-languageserver-protocol'; -export async function registerCopilotExtension(languageServer: RoslynLanguageServer, channel: vscode.OutputChannel) { +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, + tracingChannel: vscode.OutputChannel +) { const ext = vscode.extensions.getExtension('github.copilot'); if (!ext) { - channel.appendLine('GitHub Copilot extension not installed. Skipping call to `registerRelatedFilesProvider`'); + tracingChannel.appendLine( + 'GitHub Copilot extension not installed. Skip registeration of C# related files provider.' + ); return; } await ext.activate(); - const relatedAPI = ext.exports as - | { - registerRelatedFilesProvider( - providerId: { extensionId: string; languageId: string }, - callback: ( - uri: vscode.Uri - ) => Promise<{ entries: vscode.Uri[]; traits?: { name: string; value: string }[] }> - ): void; - } - | undefined; + const relatedAPI = ext.exports as CopilotRelatedFilesProviderRegistration | undefined; if (!relatedAPI) { - channel.appendLine( - 'Incompatible GitHub Copilot extension installed. Skipping call to `registerRelatedFilesProvider`' + tracingChannel.appendLine( + 'Incompatible GitHub Copilot extension installed. Skip registeration of C# related files provider.' ); return; } - channel.appendLine('registerRelatedFilesProvider succeeded.'); + tracingChannel.appendLine('registeration of C# related files provider for GitHub Copilot extension succeeded.'); const id = { extensionId: CSharpExtensionId, @@ -41,19 +44,17 @@ export async function registerCopilotExtension(languageServer: RoslynLanguageSer }; relatedAPI.registerRelatedFilesProvider(id, async (uri) => { - const writeOutput = (output: CopilotRelatedDocumentsReport[], builder: vscode.Uri[] | null) => { - if (output) { - for (const report of output) { + 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) { - channel.appendLine('found related file: ' + filePath); builder?.push(vscode.Uri.file(filePath)); } } } } }; - const relatedFiles: vscode.Uri[] = []; const uriString = UriConverter.serialize(uri); const textDocument = TextDocumentIdentifier.create(uriString); @@ -66,24 +67,16 @@ export async function registerCopilotExtension(languageServer: RoslynLanguageSer character: 0, }, }, - async (p) => { - writeOutput(p, relatedFiles); - } + async (r) => buildResult(r, relatedFiles) ); - await responsePromise.then( - (result) => { - writeOutput(result, null); - return; - }, - (err) => { - channel.appendLine(err); - return; + try { + await responsePromise; + } catch (e) { + if (e instanceof Error) { + tracingChannel.appendLine(e.message); } - ); - - return { - entries: relatedFiles, - }; + } + return { entries: relatedFiles }; }); } diff --git a/src/lsptoolshost/roslynLanguageServer.ts b/src/lsptoolshost/roslynLanguageServer.ts index 105d0e9f0..73a38b7b3 100644 --- a/src/lsptoolshost/roslynLanguageServer.ts +++ b/src/lsptoolshost/roslynLanguageServer.ts @@ -68,7 +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 { registerCopilotExtension } from './copilot'; +import { registerCopilotExtensionAsync } from './copilot'; let _channel: vscode.OutputChannel; let _traceChannel: vscode.OutputChannel; @@ -1037,7 +1037,7 @@ export async function activateRoslynLanguageServer( ); registerLanguageStatusItems(context, languageServer, languageServerEvents); - await registerCopilotExtension(languageServer, _channel); + await registerCopilotExtensionAsync(languageServer, _traceChannel); // 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 942212c18..9c612502b 100644 --- a/src/lsptoolshost/roslynProtocol.ts +++ b/src/lsptoolshost/roslynProtocol.ts @@ -227,8 +227,7 @@ export interface CopilotRelatedDocumentsParams extends lsp.WorkDoneProgressParam } export interface CopilotRelatedDocumentsReport { - _vs_resultId: string | null; - _vs_file_paths: string[] | null; + _vs_file_paths?: string[]; } export namespace WorkspaceDebugConfigurationRequest { From abd21b3c8b0f43f739787ca5a923059c2ed4ff4d Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Thu, 19 Sep 2024 17:19:59 -0700 Subject: [PATCH 3/3] Fix --- src/lsptoolshost/copilot.ts | 54 ++++++++++++++---------- src/lsptoolshost/roslynLanguageServer.ts | 2 +- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/lsptoolshost/copilot.ts b/src/lsptoolshost/copilot.ts index 935bda00d..53a67912b 100644 --- a/src/lsptoolshost/copilot.ts +++ b/src/lsptoolshost/copilot.ts @@ -9,6 +9,7 @@ import { CopilotRelatedDocumentsReport, CopilotRelatedDocumentsRequest } from '. import { RoslynLanguageServer } from './roslynLanguageServer'; import { UriConverter } from './uriConverter'; import { TextDocumentIdentifier } from 'vscode-languageserver-protocol'; +import { languageServerOptions } from '../shared/options'; interface CopilotRelatedFilesProviderRegistration { registerRelatedFilesProvider( @@ -19,24 +20,35 @@ interface CopilotRelatedFilesProviderRegistration { export async function registerCopilotExtensionAsync( languageServer: RoslynLanguageServer, - tracingChannel: vscode.OutputChannel + channel: vscode.OutputChannel ) { + const isTraceLogLevel = + languageServerOptions.logLevel && + (languageServerOptions.logLevel === 'Trace' || languageServerOptions.logLevel === 'Debug'); + const ext = vscode.extensions.getExtension('github.copilot'); if (!ext) { - tracingChannel.appendLine( - 'GitHub Copilot extension not installed. Skip registeration of C# related files provider.' - ); + 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) { - tracingChannel.appendLine( - 'Incompatible GitHub Copilot extension installed. Skip registeration of C# related files provider.' - ); + if (isTraceLogLevel) { + channel.appendLine( + 'Incompatible GitHub Copilot extension installed. Skip registeration of C# related files provider.' + ); + } return; } - tracingChannel.appendLine('registeration of C# related files provider for GitHub Copilot extension succeeded.'); + + if (isTraceLogLevel) { + channel.appendLine('registeration of C# related files provider for GitHub Copilot extension succeeded.'); + } const id = { extensionId: CSharpExtensionId, @@ -58,23 +70,21 @@ export async function registerCopilotExtensionAsync( const relatedFiles: vscode.Uri[] = []; const uriString = UriConverter.serialize(uri); const textDocument = TextDocumentIdentifier.create(uriString); - const responsePromise = languageServer.sendRequestWithProgress( - CopilotRelatedDocumentsRequest.type, - { - _vs_textDocument: textDocument, - position: { - line: 0, - character: 0, - }, - }, - async (r) => buildResult(r, relatedFiles) - ); - try { - await responsePromise; + await languageServer.sendRequestWithProgress( + CopilotRelatedDocumentsRequest.type, + { + _vs_textDocument: textDocument, + position: { + line: 0, + character: 0, + }, + }, + async (r) => buildResult(r, relatedFiles) + ); } catch (e) { if (e instanceof Error) { - tracingChannel.appendLine(e.message); + channel.appendLine(e.message); } } return { entries: relatedFiles }; diff --git a/src/lsptoolshost/roslynLanguageServer.ts b/src/lsptoolshost/roslynLanguageServer.ts index 73a38b7b3..e525eec57 100644 --- a/src/lsptoolshost/roslynLanguageServer.ts +++ b/src/lsptoolshost/roslynLanguageServer.ts @@ -1037,7 +1037,7 @@ export async function activateRoslynLanguageServer( ); registerLanguageStatusItems(context, languageServer, languageServerEvents); - await registerCopilotExtensionAsync(languageServer, _traceChannel); + await registerCopilotExtensionAsync(languageServer, _channel); // Register any commands that need to be handled by the extension. registerCommands(context, languageServer, hostExecutableResolver, _channel);