use std::cell::RefCell;
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::sync::Arc;
use crate::config;
use crate::model::join::BeadKey;
use crate::model::snapshot::{Counts, Node as Bead, Snapshot, Tree};
use crate::model::tree::Link;
use crate::model::types::Edge;
use crate::view::draw::identity_widths;
use crate::view::lines::{
links_below, marker, notes_of, prefix, root_key, run_size, way_below, BeadFacts, Content,
Group, GroupKind, Item, Line, Note, Place, ProjectLine, Unread, INDENT,
};
use crate::view::row::{self, Widths};
use super::drawn::{Beneath, Count, Counted, Drawn, Ground, Node, Undrawn};
use super::facts::{Facts, TreeFacts, Uniform};
use super::handle::{item_key, Folds, Handle, ItemKey, Scope};
use super::spine::{Chosen, Stand};
enum Child<'a> {
Note(Note),
Node(&'a Link),
Elided(Vec<&'a Link>),
}
fn shut_over(beneath: Counts, folded: Option<bool>) -> Option<Counts> {
(folded == Some(false)).then_some(beneath)
}
pub(super) fn lay_out(
snapshot: &Snapshot,
facts: &Arc<Facts>,
folds: &Folds,
rooted: Option<&Rooted>,
row: &row::Layout,
) -> Drawn {
Layout::new(snapshot, facts, folds, rooted, false, &[], row).draw()
}
#[cfg(test)]
pub(super) fn draw(
snapshot: &Snapshot,
facts: &Arc<Facts>,
folds: &Folds,
rooted: Option<&Rooted>,
) -> Vec<Line> {
lay_out(snapshot, facts, folds, rooted, &row::Layout::default())
.iter()
.cloned()
.collect()
}
pub(super) fn draw_beneath_every_fold(
snapshot: &Snapshot,
facts: &Arc<Facts>,
folds: &Folds,
rooted: Option<&Rooted>,
also: &[Handle],
row: &row::Layout,
) -> Drawn {
Layout::new(snapshot, facts, folds, rooted, true, also, row).draw()
}
#[cfg(test)]
thread_local! {
static DRAWS: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
}
#[cfg(test)]
pub(super) fn draws_so_far() -> usize {
DRAWS.with(|draws| draws.get())
}
pub(super) struct Rooted {
pub(super) place: Place,
pub(super) way: Vec<usize>,
}
pub(super) fn group_drawn(
snapshot: &Snapshot,
kind: GroupKind,
project: Option<&str>,
rooted: Option<&Rooted>,
) -> bool {
group_of(snapshot, kind, project, rooted).is_some()
}
pub(super) fn project_drawn(snapshot: &Snapshot, project: &str) -> bool {
snapshot.trees.iter().any(|tree| tree.project == project)
|| snapshot
.hidden_trees
.iter()
.any(|hidden| hidden.project == project)
|| snapshot
.unattributed
.iter()
.any(|pane| pane.project == project)
|| !snapshot.read_at.contains_key(project)
}
fn hidden_trees<'a>(snapshot: &'a Snapshot, project: Option<&str>) -> Vec<&'a Arc<Tree>> {
snapshot
.hidden_trees
.iter()
.filter(|hidden| Some(hidden.project.as_str()) == project)
.filter_map(|hidden| {
snapshot.shared_tree(&BeadKey {
project: hidden.project.clone(),
id: hidden.root.clone(),
})
})
.collect()
}
pub(super) fn first_bead_of(
snapshot: &Snapshot,
kind: GroupKind,
project: Option<&str>,
rooted: Option<&Rooted>,
) -> Option<BeadKey> {
let roots: Vec<&Arc<Tree>> = match kind {
GroupKind::HiddenTrees => hidden_trees(snapshot, project),
GroupKind::OutOfTheWay => out_of_the_way(snapshot, project, rooted)
.into_iter()
.map(|root| root.tree)
.collect(),
_ => return None,
};
roots.into_iter().find_map(|tree| {
Some(BeadKey {
project: tree.project.clone(),
id: tree.beads.first()?.id.clone(),
})
})
}
pub(super) fn walked<'a>(
snapshot: &'a Snapshot,
rooted: Option<&Rooted>,
) -> Vec<(&'a Tree, Vec<usize>)> {
let mut drawn = Vec::new();
for project in &snapshot.projects {
if !project_drawn(snapshot, project) {
continue;
}
let Some(rooted) = rooted else {
drawn.extend(
snapshot
.trees
.iter()
.filter(|tree| tree.project == *project)
.map(|tree| (Arc::as_ref(tree), vec![0])),
);
drawn.extend(
hidden_trees(snapshot, Some(project))
.into_iter()
.map(|tree| (Arc::as_ref(tree), vec![0])),
);
continue;
};
if rooted.place.tree.project == *project {
drawn.extend(
snapshot
.tree(&rooted.place.tree)
.map(|tree| (tree, rooted.way.clone())),
);
}
drawn.extend(
out_of_the_way(snapshot, Some(project), Some(rooted))
.into_iter()
.map(|root| (Arc::as_ref(root.tree), vec![0])),
);
}
drawn
}
fn group_of(
snapshot: &Snapshot,
kind: GroupKind,
project: Option<&str>,
rooted: Option<&Rooted>,
) -> Option<Group> {
let (count, with_findings, held) = match kind {
GroupKind::HiddenTrees => {
let hidden = snapshot
.hidden_trees
.iter()
.filter(|hidden| Some(hidden.project.as_str()) == project);
(
hidden.clone().count(),
hidden.filter(|hidden| hidden.findings).count(),
None,
)
}
GroupKind::OutOfTheWay => {
let held = out_of_the_way(snapshot, project, rooted);
let counts = Counts::over(held.iter().flat_map(Behind::beads));
(held.len(), 0, Some(counts))
}
_ => (group_items(snapshot, kind, project).len(), 0, None),
};
(count > 0).then_some(Group {
kind,
project: project.map(str::to_string),
count,
with_findings,
held,
})
}
struct Behind<'a> {
tree: &'a Arc<Tree>,
without: Option<usize>,
}
impl<'a> Behind<'a> {
fn beads(&self) -> Vec<&'a Bead> {
reached(self.tree, [0], self.without)
.into_iter()
.filter_map(|at| self.tree.beads.get(at))
.collect()
}
}
fn reached(
tree: &Tree,
from: impl IntoIterator<Item = usize>,
without: Option<usize>,
) -> BTreeSet<usize> {
let mut walked = BTreeSet::new();
let mut left: Vec<usize> = from.into_iter().collect();
while let Some(at) = left.pop() {
if Some(at) == without || !walked.insert(at) {
continue;
}
left.extend(
tree.children
.get(at)
.into_iter()
.flatten()
.map(|link| link.bead),
);
}
walked
}
fn out_of_the_way<'a>(
snapshot: &'a Snapshot,
project: Option<&str>,
rooted: Option<&Rooted>,
) -> Vec<Behind<'a>> {
let Some(rooted) = rooted else {
return Vec::new();
};
let (focused, above) = rooted.way.split_last().expect("a way down ends somewhere");
snapshot
.collected
.iter()
.filter(|tree| Some(tree.project.as_str()) == project)
.filter_map(|tree| {
if root_key(tree) != rooted.place.tree {
return Some(Behind {
tree,
without: None,
});
}
(!above.is_empty()).then_some(Behind {
tree,
without: Some(*focused),
})
})
.collect()
}
pub(super) fn every_group(
snapshot: &Snapshot,
) -> impl Iterator<Item = (GroupKind, Option<String>)> + '_ {
snapshot
.projects
.iter()
.flat_map(|project| {
GroupKind::UNDER_A_PROJECT
.into_iter()
.map(move |kind| (kind, Some(project.clone())))
})
.chain(
GroupKind::BELOW_THE_TREES
.into_iter()
.map(|kind| (kind, None)),
)
}
pub(super) fn group_holding(snapshot: &Snapshot, key: &ItemKey) -> Option<Handle> {
every_group(snapshot)
.find(|(kind, project)| {
group_items(snapshot, *kind, project.as_deref())
.iter()
.any(|item| item_key(item).as_ref() == Some(key))
})
.map(|(kind, project)| Handle::Group(kind, project))
}
#[derive(Default)]
struct Named(BTreeMap<BeadKey, Mentioned>);
#[derive(Default)]
struct Mentioned {
under: HashMap<String, Mentioned>,
}
impl Named {
fn of<'a>(handles: impl IntoIterator<Item = &'a Handle>) -> Self {
let mut named = Named::default();
for handle in handles {
let (Handle::Bead(place) | Handle::Elided(place)) = handle else {
continue;
};
let mut at = named.0.entry(place.tree.clone()).or_default();
for step in &place.steps {
at = at.under.entry(step.id.clone()).or_default();
}
}
named
}
fn tree(&self, root: &BeadKey) -> Option<&Mentioned> {
self.0.get(root)
}
}
impl Mentioned {
fn under(&self, id: &str) -> Option<&Mentioned> {
self.under.get(id)
}
}
#[derive(Default)]
struct Kept {
trees: Vec<Arc<Tree>>,
beads: HashMap<Counted, Count>,
runs: HashMap<Counted, Count>,
}
struct Layout<'a> {
snapshot: &'a Snapshot,
facts: &'a Arc<Facts>,
folds: &'a Folds,
rooted: Option<&'a Rooted>,
beneath_shut: bool,
row: &'a row::Layout,
named: Named,
kept: RefCell<Kept>,
}
impl<'a> Layout<'a> {
#[allow(clippy::too_many_arguments)]
fn new(
snapshot: &'a Snapshot,
facts: &'a Arc<Facts>,
folds: &'a Folds,
rooted: Option<&'a Rooted>,
beneath_shut: bool,
also: &[Handle],
row: &'a row::Layout,
) -> Self {
Layout {
snapshot,
facts,
folds,
rooted,
beneath_shut,
row,
named: Named::of(folds.mentioned().chain(facts.beginnings()).chain(also)),
kept: RefCell::default(),
}
}
fn draw(self) -> Drawn {
#[cfg(test)]
DRAWS.with(|draws| draws.set(draws.get() + 1));
let mut lines = Vec::new();
for project in &self.snapshot.projects {
if project_drawn(self.snapshot, project) {
lines.push(self.draw_project(project));
}
}
self.draw_groups(&mut lines);
if lines.is_empty() {
lines.push(Node::drawn(nothing_to_draw(), Vec::new()));
}
self.say_what_the_directory_chose(&mut lines);
let kept = self.kept.into_inner();
Drawn::over(
lines,
Some(Ground {
trees: kept.trees,
facts: Arc::clone(self.facts),
beads: kept.beads,
runs: kept.runs,
beneath_shut: self.beneath_shut,
}),
self.row,
)
}
fn tree_index(&self, tree: &Arc<Tree>) -> usize {
let mut kept = self.kept.borrow_mut();
match kept.trees.iter().position(|held| Arc::ptr_eq(held, tree)) {
Some(index) => index,
None => {
kept.trees.push(Arc::clone(tree));
kept.trees.len() - 1
}
}
}
fn tree_layout(
&'a self,
tree: &'a Arc<Tree>,
over: Option<&'a Scope>,
rests_shut: bool,
rooted: Option<&'a Rooted>,
without: Option<usize>,
) -> TreeLayout<'a> {
let root = root_key(tree);
let facts = self.facts.tree(&root);
TreeLayout {
layout: self,
over,
index: self.tree_index(tree),
tree,
facts,
answers: self.facts.uniform(&root),
named: self.named.tree(&root),
rests_shut,
rooted,
without,
}
}
fn draw_project(&'a self, project: &str) -> Node {
let project = project.to_string();
let handle = Handle::Project(project.clone());
let pointed = self.folds.pointed(&handle, None);
let open = pointed.unwrap_or(true);
let over = self.folds.beneath(&handle, None);
let line = Line {
prefix: marker(open).to_string(),
depth: 0,
folded: Some(open),
place: None,
content: Content::Project(ProjectLine {
every_root_read: self.snapshot.every_root_read(&project),
counts: self.facts.project(&project),
project: project.clone(),
}),
};
if !open && !self.beneath_shut {
return Node::drawn(line, Vec::new());
}
let trees: Vec<&Arc<Tree>> = match self.rooted {
Some(rooted) if rooted.place.tree.project == project => self
.snapshot
.shared_tree(&rooted.place.tree)
.into_iter()
.collect(),
Some(_) => Vec::new(),
None => self
.snapshot
.trees
.iter()
.filter(|tree| tree.project == project)
.collect(),
};
let groups: Vec<Group> = GroupKind::UNDER_A_PROJECT
.into_iter()
.filter(|kind| self.rooted.is_none() || *kind != GroupKind::HiddenTrees)
.filter_map(|kind| group_of(self.snapshot, kind, Some(&project), self.rooted))
.collect();
let mut entries = trees.len() + groups.len();
let mut trunk = Vec::new();
let mut children = Vec::new();
for tree in trees {
entries -= 1;
children.push(
self.tree_layout(tree, over, false, self.rooted, None)
.draw(&mut trunk, entries == 0),
);
}
for group in groups {
entries -= 1;
children.push(self.draw_group(group, over, &mut trunk, entries == 0));
}
Node::drawn(line, children)
}
fn draw_group(
&'a self,
group: Group,
over: Option<&'a Scope>,
trunk: &mut Vec<bool>,
last: bool,
) -> Node {
let handle = Handle::Group(group.kind, group.project.clone());
let pointed = self.folds.pointed(&handle, over);
let open = pointed.unwrap_or(group.kind.live());
let over = self.folds.beneath(&handle, over);
let prefix = if trunk.is_empty() && group.project.is_none() {
marker(open).to_string()
} else {
prefix(trunk, last, !open, None)
};
let depth = trunk.len() as u16 + u16::from(group.project.is_some());
let kind = group.kind;
let project = group.project.clone();
let line = Line {
prefix,
depth,
folded: Some(open),
place: None,
content: Content::Group(group),
};
if !open && !self.beneath_shut {
return Node::drawn(line, Vec::new());
}
if project.is_some() {
trunk.push(!last);
}
let children = match kind {
GroupKind::HiddenTrees | GroupKind::OutOfTheWay => {
let roots = self.roots_in(kind, project.as_deref());
let count = roots.len();
roots
.into_iter()
.enumerate()
.map(|(n, root)| {
self.tree_layout(root.tree, over, true, None, root.without)
.draw(trunk, n + 1 == count)
})
.collect()
}
_ => self.draw_items(group_items(self.snapshot, kind, project.as_deref()), trunk),
};
if project.is_some() {
trunk.pop();
}
Node::drawn(line, children)
}
fn roots_in(&self, kind: GroupKind, project: Option<&str>) -> Vec<Behind<'a>> {
match kind {
GroupKind::OutOfTheWay => out_of_the_way(self.snapshot, project, self.rooted),
_ => hidden_trees(self.snapshot, project)
.into_iter()
.map(|tree| Behind {
tree,
without: None,
})
.collect(),
}
}
fn draw_items(&self, items: Vec<Item>, trunk: &[bool]) -> Vec<Node> {
let count = items.len();
let depth = trunk.len() as u16 + 1;
items
.into_iter()
.enumerate()
.map(|(n, item)| {
Node::drawn(
Line {
prefix: prefix(trunk, n + 1 == count, false, None),
depth,
folded: None,
place: None,
content: Content::Item(item),
},
Vec::new(),
)
})
.collect()
}
fn draw_groups(&'a self, lines: &mut Vec<Node>) {
for kind in GroupKind::BELOW_THE_TREES {
let Some(group) = group_of(self.snapshot, kind, None, self.rooted) else {
continue;
};
lines.push(self.draw_group(group, None, &mut Vec::new(), true));
}
}
fn count(&self, tree: &Tree, answers: Uniform, counted: Counted) -> Count {
let mut kept = self.kept.borrow_mut();
count(
&mut kept,
tree,
answers,
self.beneath_shut,
self.row,
counted,
)
}
}
struct TreeLayout<'a> {
layout: &'a Layout<'a>,
over: Option<&'a Scope>,
index: usize,
tree: &'a Arc<Tree>,
facts: &'a TreeFacts,
answers: Option<Uniform<'a>>,
named: Option<&'a Mentioned>,
rests_shut: bool,
rooted: Option<&'a Rooted>,
without: Option<usize>,
}
impl<'a> TreeLayout<'a> {
fn draw(&self, trunk: &mut Vec<bool>, last: bool) -> Node {
let folds = self.layout.folds;
let (root, way, (over, stand), named) = match self.rooted {
Some(rooted) => (
rooted.place.clone(),
rooted.way.clone(),
self.over_rooted(rooted),
self.named_under(&rooted.place),
),
None => {
let root = Place::root(root_key(self.tree));
let stand = self.begins_at(&root);
(root, vec![0], (self.over, stand), self.named)
}
};
let (at, above) = way.split_last().expect("a way down ends somewhere");
let at = *at;
let depth = trunk.len() as u16 + 1;
let Some(node) = self.tree.beads.get(at) else {
return Node::drawn(
Line {
prefix: prefix(trunk, last, false, None),
depth,
folded: None,
place: Some(root),
content: Content::Unread(Unread {
root: self.tree.root.clone(),
tracker: self.tree.tracker.clone(),
}),
},
Vec::new(),
);
};
let kids = self.children_entries(at, above);
let bead = self.facts.bead(self.tree, at, above);
let handle = Handle::Bead(root.clone());
let pointed = folds.pointed(&handle, over);
let open = !kids.is_empty() && pointed.unwrap_or(!self.rests_shut && bead.opens_a_fold);
let below = folds.beneath(&handle, over);
let folded = (!kids.is_empty()).then_some(open);
let line = bead_line(
node,
None,
root.clone(),
trunk,
last,
depth,
None,
folded,
&bead,
);
let mut entries: Vec<Child> = notes_of(self.tree).into_iter().map(Child::Note).collect();
if open || self.layout.beneath_shut {
entries.extend(kids);
}
trunk.push(!last);
let children = self.draw_children(entries, &root, &way, below, named, stand, trunk);
trunk.pop();
Node::drawn(line, children)
}
fn named_under(&self, place: &Place) -> Option<&'a Mentioned> {
place
.steps
.iter()
.fold(self.named, |named, step| named?.under(&step.id))
}
fn over_rooted(&self, rooted: &Rooted) -> (Option<&'a Scope>, Stand) {
let folds: &'a Folds = self.layout.folds;
let mut over = self.over;
let mut place = Place::root(root_key(self.tree));
let mut stand = self.begins_at(&place);
for step in rooted.way.windows(2) {
over = folds.beneath(&Handle::Bead(place.clone()), over);
let link = self.tree.children[step[0]]
.iter()
.find(|link| link.bead == step[1])
.expect("a way down follows the tree's own links");
place = place.step_to(self.key_of(link));
stand = self.stand_at(&place, stand, step[0], link);
}
(over, stand)
}
fn begins_at(&self, root: &Place) -> Stand {
self.begun(root).unwrap_or_else(Stand::over_nothing)
}
fn stand_at(&self, place: &Place, stand: Stand, from: usize, link: &Link) -> Stand {
self.begun(place)
.unwrap_or_else(|| stand.beneath(from, link, self.chosen()))
}
fn begun(&self, place: &Place) -> Option<Stand> {
self.layout.facts.begun(&Handle::Bead(place.clone()))
}
fn chosen(&self) -> &'a [Chosen] {
self.layout.facts.chosen()
}
fn key_of(&self, link: &Link) -> BeadKey {
BeadKey {
project: self.tree.project.clone(),
id: self.tree.beads[link.bead].id.clone(),
}
}
#[allow(clippy::too_many_arguments)]
fn draw_children(
&self,
entries: Vec<Child>,
parent: &Place,
above: &[usize],
over: Option<&'a Scope>,
named: Option<&'a Mentioned>,
stand: Stand,
trunk: &mut Vec<bool>,
) -> Vec<Node> {
let folds = self.layout.folds;
let count = entries.len();
let depth = trunk.len() as u16 + 1;
let mut drawn = Vec::with_capacity(count);
for (n, entry) in entries.into_iter().enumerate() {
let last = n + 1 == count;
match entry {
Child::Note(note) => drawn.push(Node::drawn(
Line {
prefix: prefix(trunk, last, false, None),
depth,
folded: None,
place: None,
content: Content::Note(note),
},
Vec::new(),
)),
Child::Elided(members) => {
let handle = Handle::Elided(parent.clone());
let pointed = folds.pointed(&handle, over);
let open = pointed.unwrap_or(false);
let below = folds.beneath(&handle, over);
let line = run_line(
parent,
self.run_size(&members, above),
trunk,
last,
depth,
open,
);
let mut children = Vec::new();
if open || self.layout.beneath_shut {
trunk.push(!last);
let entries = members.into_iter().map(Child::Node).collect();
children =
self.draw_children(entries, parent, above, below, named, stand, trunk);
trunk.pop();
}
drawn.push(Node::drawn(line, children));
}
Child::Node(link) => {
let node = &self.tree.beads[link.bead];
let named = named.and_then(|named| named.under(&node.id));
let from = *above.last().expect("a way down ends on the parent");
drawn.push(match (named, self.answers) {
(None, Some(answers)) => self.undrawn(
answers,
link,
parent,
over,
stand.beneath(from, link, self.chosen()),
trunk,
last,
depth,
),
(named, _) => {
self.draw_child(link, parent, above, over, named, stand, trunk, last)
}
});
}
}
}
drawn
}
#[allow(clippy::too_many_arguments)]
fn draw_child(
&self,
link: &Link,
parent: &Place,
above: &[usize],
over: Option<&'a Scope>,
named: Option<&'a Mentioned>,
stand: Stand,
trunk: &mut Vec<bool>,
last: bool,
) -> Node {
let folds = self.layout.folds;
let at = link.bead;
let node = &self.tree.beads[at];
let place = parent.step_to(self.key_of(link));
let from = *above.last().expect("a way down ends on the parent");
let stand = self.stand_at(&place, stand, from, link);
let kids = self.children_entries(at, above);
let bead = self.facts.bead(self.tree, at, above);
let handle = Handle::Bead(place.clone());
let pointed = folds.pointed(&handle, over);
let open = !kids.is_empty() && pointed.unwrap_or(stand.rests_open(&bead));
let below = folds.beneath(&handle, over);
let folded = (!kids.is_empty()).then_some(open);
let line = bead_line(
node,
Some(&parent.key().id),
place.clone(),
trunk,
last,
trunk.len() as u16 + 1,
Some(&link.edge),
folded,
&bead,
);
let mut children = Vec::new();
if open || self.layout.beneath_shut {
trunk.push(!last);
let way = way_below(above, at);
children = self.draw_children(kids, &place, &way, below, named, stand, trunk);
trunk.pop();
}
Node::drawn(line, children)
}
#[allow(clippy::too_many_arguments)]
fn undrawn(
&self,
answers: Uniform<'a>,
link: &Link,
parent: &Place,
over: Option<&Scope>,
stand: Stand,
trunk: &[bool],
last: bool,
depth: u16,
) -> Node {
let counted = Counted {
tree: self.index,
at: link.bead,
stand,
forced: Folds::forced(over),
without: self.without,
};
let count = self.layout.count(self.tree, answers, counted);
undrawn_node(
self.tree,
answers,
self.layout.beneath_shut,
counted,
count.rows,
link,
parent,
trunk,
last,
depth,
)
}
fn children_entries<'b>(&'b self, at: usize, above: &[usize]) -> Vec<Child<'b>> {
let (drawn, elided) = self.facts.split(self.tree, at, above);
let mut entries: Vec<Child> = drawn
.into_iter()
.filter(|link| self.draws(link))
.map(Child::Node)
.collect();
let elided: Vec<&Link> = elided.into_iter().filter(|link| self.draws(link)).collect();
if !elided.is_empty() {
entries.push(Child::Elided(elided));
}
entries
}
fn draws(&self, link: &Link) -> bool {
Some(link.bead) != self.without
}
fn run_size(&self, members: &[&Link], above: &[usize]) -> usize {
match self.without {
Some(elsewhere) => {
let above: Vec<usize> = above.iter().copied().chain([elsewhere]).collect();
run_size(self.tree, members, &above)
}
None => self.facts.run_size(self.tree, members, above),
}
}
}
#[allow(clippy::too_many_arguments)]
fn bead_line(
node: &Bead,
parent: Option<&str>,
place: Place,
trunk: &[bool],
last: bool,
depth: u16,
edge: Option<&Edge>,
folded: Option<bool>,
bead: &BeadFacts,
) -> Line {
Line {
prefix: prefix(trunk, last, folded == Some(false), edge),
depth,
folded,
place: Some(place),
content: Content::Bead(row::cells(
node,
parent,
bead.progress,
shut_over(bead.beneath.clone(), folded),
)),
}
}
fn run_line(
parent: &Place,
count: usize,
trunk: &[bool],
last: bool,
depth: u16,
open: bool,
) -> Line {
Line {
prefix: prefix(trunk, last, !open, None),
depth,
folded: Some(open),
place: None,
content: Content::Elided {
count,
under: parent.clone(),
},
}
}
fn split_without<'t>(
answers: Uniform,
tree: &'t Tree,
at: usize,
without: Option<usize>,
) -> (Vec<&'t Link>, Vec<&'t Link>) {
let (mut drawn, mut elided) = answers.split(tree, at);
if let Some(elsewhere) = without {
drawn.retain(|link| link.bead != elsewhere);
elided.retain(|link| link.bead != elsewhere);
}
(drawn, elided)
}
fn run_size_undrawn(
answers: Uniform,
tree: &Tree,
at: usize,
members: &[&Link],
without: Option<usize>,
) -> usize {
match without {
Some(elsewhere) => run_size(tree, members, &[at, elsewhere]),
None => answers.run(at),
}
}
fn count(
kept: &mut Kept,
tree: &Tree,
answers: Uniform,
beneath_shut: bool,
row: &row::Layout,
counted: Counted,
) -> Count {
if let Some(count) = kept.beads.get(&counted) {
return count.clone();
}
let Counted {
at,
stand,
forced,
without,
..
} = counted;
let (drawn, elided) = split_without(answers, tree, at, without);
let kids = !drawn.is_empty() || !elided.is_empty();
let open = kids && forced.unwrap_or(stand.rests_open(answers.bead(at)));
let mut total = Count {
rows: 1,
widths: Widths::default(),
};
if open || beneath_shut {
for link in &drawn {
total.add(count_child(
kept,
tree,
answers,
beneath_shut,
row,
counted,
link,
));
}
if !elided.is_empty() {
total.add(count_run(
kept,
tree,
answers,
beneath_shut,
row,
counted,
&elided,
));
}
}
kept.beads.insert(counted, total.clone());
total
}
fn count_run(
kept: &mut Kept,
tree: &Tree,
answers: Uniform,
beneath_shut: bool,
row: &row::Layout,
under: Counted,
members: &[&Link],
) -> Count {
if let Some(count) = kept.runs.get(&under) {
return count.clone();
}
let open = under.forced.unwrap_or(false);
let mut total = Count {
rows: 1,
widths: Widths::default(),
};
if open || beneath_shut {
for link in members {
total.add(count_child(
kept,
tree,
answers,
beneath_shut,
row,
under,
link,
));
}
}
kept.runs.insert(under, total.clone());
total
}
fn count_child(
kept: &mut Kept,
tree: &Tree,
answers: Uniform,
beneath_shut: bool,
row: &row::Layout,
parent: Counted,
link: &Link,
) -> Count {
let child = Counted {
at: link.bead,
stand: parent.stand.beneath(parent.at, link, answers.chosen()),
..parent
};
let mut count = count(kept, tree, answers, beneath_shut, row, child);
let own = row::cells(
&tree.beads[link.bead],
Some(&tree.beads[parent.at].id),
answers.bead(link.bead).progress,
None,
);
count.widths.merge(&identity_widths(&own, row));
count
}
impl Count {
fn add(&mut self, beneath: Count) {
self.rows += beneath.rows;
self.widths.merge(&beneath.widths);
}
}
#[allow(clippy::too_many_arguments)]
fn undrawn_node(
tree: &Tree,
answers: Uniform,
beneath_shut: bool,
counted: Counted,
rows: usize,
link: &Link,
parent: &Place,
trunk: &[bool],
last: bool,
depth: u16,
) -> Node {
let at = link.bead;
let node = &tree.beads[at];
let place = parent.step_to(BeadKey {
project: tree.project.clone(),
id: node.id.clone(),
});
let kids = links_below(tree, at, &[])
.into_iter()
.any(|link| Some(link.bead) != counted.without);
let bead = answers.bead(at);
let rests_open = counted.stand.rests_open(bead);
let open = kids && counted.forced.unwrap_or(rests_open);
let folded = kids.then_some(open);
let line = bead_line(
node,
Some(&parent.key().id),
place,
trunk,
last,
depth,
Some(&link.edge),
folded,
bead,
);
let beneath = if open || beneath_shut {
Beneath::Bead(Undrawn {
counted,
trunk: trunk.iter().copied().chain([!last]).collect(),
})
} else {
Beneath::Nothing
};
Node::undrawn(line, rows, beneath)
}
fn ground_of<'g>(ground: &'g Ground, counted: &Counted) -> (&'g Arc<Tree>, Uniform<'g>) {
let tree = &ground.trees[counted.tree];
let answers = ground
.facts
.uniform(&root_key(tree))
.expect("a subtree left undrawn is in a tree with one answer per bead");
(tree, answers)
}
pub(super) fn beneath_bead(ground: &Ground, node: &Node, undrawn: &Undrawn) -> Vec<Node> {
let counted = undrawn.counted;
let (tree, answers) = ground_of(ground, &counted);
let parent = node.line.place.as_ref().expect("a bead's line has a place");
let (drawn, elided) = split_without(answers, tree, counted.at, counted.without);
let count = drawn.len() + usize::from(!elided.is_empty());
let depth = node.line.depth + 1;
let mut children: Vec<Node> = drawn
.iter()
.enumerate()
.map(|(n, link)| {
children_of(
ground,
tree,
answers,
counted,
link,
parent,
&undrawn.trunk,
n + 1 == count,
depth,
)
})
.collect();
if !elided.is_empty() {
let open = counted.forced.unwrap_or(false);
let rows = ground
.runs
.get(&counted)
.expect("a run beneath a counted bead was counted with it")
.rows;
let line = run_line(
parent,
run_size_undrawn(answers, tree, counted.at, &elided, counted.without),
&undrawn.trunk,
true,
depth,
open,
);
let beneath = if open || ground.beneath_shut {
Beneath::Run(Undrawn {
counted,
trunk: undrawn.trunk.iter().copied().chain([false]).collect(),
})
} else {
Beneath::Nothing
};
children.push(Node::undrawn(line, rows, beneath));
}
children
}
pub(super) fn beneath_run(ground: &Ground, node: &Node, undrawn: &Undrawn) -> Vec<Node> {
let counted = undrawn.counted;
let (tree, answers) = ground_of(ground, &counted);
let Content::Elided { under, .. } = &node.line.content else {
return Vec::new();
};
let (_, members) = split_without(answers, tree, counted.at, counted.without);
let count = members.len();
let depth = node.line.depth + 1;
members
.iter()
.enumerate()
.map(|(n, link)| {
children_of(
ground,
tree,
answers,
counted,
link,
under,
&undrawn.trunk,
n + 1 == count,
depth,
)
})
.collect()
}
#[allow(clippy::too_many_arguments)]
fn children_of(
ground: &Ground,
tree: &Tree,
answers: Uniform,
parent: Counted,
link: &Link,
place: &Place,
trunk: &[bool],
last: bool,
depth: u16,
) -> Node {
let counted = Counted {
at: link.bead,
stand: parent.stand.beneath(parent.at, link, answers.chosen()),
..parent
};
let rows = ground
.beads
.get(&counted)
.expect("a bead beneath a counted bead was counted with it")
.rows;
undrawn_node(
tree,
answers,
ground.beneath_shut,
counted,
rows,
link,
place,
trunk,
last,
depth,
)
}
impl Layout<'_> {
fn say_what_the_directory_chose(&self, lines: &mut Vec<Node>) {
if let config::Scope::Directory { project, .. } = &self.snapshot.scope {
lines.push(Node::drawn(
Line {
prefix: INDENT.to_string(),
depth: 0,
folded: None,
place: None,
content: Content::Scoped {
project: project.clone(),
},
},
Vec::new(),
));
}
}
}
fn group_items(snapshot: &Snapshot, kind: GroupKind, project: Option<&str>) -> Vec<Item> {
match kind {
GroupKind::FailedProjects => snapshot
.failed_projects
.iter()
.cloned()
.map(Item::Failed)
.collect(),
GroupKind::Conflicts => snapshot
.conflicts
.iter()
.cloned()
.map(Item::Conflict)
.collect(),
GroupKind::HiddenTrees | GroupKind::OutOfTheWay => Vec::new(),
GroupKind::Unattributed => snapshot
.unattributed
.iter()
.filter(|pane| Some(pane.project.as_str()) == project)
.cloned()
.map(Item::Loose)
.collect(),
GroupKind::Unconfigured => snapshot
.unconfigured
.iter()
.cloned()
.map(Item::Unconfigured)
.collect(),
}
}
fn nothing_to_draw() -> Line {
Line {
prefix: String::new(),
depth: 0,
folded: None,
place: None,
content: Content::Note(Note::NoRoots),
}
}