Skip to content

Commit

Permalink
Merge branch 'main' into coleng/update_macos_runner
Browse files Browse the repository at this point in the history
  • Loading branch information
Colengms authored Dec 3, 2024
2 parents 6f0a266 + 9efde54 commit 20c9e60
Show file tree
Hide file tree
Showing 10 changed files with 931 additions and 146 deletions.
8 changes: 8 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Each line is a file pattern followed by one or more owners.

# These owners will be the default owners for everything in
# the repo. Unless a later match takes precedence,
# @microsoft/cpptools-maintainers will be requested for
# review when someone opens a pull request.

* @microsoft/cpptools-maintainers
40 changes: 32 additions & 8 deletions Extension/src/LanguageServer/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,19 @@ export interface ChatContextResult {
targetArchitecture: string;
}

export interface FileContextResult {
compilerArguments: string[];
}

export interface ProjectContextResult {
language: string;
standardVersion: string;
compiler: string;
targetPlatform: string;
targetArchitecture: string;
fileContext: FileContextResult;
}

// Requests
const PreInitializationRequest: RequestType<void, string, void> = new RequestType<void, string, void>('cpptools/preinitialize');
const InitializationRequest: RequestType<CppInitializationParams, void, void> = new RequestType<CppInitializationParams, void, void>('cpptools/initialize');
Expand All @@ -560,7 +573,8 @@ const GoToDirectiveInGroupRequest: RequestType<GoToDirectiveInGroupParams, Posit
const GenerateDoxygenCommentRequest: RequestType<GenerateDoxygenCommentParams, GenerateDoxygenCommentResult | undefined, void> = new RequestType<GenerateDoxygenCommentParams, GenerateDoxygenCommentResult, void>('cpptools/generateDoxygenComment');
const ChangeCppPropertiesRequest: RequestType<CppPropertiesParams, void, void> = new RequestType<CppPropertiesParams, void, void>('cpptools/didChangeCppProperties');
const IncludesRequest: RequestType<GetIncludesParams, GetIncludesResult, void> = new RequestType<GetIncludesParams, GetIncludesResult, void>('cpptools/getIncludes');
const CppContextRequest: RequestType<void, ChatContextResult, void> = new RequestType<void, ChatContextResult, void>('cpptools/getChatContext');
const CppContextRequest: RequestType<TextDocumentIdentifier, ChatContextResult, void> = new RequestType<TextDocumentIdentifier, ChatContextResult, void>('cpptools/getChatContext');
const ProjectContextRequest: RequestType<TextDocumentIdentifier, ProjectContextResult, void> = new RequestType<TextDocumentIdentifier, ProjectContextResult, void>('cpptools/getProjectContext');

