Support variable references for non-file variables (#208510)

Fix microsoft/vscode-copilot-release#926
This commit is contained in:
Rob Lourens 2024-03-25 00:24:54 -03:00 committed by GitHub
parent 9b215e88df
commit 3c1fdd40ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 70 additions and 19 deletions

View file

@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Location } from 'vs/editor/common/languages';
import { coalesce } from 'vs/base/common/arrays';
import { raceCancellation } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
@ -22,7 +23,7 @@ import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extH
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import { IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents';
import { IChatFollowup, IChatProgress, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
import { IChatContentReference, IChatFollowup, IChatProgress, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { Dto } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import type * as vscode from 'vscode';
@ -115,15 +116,48 @@ class ChatAgentResponseStream {
},
reference(value) {
throwIfDone(this.reference);
const part = new extHostTypes.ChatResponseReferencePart(value);
const dto = typeConvert.ChatResponseReferencePart.to(part);
_report(dto);
if ('variableName' in value && !value.value) {
// The participant used this variable. Does that variable have any references to pull in?
const matchingVarData = that._request.variables.variables.find(v => v.name === value.variableName);
if (matchingVarData) {
let references: Dto<IChatContentReference>[] | undefined;
if (matchingVarData.references?.length) {
references = matchingVarData.references.map(r => ({
kind: 'reference',
reference: { variableName: value.variableName, value: r.reference as URI | Location }
} satisfies IChatContentReference));
} else {
// Participant sent a variableName reference but the variable produced no references. Show variable reference with no value
const part = new extHostTypes.ChatResponseReferencePart(value);
const dto = typeConvert.ChatResponseReferencePart.to(part);
references = [dto];
}
references.forEach(r => _report(r));
return this;
} else {
// Something went wrong- that variable doesn't actually exist
}
} else {
const part = new extHostTypes.ChatResponseReferencePart(value);
const dto = typeConvert.ChatResponseReferencePart.to(part);
_report(dto);
}
return this;
},
push(part) {
throwIfDone(this.push);
const dto = typeConvert.ChatResponsePart.to(part, that._commandsConverter, that._sessionDisposables);
_report(dto);
if (part instanceof extHostTypes.ChatResponseReferencePart) {
// Ensure variable reference values get fixed up
this.reference(part.value);
} else {
const dto = typeConvert.ChatResponsePart.to(part, that._commandsConverter, that._sessionDisposables);
_report(dto);
}
return this;
},
report(progress) {

View file

@ -2444,9 +2444,9 @@ export namespace ChatResponseReferencePart {
kind: 'reference',
reference: {
variableName: part.value.variableName,
value: URI.isUri(part.value.value) ?
value: URI.isUri(part.value.value) || !part.value.value ?
part.value.value :
Location.from(<vscode.Location>part.value.value)
Location.from(part.value.value as vscode.Location)
}
};
}

View file

@ -574,7 +574,8 @@ class ResourceLabelWidget extends IconLabel {
separator: this.options?.separator,
domId: this.options?.domId,
disabledCommand: this.options?.disabledCommand,
labelEscapeNewLines: this.options?.labelEscapeNewLines
labelEscapeNewLines: this.options?.labelEscapeNewLines,
descriptionTitle: this.options?.descriptionTitle,
};
const resource = toResource(this.label);

View file

@ -1237,12 +1237,19 @@ class ContentReferencesListRenderer implements IListRenderer<IChatContentReferen
const reference = data.reference;
templateData.label.element.style.display = 'flex';
if ('variableName' in reference) {
const variable = this.chatVariablesService.getVariable(reference.variableName);
if (reference.value) {
const uri = URI.isUri(reference.value) ? reference.value : reference.value.uri;
const title = this.labelService.getUriLabel(dirname(uri), { relative: true });
templateData.label.setResource({ resource: uri, name: basenameOrAuthority(uri), description: `#${reference.variableName}` }, { title });
templateData.label.setResource(
{
resource: uri,
name: basenameOrAuthority(uri),
description: `#${reference.variableName}`,
range: 'range' in reference.value ? reference.value.range : undefined
},
{ title, descriptionTitle: variable?.description });
} else {
const variable = this.chatVariablesService.getVariable(reference.variableName);
templateData.label.setLabel(`#${reference.variableName}`, undefined, { title: variable?.description });
}
} else {

View file

@ -8,12 +8,12 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { Iterable } from 'vs/base/common/iterator';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IOffsetRange } from 'vs/editor/common/core/offsetRange';
import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
import { ChatDynamicVariableModel } from 'vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables';
import { IChatModel, IChatRequestVariableData } from 'vs/workbench/contrib/chat/common/chatModel';
import { IParsedChatRequest, ChatRequestVariablePart, ChatRequestDynamicVariablePart } from 'vs/workbench/contrib/chat/common/chatParserTypes';
import { IChatVariablesService, IChatRequestVariableValue, IChatVariableData, IChatVariableResolver, IDynamicVariable, IChatVariableResolverProgress } from 'vs/workbench/contrib/chat/common/chatVariables';
import { IChatModel, IChatRequestVariableData, IChatRequestVariableEntry } from 'vs/workbench/contrib/chat/common/chatModel';
import { ChatRequestDynamicVariablePart, ChatRequestVariablePart, IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes';
import { IChatContentReference } from 'vs/workbench/contrib/chat/common/chatService';
import { IChatRequestVariableValue, IChatVariableData, IChatVariableResolver, IChatVariableResolverProgress, IChatVariablesService, IDynamicVariable } from 'vs/workbench/contrib/chat/common/chatVariables';
interface IChatData {
data: IChatVariableData;
@ -31,7 +31,7 @@ export class ChatVariablesService implements IChatVariablesService {
}
async resolveVariables(prompt: IParsedChatRequest, model: IChatModel, progress: (part: IChatVariableResolverProgress) => void, token: CancellationToken): Promise<IChatRequestVariableData> {
let resolvedVariables: { name: string; range: IOffsetRange; values: IChatRequestVariableValue[] }[] = [];
let resolvedVariables: IChatRequestVariableEntry[] = [];
const jobs: Promise<any>[] = [];
prompt.parts
@ -39,8 +39,16 @@ export class ChatVariablesService implements IChatVariablesService {
if (part instanceof ChatRequestVariablePart) {
const data = this._resolver.get(part.variableName.toLowerCase());
if (data) {
jobs.push(data.resolver(prompt.text, part.variableArg, model, progress, token).then(values => {
resolvedVariables[i] = { name: part.variableName, range: part.range, values: values ?? [] };
const references: IChatContentReference[] = [];
const variableProgressCallback = (item: IChatVariableResolverProgress) => {
if (item.kind === 'reference') {
references.push(item);
return;
}
progress(item);
};
jobs.push(data.resolver(prompt.text, part.variableArg, model, variableProgressCallback, token).then(values => {
resolvedVariables[i] = { name: part.variableName, range: part.range, values: values ?? [], references };
}).catch(onUnexpectedExternalError));
}
} else if (part instanceof ChatRequestDynamicVariablePart) {
@ -53,7 +61,7 @@ export class ChatVariablesService implements IChatVariablesService {
resolvedVariables = coalesce(resolvedVariables);
// "reverse", high index first so that replacement is simple
resolvedVariables.sort((a, b) => b.range.start - a.range.start);
resolvedVariables.sort((a, b) => b.range!.start - a.range!.start);
return {
variables: resolvedVariables,

View file

@ -31,6 +31,7 @@ export interface IChatRequestVariableEntry {
name: string;
range?: IOffsetRange;
values: IChatRequestVariableValue[];
references?: IChatContentReference[];
}
export interface IChatRequestVariableData {