Allow cmd+c to copy chat row content (#193410)

* Allow cmd+c to copy chat row content
Fix microsoft/vscode-copilot#1433

* Add onDidBlur listener
This commit is contained in:
Rob Lourens
2023-09-18 16:50:05 -07:00
committed by GitHub
parent 308a69b603
commit ee8f89f614
3 changed files with 21 additions and 3 deletions

View File

@@ -3,12 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information. * Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions'; import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions';
import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
import { CONTEXT_IN_CHAT_LIST } from 'vs/workbench/contrib/chat/common/chatContextKeys';
import { IChatRequestViewModel, IChatResponseViewModel, isRequestVM, isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { IChatRequestViewModel, IChatResponseViewModel, isRequestVM, isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel';
export function registerChatCopyActions() { export function registerChatCopyActions() {
@@ -57,14 +60,23 @@ export function registerChatCopyActions() {
category: CHAT_CATEGORY, category: CHAT_CATEGORY,
menu: { menu: {
id: MenuId.ChatContext id: MenuId.ChatContext
},
keybinding: {
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyMod.CtrlCmd | KeyCode.KeyC,
when: CONTEXT_IN_CHAT_LIST
} }
}); });
} }
run(accessor: ServicesAccessor, ...args: any[]) { run(accessor: ServicesAccessor, ...args: any[]) {
const item = args[0]; let item = args[0];
if (!isRequestVM(item) && !isResponseVM(item)) { if (!isRequestVM(item) && !isResponseVM(item)) {
return; const widgetService = accessor.get(IChatWidgetService);
item = widgetService.lastFocusedWidget?.getFocus();
if (!isRequestVM(item) && !isResponseVM(item)) {
return;
}
} }
const clipboardService = accessor.get(IClipboardService); const clipboardService = accessor.get(IClipboardService);

View File

@@ -25,7 +25,7 @@ import { ChatInputPart } from 'vs/workbench/contrib/chat/browser/chatInputPart';
import { ChatAccessibilityProvider, ChatListDelegate, ChatListItemRenderer, IChatListItemRendererOptions, IChatRendererDelegate } from 'vs/workbench/contrib/chat/browser/chatListRenderer'; import { ChatAccessibilityProvider, ChatListDelegate, ChatListItemRenderer, IChatListItemRendererOptions, IChatRendererDelegate } from 'vs/workbench/contrib/chat/browser/chatListRenderer';
import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions'; import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions';
import { ChatViewPane } from 'vs/workbench/contrib/chat/browser/chatViewPane'; import { ChatViewPane } from 'vs/workbench/contrib/chat/browser/chatViewPane';
import { CONTEXT_CHAT_REQUEST_IN_PROGRESS, CONTEXT_IN_CHAT_SESSION } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { CONTEXT_CHAT_REQUEST_IN_PROGRESS, CONTEXT_IN_CHAT_LIST, CONTEXT_IN_CHAT_SESSION } from 'vs/workbench/contrib/chat/common/chatContextKeys';
import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService'; import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService';
import { IChatModel } from 'vs/workbench/contrib/chat/common/chatModel'; import { IChatModel } from 'vs/workbench/contrib/chat/common/chatModel';
import { IChatReplyFollowup, IChatService, ISlashCommand } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatReplyFollowup, IChatService, ISlashCommand } from 'vs/workbench/contrib/chat/common/chatService';
@@ -119,6 +119,8 @@ export class ChatWidget extends Disposable implements IChatWidget {
private lastSlashCommands: ISlashCommand[] | undefined; private lastSlashCommands: ISlashCommand[] | undefined;
private slashCommandsPromise: Promise<ISlashCommand[] | undefined> | undefined; private slashCommandsPromise: Promise<ISlashCommand[] | undefined> | undefined;
private readonly chatListFocused: IContextKey<boolean>;
constructor( constructor(
readonly viewContext: IChatWidgetViewContext, readonly viewContext: IChatWidgetViewContext,
private readonly styles: IChatWidgetStyles, private readonly styles: IChatWidgetStyles,
@@ -132,6 +134,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
) { ) {
super(); super();
CONTEXT_IN_CHAT_SESSION.bindTo(contextKeyService).set(true); CONTEXT_IN_CHAT_SESSION.bindTo(contextKeyService).set(true);
this.chatListFocused = CONTEXT_IN_CHAT_LIST.bindTo(contextKeyService);
this.requestInProgress = CONTEXT_CHAT_REQUEST_IN_PROGRESS.bindTo(contextKeyService); this.requestInProgress = CONTEXT_CHAT_REQUEST_IN_PROGRESS.bindTo(contextKeyService);
this._register((chatWidgetService as ChatWidgetService).register(this)); this._register((chatWidgetService as ChatWidgetService).register(this));
@@ -351,7 +354,9 @@ export class ChatWidget extends Disposable implements IChatWidget {
})); }));
this._register(this.tree.onDidFocus(() => { this._register(this.tree.onDidFocus(() => {
this._onDidFocus.fire(); this._onDidFocus.fire();
this.chatListFocused.set(this.tree.isDOMFocused());
})); }));
this._register(this.tree.onDidBlur(() => this.chatListFocused.set(false)));
} }
private onContextMenu(e: ITreeContextMenuEvent<ChatTreeItem | null>): void { private onContextMenu(e: ITreeContextMenuEvent<ChatTreeItem | null>): void {

View File

@@ -17,5 +17,6 @@ export const CONTEXT_REQUEST = new RawContextKey<boolean>('chatRequest', false,
export const CONTEXT_CHAT_INPUT_HAS_TEXT = new RawContextKey<boolean>('chatInputHasText', false, { type: 'boolean', description: localize('interactiveInputHasText', "True when the chat input has text.") }); export const CONTEXT_CHAT_INPUT_HAS_TEXT = new RawContextKey<boolean>('chatInputHasText', false, { type: 'boolean', description: localize('interactiveInputHasText', "True when the chat input has text.") });
export const CONTEXT_IN_CHAT_INPUT = new RawContextKey<boolean>('inChatInput', false, { type: 'boolean', description: localize('inInteractiveInput', "True when focus is in the chat input, false otherwise.") }); export const CONTEXT_IN_CHAT_INPUT = new RawContextKey<boolean>('inChatInput', false, { type: 'boolean', description: localize('inInteractiveInput', "True when focus is in the chat input, false otherwise.") });
export const CONTEXT_IN_CHAT_SESSION = new RawContextKey<boolean>('inChat', false, { type: 'boolean', description: localize('inChat', "True when focus is in the chat widget, false otherwise.") }); export const CONTEXT_IN_CHAT_SESSION = new RawContextKey<boolean>('inChat', false, { type: 'boolean', description: localize('inChat', "True when focus is in the chat widget, false otherwise.") });
export const CONTEXT_IN_CHAT_LIST = new RawContextKey<boolean>('chatListFocused', false, { type: 'boolean', description: localize('chatListFocused', "True when a row of the chat list is focused, but not when focus is on a different element inside the chat row.") });
export const CONTEXT_PROVIDER_EXISTS = new RawContextKey<boolean>('hasChatProvider', false, { type: 'boolean', description: localize('hasChatProvider', "True when some chat provider has been registered.") }); export const CONTEXT_PROVIDER_EXISTS = new RawContextKey<boolean>('hasChatProvider', false, { type: 'boolean', description: localize('hasChatProvider', "True when some chat provider has been registered.") });