Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@

import './nativeEditContext.css';
import { isFirefox } from '../../../../../base/browser/browser.js';
import { addDisposableListener, getActiveElement, getWindow, getWindowId } from '../../../../../base/browser/dom.js';
import { addDisposableListener, getActiveElement, getWindow, getWindowId, scheduleAtNextAnimationFrame } from '../../../../../base/browser/dom.js';
import { FastDomNode } from '../../../../../base/browser/fastDomNode.js';
import { StandardKeyboardEvent } from '../../../../../base/browser/keyboardEvent.js';
import { KeyCode } from '../../../../../base/common/keyCodes.js';
import { IDisposable } from '../../../../../base/common/lifecycle.js';
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
import { EditorOption } from '../../../../common/config/editorOptions.js';
import { EndOfLinePreference, IModelDeltaDecoration } from '../../../../common/model.js';
Expand Down Expand Up @@ -68,6 +69,7 @@ export class NativeEditContext extends AbstractEditContext {
private _targetWindowId: number = -1;
private _scrollTop: number = 0;
private _scrollLeft: number = 0;
private _selectionAndControlBoundsUpdateDisposable: IDisposable | undefined;
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The field _selectionAndControlBoundsUpdateDisposable uses undefined as the sentinel value, while the analogous field _scheduledRender in TextAreaEditContext uses null. For consistency across these related implementations, consider using the same sentinel value (either null or undefined) in both files.

Suggested change
private _selectionAndControlBoundsUpdateDisposable: IDisposable | undefined;
private _selectionAndControlBoundsUpdateDisposable: IDisposable | null = null;
Copilot uses AI. Check for mistakes.

private readonly _focusTracker: FocusTracker;

Expand Down Expand Up @@ -217,6 +219,8 @@ export class NativeEditContext extends AbstractEditContext {
this.domNode.domNode.blur();
this.domNode.domNode.remove();
this._imeTextArea.domNode.remove();
this._selectionAndControlBoundsUpdateDisposable?.dispose();
this._selectionAndControlBoundsUpdateDisposable = undefined;
super.dispose();
}

Expand Down Expand Up @@ -489,7 +493,19 @@ export class NativeEditContext extends AbstractEditContext {
}
}

private _updateSelectionAndControlBoundsAfterRender() {
private _updateSelectionAndControlBoundsAfterRender(): void {
if (this._selectionAndControlBoundsUpdateDisposable) {
return;
}
// Schedule this work after render so we avoid triggering a layout while still painting.
const targetWindow = getWindow(this.domNode.domNode);
this._selectionAndControlBoundsUpdateDisposable = scheduleAtNextAnimationFrame(targetWindow, () => {
this._selectionAndControlBoundsUpdateDisposable = undefined;
this._applySelectionAndControlBounds();
});
}

private _applySelectionAndControlBounds(): void {
const options = this._context.configuration.options;
const contentLeft = options.get(EditorOption.layoutInfo).contentLeft;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import './textAreaEditContext.css';
import * as nls from '../../../../../nls.js';
import * as browser from '../../../../../base/browser/browser.js';
import { scheduleAtNextAnimationFrame, getWindow } from '../../../../../base/browser/dom.js';
import { FastDomNode, createFastDomNode } from '../../../../../base/browser/fastDomNode.js';
import { IKeyboardEvent } from '../../../../../base/browser/keyboardEvent.js';
import * as platform from '../../../../../base/common/platform.js';
Expand All @@ -31,6 +32,7 @@ import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from '../../../../../base/browser/ui
import { TokenizationRegistry } from '../../../../common/languages.js';
import { ColorId, ITokenPresentation } from '../../../../common/encodedTokenAttributes.js';
import { Color } from '../../../../../base/common/color.js';
import { IDisposable } from '../../../../../base/common/lifecycle.js';
import { IME } from '../../../../../base/common/ime.js';
import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js';
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
Expand Down Expand Up @@ -139,6 +141,7 @@ export class TextAreaEditContext extends AbstractEditContext {
* This is useful for hit-testing and determining the mouse position.
*/
private _lastRenderPosition: Position | null;
private _scheduledRender: IDisposable | null = null;

public readonly textArea: FastDomNode<HTMLTextAreaElement>;
public readonly textAreaCover: FastDomNode<HTMLElement>;
Expand Down Expand Up @@ -450,6 +453,8 @@ export class TextAreaEditContext extends AbstractEditContext {
}

public override dispose(): void {
this._scheduledRender?.dispose();
this._scheduledRender = null;
super.dispose();
this.textArea.domNode.remove();
this.textAreaCover.domNode.remove();
Expand Down Expand Up @@ -672,7 +677,20 @@ export class TextAreaEditContext extends AbstractEditContext {

public render(ctx: RestrictedRenderingContext): void {
this._textAreaInput.writeNativeTextAreaContent('render');
this._render();
this._scheduleRender();
}

// Delay expensive DOM updates until the next animation frame to reduce reflow pressure.
private _scheduleRender(): void {
if (this._scheduledRender) {
return;
}

const targetWindow = getWindow(this.textArea.domNode);
this._scheduledRender = scheduleAtNextAnimationFrame(targetWindow, () => {
this._scheduledRender = null;
this._render();
});
}

private _render(): void {
Expand Down
Loading