Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/inlineChat/node/codeContextRegion.ts
13399 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 { TextDocumentSnapshot } from '../../../platform/editing/common/textDocumentSnapshot';
8
import { ILanguage } from '../../../util/common/languages';
9
import { FilePathCodeMarker } from '../../context/node/resolvers/selectionContextHelpers';
10
11
/**
12
* A tracker for the number of characters in a sequence of lines.
13
*/
14
export class CodeContextTracker {
15
private _totalChars = 0;
16
17
constructor(private readonly charLimit: number) { }
18
19
public get totalChars(): number {
20
return this._totalChars;
21
}
22
23
public addLine(line: string): void {
24
this._totalChars += line.length + 1;
25
}
26
27
public lineWouldFit(line: string): boolean {
28
return this._totalChars + line.length + 1 < this.charLimit;
29
}
30
}
31
32
/**
33
* Represents a sequence of lines in the document.
34
*/
35
export class CodeContextRegion {
36
public readonly lines: string[] = [];
37
private firstLineIndex: number = this.document.lineCount;
38
private lastLineIndex = -1;
39
public isComplete = false;
40
private nonTrimWhitespaceCharCount = 0;
41
42
private get hasContent(): boolean {
43
if (this.lines.length === 0 || this.nonTrimWhitespaceCharCount === 0) {
44
return false;
45
}
46
return this.lines.length > 0;
47
}
48
49
constructor(
50
private readonly tracker: CodeContextTracker,
51
private readonly document: TextDocumentSnapshot,
52
public readonly language: ILanguage,
53
) {
54
this.lines = [];
55
this.firstLineIndex = document.lineCount;
56
this.lastLineIndex = -1;
57
}
58
59
public generatePrompt(): string[] {
60
if (!this.hasContent) {
61
return [];
62
}
63
const result: string[] = [];
64
result.push('```' + this.language.languageId); // TODO@ulugbekna: use languageIdToMDCodeBlockLang & createFencedCodeBlock
65
result.push(FilePathCodeMarker.forDocument(this.language, this.document));//
66
result.push(...this.lines);
67
result.push('```');
68
return result;
69
}
70
71
public prependLine(lineIndex: number): boolean {
72
const line = this.document.lineAt(lineIndex);
73
const lineText = line.text;
74
if (!this.tracker.lineWouldFit(lineText)) {
75
return false;
76
}
77
this.firstLineIndex = Math.min(this.firstLineIndex, lineIndex);
78
this.lastLineIndex = Math.max(this.lastLineIndex, lineIndex);
79
this.lines.unshift(lineText);
80
this.tracker.addLine(lineText);
81
this.nonTrimWhitespaceCharCount += lineText.trim().length;
82
return true;
83
}
84
85
public appendLine(lineIndex: number): boolean {
86
const line = this.document.lineAt(lineIndex);
87
const lineText = line.text;
88
if (!this.tracker.lineWouldFit(lineText)) {
89
return false;
90
}
91
this.firstLineIndex = Math.min(this.firstLineIndex, lineIndex);
92
this.lastLineIndex = Math.max(this.lastLineIndex, lineIndex);
93
this.lines.push(lineText);
94
this.tracker.addLine(lineText);
95
this.nonTrimWhitespaceCharCount += lineText.trim().length;
96
return true;
97
}
98
99
/**
100
* Trims the empty lines from the beginning and end of the code context region.
101
* If a `rangeToNotModify` is provided, it will not trim away lines included in that range.
102
* @param rangeToNotModify Optional range to not modify while trimming.
103
*/
104
public trim(rangeToNotModify?: vscode.Range): void {
105
// remove empty lines from the beginning
106
// but do not trim away lines included in `rangeToNotModify`
107
const maxFirstLineIndex = rangeToNotModify ? Math.min(this.lastLineIndex, rangeToNotModify.start.line) : this.lastLineIndex;
108
while (this.firstLineIndex < maxFirstLineIndex && this.lines.length > 0 && this.lines[0].trim().length === 0) {
109
this.firstLineIndex++;
110
this.lines.shift();
111
}
112
113
// remove empty lines from the end
114
// but do not trim away lines included in `rangeToNotModify`
115
const minLastLineIndex = rangeToNotModify ? Math.max(this.firstLineIndex, rangeToNotModify.end.line) : this.firstLineIndex;
116
while (minLastLineIndex < this.lastLineIndex &&
117
this.lines.length > 0 &&
118
this.lines[this.lines.length - 1].trim().length === 0) {
119
this.lastLineIndex--;
120
this.lines.pop();
121
}
122
}
123
124
public toString(): string {
125
return `{${this.firstLineIndex} -> ${this.lastLineIndex}}`;
126
}
127
}
128
129