UNPKG

@21epub/epub-thirdparty

Version:
867 lines (866 loc) 40.9 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); } }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import * as dom from '../../../base/browser/dom.js'; import '../../../base/browser/ui/codicons/codiconStyles.js'; // The codicon symbol styles are defined here and must be loaded import { List } from '../../../base/browser/ui/list/listWidget.js'; import { createCancelablePromise, disposableTimeout, TimeoutTimer } from '../../../base/common/async.js'; import { onUnexpectedError } from '../../../base/common/errors.js'; import { Emitter } from '../../../base/common/event.js'; import { DisposableStore } from '../../../base/common/lifecycle.js'; import { clamp } from '../../../base/common/numbers.js'; import * as strings from '../../../base/common/strings.js'; import './media/suggest.css'; import { EmbeddedCodeEditorWidget } from '../../browser/widget/embeddedCodeEditorWidget.js'; import { SuggestWidgetStatus } from './suggestWidgetStatus.js'; import '../symbolIcons/symbolIcons.js'; // The codicon symbol colors are defined here and must be loaded to get colors import * as nls from '../../../nls.js'; import { IContextKeyService } from '../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; import { IStorageService } from '../../../platform/storage/common/storage.js'; import { activeContrastBorder, editorForeground, editorWidgetBackground, editorWidgetBorder, focusBorder, listFocusHighlightForeground, listHighlightForeground, quickInputListFocusBackground, quickInputListFocusForeground, quickInputListFocusIconForeground, registerColor, textCodeBlockBackground, textLinkActiveForeground, textLinkForeground } from '../../../platform/theme/common/colorRegistry.js'; import { attachListStyler } from '../../../platform/theme/common/styler.js'; import { IThemeService, registerThemingParticipant } from '../../../platform/theme/common/themeService.js'; import { ResizableHTMLElement } from './resizable.js'; import { Context as SuggestContext } from './suggest.js'; import { canExpandCompletionItem, SuggestDetailsOverlay, SuggestDetailsWidget } from './suggestWidgetDetails.js'; import { getAriaId, ItemRenderer } from './suggestWidgetRenderer.js'; /** * Suggest widget colors */ export const editorSuggestWidgetBackground = registerColor('editorSuggestWidget.background', { dark: editorWidgetBackground, light: editorWidgetBackground, hc: editorWidgetBackground }, nls.localize('editorSuggestWidgetBackground', 'Background color of the suggest widget.')); export const editorSuggestWidgetBorder = registerColor('editorSuggestWidget.border', { dark: editorWidgetBorder, light: editorWidgetBorder, hc: editorWidgetBorder }, nls.localize('editorSuggestWidgetBorder', 'Border color of the suggest widget.')); export const editorSuggestWidgetForeground = registerColor('editorSuggestWidget.foreground', { dark: editorForeground, light: editorForeground, hc: editorForeground }, nls.localize('editorSuggestWidgetForeground', 'Foreground color of the suggest widget.')); export const editorSuggestWidgetSelectedForeground = registerColor('editorSuggestWidget.selectedForeground', { dark: quickInputListFocusForeground, light: quickInputListFocusForeground, hc: quickInputListFocusForeground }, nls.localize('editorSuggestWidgetSelectedForeground', 'Foreground color of the selected entry in the suggest widget.')); export const editorSuggestWidgetSelectedIconForeground = registerColor('editorSuggestWidget.selectedIconForeground', { dark: quickInputListFocusIconForeground, light: quickInputListFocusIconForeground, hc: quickInputListFocusIconForeground }, nls.localize('editorSuggestWidgetSelectedIconForeground', 'Icon foreground color of the selected entry in the suggest widget.')); export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: quickInputListFocusBackground, light: quickInputListFocusBackground, hc: quickInputListFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.')); export const editorSuggestWidgetHighlightForeground = registerColor('editorSuggestWidget.highlightForeground', { dark: listHighlightForeground, light: listHighlightForeground, hc: listHighlightForeground }, nls.localize('editorSuggestWidgetHighlightForeground', 'Color of the match highlights in the suggest widget.')); export const editorSuggestWidgetHighlightFocusForeground = registerColor('editorSuggestWidget.focusHighlightForeground', { dark: listFocusHighlightForeground, light: listFocusHighlightForeground, hc: listFocusHighlightForeground }, nls.localize('editorSuggestWidgetFocusHighlightForeground', 'Color of the match highlights in the suggest widget when an item is focused.')); class PersistedWidgetSize { constructor(_service, editor) { this._service = _service; this._key = `suggestWidget.size/${editor.getEditorType()}/${editor instanceof EmbeddedCodeEditorWidget}`; } restore() { var _a; const raw = (_a = this._service.get(this._key, 0 /* GLOBAL */)) !== null && _a !== void 0 ? _a : ''; try { const obj = JSON.parse(raw); if (dom.Dimension.is(obj)) { return dom.Dimension.lift(obj); } } catch (_b) { // ignore } return undefined; } store(size) { this._service.store(this._key, JSON.stringify(size), 0 /* GLOBAL */, 1 /* MACHINE */); } reset() { this._service.remove(this._key, 0 /* GLOBAL */); } } let SuggestWidget = class SuggestWidget { constructor(editor, _storageService, _contextKeyService, _themeService, instantiationService) { this.editor = editor; this._storageService = _storageService; this._state = 0 /* Hidden */; this._isAuto = false; this._ignoreFocusEvents = false; this._forceRenderingAbove = false; this._explainMode = false; this._showTimeout = new TimeoutTimer(); this._disposables = new DisposableStore(); this._onDidSelect = new Emitter(); this._onDidFocus = new Emitter(); this._onDidHide = new Emitter(); this._onDidShow = new Emitter(); this.onDidSelect = this._onDidSelect.event; this.onDidFocus = this._onDidFocus.event; this.onDidHide = this._onDidHide.event; this.onDidShow = this._onDidShow.event; this._onDetailsKeydown = new Emitter(); this.onDetailsKeyDown = this._onDetailsKeydown.event; this.element = new ResizableHTMLElement(); this.element.domNode.classList.add('editor-widget', 'suggest-widget'); this._contentWidget = new SuggestContentWidget(this, editor); this._persistedSize = new PersistedWidgetSize(_storageService, editor); class ResizeState { constructor(persistedSize, currentSize, persistHeight = false, persistWidth = false) { this.persistedSize = persistedSize; this.currentSize = currentSize; this.persistHeight = persistHeight; this.persistWidth = persistWidth; } } let state; this._disposables.add(this.element.onDidWillResize(() => { this._contentWidget.lockPreference(); state = new ResizeState(this._persistedSize.restore(), this.element.size); })); this._disposables.add(this.element.onDidResize(e => { var _a, _b, _c, _d; this._resize(e.dimension.width, e.dimension.height); if (state) { state.persistHeight = state.persistHeight || !!e.north || !!e.south; state.persistWidth = state.persistWidth || !!e.east || !!e.west; } if (!e.done) { return; } if (state) { // only store width or height value that have changed and also // only store changes that are above a certain threshold const { itemHeight, defaultSize } = this.getLayoutInfo(); const threshold = Math.round(itemHeight / 2); let { width, height } = this.element.size; if (!state.persistHeight || Math.abs(state.currentSize.height - height) <= threshold) { height = (_b = (_a = state.persistedSize) === null || _a === void 0 ? void 0 : _a.height) !== null && _b !== void 0 ? _b : defaultSize.height; } if (!state.persistWidth || Math.abs(state.currentSize.width - width) <= threshold) { width = (_d = (_c = state.persistedSize) === null || _c === void 0 ? void 0 : _c.width) !== null && _d !== void 0 ? _d : defaultSize.width; } this._persistedSize.store(new dom.Dimension(width, height)); } // reset working state this._contentWidget.unlockPreference(); state = undefined; })); this._messageElement = dom.append(this.element.domNode, dom.$('.message')); this._listElement = dom.append(this.element.domNode, dom.$('.tree')); const details = instantiationService.createInstance(SuggestDetailsWidget, this.editor); details.onDidClose(this.toggleDetails, this, this._disposables); this._details = new SuggestDetailsOverlay(details, this.editor); const applyIconStyle = () => this.element.domNode.classList.toggle('no-icons', !this.editor.getOption(105 /* suggest */).showIcons); applyIconStyle(); const renderer = instantiationService.createInstance(ItemRenderer, this.editor); this._disposables.add(renderer); this._disposables.add(renderer.onDidToggleDetails(() => this.toggleDetails())); this._list = new List('SuggestWidget', this._listElement, { getHeight: (_element) => this.getLayoutInfo().itemHeight, getTemplateId: (_element) => 'suggestion' }, [renderer], { alwaysConsumeMouseWheel: true, useShadows: false, mouseSupport: false, accessibilityProvider: { getRole: () => 'option', getAriaLabel: (item) => { if (item.isResolved && this._isDetailsVisible()) { const { documentation, detail } = item.completion; const docs = strings.format('{0}{1}', detail || '', documentation ? (typeof documentation === 'string' ? documentation : documentation.value) : ''); return nls.localize('ariaCurrenttSuggestionReadDetails', "{0}, docs: {1}", item.textLabel, docs); } else { return item.textLabel; } }, getWidgetAriaLabel: () => nls.localize('suggest', "Suggest"), getWidgetRole: () => 'listbox' } }); this._status = instantiationService.createInstance(SuggestWidgetStatus, this.element.domNode); const applyStatusBarStyle = () => this.element.domNode.classList.toggle('with-status-bar', this.editor.getOption(105 /* suggest */).showStatusBar); applyStatusBarStyle(); this._disposables.add(attachListStyler(this._list, _themeService, { listInactiveFocusBackground: editorSuggestWidgetSelectedBackground, listInactiveFocusOutline: activeContrastBorder })); this._disposables.add(_themeService.onDidColorThemeChange(t => this._onThemeChange(t))); this._onThemeChange(_themeService.getColorTheme()); this._disposables.add(this._list.onMouseDown(e => this._onListMouseDownOrTap(e))); this._disposables.add(this._list.onTap(e => this._onListMouseDownOrTap(e))); this._disposables.add(this._list.onDidChangeSelection(e => this._onListSelection(e))); this._disposables.add(this._list.onDidChangeFocus(e => this._onListFocus(e))); this._disposables.add(this.editor.onDidChangeCursorSelection(() => this._onCursorSelectionChanged())); this._disposables.add(this.editor.onDidChangeConfiguration(e => { if (e.hasChanged(105 /* suggest */)) { applyStatusBarStyle(); applyIconStyle(); } })); this._ctxSuggestWidgetVisible = SuggestContext.Visible.bindTo(_contextKeyService); this._ctxSuggestWidgetDetailsVisible = SuggestContext.DetailsVisible.bindTo(_contextKeyService); this._ctxSuggestWidgetMultipleSuggestions = SuggestContext.MultipleSuggestions.bindTo(_contextKeyService); this._disposables.add(dom.addStandardDisposableListener(this._details.widget.domNode, 'keydown', e => { this._onDetailsKeydown.fire(e); })); this._disposables.add(this.editor.onMouseDown((e) => this._onEditorMouseDown(e))); } dispose() { var _a; this._details.widget.dispose(); this._details.dispose(); this._list.dispose(); this._status.dispose(); this._disposables.dispose(); (_a = this._loadingTimeout) === null || _a === void 0 ? void 0 : _a.dispose(); this._showTimeout.dispose(); this._contentWidget.dispose(); this.element.dispose(); } _onEditorMouseDown(mouseEvent) { if (this._details.widget.domNode.contains(mouseEvent.target.element)) { // Clicking inside details this._details.widget.domNode.focus(); } else { // Clicking outside details and inside suggest if (this.element.domNode.contains(mouseEvent.target.element)) { this.editor.focus(); } } } _onCursorSelectionChanged() { if (this._state !== 0 /* Hidden */) { this._contentWidget.layout(); } } _onListMouseDownOrTap(e) { if (typeof e.element === 'undefined' || typeof e.index === 'undefined') { return; } // prevent stealing browser focus from the editor e.browserEvent.preventDefault(); e.browserEvent.stopPropagation(); this._select(e.element, e.index); } _onListSelection(e) { if (e.elements.length) { this._select(e.elements[0], e.indexes[0]); } } _select(item, index) { const completionModel = this._completionModel; if (completionModel) { this._onDidSelect.fire({ item, index, model: completionModel }); this.editor.focus(); } } _onThemeChange(theme) { const backgroundColor = theme.getColor(editorSuggestWidgetBackground); if (backgroundColor) { this.element.domNode.style.backgroundColor = backgroundColor.toString(); this._messageElement.style.backgroundColor = backgroundColor.toString(); this._details.widget.domNode.style.backgroundColor = backgroundColor.toString(); } const borderColor = theme.getColor(editorSuggestWidgetBorder); if (borderColor) { this.element.domNode.style.borderColor = borderColor.toString(); this._messageElement.style.borderColor = borderColor.toString(); this._status.element.style.borderTopColor = borderColor.toString(); this._details.widget.domNode.style.borderColor = borderColor.toString(); this._detailsBorderColor = borderColor.toString(); } const focusBorderColor = theme.getColor(focusBorder); if (focusBorderColor) { this._detailsFocusBorderColor = focusBorderColor.toString(); } this._details.widget.borderWidth = theme.type === 'hc' ? 2 : 1; } _onListFocus(e) { var _a; if (this._ignoreFocusEvents) { return; } if (!e.elements.length) { if (this._currentSuggestionDetails) { this._currentSuggestionDetails.cancel(); this._currentSuggestionDetails = undefined; this._focusedItem = undefined; } this.editor.setAriaOptions({ activeDescendant: undefined }); return; } if (!this._completionModel) { return; } const item = e.elements[0]; const index = e.indexes[0]; if (item !== this._focusedItem) { (_a = this._currentSuggestionDetails) === null || _a === void 0 ? void 0 : _a.cancel(); this._currentSuggestionDetails = undefined; this._focusedItem = item; this._list.reveal(index); this._currentSuggestionDetails = createCancelablePromise((token) => __awaiter(this, void 0, void 0, function* () { const loading = disposableTimeout(() => { if (this._isDetailsVisible()) { this.showDetails(true); } }, 250); token.onCancellationRequested(() => loading.dispose()); const result = yield item.resolve(token); loading.dispose(); return result; })); this._currentSuggestionDetails.then(() => { if (index >= this._list.length || item !== this._list.element(index)) { return; } // item can have extra information, so re-render this._ignoreFocusEvents = true; this._list.splice(index, 1, [item]); this._list.setFocus([index]); this._ignoreFocusEvents = false; if (this._isDetailsVisible()) { this.showDetails(false); } else { this.element.domNode.classList.remove('docs-side'); } this.editor.setAriaOptions({ activeDescendant: getAriaId(index) }); }).catch(onUnexpectedError); } // emit an event this._onDidFocus.fire({ item, index, model: this._completionModel }); } _setState(state) { if (this._state === state) { return; } this._state = state; this.element.domNode.classList.toggle('frozen', state === 4 /* Frozen */); this.element.domNode.classList.remove('message'); switch (state) { case 0 /* Hidden */: dom.hide(this._messageElement, this._listElement, this._status.element); this._details.hide(true); this._status.hide(); this._contentWidget.hide(); this._ctxSuggestWidgetVisible.reset(); this._ctxSuggestWidgetMultipleSuggestions.reset(); this._showTimeout.cancel(); this.element.domNode.classList.remove('visible'); this._list.splice(0, this._list.length); this._focusedItem = undefined; this._cappedHeight = undefined; this._explainMode = false; break; case 1 /* Loading */: this.element.domNode.classList.add('message'); this._messageElement.textContent = SuggestWidget.LOADING_MESSAGE; dom.hide(this._listElement, this._status.element); dom.show(this._messageElement); this._details.hide(); this._show(); this._focusedItem = undefined; break; case 2 /* Empty */: this.element.domNode.classList.add('message'); this._messageElement.textContent = SuggestWidget.NO_SUGGESTIONS_MESSAGE; dom.hide(this._listElement, this._status.element); dom.show(this._messageElement); this._details.hide(); this._show(); this._focusedItem = undefined; break; case 3 /* Open */: dom.hide(this._messageElement); dom.show(this._listElement, this._status.element); this._show(); break; case 4 /* Frozen */: dom.hide(this._messageElement); dom.show(this._listElement, this._status.element); this._show(); break; case 5 /* Details */: dom.hide(this._messageElement); dom.show(this._listElement, this._status.element); this._details.show(); this._show(); break; } } _show() { this._status.show(); this._contentWidget.show(); this._layout(this._persistedSize.restore()); this._ctxSuggestWidgetVisible.set(true); this._showTimeout.cancelAndSet(() => { this.element.domNode.classList.add('visible'); this._onDidShow.fire(this); }, 100); } showTriggered(auto, delay) { if (this._state !== 0 /* Hidden */) { return; } this._contentWidget.setPosition(this.editor.getPosition()); this._isAuto = !!auto; if (!this._isAuto) { this._loadingTimeout = disposableTimeout(() => this._setState(1 /* Loading */), delay); } } showSuggestions(completionModel, selectionIndex, isFrozen, isAuto) { var _a, _b; this._contentWidget.setPosition(this.editor.getPosition()); (_a = this._loadingTimeout) === null || _a === void 0 ? void 0 : _a.dispose(); (_b = this._currentSuggestionDetails) === null || _b === void 0 ? void 0 : _b.cancel(); this._currentSuggestionDetails = undefined; if (this._completionModel !== completionModel) { this._completionModel = completionModel; } if (isFrozen && this._state !== 2 /* Empty */ && this._state !== 0 /* Hidden */) { this._setState(4 /* Frozen */); return; } const visibleCount = this._completionModel.items.length; const isEmpty = visibleCount === 0; this._ctxSuggestWidgetMultipleSuggestions.set(visibleCount > 1); if (isEmpty) { this._setState(isAuto ? 0 /* Hidden */ : 2 /* Empty */); this._completionModel = undefined; return; } this._focusedItem = undefined; this._list.splice(0, this._list.length, this._completionModel.items); this._setState(isFrozen ? 4 /* Frozen */ : 3 /* Open */); this._list.reveal(selectionIndex, 0); this._list.setFocus([selectionIndex]); this._layout(this.element.size); // Reset focus border if (this._detailsBorderColor) { this._details.widget.domNode.style.borderColor = this._detailsBorderColor; } } selectNextPage() { switch (this._state) { case 0 /* Hidden */: return false; case 5 /* Details */: this._details.widget.pageDown(); return true; case 1 /* Loading */: return !this._isAuto; default: this._list.focusNextPage(); return true; } } selectNext() { switch (this._state) { case 0 /* Hidden */: return false; case 1 /* Loading */: return !this._isAuto; default: this._list.focusNext(1, true); return true; } } selectLast() { switch (this._state) { case 0 /* Hidden */: return false; case 5 /* Details */: this._details.widget.scrollBottom(); return true; case 1 /* Loading */: return !this._isAuto; default: this._list.focusLast(); return true; } } selectPreviousPage() { switch (this._state) { case 0 /* Hidden */: return false; case 5 /* Details */: this._details.widget.pageUp(); return true; case 1 /* Loading */: return !this._isAuto; default: this._list.focusPreviousPage(); return true; } } selectPrevious() { switch (this._state) { case 0 /* Hidden */: return false; case 1 /* Loading */: return !this._isAuto; default: this._list.focusPrevious(1, true); return false; } } selectFirst() { switch (this._state) { case 0 /* Hidden */: return false; case 5 /* Details */: this._details.widget.scrollTop(); return true; case 1 /* Loading */: return !this._isAuto; default: this._list.focusFirst(); return true; } } getFocusedItem() { if (this._state !== 0 /* Hidden */ && this._state !== 2 /* Empty */ && this._state !== 1 /* Loading */ && this._completionModel) { return { item: this._list.getFocusedElements()[0], index: this._list.getFocus()[0], model: this._completionModel }; } return undefined; } toggleDetailsFocus() { if (this._state === 5 /* Details */) { this._setState(3 /* Open */); if (this._detailsBorderColor) { this._details.widget.domNode.style.borderColor = this._detailsBorderColor; } } else if (this._state === 3 /* Open */ && this._isDetailsVisible()) { this._setState(5 /* Details */); if (this._detailsFocusBorderColor) { this._details.widget.domNode.style.borderColor = this._detailsFocusBorderColor; } } } toggleDetails() { if (this._isDetailsVisible()) { // hide details widget this._ctxSuggestWidgetDetailsVisible.set(false); this._setDetailsVisible(false); this._details.hide(); this.element.domNode.classList.remove('shows-details'); } else if ((canExpandCompletionItem(this._list.getFocusedElements()[0]) || this._explainMode) && (this._state === 3 /* Open */ || this._state === 5 /* Details */ || this._state === 4 /* Frozen */)) { // show details widget (iff possible) this._ctxSuggestWidgetDetailsVisible.set(true); this._setDetailsVisible(true); this.showDetails(false); } } showDetails(loading) { this._details.show(); if (loading) { this._details.widget.renderLoading(); } else { this._details.widget.renderItem(this._list.getFocusedElements()[0], this._explainMode); } this._positionDetails(); this.editor.focus(); this.element.domNode.classList.add('shows-details'); } toggleExplainMode() { if (this._list.getFocusedElements()[0]) { this._explainMode = !this._explainMode; if (!this._isDetailsVisible()) { this.toggleDetails(); } else { this.showDetails(false); } } } resetPersistedSize() { this._persistedSize.reset(); } hideWidget() { var _a; (_a = this._loadingTimeout) === null || _a === void 0 ? void 0 : _a.dispose(); this._setState(0 /* Hidden */); this._onDidHide.fire(this); this.element.clearSashHoverState(); // ensure that a reasonable widget height is persisted so that // accidential "resize-to-single-items" cases aren't happening const dim = this._persistedSize.restore(); const minPersistedHeight = Math.ceil(this.getLayoutInfo().itemHeight * 4.3); if (dim && dim.height < minPersistedHeight) { this._persistedSize.store(dim.with(undefined, minPersistedHeight)); } } isFrozen() { return this._state === 4 /* Frozen */; } _afterRender(position) { if (position === null) { if (this._isDetailsVisible()) { this._details.hide(); //todo@jrieken soft-hide } return; } if (this._state === 2 /* Empty */ || this._state === 1 /* Loading */) { // no special positioning when widget isn't showing list return; } if (this._isDetailsVisible()) { this._details.show(); } this._positionDetails(); } _layout(size) { var _a, _b, _c; if (!this.editor.hasModel()) { return; } if (!this.editor.getDomNode()) { // happens when running tests return; } const bodyBox = dom.getClientArea(document.body); const info = this.getLayoutInfo(); if (!size) { size = info.defaultSize; } let height = size.height; let width = size.width; // status bar this._status.element.style.lineHeight = `${info.itemHeight}px`; if (this._state === 2 /* Empty */ || this._state === 1 /* Loading */) { // showing a message only height = info.itemHeight + info.borderHeight; width = info.defaultSize.width / 2; this.element.enableSashes(false, false, false, false); this.element.minSize = this.element.maxSize = new dom.Dimension(width, height); this._contentWidget.setPreference(2 /* BELOW */); } else { // showing items // width math const maxWidth = bodyBox.width - info.borderHeight - 2 * info.horizontalPadding; if (width > maxWidth) { width = maxWidth; } const preferredWidth = this._completionModel ? this._completionModel.stats.pLabelLen * info.typicalHalfwidthCharacterWidth : width; // height math const fullHeight = info.statusBarHeight + this._list.contentHeight + info.borderHeight; const minHeight = info.itemHeight + info.statusBarHeight; const editorBox = dom.getDomNodePagePosition(this.editor.getDomNode()); const cursorBox = this.editor.getScrolledVisiblePosition(this.editor.getPosition()); const cursorBottom = editorBox.top + cursorBox.top + cursorBox.height; const maxHeightBelow = Math.min(bodyBox.height - cursorBottom - info.verticalPadding, fullHeight); const availableSpaceAbove = editorBox.top + cursorBox.top - info.verticalPadding; const maxHeightAbove = Math.min(availableSpaceAbove, fullHeight); let maxHeight = Math.min(Math.max(maxHeightAbove, maxHeightBelow) + info.borderHeight, fullHeight); if (height === ((_a = this._cappedHeight) === null || _a === void 0 ? void 0 : _a.capped)) { // Restore the old (wanted) height when the current // height is capped to fit height = this._cappedHeight.wanted; } if (height < minHeight) { height = minHeight; } if (height > maxHeight) { height = maxHeight; } const forceRenderingAboveRequiredSpace = 150; if (height > maxHeightBelow || (this._forceRenderingAbove && availableSpaceAbove > forceRenderingAboveRequiredSpace)) { this._contentWidget.setPreference(1 /* ABOVE */); this.element.enableSashes(true, true, false, false); maxHeight = maxHeightAbove; } else { this._contentWidget.setPreference(2 /* BELOW */); this.element.enableSashes(false, true, true, false); maxHeight = maxHeightBelow; } this.element.preferredSize = new dom.Dimension(preferredWidth, info.defaultSize.height); this.element.maxSize = new dom.Dimension(maxWidth, maxHeight); this.element.minSize = new dom.Dimension(220, minHeight); // Know when the height was capped to fit and remember // the wanted height for later. This is required when going // left to widen suggestions. this._cappedHeight = height === fullHeight ? { wanted: (_c = (_b = this._cappedHeight) === null || _b === void 0 ? void 0 : _b.wanted) !== null && _c !== void 0 ? _c : size.height, capped: height } : undefined; } this._resize(width, height); } _resize(width, height) { const { width: maxWidth, height: maxHeight } = this.element.maxSize; width = Math.min(maxWidth, width); height = Math.min(maxHeight, height); const { statusBarHeight } = this.getLayoutInfo(); this._list.layout(height - statusBarHeight, width); this._listElement.style.height = `${height - statusBarHeight}px`; this.element.layout(height, width); this._contentWidget.layout(); this._positionDetails(); } _positionDetails() { if (this._isDetailsVisible()) { this._details.placeAtAnchor(this.element.domNode); } } getLayoutInfo() { const fontInfo = this.editor.getOption(43 /* fontInfo */); const itemHeight = clamp(this.editor.getOption(107 /* suggestLineHeight */) || fontInfo.lineHeight, 8, 1000); const statusBarHeight = !this.editor.getOption(105 /* suggest */).showStatusBar || this._state === 2 /* Empty */ || this._state === 1 /* Loading */ ? 0 : itemHeight; const borderWidth = this._details.widget.borderWidth; const borderHeight = 2 * borderWidth; return { itemHeight, statusBarHeight, borderWidth, borderHeight, typicalHalfwidthCharacterWidth: fontInfo.typicalHalfwidthCharacterWidth, verticalPadding: 22, horizontalPadding: 14, defaultSize: new dom.Dimension(430, statusBarHeight + 12 * itemHeight + borderHeight) }; } _isDetailsVisible() { return this._storageService.getBoolean('expandSuggestionDocs', 0 /* GLOBAL */, false); } _setDetailsVisible(value) { this._storageService.store('expandSuggestionDocs', value, 0 /* GLOBAL */, 0 /* USER */); } forceRenderingAbove() { if (!this._forceRenderingAbove) { this._forceRenderingAbove = true; this._layout(this._persistedSize.restore()); } } stopForceRenderingAbove() { this._forceRenderingAbove = false; } }; SuggestWidget.LOADING_MESSAGE = nls.localize('suggestWidget.loading', "Loading..."); SuggestWidget.NO_SUGGESTIONS_MESSAGE = nls.localize('suggestWidget.noSuggestions', "No suggestions."); SuggestWidget = __decorate([ __param(1, IStorageService), __param(2, IContextKeyService), __param(3, IThemeService), __param(4, IInstantiationService) ], SuggestWidget); export { SuggestWidget }; export class SuggestContentWidget { constructor(_widget, _editor) { this._widget = _widget; this._editor = _editor; this.allowEditorOverflow = true; this.suppressMouseDown = false; this._preferenceLocked = false; this._added = false; this._hidden = false; } dispose() { if (this._added) { this._added = false; this._editor.removeContentWidget(this); } } getId() { return 'editor.widget.suggestWidget'; } getDomNode() { return this._widget.element.domNode; } show() { this._hidden = false; if (!this._added) { this._added = true; this._editor.addContentWidget(this); } } hide() { if (!this._hidden) { this._hidden = true; this.layout(); } } layout() { this._editor.layoutContentWidget(this); } getPosition() { if (this._hidden || !this._position || !this._preference) { return null; } return { position: this._position, preference: [this._preference] }; } beforeRender() { const { height, width } = this._widget.element.size; const { borderWidth, horizontalPadding } = this._widget.getLayoutInfo(); return new dom.Dimension(width + 2 * borderWidth + horizontalPadding, height + 2 * borderWidth); } afterRender(position) { this._widget._afterRender(position); } setPreference(preference) { if (!this._preferenceLocked) { this._preference = preference; } } lockPreference() { this._preferenceLocked = true; } unlockPreference() { this._preferenceLocked = false; } setPosition(position) { this._position = position; } } registerThemingParticipant((theme, collector) => { const matchHighlight = theme.getColor(editorSuggestWidgetHighlightForeground); if (matchHighlight) { collector.addRule(`.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-highlighted-label .highlight { color: ${matchHighlight}; }`); } const matchHighlightFocus = theme.getColor(editorSuggestWidgetHighlightFocusForeground); if (matchHighlight) { collector.addRule(`.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused .monaco-highlighted-label .highlight { color: ${matchHighlightFocus}; }`); } const foreground = theme.getColor(editorSuggestWidgetForeground); if (foreground) { collector.addRule(`.monaco-editor .suggest-widget, .monaco-editor .suggest-details { color: ${foreground}; }`); } const selectedForeground = theme.getColor(editorSuggestWidgetSelectedForeground); if (selectedForeground) { collector.addRule(`.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused { color: ${selectedForeground}; }`); } const selectedIconForeground = theme.getColor(editorSuggestWidgetSelectedIconForeground); if (selectedIconForeground) { collector.addRule(`.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused .codicon { color: ${selectedIconForeground}; }`); } const link = theme.getColor(textLinkForeground); if (link) { collector.addRule(`.monaco-editor .suggest-details a { color: ${link}; }`); } const linkHover = theme.getColor(textLinkActiveForeground); if (linkHover) { collector.addRule(`.monaco-editor .suggest-details a:hover { color: ${linkHover}; }`); } const codeBackground = theme.getColor(textCodeBlockBackground); if (codeBackground) { collector.addRule(`.monaco-editor .suggest-details code { background-color: ${codeBackground}; }`); } });