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

Add submit button to interactive session input box #177157

Merged
merged 5 commits into from
Mar 15, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/vs/platform/actions/common/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export class MenuId {
static readonly InteractiveSessionContext = new MenuId('InteractiveSessionContext');
static readonly InteractiveSessionCodeBlock = new MenuId('InteractiveSessionCodeblock');
static readonly InteractiveSessionTitle = new MenuId('InteractiveSessionTitle');
static readonly InteractiveSessionExecute = new MenuId('InteractiveSessionExecute');

/**
* Create or reuse a `MenuId` with the given identifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import { ActiveEditorContext } from 'vs/workbench/common/contextkeys';
import { IViewsService } from 'vs/workbench/common/views';
import { IInteractiveSessionEditorOptions, InteractiveSessionEditor } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionEditor';
import { InteractiveSessionViewPane } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionSidebar';
import { CONTEXT_IN_INTERACTIVE_INPUT, CONTEXT_IN_INTERACTIVE_SESSION, IInteractiveSessionWidgetService } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionWidget';
import { IInteractiveSessionWidgetService } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionWidget';
import { CONTEXT_IN_INTERACTIVE_INPUT, CONTEXT_IN_INTERACTIVE_SESSION } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionContextKeys';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';

export const INTERACTIVE_SESSION_CATEGORY = { value: localize('interactiveSession.category', "Interactive Session"), original: 'Interactive Session' };
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Codicon } from 'vs/base/common/codicons';
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { localize } from 'vs/nls';
import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
import { INTERACTIVE_SESSION_CATEGORY } from 'vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionActions';
import { IInteractiveSessionWidget } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSession';

export interface IInteractiveSessionExecuteActionContext {
widget: IInteractiveSessionWidget;
}

function isExecuteActionContext(thing: unknown): thing is IInteractiveSessionExecuteActionContext {
return typeof thing === 'object' && thing !== null && 'widget' in thing;
}

export function registerInteractiveSessionExecuteActions() {
registerAction2(class SubmitAction extends Action2 {
constructor() {
super({
id: 'workbench.action.interactiveSession.submit',
title: {
value: localize('interactive.submit.label', "Submit"),
original: 'Submit'
},
f1: false,
category: INTERACTIVE_SESSION_CATEGORY,
icon: Codicon.send,
menu: {
id: MenuId.InteractiveSessionExecute,
group: 'navigation',
}
});
}

run(accessor: ServicesAccessor, ...args: any[]) {
const context = args[0];
if (!isExecuteActionContext(context)) {
return;
}

context.widget.acceptInput();
}
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@ import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { EditorExtensions } from 'vs/workbench/common/editor';
import { registerInteractiveSessionActions } from 'vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionActions';
import { registerInteractiveSessionCodeBlockActions } from 'vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCodeblockActions';
import { registerInteractiveSessionCopyActions } from 'vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCopyActions';
import { registerInteractiveSessionExecuteActions } from 'vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionExecuteActions';
import { registerInteractiveSessionTitleActions } from 'vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionTitleActions';
import { InteractiveSessionContributionService } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionContributionServiceImpl';
import { InteractiveSessionEditor } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionEditor';
import { InteractiveSessionEditorInput } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionEditorInput';
import { IInteractiveSessionWidgetService, InteractiveSessionWidgetService } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionWidget';
import { IInteractiveSessionContributionService } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionContributionService';
import { IInteractiveSessionService } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionService';
import { InteractiveSessionService } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionServiceImpl';
import { IEditorResolverService, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import '../common/interactiveSessionColors';
import { IInteractiveSessionWidgetService, InteractiveSessionWidgetService } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionWidget';
import { registerInteractiveSessionCopyActions } from 'vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCopyActions';
import { registerInteractiveSessionCodeBlockActions } from 'vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCodeblockActions';
import { registerInteractiveSessionTitleActions } from 'vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionTitleActions';


// Register configuration
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
Expand Down Expand Up @@ -112,6 +112,7 @@ registerInteractiveSessionActions();
registerInteractiveSessionCopyActions();
registerInteractiveSessionCodeBlockActions();
registerInteractiveSessionTitleActions();
registerInteractiveSessionExecuteActions();

registerSingleton(IInteractiveSessionService, InteractiveSessionService, InstantiationType.Delayed);
registerSingleton(IInteractiveSessionContributionService, InteractiveSessionContributionService, InstantiationType.Delayed);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ export interface IInteractiveSessionWidget {
readonly viewModel: IInteractiveSessionViewModel | undefined;
readonly inputEditor: ICodeEditor;

acceptInput(): void;
getSlashCommands(): Promise<IInteractiveSlashCommand[] | undefined>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { ITextModel } from 'vs/editor/common/model';
import { IModelService } from 'vs/editor/common/services/model';
import { localize } from 'vs/nls';
import { MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar';
import { MenuId } from 'vs/platform/actions/common/actions';
import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
Expand All @@ -26,10 +27,12 @@ import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles';
import { foreground } from 'vs/platform/theme/common/colorRegistry';
import { DEFAULT_FONT_FAMILY } from 'vs/workbench/browser/style';
import { getSimpleCodeEditorWidgetOptions, getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions';
import { IInteractiveSessionExecuteActionContext } from 'vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionExecuteActions';
import { IInteractiveSessionWidget } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSession';
import { InteractiveSessionFollowups } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionFollowups';
import { IInteractiveSessionRendererDelegate, InteractiveListItemRenderer, InteractiveSessionAccessibilityProvider, InteractiveSessionListDelegate, InteractiveTreeItem } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionListRenderer';
import { InteractiveSessionEditorOptions } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionOptions';
import { CONTEXT_IN_INTERACTIVE_INPUT, CONTEXT_IN_INTERACTIVE_SESSION } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionContextKeys';
import { IInteractiveSessionReplyFollowup, IInteractiveSessionService, IInteractiveSlashCommand } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionService';
import { IInteractiveSessionViewModel, InteractiveSessionViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionViewModel';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
Expand All @@ -50,9 +53,6 @@ export interface IInteractiveSessionWidgetService {

const $ = dom.$;

export const CONTEXT_IN_INTERACTIVE_INPUT = new RawContextKey<boolean>('inInteractiveInput', false, { type: 'boolean', description: localize('inInteractiveInput', "True when focus is in the interactive input, false otherwise.") });
export const CONTEXT_IN_INTERACTIVE_SESSION = new RawContextKey<boolean>('inInteractiveSession', false, { type: 'boolean', description: localize('inInteractiveSession', "True when focus is in the interactive session widget, false otherwise.") });

function revealLastElement(list: WorkbenchObjectTree<any>) {
list.scrollTop = list.scrollHeight - list.renderHeight;
}
Expand Down Expand Up @@ -225,6 +225,7 @@ export class InteractiveSessionWidget extends Disposable implements IInteractive
if (visible) {
if (!this.inputModel) {
this.inputModel = this.modelService.getModel(this.inputUri) || this.modelService.createModel('', null, this.inputUri, true);
this.inputModel.updateOptions({ bracketColorizationOptions: { enabled: false, independentColorPoolPerBracketType: false } });
}
this._inputEditor.setModel(this.inputModel);

Expand Down Expand Up @@ -370,7 +371,7 @@ export class InteractiveSessionWidget extends Disposable implements IInteractive
const inputPart = dom.append(container, $('.interactive-input-part'));
this.followupsContainer = dom.append(inputPart, $('.interactive-input-followups'));

const inputContainer = dom.append(inputPart, $('.interactive-input-wrapper'));
const inputContainer = dom.append(inputPart, $('.interactive-input-and-toolbar'));

const inputScopedContextKeyService = this._register(this.contextKeyService.createScoped(inputContainer));
CONTEXT_IN_INTERACTIVE_INPUT.bindTo(inputScopedContextKeyService).set(true);
Expand Down Expand Up @@ -398,10 +399,19 @@ export class InteractiveSessionWidget extends Disposable implements IInteractive
this.layout(this.bodyDimension.height, this.bodyDimension.width);
}
}));
this._register(this._inputEditor.onDidFocusEditorText(() => this._onDidFocus.fire()));
this._register(this._inputEditor.onDidFocusEditorText(() => {
this._onDidFocus.fire();
inputContainer.classList.toggle('focused', true);
}));
this._register(this._inputEditor.onDidBlurEditorText(() => inputContainer.classList.toggle('focused', false)));

this._register(dom.addStandardDisposableListener(inputContainer, dom.EventType.FOCUS, () => inputContainer.classList.add('synthetic-focus')));
this._register(dom.addStandardDisposableListener(inputContainer, dom.EventType.BLUR, () => inputContainer.classList.remove('synthetic-focus')));
const toolbar = this._register(this.instantiationService.createInstance(MenuWorkbenchToolBar, inputContainer, MenuId.InteractiveSessionExecute, {
menuOptions: {
shouldForwardArgs: true
}
}));
toolbar.getElement().classList.add('interactive-execute-toolbar');
toolbar.context = <IInteractiveSessionExecuteActionContext>{ widget: this };
}

private async initializeSessionModel(initial = false) {
Expand Down Expand Up @@ -497,7 +507,10 @@ export class InteractiveSessionWidget extends Disposable implements IInteractive
this.welcomeViewContainer.style.height = `${height - inputPartHeight}px`;
this.listContainer.style.height = `${height - inputPartHeight}px`;

this._inputEditor.layout({ width: width - inputPartPadding, height: inputEditorHeight });
const editorBorder = 2;
const editorPadding = 8;
const executeToolbarWidth = 27;
this._inputEditor.layout({ width: width - inputPartPadding - editorBorder - editorPadding - executeToolbarWidth, height: inputEditorHeight });
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,31 +125,36 @@
font-family: var(--monaco-monospace-font);
}

.interactive-session .interactive-input-wrapper {
.interactive-session .interactive-input-and-toolbar {
display: flex;
border-radius: 2px;
box-sizing: border-box;
cursor: text;
padding: 0px 12px;
}
margin: 0px 12px;

.interactive-session .interactive-input-wrapper .monaco-editor-background {
background-color: var(--vscode-input-background);
padding: 0 8px;
border: 1px solid var(--vscode-input-border);
border-radius: 4px;
position: relative;
padding-left: 8px;
}

/* TODO @daviddossett only apply focus border on focus */
.interactive-session .interactive-input-wrapper .monaco-editor {
border: 1px solid var(--vscode-focusBorder);
.interactive-session .interactive-input-and-toolbar.focused {
border-color: var(--vscode-focusBorder);
}

.interactive-session .interactive-input-wrapper .monaco-editor,
.interactive-session .interactive-input-wrapper .monaco-editor .overflow-guard {
border-radius: 4px;
.interactive-session .interactive-input-and-toolbar .monaco-editor .cursors-layer {
padding-left: 4px;
}

.interactive-session .interactive-input-wrapper .monaco-editor .cursors-layer {
padding-left: 4px;
.interactive-session .interactive-input-part .interactive-execute-toolbar {
position: absolute;
right: 1px;
}

.interactive-session .interactive-input-part .interactive-execute-toolbar .codicon {
font-size: 20px;
height: 20px;
width: 20px;
}

.interactive-session .monaco-inputbox {
Expand Down Expand Up @@ -247,7 +252,7 @@
}

.interactive-session .interactive-input-part .interactive-input-followups {
padding: 0px 20px;
margin: 0px 20px;
}

.interactive-session .interactive-input-part .interactive-input-followups .interactive-session-followups {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { localize } from 'vs/nls';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';

export const interactiveSessionResponseHasProviderId = new RawContextKey<boolean>('interactiveSessionResponseHasProviderId', false);
export const interactiveSessionResponseHasProviderId = new RawContextKey<boolean>('interactiveSessionResponseHasProviderId', false, { type: 'boolean', description: localize('interactiveSessionResponseHasProviderId', "True when the provider has assigned an id to this response.") });
export const interactiveSessionRequestInProgress = new RawContextKey<boolean>('interactiveSessionRequestInProgress', false, { type: 'boolean', description: localize('interactiveSessionRequestInProgress', "True when the current request is still in progress.") });

export const CONTEXT_IN_INTERACTIVE_INPUT = new RawContextKey<boolean>('inInteractiveInput', false, { type: 'boolean', description: localize('inInteractiveInput', "True when focus is in the interactive input, false otherwise.") });
export const CONTEXT_IN_INTERACTIVE_SESSION = new RawContextKey<boolean>('inInteractiveSession', false, { type: 'boolean', description: localize('inInteractiveSession', "True when focus is in the interactive session widget, false otherwise.") });