enso/app/gui2/shared/util/data/iterable.ts
Kaz Wesley c811a5ae8b
Enable the Code Editor, with new apply-text-edits algo. (#9055)
- Fix the UI problems with our CodeMirror integration (Fixed view stability; Fixed a focus bug; Fixed errors caused by diagnostics range exceptions; Fixed linter invalidation--see https://discuss.codemirror.net/t/problem-trying-to-force-linting/5823; Implemented edit-coalescing for performance).
- Introduce an algorithm for applying text edits to an AST. Compared to the GUI1 approach, the new algorithm supports deeper identity-stability for expressions (which is important for subexpression metadata and Y.Js sync), as well as reordered-subtree identification.
- Enable the code editor.
2024-02-19 23:57:42 +00:00

99 lines
2.9 KiB
TypeScript

/** @file Functions for manipulating {@link Iterable}s. */
export function* empty(): Generator<never> {}
export function* range(start: number, stop: number, step = start <= stop ? 1 : -1) {
if ((step > 0 && start > stop) || (step < 0 && start < stop)) {
throw new Error(
"The range's step is in the wrong direction - please use Infinity or -Infinity as the endpoint for an infinite range.",
)
}
if (start <= stop) {
while (start < stop) {
yield start
start += step
}
} else {
while (start > stop) {
yield start
start += step
}
}
}
export function* map<T, U>(iter: Iterable<T>, map: (value: T) => U) {
for (const value of iter) {
yield map(value)
}
}
export function* chain<T>(...iters: Iterable<T>[]) {
for (const iter of iters) {
yield* iter
}
}
export function* zip<T, U>(left: Iterable<T>, right: Iterable<U>): Generator<[T, U]> {
const leftIterator = left[Symbol.iterator]()
const rightIterator = right[Symbol.iterator]()
while (true) {
const leftResult = leftIterator.next()
const rightResult = rightIterator.next()
if (leftResult.done || rightResult.done) break
yield [leftResult.value, rightResult.value]
}
}
export function* zipLongest<T, U>(
left: Iterable<T>,
right: Iterable<U>,
): Generator<[T | undefined, U | undefined]> {
const leftIterator = left[Symbol.iterator]()
const rightIterator = right[Symbol.iterator]()
while (true) {
const leftResult = leftIterator.next()
const rightResult = rightIterator.next()
if (leftResult.done && rightResult.done) break
yield [
leftResult.done ? undefined : leftResult.value,
rightResult.done ? undefined : rightResult.value,
]
}
}
export function tryGetSoleValue<T>(iter: Iterable<T>): T | undefined {
const iterator = iter[Symbol.iterator]()
const result = iterator.next()
if (result.done) return
const excessResult = iterator.next()
if (!excessResult.done) return
return result.value
}
/** Utility to simplify consuming an iterator a part at a time. */
export class Resumable<T> {
private readonly iterator: Iterator<T>
private current: IteratorResult<T>
constructor(iterable: Iterable<T>) {
this.iterator = iterable[Symbol.iterator]()
this.current = this.iterator.next()
}
/** The given function peeks at the current value. If the function returns `true`, the current value will be advanced
* and the function called again; if it returns `false`, the peeked value remains current and `advanceWhile` returns.
*/
advanceWhile(f: (value: T) => boolean) {
while (!this.current.done && f(this.current.value)) {
this.current = this.iterator.next()
}
}
/** Apply the given function to all values remaining in the iterator. */
forEach(f: (value: T) => void) {
while (!this.current.done) {
f(this.current.value)
this.current = this.iterator.next()
}
}
}