monaco-editor-core
Version:
A browser based code editor
240 lines (239 loc) • 12.2 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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);
}