UNPKG

monaco-editor-core

Version:

A browser based code editor

240 lines (239 loc) • 12.2 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as browser from '../../../../base/browser/browser.js'; import { getActiveDocument } from '../../../../base/browser/dom.js'; import * as platform from '../../../../base/common/platform.js'; import { CopyOptions, InMemoryClipboardMetadataManager } from '../../../browser/controller/textAreaInput.js'; import { EditorAction, MultiCommand, registerEditorAction } from '../../../browser/editorExtensions.js'; import { ICodeEditorService } from '../../../browser/services/codeEditorService.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import { CopyPasteController } from '../../dropOrPasteInto/browser/copyPasteController.js'; import * as nls from '../../../../nls.js'; import { MenuId, MenuRegistry } from '../../../../platform/actions/common/actions.js'; import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste'; const supportsCut = (platform.isNative || document.queryCommandSupported('cut')); const supportsCopy = (platform.isNative || document.queryCommandSupported('copy')); // Firefox only supports navigator.clipboard.readText() in browser extensions. // See https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/readText#Browser_compatibility // When loading over http, navigator.clipboard can be undefined. See https://github.com/microsoft/monaco-editor/issues/2313 const supportsPaste = (typeof navigator.clipboard === 'undefined' || browser.isFirefox) ? document.queryCommandSupported('paste') : true; function registerCommand(command) { command.register(); return command; } export const CutAction = supportsCut ? registerCommand(new MultiCommand({ id: 'editor.action.clipboardCutAction', precondition: undefined, kbOpts: ( // Do not bind cut keybindings in the browser, // since browsers do that for us and it avoids security prompts platform.isNative ? { primary: 2048 /* KeyMod.CtrlCmd */ | 54 /* KeyCode.KeyX */, win: { primary: 2048 /* KeyMod.CtrlCmd */ | 54 /* KeyCode.KeyX */, secondary: [1024 /* KeyMod.Shift */ | 20 /* KeyCode.Delete */] }, weight: 100 /* KeybindingWeight.EditorContrib */ } : undefined), menuOpts: [{ menuId: MenuId.MenubarEditMenu, group: '2_ccp', title: nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"), order: 1 }, { menuId: MenuId.EditorContext, group: CLIPBOARD_CONTEXT_MENU_GROUP, title: nls.localize('actions.clipboard.cutLabel', "Cut"), when: EditorContextKeys.writable, order: 1, }, { menuId: MenuId.CommandPalette, group: '', title: nls.localize('actions.clipboard.cutLabel', "Cut"), order: 1 }, { menuId: MenuId.SimpleEditorContext, group: CLIPBOARD_CONTEXT_MENU_GROUP, title: nls.localize('actions.clipboard.cutLabel', "Cut"), when: EditorContextKeys.writable, order: 1, }] })) : undefined; export const CopyAction = supportsCopy ? registerCommand(new MultiCommand({ id: 'editor.action.clipboardCopyAction', precondition: undefined, kbOpts: ( // Do not bind copy keybindings in the browser, // since browsers do that for us and it avoids security prompts platform.isNative ? { primary: 2048 /* KeyMod.CtrlCmd */ | 33 /* KeyCode.KeyC */, win: { primary: 2048 /* KeyMod.CtrlCmd */ | 33 /* KeyCode.KeyC */, secondary: [2048 /* KeyMod.CtrlCmd */ | 19 /* KeyCode.Insert */] }, weight: 100 /* KeybindingWeight.EditorContrib */ } : undefined), menuOpts: [{ menuId: MenuId.MenubarEditMenu, group: '2_ccp', title: nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"), order: 2 }, { menuId: MenuId.EditorContext, group: CLIPBOARD_CONTEXT_MENU_GROUP, title: nls.localize('actions.clipboard.copyLabel', "Copy"), order: 2, }, { menuId: MenuId.CommandPalette, group: '', title: nls.localize('actions.clipboard.copyLabel', "Copy"), order: 1 }, { menuId: MenuId.SimpleEditorContext, group: CLIPBOARD_CONTEXT_MENU_GROUP, title: nls.localize('actions.clipboard.copyLabel', "Copy"), order: 2, }] })) : undefined; MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { submenu: MenuId.MenubarCopy, title: nls.localize2('copy as', "Copy As"), group: '2_ccp', order: 3 }); MenuRegistry.appendMenuItem(MenuId.EditorContext, { submenu: MenuId.EditorContextCopy, title: nls.localize2('copy as', "Copy As"), group: CLIPBOARD_CONTEXT_MENU_GROUP, order: 3 }); MenuRegistry.appendMenuItem(MenuId.EditorContext, { submenu: MenuId.EditorContextShare, title: nls.localize2('share', "Share"), group: '11_share', order: -1, when: ContextKeyExpr.and(ContextKeyExpr.notEquals('resourceScheme', 'output'), EditorContextKeys.editorTextFocus) }); MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { submenu: MenuId.ExplorerContextShare, title: nls.localize2('share', "Share"), group: '11_share', order: -1 }); export const PasteAction = supportsPaste ? registerCommand(new MultiCommand({ id: 'editor.action.clipboardPasteAction', precondition: undefined, kbOpts: ( // Do not bind paste keybindings in the browser, // since browsers do that for us and it avoids security prompts platform.isNative ? { primary: 2048 /* KeyMod.CtrlCmd */ | 52 /* KeyCode.KeyV */, win: { primary: 2048 /* KeyMod.CtrlCmd */ | 52 /* KeyCode.KeyV */, secondary: [1024 /* KeyMod.Shift */ | 19 /* KeyCode.Insert */] }, linux: { primary: 2048 /* KeyMod.CtrlCmd */ | 52 /* KeyCode.KeyV */, secondary: [1024 /* KeyMod.Shift */ | 19 /* KeyCode.Insert */] }, weight: 100 /* KeybindingWeight.EditorContrib */ } : undefined), menuOpts: [{ menuId: MenuId.MenubarEditMenu, group: '2_ccp', title: nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), order: 4 }, { menuId: MenuId.EditorContext, group: CLIPBOARD_CONTEXT_MENU_GROUP, title: nls.localize('actions.clipboard.pasteLabel', "Paste"), when: EditorContextKeys.writable, order: 4, }, { menuId: MenuId.CommandPalette, group: '', title: nls.localize('actions.clipboard.pasteLabel', "Paste"), order: 1 }, { menuId: MenuId.SimpleEditorContext, group: CLIPBOARD_CONTEXT_MENU_GROUP, title: nls.localize('actions.clipboard.pasteLabel', "Paste"), when: EditorContextKeys.writable, order: 4, }] })) : undefined; class ExecCommandCopyWithSyntaxHighlightingAction extends EditorAction { constructor() { super({ id: 'editor.action.clipboardCopyWithSyntaxHighlightingAction', label: nls.localize('actions.clipboard.copyWithSyntaxHighlightingLabel', "Copy With Syntax Highlighting"), alias: 'Copy With Syntax Highlighting', precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: 0, weight: 100 /* KeybindingWeight.EditorContrib */ } }); } run(accessor, editor) { if (!editor.hasModel()) { return; } const emptySelectionClipboard = editor.getOption(37 /* EditorOption.emptySelectionClipboard */); if (!emptySelectionClipboard && editor.getSelection().isEmpty()) { return; } CopyOptions.forceCopyWithSyntaxHighlighting = true; editor.focus(); editor.getContainerDomNode().ownerDocument.execCommand('copy'); CopyOptions.forceCopyWithSyntaxHighlighting = false; } } function registerExecCommandImpl(target, browserCommand) { if (!target) { return; } // 1. handle case when focus is in editor. target.addImplementation(10000, 'code-editor', (accessor, args) => { // Only if editor text focus (i.e. not if editor has widget focus). const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); if (focusedEditor && focusedEditor.hasTextFocus()) { // Do not execute if there is no selection and empty selection clipboard is off const emptySelectionClipboard = focusedEditor.getOption(37 /* EditorOption.emptySelectionClipboard */); const selection = focusedEditor.getSelection(); if (selection && selection.isEmpty() && !emptySelectionClipboard) { return true; } focusedEditor.getContainerDomNode().ownerDocument.execCommand(browserCommand); return true; } return false; }); // 2. (default) handle case when focus is somewhere else. target.addImplementation(0, 'generic-dom', (accessor, args) => { getActiveDocument().execCommand(browserCommand); return true; }); } registerExecCommandImpl(CutAction, 'cut'); registerExecCommandImpl(CopyAction, 'copy'); if (PasteAction) { // 1. Paste: handle case when focus is in editor. PasteAction.addImplementation(10000, 'code-editor', (accessor, args) => { const codeEditorService = accessor.get(ICodeEditorService); const clipboardService = accessor.get(IClipboardService); // Only if editor text focus (i.e. not if editor has widget focus). const focusedEditor = codeEditorService.getFocusedCodeEditor(); if (focusedEditor && focusedEditor.hasTextFocus()) { const result = focusedEditor.getContainerDomNode().ownerDocument.execCommand('paste'); if (result) { return CopyPasteController.get(focusedEditor)?.finishedPaste() ?? Promise.resolve(); } else if (platform.isWeb) { // Use the clipboard service if document.execCommand('paste') was not successful return (async () => { const clipboardText = await clipboardService.readText(); if (clipboardText !== '') { const metadata = InMemoryClipboardMetadataManager.INSTANCE.get(clipboardText); let pasteOnNewLine = false; let multicursorText = null; let mode = null; if (metadata) { pasteOnNewLine = (focusedEditor.getOption(37 /* EditorOption.emptySelectionClipboard */) && !!metadata.isFromEmptySelection); multicursorText = (typeof metadata.multicursorText !== 'undefined' ? metadata.multicursorText : null); mode = metadata.mode; } focusedEditor.trigger('keyboard', "paste" /* Handler.Paste */, { text: clipboardText, pasteOnNewLine, multicursorText, mode }); } })(); } return true; } return false; }); // 2. Paste: (default) handle case when focus is somewhere else. PasteAction.addImplementation(0, 'generic-dom', (accessor, args) => { getActiveDocument().execCommand('paste'); return true; }); } if (supportsCopy) { registerEditorAction(ExecCommandCopyWithSyntaxHighlightingAction); }