Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/prompts/node/inline/fixCookbookService.ts
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
import { Diagnostic } from '../../../../vscodeTypes';
6
7
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry';
8
import { createServiceIdentifier } from '../../../../util/common/services';
9
import { pythonRuffCookbooks } from './pythonCookbookData';
10
11
export const IFixCookbookService = createServiceIdentifier<IFixCookbookService>('IFixCookbookService');
12
13
export interface IFixCookbookService {
14
readonly _serviceBrand: undefined;
15
getCookbook(language: string, diagnostic: Diagnostic): Cookbook;
16
}
17
18
export enum ContextLocation {
19
ParentCallDefinition,
20
DefinitionAtLocation
21
}
22
23
export type ManualSuggestedFix = {
24
title: string;
25
/** The copilot query is equal to title + message. */
26
message: string;
27
/** If true, replace the diagnostic provider's message with this message. */
28
replaceMessage?: string;
29
/** If true, display definition */
30
additionalContext?: ContextLocation;
31
};
32
33
export class FixCookbookService implements IFixCookbookService {
34
readonly _serviceBrand: undefined;
35
constructor(
36
@ITelemetryService private readonly telemetryService: ITelemetryService
37
) {
38
// Always enable the Ruff cookbook by default
39
errorPrompts.Ruff = pythonRuffCookbooks;
40
}
41
42
getCookbook(language: string, diagnostic: Diagnostic): Cookbook {
43
const code = typeof diagnostic.code === 'object' ? diagnostic.code.value : diagnostic.code;
44
const fixes = this._getManualSuggestedFixes(language, diagnostic.source as Provider | undefined, code);
45
return {
46
fixes,
47
messageReplacement() {
48
for (const fix of fixes) {
49
if (fix.replaceMessage !== undefined) {
50
return fix.replaceMessage;
51
}
52
}
53
return undefined;
54
},
55
additionalContext() {
56
const definitions: ContextLocation[] = [];
57
for (const fix of fixes) {
58
if (fix.additionalContext !== undefined) {
59
definitions.push(fix.additionalContext);
60
}
61
}
62
return definitions;
63
}
64
};
65
}
66
67
private _getManualSuggestedFixes(languageId: string, provider: Provider | undefined, diagnostic: string | number | undefined): ManualSuggestedFix[] {
68
if (!provider || diagnostic === undefined) {
69
return [];
70
}
71
72
const providerPrompts = errorPrompts[provider];
73
if (!providerPrompts) {
74
return [];
75
}
76
77
const diagnosticPrompts = providerPrompts[diagnostic];
78
if (!diagnosticPrompts) {
79
return [];
80
}
81
82
// send telemetry
83
/* __GDPR__
84
"cookbook.accessed" : {
85
"owner": "luabud",
86
"comment": "Reports when a cookbook entry is accessed for a diagnostic.",
87
"languageID": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The current file language." },
88
"diagnosticCode": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The diagnostic code accessed in the cookbook." },
89
"provider": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The tool which is the diagnostic provider." }
90
}
91
*/
92
this.telemetryService.sendMSFTTelemetryEvent('cookbook.accessed', {
93
languageId,
94
diagnosticCode: diagnostic.toString(),
95
provider
96
});
97
98
// Ensure result is always an array of ManualSuggestedFix
99
const prompts = Array.isArray(diagnosticPrompts) ? diagnosticPrompts : [diagnosticPrompts];
100
return prompts.map(prompt => typeof prompt === 'string' ? { title: prompt, message: '' } : prompt);
101
}
102
103
104
}
105
106
export interface Cookbook {
107
messageReplacement(): string | undefined;
108
additionalContext(): ContextLocation[];
109
readonly fixes: readonly ManualSuggestedFix[];
110
}
111
112
113
/** add diagnostic providers here as needed */
114
type Provider = 'eslint' | 'ts' | 'pylint' | 'Pylint' | 'Ruff';
115
type Prompt = string | ManualSuggestedFix | (string | ManualSuggestedFix)[];
116
type CookbookInternal = Record<Provider, Record<string, Prompt>>;
117
118
119
const errorPrompts: CookbookInternal = {
120
'Ruff': {}, // default to empty during experimentation
121
122
'pylint': {
123
'C0301': [
124
'Split into many short lines to make sure each line is less than 20 tokens; split into many more lines than you normally would. Make sure to do the following: You must split all long strings, comments, and dictionary arguments and lists into shorter lines.',
125
]
126
},
127
'Pylint': {
128
'C0301:line-too-long': [
129
'Split into many short lines to make sure each line is less than 20 tokens; split into many more lines than you normally would. Make sure to do the following: You must split all long strings, comments, and dictionary arguments and lists into shorter lines.',
130
]
131
},
132
'ts': {
133
2345: { title: 'Use this declaration and other usages as examples.', message: '', additionalContext: ContextLocation.ParentCallDefinition },
134
2554: { title: 'Use this declaration and other usages as examples.', message: '', additionalContext: ContextLocation.ParentCallDefinition },
135
},
136
'eslint': {
137
'class-methods-use-this': [
138
'Make the method static.',
139
'Move the method outside of the class.',
140
'Rewrite the method to use properties of the class.',
141
],
142
'consistent-this': [
143
'Use the alias for this required by eslint consistent-this rule.',
144
'Use this directly instead of an alias.',
145
],
146
'constructor-super': {
147
title: 'Add missing super call and pass through new arguments.',
148
message: 'The code is missing a super call in the constructor. Copy base class parameters to this constructor and pass them to super.',
149
replaceMessage: 'Missing super call in constructor',
150
},
151
'func-names': [
152
'Give the function expression the same name as the variable it is assigned to.',
153
'Convert the function to an arrow function.',
154
],
155
'func-style': [
156
{
157
title: 'Convert the function declaration to an expression.',
158
message: 'The function expression should be assigned to a variable with the name of the original function declaration.',
159
},
160
],
161
'max-lines-per-function': [
162
'Split into multiple functions.',
163
],
164
'max-nested-callbacks': [
165
'Rewrite to avoid at least one callback.',
166
],
167
'max-params': [
168
{
169
title: 'Rewrite the signature to use an object parameter.',
170
message: 'Preserve all the parameters of the original signature.'
171
}
172
],
173
'max-statements': [
174
'Split into multiple functions.',
175
],
176
'no-case-declarations': [
177
'Surround the case block with braces.',
178
'Move the declaration outside the case block.',
179
],
180
'no-dupe-else-if': [
181
'Fix the duplicate condition to be different from the first.',
182
'Remove the duplicate condition',
183
],
184
'no-duplicate-case': [
185
{
186
title: 'Change the duplicate condition to be different.',
187
message: 'Do not delete the duplicate case, just fix it',
188
replaceMessage: 'Duplicated condition.'
189
},
190
'Remove the duplicate condition',
191
],
192
'no-duplicate-imports': 'Merge the duplicated import lines.',
193
'no-fallthrough': [
194
{
195
title: 'Rewrite to avoid fallthrough.',
196
message: 'Use the return value of the following cases and copy it to the preceding fallthrough case.',
197
replaceMessage: 'Fallthrough case in switch statement.'
198
},
199
'Add a // fallthrough comment.',
200
'Add a break statement.',
201
],
202
'no-inner-declarations': [
203
'Move the inner declaration to the top of its containing function.',
204
'Move the inner declaration to the bottom of its containing function.',
205
'Change the inner function declaration to an expression.',
206
],
207
'no-multi-assign': 'Assign each variable in separate statements.',
208
'no-negated-condition': 'Invert the branches of the conditional.',
209
'no-new': [
210
'Convert the class to functions.',
211
'Assign the resulting object to a variable.',
212
],
213
'no-sequences': [
214
{ title: 'Wrap the whole comma sequence in parentheses.', message: '', replaceMessage: 'Unnecessary comma sequence' },
215
{ title: 'Rewrite, preserving the original behavior.', message: 'The last element of the comma sequence is the one returned.' },
216
{ title: 'Delete the non-final elements of the sequence.', message: 'They are unused unless it is for side effects.' },
217
],
218
'no-sparse-arrays': [
219
'Remove duplicated commas.',
220
'Add default values for the missing elements.',
221
],
222
'require-await': [
223
'Removed the unused async keyword.',
224
{
225
title: 'Rewrite the function to use await.',
226
message: 'The code should change to call asynchronous functions where appropriate.'
227
},
228
],
229
'sort-keys': {
230
title: 'Sort the properties of the entire object literal.',
231
message: '',
232
replaceMessage: 'Unsorted keys in object literal.',
233
}
234
}
235
};
236
237