Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 56 additions & 39 deletions pumpkin/src/command/tree/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ fn flatten_require_nodes(nodes: &[Node], children: &[usize]) -> Vec<usize> {
let node = &nodes[i];
match &node.node_type {
NodeType::Require { .. } => {
new_children.extend(flatten_require_nodes(nodes, node.children.as_slice()));
new_children.extend(flatten_require_nodes(nodes, &node.children));
}
_ => new_children.push(i),
}
Expand All @@ -49,57 +49,74 @@ fn flatten_require_nodes(nodes: &[Node], children: &[usize]) -> Vec<usize> {
new_children
}

impl Display for CommandTree {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "/{}", self.names[0])?;
fn write_tree(f: &mut Formatter<'_>, nodes: &[Node], children: &[usize]) -> std::fmt::Result {
// Map node indices to actual nodes
// NOTE: We assume that Require nodes have already been "flattened" out
let nodes_iter = children.iter().map(|&idx| &nodes[idx]);

let current_children = &self.children[..];
// Check if there is a sibling of type ExecuteLeaf
// If there is, arguments on the current level are optional and will be printed surrounded by square brackets
let argument_is_optional = nodes_iter
.clone()
.any(|node| matches!(node.node_type, NodeType::ExecuteLeaf { .. }));

while !current_children.is_empty() {
let flattened = flatten_require_nodes(&self.nodes, current_children);
let visible_nodes: Vec<&Node> = nodes_iter
.clone()
.filter(|node| node.is_visible())
.collect();

let mut visible_iter = flattened
.iter()
.copied()
.filter(|&i| self.nodes[i].is_visible());
if visible_nodes.is_empty() {
return Ok(());
}

let Some(first_idx) = visible_iter.next() else {
break;
};
let multiple_visible_nodes = visible_nodes.len() > 1;

let second_idx = visible_iter.next();
write!(f, " ")?;

f.write_char(' ')?;
if argument_is_optional {
write!(f, "[")?;
}

let is_optional = flattened
.iter()
.any(|&i| matches!(self.nodes[i].node_type, NodeType::ExecuteLeaf { .. }));
if multiple_visible_nodes {
write!(f, "(")?;
}

if is_optional {
f.write_char('[')?;
}
for (idx, &node) in visible_nodes.iter().enumerate() {
// Print usage of this node
write!(f, "{node}")?;

if let Some(second_idx) = second_idx {
f.write_char('(')?;
self.nodes[first_idx].fmt(f)?;
// Recursively print usage of it's children
write_tree(f, nodes, &node.children)?;

write!(f, " | ")?;
self.nodes[second_idx].fmt(f)?;
if multiple_visible_nodes && idx != visible_nodes.len() - 1 {
write!(f, " | ")?;
}
}

for idx in visible_iter {
write!(f, " | ")?;
self.nodes[idx].fmt(f)?;
}
f.write_char(')')?;
if multiple_visible_nodes {
write!(f, ")")?;
}

break;
}
if argument_is_optional {
write!(f, "]")?;
}

if is_optional {
f.write_char(']')?;
}
}
Ok(())
}

Ok(())
impl Display for CommandTree {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "/{}", self.names[0])?;
// TODO: Help of commands like /bossbar becomes really long
// A possible solution would be to add a check if the first level consists of literals
// only and if so to run the printing as if it were separate commands

// TODO: Consider adding a max depth to print command usage only up to a certain depth.
// Vanilla seems to do this too (as can be seen with /help effect)

// Clean up graph by 'flattening' require nodes to their children
let flattened = flatten_require_nodes(&self.nodes, &self.children);
// Recursively iterate over tree to print help usage of a command
write_tree(f, &self.nodes, &flattened)
}
}