Path: blob/main/extensions/copilot/test/pipeline/alternativeAction/processor.ts
13394 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 { IAlternativeAction } from '../../../src/extension/inlineEdits/node/nextEditProviderTelemetry';6import { Edits } from '../../../src/platform/inlineEdits/common/dataTypes/edit';7import { LogEntry } from '../../../src/platform/workspaceRecorder/common/workspaceLog';8import { StringEdit, StringReplacement } from '../../../src/util/vs/editor/common/core/edits/stringEdit';9import { OffsetRange } from '../../../src/util/vs/editor/common/core/ranges/offsetRange';10import { ISerializedEdit } from '../logRecordingTypes';11import { IStringReplacement, NextUserEdit, Recording, Scoring, SuggestedEdit } from './types';12import { binarySearch, log } from './util';1314export namespace Processor {1516export function createScoringForAlternativeAction(17altAction: IAlternativeAction,18proposedEdits: IStringReplacement[],19isAccepted: boolean,20): Scoring.t | undefined {2122const processedRecording = splitRecordingAtRequestTime(altAction);23if (!processedRecording) {24log('Could not split recording at request time');25return undefined;26}2728const { recordingPriorToRequest, recordingAfterRequest } = processedRecording;2930const currentFileId = determineCurrentFileId(recordingPriorToRequest);31if (currentFileId === undefined) {32log('Could not determine current file ID from recording prior to request');33return undefined;34}3536const idToFileMap = documentIndexMapping(recordingPriorToRequest);3738const currentFilePath = idToFileMap.get(currentFileId);3940if (!currentFilePath) {41log('Could not find current file path from ID mapping');42return undefined;43}4445const currentFile = { id: currentFileId, relativePath: currentFilePath };4647const nextUserEdit = getNextUserEdit(currentFile, recordingPriorToRequest, recordingAfterRequest);4849const reconstructedRecording: Recording.t = {50log: recordingPriorToRequest,51nextUserEdit,52};5354const nesEdits = proposedEdits.map((se): SuggestedEdit.t => ({55documentUri: currentFile.relativePath,56edit: [se],57score: isAccepted ? 1 : 0,58scoreCategory: 'nextEdit',59}));6061const scoring = Scoring.create(reconstructedRecording, nesEdits);6263return scoring;64}6566function splitRecordingAtRequestTime(altAction: IAlternativeAction): {67recordingPriorToRequest: LogEntry[];68recordingAfterRequest: LogEntry[];69} | undefined {7071if (!altAction.recording) {72return undefined;73}7475const recording = altAction.recording.entries;76if (!recording || recording.length === 0) {77return undefined;78}7980const requestTime = altAction.recording.requestTime;8182const recordingIdxOfRequestTime = binarySearch(recording, (entry: LogEntry) => {83if (entry.kind === 'meta') {84return -1;85} else {86return entry.time - requestTime;87}88});8990if (recordingIdxOfRequestTime === -1) {91log('Request time is before any recording entries');92return undefined;93}9495const recordingPriorToRequest = recording.slice(0, recordingIdxOfRequestTime + 1);96const recordingAfterRequest = recording.slice(recordingIdxOfRequestTime + 1);9798return {99recordingPriorToRequest,100recordingAfterRequest101};102}103104function documentIndexMapping(recording: LogEntry[]): Map<number, string> {105const map = new Map<number, string>();106for (const entry of recording) {107if (entry.kind === 'documentEncountered') {108map.set(entry.id, entry.relativePath);109}110}111return map;112}113114function determineCurrentFileId(recording: LogEntry[]): number | undefined {115let fileId: number | undefined;116for (let i = recording.length - 1; i >= 0; i--) {117const entry = recording[i];118if ('id' in entry) {119fileId = entry.id;120break;121}122}123return fileId;124}125126function getNextUserEdit(currentFile: { id: number; relativePath: string }, recordingBeforeRequest: LogEntry[], recordingAfterRequest: LogEntry[]): NextUserEdit.t {127128const N_EDITS_LIMIT = 10;129130const serializedEdits: ISerializedEdit[] = [];131for (const entry of recordingAfterRequest) {132if (entry.kind === 'changed' && 'id' in entry && entry.id === currentFile.id) {133serializedEdits.push(entry.edit);134}135if (serializedEdits.length > N_EDITS_LIMIT) {136break;137}138}139140const edits = new Edits(141StringEdit,142serializedEdits.map(se => new StringEdit(se.map(r => new StringReplacement(new OffsetRange(r[0], r[1]), r[2]))))143);144145return {146edit: edits.compose().replacements.map(r => [r.replaceRange.start, r.replaceRange.endExclusive, r.newText] as const),147relativePath: currentFile.relativePath,148originalOpIdx: recordingBeforeRequest.length - 1149};150}151}152153154