generated from Arnesfield/template.ts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstringify.ts
112 lines (100 loc) · 3.29 KB
/
stringify.ts
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import * as PREFIX from '../utils/prefix.js';
import { PrefixOptions } from '../utils/prefix.js';
import { Node } from './core.types.js';
/** The stringify options. */
export interface StringifyOptions {
/**
* Include node arguments.
* @default true
*/
args?: boolean;
/** Include node ancestors. */
ancestors?: boolean;
/** Include node descendants. */
descendants?: boolean;
}
/**
* Create a tree structure string from the provided node object.
* @param node The node object.
* @param options The stringify options.
* @returns The tree string.
*/
export function stringify(node: Node, options: StringifyOptions = {}): string {
// set default show values
options = { ...options };
options.args ??= true;
const lines: string[] = [];
function draw(opts: PrefixOptions & { node: Node; children?: boolean }) {
const { node } = opts;
// 1 - the args label
// 2 - list of children
// 3 - the ancestors label
// 4 - the descendants label
const sub = {
index: -1,
prefix: PREFIX.child(opts),
length: opts.children
? +!!(options.args && node.args.length > 0) +
node.children.length +
+!!(options.ancestors && node.ancestors.length > 0) +
+!!(options.descendants && node.descendants.length > 0)
: 0
};
opts.next = sub.length > 0;
const id = node.id ?? node.raw;
const labels = [`depth: ${node.depth}`];
// only show if not the same as the displayed id
if (node.raw !== null && node.raw !== id) {
labels.push(`raw: ${node.raw}`);
}
if (node.alias !== null) {
labels.push(`alias: ${node.alias}`);
}
if (node.name !== null && node.name !== id) {
labels.push(`name: ${node.name}`);
}
lines.push(PREFIX.self(opts) + id + ` (${labels.join(', ')})`);
if (!opts.next) {
return;
}
// draw args
if (options.args && node.args.length > 0) {
// increment once only
const last = ++sub.index >= sub.length - 1;
const p = PREFIX.self({ last, next: true, prefix: sub.prefix });
lines.push(`${p}:args (total: ${node.args.length})`);
const prefix = PREFIX.child({ last, prefix: sub.prefix });
// no sub nodes for args
node.args.forEach((arg, index, array) => {
const p = PREFIX.self({ prefix, last: index >= array.length - 1 });
lines.push(p + arg);
});
}
// draw children
for (const subnode of node.children) {
// sub.length already accounts for node.children.length
draw({
node: subnode,
children: true,
prefix: sub.prefix,
last: ++sub.index >= sub.length - 1
});
}
// draw ancestors and descendants
for (const type of ['ancestors', 'descendants'] as const) {
if (!options[type] || node[type].length === 0) {
continue;
}
// increment once per type
const last = ++sub.index >= sub.length - 1;
const p = PREFIX.self({ last, next: true, prefix: sub.prefix });
lines.push(`${p}:${type} (total: ${node[type].length})`);
node[type].forEach((node, index, array) => {
const prefix = PREFIX.child({ last, prefix: sub.prefix });
draw({ node, prefix, last: index >= array.length - 1 });
});
}
}
draw({ node, first: true, children: true });
return lines.join('\n');
}