From 70f22f586e3b4c6537e221a55a798e9b3dc8d4b9 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Wed, 22 Jan 2025 09:51:42 +0100 Subject: [PATCH] Merge Go To and Accept in gutter menu (#238446) closes https://github.com/microsoft/vscode-copilot/issues/11923 --- .../view/inlineEdits/gutterIndicatorMenu.ts | 18 +++++----- .../view/inlineEdits/gutterIndicatorView.ts | 35 +++++++------------ 2 files changed, 20 insertions(+), 33 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorMenu.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorMenu.ts index afbcc2b58d5c8..089c058b6d40a 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorMenu.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorMenu.ts @@ -21,7 +21,7 @@ import { ChildNode, FirstFnArg, LiveElement, n } from './utils.js'; export class GutterIndicatorMenuContent { constructor( private readonly _menuTitle: IObservable, - private readonly _selectionOverride: IObservable<'jump' | 'accept' | undefined>, + private readonly _tabAction: IObservable<'jump' | 'accept' | 'inactive'>, private readonly _close: (focusEditor: boolean) => void, private readonly _extensionCommands: IObservable, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @@ -36,18 +36,17 @@ export class GutterIndicatorMenuContent { private _createHoverContent() { const activeElement = observableValue('active', undefined); - const activeElementOrDefault = derived(reader => this._selectionOverride.read(reader) ?? activeElement.read(reader)); - const createOptionArgs = (options: { id: string; title: string; icon: ThemeIcon; commandId: string; commandArgs?: unknown[] }): FirstFnArg => { + const createOptionArgs = (options: { id: string; title: string; icon: IObservable | ThemeIcon; commandId: string | IObservable; commandArgs?: unknown[] }): FirstFnArg => { return { title: options.title, icon: options.icon, - keybinding: this._getKeybinding(options.commandArgs ? undefined : options.commandId), - isActive: activeElementOrDefault.map(v => v === options.id), + keybinding: typeof options.commandId === 'string' ? this._getKeybinding(options.commandArgs ? undefined : options.commandId) : derived(reader => typeof options.commandId === 'string' ? undefined : this._getKeybinding(options.commandArgs ? undefined : options.commandId.read(reader)).read(reader)), + isActive: activeElement.map(v => v === options.id), onHoverChange: v => activeElement.set(v ? options.id : undefined, undefined), onAction: () => { this._close(true); - return this._commandService.executeCommand(options.commandId, ...(options.commandArgs ?? [])); + return this._commandService.executeCommand(typeof options.commandId === 'string' ? options.commandId : options.commandId.get(), ...(options.commandArgs ?? [])); }, }; }; @@ -55,8 +54,7 @@ export class GutterIndicatorMenuContent { // TODO make this menu contributable! return hoverContent([ header(this._menuTitle), - option(createOptionArgs({ id: 'jump', title: localize('goto', "Go To"), icon: Codicon.arrowRight, commandId: new JumpToNextInlineEdit().id })), - option(createOptionArgs({ id: 'accept', title: localize('accept', "Accept"), icon: Codicon.check, commandId: new AcceptInlineCompletion().id })), + option(createOptionArgs({ id: 'gotoAndAccept', title: `${localize('goto', "Go To")} / ${localize('accept', "Accept")}`, icon: this._tabAction.map(action => action === 'accept' ? Codicon.check : Codicon.arrowRight), commandId: this._tabAction.map(action => action === 'accept' ? new AcceptInlineCompletion().id : new JumpToNextInlineEdit().id) })), option(createOptionArgs({ id: 'reject', title: localize('reject', "Reject"), icon: Codicon.close, commandId: new HideInlineCompletion().id })), separator(), this._extensionCommands?.map(c => c && c.length > 0 ? [ @@ -100,7 +98,7 @@ function header(title: string | IObservable) { function option(props: { title: string; - icon: ThemeIcon; + icon: IObservable | ThemeIcon; keybinding: IObservable; isActive?: IObservable; onHoverChange?: (isHovered: boolean) => void; @@ -123,7 +121,7 @@ function option(props: { fontSize: 16, display: 'flex', } - }, [renderIcon(props.icon)]), + }, [ThemeIcon.isThemeIcon(props.icon) ? renderIcon(props.icon) : props.icon.map(icon => renderIcon(icon))]), n.elem('span', {}, [props.title]), n.div({ style: { marginLeft: 'auto', opacity: '0.6' }, diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts index 9fa6bb711e440..7dc1c30042ac0 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts @@ -157,30 +157,9 @@ export class InlineEditsGutterIndicator extends Disposable { return 'inactive' as const; }); - private readonly _onClickAction = derived(this, reader => { - if (this._layout.map(d => d && d.docked).read(reader)) { - return { - selectionOverride: 'accept' as const, - action: () => { - this._editorObs.editor.focus(); - this._model.get()?.accept(); - } - }; - } else { - return { - selectionOverride: 'jump' as const, - action: () => { - this._editorObs.editor.focus(); - this._model.get()?.jump(); - } - }; - } - }); - private readonly _iconRef = n.ref(); private _hoverVisible: boolean = false; private readonly _isHoveredOverIcon = observableValue(this, false); - private readonly _hoverSelectionOverride = derived(this, reader => this._isHoveredOverIcon.read(reader) ? this._onClickAction.read(reader).selectionOverride : undefined); private _showHover(): void { if (this._hoverVisible) { @@ -200,7 +179,7 @@ export class InlineEditsGutterIndicator extends Disposable { const content = disposableStore.add(this._instantiationService.createInstance( GutterIndicatorMenuContent, displayName, - this._hoverSelectionOverride, + this._tabAction, (focusEditor) => { if (focusEditor) { this._editorObs.editor.focus(); @@ -232,7 +211,17 @@ export class InlineEditsGutterIndicator extends Disposable { private readonly _indicator = n.div({ class: 'inline-edits-view-gutter-indicator', - onclick: () => this._onClickAction.get().action(), + onclick: () => { + const model = this._model.get(); + if (!model) { return; } + const docked = this._layout.map(l => l && l.docked).get(); + this._editorObs.editor.focus(); + if (docked) { + model.accept(); + } else { + model.jump(); + } + }, tabIndex: 0, style: { position: 'absolute',