thorish
Version:
This is a library of useful JS concepts and data structures for Node and the browser. It it, unashamedly, a dumping ground for code needed by [@samthor](https://twitter.com/samthor)'s projects.
178 lines (177 loc) • 4.95 kB
TypeScript
/**
* @fileoverview Derived from @josephg's implementation here: https://github.com/josephg/jumprope
*
* MIT license etc etc
*
* Version that doesn't break reactivity.
*/
export type RopeLookup<K, T> = {
data: T;
length: number;
/**
* The ID of this lookup.
* This will be the same as what was passed in to perform the lookup.
*/
id: K;
/**
* The previous ID in sequence.
*/
prevId: K;
/**
* The next ID in sequence.
* This will be the zero ID if there is no following data.
*/
nextId: K;
};
export type RopeRead<T> = {
out: T[];
len: number[];
};
/**
* Implements a skip list with `O(logn)-ish` performance.
*
* We store data here, but mostly only as a convenience: it's a simple k/v map.
* The length is far more important.
*
* Individual parts can have zero length.
*/
export declare class Rope<K, T = void> {
private _length;
private head;
private tail;
private readonly _zeroId;
private readonly byId;
private readonly dataById;
private readonly nodeCache;
/**
* The zero ID that this {@link Rope} was created with.
*/
zeroId(): K;
/**
* Clones this {@link Rope} using {@link structuredClone}.
*/
clone(): Rope<K, T>;
/**
* Constructs a new {@link Rope}.
*
* The second argument may be passed purely as a TypeScript hint for {@link T} and it is stored on the root node, but it is not required and cannot ever be read.
*/
constructor(zeroId: K, sampleData?: T);
/**
* The total length of all items in this {@link Rope}.
*/
length(): number;
/**
* The ID of the right-most entry here.
*/
last(): K;
/**
* The count of items in this {@link Rope}, even zero-length ones.
*/
count(): number;
/**
* Find the length between these two valid IDs.
*
* This isn't a substitute for {@link compare} as zero length entries are allowed, so this won't return which one is first.
*
* Currently just calls {@link find} twice, so `O(logn)-ish`.
*/
lengthBetween(low: K, high: K): number;
/**
* Prints out the rope for debugging.
*/
_debug(): void;
[Symbol.iterator](): Iterator<T, void, void>;
/**
* Finds the position after the given ID.
*
* Perf: `O(logn)-ish`.
*/
find(ropeId: K): number;
/**
* Does this ID exist here.
*
* Perf: `O(1)-ish` (just {@link Map}).
*/
has(ropeId: K): boolean;
/**
* Lookup information on this ID.
*
* This throws when trying to look up the zero ID.
*
* Perf: `O(1)-ish` (just {@link Map}).
*/
lookup(ropeId: K): RopeLookup<K, T>;
/**
* Find the ID for the given position, and the offset from the end of that ID.
* Always returns a valid value, is clamped to edge.
*
* By default, this will be the left-most ID that contains the position (even 'at end').
* For example, looking up `offset=0` in an already-used rope will always yield `id=0`, as it has zero length.
*
* Specify the `biasEnd` parameter to flip this behavior.
*
* Perf: `O(logn)-ish`.
*/
byPosition(position: number, biasAfter?: boolean): {
id: K;
offset: number;
};
/**
* Reduced version of `rseek` for various purposes...
*/
private rseekNodes;
/**
* Adjust the given entry's data/length.
*/
adjust(id: K, data: T, length: number): void;
/**
* Inserts a node after a previous node.
*
* Perf: `O(logn)-ish`.
*/
insertAfter(afterId: K, newId: K, length: number, data: T): void;
/**
* Deletes the given ID from this rope.
*
* Perf: `O(logn)-ish`.
*/
deleteById(id: K): void;
/**
* Deletes after the given ID until the target ID.
*
* Perf: `O(logn)-ish`.
*/
deleteTo(afterId: K, untilId: K): void;
private insertIntoPool;
/**
* Is the ID in `a` before the ID in `b`?
*
* Perf: `O(logn)-ish`.
*/
before(a: K, b: K): boolean;
/**
* Compares the position of these two IDs.
*
* Returns -1 if A is before B, zero if they are the same, and +1 if A is after B.
*
* Perf: `O(logn)-ish`.
*/
compare(a: K, b: K): number;
/**
* Iterate from after the given ID, to the target ID inclusive (i.e., `(afterId,untilId]`).
*
* If no `untilId` is passed or the IDs are in the wrong order, iterates from after `afterId` until the end of this {@link Rope}.
*/
iter(afterId?: K, untilId?: K): Iterable<{
id: K;
data: T;
length: number;
}, void, void>;
/**
* Reads all data from after the given ID, to the target ID inclusive.
*
* This is a convenience over {@link iter} and has the same semantics.
*/
read(afterId?: K, untilId?: K): RopeRead<T>;
}