diff --git a/src/commands/interface-manager/DeadDocumentMarkdown.ts b/src/commands/interface-manager/DeadDocumentMarkdown.ts index 61bde0cf..dfa7bb04 100644 --- a/src/commands/interface-manager/DeadDocumentMarkdown.ts +++ b/src/commands/interface-manager/DeadDocumentMarkdown.ts @@ -11,17 +11,26 @@ import { DocumentNode, FringeInnerRenderFunction, FringeLeafRenderFunction, Frin // that renders the node twice. // The first thing to get full we stop. // this is possible if we make an iterator that just renders 1 node at a time -export class TransactionalOutputStream { +export class PagedOutputStream { private buffer: string = ''; - public output: string = ''; + private pages: string[] = [''] + private lastCommittedNode?: DocumentNode; constructor( public readonly sizeLimit = 20_000, ) { - } - public writeString(string: string): TransactionalOutputStream { + private get currentPage(): string { + return this.pages.at(this.pages.length - 1)!; + } + + private appendToCurrentPage(string: string) { + const currentIndex = this.pages.length - 1; + this.pages[currentIndex] = this.pages[currentIndex] + string; + } + + public writeString(string: string): PagedOutputStream { this.buffer += string; return this; } @@ -30,34 +39,41 @@ export class TransactionalOutputStream { return this.buffer.length; } - private adjust(source: string, target: string): void { - if (source.at(-2) === '\n' && source.at(-1) === '\n') return; - if (source.at(-1) === '\n') { - target += '\n'; - return; + public getPages(): string[] { + if (this.buffer.length !== 0) { + throw new TypeError('Stream has uncommitted buffered output'); } - target += '\n\n'; + return [...this.pages] } - public adjustForNextElement(): void { - this.adjust(this.buffer, this.buffer) + public isPageAndBufferOverSize(): boolean { + return (this.currentPage.length + this.buffer.length) > this.sizeLimit; + } + + public forceNewPage(node: DocumentNode): void { + if (this.currentPage.length === 0 && (this.buffer.length > this.sizeLimit)) { + throw new TypeError('Commit is too large, could not write a page for this commit'); + } + this.pages.push(this.buffer); + this.buffer = ''; + this.lastCommittedNode = node; } /** * Attempt to commit the buffer to the output stream. - * @param node The node to associate with the commit. - * @returns True if commited, false if uncomitted. Buffer remains unchanged - * if commit was unsuccessful. + * OR create a new page + * Returns true if a new page was created. */ - public attemptCommit(node: DocumentNode): boolean { - if ((this.output.length + this.buffer.length) > this.sizeLimit) { + public commit(node: DocumentNode): boolean { + if (this.isPageAndBufferOverSize()) { + this.forceNewPage(node); + return true; + } else { + this.appendToCurrentPage(this.buffer); + this.buffer = ''; + this.lastCommittedNode = node; return false; } - this.output += this.buffer; - this.buffer = ''; - this.adjust(this.output, this.buffer); - this.lastCommittedNode = node; - return true; } public getLastCommittedNode(): DocumentNode|undefined { @@ -66,7 +82,7 @@ export class TransactionalOutputStream { } export interface TransactionalOutputContext { - output: TransactionalOutputStream + output: PagedOutputStream } export function staticString(string: string): FringeInnerRenderFunction { diff --git a/src/commands/interface-manager/DeadDocumentMatrix.ts b/src/commands/interface-manager/DeadDocumentMatrix.ts index 029ec22a..c9d78af5 100644 --- a/src/commands/interface-manager/DeadDocumentMatrix.ts +++ b/src/commands/interface-manager/DeadDocumentMatrix.ts @@ -5,7 +5,7 @@ import { AbstractNode, DocumentNode, FringeWalker } from "./DeadDocument"; import { HTML_RENDERER } from "./DeadDocumentHtml"; -import { MARKDOWN_RENDERER, TransactionalOutputStream } from "./DeadDocumentMarkdown"; +import { MARKDOWN_RENDERER, PagedOutputStream } from "./DeadDocumentMarkdown"; function checkEqual(node1: AbstractNode|undefined, node2: AbstractNode|undefined): true { if (!Object.is(node1, node2)) { @@ -15,23 +15,37 @@ function checkEqual(node1: AbstractNode|undefined, node2: AbstractNode|undefined } export function renderMatrix(node: DocumentNode) { - const markdownOutput = new TransactionalOutputStream(); + const markdownOutput = new PagedOutputStream(); const markdownWalker = new FringeWalker( node, { output: markdownOutput }, MARKDOWN_RENDERER ); - const htmlOutput = new TransactionalOutputStream(); + const htmlOutput = new PagedOutputStream(); const htmlWalker = new FringeWalker( node, { output: htmlOutput }, HTML_RENDERER ); + const outputs = [htmlOutput, markdownOutput]; + // surely this shit should be internal to the transactional output stream + // and it should be the paged output stream? + // well we can't make it internal if both of the things are writing to it. + // what we can do is make + // Why the hell are we making the pages AOT?? + // There should only be one page inside the stream + // and it should have to be consumed before continuing + // otherwise we just stack up tonnes of garbage. + // What if we make a class that eats walkers. + // it creats streams and writes the pa-- + // wait a minute that's what this is ? let currentMarkdownNode = markdownWalker.increment(); let currentHtmlNode = htmlWalker.increment(); checkEqual(currentHtmlNode, currentMarkdownNode); - while (currentHtmlNode) { - htmlOutput.attemptCommit + while (currentHtmlNode !== undefined) { + if (outputs.some(o => o.isPageAndBufferOverSize())) { + outputs.forEach(o => o.forceNewPage(currentHtmlNode!)); + } } } \ No newline at end of file