export class ListenableGenerator {
    onError;
    _source;
    _buffer = [];
    _listeners = new Set();
    _isEnded = false;
    constructor(source, onError) {
        this.onError = onError;
        this._source = source;
        this._start();
    }
    cancel() {
        this._isEnded = true;
    }
    async _start() {
        try {
            for await (const value of this._source) {
                if (this._isEnded) {
                    break;
                }
                this._buffer.push(value);
                for (const listener of this._listeners) {
                    listener(value);
                }
            }
        }
        catch (e) {
            this.onError(e);
        }
        finally {
            this._isEnded = true;
            for (const listener of this._listeners) {
                listener(null);
            }
        }
    }
    listen(listener) {
        this._listeners.add(listener);
        for (const value of this._buffer) {
            listener(value);
        }
        if (this._isEnded) {
            listener(null);
        }
    }
    async *tee() {
        try {
            let i = 0;
            while (i < this._buffer.length) {
                yield this._buffer[i++];
            }
            while (!this._isEnded) {
                let resolve;
                const promise = new Promise((res) => {
                    resolve = res;
                    this._listeners.add(resolve);
                });
                await promise;
                this._listeners.delete(resolve);
                // Possible timing caused something to slip in between
                // timers so we iterate over the buffer
                while (i < this._buffer.length) {
                    yield this._buffer[i++];
                }
            }
        }
        finally {
            // this._listeners.delete(resolve!);
        }
    }
}
export class GeneratorReuseManager {
    onError;
    currentGenerator;
    pendingGeneratorPrefix;
    pendingCompletion = "";
    constructor(onError) {
        this.onError = onError;
    }
    _createListenableGenerator(gen, prefix) {
        this.currentGenerator?.cancel();
        const listenableGen = new ListenableGenerator(gen, this.onError);
        listenableGen.listen((chunk) => (this.pendingCompletion += chunk ?? ""));
        this.pendingGeneratorPrefix = prefix;
        this.pendingCompletion = "";
        this.currentGenerator = listenableGen;
    }
    async *getGenerator(prefix, newGenerator, multiline) {
        // Check if current can be reused
        if (!(this.currentGenerator &&
            this.pendingGeneratorPrefix &&
            (this.pendingGeneratorPrefix + this.pendingCompletion).startsWith(prefix) &&
            // for e.g. backspace
            this.pendingGeneratorPrefix?.length <= prefix?.length)) {
            // Create a wrapper over the current generator to fix the prompt
            this._createListenableGenerator(newGenerator(), prefix);
        }
        let alreadyTyped = prefix.slice(this.pendingGeneratorPrefix?.length) || "";
        for await (let chunk of this.currentGenerator?.tee() ?? []) {
            if (!chunk) {
                continue;
            }
            while (chunk.length && alreadyTyped.length) {
                if (chunk[0] === alreadyTyped[0]) {
                    alreadyTyped = alreadyTyped.slice(1);
                    chunk = chunk.slice(1);
                }
                else {
                    break;
                }
            }
            const newLineIndex = chunk.indexOf("\n");
            if (multiline || newLineIndex === -1) {
                yield chunk;
            }
            else {
                yield chunk.slice(0, newLineIndex);
                break;
            }
        }
    }
}
