Skip to content

Commit 20c9e60

Browse files
authored
Merge branch 'main' into coleng/update_macos_runner
2 parents 6f0a266 + 9efde54 commit 20c9e60

File tree

10 files changed

+931
-146
lines changed

10 files changed

+931
-146
lines changed

CODEOWNERS

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Each line is a file pattern followed by one or more owners.
2+
3+
# These owners will be the default owners for everything in
4+
# the repo. Unless a later match takes precedence,
5+
# @microsoft/cpptools-maintainers will be requested for
6+
# review when someone opens a pull request.
7+
8+
* @microsoft/cpptools-maintainers

Extension/src/LanguageServer/client.ts

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,19 @@ export interface ChatContextResult {
541541
targetArchitecture: string;
542542
}
543543

544+
export interface FileContextResult {
545+
compilerArguments: string[];
546+
}
547+
548+
export interface ProjectContextResult {
549+
language: string;
550+
standardVersion: string;
551+
compiler: string;
552+
targetPlatform: string;
553+
targetArchitecture: string;
554+
fileContext: FileContextResult;
555+
}
556+
544557
// Requests
545558
const PreInitializationRequest: RequestType<void, string, void> = new RequestType<void, string, void>('cpptools/preinitialize');
546559
const InitializationRequest: RequestType<CppInitializationParams, void, void> = new RequestType<CppInitializationParams, void, void>('cpptools/initialize');
@@ -560,7 +573,8 @@ const GoToDirectiveInGroupRequest: RequestType<GoToDirectiveInGroupParams, Posit
560573
const GenerateDoxygenCommentRequest: RequestType<GenerateDoxygenCommentParams, GenerateDoxygenCommentResult | undefined, void> = new RequestType<GenerateDoxygenCommentParams, GenerateDoxygenCommentResult, void>('cpptools/generateDoxygenComment');
561574
const ChangeCppPropertiesRequest: RequestType<CppPropertiesParams, void, void> = new RequestType<CppPropertiesParams, void, void>('cpptools/didChangeCppProperties');
562575
const IncludesRequest: RequestType<GetIncludesParams, GetIncludesResult, void> = new RequestType<GetIncludesParams, GetIncludesResult, void>('cpptools/getIncludes');
563-
const CppContextRequest: RequestType<void, ChatContextResult, void> = new RequestType<void, ChatContextResult, void>('cpptools/getChatContext');
576+
const CppContextRequest: RequestType<TextDocumentIdentifier, ChatContextResult, void> = new RequestType<TextDocumentIdentifier, ChatContextResult, void>('cpptools/getChatContext');
577+
const ProjectContextRequest: RequestType<TextDocumentIdentifier, ProjectContextResult, void> = new RequestType<TextDocumentIdentifier, ProjectContextResult, void>('cpptools/getProjectContext');
564578

