Terrace/packages/js/src/document.ts
2023-02-07 13:33:23 -05:00

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
}
}