diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index 5e32893e..6e45e461 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -10,9 +10,9 @@ import { IBufferService, ICoreMouseService, IOptionsService } from 'common/servi import { CoreMouseEventType } from 'common/Types'; import { addDisposableListener, scheduleAtNextAnimationFrame } from 'browser/Dom'; import { SmoothScrollableElement } from 'browser/scrollable/scrollableElement'; -import type { ScrollableElementChangeOptions } from 'browser/scrollable/scrollableElementOptions'; +import type { IScrollableElementChangeOptions } from 'browser/scrollable/scrollableElementOptions'; import { Emitter, EventUtils } from 'common/Event'; -import { Scrollable, ScrollbarVisibility, type ScrollEvent } from 'browser/scrollable/scrollable'; +import { Scrollable, ScrollbarVisibility, type IScrollEvent } from 'browser/scrollable/scrollable'; import { Gesture, EventType as GestureEventType, type GestureEvent } from 'browser/scrollable/touch'; export class Viewport extends Disposable { @@ -52,8 +52,8 @@ export class Viewport extends Disposable { })); this._scrollableElement = this._register(new SmoothScrollableElement(screenElement, { - vertical: ScrollbarVisibility.Auto, - horizontal: ScrollbarVisibility.Hidden, + vertical: ScrollbarVisibility.AUTO, + horizontal: ScrollbarVisibility.HIDDEN, useShadows: false, mouseWheelSmoothScroll: true, ...this._getChangeOptions() @@ -129,7 +129,7 @@ export class Viewport extends Disposable { }); } - private _getChangeOptions(): ScrollableElementChangeOptions { + private _getChangeOptions(): IScrollableElementChangeOptions { return { mouseWheelScrollSensitivity: this._optionsService.rawOptions.scrollSensitivity, fastScrollSensitivity: this._optionsService.rawOptions.fastScrollSensitivity, @@ -179,7 +179,7 @@ export class Viewport extends Disposable { this._isSyncing = false; } - private _handleScroll(e: ScrollEvent): void { + private _handleScroll(e: IScrollEvent): void { if (!this._renderService) { return; } diff --git a/src/browser/scrollable/abstractScrollbar.ts b/src/browser/scrollable/abstractScrollbar.ts index 4d3dd0a8..b330fe1c 100644 --- a/src/browser/scrollable/abstractScrollbar.ts +++ b/src/browser/scrollable/abstractScrollbar.ts @@ -7,7 +7,7 @@ import * as dom from './dom'; import { createFastDomNode, FastDomNode } from './fastDomNode'; import { GlobalPointerMoveMonitor } from './globalPointerMoveMonitor'; import { StandardWheelEvent } from './mouseEvent'; -import { ScrollbarArrow, ScrollbarArrowOptions } from './scrollbarArrow'; +import { ScrollbarArrow, IScrollbarArrowOptions } from './scrollbarArrow'; import { ScrollbarState } from './scrollbarState'; import { ScrollbarVisibilityController } from './scrollbarVisibilityController'; import { Widget } from './widget'; @@ -25,15 +25,15 @@ export interface ISimplifiedPointerEvent { pageY: number; } -export interface ScrollbarHost { - onMouseWheel(mouseWheelEvent: StandardWheelEvent): void; - onDragStart(): void; - onDragEnd(): void; +export interface IScrollbarHost { + handleMouseWheel(mouseWheelEvent: StandardWheelEvent): void; + handleDragStart(): void; + handleDragEnd(): void; } -interface AbstractScrollbarOptions { +interface IAbstractScrollbarOptions { lazyRender: boolean; - host: ScrollbarHost; + host: IScrollbarHost; scrollbarState: ScrollbarState; visibility: ScrollbarVisibility; extraScrollbarClassName: string; @@ -43,7 +43,7 @@ interface AbstractScrollbarOptions { export abstract class AbstractScrollbar extends Widget { - protected _host: ScrollbarHost; + protected _host: IScrollbarHost; protected _scrollable: Scrollable; protected _scrollByPage: boolean; private _lazyRender: boolean; @@ -56,7 +56,7 @@ export abstract class AbstractScrollbar extends Widget { protected _shouldRender: boolean; - constructor(opts: AbstractScrollbarOptions) { + constructor(opts: IAbstractScrollbarOptions) { super(); this._lazyRender = opts.lazyRender; this._host = opts.host; @@ -74,7 +74,7 @@ export abstract class AbstractScrollbar extends Widget { this._visibilityController.setDomNode(this.domNode); this.domNode.setPosition('absolute'); - this._register(dom.addDisposableListener(this.domNode.domNode, dom.EventType.POINTER_DOWN, (e: PointerEvent) => this._domNodePointerDown(e))); + this._register(dom.addDisposableListener(this.domNode.domNode, dom.eventType.POINTER_DOWN, (e: PointerEvent) => this._domNodePointerDown(e))); } // ----------------- creation @@ -82,7 +82,7 @@ export abstract class AbstractScrollbar extends Widget { /** * Creates the dom node for an arrow & adds it to the container */ - protected _createArrow(opts: ScrollbarArrowOptions): void { + protected _createArrow(opts: IScrollbarArrowOptions): void { const arrow = this._register(new ScrollbarArrow(opts)); this.domNode.domNode.appendChild(arrow.bgDomNode); this.domNode.domNode.appendChild(arrow.domNode); @@ -110,7 +110,7 @@ export abstract class AbstractScrollbar extends Widget { this._register(dom.addDisposableListener( this.slider.domNode, - dom.EventType.POINTER_DOWN, + dom.eventType.POINTER_DOWN, (e: PointerEvent) => { if (e.button === 0) { e.preventDefault(); @@ -119,7 +119,7 @@ export abstract class AbstractScrollbar extends Widget { } )); - this.onclick(this.slider.domNode, e => { + this._onclick(this.slider.domNode, e => { if (e.leftButton) { e.stopPropagation(); } @@ -128,7 +128,7 @@ export abstract class AbstractScrollbar extends Widget { // ----------------- Update state - protected _onElementSize(visibleSize: number): boolean { + protected _handleElementSize(visibleSize: number): boolean { if (this._scrollbarState.setVisibleSize(visibleSize)) { this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded()); this._shouldRender = true; @@ -139,7 +139,7 @@ export abstract class AbstractScrollbar extends Widget { return this._shouldRender; } - protected _onElementScrollSize(elementScrollSize: number): boolean { + protected _handleElementScrollSize(elementScrollSize: number): boolean { if (this._scrollbarState.setScrollSize(elementScrollSize)) { this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded()); this._shouldRender = true; @@ -150,7 +150,7 @@ export abstract class AbstractScrollbar extends Widget { return this._shouldRender; } - protected _onElementScrollPosition(elementScrollPosition: number): boolean { + protected _handleElementScrollPosition(elementScrollPosition: number): boolean { if (this._scrollbarState.setScrollPosition(elementScrollPosition)) { this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded()); this._shouldRender = true; @@ -186,7 +186,7 @@ export abstract class AbstractScrollbar extends Widget { if (e.target !== this.domNode.domNode) { return; } - this._onPointerDown(e); + this._handlePointerDown(e); } public delegatePointerDown(e: PointerEvent): void { @@ -200,11 +200,11 @@ export abstract class AbstractScrollbar extends Widget { this._sliderPointerDown(e); } } else { - this._onPointerDown(e); + this._handlePointerDown(e); } } - private _onPointerDown(e: PointerEvent): void { + private _handlePointerDown(e: PointerEvent): void { let offsetX: number; let offsetY: number; if (e.target === this.domNode.domNode && typeof e.offsetX === 'number' && typeof e.offsetY === 'number') { @@ -257,11 +257,11 @@ export abstract class AbstractScrollbar extends Widget { }, () => { this.slider.toggleClassName('active', false); - this._host.onDragEnd(); + this._host.handleDragEnd(); } ); - this._host.onDragStart(); + this._host.handleDragStart(); } private _setDesiredScrollPositionNow(_desiredScrollPosition: number): void { diff --git a/src/browser/scrollable/decorators.ts b/src/browser/scrollable/decorators.ts index 134008f0..2f01ae89 100644 --- a/src/browser/scrollable/decorators.ts +++ b/src/browser/scrollable/decorators.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export function memoize(_target: any, key: string, descriptor: any) { +export function memoize(_target: any, key: string, descriptor: PropertyDescriptor): void { let fnKey: string | null = null; let fn: Function | null = null; @@ -24,7 +24,8 @@ export function memoize(_target: any, key: string, descriptor: any) { } const memoizeKey = `$memoize$${key}`; - descriptor[fnKey!] = function (...args: any[]) { + const descriptorAny = descriptor as { [key: string]: any }; + descriptorAny[fnKey!] = function (...args: any[]) { if (!this.hasOwnProperty(memoizeKey)) { Object.defineProperty(this, memoizeKey, { configurable: false, @@ -34,7 +35,7 @@ export function memoize(_target: any, key: string, descriptor: any) { }); } - return this[memoizeKey]; + return (this as { [key: string]: any })[memoizeKey]; }; } diff --git a/src/browser/scrollable/dom.ts b/src/browser/scrollable/dom.ts index fa56cd09..901dc3f6 100644 --- a/src/browser/scrollable/dom.ts +++ b/src/browser/scrollable/dom.ts @@ -12,12 +12,12 @@ export interface IRegisteredWindow { readonly disposables: DisposableStore; } -const _onDidRegisterWindow = new Emitter(); -export const onDidRegisterWindow: IEvent = _onDidRegisterWindow.event; +const onDidRegisterWindowEmitter = new Emitter(); +export const onDidRegisterWindow: IEvent = onDidRegisterWindowEmitter.event; export function registerWindow(window: Window): IDisposable { const disposables = new DisposableStore(); - _onDidRegisterWindow.fire({ window, disposables }); + onDidRegisterWindowEmitter.fire({ window, disposables }); return disposables; } @@ -70,7 +70,7 @@ export function addStandardDisposableListener(node: HTMLElement, type: string, h return addDisposableListener(node, type, handler, useCapture); } -export const EventType = { +export const eventType = { CLICK: 'click', MOUSE_DOWN: 'mousedown', MOUSE_OVER: 'mouseover', @@ -88,6 +88,8 @@ export const EventType = { WHEEL: 'wheel' } as const; +export const EventType = eventType; + export function getDomNodePagePosition(domNode: HTMLElement): { left: number, top: number, width: number, height: number } { const bb = domNode.getBoundingClientRect(); const win = getWindow(domNode); diff --git a/src/browser/scrollable/globalPointerMoveMonitor.ts b/src/browser/scrollable/globalPointerMoveMonitor.ts index cf692571..6d252c89 100644 --- a/src/browser/scrollable/globalPointerMoveMonitor.ts +++ b/src/browser/scrollable/globalPointerMoveMonitor.ts @@ -59,11 +59,11 @@ export class GlobalPointerMoveMonitor implements IDisposable { this._hooks.add(toDisposable(() => { try { initialElement.releasePointerCapture(pointerId); - } catch (err) { + } catch { // ignore } })); - } catch (err) { + } catch { eventSource = dom.getWindow(initialElement); } diff --git a/src/browser/scrollable/horizontalScrollbar.ts b/src/browser/scrollable/horizontalScrollbar.ts index 8f9732e3..70058ca7 100644 --- a/src/browser/scrollable/horizontalScrollbar.ts +++ b/src/browser/scrollable/horizontalScrollbar.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { AbstractScrollbar, ISimplifiedPointerEvent, ScrollbarHost } from './abstractScrollbar'; -import { ScrollableElementResolvedOptions } from './scrollableElementOptions'; +import { AbstractScrollbar, ISimplifiedPointerEvent, IScrollbarHost } from './abstractScrollbar'; +import { IScrollableElementResolvedOptions } from './scrollableElementOptions'; import { ScrollbarState } from './scrollbarState'; -import { INewScrollPosition, Scrollable, ScrollbarVisibility, ScrollEvent } from './scrollable'; +import { INewScrollPosition, Scrollable, ScrollbarVisibility, IScrollEvent } from './scrollable'; export class HorizontalScrollbar extends AbstractScrollbar { - constructor(scrollable: Scrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) { + constructor(scrollable: Scrollable, options: IScrollableElementResolvedOptions, host: IScrollbarHost) { const scrollDimensions = scrollable.getScrollDimensions(); const scrollPosition = scrollable.getCurrentScrollPosition(); super({ @@ -18,8 +18,8 @@ export class HorizontalScrollbar extends AbstractScrollbar { host: host, scrollbarState: new ScrollbarState( (options.horizontalHasArrows ? options.arrowSize : 0), - (options.horizontal === ScrollbarVisibility.Hidden ? 0 : options.horizontalScrollbarSize), - (options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize), + (options.horizontal === ScrollbarVisibility.HIDDEN ? 0 : options.horizontalScrollbarSize), + (options.vertical === ScrollbarVisibility.HIDDEN ? 0 : options.verticalScrollbarSize), scrollDimensions.width, scrollDimensions.scrollWidth, scrollPosition.scrollLeft @@ -49,10 +49,10 @@ export class HorizontalScrollbar extends AbstractScrollbar { this.domNode.setBottom(0); } - public onDidScroll(e: ScrollEvent): boolean { - this._shouldRender = this._onElementScrollSize(e.scrollWidth) || this._shouldRender; - this._shouldRender = this._onElementScrollPosition(e.scrollLeft) || this._shouldRender; - this._shouldRender = this._onElementSize(e.width) || this._shouldRender; + public handleScroll(e: IScrollEvent): boolean { + this._shouldRender = this._handleElementScrollSize(e.scrollWidth) || this._shouldRender; + this._shouldRender = this._handleElementScrollPosition(e.scrollLeft) || this._shouldRender; + this._shouldRender = this._handleElementSize(e.width) || this._shouldRender; return this._shouldRender; } @@ -76,9 +76,9 @@ export class HorizontalScrollbar extends AbstractScrollbar { target.scrollLeft = scrollPosition; } - public updateOptions(options: ScrollableElementResolvedOptions): void { - this.updateScrollbarSize(options.horizontal === ScrollbarVisibility.Hidden ? 0 : options.horizontalScrollbarSize); - this._scrollbarState.setOppositeScrollbarSize(options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize); + public updateOptions(options: IScrollableElementResolvedOptions): void { + this.updateScrollbarSize(options.horizontal === ScrollbarVisibility.HIDDEN ? 0 : options.horizontalScrollbarSize); + this._scrollbarState.setOppositeScrollbarSize(options.vertical === ScrollbarVisibility.HIDDEN ? 0 : options.verticalScrollbarSize); this._visibilityController.setVisibility(options.horizontal); this._scrollByPage = options.scrollByPage; } diff --git a/src/browser/scrollable/linkedList.ts b/src/browser/scrollable/linkedList.ts index 80a4751c..a7132e29 100644 --- a/src/browser/scrollable/linkedList.ts +++ b/src/browser/scrollable/linkedList.ts @@ -5,13 +5,13 @@ class Node { - static readonly Undefined = new Node(undefined); + public static readonly Undefined = new Node(undefined); - element: E; - next: Node; - prev: Node; + public element: E; + public next: Node; + public prev: Node; - constructor(element: E) { + public constructor(element: E) { this.element = element; this.next = Node.Undefined; this.prev = Node.Undefined; @@ -24,15 +24,15 @@ export class LinkedList { private _last: Node = Node.Undefined; private _size: number = 0; - get size(): number { + public get size(): number { return this._size; } - isEmpty(): boolean { + public isEmpty(): boolean { return this._first === Node.Undefined; } - clear(): void { + public clear(): void { let node = this._first; while (node !== Node.Undefined) { const next = node.next; @@ -46,11 +46,11 @@ export class LinkedList { this._size = 0; } - unshift(element: E): () => void { + public unshift(element: E): () => void { return this._insert(element, false); } - push(element: E): () => void { + public push(element: E): () => void { return this._insert(element, true); } @@ -85,7 +85,7 @@ export class LinkedList { }; } - shift(): E | undefined { + public shift(): E | undefined { if (this._first === Node.Undefined) { return undefined; } @@ -95,7 +95,7 @@ export class LinkedList { } - pop(): E | undefined { + public pop(): E | undefined { if (this._last === Node.Undefined) { return undefined; } @@ -132,7 +132,7 @@ export class LinkedList { this._size -= 1; } - *[Symbol.iterator](): Iterator { + public *[Symbol.iterator](): Iterator { let node = this._first; while (node !== Node.Undefined) { yield node.element; diff --git a/src/browser/scrollable/mouseEvent.ts b/src/browser/scrollable/mouseEvent.ts index 550d665d..994cd85f 100644 --- a/src/browser/scrollable/mouseEvent.ts +++ b/src/browser/scrollable/mouseEvent.ts @@ -55,7 +55,7 @@ export class StandardMouseEvent implements IMouseEvent { this.target = e.target as HTMLElement; - this.detail = e.detail || 1; + this.detail = e.detail ?? 1; if (e.type === 'dblclick') { this.detail = 2; } @@ -134,7 +134,7 @@ export class StandardWheelEvent { if (e) { const e1 = e as IWebKitMouseWheelEvent as any; const e2 = e as unknown as IGeckoMouseWheelEvent; - const devicePixelRatio = e.view?.devicePixelRatio || 1; + const devicePixelRatio = e.view?.devicePixelRatio ?? 1; if (typeof e1.wheelDeltaY !== 'undefined') { if (shouldFactorDPR) { diff --git a/src/browser/scrollable/scrollable.ts b/src/browser/scrollable/scrollable.ts index 06895e7a..9e57c387 100644 --- a/src/browser/scrollable/scrollable.ts +++ b/src/browser/scrollable/scrollable.ts @@ -7,12 +7,12 @@ import { Emitter, IEvent } from 'common/Event'; import { Disposable, IDisposable } from 'common/Lifecycle'; export const enum ScrollbarVisibility { - Auto = 1, - Hidden = 2, - Visible = 3 + AUTO = 1, + HIDDEN = 2, + VISIBLE = 3 } -export interface ScrollEvent { +export interface IScrollEvent { inSmoothScrolling: boolean; oldWidth: number; @@ -41,7 +41,7 @@ export interface ScrollEvent { } export class ScrollState implements IScrollDimensions, IScrollPosition { - _scrollStateBrand: void = undefined; + private _scrollStateBrand: void = undefined; public readonly rawScrollLeft: number; public readonly rawScrollTop: number; @@ -139,7 +139,7 @@ export class ScrollState implements IScrollDimensions, IScrollPosition { ); } - public createScrollEvent(previous: ScrollState, inSmoothScrolling: boolean): ScrollEvent { + public createScrollEvent(previous: ScrollState, inSmoothScrolling: boolean): IScrollEvent { const widthChanged = (this.width !== previous.width); const scrollWidthChanged = (this.scrollWidth !== previous.scrollWidth); const scrollLeftChanged = (this.scrollLeft !== previous.scrollLeft); @@ -215,15 +215,15 @@ export interface IScrollableOptions { export class Scrollable extends Disposable { - _scrollableBrand: void = undefined; + private _scrollableBrand: void = undefined; private _smoothScrollDuration: number; private readonly _scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable; private _state: ScrollState; private _smoothScrolling: SmoothScrollingOperation | null; - private _onScroll = this._register(new Emitter()); - public readonly onScroll: IEvent = this._onScroll.event; + private _onScroll = this._register(new Emitter()); + public readonly onScroll: IEvent = this._onScroll.event; constructor(options: IScrollableOptions) { super(); @@ -406,8 +406,8 @@ export class SmoothScrollingOperation { public readonly startTime: number; public animationFrameDisposable: IDisposable | null; - private scrollLeft!: IAnimation; - private scrollTop!: IAnimation; + private _scrollLeft!: IAnimation; + private _scrollTop!: IAnimation; constructor(from: ISmoothScrollPosition, to: ISmoothScrollPosition, startTime: number, duration: number) { this.from = from; @@ -421,8 +421,8 @@ export class SmoothScrollingOperation { } private _initAnimations(): void { - this.scrollLeft = this._initAnimation(this.from.scrollLeft, this.to.scrollLeft, this.to.width); - this.scrollTop = this._initAnimation(this.from.scrollTop, this.to.scrollTop, this.to.height); + this._scrollLeft = this._initAnimation(this.from.scrollLeft, this.to.scrollLeft, this.to.width); + this._scrollTop = this._initAnimation(this.from.scrollTop, this.to.scrollTop, this.to.height); } private _initAnimation(from: number, to: number, viewportSize: number): IAnimation { @@ -461,8 +461,8 @@ export class SmoothScrollingOperation { const completion = (now - this.startTime) / this.duration; if (completion < 1) { - const newScrollLeft = this.scrollLeft(completion); - const newScrollTop = this.scrollTop(completion); + const newScrollLeft = this._scrollLeft(completion); + const newScrollTop = this._scrollTop(completion); return new SmoothScrollingUpdate(newScrollLeft, newScrollTop, false); } @@ -481,10 +481,10 @@ export class SmoothScrollingOperation { } } -function easeInCubic(t: number) { +function easeInCubic(t: number): number { return Math.pow(t, 3); } -function easeOutCubic(t: number) { +function easeOutCubic(t: number): number { return 1 - easeInCubic(1 - t); } diff --git a/src/browser/scrollable/scrollableElement.ts b/src/browser/scrollable/scrollableElement.ts index dc973248..6c07d6a3 100644 --- a/src/browser/scrollable/scrollableElement.ts +++ b/src/browser/scrollable/scrollableElement.ts @@ -7,16 +7,16 @@ import { getZoomFactor, isChrome } from './browser'; import * as dom from './dom'; import { FastDomNode, createFastDomNode } from './fastDomNode'; import { IMouseEvent, IMouseWheelEvent, StandardWheelEvent } from './mouseEvent'; -import { ScrollbarHost } from './abstractScrollbar'; +import { IScrollbarHost } from './abstractScrollbar'; import { HorizontalScrollbar } from './horizontalScrollbar'; -import { ScrollableElementChangeOptions, ScrollableElementCreationOptions, ScrollableElementResolvedOptions } from './scrollableElementOptions'; +import { IScrollableElementChangeOptions, IScrollableElementCreationOptions, IScrollableElementResolvedOptions } from './scrollableElementOptions'; import { VerticalScrollbar } from './verticalScrollbar'; import { Widget } from './widget'; 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 { INewScrollDimensions, INewScrollPosition, IScrollDimensions, IScrollPosition, IScrollEvent, Scrollable, ScrollbarVisibility } from './scrollable'; // import 'vs/css!./media/scrollbars'; const HIDE_TIMEOUT = 500; @@ -68,7 +68,7 @@ export class MouseWheelClassifier { let iteration = 1; let index = this._rear; - do { + while (index !== -1) { const influence = (index === this._front ? remainingInfluence : Math.pow(2, -iteration)); remainingInfluence -= influence; score += this._memory[index].score * influence; @@ -79,7 +79,7 @@ export class MouseWheelClassifier { index = (this._capacity + index - 1) % this._capacity; iteration++; - } while (true); + } return (score <= 0.5); } @@ -157,7 +157,7 @@ export class MouseWheelClassifier { export abstract class AbstractScrollableElement extends Widget { - private readonly _options: ScrollableElementResolvedOptions; + private readonly _options: IScrollableElementResolvedOptions; protected readonly _scrollable: Scrollable; private readonly _verticalScrollbar: VerticalScrollbar; private readonly _horizontalScrollbar: HorizontalScrollbar; @@ -179,31 +179,31 @@ export abstract class AbstractScrollableElement extends Widget { private _revealOnScroll: boolean; - private readonly _onScroll = this._register(new Emitter()); - public readonly onScroll: IEvent = this._onScroll.event; + private readonly _onScroll = this._register(new Emitter()); + public readonly onScroll: IEvent = this._onScroll.event; - private readonly _onWillScroll = this._register(new Emitter()); - public readonly onWillScroll: IEvent = this._onWillScroll.event; + private readonly _onWillScroll = this._register(new Emitter()); + public readonly onWillScroll: IEvent = this._onWillScroll.event; - public get options(): Readonly { + public get options(): Readonly { return this._options; } - protected constructor(element: HTMLElement, options: ScrollableElementCreationOptions, scrollable: Scrollable) { + protected constructor(element: HTMLElement, options: IScrollableElementCreationOptions, scrollable: Scrollable) { super(); this._options = resolveOptions(options); this._scrollable = scrollable; this._register(this._scrollable.onScroll((e) => { this._onWillScroll.fire(e); - this._onDidScroll(e); + this._handleScroll(e); this._onScroll.fire(e); })); - const scrollbarHost: ScrollbarHost = { - onMouseWheel: (mouseWheelEvent: StandardWheelEvent) => this._onMouseWheel(mouseWheelEvent), - onDragStart: () => this._onDragStart(), - onDragEnd: () => this._onDragEnd(), + const scrollbarHost: IScrollbarHost = { + handleMouseWheel: (mouseWheelEvent: StandardWheelEvent) => this._handleMouseWheel(mouseWheelEvent), + handleDragStart: () => this._handleDragStart(), + handleDragEnd: () => this._handleDragEnd(), }; this._verticalScrollbar = this._register(new VerticalScrollbar(this._scrollable, this._options, scrollbarHost)); this._horizontalScrollbar = this._register(new HorizontalScrollbar(this._scrollable, this._options, scrollbarHost)); @@ -239,8 +239,8 @@ export abstract class AbstractScrollableElement extends Widget { this._mouseWheelToDispose = []; this._setListeningToMouseWheel(this._options.handleMouseWheel); - this.onmouseover(this._listenOnDomNode, (e) => this._onMouseOver(e)); - this.onmouseleave(this._listenOnDomNode, (e) => this._onMouseLeave(e)); + this._onmouseover(this._listenOnDomNode, (e) => this._handleMouseOver(e)); + this._onmouseleave(this._listenOnDomNode, (e) => this._handleMouseLeave(e)); this._hideTimeout = this._register(new TimeoutTimer()); this._isDragging = false; @@ -287,7 +287,7 @@ export abstract class AbstractScrollableElement extends Widget { this._domNode.className = 'xterm-scrollable-element ' + this._options.className; } - public updateOptions(newOptions: ScrollableElementChangeOptions): void { + public updateOptions(newOptions: IScrollableElementChangeOptions): void { if (typeof newOptions.handleMouseWheel !== 'undefined') { this._options.handleMouseWheel = newOptions.handleMouseWheel; this._setListeningToMouseWheel(this._options.handleMouseWheel); @@ -324,12 +324,12 @@ export abstract class AbstractScrollableElement extends Widget { } } - public setRevealOnScroll(value: boolean) { + public setRevealOnScroll(value: boolean): void { this._revealOnScroll = value; } - public delegateScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) { - this._onMouseWheel(new StandardWheelEvent(browserEvent)); + public delegateScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent): void { + this._handleMouseWheel(new StandardWheelEvent(browserEvent)); } // -------------------- mouse wheel scrolling -------------------- @@ -345,14 +345,14 @@ export abstract class AbstractScrollableElement extends Widget { if (shouldListen) { const onMouseWheel = (browserEvent: IMouseWheelEvent) => { - this._onMouseWheel(new StandardWheelEvent(browserEvent)); + this._handleMouseWheel(new StandardWheelEvent(browserEvent)); }; - this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, dom.EventType.MOUSE_WHEEL, onMouseWheel, { passive: false })); + this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, dom.eventType.MOUSE_WHEEL, onMouseWheel, { passive: false })); } } - private _onMouseWheel(e: StandardWheelEvent): void { + private _handleMouseWheel(e: StandardWheelEvent): void { if (e.browserEvent?.defaultPrevented) { return; } @@ -441,9 +441,9 @@ export abstract class AbstractScrollableElement extends Widget { } } - private _onDidScroll(e: ScrollEvent): void { - this._shouldRender = this._horizontalScrollbar.onDidScroll(e) || this._shouldRender; - this._shouldRender = this._verticalScrollbar.onDidScroll(e) || this._shouldRender; + private _handleScroll(e: IScrollEvent): void { + this._shouldRender = this._horizontalScrollbar.handleScroll(e) || this._shouldRender; + this._shouldRender = this._verticalScrollbar.handleScroll(e) || this._shouldRender; if (this._options.useShadows) { this._shouldRender = true; @@ -492,22 +492,22 @@ export abstract class AbstractScrollableElement extends Widget { // -------------------- fade in / fade out -------------------- - private _onDragStart(): void { + private _handleDragStart(): void { this._isDragging = true; this._reveal(); } - private _onDragEnd(): void { + private _handleDragEnd(): void { this._isDragging = false; this._hide(); } - private _onMouseLeave(e: IMouseEvent): void { + private _handleMouseLeave(e: IMouseEvent): void { this._mouseIsOver = false; this._hide(); } - private _onMouseOver(e: IMouseEvent): void { + private _handleMouseOver(e: IMouseEvent): void { this._mouseIsOver = true; this._reveal(); } @@ -534,8 +534,8 @@ export abstract class AbstractScrollableElement extends Widget { export class ScrollableElement extends AbstractScrollableElement { - constructor(element: HTMLElement, options: ScrollableElementCreationOptions) { - options = options || {}; + constructor(element: HTMLElement, options: IScrollableElementCreationOptions) { + options = options ?? {}; options.mouseWheelSmoothScroll = false; const scrollable = new Scrollable({ forceIntegerValues: true, @@ -557,7 +557,7 @@ export class ScrollableElement extends AbstractScrollableElement { export class SmoothScrollableElement extends AbstractScrollableElement { - constructor(element: HTMLElement, options: ScrollableElementCreationOptions, scrollable: Scrollable) { + constructor(element: HTMLElement, options: IScrollableElementCreationOptions, scrollable: Scrollable) { super(element, options, scrollable); } @@ -575,8 +575,8 @@ export class SmoothScrollableElement extends AbstractScrollableElement { } -function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableElementResolvedOptions { - const result: ScrollableElementResolvedOptions = { +function resolveOptions(opts: IScrollableElementCreationOptions): IScrollableElementResolvedOptions { + const result: IScrollableElementResolvedOptions = { lazyRender: (typeof opts.lazyRender !== 'undefined' ? opts.lazyRender : false), className: (typeof opts.className !== 'undefined' ? opts.className : ''), useShadows: (typeof opts.useShadows !== 'undefined' ? opts.useShadows : true), @@ -593,12 +593,12 @@ function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableEleme listenOnDomNode: (typeof opts.listenOnDomNode !== 'undefined' ? opts.listenOnDomNode : null), - horizontal: (typeof opts.horizontal !== 'undefined' ? opts.horizontal : ScrollbarVisibility.Auto), + horizontal: (typeof opts.horizontal !== 'undefined' ? opts.horizontal : ScrollbarVisibility.AUTO), horizontalScrollbarSize: (typeof opts.horizontalScrollbarSize !== 'undefined' ? opts.horizontalScrollbarSize : 10), horizontalSliderSize: (typeof opts.horizontalSliderSize !== 'undefined' ? opts.horizontalSliderSize : 0), horizontalHasArrows: (typeof opts.horizontalHasArrows !== 'undefined' ? opts.horizontalHasArrows : false), - vertical: (typeof opts.vertical !== 'undefined' ? opts.vertical : ScrollbarVisibility.Auto), + vertical: (typeof opts.vertical !== 'undefined' ? opts.vertical : ScrollbarVisibility.AUTO), verticalScrollbarSize: (typeof opts.verticalScrollbarSize !== 'undefined' ? opts.verticalScrollbarSize : 10), verticalHasArrows: (typeof opts.verticalHasArrows !== 'undefined' ? opts.verticalHasArrows : false), verticalSliderSize: (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : 0), diff --git a/src/browser/scrollable/scrollableElementOptions.ts b/src/browser/scrollable/scrollableElementOptions.ts index c3cf4ea6..763ab622 100644 --- a/src/browser/scrollable/scrollableElementOptions.ts +++ b/src/browser/scrollable/scrollableElementOptions.ts @@ -5,7 +5,7 @@ import { ScrollbarVisibility } from './scrollable'; -export interface ScrollableElementCreationOptions { +export interface IScrollableElementCreationOptions { /** * The scrollable element should not do any DOM mutations until renderNow() is called. * Defaults to false. @@ -73,8 +73,8 @@ export interface ScrollableElementCreationOptions { */ arrowSize?: number; /** - * The dom node events should be bound to. - * If no listenOnDomNode is provided, the dom node passed to the constructor will be used for event listening. + * The dom node events should be bound to. + * If no listenOnDomNode is provided, the constructor dom node is used. */ listenOnDomNode?: HTMLElement; /** @@ -126,7 +126,7 @@ export interface ScrollableElementCreationOptions { scrollByPage?: boolean; } -export interface ScrollableElementChangeOptions { +export interface IScrollableElementChangeOptions { handleMouseWheel?: boolean; mouseWheelScrollSensitivity?: number; fastScrollSensitivity?: number; @@ -138,7 +138,7 @@ export interface ScrollableElementChangeOptions { scrollByPage?: boolean; } -export interface ScrollableElementResolvedOptions { +export interface IScrollableElementResolvedOptions { lazyRender: boolean; className: string; useShadows: boolean; diff --git a/src/browser/scrollable/scrollbarArrow.ts b/src/browser/scrollable/scrollbarArrow.ts index 81e52998..b3ac995b 100644 --- a/src/browser/scrollable/scrollbarArrow.ts +++ b/src/browser/scrollable/scrollbarArrow.ts @@ -13,8 +13,8 @@ import * as dom from './dom'; */ const ARROW_IMG_SIZE = 11; -export interface ScrollbarArrowOptions { - onActivate: () => void; +export interface IScrollbarArrowOptions { + handleActivate: () => void; className: string; // icon: ThemeIcon; @@ -29,16 +29,16 @@ export interface ScrollbarArrowOptions { export class ScrollbarArrow extends Widget { - private _onActivate: () => void; + private _handleActivate: () => void; public bgDomNode: HTMLElement; public domNode: HTMLElement; private _pointerdownRepeatTimer: dom.WindowIntervalTimer; private _pointerdownScheduleRepeatTimer: TimeoutTimer; private _pointerMoveMonitor: GlobalPointerMoveMonitor; - constructor(opts: ScrollbarArrowOptions) { + constructor(opts: IScrollbarArrowOptions) { super(); - this._onActivate = opts.onActivate; + this._handleActivate = opts.handleActivate; this.bgDomNode = document.createElement('div'); this.bgDomNode.className = 'arrow-background'; @@ -79,8 +79,8 @@ export class ScrollbarArrow extends Widget { } this._pointerMoveMonitor = this._register(new GlobalPointerMoveMonitor()); - this._register(dom.addStandardDisposableListener(this.bgDomNode, dom.EventType.POINTER_DOWN, (e) => this._arrowPointerDown(e))); - this._register(dom.addStandardDisposableListener(this.domNode, dom.EventType.POINTER_DOWN, (e) => this._arrowPointerDown(e))); + this._register(dom.addStandardDisposableListener(this.bgDomNode, dom.eventType.POINTER_DOWN, (e) => this._arrowPointerDown(e))); + this._register(dom.addStandardDisposableListener(this.domNode, dom.eventType.POINTER_DOWN, (e) => this._arrowPointerDown(e))); this._pointerdownRepeatTimer = this._register(new dom.WindowIntervalTimer()); this._pointerdownScheduleRepeatTimer = this._register(new TimeoutTimer()); @@ -90,11 +90,11 @@ export class ScrollbarArrow extends Widget { if (!e.target || !(e.target instanceof Element)) { return; } - const scheduleRepeater = () => { - this._pointerdownRepeatTimer.cancelAndSet(() => this._onActivate(), 1000 / 24, dom.getWindow(e)); + const scheduleRepeater = (): void => { + this._pointerdownRepeatTimer.cancelAndSet(() => this._handleActivate(), 1000 / 24, dom.getWindow(e)); }; - this._onActivate(); + this._handleActivate(); this._pointerdownRepeatTimer.cancel(); this._pointerdownScheduleRepeatTimer.cancelAndSet(scheduleRepeater, 200); diff --git a/src/browser/scrollable/scrollbarState.ts b/src/browser/scrollable/scrollbarState.ts index 1aa7e3a6..9af66401 100644 --- a/src/browser/scrollable/scrollbarState.ts +++ b/src/browser/scrollable/scrollbarState.ts @@ -4,10 +4,19 @@ *--------------------------------------------------------------------------------------------*/ /** - * The minimal size of the slider (such that it can still be clickable) -- it is artificially enlarged. + * The minimal size of the slider (such that it can still be clickable). + * The slider is artificially enlarged to keep it usable. */ const MINIMUM_SLIDER_SIZE = 20; +interface IScrollbarStateComputedValues { + computedAvailableSize: number; + computedIsNeeded: boolean; + computedSliderSize: number; + computedSliderRatio: number; + computedSliderPosition: number; +} + export class ScrollbarState { /** @@ -122,7 +131,13 @@ export class ScrollbarState { this._oppositeScrollbarSize = Math.round(oppositeScrollbarSize); } - private static _computeValues(oppositeScrollbarSize: number, arrowSize: number, visibleSize: number, scrollSize: number, scrollPosition: number) { + private static _computeValues( + oppositeScrollbarSize: number, + arrowSize: number, + visibleSize: number, + scrollSize: number, + scrollPosition: number + ): IScrollbarStateComputedValues { const computedAvailableSize = Math.max(0, visibleSize - oppositeScrollbarSize); const computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * arrowSize); const computedIsNeeded = (scrollSize > 0 && scrollSize > visibleSize); diff --git a/src/browser/scrollable/scrollbarVisibilityController.ts b/src/browser/scrollable/scrollbarVisibilityController.ts index a35388fd..84da4b9b 100644 --- a/src/browser/scrollable/scrollbarVisibilityController.ts +++ b/src/browser/scrollable/scrollbarVisibilityController.ts @@ -45,10 +45,10 @@ export class ScrollbarVisibilityController extends Disposable { } private _applyVisibilitySetting(): boolean { - if (this._visibility === ScrollbarVisibility.Hidden) { + if (this._visibility === ScrollbarVisibility.HIDDEN) { return false; } - if (this._visibility === ScrollbarVisibility.Visible) { + if (this._visibility === ScrollbarVisibility.VISIBLE) { return true; } return this._rawShouldBeVisible; diff --git a/src/browser/scrollable/verticalScrollbar.ts b/src/browser/scrollable/verticalScrollbar.ts index 42f62231..e55373e2 100644 --- a/src/browser/scrollable/verticalScrollbar.ts +++ b/src/browser/scrollable/verticalScrollbar.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { AbstractScrollbar, ISimplifiedPointerEvent, ScrollbarHost } from './abstractScrollbar'; -import { ScrollableElementResolvedOptions } from './scrollableElementOptions'; +import { AbstractScrollbar, ISimplifiedPointerEvent, IScrollbarHost } from './abstractScrollbar'; +import { IScrollableElementResolvedOptions } from './scrollableElementOptions'; import { ScrollbarState } from './scrollbarState'; -import { INewScrollPosition, Scrollable, ScrollbarVisibility, ScrollEvent } from './scrollable'; +import { INewScrollPosition, Scrollable, ScrollbarVisibility, IScrollEvent } from './scrollable'; export class VerticalScrollbar extends AbstractScrollbar { - constructor(scrollable: Scrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) { + constructor(scrollable: Scrollable, options: IScrollableElementResolvedOptions, host: IScrollbarHost) { const scrollDimensions = scrollable.getScrollDimensions(); const scrollPosition = scrollable.getCurrentScrollPosition(); super({ @@ -18,7 +18,7 @@ export class VerticalScrollbar extends AbstractScrollbar { host: host, scrollbarState: new ScrollbarState( (options.verticalHasArrows ? options.arrowSize : 0), - (options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize), + (options.vertical === ScrollbarVisibility.HIDDEN ? 0 : options.verticalScrollbarSize), 0, scrollDimensions.height, scrollDimensions.scrollHeight, @@ -49,10 +49,10 @@ export class VerticalScrollbar extends AbstractScrollbar { this.domNode.setTop(0); } - public onDidScroll(e: ScrollEvent): boolean { - this._shouldRender = this._onElementScrollSize(e.scrollHeight) || this._shouldRender; - this._shouldRender = this._onElementScrollPosition(e.scrollTop) || this._shouldRender; - this._shouldRender = this._onElementSize(e.height) || this._shouldRender; + public handleScroll(e: IScrollEvent): boolean { + this._shouldRender = this._handleElementScrollSize(e.scrollHeight) || this._shouldRender; + this._shouldRender = this._handleElementScrollPosition(e.scrollTop) || this._shouldRender; + this._shouldRender = this._handleElementSize(e.height) || this._shouldRender; return this._shouldRender; } @@ -76,8 +76,8 @@ export class VerticalScrollbar extends AbstractScrollbar { target.scrollTop = scrollPosition; } - public updateOptions(options: ScrollableElementResolvedOptions): void { - this.updateScrollbarSize(options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize); + public updateOptions(options: IScrollableElementResolvedOptions): void { + this.updateScrollbarSize(options.vertical === ScrollbarVisibility.HIDDEN ? 0 : options.verticalScrollbarSize); this._scrollbarState.setOppositeScrollbarSize(0); this._visibilityController.setVisibility(options.vertical); this._scrollByPage = options.scrollByPage; diff --git a/src/browser/scrollable/widget.ts b/src/browser/scrollable/widget.ts index 19a476a8..b71c38c5 100644 --- a/src/browser/scrollable/widget.ts +++ b/src/browser/scrollable/widget.ts @@ -11,47 +11,47 @@ import { Disposable, IDisposable } from 'common/Lifecycle'; export abstract class Widget extends Disposable { - protected onclick(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void { + protected _onclick(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void { this._register(dom.addDisposableListener(domNode, dom.EventType.CLICK, (e: MouseEvent) => listener(new StandardMouseEvent(dom.getWindow(domNode), e)))); } - protected onmousedown(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void { + protected _onmousedown(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void { this._register(dom.addDisposableListener(domNode, dom.EventType.MOUSE_DOWN, (e: MouseEvent) => listener(new StandardMouseEvent(dom.getWindow(domNode), e)))); } - protected onmouseover(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void { + protected _onmouseover(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void { this._register(dom.addDisposableListener(domNode, dom.EventType.MOUSE_OVER, (e: MouseEvent) => listener(new StandardMouseEvent(dom.getWindow(domNode), e)))); } - protected onmouseleave(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void { + protected _onmouseleave(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void { this._register(dom.addDisposableListener(domNode, dom.EventType.MOUSE_LEAVE, (e: MouseEvent) => listener(new StandardMouseEvent(dom.getWindow(domNode), e)))); } - protected onkeydown(domNode: HTMLElement, listener: (e: IKeyboardEvent) => void): void { + protected _onkeydown(domNode: HTMLElement, listener: (e: IKeyboardEvent) => void): void { this._register(dom.addDisposableListener(domNode, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => listener(new StandardKeyboardEvent(e)))); } - protected onkeyup(domNode: HTMLElement, listener: (e: IKeyboardEvent) => void): void { + protected _onkeyup(domNode: HTMLElement, listener: (e: IKeyboardEvent) => void): void { this._register(dom.addDisposableListener(domNode, dom.EventType.KEY_UP, (e: KeyboardEvent) => listener(new StandardKeyboardEvent(e)))); } - protected oninput(domNode: HTMLElement, listener: (e: Event) => void): void { + protected _oninput(domNode: HTMLElement, listener: (e: Event) => void): void { this._register(dom.addDisposableListener(domNode, dom.EventType.INPUT, listener)); } - protected onblur(domNode: HTMLElement, listener: (e: Event) => void): void { + protected _onblur(domNode: HTMLElement, listener: (e: Event) => void): void { this._register(dom.addDisposableListener(domNode, dom.EventType.BLUR, listener)); } - protected onfocus(domNode: HTMLElement, listener: (e: Event) => void): void { + protected _onfocus(domNode: HTMLElement, listener: (e: Event) => void): void { this._register(dom.addDisposableListener(domNode, dom.EventType.FOCUS, listener)); } - protected onchange(domNode: HTMLElement, listener: (e: Event) => void): void { + protected _onchange(domNode: HTMLElement, listener: (e: Event) => void): void { this._register(dom.addDisposableListener(domNode, dom.EventType.CHANGE, listener)); } - protected ignoreGesture(domNode: HTMLElement): IDisposable { + protected _ignoreGesture(domNode: HTMLElement): IDisposable { return Gesture.ignoreTarget(domNode); } } diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 52e542c0..a3b50ab0 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -3,11 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationToken } from 'vs/base/common/cancellation'; -import { CancellationError } from 'vs/base/common/errors'; -import { ISplice } from 'vs/base/common/sequence'; -import { findFirstIdxMonotonousOrArrLen } from './arraysFind'; - /** * Returns the last element of an array. * @param array The array. @@ -17,460 +12,6 @@ export function tail(array: ArrayLike, n: number = 0): T | undefined { return array[array.length - (1 + n)]; } -export function tail2(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]]; -} - -export function equals(one: ReadonlyArray | undefined, other: ReadonlyArray | undefined, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean { - if (one === other) { - return true; - } - - if (!one || !other) { - return false; - } - - if (one.length !== other.length) { - return false; - } - - for (let i = 0, len = one.length; i < len; i++) { - if (!itemEquals(one[i], other[i])) { - return false; - } - } - - return true; -} - -/** - * Remove the element at `index` by replacing it with the last element. This is faster than `splice` - * but changes the order of the array - */ -export function removeFastWithoutKeepingOrder(array: T[], index: number) { - const last = array.length - 1; - if (index < last) { - array[index] = array[last]; - } - array.pop(); -} - -/** - * Performs a binary search algorithm over a sorted array. - * - * @param array The array being searched. - * @param key The value we search for. - * @param comparator A function that takes two array elements and returns zero - * if they are equal, a negative number if the first element precedes the - * second one in the sorting order, or a positive number if the second element - * precedes the first one. - * @return See {@link binarySearch2} - */ -export function binarySearch(array: ReadonlyArray, key: T, comparator: (op1: T, op2: T) => number): number { - return binarySearch2(array.length, i => comparator(array[i], key)); -} - -/** - * Performs a binary search algorithm over a sorted collection. Useful for cases - * when we need to perform a binary search over something that isn't actually an - * array, and converting data to an array would defeat the use of binary search - * in the first place. - * - * @param length The collection length. - * @param compareToKey A function that takes an index of an element in the - * collection and returns zero if the value at this index is equal to the - * search key, a negative number if the value precedes the search key in the - * sorting order, or a positive number if the search key precedes the value. - * @return A non-negative index of an element, if found. If not found, the - * result is -(n+1) (or ~n, using bitwise notation), where n is the index - * where the key should be inserted to maintain the sorting order. - */ -export function binarySearch2(length: number, compareToKey: (index: number) => number): number { - let low = 0, - high = length - 1; - - while (low <= high) { - const mid = ((low + high) / 2) | 0; - const comp = compareToKey(mid); - if (comp < 0) { - low = mid + 1; - } else if (comp > 0) { - high = mid - 1; - } else { - return mid; - } - } - return -(low + 1); -} - -type Compare = (a: T, b: T) => number; - - -export function quickSelect(nth: number, data: T[], compare: Compare): T { - - nth = nth | 0; - - if (nth >= data.length) { - throw new TypeError('invalid index'); - } - - const pivotValue = data[Math.floor(data.length * Math.random())]; - const lower: T[] = []; - const higher: T[] = []; - const pivots: T[] = []; - - for (const value of data) { - const val = compare(value, pivotValue); - if (val < 0) { - lower.push(value); - } else if (val > 0) { - higher.push(value); - } else { - pivots.push(value); - } - } - - if (nth < lower.length) { - return quickSelect(nth, lower, compare); - } else if (nth < lower.length + pivots.length) { - return pivots[0]; - } else { - return quickSelect(nth - (lower.length + pivots.length), higher, compare); - } -} - -export function groupBy(data: ReadonlyArray, compare: (a: T, b: T) => number): T[][] { - const result: T[][] = []; - let currentGroup: T[] | undefined = undefined; - for (const element of data.slice(0).sort(compare)) { - if (!currentGroup || compare(currentGroup[0], element) !== 0) { - currentGroup = [element]; - result.push(currentGroup); - } else { - currentGroup.push(element); - } - } - return result; -} - -/** - * Splits the given items into a list of (non-empty) groups. - * `shouldBeGrouped` is used to decide if two consecutive items should be in the same group. - * The order of the items is preserved. - */ -export function* groupAdjacentBy(items: Iterable, shouldBeGrouped: (item1: T, item2: T) => boolean): Iterable { - let currentGroup: T[] | undefined; - let last: T | undefined; - for (const item of items) { - if (last !== undefined && shouldBeGrouped(last, item)) { - currentGroup!.push(item); - } else { - if (currentGroup) { - yield currentGroup; - } - currentGroup = [item]; - } - last = item; - } - if (currentGroup) { - yield currentGroup; - } -} - -export function forEachAdjacent(arr: T[], f: (item1: T | undefined, item2: T | undefined) => void): void { - for (let i = 0; i <= arr.length; i++) { - f(i === 0 ? undefined : arr[i - 1], i === arr.length ? undefined : arr[i]); - } -} - -export function forEachWithNeighbors(arr: T[], f: (before: T | undefined, element: T, after: T | undefined) => void): void { - for (let i = 0; i < arr.length; i++) { - f(i === 0 ? undefined : arr[i - 1], arr[i], i + 1 === arr.length ? undefined : arr[i + 1]); - } -} - -interface IMutableSplice extends ISplice { - readonly toInsert: T[]; - deleteCount: number; -} - -/** - * Diffs two *sorted* arrays and computes the splices which apply the diff. - */ -export function sortedDiff(before: ReadonlyArray, after: ReadonlyArray, compare: (a: T, b: T) => number): ISplice[] { - const result: IMutableSplice[] = []; - - function pushSplice(start: number, deleteCount: number, toInsert: T[]): void { - if (deleteCount === 0 && toInsert.length === 0) { - return; - } - - const latest = result[result.length - 1]; - - if (latest && latest.start + latest.deleteCount === start) { - latest.deleteCount += deleteCount; - latest.toInsert.push(...toInsert); - } else { - result.push({ start, deleteCount, toInsert }); - } - } - - let beforeIdx = 0; - let afterIdx = 0; - - while (true) { - if (beforeIdx === before.length) { - pushSplice(beforeIdx, 0, after.slice(afterIdx)); - break; - } - if (afterIdx === after.length) { - pushSplice(beforeIdx, before.length - beforeIdx, []); - break; - } - - const beforeElement = before[beforeIdx]; - const afterElement = after[afterIdx]; - const n = compare(beforeElement, afterElement); - if (n === 0) { - // equal - beforeIdx += 1; - afterIdx += 1; - } else if (n < 0) { - // beforeElement is smaller -> before element removed - pushSplice(beforeIdx, 1, []); - beforeIdx += 1; - } else if (n > 0) { - // beforeElement is greater -> after element added - pushSplice(beforeIdx, 0, [afterElement]); - afterIdx += 1; - } - } - - return result; -} - -/** - * Takes two *sorted* arrays and computes their delta (removed, added elements). - * Finishes in `Math.min(before.length, after.length)` steps. - */ -export function delta(before: ReadonlyArray, after: ReadonlyArray, compare: (a: T, b: T) => number): { removed: T[]; added: T[] } { - const splices = sortedDiff(before, after, compare); - const removed: T[] = []; - const added: T[] = []; - - for (const splice of splices) { - removed.push(...before.slice(splice.start, splice.start + splice.deleteCount)); - added.push(...splice.toInsert); - } - - return { removed, added }; -} - -/** - * Returns the top N elements from the array. - * - * Faster than sorting the entire array when the array is a lot larger than N. - * - * @param array The unsorted array. - * @param compare A sort function for the elements. - * @param n The number of elements to return. - * @return The first n elements from array when sorted with compare. - */ -export function top(array: ReadonlyArray, compare: (a: T, b: T) => number, n: number): T[] { - if (n === 0) { - return []; - } - const result = array.slice(0, n).sort(compare); - topStep(array, compare, result, n, array.length); - return result; -} - -/** - * Asynchronous variant of `top()` allowing for splitting up work in batches between which the event loop can run. - * - * Returns the top N elements from the array. - * - * Faster than sorting the entire array when the array is a lot larger than N. - * - * @param array The unsorted array. - * @param compare A sort function for the elements. - * @param n The number of elements to return. - * @param batch The number of elements to examine before yielding to the event loop. - * @return The first n elements from array when sorted with compare. - */ -export function topAsync(array: T[], compare: (a: T, b: T) => number, n: number, batch: number, token?: CancellationToken): Promise { - if (n === 0) { - return Promise.resolve([]); - } - - return new Promise((resolve, reject) => { - (async () => { - const o = array.length; - const result = array.slice(0, n).sort(compare); - for (let i = n, m = Math.min(n + batch, o); i < o; i = m, m = Math.min(m + batch, o)) { - if (i > n) { - await new Promise(resolve => setTimeout(resolve)); // any other delay function would starve I/O - } - if (token && token.isCancellationRequested) { - throw new CancellationError(); - } - topStep(array, compare, result, i, m); - } - return result; - })() - .then(resolve, reject); - }); -} - -function topStep(array: ReadonlyArray, compare: (a: T, b: T) => number, result: T[], i: number, m: number): void { - for (const n = result.length; i < m; i++) { - const element = array[i]; - if (compare(element, result[n - 1]) < 0) { - result.pop(); - const j = findFirstIdxMonotonousOrArrLen(result, e => compare(element, e) < 0); - result.splice(j, 0, element); - } - } -} - -/** - * @returns New array with all falsy values removed. The original array IS NOT modified. - */ -export function coalesce(array: ReadonlyArray): T[] { - return array.filter((e): e is T => !!e); -} - -/** - * Remove all falsy values from `array`. The original array IS modified. - */ -export function coalesceInPlace(array: Array): asserts array is Array { - let to = 0; - for (let i = 0; i < array.length; i++) { - if (!!array[i]) { - array[to] = array[i]; - to += 1; - } - } - array.length = to; -} - -/** - * @deprecated Use `Array.copyWithin` instead - */ -export function move(array: any[], from: number, to: number): void { - array.splice(to, 0, array.splice(from, 1)[0]); -} - -/** - * @returns false if the provided object is an array and not empty. - */ -export function isFalsyOrEmpty(obj: any): boolean { - return !Array.isArray(obj) || obj.length === 0; -} - -/** - * @returns True if the provided object is an array and has at least one element. - */ -export function isNonEmptyArray(obj: T[] | undefined | null): obj is T[]; -export function isNonEmptyArray(obj: readonly T[] | undefined | null): obj is readonly T[]; -export function isNonEmptyArray(obj: T[] | readonly T[] | undefined | null): obj is T[] | readonly T[] { - return Array.isArray(obj) && obj.length > 0; -} - -/** - * Removes duplicates from the given array. The optional keyFn allows to specify - * how elements are checked for equality by returning an alternate value for each. - */ -export function distinct(array: ReadonlyArray, keyFn: (value: T) => any = value => value): T[] { - const seen = new Set(); - - return array.filter(element => { - const key = keyFn!(element); - if (seen.has(key)) { - return false; - } - seen.add(key); - return true; - }); -} - -export function uniqueFilter(keyFn: (t: T) => R): (t: T) => boolean { - const seen = new Set(); - - return element => { - const key = keyFn(element); - - if (seen.has(key)) { - return false; - } - - seen.add(key); - return true; - }; -} - -export function firstOrDefault(array: ReadonlyArray, notFoundValue: NotFound): T | NotFound; -export function firstOrDefault(array: ReadonlyArray): T | undefined; -export function firstOrDefault(array: ReadonlyArray, notFoundValue?: NotFound): T | NotFound | undefined { - return array.length > 0 ? array[0] : notFoundValue; -} - -export function lastOrDefault(array: ReadonlyArray, notFoundValue: NotFound): T | NotFound; -export function lastOrDefault(array: ReadonlyArray): T | undefined; -export function lastOrDefault(array: ReadonlyArray, notFoundValue?: NotFound): T | NotFound | undefined { - return array.length > 0 ? array[array.length - 1] : notFoundValue; -} - -export function commonPrefixLength(one: ReadonlyArray, other: ReadonlyArray, equals: (a: T, b: T) => boolean = (a, b) => a === b): number { - let result = 0; - - for (let i = 0, len = Math.min(one.length, other.length); i < len && equals(one[i], other[i]); i++) { - result++; - } - - return result; -} - -export function range(to: number): number[]; -export function range(from: number, to: number): number[]; -export function range(arg: number, to?: number): number[] { - let from = typeof to === 'number' ? arg : 0; - - if (typeof to === 'number') { - from = arg; - } else { - from = 0; - to = arg; - } - - const result: number[] = []; - - if (from <= to) { - for (let i = from; i < to; i++) { - result.push(i); - } - } else { - for (let i = from; i > to; i--) { - result.push(i); - } - } - - return result; -} - -export function index(array: ReadonlyArray, indexer: (t: T) => string): { [key: string]: T }; -export function index(array: ReadonlyArray, indexer: (t: T) => string, mapper: (t: T) => R): { [key: string]: R }; -export function index(array: ReadonlyArray, indexer: (t: T) => string, mapper?: (t: T) => R): { [key: string]: R } { - return array.reduce((r, t) => { - r[indexer(t)] = mapper ? mapper(t) : t; - return r; - }, Object.create(null)); -} - /** * Inserts an element into an array. Returns a function which, when * called, will remove that element from the array. diff --git a/src/vs/base/common/arraysFind.ts b/src/vs/base/common/arraysFind.ts deleted file mode 100644 index 1dd102e9..00000000 --- a/src/vs/base/common/arraysFind.ts +++ /dev/null @@ -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. - *--------------------------------------------------------------------------------------------*/ - -import { Comparator } from './arrays'; - -export function findLast(array: readonly T[], predicate: (item: T) => boolean): T | undefined { - const idx = findLastIdx(array, predicate); - if (idx === -1) { - return undefined; - } - return array[idx]; -} - -export function findLastIdx(array: readonly T[], predicate: (item: T) => boolean, fromIndex = array.length - 1): number { - for (let i = fromIndex; i >= 0; i--) { - const element = array[i]; - - if (predicate(element)) { - return i; - } - } - - return -1; -} - -/** - * Finds the last item where predicate is true using binary search. - * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`! - * - * @returns `undefined` if no item matches, otherwise the last item that matches the predicate. - */ -export function findLastMonotonous(array: readonly T[], predicate: (item: T) => boolean): T | undefined { - const idx = findLastIdxMonotonous(array, predicate); - return idx === -1 ? undefined : array[idx]; -} - -/** - * Finds the last item where predicate is true using binary search. - * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`! - * - * @returns `startIdx - 1` if predicate is false for all items, otherwise the index of the last item that matches the predicate. - */ -export function findLastIdxMonotonous(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number { - let i = startIdx; - let j = endIdxEx; - while (i < j) { - const k = Math.floor((i + j) / 2); - if (predicate(array[k])) { - i = k + 1; - } else { - j = k; - } - } - return i - 1; -} - -/** - * Finds the first item where predicate is true using binary search. - * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`! - * - * @returns `undefined` if no item matches, otherwise the first item that matches the predicate. - */ -export function findFirstMonotonous(array: readonly T[], predicate: (item: T) => boolean): T | undefined { - const idx = findFirstIdxMonotonousOrArrLen(array, predicate); - return idx === array.length ? undefined : array[idx]; -} - -/** - * Finds the first item where predicate is true using binary search. - * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`! - * - * @returns `endIdxEx` if predicate is false for all items, otherwise the index of the first item that matches the predicate. - */ -export function findFirstIdxMonotonousOrArrLen(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number { - let i = startIdx; - let j = endIdxEx; - while (i < j) { - const k = Math.floor((i + j) / 2); - if (predicate(array[k])) { - j = k; - } else { - i = k + 1; - } - } - return i; -} - -export function findFirstIdxMonotonous(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number { - const idx = findFirstIdxMonotonousOrArrLen(array, predicate, startIdx, endIdxEx); - return idx === array.length ? -1 : idx; -} - -/** - * Use this when - * * You have a sorted array - * * You query this array with a monotonous predicate to find the last item that has a certain property. - * * You query this array multiple times with monotonous predicates that get weaker and weaker. - */ -export class MonotonousArray { - public static assertInvariants = false; - - private _findLastMonotonousLastIdx = 0; - private _prevFindLastPredicate: ((item: T) => boolean) | undefined; - - constructor(private readonly _array: readonly T[]) { - } - - /** - * The predicate must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`! - * For subsequent calls, current predicate must be weaker than (or equal to) the previous predicate, i.e. more entries must be `true`. - */ - findLastMonotonous(predicate: (item: T) => boolean): T | undefined { - if (MonotonousArray.assertInvariants) { - if (this._prevFindLastPredicate) { - for (const item of this._array) { - if (this._prevFindLastPredicate(item) && !predicate(item)) { - throw new Error('MonotonousArray: current predicate must be weaker than (or equal to) the previous predicate.'); - } - } - } - this._prevFindLastPredicate = predicate; - } - - const idx = findLastIdxMonotonous(this._array, predicate, this._findLastMonotonousLastIdx); - this._findLastMonotonousLastIdx = idx + 1; - return idx === -1 ? undefined : this._array[idx]; - } -} - -/** - * Returns the first item that is equal to or greater than every other item. -*/ -export function findFirstMax(array: readonly T[], comparator: Comparator): T | undefined { - if (array.length === 0) { - return undefined; - } - - let max = array[0]; - for (let i = 1; i < array.length; i++) { - const item = array[i]; - if (comparator(item, max) > 0) { - max = item; - } - } - return max; -} - -/** - * Returns the last item that is equal to or greater than every other item. -*/ -export function findLastMax(array: readonly T[], comparator: Comparator): T | undefined { - if (array.length === 0) { - return undefined; - } - - let max = array[0]; - for (let i = 1; i < array.length; i++) { - const item = array[i]; - if (comparator(item, max) >= 0) { - max = item; - } - } - return max; -} - -/** - * Returns the first item that is equal to or less than every other item. -*/ -export function findFirstMin(array: readonly T[], comparator: Comparator): T | undefined { - return findFirstMax(array, (a, b) => -comparator(a, b)); -} - -export function findMaxIdx(array: readonly T[], comparator: Comparator): number { - if (array.length === 0) { - return -1; - } - - let maxIdx = 0; - for (let i = 1; i < array.length; i++) { - const item = array[i]; - if (comparator(item, array[maxIdx]) > 0) { - maxIdx = i; - } - } - return maxIdx; -} - -/** - * Returns the first mapped value of the array which is not undefined. - */ -export function mapFindFirst(items: Iterable, mapFn: (value: T) => R | undefined): R | undefined { - for (const value of items) { - const mapped = mapFn(value); - if (mapped !== undefined) { - return mapped; - } - } - - return undefined; -} diff --git a/src/vs/base/common/equals.ts b/src/vs/base/common/equals.ts index 6e2ae850..8ce99433 100644 --- a/src/vs/base/common/equals.ts +++ b/src/vs/base/common/equals.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as arrays from 'vs/base/common/arrays'; - export type EqualityComparer = (a: T, b: T) => boolean; /** @@ -12,14 +10,6 @@ export type EqualityComparer = (a: T, b: T) => boolean; */ export const strictEquals: EqualityComparer = (a, b) => a === b; -/** - * Checks if the items of two arrays are equal. - * By default, strict equality is used to compare elements, but a custom equality comparer can be provided. - */ -export function itemsEquals(itemEquals: EqualityComparer = strictEquals): EqualityComparer { - return (a, b) => arrays.equals(a, b, itemEquals); -} - /** * Two items are considered equal, if their stringified representations are equal. */