Skip to content

Commit 3a0ef8d

Browse files
committed
Avoid unnecessary work when putting request into edit mode
We fire onDidChange to trigger a rerender to apply a css class. When onDidChange happens and the response is complete, then we update all the codeblock textmodels (?). Use an observable to avoid all this. #286660
1 parent 343e7c3 commit 3a0ef8d

4 files changed

Lines changed: 30 additions & 24 deletions

File tree

src/vs/workbench/contrib/chat/browser/widget/chatListRenderer.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ import { ChatToolInvocationPart } from './chatContentParts/toolInvocationParts/c
9393
import { ChatMarkdownDecorationsRenderer } from './chatContentParts/chatMarkdownDecorationsRenderer.js';
9494
import { ChatEditorOptions } from './chatOptions.js';
9595
import { ChatCodeBlockContentProvider, CodeBlockPart } from './chatContentParts/codeBlockPart.js';
96-
import { observableValue } from '../../../../../base/common/observable.js';
96+
import { autorun, observableValue } from '../../../../../base/common/observable.js';
9797

9898
const $ = dom.$;
9999

@@ -610,7 +610,10 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
610610
const editing = element.id === this.viewModel?.editing?.id;
611611
const isInput = this.configService.getValue<string>('chat.editRequests') === 'input';
612612

613-
templateData.disabledOverlay.classList.toggle('disabled', element.shouldBeBlocked && !editing && this.viewModel?.editing !== undefined);
613+
templateData.elementDisposables.add(autorun(r => {
614+
const shouldBeBlocked = element.shouldBeBlocked.read(r);
615+
templateData.disabledOverlay.classList.toggle('disabled', shouldBeBlocked && !editing && this.viewModel?.editing !== undefined);
616+
}));
614617
templateData.rowContainer.classList.toggle('editing', editing && !isInput);
615618
templateData.rowContainer.classList.toggle('editing-input', editing && isInput);
616619
templateData.requestHover.classList.toggle('editing', editing && isInput);

