Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/browser/chatEditor.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 * as dom from '../../../../base/browser/dom.js';
7
import { raceCancellationError } from '../../../../base/common/async.js';
8
import { CancellationToken } from '../../../../base/common/cancellation.js';
9
import { IContextKeyService, IScopedContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
10
import { IEditorOptions } from '../../../../platform/editor/common/editor.js';
11
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
12
import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js';
13
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
14
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
15
import { editorBackground, editorForeground, inputBackground } from '../../../../platform/theme/common/colorRegistry.js';
16
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
17
import { EditorPane } from '../../../browser/parts/editor/editorPane.js';
18
import { IEditorOpenContext } from '../../../common/editor.js';
19
import { Memento } from '../../../common/memento.js';
20
import { EDITOR_DRAG_AND_DROP_BACKGROUND } from '../../../common/theme.js';
21
import { IEditorGroup } from '../../../services/editor/common/editorGroupsService.js';
22
import { ChatContextKeys } from '../common/chatContextKeys.js';
23
import { IChatModel, IExportableChatData, ISerializableChatData } from '../common/chatModel.js';
24
import { CHAT_PROVIDER_ID } from '../common/chatParticipantContribTypes.js';
25
import { IChatSessionsService } from '../common/chatSessionsService.js';
26
import { ChatAgentLocation, ChatModeKind } from '../common/constants.js';
27
import { clearChatEditor } from './actions/chatClear.js';
28
import { ChatEditorInput } from './chatEditorInput.js';
29
import { getChatSessionType } from './chatSessions/common.js';
30
import { ChatWidget, IChatViewState } from './chatWidget.js';
31
32
export interface IChatEditorOptions extends IEditorOptions {
33
target?: { sessionId: string } | { data: IExportableChatData | ISerializableChatData };
34
preferredTitle?: string;
35
ignoreInView?: boolean;
36
}
37
38
export class ChatEditor extends EditorPane {
39
private _widget!: ChatWidget;
40
public get widget(): ChatWidget {
41
return this._widget;
42
}
43
private _scopedContextKeyService!: IScopedContextKeyService;
44
override get scopedContextKeyService() {
45
return this._scopedContextKeyService;
46
}
47
48
private _memento: Memento | undefined;
49
private _viewState: IChatViewState | undefined;
50
private dimension = new dom.Dimension(0, 0);
51
52
constructor(
53
group: IEditorGroup,
54
@ITelemetryService telemetryService: ITelemetryService,
55
@IThemeService themeService: IThemeService,
56
@IInstantiationService private readonly instantiationService: IInstantiationService,
57
@IStorageService private readonly storageService: IStorageService,
58
@IChatSessionsService private readonly chatSessionsService: IChatSessionsService,
59
@IContextKeyService private readonly contextKeyService: IContextKeyService,
60
) {
61
super(ChatEditorInput.EditorID, group, telemetryService, themeService, storageService);
62
}
63
64
private async clear() {
65
if (this.input) {
66
return this.instantiationService.invokeFunction(clearChatEditor, this.input as ChatEditorInput);
67
}
68
}
69
70
protected override createEditor(parent: HTMLElement): void {
71
this._scopedContextKeyService = this._register(this.contextKeyService.createScoped(parent));
72
const scopedInstantiationService = this._register(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService])));
73
ChatContextKeys.inChatEditor.bindTo(this._scopedContextKeyService).set(true);
74
75
this._widget = this._register(
76
scopedInstantiationService.createInstance(
77
ChatWidget,
78
ChatAgentLocation.Panel,
79
undefined,
80
{
81
autoScroll: mode => mode !== ChatModeKind.Ask,
82
renderFollowups: true,
83
supportsFileReferences: true,
84
rendererOptions: {
85
renderTextEditsAsSummary: (uri) => {
86
return true;
87
},
88
referencesExpandedWhenEmptyResponse: false,
89
progressMessageAtBottomOfResponse: mode => mode !== ChatModeKind.Ask,
90
},
91
enableImplicitContext: true,
92
enableWorkingSet: 'explicit',
93
supportsChangingModes: true,
94
},
95
{
96
listForeground: editorForeground,
97
listBackground: editorBackground,
98
overlayBackground: EDITOR_DRAG_AND_DROP_BACKGROUND,
99
inputEditorBackground: inputBackground,
100
resultEditorBackground: editorBackground
101
}));
102
this._register(this.widget.onDidClear(() => this.clear()));
103
this.widget.render(parent);
104
this.widget.setVisible(true);
105
}
106
107
protected override setEditorVisible(visible: boolean): void {
108
super.setEditorVisible(visible);
109
110
this.widget?.setVisible(visible);
111
112
if (visible && this.widget) {
113
this.widget.layout(this.dimension.height, this.dimension.width);
114
}
115
}
116
117
public override focus(): void {
118
super.focus();
119
120
this.widget?.focusInput();
121
}
122
123
override clearInput(): void {
124
this.saveState();
125
super.clearInput();
126
}
127
128
override async setInput(input: ChatEditorInput, options: IChatEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise<void> {
129
await super.setInput(input, options, context, token);
130
if (token.isCancellationRequested) {
131
return;
132
}
133
134
if (!this.widget) {
135
throw new Error('ChatEditor lifecycle issue: no editor widget');
136
}
137
138
let isContributedChatSession = false;
139
const chatSessionType = getChatSessionType(input);
140
if (chatSessionType !== 'local') {
141
await raceCancellationError(this.chatSessionsService.canResolveContentProvider(chatSessionType), token);
142
const contributions = this.chatSessionsService.getAllChatSessionContributions();
143
const contribution = contributions.find(c => c.type === chatSessionType);
144
if (contribution) {
145
this.widget.lockToCodingAgent(contribution.name, contribution.displayName, contribution.type);
146
isContributedChatSession = true;
147
} else {
148
this.widget.unlockFromCodingAgent();
149
}
150
} else {
151
this.widget.unlockFromCodingAgent();
152
}
153
154
const editorModel = await raceCancellationError(input.resolve(), token);
155
156
if (!editorModel) {
157
throw new Error(`Failed to get model for chat editor. id: ${input.sessionId}`);
158
}
159
const viewState = options?.viewState ?? input.options.viewState;
160
this.updateModel(editorModel.model, viewState);
161
162
if (isContributedChatSession && options?.preferredTitle) {
163
editorModel.model.setCustomTitle(options?.preferredTitle);
164
}
165
}
166
167
private updateModel(model: IChatModel, viewState?: IChatViewState): void {
168
this._memento = new Memento('interactive-session-editor-' + CHAT_PROVIDER_ID, this.storageService);
169
this._viewState = viewState ?? this._memento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE) as IChatViewState;
170
this.widget.setModel(model, { ...this._viewState });
171
}
172
173
protected override saveState(): void {
174
this.widget?.saveState();
175
176
if (this._memento && this._viewState) {
177
const widgetViewState = this.widget.getViewState();
178
179
// Need to set props individually on the memento
180
this._viewState.inputValue = widgetViewState.inputValue;
181
this._viewState.inputState = widgetViewState.inputState;
182
this._memento.saveMemento();
183
}
184
}
185
186
override getViewState(): object | undefined {
187
return { ...this._viewState };
188
}
189
190
override layout(dimension: dom.Dimension, position?: dom.IDomPosition | undefined): void {
191
this.dimension = dimension;
192
if (this.widget) {
193
this.widget.layout(dimension.height, dimension.width);
194
}
195
}
196
}
197
198