diff --git a/extensions/typescript-language-features/src/languageFeatures/copilotRelated.ts b/extensions/typescript-language-features/src/languageFeatures/copilotRelated.ts new file mode 100644 index 0000000000000..f1ceac1a584a7 --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/copilotRelated.ts @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { isSupportedLanguageMode } from '../configuration/languageIds'; +import { DocumentSelector } from '../configuration/documentSelector'; +import { API } from '../tsServer/api'; +import type * as Proto from '../tsServer/protocol/protocol'; +import { ITypeScriptServiceClient } from '../typescriptService'; +import { conditionalRegistration, requireMinVersion } from './util/dependentRegistration'; + +const minVersion = API.v570; + +export function register( + selector: DocumentSelector, + client: ITypeScriptServiceClient, +): vscode.Disposable { + return conditionalRegistration([ + requireMinVersion(client, minVersion), + ], () => { + const ext = vscode.extensions.getExtension('github.copilot'); + if (!ext) { + return new vscode.Disposable(() => { }); + } + const disposers: vscode.Disposable[] = []; + ext.activate().then(() => { + const relatedAPI = ext.exports as { + registerRelatedFilesProvider( + providerId: { extensionId: string; languageId: string }, + callback: ( + uri: vscode.Uri, + context: { flags: Record }, + cancellationToken: vscode.CancellationToken + ) => Promise<{ + entries: vscode.Uri[]; + traits?: Array<{ name: string; value: string; includeInPrompt?: boolean; promptTextOverride?: string }>; + }> + ): vscode.Disposable; + } | undefined; + if (relatedAPI?.registerRelatedFilesProvider) { + for (const syntax of selector.syntax) { + if (!syntax.language) { + continue; + } + const id = { + extensionId: 'vscode.typescript-language-features', + languageId: syntax.language + }; + disposers.push(relatedAPI.registerRelatedFilesProvider(id, async (uri, _context, token) => { + let document; + try { + document = await vscode.workspace.openTextDocument(uri); + } catch { + if (!vscode.window.activeTextEditor) { + vscode.window.showErrorMessage(vscode.l10n.t("Related files provider failed. No active text editor.")); + return { entries: [] }; + } + // something is REALLY wrong if you can't open the active text editor's document, so don't catch that + document = await vscode.workspace.openTextDocument(vscode.window.activeTextEditor.document.uri); + } + + if (!isSupportedLanguageMode(document)) { + vscode.window.showErrorMessage(vscode.l10n.t("Related files provider failed. Copilot requested file with unsupported language mode.")); + return { entries: [] }; + } + + const file = client.toOpenTsFilePath(document); + if (!file) { + return { entries: [] }; + } + // @ts-expect-error until ts5.7 + const response = await client.execute('copilotRelated', { file, }, token) as Proto.CopilotRelatedResponse; + if (response.type !== 'response' || !response.body) { + return { entries: [] }; + } + // @ts-expect-error until ts5.7 + return { entries: response.body.relatedFiles.map(f => client.toResource(f)), traits: [] }; + })); + } + } + }); + return vscode.Disposable.from(...disposers); + }); +} diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 7b95591604bd0..09a4fe3ccc9c6 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -91,6 +91,7 @@ export default class LanguageProvider extends Disposable { import('./languageFeatures/sourceDefinition').then(provider => this._register(provider.register(this.client, this.commandManager))), import('./languageFeatures/tagClosing').then(provider => this._register(provider.register(selector, this.description, this.client))), import('./languageFeatures/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/copilotRelated').then(provider => this._register(provider.register(selector, this.client))), ]); } diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 90528ee47dc8c..33d89e1df8d5b 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -77,6 +77,8 @@ interface StandardTsServerRequests { 'getMoveToRefactoringFileSuggestions': [Proto.GetMoveToRefactoringFileSuggestionsRequestArgs, Proto.GetMoveToRefactoringFileSuggestions]; 'linkedEditingRange': [Proto.FileLocationRequestArgs, Proto.LinkedEditingRangeResponse]; 'mapCode': [Proto.MapCodeRequestArgs, Proto.MapCodeResponse]; + // @ts-expect-error until ts5.7 + 'copilotRelated': [Proto.FileRequestArgs, Proto.CopilotRelatedResponse]; 'getPasteEdits': [Proto.GetPasteEditsRequestArgs, Proto.GetPasteEditsResponse]; 'preparePasteEdits': [Proto.PreparePasteEditsRequestArgs, Proto.PreparePasteEditsResponse]; }