247 lines
9.4 KiB
TypeScript
247 lines
9.4 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import * as nls from 'vs/nls';
|
|
import * as browser from 'vs/base/browser/browser';
|
|
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
|
import * as platform from 'vs/base/common/platform';
|
|
import { CopyOptions, InMemoryClipboardMetadataManager } from 'vs/editor/browser/controller/textAreaInput';
|
|
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
|
import { EditorAction, registerEditorAction, Command, MultiCommand } from 'vs/editor/browser/editorExtensions';
|
|
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
|
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
|
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
|
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
|
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
|
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
|
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
|
import { Handler } from 'vs/editor/common/editorCommon';
|
|
|
|
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<T extends Command>(command: T): T {
|
|
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: KeyMod.CtrlCmd | KeyCode.KEY_X,
|
|
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, secondary: [KeyMod.Shift | KeyCode.Delete] },
|
|
weight: 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
|
|
}]
|
|
})) : 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: KeyMod.CtrlCmd | KeyCode.KEY_C,
|
|
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyCode.Insert] },
|
|
weight: 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
|
|
}]
|
|
})) : undefined;
|
|
|
|
MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { submenu: MenuId.MenubarCopy, title: { value: nls.localize('copy as', "Copy As"), original: 'Copy As', }, group: '2_ccp', order: 3 });
|
|
MenuRegistry.appendMenuItem(MenuId.EditorContext, { submenu: MenuId.EditorContextCopy, title: { value: nls.localize('copy as', "Copy As"), original: 'Copy As', }, group: CLIPBOARD_CONTEXT_MENU_GROUP, order: 3 });
|
|
|
|
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: KeyMod.CtrlCmd | KeyCode.KEY_V,
|
|
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] },
|
|
linux: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] },
|
|
weight: 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
|
|
}]
|
|
})) : 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: KeybindingWeight.EditorContrib
|
|
}
|
|
});
|
|
}
|
|
|
|
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
|
|
if (!editor.hasModel()) {
|
|
return;
|
|
}
|
|
|
|
const emptySelectionClipboard = editor.getOption(EditorOption.emptySelectionClipboard);
|
|
|
|
if (!emptySelectionClipboard && editor.getSelection().isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
CopyOptions.forceCopyWithSyntaxHighlighting = true;
|
|
editor.focus();
|
|
document.execCommand('copy');
|
|
CopyOptions.forceCopyWithSyntaxHighlighting = false;
|
|
}
|
|
}
|
|
|
|
function registerExecCommandImpl(target: MultiCommand | undefined, browserCommand: 'cut' | 'copy'): void {
|
|
if (!target) {
|
|
return;
|
|
}
|
|
|
|
// 1. handle case when focus is in editor.
|
|
target.addImplementation(10000, 'code-editor', (accessor: ServicesAccessor, args: any) => {
|
|
// 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(EditorOption.emptySelectionClipboard);
|
|
const selection = focusedEditor.getSelection();
|
|
if (selection && selection.isEmpty() && !emptySelectionClipboard) {
|
|
return true;
|
|
}
|
|
document.execCommand(browserCommand);
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
// 2. (default) handle case when focus is somewhere else.
|
|
target.addImplementation(0, 'generic-dom', (accessor: ServicesAccessor, args: any) => {
|
|
document.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: ServicesAccessor, args: any) => {
|
|
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 = document.execCommand('paste');
|
|
// Use the clipboard service if document.execCommand('paste') was not successful
|
|
if (!result && platform.isWeb) {
|
|
(async () => {
|
|
const clipboardText = await clipboardService.readText();
|
|
if (clipboardText !== '') {
|
|
const metadata = InMemoryClipboardMetadataManager.INSTANCE.get(clipboardText);
|
|
let pasteOnNewLine = false;
|
|
let multicursorText: string[] | null = null;
|
|
let mode: string | null = null;
|
|
if (metadata) {
|
|
pasteOnNewLine = (focusedEditor.getOption(EditorOption.emptySelectionClipboard) && !!metadata.isFromEmptySelection);
|
|
multicursorText = (typeof metadata.multicursorText !== 'undefined' ? metadata.multicursorText : null);
|
|
mode = metadata.mode;
|
|
}
|
|
focusedEditor.trigger('keyboard', Handler.Paste, {
|
|
text: clipboardText,
|
|
pasteOnNewLine,
|
|
multicursorText,
|
|
mode
|
|
});
|
|
}
|
|
})();
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
// 2. Paste: (default) handle case when focus is somewhere else.
|
|
PasteAction.addImplementation(0, 'generic-dom', (accessor: ServicesAccessor, args: any) => {
|
|
document.execCommand('paste');
|
|
return true;
|
|
});
|
|
}
|
|
|
|
if (supportsCopy) {
|
|
registerEditorAction(ExecCommandCopyWithSyntaxHighlightingAction);
|
|
}
|