1use self::fold::Fold;
2use crate::{
3 tree::{CrateDoc, DocTree, Tag},
4 util::XString,
5};
6use ratatui::style::{Color, Style};
7use std::{
8 fmt::{self, Write},
9 rc::Rc,
10};
11use termtree::Tree;
12use unicode_width::UnicodeWidthStr;
13
14mod fold;
15
16#[derive(Clone, Default)]
18pub struct TextTag {
19 pub text: XString,
20 pub tag: Tag,
21 pub id: Option<XString>,
22}
23
24impl fmt::Display for TextTag {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 write!(f, "{}", self.text)
28 }
29}
30
31#[derive(Debug, Clone)]
33pub struct Text {
34 pub text: XString,
35 pub style: Style,
36}
37
38impl Text {
39 pub fn new(text: XString, style: Style) -> Self {
40 Self { text, style }
41 }
42
43 pub fn new_text(text: XString) -> Self {
44 Text {
45 text,
46 style: Style::default(),
47 }
48 }
49}
50
51#[derive(Clone)]
52pub struct TreeLine {
53 pub glyph: Text,
54 pub tag: Tag,
55 pub level: u8,
57 pub id: Option<XString>,
59 pub name: Text,
60}
61
62impl fmt::Debug for TreeLine {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 f.debug_struct("TreeLine")
65 .field("tag", &self.tag)
66 .field("level", &self.level)
67 .field("name.text", &self.name.text)
68 .finish()
69 }
70}
71
72impl TreeLine {
73 fn flatten(tree: DocTree) -> (Vec<TreeLine>, Tree<Empty>) {
74 let mut level = 0u8;
75 let mut emptytree = Tree::new(Empty).with_glyphs(tree.tree.root.tag.glyph());
76 let root = TreeLine::new(tree.tree.root, level);
77 let mut flatten = Vec::<TreeLine>::with_capacity(512);
78 flatten.push(root);
79 empty_tree_and_flatten(tree.tree.leaves, &mut level, &mut flatten, &mut emptytree);
80 (flatten, emptytree)
81 }
82
83 pub fn new(tt: TextTag, level: u8) -> Self {
84 let text = tt.text;
85 let tag = tt.tag;
86 let id = tt.id;
87 let style = tag.style();
88 let name = Text { text, style };
89
90 let glyph = Text {
92 text: Default::default(),
93 style: Default::default(),
94 };
95 Self {
96 glyph,
97 tag,
98 level,
99 id,
100 name,
101 }
102 }
103
104 fn set_glyph(&mut self, glyph: XString) {
105 self.glyph.text = glyph;
106 self.glyph.style = Style::default().fg(Color::Gray);
107 }
108
109 pub fn glyph_name(&self) -> [(&str, Style); 2] {
111 [
112 (&self.glyph.text, self.glyph.style),
113 (&self.name.text, self.name.style),
114 ]
115 }
116
117 pub fn width(&self) -> u16 {
123 let (g, n) = (&*self.glyph.text, &*self.name.text);
124 (g.width() + n.width())
125 .try_into()
126 .unwrap_or_else(|_| panic!("The total width exceeds u16::MAX in `{g}{n}`"))
127 }
128}
129
130pub struct TreeLines {
132 doc: CrateDoc,
133 lines: Rc<[TreeLine]>,
134 fold: Fold,
135}
136
137impl TreeLines {
138 pub fn new_with(doc: CrateDoc, init: impl FnOnce(&CrateDoc) -> DocTree) -> (Self, Tree<Empty>) {
140 let doctree = init(&doc);
141 let (lines, layout) = doctree.cache_lines();
142
143 (
144 TreeLines {
145 doc,
146 lines,
147 fold: Fold::default(),
148 },
149 layout,
150 )
151 }
152
153 pub fn try_new_with(
154 doc: &CrateDoc,
155 init: impl FnOnce(&CrateDoc) -> Option<DocTree>,
156 ) -> Option<Self> {
157 let doctree = init(doc)?;
158 let (lines, _) = doctree.cache_lines();
159
160 Some(TreeLines {
161 doc: doc.clone(),
162 lines,
163 fold: Fold::default(),
164 })
165 }
166
167 pub fn new(doc: CrateDoc) -> Self {
168 let mut lines = TreeLines {
171 doc,
172 ..Default::default()
173 };
174 lines.expand_zero_level();
176 info!("Outline TreeLines ready");
177 lines
178 }
179
180 pub fn all_lines(&self) -> &[TreeLine] {
181 &self.lines
182 }
183
184 pub fn display_as_plain_text(&self) -> String {
186 struct DisplayPlain<'s> {
187 glyph: &'s str,
188 name: &'s str,
189 }
190 impl fmt::Display for DisplayPlain<'_> {
191 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192 writeln!(f, "{}{}", self.glyph, self.name)
193 }
194 }
195 let mut buf = String::with_capacity(self.len() * 50);
196 for l in &*self.lines {
197 let plain = DisplayPlain {
198 glyph: &l.glyph.text,
199 name: &l.name.text,
200 };
201 write!(&mut buf, "{}", plain).unwrap();
202 }
203 buf.shrink_to_fit();
204 buf
205 }
206
207 pub fn doc(&self) -> CrateDoc {
208 self.doc.clone()
209 }
210
211 pub fn doc_ref(&self) -> &CrateDoc {
212 &self.doc
213 }
214}
215
216impl From<CrateDoc> for TreeLines {
217 fn from(doc: CrateDoc) -> Self {
218 TreeLines::new(doc)
219 }
220}
221
222impl DocTree {
223 fn cache_lines(self) -> (Rc<[TreeLine]>, Tree<Empty>) {
224 let (mut lines, layout) = TreeLine::flatten(self);
225 let tree_glyph = glyph(&layout);
226
227 let (len_nodes, len_glyph) = (lines.len(), tree_glyph.len());
228 assert_eq!(
229 len_nodes, len_glyph,
230 "the amount of nodes is {len_nodes}, but that of glyph is {len_glyph}"
231 );
232
233 lines
234 .iter_mut()
235 .zip(tree_glyph)
236 .for_each(|(l, g)| l.set_glyph(g));
237 (lines.into(), layout)
238 }
239}
240
241impl std::ops::Deref for TreeLines {
242 type Target = [TreeLine];
243
244 fn deref(&self) -> &Self::Target {
245 &self.lines
246 }
247}
248
249impl Default for TreeLines {
250 fn default() -> Self {
251 TreeLines {
252 doc: CrateDoc::default(),
253 lines: Rc::new([]),
254 fold: Fold::default(),
255 }
256 }
257}
258
259pub struct Empty;
262
263impl fmt::Display for Empty {
264 fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
265 Ok(())
266 }
267}
268
269fn empty_tree_and_flatten(
276 leaves: Vec<Tree<TextTag>>,
277 level: &mut u8,
278 flatten: &mut Vec<TreeLine>,
279 empty: &mut Tree<Empty>,
280) {
281 *level += 1;
282 for tree in leaves {
284 let mut current = *level;
285 let glyph = tree.root.tag.glyph();
286
287 flatten.push(TreeLine::new(tree.root, current));
289
290 let mut root = Tree::new(Empty).with_glyphs(glyph);
292 empty_tree_and_flatten(tree.leaves, &mut current, flatten, &mut root);
293 empty.push(root);
294 }
295}
296
297fn glyph(layout: &Tree<Empty>) -> Vec<XString> {
298 let mut buf = String::with_capacity(1024);
299 write!(&mut buf, "{layout}").expect("can't write Tree<Empty> to the string buf");
300 buf.lines().map(XString::from).collect()
301}