5a7c0c6e550fcba29d5284c239381311c28ff605bbd2acb7ad51cc13a495f5e71f320fc596f1d2588da0751b0ac3431ff115cfaa0fdd260c3e122750ef14c4 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  6. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  7. return new (P || (P = Promise))(function (resolve, reject) {
  8. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  9. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  10. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  11. step((generator = generator.apply(thisArg, _arguments || [])).next());
  12. });
  13. };
  14. import * as browser from '../../../../base/browser/browser.js';
  15. import * as platform from '../../../../base/common/platform.js';
  16. import { CopyOptions, InMemoryClipboardMetadataManager } from '../../../browser/controller/textAreaInput.js';
  17. import { EditorAction, MultiCommand, registerEditorAction } from '../../../browser/editorExtensions.js';
  18. import { ICodeEditorService } from '../../../browser/services/codeEditorService.js';
  19. import { EditorContextKeys } from '../../../common/editorContextKeys.js';
  20. import * as nls from '../../../../nls.js';
  21. import { MenuId, MenuRegistry } from '../../../../platform/actions/common/actions.js';
  22. import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
  23. const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste';
  24. const supportsCut = (platform.isNative || document.queryCommandSupported('cut'));
  25. const supportsCopy = (platform.isNative || document.queryCommandSupported('copy'));
  26. // Firefox only supports navigator.clipboard.readText() in browser extensions.
  27. // See https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/readText#Browser_compatibility
  28. // When loading over http, navigator.clipboard can be undefined. See https://github.com/microsoft/monaco-editor/issues/2313
  29. const supportsPaste = (typeof navigator.clipboard === 'undefined' || browser.isFirefox) ? document.queryCommandSupported('paste') : true;
  30. function registerCommand(command) {
  31. command.register();
  32. return command;
  33. }
  34. export const CutAction = supportsCut ? registerCommand(new MultiCommand({
  35. id: 'editor.action.clipboardCutAction',
  36. precondition: undefined,
  37. kbOpts: (
  38. // Do not bind cut keybindings in the browser,
  39. // since browsers do that for us and it avoids security prompts
  40. platform.isNative ? {
  41. primary: 2048 /* KeyMod.CtrlCmd */ | 54 /* KeyCode.KeyX */,
  42. win: { primary: 2048 /* KeyMod.CtrlCmd */ | 54 /* KeyCode.KeyX */, secondary: [1024 /* KeyMod.Shift */ | 20 /* KeyCode.Delete */] },
  43. weight: 100 /* KeybindingWeight.EditorContrib */
  44. } : undefined),
  45. menuOpts: [{
  46. menuId: MenuId.MenubarEditMenu,
  47. group: '2_ccp',
  48. title: nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"),
  49. order: 1
  50. }, {
  51. menuId: MenuId.EditorContext,
  52. group: CLIPBOARD_CONTEXT_MENU_GROUP,
  53. title: nls.localize('actions.clipboard.cutLabel', "Cut"),
  54. when: EditorContextKeys.writable,
  55. order: 1,
  56. }, {
  57. menuId: MenuId.CommandPalette,
  58. group: '',
  59. title: nls.localize('actions.clipboard.cutLabel', "Cut"),
  60. order: 1
  61. }, {
  62. menuId: MenuId.SimpleEditorContext,
  63. group: CLIPBOARD_CONTEXT_MENU_GROUP,
  64. title: nls.localize('actions.clipboard.cutLabel', "Cut"),
  65. when: EditorContextKeys.writable,
  66. order: 1,
  67. }]
  68. })) : undefined;
  69. export const CopyAction = supportsCopy ? registerCommand(new MultiCommand({
  70. id: 'editor.action.clipboardCopyAction',
  71. precondition: undefined,
  72. kbOpts: (
  73. // Do not bind copy keybindings in the browser,
  74. // since browsers do that for us and it avoids security prompts
  75. platform.isNative ? {
  76. primary: 2048 /* KeyMod.CtrlCmd */ | 33 /* KeyCode.KeyC */,
  77. win: { primary: 2048 /* KeyMod.CtrlCmd */ | 33 /* KeyCode.KeyC */, secondary: [2048 /* KeyMod.CtrlCmd */ | 19 /* KeyCode.Insert */] },
  78. weight: 100 /* KeybindingWeight.EditorContrib */
  79. } : undefined),
  80. menuOpts: [{
  81. menuId: MenuId.MenubarEditMenu,
  82. group: '2_ccp',
  83. title: nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"),
  84. order: 2
  85. }, {
  86. menuId: MenuId.EditorContext,
  87. group: CLIPBOARD_CONTEXT_MENU_GROUP,
  88. title: nls.localize('actions.clipboard.copyLabel', "Copy"),
  89. order: 2,
  90. }, {
  91. menuId: MenuId.CommandPalette,
  92. group: '',
  93. title: nls.localize('actions.clipboard.copyLabel', "Copy"),
  94. order: 1
  95. }, {
  96. menuId: MenuId.SimpleEditorContext,
  97. group: CLIPBOARD_CONTEXT_MENU_GROUP,
  98. title: nls.localize('actions.clipboard.copyLabel', "Copy"),
  99. order: 2,
  100. }]
  101. })) : undefined;
  102. MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { submenu: MenuId.MenubarCopy, title: { value: nls.localize('copy as', "Copy As"), original: 'Copy As', }, group: '2_ccp', order: 3 });
  103. 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 });
  104. MenuRegistry.appendMenuItem(MenuId.EditorContext, { submenu: MenuId.EditorContextShare, title: { value: nls.localize('share', "Share"), original: 'Share', }, group: '11_share', order: -1 });
  105. export const PasteAction = supportsPaste ? registerCommand(new MultiCommand({
  106. id: 'editor.action.clipboardPasteAction',
  107. precondition: undefined,
  108. kbOpts: (
  109. // Do not bind paste keybindings in the browser,
  110. // since browsers do that for us and it avoids security prompts
  111. platform.isNative ? {
  112. primary: 2048 /* KeyMod.CtrlCmd */ | 52 /* KeyCode.KeyV */,
  113. win: { primary: 2048 /* KeyMod.CtrlCmd */ | 52 /* KeyCode.KeyV */, secondary: [1024 /* KeyMod.Shift */ | 19 /* KeyCode.Insert */] },
  114. linux: { primary: 2048 /* KeyMod.CtrlCmd */ | 52 /* KeyCode.KeyV */, secondary: [1024 /* KeyMod.Shift */ | 19 /* KeyCode.Insert */] },
  115. weight: 100 /* KeybindingWeight.EditorContrib */
  116. } : undefined),
  117. menuOpts: [{
  118. menuId: MenuId.MenubarEditMenu,
  119. group: '2_ccp',
  120. title: nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"),
  121. order: 4
  122. }, {
  123. menuId: MenuId.EditorContext,
  124. group: CLIPBOARD_CONTEXT_MENU_GROUP,
  125. title: nls.localize('actions.clipboard.pasteLabel', "Paste"),
  126. when: EditorContextKeys.writable,
  127. order: 4,
  128. }, {
  129. menuId: MenuId.CommandPalette,
  130. group: '',
  131. title: nls.localize('actions.clipboard.pasteLabel', "Paste"),
  132. order: 1
  133. }, {
  134. menuId: MenuId.SimpleEditorContext,
  135. group: CLIPBOARD_CONTEXT_MENU_GROUP,
  136. title: nls.localize('actions.clipboard.pasteLabel', "Paste"),
  137. when: EditorContextKeys.writable,
  138. order: 4,
  139. }]
  140. })) : undefined;
  141. class ExecCommandCopyWithSyntaxHighlightingAction extends EditorAction {
  142. constructor() {
  143. super({
  144. id: 'editor.action.clipboardCopyWithSyntaxHighlightingAction',
  145. label: nls.localize('actions.clipboard.copyWithSyntaxHighlightingLabel', "Copy With Syntax Highlighting"),
  146. alias: 'Copy With Syntax Highlighting',
  147. precondition: undefined,
  148. kbOpts: {
  149. kbExpr: EditorContextKeys.textInputFocus,
  150. primary: 0,
  151. weight: 100 /* KeybindingWeight.EditorContrib */
  152. }
  153. });
  154. }
  155. run(accessor, editor) {
  156. if (!editor.hasModel()) {
  157. return;
  158. }
  159. const emptySelectionClipboard = editor.getOption(33 /* EditorOption.emptySelectionClipboard */);
  160. if (!emptySelectionClipboard && editor.getSelection().isEmpty()) {
  161. return;
  162. }
  163. CopyOptions.forceCopyWithSyntaxHighlighting = true;
  164. editor.focus();
  165. document.execCommand('copy');
  166. CopyOptions.forceCopyWithSyntaxHighlighting = false;
  167. }
  168. }
  169. function registerExecCommandImpl(target, browserCommand) {
  170. if (!target) {
  171. return;
  172. }
  173. // 1. handle case when focus is in editor.
  174. target.addImplementation(10000, 'code-editor', (accessor, args) => {
  175. // Only if editor text focus (i.e. not if editor has widget focus).
  176. const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
  177. if (focusedEditor && focusedEditor.hasTextFocus()) {
  178. // Do not execute if there is no selection and empty selection clipboard is off
  179. const emptySelectionClipboard = focusedEditor.getOption(33 /* EditorOption.emptySelectionClipboard */);
  180. const selection = focusedEditor.getSelection();
  181. if (selection && selection.isEmpty() && !emptySelectionClipboard) {
  182. return true;
  183. }
  184. document.execCommand(browserCommand);
  185. return true;
  186. }
  187. return false;
  188. });
  189. // 2. (default) handle case when focus is somewhere else.
  190. target.addImplementation(0, 'generic-dom', (accessor, args) => {
  191. document.execCommand(browserCommand);
  192. return true;
  193. });
  194. }
  195. registerExecCommandImpl(CutAction, 'cut');
  196. registerExecCommandImpl(CopyAction, 'copy');
  197. if (PasteAction) {
  198. // 1. Paste: handle case when focus is in editor.
  199. PasteAction.addImplementation(10000, 'code-editor', (accessor, args) => {
  200. const codeEditorService = accessor.get(ICodeEditorService);
  201. const clipboardService = accessor.get(IClipboardService);
  202. // Only if editor text focus (i.e. not if editor has widget focus).
  203. const focusedEditor = codeEditorService.getFocusedCodeEditor();
  204. if (focusedEditor && focusedEditor.hasTextFocus()) {
  205. const result = document.execCommand('paste');
  206. // Use the clipboard service if document.execCommand('paste') was not successful
  207. if (!result && platform.isWeb) {
  208. return (() => __awaiter(void 0, void 0, void 0, function* () {
  209. const clipboardText = yield clipboardService.readText();
  210. if (clipboardText !== '') {
  211. const metadata = InMemoryClipboardMetadataManager.INSTANCE.get(clipboardText);
  212. let pasteOnNewLine = false;
  213. let multicursorText = null;
  214. let mode = null;
  215. if (metadata) {
  216. pasteOnNewLine = (focusedEditor.getOption(33 /* EditorOption.emptySelectionClipboard */) && !!metadata.isFromEmptySelection);
  217. multicursorText = (typeof metadata.multicursorText !== 'undefined' ? metadata.multicursorText : null);
  218. mode = metadata.mode;
  219. }
  220. focusedEditor.trigger('keyboard', "paste" /* Handler.Paste */, {
  221. text: clipboardText,
  222. pasteOnNewLine,
  223. multicursorText,
  224. mode
  225. });
  226. }
  227. }))();
  228. }
  229. return true;
  230. }
  231. return false;
  232. });
  233. // 2. Paste: (default) handle case when focus is somewhere else.
  234. PasteAction.addImplementation(0, 'generic-dom', (accessor, args) => {
  235. document.execCommand('paste');
  236. return true;
  237. });
  238. }
  239. if (supportsCopy) {
  240. registerEditorAction(ExecCommandCopyWithSyntaxHighlightingAction);
  241. }