Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/prompts/node/codeMapper/codeMapperPrompt.tsx
13405 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 { AssistantMessage, BasePromptElementProps, Chunk, PrioritizedList, PromptElement, PromptReference, PromptSizing, SystemMessage, UserMessage } from '@vscode/prompt-tsx';
7
8
import { NotebookDocumentSnapshot } from '../../../../platform/editing/common/notebookDocumentSnapshot';
9
import { TextDocumentSnapshot } from '../../../../platform/editing/common/textDocumentSnapshot';
10
import { IIgnoreService } from '../../../../platform/ignore/common/ignoreService';
11
import { IParserService } from '../../../../platform/parser/node/parserService';
12
import { getLanguageForResource } from '../../../../util/common/languages';
13
import { CharCode } from '../../../../util/vs/base/common/charCode';
14
import { StringEdit } from '../../../../util/vs/editor/common/core/edits/stringEdit';
15
import { IInstantiationService } from '../../../../util/vs/platform/instantiation/common/instantiation';
16
import { Uri } from '../../../../vscodeTypes';
17
import { getStructure } from '../../../context/node/resolvers/selectionContextHelpers';
18
import { CompositeElement } from '../base/common';
19
import { ResponseTranslationRules } from '../base/responseTranslationRules';
20
import { LegacySafetyRules } from '../base/safetyRules';
21
import { Tag } from '../base/tag';
22
import { ProjectedDocument } from '../inline/summarizedDocument/summarizeDocument';
23
import { DocumentSummarizer, NotebookDocumentSummarizer, summarizeDocumentSync } from '../inline/summarizedDocument/summarizeDocumentHelpers';
24
import { EXISTING_CODE_MARKER } from '../panel/codeBlockFormattingRules';
25
import { fileVariableCostFn } from '../panel/fileVariable';
26
import { CodeBlock } from '../panel/safeElements';
27
import { UnsafeCodeBlock } from '../panel/unsafeElements';
28
import { ICodeMapperRequestInput, isNewDocument } from './codeMapper';
29
import { PatchEditExamplePatch, PatchEditInputCodeBlock, PatchEditRules } from './patchEditGeneration';
30
31
export interface CodeMapperPromptProps extends BasePromptElementProps {
32
readonly request: ICodeMapperRequestInput;
33
readonly shouldTrimCodeBlocks?: boolean;
34
readonly inProgressRewriteContent?: string;
35
}
36
37
export class CodeMapperPatchRewritePrompt extends PromptElement<CodeMapperPromptProps> {
38
39
constructor(
40
props: CodeMapperPromptProps,
41
@IIgnoreService private readonly ignoreService: IIgnoreService,
42
@IParserService private readonly parserService: IParserService,
43
) {
44
super(props);
45
}
46
47
async render(state: void, sizing: PromptSizing) {
48
if (isNewDocument(this.props.request)) {
49
// TODO@joyceerhl @aeschli remove the find/replace variant
50
return;
51
}
52
53
const document = this.props.request.existingDocument;
54
55
const isIgnored = await this.ignoreService.isCopilotIgnored(document.uri);
56
if (isIgnored) {
57
return <ignoredFiles value={[document.uri]} />;
58
}
59
60
const inputDocCharLimit = (sizing.endpoint.modelMaxPromptTokens / 3) * 4; // consume one 3rd of the model window, estimating roughly 4 chars per token;
61
let projectedDocument: ProjectedDocument;
62
if (document.getText().length > inputDocCharLimit && document instanceof TextDocumentSnapshot) { // TODO@rebornix @DonJayamanne handle large notebook document
63
// only compute the summarized document if needed
64
const structure = await getStructure(this.parserService, document, undefined);
65
projectedDocument = summarizeDocumentSync(inputDocCharLimit, document, undefined, structure, { tryPreserveTypeChecking: false });
66
} else {
67
projectedDocument = new ProjectedDocument(document.getText(), StringEdit.empty, document.languageId);
68
}
69
const exampleUri = Uri.file('/someFolder/myFile.ts');
70
return (
71
<>
72
<references value={[new PromptReference(document.uri)]} />
73
<SystemMessage priority={1000}>
74
You are an AI programming assistant that is specialized in applying code changes to an existing document.<br />
75
I have a code block that represents a suggestion for a code change and I have a {document.languageId} file opened in a code editor.<br />
76
I expect you to come up with code changes that apply the code block to the editor.<br />
77
I want the changes to be applied in a way that is safe and does not break the existing code, is correctly indented and matching the code style.<br />
78
For the response, always follow these instructions:<br />
79
1. Analyse the code block, the content of the editor and the current selection to decide if the code block should replace existing code or is to be inserted.<br />
80
2. A line comment with `{EXISTING_CODE_MARKER}` indicates a section of code that has not changed<br />
81
3. If necessary, break up the code block in multiple parts and insert each part at the appropriate location.<br />
82
4. If necessary, make changes to other parts in the editor so that the final result is valid, properly formatted and indented.<br />
83
5. Finally, provide the code modifications<br />
84
<PatchEditRules />
85
<br />
86
<LegacySafetyRules />
87
<ResponseTranslationRules />
88
<Tag name='example' priority={100}>
89
<Tag name='user'>
90
91
I have the following code open in the editor.<br />
92
<PatchEditInputCodeBlock
93
uri={exampleUri}
94
languageId='typescript'
95
code={[`import { readFileSync } from 'fs';`, '', 'class C { }']}
96
/>
97
< br />
98
This is the code block that represents a suggestion for a code change:<br />
99
<UnsafeCodeBlock code={'private _stream: Stream;'} languageId={'typescript'} includeFilepath={false} />
100
< br />
101
Please find out how the code block can be applied to the editor.
102
</Tag>
103
<Tag name='assistant'>
104
<PatchEditExamplePatch
105
changes={
106
[
107
{
108
uri: exampleUri,
109
find: [`import { readFileSync } from 'fs';`,],
110
replace: [`import { readFileSync } from 'fs';`, `import { Stream } from 'stream';`]
111
},
112
{
113
uri: exampleUri,
114
find: ['class C { }'],
115
replace: ['class C {', '\tprivate _stream: Stream;', '}']
116
},
117
118
]
119
}
120
/>
121
</Tag>
122
</Tag>
123
</SystemMessage>
124
<UserMessage priority={700}>
125
<CompositeElement /*priority={600} TODO@aeschli commented out for fixed prompt-tsx issue */>
126
{
127
projectedDocument.text.length > 0 ?
128
<>
129
I have the following code open in the editor, starting from line 1 to line {projectedDocument.lineCount}.<br />
130
</> :
131
<>
132
I am in an empty file:<br />
133
</>
134
}
135
<PatchEditInputCodeBlock uri={document.uri} languageId={document.languageId} code={projectedDocument.text} shouldTrim={false} /><br />
136
</CompositeElement >
137
<CodeBlockChangeDescription markdownBeforeBlock={getLastSentence(this.props.request.markdownBeforeBlock)} />
138
This is the code block that represents a suggestion for a code change:<br />
139
<CodeBlock uri={document.uri} languageId={document.languageId} code={this.props.request.codeBlock} shouldTrim={false} includeFilepath={false} /><br />
140
<Tag name='userPrompt'>
141
Please find out how the code block can be applied to the editor. Provide the code changes in the format as described above.<br />
142
</Tag>
143
</UserMessage >
144
</>
145
);
146
}
147
}
148
149
interface CodeBlockChangeDescriptionProps extends BasePromptElementProps {
150
readonly markdownBeforeBlock?: string;
151
}
152
153
class CodeBlockChangeDescription extends PromptElement<CodeBlockChangeDescriptionProps> {
154
render() {
155
if (this.props.markdownBeforeBlock) {
156
return (<>
157
This is the description of what the code block changes:<br />
158
<Tag name='changeDescription'>
159
{this.props.markdownBeforeBlock}
160
</Tag>
161
<br />
162
</>
163
);
164
}
165
return undefined;
166
}
167
}
168
169
export class CodeMapperFullRewritePrompt extends PromptElement<CodeMapperPromptProps> {
170
constructor(
171
props: CodeMapperPromptProps,
172
@IIgnoreService private readonly ignoreService: IIgnoreService,
173
@IInstantiationService private readonly instantiationService: IInstantiationService,
174
) {
175
super(props);
176
}
177
178
async render(state: void, sizing: PromptSizing) {
179
180
const shouldTrimCodeBlocks = this.props.shouldTrimCodeBlocks ?? false;
181
if (isNewDocument(this.props.request)) {
182
const validDocumentContext = [];
183
for (const context of this.props.request.workingSet) {
184
const isIgnored = await this.ignoreService.isCopilotIgnored(context.uri);
185
if (!isIgnored) {
186
validDocumentContext.push(context);
187
}
188
}
189
190
return (<>
191
<references value={validDocumentContext.map(document => new PromptReference(document.uri))} />
192
<SystemMessage priority={1000}>
193
You are an AI programming assistant that is specialized in generating code for a new document.< br />
194
<LegacySafetyRules />
195
The user has a code block that represents a suggestion for the contents of a single new file, and several other files opened in a code editor.<br />
196
The provided files may contain code relevant to the new file. Consider them when generating the new file.<br />
197
For the response, always follow these instructions:<br />
198
1. Analyse the code block and the existing documents to decide which parts of the existing document should be incorporated in the generated code.<br />
199
2. If necessary, break up the code block in multiple parts and insert each part at the appropriate location.< br />
200
3. Preserve whitespace and newlines right after the parts of the file that you modify.<br />
201
4. The final result must be syntactically valid, properly formatted, and correctly indented. It should not contain any {EXISTING_CODE_MARKER} comments.<br />
202
5. Finally, provide the full contents of the new file.<br />
203
</SystemMessage>
204
<UserMessage priority={700}>
205
<PrioritizedList priority={690} descending={true}>
206
{/* Skip empty files since they contain no useful context */}
207
{validDocumentContext.map(document => (document.lineCount === 0 ? undefined : <Chunk>
208
<>
209
I have the following code from the file {document.uri.toString()} open in the editor, starting from line 1 to line {document.lineCount}.<br />
210
<CodeBlock uri={document.uri} languageId={document.languageId} code={document.getText()} shouldTrim={shouldTrimCodeBlocks} /><br />
211
</>
212
</Chunk>))}
213
</PrioritizedList>
214
<Chunk priority={695}>
215
<CodeBlockChangeDescription markdownBeforeBlock={this.props.request.markdownBeforeBlock} />
216
This is the code block that represents the suggested code change:<br />
217
<CodeBlock uri={this.props.request.uri} languageId={getLanguageForResource(this.props.request.uri).languageId} code={this.props.request.codeBlock} shouldTrim={shouldTrimCodeBlocks} /><br />
218
<Tag name='userPrompt'>
219
Provide the contents of the new file.
220
</Tag>
221
</Chunk>
222
</UserMessage>
223
{this.props.inProgressRewriteContent && <>
224
<AssistantMessage priority={800}>
225
{this.props.inProgressRewriteContent}
226
</AssistantMessage>
227
<UserMessage priority={900}>
228
Please continue providing the next part of the response.
229
</UserMessage>
230
</>}
231
</>);
232
}
233
234
const document = this.props.request.existingDocument;
235
const isIgnored = await this.ignoreService.isCopilotIgnored(document.uri);
236
if (isIgnored) {
237
return <ignoredFiles value={[document.uri]} />;
238
}
239
240
const summarized = document instanceof NotebookDocumentSnapshot ?
241
await this.instantiationService.createInstance(NotebookDocumentSummarizer).summarizeDocument(document, undefined, undefined, sizing.tokenBudget, {
242
costFnOverride: fileVariableCostFn,
243
}) :
244
await this.instantiationService.createInstance(DocumentSummarizer).summarizeDocument(document, undefined, undefined, sizing.tokenBudget, {
245
costFnOverride: fileVariableCostFn,
246
});
247
const code = summarized.text;
248
249
return (
250
<>
251
<references value={[new PromptReference(document.uri)]} />
252
<SystemMessage priority={1000}>
253
You are an AI programming assistant that is specialized in applying code changes to an existing document.< br />
254
<LegacySafetyRules />
255
The user has a code block that represents a suggestion for a code change and a {document.languageId} file opened in a code editor.<br />
256
Rewrite the existing document to fully incorporate the code changes in the provided code block.<br />
257
For the response, always follow these instructions:<br />
258
1. Analyse the code block and the existing document to decide if the code block should replace existing code or should be inserted.<br />
259
2. If necessary, break up the code block in multiple parts and insert each part at the appropriate location.< br />
260
3. Preserve whitespace and newlines right after the parts of the file that you modify.<br />
261
4. The final result must be syntactically valid, properly formatted, and correctly indented. It should not contain any ...existing code... comments.<br />
262
5. Finally, provide the fully rewritten file. You must output the complete file.<br />
263
</SystemMessage>
264
<UserMessage priority={700}>
265
{
266
document.lineCount > 0 ?
267
<>
268
I have the following code open in the editor, starting from line 1 to line {document.lineCount}.<br />
269
<CodeBlock uri={document.uri} languageId={document.languageId} code={code} shouldTrim={shouldTrimCodeBlocks} /><br />
270
</> :
271
<>
272
I am in an empty editor.
273
</>
274
}
275
<CodeBlockChangeDescription markdownBeforeBlock={this.props.request.markdownBeforeBlock} />
276
This is the code block that represents the suggested code change:<br />
277
<CodeBlock uri={document.uri} languageId={document.languageId} code={this.props.request.codeBlock} shouldTrim={shouldTrimCodeBlocks} /><br />
278
<Tag name='userPrompt'>
279
Provide the fully rewritten file, incorporating the suggested code change. You must produce the complete file.
280
</Tag>
281
</UserMessage>
282
{this.props.inProgressRewriteContent && <>
283
<AssistantMessage priority={800}>
284
{this.props.inProgressRewriteContent}
285
</AssistantMessage>
286
<UserMessage priority={900}>
287
Please continue providing the next part of the response.
288
</UserMessage>
289
</>}
290
</>
291
);
292
}
293
}
294
295
function getLastSentence(markdownBeforeBlock?: string): string | undefined {
296
if (markdownBeforeBlock) {
297
const whitespaces = [CharCode.Space, CharCode.Tab, CharCode.LineFeed, CharCode.CarriageReturn];
298
const newlines = [CharCode.LineFeed, CharCode.CarriageReturn];
299
let end = markdownBeforeBlock.length;
300
while (end > 0 && whitespaces.includes(markdownBeforeBlock.charCodeAt(end - 1))) {
301
end--;
302
}
303
let start = end;
304
while (start > 0 && !newlines.includes(markdownBeforeBlock.charCodeAt(start - 1))) {
305
start--;
306
}
307
if (start < end) {
308
return markdownBeforeBlock.substring(start, end);
309
}
310
311
}
312
return undefined;
313
}
314
315