Skip to content

Commit 731cccd

Browse files
authored
Add C++ configuration as a language model tool (luca) (#12685)
1 parent 8718b96 commit 731cccd

File tree

8 files changed

+288
-107
lines changed

8 files changed

+288
-107
lines changed

Extension/.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
},
3838
"[typescript]": {
3939
"editor.tabSize": 4,
40-
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
40+
"editor.defaultFormatter": "vscode.typescript-language-features",
4141
"editor.formatOnSave": true,
4242
"files.insertFinalNewline": true,
4343
"editor.codeActionsOnSave": {

Extension/package.json

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@
3838
"Snippets"
3939
],
4040
"enabledApiProposals": [
41-
"terminalDataWriteEvent"
41+
"terminalDataWriteEvent",
42+
"lmTools"
4243
],
4344
"capabilities": {
4445
"untrustedWorkspaces": {
@@ -6440,6 +6441,19 @@
64406441
"description": "%c_cpp.codeActions.refactor.extract.function.description%"
64416442
}
64426443
}
6444+
],
6445+
"languageModelTools": [
6446+
{
6447+
"id": "cpptools-lmtool-configuration",
6448+
"name": "cpp",
6449+
"displayName": "%c_cpp.languageModelTools.configuration.displayName%",
6450+
"canBeInvokedManually": true,
6451+
"userDescription": "%c_cpp.languageModelTools.configuration.userDescription%",
6452+
"modelDescription": "For the active C or C++ file, this tool provides: the language (C, C++, or CUDA), the language standard version (such as C++11, C++14, C++17, or C++20), the compiler (such as GCC, Clang, or MSVC), the target platform (such as x86, x64, or ARM), and the target architecture (such as 32-bit or 64-bit).",
6453+
"icon": "$(file-code)",
6454+
"parametersSchema": {},
6455+
"when": "(config.C_Cpp.experimentalFeatures =~ /^[eE]nabled$/)"
6456+
}
64436457
]
64446458
},
64456459
"scripts": {

Extension/package.nls.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1006,5 +1006,7 @@
10061006
"c_cpp.configuration.refactoring.includeHeader.markdownDescription": "Controls whether to include the header file of a refactored function/symbol to its corresponding source file when doing a refactoring action, such as create declaration/definition.",
10071007
"c_cpp.configuration.refactoring.includeHeader.always.description": "Always include the header file if it is not included explicitly in its source file.",
10081008
"c_cpp.configuration.refactoring.includeHeader.ifNeeded.description": "Only include the header file if it is not included explicitly in its source file or through implicit inclusion.",
1009-
"c_cpp.configuration.refactoring.includeHeader.never.description": "Never include the header file."
1009+
"c_cpp.configuration.refactoring.includeHeader.never.description": "Never include the header file.",
1010+
"c_cpp.languageModelTools.configuration.displayName": "C/C++ configuration",
1011+
"c_cpp.languageModelTools.configuration.userDescription": "Configuration of the active C or C++ file, like language standard version and target platform."
10101012
}

Extension/src/LanguageServer/client.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import * as fs from 'fs';
2727
import * as os from 'os';
2828
import { SourceFileConfiguration, SourceFileConfigurationItem, Version, WorkspaceBrowseConfiguration } from 'vscode-cpptools';
2929
import { IntelliSenseStatus, Status } from 'vscode-cpptools/out/testApi';
30-
import { CloseAction, DidOpenTextDocumentParams, ErrorAction, LanguageClientOptions, NotificationType, Position, Range, RequestType, TextDocumentIdentifier, TextDocumentPositionParams } from 'vscode-languageclient';
30+
import { CloseAction, DidOpenTextDocumentParams, ErrorAction, LanguageClientOptions, NotificationType, Position, Range, RequestType, ResponseError, TextDocumentIdentifier, TextDocumentPositionParams } from 'vscode-languageclient';
3131
import { LanguageClient, ServerOptions } from 'vscode-languageclient/node';
3232
import * as nls from 'vscode-nls';
3333
import { DebugConfigurationProvider } from '../Debugger/configurationProvider';
@@ -58,12 +58,12 @@ import { cachedEditorConfigSettings, getEditorConfigSettings } from './editorCon
5858
import { CppSourceStr, clients, configPrefix, updateLanguageConfigurations, usesCrashHandler, watchForCrashes } from './extension';
5959
import { LocalizeStringParams, getLocaleId, getLocalizedString } from './localization';
6060
import { PersistentFolderState, PersistentWorkspaceState } from './persistentState';
61-
import { createProtocolFilter } from './protocolFilter';
61+
import { RequestCancelled, ServerCancelled, createProtocolFilter } from './protocolFilter';
6262
import * as refs from './references';
6363
import { CppSettings, OtherSettings, SettingsParams, WorkspaceFolderSettingsParams } from './settings';
6464
import { SettingsTracker } from './settingsTracker';
6565
import { ConfigurationType, LanguageStatusUI, getUI } from './ui';
66-
import { handleChangedFromCppToC, makeLspRange, makeVscodeLocation, makeVscodeRange } from './utils';
66+
import { handleChangedFromCppToC, makeLspRange, makeVscodeLocation, makeVscodeRange, withCancellation } from './utils';
6767
import minimatch = require("minimatch");
6868

