forked from microsoft/vscode-copilot-chat
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfindFilesTool.tsx
More file actions
117 lines (99 loc) · 5.15 KB
/
Copy pathfindFilesTool.tsx
File metadata and controls
117 lines (99 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { BasePromptElementProps, PromptElement, PromptElementProps, PromptPiece, PromptReference, PromptSizing, TextChunk } from '@vscode/prompt-tsx';
import type * as vscode from 'vscode';
import { IPromptPathRepresentationService } from '../../../platform/prompts/common/promptPathRepresentationService';
import { URI } from '../../../util/vs/base/common/uri';
import * as l10n from '@vscode/l10n';
import { ISearchService } from '../../../platform/search/common/searchService';
import { IWorkspaceService } from '../../../platform/workspace/common/workspaceService';
import { CancellationToken } from '../../../util/vs/base/common/cancellation';
import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';
import { ExtendedLanguageModelToolResult, LanguageModelPromptTsxPart, MarkdownString } from '../../../vscodeTypes';
import { IBuildPromptContext } from '../../prompt/common/intents';
import { renderPromptElementJSON } from '../../prompts/node/base/promptRenderer';
import { ToolName } from '../common/toolNames';
import { CopilotToolMode, ICopilotTool, ToolRegistry } from '../common/toolsRegistry';
import { checkCancellation, inputGlobToPattern } from './toolUtils';
export interface IFindFilesToolParams {
query: string;
maxResults?: number;
}
export class FindFilesTool implements ICopilotTool<IFindFilesToolParams> {
public static readonly toolName = ToolName.FindFiles;
constructor(
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ISearchService private readonly searchService: ISearchService,
@IWorkspaceService private readonly workspaceService: IWorkspaceService,
) { }
async invoke(options: vscode.LanguageModelToolInvocationOptions<IFindFilesToolParams>, token: CancellationToken) {
checkCancellation(token);
// The input _should_ be a pattern matching inside a workspace, folder, but sometimes we get absolute paths, so try to resolve them
const pattern = inputGlobToPattern(options.input.query, this.workspaceService);
const results = await this.searchService.findFiles(pattern, undefined, token);
checkCancellation(token);
const maxResults = options.input.maxResults ?? 20;
const resultsToShow = results.slice(0, maxResults);
const result = new ExtendedLanguageModelToolResult([
new LanguageModelPromptTsxPart(
await renderPromptElementJSON(this.instantiationService, FindFilesResult, { fileResults: resultsToShow, totalResults: results.length }, options.tokenizationOptions, token))]);
const query = `\`${options.input.query}\``;
result.toolResultMessage = resultsToShow.length === 0 ?
new MarkdownString(l10n.t`Searched for files matching ${query}, no matches`) :
resultsToShow.length === 1 ?
new MarkdownString(l10n.t`Searched for files matching ${query}, 1 match`) :
new MarkdownString(l10n.t`Searched for files matching ${query}, ${resultsToShow.length} matches`);
result.toolResultDetails = resultsToShow;
return result;
}
prepareInvocation(options: vscode.LanguageModelToolInvocationPrepareOptions<IFindFilesToolParams>, token: vscode.CancellationToken): vscode.ProviderResult<vscode.PreparedToolInvocation> {
const query = `\`${options.input.query}\``;
return {
invocationMessage: new MarkdownString(l10n.t`Searching for files matching ${query}`)
};
}
async resolveInput(input: IFindFilesToolParams, _promptContext: IBuildPromptContext, mode: CopilotToolMode): Promise<IFindFilesToolParams> {
let query = input.query;
if (!query.startsWith('**/')) {
query = `**/${query}`;
}
if (query.endsWith('/')) {
query = `${query}**`;
}
return {
...input,
query,
maxResults: mode === CopilotToolMode.FullContext ?
Math.max(input.maxResults ?? 0, 200) :
input.maxResults ?? 20,
};
}
}
ToolRegistry.registerTool(FindFilesTool);
export interface FindFilesResultProps extends BasePromptElementProps {
fileResults: URI[];
totalResults: number;
}
export class FindFilesResult extends PromptElement<FindFilesResultProps> {
constructor(
props: PromptElementProps<FindFilesResultProps>,
@IPromptPathRepresentationService private readonly promptPathRepresentationService: IPromptPathRepresentationService,
) {
super(props);
}
override render(state: void, sizing: PromptSizing): PromptPiece<any, any> | undefined {
if (this.props.fileResults.length === 0) {
return <>No files found</>;
}
return <>
{<TextChunk priority={20}>{this.props.totalResults === 1 ? '1 total result' : `${this.props.totalResults} total results`}</TextChunk>}
{this.props.fileResults.map(file => <TextChunk priority={10}>
<references value={[new PromptReference(file, undefined, { isFromTool: true })]} />
{this.promptPathRepresentationService.getFilePath(file)}
</TextChunk>)}
{this.props.totalResults > this.props.fileResults.length && <TextChunk priority={20}>{'...'}</TextChunk>}
</>;
}
}