UNPKG

monaco-editor

Version:
344 lines (343 loc) • 16.5 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 ContextMenuController_1; import * as dom from '../../../../base/browser/dom.js'; import { ActionViewItem } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; import { Separator, SubmenuAction } from '../../../../base/common/actions.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { isIOS } from '../../../../base/common/platform.js'; import { EditorAction, registerEditorAction, registerEditorContribution } from '../../../browser/editorExtensions.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import * as nls from '../../../../nls.js'; import { IMenuService, SubmenuItemAction } from '../../../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService, IContextViewService } from '../../../../platform/contextview/browser/contextView.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IWorkspaceContextService, isStandaloneEditorWorkspace } from '../../../../platform/workspace/common/workspace.js'; let ContextMenuController = class ContextMenuController { static { ContextMenuController_1 = this; } static { this.ID = 'editor.contrib.contextmenu'; } static get(editor) { return editor.getContribution(ContextMenuController_1.ID); } constructor(editor, _contextMenuService, _contextViewService, _contextKeyService, _keybindingService, _menuService, _configurationService, _workspaceContextService) { this._contextMenuService = _contextMenuService; this._contextViewService = _contextViewService; this._contextKeyService = _contextKeyService; this._keybindingService = _keybindingService; this._menuService = _menuService; this._configurationService = _configurationService; this._workspaceContextService = _workspaceContextService; this._toDispose = new DisposableStore(); this._contextMenuIsBeingShownCount = 0; this._editor = editor; this._toDispose.add(this._editor.onContextMenu((e) => this._onContextMenu(e))); this._toDispose.add(this._editor.onMouseWheel((e) => { if (this._contextMenuIsBeingShownCount > 0) { const view = this._contextViewService.getContextViewElement(); const target = e.srcElement; // Event triggers on shadow root host first // Check if the context view is under this host before hiding it #103169 if (!(target.shadowRoot && dom.getShadowRoot(view) === target.shadowRoot)) { this._contextViewService.hideContextView(); } } })); this._toDispose.add(this._editor.onKeyDown((e) => { if (!this._editor.getOption(24 /* EditorOption.contextmenu */)) { return; // Context menu is turned off through configuration } if (e.keyCode === 58 /* KeyCode.ContextMenu */) { // Chrome is funny like that e.preventDefault(); e.stopPropagation(); this.showContextMenu(); } })); } _onContextMenu(e) { if (!this._editor.hasModel()) { return; } if (!this._editor.getOption(24 /* EditorOption.contextmenu */)) { this._editor.focus(); // Ensure the cursor is at the position of the mouse click if (e.target.position && !this._editor.getSelection().containsPosition(e.target.position)) { this._editor.setPosition(e.target.position); } return; // Context menu is turned off through configuration } if (e.target.type === 12 /* MouseTargetType.OVERLAY_WIDGET */) { return; // allow native menu on widgets to support right click on input field for example in find } if (e.target.type === 6 /* MouseTargetType.CONTENT_TEXT */ && e.target.detail.injectedText) { return; // allow native menu on injected text } e.event.preventDefault(); e.event.stopPropagation(); if (e.target.type === 11 /* MouseTargetType.SCROLLBAR */) { return this._showScrollbarContextMenu(e.event); } if (e.target.type !== 6 /* MouseTargetType.CONTENT_TEXT */ && e.target.type !== 7 /* MouseTargetType.CONTENT_EMPTY */ && e.target.type !== 1 /* MouseTargetType.TEXTAREA */) { return; // only support mouse click into text or native context menu key for now } // Ensure the editor gets focus if it hasn't, so the right events are being sent to other contributions this._editor.focus(); // Ensure the cursor is at the position of the mouse click if (e.target.position) { let hasSelectionAtPosition = false; for (const selection of this._editor.getSelections()) { if (selection.containsPosition(e.target.position)) { hasSelectionAtPosition = true; break; } } if (!hasSelectionAtPosition) { this._editor.setPosition(e.target.position); } } // Unless the user triggerd the context menu through Shift+F10, use the mouse position as menu position let anchor = null; if (e.target.type !== 1 /* MouseTargetType.TEXTAREA */) { anchor = e.event; } // Show the context menu this.showContextMenu(anchor); } showContextMenu(anchor) { if (!this._editor.getOption(24 /* EditorOption.contextmenu */)) { return; // Context menu is turned off through configuration } if (!this._editor.hasModel()) { return; } // Find actions available for menu const menuActions = this._getMenuActions(this._editor.getModel(), this._editor.contextMenuId); // Show menu if we have actions to show if (menuActions.length > 0) { this._doShowContextMenu(menuActions, anchor); } } _getMenuActions(model, menuId) { const result = []; // get menu groups const groups = this._menuService.getMenuActions(menuId, this._contextKeyService, { arg: model.uri }); // translate them into other actions for (const group of groups) { const [, actions] = group; let addedItems = 0; for (const action of actions) { if (action instanceof SubmenuItemAction) { const subActions = this._getMenuActions(model, action.item.submenu); if (subActions.length > 0) { result.push(new SubmenuAction(action.id, action.label, subActions)); addedItems++; } } else { result.push(action); addedItems++; } } if (addedItems) { result.push(new Separator()); } } if (result.length) { result.pop(); // remove last separator } return result; } _doShowContextMenu(actions, event = null) { if (!this._editor.hasModel()) { return; } // Disable hover const oldHoverSetting = this._editor.getOption(60 /* EditorOption.hover */); this._editor.updateOptions({ hover: { enabled: false } }); let anchor = event; if (!anchor) { // Ensure selection is visible this._editor.revealPosition(this._editor.getPosition(), 1 /* ScrollType.Immediate */); this._editor.render(); const cursorCoords = this._editor.getScrolledVisiblePosition(this._editor.getPosition()); // Translate to absolute editor position const editorCoords = dom.getDomNodePagePosition(this._editor.getDomNode()); const posx = editorCoords.left + cursorCoords.left; const posy = editorCoords.top + cursorCoords.top + cursorCoords.height; anchor = { x: posx, y: posy }; } const useShadowDOM = this._editor.getOption(128 /* EditorOption.useShadowDOM */) && !isIOS; // Do not use shadow dom on IOS #122035 // Show menu this._contextMenuIsBeingShownCount++; this._contextMenuService.showContextMenu({ domForShadowRoot: useShadowDOM ? this._editor.getOverflowWidgetsDomNode() ?? this._editor.getDomNode() : undefined, getAnchor: () => anchor, getActions: () => actions, getActionViewItem: (action) => { const keybinding = this._keybindingFor(action); if (keybinding) { return new ActionViewItem(action, action, { label: true, keybinding: keybinding.getLabel(), isMenu: true }); } const customActionViewItem = action; if (typeof customActionViewItem.getActionViewItem === 'function') { return customActionViewItem.getActionViewItem(); } return new ActionViewItem(action, action, { icon: true, label: true, isMenu: true }); }, getKeyBinding: (action) => { return this._keybindingFor(action); }, onHide: (wasCancelled) => { this._contextMenuIsBeingShownCount--; this._editor.updateOptions({ hover: oldHoverSetting }); } }); } _showScrollbarContextMenu(anchor) { if (!this._editor.hasModel()) { return; } if (isStandaloneEditorWorkspace(this._workspaceContextService.getWorkspace())) { // can't update the configuration properly in the standalone editor return; } const minimapOptions = this._editor.getOption(73 /* EditorOption.minimap */); let lastId = 0; const createAction = (opts) => { return { id: `menu-action-${++lastId}`, label: opts.label, tooltip: '', class: undefined, enabled: (typeof opts.enabled === 'undefined' ? true : opts.enabled), checked: opts.checked, run: opts.run }; }; const createSubmenuAction = (label, actions) => { return new SubmenuAction(`menu-action-${++lastId}`, label, actions, undefined); }; const createEnumAction = (label, enabled, configName, configuredValue, options) => { if (!enabled) { return createAction({ label, enabled, run: () => { } }); } const createRunner = (value) => { return () => { this._configurationService.updateValue(configName, value); }; }; const actions = []; for (const option of options) { actions.push(createAction({ label: option.label, checked: configuredValue === option.value, run: createRunner(option.value) })); } return createSubmenuAction(label, actions); }; const actions = []; actions.push(createAction({ label: nls.localize('context.minimap.minimap', "Minimap"), checked: minimapOptions.enabled, run: () => { this._configurationService.updateValue(`editor.minimap.enabled`, !minimapOptions.enabled); } })); actions.push(new Separator()); actions.push(createAction({ label: nls.localize('context.minimap.renderCharacters', "Render Characters"), enabled: minimapOptions.enabled, checked: minimapOptions.renderCharacters, run: () => { this._configurationService.updateValue(`editor.minimap.renderCharacters`, !minimapOptions.renderCharacters); } })); actions.push(createEnumAction(nls.localize('context.minimap.size', "Vertical size"), minimapOptions.enabled, 'editor.minimap.size', minimapOptions.size, [{ label: nls.localize('context.minimap.size.proportional', "Proportional"), value: 'proportional' }, { label: nls.localize('context.minimap.size.fill', "Fill"), value: 'fill' }, { label: nls.localize('context.minimap.size.fit', "Fit"), value: 'fit' }])); actions.push(createEnumAction(nls.localize('context.minimap.slider', "Slider"), minimapOptions.enabled, 'editor.minimap.showSlider', minimapOptions.showSlider, [{ label: nls.localize('context.minimap.slider.mouseover', "Mouse Over"), value: 'mouseover' }, { label: nls.localize('context.minimap.slider.always', "Always"), value: 'always' }])); const useShadowDOM = this._editor.getOption(128 /* EditorOption.useShadowDOM */) && !isIOS; // Do not use shadow dom on IOS #122035 this._contextMenuIsBeingShownCount++; this._contextMenuService.showContextMenu({ domForShadowRoot: useShadowDOM ? this._editor.getDomNode() : undefined, getAnchor: () => anchor, getActions: () => actions, onHide: (wasCancelled) => { this._contextMenuIsBeingShownCount--; this._editor.focus(); } }); } _keybindingFor(action) { return this._keybindingService.lookupKeybinding(action.id); } dispose() { if (this._contextMenuIsBeingShownCount > 0) { this._contextViewService.hideContextView(); } this._toDispose.dispose(); } }; ContextMenuController = ContextMenuController_1 = __decorate([ __param(1, IContextMenuService), __param(2, IContextViewService), __param(3, IContextKeyService), __param(4, IKeybindingService), __param(5, IMenuService), __param(6, IConfigurationService), __param(7, IWorkspaceContextService) ], ContextMenuController); export { ContextMenuController }; class ShowContextMenu extends EditorAction { constructor() { super({ id: 'editor.action.showContextMenu', label: nls.localize('action.showContextMenu.label', "Show Editor Context Menu"), alias: 'Show Editor Context Menu', precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: 1024 /* KeyMod.Shift */ | 68 /* KeyCode.F10 */, weight: 100 /* KeybindingWeight.EditorContrib */ } }); } run(accessor, editor) { ContextMenuController.get(editor)?.showContextMenu(); } } registerEditorContribution(ContextMenuController.ID, ContextMenuController, 2 /* EditorContributionInstantiation.BeforeFirstInteraction */); registerEditorAction(ShowContextMenu);