import type { Reader } from './readers/reader' import { createLineData, parseLine } from './parser' export type Document = { next: (startLevel?: number) => Promise level: () => number, line: (startOffset?: number) => string, head: () => string, tail: () => string, match: (matchHead: string) => boolean } export function useDocument (reader: Reader, indent: string = ' '): Document { const lineData = createLineData('', indent) let repeat = false async function next(startLevel: number = -1): Promise { // Repeat the current line instead of parsing a new one if the previous call to next() // determined the current line to be out of its scope. if (repeat) repeat = false // Otherwise parse the line normally. else { const line = await reader() // If there are no more lines, bail out. if (line == null) return false lineData.line = line parseLine(lineData) } // If we shouldn't be handling this line, make the next call to next() repeat the current line. // Allows a child loop to look forward, determine that the next line will be outside its purview, // and return control to the calling loop transparently without additional logic. if (level() <= startLevel) { repeat = true return false } return true } const level = () => lineData.level const line = (startOffset: number = lineData.offsetHead) => lineData.line.slice(startOffset) const head = () => lineData.line.slice(lineData.offsetHead, lineData.offsetTail) const tail = () => lineData.line.slice(lineData.offsetTail + 1) // Skip the space const match = (matchHead: string): boolean => matchHead === head() return { next, level, line, head, tail, match } }