1
>
/*---------------------------------------------------------------------------------------------
chatEditingService.ts
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 { decodeHex, encodeHex, VSBuffer } from '../../../../../base/common/buffer.js';
7
>
import { CancellationToken } from '../../../../../base/common/cancellation.js';
8
>
import { CancellationError } from '../../../../../base/common/errors.js';
9
>
import { Event } from '../../../../../base/common/event.js';
10
>
import { IDisposable } from '../../../../../base/common/lifecycle.js';
11
>
import { autorunSelfDisposable, IObservable, IReader } from '../../../../../base/common/observable.js';
12
>
import { hasKey } from '../../../../../base/common/types.js';
13
>
import { URI } from '../../../../../base/common/uri.js';
14
>
import { IDocumentDiff } from '../../../../../editor/common/diff/documentDiffProvider.js';
15
>
import { TextEdit } from '../../../../../editor/common/languages.js';
16
>
import { ITextModel } from '../../../../../editor/common/model.js';
17
>
import { EditSuggestionId } from '../../../../../editor/common/textModelEditSource.js';
18
>
import { localize } from '../../../../../nls.js';
19
>
import { RawContextKey } from '../../../../../platform/contextkey/common/contextkey.js';
20
>
import { createDecorator } from '../../../../../platform/instantiation/common/instantiation.js';
21
>
import { IEditorPane } from '../../../../common/editor.js';
22
>
import { ICellEditOperation } from '../../../notebook/common/notebookCommon.js';
23
>
import { IChatMultiDiffData, IChatMultiDiffDataSerialized, IChatProgress, IChatWorkspaceEdit } from '../chatService/chatService.js';
24
>
import { ChatModel, IChatRequestDisablement, IChatResponseModel } from '../model/chatModel.js';
25
>
import { IChatAgentResult } from '../participants/chatAgents.js';
26
>
27
>
export const IChatEditingService = createDecorator<IChatEditingService>('chatEditingService');
28
>
29
>
export interface IChatEditingSessionProvider {
30
>
createEditingSession(chatSessionResource: URI): IChatEditingSession;
31
>
}
32
>
33
>
export interface IChatEditingService {
34
>
35
>
_serviceBrand: undefined;
36
>
37
>
startOrContinueGlobalEditingSession(chatModel: ChatModel): IChatEditingSession;
38
>
39
>
getEditingSession(chatSessionResource: URI): IChatEditingSession | undefined;
40
>
41
>
/**
42
>
* All editing sessions, sorted by recency, e.g the last created session comes first.
43
>
*/
44
>
readonly editingSessionsObs: IObservable<readonly IChatEditingSession[]>;
45
>
46
>
/**
47
>
* Creates a new short lived editing session
48
>
*/
49
>
createEditingSession(chatModel: ChatModel): IChatEditingSession;
50
>
51
>
/**
52
>
* Creates an editing session with state transferred from the provided session.
53
>
*/
54
>
transferEditingSession(chatModel: ChatModel, session: IChatEditingSession): IChatEditingSession;
55
>
56
>
/**
57
>
* Registers a provider that creates editing sessions for chat sessions
58
>
* with the given URI scheme. When {@link createEditingSession} is called
59
>
* for a chat model whose sessionResource matches the scheme, the provider
60
>
* is used instead of the default implementation.
61
>
*/
62
>
registerEditingSessionProvider(scheme: string, provider: IChatEditingSessionProvider): IDisposable;
63
>
}
64
>
65
>
export interface WorkingSetDisplayMetadata {
66
>
state: ModifiedFileEntryState;
67
>
description?: string;
68
>
}
69
>
70
>
export interface IStreamingEdits {
71
>
pushText(edits: TextEdit[], isLastEdits: boolean): void;
72
>
pushNotebookCellText(cell: URI, edits: TextEdit[], isLastEdits: boolean): void;
73
>
pushNotebook(edits: ICellEditOperation[], isLastEdits: boolean): void;
74
>
/** Marks edits as done, idempotent */
75
>
complete(): void;
76
>
}
77
>
78
>
export interface IModifiedEntryTelemetryInfo {
79
>
readonly agentId: string | undefined;
80
>
readonly command: string | undefined;
81
>
readonly sessionResource: URI;
82
>
readonly requestId: string;
83
>
readonly result: IChatAgentResult | undefined;
84
>
readonly modelId: string | undefined;
85
>
readonly modeId: 'ask' | 'edit' | 'agent' | 'custom' | 'applyCodeBlock' | undefined;
86
>
readonly applyCodeBlockSuggestionId: EditSuggestionId | undefined;
87
>
readonly feature: 'sideBarChat' | 'inlineChat' | undefined;
88
>
}
89
>
90
>
export interface ISnapshotEntry {
91
>
readonly resource: URI;
92
>
readonly languageId: string;
93
>
readonly snapshotUri: URI;
94
>
readonly original: string;
95
>
readonly current: string;
96
>
readonly state: ModifiedFileEntryState;
97
>
telemetryInfo: IModifiedEntryTelemetryInfo;
98
>
/** True if this entry represents a deleted file */
99
>
readonly isDeleted?: boolean;
100
>
}
101
>
102
>
export interface IChatEditingSession extends IDisposable {
103
>
readonly isGlobalEditingSession: boolean;
104
>
readonly supportsKeepUndo: boolean;
105
>
readonly chatSessionResource: URI;
106
>
readonly onDidDispose: Event<void>;
107
>
readonly state: IObservable<ChatEditingSessionState>;
108
>
readonly entries: IObservable<readonly IModifiedFileEntry[]>;
109
>
/** Requests disabled by undo/redo in the session */
110
>
readonly requestDisablement: IObservable<IChatRequestDisablement[]>;
111
>
112
>
show(previousChanges?: boolean): Promise<void>;
113
>
accept(...uris: URI[]): Promise<void>;
114
>
reject(...uris: URI[]): Promise<void>;
115
>
getEntry(uri: URI): IModifiedFileEntry | undefined;
116
>
readEntry(uri: URI, reader: IReader): IModifiedFileEntry | undefined;
117
>
118
>
restoreSnapshot(requestId: string, stopId: string | undefined): Promise<void>;
119
>
120
>
/**
121
>
* Marks all edits to the given resources as agent edits until
122
>
* {@link stopExternalEdits} is called with the same ID. This is used for
123
>
* agents that make changes on-disk rather than streaming edits through the
124
>
* chat session.
125
>
*/
126
>
startExternalEdits(responseModel: IChatResponseModel, operationId: number, resources: URI[], undoStopId: string, contentFor?: URI[]): Promise<IChatProgress[]>;
127
>
stopExternalEdits(responseModel: IChatResponseModel, operationId: number, contentFor?: URI[]): Promise<IChatProgress[]>;
128
>
129
>
/**
130
>
* Gets the snapshot URI of a file at the request and _after_ changes made in the undo stop.
131
>
* @param uri File in the workspace
132
>
*/
133
>
getSnapshotUri(requestId: string, uri: URI, stopId: string | undefined): URI | undefined;
134
>
135
>
getSnapshotContents(requestId: string, uri: URI, stopId: string | undefined): Promise<VSBuffer | undefined>;
136
>
getSnapshotModel(requestId: string, undoStop: string | undefined, snapshotUri: URI): Promise<ITextModel | null>;
137
>
138
>
/**
139
>
* Will lead to this object getting disposed
140
>
*/
141
>
stop(clearState?: boolean): Promise<void>;
142
>
143
>
/**
144
>
* Starts making edits to the resource.
145
>
* @param resource URI that's being edited
146
>
* @param responseModel The response model making the edits
147
>
* @param inUndoStop The undo stop the edits will be grouped in
148
>
*/
149
>
startStreamingEdits(resource: URI, responseModel: IChatResponseModel, inUndoStop: string | undefined): IStreamingEdits;
150
>
151
>
/**
152
>
* Applies a workspace edit (file deletions, creations, renames).
153
>
* @param edit The workspace edit containing file operations
154
>
* @param responseModel The response model making the edit
155
>
* @param undoStopId The undo stop ID for this edit
156
>
*/
157
>
applyWorkspaceEdit(edit: IChatWorkspaceEdit, responseModel: IChatResponseModel, undoStopId: string): void;
158
>
159
>
/**
160
>
* Gets the document diff of a change made to a URI between one undo stop and
161
>
* the next one.
162
>
* @returns The observable or undefined if there is no diff between the stops.
163
>
*/
164
>
getEntryDiffBetweenStops(uri: URI, requestId: string | undefined, stopId: string | undefined): IObservable<IEditSessionEntryDiff | undefined> | undefined;
165
>
166
>
/**
167
>
* Gets the document diff of a change made to a URI between one request to another one.
168
>
* @returns The observable or undefined if there is no diff between the requests.
169
>
*/
170
>
getEntryDiffBetweenRequests(uri: URI, startRequestIs: string, stopRequestId: string): IObservable<IEditSessionEntryDiff | undefined>;
171
>
172
>
/**
173
>
* Gets the diff of each file modified in this session, comparing the initial
174
>
* baseline to the current state.
175
>
*/
176
>
getDiffsForFilesInSession(): IObservable<readonly IEditSessionEntryDiff[]>;
177
>
178
>
/**
179
>
* Gets the diff of each file modified in the request.
180
>
*/
181
>
getDiffsForFilesInRequest(requestId: string): IObservable<readonly IEditSessionEntryDiff[]>;
182
>
183
>
/**
184
>
* Whether there are any edits made in the given request.
185
>
*/
186
>
hasEditsInRequest(requestId: string, reader?: IReader): boolean;
187
>
188
>
/**
189
>
* Gets the aggregated diff stats for all files modified in this session.
190
>
*/
191
>
getDiffForSession(): IObservable<IEditSessionDiffStats>;
192
>
193
>
readonly canUndo: IObservable<boolean>;
194
>
readonly canRedo: IObservable<boolean>;
195
>
undoInteraction(): Promise<void>;
196
>
redoInteraction(): Promise<void>;
197
>
198
>
/**
199
>
* Triggers generation of explanations for all modified files in the session.
200
>
*/
201
>
triggerExplanationGeneration(): Promise<void>;
202
>
203
>
/**
204
>
* Clears any active explanation generation.
205
>
*/
206
>
clearExplanations(): void;
207
>
208
>
/**
209
>
* Whether explanations are currently being generated or displayed.
210
>
*/
211
>
hasExplanations(): boolean;
212
>
}
213
>
214
>
export function chatEditingSessionIsReady(session: IChatEditingSession): Promise<void> {
215
return new Promise<void>(resolve => {
216
autorunSelfDisposable(reader => {