// Notifications to the server
const DidOpenNotification: NotificationType<DidOpenTextDocumentParams> = new NotificationType<DidOpenTextDocumentParams>('textDocument/didOpen');
Expand Down Expand Up @@ -762,7 +776,7 @@ export interface Client {
PauseCodeAnalysis(): void;
ResumeCodeAnalysis(): void;
CancelCodeAnalysis(): void;
handleConfigurationSelectCommand(): Promise<void>;
handleConfigurationSelectCommand(config?: string): Promise<void>;
handleConfigurationProviderSelectCommand(): Promise<void>;
handleShowActiveCodeAnalysisCommands(): Promise<void>;
handleShowIdleCodeAnalysisCommands(): Promise<void>;
Expand Down Expand Up @@ -791,7 +805,8 @@ export interface Client {
setShowConfigureIntelliSenseButton(show: boolean): void;
addTrustedCompiler(path: string): Promise<void>;
getIncludes(maxDepth: number, token: vscode.CancellationToken): Promise<GetIncludesResult>;
getChatContext(token: vscode.CancellationToken): Promise<ChatContextResult>;
getChatContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ChatContextResult>;
getProjectContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ProjectContextResult>;
}

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

public async getChatContext(token: vscode.CancellationToken): Promise<ChatContextResult> {
public async getChatContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ChatContextResult> {
const params: TextDocumentIdentifier = { uri: uri.toString() };
await withCancellation(this.ready, token);
return DefaultClient.withLspCancellationHandling(
() => this.languageClient.sendRequest(CppContextRequest, params, token), token);
}

public async getProjectContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ProjectContextResult> {
const params: TextDocumentIdentifier = { uri: uri.toString() };
await withCancellation(this.ready, token);
return DefaultClient.withLspCancellationHandling(
() => this.languageClient.sendRequest(CppContextRequest, null, token), token);
() => this.languageClient.sendRequest(ProjectContextRequest, params, token), token);
}

/**
Expand Down Expand Up @@ -3248,11 +3271,11 @@ export class DefaultClient implements Client {
/**
* command handlers
*/
public async handleConfigurationSelectCommand(): Promise<void> {
public async handleConfigurationSelectCommand(config?: string): Promise<void> {
await this.ready;
const configNames: string[] | undefined = this.configuration.ConfigurationNames;
if (configNames) {
const index: number = await ui.showConfigurations(configNames);
const index: number = config ? configNames.indexOf(config) : await ui.showConfigurations(configNames);
if (index < 0) {
return;
}
Expand Down Expand Up @@ -4129,5 +4152,6 @@ class NullClient implements Client {
setShowConfigureIntelliSenseButton(show: boolean): void { }
addTrustedCompiler(path: string): Promise<void> { return Promise.resolve(); }
getIncludes(maxDepth: number, token: vscode.CancellationToken): Promise<GetIncludesResult> { return Promise.resolve({} as GetIncludesResult); }
getChatContext(token: vscode.CancellationToken): Promise<ChatContextResult> { return Promise.resolve({} as ChatContextResult); }
getChatContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ChatContextResult> { return Promise.resolve({} as ChatContextResult); }
getProjectContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ProjectContextResult> { return Promise.resolve({} as ProjectContextResult); }
}
140 changes: 111 additions & 29 deletions Extension/src/LanguageServer/copilotProviders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
'use strict';

import * as vscode from 'vscode';
import { localize } from 'vscode-nls';
import * as util from '../common';
import { ChatContextResult, GetIncludesResult } from './client';
import * as logger from '../logger';
import * as telemetry from '../telemetry';
import { GetIncludesResult } from './client';
import { getActiveClient } from './extension';
import { getCompilerArgumentFilterMap, getProjectContext } from './lmTool';

export interface CopilotTrait {
name: string;
Expand All @@ -34,35 +38,113 @@ export async function registerRelatedFilesProvider(): Promise<void> {
for (const languageId of ['c', 'cpp', 'cuda-cpp']) {
api.registerRelatedFilesProvider(
{ extensionId: util.extensionContext.extension.id, languageId },
async (_uri: vscode.Uri, context: { flags: Record<string, unknown> }, token: vscode.CancellationToken) => {

const getIncludesHandler = async () => (await getIncludesWithCancellation(1, token))?.includedFiles.map(file => vscode.Uri.file(file)) ?? [];
const getTraitsHandler = async () => {
const chatContext: ChatContextResult | undefined = await (getActiveClient().getChatContext(token) ?? undefined);

if (!chatContext) {
return undefined;
async (uri: vscode.Uri, context: { flags: Record<string, unknown> }, token: vscode.CancellationToken) => {
const start = performance.now();
const telemetryProperties: Record<string, string> = {};
const telemetryMetrics: Record<string, number> = {};
try {
const getIncludesHandler = async () => (await getIncludesWithCancellation(1, token))?.includedFiles.map(file => vscode.Uri.file(file)) ?? [];
const getTraitsHandler = async () => {
const projectContext = await getProjectContext(uri, context, token);

if (!projectContext) {
return undefined;
}

let traits: CopilotTrait[] = [
{ 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.` },
{ name: "intelliSenseDisclaimerBeginning", value: '', includeInPrompt: true, promptTextOverride: `Beginning of IntelliSense information.` }
];
if (projectContext.language) {
traits.push({ name: "language", value: projectContext.language, includeInPrompt: true, promptTextOverride: `The language is ${projectContext.language}.` });
}
if (projectContext.compiler) {
traits.push({ name: "compiler", value: projectContext.compiler, includeInPrompt: true, promptTextOverride: `This project compiles using ${projectContext.compiler}.` });
}
if (projectContext.standardVersion) {
traits.push({ name: "standardVersion", value: projectContext.standardVersion, includeInPrompt: true, promptTextOverride: `This project uses the ${projectContext.standardVersion} language standard.` });
}
if (projectContext.targetPlatform) {
traits.push({ name: "targetPlatform", value: projectContext.targetPlatform, includeInPrompt: true, promptTextOverride: `This build targets ${projectContext.targetPlatform}.` });
}
if (projectContext.targetArchitecture) {
traits.push({ name: "targetArchitecture", value: projectContext.targetArchitecture, includeInPrompt: true, promptTextOverride: `This build targets ${projectContext.targetArchitecture}.` });
}

if (projectContext.compiler) {
// We will process compiler arguments based on copilotcppXXXCompilerArgumentFilters and copilotcppCompilerArgumentDirectAskMap feature flags.
// The copilotcppXXXCompilerArgumentFilters are maps. The keys are regex strings for filtering and the values, if not empty,
// are the prompt text to use when no arguments are found.
// copilotcppCompilerArgumentDirectAskMap map individual matched argument to a prompt text.
// For duplicate matches, the last one will be used.
const filterMap = getCompilerArgumentFilterMap(projectContext.compiler, context);
if (filterMap !== undefined) {
const directAskMap: Record<string, string> = context.flags.copilotcppCompilerArgumentDirectAskMap ? JSON.parse(context.flags.copilotcppCompilerArgumentDirectAskMap as string) : {};
let directAsks: string = '';
const remainingArguments: string[] = [];

for (const key in filterMap) {
if (!key) {
continue;
}

const matchedArgument = projectContext.compilerArguments[key] as string;
if (matchedArgument?.length > 0) {
if (directAskMap[matchedArgument]) {
directAsks += `${directAskMap[matchedArgument]} `;
} else {
remainingArguments.push(matchedArgument);
}
} else if (filterMap[key]) {
// Use the prompt text in the absence of argument.
directAsks += `${filterMap[key]} `;
}
}

if (remainingArguments.length > 0) {
const compilerArgumentsValue = remainingArguments.join(", ");
traits.push({ name: "compilerArguments", value: compilerArgumentsValue, includeInPrompt: true, promptTextOverride: `The compiler arguments include: ${compilerArgumentsValue}.` });
}

if (directAsks) {
traits.push({ name: "directAsks", value: directAsks, includeInPrompt: true, promptTextOverride: directAsks });
}
}
}

traits.push({ name: "intelliSenseDisclaimerEnd", value: '', includeInPrompt: true, promptTextOverride: `End of IntelliSense information.` });

const includeTraitsArray = context.flags.copilotcppIncludeTraits ? context.flags.copilotcppIncludeTraits as string[] : [];
const includeTraits = new Set(includeTraitsArray);
telemetryProperties["includeTraits"] = includeTraitsArray.join(',');

// standardVersion trait is enabled by default.
traits = traits.filter(trait => includeTraits.has(trait.name) || trait.name === 'standardVersion');

telemetryProperties["traits"] = traits.map(trait => trait.name).join(',');
return traits.length > 0 ? traits : undefined;
};

// Call both handlers in parallel
const traitsPromise = getTraitsHandler();
const includesPromise = getIncludesHandler();

return { entries: await includesPromise, traits: await traitsPromise };
}
catch (exception) {
try {
const err: Error = exception as Error;
logger.getOutputChannelLogger().appendLine(localize("copilot.relatedfilesprovider.error", "Error while retrieving result. Reason: {0}", err.message));
}

let traits: CopilotTrait[] = [
{ name: "language", value: chatContext.language, includeInPrompt: true, promptTextOverride: `The language is ${chatContext.language}.` },
{ name: "compiler", value: chatContext.compiler, includeInPrompt: true, promptTextOverride: `This project compiles using ${chatContext.compiler}.` },
{ name: "standardVersion", value: chatContext.standardVersion, includeInPrompt: true, promptTextOverride: `This project uses the ${chatContext.standardVersion} language standard.` },
{ name: "targetPlatform", value: chatContext.targetPlatform, includeInPrompt: true, promptTextOverride: `This build targets ${chatContext.targetPlatform}.` },
{ name: "targetArchitecture", value: chatContext.targetArchitecture, includeInPrompt: true, promptTextOverride: `This build targets ${chatContext.targetArchitecture}.` }
];

const excludeTraits = context.flags.copilotcppExcludeTraits as string[] ?? [];
traits = traits.filter(trait => !excludeTraits.includes(trait.name));

return traits.length > 0 ? traits : undefined;
};

// Call both handlers in parallel
const traitsPromise = ((context.flags.copilotcppTraits as boolean) ?? false) ? getTraitsHandler() : Promise.resolve(undefined);
const includesPromise = getIncludesHandler();

return { entries: await includesPromise, traits: await traitsPromise };
catch {
// Intentionally swallow any exception.
}
telemetryProperties["error"] = "true";
throw exception; // Throw the exception for auto-retry.
} finally {
telemetryMetrics['duration'] = performance.now() - start;
telemetry.logCopilotEvent('RelatedFilesProvider', telemetryProperties, telemetryMetrics);
}
}
);
}
Expand Down
4 changes: 2 additions & 2 deletions Extension/src/LanguageServer/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -584,13 +584,13 @@ async function installCompiler(sender?: any): Promise<void> {
telemetry.logLanguageServerEvent('installCompiler', telemetryProperties);
}

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

Expand Down
Loading

0 comments on commit 20c9e60

Please sign in to comment.