-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrender-tree.ts
145 lines (141 loc) · 5.05 KB
/
render-tree.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { dim } from 'ansis';
import { TraversalContext } from './traversal.type';
export interface TreeConfig {
style: (text: string) => string;
tree: {
end: string;
middle: string;
line: string;
indentSpace: string;
};
}
export type RenderTree = {
renderTreeIndent(context: TraversalContext): string;
updateActiveBranchLevels(context: TraversalContext): void;
};
export function createTreeRenderer(
config: Partial<TreeConfig> = {
style: dim,
tree: {
end: '└── ',
middle: '├── ',
line: '│ ',
indentSpace: ' ', // Space when last item
},
}
): RenderTree {
const activeParentLevels = new Set<number>();
return {
/**
* Computes the **tree indentation** string based on the traversal context.
*
* This function ensures that **vertical tree lines (`│`)** are drawn correctly,
* maintaining **proper indentation** for hierarchical structures.
*
* ---
*
* ### 🛠 **How It Works**
* - **Loops through all levels** (`context.level`) and:
* - If the level is in `activeParentLevels`, it **adds a vertical tree line (`│`)**.
* - Otherwise, it **adds empty spaces (` `) for alignment**.
* - **Appends the correct tree branch marker (`├──` or `└──`)**:
* - If the node is **not the last child** (`isLast === false`), it uses `tree.middle` (`├──`).
* - If the node **is the last child** (`isLast === true`), it uses `tree.end` (`└──`).
*
* ---
*
* ### 📌 **Example: How It Affects Indentation**
*
* **Before Processing a Last Node in a Level:**
* ```
* ├── Department A
* │ ├── Employee 1
* │ ├── Employee 2
* │ └── Employee 3 <-- getTreeIndent() adds `└──` here
* ```
*
* **Before Processing a Non-Last Node in a Level:**
* ```
* ├── Department A
* │ ├── Employee 1 <-- getTreeIndent() adds `├──` here
* │ ├── Employee 2
* │ └── Employee 3
* ```
*
* ---
*
* ### 🎯 **Why This Matters**
* - Ensures **correct visual alignment** for hierarchical trees.
* - Prevents **extra or missing** tree lines (`│`) at nested levels.
* - Maintains **professional and structured output**.
*
* ---
* @param context - The current traversal context containing:
* - `level` (number): The **depth level** of the current node in the tree.
* - `isLast` (boolean): Whether this node is the **last child** at its level.
* @returns The computed **tree indentation string**.
*/
renderTreeIndent(context: TraversalContext): string {
return Array.from({ length: context.level })
.map((_, i) =>
activeParentLevels.has(i) ? config.style(config.tree.line) : ' '
)
.join('')
.concat(
config.style(context.last ? config.tree.end : config.tree.middle)
);
},
/**
* Updates the set of active parent levels based on the traversal context.
*
* This method ensures that the **tree structure maintains correct indentation** by
* tracking which parent levels should **continue their vertical lines (`│`)**.
*
* ---
*
* ### How It Works
* - If the **current node is NOT the last child** (`isLast === false`), it means this level
* **has more children** to process. So, we **add** the current `level` to `activeParentLevels`,
* ensuring the tree **continues drawing vertical lines (`│`) at this depth**.
* - If the **current node IS the last child** (`isLast === true`), we **remove** the
* current `level` from `activeParentLevels`, stopping further indentation lines
* at this depth.
*
* This function is called **after processing** a department or employee to update
* indentation behavior for all subsequent sibling nodes.
*
* ---
*
* ### 📌 **Example: How It Affects Indentation**
*
* **Before Processing a Last Node in a Level:**
* ```
* ├── Department A
* │ ├── Employee 1
* │ ├── Employee 2
* │ └── Employee 3 <-- updateActiveParentLevels removes level here
* ```
*
* **Before Processing a Non-Last Node in a Level:**
* ```
* ├── Department A
* │ ├── Employee 1 <-- updateActiveParentLevels adds level here
* │ ├── Employee 2
* │ └── Employee 3
* ```
*
* ---
*
* @param context - The current traversal context containing:
* - `level` (number): The **depth level** of the current node in the tree.
* - `isLast` (boolean): Whether this node is the **last child** at its level.
*/
updateActiveBranchLevels(context: TraversalContext) {
if (!context.last) {
activeParentLevels.add(context.level); // Continue the branch
} else {
activeParentLevels.delete(context.level); // Stop indentation at this level
}
},
};
}