Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/intents/node/searchIntent.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 { parse } from 'jsonc-parser';
8
import type * as vscode from 'vscode';
9
import { IResponsePart } from '../../../platform/chat/common/chatMLFetcher';
10
import { ChatLocation } from '../../../platform/chat/common/commonTypes';
11
import { IEndpointProvider } from '../../../platform/endpoint/common/endpointProvider';
12
import { isPreRelease } from '../../../platform/env/common/packagejson';
13
import { IResponseDelta } from '../../../platform/networking/common/fetch';
14
import { IChatEndpoint } from '../../../platform/networking/common/networking';
15
import { extractCodeBlocks } from '../../../util/common/markdown';
16
import { CancellationToken } from '../../../util/vs/base/common/cancellation';
17
import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';
18
import { Intent } from '../../common/constants';
19
import { IBuildPromptContext } from '../../prompt/common/intents';
20
import { IIntent, IIntentInvocation, IIntentInvocationContext, IIntentSlashCommandInfo, IResponseProcessorContext } from '../../prompt/node/intents';
21
import { PseudoStopStartResponseProcessor } from '../../prompt/node/pseudoStartStopConversationCallback';
22
import { PromptRenderer, RendererIntentInvocation } from '../../prompts/node/base/promptRenderer';
23
import { SearchPrompt } from '../../prompts/node/panel/search';
24
25
26
export interface FindInFilesArgs {
27
query: string;
28
replace: string;
29
filesToInclude: string;
30
filesToExclude: string;
31
isRegex: boolean;
32
isCaseSensitive: boolean;
33
}
34
35
function createSearchFollowUps(args: any): vscode.Command[] {
36
if (!args) {
37
return [];
38
}
39
const searchResponses: vscode.Command[] = [];
40
41
const searchArg: FindInFilesArgs = {
42
query: args.query ?? '',
43
replace: args.replace ?? '',
44
filesToInclude: args.filesToInclude ?? '',
45
filesToExclude: args.filesToExclude ?? '',
46
isRegex: args.isRegex ?? false,
47
isCaseSensitive: args.isRegex ?? false,
48
};
49
searchResponses.push({
50
command: 'github.copilot.executeSearch',
51
arguments: [searchArg],
52
title: l10n.t("Search"),
53
});
54
return searchResponses;
55
}
56
57
export function parseSearchParams(modelResponseString: string): any {
58
const codeBlock = extractCodeBlocks(modelResponseString).at(0);
59
let args: any = undefined;
60
if (codeBlock) {
61
let parsed: any | undefined;
62
try {
63
parsed = parse(codeBlock.code);
64
} catch (e) {
65
// Ignore
66
}
67
68
if (parsed) {
69
args = parsed;
70
}
71
}
72
return args;
73
}
74
75
function jsonToTable(args: any): string[] {
76
if (!args) {
77
return [];
78
}
79
const table = ['| Parameter | Value |\n', '| ------ | ----- |\n'];
80
for (const [key, value] of Object.entries(args)) {
81
if (value === '') {
82
continue;
83
}
84
let nonEscapeValue = value;
85
if (typeof value === 'string' || value instanceof String) {
86
// CodeQL [SM02383] Since this is inside of a markdown table cell, only a `|` pipe character would interfere with formatting.
87
nonEscapeValue = value.replace(/\|/g, '\\|');
88
}
89
table.push(`| ${key} | \`${nonEscapeValue}\` |\n`);
90
}
91
table.push(`\n`);
92
return table;
93
}
94
95
export const searchIntentPromptSnippet = `Search for 'foo' in all files under my 'src' directory`;
96
97
class SearchIntentInvocation extends RendererIntentInvocation implements IIntentInvocation {
98
99
constructor(
100
intent: IIntent,
101
location: ChatLocation,
102
endpoint: IChatEndpoint,
103
@IInstantiationService private readonly instantiationService: IInstantiationService,
104
) {
105
super(intent, location, endpoint);
106
}
107
108
createRenderer(promptContext: IBuildPromptContext, endpoint: IChatEndpoint, progress: vscode.Progress<vscode.ChatResponseProgressPart | vscode.ChatResponseReferencePart>, token: vscode.CancellationToken) {
109
return PromptRenderer.create(this.instantiationService, endpoint, SearchPrompt, {
110
promptContext
111
});
112
}
113
114
processResponse(context: IResponseProcessorContext, inputStream: AsyncIterable<IResponsePart>, outputStream: vscode.ChatResponseStream, token: CancellationToken): Promise<void> {
115
const responseProcessor = this.instantiationService.createInstance(SearchResponseProcessor);
116
return responseProcessor.processResponse(context, inputStream, outputStream, token);
117
}
118
}
119
120
class SearchResponseProcessor extends PseudoStopStartResponseProcessor {
121
122
private _response = '';
123
124
constructor() {
125
super(
126
[{ start: '[ARGS END]', stop: '[ARGS START]' }],
127
(delta) => jsonToTable(parseSearchParams(delta.join(''))),
128
);
129
}
130
131
override async doProcessResponse(responseStream: AsyncIterable<IResponsePart>, progress: vscode.ChatResponseStream, token: CancellationToken): Promise<void> {
132
await super.doProcessResponse(responseStream, progress, token);
133
const args = parseSearchParams(this._response ?? '');
134
for (const command of createSearchFollowUps(args)) {
135
progress.button(command);
136
}
137
}
138
139
protected override applyDelta(delta: IResponseDelta, progress: vscode.ChatResponseStream): void {
140
this._response += delta.text;
141
super.applyDelta(delta, progress);
142
}
143
}
144
145
export class SearchIntent implements IIntent {
146
147
static readonly ID = Intent.Search;
148
readonly id: string = Intent.Search;
149
readonly locations = [ChatLocation.Panel];
150
readonly description: string = l10n.t('Generate query parameters for workspace search');
151
152
readonly commandInfo: IIntentSlashCommandInfo = {
153
allowsEmptyArgs: false,
154
defaultEnablement: isPreRelease,
155
};
156
157
constructor(
158
@IInstantiationService private readonly instantiationService: IInstantiationService,
159
@IEndpointProvider private readonly endpointProvider: IEndpointProvider,
160
) { }
161
162
async invoke(invocationContext: IIntentInvocationContext): Promise<IIntentInvocation> {
163
const location = invocationContext.location;
164
const endpoint = await this.endpointProvider.getChatEndpoint(invocationContext.request);
165
return this.instantiationService.createInstance(SearchIntentInvocation, this, location, endpoint);
166
}
167
}
168
169