Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/intents/node/vscodeIntent.ts
13399 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as l10n from '@vscode/l10n';
7
import type * as vscode from 'vscode';
8
import { IResponsePart } from '../../../platform/chat/common/chatMLFetcher';
9
import { ChatLocation } from '../../../platform/chat/common/commonTypes';
10
import { IEndpointProvider } from '../../../platform/endpoint/common/endpointProvider';
11
import { IChatEndpoint } from '../../../platform/networking/common/networking';
12
import { IWorkbenchService } from '../../../platform/workbench/common/workbenchService';
13
import { CancellationToken } from '../../../util/vs/base/common/cancellation';
14
import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';
15
import { Intent } from '../../common/constants';
16
import { parseSettingsAndCommands } from '../../context/node/resolvers/vscodeContext';
17
import { IBuildPromptContext } from '../../prompt/common/intents';
18
import { IIntent, IIntentInvocation, IIntentInvocationContext, IIntentSlashCommandInfo, IntentLinkificationOptions, IResponseProcessorContext } from '../../prompt/node/intents';
19
import { PromptRenderer, RendererIntentInvocation } from '../../prompts/node/base/promptRenderer';
20
import { VscodePrompt } from '../../prompts/node/panel/vscode';
21
import { ToolName } from '../../tools/common/toolNames';
22
import { IToolsService } from '../../tools/common/toolsService';
23
24
25
class VSCodeIntentInvocation extends RendererIntentInvocation implements IIntentInvocation {
26
27
readonly linkification: IntentLinkificationOptions = { disable: true };
28
29
constructor(
30
intent: IIntent,
31
location: ChatLocation,
32
endpoint: IChatEndpoint,
33
private readonly request: vscode.ChatRequest,
34
@IInstantiationService private readonly instantiationService: IInstantiationService,
35
@IWorkbenchService private readonly workbenchService: IWorkbenchService,
36
@IToolsService private readonly toolsService: IToolsService,
37
) {
38
super(intent, location, endpoint);
39
}
40
41
async createRenderer(promptContext: IBuildPromptContext, endpoint: IChatEndpoint, progress: vscode.Progress<vscode.ChatResponseProgressPart | vscode.ChatResponseReferencePart>, token: vscode.CancellationToken) {
42
return PromptRenderer.create(this.instantiationService, endpoint, VscodePrompt, {
43
endpoint,
44
promptContext
45
});
46
}
47
48
processResponse(context: IResponseProcessorContext, inputStream: AsyncIterable<IResponsePart>, outputStream: vscode.ChatResponseStream, token: CancellationToken): Promise<void> {
49
const responseProcessor = new VSCodeResponseProcessor(this.workbenchService);
50
return responseProcessor.processResponse(context, inputStream, outputStream, token);
51
}
52
53
getAvailableTools(): vscode.LanguageModelToolInformation[] | Promise<vscode.LanguageModelToolInformation[]> | undefined {
54
return this.toolsService.getEnabledTools(this.request, this.endpoint, tool =>
55
tool.name === 'vscode_searchExtensions_internal' ||
56
tool.name === ToolName.VSCodeAPI
57
);
58
}
59
}
60
61
class VSCodeResponseProcessor {
62
private stagedTextToApply = '';
63
constructor(private readonly workbenchService: IWorkbenchService) {
64
}
65
66
async processResponse(context: IResponseProcessorContext, inputStream: AsyncIterable<IResponsePart>, outputStream: vscode.ChatResponseStream, token: vscode.CancellationToken): Promise<void> {
67
for await (const { delta } of inputStream) {
68
if (token.isCancellationRequested) {
69
return;
70
}
71
await this.applyDelta(delta.text, outputStream);
72
}
73
}
74
75
/**
76
* Parses a raw Markdown string containing a code block and either extracts settings and commands to show as buttons for the user, or shows the code block.
77
* @param codeBlock Markdown string containing a single code block surrounded by "```"
78
*/
79
// textDelta is a string with a single Markdown code block (wrapped by ```). It might or might not be of json type.
80
private async processNonReporting(codeBlock: string, progress: vscode.ChatResponseStream) {
81
const parsedCommands = await parseSettingsAndCommands(this.workbenchService, codeBlock);
82
83
if (parsedCommands.length === 0) {
84
// Show code block
85
progress.markdown('\n' + codeBlock + '\n');
86
} else {
87
// Show buttons for commands to run (which can include commands to change settings)
88
for (const parsedCommand of parsedCommands) {
89
if (parsedCommand.commandToRun) {
90
progress.button(parsedCommand.commandToRun);
91
}
92
}
93
}
94
}
95
96
private _incodeblock = false;
97
private async applyDelta(textDelta: string, progress: vscode.ChatResponseStream) {
98
99
textDelta = this.stagedTextToApply + textDelta;
100
this.stagedTextToApply = '';
101
const codeblockStart = textDelta.indexOf('```');
102
103
if (this._incodeblock) {
104
const codeblockEnd = textDelta.indexOf('```');
105
if (codeblockEnd === -1) {
106
this.stagedTextToApply = textDelta;
107
} else {
108
this._incodeblock = false;
109
const codeBlock = '```' + textDelta.substring(0, codeblockEnd) + '```';
110
await this.processNonReporting(codeBlock, progress);
111
// Output any text that comes after the code block
112
progress.markdown(textDelta.substring(codeblockEnd + 3));
113
}
114
}
115
else if (codeblockStart !== -1) {
116
this._incodeblock = true;
117
const codeblockEnd = textDelta.indexOf('```', codeblockStart + 3);
118
if (codeblockEnd !== -1) {
119
this._incodeblock = false;
120
// Output any text that comes before the code block
121
progress.markdown(textDelta.substring(0, codeblockStart));
122
// Process the codeblock
123
const codeBlock = '```' + textDelta.substring(codeblockStart + 3, codeblockEnd) + '```';
124
await this.processNonReporting(codeBlock, progress);
125
// Output any text that comes after the code block
126
progress.markdown(textDelta.substring(codeblockEnd + 3));
127
} else {
128
this.stagedTextToApply = textDelta.substring(codeblockStart + 3);
129
// Output any text that comes before the code block
130
const textToReport = textDelta.substring(0, codeblockStart);
131
if (textToReport) {
132
progress.markdown(textToReport);
133
}
134
}
135
} else {
136
// We have no stop word or partial, so apply the text to the progress and turn
137
progress.markdown(textDelta);
138
}
139
}
140
}
141
142
export class VscodeIntent implements IIntent {
143
144
static readonly ID = Intent.VSCode;
145
readonly id: string = VscodeIntent.ID;
146
readonly locations = [ChatLocation.Panel];
147
readonly description: string = l10n.t('Ask questions about VS Code');
148
149
readonly commandInfo: IIntentSlashCommandInfo = {
150
allowsEmptyArgs: true,
151
};
152
153
constructor(
154
@IInstantiationService private readonly instantiationService: IInstantiationService,
155
@IEndpointProvider private readonly endpointProvider: IEndpointProvider,
156
) { }
157
158
async invoke(invocationContext: IIntentInvocationContext): Promise<IIntentInvocation> {
159
const location = invocationContext.location;
160
const endpoint = await this.endpointProvider.getChatEndpoint(invocationContext.request);
161
return this.instantiationService.createInstance(VSCodeIntentInvocation, this, location, endpoint, invocationContext.request);
162
}
163
}
164
165