Skip to content

Commit

Permalink
add toggle details focus for terminal suggest widget (#238022)
Browse files Browse the repository at this point in the history
  • Loading branch information
meganrogge authored Jan 17, 2025
1 parent 862fa30 commit 8159b3e
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { SuggestAddon } from './terminalSuggestAddon.js';
import { TerminalClipboardContribution } from '../../clipboard/browser/terminal.clipboard.contribution.js';
import { PwshCompletionProviderAddon } from './pwshCompletionProviderAddon.js';
import { SimpleSuggestContext } from '../../../../services/suggest/browser/simpleSuggestWidget.js';
import { SuggestDetailsClassName } from '../../../../services/suggest/browser/simpleSuggestWidgetDetails.js';
import { EditorContextKeys } from '../../../../../editor/common/editorContextKeys.js';

registerSingleton(ITerminalCompletionService, TerminalCompletionService, InstantiationType.Delayed);

Expand Down Expand Up @@ -153,7 +155,16 @@ class TerminalSuggestContribution extends DisposableStore implements ITerminalCo
addon.setContainerWithOverflow(dom.findParentWithClass(xterm.element!, 'panel')!);
}
addon.setScreen(xterm.element!.querySelector('.xterm-screen')!);
this.add(this._ctx.instance.onDidBlur(() => addon.hideSuggestWidget()));

this.add(dom.addDisposableListener(this._ctx.instance.domElement, dom.EventType.FOCUS_OUT, (e) => {
const focusedElement = e.relatedTarget as HTMLElement;
if (focusedElement.className === SuggestDetailsClassName) {
// Don't hide the suggest widget if the focus is moving to the details
return;
}
addon.hideSuggestWidget();
}));

this.add(addon.onAcceptedCompletion(async text => {
this._ctx.instance.focus();
this._ctx.instance.sendText(text, false);
Expand Down Expand Up @@ -263,11 +274,25 @@ registerActiveInstanceAction({
run: (activeInstance) => TerminalSuggestContribution.get(activeInstance)?.addon?.toggleExplainMode()
});

registerActiveInstanceAction({
id: TerminalSuggestCommandId.ToggleDetailsFocus,
title: localize2('workbench.action.terminal.suggestToggleDetailsFocus', 'Suggest Toggle Suggestion Focus'),
f1: false,
// HACK: This does not work with a precondition of `TerminalContextKeys.suggestWidgetVisible`, so make sure to not override the editor's keybinding
precondition: EditorContextKeys.textInputFocus.negate(),
keybinding: {
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Space,
mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Space }
},
run: (activeInstance) => TerminalSuggestContribution.get(activeInstance)?.addon?.toggleSuggestionFocus()
});

registerActiveInstanceAction({
id: TerminalSuggestCommandId.ToggleDetails,
title: localize2('workbench.action.terminal.suggestToggleDetails', 'Suggest Toggle Details'),
f1: false,
precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.focus, TerminalContextKeys.isOpen, TerminalContextKeys.suggestWidgetVisible, SimpleSuggestContext.HasFocusedSuggestion),
precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.isOpen, TerminalContextKeys.focus, TerminalContextKeys.suggestWidgetVisible, SimpleSuggestContext.HasFocusedSuggestion),
keybinding: {
// HACK: Force weight to be higher than that to start terminal chat
weight: KeybindingWeight.ExternalExtension + 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
this._suggestWidget?.toggleExplainMode();
}

toggleSuggestionFocus(): void {
this._suggestWidget?.toggleDetailsFocus();
}

toggleSuggestionDetails(): void {
this._suggestWidget?.toggleDetails();
}
Expand Down Expand Up @@ -373,6 +377,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
}
const suggestWidget = this._ensureSuggestWidget(this._terminal);
suggestWidget.setCompletionModel(model);
this._register(suggestWidget.onDidFocus(() => this._terminal?.focus()));
if (!this._promptInputModel || !explicitlyInvoked && model.items.length === 0) {
return;
}
Expand Down Expand Up @@ -413,8 +418,29 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
listInactiveFocusOutline: activeContrastBorder
}));
this._register(this._suggestWidget.onDidSelect(async e => this.acceptSelectedSuggestion(e)));
this._register(this._suggestWidget.onDidHide(() => this._terminalSuggestWidgetVisibleContextKey.set(false)));
this._register(this._suggestWidget.onDidHide(() => this._terminalSuggestWidgetVisibleContextKey.reset()));
this._register(this._suggestWidget.onDidShow(() => this._terminalSuggestWidgetVisibleContextKey.set(true)));

