Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/inlineChat/browser/utils.ts
3296 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 { EditOperation } from '../../../../editor/common/core/editOperation.js';
7
import { IRange } from '../../../../editor/common/core/range.js';
8
import { IIdentifiedSingleEditOperation, ITextModel, IValidEditOperation, TrackedRangeStickiness } from '../../../../editor/common/model.js';
9
import { IEditObserver } from './inlineChatStrategies.js';
10
import { IProgress } from '../../../../platform/progress/common/progress.js';
11
import { IntervalTimer, AsyncIterableSource } from '../../../../base/common/async.js';
12
import { CancellationToken } from '../../../../base/common/cancellation.js';
13
import { getNWords } from '../../chat/common/chatWordCounter.js';
14
import { TextModelEditSource } from '../../../../editor/common/textModelEditSource.js';
15
16
17
18
// --- async edit
19
20
export interface AsyncTextEdit {
21
readonly range: IRange;
22
readonly newText: AsyncIterable<string>;
23
}
24
25
export async function performAsyncTextEdit(model: ITextModel, edit: AsyncTextEdit, progress?: IProgress<IValidEditOperation[]>, obs?: IEditObserver, editSource?: TextModelEditSource) {
26
27
const [id] = model.deltaDecorations([], [{
28
range: edit.range,
29
options: {
30
description: 'asyncTextEdit',
31
stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges
32
}
33
}]);
34
35
let first = true;
36
for await (const part of edit.newText) {
37
38
if (model.isDisposed()) {
39
break;
40
}
41
42
const range = model.getDecorationRange(id);
43
if (!range) {
44
throw new Error('FAILED to perform async replace edit because the anchor decoration was removed');
45
}
46
47
const edit = first
48
? EditOperation.replace(range, part) // first edit needs to override the "anchor"
49
: EditOperation.insert(range.getEndPosition(), part);
50
obs?.start();
51
52
model.pushEditOperations(null, [edit], (undoEdits) => {
53
progress?.report(undoEdits);
54
return null;
55
}, undefined, editSource);
56
57
obs?.stop();
58
first = false;
59
}
60
}
61
62
export function asProgressiveEdit(interval: IntervalTimer, edit: IIdentifiedSingleEditOperation, wordsPerSec: number, token: CancellationToken): AsyncTextEdit {
63
64
wordsPerSec = Math.max(30, wordsPerSec);
65
66
const stream = new AsyncIterableSource<string>();
67
let newText = edit.text ?? '';
68
69
interval.cancelAndSet(() => {
70
if (token.isCancellationRequested) {
71
return;
72
}
73
const r = getNWords(newText, 1);
74
stream.emitOne(r.value);
75
newText = newText.substring(r.value.length);
76
if (r.isFullString) {
77
interval.cancel();
78
stream.resolve();
79
d.dispose();
80
}
81
82
}, 1000 / wordsPerSec);
83
84
// cancel ASAP
85
const d = token.onCancellationRequested(() => {
86
interval.cancel();
87
stream.resolve();
88
d.dispose();
89
});
90
91
return {
92
range: edit.range,
93
newText: stream.asyncIterable
94
};
95
}
96
97