src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -838,8 +838,6 @@ export class ChatWidget extends Disposable implements IChatWidget {
838838
(isResponseVM(element) ? `_${element.contentReferences.length}` : '') +
839839
// Re-render if element becomes hidden due to undo/redo
840840
`_${element.shouldBeRemovedOnSend ? `${element.shouldBeRemovedOnSend.afterUndoStop || '1'}` : '0'}` +
841-
// Re-render if element becomes enabled/disabled due to checkpointing
842-
`_${element.shouldBeBlocked ? '1' : '0'}` +
843841
// Re-render if we have an element currently being edited
844842
`_${this.viewModel?.editing ? '1' : '0'}` +
845843
// Re-render if we have an element currently being checkpointed
@@ -1620,7 +1618,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
16201618
for (let i = requests.length - 1; i >= 0; i -= 1) {
16211619
const request = requests[i];
16221620
if (request.id === currentElement.id) {
1623-
request.shouldBeBlocked = false; // unblocking just this request.
1621+
request.setShouldBeBlocked(false); // unblocking just this request.
16241622
request.attachedContext?.forEach(addToContext);
16251623
currentElement.variables.forEach(addToContext);
16261624
}

src/vs/workbench/contrib/chat/common/model/chatModel.ts

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ export interface IChatRequestModel {
7777
readonly response?: IChatResponseModel;
7878
readonly editedFileEvents?: IChatAgentEditedFileEvent[];
7979
shouldBeRemovedOnSend: IChatRequestDisablement | undefined;
80-
shouldBeBlocked: boolean;
80+
readonly shouldBeBlocked: IObservable<boolean>;
81+
setShouldBeBlocked(value: boolean): void;
8182
readonly modelId?: string;
8283
readonly userSelectedTools?: UserSelectedTools;
8384
}
@@ -215,7 +216,7 @@ export interface IChatResponseModel {
215216
readonly isPendingConfirmation: IObservable<{ startedWaitingAt: number; detail?: string } | undefined>;
216217
readonly isInProgress: IObservable<boolean>;
217218
readonly shouldBeRemovedOnSend: IChatRequestDisablement | undefined;
218-
shouldBeBlocked: boolean;
219+
readonly shouldBeBlocked: IObservable<boolean>;
219220
readonly isCompleteAddedRequest: boolean;
220221
/** A stale response is one that has been persisted and rehydrated, so e.g. Commands that have their arguments stored in the EH are gone. */
221222
readonly isStale: boolean;
@@ -290,7 +291,14 @@ export class ChatRequestModel implements IChatRequestModel {
290291
public readonly modeInfo?: IChatRequestModeInfo;
291292
public readonly userSelectedTools?: UserSelectedTools;
292293

293-
public shouldBeBlocked: boolean = false;
294+
private readonly _shouldBeBlocked = observableValue<boolean>(this, false);
295+
public get shouldBeBlocked(): IObservable<boolean> {
296+
return this._shouldBeBlocked;
297+
}
298+
299+
public setShouldBeBlocked(value: boolean): void {
300+
this._shouldBeBlocked.set(value, undefined);
301+
}
294302

295303
private _session: ChatModel;
296304
private readonly _attempt: number;
@@ -811,13 +819,13 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
811819
private _result?: IChatAgentResult;
812820
private _shouldBeRemovedOnSend: IChatRequestDisablement | undefined;
813821
public readonly isCompleteAddedRequest: boolean;
814-
private _shouldBeBlocked: boolean = false;
822+
private readonly _shouldBeBlocked = observableValue<boolean>(this, false);
815823
private readonly _timestamp: number;
816824
private _timeSpentWaitingAccumulator: number;
817825

818826
public confirmationAdjustedTimestamp: IObservable<number>;
819827

820-
public get shouldBeBlocked() {
828+
public get shouldBeBlocked(): IObservable<boolean> {
821829
return this._shouldBeBlocked;
822830
}
823831

@@ -980,7 +988,7 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
980988
this._followups = params.followups ? [...params.followups] : undefined;
981989
this.isCompleteAddedRequest = params.isCompleteAddedRequest ?? false;
982990
this._shouldBeRemovedOnSend = params.shouldBeRemovedOnSend;
983-
this._shouldBeBlocked = params.shouldBeBlocked ?? false;
991+
this._shouldBeBlocked.set(params.shouldBeBlocked ?? false, undefined);
984992

985993
// If we are creating a response with some existing content, consider it stale
986994
this._isStale = Array.isArray(params.responseContent) && (params.responseContent.length !== 0 || isMarkdownString(params.responseContent) && params.responseContent.value.length !== 0);
@@ -1028,11 +1036,7 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
10281036
this._register(this._session.onDidChange((e) => {
10291037
if (e.kind === 'setCheckpoint') {
10301038
const isDisabled = e.disabledResponseIds.has(this.id);
1031-
const didChange = this._shouldBeBlocked === isDisabled;
1032-
this._shouldBeBlocked = isDisabled;
1033-
if (didChange) {
1034-
this._onDidChange.fire(defaultChatResponseModelChangeReason);
1035-
}
1039+
this._shouldBeBlocked.set(isDisabled, undefined);
10361040
}
10371041
}));
10381042

@@ -1948,7 +1952,7 @@ export class ChatModel extends Disposable implements IChatModel {
19481952
followups: raw.followups,
19491953
restoredId: raw.responseId,
19501954
timeSpentWaiting: raw.timeSpentWaiting,
1951-
shouldBeBlocked: request.shouldBeBlocked,
1955+
shouldBeBlocked: request.shouldBeBlocked.get(),
19521956
codeBlockInfos: raw.responseMarkdownInfo?.map<ICodeBlockInfo>(info => ({ suggestionId: info.suggestionId })),
19531957
});
19541958
request.response.shouldBeRemovedOnSend = raw.isHidden ? { requestId: raw.requestId } : raw.shouldBeRemovedOnSend;
@@ -1994,7 +1998,7 @@ export class ChatModel extends Disposable implements IChatModel {
19941998

19951999
resetCheckpoint(): void {
19962000
for (const request of this._requests) {
1997-
request.shouldBeBlocked = false;
2001+
request.setShouldBeBlocked(false);
19982002
}
19992003
}
20002004

@@ -2006,7 +2010,7 @@ export class ChatModel extends Disposable implements IChatModel {
20062010
if (request.id === requestId) {
20072011
checkpointIndex = index;
20082012
checkpoint = request;
2009-
request.shouldBeBlocked = true;
2013+
request.setShouldBeBlocked(true);
20102014
}
20112015
});
20122016

@@ -2020,15 +2024,15 @@ export class ChatModel extends Disposable implements IChatModel {
20202024
for (let i = this._requests.length - 1; i >= 0; i -= 1) {
20212025
const request = this._requests[i];
20222026
if (this._checkpoint && !checkpoint) {
2023-
request.shouldBeBlocked = false;
2027+
request.setShouldBeBlocked(false);
20242028
} else if (checkpoint && i >= checkpointIndex) {
2025-
request.shouldBeBlocked = true;
2029+
request.setShouldBeBlocked(true);
20262030
disabledRequestIds.add(request.id);
20272031
if (request.response) {
20282032
disabledResponseIds.add(request.response.id);
20292033
}
20302034
} else if (checkpoint && i < checkpointIndex) {
2031-
request.shouldBeBlocked = false;
2035+
request.setShouldBeBlocked(false);
20322036
}
20332037
}
20342038

src/vs/workbench/contrib/chat/common/model/chatViewModel.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { hash } from '../../../../../base/common/hash.js';
99
import { IMarkdownString } from '../../../../../base/common/htmlContent.js';
1010
import { Disposable, dispose } from '../../../../../base/common/lifecycle.js';
1111
import * as marked from '../../../../../base/common/marked/marked.js';
12+
import { IObservable } from '../../../../../base/common/observable.js';
1213
import { ThemeIcon } from '../../../../../base/common/themables.js';
1314
import { URI } from '../../../../../base/common/uri.js';
1415
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
@@ -96,7 +97,7 @@ export interface IChatRequestViewModel {
9697
readonly isCompleteAddedRequest: boolean;
9798
readonly slashCommand: IChatAgentCommand | undefined;
9899
readonly agentOrSlashCommandDetected: boolean;
99-
readonly shouldBeBlocked?: boolean;
100+
readonly shouldBeBlocked: IObservable<boolean>;
100101
readonly modelId?: string;
101102
}
102103

@@ -224,7 +225,7 @@ export interface IChatResponseViewModel {
224225
usedReferencesExpanded?: boolean;
225226
vulnerabilitiesListExpanded: boolean;
226227
setEditApplied(edit: IChatTextEditGroup, editCount: number): void;
227-
readonly shouldBeBlocked: boolean;
228+
readonly shouldBeBlocked: IObservable<boolean>;
228229
}
229230

230231
export class ChatViewModel extends Disposable implements IChatViewModel {

0 commit comments

Comments
 (0)