Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/context/node/resolvers/fixSelection.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
6
import type * as vscode from 'vscode';
7
import { ILanguageDiagnosticsService } from '../../../../platform/languages/common/languageDiagnosticsService';
8
import { IChatEndpoint } from '../../../../platform/networking/common/networking';
9
import { TreeSitterAST, treeSitterToVSCodeRange, vscodeToTreeSitterRange } from '../../../../platform/parser/node/parserService';
10
import { ILanguage } from '../../../../util/common/languages';
11
import { Range } from '../../../../vscodeTypes';
12
import { CodeContextRegion, CodeContextTracker } from '../../../inlineChat/node/codeContextRegion';
13
import { IDocumentContext } from '../../../prompt/node/documentContext';
14
import { processCodeAroundSelection } from './inlineChatSelection';
15
16
interface IFixCodeContextInfo {
17
language: ILanguage;
18
above: CodeContextRegion;
19
range: CodeContextRegion;
20
below: CodeContextRegion;
21
}
22
23
24
export function generateFixContext(
25
endpoint: IChatEndpoint,
26
documentContext: IDocumentContext,
27
range: Range,
28
rangeOfInterest: Range
29
): { contextInfo: IFixCodeContextInfo; tracker: CodeContextTracker } {
30
31
// Number of tokens the endpoint can handle, 4 chars per token, we consume one 3rd
32
const charLimit = (endpoint.modelMaxPromptTokens * 4) / 3;
33
const tracker = new CodeContextTracker(charLimit);
34
const document = documentContext.document;
35
const language = documentContext.language;
36
37
const rangeInfo = new CodeContextRegion(tracker, document, language);
38
const aboveInfo = new CodeContextRegion(tracker, document, language);
39
const belowInfo = new CodeContextRegion(tracker, document, language);
40
41
const finish = () => {
42
aboveInfo.trim();
43
rangeInfo.trim();
44
belowInfo.trim();
45
return { contextInfo: { language, above: aboveInfo, range: rangeInfo, below: belowInfo }, tracker };
46
};
47
48
const continueExecution = processFixSelection(rangeInfo, range, rangeOfInterest);
49
50
if (!continueExecution) {
51
return finish();
52
}
53
54
const constraints = {
55
aboveLineIndex: rangeOfInterest.start.line - 1,
56
belowLineIndex: rangeOfInterest.end.line + 1,
57
minimumLineIndex: 0,
58
maximumLineIndex: document.lineCount - 1
59
};
60
61
processCodeAroundSelection(constraints, aboveInfo, belowInfo);
62
63
return finish();
64
}
65
66
/**
67
* Returns the range of the selection to use in the user context when running /fix
68
* @param range the code context region to process, where to store the selection information
69
* @param diagnosticsRange the range spanning the diagnostics
70
* @diagnosticsRangeOfInterest range around this spanning range which is permitted for editing
71
* @returns a boolean indicating whether to continue code execution
72
*/
73
function processFixSelection(range: CodeContextRegion, diagnosticsRange: Range, diagnosticsRangeOfInterest: Range): boolean {
74
const diagnosticsRangeMidLine = Math.floor((diagnosticsRange.start.line + diagnosticsRange.end.line) / 2);
75
const maximumRadius = Math.max(diagnosticsRangeMidLine - diagnosticsRangeOfInterest.start.line, diagnosticsRangeOfInterest.end.line - diagnosticsRangeMidLine);
76
77
range.appendLine(diagnosticsRangeMidLine);
78
for (let radius = 1; radius <= maximumRadius; radius++) {
79
const beforeMidLine = diagnosticsRangeMidLine - radius;
80
const afterMidLine = diagnosticsRangeMidLine + radius;
81
if (beforeMidLine >= diagnosticsRangeOfInterest.start.line) {
82
if (!range.prependLine(beforeMidLine)) {
83
return false;
84
}
85
}
86
if (afterMidLine <= diagnosticsRangeOfInterest.end.line) {
87
if (!range.appendLine(afterMidLine)) {
88
return false;
89
}
90
}
91
}
92
return true;
93
}
94
95
96
/**
97
* This function finds the diagnostics at the given selection and filtered by the actual prompt
98
*/
99
export function findDiagnosticForSelectionAndPrompt(diagnosticService: ILanguageDiagnosticsService, resource: vscode.Uri, selection: vscode.Selection | vscode.Range, prompt: string | undefined): vscode.Diagnostic[] {
100
const diagnostics = diagnosticService.getDiagnostics(resource).filter(d => !!d.range.intersection(selection));
101
if (prompt) {
102
const diagnosticsForPrompt = diagnostics.filter(d => prompt.includes(d.message));
103
if (diagnosticsForPrompt.length > 0) {
104
return diagnosticsForPrompt;
105
}
106
}
107
return diagnostics;
108
}
109
110
/**
111
* This function finds the range of interest for the input range for the /fix command
112
* @param maximumNumberOfLines the maximum number of lines in the range of interest
113
*/
114
export async function findFixRangeOfInterest(treeSitterAST: TreeSitterAST, range: Range, maximumNumberOfLines: number): Promise<Range> {
115
const treeSitterRange = vscodeToTreeSitterRange(range);
116
const maxNumberOfAdditionalLinesInRangeOfInterest = Math.max(maximumNumberOfLines, range.end.line - range.start.line + maximumNumberOfLines);
117
const treeSitterRangeOfInterest = await treeSitterAST.getFixSelectionOfInterest(treeSitterRange, maxNumberOfAdditionalLinesInRangeOfInterest);
118
return treeSitterToVSCodeRange(treeSitterRangeOfInterest);
119
}
120
121