Path: blob/main/src/vs/workbench/contrib/inlineChat/browser/utils.ts
3296 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 { EditOperation } from '../../../../editor/common/core/editOperation.js';6import { IRange } from '../../../../editor/common/core/range.js';7import { IIdentifiedSingleEditOperation, ITextModel, IValidEditOperation, TrackedRangeStickiness } from '../../../../editor/common/model.js';8import { IEditObserver } from './inlineChatStrategies.js';9import { IProgress } from '../../../../platform/progress/common/progress.js';10import { IntervalTimer, AsyncIterableSource } from '../../../../base/common/async.js';11import { CancellationToken } from '../../../../base/common/cancellation.js';12import { getNWords } from '../../chat/common/chatWordCounter.js';13import { TextModelEditSource } from '../../../../editor/common/textModelEditSource.js';14151617// --- async edit1819export interface AsyncTextEdit {20readonly range: IRange;21readonly newText: AsyncIterable<string>;22}2324export async function performAsyncTextEdit(model: ITextModel, edit: AsyncTextEdit, progress?: IProgress<IValidEditOperation[]>, obs?: IEditObserver, editSource?: TextModelEditSource) {2526const [id] = model.deltaDecorations([], [{27range: edit.range,28options: {29description: 'asyncTextEdit',30stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges31}32}]);3334let first = true;35for await (const part of edit.newText) {3637if (model.isDisposed()) {38break;39}4041const range = model.getDecorationRange(id);42if (!range) {43throw new Error('FAILED to perform async replace edit because the anchor decoration was removed');44}4546const edit = first47? EditOperation.replace(range, part) // first edit needs to override the "anchor"48: EditOperation.insert(range.getEndPosition(), part);49obs?.start();5051model.pushEditOperations(null, [edit], (undoEdits) => {52progress?.report(undoEdits);53return null;54}, undefined, editSource);5556obs?.stop();57first = false;58}59}6061export function asProgressiveEdit(interval: IntervalTimer, edit: IIdentifiedSingleEditOperation, wordsPerSec: number, token: CancellationToken): AsyncTextEdit {6263wordsPerSec = Math.max(30, wordsPerSec);6465const stream = new AsyncIterableSource<string>();66let newText = edit.text ?? '';6768interval.cancelAndSet(() => {69if (token.isCancellationRequested) {70return;71}72const r = getNWords(newText, 1);73stream.emitOne(r.value);74newText = newText.substring(r.value.length);75if (r.isFullString) {76interval.cancel();77stream.resolve();78d.dispose();79}8081}, 1000 / wordsPerSec);8283// cancel ASAP84const d = token.onCancellationRequested(() => {85interval.cancel();86stream.resolve();87d.dispose();88});8990return {91range: edit.range,92newText: stream.asyncIterable93};94}959697