mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Lint and remove more parts
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user