57 lines
1.7 KiB
TypeScript
57 lines
1.7 KiB
TypeScript
import type { Reader } from './readers/reader'
|
|
import { createLineData, parseLine } from './parser'
|
|
|
|
export type Document = {
|
|
next: (startLevel?: number) => Promise<boolean>
|
|
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<boolean> {
|
|
// 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
|
|
}
|
|
}
|