Path: blob/main/extensions/copilot/src/platform/editSurvivalTracking/common/editCollector.ts
13401 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 { CharCode } from '../../../util/vs/base/common/charCode';6import { StringEdit, StringReplacement } from '../../../util/vs/editor/common/core/edits/stringEdit';7import { OffsetRange } from '../../../util/vs/editor/common/core/ranges/offsetRange';8import * as vscodeTypes from '../../../vscodeTypes';9import { IDiffService } from '../../diff/common/diffService';10import { stringEditFromDiff } from '../../editing/common/edit';11import { OffsetLineColumnConverter } from '../../editing/common/offsetLineColumnConverter';1213export interface IEditCollector {14initialText: string;15addEdits(edits: vscodeTypes.TextEdit[]): void;16getText(): string;17getEdits(): Promise<StringEdit>;18}1920export class EditCollector implements IEditCollector {21private readonly _document: OffsetBasedTextDocument;2223constructor(24public readonly initialText: string,25@IDiffService private readonly _diffService: IDiffService,26) {27this._document = new OffsetBasedTextDocument(initialText);28}2930public addEdits(edits: vscodeTypes.TextEdit[]): void {31this._document.applyTextEdits(edits);32}3334public getText(): string {35return this._document.getValue();36}3738public async getEdits(): Promise<StringEdit> {39const newText = this.getText();40const edits = await stringEditFromDiff(this.initialText, newText, this._diffService);41return edits;42}43}4445export class OffsetBasedTextDocument {46private _converter: OffsetLineColumnConverter | undefined = undefined;47private _value: string = '';48constructor(initialValue: string = '') {49this._value = initialValue;50}5152getValue(): string {53return this._value;54}5556applyTextEdits(edits: vscodeTypes.TextEdit[]) {57const offsetEdit = new StringEdit(edits.map(e => {58const start = this.positionToOffset(e.range.start);59const end = this.positionToOffset(e.range.end);60return new StringReplacement(new OffsetRange(start, end), e.newText);61}));62this.applyOffsetEdit(offsetEdit);63}6465applyOffsetEdit(edit: StringEdit): void {66this._value = edit.apply(this._value);67this._converter = undefined;68}6970positionToOffset(position: vscodeTypes.Position): number {71if (!this._converter) {72this._converter = new OffsetLineColumnConverter(this._value);73}74const line = position.line;75if (line < 0) {76return 0;77} else if (line >= this._converter.lines) {78return this._value.length;79}80const character = position.character;81const lineOffet = this._converter.lineOffset(line + 1);82if (character <= 0) {83return lineOffet;84}85let endLineOffest;86if (line + 1 < this._converter.lines) {87endLineOffest = this._converter.lineOffset(line + 2);88if (endLineOffest > lineOffet) {89const ch = this._value.charCodeAt(endLineOffest - 1);90if (ch === CharCode.CarriageReturn || ch === CharCode.LineFeed) {91endLineOffest--;92}93if (ch === CharCode.LineFeed && endLineOffest > lineOffet && this._value.charCodeAt(endLineOffest - 1) === CharCode.CarriageReturn) {94endLineOffest--;95}96} else {97endLineOffest = lineOffet;98}99} else {100endLineOffest = this._value.length;101}102if (character > endLineOffest - lineOffet) {103return endLineOffest;104}105return lineOffet + character;106}107}108109110