From 57a3f1b90a98d7b9f1e705ea219799c2285b4d6a Mon Sep 17 00:00:00 2001 From: luca cappa Date: Thu, 29 Feb 2024 10:28:15 -0800 Subject: [PATCH] #cpp chat variable --- Extension/package.json | 4 +- Extension/src/LanguageServer/client.ts | 16 +++++ Extension/src/LanguageServer/extension.ts | 78 ++++++++++++++++++++++- Extension/src/telemetry.ts | 14 ++++ 4 files changed, 110 insertions(+), 2 deletions(-) diff --git a/Extension/package.json b/Extension/package.json index fd99aa4f64..022a2d67bb 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -38,7 +38,9 @@ "Snippets" ], "enabledApiProposals": [ - "terminalDataWriteEvent" + "terminalDataWriteEvent", + "chatParticipant", + "chatVariableResolver" ], "capabilities": { "untrustedWorkspaces": { diff --git a/Extension/src/LanguageServer/client.ts b/Extension/src/LanguageServer/client.ts index 177fa9e50c..f8dc1657cc 100644 --- a/Extension/src/LanguageServer/client.ts +++ b/Extension/src/LanguageServer/client.ts @@ -536,6 +536,14 @@ interface DidChangeActiveEditorParams { selection?: Range; } +export interface ChatContextResult { + language: string; + standardVersion: string; + compiler: string; + targetPlatform: string; + targetArchitecture: string; +} + // Requests const InitializationRequest: RequestType = new RequestType('cpptools/initialize'); const QueryCompilerDefaultsRequest: RequestType = new RequestType('cpptools/queryCompilerDefaults'); @@ -553,6 +561,7 @@ const ExtractToFunctionRequest: RequestType = new RequestType('cpptools/goToDirectiveInGroup'); const GenerateDoxygenCommentRequest: RequestType = new RequestType('cpptools/generateDoxygenComment'); const ChangeCppPropertiesRequest: RequestType = new RequestType('cpptools/didChangeCppProperties'); +const CppContextRequest: RequestType = new RequestType('cpptools/getChatContext'); // Notifications to the server const DidOpenNotification: NotificationType = new NotificationType('textDocument/didOpen'); @@ -778,6 +787,7 @@ export interface Client { getShowConfigureIntelliSenseButton(): boolean; setShowConfigureIntelliSenseButton(show: boolean): void; addTrustedCompiler(path: string): Promise; + getChatContext(): Promise; } export function createClient(workspaceFolder?: vscode.WorkspaceFolder): Client { @@ -2187,6 +2197,11 @@ export class DefaultClient implements Client { await this.languageClient.sendNotification(DidOpenNotification, params); } + public async getChatContext(): Promise { + await this.ready; + return this.languageClient.sendRequest(CppContextRequest, null); + } + /** * a Promise that can be awaited to know when it's ok to proceed. * @@ -4007,4 +4022,5 @@ class NullClient implements Client { getShowConfigureIntelliSenseButton(): boolean { return false; } setShowConfigureIntelliSenseButton(show: boolean): void { } addTrustedCompiler(path: string): Promise { return Promise.resolve(); } + getChatContext(): Promise { return Promise.resolve({} as ChatContextResult); } } diff --git a/Extension/src/LanguageServer/extension.ts b/Extension/src/LanguageServer/extension.ts index 5088c6aec8..9c11e88a29 100644 --- a/Extension/src/LanguageServer/extension.ts +++ b/Extension/src/LanguageServer/extension.ts @@ -19,7 +19,7 @@ import { logAndReturn } from '../Utility/Async/returns'; import * as util from '../common'; import { PlatformInformation } from '../platform'; import * as telemetry from '../telemetry'; -import { Client, DefaultClient, DoxygenCodeActionCommandArguments, openFileVersions } from './client'; +import { ChatContextResult, Client, DefaultClient, DoxygenCodeActionCommandArguments, openFileVersions } from './client'; import { ClientCollection } from './clientCollection'; import { CodeActionDiagnosticInfo, CodeAnalysisDiagnosticIdentifiersAndUri, codeAnalysisAllFixes, codeAnalysisCodeToFixes, codeAnalysisFileToCodeActions } from './codeAnalysis'; import { CppBuildTaskProvider } from './cppBuildTaskProvider'; @@ -246,6 +246,78 @@ export async function activate(): Promise { clients.timeTelemetryCollector.setFirstFile(activeEditor.document.uri); activeDocument = activeEditor.document; } + + await setupCppChatVariable(util.extensionContext); +} + +export async function setupCppChatVariable(context: vscode.ExtensionContext | undefined): Promise +{ + if (!context) { + return; + } + + vscode.chat.registerChatVariableResolver('cpp', + `Describes the the C++ language features that can be used according + to the following information: the C++ language standard version, the target architecture and target operating system.`, { + resolve: async (name, _context, _token) => { + function fixUpLanguage(languageId: string) { + const languageIdToLanguage: { [id: string]: string } = + { 'c': 'C', 'cpp': 'C++', 'cuda-cpp': 'CUDA C++' }; + return languageIdToLanguage[languageId] || languageId; + } + + function fixUpCompiler(compilerId: string) { + const compilerIdToCompiler: { [id: string]: string } = + { 'msvc': 'MSVC', 'clang': 'Clang', 'gcc': 'GCC' }; + return compilerIdToCompiler[compilerId] || compilerId; + } + + function fixUpStandardVersion(stdVerId: string) { + const stdVerIdToStdVer: { [id: string]: string } = + { 'c++98': 'C++98', 'c++03': 'C++03', 'c++11': 'C++11', 'c++14': 'C++14', 'c++17': 'C++17', + 'c++20': 'C++20', 'c++23': 'C++23', 'c90' : "C90", 'c99': "C99", 'c11': "C11", 'c17': "C17", + 'c23': "C23" }; + return stdVerIdToStdVer[stdVerId] || stdVerId; + } + + function fixTargetPlatform(targetPlatformId: string) { + const platformIdToPlatform: { [id: string]: string } = { 'windows': 'Windows', 'Linux': 'Linux', 'macos': 'macOS' }; + return platformIdToPlatform[targetPlatformId] || targetPlatformId; + } + + if (name !== 'cpp') { + return undefined; + } + + // Return undefined if the active document is not a C++/C/CUDA/Header file. + const currentDoc = vscode.window.activeTextEditor?.document; + if (!currentDoc || (!util.isCpp(currentDoc) && !util.isHeaderFile(currentDoc.uri))) { + return undefined; + } + + const chatContext: ChatContextResult | undefined = await getChatContext(); + if (!chatContext) { + return undefined; + } + + telemetry.logCppChatVariableEvent('cpp', + { "language": chatContext.language, "compiler": chatContext.compiler, "standardVersion": chatContext.standardVersion, + "targetPlatform": chatContext.targetPlatform, "targetArchitecture": chatContext.targetArchitecture }); + + const language = fixUpLanguage(chatContext.language); + const compiler = fixUpCompiler(chatContext.compiler); + const standardVersion = fixUpStandardVersion(chatContext.standardVersion); + const targetPlatform = fixTargetPlatform(chatContext.targetPlatform); + const value = `I am working on a project of the following nature: +- Using ${language}. Prefer a solution written in ${language} to any other language. Call out which language you are using in the answer. +- Using the ${language} standard language version ${standardVersion}. Prefer solutions using the new and more recent features introduced in ${standardVersion}. Call out which standard version you are using in the answer. +- Using the ${compiler} compiler. Prefer solutions supported by the ${compiler} compiler. +- Targeting the ${targetPlatform} platform. Prefer solutions and API that are supported on ${targetPlatform}. +- Targeting the ${chatContext.targetArchitecture} architecture. Prefer solutions and techniques that are supported on the ${chatContext.targetArchitecture} architecture. +`; + return [ { level: vscode.ChatVariableLevel.Full, value: value } ]; + } + }); } export function updateLanguageConfigurations(): void { @@ -1305,3 +1377,7 @@ export async function preReleaseCheck(): Promise { } } } + +export async function getChatContext(): Promise { + return clients?.ActiveClient?.getChatContext() ?? undefined; +} diff --git a/Extension/src/telemetry.ts b/Extension/src/telemetry.ts index 7e079cd79a..6ad3e7675e 100644 --- a/Extension/src/telemetry.ts +++ b/Extension/src/telemetry.ts @@ -123,6 +123,20 @@ export function logLanguageServerEvent(eventName: string, properties?: Record, metrics?: Record): void { + const sendTelemetry = () => { + if (experimentationTelemetry) { + const eventNamePrefix: string = "C_Cpp/Copilot/Chat/Variable/"; + experimentationTelemetry.sendTelemetryEvent(eventNamePrefix + eventName, properties, metrics); + } + }; + + if (is.promise(initializationPromise)) { + return void initializationPromise.catch(logAndReturn.undefined).then(sendTelemetry).catch(logAndReturn.undefined); + } + sendTelemetry(); +} + function getPackageInfo(): IPackageInfo { return { name: util.packageJson.publisher + "." + util.packageJson.name,