Simplify code

This commit is contained in:
Alex Dima
2026-01-19 20:25:30 +01:00
parent ed0d4c4cbf
commit a6c1a0c19b
4 changed files with 78 additions and 116 deletions
@@ -10,6 +10,8 @@ import { ViewContext } from '../../../common/viewModel/viewContext.js';
import { ILogService, LogLevel } from '../../../../platform/log/common/log.js';
import { EditorOption, IComputedEditorOptions } from '../../../common/config/editorOptions.js';
import { generateUuid } from '../../../../base/common/uuid.js';
import { VSDataTransfer } from '../../../../base/common/dataTransfer.js';
import { toExternalVSDataTransfer } from '../../dataTransfer.js';
export function ensureClipboardGetsEditorSelection(e: ClipboardEvent, context: ViewContext, logService: ILogService, isFirefox: boolean): void {
const viewModel = context.viewModel;
@@ -139,7 +141,7 @@ interface InMemoryClipboardMetadata {
export const ClipboardEventUtils = {
getTextData(clipboardData: DataTransfer): [string, ClipboardStoredMetadata | null] {
getTextData(clipboardData: IReadableClipboardData | DataTransfer): [string, ClipboardStoredMetadata | null] {
const text = clipboardData.getData(Mimes.text);
let metadata: ClipboardStoredMetadata | null = null;
const rawmetadata = clipboardData.getData('vscode-editor-data');
@@ -161,7 +163,7 @@ export const ClipboardEventUtils = {
return [text, metadata];
},
setTextData(clipboardData: DataTransfer, text: string, html: string | null | undefined, metadata: ClipboardStoredMetadata): void {
setTextData(clipboardData: IWritableClipboardData, text: string, html: string | null | undefined, metadata: ClipboardStoredMetadata): void {
clipboardData.setData(Mimes.text, text);
if (typeof html === 'string') {
clipboardData.setData('text/html', html);
@@ -171,29 +173,13 @@ export const ClipboardEventUtils = {
};
/**
* Abstracted clipboard data that does not directly expose DOM ClipboardEvent/DataTransfer.
* This allows editor contributions to work with clipboard data without DOM dependencies.
* Readable clipboard data for paste operations.
*/
export interface IClipboardData {
/**
* The text content from the clipboard.
*/
readonly text: string;
/**
* The HTML content from the clipboard, if available.
*/
readonly html: string | undefined;
/**
* VS Code editor metadata associated with this clipboard data.
*/
readonly metadata: ClipboardStoredMetadata | null;
export interface IReadableClipboardData {
/**
* All MIME types present in the clipboard.
*/
readonly types: readonly string[];
types: string[];
/**
* Files from the clipboard (for paste operations).
@@ -209,7 +195,7 @@ export interface IClipboardData {
/**
* Writable clipboard data for copy/cut operations.
*/
export interface IWritableClipboardData extends IClipboardData {
export interface IWritableClipboardData {
/**
* Set data for a specific MIME type.
*/
@@ -248,7 +234,17 @@ export interface IClipboardPasteEvent {
/**
* The clipboard data being pasted.
*/
readonly clipboardData: IClipboardData;
readonly clipboardData: IReadableClipboardData;
/**
* The metadata stored alongside the clipboard data, if any.
*/
readonly metadata: ClipboardStoredMetadata | null;
/**
* The text content being pasted.
*/
readonly text: string;
/**
* The underlying DOM event, if available.
@@ -256,6 +252,8 @@ export interface IClipboardPasteEvent {
*/
readonly browserEvent: ClipboardEvent | undefined;
toExternalVSDataTransfer(): VSDataTransfer | undefined;
/**
* Signal that the event has been handled and default processing should be skipped.
*/
@@ -267,35 +265,6 @@ export interface IClipboardPasteEvent {
readonly isHandled: boolean;
}
/**
* Creates an IClipboardData from a DOM DataTransfer.
*/
export function createClipboardData(dataTransfer: DataTransfer): IClipboardData {
const [text, metadata] = ClipboardEventUtils.getTextData(dataTransfer);
const html = dataTransfer.getData('text/html') || undefined;
const files: File[] = Array.prototype.slice.call(dataTransfer.files, 0);
return {
text,
html,
metadata,
types: Array.from(dataTransfer.types),
files,
getData: (type: string) => dataTransfer.getData(type),
};
}
/**
* Creates an IWritableClipboardData from a DOM DataTransfer.
*/
export function createWritableClipboardData(dataTransfer: DataTransfer): IWritableClipboardData {
const base = createClipboardData(dataTransfer);
return {
...base,
setData: (type: string, value: string) => dataTransfer.setData(type, value),
};
}
/**
* Creates an IClipboardCopyEvent from a DOM ClipboardEvent.
*/
@@ -303,14 +272,10 @@ export function createClipboardCopyEvent(e: ClipboardEvent, isCut: boolean): ICl
let handled = false;
return {
isCut,
clipboardData: e.clipboardData ? createWritableClipboardData(e.clipboardData) : {
text: '',
html: undefined,
metadata: null,
types: [],
files: [],
getData: () => '',
setData: () => { },
clipboardData: {
setData: (type: string, value: string) => {
e.clipboardData?.setData(type, value);
},
},
setHandled: () => {
handled = true;
@@ -326,15 +291,13 @@ export function createClipboardCopyEvent(e: ClipboardEvent, isCut: boolean): ICl
*/
export function createClipboardPasteEvent(e: ClipboardEvent): IClipboardPasteEvent {
let handled = false;
let [text, metadata] = e.clipboardData ? ClipboardEventUtils.getTextData(e.clipboardData) : ['', null];
metadata = metadata || InMemoryClipboardMetadataManager.INSTANCE.get(text);
return {
clipboardData: e.clipboardData ? createClipboardData(e.clipboardData) : {
text: '',
html: undefined,
metadata: null,
types: [],
files: [],
getData: () => '',
},
clipboardData: createReadableClipboardData(e.clipboardData),
metadata,
text,
toExternalVSDataTransfer: () => e.clipboardData ? toExternalVSDataTransfer(e.clipboardData) : undefined,
browserEvent: e,
setHandled: () => {
handled = true;
@@ -344,3 +307,17 @@ export function createClipboardPasteEvent(e: ClipboardEvent): IClipboardPasteEve
get isHandled() { return handled; },
};
}
export function createReadableClipboardData(dataTransfer: DataTransfer | undefined | null): IReadableClipboardData {
return {
types: Array.from(dataTransfer?.types ?? []),
files: Array.prototype.slice.call(dataTransfer?.files ?? [], 0),
getData: (type: string) => dataTransfer?.getData(type) ?? '',
};
}
export function createWritableClipboardData(dataTransfer: DataTransfer | undefined | null): IWritableClipboardData {
return {
setData: (type: string, value: string) => dataTransfer?.setData(type, value),
};
}
@@ -16,7 +16,7 @@ import { ViewConfigurationChangedEvent, ViewCursorStateChangedEvent, ViewDecorat
import { ViewContext } from '../../../../common/viewModel/viewContext.js';
import { RestrictedRenderingContext, RenderingContext, HorizontalPosition } from '../../../view/renderingContext.js';
import { ViewController } from '../../../view/viewController.js';
import { ClipboardEventUtils, createClipboardCopyEvent, createClipboardPasteEvent, ensureClipboardGetsEditorSelection, InMemoryClipboardMetadataManager } from '../clipboardUtils.js';
import { createClipboardCopyEvent, createClipboardPasteEvent, ensureClipboardGetsEditorSelection } from '../clipboardUtils.js';
import { AbstractEditContext } from '../editContext.js';
import { editContextAddDisposableListener, FocusTracker, ITypeData } from './nativeEditContextUtils.js';
import { ScreenReaderSupport } from './screenReaderSupport.js';
@@ -161,24 +161,22 @@ export class NativeEditContext extends AbstractEditContext {
if (!e.clipboardData) {
return;
}
let [text, metadata] = ClipboardEventUtils.getTextData(e.clipboardData);
this.logService.trace('NativeEditContext#paste with id : ', metadata?.id, ' with text.length: ', text.length);
if (!text) {
this.logService.trace('NativeEditContext#paste with id : ', pasteEvent.metadata?.id, ' with text.length: ', pasteEvent.text.length);
if (!pasteEvent.text) {
return;
}
metadata = metadata || InMemoryClipboardMetadataManager.INSTANCE.get(text);
let pasteOnNewLine = false;
let multicursorText: string[] | null = null;
let mode: string | null = null;
if (metadata) {
if (pasteEvent.metadata) {
const options = this._context.configuration.options;
const emptySelectionClipboard = options.get(EditorOption.emptySelectionClipboard);
pasteOnNewLine = emptySelectionClipboard && !!metadata.isFromEmptySelection;
multicursorText = typeof metadata.multicursorText !== 'undefined' ? metadata.multicursorText : null;
mode = metadata.mode;
pasteOnNewLine = emptySelectionClipboard && !!pasteEvent.metadata.isFromEmptySelection;
multicursorText = typeof pasteEvent.metadata.multicursorText !== 'undefined' ? pasteEvent.metadata.multicursorText : null;
mode = pasteEvent.metadata.mode;
}
this.logService.trace('NativeEditContext#paste (before viewController.paste)');
this._viewController.paste(text, pasteOnNewLine, multicursorText, mode);
this._viewController.paste(pasteEvent.text, pasteOnNewLine, multicursorText, mode);
}));
// Edit context events
@@ -18,7 +18,7 @@ import { Position } from '../../../../common/core/position.js';
import { Selection } from '../../../../common/core/selection.js';
import { IAccessibilityService } from '../../../../../platform/accessibility/common/accessibility.js';
import { ILogService } from '../../../../../platform/log/common/log.js';
import { ClipboardEventUtils, ClipboardStoredMetadata, createClipboardCopyEvent, createClipboardPasteEvent, ensureClipboardGetsEditorSelection, IClipboardCopyEvent, IClipboardPasteEvent, InMemoryClipboardMetadataManager } from '../clipboardUtils.js';
import { ClipboardStoredMetadata, createClipboardCopyEvent, createClipboardPasteEvent, ensureClipboardGetsEditorSelection, IClipboardCopyEvent, IClipboardPasteEvent, InMemoryClipboardMetadataManager } from '../clipboardUtils.js';
import { _debugComposition, ITextAreaWrapper, ITypeData, TextAreaState } from './textAreaEditContextState.js';
import { ViewContext } from '../../../../common/viewModel/viewContext.js';
@@ -420,23 +420,15 @@ export class TextAreaInput extends Disposable {
e.preventDefault();
if (!e.clipboardData) {
this._logService.trace(`TextAreaInput#onPaste with id : `, pasteEvent.metadata?.id, ' with text.length: ', pasteEvent.text.length);
if (!pasteEvent.text) {
return;
}
let [text, metadata] = ClipboardEventUtils.getTextData(e.clipboardData);
this._logService.trace(`TextAreaInput#onPaste with id : `, metadata?.id, ' with text.length: ', text.length);
if (!text) {
return;
}
// try the in-memory store
metadata = metadata || InMemoryClipboardMetadataManager.INSTANCE.get(text);
this._logService.trace(`TextAreaInput#onPaste (before onPaste)`);
this._onPaste.fire({
text: text,
metadata: metadata
text: pasteEvent.text,
metadata: pasteEvent.metadata
});
}));
@@ -24,8 +24,7 @@ import { IInstantiationService } from '../../../../platform/instantiation/common
import { ILogService } from '../../../../platform/log/common/log.js';
import { IProgressService, ProgressLocation } from '../../../../platform/progress/common/progress.js';
import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from '../../../../platform/quickinput/common/quickInput.js';
import { ClipboardEventUtils, IClipboardCopyEvent, IClipboardPasteEvent, InMemoryClipboardMetadataManager, IWritableClipboardData } from '../../../browser/controller/editContext/clipboardUtils.js';
import { toExternalVSDataTransfer } from '../../../browser/dataTransfer.js';
import { IClipboardCopyEvent, IClipboardPasteEvent, IWritableClipboardData } from '../../../browser/controller/editContext/clipboardUtils.js';
import { ICodeEditor, PastePayload } from '../../../browser/editorBrowser.js';
import { IBulkEditService } from '../../../browser/services/bulkEditService.js';
import { EditorOption } from '../../../common/config/editorOptions.js';
@@ -247,18 +246,18 @@ export class CopyPasteController extends Disposable implements IEditorContributi
}
private async handlePaste(e: IClipboardPasteEvent) {
const clipboardData = e.browserEvent?.clipboardData;
if (clipboardData) {
const [text, metadata] = ClipboardEventUtils.getTextData(clipboardData);
const metadataComputed = metadata || InMemoryClipboardMetadataManager.INSTANCE.get(text);
this._logService.trace('CopyPasteController#handlePaste for id : ', metadataComputed?.id);
} else {
this._logService.trace('CopyPasteController#handlePaste');
}
if (!clipboardData || !this._editor.hasTextFocus()) {
this._logService.trace('CopyPasteController#handlePaste for id : ', e.metadata?.id);
if (!this._editor.hasTextFocus()) {
return;
}
const dataTransfer = e.toExternalVSDataTransfer();
if (!dataTransfer) {
return;
}
dataTransfer.delete(vscodeClipboardMime);
MessageController.get(this._editor)?.closeMessage();
this._currentPasteOperation?.cancel();
this._currentPasteOperation = undefined;
@@ -276,15 +275,13 @@ export class CopyPasteController extends Disposable implements IEditorContributi
return;
}
const metadata = this.fetchCopyMetadata(clipboardData);
this._logService.trace('CopyPasteController#handlePaste with metadata : ', metadata?.id, ' and text.length : ', clipboardData.getData('text/plain').length);
const dataTransfer = toExternalVSDataTransfer(clipboardData);
dataTransfer.delete(vscodeClipboardMime);
const metadata = this.fetchCopyMetadata(e);
this._logService.trace('CopyPasteController#handlePaste with metadata : ', metadata?.id, ' and text.length : ', e.clipboardData.getData('text/plain').length);
const fileTypes = Array.from(clipboardData.files).map(file => file.type);
const fileTypes = Array.from(e.clipboardData.files).map(file => file.type);
const allPotentialMimeTypes = [
...clipboardData.types,
...e.clipboardData.types,
...fileTypes,
...metadata?.providerCopyMimeTypes ?? [],
// TODO: always adds `uri-list` because this get set if there are resources in the system clipboard.
@@ -551,11 +548,11 @@ export class CopyPasteController extends Disposable implements IEditorContributi
clipboardData.setData(vscodeClipboardMime, JSON.stringify(metadata));
}
private fetchCopyMetadata(clipboardData: DataTransfer): CopyMetadata | undefined {
private fetchCopyMetadata(e: IClipboardPasteEvent): CopyMetadata | undefined {
this._logService.trace('CopyPasteController#fetchCopyMetadata');
// Prefer using the clipboard data we saved off
const rawMetadata = clipboardData.getData(vscodeClipboardMime);
const rawMetadata = e.clipboardData.getData(vscodeClipboardMime);
if (rawMetadata) {
try {
return JSON.parse(rawMetadata);
@@ -564,14 +561,12 @@ export class CopyPasteController extends Disposable implements IEditorContributi
}
}
// Otherwise try to extract the generic text editor metadata
const [_, metadata] = ClipboardEventUtils.getTextData(clipboardData);
if (metadata) {
if (e.metadata) {
return {
defaultPastePayload: {
mode: metadata.mode,
multicursorText: metadata.multicursorText ?? null,
pasteOnNewLine: !!metadata.isFromEmptySelection,
mode: e.metadata.mode,
multicursorText: e.metadata.multicursorText ?? null,
pasteOnNewLine: !!e.metadata.isFromEmptySelection,
},
};
}