From 6e8a36d81b242632149b2d1d9c9590311581b9b4 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 8 Mar 2023 20:58:58 -0500 Subject: [PATCH 1/3] Add Copy/Copy All actions to interactive sessions --- src/vs/platform/actions/common/actions.ts | 1 + .../interactiveSessionActions.ts | 8 +- .../actions/interactiveSessionCopyActions.ts | 79 +++++++++++++++++++ .../interactiveSession.contribution.ts | 4 +- ...teractiveSessionContributionServiceImpl.ts | 2 +- .../browser/interactiveSessionWidget.ts | 25 +++++- .../common/interactiveSessionViewModel.ts | 2 +- 7 files changed, 112 insertions(+), 9 deletions(-) rename src/vs/workbench/contrib/interactiveSession/browser/{ => actions}/interactiveSessionActions.ts (96%) create mode 100644 src/vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCopyActions.ts diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 0fe3a86e44583..f74a9a82dea37 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -179,6 +179,7 @@ export class MenuId { static readonly MergeBaseToolbar = new MenuId('MergeBaseToolbar'); static readonly MergeInputResultToolbar = new MenuId('MergeToolbarResultToolbar'); static readonly InlineSuggestionToolbar = new MenuId('InlineSuggestionToolbar'); + static readonly InteractiveSessionContext = new MenuId('InteractiveSessionContext'); /** * Create or reuse a `MenuId` with the given identifier diff --git a/src/vs/workbench/contrib/interactiveSession/browser/interactiveSessionActions.ts b/src/vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionActions.ts similarity index 96% rename from src/vs/workbench/contrib/interactiveSession/browser/interactiveSessionActions.ts rename to src/vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionActions.ts index 9d0a01ddf9044..1ddac5f9616e4 100644 --- a/src/vs/workbench/contrib/interactiveSession/browser/interactiveSessionActions.ts +++ b/src/vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionActions.ts @@ -21,7 +21,7 @@ import { InteractiveSessionViewPane } from 'vs/workbench/contrib/interactiveSess import { CONTEXT_IN_INTERACTIVE_INPUT, CONTEXT_IN_INTERACTIVE_SESSION, IInteractiveSessionWidgetService } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionWidget'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -const category = { value: localize('interactiveSession.category', "Interactive Session"), original: 'Interactive Session' }; +export const INTERACTIVE_SESSION_CATEGORY = { value: localize('interactiveSession.category', "Interactive Session"), original: 'Interactive Session' }; export function registerInteractiveSessionActions() { registerEditorAction(class InteractiveSessionAcceptInput extends EditorAction { @@ -153,7 +153,7 @@ export function registerInteractiveSessionActions() { value: localize('interactiveSession.clear.label', "Clear"), original: 'Clear' }, - category, + category: INTERACTIVE_SESSION_CATEGORY, icon: Codicon.clearAll, f1: true }); @@ -172,7 +172,7 @@ export function getOpenInteractiveSessionEditorAction(id: string, label: string, id: `workbench.action.openInteractiveSession.${id}`, title: { value: localize('interactiveSession.open', "Open Editor ({0})", label), original: `Open Editor (${label})` }, f1: true, - category, + category: INTERACTIVE_SESSION_CATEGORY, precondition: ContextKeyExpr.deserialize(when) }); } @@ -197,7 +197,7 @@ const getClearInteractiveSessionActionDescriptorForViewTitle = (viewId: string, group: 'navigation', order: 0 }, - category, + category: INTERACTIVE_SESSION_CATEGORY, icon: Codicon.clearAll, f1: false }); diff --git a/src/vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCopyActions.ts b/src/vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCopyActions.ts new file mode 100644 index 0000000000000..95c453336894b --- /dev/null +++ b/src/vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCopyActions.ts @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { localize } from 'vs/nls'; +import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { INTERACTIVE_SESSION_CATEGORY } from 'vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionActions'; +import { IInteractiveSessionWidgetService } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionWidget'; +import { IInteractiveRequestViewModel, IInteractiveResponseViewModel, isRequestVM, isResponseVM } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionViewModel'; + +export function registerInteractiveSessionCopyActions() { + registerAction2(class CopyAllAction extends Action2 { + constructor() { + super({ + id: 'workbench.action.interactiveSession.copyAll', + title: { + value: localize('interactive.copyAll.label', "Copy All"), + original: 'Copy All' + }, + f1: true, + category: INTERACTIVE_SESSION_CATEGORY, + menu: { + id: MenuId.InteractiveSessionContext + } + }); + } + + run(accessor: ServicesAccessor, ...args: any[]) { + const clipboardService = accessor.get(IClipboardService); + const interactiveWidgetService = accessor.get(IInteractiveSessionWidgetService); + const widget = interactiveWidgetService.lastFocusedWidget; + if (widget) { + const model = widget.getModel(); + const sessionAsText = model?.getItems() + .map(stringifyItem) + .join('\n\n'); + if (sessionAsText) { + clipboardService.writeText(sessionAsText); + } + } + } + }); + + registerAction2(class CopyItemAction extends Action2 { + constructor() { + super({ + id: 'workbench.action.interactiveSession.copyItem', + title: { + value: localize('interactive.copyItem.label', "Copy"), + original: 'Copy' + }, + f1: true, + category: INTERACTIVE_SESSION_CATEGORY, + menu: { + id: MenuId.InteractiveSessionContext + } + }); + } + + run(accessor: ServicesAccessor, ...args: any[]) { + const item = args[0]; + if (!isRequestVM(item) && !isResponseVM(item)) { + return; + } + + const clipboardService = accessor.get(IClipboardService); + const text = stringifyItem(item); + clipboardService.writeText(text); + } + }); +} + +function stringifyItem(item: IInteractiveRequestViewModel | IInteractiveResponseViewModel): string { + return isRequestVM(item) ? + `${item.username}: ${item.message}` : `${item.username}: ${item.response.value}`; +} diff --git a/src/vs/workbench/contrib/interactiveSession/browser/interactiveSession.contribution.ts b/src/vs/workbench/contrib/interactiveSession/browser/interactiveSession.contribution.ts index 1674b254bda8a..9d72d5d5ec4cf 100644 --- a/src/vs/workbench/contrib/interactiveSession/browser/interactiveSession.contribution.ts +++ b/src/vs/workbench/contrib/interactiveSession/browser/interactiveSession.contribution.ts @@ -14,7 +14,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor'; 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/interactiveSessionActions'; +import { registerInteractiveSessionActions } from 'vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionActions'; 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'; @@ -25,6 +25,7 @@ import { IEditorResolverService, RegisteredEditorPriority } from 'vs/workbench/s 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'; // Register configuration @@ -100,6 +101,7 @@ const workbenchContributionsRegistry = Registry.as this.onContextMenu(e)); this._register(this.tree.onDidChangeContentHeight(() => { this.onDidChangeTreeContentHeight(); @@ -253,6 +257,19 @@ export class InteractiveSessionWidget extends Disposable { })); } + private onContextMenu(e: ITreeContextMenuEvent): void { + e.browserEvent.preventDefault(); + e.browserEvent.stopPropagation(); + + this.contextMenuService.showContextMenu({ + menuId: MenuId.InteractiveSessionContext, + menuActionOptions: { shouldForwardArgs: true }, + contextKeyService: this.contextKeyService, + getAnchor: () => e.anchor, + getActionsContext: () => e.element, + }); + } + private onDidChangeTreeContentHeight(): void { if (this.tree.scrollHeight !== this.previousTreeScrollHeight) { // Due to rounding, the scrollTop + renderHeight will not exactly match the scrollHeight. @@ -360,6 +377,10 @@ export class InteractiveSessionWidget extends Disposable { } } + getModel(): IInteractiveSessionViewModel | undefined { + return this.viewModel; + } + layout(height: number, width: number): void { this.bodyDimension = new dom.Dimension(width, height); const inputHeight = Math.min(this.inputEditor.getContentHeight(), height, INPUT_EDITOR_MAX_HEIGHT); diff --git a/src/vs/workbench/contrib/interactiveSession/common/interactiveSessionViewModel.ts b/src/vs/workbench/contrib/interactiveSession/common/interactiveSessionViewModel.ts index a2fee1ab16282..53029f5525d12 100644 --- a/src/vs/workbench/contrib/interactiveSession/common/interactiveSessionViewModel.ts +++ b/src/vs/workbench/contrib/interactiveSession/common/interactiveSessionViewModel.ts @@ -65,7 +65,7 @@ export interface IInteractiveResponseViewModel { currentRenderedHeight: number | undefined; } -export class InteractiveSessionViewModel extends Disposable { +export class InteractiveSessionViewModel extends Disposable implements IInteractiveSessionViewModel { private readonly _onDidDisposeModel = this._register(new Emitter()); readonly onDidDisposeModel = this._onDidDisposeModel.event; From f4b714846623553db60ce9ca501b6d7d5310987a Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 8 Mar 2023 20:59:35 -0500 Subject: [PATCH 2/3] Remove Copy from F1 --- .../browser/actions/interactiveSessionCopyActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCopyActions.ts b/src/vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCopyActions.ts index 95c453336894b..6c12ce013edc7 100644 --- a/src/vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCopyActions.ts +++ b/src/vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCopyActions.ts @@ -52,7 +52,7 @@ export function registerInteractiveSessionCopyActions() { value: localize('interactive.copyItem.label', "Copy"), original: 'Copy' }, - f1: true, + f1: false, category: INTERACTIVE_SESSION_CATEGORY, menu: { id: MenuId.InteractiveSessionContext From ca770173b793580f836e07d4f523b9fb17fe2859 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 9 Mar 2023 17:10:22 -0800 Subject: [PATCH 3/3] Remove from command palette --- .../browser/actions/interactiveSessionCopyActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCopyActions.ts b/src/vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCopyActions.ts index 6c12ce013edc7..3df37cc29b3d3 100644 --- a/src/vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCopyActions.ts +++ b/src/vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCopyActions.ts @@ -20,7 +20,7 @@ export function registerInteractiveSessionCopyActions() { value: localize('interactive.copyAll.label', "Copy All"), original: 'Copy All' }, - f1: true, + f1: false, category: INTERACTIVE_SESSION_CATEGORY, menu: { id: MenuId.InteractiveSessionContext