mirror of
https://github.com/Microsoft/vscode
synced 2024-10-12 14:30:13 +00:00
Add "variable references" (#208478)
Towards microsoft/vscode-copilot-release#926
This commit is contained in:
parent
32c524af96
commit
871cab2b67
|
@ -2439,15 +2439,38 @@ export namespace ChatResponseCommandButtonPart {
|
|||
|
||||
export namespace ChatResponseReferencePart {
|
||||
export function to(part: vscode.ChatResponseReferencePart): Dto<IChatContentReference> {
|
||||
if ('variableName' in part.value) {
|
||||
return {
|
||||
kind: 'reference',
|
||||
reference: {
|
||||
variableName: part.value.variableName,
|
||||
value: URI.isUri(part.value.value) ?
|
||||
part.value.value :
|
||||
Location.from(<vscode.Location>part.value.value)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
kind: 'reference',
|
||||
reference: !URI.isUri(part.value) ? Location.from(<vscode.Location>part.value) : part.value
|
||||
reference: URI.isUri(part.value) ?
|
||||
part.value :
|
||||
Location.from(<vscode.Location>part.value)
|
||||
};
|
||||
}
|
||||
export function from(part: Dto<IChatContentReference>): vscode.ChatResponseReferencePart {
|
||||
const value = revive<IChatContentReference>(part);
|
||||
|
||||
const mapValue = (value: URI | languages.Location): vscode.Uri | vscode.Location => URI.isUri(value) ?
|
||||
value :
|
||||
Location.to(value);
|
||||
|
||||
return new types.ChatResponseReferencePart(
|
||||
URI.isUri(value.reference) ? value.reference : Location.to(value.reference)
|
||||
'variableName' in value.reference ? {
|
||||
variableName: value.reference.variableName,
|
||||
value: value.reference.value && mapValue(value.reference.value)
|
||||
} :
|
||||
mapValue(value.reference)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2556,36 +2579,6 @@ export namespace ChatResponseProgress {
|
|||
}
|
||||
}
|
||||
|
||||
export function to(progress: extHostProtocol.IChatProgressDto): vscode.ChatProgress | undefined {
|
||||
switch (progress.kind) {
|
||||
case 'markdownContent':
|
||||
case 'inlineReference':
|
||||
case 'treeData':
|
||||
return ChatResponseProgress.to(progress);
|
||||
case 'content':
|
||||
return { content: progress.content };
|
||||
case 'usedContext':
|
||||
return { documents: progress.documents.map(d => ({ uri: URI.revive(d.uri), version: d.version, ranges: d.ranges.map(r => Range.to(r)) })) };
|
||||
case 'reference':
|
||||
return {
|
||||
reference:
|
||||
isUriComponents(progress.reference) ?
|
||||
URI.revive(progress.reference) :
|
||||
Location.to(progress.reference)
|
||||
};
|
||||
case 'agentDetection':
|
||||
// For simplicity, don't sent back the 'extended' types
|
||||
return undefined;
|
||||
case 'progressMessage':
|
||||
return { message: progress.content.value };
|
||||
case 'vulnerability':
|
||||
return { content: progress.content, vulnerabilities: progress.vulnerabilities };
|
||||
default:
|
||||
// Unknown type, eg something in history that was removed? Ignore
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function toProgressContent(progress: extHostProtocol.IChatContentProgressDto, commandsConverter: Command.ICommandsConverter): vscode.ChatContentProgress | undefined {
|
||||
switch (progress.kind) {
|
||||
case 'markdownContent':
|
||||
|
|
|
@ -4298,8 +4298,8 @@ export class ChatResponseCommandButtonPart {
|
|||
}
|
||||
|
||||
export class ChatResponseReferencePart {
|
||||
value: vscode.Uri | vscode.Location;
|
||||
constructor(value: vscode.Uri | vscode.Location) {
|
||||
value: vscode.Uri | vscode.Location | { variableName: string; value?: vscode.Uri | vscode.Location };
|
||||
constructor(value: vscode.Uri | vscode.Location | { variableName: string; value?: vscode.Uri | vscode.Location }) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import { ResourceMap } from 'vs/base/common/map';
|
|||
import { FileAccess, Schemas, matchesSomeScheme } from 'vs/base/common/network';
|
||||
import { clamp } from 'vs/base/common/numbers';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
import { basenameOrAuthority, dirname } from 'vs/base/common/resources';
|
||||
import { equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
@ -41,6 +42,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
|||
import { FileKind, FileType } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { WorkbenchCompressibleAsyncDataTree, WorkbenchList } from 'vs/platform/list/browser/listService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
|
@ -58,6 +60,7 @@ import { CONTEXT_CHAT_RESPONSE_SUPPORT_ISSUE_REPORTING, CONTEXT_REQUEST, CONTEXT
|
|||
import { IChatProgressRenderableResponseContent } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { chatAgentLeader, chatSubcommandLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { IChatCommandButton, IChatContentReference, IChatFollowup, IChatProgressMessage, IChatResponseProgressFileTreeData, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
import { IChatProgressMessageRenderData, IChatRenderData, IChatResponseMarkdownRenderData, IChatResponseViewModel, IChatWelcomeMessageViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
import { IWordCountResult, getNWords } from 'vs/workbench/contrib/chat/common/chatWordCounter';
|
||||
import { createFileIconThemableTreeContainerScope } from 'vs/workbench/contrib/files/browser/views/explorerView';
|
||||
|
@ -800,17 +803,22 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
|||
|
||||
listDisposables.add(list.onDidOpen((e) => {
|
||||
if (e.element) {
|
||||
this.openerService.open(
|
||||
'uri' in e.element.reference ? e.element.reference.uri : e.element.reference,
|
||||
{
|
||||
fromUserGesture: true,
|
||||
editorOptions: {
|
||||
...e.editorOptions,
|
||||
...{
|
||||
selection: 'range' in e.element.reference ? e.element.reference.range : undefined
|
||||
const uriOrLocation = 'variableName' in e.element.reference ? e.element.reference.value : e.element.reference;
|
||||
const uri = URI.isUri(uriOrLocation) ? uriOrLocation :
|
||||
uriOrLocation?.uri;
|
||||
if (uri) {
|
||||
this.openerService.open(
|
||||
uri,
|
||||
{
|
||||
fromUserGesture: true,
|
||||
editorOptions: {
|
||||
...e.editorOptions,
|
||||
...{
|
||||
selection: 'range' in e.element.reference ? e.element.reference.range : undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}));
|
||||
listDisposables.add(list.onContextMenu((e) => {
|
||||
|
@ -1163,10 +1171,13 @@ class ContentReferencesListPool extends Disposable {
|
|||
alwaysConsumeMouseWheel: false,
|
||||
accessibilityProvider: {
|
||||
getAriaLabel: (element: IChatContentReference) => {
|
||||
if (URI.isUri(element.reference)) {
|
||||
return basename(element.reference.path);
|
||||
const reference = element.reference;
|
||||
if ('variableName' in reference) {
|
||||
return reference.variableName;
|
||||
} else if (URI.isUri(reference)) {
|
||||
return basename(reference.path);
|
||||
} else {
|
||||
return basename(element.reference.uri.path);
|
||||
return basename(reference.uri.path);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1212,6 +1223,8 @@ class ContentReferencesListRenderer implements IListRenderer<IChatContentReferen
|
|||
|
||||
constructor(
|
||||
private labels: ResourceLabels,
|
||||
@ILabelService private readonly labelService: ILabelService,
|
||||
@IChatVariablesService private readonly chatVariablesService: IChatVariablesService,
|
||||
) { }
|
||||
|
||||
renderTemplate(container: HTMLElement): IChatContentReferenceListTemplate {
|
||||
|
@ -1220,18 +1233,30 @@ class ContentReferencesListRenderer implements IListRenderer<IChatContentReferen
|
|||
return { templateDisposables, label };
|
||||
}
|
||||
|
||||
renderElement(element: IChatContentReference, index: number, templateData: IChatContentReferenceListTemplate, height: number | undefined): void {
|
||||
renderElement(data: IChatContentReference, index: number, templateData: IChatContentReferenceListTemplate, height: number | undefined): void {
|
||||
const reference = data.reference;
|
||||
templateData.label.element.style.display = 'flex';
|
||||
const uri = 'uri' in element.reference ? element.reference.uri : element.reference;
|
||||
if (matchesSomeScheme(uri, Schemas.mailto, Schemas.http, Schemas.https)) {
|
||||
templateData.label.setResource({ resource: uri, name: uri.toString() }, { icon: Codicon.globe });
|
||||
if ('variableName' in reference) {
|
||||
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 });
|
||||
} else {
|
||||
const variable = this.chatVariablesService.getVariable(reference.variableName);
|
||||
templateData.label.setLabel(`#${reference.variableName}`, undefined, { title: variable?.description });
|
||||
}
|
||||
} else {
|
||||
templateData.label.setFile(uri, {
|
||||
fileKind: FileKind.FILE,
|
||||
// Should not have this live-updating data on a historical reference
|
||||
fileDecorations: { badges: false, colors: false },
|
||||
range: 'range' in element.reference ? element.reference.range : undefined
|
||||
});
|
||||
const uri = 'uri' in reference ? reference.uri : reference;
|
||||
if (matchesSomeScheme(uri, Schemas.mailto, Schemas.http, Schemas.https)) {
|
||||
templateData.label.setResource({ resource: uri, name: uri.toString() }, { icon: Codicon.globe });
|
||||
} else {
|
||||
templateData.label.setFile(uri, {
|
||||
fileKind: FileKind.FILE,
|
||||
// Should not have this live-updating data on a historical reference
|
||||
fileDecorations: { badges: false, colors: false },
|
||||
range: 'range' in reference ? reference.range : undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -73,6 +73,10 @@ export class ChatVariablesService implements IChatVariablesService {
|
|||
return this._resolver.has(name.toLowerCase());
|
||||
}
|
||||
|
||||
getVariable(name: string): IChatVariableData | undefined {
|
||||
return this._resolver.get(name.toLowerCase())?.data;
|
||||
}
|
||||
|
||||
getVariables(): Iterable<Readonly<IChatVariableData>> {
|
||||
const all = Iterable.map(this._resolver.values(), data => data.data);
|
||||
return Iterable.filter(all, data => !data.hidden);
|
||||
|
|
|
@ -74,8 +74,13 @@ export function isIUsedContext(obj: unknown): obj is IChatUsedContext {
|
|||
);
|
||||
}
|
||||
|
||||
export interface IChatContentVariableReference {
|
||||
variableName: string;
|
||||
value?: URI | Location;
|
||||
}
|
||||
|
||||
export interface IChatContentReference {
|
||||
reference: URI | Location;
|
||||
reference: URI | Location | IChatContentVariableReference;
|
||||
kind: 'reference';
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ export interface IChatVariablesService {
|
|||
_serviceBrand: undefined;
|
||||
registerVariable(data: IChatVariableData, resolver: IChatVariableResolver): IDisposable;
|
||||
hasVariable(name: string): boolean;
|
||||
getVariable(name: string): IChatVariableData | undefined;
|
||||
getVariables(): Iterable<Readonly<IChatVariableData>>;
|
||||
getDynamicVariables(sessionId: string): ReadonlyArray<IDynamicVariable>; // should be its own service?
|
||||
|
||||
|
|
|
@ -108,15 +108,22 @@ export class CodeBlockModelCollection extends Disposable {
|
|||
|
||||
return {
|
||||
references: chat.contentReferences.map(ref => {
|
||||
if (URI.isUri(ref.reference)) {
|
||||
const uriOrLocation = 'variableName' in ref.reference ?
|
||||
ref.reference.value :
|
||||
ref.reference;
|
||||
if (!uriOrLocation) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (URI.isUri(uriOrLocation)) {
|
||||
return {
|
||||
uri: ref.reference.toJSON()
|
||||
uri: uriOrLocation.toJSON()
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
uri: ref.reference.uri.toJSON(),
|
||||
range: ref.reference.range,
|
||||
uri: uriOrLocation.uri.toJSON(),
|
||||
range: uriOrLocation.range,
|
||||
};
|
||||
})
|
||||
};
|
||||
|
|
|
@ -15,6 +15,10 @@ export class MockChatVariablesService implements IChatVariablesService {
|
|||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getVariable(name: string): IChatVariableData | undefined {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
hasVariable(name: string): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
|
|
@ -392,7 +392,7 @@ declare module 'vscode' {
|
|||
* @param value A uri or location
|
||||
* @returns This stream.
|
||||
*/
|
||||
reference(value: Uri | Location): ChatResponseStream;
|
||||
reference(value: Uri | Location | { variableName: string; value?: Uri | Location }): ChatResponseStream;
|
||||
|
||||
/**
|
||||
* Pushes a part to this stream.
|
||||
|
@ -430,8 +430,8 @@ declare module 'vscode' {
|
|||
}
|
||||
|
||||
export class ChatResponseReferencePart {
|
||||
value: Uri | Location;
|
||||
constructor(value: Uri | Location);
|
||||
value: Uri | Location | { variableName: string; value?: Uri | Location };
|
||||
constructor(value: Uri | Location | { variableName: string; value?: Uri | Location });
|
||||
}
|
||||
|
||||
export class ChatResponseCommandButtonPart {
|
||||
|
|
Loading…
Reference in a new issue