Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions Extension/src/LanguageServer/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -555,19 +555,6 @@ 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;
}

interface FolderFilesEncodingChanged {
uri: string;
filesEncoding: string;
Expand Down Expand Up @@ -614,7 +601,6 @@ const GenerateDoxygenCommentRequest: RequestType<GenerateDoxygenCommentParams, G
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<TextDocumentIdentifier, ChatContextResult, void> = new RequestType<TextDocumentIdentifier, ChatContextResult, void>('cpptools/getChatContext');
const ProjectContextRequest: RequestType<TextDocumentIdentifier, ProjectContextResult, void> = new RequestType<TextDocumentIdentifier, ProjectContextResult, void>('cpptools/getProjectContext');
const CopilotCompletionContextRequest: RequestType<CopilotCompletionContextParams, CopilotCompletionContextResult, void> = new RequestType<CopilotCompletionContextParams, CopilotCompletionContextResult, void>('cpptools/getCompletionContext');

// Notifications to the server
Expand Down Expand Up @@ -849,7 +835,6 @@ export interface Client {
getCopilotHoverProvider(): CopilotHoverProvider | undefined;
getIncludes(uri: vscode.Uri, maxDepth: number): Promise<GetIncludesResult>;
getChatContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ChatContextResult>;
getProjectContext(uri: vscode.Uri): Promise<ProjectContextResult>;
filesEncodingChanged(filesEncodingChanged: FilesEncodingChanged): void;
getCompletionContext(fileName: vscode.Uri, caretOffset: number, featureFlag: CopilotCompletionContextFeatures, token: vscode.CancellationToken): Promise<CopilotCompletionContextResult>;
}
Expand Down Expand Up @@ -2338,12 +2323,6 @@ export class DefaultClient implements Client {
return this.languageClient.sendRequest(IncludesRequest, params);
}

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

public async getChatContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ChatContextResult> {
const params: TextDocumentIdentifier = { uri: uri.toString() };
await withCancellation(this.ready, token);
Expand Down Expand Up @@ -4292,7 +4271,6 @@ class NullClient implements Client {
getCopilotHoverProvider(): CopilotHoverProvider | undefined { return undefined; }
getIncludes(uri: vscode.Uri, maxDepth: number): Promise<GetIncludesResult> { return Promise.resolve({} as GetIncludesResult); }
getChatContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ChatContextResult> { return Promise.resolve({} as ChatContextResult); }
getProjectContext(uri: vscode.Uri): Promise<ProjectContextResult> { return Promise.resolve({} as ProjectContextResult); }
filesEncodingChanged(filesEncodingChanged: FilesEncodingChanged): void { }
getCompletionContext(file: vscode.Uri, caretOffset: number, featureFlag: CopilotCompletionContextFeatures, token: vscode.CancellationToken): Promise<CopilotCompletionContextResult> { return Promise.resolve({} as CopilotCompletionContextResult); }
}
54 changes: 4 additions & 50 deletions Extension/src/LanguageServer/copilotProviders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import * as logger from '../logger';
import * as telemetry from '../telemetry';
import { GetIncludesResult } from './client';
import { getClients } from './extension';
import { getCompilerArgumentFilterMap, getProjectContext } from './lmTool';
import { getProjectContext } from './lmTool';

nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
Expand Down Expand Up @@ -43,23 +43,20 @@ 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> }) => {
async (uri: vscode.Uri, context: { flags: Record<string, unknown> }, cancellationToken: vscode.CancellationToken) => {
const start = performance.now();
const telemetryProperties: Record<string, string> = {};
const telemetryMetrics: Record<string, number> = {};
try {
const getIncludesHandler = async () => (await getIncludes(uri, 1))?.includedFiles.map(file => vscode.Uri.file(file)) ?? [];
const getTraitsHandler = async () => {
const projectContext = await getProjectContext(uri, context, telemetryProperties, telemetryMetrics);
const projectContext = await getProjectContext(uri, context, cancellationToken, telemetryProperties, telemetryMetrics);

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.` }
];
let traits: CopilotTrait[] = [];
if (projectContext.language) {
traits.push({ name: "language", value: projectContext.language, includeInPrompt: true, promptTextOverride: `The language is ${projectContext.language}.` });
}
Expand All @@ -76,49 +73,6 @@ export async function registerRelatedFilesProvider(): Promise<void> {
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(',');
Expand Down
77 changes: 5 additions & 72 deletions Extension/src/LanguageServer/lmTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import * as nls from 'vscode-nls';
import * as util from '../common';
import * as logger from '../logger';
import * as telemetry from '../telemetry';
import { ChatContextResult, ProjectContextResult } from './client';
import { ChatContextResult } from './client';
import { getClients } from './extension';
import { checkDuration } from './utils';

Expand Down Expand Up @@ -51,7 +51,7 @@ const knownValues: { [Property in keyof ChatContextResult]?: { [id: string]: str
}
};

function formatChatContext(context: ChatContextResult | ProjectContextResult): void {
function formatChatContext(context: ChatContextResult): void {
type KnownKeys = 'language' | 'standardVersion' | 'compiler' | 'targetPlatform';
for (const key in knownValues) {
const knownKey = key as KnownKeys;
Expand All @@ -68,75 +68,11 @@ export interface ProjectContext {
compiler: string;
targetPlatform: string;
targetArchitecture: string;
compilerArguments: Record<string, string>;
}

export function getCompilerArgumentFilterMap(compiler: string, context: { flags: Record<string, unknown> }): Record<string, string> | undefined {
// The copilotcppXXXCompilerArgumentFilters are maps.
// The keys are regex strings and the values, if not empty, are the prompt text to use when no arguments are found.
let filterMap: Record<string, string> | undefined;
export async function getProjectContext(uri: vscode.Uri, context: { flags: Record<string, unknown> }, cancellationToken: vscode.CancellationToken, telemetryProperties: Record<string, string>, telemetryMetrics: Record<string, number>): Promise<ProjectContext | undefined> {
try {
switch (compiler) {
case MSVC:
if (context.flags.copilotcppMsvcCompilerArgumentFilter !== undefined) {
filterMap = JSON.parse(context.flags.copilotcppMsvcCompilerArgumentFilter as string);
}
break;
case Clang:
if (context.flags.copilotcppClangCompilerArgumentFilter !== undefined) {
filterMap = JSON.parse(context.flags.copilotcppClangCompilerArgumentFilter as string);
}
break;
case GCC:
if (context.flags.copilotcppGccCompilerArgumentFilter !== undefined) {
filterMap = JSON.parse(context.flags.copilotcppGccCompilerArgumentFilter as string);
}
break;
}
}
catch {
// Intentionally swallow any exception.
}
return filterMap;
}

function filterCompilerArguments(compiler: string, compilerArguments: string[], context: { flags: Record<string, unknown> }, telemetryProperties: Record<string, string>): Record<string, string> {
const filterMap = getCompilerArgumentFilterMap(compiler, context);
if (filterMap === undefined) {
return {};
}

const combinedArguments = compilerArguments.join(' ');
const result: Record<string, string> = {};
const filteredCompilerArguments: string[] = [];
for (const key in filterMap) {
if (!key) {
continue;
}
const filter = new RegExp(key, 'g');
const filtered = combinedArguments.match(filter);
if (filtered) {
filteredCompilerArguments.push(...filtered);
result[key] = filtered[filtered.length - 1];
}
}

if (filteredCompilerArguments.length > 0) {
// Telemetry to learn about the argument distribution. The filtered arguments are expected to be non-PII.
telemetryProperties["filteredCompilerArguments"] = filteredCompilerArguments.join(',');
}

const filters = Object.keys(filterMap).filter(filter => !!filter).join(',');
if (filters) {
telemetryProperties["filters"] = filters;
}

return result;
}

export async function getProjectContext(uri: vscode.Uri, context: { flags: Record<string, unknown> }, telemetryProperties: Record<string, string>, telemetryMetrics: Record<string, number>): Promise<ProjectContext | undefined> {
try {
const projectContext = await checkDuration<ProjectContextResult | undefined>(async () => await getClients()?.ActiveClient?.getProjectContext(uri) ?? undefined);
const projectContext = await checkDuration<ChatContextResult | undefined>(async () => await getClients()?.ActiveClient?.getChatContext(uri, cancellationToken) ?? undefined);
telemetryMetrics["projectContextDuration"] = projectContext.duration;
if (!projectContext.result) {
return undefined;
Expand All @@ -151,8 +87,7 @@ export async function getProjectContext(uri: vscode.Uri, context: { flags: Recor
standardVersion: projectContext.result.standardVersion,
compiler: projectContext.result.compiler,
targetPlatform: projectContext.result.targetPlatform,
targetArchitecture: projectContext.result.targetArchitecture,
compilerArguments: {}
targetArchitecture: projectContext.result.targetArchitecture
};

if (projectContext.result.language) {
Expand All @@ -175,8 +110,6 @@ export async function getProjectContext(uri: vscode.Uri, context: { flags: Recor
if (projectContext.result.targetArchitecture) {
telemetryProperties["targetArchitecture"] = projectContext.result.targetArchitecture;
}
telemetryMetrics["compilerArgumentCount"] = projectContext.result.fileContext.compilerArguments.length;
result.compilerArguments = filterCompilerArguments(projectContext.result.compiler, projectContext.result.fileContext.compilerArguments, context, telemetryProperties);

return result;
}
Expand Down
Loading
Loading