Path: blob/main/extensions/copilot/src/extension/context/node/resolvers/fixSelection.ts
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 type * as vscode from 'vscode';6import { ILanguageDiagnosticsService } from '../../../../platform/languages/common/languageDiagnosticsService';7import { IChatEndpoint } from '../../../../platform/networking/common/networking';8import { TreeSitterAST, treeSitterToVSCodeRange, vscodeToTreeSitterRange } from '../../../../platform/parser/node/parserService';9import { ILanguage } from '../../../../util/common/languages';10import { Range } from '../../../../vscodeTypes';11import { CodeContextRegion, CodeContextTracker } from '../../../inlineChat/node/codeContextRegion';12import { IDocumentContext } from '../../../prompt/node/documentContext';13import { processCodeAroundSelection } from './inlineChatSelection';1415interface IFixCodeContextInfo {16language: ILanguage;17above: CodeContextRegion;18range: CodeContextRegion;19below: CodeContextRegion;20}212223export function generateFixContext(24endpoint: IChatEndpoint,25documentContext: IDocumentContext,26range: Range,27rangeOfInterest: Range28): { contextInfo: IFixCodeContextInfo; tracker: CodeContextTracker } {2930// Number of tokens the endpoint can handle, 4 chars per token, we consume one 3rd31const charLimit = (endpoint.modelMaxPromptTokens * 4) / 3;32const tracker = new CodeContextTracker(charLimit);33const document = documentContext.document;34const language = documentContext.language;3536const rangeInfo = new CodeContextRegion(tracker, document, language);37const aboveInfo = new CodeContextRegion(tracker, document, language);38const belowInfo = new CodeContextRegion(tracker, document, language);3940const finish = () => {41aboveInfo.trim();42rangeInfo.trim();43belowInfo.trim();44return { contextInfo: { language, above: aboveInfo, range: rangeInfo, below: belowInfo }, tracker };45};4647const continueExecution = processFixSelection(rangeInfo, range, rangeOfInterest);4849if (!continueExecution) {50return finish();51}5253const constraints = {54aboveLineIndex: rangeOfInterest.start.line - 1,55belowLineIndex: rangeOfInterest.end.line + 1,56minimumLineIndex: 0,57maximumLineIndex: document.lineCount - 158};5960processCodeAroundSelection(constraints, aboveInfo, belowInfo);6162return finish();63}6465/**66* Returns the range of the selection to use in the user context when running /fix67* @param range the code context region to process, where to store the selection information68* @param diagnosticsRange the range spanning the diagnostics69* @diagnosticsRangeOfInterest range around this spanning range which is permitted for editing70* @returns a boolean indicating whether to continue code execution71*/72function processFixSelection(range: CodeContextRegion, diagnosticsRange: Range, diagnosticsRangeOfInterest: Range): boolean {73const diagnosticsRangeMidLine = Math.floor((diagnosticsRange.start.line + diagnosticsRange.end.line) / 2);74const maximumRadius = Math.max(diagnosticsRangeMidLine - diagnosticsRangeOfInterest.start.line, diagnosticsRangeOfInterest.end.line - diagnosticsRangeMidLine);7576range.appendLine(diagnosticsRangeMidLine);77for (let radius = 1; radius <= maximumRadius; radius++) {78const beforeMidLine = diagnosticsRangeMidLine - radius;79const afterMidLine = diagnosticsRangeMidLine + radius;80if (beforeMidLine >= diagnosticsRangeOfInterest.start.line) {81if (!range.prependLine(beforeMidLine)) {82return false;83}84}85if (afterMidLine <= diagnosticsRangeOfInterest.end.line) {86if (!range.appendLine(afterMidLine)) {87return false;88}89}90}91return true;92}939495/**96* This function finds the diagnostics at the given selection and filtered by the actual prompt97*/98export function findDiagnosticForSelectionAndPrompt(diagnosticService: ILanguageDiagnosticsService, resource: vscode.Uri, selection: vscode.Selection | vscode.Range, prompt: string | undefined): vscode.Diagnostic[] {99const diagnostics = diagnosticService.getDiagnostics(resource).filter(d => !!d.range.intersection(selection));100if (prompt) {101const diagnosticsForPrompt = diagnostics.filter(d => prompt.includes(d.message));102if (diagnosticsForPrompt.length > 0) {103return diagnosticsForPrompt;104}105}106return diagnostics;107}108109/**110* This function finds the range of interest for the input range for the /fix command111* @param maximumNumberOfLines the maximum number of lines in the range of interest112*/113export async function findFixRangeOfInterest(treeSitterAST: TreeSitterAST, range: Range, maximumNumberOfLines: number): Promise<Range> {114const treeSitterRange = vscodeToTreeSitterRange(range);115const maxNumberOfAdditionalLinesInRangeOfInterest = Math.max(maximumNumberOfLines, range.end.line - range.start.line + maximumNumberOfLines);116const treeSitterRangeOfInterest = await treeSitterAST.getFixSelectionOfInterest(treeSitterRange, maxNumberOfAdditionalLinesInRangeOfInterest);117return treeSitterToVSCodeRange(treeSitterRangeOfInterest);118}119120121