Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/test/pipeline/alternativeAction/processor.ts
13394 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 { IAlternativeAction } from '../../../src/extension/inlineEdits/node/nextEditProviderTelemetry';
7
import { Edits } from '../../../src/platform/inlineEdits/common/dataTypes/edit';
8
import { LogEntry } from '../../../src/platform/workspaceRecorder/common/workspaceLog';
9
import { StringEdit, StringReplacement } from '../../../src/util/vs/editor/common/core/edits/stringEdit';
10
import { OffsetRange } from '../../../src/util/vs/editor/common/core/ranges/offsetRange';
11
import { ISerializedEdit } from '../logRecordingTypes';
12
import { IStringReplacement, NextUserEdit, Recording, Scoring, SuggestedEdit } from './types';
13
import { binarySearch, log } from './util';
14
15
export namespace Processor {
16
17
export function createScoringForAlternativeAction(
18
altAction: IAlternativeAction,
19
proposedEdits: IStringReplacement[],
20
isAccepted: boolean,
21
): Scoring.t | undefined {
22
23
const processedRecording = splitRecordingAtRequestTime(altAction);
24
if (!processedRecording) {
25
log('Could not split recording at request time');
26
return undefined;
27
}
28
29
const { recordingPriorToRequest, recordingAfterRequest } = processedRecording;
30
31
const currentFileId = determineCurrentFileId(recordingPriorToRequest);
32
if (currentFileId === undefined) {
33
log('Could not determine current file ID from recording prior to request');
34
return undefined;
35
}
36
37
const idToFileMap = documentIndexMapping(recordingPriorToRequest);
38
39
const currentFilePath = idToFileMap.get(currentFileId);
40
41
if (!currentFilePath) {
42
log('Could not find current file path from ID mapping');
43
return undefined;
44
}
45
46
const currentFile = { id: currentFileId, relativePath: currentFilePath };
47
48
const nextUserEdit = getNextUserEdit(currentFile, recordingPriorToRequest, recordingAfterRequest);
49
50
const reconstructedRecording: Recording.t = {
51
log: recordingPriorToRequest,
52
nextUserEdit,
53
};
54
55
const nesEdits = proposedEdits.map((se): SuggestedEdit.t => ({
56
documentUri: currentFile.relativePath,
57
edit: [se],
58
score: isAccepted ? 1 : 0,
59
scoreCategory: 'nextEdit',
60
}));
61
62
const scoring = Scoring.create(reconstructedRecording, nesEdits);
63
64
return scoring;
65
}
66
67
function splitRecordingAtRequestTime(altAction: IAlternativeAction): {
68
recordingPriorToRequest: LogEntry[];
69
recordingAfterRequest: LogEntry[];
70
} | undefined {
71
72
if (!altAction.recording) {
73
return undefined;
74
}
75
76
const recording = altAction.recording.entries;
77
if (!recording || recording.length === 0) {
78
return undefined;
79
}
80
81
const requestTime = altAction.recording.requestTime;
82
83
const recordingIdxOfRequestTime = binarySearch(recording, (entry: LogEntry) => {
84
if (entry.kind === 'meta') {
85
return -1;
86
} else {
87
return entry.time - requestTime;
88
}
89
});
90
91
if (recordingIdxOfRequestTime === -1) {
92
log('Request time is before any recording entries');
93
return undefined;
94
}
95
96
const recordingPriorToRequest = recording.slice(0, recordingIdxOfRequestTime + 1);
97
const recordingAfterRequest = recording.slice(recordingIdxOfRequestTime + 1);
98
99
return {
100
recordingPriorToRequest,
101
recordingAfterRequest
102
};
103
}
104
105
function documentIndexMapping(recording: LogEntry[]): Map<number, string> {
106
const map = new Map<number, string>();
107
for (const entry of recording) {
108
if (entry.kind === 'documentEncountered') {
109
map.set(entry.id, entry.relativePath);
110
}
111
}
112
return map;
113
}
114
115
function determineCurrentFileId(recording: LogEntry[]): number | undefined {
116
let fileId: number | undefined;
117
for (let i = recording.length - 1; i >= 0; i--) {
118
const entry = recording[i];
119
if ('id' in entry) {
120
fileId = entry.id;
121
break;
122
}
123
}
124
return fileId;
125
}
126
127
function getNextUserEdit(currentFile: { id: number; relativePath: string }, recordingBeforeRequest: LogEntry[], recordingAfterRequest: LogEntry[]): NextUserEdit.t {
128
129
const N_EDITS_LIMIT = 10;
130
131
const serializedEdits: ISerializedEdit[] = [];
132
for (const entry of recordingAfterRequest) {
133
if (entry.kind === 'changed' && 'id' in entry && entry.id === currentFile.id) {
134
serializedEdits.push(entry.edit);
135
}
136
if (serializedEdits.length > N_EDITS_LIMIT) {
137
break;
138
}
139
}
140
141
const edits = new Edits(
142
StringEdit,
143
serializedEdits.map(se => new StringEdit(se.map(r => new StringReplacement(new OffsetRange(r[0], r[1]), r[2]))))
144
);
145
146
return {
147
edit: edits.compose().replacements.map(r => [r.replaceRange.start, r.replaceRange.endExclusive, r.newText] as const),
148
relativePath: currentFile.relativePath,
149
originalOpIdx: recordingBeforeRequest.length - 1
150
};
151
}
152
}
153
154