Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: render percentage progress in chat code block pills #233723

Merged
merged 2 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import * as dom from '../../../../../base/browser/dom.js';
import { StandardMouseEvent } from '../../../../../base/browser/mouseEvent.js';
import { Codicon } from '../../../../../base/common/codicons.js';
import { Emitter } from '../../../../../base/common/event.js';
import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js';
import { Disposable, DisposableStore, IDisposable } from '../../../../../base/common/lifecycle.js';
import { autorun } from '../../../../../base/common/observable.js';
import { equalsIgnoreCase } from '../../../../../base/common/strings.js';
import { ThemeIcon } from '../../../../../base/common/themables.js';
import { URI } from '../../../../../base/common/uri.js';
Expand All @@ -34,6 +35,7 @@ import { IChatMarkdownContent } from '../../common/chatService.js';
import { isRequestVM, isResponseVM } from '../../common/chatViewModel.js';
import { CodeBlockModelCollection } from '../../common/codeBlockModelCollection.js';
import { IChatCodeBlockInfo, IChatListItemRendererOptions } from '../chat.js';
import { AnimatedValue, ObservableAnimatedValue } from '../chatEditorOverlay.js';
import { IChatRendererDelegate } from '../chatListRenderer.js';
import { ChatMarkdownDecorationsRenderer } from '../chatMarkdownDecorationsRenderer.js';
import { ChatEditorOptions } from '../chatOptions.js';
Expand Down Expand Up @@ -281,6 +283,8 @@ class CollapsedCodeBlock extends Disposable {
return this._uri;
}

private readonly _progressStore = new DisposableStore();

