Path: blob/main/extensions/copilot/src/extension/prompts/node/inline/diffEditGeneration.tsx
13405 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { PromptElement } from '@vscode/prompt-tsx';6import { extractCodeBlocks } from '../../../../util/common/markdown';7import { splitLines } from '../../../../util/vs/base/common/strings';8import { TextEdit } from '../../../../vscodeTypes';9import { OutcomeAnnotation } from '../../../inlineChat/node/promptCraftingTypes';10import { createEditsFromPseudoDiff } from '../../../prompt/node/editFromDiffGeneration';11import { LineRange, Lines, LinesEdit } from '../../../prompt/node/editGeneration';1213export class EditGenerationRules extends PromptElement {14render() {15return (16<>17For the response always follow these instructions:<br />18Describe in a single sentence how you would solve the problem. After that sentence, add an empty line. Then add a code block with the fix.<br />19When proposing a change in the code, use a single code block that starts with ```diff and that describes the changes in the diff format. In the diff, always use tab to indent, never spaces. Make sure that the diff format is valid and contains all changes: Removed and unchanged lines must match exactly the original code line. Keep the changes minimal and the diff short.<br />20When proposing to fix the problem by running a terminal command, provide the terminal script in a code block that starts with ```bash.<br />21</>22);23}24}2526export class EditGenerationExampleSetup extends PromptElement {27render() {28return (29<>30```csharp<br />31// This is my class<br />32class C { }<br />33<br />34new C().Field = 9;<br />35```36</>37);38}39}4041export class EditGenerationExampleSolution extends PromptElement {42render() {43return (44<>45The problem is that the class 'C' does not have a field or property named 'Field'. To fix this, you need to add a 'Field' property to the 'C' class.<br />46<br />47```diff<br />48// This is my class<br />49-class C { }<br />50+class C {<br />51+ public int Field { get; set; }<br />52+}<br />53<br />54new C().Field = 9;<br />55```<br />56</>57);58}59}6061export interface ReplyProcessor {62getFirstSentence(text: string): string;63process(replyText: string, documentText: string, lineRange: LineRange): ReplyProcessorResult;64}6566export type ReplyProcessorResult = { edits?: TextEdit[]; content?: string; annotations: OutcomeAnnotation[] };6768export function getReplyProcessor(): ReplyProcessor {69return {70getFirstSentence(text: string): string {71return text.split('```', 1)[0].match(/^.+/)?.[0] ?? '';72},73process(replyText: string, documentText: string, lineRange: LineRange): ReplyProcessorResult {74const annotations: OutcomeAnnotation[] = [];75const extractResult = extractAndParseFirstCodeBlock(replyText);76if (!extractResult || extractResult.language === 'bash' || extractResult.language === 'ps1') {77return { content: replyText, annotations };78}79let lineEdits: LinesEdit[] = [];80if (extractResult.language === 'diff') {81const diff = Lines.fromString(extractResult.code);82const code = Lines.fromString(documentText);83const reporter = {84recovery: () => { },85warning(message: string) {86if (annotations.length === 0) {87annotations.push({ message: message, label: 'invalid diff', severity: 'error' });88}89}90};91lineEdits = createEditsFromPseudoDiff(code, diff, reporter);92} else {93lineEdits = [new LinesEdit(lineRange.firstLineIndex, lineRange.endLineIndex, Lines.fromString(extractResult.code))];94}95const edits = lineEdits.map(e => e.toTextEdit());96return { edits, annotations };97}9899};100}101102function extractAndParseFirstCodeBlock(text: string): { code: string; contentBeforeCode: string; language: string } | undefined {103const blocks = extractCodeBlocks(text);104const firstBlock = blocks.at(0);105if (firstBlock) {106const lines = splitLines(text);107return { code: firstBlock.code, contentBeforeCode: lines.slice(0, firstBlock.startLine).join('\n').trimEnd(), language: firstBlock.language };108}109return undefined;110}111112113