Lint and remove more parts

This commit is contained in:
Daniel Imms
2026-02-01 22:36:14 -08:00
parent d5900c7eae
commit a105d3e457
19 changed files with 180 additions and 833 deletions
+6 -6
View File
@@ -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;
}
+21 -21
View File
@@ -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 {
+4 -3
View File
@@ -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];
};
}
+6 -4
View File
@@ -12,12 +12,12 @@ export interface IRegisteredWindow {
readonly disposables: DisposableStore;
}
const _onDidRegisterWindow = new Emitter<IRegisteredWindow>();
export const onDidRegisterWindow: IEvent<IRegisteredWindow> = _onDidRegisterWindow.event;
const onDidRegisterWindowEmitter = new Emitter<IRegisteredWindow>();
export const onDidRegisterWindow: IEvent<IRegisteredWindow> = 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);
@@ -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);
}
+13 -13
View File
@@ -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;
}
+13 -13
View File
@@ -5,13 +5,13 @@
class Node<E> {
static readonly Undefined = new Node<any>(undefined);
public static readonly Undefined = new Node<any>(undefined);
element: E;
next: Node<E>;
prev: Node<E>;
public element: E;
public next: Node<E>;
public prev: Node<E>;
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<E> {
private _last: Node<E> = 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<E> {
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<E> {
};
}
shift(): E | undefined {
public shift(): E | undefined {
if (this._first === Node.Undefined) {
return undefined;
}
@@ -95,7 +95,7 @@ export class LinkedList<E> {
}
pop(): E | undefined {
public pop(): E | undefined {
if (this._last === Node.Undefined) {
return undefined;
}
@@ -132,7 +132,7 @@ export class LinkedList<E> {
this._size -= 1;
}
*[Symbol.iterator](): Iterator<E> {
public *[Symbol.iterator](): Iterator<E> {
let node = this._first;
while (node !== Node.Undefined) {
yield node.element;
+2 -2
View File
@@ -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) {
+17 -17
View File
@@ -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<ScrollEvent>());
public readonly onScroll: IEvent<ScrollEvent> = this._onScroll.event;
private _onScroll = this._register(new Emitter<IScrollEvent>());
public readonly onScroll: IEvent<IScrollEvent> = 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);
}
+40 -40
View File
@@ -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<ScrollEvent>());
public readonly onScroll: IEvent<ScrollEvent> = this._onScroll.event;
private readonly _onScroll = this._register(new Emitter<IScrollEvent>());
public readonly onScroll: IEvent<IScrollEvent> = this._onScroll.event;
private readonly _onWillScroll = this._register(new Emitter<ScrollEvent>());
public readonly onWillScroll: IEvent<ScrollEvent> = this._onWillScroll.event;
private readonly _onWillScroll = this._register(new Emitter<IScrollEvent>());
public readonly onWillScroll: IEvent<IScrollEvent> = this._onWillScroll.event;
public get options(): Readonly<ScrollableElementResolvedOptions> {
public get options(): Readonly<IScrollableElementResolvedOptions> {
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),
@@ -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;
+10 -10
View File
@@ -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);
+17 -2
View File
@@ -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);
@@ -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;
+11 -11
View File
@@ -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;
+11 -11
View File
@@ -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);
}
}
-459
View File
@@ -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<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]];
}
export function equals<T>(one: ReadonlyArray<T> | undefined, other: ReadonlyArray<T> | 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<T>(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<T>(array: ReadonlyArray<T>, 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<T> = (a: T, b: T) => number;
export function quickSelect<T>(nth: number, data: T[], compare: Compare<T>): 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<T>(data: ReadonlyArray<T>, 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<T>(items: Iterable<T>, shouldBeGrouped: (item1: T, item2: T) => boolean): Iterable<T[]> {
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<T>(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<T>(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<T> extends ISplice<T> {
readonly toInsert: T[];
deleteCount: number;
}
/**
* Diffs two *sorted* arrays and computes the splices which apply the diff.
*/
export function sortedDiff<T>(before: ReadonlyArray<T>, after: ReadonlyArray<T>, compare: (a: T, b: T) => number): ISplice<T>[] {
const result: IMutableSplice<T>[] = [];
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<T>(before: ReadonlyArray<T>, after: ReadonlyArray<T>, 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<T>(array: ReadonlyArray<T>, 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<T>(array: T[], compare: (a: T, b: T) => number, n: number, batch: number, token?: CancellationToken): Promise<T[]> {
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<T>(array: ReadonlyArray<T>, 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<T>(array: ReadonlyArray<T | undefined | null>): T[] {
return array.filter((e): e is T => !!e);
}
/**
* Remove all falsy values from `array`. The original array IS modified.
*/
export function coalesceInPlace<T>(array: Array<T | undefined | null>): asserts array is Array<T> {
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<T>(obj: T[] | undefined | null): obj is T[];
export function isNonEmptyArray<T>(obj: readonly T[] | undefined | null): obj is readonly T[];
export function isNonEmptyArray<T>(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<T>(array: ReadonlyArray<T>, keyFn: (value: T) => any = value => value): T[] {
const seen = new Set<any>();
return array.filter(element => {
const key = keyFn!(element);
if (seen.has(key)) {
return false;
}
seen.add(key);
return true;
});
}
export function uniqueFilter<T, R>(keyFn: (t: T) => R): (t: T) => boolean {
const seen = new Set<R>();
return element => {
const key = keyFn(element);
if (seen.has(key)) {
return false;
}
seen.add(key);
return true;
};
}
export function firstOrDefault<T, NotFound = T>(array: ReadonlyArray<T>, notFoundValue: NotFound): T | NotFound;
export function firstOrDefault<T>(array: ReadonlyArray<T>): T | undefined;
export function firstOrDefault<T, NotFound = T>(array: ReadonlyArray<T>, notFoundValue?: NotFound): T | NotFound | undefined {
return array.length > 0 ? array[0] : notFoundValue;
}
export function lastOrDefault<T, NotFound = T>(array: ReadonlyArray<T>, notFoundValue: NotFound): T | NotFound;
export function lastOrDefault<T>(array: ReadonlyArray<T>): T | undefined;
export function lastOrDefault<T, NotFound = T>(array: ReadonlyArray<T>, notFoundValue?: NotFound): T | NotFound | undefined {
return array.length > 0 ? array[array.length - 1] : notFoundValue;
}
export function commonPrefixLength<T>(one: ReadonlyArray<T>, other: ReadonlyArray<T>, 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<T>(array: ReadonlyArray<T>, indexer: (t: T) => string): { [key: string]: T };
export function index<T, R>(array: ReadonlyArray<T>, indexer: (t: T) => string, mapper: (t: T) => R): { [key: string]: R };
export function index<T, R>(array: ReadonlyArray<T>, 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.
-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.
*--------------------------------------------------------------------------------------------*/
import { Comparator } from './arrays';
export function findLast<T>(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<T>(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<T>(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<T>(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<T>(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<T>(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<T>(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<T> {
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<T>(array: readonly T[], comparator: Comparator<T>): 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<T>(array: readonly T[], comparator: Comparator<T>): 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<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {
return findFirstMax(array, (a, b) => -comparator(a, b));
}
export function findMaxIdx<T>(array: readonly T[], comparator: Comparator<T>): 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<T, R>(items: Iterable<T>, mapFn: (value: T) => R | undefined): R | undefined {
for (const value of items) {
const mapped = mapFn(value);
if (mapped !== undefined) {
return mapped;
}
}
return undefined;
}
-10
View File
@@ -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<T> = (a: T, b: T) => boolean;
/**
@@ -12,14 +10,6 @@ export type EqualityComparer<T> = (a: T, b: T) => boolean;
*/
export const strictEquals: EqualityComparer<any> = (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<T>(itemEquals: EqualityComparer<T> = strictEquals): EqualityComparer<readonly T[]> {
return (a, b) => arrays.equals(a, b, itemEquals);
}
/**
* Two items are considered equal, if their stringified representations are equal.
*/