constructor(
sessionId: string,
requestId: string,
Expand Down Expand Up @@ -317,6 +321,8 @@ class CollapsedCodeBlock extends Disposable {
}

render(uri: URI, isStreaming?: boolean): void {
this._progressStore.clear();

this._uri = uri;

const iconText = this.labelService.getUriBasenameLabel(uri);
Expand All @@ -335,16 +341,39 @@ class CollapsedCodeBlock extends Disposable {
const iconEl = dom.$('span.icon');
iconEl.classList.add(...iconClasses);

const statusText = isStreaming ? localize('chat.codeblock.generating', "Generating edits...")
: !isComplete ? localize('chat.codeblock.applying', "Applying edits...")
: '';

const children = [dom.$('span.icon-label', {}, iconText)];
if (statusText) {
children.push(dom.$('span.label-detail', {}, statusText));
if (isStreaming) {
children.push(dom.$('span.label-detail', {}, localize('chat.codeblock.generating', "Generating edits...")));
} else if (!isComplete) {
children.push(dom.$('span.label-detail', {}, ''));
}

this.element.replaceChildren(iconEl, ...children);
this.element.title = this.labelService.getUriLabel(uri, { relative: false });

// Show a percentage progress that is driven by the rewrite
const slickRatio = ObservableAnimatedValue.const(0);
let t = Date.now();
this._progressStore.add(autorun(r => {
const rewriteRatio = modifiedEntry?.rewriteRatio.read(r);
if (rewriteRatio) {
slickRatio.changeAnimation(prev => {
const result = new AnimatedValue(prev.getValue(), rewriteRatio, Date.now() - t);
t = Date.now();
return result;
}, undefined);
}

const labelDetail = this.element.querySelector('.label-detail');
const isComplete = !modifiedEntry?.isCurrentlyBeingModified.read(r);
if (labelDetail && !isStreaming && !isComplete) {
const value = slickRatio.getValue(undefined);
labelDetail.textContent = value === 0 ? localize('chat.codeblock.applying', "Applying edits...") : localize('chat.codeblock.applyingPercentage', "Applying edits ({0}%)...", Math.round(value * 100));
} else if (labelDetail && !isStreaming && isComplete) {
iconEl.classList.remove(...iconClasses);
const fileKind = uri.path.endsWith('/') ? FileKind.FOLDER : FileKind.FILE;
iconEl.classList.add(...getIconClasses(this.modelService, this.languageService, uri, fileKind));
labelDetail.textContent = '';
}
}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
const entriesArr: ChatEditingModifiedFileEntry[] = [];
// Restore all entries from the snapshot
for (const snapshotEntry of snapshot.entries.values()) {
const { entry } = await this._getOrCreateModifiedFileEntry(snapshotEntry.resource, snapshotEntry.telemetryInfo);
const entry = await this._getOrCreateModifiedFileEntry(snapshotEntry.resource, snapshotEntry.telemetryInfo);
entry.restoreFromSnapshot(snapshotEntry);
entriesArr.push(entry);
}
Expand Down Expand Up @@ -539,11 +539,8 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
get requestId() { return responseModel.requestId; }
get result() { return responseModel.result; }
};
const { entry, isFirstEditForFileInResponse } = await this._getOrCreateModifiedFileEntry(resource, telemetryInfo);
const entry = await this._getOrCreateModifiedFileEntry(resource, telemetryInfo);
entry.acceptAgentEdits(textEdits, isLastEdits);
if (isFirstEditForFileInResponse || isLastEdits) {
responseModel.reportEditCountChange();
}
// await this._editorService.openEditor({ resource: entry.modifiedURI, options: { inactive: true } });
}

Expand All @@ -557,14 +554,13 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
this._onDidChange.fire();
}

private async _getOrCreateModifiedFileEntry(resource: URI, responseModel: IModifiedEntryTelemetryInfo): Promise<{ entry: ChatEditingModifiedFileEntry; isFirstEditForFileInResponse: boolean }> {
private async _getOrCreateModifiedFileEntry(resource: URI, responseModel: IModifiedEntryTelemetryInfo): Promise<ChatEditingModifiedFileEntry> {
const existingEntry = this._entriesObs.get().find(e => isEqual(e.modifiedURI, resource));
if (existingEntry) {
const isFirstEditForFileInResponse = responseModel.requestId !== existingEntry.telemetryInfo.requestId;
if (isFirstEditForFileInResponse) {
if (responseModel.requestId !== existingEntry.telemetryInfo.requestId) {
existingEntry.updateTelemetryInfo(responseModel);
}
return { entry: existingEntry, isFirstEditForFileInResponse };
return existingEntry;
}

// This gets manually disposed in .dispose() or in .restoreSnapshot()
Expand All @@ -586,7 +582,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
this._entriesObs.set(entriesArr, undefined);
this._onDidChange.fire();

return { entry, isFirstEditForFileInResponse: true };
return entry;
}

private async _createModifiedFileEntry(resource: URI, responseModel: IModifiedEntryTelemetryInfo, mustExist = false): Promise<ChatEditingModifiedFileEntry> {
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/contrib/chat/browser/chatEditorOverlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ class Scheduler {
}
}

class AnimatedValue {
export class AnimatedValue {

static const(value: number): AnimatedValue {
return new AnimatedValue(value, value, 0);
Expand Down
2 changes: 0 additions & 2 deletions src/vs/workbench/contrib/chat/browser/chatWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -534,8 +534,6 @@ export class ChatWidget extends Disposable implements IChatWidget {
(isResponseVM(element) ? `_${element.contentReferences.length}` : '') +
// Re-render if element becomes hidden due to undo/redo
`_${element.isHidden ? '1' : '0'}` +
// Re-render if element working set state changes
(isResponseVM(element) ? `_${element.editChangeCount}` : '') +
// Rerender request if we got new content references in the response
// since this may change how we render the corresponding attachments in the request
(isRequestVM(element) && element.contentReferences ? `_${element.contentReferences?.length}` : '');
Expand Down
12 changes: 0 additions & 12 deletions src/vs/workbench/contrib/chat/common/chatModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,9 @@ export interface IChatResponseModel {
readonly voteDownReason: ChatAgentVoteDownReason | undefined;
readonly followups?: IChatFollowup[] | undefined;
readonly result?: IChatAgentResult;
readonly editChangeCount: number;
setVote(vote: ChatAgentVoteDirection): void;
setVoteDownReason(reason: ChatAgentVoteDownReason | undefined): void;
setEditApplied(edit: IChatTextEditGroup, editCount: number): boolean;
reportEditCountChange(): void;
}

export class ChatRequestModel implements IChatRequestModel {
Expand Down Expand Up @@ -420,11 +418,6 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
return this._session;
}

private _editChangeCount: number = 0;
public get editChangeCount(): number {
return this._editChangeCount;
}

public get isHidden() {
return this._isHidden;
}
Expand Down Expand Up @@ -616,11 +609,6 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
return true;
}

reportEditCountChange() {
this._editChangeCount++;
this._onDidChange.fire();
}

adoptTo(session: ChatModel) {
this._session = session;
this._onDidChange.fire();
Expand Down
5 changes: 0 additions & 5 deletions src/vs/workbench/contrib/chat/common/chatViewModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,6 @@ export interface IChatResponseViewModel {
readonly contentUpdateTimings?: IChatLiveUpdateData;
readonly isHidden: boolean;
readonly isCompleteAddedRequest: boolean;
readonly editChangeCount: number;
renderData?: IChatResponseRenderData;
currentRenderedHeight: number | undefined;
setVote(vote: ChatAgentVoteDirection): void;
Expand Down Expand Up @@ -409,10 +408,6 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi
return this._model.id;
}

get editChangeCount() {
return this._model.editChangeCount;
}

get dataId() {
return this._model.id +
`_${this._modelChangeCount}` +
Expand Down
Loading