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

Implement "followup commands" for interactive session API #176436

Merged
merged 5 commits into from
Mar 7, 2023
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
2 changes: 2 additions & 0 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import { SaveReason } from 'vs/workbench/common/editor';
import { IRevealOptions, ITreeItem, IViewBadge } from 'vs/workbench/common/views';
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { DebugConfigurationProviderTriggerKind, IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug';
import { IInteractiveSessionResponseCommandFollowup } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionModel';
import * as notebookCommon from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellExecutionUpdateType } from 'vs/workbench/contrib/notebook/common/notebookExecutionService';
import { ICellExecutionComplete, ICellExecutionStateUpdate } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
Expand Down Expand Up @@ -1100,6 +1101,7 @@ export interface IInteractiveRequestDto {

export interface IInteractiveResponseDto {
followups?: string[];
commandFollowups?: IInteractiveSessionResponseCommandFollowup[];
}

export interface IInteractiveResponseProgressDto {
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/api/common/extHostInteractiveSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export class ExtHostInteractiveSession implements ExtHostInteractiveSessionShape
return;
}

return { followups: res.followups };
return { followups: res.followups, commandFollowups: res.commands };
}

throw new Error('provider must implement either provideResponse or provideResponseWithProgress');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { IMarkdownRenderResult, MarkdownRenderer } from 'vs/editor/contrib/markd
import { ViewportSemanticTokensContribution } from 'vs/editor/contrib/semanticTokens/browser/viewportSemanticTokens';
import { SmartSelectController } from 'vs/editor/contrib/smartSelect/browser/smartSelect';
import { localize } from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
Expand All @@ -37,6 +38,7 @@ import { MenuPreventer } from 'vs/workbench/contrib/codeEditor/browser/menuPreve
import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard';
import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions';
import { InteractiveSessionEditorOptions } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionOptions';
import { IInteractiveSessionResponseCommandFollowup } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionModel';
import { IInteractiveRequestViewModel, IInteractiveResponseViewModel, isRequestVM, isResponseVM } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionViewModel';
import { getNWords } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionWordCounter';