const element = this._terminal?.element?.querySelector('.xterm-helper-textarea');
if (element) {
this._register(dom.addDisposableListener(dom.getActiveDocument(), 'click', (event) => {
const target = event.target as HTMLElement;
if (this._terminal?.element?.contains(target)) {
this._suggestWidget?.hide();
}
}));
}

this._register(this._suggestWidget.onDidBlurDetails((e) => {
const elt = e.relatedTarget as HTMLElement;
if (this._terminal?.element?.contains(elt)) {
// Do nothing, just the terminal getting focused
// If there was a mouse click, the suggest widget will be
// hidden above
return;
}
this._suggestWidget?.hide();
}));
this._terminalSuggestWidgetVisibleContextKey.set(false);
}
return this._suggestWidget;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export const enum TerminalSuggestCommandId {
ClearSuggestCache = 'workbench.action.terminal.clearSuggestCache',
RequestCompletions = 'workbench.action.terminal.requestCompletions',
ResetWidgetSize = 'workbench.action.terminal.resetSuggestWidgetSize',
ToggleDetails = 'workbench.action.terminal.suggestToggleDetails'
ToggleDetails = 'workbench.action.terminal.suggestToggleDetails',
ToggleDetailsFocus = 'workbench.action.terminal.suggestToggleDetailsFocus',
}

export const defaultTerminalSuggestCommandsToSkipShell = [
Expand All @@ -27,5 +28,6 @@ export const defaultTerminalSuggestCommandsToSkipShell = [
TerminalSuggestCommandId.HideSuggestWidget,
TerminalSuggestCommandId.ClearSuggestCache,
TerminalSuggestCommandId.RequestCompletions,
TerminalSuggestCommandId.ToggleDetails
TerminalSuggestCommandId.ToggleDetails,
TerminalSuggestCommandId.ToggleDetailsFocus,
];
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ export class SimpleSuggestWidget extends Disposable {
readonly onDidShow: Event<this> = this._onDidShow.event;
private readonly _onDidFocus = new PauseableEmitter<ISimpleSelectedSuggestion>();
readonly onDidFocus: Event<ISimpleSelectedSuggestion> = this._onDidFocus.event;
private readonly _onDidBlurDetails = this._register(new Emitter<FocusEvent>());
readonly onDidBlurDetails = this._onDidBlurDetails.event;

get list(): List<SimpleCompletionItem> { return this._list; }

Expand Down Expand Up @@ -223,6 +225,7 @@ export class SimpleSuggestWidget extends Disposable {
const details: SimpleSuggestDetailsWidget = this._register(instantiationService.createInstance(SimpleSuggestDetailsWidget));
this._register(details.onDidClose(() => this.toggleDetails()));
this._details = this._register(new SimpleSuggestDetailsOverlay(details, this._listElement));
this._register(dom.addDisposableListener(this._details.widget.domNode, 'blur', (e) => this._onDidBlurDetails.fire(e)));

if (options.statusBarMenuId) {
this._status = this._register(instantiationService.createInstance(SuggestWidgetStatus, this.element.domNode, options.statusBarMenuId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export function canExpandCompletionItem(item: SimpleCompletionItem | undefined):
return !!item && Boolean(item.completion.detail && item.completion.detail !== item.completion.label);
}

export const SuggestDetailsClassName = 'suggest-details';

export class SimpleSuggestDetailsWidget {

readonly domNode: HTMLDivElement;
Expand Down Expand Up @@ -158,7 +160,7 @@ export class SimpleSuggestDetailsWidget {
this._renderDisposeable.add(renderedContents);
}

// this.domNode.classList.toggle('detail-and-doc', !!documentation);
this.domNode.classList.toggle('detail-and-doc', !!detail && !!documentation);

this.domNode.style.userSelect = 'text';
this.domNode.tabIndex = -1;
Expand Down

0 comments on commit 8159b3e

Please sign in to comment.