Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/prompts/node/test/utils.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 * as fs from 'fs';
7
import type * as vscode from 'vscode';
8
import { VsCodeTextDocument } from '../../../../platform/editing/common/abstractText';
9
import { TextDocumentSnapshot } from '../../../../platform/editing/common/textDocumentSnapshot';
10
import { getStructureUsingIndentation } from '../../../../platform/parser/node/indentationStructure';
11
import { IParserService } from '../../../../platform/parser/node/parserService';
12
import { WASMLanguage } from '../../../../platform/parser/node/treeSitterLanguages';
13
import { IPlaygroundRunnerGlobals } from '../../../../util/common/debugValueEditorGlobals';
14
import { createTextDocumentData } from '../../../../util/common/test/shims/textDocument';
15
import * as path from '../../../../util/vs/base/common/path';
16
import { URI } from '../../../../util/vs/base/common/uri';
17
import { OffsetRange } from '../../../../util/vs/editor/common/core/ranges/offsetRange';
18
import { Range, Selection } from '../../../../vscodeTypes';
19
import { createExtensionUnitTestingServices } from '../../../test/node/services';
20
import { getAdjustedSelection } from '../inline/adjustSelection';
21
import { IProjectedDocumentDebugInfo } from '../inline/summarizedDocument/implementation';
22
import { IDocumentSummarizationItem, ISummarizedDocumentSettings, summarizeDocumentsSync } from '../inline/summarizedDocument/summarizeDocument';
23
import { summarizeDocumentSync } from '../inline/summarizedDocument/summarizeDocumentHelpers';
24
import { SummarizeDocumentPlayground } from './summarizeDocumentPlayground';
25
26
export const DEFAULT_CHAR_LIMIT = 9557.333333333334;
27
28
export function loadFile(data: FixtureData): Promise<ITestFile>;
29
export function loadFile(data: Omit<FixtureData, 'filePath'> & { fileName: string; fileContents: string }): Promise<'not_supported'>;
30
export async function loadFile(data: FixtureData | (Omit<FixtureData, 'filePath'> & { fileName: string; fileContents: string })): Promise<ITestFile | 'not_supported'> {
31
if ('fileName' in data) { return 'not_supported'; }
32
const contents = (await fs.promises.readFile(data.filePath)).toString();
33
return {
34
contents,
35
filePath: data.filePath,
36
languageId: data.languageId,
37
formattingOptions: undefined,
38
};
39
}
40
41
interface FixtureData {
42
filePath: string;
43
languageId: 'typescript' | string;
44
}
45
46
/** See https://github.com/microsoft/vscode-ts-file-path-support */
47
export type RelativeFilePath<T extends string> = string & { baseDir?: T };
48
49
export function fixture(relativePath: RelativeFilePath<'$dir/fixtures'>): string {
50
const filePath = path.join(__dirname, 'fixtures', relativePath);
51
return filePath;
52
}
53
export function getSummarizedSnapshotPath(data: ITestFile, version?: string): string {
54
const secondaryExtension = (version ? `${version}.` : '') + 'summarized';
55
return addSecondaryExtension(data.filePath, secondaryExtension);
56
}
57
58
function addSecondaryExtension(filePath: string, extension: string): string {
59
const parts = filePath.split('.');
60
parts.splice(parts.length - 1, 0, extension);
61
return parts.join('.');
62
}
63
64
65
export async function fromFixtureOld(
66
pathWithinFixturesDir: string,
67
languageId: WASMLanguage | string,
68
formattingOptions?: vscode.FormattingOptions
69
): Promise<ITestFile> {
70
const filePath = path.join(__dirname, 'fixtures', pathWithinFixturesDir);
71
const contents = (await fs.promises.readFile(filePath)).toString();
72
return { filePath: filePath, contents, languageId, formattingOptions };
73
}
74
75
export function docPathInFixture(pathWithinFixturesDir: string, type: 'summarized' | 'selection') {
76
const dirname = path.dirname(pathWithinFixturesDir);
77
const basename = path.basename(pathWithinFixturesDir);
78
const basenameByDots = basename.split('.');
79
basenameByDots.splice(basenameByDots.length - 1, 0, type);
80
const docBasename = basenameByDots.join('.');
81
const docPathWithinFixturesDir = path.join(dirname, docBasename);
82
return path.join(__dirname, 'fixtures', docPathWithinFixturesDir);
83
}
84
85
export function summarizedDocPathInFixture(pathWithinFixturesDir: string) {
86
return docPathInFixture(pathWithinFixturesDir, 'summarized');
87
}
88
89
export function selectionDocPathInFixture(pathWithinFixturesDir: string) {
90
return docPathInFixture(pathWithinFixturesDir, 'selection');
91
}
92
93
interface ITestFile {
94
contents: string;
95
filePath: string;
96
languageId: WASMLanguage | string;
97
formattingOptions?: vscode.FormattingOptions;
98
}
99
export async function generateSummarizedDocument(
100
filePromise: ITestFile | Promise<ITestFile>,
101
selection: [lineNumber: number, columnNumber: number] | [number, number, number, number] | undefined,
102
charLimit: number = DEFAULT_CHAR_LIMIT,
103
settings: ISummarizedDocumentSettings = {},
104
): Promise<{ text: string; adjustedSelection: OffsetRange }> {
105
const file = await filePromise;
106
const doc = TextDocumentSnapshot.create(createTextDocumentData(
107
URI.from({ scheme: 'test', path: '/path/file.txt' }),
108
file.contents,
109
file.languageId
110
).document);
111
const accessor = createExtensionUnitTestingServices().createTestingAccessor();
112
const parserService = accessor.get(IParserService);
113
const currentDocAST = parserService.getTreeSitterAST(doc);
114
let structure = currentDocAST
115
? await currentDocAST.getStructure()
116
: undefined;
117
if (!structure) {
118
structure = getStructureUsingIndentation(
119
new VsCodeTextDocument(doc),
120
doc.languageId,
121
file.formattingOptions
122
);
123
}
124
const selections = selection ? getAdjustedSelection(structure, new VsCodeTextDocument(doc), toSelection(selection)) : undefined;
125
const summarizedDoc = summarizeDocumentSync(
126
charLimit,
127
doc,
128
selection ? toSelection(selection) : undefined,
129
structure,
130
settings,
131
) as IProjectedDocumentDebugInfo;
132
133
const playgroundRunnerData = (globalThis as any as IPlaygroundRunnerGlobals).$$playgroundRunner_data;
134
if (playgroundRunnerData) {
135
function getDoc(text: string) {
136
const file = { contents: text, languageId: doc.languageId };
137
const data = createTextDocumentData(
138
URI.from({ scheme: 'test', path: '/path/file.ts' }),
139
file.contents,
140
file.languageId,
141
);
142
return data;
143
}
144
145
globalThis.playground = new SummarizeDocumentPlayground(
146
summarizedDoc,
147
selection ? toSelection(selection) : new Range(0, 0, 0, 0),
148
charLimit,
149
(text) => parserService.getTreeSitterAST(getDoc(text).document)!.getStructure(),
150
(text, charLimit, selection, structure) => summarizeDocumentSync(
151
charLimit,
152
TextDocumentSnapshot.create(getDoc(text).document),
153
selection,
154
structure,
155
settings,
156
) as IProjectedDocumentDebugInfo
157
);
158
159
globalThis.summarizedDoc = summarizedDoc;
160
const g = globalThis as any;
161
g.$$debugValueEditor_properties = [
162
{
163
label: `Active Test: "${playgroundRunnerData.currentPath.join(' > ')}"`,
164
},
165
{
166
label: 'Summarized Document',
167
expression: getExprText(() => globalThis.playground!.getSummarizedText()),
168
},
169
{
170
label: `Document Syntax Tree`,
171
expression: getExprText(() => globalThis.playground!.getAst()),
172
},
173
{
174
label: 'Input Document + Selection',
175
expression: getExprText(() => globalThis.playground!.inputDocument),
176
},
177
{
178
label: 'Input Options',
179
expression: getExprText(() => globalThis.playground!.inputOptions),
180
},
181
];
182
g.$$debugValueEditor_refresh?.('{}');
183
}
184
185
return {
186
text: summarizedDoc.text,
187
adjustedSelection: selections ? summarizedDoc.projectOffsetRange(selections.adjusted) : new OffsetRange(0, 0),
188
};
189
}
190
export async function generateSummarizedDocuments(
191
input: {
192
filePromise: ITestFile | Promise<ITestFile>;
193
selection: [number, number] | [number, number, number, number] | undefined;
194
}[],
195
charLimit: number = DEFAULT_CHAR_LIMIT,
196
settings: ISummarizedDocumentSettings = {},
197
) {
198
199
const items: IDocumentSummarizationItem[] = [];
200
201
for (const { filePromise, selection } of input) {
202
203
204
const file = await filePromise;
205
const doc = TextDocumentSnapshot.create(createTextDocumentData(
206
URI.from({ scheme: 'test', path: file.filePath }),
207
file.contents,
208
file.languageId
209
).document);
210
const accessor = createExtensionUnitTestingServices().createTestingAccessor();
211
const parserService = accessor.get(IParserService);
212
const currentDocAST = parserService.getTreeSitterAST(doc);
213
let structure = currentDocAST
214
? await currentDocAST.getStructure()
215
: undefined;
216
if (!structure) {
217
structure = getStructureUsingIndentation(
218
new VsCodeTextDocument(doc),
219
doc.languageId,
220
file.formattingOptions
221
);
222
}
223
// const selections = selection ? getAdjustedSelection(structure, doc, toSelection(selection)) : undefined;
224
225
items.push({
226
document: doc,
227
overlayNodeRoot: structure,
228
selection: selection && toSelection(selection)
229
});
230
231
}
232
233
return summarizeDocumentsSync(
234
charLimit,
235
settings,
236
items
237
);
238
}
239
240
export function getExprText(arrowFn: () => any): string {
241
const src = arrowFn.toString();
242
const parts = src.split('=>');
243
const expr = parts[1];
244
return expr.trim();
245
}
246
247
declare namespace globalThis {
248
export let playground: SummarizeDocumentPlayground | undefined;
249
export let summarizedDoc: IProjectedDocumentDebugInfo | undefined;
250
}
251
export async function generateSummarizedDocumentAndExtractGoodSelection(
252
filePromise: ITestFile | Promise<ITestFile>,
253
selection: [number, number] | [number, number, number, number],
254
charLimit: number = DEFAULT_CHAR_LIMIT
255
): Promise<[string | undefined, string | undefined]> {
256
const result = await generateSummarizedDocument(filePromise, selection, charLimit);
257
if (!result) {
258
return [undefined, undefined];
259
}
260
const adjustedSelection = result.adjustedSelection;
261
const codeAbove = result.text.substring(
262
0,
263
adjustedSelection.start
264
);
265
const adjustedSelectedCode = result.text.substring(
266
adjustedSelection.start,
267
adjustedSelection.endExclusive
268
);
269
const codeBelow = result.text.substring(
270
adjustedSelection.endExclusive
271
);
272
273
return [`${codeAbove}__SELECTION_HERE__${codeBelow}`, adjustedSelectedCode];
274
}
275
function toSelection(
276
selection: [number, number] | [number, number, number, number]
277
): vscode.Selection {
278
if (selection.length === 2) {
279
return new Selection(
280
selection[0],
281
selection[1],
282
selection[0],
283
selection[1]
284
);
285
} else {
286
return new Selection(
287
selection[0],
288
selection[1],
289
selection[2],
290
selection[3]
291
);
292
}
293
}
294
295