Expand Down Expand Up @@ -82,6 +84,7 @@ export class InteractiveListItemRenderer extends Disposable implements ITreeRend
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IConfigurationService private readonly configService: IConfigurationService,
@ILogService private readonly logService: ILogService,
@ICommandService private readonly commandService: ICommandService,
) {
super();
this.renderer = this.instantiationService.createInstance(MarkdownRenderer, {});
Expand Down Expand Up @@ -193,13 +196,24 @@ export class InteractiveListItemRenderer extends Disposable implements ITreeRend
templateData.value.appendChild(result.element);
templateData.elementDisposables.add(result);

if (isResponseVM(element) && element.followups?.length && index === this.delegate.getListLength() - 1) {
if (isResponseVM(element) && index === this.delegate.getListLength() - 1) {
const followupsContainer = dom.append(templateData.value, $('.interactive-response-followups'));
element.followups.forEach(q => {
const button = templateData.elementDisposables.add(new Button(followupsContainer, defaultButtonStyles));
button.label = `"${q}"`;
templateData.elementDisposables.add(button.onDidClick(() => this._onDidSelectFollowup.fire(q)));
});
const followups = element.commandFollowups ?? element.followups ?? [];
followups.forEach(q => this.renderFollowup(followupsContainer, templateData, q));
}
}

private renderFollowup(container: HTMLElement, templateData: IInteractiveListItemTemplate, followup: string | IInteractiveSessionResponseCommandFollowup): void {
const button = templateData.elementDisposables.add(new Button(container, { ...defaultButtonStyles, supportIcons: typeof followup !== 'string' }));
const label = typeof followup === 'string' ? `"${followup}"` : followup.title;
button.label = label;
if (typeof followup === 'string') {
// This should probably be a command as well?
templateData.elementDisposables.add(button.onDidClick(() => this._onDidSelectFollowup.fire(followup)));
} else {
templateData.elementDisposables.add(button.onDidClick(() => {
this.commandService.executeCommand(followup.commandId, ...(followup.args ?? []));
}));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,15 @@
margin: 0;
}

.interactive-session .interactive-response .interactive-session-response-followups {
.interactive-session .interactive-response .interactive-response-followups {
display: flex;
flex-direction: column;
gap: 8px;
align-items: start;
margin-bottom: 1em; /* This is matching the margin on rendered markdown */
}

.interactive-session .interactive-response .interactive-session-response-followups .monaco-button {
.interactive-session .interactive-response .interactive-response-followups .monaco-button {
width: 100%;
padding: 2px 8px;
font-size: 11px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ export interface IInteractiveRequestModel {
readonly response: IInteractiveResponseModel | undefined;
}

export interface IInteractiveSessionResponseCommandFollowup {
commandId: string;
args: any[];
title: string; // supports codicon strings
}

export interface IInteractiveResponseModel {
readonly onDidChange: Event<void>;
readonly id: string;
Expand All @@ -26,6 +32,7 @@ export interface IInteractiveResponseModel {
readonly response: IMarkdownString;
readonly isComplete: boolean;
readonly followups?: string[];
readonly commandFollowups?: IInteractiveSessionResponseCommandFollowup[];
}

export function isRequest(item: unknown): item is IInteractiveRequestModel {
Expand Down Expand Up @@ -72,6 +79,11 @@ export class InteractiveResponseModel extends Disposable implements IInteractive
return this._followups;
}

private _commandFollowups: IInteractiveSessionResponseCommandFollowup[] | undefined;
public get commandFollowups(): IInteractiveSessionResponseCommandFollowup[] | undefined {
return this._commandFollowups;
}

private _response: IMarkdownString;
public get response(): IMarkdownString {
return this._response;
Expand All @@ -90,9 +102,10 @@ export class InteractiveResponseModel extends Disposable implements IInteractive
this._onDidChange.fire();
}

complete(followups: string[] | undefined): void {
complete(followups: string[] | undefined, commandFollowups: IInteractiveSessionResponseCommandFollowup[] | undefined): void {
this._isComplete = true;
this._followups = followups;
this._commandFollowups = commandFollowups;
this._onDidChange.fire();
}
}
Expand Down Expand Up @@ -211,8 +224,8 @@ export class InteractiveSessionModel extends Disposable implements IInteractiveS
}
}

completeResponse(request: InteractiveRequestModel, followups?: string[]): void {
request.response!.complete(followups);
completeResponse(request: InteractiveRequestModel, followups?: string[], commandFollowups?: IInteractiveSessionResponseCommandFollowup[]): void {
request.response!.complete(followups, commandFollowups);
}

setResponse(request: InteractiveRequestModel, response: InteractiveResponseModel): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { ProviderResult } from 'vs/editor/common/languages';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { InteractiveSessionModel } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionModel';
import { IInteractiveSessionResponseCommandFollowup, InteractiveSessionModel } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionModel';

export interface IInteractiveSession {
id: number;
Expand All @@ -27,6 +27,7 @@ export interface IInteractiveRequest {
export interface IInteractiveResponse {
session: IInteractiveSession;
followups?: string[];
commandFollowups?: IInteractiveSessionResponseCommandFollowup[];
}

export interface IInteractiveProgress {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export class InteractiveSessionService extends Disposable implements IInteractiv
return;
}

model.completeResponse(request, rawResponse.followups);
model.completeResponse(request, rawResponse.followups, rawResponse.commandFollowups);
this.trace('sendRequest', `Provider returned response for session ${sessionId} with ${rawResponse.followups} followups`);
} finally {
this._pendingRequestSessions.delete(sessionId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
import { IInteractiveRequestModel, IInteractiveResponseModel, IInteractiveSessionModel } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionModel';
import { IInteractiveRequestModel, IInteractiveResponseModel, IInteractiveSessionModel, IInteractiveSessionResponseCommandFollowup } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionModel';
import { IInteractiveSessionService } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionService';
import { countWords } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionWordCounter';

Expand Down Expand Up @@ -57,6 +57,7 @@ export interface IInteractiveResponseViewModel {
readonly response: IMarkdownString;
readonly isComplete: boolean;
readonly followups?: string[];
readonly commandFollowups?: IInteractiveSessionResponseCommandFollowup[];
readonly progressiveResponseRenderingEnabled: boolean;
readonly contentUpdateTimings?: IInteractiveSessionLiveUpdateData;
renderData?: IInteractiveResponseRenderData;
Expand Down Expand Up @@ -191,6 +192,10 @@ export class InteractiveResponseViewModel extends Disposable implements IInterac
return this._model.followups;
}

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

renderData: IInteractiveResponseRenderData | undefined = undefined;

currentRenderedHeight: number | undefined;
Expand Down
7 changes: 7 additions & 0 deletions src/vscode-dts/vscode.proposed.interactive.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,19 @@ declare module 'vscode' {

export interface InteractiveResponseForProgress {
followups?: string[];
commands?: InteractiveResponseCommand[];
}

export interface InteractiveProgress {
content: string;
}

export interface InteractiveResponseCommand {
commandId: string;
args: any[];
title: string; // supports codicon strings
}

export interface InteractiveSessionProvider {
provideInitialSuggestions?(token: CancellationToken): ProviderResult<string[]>;
prepareSession(initialState: InteractiveSessionState | undefined, token: CancellationToken): ProviderResult<InteractiveSession>;
Expand Down