Remove more parts

This commit is contained in:
Daniel Imms
2026-02-01 22:22:29 -08:00
parent beebe8b5dc
commit d5900c7eae
30 changed files with 136 additions and 1417 deletions
+2 -2
View File
@@ -11,7 +11,7 @@ import { ScrollbarArrow, ScrollbarArrowOptions } from './scrollbarArrow';
import { ScrollbarState } from './scrollbarState';
import { ScrollbarVisibilityController } from './scrollbarVisibilityController';
import { Widget } from './widget';
import * as platform from './platform';
import * as platform from 'common/Platform';
import { INewScrollPosition, Scrollable, ScrollbarVisibility } from './scrollable';
/**
@@ -31,7 +31,7 @@ export interface ScrollbarHost {
onDragEnd(): void;
}
export interface AbstractScrollbarOptions {
interface AbstractScrollbarOptions {
lazyRender: boolean;
host: ScrollbarHost;
scrollbarState: ScrollbarState;
-8
View File
@@ -6,11 +6,3 @@
export function tail<T>(array: ArrayLike<T>, n: number = 0): T | undefined {
return array[array.length - (1 + n)];
}
export function tail2<T>(arr: T[]): [T[], T] {
if (arr.length === 0) {
throw new Error('Invalid tail call');
}
return [arr.slice(0, arr.length - 1), arr[arr.length - 1]];
}
-78
View File
@@ -1,78 +0,0 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { IDisposable } from './lifecycle';
export class TimeoutTimer implements IDisposable {
private _token: any = -1;
private _isDisposed = false;
dispose(): void {
this.cancel();
this._isDisposed = true;
}
cancel(): void {
if (this._token !== -1) {
clearTimeout(this._token);
this._token = -1;
}
}
cancelAndSet(runner: () => void, timeout: number): void {
if (this._isDisposed) {
throw new Error('Calling cancelAndSet on a disposed TimeoutTimer');
}
this.cancel();
this._token = setTimeout(() => {
this._token = -1;
runner();
}, timeout);
}
setIfNotSet(runner: () => void, timeout: number): void {
if (this._isDisposed) {
throw new Error('Calling setIfNotSet on a disposed TimeoutTimer');
}
if (this._token !== -1) {
return;
}
this._token = setTimeout(() => {
this._token = -1;
runner();
}, timeout);
}
}
export class IntervalTimer implements IDisposable {
private _disposable: IDisposable | undefined;
private _isDisposed = false;
cancel(): void {
this._disposable?.dispose();
this._disposable = undefined;
}
cancelAndSet(runner: () => void, interval: number, context: Window | typeof globalThis = globalThis): void {
if (this._isDisposed) {
throw new Error('Calling cancelAndSet on a disposed IntervalTimer');
}
this.cancel();
const handle = context.setInterval(() => {
runner();
}, interval);
this._disposable = {
dispose: () => {
context.clearInterval(handle as any);
this._disposable = undefined;
}
};
}
dispose(): void {
this.cancel();
this._isDisposed = true;
}
}
+2 -129
View File
@@ -3,139 +3,12 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CodeWindow, mainWindow } from './window';
import { Emitter } from './event';
class WindowManager {
static readonly INSTANCE = new WindowManager();
// --- Zoom Level
private readonly mapWindowIdToZoomLevel = new Map<number, number>();
private readonly _onDidChangeZoomLevel = new Emitter<number>();
readonly onDidChangeZoomLevel = this._onDidChangeZoomLevel.event;
getZoomLevel(targetWindow: Window): number {
return this.mapWindowIdToZoomLevel.get(this.getWindowId(targetWindow)) ?? 0;
}
setZoomLevel(zoomLevel: number, targetWindow: Window): void {
if (this.getZoomLevel(targetWindow) === zoomLevel) {
return;
}
const targetWindowId = this.getWindowId(targetWindow);
this.mapWindowIdToZoomLevel.set(targetWindowId, zoomLevel);
this._onDidChangeZoomLevel.fire(targetWindowId);
}
// --- Zoom Factor
private readonly mapWindowIdToZoomFactor = new Map<number, number>();
getZoomFactor(targetWindow: Window): number {
return this.mapWindowIdToZoomFactor.get(this.getWindowId(targetWindow)) ?? 1;
}
setZoomFactor(zoomFactor: number, targetWindow: Window): void {
this.mapWindowIdToZoomFactor.set(this.getWindowId(targetWindow), zoomFactor);
}
// --- Fullscreen
private readonly _onDidChangeFullscreen = new Emitter<number>();
readonly onDidChangeFullscreen = this._onDidChangeFullscreen.event;
private readonly mapWindowIdToFullScreen = new Map<number, boolean>();
setFullscreen(fullscreen: boolean, targetWindow: Window): void {
if (this.isFullscreen(targetWindow) === fullscreen) {
return;
}
const windowId = this.getWindowId(targetWindow);
this.mapWindowIdToFullScreen.set(windowId, fullscreen);
this._onDidChangeFullscreen.fire(windowId);
}
isFullscreen(targetWindow: Window): boolean {
return !!this.mapWindowIdToFullScreen.get(this.getWindowId(targetWindow));
}
private getWindowId(targetWindow: Window): number {
return (targetWindow as CodeWindow).vscodeWindowId;
}
}
export function addMatchMediaChangeListener(targetWindow: Window, query: string | MediaQueryList, callback: (this: MediaQueryList, ev: MediaQueryListEvent) => any): void {
if (typeof query === 'string') {
query = targetWindow.matchMedia(query);
}
query.addEventListener('change', callback);
}
/** A zoom index, e.g. 1, 2, 3 */
export function setZoomLevel(zoomLevel: number, targetWindow: Window): void {
WindowManager.INSTANCE.setZoomLevel(zoomLevel, targetWindow);
}
export function getZoomLevel(targetWindow: Window): number {
return WindowManager.INSTANCE.getZoomLevel(targetWindow);
}
export const onDidChangeZoomLevel = WindowManager.INSTANCE.onDidChangeZoomLevel;
/** The zoom scale for an index, e.g. 1, 1.2, 1.4 */
export function getZoomFactor(targetWindow: Window): number {
return WindowManager.INSTANCE.getZoomFactor(targetWindow);
}
export function setZoomFactor(zoomFactor: number, targetWindow: Window): void {
WindowManager.INSTANCE.setZoomFactor(zoomFactor, targetWindow);
}
export function setFullscreen(fullscreen: boolean, targetWindow: Window): void {
WindowManager.INSTANCE.setFullscreen(fullscreen, targetWindow);
}
export function isFullscreen(targetWindow: Window): boolean {
return WindowManager.INSTANCE.isFullscreen(targetWindow);
}
export const onDidChangeFullscreen = WindowManager.INSTANCE.onDidChangeFullscreen;
const userAgent = typeof navigator === 'object' ? navigator.userAgent : '';
export const isFirefox = (userAgent.indexOf('Firefox') >= 0);
export const isWebKit = (userAgent.indexOf('AppleWebKit') >= 0);
export const isChrome = (userAgent.indexOf('Chrome') >= 0);
export const isSafari = (!isChrome && (userAgent.indexOf('Safari') >= 0));
export const isWebkitWebView = (!isChrome && !isSafari && isWebKit);
export const isElectron = (userAgent.indexOf('Electron/') >= 0);
export const isAndroid = (userAgent.indexOf('Android') >= 0);
let standalone = false;
if (typeof mainWindow.matchMedia === 'function') {
const standaloneMatchMedia = mainWindow.matchMedia('(display-mode: standalone) or (display-mode: window-controls-overlay)');
const fullScreenMatchMedia = mainWindow.matchMedia('(display-mode: fullscreen)');
standalone = standaloneMatchMedia.matches;
addMatchMediaChangeListener(mainWindow, standaloneMatchMedia, ({ matches }) => {
// entering fullscreen would change standaloneMatchMedia.matches to false
// if standalone is true (running as PWA) and entering fullscreen, skip this change
if (standalone && fullScreenMatchMedia.matches) {
return;
}
// otherwise update standalone (browser to PWA or PWA to browser)
standalone = matches;
});
}
export function isStandalone(): boolean {
return standalone;
}
// Visible means that the feature is enabled, not necessarily being rendered
// e.g. visible is true even in fullscreen mode where the controls are hidden
// See docs at https://developer.mozilla.org/en-US/docs/Web/API/WindowControlsOverlay/visible
export function isWCOEnabled(): boolean {
return (navigator as any)?.windowControlsOverlay?.visible;
}
// Returns the bounding rect of the titlebar area if it is supported and defined
// See docs at https://developer.mozilla.org/en-US/docs/Web/API/WindowControlsOverlay/getTitlebarAreaRect
export function getWCOBoundingRect(): DOMRect | undefined {
return (navigator as any)?.windowControlsOverlay?.getTitlebarAreaRect();
export function getZoomFactor(_targetWindow: Window): number {
return 1;
}
-140
View File
@@ -1,140 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* An interface for a JavaScript object that
* acts a dictionary. The keys are strings.
*/
export type IStringDictionary<V> = Record<string, V>;
/**
* An interface for a JavaScript object that
* acts a dictionary. The keys are numbers.
*/
export type INumberDictionary<V> = Record<number, V>;
/**
* Groups the collection into a dictionary based on the provided
* group function.
*/
export function groupBy<K extends string | number | symbol, V>(data: V[], groupFn: (element: V) => K): Record<K, V[]> {
const result: Record<K, V[]> = Object.create(null);
for (const element of data) {
const key = groupFn(element);
let target = result[key];
if (!target) {
target = result[key] = [];
}
target.push(element);
}
return result;
}
export function diffSets<T>(before: Set<T>, after: Set<T>): { removed: T[], added: T[] } {
const removed: T[] = [];
const added: T[] = [];
for (const element of before) {
if (!after.has(element)) {
removed.push(element);
}
}
for (const element of after) {
if (!before.has(element)) {
added.push(element);
}
}
return { removed, added };
}
export function diffMaps<K, V>(before: Map<K, V>, after: Map<K, V>): { removed: V[], added: V[] } {
const removed: V[] = [];
const added: V[] = [];
for (const [index, value] of before) {
if (!after.has(index)) {
removed.push(value);
}
}
for (const [index, value] of after) {
if (!before.has(index)) {
added.push(value);
}
}
return { removed, added };
}
/**
* Computes the intersection of two sets.
*
* @param setA - The first set.
* @param setB - The second iterable.
* @returns A new set containing the elements that are in both `setA` and `setB`.
*/
export function intersection<T>(setA: Set<T>, setB: Iterable<T>): Set<T> {
const result = new Set<T>();
for (const elem of setB) {
if (setA.has(elem)) {
result.add(elem);
}
}
return result;
}
export class SetWithKey<T> implements Set<T> {
private _map = new Map<any, T>();
constructor(values: T[], private toKey: (t: T) => any) {
for (const value of values) {
this.add(value);
}
}
get size(): number {
return this._map.size;
}
add(value: T): this {
const key = this.toKey(value);
this._map.set(key, value);
return this;
}
delete(value: T): boolean {
return this._map.delete(this.toKey(value));
}
has(value: T): boolean {
return this._map.has(this.toKey(value));
}
*entries(): IterableIterator<[T, T]> {
for (const entry of this._map.values()) {
yield [entry, entry];
}
}
keys(): IterableIterator<T> {
return this.values();
}
*values(): IterableIterator<T> {
for (const entry of this._map.values()) {
yield entry;
}
}
clear(): void {
this._map.clear();
}
forEach(callbackfn: (value: T, value2: T, set: Set<T>) => void, thisArg?: any): void {
this._map.forEach(entry => callbackfn.call(thisArg, entry, entry, this));
}
[Symbol.iterator](): IterableIterator<T> {
return this.values();
}
[Symbol.toStringTag]: string = 'SetWithKey';
}
-90
View File
@@ -3,27 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
function createDecorator(mapFn: (fn: Function, key: string) => Function): Function {
return (target: any, key: string, descriptor: any) => {
let fnKey: string | null = null;
let fn: Function | null = null;
if (typeof descriptor.value === 'function') {
fnKey = 'value';
fn = descriptor.value;
} else if (typeof descriptor.get === 'function') {
fnKey = 'get';
fn = descriptor.get;
}
if (!fn) {
throw new Error('not supported');
}
descriptor[fnKey!] = mapFn(fn, key);
};
}
export function memoize(_target: any, key: string, descriptor: any) {
let fnKey: string | null = null;
let fn: Function | null = null;
@@ -59,72 +38,3 @@ export function memoize(_target: any, key: string, descriptor: any) {
};
}
export interface IDebounceReducer<T> {
(previousValue: T, ...args: any[]): T;
}
export function debounce<T>(delay: number, reducer?: IDebounceReducer<T>, initialValueProvider?: () => T): Function {
return createDecorator((fn, key) => {
const timerKey = `$debounce$${key}`;
const resultKey = `$debounce$result$${key}`;
return function (this: any, ...args: any[]) {
if (!this[resultKey]) {
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
}
clearTimeout(this[timerKey]);
if (reducer) {
this[resultKey] = reducer(this[resultKey], ...args);
args = [this[resultKey]];
}
this[timerKey] = setTimeout(() => {
fn.apply(this, args);
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
}, delay);
};
});
}
export function throttle<T>(delay: number, reducer?: IDebounceReducer<T>, initialValueProvider?: () => T): Function {
return createDecorator((fn, key) => {
const timerKey = `$throttle$timer$${key}`;
const resultKey = `$throttle$result$${key}`;
const lastRunKey = `$throttle$lastRun$${key}`;
const pendingKey = `$throttle$pending$${key}`;
return function (this: any, ...args: any[]) {
if (!this[resultKey]) {
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
}
if (this[lastRunKey] === null || this[lastRunKey] === undefined) {
this[lastRunKey] = -Number.MAX_VALUE;
}
if (reducer) {
this[resultKey] = reducer(this[resultKey], ...args);
}
if (this[pendingKey]) {
return;
}
const nextTime = this[lastRunKey] + delay;
if (nextTime <= Date.now()) {
this[lastRunKey] = Date.now();
fn.apply(this, [this[resultKey]]);
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
} else {
this[pendingKey] = true;
this[timerKey] = setTimeout(() => {
this[pendingKey] = false;
this[lastRunKey] = Date.now();
fn.apply(this, [this[resultKey]]);
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
}, nextTime - Date.now());
}
};
});
}
+5 -12
View File
@@ -3,9 +3,9 @@
* @license MIT
*/
import { IntervalTimer } from './async';
import { Emitter, Event } from './event';
import { DisposableStore, IDisposable } from './lifecycle';
import { IntervalTimer } from 'common/Async';
import { Emitter, IEvent } from 'common/Event';
import { DisposableStore, IDisposable } from 'common/Lifecycle';
export interface IRegisteredWindow {
readonly window: Window;
@@ -13,7 +13,7 @@ export interface IRegisteredWindow {
}
const _onDidRegisterWindow = new Emitter<IRegisteredWindow>();
export const onDidRegisterWindow: Event<IRegisteredWindow> = _onDidRegisterWindow.event;
export const onDidRegisterWindow: IEvent<IRegisteredWindow> = _onDidRegisterWindow.event;
export function registerWindow(window: Window): IDisposable {
const disposables = new DisposableStore();
@@ -88,14 +88,7 @@ export const EventType = {
WHEEL: 'wheel'
} as const;
export interface IDomNodePagePosition {
left: number;
top: number;
width: number;
height: number;
}
export function getDomNodePagePosition(domNode: HTMLElement): IDomNodePagePosition {
export function getDomNodePagePosition(domNode: HTMLElement): { left: number, top: number, width: number, height: number } {
const bb = domNode.getBoundingClientRect();
const win = getWindow(domNode);
return {
-85
View File
@@ -1,85 +0,0 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*
* Minimal event utilities for scrollable components.
*/
import { Disposable, DisposableStore, IDisposable, toDisposable } from './lifecycle';
export interface Event<T> {
(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore): IDisposable;
}
export class Emitter<T> {
private _listeners: { fn: (e: T) => any, thisArgs: any }[] = [];
private _disposed = false;
private _event: Event<T> | undefined;
public get event(): Event<T> {
if (this._event) {
return this._event;
}
this._event = (listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore) => {
if (this._disposed) {
return Disposable.None;
}
const entry = { fn: listener, thisArgs };
this._listeners.push(entry);
const result = toDisposable(() => {
const idx = this._listeners.indexOf(entry);
if (idx !== -1) {
this._listeners.splice(idx, 1);
}
});
if (disposables) {
if (Array.isArray(disposables)) {
disposables.push(result);
} else {
disposables.add(result);
}
}
return result;
};
return this._event;
}
public fire(event: T): void {
if (this._disposed || this._listeners.length === 0) {
return;
}
if (this._listeners.length === 1) {
const { fn, thisArgs } = this._listeners[0];
fn.call(thisArgs, event);
return;
}
const listeners = this._listeners.slice();
for (const { fn, thisArgs } of listeners) {
fn.call(thisArgs, event);
}
}
public dispose(): void {
if (this._disposed) {
return;
}
this._disposed = true;
this._listeners.length = 0;
}
}
export namespace Event {
export const None: Event<any> = () => Disposable.None;
export function runAndSubscribe<T>(event: Event<T>, handler: (e: T) => void, initial: T): IDisposable;
export function runAndSubscribe<T>(event: Event<T>, handler: (e: T | undefined) => void): IDisposable;
export function runAndSubscribe<T>(event: Event<T>, handler: (e: T | undefined) => void, initial?: T): IDisposable {
handler(initial);
return event(e => handler(e));
}
}
-32
View File
@@ -1,32 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* Given a function, returns a function that is only calling that function once.
*/
export function createSingleCallFunction<T extends Function>(this: unknown, fn: T, fnDidRunCallback?: () => void): T {
const _this = this;
let didCall = false;
let result: unknown;
return function () {
if (didCall) {
return result;
}
didCall = true;
if (fnDidRunCallback) {
try {
result = fn.apply(_this, arguments);
} finally {
fnDidRunCallback();
}
} else {
result = fn.apply(_this, arguments);
}
return result;
} as unknown as T;
}
@@ -4,21 +4,16 @@
*--------------------------------------------------------------------------------------------*/
import * as dom from './dom';
import { DisposableStore, IDisposable, toDisposable } from './lifecycle';
import { DisposableStore, IDisposable, toDisposable } from 'common/Lifecycle';
export interface IPointerMoveCallback {
(event: PointerEvent): void;
}
export interface IOnStopCallback {
(browserEvent?: PointerEvent | KeyboardEvent): void;
}
type PointerMoveCallback = (event: PointerEvent) => void;
type OnStopCallback = (browserEvent?: PointerEvent | KeyboardEvent) => void;
export class GlobalPointerMoveMonitor implements IDisposable {
private readonly _hooks = new DisposableStore();
private _pointerMoveCallback: IPointerMoveCallback | null = null;
private _onStopCallback: IOnStopCallback | null = null;
private _pointerMoveCallback: PointerMoveCallback | null = null;
private _onStopCallback: OnStopCallback | null = null;
public dispose(): void {
this.stopMonitoring(false);
@@ -48,8 +43,8 @@ export class GlobalPointerMoveMonitor implements IDisposable {
initialElement: Element,
pointerId: number,
initialButtons: number,
pointerMoveCallback: IPointerMoveCallback,
onStopCallback: IOnStopCallback
pointerMoveCallback: PointerMoveCallback,
onStopCallback: OnStopCallback
): void {
if (this.isMonitoring()) {
this.stopMonitoring(false);
-19
View File
@@ -114,22 +114,3 @@ export class IframeUtils {
/**
* Returns a sha-256 composed of `parentOrigin` and `salt` converted to base 32
*/
export async function parentOriginHash(parentOrigin: string, salt: string): Promise<string> {
// This same code is also inlined at `src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html`
if (!crypto.subtle) {
throw new Error(`'crypto.subtle' is not available so webviews will not work. This is likely because the editor is not running in a secure context (https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).`);
}
const strData = JSON.stringify({ parentOrigin, salt });
const encoder = new TextEncoder();
const arrData = encoder.encode(strData);
const hash = await crypto.subtle.digest('sha-256', arrData);
return sha256AsBase32(hash);
}
function sha256AsBase32(bytes: ArrayBuffer): string {
const array = Array.from(new Uint8Array(bytes));
const hexArray = array.map(b => b.toString(16).padStart(2, '0')).join('');
// sha256 has 256 bits, so we need at most ceil(lg(2^256-1)/lg(32)) = 52 chars to represent it in base 32
return BigInt(`0x${hexArray}`).toString(32).padStart(52, '0');
}
-159
View File
@@ -1,159 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export namespace Iterable {
export function is<T = any>(thing: any): thing is Iterable<T> {
return thing && typeof thing === 'object' && typeof thing[Symbol.iterator] === 'function';
}
const _empty: Iterable<any> = Object.freeze([]);
export function empty<T = any>(): Iterable<T> {
return _empty;
}
export function* single<T>(element: T): Iterable<T> {
yield element;
}
export function wrap<T>(iterableOrElement: Iterable<T> | T): Iterable<T> {
if (is(iterableOrElement)) {
return iterableOrElement;
}
return single(iterableOrElement);
}
export function from<T>(iterable: Iterable<T> | undefined | null): Iterable<T> {
return iterable || _empty;
}
export function* reverse<T>(array: T[]): Iterable<T> {
for (let i = array.length - 1; i >= 0; i--) {
yield array[i];
}
}
export function isEmpty<T>(iterable: Iterable<T> | undefined | null): boolean {
return !iterable || iterable[Symbol.iterator]().next().done === true;
}
export function first<T>(iterable: Iterable<T>): T | undefined {
return iterable[Symbol.iterator]().next().value;
}
export function some<T>(iterable: Iterable<T>, predicate: (t: T, i: number) => unknown): boolean {
let i = 0;
for (const element of iterable) {
if (predicate(element, i++)) {
return true;
}
}
return false;
}
export function find<T, R extends T>(iterable: Iterable<T>, predicate: (t: T) => t is R): R | undefined;
export function find<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): T | undefined;
export function find<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): T | undefined {
for (const element of iterable) {
if (predicate(element)) {
return element;
}
}
return undefined;
}
export function filter<T, R extends T>(iterable: Iterable<T>, predicate: (t: T) => t is R): Iterable<R>;
export function filter<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): Iterable<T>;
export function* filter<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): Iterable<T> {
for (const element of iterable) {
if (predicate(element)) {
yield element;
}
}
}
export function* map<T, R>(iterable: Iterable<T>, fn: (t: T, index: number) => R): Iterable<R> {
let index = 0;
for (const element of iterable) {
yield fn(element, index++);
}
}
export function* flatMap<T, R>(iterable: Iterable<T>, fn: (t: T, index: number) => Iterable<R>): Iterable<R> {
let index = 0;
for (const element of iterable) {
yield* fn(element, index++);
}
}
export function* concat<T>(...iterables: Iterable<T>[]): Iterable<T> {
for (const iterable of iterables) {
yield* iterable;
}
}
export function reduce<T, R>(iterable: Iterable<T>, reducer: (previousValue: R, currentValue: T) => R, initialValue: R): R {
let value = initialValue;
for (const element of iterable) {
value = reducer(value, element);
}
return value;
}
/**
* Returns an iterable slice of the array, with the same semantics as `array.slice()`.
*/
export function* slice<T>(arr: ReadonlyArray<T>, from: number, to = arr.length): Iterable<T> {
if (from < 0) {
from += arr.length;
}
if (to < 0) {
to += arr.length;
} else if (to > arr.length) {
to = arr.length;
}
for (; from < to; from++) {
yield arr[from];
}
}
/**
* Consumes `atMost` elements from iterable and returns the consumed elements,
* and an iterable for the rest of the elements.
*/
export function consume<T>(iterable: Iterable<T>, atMost: number = Number.POSITIVE_INFINITY): [T[], Iterable<T>] {
const consumed: T[] = [];
if (atMost === 0) {
return [consumed, iterable];
}
const iterator = iterable[Symbol.iterator]();
for (let i = 0; i < atMost; i++) {
const next = iterator.next();
if (next.done) {
return [consumed, Iterable.empty()];
}
consumed.push(next.value);
}
return [consumed, { [Symbol.iterator]() { return iterator; } }];
}
export async function asyncToArray<T>(iterable: AsyncIterable<T>): Promise<T[]> {
const result: T[] = [];
for await (const item of iterable) {
result.push(item);
}
return Promise.resolve(result);
}
}
-112
View File
@@ -1,112 +0,0 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*
* Minimal lifecycle utilities for scrollable components.
*/
export interface IDisposable {
dispose(): void;
}
export function toDisposable(fn: () => void): IDisposable {
return { dispose: fn };
}
export function dispose<T extends IDisposable>(disposable: T): T;
export function dispose<T extends IDisposable>(disposable: T | undefined): T | undefined;
export function dispose<T extends IDisposable>(disposables: T[]): T[];
export function dispose<T extends IDisposable>(arg: T | T[] | undefined): T | T[] | undefined {
if (!arg) {
return arg;
}
if (Array.isArray(arg)) {
for (const d of arg) {
d.dispose();
}
return [];
}
arg.dispose();
return arg;
}
export function combinedDisposable(...disposables: IDisposable[]): IDisposable {
return toDisposable(() => dispose(disposables));
}
export class DisposableStore implements IDisposable {
private readonly _disposables = new Set<IDisposable>();
private _isDisposed = false;
public add<T extends IDisposable>(o: T): T {
if (this._isDisposed) {
o.dispose();
} else {
this._disposables.add(o);
}
return o;
}
public dispose(): void {
if (this._isDisposed) {
return;
}
this._isDisposed = true;
for (const d of this._disposables) {
d.dispose();
}
this._disposables.clear();
}
public clear(): void {
for (const d of this._disposables) {
d.dispose();
}
this._disposables.clear();
}
}
export abstract class Disposable implements IDisposable {
static readonly None: IDisposable = Object.freeze({ dispose() { } });
protected readonly _store = new DisposableStore();
public dispose(): void {
this._store.dispose();
}
protected _register<T extends IDisposable>(o: T): T {
return this._store.add(o);
}
}
export function markAsSingleton<T extends IDisposable>(singleton: T): T {
return singleton;
}
export class MutableDisposable<T extends IDisposable> implements IDisposable {
private _value: T | undefined;
private _isDisposed = false;
public get value(): T | undefined {
return this._isDisposed ? undefined : this._value;
}
public set value(value: T | undefined) {
if (this._isDisposed || value === this._value) {
return;
}
this._value?.dispose();
this._value = value;
}
public clear(): void {
this.value = undefined;
}
public dispose(): void {
this._isDisposed = true;
this._value?.dispose();
this._value = undefined;
}
}
-202
View File
@@ -1,202 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export function getOrSet<K, V>(map: Map<K, V>, key: K, value: V): V {
let result = map.get(key);
if (result === undefined) {
result = value;
map.set(key, result);
}
return result;
}
export function mapToString<K, V>(map: Map<K, V>): string {
const entries: string[] = [];
map.forEach((value, key) => {
entries.push(`${key} => ${value}`);
});
return `Map(${map.size}) {${entries.join(', ')}}`;
}
export function setToString<K>(set: Set<K>): string {
const entries: K[] = [];
set.forEach(value => {
entries.push(value);
});
return `Set(${set.size}) {${entries.join(', ')}}`;
}
export const enum Touch {
None = 0,
AsOld = 1,
AsNew = 2
}
export class CounterSet<T> {
private map = new Map<T, number>();
add(value: T): CounterSet<T> {
this.map.set(value, (this.map.get(value) || 0) + 1);
return this;
}
delete(value: T): boolean {
let counter = this.map.get(value) || 0;
if (counter === 0) {
return false;
}
counter--;
if (counter === 0) {
this.map.delete(value);
} else {
this.map.set(value, counter);
}
return true;
}
has(value: T): boolean {
return this.map.has(value);
}
}
/**
* A map that allows access both by keys and values.
* **NOTE**: values need to be unique.
*/
export class BidirectionalMap<K, V> {
private readonly _m1 = new Map<K, V>();
private readonly _m2 = new Map<V, K>();
constructor(entries?: ReadonlyArray<readonly [K, V]>) {
if (entries) {
for (const [key, value] of entries) {
this.set(key, value);
}
}
}
clear(): void {
this._m1.clear();
this._m2.clear();
}
set(key: K, value: V): void {
this._m1.set(key, value);
this._m2.set(value, key);
}
get(key: K): V | undefined {
return this._m1.get(key);
}
getKey(value: V): K | undefined {
return this._m2.get(value);
}
delete(key: K): boolean {
const value = this._m1.get(key);
if (value === undefined) {
return false;
}
this._m1.delete(key);
this._m2.delete(value);
return true;
}
forEach(callbackfn: (value: V, key: K, map: BidirectionalMap<K, V>) => void, thisArg?: any): void {
this._m1.forEach((value, key) => {
callbackfn.call(thisArg, value, key, this);
});
}
keys(): IterableIterator<K> {
return this._m1.keys();
}
values(): IterableIterator<V> {
return this._m1.values();
}
}
export class SetMap<K, V> {
private map = new Map<K, Set<V>>();
add(key: K, value: V): void {
let values = this.map.get(key);
if (!values) {
values = new Set<V>();
this.map.set(key, values);
}
values.add(value);
}
delete(key: K, value: V): void {
const values = this.map.get(key);
if (!values) {
return;
}
values.delete(value);
if (values.size === 0) {
this.map.delete(key);
}
}
forEach(key: K, fn: (value: V) => void): void {
const values = this.map.get(key);
if (!values) {
return;
}
values.forEach(fn);
}
get(key: K): ReadonlySet<V> {
const values = this.map.get(key);
if (!values) {
return new Set<V>();
}
return values;
}
}
export function mapsStrictEqualIgnoreOrder(a: Map<unknown, unknown>, b: Map<unknown, unknown>): boolean {
if (a === b) {
return true;
}
if (a.size !== b.size) {
return false;
}
for (const [key, value] of a) {
if (!b.has(key) || b.get(key) !== value) {
return false;
}
}
for (const [key] of b) {
if (!a.has(key)) {
return false;
}
}
return true;
}
+3 -3
View File
@@ -5,7 +5,7 @@
import * as browser from './browser';
import { IframeUtils } from './iframe';
import * as platform from './platform';
import * as platform from 'common/Platform';
export interface IMouseEvent {
readonly browserEvent: MouseEvent;
@@ -148,7 +148,7 @@ export class StandardWheelEvent {
const ev = e as unknown as WheelEvent;
if (ev.deltaMode === ev.DOM_DELTA_LINE) {
if (browser.isFirefox && !platform.isMacintosh) {
if (browser.isFirefox && !platform.isMac) {
this.deltaY = -e.deltaY / 3;
} else {
this.deltaY = -e.deltaY;
@@ -172,7 +172,7 @@ export class StandardWheelEvent {
const ev = e as unknown as WheelEvent;
if (ev.deltaMode === ev.DOM_DELTA_LINE) {
if (browser.isFirefox && !platform.isMacintosh) {
if (browser.isFirefox && !platform.isMac) {
this.deltaX = -e.deltaX / 3;
} else {
this.deltaX = -e.deltaX;
-90
View File
@@ -1,90 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface ILocalizeInfo {
key: string;
comment: string[];
}
export function localize(info: ILocalizeInfo | string, message: string, ...args: (string | number | boolean | undefined | null)[]): string {
return message;
}
export interface INLSLanguagePackConfiguration {
/**
* The path to the translations config file that contains pointers to
* all message bundles for `main` and extensions.
*/
readonly translationsConfigFile: string;
/**
* The path to the file containing the translations for this language
* pack as flat string array.
*/
readonly messagesFile: string;
/**
* The path to the file that can be used to signal a corrupt language
* pack, for example when reading the `messagesFile` fails. This will
* instruct the application to re-create the cache on next startup.
*/
readonly corruptMarkerFile: string;
}
export interface INLSConfiguration {
/**
* Locale as defined in `argv.json` or `app.getLocale()`.
*/
readonly userLocale: string;
/**
* Locale as defined by the OS (e.g. `app.getPreferredSystemLanguages()`).
*/
readonly osLocale: string;
/**
* The actual language of the UI that ends up being used considering `userLocale`
* and `osLocale`.
*/
readonly resolvedLanguage: string;
/**
* Defined if a language pack is used that is not the
* default english language pack. This requires a language
* pack to be installed as extension.
*/
readonly languagePack?: INLSLanguagePackConfiguration;
/**
* The path to the file containing the default english messages
* as flat string array. The file is only present in built
* versions of the application.
*/
readonly defaultMessagesFile: string;
/**
* Below properties are deprecated and only there to continue support
* for `vscode-nls` module that depends on them.
* Refs https://github.com/microsoft/vscode-nls/blob/main/src/node/main.ts#L36-L46
*/
/** @deprecated */
readonly locale: string;
/** @deprecated */
readonly availableLanguages: Record<string, string>;
/** @deprecated */
readonly _languagePackSupport?: boolean;
/** @deprecated */
readonly _languagePackId?: string;
/** @deprecated */
readonly _translationsConfigFile?: string;
/** @deprecated */
readonly _cacheRoot?: string;
/** @deprecated */
readonly _resolvedLanguagePackCoreLocation?: string;
/** @deprecated */
readonly _corruptedFile?: string;
}
-98
View File
@@ -1,98 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export function clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max);
}
export function rot(index: number, modulo: number): number {
return (modulo + (index % modulo)) % modulo;
}
export class Counter {
private _next = 0;
getNext(): number {
return this._next++;
}
}
export class MovingAverage {
private _n = 1;
private _val = 0;
update(value: number): number {
this._val = this._val + (value - this._val) / this._n;
this._n += 1;
return this._val;
}
get value(): number {
return this._val;
}
}
export class SlidingWindowAverage {
private _n: number = 0;
private _val = 0;
private readonly _values: number[] = [];
private _index: number = 0;
private _sum = 0;
constructor(size: number) {
this._values = new Array(size);
this._values.fill(0, 0, size);
}
update(value: number): number {
const oldValue = this._values[this._index];
this._values[this._index] = value;
this._index = (this._index + 1) % this._values.length;
this._sum -= oldValue;
this._sum += value;
if (this._n < this._values.length) {
this._n += 1;
}
this._val = this._sum / this._n;
return this._val;
}
get value(): number {
return this._val;
}
}
/** Returns whether the point is within the triangle formed by the following 6 x/y point pairs */
export function isPointWithinTriangle(
x: number, y: number,
ax: number, ay: number,
bx: number, by: number,
cx: number, cy: number
) {
const v0x = cx - ax;
const v0y = cy - ay;
const v1x = bx - ax;
const v1y = by - ay;
const v2x = x - ax;
const v2y = y - ay;
const dot00 = v0x * v0x + v0y * v0y;
const dot01 = v0x * v1x + v0y * v1y;
const dot02 = v0x * v2x + v0y * v2y;
const dot11 = v1x * v1x + v1y * v1y;
const dot12 = v1x * v2x + v1y * v2y;
const invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
const u = (dot11 * dot02 - dot01 * dot12) * invDenom;
const v = (dot00 * dot12 - dot01 * dot02) * invDenom;
return u >= 0 && v >= 0 && u + v < 1;
}
-12
View File
@@ -1,12 +0,0 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
const userAgent = typeof navigator === 'object' ? navigator.userAgent : '';
const platform = typeof navigator === 'object' ? navigator.platform : '';
export const isMacintosh = platform.indexOf('Mac') >= 0;
export const isWindows = platform.indexOf('Win') >= 0;
export const isLinux = platform.indexOf('Linux') >= 0;
export const isIOS = /iPad|iPhone|iPod/.test(userAgent);
+3 -3
View File
@@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from './event';
import { Disposable, IDisposable } from './lifecycle';
import { Emitter, IEvent } from 'common/Event';
import { Disposable, IDisposable } from 'common/Lifecycle';
export const enum ScrollbarVisibility {
Auto = 1,
@@ -223,7 +223,7 @@ export class Scrollable extends Disposable {
private _smoothScrolling: SmoothScrollingOperation | null;
private _onScroll = this._register(new Emitter<ScrollEvent>());
public readonly onScroll: Event<ScrollEvent> = this._onScroll.event;
public readonly onScroll: IEvent<ScrollEvent> = this._onScroll.event;
constructor(options: IScrollableOptions) {
super();
+9 -57
View File
@@ -12,10 +12,10 @@ import { HorizontalScrollbar } from './horizontalScrollbar';
import { ScrollableElementChangeOptions, ScrollableElementCreationOptions, ScrollableElementResolvedOptions } from './scrollableElementOptions';
import { VerticalScrollbar } from './verticalScrollbar';
import { Widget } from './widget';
import { TimeoutTimer } from './async';
import { Emitter, Event } from './event';
import { IDisposable, dispose } from './lifecycle';
import * as platform from './platform';
import { TimeoutTimer } from 'common/Async';
import { Emitter, IEvent } from 'common/Event';
import { IDisposable, dispose } from 'common/Lifecycle';
import * as platform from 'common/Platform';
import { INewScrollDimensions, INewScrollPosition, IScrollDimensions, IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from './scrollable';
// import 'vs/css!./media/scrollbars';
@@ -180,10 +180,10 @@ export abstract class AbstractScrollableElement extends Widget {
private _revealOnScroll: boolean;
private readonly _onScroll = this._register(new Emitter<ScrollEvent>());
public readonly onScroll: Event<ScrollEvent> = this._onScroll.event;
public readonly onScroll: IEvent<ScrollEvent> = this._onScroll.event;
private readonly _onWillScroll = this._register(new Emitter<ScrollEvent>());
public readonly onWillScroll: Event<ScrollEvent> = this._onWillScroll.event;
public readonly onWillScroll: IEvent<ScrollEvent> = this._onWillScroll.event;
public get options(): Readonly<ScrollableElementResolvedOptions> {
return this._options;
@@ -281,7 +281,7 @@ export abstract class AbstractScrollableElement extends Widget {
public updateClassName(newClassName: string): void {
this._options.className = newClassName;
if (platform.isMacintosh) {
if (platform.isMac) {
this._options.className += ' mac';
}
this._domNode.className = 'xterm-scrollable-element ' + this._options.className;
@@ -382,7 +382,7 @@ export abstract class AbstractScrollableElement extends Widget {
[deltaY, deltaX] = [deltaX, deltaY];
}
const shiftConvert = !platform.isMacintosh && e.browserEvent && e.browserEvent.shiftKey;
const shiftConvert = !platform.isMac && e.browserEvent && e.browserEvent.shiftKey;
if ((this._options.scrollYToX || shiftConvert) && !deltaX) {
deltaX = deltaY;
deltaY = 0;
@@ -575,54 +575,6 @@ export class SmoothScrollableElement extends AbstractScrollableElement {
}
export class DomScrollableElement extends AbstractScrollableElement {
private _element: HTMLElement;
constructor(element: HTMLElement, options: ScrollableElementCreationOptions) {
options = options || {};
options.mouseWheelSmoothScroll = false;
const scrollable = new Scrollable({
forceIntegerValues: false,
smoothScrollDuration: 0,
scheduleAtNextAnimationFrame: (callback) => dom.scheduleAtNextAnimationFrame(dom.getWindow(element), callback)
});
super(element, options, scrollable);
this._register(scrollable);
this._element = element;
this._register(this.onScroll((e) => {
if (e.scrollTopChanged) {
this._element.scrollTop = e.scrollTop;
}
if (e.scrollLeftChanged) {
this._element.scrollLeft = e.scrollLeft;
}
}));
this.scanDomNode();
}
public setScrollPosition(update: INewScrollPosition): void {
this._scrollable.setScrollPositionNow(update);
}
public getScrollPosition(): IScrollPosition {
return this._scrollable.getCurrentScrollPosition();
}
public scanDomNode(): void {
this.setScrollDimensions({
width: this._element.clientWidth,
scrollWidth: this._element.scrollWidth,
height: this._element.clientHeight,
scrollHeight: this._element.scrollHeight
});
this.setScrollPosition({
scrollLeft: this._element.scrollLeft,
scrollTop: this._element.scrollTop,
});
}
}
function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableElementResolvedOptions {
const result: ScrollableElementResolvedOptions = {
lazyRender: (typeof opts.lazyRender !== 'undefined' ? opts.lazyRender : false),
@@ -657,7 +609,7 @@ function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableEleme
result.horizontalSliderSize = (typeof opts.horizontalSliderSize !== 'undefined' ? opts.horizontalSliderSize : result.horizontalScrollbarSize);
result.verticalSliderSize = (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : result.verticalScrollbarSize);
if (platform.isMacintosh) {
if (platform.isMac) {
result.className += ' mac';
}

Some files were not shown because too many files have changed in this diff Show More