Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | 1x 1x 1x 1x 1x 102x 102x 102x 102x 102x 102x 102x 257x 102x 102x 1x 1x 1x 1x 3x 102x 1x 1x 83x 83x 22x 22x 22x 22x 22x 22x 22x 22x 1x 145x 145x 145x 145x 145x 145x 44x 44x 145x 141x 62x 62x 62x 79x 44x 44x 1x 167x | import {annotateDepthUnfold, preOrderFold} from '#ops'
import {fixTree, treeAna, treeHylo, type Tree} from '#tree'
import * as TreeF from '#treeF'
import {Array, pipe, String} from '#util'
const _encode = (self: Tree<string>, indent = 2): Array.NonEmptyArray<string> =>
pipe(
[self, 0],
treeHylo(
annotateDepthUnfold<string>,
preOrderFold<readonly [string, number]>,
),
Array.map(([a, depth]) =>
pipe((depth - 1) * indent, String.nSpaces, String.suffix(a)),
),
)
/**
* Encode a string tree into a `YAML`-like indented format where indentation, by
* default set at `2` spaces, indicates node depth.
*
* You will find a curried version under the key `curried`.
* @param self The tree to be encoded.
* @param indent Optional number of space characters that separate adjacent tree levels. Default is `2`.
* @category codec
* @function
*/
export const encode: {
(self: Tree<string>, indent?: number): Array.NonEmptyArray<string>
curried: (
indent?: number,
) => (self: Tree<string>) => Array.NonEmptyArray<string>
} = Object.assign(_encode, {
curried:
(indent?: number) =>
(self: Tree<string>): Array.NonEmptyArray<string> =>
_encode(self, indent),
})
/**
* Decode a list of indented lines into a string tree.
* @param lines Non-empty array of non-empty strings, each encoding a tree
* node with indent set as a multiple of node depth.
* @returns A decoded string tree.
* @category codec
* @function
*/
export const decode = (lines: Array.NonEmptyArray<string>): Tree<string> => {
const [first, second] = lines
if (second === undefined) return fixTree(TreeF.leafF(first))
const indentSize = countStartingSpaces(second),
computeDepth = (line: string) => countStartingSpaces(line) / indentSize,
mapped: Array.NonEmptyArray<[string, number]> = pipe(
lines,
Array.map(line => [line.trimStart(), computeDepth(line)] as const),
)
return pipe(mapped, treeAna(decodeIndentedUnfold))
}
/**
* Decode a single level of a tree from a list of indented lines where indentation
* represents node depth.
* @category codec
* @category unfold
* @function
*/
export const decodeIndentedUnfold = ([
[value, depth],
second,
...tail
]: Array.NonEmptyArray<[string, number]>): TreeF.TreeF<
string,
Array.NonEmptyArray<[string, number]>
> => {
if (second === undefined) return TreeF.leafF(value)
const nodes: Array.NonEmptyArray2<[string, number]> = [[second]]
for (const [lineValue, lineDepth] of tail)
if (lineDepth > depth + 1)
(nodes.at(-1) as Array.NonEmptyArray<[string, number]>).push([
lineValue,
lineDepth,
])
else nodes.push([[lineValue, lineDepth]])
return TreeF.branchF(value, nodes)
}
const countStartingSpaces = (s: string): number =>
pipe(s, String.takeWhile(String.isEqual(' ')), String.length)
|