Path: blob/main/src/vs/editor/browser/controller/editContext/native/nativeEditContext.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 './nativeEditContext.css';6import { isFirefox } from '../../../../../base/browser/browser.js';7import { addDisposableListener, getActiveElement, getWindow, getWindowId } from '../../../../../base/browser/dom.js';8import { FastDomNode } from '../../../../../base/browser/fastDomNode.js';9import { StandardKeyboardEvent } from '../../../../../base/browser/keyboardEvent.js';10import { KeyCode } from '../../../../../base/common/keyCodes.js';11import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';12import { EditorOption } from '../../../../common/config/editorOptions.js';13import { EndOfLinePreference, IModelDeltaDecoration } from '../../../../common/model.js';14import { ViewConfigurationChangedEvent, ViewCursorStateChangedEvent, ViewDecorationsChangedEvent, ViewFlushedEvent, ViewLinesChangedEvent, ViewLinesDeletedEvent, ViewLinesInsertedEvent, ViewScrollChangedEvent, ViewZonesChangedEvent } from '../../../../common/viewEvents.js';15import { ViewContext } from '../../../../common/viewModel/viewContext.js';16import { RestrictedRenderingContext, RenderingContext } from '../../../view/renderingContext.js';17import { ViewController } from '../../../view/viewController.js';18import { ClipboardEventUtils, ClipboardStoredMetadata, getDataToCopy, InMemoryClipboardMetadataManager } from '../clipboardUtils.js';19import { AbstractEditContext } from '../editContext.js';20import { editContextAddDisposableListener, FocusTracker, ITypeData } from './nativeEditContextUtils.js';21import { ScreenReaderSupport } from './screenReaderSupport.js';22import { Range } from '../../../../common/core/range.js';23import { Selection } from '../../../../common/core/selection.js';24import { Position } from '../../../../common/core/position.js';25import { IVisibleRangeProvider } from '../textArea/textAreaEditContext.js';26import { PositionOffsetTransformer } from '../../../../common/core/text/positionToOffset.js';27import { EditContext } from './editContextFactory.js';28import { NativeEditContextRegistry } from './nativeEditContextRegistry.js';29import { IEditorAriaOptions } from '../../../editorBrowser.js';30import { isHighSurrogate, isLowSurrogate } from '../../../../../base/common/strings.js';31import { IME } from '../../../../../base/common/ime.js';32import { OffsetRange } from '../../../../common/core/ranges/offsetRange.js';33import { ILogService, LogLevel } from '../../../../../platform/log/common/log.js';34import { generateUuid } from '../../../../../base/common/uuid.js';3536// Corresponds to classes in nativeEditContext.css37enum CompositionClassName {38NONE = 'edit-context-composition-none',39SECONDARY = 'edit-context-composition-secondary',40PRIMARY = 'edit-context-composition-primary',41}4243interface ITextUpdateEvent {44text: string;45selectionStart: number;46selectionEnd: number;47updateRangeStart: number;48updateRangeEnd: number;49}5051export class NativeEditContext extends AbstractEditContext {5253// Text area used to handle paste events54public readonly domNode: FastDomNode<HTMLDivElement>;55private readonly _imeTextArea: FastDomNode<HTMLTextAreaElement>;56private readonly _editContext: EditContext;57private readonly _screenReaderSupport: ScreenReaderSupport;58private _previousEditContextSelection: OffsetRange = new OffsetRange(0, 0);59private _editContextPrimarySelection: Selection = new Selection(1, 1, 1, 1);6061// Overflow guard container62private _parent: HTMLElement | undefined;63private _decorations: string[] = [];64private _primarySelection: Selection = new Selection(1, 1, 1, 1);656667private _targetWindowId: number = -1;68private _scrollTop: number = 0;69private _scrollLeft: number = 0;7071private readonly _focusTracker: FocusTracker;7273constructor(74ownerID: string,75context: ViewContext,76overflowGuardContainer: FastDomNode<HTMLElement>,77private readonly _viewController: ViewController,78private readonly _visibleRangeProvider: IVisibleRangeProvider,79@IInstantiationService instantiationService: IInstantiationService,80@ILogService private readonly logService: ILogService81) {82super(context);8384this.domNode = new FastDomNode(document.createElement('div'));85this.domNode.setClassName(`native-edit-context`);86this._imeTextArea = new FastDomNode(document.createElement('textarea'));87this._imeTextArea.setClassName(`ime-text-area`);88this._imeTextArea.setAttribute('readonly', 'true');89this._imeTextArea.setAttribute('tabindex', '-1');90this._imeTextArea.setAttribute('aria-hidden', 'true');91this.domNode.setAttribute('autocorrect', 'off');92this.domNode.setAttribute('autocapitalize', 'off');93this.domNode.setAttribute('autocomplete', 'off');94this.domNode.setAttribute('spellcheck', 'false');9596this._updateDomAttributes();9798overflowGuardContainer.appendChild(this.domNode);99overflowGuardContainer.appendChild(this._imeTextArea);100this._parent = overflowGuardContainer.domNode;101102this._focusTracker = this._register(new FocusTracker(logService, this.domNode.domNode, (newFocusValue: boolean) => {103logService.trace('NativeEditContext#handleFocusChange : ', newFocusValue);104this._screenReaderSupport.handleFocusChange(newFocusValue);105this._context.viewModel.setHasFocus(newFocusValue);106}));107108const window = getWindow(this.domNode.domNode);109this._editContext = EditContext.create(window);110this.setEditContextOnDomNode();111112this._screenReaderSupport = this._register(instantiationService.createInstance(ScreenReaderSupport, this.domNode, context, this._viewController));113114this._register(addDisposableListener(this.domNode.domNode, 'copy', (e) => {115this.logService.trace('NativeEditContext#copy');116this._ensureClipboardGetsEditorSelection(e);117}));118this._register(addDisposableListener(this.domNode.domNode, 'cut', (e) => {119this.logService.trace('NativeEditContext#cut');120// Pretend here we touched the text area, as the `cut` event will most likely121// result in a `selectionchange` event which we want to ignore122this._screenReaderSupport.onWillCut();123this._ensureClipboardGetsEditorSelection(e);124this.logService.trace('NativeEditContext#cut (before viewController.cut)');125this._viewController.cut();126}));127128this._register(addDisposableListener(this.domNode.domNode, 'keyup', (e) => this._onKeyUp(e)));129this._register(addDisposableListener(this.domNode.domNode, 'keydown', async (e) => this._onKeyDown(e)));130this._register(addDisposableListener(this._imeTextArea.domNode, 'keyup', (e) => this._onKeyUp(e)));131this._register(addDisposableListener(this._imeTextArea.domNode, 'keydown', async (e) => this._onKeyDown(e)));132this._register(addDisposableListener(this.domNode.domNode, 'beforeinput', async (e) => {133if (e.inputType === 'insertParagraph' || e.inputType === 'insertLineBreak') {134this._onType(this._viewController, { text: '\n', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 });135}136}));137this._register(addDisposableListener(this.domNode.domNode, 'paste', (e) => {138this.logService.trace('NativeEditContext#paste');139e.preventDefault();140if (!e.clipboardData) {141return;142}143let [text, metadata] = ClipboardEventUtils.getTextData(e.clipboardData);144this.logService.trace('NativeEditContext#paste with id : ', metadata?.id, ' with text.length: ', text.length);145if (!text) {146return;147}148metadata = metadata || InMemoryClipboardMetadataManager.INSTANCE.get(text);149let pasteOnNewLine = false;150let multicursorText: string[] | null = null;151let mode: string | null = null;152if (metadata) {153const options = this._context.configuration.options;154const emptySelectionClipboard = options.get(EditorOption.emptySelectionClipboard);155pasteOnNewLine = emptySelectionClipboard && !!metadata.isFromEmptySelection;156multicursorText = typeof metadata.multicursorText !== 'undefined' ? metadata.multicursorText : null;157mode = metadata.mode;158}159this.logService.trace('NativeEditContext#paste (before viewController.paste)');160this._viewController.paste(text, pasteOnNewLine, multicursorText, mode);161}));162163// Edit context events164this._register(editContextAddDisposableListener(this._editContext, 'textformatupdate', (e) => this._handleTextFormatUpdate(e)));165this._register(editContextAddDisposableListener(this._editContext, 'characterboundsupdate', (e) => this._updateCharacterBounds(e)));166let highSurrogateCharacter: string | undefined;167this._register(editContextAddDisposableListener(this._editContext, 'textupdate', (e) => {168const text = e.text;169if (text.length === 1) {170const charCode = text.charCodeAt(0);171if (isHighSurrogate(charCode)) {172highSurrogateCharacter = text;173return;174}175if (isLowSurrogate(charCode) && highSurrogateCharacter) {176const textUpdateEvent: ITextUpdateEvent = {177text: highSurrogateCharacter + text,178selectionEnd: e.selectionEnd,179selectionStart: e.selectionStart,180updateRangeStart: e.updateRangeStart - 1,181updateRangeEnd: e.updateRangeEnd - 1182};183highSurrogateCharacter = undefined;184this._emitTypeEvent(this._viewController, textUpdateEvent);185return;186}187}188this._emitTypeEvent(this._viewController, e);189}));190this._register(editContextAddDisposableListener(this._editContext, 'compositionstart', (e) => {191this._updateEditContext();192// Utlimately fires onDidCompositionStart() on the editor to notify for example suggest model of composition state193// Updates the composition state of the cursor controller which determines behavior of typing with interceptors194this._viewController.compositionStart();195// Emits ViewCompositionStartEvent which can be depended on by ViewEventHandlers196this._context.viewModel.onCompositionStart();197}));198this._register(editContextAddDisposableListener(this._editContext, 'compositionend', (e) => {199this._updateEditContext();200// Utlimately fires compositionEnd() on the editor to notify for example suggest model of composition state201// Updates the composition state of the cursor controller which determines behavior of typing with interceptors202this._viewController.compositionEnd();203// Emits ViewCompositionEndEvent which can be depended on by ViewEventHandlers204this._context.viewModel.onCompositionEnd();205}));206let reenableTracking: boolean = false;207this._register(IME.onDidChange(() => {208if (IME.enabled && reenableTracking) {209this._focusTracker.resume();210this.domNode.focus();211reenableTracking = false;212}213if (!IME.enabled && this.isFocused()) {214this._focusTracker.pause();215this._imeTextArea.focus();216reenableTracking = true;217}218}));219this._register(NativeEditContextRegistry.register(ownerID, this));220}221222// --- Public methods ---223224public override dispose(): void {225// Force blue the dom node so can write in pane with no native edit context after disposal226this.domNode.domNode.editContext = undefined;227this.domNode.domNode.blur();228this.domNode.domNode.remove();229this._imeTextArea.domNode.remove();230super.dispose();231}232233public setAriaOptions(options: IEditorAriaOptions): void {234this._screenReaderSupport.setAriaOptions(options);235}236237/* Last rendered data needed for correct hit-testing and determining the mouse position.238* Without this, the selection will blink as incorrect mouse position is calculated */239public getLastRenderData(): Position | null {240return this._primarySelection.getPosition();241}242243public prepareRender(ctx: RenderingContext): void {244this._screenReaderSupport.prepareRender(ctx);245this._updateSelectionAndControlBounds(ctx);246}247248public render(ctx: RestrictedRenderingContext): void {249this._screenReaderSupport.render(ctx);250}251252public override onCursorStateChanged(e: ViewCursorStateChangedEvent): boolean {253this._primarySelection = e.modelSelections[0] ?? new Selection(1, 1, 1, 1);254this._screenReaderSupport.onCursorStateChanged(e);255this._updateEditContext();256return true;257}258259public override onConfigurationChanged(e: ViewConfigurationChangedEvent): boolean {260this._screenReaderSupport.onConfigurationChanged(e);261this._updateDomAttributes();262return true;263}264265public override onDecorationsChanged(e: ViewDecorationsChangedEvent): boolean {266// true for inline decorations that can end up relayouting text267return true;268}269270public override onFlushed(e: ViewFlushedEvent): boolean {271return true;272}273274public override onLinesChanged(e: ViewLinesChangedEvent): boolean {275this._updateEditContextOnLineChange(e.fromLineNumber, e.fromLineNumber + e.count - 1);276return true;277}278279public override onLinesDeleted(e: ViewLinesDeletedEvent): boolean {280this._updateEditContextOnLineChange(e.fromLineNumber, e.toLineNumber);281return true;282}283284public override onLinesInserted(e: ViewLinesInsertedEvent): boolean {285this._updateEditContextOnLineChange(e.fromLineNumber, e.toLineNumber);286return true;287}288289private _updateEditContextOnLineChange(fromLineNumber: number, toLineNumber: number): void {290if (this._editContextPrimarySelection.endLineNumber < fromLineNumber || this._editContextPrimarySelection.startLineNumber > toLineNumber) {291return;292}293this._updateEditContext();294}295296public override onScrollChanged(e: ViewScrollChangedEvent): boolean {297this._scrollLeft = e.scrollLeft;298this._scrollTop = e.scrollTop;299return true;300}301302public override onZonesChanged(e: ViewZonesChangedEvent): boolean {303return true;304}305306public onWillPaste(): void {307this.logService.trace('NativeEditContext#onWillPaste');308this._onWillPaste();309}310311private _onWillPaste(): void {312this._screenReaderSupport.onWillPaste();313}314315public onWillCopy(): void {316this.logService.trace('NativeEditContext#onWillCopy');317this.logService.trace('NativeEditContext#isFocused : ', this.domNode.domNode === getActiveElement());318}319320public writeScreenReaderContent(): void {321this._screenReaderSupport.writeScreenReaderContent();322}323324public isFocused(): boolean {325return this._focusTracker.isFocused;326}327328public focus(): void {329this._focusTracker.focus();330331// If the editor is off DOM, focus cannot be really set, so let's double check that we have managed to set the focus332this.refreshFocusState();333}334335public refreshFocusState(): void {336this._focusTracker.refreshFocusState();337}338339// TODO: added as a workaround fix for https://github.com/microsoft/vscode/issues/229825340// When this issue will be fixed the following should be removed.341public setEditContextOnDomNode(): void {342const targetWindow = getWindow(this.domNode.domNode);343const targetWindowId = getWindowId(targetWindow);344if (this._targetWindowId !== targetWindowId) {345this.domNode.domNode.editContext = this._editContext;346this._targetWindowId = targetWindowId;347}348}349350// --- Private methods ---351352private _onKeyUp(e: KeyboardEvent) {353this._viewController.emitKeyUp(new StandardKeyboardEvent(e));354}355356private _onKeyDown(e: KeyboardEvent) {357const standardKeyboardEvent = new StandardKeyboardEvent(e);358// When the IME is visible, the keys, like arrow-left and arrow-right, should be used to navigate in the IME, and should not be propagated further359if (standardKeyboardEvent.keyCode === KeyCode.KEY_IN_COMPOSITION) {360standardKeyboardEvent.stopPropagation();361}362this._viewController.emitKeyDown(standardKeyboardEvent);363}364365private _updateDomAttributes(): void {366const options = this._context.configuration.options;367this.domNode.domNode.setAttribute('tabindex', String(options.get(EditorOption.tabIndex)));368}369370private _updateEditContext(): void {371const editContextState = this._getNewEditContextState();372if (!editContextState) {373return;374}375this._editContext.updateText(0, Number.MAX_SAFE_INTEGER, editContextState.text ?? ' ');376this._editContext.updateSelection(editContextState.selectionStartOffset, editContextState.selectionEndOffset);377this._editContextPrimarySelection = editContextState.editContextPrimarySelection;378this._previousEditContextSelection = new OffsetRange(editContextState.selectionStartOffset, editContextState.selectionEndOffset);379}380381private _emitTypeEvent(viewController: ViewController, e: ITextUpdateEvent): void {382if (!this._editContext) {383return;384}385const selectionEndOffset = this._previousEditContextSelection.endExclusive;386const selectionStartOffset = this._previousEditContextSelection.start;387this._previousEditContextSelection = new OffsetRange(e.selectionStart, e.selectionEnd);388389let replaceNextCharCnt = 0;390let replacePrevCharCnt = 0;391if (e.updateRangeEnd > selectionEndOffset) {392replaceNextCharCnt = e.updateRangeEnd - selectionEndOffset;393}394if (e.updateRangeStart < selectionStartOffset) {395replacePrevCharCnt = selectionStartOffset - e.updateRangeStart;396}397let text = '';398if (selectionStartOffset < e.updateRangeStart) {399text += this._editContext.text.substring(selectionStartOffset, e.updateRangeStart);400}401text += e.text;402if (selectionEndOffset > e.updateRangeEnd) {403text += this._editContext.text.substring(e.updateRangeEnd, selectionEndOffset);404}405let positionDelta = 0;406if (e.selectionStart === e.selectionEnd && selectionStartOffset === selectionEndOffset) {407positionDelta = e.selectionStart - (e.updateRangeStart + e.text.length);408}409const typeInput: ITypeData = {410text,411replacePrevCharCnt,412replaceNextCharCnt,413positionDelta414};415this._onType(viewController, typeInput);416}417418private _onType(viewController: ViewController, typeInput: ITypeData): void {419if (typeInput.replacePrevCharCnt || typeInput.replaceNextCharCnt || typeInput.positionDelta) {420viewController.compositionType(typeInput.text, typeInput.replacePrevCharCnt, typeInput.replaceNextCharCnt, typeInput.positionDelta);421} else {422viewController.type(typeInput.text);423}424}425426private _getNewEditContextState(): { text: string; selectionStartOffset: number; selectionEndOffset: number; editContextPrimarySelection: Selection } | undefined {427const editContextPrimarySelection = this._primarySelection;428const model = this._context.viewModel.model;429if (!model.isValidRange(editContextPrimarySelection)) {430return;431}432const primarySelectionStartLine = editContextPrimarySelection.startLineNumber;433const primarySelectionEndLine = editContextPrimarySelection.endLineNumber;434const endColumnOfEndLineNumber = model.getLineMaxColumn(primarySelectionEndLine);435const rangeOfText = new Range(primarySelectionStartLine, 1, primarySelectionEndLine, endColumnOfEndLineNumber);436const text = model.getValueInRange(rangeOfText, EndOfLinePreference.TextDefined);437const selectionStartOffset = editContextPrimarySelection.startColumn - 1;438const selectionEndOffset = text.length + editContextPrimarySelection.endColumn - endColumnOfEndLineNumber;439return {440text,441selectionStartOffset,442selectionEndOffset,443editContextPrimarySelection444};445}446447private _editContextStartPosition(): Position {448return new Position(this._editContextPrimarySelection.startLineNumber, 1);449}450451private _handleTextFormatUpdate(e: TextFormatUpdateEvent): void {452if (!this._editContext) {453return;454}455const formats = e.getTextFormats();456const editContextStartPosition = this._editContextStartPosition();457const decorations: IModelDeltaDecoration[] = [];458formats.forEach(f => {459const textModel = this._context.viewModel.model;460const offsetOfEditContextText = textModel.getOffsetAt(editContextStartPosition);461const startPositionOfDecoration = textModel.getPositionAt(offsetOfEditContextText + f.rangeStart);462const endPositionOfDecoration = textModel.getPositionAt(offsetOfEditContextText + f.rangeEnd);463const decorationRange = Range.fromPositions(startPositionOfDecoration, endPositionOfDecoration);464const thickness = f.underlineThickness.toLowerCase();465let decorationClassName: string = CompositionClassName.NONE;466switch (thickness) {467case 'thin':468decorationClassName = CompositionClassName.SECONDARY;469break;470case 'thick':471decorationClassName = CompositionClassName.PRIMARY;472break;473}474decorations.push({475range: decorationRange,476options: {477description: 'textFormatDecoration',478inlineClassName: decorationClassName,479}480});481});482this._decorations = this._context.viewModel.model.deltaDecorations(this._decorations, decorations);483}484485private _updateSelectionAndControlBounds(ctx: RenderingContext) {486if (!this._parent) {487return;488}489const options = this._context.configuration.options;490const contentLeft = options.get(EditorOption.layoutInfo).contentLeft;491const parentBounds = this._parent.getBoundingClientRect();492const viewSelection = this._context.viewModel.coordinatesConverter.convertModelRangeToViewRange(this._primarySelection);493const verticalOffsetStart = this._context.viewLayout.getVerticalOffsetForLineNumber(viewSelection.startLineNumber);494495const top = parentBounds.top + verticalOffsetStart - this._scrollTop;496const verticalOffsetEnd = this._context.viewLayout.getVerticalOffsetAfterLineNumber(viewSelection.endLineNumber);497const height = verticalOffsetEnd - verticalOffsetStart;498let left = parentBounds.left + contentLeft - this._scrollLeft;499let width: number;500501if (this._primarySelection.isEmpty()) {502const linesVisibleRanges = ctx.visibleRangeForPosition(viewSelection.getStartPosition());503if (linesVisibleRanges) {504left += linesVisibleRanges.left;505}506width = 0;507} else {508width = parentBounds.width - contentLeft;509}510511const selectionBounds = new DOMRect(left, top, width, height);512this._editContext.updateSelectionBounds(selectionBounds);513this._editContext.updateControlBounds(selectionBounds);514}515516private _updateCharacterBounds(e: CharacterBoundsUpdateEvent): void {517if (!this._parent) {518return;519}520const options = this._context.configuration.options;521const typicalHalfWidthCharacterWidth = options.get(EditorOption.fontInfo).typicalHalfwidthCharacterWidth;522const contentLeft = options.get(EditorOption.layoutInfo).contentLeft;523const parentBounds = this._parent.getBoundingClientRect();524525const characterBounds: DOMRect[] = [];526const offsetTransformer = new PositionOffsetTransformer(this._editContext.text);527for (let offset = e.rangeStart; offset < e.rangeEnd; offset++) {528const editContextStartPosition = offsetTransformer.getPosition(offset);529const textStartLineOffsetWithinEditor = this._editContextPrimarySelection.startLineNumber - 1;530const characterStartPosition = new Position(textStartLineOffsetWithinEditor + editContextStartPosition.lineNumber, editContextStartPosition.column);531const characterEndPosition = characterStartPosition.delta(0, 1);532const characterModelRange = Range.fromPositions(characterStartPosition, characterEndPosition);533const characterViewRange = this._context.viewModel.coordinatesConverter.convertModelRangeToViewRange(characterModelRange);534const characterLinesVisibleRanges = this._visibleRangeProvider.linesVisibleRangesForRange(characterViewRange, true) ?? [];535const lineNumber = characterViewRange.startLineNumber;536const characterVerticalOffset = this._context.viewLayout.getVerticalOffsetForLineNumber(lineNumber);537const top = parentBounds.top + characterVerticalOffset - this._scrollTop;538539let left = 0;540let width = typicalHalfWidthCharacterWidth;541if (characterLinesVisibleRanges.length > 0) {542for (const visibleRange of characterLinesVisibleRanges[0].ranges) {543left = visibleRange.left;544width = visibleRange.width;545break;546}547}548const lineHeight = this._context.viewLayout.getLineHeightForLineNumber(lineNumber);549characterBounds.push(new DOMRect(parentBounds.left + contentLeft + left - this._scrollLeft, top, width, lineHeight));550}551this._editContext.updateCharacterBounds(e.rangeStart, characterBounds);552}553554private _ensureClipboardGetsEditorSelection(e: ClipboardEvent): void {555const options = this._context.configuration.options;556const emptySelectionClipboard = options.get(EditorOption.emptySelectionClipboard);557const copyWithSyntaxHighlighting = options.get(EditorOption.copyWithSyntaxHighlighting);558const selections = this._context.viewModel.getCursorStates().map(cursorState => cursorState.modelState.selection);559const dataToCopy = getDataToCopy(this._context.viewModel, selections, emptySelectionClipboard, copyWithSyntaxHighlighting);560let id = undefined;561if (this.logService.getLevel() === LogLevel.Trace) {562id = generateUuid();563}564const storedMetadata: ClipboardStoredMetadata = {565version: 1,566id,567isFromEmptySelection: dataToCopy.isFromEmptySelection,568multicursorText: dataToCopy.multicursorText,569mode: dataToCopy.mode570};571InMemoryClipboardMetadataManager.INSTANCE.set(572// When writing "LINE\r\n" to the clipboard and then pasting,573// Firefox pastes "LINE\n", so let's work around this quirk574(isFirefox ? dataToCopy.text.replace(/\r\n/g, '\n') : dataToCopy.text),575storedMetadata576);577e.preventDefault();578if (e.clipboardData) {579ClipboardEventUtils.setTextData(e.clipboardData, dataToCopy.text, dataToCopy.html, storedMetadata);580}581this.logService.trace('NativeEditContext#_ensureClipboardGetsEditorSelectios with id : ', id, ' with text.length: ', dataToCopy.text.length);582}583}584585586