Merge branch 'master' into 1134_3
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { FourKeyMap, TwoKeyMap } from 'common/MultiKeyMap';
|
||||
|
||||
const strictEqual = assert.strictEqual;
|
||||
|
||||
describe('TwoKeyMap', () => {
|
||||
let map: TwoKeyMap<number | string, number | string, string>;
|
||||
|
||||
beforeEach(() => {
|
||||
map = new TwoKeyMap();
|
||||
});
|
||||
|
||||
it('set, get', () => {
|
||||
strictEqual(map.get(1, 2), undefined);
|
||||
map.set(1, 2, 'foo');
|
||||
strictEqual(map.get(1, 2), 'foo');
|
||||
map.set(1, 3, 'bar');
|
||||
strictEqual(map.get(1, 2), 'foo');
|
||||
strictEqual(map.get(1, 3), 'bar');
|
||||
map.set(2, 2, 'foo2');
|
||||
map.set(2, 3, 'bar2');
|
||||
strictEqual(map.get(1, 2), 'foo');
|
||||
strictEqual(map.get(1, 3), 'bar');
|
||||
strictEqual(map.get(2, 2), 'foo2');
|
||||
strictEqual(map.get(2, 3), 'bar2');
|
||||
});
|
||||
it('clear', () => {
|
||||
strictEqual(map.get(1, 2), undefined);
|
||||
map.set(1, 2, 'foo');
|
||||
strictEqual(map.get(1, 2), 'foo');
|
||||
map.clear();
|
||||
strictEqual(map.get(1, 2), undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('FourKeyMap', () => {
|
||||
let map: FourKeyMap<number | string, number | string, number | string, number | string, string>;
|
||||
|
||||
beforeEach(() => {
|
||||
map = new FourKeyMap();
|
||||
});
|
||||
|
||||
it('set, get', () => {
|
||||
strictEqual(map.get(1, 2, 3, 4), undefined);
|
||||
map.set(1, 2, 3, 4, 'foo');
|
||||
strictEqual(map.get(1, 2, 3, 4), 'foo');
|
||||
map.set(1, 3, 3, 4, 'bar');
|
||||
strictEqual(map.get(1, 2, 3, 4), 'foo');
|
||||
strictEqual(map.get(1, 3, 3, 4), 'bar');
|
||||
map.set(2, 2, 3, 4, 'foo2');
|
||||
map.set(2, 3, 3, 4, 'bar2');
|
||||
strictEqual(map.get(1, 2, 3, 4), 'foo');
|
||||
strictEqual(map.get(1, 3, 3, 4), 'bar');
|
||||
strictEqual(map.get(2, 2, 3, 4), 'foo2');
|
||||
strictEqual(map.get(2, 3, 3, 4), 'bar2');
|
||||
});
|
||||
it('clear', () => {
|
||||
strictEqual(map.get(1, 2, 3, 4), undefined);
|
||||
map.set(1, 2, 3, 4, 'foo');
|
||||
strictEqual(map.get(1, 2, 3, 4), 'foo');
|
||||
map.clear();
|
||||
strictEqual(map.get(1, 2, 3, 4), undefined);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2022 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
export class TwoKeyMap<TFirst extends string | number, TSecond extends string | number, TValue> {
|
||||
private _data: { [bg: string | number]: { [fg: string | number]: TValue | undefined } | undefined } = {};
|
||||
|
||||
public set(first: TFirst, second: TSecond, value: TValue): void {
|
||||
if (!this._data[first]) {
|
||||
this._data[first] = {};
|
||||
}
|
||||
this._data[first as string | number]![second] = value;
|
||||
}
|
||||
|
||||
public get(first: TFirst, second: TSecond): TValue | undefined {
|
||||
return this._data[first as string | number] ? this._data[first as string | number]![second] : undefined;
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this._data = {};
|
||||
}
|
||||
}
|
||||
|
||||
export class FourKeyMap<TFirst extends string | number, TSecond extends string | number, TThird extends string | number, TFourth extends string | number, TValue> {
|
||||
private _data: TwoKeyMap<TFirst, TSecond, TwoKeyMap<TThird, TFourth, TValue>> = new TwoKeyMap();
|
||||
|
||||
public set(first: TFirst, second: TSecond, third: TThird, fourth: TFourth, value: TValue): void {
|
||||
if (!this._data.get(first, second)) {
|
||||
this._data.set(first, second, new TwoKeyMap());
|
||||
}
|
||||
this._data.get(first, second)!.set(third, fourth, value);
|
||||
}
|
||||
|
||||
public get(first: TFirst, second: TSecond, third: TThird, fourth: TFourth): TValue | undefined {
|
||||
return this._data.get(first, second)?.get(third, fourth);
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this._data.clear();
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,9 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
// Work variables to avoid garbage collection.
|
||||
let i = 0;
|
||||
|
||||
/**
|
||||
* A generic list that is maintained in sorted order and allows values with duplicate keys. This
|
||||
* list is based on binary search and as such locating a key will take O(log n) amortized, this
|
||||
@@ -25,7 +28,7 @@ export class SortedList<T> {
|
||||
this._array.push(value);
|
||||
return;
|
||||
}
|
||||
const i = this._search(this._getKey(value), 0, this._array.length - 1);
|
||||
i = this._search(this._getKey(value), 0, this._array.length - 1);
|
||||
this._array.splice(i, 0, value);
|
||||
}
|
||||
|
||||
@@ -37,7 +40,7 @@ export class SortedList<T> {
|
||||
if (key === undefined) {
|
||||
return false;
|
||||
}
|
||||
let i = this._search(key, 0, this._array.length - 1);
|
||||
i = this._search(key, 0, this._array.length - 1);
|
||||
if (i === -1) {
|
||||
return false;
|
||||
}
|
||||
@@ -57,7 +60,7 @@ export class SortedList<T> {
|
||||
if (this._array.length === 0) {
|
||||
return;
|
||||
}
|
||||
let i = this._search(key, 0, this._array.length - 1);
|
||||
i = this._search(key, 0, this._array.length - 1);
|
||||
if (i < 0 || i >= this._array.length) {
|
||||
return;
|
||||
}
|
||||
@@ -69,6 +72,22 @@ export class SortedList<T> {
|
||||
} while (++i < this._array.length && this._getKey(this._array[i]) === key);
|
||||
}
|
||||
|
||||
public forEachByKey(key: number, callback: (value: T) => void): void {
|
||||
if (this._array.length === 0) {
|
||||
return;
|
||||
}
|
||||
i = this._search(key, 0, this._array.length - 1);
|
||||
if (i < 0 || i >= this._array.length) {
|
||||
return;
|
||||
}
|
||||
if (this._getKey(this._array[i]) !== key) {
|
||||
return;
|
||||
}
|
||||
do {
|
||||
callback(this._array[i]);
|
||||
} while (++i < this._array.length && this._getKey(this._array[i]) === key);
|
||||
}
|
||||
|
||||
public values(): IterableIterator<T> {
|
||||
return this._array.values();
|
||||
}
|
||||
|
||||
@@ -173,7 +173,6 @@ export class MockDecorationService implements IDecorationService {
|
||||
public onDecorationRemoved = new EventEmitter<IInternalDecoration>().event;
|
||||
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined { return undefined; }
|
||||
public reset(): void { }
|
||||
public *getDecorationsAtLine(line: number): IterableIterator<IInternalDecoration> { }
|
||||
public *getDecorationsAtCell(x: number, line: number): IterableIterator<IInternalDecoration> { }
|
||||
public forEachDecorationAtCell(x: number, line: number, layer: 'bottom' | 'top' | undefined, callback: (decoration: IInternalDecoration) => void): void { }
|
||||
public dispose(): void { }
|
||||
}
|
||||
|
||||
@@ -37,6 +37,11 @@ const enum Cell {
|
||||
|
||||
export const DEFAULT_ATTR_DATA = Object.freeze(new AttributeData());
|
||||
|
||||
/** Work variables to avoid garbage collection. */
|
||||
const w: { startIndex: number } = {
|
||||
startIndex: 0
|
||||
};
|
||||
|
||||
/**
|
||||
* Typed array based bufferline implementation.
|
||||
*
|
||||
@@ -168,10 +173,10 @@ export class BufferLine implements IBufferLine {
|
||||
* to GC as it significantly reduced the amount of new objects/references needed.
|
||||
*/
|
||||
public loadCell(index: number, cell: ICellData): ICellData {
|
||||
const startIndex = index * CELL_SIZE;
|
||||
cell.content = this._data[startIndex + Cell.CONTENT];
|
||||
cell.fg = this._data[startIndex + Cell.FG];
|
||||
cell.bg = this._data[startIndex + Cell.BG];
|
||||
w.startIndex = index * CELL_SIZE;
|
||||
cell.content = this._data[w.startIndex + Cell.CONTENT];
|
||||
cell.fg = this._data[w.startIndex + Cell.FG];
|
||||
cell.bg = this._data[w.startIndex + Cell.BG];
|
||||
if (cell.content & Content.IS_COMBINED_MASK) {
|
||||
cell.combinedData = this._combined[index];
|
||||
}
|
||||
|
||||
@@ -58,14 +58,14 @@ export class BufferSet extends Disposable implements IBufferSet {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the normal Buffer of the BufferSet
|
||||
* Returns the currently active Buffer of the BufferSet
|
||||
*/
|
||||
public get active(): Buffer {
|
||||
return this._activeBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently active Buffer of the BufferSet
|
||||
* Returns the normal Buffer of the BufferSet
|
||||
*/
|
||||
public get normal(): Buffer {
|
||||
return this._normal;
|
||||
|
||||
@@ -11,6 +11,12 @@ import { SortedList } from 'common/SortedList';
|
||||
import { IColor } from 'common/Types';
|
||||
import { IDecorationOptions, IDecoration, IMarker, IEvent } from 'xterm';
|
||||
|
||||
/** Work variables to avoid garbage collection. */
|
||||
const w = {
|
||||
xmin: 0,
|
||||
xmax: 0
|
||||
};
|
||||
|
||||
export class DecorationService extends Disposable implements IDecorationService {
|
||||
public serviceBrand: any;
|
||||
|
||||
@@ -56,10 +62,6 @@ export class DecorationService extends Disposable implements IDecorationService
|
||||
this._decorations.clear();
|
||||
}
|
||||
|
||||
public *getDecorationsAtLine(line: number): IterableIterator<IInternalDecoration> {
|
||||
return this._decorations.getKeyIterator(line);
|
||||
}
|
||||
|
||||
public *getDecorationsAtCell(x: number, line: number, layer?: 'bottom' | 'top'): IterableIterator<IInternalDecoration> {
|
||||
let xmin = 0;
|
||||
let xmax = 0;
|
||||
@@ -72,6 +74,16 @@ export class DecorationService extends Disposable implements IDecorationService
|
||||
}
|
||||
}
|
||||
|
||||
public forEachDecorationAtCell(x: number, line: number, layer: 'bottom' | 'top' | undefined, callback: (decoration: IInternalDecoration) => void): void {
|
||||
this._decorations.forEachByKey(line, d => {
|
||||
w.xmin = d.options.x ?? 0;
|
||||
w.xmax = w.xmin + (d.options.width ?? 1);
|
||||
if (x >= w.xmin && x < w.xmax && (!layer || (d.options.layer ?? 'bottom') === layer)) {
|
||||
callback(d);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
for (const d of this._decorations.values()) {
|
||||
this._onDecorationRemoved.fire(d);
|
||||
|
||||
@@ -321,10 +321,11 @@ export interface IDecorationService extends IDisposable {
|
||||
readonly onDecorationRemoved: IEvent<IInternalDecoration>;
|
||||
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
|
||||
reset(): void;
|
||||
/** Iterates over the decorations at a line (in no particular order). */
|
||||
getDecorationsAtLine(line: number): IterableIterator<IInternalDecoration>;
|
||||
/** Iterates over the decorations at a cell (in no particular order). */
|
||||
getDecorationsAtCell(x: number, line: number, layer?: 'bottom' | 'top'): IterableIterator<IInternalDecoration>;
|
||||
/**
|
||||
* Trigger a callback over the decoration at a cell (in no particular order). This uses a callback
|
||||
* instead of an iterator as it's typically used in hot code paths.
|
||||
*/
|
||||
forEachDecorationAtCell(x: number, line: number, layer: 'bottom' | 'top' | undefined, callback: (decoration: IInternalDecoration) => void): void;
|
||||
}
|
||||
export interface IInternalDecoration extends IDecoration {
|
||||
readonly options: IDecorationOptions;
|
||||
|
||||
Reference in New Issue
Block a user