Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/browser/actions/reviewEdits.ts
13406 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 { raceCancellation } from '../../../../../base/common/async.js';
7
import { CancellationToken } from '../../../../../base/common/cancellation.js';
8
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
9
import { derived, waitForState } from '../../../../../base/common/observable.js';
10
import { assertType } from '../../../../../base/common/types.js';
11
import { URI } from '../../../../../base/common/uri.js';
12
import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js';
13
import { TextEdit } from '../../../../../editor/common/languages.js';
14
import { EditSuggestionId } from '../../../../../editor/common/textModelEditSource.js';
15
import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js';
16
import { IChatService } from '../../common/chatService/chatService.js';
17
import { ChatAgentLocation } from '../../common/constants.js';
18
import { ModifiedFileEntryState } from '../../common/editing/chatEditingService.js';
19
import { ChatModel } from '../../common/model/chatModel.js';
20
import { ICellEditOperation } from '../../../notebook/common/notebookCommon.js';
21
import { INotebookService } from '../../../notebook/common/notebookService.js';
22
23
24
export async function reviewEdits(accessor: ServicesAccessor, editor: ICodeEditor, stream: AsyncIterable<TextEdit[]>, token: CancellationToken, applyCodeBlockSuggestionId: EditSuggestionId | undefined): Promise<boolean> {
25
if (!editor.hasModel()) {
26
return false;
27
}
28
29
const chatService = accessor.get(IChatService);
30
const uri = editor.getModel().uri;
31
const chatModelRef = chatService.startNewLocalSession(ChatAgentLocation.EditorInline);
32
const chatModel = chatModelRef.object as ChatModel;
33
34
chatModel.startEditingSession(true);
35
36
const store = new DisposableStore();
37
store.add(chatModelRef);
38
39
// STREAM
40
const chatRequest = chatModel?.addRequest({ text: '', parts: [] }, { variables: [] }, 0, {
41
kind: undefined,
42
modeId: 'applyCodeBlock',
43
modeInstructions: undefined,
44
isBuiltin: true,
45
applyCodeBlockSuggestionId,
46
});
47
assertType(chatRequest.response);
48
chatRequest.response.updateContent({ kind: 'textEdit', uri, edits: [], done: false });
49
for await (const chunk of stream) {
50
51
if (token.isCancellationRequested) {
52
chatRequest.response.cancel();
53
break;
54
}
55
56
chatRequest.response.updateContent({ kind: 'textEdit', uri, edits: chunk, done: false });
57
}
58
chatRequest.response.updateContent({ kind: 'textEdit', uri, edits: [], done: true });
59
60
if (!token.isCancellationRequested) {
61
chatRequest.response.complete();
62
}
63
64
const isSettled = derived(r => {
65
const entry = chatModel.editingSession?.readEntry(uri, r);
66
if (!entry) {
67
return false;
68
}
69
const state = entry.state.read(r);
70
return state === ModifiedFileEntryState.Accepted || state === ModifiedFileEntryState.Rejected;
71
});
72
const whenDecided = waitForState(isSettled, Boolean);
73
await raceCancellation(whenDecided, token);
74
store.dispose();
75
return true;
76
}
77
78
export async function reviewNotebookEdits(accessor: ServicesAccessor, uri: URI, stream: AsyncIterable<[URI, TextEdit[]] | ICellEditOperation[]>, token: CancellationToken): Promise<boolean> {
79
80
const chatService = accessor.get(IChatService);
81
const notebookService = accessor.get(INotebookService);
82
const isNotebook = notebookService.hasSupportedNotebooks(uri);
83
const chatModelRef = chatService.startNewLocalSession(ChatAgentLocation.EditorInline);
84
const chatModel = chatModelRef.object as ChatModel;
85
86
chatModel.startEditingSession(true);
87
88
const store = new DisposableStore();
89
store.add(chatModelRef);
90
91
// STREAM
92
const chatRequest = chatModel?.addRequest({ text: '', parts: [] }, { variables: [] }, 0);
93
assertType(chatRequest.response);
94
if (isNotebook) {
95
chatRequest.response.updateContent({ kind: 'notebookEdit', uri, edits: [], done: false });
96
} else {
97
chatRequest.response.updateContent({ kind: 'textEdit', uri, edits: [], done: false });
98
}
99
for await (const chunk of stream) {
100
101
if (token.isCancellationRequested) {
102
chatRequest.response.cancel();
103
break;
104
}
105
if (chunk.every(isCellEditOperation)) {
106
chatRequest.response.updateContent({ kind: 'notebookEdit', uri, edits: chunk, done: false });
107
} else {
108
chatRequest.response.updateContent({ kind: 'textEdit', uri: chunk[0], edits: chunk[1], done: false });
109
}
110
}
111
if (isNotebook) {
112
chatRequest.response.updateContent({ kind: 'notebookEdit', uri, edits: [], done: true });
113
} else {
114
chatRequest.response.updateContent({ kind: 'textEdit', uri, edits: [], done: true });
115
}
116
117
if (!token.isCancellationRequested) {
118
chatRequest.response.complete();
119
}
120
121
const isSettled = derived(r => {
122
const entry = chatModel.editingSession?.readEntry(uri, r);
123
if (!entry) {
124
return false;
125
}
126
const state = entry.state.read(r);
127
return state === ModifiedFileEntryState.Accepted || state === ModifiedFileEntryState.Rejected;
128
});
129
130
const whenDecided = waitForState(isSettled, Boolean);
131
132
await raceCancellation(whenDecided, token);
133
134
store.dispose();
135
136
return true;
137
}
138
function isCellEditOperation(edit: URI | TextEdit[] | ICellEditOperation): edit is ICellEditOperation {
139
if (URI.isUri(edit)) {
140
return false;
141
}
142
if (Array.isArray(edit)) {
143
return false;
144
}
145
return true;
146
}
147
148