565579
// Notifications to the server
566580
const DidOpenNotification: NotificationType<DidOpenTextDocumentParams> = new NotificationType<DidOpenTextDocumentParams>('textDocument/didOpen');
@@ -762,7 +776,7 @@ export interface Client {
762776
PauseCodeAnalysis(): void;
763777
ResumeCodeAnalysis(): void;
764778
CancelCodeAnalysis(): void;
765-
handleConfigurationSelectCommand(): Promise<void>;
779+
handleConfigurationSelectCommand(config?: string): Promise<void>;
766780
handleConfigurationProviderSelectCommand(): Promise<void>;
767781
handleShowActiveCodeAnalysisCommands(): Promise<void>;
768782
handleShowIdleCodeAnalysisCommands(): Promise<void>;
@@ -791,7 +805,8 @@ export interface Client {
791805
setShowConfigureIntelliSenseButton(show: boolean): void;
792806
addTrustedCompiler(path: string): Promise<void>;
793807
getIncludes(maxDepth: number, token: vscode.CancellationToken): Promise<GetIncludesResult>;
794-
getChatContext(token: vscode.CancellationToken): Promise<ChatContextResult>;
808+
getChatContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ChatContextResult>;
809+
getProjectContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ProjectContextResult>;
795810
}
796811

797812
export function createClient(workspaceFolder?: vscode.WorkspaceFolder): Client {
@@ -2220,10 +2235,18 @@ export class DefaultClient implements Client {
22202235
() => this.languageClient.sendRequest(IncludesRequest, params, token), token);
22212236
}
22222237

2223-
public async getChatContext(token: vscode.CancellationToken): Promise<ChatContextResult> {
2238+
public async getChatContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ChatContextResult> {
2239+
const params: TextDocumentIdentifier = { uri: uri.toString() };
2240+
await withCancellation(this.ready, token);
2241+
return DefaultClient.withLspCancellationHandling(
2242+
() => this.languageClient.sendRequest(CppContextRequest, params, token), token);
2243+
}
2244+
2245+
public async getProjectContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ProjectContextResult> {
2246+
const params: TextDocumentIdentifier = { uri: uri.toString() };
22242247
await withCancellation(this.ready, token);
22252248
return DefaultClient.withLspCancellationHandling(
2226-
() => this.languageClient.sendRequest(CppContextRequest, null, token), token);
2249+
() => this.languageClient.sendRequest(ProjectContextRequest, params, token), token);
22272250
}
22282251

22292252
/**
@@ -3248,11 +3271,11 @@ export class DefaultClient implements Client {
32483271
/**
32493272
* command handlers
32503273
*/
3251-
public async handleConfigurationSelectCommand(): Promise<void> {
3274+
public async handleConfigurationSelectCommand(config?: string): Promise<void> {
32523275
await this.ready;
32533276
const configNames: string[] | undefined = this.configuration.ConfigurationNames;
32543277
if (configNames) {
3255-
const index: number = await ui.showConfigurations(configNames);
3278+
const index: number = config ? configNames.indexOf(config) : await ui.showConfigurations(configNames);
32563279
if (index < 0) {
32573280
return;
32583281
}
@@ -4129,5 +4152,6 @@ class NullClient implements Client {
41294152
setShowConfigureIntelliSenseButton(show: boolean): void { }
41304153
addTrustedCompiler(path: string): Promise<void> { return Promise.resolve(); }
41314154
getIncludes(maxDepth: number, token: vscode.CancellationToken): Promise<GetIncludesResult> { return Promise.resolve({} as GetIncludesResult); }
4132-
getChatContext(token: vscode.CancellationToken): Promise<ChatContextResult> { return Promise.resolve({} as ChatContextResult); }
4155+
getChatContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ChatContextResult> { return Promise.resolve({} as ChatContextResult); }
4156+
getProjectContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ProjectContextResult> { return Promise.resolve({} as ProjectContextResult); }
41334157
}

Extension/src/LanguageServer/copilotProviders.ts

Lines changed: 111 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
'use strict';
66

77
import * as vscode from 'vscode';
8+
import { localize } from 'vscode-nls';
89
import * as util from '../common';
9-
import { ChatContextResult, GetIncludesResult } from './client';
10+
import * as logger from '../logger';
11+
import * as telemetry from '../telemetry';
12+
import { GetIncludesResult } from './client';
1013
import { getActiveClient } from './extension';
14+
import { getCompilerArgumentFilterMap, getProjectContext } from './lmTool';
1115

1216
export interface CopilotTrait {
1317
name: string;
@@ -34,35 +38,113 @@ export async function registerRelatedFilesProvider(): Promise<void> {
3438
for (const languageId of ['c', 'cpp', 'cuda-cpp']) {
3539
api.registerRelatedFilesProvider(
3640
{ extensionId: util.extensionContext.extension.id, languageId },
37-
async (_uri: vscode.Uri, context: { flags: Record<string, unknown> }, token: vscode.CancellationToken) => {
38-
39-
const getIncludesHandler = async () => (await getIncludesWithCancellation(1, token))?.includedFiles.map(file => vscode.Uri.file(file)) ?? [];
40-
const getTraitsHandler = async () => {
41-
const chatContext: ChatContextResult | undefined = await (getActiveClient().getChatContext(token) ?? undefined);
42-
43-
if (!chatContext) {
44-
return undefined;
41+
async (uri: vscode.Uri, context: { flags: Record<string, unknown> }, token: vscode.CancellationToken) => {
42+
const start = performance.now();
43+
const telemetryProperties: Record<string, string> = {};
44+
const telemetryMetrics: Record<string, number> = {};
45+
try {
46+
const getIncludesHandler = async () => (await getIncludesWithCancellation(1, token))?.includedFiles.map(file => vscode.Uri.file(file)) ?? [];
47+
const getTraitsHandler = async () => {
48+
const projectContext = await getProjectContext(uri, context, token);
49+
50+
if (!projectContext) {
51+
return undefined;
52+
}
53+
54+
let traits: CopilotTrait[] = [
55+
{ name: "intelliSenseDisclaimer", value: '', includeInPrompt: true, promptTextOverride: `IntelliSense is currently configured with the following compiler information. It reflects the active configuration, and the project may have more configurations targeting different platforms.` },
56+
{ name: "intelliSenseDisclaimerBeginning", value: '', includeInPrompt: true, promptTextOverride: `Beginning of IntelliSense information.` }
57+
];
58+
if (projectContext.language) {
59+
traits.push({ name: "language", value: projectContext.language, includeInPrompt: true, promptTextOverride: `The language is ${projectContext.language}.` });
60+
}
61+
if (projectContext.compiler) {
62+
traits.push({ name: "compiler", value: projectContext.compiler, includeInPrompt: true, promptTextOverride: `This project compiles using ${projectContext.compiler}.` });
63+
}
64+
if (projectContext.standardVersion) {
65+
traits.push({ name: "standardVersion", value: projectContext.standardVersion, includeInPrompt: true, promptTextOverride: `This project uses the ${projectContext.standardVersion} language standard.` });
66+
}
67+
if (projectContext.targetPlatform) {
68+
traits.push({ name: "targetPlatform", value: projectContext.targetPlatform, includeInPrompt: true, promptTextOverride: `This build targets ${projectContext.targetPlatform}.` });
69+
}
70+
if (projectContext.targetArchitecture) {
71+
traits.push({ name: "targetArchitecture", value: projectContext.targetArchitecture, includeInPrompt: true, promptTextOverride: `This build targets ${projectContext.targetArchitecture}.` });
72+
}
73+
74+
if (projectContext.compiler) {
75+
// We will process compiler arguments based on copilotcppXXXCompilerArgumentFilters and copilotcppCompilerArgumentDirectAskMap feature flags.
76+
// The copilotcppXXXCompilerArgumentFilters are maps. The keys are regex strings for filtering and the values, if not empty,
77+
// are the prompt text to use when no arguments are found.
78+
// copilotcppCompilerArgumentDirectAskMap map individual matched argument to a prompt text.
79+
// For duplicate matches, the last one will be used.
80+
const filterMap = getCompilerArgumentFilterMap(projectContext.compiler, context);
81+
if (filterMap !== undefined) {
82+
const directAskMap: Record<string, string> = context.flags.copilotcppCompilerArgumentDirectAskMap ? JSON.parse(context.flags.copilotcppCompilerArgumentDirectAskMap as string) : {};
83+
let directAsks: string = '';
84+
const remainingArguments: string[] = [];
85+
86+
for (const key in filterMap) {
87+
if (!key) {
88+
continue;
89+
}
90+
91+
const matchedArgument = projectContext.compilerArguments[key] as string;
92+
if (matchedArgument?.length > 0) {
93+
if (directAskMap[matchedArgument]) {
94+
directAsks += `${directAskMap[matchedArgument]} `;
95+
} else {
96+
remainingArguments.push(matchedArgument);
97+
}
98+
} else if (filterMap[key]) {
99+
// Use the prompt text in the absence of argument.
100+
directAsks += `${filterMap[key]} `;
101+
}
102+
}
103+
104+
if (remainingArguments.length > 0) {
105+
const compilerArgumentsValue = remainingArguments.join(", ");
106+
traits.push({ name: "compilerArguments", value: compilerArgumentsValue, includeInPrompt: true, promptTextOverride: `The compiler arguments include: ${compilerArgumentsValue}.` });
107+
}
108+
109+
if (directAsks) {
110+
traits.push({ name: "directAsks", value: directAsks, includeInPrompt: true, promptTextOverride: directAsks });
111+
}
112+
}
113+
}
114+
115+
traits.push({ name: "intelliSenseDisclaimerEnd", value: '', includeInPrompt: true, promptTextOverride: `End of IntelliSense information.` });
116+
117+
const includeTraitsArray = context.flags.copilotcppIncludeTraits ? context.flags.copilotcppIncludeTraits as string[] : [];
118+
const includeTraits = new Set(includeTraitsArray);
119+
telemetryProperties["includeTraits"] = includeTraitsArray.join(',');
120+
121+
// standardVersion trait is enabled by default.
122+
traits = traits.filter(trait => includeTraits.has(trait.name) || trait.name === 'standardVersion');
123+
124+
telemetryProperties["traits"] = traits.map(trait => trait.name).join(',');
125+
return traits.length > 0 ? traits : undefined;
126+
};
127+
128+
// Call both handlers in parallel
129+
const traitsPromise = getTraitsHandler();
130+
const includesPromise = getIncludesHandler();
131+
132+
return { entries: await includesPromise, traits: await traitsPromise };
133+
}
134+
catch (exception) {
135+
try {
136+
const err: Error = exception as Error;
137+
logger.getOutputChannelLogger().appendLine(localize("copilot.relatedfilesprovider.error", "Error while retrieving result. Reason: {0}", err.message));
45138
}
46-
47-
let traits: CopilotTrait[] = [
48-
{ name: "language", value: chatContext.language, includeInPrompt: true, promptTextOverride: `The language is ${chatContext.language}.` },
49-
{ name: "compiler", value: chatContext.compiler, includeInPrompt: true, promptTextOverride: `This project compiles using ${chatContext.compiler}.` },
50-
{ name: "standardVersion", value: chatContext.standardVersion, includeInPrompt: true, promptTextOverride: `This project uses the ${chatContext.standardVersion} language standard.` },
51-
{ name: "targetPlatform", value: chatContext.targetPlatform, includeInPrompt: true, promptTextOverride: `This build targets ${chatContext.targetPlatform}.` },
52-
{ name: "targetArchitecture", value: chatContext.targetArchitecture, includeInPrompt: true, promptTextOverride: `This build targets ${chatContext.targetArchitecture}.` }
53-
];
54-
55-
const excludeTraits = context.flags.copilotcppExcludeTraits as string[] ?? [];
56-
traits = traits.filter(trait => !excludeTraits.includes(trait.name));
57-
58-
return traits.length > 0 ? traits : undefined;
59-
};
60-
61-
// Call both handlers in parallel
62-
const traitsPromise = ((context.flags.copilotcppTraits as boolean) ?? false) ? getTraitsHandler() : Promise.resolve(undefined);
63-
const includesPromise = getIncludesHandler();
64-
65-
return { entries: await includesPromise, traits: await traitsPromise };
139+
catch {
140+
// Intentionally swallow any exception.
141+
}
142+
telemetryProperties["error"] = "true";
143+
throw exception; // Throw the exception for auto-retry.
144+
} finally {
145+
telemetryMetrics['duration'] = performance.now() - start;
146+
telemetry.logCopilotEvent('RelatedFilesProvider', telemetryProperties, telemetryMetrics);
147+
}
66148
}
67149
);
68150
}

Extension/src/LanguageServer/extension.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -584,13 +584,13 @@ async function installCompiler(sender?: any): Promise<void> {
584584
telemetry.logLanguageServerEvent('installCompiler', telemetryProperties);
585585
}
586586

587-
async function onSelectConfiguration(): Promise<void> {
587+
async function onSelectConfiguration(config?: string): Promise<void> {
588588
if (!isFolderOpen()) {
589589
void vscode.window.showInformationMessage(localize("configuration.select.first", 'Open a folder first to select a configuration.'));
590590
} else {
591591
// This only applies to the active client. You cannot change the configuration for
592592
// a client that is not active since that client's UI will not be visible.
593-
return clients.ActiveClient.handleConfigurationSelectCommand();
593+
return clients.ActiveClient.handleConfigurationSelectCommand(config);
594594
}
595595
}
596596

0 commit comments

Comments
 (0)