Path: blob/main/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as nls from '../../../../nls.js';6import { RunOnceScheduler } from '../../../../base/common/async.js';7import { Disposable } from '../../../../base/common/lifecycle.js';8import * as platform from '../../../../base/common/platform.js';9import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';10import { registerEditorContribution, EditorAction, ServicesAccessor, registerEditorAction, EditorContributionInstantiation } from '../../../../editor/browser/editorExtensions.js';11import { ConfigurationChangedEvent, EditorOption } from '../../../../editor/common/config/editorOptions.js';12import { ICursorSelectionChangedEvent } from '../../../../editor/common/cursorEvents.js';13import { Range } from '../../../../editor/common/core/range.js';14import { IEditorContribution, Handler } from '../../../../editor/common/editorCommon.js';15import { EndOfLinePreference } from '../../../../editor/common/model.js';16import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';17import { SelectionClipboardContributionID } from '../browser/selectionClipboard.js';18import { IWorkbenchContribution, WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js';19import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';20import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js';21import { mainWindow } from '../../../../base/browser/window.js';22import { Event } from '../../../../base/common/event.js';23import { addDisposableListener, onDidRegisterWindow } from '../../../../base/browser/dom.js';2425export class SelectionClipboard extends Disposable implements IEditorContribution {26private static readonly SELECTION_LENGTH_LIMIT = 65536;2728constructor(editor: ICodeEditor, @IClipboardService clipboardService: IClipboardService) {29super();3031if (platform.isLinux) {32let isEnabled = editor.getOption(EditorOption.selectionClipboard);3334this._register(editor.onDidChangeConfiguration((e: ConfigurationChangedEvent) => {35if (e.hasChanged(EditorOption.selectionClipboard)) {36isEnabled = editor.getOption(EditorOption.selectionClipboard);37}38}));3940const setSelectionToClipboard = this._register(new RunOnceScheduler(() => {41if (!editor.hasModel()) {42return;43}44const model = editor.getModel();45let selections = editor.getSelections();46selections = selections.slice(0);47selections.sort(Range.compareRangesUsingStarts);4849let resultLength = 0;50for (const sel of selections) {51if (sel.isEmpty()) {52// Only write if all cursors have selection53return;54}55resultLength += model.getValueLengthInRange(sel);56}5758if (resultLength > SelectionClipboard.SELECTION_LENGTH_LIMIT) {59// This is a large selection!60// => do not write it to the selection clipboard61return;62}6364const result: string[] = [];65for (const sel of selections) {66result.push(model.getValueInRange(sel, EndOfLinePreference.TextDefined));67}6869const textToCopy = result.join(model.getEOL());70clipboardService.writeText(textToCopy, 'selection');71}, 100));7273this._register(editor.onDidChangeCursorSelection((e: ICursorSelectionChangedEvent) => {74if (!isEnabled) {75return;76}77if (e.source === 'restoreState') {78// do not set selection to clipboard if this selection change79// was caused by restoring editors...80return;81}82setSelectionToClipboard.schedule();83}));84}85}8687public override dispose(): void {88super.dispose();89}90}9192class LinuxSelectionClipboardPastePreventer extends Disposable implements IWorkbenchContribution {9394static readonly ID = 'workbench.contrib.linuxSelectionClipboardPastePreventer';9596constructor(97@IConfigurationService configurationService: IConfigurationService98) {99super();100101this._register(Event.runAndSubscribe(onDidRegisterWindow, ({ window, disposables }) => {102disposables.add(addDisposableListener(window.document, 'mouseup', e => {103if (e.button === 1) {104// middle button105const config = configurationService.getValue<{ selectionClipboard: boolean }>('editor');106if (!config.selectionClipboard) {107// selection clipboard is disabled108// try to stop the upcoming paste109e.preventDefault();110}111}112}));113}, { window: mainWindow, disposables: this._store }));114}115}116117class PasteSelectionClipboardAction extends EditorAction {118119constructor() {120super({121id: 'editor.action.selectionClipboardPaste',122label: nls.localize2('actions.pasteSelectionClipboard', "Paste Selection Clipboard"),123precondition: EditorContextKeys.writable124});125}126127public async run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): Promise<void> {128const clipboardService = accessor.get(IClipboardService);129130// read selection clipboard131const text = await clipboardService.readText('selection');132133editor.trigger('keyboard', Handler.Paste, {134text: text,135pasteOnNewLine: false,136multicursorText: null137});138}139}140141registerEditorContribution(SelectionClipboardContributionID, SelectionClipboard, EditorContributionInstantiation.Eager); // eager because it needs to listen to selection change events142if (platform.isLinux) {143registerWorkbenchContribution2(LinuxSelectionClipboardPastePreventer.ID, LinuxSelectionClipboardPastePreventer, WorkbenchPhase.BlockRestore); // eager because it listens to mouse-up events globally144registerEditorAction(PasteSelectionClipboardAction);145}146147148