use self::fold::Fold;
use crate::{
tree::{CrateDoc, DocTree, Tag},
util::XString,
};
use ratatui::style::{Color, Style};
use std::{
fmt::{self, Write},
rc::Rc,
};
use termtree::Tree;
use unicode_width::UnicodeWidthStr;
mod fold;
#[derive(Clone)]
pub struct TextTag {
pub text: XString,
pub tag: Tag,
pub id: Option<XString>,
}
impl fmt::Display for TextTag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.text)
}
}
#[derive(Debug, Clone)]
pub struct Text {
pub text: XString,
pub style: Style,
}
impl Text {
pub fn new(text: XString, style: Style) -> Self {
Self { text, style }
}
pub fn new_text(text: XString) -> Self {
Text {
text,
style: Style::default(),
}
}
}
#[derive(Clone)]
pub struct TreeLine {
pub glyph: Text,
pub tag: Tag,
pub level: u8,
pub id: Option<XString>,
pub name: Text,
}
impl fmt::Debug for TreeLine {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TreeLine")
.field("tag", &self.tag)
.field("level", &self.level)
.field("name.text", &self.name.text)
.finish()
}
}
impl TreeLine {
fn flatten(tree: DocTree) -> (Vec<TreeLine>, Tree<Empty>) {
let mut level = 0u8;
let mut emptytree = Tree::new(Empty).with_glyphs(tree.tree.root.tag.glyph());
let root = TreeLine::new(tree.tree.root, level);
let mut flatten = Vec::<TreeLine>::with_capacity(512);
flatten.push(root);
empty_tree_and_flatten(tree.tree.leaves, &mut level, &mut flatten, &mut emptytree);
(flatten, emptytree)
}
pub fn new(tt: TextTag, level: u8) -> Self {
let text = tt.text;
let tag = tt.tag;
let id = tt.id;
let style = tag.style();
let name = Text { text, style };
let glyph = Text {
text: Default::default(),
style: Default::default(),
};
Self {
glyph,
tag,
level,
id,
name,
}
}
fn set_glyph(&mut self, glyph: XString) {
self.glyph.text = glyph;
self.glyph.style = Style::default().fg(Color::Gray);
}
pub fn glyph_name(&self) -> [(&str, Style); 2] {
[
(&self.glyph.text, self.glyph.style),
(&self.name.text, self.name.style),
]
}
pub fn width(&self) -> u16 {
let (g, n) = (&*self.glyph.text, &*self.name.text);
(g.width() + n.width())
.try_into()
.unwrap_or_else(|_| panic!("The total width exceeds u16::MAX in `{g}{n}`"))
}
}
pub struct TreeLines {
doc: CrateDoc,
lines: Rc<[TreeLine]>,
fold: Fold,
}
impl TreeLines {
pub fn new_with(doc: CrateDoc, init: impl FnOnce(&CrateDoc) -> DocTree) -> (Self, Tree<Empty>) {
let doctree = init(&doc);
let (lines, layout) = doctree.cache_lines();
(
TreeLines {
doc,
lines,
fold: Fold::default(),
},
layout,
)
}
pub fn new(doc: CrateDoc) -> Self {
let mut lines = TreeLines {
doc,
..Default::default()
};
lines.expand_zero_level();
info!("Outline TreeLines ready");
lines
}
pub fn all_lines(&self) -> &[TreeLine] {
&self.lines
}
pub fn display_as_plain_text(&self) -> String {
struct DisplayPlain<'s> {
glyph: &'s str,
name: &'s str,
}
impl fmt::Display for DisplayPlain<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "{}{}", self.glyph, self.name)
}
}
let mut buf = String::with_capacity(self.len() * 50);
for l in &*self.lines {
let plain = DisplayPlain {
glyph: &l.glyph.text,
name: &l.name.text,
};
write!(&mut buf, "{}", plain).unwrap();
}
buf.shrink_to_fit();
buf
}
pub fn doc(&self) -> CrateDoc {
self.doc.clone()
}
}
impl From<CrateDoc> for TreeLines {
fn from(doc: CrateDoc) -> Self {
TreeLines::new(doc)
}
}
impl DocTree {
fn cache_lines(self) -> (Rc<[TreeLine]>, Tree<Empty>) {
let (mut lines, layout) = TreeLine::flatten(self);
let tree_glyph = glyph(&layout);
let (len_nodes, len_glyph) = (lines.len(), tree_glyph.len());
assert_eq!(
len_nodes, len_glyph,
"the amount of nodes is {len_nodes}, but that of glyph is {len_glyph}"
);
lines
.iter_mut()
.zip(tree_glyph)
.for_each(|(l, g)| l.set_glyph(g));
(lines.into(), layout)
}
}
impl std::ops::Deref for TreeLines {
type Target = [TreeLine];
fn deref(&self) -> &Self::Target {
&self.lines
}
}
impl Default for TreeLines {
fn default() -> Self {
TreeLines {
doc: CrateDoc::default(),
lines: Rc::new([]),
fold: Fold::default(),
}
}
}
pub struct Empty;
impl fmt::Display for Empty {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
Ok(())
}
}
fn empty_tree_and_flatten(
leaves: Vec<Tree<TextTag>>,
level: &mut u8,
flatten: &mut Vec<TreeLine>,
empty: &mut Tree<Empty>,
) {
*level += 1;
for tree in leaves {
let mut current = *level;
let glyph = tree.root.tag.glyph();
flatten.push(TreeLine::new(tree.root, current));
let mut root = Tree::new(Empty).with_glyphs(glyph);
empty_tree_and_flatten(tree.leaves, &mut current, flatten, &mut root);
empty.push(root);
}
}
fn glyph(layout: &Tree<Empty>) -> Vec<XString> {
let mut buf = String::with_capacity(1024);
write!(&mut buf, "{layout}").expect("can't write Tree<Empty> to the string buf");
buf.lines().map(XString::from).collect()
}