UNPKG

monaco-editor-core

Version:

A browser based code editor

942 lines (941 loc) • 37.3 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; import * as dom from '../../../base/browser/dom.js'; import { StandardKeyboardEvent } from '../../../base/browser/keyboardEvent.js'; import { Toggle } from '../../../base/browser/ui/toggle/toggle.js'; import { equals } from '../../../base/common/arrays.js'; import { TimeoutTimer } from '../../../base/common/async.js'; import { Codicon } from '../../../base/common/codicons.js'; import { Emitter, EventBufferer } from '../../../base/common/event.js'; import { Disposable, DisposableStore } from '../../../base/common/lifecycle.js'; import { isIOS } from '../../../base/common/platform.js'; import Severity from '../../../base/common/severity.js'; import { ThemeIcon } from '../../../base/common/themables.js'; import './media/quickInput.css'; import { localize } from '../../../nls.js'; import { ItemActivation, NO_KEY_MODS, QuickInputButtonLocation, QuickInputHideReason, QuickPickFocus } from '../common/quickInput.js'; import { quickInputButtonToAction, renderQuickInputDescription } from './quickInputUtils.js'; import { IConfigurationService } from '../../configuration/common/configuration.js'; import { IHoverService, WorkbenchHoverDelegate } from '../../hover/browser/hover.js'; import { ContextKeyExpr, RawContextKey } from '../../contextkey/common/contextkey.js'; export const inQuickInputContextKeyValue = 'inQuickInput'; export const InQuickInputContextKey = new RawContextKey(inQuickInputContextKeyValue, false, localize('inQuickInput', "Whether keyboard focus is inside the quick input control")); export const inQuickInputContext = ContextKeyExpr.has(inQuickInputContextKeyValue); export const quickInputTypeContextKeyValue = 'quickInputType'; export const QuickInputTypeContextKey = new RawContextKey(quickInputTypeContextKeyValue, undefined, localize('quickInputType', "The type of the currently visible quick input")); export const endOfQuickInputBoxContextKeyValue = 'cursorAtEndOfQuickInputBox'; export const EndOfQuickInputBoxContextKey = new RawContextKey(endOfQuickInputBoxContextKeyValue, false, localize('cursorAtEndOfQuickInputBox', "Whether the cursor in the quick input is at the end of the input box")); export const endOfQuickInputBoxContext = ContextKeyExpr.has(endOfQuickInputBoxContextKeyValue); export const backButton = { iconClass: ThemeIcon.asClassName(Codicon.quickInputBack), tooltip: localize('quickInput.back', "Back"), handle: -1 // TODO }; class QuickInput extends Disposable { static { this.noPromptMessage = localize('inputModeEntry', "Press 'Enter' to confirm your input or 'Escape' to cancel"); } constructor(ui) { super(); this.ui = ui; this._widgetUpdated = false; this.visible = false; this._enabled = true; this._busy = false; this._ignoreFocusOut = false; this._leftButtons = []; this._rightButtons = []; this._inlineButtons = []; this.buttonsUpdated = false; this._toggles = []; this.togglesUpdated = false; this.noValidationMessage = QuickInput.noPromptMessage; this._severity = Severity.Ignore; this.onDidTriggerButtonEmitter = this._register(new Emitter()); this.onDidHideEmitter = this._register(new Emitter()); this.onWillHideEmitter = this._register(new Emitter()); this.onDisposeEmitter = this._register(new Emitter()); this.visibleDisposables = this._register(new DisposableStore()); this.onDidHide = this.onDidHideEmitter.event; } get title() { return this._title; } set title(title) { this._title = title; this.update(); } get description() { return this._description; } set description(description) { this._description = description; this.update(); } get step() { return this._steps; } set step(step) { this._steps = step; this.update(); } get totalSteps() { return this._totalSteps; } set totalSteps(totalSteps) { this._totalSteps = totalSteps; this.update(); } get enabled() { return this._enabled; } set enabled(enabled) { this._enabled = enabled; this.update(); } get contextKey() { return this._contextKey; } set contextKey(contextKey) { this._contextKey = contextKey; this.update(); } get busy() { return this._busy; } set busy(busy) { this._busy = busy; this.update(); } get ignoreFocusOut() { return this._ignoreFocusOut; } set ignoreFocusOut(ignoreFocusOut) { const shouldUpdate = this._ignoreFocusOut !== ignoreFocusOut && !isIOS; this._ignoreFocusOut = ignoreFocusOut && !isIOS; if (shouldUpdate) { this.update(); } } get titleButtons() { return this._leftButtons.length ? [...this._leftButtons, this._rightButtons] : this._rightButtons; } get buttons() { return [ ...this._leftButtons, ...this._rightButtons, ...this._inlineButtons ]; } set buttons(buttons) { this._leftButtons = buttons.filter(b => b === backButton); this._rightButtons = buttons.filter(b => b !== backButton && b.location !== QuickInputButtonLocation.Inline); this._inlineButtons = buttons.filter(b => b.location === QuickInputButtonLocation.Inline); this.buttonsUpdated = true; this.update(); } get toggles() { return this._toggles; } set toggles(toggles) { this._toggles = toggles ?? []; this.togglesUpdated = true; this.update(); } get validationMessage() { return this._validationMessage; } set validationMessage(validationMessage) { this._validationMessage = validationMessage; this.update(); } get severity() { return this._severity; } set severity(severity) { this._severity = severity; this.update(); } show() { if (this.visible) { return; } this.visibleDisposables.add(this.ui.onDidTriggerButton(button => { if (this.buttons.indexOf(button) !== -1) { this.onDidTriggerButtonEmitter.fire(button); } })); this.ui.show(this); // update properties in the controller that get reset in the ui.show() call this.visible = true; // This ensures the message/prompt gets rendered this._lastValidationMessage = undefined; // This ensures the input box has the right severity applied this._lastSeverity = undefined; if (this.buttons.length) { // if there are buttons, the ui.show() clears them out of the UI so we should // rerender them. this.buttonsUpdated = true; } if (this.toggles.length) { // if there are toggles, the ui.show() clears them out of the UI so we should // rerender them. this.togglesUpdated = true; } this.update(); } hide() { if (!this.visible) { return; } this.ui.hide(); } didHide(reason = QuickInputHideReason.Other) { this.visible = false; this.visibleDisposables.clear(); this.onDidHideEmitter.fire({ reason }); } willHide(reason = QuickInputHideReason.Other) { this.onWillHideEmitter.fire({ reason }); } update() { if (!this.visible) { return; } const title = this.getTitle(); if (title && this.ui.title.textContent !== title) { this.ui.title.textContent = title; } else if (!title && this.ui.title.innerHTML !== '&nbsp;') { this.ui.title.innerText = '\u00a0'; } const description = this.getDescription(); if (this.ui.description1.textContent !== description) { this.ui.description1.textContent = description; } if (this.ui.description2.textContent !== description) { this.ui.description2.textContent = description; } if (this._widgetUpdated) { this._widgetUpdated = false; if (this._widget) { dom.reset(this.ui.widget, this._widget); } else { dom.reset(this.ui.widget); } } if (this.busy && !this.busyDelay) { this.busyDelay = new TimeoutTimer(); this.busyDelay.setIfNotSet(() => { if (this.visible) { this.ui.progressBar.infinite(); } }, 800); } if (!this.busy && this.busyDelay) { this.ui.progressBar.stop(); this.busyDelay.cancel(); this.busyDelay = undefined; } if (this.buttonsUpdated) { this.buttonsUpdated = false; this.ui.leftActionBar.clear(); const leftButtons = this._leftButtons .map((button, index) => quickInputButtonToAction(button, `id-${index}`, async () => this.onDidTriggerButtonEmitter.fire(button))); this.ui.leftActionBar.push(leftButtons, { icon: true, label: false }); this.ui.rightActionBar.clear(); const rightButtons = this._rightButtons .map((button, index) => quickInputButtonToAction(button, `id-${index}`, async () => this.onDidTriggerButtonEmitter.fire(button))); this.ui.rightActionBar.push(rightButtons, { icon: true, label: false }); this.ui.inlineActionBar.clear(); const inlineButtons = this._inlineButtons .map((button, index) => quickInputButtonToAction(button, `id-${index}`, async () => this.onDidTriggerButtonEmitter.fire(button))); this.ui.inlineActionBar.push(inlineButtons, { icon: true, label: false }); } if (this.togglesUpdated) { this.togglesUpdated = false; // HACK: Filter out toggles here that are not concrete Toggle objects. This is to workaround // a layering issue as quick input's interface is in common but Toggle is in browser and // it requires a HTMLElement on its interface const concreteToggles = this.toggles?.filter(opts => opts instanceof Toggle) ?? []; this.ui.inputBox.toggles = concreteToggles; } this.ui.ignoreFocusOut = this.ignoreFocusOut; this.ui.setEnabled(this.enabled); this.ui.setContextKey(this.contextKey); const validationMessage = this.validationMessage || this.noValidationMessage; if (this._lastValidationMessage !== validationMessage) { this._lastValidationMessage = validationMessage; dom.reset(this.ui.message); renderQuickInputDescription(validationMessage, this.ui.message, { callback: (content) => { this.ui.linkOpenerDelegate(content); }, disposables: this.visibleDisposables, }); } if (this._lastSeverity !== this.severity) { this._lastSeverity = this.severity; this.showMessageDecoration(this.severity); } } getTitle() { if (this.title && this.step) { return `${this.title} (${this.getSteps()})`; } if (this.title) { return this.title; } if (this.step) { return this.getSteps(); } return ''; } getDescription() { return this.description || ''; } getSteps() { if (this.step && this.totalSteps) { return localize('quickInput.steps', "{0}/{1}", this.step, this.totalSteps); } if (this.step) { return String(this.step); } return ''; } showMessageDecoration(severity) { this.ui.inputBox.showDecoration(severity); if (severity !== Severity.Ignore) { const styles = this.ui.inputBox.stylesForType(severity); this.ui.message.style.color = styles.foreground ? `${styles.foreground}` : ''; this.ui.message.style.backgroundColor = styles.background ? `${styles.background}` : ''; this.ui.message.style.border = styles.border ? `1px solid ${styles.border}` : ''; this.ui.message.style.marginBottom = '-2px'; } else { this.ui.message.style.color = ''; this.ui.message.style.backgroundColor = ''; this.ui.message.style.border = ''; this.ui.message.style.marginBottom = ''; } } dispose() { this.hide(); this.onDisposeEmitter.fire(); super.dispose(); } } export class QuickPick extends QuickInput { constructor() { super(...arguments); this._value = ''; this.onDidChangeValueEmitter = this._register(new Emitter()); this.onWillAcceptEmitter = this._register(new Emitter()); this.onDidAcceptEmitter = this._register(new Emitter()); this.onDidCustomEmitter = this._register(new Emitter()); this._items = []; this.itemsUpdated = false; this._canSelectMany = false; this._canAcceptInBackground = false; this._matchOnDescription = false; this._matchOnDetail = false; this._matchOnLabel = true; this._matchOnLabelMode = 'fuzzy'; this._sortByLabel = true; this._keepScrollPosition = false; this._itemActivation = ItemActivation.FIRST; this._activeItems = []; this.activeItemsUpdated = false; this.activeItemsToConfirm = []; this.onDidChangeActiveEmitter = this._register(new Emitter()); this._selectedItems = []; this.selectedItemsUpdated = false; this.selectedItemsToConfirm = []; this.onDidChangeSelectionEmitter = this._register(new Emitter()); this.onDidTriggerItemButtonEmitter = this._register(new Emitter()); this.onDidTriggerSeparatorButtonEmitter = this._register(new Emitter()); this.valueSelectionUpdated = true; this._ok = 'default'; this._customButton = false; this._focusEventBufferer = new EventBufferer(); this.type = "quickPick" /* QuickInputType.QuickPick */; this.filterValue = (value) => value; this.onDidChangeValue = this.onDidChangeValueEmitter.event; this.onWillAccept = this.onWillAcceptEmitter.event; this.onDidAccept = this.onDidAcceptEmitter.event; this.onDidChangeActive = this.onDidChangeActiveEmitter.event; this.onDidChangeSelection = this.onDidChangeSelectionEmitter.event; this.onDidTriggerItemButton = this.onDidTriggerItemButtonEmitter.event; this.onDidTriggerSeparatorButton = this.onDidTriggerSeparatorButtonEmitter.event; } static { this.DEFAULT_ARIA_LABEL = localize('quickInputBox.ariaLabel', "Type to narrow down results."); } get quickNavigate() { return this._quickNavigate; } set quickNavigate(quickNavigate) { this._quickNavigate = quickNavigate; this.update(); } get value() { return this._value; } set value(value) { this.doSetValue(value); } doSetValue(value, skipUpdate) { if (this._value !== value) { this._value = value; if (!skipUpdate) { this.update(); } if (this.visible) { const didFilter = this.ui.list.filter(this.filterValue(this._value)); if (didFilter) { this.trySelectFirst(); } } this.onDidChangeValueEmitter.fire(this._value); } } set ariaLabel(ariaLabel) { this._ariaLabel = ariaLabel; this.update(); } get ariaLabel() { return this._ariaLabel; } get placeholder() { return this._placeholder; } set placeholder(placeholder) { this._placeholder = placeholder; this.update(); } get items() { return this._items; } get scrollTop() { return this.ui.list.scrollTop; } set scrollTop(scrollTop) { this.ui.list.scrollTop = scrollTop; } set items(items) { this._items = items; this.itemsUpdated = true; this.update(); } get canSelectMany() { return this._canSelectMany; } set canSelectMany(canSelectMany) { this._canSelectMany = canSelectMany; this.update(); } get canAcceptInBackground() { return this._canAcceptInBackground; } set canAcceptInBackground(canAcceptInBackground) { this._canAcceptInBackground = canAcceptInBackground; } get matchOnDescription() { return this._matchOnDescription; } set matchOnDescription(matchOnDescription) { this._matchOnDescription = matchOnDescription; this.update(); } get matchOnDetail() { return this._matchOnDetail; } set matchOnDetail(matchOnDetail) { this._matchOnDetail = matchOnDetail; this.update(); } get matchOnLabel() { return this._matchOnLabel; } set matchOnLabel(matchOnLabel) { this._matchOnLabel = matchOnLabel; this.update(); } get matchOnLabelMode() { return this._matchOnLabelMode; } set matchOnLabelMode(matchOnLabelMode) { this._matchOnLabelMode = matchOnLabelMode; this.update(); } get sortByLabel() { return this._sortByLabel; } set sortByLabel(sortByLabel) { this._sortByLabel = sortByLabel; this.update(); } get keepScrollPosition() { return this._keepScrollPosition; } set keepScrollPosition(keepScrollPosition) { this._keepScrollPosition = keepScrollPosition; } get itemActivation() { return this._itemActivation; } set itemActivation(itemActivation) { this._itemActivation = itemActivation; } get activeItems() { return this._activeItems; } set activeItems(activeItems) { this._activeItems = activeItems; this.activeItemsUpdated = true; this.update(); } get selectedItems() { return this._selectedItems; } set selectedItems(selectedItems) { this._selectedItems = selectedItems; this.selectedItemsUpdated = true; this.update(); } get keyMods() { if (this._quickNavigate) { // Disable keyMods when quick navigate is enabled // because in this model the interaction is purely // keyboard driven and Ctrl/Alt are typically // pressed and hold during this interaction. return NO_KEY_MODS; } return this.ui.keyMods; } get valueSelection() { const selection = this.ui.inputBox.getSelection(); if (!selection) { return undefined; } return [selection.start, selection.end]; } set valueSelection(valueSelection) { this._valueSelection = valueSelection; this.valueSelectionUpdated = true; this.update(); } get customButton() { return this._customButton; } set customButton(showCustomButton) { this._customButton = showCustomButton; this.update(); } get customLabel() { return this._customButtonLabel; } set customLabel(label) { this._customButtonLabel = label; this.update(); } get customHover() { return this._customButtonHover; } set customHover(hover) { this._customButtonHover = hover; this.update(); } get ok() { return this._ok; } set ok(showOkButton) { this._ok = showOkButton; this.update(); } get hideInput() { return !!this._hideInput; } set hideInput(hideInput) { this._hideInput = hideInput; this.update(); } trySelectFirst() { if (!this.canSelectMany) { this.ui.list.focus(QuickPickFocus.First); } } show() { if (!this.visible) { this.visibleDisposables.add(this.ui.inputBox.onDidChange(value => { this.doSetValue(value, true /* skip update since this originates from the UI */); })); this.visibleDisposables.add(this.ui.onDidAccept(() => { if (this.canSelectMany) { // if there are no checked elements, it means that an onDidChangeSelection never fired to overwrite // `_selectedItems`. In that case, we should emit one with an empty array to ensure that // `.selectedItems` is up to date. if (!this.ui.list.getCheckedElements().length) { this._selectedItems = []; this.onDidChangeSelectionEmitter.fire(this.selectedItems); } } else if (this.activeItems[0]) { // For single-select, we set `selectedItems` to the item that was accepted. this._selectedItems = [this.activeItems[0]]; this.onDidChangeSelectionEmitter.fire(this.selectedItems); } this.handleAccept(false); })); this.visibleDisposables.add(this.ui.onDidCustom(() => { this.onDidCustomEmitter.fire(); })); this.visibleDisposables.add(this._focusEventBufferer.wrapEvent(this.ui.list.onDidChangeFocus, // Only fire the last event (_, e) => e)(focusedItems => { if (this.activeItemsUpdated) { return; // Expect another event. } if (this.activeItemsToConfirm !== this._activeItems && equals(focusedItems, this._activeItems, (a, b) => a === b)) { return; } this._activeItems = focusedItems; this.onDidChangeActiveEmitter.fire(focusedItems); })); this.visibleDisposables.add(this.ui.list.onDidChangeSelection(({ items: selectedItems, event }) => { if (this.canSelectMany) { if (selectedItems.length) { this.ui.list.setSelectedElements([]); } return; } if (this.selectedItemsToConfirm !== this._selectedItems && equals(selectedItems, this._selectedItems, (a, b) => a === b)) { return; } this._selectedItems = selectedItems; this.onDidChangeSelectionEmitter.fire(selectedItems); if (selectedItems.length) { this.handleAccept(dom.isMouseEvent(event) && event.button === 1 /* mouse middle click */); } })); this.visibleDisposables.add(this.ui.list.onChangedCheckedElements(checkedItems => { if (!this.canSelectMany || !this.visible) { return; } if (this.selectedItemsToConfirm !== this._selectedItems && equals(checkedItems, this._selectedItems, (a, b) => a === b)) { return; } this._selectedItems = checkedItems; this.onDidChangeSelectionEmitter.fire(checkedItems); })); this.visibleDisposables.add(this.ui.list.onButtonTriggered(event => this.onDidTriggerItemButtonEmitter.fire(event))); this.visibleDisposables.add(this.ui.list.onSeparatorButtonTriggered(event => this.onDidTriggerSeparatorButtonEmitter.fire(event))); this.visibleDisposables.add(this.registerQuickNavigation()); this.valueSelectionUpdated = true; } super.show(); // TODO: Why have show() bubble up while update() trickles down? } handleAccept(inBackground) { // Figure out veto via `onWillAccept` event let veto = false; this.onWillAcceptEmitter.fire({ veto: () => veto = true }); // Continue with `onDidAccept` if no veto if (!veto) { this.onDidAcceptEmitter.fire({ inBackground }); } } registerQuickNavigation() { return dom.addDisposableListener(this.ui.container, dom.EventType.KEY_UP, e => { if (this.canSelectMany || !this._quickNavigate) { return; } const keyboardEvent = new StandardKeyboardEvent(e); const keyCode = keyboardEvent.keyCode; // Select element when keys are pressed that signal it const quickNavKeys = this._quickNavigate.keybindings; const wasTriggerKeyPressed = quickNavKeys.some(k => { const chords = k.getChords(); if (chords.length > 1) { return false; } if (chords[0].shiftKey && keyCode === 4 /* KeyCode.Shift */) { if (keyboardEvent.ctrlKey || keyboardEvent.altKey || keyboardEvent.metaKey) { return false; // this is an optimistic check for the shift key being used to navigate back in quick input } return true; } if (chords[0].altKey && keyCode === 6 /* KeyCode.Alt */) { return true; } if (chords[0].ctrlKey && keyCode === 5 /* KeyCode.Ctrl */) { return true; } if (chords[0].metaKey && keyCode === 57 /* KeyCode.Meta */) { return true; } return false; }); if (wasTriggerKeyPressed) { if (this.activeItems[0]) { this._selectedItems = [this.activeItems[0]]; this.onDidChangeSelectionEmitter.fire(this.selectedItems); this.handleAccept(false); } // Unset quick navigate after press. It is only valid once // and should not result in any behaviour change afterwards // if the picker remains open because there was no active item this._quickNavigate = undefined; } }); } update() { if (!this.visible) { return; } // store the scrollTop before it is reset const scrollTopBefore = this.keepScrollPosition ? this.scrollTop : 0; const hasDescription = !!this.description; const visibilities = { title: !!this.title || !!this.step || !!this.titleButtons.length, description: hasDescription, checkAll: this.canSelectMany && !this._hideCheckAll, checkBox: this.canSelectMany, inputBox: !this._hideInput, progressBar: !this._hideInput || hasDescription, visibleCount: true, count: this.canSelectMany && !this._hideCountBadge, ok: this.ok === 'default' ? this.canSelectMany : this.ok, list: true, message: !!this.validationMessage, customButton: this.customButton }; this.ui.setVisibilities(visibilities); super.update(); if (this.ui.inputBox.value !== this.value) { this.ui.inputBox.value = this.value; } if (this.valueSelectionUpdated) { this.valueSelectionUpdated = false; this.ui.inputBox.select(this._valueSelection && { start: this._valueSelection[0], end: this._valueSelection[1] }); } if (this.ui.inputBox.placeholder !== (this.placeholder || '')) { this.ui.inputBox.placeholder = (this.placeholder || ''); } let ariaLabel = this.ariaLabel; // Only set aria label to the input box placeholder if we actually have an input box. if (!ariaLabel && visibilities.inputBox) { ariaLabel = this.placeholder || QuickPick.DEFAULT_ARIA_LABEL; // If we have a title, include it in the aria label. if (this.title) { ariaLabel += ` - ${this.title}`; } } if (this.ui.list.ariaLabel !== ariaLabel) { this.ui.list.ariaLabel = ariaLabel ?? null; } this.ui.list.matchOnDescription = this.matchOnDescription; this.ui.list.matchOnDetail = this.matchOnDetail; this.ui.list.matchOnLabel = this.matchOnLabel; this.ui.list.matchOnLabelMode = this.matchOnLabelMode; this.ui.list.sortByLabel = this.sortByLabel; if (this.itemsUpdated) { this.itemsUpdated = false; this._focusEventBufferer.bufferEvents(() => { this.ui.list.setElements(this.items); // We want focus to exist in the list if there are items so that space can be used to toggle this.ui.list.shouldLoop = !this.canSelectMany; this.ui.list.filter(this.filterValue(this.ui.inputBox.value)); switch (this._itemActivation) { case ItemActivation.NONE: this._itemActivation = ItemActivation.FIRST; // only valid once, then unset break; case ItemActivation.SECOND: this.ui.list.focus(QuickPickFocus.Second); this._itemActivation = ItemActivation.FIRST; // only valid once, then unset break; case ItemActivation.LAST: this.ui.list.focus(QuickPickFocus.Last); this._itemActivation = ItemActivation.FIRST; // only valid once, then unset break; default: this.trySelectFirst(); break; } }); } if (this.ui.container.classList.contains('show-checkboxes') !== !!this.canSelectMany) { if (this.canSelectMany) { this.ui.list.clearFocus(); } else { this.trySelectFirst(); } } if (this.activeItemsUpdated) { this.activeItemsUpdated = false; this.activeItemsToConfirm = this._activeItems; this.ui.list.setFocusedElements(this.activeItems); if (this.activeItemsToConfirm === this._activeItems) { this.activeItemsToConfirm = null; } } if (this.selectedItemsUpdated) { this.selectedItemsUpdated = false; this.selectedItemsToConfirm = this._selectedItems; if (this.canSelectMany) { this.ui.list.setCheckedElements(this.selectedItems); } else { this.ui.list.setSelectedElements(this.selectedItems); } if (this.selectedItemsToConfirm === this._selectedItems) { this.selectedItemsToConfirm = null; } } this.ui.customButton.label = this.customLabel || ''; this.ui.customButton.element.title = this.customHover || ''; if (!visibilities.inputBox) { // we need to move focus into the tree to detect keybindings // properly when the input box is not visible (quick nav) this.ui.list.domFocus(); // Focus the first element in the list if multiselect is enabled if (this.canSelectMany) { this.ui.list.focus(QuickPickFocus.First); } } // Set the scroll position to what it was before updating the items if (this.keepScrollPosition) { this.scrollTop = scrollTopBefore; } } focus(focus) { this.ui.list.focus(focus); // To allow things like space to check/uncheck items if (this.canSelectMany) { this.ui.list.domFocus(); } } accept(inBackground) { if (inBackground && !this._canAcceptInBackground) { return; // needs to be enabled } if (this.activeItems[0]) { this._selectedItems = [this.activeItems[0]]; this.onDidChangeSelectionEmitter.fire(this.selectedItems); this.handleAccept(inBackground ?? false); } } } export class InputBox extends QuickInput { constructor() { super(...arguments); this._value = ''; this.valueSelectionUpdated = true; this._password = false; this.onDidValueChangeEmitter = this._register(new Emitter()); this.onDidAcceptEmitter = this._register(new Emitter()); this.type = "inputBox" /* QuickInputType.InputBox */; this.onDidChangeValue = this.onDidValueChangeEmitter.event; this.onDidAccept = this.onDidAcceptEmitter.event; } get value() { return this._value; } set value(value) { this._value = value || ''; this.update(); } get placeholder() { return this._placeholder; } set placeholder(placeholder) { this._placeholder = placeholder; this.update(); } get password() { return this._password; } set password(password) { this._password = password; this.update(); } show() { if (!this.visible) { this.visibleDisposables.add(this.ui.inputBox.onDidChange(value => { if (value === this.value) { return; } this._value = value; this.onDidValueChangeEmitter.fire(value); })); this.visibleDisposables.add(this.ui.onDidAccept(() => this.onDidAcceptEmitter.fire())); this.valueSelectionUpdated = true; } super.show(); } update() { if (!this.visible) { return; } this.ui.container.classList.remove('hidden-input'); const visibilities = { title: !!this.title || !!this.step || !!this.titleButtons.length, description: !!this.description || !!this.step, inputBox: true, message: true, progressBar: true }; this.ui.setVisibilities(visibilities); super.update(); if (this.ui.inputBox.value !== this.value) { this.ui.inputBox.value = this.value; } if (this.valueSelectionUpdated) { this.valueSelectionUpdated = false; this.ui.inputBox.select(this._valueSelection && { start: this._valueSelection[0], end: this._valueSelection[1] }); } if (this.ui.inputBox.placeholder !== (this.placeholder || '')) { this.ui.inputBox.placeholder = (this.placeholder || ''); } if (this.ui.inputBox.password !== this.password) { this.ui.inputBox.password = this.password; } } } let QuickInputHoverDelegate = class QuickInputHoverDelegate extends WorkbenchHoverDelegate { constructor(configurationService, hoverService) { super('element', false, (options) => this.getOverrideOptions(options), configurationService, hoverService); } getOverrideOptions(options) { // Only show the hover hint if the content is of a decent size const showHoverHint = (dom.isHTMLElement(options.content) ? options.content.textContent ?? '' : typeof options.content === 'string' ? options.content : options.content.value).includes('\n'); return { persistence: { hideOnKeyDown: false, }, appearance: { showHoverHint, skipFadeInAnimation: true, }, }; } }; QuickInputHoverDelegate = __decorate([ __param(0, IConfigurationService), __param(1, IHoverService) ], QuickInputHoverDelegate); export { QuickInputHoverDelegate };