Path: blob/main/src/vs/workbench/contrib/interactive/browser/replInputHintContentWidget.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as dom from '../../../../base/browser/dom.js';6import { status } from '../../../../base/browser/ui/aria/aria.js';7import { KeybindingLabel } from '../../../../base/browser/ui/keybindingLabel/keybindingLabel.js';8import { Event } from '../../../../base/common/event.js';9import { ResolvedKeybinding } from '../../../../base/common/keybindings.js';10import { Disposable } from '../../../../base/common/lifecycle.js';11import { OS } from '../../../../base/common/platform.js';12import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../../editor/browser/editorBrowser.js';13import { ConfigurationChangedEvent, EditorOption } from '../../../../editor/common/config/editorOptions.js';14import { Position } from '../../../../editor/common/core/position.js';15import { localize } from '../../../../nls.js';16import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';17import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';18import { AccessibilityVerbositySettingId } from '../../accessibility/browser/accessibilityConfiguration.js';19import { AccessibilityCommandId } from '../../accessibility/common/accessibilityCommands.js';20import { ReplEditorSettings } from './interactiveCommon.js';212223export class ReplInputHintContentWidget extends Disposable implements IContentWidget {2425private static readonly ID = 'replInput.widget.emptyHint';2627private domNode: HTMLElement | undefined;28private ariaLabel: string = '';29private label: KeybindingLabel | undefined;3031constructor(32private readonly editor: ICodeEditor,33@IConfigurationService private readonly configurationService: IConfigurationService,34@IKeybindingService private readonly keybindingService: IKeybindingService,35) {36super();3738this._register(this.editor.onDidChangeConfiguration((e: ConfigurationChangedEvent) => {39if (this.domNode && e.hasChanged(EditorOption.fontInfo)) {40this.editor.applyFontInfo(this.domNode);41}42}));43const onDidFocusEditorText = Event.debounce(this.editor.onDidFocusEditorText, () => undefined, 500);44this._register(onDidFocusEditorText(() => {45if (this.editor.hasTextFocus() && this.ariaLabel && configurationService.getValue(AccessibilityVerbositySettingId.ReplEditor)) {46status(this.ariaLabel);47}48}));49this._register(configurationService.onDidChangeConfiguration(e => {50if (e.affectsConfiguration(ReplEditorSettings.executeWithShiftEnter)) {51this.setHint();52}53}));54this.editor.addContentWidget(this);55}5657getId(): string {58return ReplInputHintContentWidget.ID;59}6061getPosition(): IContentWidgetPosition | null {62return {63position: { lineNumber: 1, column: 1 },64preference: [ContentWidgetPositionPreference.EXACT]65};66}6768getDomNode(): HTMLElement {69if (!this.domNode) {70this.domNode = dom.$('.empty-editor-hint');71this.domNode.style.width = 'max-content';72this.domNode.style.paddingLeft = '4px';7374this.setHint();7576this._register(dom.addDisposableListener(this.domNode, 'click', () => {77this.editor.focus();78}));7980this.editor.applyFontInfo(this.domNode);81const lineHeight = this.editor.getLineHeightForPosition(new Position(1, 1));82this.domNode.style.lineHeight = lineHeight + 'px';83}8485return this.domNode;86}8788private setHint() {89if (!this.domNode) {90return;91}92while (this.domNode.firstChild) {93this.domNode.removeChild(this.domNode.firstChild);94}9596const hintElement = dom.$('div.empty-hint-text');97hintElement.style.cursor = 'text';98hintElement.style.whiteSpace = 'nowrap';99100const keybinding = this.getKeybinding();101const keybindingHintLabel = keybinding?.getLabel();102103if (keybinding && keybindingHintLabel) {104const actionPart = localize('emptyHintText', 'Press {0} to execute. ', keybindingHintLabel);105106const [before, after] = actionPart.split(keybindingHintLabel).map((fragment) => {107const hintPart = dom.$('span', undefined, fragment);108hintPart.style.fontStyle = 'italic';109return hintPart;110});111112hintElement.appendChild(before);113114if (this.label) {115this.label.dispose();116}117this.label = this._register(new KeybindingLabel(hintElement, OS));118this.label.set(keybinding);119this.label.element.style.width = 'min-content';120this.label.element.style.display = 'inline';121122hintElement.appendChild(after);123this.domNode.append(hintElement);124125const helpKeybinding = this.keybindingService.lookupKeybinding(AccessibilityCommandId.OpenAccessibilityHelp)?.getLabel();126const helpInfo = helpKeybinding127? localize('ReplInputAriaLabelHelp', "Use {0} for accessibility help. ", helpKeybinding)128: localize('ReplInputAriaLabelHelpNoKb', "Run the Open Accessibility Help command for more information. ");129130this.ariaLabel = actionPart.concat(helpInfo, localize('disableHint', ' Toggle {0} in settings to disable this hint.', AccessibilityVerbositySettingId.ReplEditor));131}132}133134private getKeybinding() {135const keybindings = this.keybindingService.lookupKeybindings('interactive.execute');136const shiftEnterConfig = this.configurationService.getValue(ReplEditorSettings.executeWithShiftEnter);137const hasEnterChord = (kb: ResolvedKeybinding, modifier: string = '') => {138const chords = kb.getDispatchChords();139const chord = modifier + 'Enter';140const chordAlt = modifier + '[Enter]';141return chords.length === 1 && (chords[0] === chord || chords[0] === chordAlt);142};143144if (shiftEnterConfig) {145const keybinding = keybindings.find(kb => hasEnterChord(kb, 'shift+'));146if (keybinding) {147return keybinding;148}149} else {150let keybinding = keybindings.find(kb => hasEnterChord(kb));151if (keybinding) {152return keybinding;153}154keybinding = this.keybindingService.lookupKeybindings('python.execInREPLEnter')155.find(kb => hasEnterChord(kb));156if (keybinding) {157return keybinding;158}159}160161return keybindings?.[0];162}163164override dispose(): void {165super.dispose();166this.editor.removeContentWidget(this);167this.label?.dispose();168}169}170171172