6969
function deepCopy(obj: any) {
@@ -542,6 +542,14 @@ interface GetIncludesResult {
542542
includedFiles: string[];
543543
}
544544

545+
export interface ChatContextResult {
546+
language: string;
547+
standardVersion: string;
548+
compiler: string;
549+
targetPlatform: string;
550+
targetArchitecture: string;
551+
}
552+
545553
// Requests
546554
const PreInitializationRequest: RequestType<void, string, void> = new RequestType<void, string, void>('cpptools/preinitialize');
547555
const InitializationRequest: RequestType<CppInitializationParams, void, void> = new RequestType<CppInitializationParams, void, void>('cpptools/initialize');
@@ -562,6 +570,7 @@ const GoToDirectiveInGroupRequest: RequestType<GoToDirectiveInGroupParams, Posit
562570
const GenerateDoxygenCommentRequest: RequestType<GenerateDoxygenCommentParams, GenerateDoxygenCommentResult | undefined, void> = new RequestType<GenerateDoxygenCommentParams, GenerateDoxygenCommentResult, void>('cpptools/generateDoxygenComment');
563571
const ChangeCppPropertiesRequest: RequestType<CppPropertiesParams, void, void> = new RequestType<CppPropertiesParams, void, void>('cpptools/didChangeCppProperties');
564572
const IncludesRequest: RequestType<GetIncludesParams, GetIncludesResult, void> = new RequestType<GetIncludesParams, GetIncludesResult, void>('cpptools/getIncludes');
573+
const CppContextRequest: RequestType<void, ChatContextResult, void> = new RequestType<void, ChatContextResult, void>('cpptools/getChatContext');
565574

566575
// Notifications to the server
567576
const DidOpenNotification: NotificationType<DidOpenTextDocumentParams> = new NotificationType<DidOpenTextDocumentParams>('textDocument/didOpen');
@@ -792,6 +801,7 @@ export interface Client {
792801
setShowConfigureIntelliSenseButton(show: boolean): void;
793802
addTrustedCompiler(path: string): Promise<void>;
794803
getIncludes(maxDepth: number): Promise<GetIncludesResult>;
804+
getChatContext(token: vscode.CancellationToken): Promise<ChatContextResult>;
795805
}
796806

797807
export function createClient(workspaceFolder?: vscode.WorkspaceFolder): Client {
@@ -2234,6 +2244,25 @@ export class DefaultClient implements Client {
22342244
return this.languageClient.sendRequest(IncludesRequest, params);
22352245
}
22362246

2247+
public async getChatContext(token: vscode.CancellationToken): Promise<ChatContextResult> {
2248+
await withCancellation(this.ready, token);
2249+
let result: ChatContextResult;
2250+
try {
2251+
result = await this.languageClient.sendRequest(CppContextRequest, null, token);
2252+
} catch (e: any) {
2253+
if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) {
2254+
throw new vscode.CancellationError();
2255+
}
2256+
2257+
throw e;
2258+
}
2259+
if (token.isCancellationRequested) {
2260+
throw new vscode.CancellationError();
2261+
}
2262+
2263+
return result;
2264+
}
2265+
22372266
/**
22382267
* a Promise that can be awaited to know when it's ok to proceed.
22392268
*
@@ -4119,4 +4148,5 @@ class NullClient implements Client {
41194148
setShowConfigureIntelliSenseButton(show: boolean): void { }
41204149
addTrustedCompiler(path: string): Promise<void> { return Promise.resolve(); }
41214150
getIncludes(): Promise<GetIncludesResult> { return Promise.resolve({} as GetIncludesResult); }
4151+
getChatContext(token: vscode.CancellationToken): Promise<ChatContextResult> { return Promise.resolve({} as ChatContextResult); }
41224152
}

Extension/src/LanguageServer/extension.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { CodeActionDiagnosticInfo, CodeAnalysisDiagnosticIdentifiersAndUri, code
2626
import { CppBuildTaskProvider } from './cppBuildTaskProvider';
2727
import { getCustomConfigProviders } from './customProviders';
2828
import { getLanguageConfig } from './languageConfig';
29+
import { CppConfigurationLanguageModelTool } from './lmTool';
2930
import { PersistentState } from './persistentState';
3031
import { NodeType, TreeNode } from './referencesModel';
3132
import { CppSettings } from './settings';
@@ -248,6 +249,11 @@ export async function activate(): Promise<void> {
248249
clients.timeTelemetryCollector.setFirstFile(activeEditor.document.uri);
249250
activeDocument = activeEditor.document;
250251
}
252+
253+
if (util.extensionContext && new CppSettings().experimentalFeatures) {
254+
const tool = vscode.lm.registerTool('cpptools-lmtool-configuration', new CppConfigurationLanguageModelTool());
255+
disposables.push(tool);
256+
}
251257
}
252258

253259
export function updateLanguageConfigurations(): void {
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/* --------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All Rights Reserved.
3+
* See 'LICENSE' in the project root for license information.
4+
* ------------------------------------------------------------------------------------------ */
5+
'use strict';
6+
7+
import * as vscode from 'vscode';
8+
import { localize } from 'vscode-nls';
9+
import * as util from '../common';
10+
import * as logger from '../logger';
11+
import * as telemetry from '../telemetry';
12+
import { ChatContextResult } from './client';
13+
import { getClients } from './extension';
14+
15+
const knownValues: { [Property in keyof ChatContextResult]?: { [id: string]: string } } = {
16+
language: {
17+
'c': 'C',
18+
'cpp': 'C++',
19+
'cuda-cpp': 'CUDA C++'
20+
},
21+
compiler: {
22+
'msvc': 'MSVC',
23+
'clang': 'Clang',
24+
'gcc': 'GCC'
25+
},
26+
standardVersion: {
27+
'c++98': 'C++98',
28+
'c++03': 'C++03',
29+
'c++11': 'C++11',
30+
'c++14': 'C++14',
31+
'c++17': 'C++17',
32+
'c++20': 'C++20',
33+
'c++23': 'C++23',
34+
'c90': "C90",
35+
'c99': "C99",
36+
'c11': "C11",
37+
'c17': "C17",
38+
'c23': "C23"
39+
},
40+
targetPlatform: {
41+
'windows': 'Windows',
42+
'Linux': 'Linux',
43+
'macos': 'macOS'
44+
}
45+
};
46+
47+
class StringLanguageModelToolResult implements vscode.LanguageModelToolResult {
48+
public constructor(public readonly value: string) { }
49+
public toString(): string { return this.value; }
50+
}
51+
52+
export class CppConfigurationLanguageModelTool implements vscode.LanguageModelTool {
53+
public async invoke(_parameters: any, token: vscode.CancellationToken): Promise<vscode.LanguageModelToolResult> {
54+
return new StringLanguageModelToolResult(await this.getContext(token));
55+
}
56+
57+
private async getContext(token: vscode.CancellationToken): Promise<string> {
58+
try {
59+
const currentDoc = vscode.window.activeTextEditor?.document;
60+
if (!currentDoc || (!util.isCpp(currentDoc) && !util.isHeaderFile(currentDoc.uri))) {
61+
return 'The active document is not a C, C++, or CUDA file.';
62+
}
63+
64+
const chatContext: ChatContextResult | undefined = await (getClients()?.ActiveClient?.getChatContext(token) ?? undefined);
65+
if (!chatContext) {
66+
return 'No configuration information is available for the active document.';
67+
}
68+
69+
telemetry.logLanguageModelToolEvent(
70+
'cpp',
71+
{
72+
"language": chatContext.language,
73+
"compiler": chatContext.compiler,
74+
"standardVersion": chatContext.standardVersion,
75+
"targetPlatform": chatContext.targetPlatform,
76+
"targetArchitecture": chatContext.targetArchitecture
77+
});
78+
79+
for (const key in knownValues) {
80+
const knownKey = key as keyof ChatContextResult;
81+
if (knownValues[knownKey] && chatContext[knownKey]) {
82+
chatContext[knownKey] = knownValues[knownKey][chatContext[knownKey]] || chatContext[knownKey];
83+
}
84+
}
85+
86+
return `The user is working on a ${chatContext.language} project. The project uses language version ${chatContext.standardVersion}, compiles using the ${chatContext.compiler} compiler, targets the ${chatContext.targetPlatform} platform, and targets the ${chatContext.targetArchitecture} architecture.`;
87+
}
88+
catch {
89+
await this.reportError();
90+
return "";
91+
}
92+
}
93+
94+
private async reportError(): Promise<void> {
95+
try {
96+
logger.getOutputChannelLogger().appendLine(localize("copilot.cppcontext.error", "Error while retrieving the #cpp context."));
97+
}
98+
catch {
99+
// Intentionally swallow any exception.
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)