Path: blob/main/extensions/copilot/src/extension/inlineChat/node/codeContextRegion.ts
13399 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 { TextDocumentSnapshot } from '../../../platform/editing/common/textDocumentSnapshot';7import { ILanguage } from '../../../util/common/languages';8import { FilePathCodeMarker } from '../../context/node/resolvers/selectionContextHelpers';910/**11* A tracker for the number of characters in a sequence of lines.12*/13export class CodeContextTracker {14private _totalChars = 0;1516constructor(private readonly charLimit: number) { }1718public get totalChars(): number {19return this._totalChars;20}2122public addLine(line: string): void {23this._totalChars += line.length + 1;24}2526public lineWouldFit(line: string): boolean {27return this._totalChars + line.length + 1 < this.charLimit;28}29}3031/**32* Represents a sequence of lines in the document.33*/34export class CodeContextRegion {35public readonly lines: string[] = [];36private firstLineIndex: number = this.document.lineCount;37private lastLineIndex = -1;38public isComplete = false;39private nonTrimWhitespaceCharCount = 0;4041private get hasContent(): boolean {42if (this.lines.length === 0 || this.nonTrimWhitespaceCharCount === 0) {43return false;44}45return this.lines.length > 0;46}4748constructor(49private readonly tracker: CodeContextTracker,50private readonly document: TextDocumentSnapshot,51public readonly language: ILanguage,52) {53this.lines = [];54this.firstLineIndex = document.lineCount;55this.lastLineIndex = -1;56}5758public generatePrompt(): string[] {59if (!this.hasContent) {60return [];61}62const result: string[] = [];63result.push('```' + this.language.languageId); // TODO@ulugbekna: use languageIdToMDCodeBlockLang & createFencedCodeBlock64result.push(FilePathCodeMarker.forDocument(this.language, this.document));//65result.push(...this.lines);66result.push('```');67return result;68}6970public prependLine(lineIndex: number): boolean {71const line = this.document.lineAt(lineIndex);72const lineText = line.text;73if (!this.tracker.lineWouldFit(lineText)) {74return false;75}76this.firstLineIndex = Math.min(this.firstLineIndex, lineIndex);77this.lastLineIndex = Math.max(this.lastLineIndex, lineIndex);78this.lines.unshift(lineText);79this.tracker.addLine(lineText);80this.nonTrimWhitespaceCharCount += lineText.trim().length;81return true;82}8384public appendLine(lineIndex: number): boolean {85const line = this.document.lineAt(lineIndex);86const lineText = line.text;87if (!this.tracker.lineWouldFit(lineText)) {88return false;89}90this.firstLineIndex = Math.min(this.firstLineIndex, lineIndex);91this.lastLineIndex = Math.max(this.lastLineIndex, lineIndex);92this.lines.push(lineText);93this.tracker.addLine(lineText);94this.nonTrimWhitespaceCharCount += lineText.trim().length;95return true;96}9798/**99* Trims the empty lines from the beginning and end of the code context region.100* If a `rangeToNotModify` is provided, it will not trim away lines included in that range.101* @param rangeToNotModify Optional range to not modify while trimming.102*/103public trim(rangeToNotModify?: vscode.Range): void {104// remove empty lines from the beginning105// but do not trim away lines included in `rangeToNotModify`106const maxFirstLineIndex = rangeToNotModify ? Math.min(this.lastLineIndex, rangeToNotModify.start.line) : this.lastLineIndex;107while (this.firstLineIndex < maxFirstLineIndex && this.lines.length > 0 && this.lines[0].trim().length === 0) {108this.firstLineIndex++;109this.lines.shift();110}111112// remove empty lines from the end113// but do not trim away lines included in `rangeToNotModify`114const minLastLineIndex = rangeToNotModify ? Math.max(this.firstLineIndex, rangeToNotModify.end.line) : this.firstLineIndex;115while (minLastLineIndex < this.lastLineIndex &&116this.lines.length > 0 &&117this.lines[this.lines.length - 1].trim().length === 0) {118this.lastLineIndex--;119this.lines.pop();120}121}122123public toString(): string {124return `{${this.firstLineIndex} -> ${this.lastLineIndex}}`;125}126}127128129