// NOTE: This file is generated by the templates/template.rb script and should not
// be modified manually. See /home/runner/work/herb/herb/templates/rust/src/nodes.rs.erb

use crate::errors::{AnyError, ErrorNode};
use crate::{Location, Token};
use crate::union_types::*;
use colored::*;

pub(crate) fn prettify_prism_tree(input: &str) -> String {
  let input = input.replace("+-- ", "├── ").replace("|   ", "│   ");
  let lines: Vec<&str> = input.lines().collect();
  let mut result: Vec<String> = lines.iter().map(|l| l.to_string()).collect();

  for i in 0..result.len() {
    if let Some(position) = result[i].find("├── ") {
      let indent = &result[i][..position];
      let is_last = !lines[i + 1..].iter().any(|next| {
        next.starts_with(&format!("{}├── ", indent)) || next.starts_with(&format!("{}│   ", indent))
      });

      if is_last {
        result[i] = format!("{}└── {}", indent, &result[i][position + "├── ".len()..]);
      }
    }
  }

  result.join("\n")
}

fn escape_string(s: &str) -> String {
  s.replace('\\', "\\\\")
    .replace('\n', "\\n")
    .replace('\r', "\\r")
    .replace('\t', "\\t")
    .replace('"', "\\\"")
}

fn format_string_value(value: &str) -> String {
  format!("\"{}\"", escape_string(value)).green().to_string()
}

fn format_token_value(token: &Option<Token>) -> String {
  token.as_ref().map(|t| t.tree_inspect()).unwrap_or_else(|| "∅".magenta().to_string())
}

fn format_bool_value(value: bool) -> String {
  value.to_string().magenta().bold().to_string()
}

fn format_node_value<T: Node + ?Sized>(node: &Option<Box<T>>, prefix: &str, add_spacing: bool) -> String {
  if let Some(ref n) = node {
    let mut output = String::new();
    output.push('\n');
    output.push_str(&inspect_node_field(n.as_ref(), prefix));
    if add_spacing {
      output.push_str(&format!("{}\n", prefix));
    }
    output
  } else {
    format!("{}\n", "∅".magenta())
  }
}

fn format_union_node_value<T>(node: &Option<T>, prefix: &str, add_spacing: bool, tree_inspect_fn: impl Fn(&T) -> String) -> String {
  if let Some(ref n) = node {
    let mut output = String::new();
    output.push('\n');
    let tree = tree_inspect_fn(n);
    let lines: Vec<&str> = tree.lines().collect();
    if !lines.is_empty() {
      output.push_str(prefix);
      output.push_str(&"└── ".white().to_string());
      output.push_str(lines[0]);
      output.push('\n');
      for line in lines.iter().skip(1) {
        if line.is_empty() {
          output.push_str(prefix);
          output.push('\n');
        } else {
          output.push_str(prefix);
          output.push_str("    ");
          output.push_str(line);
          output.push('\n');
        }
      }
    }
    if add_spacing {
      output.push_str(&format!("{}\n", prefix));
    }
    output
  } else {
    format!("{}\n", "∅".magenta())
  }
}

fn format_array_value(array: &[AnyNode], prefix: &str) -> String {
  inspect_array(array, prefix)
}

fn format_errors_field(errors: &[AnyError], prefix: &str) -> String {
  inspect_errors(errors, prefix)
}

fn inspect_errors(errors: &[AnyError], prefix: &str) -> String {
  if errors.is_empty() {
    return String::new();
  }

  let mut output = String::new();
  output.push_str(&format!("{} {}: {}\n",
    "├──".white(),
    "errors".red().bold(),
    format!("({} error{})", errors.len(), if errors.len() == 1 { "" } else { "s" }).dimmed()
  ));

  for (i, error) in errors.iter().enumerate() {
    let is_last = i == errors.len() - 1;
    let symbol = if is_last { "└── " } else { "├── " };
    let next_prefix_str = if is_last { "    " } else { "│   " };
    let next_prefix = next_prefix_str.white().to_string();

    let tree = error.tree_inspect();
    let tree = tree.trim_end_matches('\n');
    output.push_str(&format!("{}{}{}\n", prefix, symbol.white(), tree.replace('\n', &format!("\n{}{}", prefix, next_prefix))));

    if !is_last {
      output.push_str(&format!("{}{}\n", prefix, "│   ".white()));
    }
  }

  output.push_str(&format!("{}\n", prefix));
  output
}

fn inspect_array(array: &[AnyNode], prefix: &str) -> String {
  if array.is_empty() {
    return format!("{}\n", "[]".dimmed());
  }

  let mut output = String::new();
  output.push_str(&format!("{}\n", format!("({} item{})", array.len(), if array.len() == 1 { "" } else { "s" }).dimmed()));

  for (i, item) in array.iter().enumerate() {
    let is_last = i == array.len() - 1;
    let symbol = if is_last { "└── " } else { "├── " };
    let next_prefix_str = if is_last { "    " } else { "│   " };
    let next_prefix = next_prefix_str.white().to_string();

    let tree = item.tree_inspect();
    let tree = tree.trim_end_matches('\n');

    output.push_str(prefix);
    output.push_str(&symbol.white().to_string());
    output.push_str(&tree.replace('\n', &format!("\n{}{}", prefix, next_prefix)));
    output.push('\n');

    if !is_last {
      output.push_str(prefix);
      output.push_str(&next_prefix);
      output.push('\n');
    }
  }

  output
}

fn inspect_node_field<T: Node + ?Sized>(node: &T, prefix: &str) -> String {
  let tree = node.tree_inspect();
  let tree = tree.trim_end_matches('\n');

  let lines: Vec<&str> = tree.split('\n').collect();
  if lines.is_empty() {
    return format!("{}\n", "∅".magenta());
  }

  let mut result = String::new();
  result.push_str(prefix);
  result.push_str(&"└── ".white().to_string());
  result.push_str(lines[0]);
  result.push('\n');

  for line in lines.iter().skip(1) {
    if line.is_empty() {
      result.push_str(prefix);
      result.push('\n');
    } else {
      result.push_str(prefix);
      result.push_str("    ");
      result.push_str(line);
      result.push('\n');
    }
  }

  result
}

pub trait Node {
  fn node_type(&self) -> &str;
  fn location(&self) -> &Location;
  fn errors(&self) -> &[AnyError];
  fn child_nodes(&self) -> Vec<&dyn Node>;
  fn recursive_errors(&self) -> Vec<&dyn ErrorNode>;
  fn tree_inspect(&self) -> String;
}

pub trait ERBNode {
  fn tag_opening(&self) -> &Option<Token>;
  fn content(&self) -> &Option<Token>;
  fn tag_closing(&self) -> &Option<Token>;
  fn location(&self) -> &Location;
}

#[derive(Debug, Clone)]
pub enum AnyNode {
  DocumentNode(Box<DocumentNode>),
  LiteralNode(Box<LiteralNode>),
  HTMLOpenTagNode(Box<HTMLOpenTagNode>),
  HTMLConditionalOpenTagNode(Box<HTMLConditionalOpenTagNode>),
  HTMLCloseTagNode(Box<HTMLCloseTagNode>),
  HTMLOmittedCloseTagNode(Box<HTMLOmittedCloseTagNode>),
  HTMLVirtualCloseTagNode(Box<HTMLVirtualCloseTagNode>),
  HTMLElementNode(Box<HTMLElementNode>),
  HTMLConditionalElementNode(Box<HTMLConditionalElementNode>),
  HTMLAttributeValueNode(Box<HTMLAttributeValueNode>),
  HTMLAttributeNameNode(Box<HTMLAttributeNameNode>),
  HTMLAttributeNode(Box<HTMLAttributeNode>),
  RubyLiteralNode(Box<RubyLiteralNode>),
  RubyHTMLAttributesSplatNode(Box<RubyHTMLAttributesSplatNode>),
  ERBOpenTagNode(Box<ERBOpenTagNode>),
  HTMLTextNode(Box<HTMLTextNode>),
  HTMLCommentNode(Box<HTMLCommentNode>),
  HTMLDoctypeNode(Box<HTMLDoctypeNode>),
  XMLDeclarationNode(Box<XMLDeclarationNode>),
  CDATANode(Box<CDATANode>),
  WhitespaceNode(Box<WhitespaceNode>),
  ERBContentNode(Box<ERBContentNode>),
  ERBEndNode(Box<ERBEndNode>),
  ERBElseNode(Box<ERBElseNode>),
  ERBIfNode(Box<ERBIfNode>),
  ERBBlockNode(Box<ERBBlockNode>),
  ERBWhenNode(Box<ERBWhenNode>),
  ERBCaseNode(Box<ERBCaseNode>),
  ERBCaseMatchNode(Box<ERBCaseMatchNode>),
  ERBWhileNode(Box<ERBWhileNode>),
  ERBUntilNode(Box<ERBUntilNode>),
  ERBForNode(Box<ERBForNode>),
  ERBRescueNode(Box<ERBRescueNode>),
  ERBEnsureNode(Box<ERBEnsureNode>),
  ERBBeginNode(Box<ERBBeginNode>),
  ERBUnlessNode(Box<ERBUnlessNode>),
  RubyRenderLocalNode(Box<RubyRenderLocalNode>),
  ERBRenderNode(Box<ERBRenderNode>),
  RubyStrictLocalNode(Box<RubyStrictLocalNode>),
  ERBStrictLocalsNode(Box<ERBStrictLocalsNode>),
  ERBYieldNode(Box<ERBYieldNode>),
  ERBInNode(Box<ERBInNode>),
}

impl AnyNode {
  pub fn node_type(&self) -> &str {
    match self {
      AnyNode::DocumentNode(n) => &n.node_type,
      AnyNode::LiteralNode(n) => &n.node_type,
      AnyNode::HTMLOpenTagNode(n) => &n.node_type,
      AnyNode::HTMLConditionalOpenTagNode(n) => &n.node_type,
      AnyNode::HTMLCloseTagNode(n) => &n.node_type,
      AnyNode::HTMLOmittedCloseTagNode(n) => &n.node_type,
      AnyNode::HTMLVirtualCloseTagNode(n) => &n.node_type,
      AnyNode::HTMLElementNode(n) => &n.node_type,
      AnyNode::HTMLConditionalElementNode(n) => &n.node_type,
      AnyNode::HTMLAttributeValueNode(n) => &n.node_type,
      AnyNode::HTMLAttributeNameNode(n) => &n.node_type,
      AnyNode::HTMLAttributeNode(n) => &n.node_type,
      AnyNode::RubyLiteralNode(n) => &n.node_type,
      AnyNode::RubyHTMLAttributesSplatNode(n) => &n.node_type,
      AnyNode::ERBOpenTagNode(n) => &n.node_type,
      AnyNode::HTMLTextNode(n) => &n.node_type,
      AnyNode::HTMLCommentNode(n) => &n.node_type,
      AnyNode::HTMLDoctypeNode(n) => &n.node_type,
      AnyNode::XMLDeclarationNode(n) => &n.node_type,
      AnyNode::CDATANode(n) => &n.node_type,
      AnyNode::WhitespaceNode(n) => &n.node_type,
      AnyNode::ERBContentNode(n) => &n.node_type,
      AnyNode::ERBEndNode(n) => &n.node_type,
      AnyNode::ERBElseNode(n) => &n.node_type,
      AnyNode::ERBIfNode(n) => &n.node_type,
      AnyNode::ERBBlockNode(n) => &n.node_type,
      AnyNode::ERBWhenNode(n) => &n.node_type,
      AnyNode::ERBCaseNode(n) => &n.node_type,
      AnyNode::ERBCaseMatchNode(n) => &n.node_type,
      AnyNode::ERBWhileNode(n) => &n.node_type,
      AnyNode::ERBUntilNode(n) => &n.node_type,
      AnyNode::ERBForNode(n) => &n.node_type,
      AnyNode::ERBRescueNode(n) => &n.node_type,
      AnyNode::ERBEnsureNode(n) => &n.node_type,
      AnyNode::ERBBeginNode(n) => &n.node_type,
      AnyNode::ERBUnlessNode(n) => &n.node_type,
      AnyNode::RubyRenderLocalNode(n) => &n.node_type,
      AnyNode::ERBRenderNode(n) => &n.node_type,
      AnyNode::RubyStrictLocalNode(n) => &n.node_type,
      AnyNode::ERBStrictLocalsNode(n) => &n.node_type,
      AnyNode::ERBYieldNode(n) => &n.node_type,
      AnyNode::ERBInNode(n) => &n.node_type,
    }
  }

  pub fn location(&self) -> &Location {
    match self {
      AnyNode::DocumentNode(n) => &n.location,
      AnyNode::LiteralNode(n) => &n.location,
      AnyNode::HTMLOpenTagNode(n) => &n.location,
      AnyNode::HTMLConditionalOpenTagNode(n) => &n.location,
      AnyNode::HTMLCloseTagNode(n) => &n.location,
      AnyNode::HTMLOmittedCloseTagNode(n) => &n.location,
      AnyNode::HTMLVirtualCloseTagNode(n) => &n.location,
      AnyNode::HTMLElementNode(n) => &n.location,
      AnyNode::HTMLConditionalElementNode(n) => &n.location,
      AnyNode::HTMLAttributeValueNode(n) => &n.location,
      AnyNode::HTMLAttributeNameNode(n) => &n.location,
      AnyNode::HTMLAttributeNode(n) => &n.location,
      AnyNode::RubyLiteralNode(n) => &n.location,
      AnyNode::RubyHTMLAttributesSplatNode(n) => &n.location,
      AnyNode::ERBOpenTagNode(n) => &n.location,
      AnyNode::HTMLTextNode(n) => &n.location,
      AnyNode::HTMLCommentNode(n) => &n.location,
      AnyNode::HTMLDoctypeNode(n) => &n.location,
      AnyNode::XMLDeclarationNode(n) => &n.location,
      AnyNode::CDATANode(n) => &n.location,
      AnyNode::WhitespaceNode(n) => &n.location,
      AnyNode::ERBContentNode(n) => &n.location,
      AnyNode::ERBEndNode(n) => &n.location,
      AnyNode::ERBElseNode(n) => &n.location,
      AnyNode::ERBIfNode(n) => &n.location,
      AnyNode::ERBBlockNode(n) => &n.location,
      AnyNode::ERBWhenNode(n) => &n.location,
      AnyNode::ERBCaseNode(n) => &n.location,
      AnyNode::ERBCaseMatchNode(n) => &n.location,
      AnyNode::ERBWhileNode(n) => &n.location,
      AnyNode::ERBUntilNode(n) => &n.location,
      AnyNode::ERBForNode(n) => &n.location,
      AnyNode::ERBRescueNode(n) => &n.location,
      AnyNode::ERBEnsureNode(n) => &n.location,
      AnyNode::ERBBeginNode(n) => &n.location,
      AnyNode::ERBUnlessNode(n) => &n.location,
      AnyNode::RubyRenderLocalNode(n) => &n.location,
      AnyNode::ERBRenderNode(n) => &n.location,
      AnyNode::RubyStrictLocalNode(n) => &n.location,
      AnyNode::ERBStrictLocalsNode(n) => &n.location,
      AnyNode::ERBYieldNode(n) => &n.location,
      AnyNode::ERBInNode(n) => &n.location,
    }
  }

  pub fn errors(&self) -> &[AnyError] {
    match self {
      AnyNode::DocumentNode(n) => &n.errors,
      AnyNode::LiteralNode(n) => &n.errors,
      AnyNode::HTMLOpenTagNode(n) => &n.errors,
      AnyNode::HTMLConditionalOpenTagNode(n) => &n.errors,
      AnyNode::HTMLCloseTagNode(n) => &n.errors,
      AnyNode::HTMLOmittedCloseTagNode(n) => &n.errors,
      AnyNode::HTMLVirtualCloseTagNode(n) => &n.errors,
      AnyNode::HTMLElementNode(n) => &n.errors,
      AnyNode::HTMLConditionalElementNode(n) => &n.errors,
      AnyNode::HTMLAttributeValueNode(n) => &n.errors,
      AnyNode::HTMLAttributeNameNode(n) => &n.errors,
      AnyNode::HTMLAttributeNode(n) => &n.errors,
      AnyNode::RubyLiteralNode(n) => &n.errors,
      AnyNode::RubyHTMLAttributesSplatNode(n) => &n.errors,
      AnyNode::ERBOpenTagNode(n) => &n.errors,
      AnyNode::HTMLTextNode(n) => &n.errors,
      AnyNode::HTMLCommentNode(n) => &n.errors,
      AnyNode::HTMLDoctypeNode(n) => &n.errors,
      AnyNode::XMLDeclarationNode(n) => &n.errors,
      AnyNode::CDATANode(n) => &n.errors,
      AnyNode::WhitespaceNode(n) => &n.errors,
      AnyNode::ERBContentNode(n) => &n.errors,
      AnyNode::ERBEndNode(n) => &n.errors,
      AnyNode::ERBElseNode(n) => &n.errors,
      AnyNode::ERBIfNode(n) => &n.errors,
      AnyNode::ERBBlockNode(n) => &n.errors,
      AnyNode::ERBWhenNode(n) => &n.errors,
      AnyNode::ERBCaseNode(n) => &n.errors,
      AnyNode::ERBCaseMatchNode(n) => &n.errors,
      AnyNode::ERBWhileNode(n) => &n.errors,
      AnyNode::ERBUntilNode(n) => &n.errors,
      AnyNode::ERBForNode(n) => &n.errors,
      AnyNode::ERBRescueNode(n) => &n.errors,
      AnyNode::ERBEnsureNode(n) => &n.errors,
      AnyNode::ERBBeginNode(n) => &n.errors,
      AnyNode::ERBUnlessNode(n) => &n.errors,
      AnyNode::RubyRenderLocalNode(n) => &n.errors,
      AnyNode::ERBRenderNode(n) => &n.errors,
      AnyNode::RubyStrictLocalNode(n) => &n.errors,
      AnyNode::ERBStrictLocalsNode(n) => &n.errors,
      AnyNode::ERBYieldNode(n) => &n.errors,
      AnyNode::ERBInNode(n) => &n.errors,
    }
  }

  pub fn tree_inspect(&self) -> String {
    match self {
      AnyNode::DocumentNode(n) => n.tree_inspect(),
      AnyNode::LiteralNode(n) => n.tree_inspect(),
      AnyNode::HTMLOpenTagNode(n) => n.tree_inspect(),
      AnyNode::HTMLConditionalOpenTagNode(n) => n.tree_inspect(),
      AnyNode::HTMLCloseTagNode(n) => n.tree_inspect(),
      AnyNode::HTMLOmittedCloseTagNode(n) => n.tree_inspect(),
      AnyNode::HTMLVirtualCloseTagNode(n) => n.tree_inspect(),
      AnyNode::HTMLElementNode(n) => n.tree_inspect(),
      AnyNode::HTMLConditionalElementNode(n) => n.tree_inspect(),
      AnyNode::HTMLAttributeValueNode(n) => n.tree_inspect(),
      AnyNode::HTMLAttributeNameNode(n) => n.tree_inspect(),
      AnyNode::HTMLAttributeNode(n) => n.tree_inspect(),
      AnyNode::RubyLiteralNode(n) => n.tree_inspect(),
      AnyNode::RubyHTMLAttributesSplatNode(n) => n.tree_inspect(),
      AnyNode::ERBOpenTagNode(n) => n.tree_inspect(),
      AnyNode::HTMLTextNode(n) => n.tree_inspect(),
      AnyNode::HTMLCommentNode(n) => n.tree_inspect(),
      AnyNode::HTMLDoctypeNode(n) => n.tree_inspect(),
      AnyNode::XMLDeclarationNode(n) => n.tree_inspect(),
      AnyNode::CDATANode(n) => n.tree_inspect(),
      AnyNode::WhitespaceNode(n) => n.tree_inspect(),
      AnyNode::ERBContentNode(n) => n.tree_inspect(),
      AnyNode::ERBEndNode(n) => n.tree_inspect(),
      AnyNode::ERBElseNode(n) => n.tree_inspect(),
      AnyNode::ERBIfNode(n) => n.tree_inspect(),
      AnyNode::ERBBlockNode(n) => n.tree_inspect(),
      AnyNode::ERBWhenNode(n) => n.tree_inspect(),
      AnyNode::ERBCaseNode(n) => n.tree_inspect(),
      AnyNode::ERBCaseMatchNode(n) => n.tree_inspect(),
      AnyNode::ERBWhileNode(n) => n.tree_inspect(),
      AnyNode::ERBUntilNode(n) => n.tree_inspect(),
      AnyNode::ERBForNode(n) => n.tree_inspect(),
      AnyNode::ERBRescueNode(n) => n.tree_inspect(),
      AnyNode::ERBEnsureNode(n) => n.tree_inspect(),
      AnyNode::ERBBeginNode(n) => n.tree_inspect(),
      AnyNode::ERBUnlessNode(n) => n.tree_inspect(),
      AnyNode::RubyRenderLocalNode(n) => n.tree_inspect(),
      AnyNode::ERBRenderNode(n) => n.tree_inspect(),
      AnyNode::RubyStrictLocalNode(n) => n.tree_inspect(),
      AnyNode::ERBStrictLocalsNode(n) => n.tree_inspect(),
      AnyNode::ERBYieldNode(n) => n.tree_inspect(),
      AnyNode::ERBInNode(n) => n.tree_inspect(),
    }
  }

  pub fn child_nodes(&self) -> Vec<&dyn Node> {
    match self {
      AnyNode::DocumentNode(n) => n.child_nodes(),
      AnyNode::LiteralNode(n) => n.child_nodes(),
      AnyNode::HTMLOpenTagNode(n) => n.child_nodes(),
      AnyNode::HTMLConditionalOpenTagNode(n) => n.child_nodes(),
      AnyNode::HTMLCloseTagNode(n) => n.child_nodes(),
      AnyNode::HTMLOmittedCloseTagNode(n) => n.child_nodes(),
      AnyNode::HTMLVirtualCloseTagNode(n) => n.child_nodes(),
      AnyNode::HTMLElementNode(n) => n.child_nodes(),
      AnyNode::HTMLConditionalElementNode(n) => n.child_nodes(),
      AnyNode::HTMLAttributeValueNode(n) => n.child_nodes(),
      AnyNode::HTMLAttributeNameNode(n) => n.child_nodes(),
      AnyNode::HTMLAttributeNode(n) => n.child_nodes(),
      AnyNode::RubyLiteralNode(n) => n.child_nodes(),
      AnyNode::RubyHTMLAttributesSplatNode(n) => n.child_nodes(),
      AnyNode::ERBOpenTagNode(n) => n.child_nodes(),
      AnyNode::HTMLTextNode(n) => n.child_nodes(),
      AnyNode::HTMLCommentNode(n) => n.child_nodes(),
      AnyNode::HTMLDoctypeNode(n) => n.child_nodes(),
      AnyNode::XMLDeclarationNode(n) => n.child_nodes(),
      AnyNode::CDATANode(n) => n.child_nodes(),
      AnyNode::WhitespaceNode(n) => n.child_nodes(),
      AnyNode::ERBContentNode(n) => n.child_nodes(),
      AnyNode::ERBEndNode(n) => n.child_nodes(),
      AnyNode::ERBElseNode(n) => n.child_nodes(),
      AnyNode::ERBIfNode(n) => n.child_nodes(),
      AnyNode::ERBBlockNode(n) => n.child_nodes(),
      AnyNode::ERBWhenNode(n) => n.child_nodes(),
      AnyNode::ERBCaseNode(n) => n.child_nodes(),
      AnyNode::ERBCaseMatchNode(n) => n.child_nodes(),
      AnyNode::ERBWhileNode(n) => n.child_nodes(),
      AnyNode::ERBUntilNode(n) => n.child_nodes(),
      AnyNode::ERBForNode(n) => n.child_nodes(),
      AnyNode::ERBRescueNode(n) => n.child_nodes(),
      AnyNode::ERBEnsureNode(n) => n.child_nodes(),
      AnyNode::ERBBeginNode(n) => n.child_nodes(),
      AnyNode::ERBUnlessNode(n) => n.child_nodes(),
      AnyNode::RubyRenderLocalNode(n) => n.child_nodes(),
      AnyNode::ERBRenderNode(n) => n.child_nodes(),
      AnyNode::RubyStrictLocalNode(n) => n.child_nodes(),
      AnyNode::ERBStrictLocalsNode(n) => n.child_nodes(),
      AnyNode::ERBYieldNode(n) => n.child_nodes(),
      AnyNode::ERBInNode(n) => n.child_nodes(),
    }
  }

  pub fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    match self {
      AnyNode::DocumentNode(n) => n.recursive_errors(),
      AnyNode::LiteralNode(n) => n.recursive_errors(),
      AnyNode::HTMLOpenTagNode(n) => n.recursive_errors(),
      AnyNode::HTMLConditionalOpenTagNode(n) => n.recursive_errors(),
      AnyNode::HTMLCloseTagNode(n) => n.recursive_errors(),
      AnyNode::HTMLOmittedCloseTagNode(n) => n.recursive_errors(),
      AnyNode::HTMLVirtualCloseTagNode(n) => n.recursive_errors(),
      AnyNode::HTMLElementNode(n) => n.recursive_errors(),
      AnyNode::HTMLConditionalElementNode(n) => n.recursive_errors(),
      AnyNode::HTMLAttributeValueNode(n) => n.recursive_errors(),
      AnyNode::HTMLAttributeNameNode(n) => n.recursive_errors(),
      AnyNode::HTMLAttributeNode(n) => n.recursive_errors(),
      AnyNode::RubyLiteralNode(n) => n.recursive_errors(),
      AnyNode::RubyHTMLAttributesSplatNode(n) => n.recursive_errors(),
      AnyNode::ERBOpenTagNode(n) => n.recursive_errors(),
      AnyNode::HTMLTextNode(n) => n.recursive_errors(),
      AnyNode::HTMLCommentNode(n) => n.recursive_errors(),
      AnyNode::HTMLDoctypeNode(n) => n.recursive_errors(),
      AnyNode::XMLDeclarationNode(n) => n.recursive_errors(),
      AnyNode::CDATANode(n) => n.recursive_errors(),
      AnyNode::WhitespaceNode(n) => n.recursive_errors(),
      AnyNode::ERBContentNode(n) => n.recursive_errors(),
      AnyNode::ERBEndNode(n) => n.recursive_errors(),
      AnyNode::ERBElseNode(n) => n.recursive_errors(),
      AnyNode::ERBIfNode(n) => n.recursive_errors(),
      AnyNode::ERBBlockNode(n) => n.recursive_errors(),
      AnyNode::ERBWhenNode(n) => n.recursive_errors(),
      AnyNode::ERBCaseNode(n) => n.recursive_errors(),
      AnyNode::ERBCaseMatchNode(n) => n.recursive_errors(),
      AnyNode::ERBWhileNode(n) => n.recursive_errors(),
      AnyNode::ERBUntilNode(n) => n.recursive_errors(),
      AnyNode::ERBForNode(n) => n.recursive_errors(),
      AnyNode::ERBRescueNode(n) => n.recursive_errors(),
      AnyNode::ERBEnsureNode(n) => n.recursive_errors(),
      AnyNode::ERBBeginNode(n) => n.recursive_errors(),
      AnyNode::ERBUnlessNode(n) => n.recursive_errors(),
      AnyNode::RubyRenderLocalNode(n) => n.recursive_errors(),
      AnyNode::ERBRenderNode(n) => n.recursive_errors(),
      AnyNode::RubyStrictLocalNode(n) => n.recursive_errors(),
      AnyNode::ERBStrictLocalsNode(n) => n.recursive_errors(),
      AnyNode::ERBYieldNode(n) => n.recursive_errors(),
      AnyNode::ERBInNode(n) => n.recursive_errors(),
    }
  }
}

impl Node for AnyNode {
  fn node_type(&self) -> &str {
    self.node_type()
  }

  fn location(&self) -> &Location {
    self.location()
  }

  fn errors(&self) -> &[AnyError] {
    self.errors()
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    self.child_nodes()
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    self.recursive_errors()
  }

  fn tree_inspect(&self) -> String {
    self.tree_inspect()
  }
}

#[derive(Debug, Clone)]
pub struct DocumentNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub children: Vec<AnyNode>,
  // no-op for prism_context
  pub prism_node: Option<String>,
}

impl Node for DocumentNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.children.iter().map(|n| n as &dyn Node));
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.children {
      all_errors.extend(child.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "DocumentNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    { let is_last = self.prism_node.is_none(); let symbol = if is_last { "└── " } else { "├── " };
    let child_prefix = if is_last { "    " } else { "│   " };
    output.push_str(&format!("{}{}: {}", symbol.white(), "children".white(), format_array_value(&self.children, &child_prefix.white().to_string())));
    }
    if let Some(ref prism_string) = self.prism_node {
      let prefix = "    ";
      output.push_str(&format!("{}{}: \n", "└── ".white(), "prism_node".white()));
      output.push_str(&format!("{}└── ", prefix));
      let prettified = prettify_prism_tree(prism_string);
      let indented = prettified.lines().enumerate().map(|(i, line)| {
        if i == 0 { line.to_string() } else { format!("{}    {}", prefix, line) }
      }).collect::<Vec<_>>().join("\n");
      output.push_str(&indented);
      output.push('\n');
    }

    output
  }
}

#[derive(Debug, Clone)]
pub struct LiteralNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub content: String,
}

impl Node for LiteralNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    Vec::new()
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    self.errors.iter().map(|e| e as &dyn ErrorNode).collect()
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "LiteralNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "content".white(), format_string_value(&self.content)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct HTMLOpenTagNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub tag_name: Option<Token>,
  pub tag_closing: Option<Token>,
  pub children: Vec<AnyNode>,
  pub is_void: bool,
}

impl Node for HTMLOpenTagNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.children.iter().map(|n| n as &dyn Node));
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.children {
      all_errors.extend(child.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "HTMLOpenTagNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_name".white(), format_token_value(&self.tag_name)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "children".white(), format_array_value(&self.children, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "is_void".white(), format_bool_value(self.is_void)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct HTMLConditionalOpenTagNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub conditional: Option<ERBIfNodeOrERBUnlessNode>,
  pub tag_name: Option<Token>,
  pub is_void: bool,
}

impl Node for HTMLConditionalOpenTagNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    if let Some(ref node) = self.conditional {
      children.push(node.as_node());
    }
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    if let Some(ref node) = self.conditional {
      all_errors.extend(node.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "HTMLConditionalOpenTagNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "conditional".white(), format_union_node_value(&self.conditional, &"│   ".white().to_string(), true, |n: &ERBIfNodeOrERBUnlessNode| n.tree_inspect())));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_name".white(), format_token_value(&self.tag_name)));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "is_void".white(), format_bool_value(self.is_void)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct HTMLCloseTagNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub tag_name: Option<Token>,
  pub children: Vec<AnyNode>,
  pub tag_closing: Option<Token>,
}

impl Node for HTMLCloseTagNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.children.iter().map(|n| n as &dyn Node));
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.children {
      all_errors.extend(child.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "HTMLCloseTagNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_name".white(), format_token_value(&self.tag_name)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "children".white(), format_array_value(&self.children, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct HTMLOmittedCloseTagNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_name: Option<Token>,
}

impl Node for HTMLOmittedCloseTagNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    Vec::new()
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    self.errors.iter().map(|e| e as &dyn ErrorNode).collect()
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "HTMLOmittedCloseTagNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "tag_name".white(), format_token_value(&self.tag_name)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct HTMLVirtualCloseTagNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_name: Option<Token>,
}

impl Node for HTMLVirtualCloseTagNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    Vec::new()
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    self.errors.iter().map(|e| e as &dyn ErrorNode).collect()
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "HTMLVirtualCloseTagNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "tag_name".white(), format_token_value(&self.tag_name)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct HTMLElementNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub open_tag: Option<ERBOpenTagNodeOrHTMLConditionalOpenTagNodeOrHTMLOpenTagNode>,
  pub tag_name: Option<Token>,
  pub body: Vec<AnyNode>,
  pub close_tag: Option<ERBEndNodeOrHTMLCloseTagNodeOrHTMLOmittedCloseTagNodeOrHTMLVirtualCloseTagNode>,
  pub is_void: bool,
  pub element_source: String,
}

impl Node for HTMLElementNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    if let Some(ref node) = self.open_tag {
      children.push(node.as_node());
    }
    children.extend(self.body.iter().map(|n| n as &dyn Node));
    if let Some(ref node) = self.close_tag {
      children.push(node.as_node());
    }
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    if let Some(ref node) = self.open_tag {
      all_errors.extend(node.recursive_errors());
    }
    for child in &self.body {
      all_errors.extend(child.recursive_errors());
    }
    if let Some(ref node) = self.close_tag {
      all_errors.extend(node.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "HTMLElementNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "open_tag".white(), format_union_node_value(&self.open_tag, &"│   ".white().to_string(), true, |n: &ERBOpenTagNodeOrHTMLConditionalOpenTagNodeOrHTMLOpenTagNode| n.tree_inspect())));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_name".white(), format_token_value(&self.tag_name)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "body".white(), format_array_value(&self.body, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "close_tag".white(), format_union_node_value(&self.close_tag, &"│   ".white().to_string(), true, |n: &ERBEndNodeOrHTMLCloseTagNodeOrHTMLOmittedCloseTagNodeOrHTMLVirtualCloseTagNode| n.tree_inspect())));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "is_void".white(), format_bool_value(self.is_void)));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "element_source".white(), format_string_value(&self.element_source)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct HTMLConditionalElementNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub condition: String,
  pub open_conditional: Option<ERBIfNodeOrERBUnlessNode>,
  pub open_tag: Option<Box<HTMLOpenTagNode>>,
  pub body: Vec<AnyNode>,
  pub close_tag: Option<HTMLCloseTagNodeOrHTMLOmittedCloseTagNode>,
  pub close_conditional: Option<ERBIfNodeOrERBUnlessNode>,
  pub tag_name: Option<Token>,
  pub element_source: String,
}

impl Node for HTMLConditionalElementNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    if let Some(ref node) = self.open_conditional {
      children.push(node.as_node());
    }
    if let Some(ref node) = self.open_tag {
      children.push(node.as_ref() as &dyn Node);
    }
    children.extend(self.body.iter().map(|n| n as &dyn Node));
    if let Some(ref node) = self.close_tag {
      children.push(node.as_node());
    }
    if let Some(ref node) = self.close_conditional {
      children.push(node.as_node());
    }
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    if let Some(ref node) = self.open_conditional {
      all_errors.extend(node.recursive_errors());
    }
    if let Some(ref node) = self.open_tag {
      all_errors.extend(node.recursive_errors());
    }
    for child in &self.body {
      all_errors.extend(child.recursive_errors());
    }
    if let Some(ref node) = self.close_tag {
      all_errors.extend(node.recursive_errors());
    }
    if let Some(ref node) = self.close_conditional {
      all_errors.extend(node.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "HTMLConditionalElementNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "condition".white(), format_string_value(&self.condition)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "open_conditional".white(), format_union_node_value(&self.open_conditional, &"│   ".white().to_string(), true, |n: &ERBIfNodeOrERBUnlessNode| n.tree_inspect())));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "open_tag".white(), format_node_value(&self.open_tag, &"│   ".white().to_string(), true)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "body".white(), format_array_value(&self.body, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "close_tag".white(), format_union_node_value(&self.close_tag, &"│   ".white().to_string(), true, |n: &HTMLCloseTagNodeOrHTMLOmittedCloseTagNode| n.tree_inspect())));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "close_conditional".white(), format_union_node_value(&self.close_conditional, &"│   ".white().to_string(), true, |n: &ERBIfNodeOrERBUnlessNode| n.tree_inspect())));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_name".white(), format_token_value(&self.tag_name)));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "element_source".white(), format_string_value(&self.element_source)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct HTMLAttributeValueNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub open_quote: Option<Token>,
  pub children: Vec<AnyNode>,
  pub close_quote: Option<Token>,
  pub quoted: bool,
}

impl Node for HTMLAttributeValueNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.children.iter().map(|n| n as &dyn Node));
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.children {
      all_errors.extend(child.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "HTMLAttributeValueNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "open_quote".white(), format_token_value(&self.open_quote)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "children".white(), format_array_value(&self.children, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "close_quote".white(), format_token_value(&self.close_quote)));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "quoted".white(), format_bool_value(self.quoted)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct HTMLAttributeNameNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub children: Vec<AnyNode>,
}

impl Node for HTMLAttributeNameNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.children.iter().map(|n| n as &dyn Node));
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.children {
      all_errors.extend(child.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "HTMLAttributeNameNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "children".white(), format_array_value(&self.children, &"    ".white().to_string())));

    output
  }
}

#[derive(Debug, Clone)]
pub struct HTMLAttributeNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub name: Option<Box<HTMLAttributeNameNode>>,
  pub equals: Option<Token>,
  pub value: Option<Box<HTMLAttributeValueNode>>,
}

impl Node for HTMLAttributeNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    if let Some(ref node) = self.name {
      children.push(node.as_ref() as &dyn Node);
    }
    if let Some(ref node) = self.value {
      children.push(node.as_ref() as &dyn Node);
    }
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    if let Some(ref node) = self.name {
      all_errors.extend(node.recursive_errors());
    }
    if let Some(ref node) = self.value {
      all_errors.extend(node.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "HTMLAttributeNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "name".white(), format_node_value(&self.name, &"│   ".white().to_string(), true)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "equals".white(), format_token_value(&self.equals)));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "value".white(), format_node_value(&self.value, &"    ".white().to_string(), false)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct RubyLiteralNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub content: String,
}

impl Node for RubyLiteralNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    Vec::new()
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    self.errors.iter().map(|e| e as &dyn ErrorNode).collect()
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "RubyLiteralNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "content".white(), format_string_value(&self.content)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct RubyHTMLAttributesSplatNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub content: String,
  pub prefix: String,
}

impl Node for RubyHTMLAttributesSplatNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    Vec::new()
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    self.errors.iter().map(|e| e as &dyn ErrorNode).collect()
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "RubyHTMLAttributesSplatNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_string_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "prefix".white(), format_string_value(&self.prefix)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct ERBOpenTagNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  pub tag_name: Option<Token>,
  pub children: Vec<AnyNode>,
}

impl Node for ERBOpenTagNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.children.iter().map(|n| n as &dyn Node));
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.children {
      all_errors.extend(child.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBOpenTagNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_name".white(), format_token_value(&self.tag_name)));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "children".white(), format_array_value(&self.children, &"    ".white().to_string())));

    output
  }
}

impl ERBNode for ERBOpenTagNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct HTMLTextNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub content: String,
}

impl Node for HTMLTextNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    Vec::new()
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    self.errors.iter().map(|e| e as &dyn ErrorNode).collect()
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "HTMLTextNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "content".white(), format_string_value(&self.content)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct HTMLCommentNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub comment_start: Option<Token>,
  pub children: Vec<AnyNode>,
  pub comment_end: Option<Token>,
}

impl Node for HTMLCommentNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.children.iter().map(|n| n as &dyn Node));
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.children {
      all_errors.extend(child.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "HTMLCommentNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "comment_start".white(), format_token_value(&self.comment_start)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "children".white(), format_array_value(&self.children, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "comment_end".white(), format_token_value(&self.comment_end)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct HTMLDoctypeNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub children: Vec<AnyNode>,
  pub tag_closing: Option<Token>,
}

impl Node for HTMLDoctypeNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.children.iter().map(|n| n as &dyn Node));
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.children {
      all_errors.extend(child.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "HTMLDoctypeNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "children".white(), format_array_value(&self.children, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct XMLDeclarationNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub children: Vec<AnyNode>,
  pub tag_closing: Option<Token>,
}

impl Node for XMLDeclarationNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.children.iter().map(|n| n as &dyn Node));
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.children {
      all_errors.extend(child.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "XMLDeclarationNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "children".white(), format_array_value(&self.children, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct CDATANode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub children: Vec<AnyNode>,
  pub tag_closing: Option<Token>,
}

impl Node for CDATANode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.children.iter().map(|n| n as &dyn Node));
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.children {
      all_errors.extend(child.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "CDATANode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "children".white(), format_array_value(&self.children, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct WhitespaceNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub value: Option<Token>,
}

impl Node for WhitespaceNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    Vec::new()
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    self.errors.iter().map(|e| e as &dyn ErrorNode).collect()
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "WhitespaceNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "value".white(), format_token_value(&self.value)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct ERBContentNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  // no-op for analyzed_ruby
  pub parsed: bool,
  pub valid: bool,
  pub prism_node: Option<String>,
}

impl Node for ERBContentNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    Vec::new()
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    self.errors.iter().map(|e| e as &dyn ErrorNode).collect()
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBContentNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "parsed".white(), format_bool_value(self.parsed)));
    { let is_last = self.prism_node.is_none(); let symbol = if is_last { "└── " } else { "├── " };
    output.push_str(&format!("{}{}: {}\n", symbol.white(), "valid".white(), format_bool_value(self.valid)));
    }
    if let Some(ref prism_string) = self.prism_node {
      let prefix = "    ";
      output.push_str(&format!("{}{}: \n", "└── ".white(), "prism_node".white()));
      output.push_str(&format!("{}└── ", prefix));
      let prettified = prettify_prism_tree(prism_string);
      let indented = prettified.lines().enumerate().map(|(i, line)| {
        if i == 0 { line.to_string() } else { format!("{}    {}", prefix, line) }
      }).collect::<Vec<_>>().join("\n");
      output.push_str(&indented);
      output.push('\n');
    }

    output
  }
}

impl ERBNode for ERBContentNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct ERBEndNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
}

impl Node for ERBEndNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    Vec::new()
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    self.errors.iter().map(|e| e as &dyn ErrorNode).collect()
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBEndNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));

    output
  }
}

impl ERBNode for ERBEndNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct ERBElseNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  pub statements: Vec<AnyNode>,
}

impl Node for ERBElseNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.statements.iter().map(|n| n as &dyn Node));
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.statements {
      all_errors.extend(child.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBElseNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "statements".white(), format_array_value(&self.statements, &"    ".white().to_string())));

    output
  }
}

impl ERBNode for ERBElseNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct ERBIfNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  pub then_keyword: Option<Location>,
  pub prism_node: Option<String>,
  pub statements: Vec<AnyNode>,
  pub subsequent: Option<ERBElseNodeOrERBIfNode>,
  pub end_node: Option<Box<ERBEndNode>>,
}

impl Node for ERBIfNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.statements.iter().map(|n| n as &dyn Node));
    if let Some(ref node) = self.subsequent {
      children.push(node.as_node());
    }
    if let Some(ref node) = self.end_node {
      children.push(node.as_ref() as &dyn Node);
    }
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.statements {
      all_errors.extend(child.recursive_errors());
    }
    if let Some(ref node) = self.subsequent {
      all_errors.extend(node.recursive_errors());
    }
    if let Some(ref node) = self.end_node {
      all_errors.extend(node.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBIfNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "then_keyword".white(), self.then_keyword.as_ref().map(|l| format!("(location: {})", l).dimmed().to_string()).unwrap_or_else(|| "∅".magenta().to_string())));
    if let Some(ref prism_string) = self.prism_node {
      let prefix = "│   ";
      output.push_str(&format!("{}{}: \n", "├── ".white(), "prism_node".white()));
      output.push_str(&format!("{}└── ", prefix));
      let prettified = prettify_prism_tree(prism_string);
      let indented = prettified.lines().enumerate().map(|(i, line)| {
        if i == 0 { line.to_string() } else { format!("{}    {}", prefix, line) }
      }).collect::<Vec<_>>().join("\n");
      output.push_str(&indented);
      output.push('\n');
    }
    output.push_str(&format!("{}{}: {}", "├── ".white(), "statements".white(), format_array_value(&self.statements, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "subsequent".white(), format_union_node_value(&self.subsequent, &"│   ".white().to_string(), true, |n: &ERBElseNodeOrERBIfNode| n.tree_inspect())));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "end_node".white(), format_node_value(&self.end_node, &"    ".white().to_string(), false)));

    output
  }
}

impl ERBNode for ERBIfNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct ERBBlockNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  pub prism_node: Option<String>,
  pub body: Vec<AnyNode>,
  pub rescue_clause: Option<Box<ERBRescueNode>>,
  pub else_clause: Option<Box<ERBElseNode>>,
  pub ensure_clause: Option<Box<ERBEnsureNode>>,
  pub end_node: Option<Box<ERBEndNode>>,
}

impl Node for ERBBlockNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.body.iter().map(|n| n as &dyn Node));
    if let Some(ref node) = self.rescue_clause {
      children.push(node.as_ref() as &dyn Node);
    }
    if let Some(ref node) = self.else_clause {
      children.push(node.as_ref() as &dyn Node);
    }
    if let Some(ref node) = self.ensure_clause {
      children.push(node.as_ref() as &dyn Node);
    }
    if let Some(ref node) = self.end_node {
      children.push(node.as_ref() as &dyn Node);
    }
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.body {
      all_errors.extend(child.recursive_errors());
    }
    if let Some(ref node) = self.rescue_clause {
      all_errors.extend(node.recursive_errors());
    }
    if let Some(ref node) = self.else_clause {
      all_errors.extend(node.recursive_errors());
    }
    if let Some(ref node) = self.ensure_clause {
      all_errors.extend(node.recursive_errors());
    }
    if let Some(ref node) = self.end_node {
      all_errors.extend(node.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBBlockNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    if let Some(ref prism_string) = self.prism_node {
      let prefix = "│   ";
      output.push_str(&format!("{}{}: \n", "├── ".white(), "prism_node".white()));
      output.push_str(&format!("{}└── ", prefix));
      let prettified = prettify_prism_tree(prism_string);
      let indented = prettified.lines().enumerate().map(|(i, line)| {
        if i == 0 { line.to_string() } else { format!("{}    {}", prefix, line) }
      }).collect::<Vec<_>>().join("\n");
      output.push_str(&indented);
      output.push('\n');
    }
    output.push_str(&format!("{}{}: {}", "├── ".white(), "body".white(), format_array_value(&self.body, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "rescue_clause".white(), format_node_value(&self.rescue_clause, &"│   ".white().to_string(), true)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "else_clause".white(), format_node_value(&self.else_clause, &"│   ".white().to_string(), true)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "ensure_clause".white(), format_node_value(&self.ensure_clause, &"│   ".white().to_string(), true)));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "end_node".white(), format_node_value(&self.end_node, &"    ".white().to_string(), false)));

    output
  }
}

impl ERBNode for ERBBlockNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct ERBWhenNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  pub then_keyword: Option<Location>,
  pub statements: Vec<AnyNode>,
}

impl Node for ERBWhenNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.statements.iter().map(|n| n as &dyn Node));
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.statements {
      all_errors.extend(child.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBWhenNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "then_keyword".white(), self.then_keyword.as_ref().map(|l| format!("(location: {})", l).dimmed().to_string()).unwrap_or_else(|| "∅".magenta().to_string())));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "statements".white(), format_array_value(&self.statements, &"    ".white().to_string())));

    output
  }
}

impl ERBNode for ERBWhenNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct ERBCaseNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  pub children: Vec<AnyNode>,
  pub prism_node: Option<String>,
  pub conditions: Vec<AnyNode>,
  pub else_clause: Option<Box<ERBElseNode>>,
  pub end_node: Option<Box<ERBEndNode>>,
}

impl Node for ERBCaseNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.children.iter().map(|n| n as &dyn Node));
    children.extend(self.conditions.iter().map(|n| n as &dyn Node));
    if let Some(ref node) = self.else_clause {
      children.push(node.as_ref() as &dyn Node);
    }
    if let Some(ref node) = self.end_node {
      children.push(node.as_ref() as &dyn Node);
    }
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.children {
      all_errors.extend(child.recursive_errors());
    }
    for child in &self.conditions {
      all_errors.extend(child.recursive_errors());
    }
    if let Some(ref node) = self.else_clause {
      all_errors.extend(node.recursive_errors());
    }
    if let Some(ref node) = self.end_node {
      all_errors.extend(node.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBCaseNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "children".white(), format_array_value(&self.children, &"│   ".white().to_string())));
    if let Some(ref prism_string) = self.prism_node {
      let prefix = "│   ";
      output.push_str(&format!("{}{}: \n", "├── ".white(), "prism_node".white()));
      output.push_str(&format!("{}└── ", prefix));
      let prettified = prettify_prism_tree(prism_string);
      let indented = prettified.lines().enumerate().map(|(i, line)| {
        if i == 0 { line.to_string() } else { format!("{}    {}", prefix, line) }
      }).collect::<Vec<_>>().join("\n");
      output.push_str(&indented);
      output.push('\n');
    }
    output.push_str(&format!("{}{}: {}", "├── ".white(), "conditions".white(), format_array_value(&self.conditions, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "else_clause".white(), format_node_value(&self.else_clause, &"│   ".white().to_string(), true)));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "end_node".white(), format_node_value(&self.end_node, &"    ".white().to_string(), false)));

    output
  }
}

impl ERBNode for ERBCaseNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct ERBCaseMatchNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  pub children: Vec<AnyNode>,
  pub prism_node: Option<String>,
  pub conditions: Vec<AnyNode>,
  pub else_clause: Option<Box<ERBElseNode>>,
  pub end_node: Option<Box<ERBEndNode>>,
}

impl Node for ERBCaseMatchNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.children.iter().map(|n| n as &dyn Node));
    children.extend(self.conditions.iter().map(|n| n as &dyn Node));
    if let Some(ref node) = self.else_clause {
      children.push(node.as_ref() as &dyn Node);
    }
    if let Some(ref node) = self.end_node {
      children.push(node.as_ref() as &dyn Node);
    }
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.children {
      all_errors.extend(child.recursive_errors());
    }
    for child in &self.conditions {
      all_errors.extend(child.recursive_errors());
    }
    if let Some(ref node) = self.else_clause {
      all_errors.extend(node.recursive_errors());
    }
    if let Some(ref node) = self.end_node {
      all_errors.extend(node.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBCaseMatchNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "children".white(), format_array_value(&self.children, &"│   ".white().to_string())));
    if let Some(ref prism_string) = self.prism_node {
      let prefix = "│   ";
      output.push_str(&format!("{}{}: \n", "├── ".white(), "prism_node".white()));
      output.push_str(&format!("{}└── ", prefix));
      let prettified = prettify_prism_tree(prism_string);
      let indented = prettified.lines().enumerate().map(|(i, line)| {
        if i == 0 { line.to_string() } else { format!("{}    {}", prefix, line) }
      }).collect::<Vec<_>>().join("\n");
      output.push_str(&indented);
      output.push('\n');
    }
    output.push_str(&format!("{}{}: {}", "├── ".white(), "conditions".white(), format_array_value(&self.conditions, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "else_clause".white(), format_node_value(&self.else_clause, &"│   ".white().to_string(), true)));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "end_node".white(), format_node_value(&self.end_node, &"    ".white().to_string(), false)));

    output
  }
}

impl ERBNode for ERBCaseMatchNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct ERBWhileNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  pub prism_node: Option<String>,
  pub statements: Vec<AnyNode>,
  pub end_node: Option<Box<ERBEndNode>>,
}

impl Node for ERBWhileNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.statements.iter().map(|n| n as &dyn Node));
    if let Some(ref node) = self.end_node {
      children.push(node.as_ref() as &dyn Node);
    }
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.statements {
      all_errors.extend(child.recursive_errors());
    }
    if let Some(ref node) = self.end_node {
      all_errors.extend(node.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBWhileNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    if let Some(ref prism_string) = self.prism_node {
      let prefix = "│   ";
      output.push_str(&format!("{}{}: \n", "├── ".white(), "prism_node".white()));
      output.push_str(&format!("{}└── ", prefix));
      let prettified = prettify_prism_tree(prism_string);
      let indented = prettified.lines().enumerate().map(|(i, line)| {
        if i == 0 { line.to_string() } else { format!("{}    {}", prefix, line) }
      }).collect::<Vec<_>>().join("\n");
      output.push_str(&indented);
      output.push('\n');
    }
    output.push_str(&format!("{}{}: {}", "├── ".white(), "statements".white(), format_array_value(&self.statements, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "end_node".white(), format_node_value(&self.end_node, &"    ".white().to_string(), false)));

    output
  }
}

impl ERBNode for ERBWhileNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct ERBUntilNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  pub prism_node: Option<String>,
  pub statements: Vec<AnyNode>,
  pub end_node: Option<Box<ERBEndNode>>,
}

impl Node for ERBUntilNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.statements.iter().map(|n| n as &dyn Node));
    if let Some(ref node) = self.end_node {
      children.push(node.as_ref() as &dyn Node);
    }
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.statements {
      all_errors.extend(child.recursive_errors());
    }
    if let Some(ref node) = self.end_node {
      all_errors.extend(node.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBUntilNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    if let Some(ref prism_string) = self.prism_node {
      let prefix = "│   ";
      output.push_str(&format!("{}{}: \n", "├── ".white(), "prism_node".white()));
      output.push_str(&format!("{}└── ", prefix));
      let prettified = prettify_prism_tree(prism_string);
      let indented = prettified.lines().enumerate().map(|(i, line)| {
        if i == 0 { line.to_string() } else { format!("{}    {}", prefix, line) }
      }).collect::<Vec<_>>().join("\n");
      output.push_str(&indented);
      output.push('\n');
    }
    output.push_str(&format!("{}{}: {}", "├── ".white(), "statements".white(), format_array_value(&self.statements, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "end_node".white(), format_node_value(&self.end_node, &"    ".white().to_string(), false)));

    output
  }
}

impl ERBNode for ERBUntilNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct ERBForNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  pub prism_node: Option<String>,
  pub statements: Vec<AnyNode>,
  pub end_node: Option<Box<ERBEndNode>>,
}

impl Node for ERBForNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.statements.iter().map(|n| n as &dyn Node));
    if let Some(ref node) = self.end_node {
      children.push(node.as_ref() as &dyn Node);
    }
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.statements {
      all_errors.extend(child.recursive_errors());
    }
    if let Some(ref node) = self.end_node {
      all_errors.extend(node.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBForNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    if let Some(ref prism_string) = self.prism_node {
      let prefix = "│   ";
      output.push_str(&format!("{}{}: \n", "├── ".white(), "prism_node".white()));
      output.push_str(&format!("{}└── ", prefix));
      let prettified = prettify_prism_tree(prism_string);
      let indented = prettified.lines().enumerate().map(|(i, line)| {
        if i == 0 { line.to_string() } else { format!("{}    {}", prefix, line) }
      }).collect::<Vec<_>>().join("\n");
      output.push_str(&indented);
      output.push('\n');
    }
    output.push_str(&format!("{}{}: {}", "├── ".white(), "statements".white(), format_array_value(&self.statements, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "end_node".white(), format_node_value(&self.end_node, &"    ".white().to_string(), false)));

    output
  }
}

impl ERBNode for ERBForNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct ERBRescueNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  pub statements: Vec<AnyNode>,
  pub subsequent: Option<Box<ERBRescueNode>>,
}

impl Node for ERBRescueNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.statements.iter().map(|n| n as &dyn Node));
    if let Some(ref node) = self.subsequent {
      children.push(node.as_ref() as &dyn Node);
    }
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.statements {
      all_errors.extend(child.recursive_errors());
    }
    if let Some(ref node) = self.subsequent {
      all_errors.extend(node.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBRescueNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "statements".white(), format_array_value(&self.statements, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "subsequent".white(), format_node_value(&self.subsequent, &"    ".white().to_string(), false)));

    output
  }
}

impl ERBNode for ERBRescueNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct ERBEnsureNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  pub statements: Vec<AnyNode>,
}

impl Node for ERBEnsureNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.statements.iter().map(|n| n as &dyn Node));
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.statements {
      all_errors.extend(child.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBEnsureNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "statements".white(), format_array_value(&self.statements, &"    ".white().to_string())));

    output
  }
}

impl ERBNode for ERBEnsureNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct ERBBeginNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  pub prism_node: Option<String>,
  pub statements: Vec<AnyNode>,
  pub rescue_clause: Option<Box<ERBRescueNode>>,
  pub else_clause: Option<Box<ERBElseNode>>,
  pub ensure_clause: Option<Box<ERBEnsureNode>>,
  pub end_node: Option<Box<ERBEndNode>>,
}

impl Node for ERBBeginNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.statements.iter().map(|n| n as &dyn Node));
    if let Some(ref node) = self.rescue_clause {
      children.push(node.as_ref() as &dyn Node);
    }
    if let Some(ref node) = self.else_clause {
      children.push(node.as_ref() as &dyn Node);
    }
    if let Some(ref node) = self.ensure_clause {
      children.push(node.as_ref() as &dyn Node);
    }
    if let Some(ref node) = self.end_node {
      children.push(node.as_ref() as &dyn Node);
    }
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.statements {
      all_errors.extend(child.recursive_errors());
    }
    if let Some(ref node) = self.rescue_clause {
      all_errors.extend(node.recursive_errors());
    }
    if let Some(ref node) = self.else_clause {
      all_errors.extend(node.recursive_errors());
    }
    if let Some(ref node) = self.ensure_clause {
      all_errors.extend(node.recursive_errors());
    }
    if let Some(ref node) = self.end_node {
      all_errors.extend(node.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBBeginNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    if let Some(ref prism_string) = self.prism_node {
      let prefix = "│   ";
      output.push_str(&format!("{}{}: \n", "├── ".white(), "prism_node".white()));
      output.push_str(&format!("{}└── ", prefix));
      let prettified = prettify_prism_tree(prism_string);
      let indented = prettified.lines().enumerate().map(|(i, line)| {
        if i == 0 { line.to_string() } else { format!("{}    {}", prefix, line) }
      }).collect::<Vec<_>>().join("\n");
      output.push_str(&indented);
      output.push('\n');
    }
    output.push_str(&format!("{}{}: {}", "├── ".white(), "statements".white(), format_array_value(&self.statements, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "rescue_clause".white(), format_node_value(&self.rescue_clause, &"│   ".white().to_string(), true)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "else_clause".white(), format_node_value(&self.else_clause, &"│   ".white().to_string(), true)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "ensure_clause".white(), format_node_value(&self.ensure_clause, &"│   ".white().to_string(), true)));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "end_node".white(), format_node_value(&self.end_node, &"    ".white().to_string(), false)));

    output
  }
}

impl ERBNode for ERBBeginNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct ERBUnlessNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  pub then_keyword: Option<Location>,
  pub prism_node: Option<String>,
  pub statements: Vec<AnyNode>,
  pub else_clause: Option<Box<ERBElseNode>>,
  pub end_node: Option<Box<ERBEndNode>>,
}

impl Node for ERBUnlessNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.statements.iter().map(|n| n as &dyn Node));
    if let Some(ref node) = self.else_clause {
      children.push(node.as_ref() as &dyn Node);
    }
    if let Some(ref node) = self.end_node {
      children.push(node.as_ref() as &dyn Node);
    }
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.statements {
      all_errors.extend(child.recursive_errors());
    }
    if let Some(ref node) = self.else_clause {
      all_errors.extend(node.recursive_errors());
    }
    if let Some(ref node) = self.end_node {
      all_errors.extend(node.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBUnlessNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "then_keyword".white(), self.then_keyword.as_ref().map(|l| format!("(location: {})", l).dimmed().to_string()).unwrap_or_else(|| "∅".magenta().to_string())));
    if let Some(ref prism_string) = self.prism_node {
      let prefix = "│   ";
      output.push_str(&format!("{}{}: \n", "├── ".white(), "prism_node".white()));
      output.push_str(&format!("{}└── ", prefix));
      let prettified = prettify_prism_tree(prism_string);
      let indented = prettified.lines().enumerate().map(|(i, line)| {
        if i == 0 { line.to_string() } else { format!("{}    {}", prefix, line) }
      }).collect::<Vec<_>>().join("\n");
      output.push_str(&indented);
      output.push('\n');
    }
    output.push_str(&format!("{}{}: {}", "├── ".white(), "statements".white(), format_array_value(&self.statements, &"│   ".white().to_string())));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "else_clause".white(), format_node_value(&self.else_clause, &"│   ".white().to_string(), true)));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "end_node".white(), format_node_value(&self.end_node, &"    ".white().to_string(), false)));

    output
  }
}

impl ERBNode for ERBUnlessNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct RubyRenderLocalNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub name: Option<Token>,
  pub value: Option<Box<RubyLiteralNode>>,
}

impl Node for RubyRenderLocalNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    if let Some(ref node) = self.value {
      children.push(node.as_ref() as &dyn Node);
    }
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    if let Some(ref node) = self.value {
      all_errors.extend(node.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "RubyRenderLocalNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "name".white(), format_token_value(&self.name)));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "value".white(), format_node_value(&self.value, &"    ".white().to_string(), false)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct ERBRenderNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  // no-op for analyzed_ruby
  pub prism_node: Option<String>,
  pub partial: Option<Token>,
  pub template_path: Option<Token>,
  pub layout: Option<Token>,
  pub file: Option<Token>,
  pub inline_template: Option<Token>,
  pub body: Option<Token>,
  pub plain: Option<Token>,
  pub html: Option<Token>,
  pub renderable: Option<Token>,
  pub collection: Option<Token>,
  pub object: Option<Token>,
  pub as_name: Option<Token>,
  pub spacer_template: Option<Token>,
  pub formats: Option<Token>,
  pub variants: Option<Token>,
  pub handlers: Option<Token>,
  pub content_type: Option<Token>,
  pub locals: Vec<AnyNode>,
}

impl Node for ERBRenderNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.locals.iter().map(|n| n as &dyn Node));
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.locals {
      all_errors.extend(child.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBRenderNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    if let Some(ref prism_string) = self.prism_node {
      let prefix = "│   ";
      output.push_str(&format!("{}{}: \n", "├── ".white(), "prism_node".white()));
      output.push_str(&format!("{}└── ", prefix));
      let prettified = prettify_prism_tree(prism_string);
      let indented = prettified.lines().enumerate().map(|(i, line)| {
        if i == 0 { line.to_string() } else { format!("{}    {}", prefix, line) }
      }).collect::<Vec<_>>().join("\n");
      output.push_str(&indented);
      output.push('\n');
    }
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "partial".white(), format_token_value(&self.partial)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "template_path".white(), format_token_value(&self.template_path)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "layout".white(), format_token_value(&self.layout)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "file".white(), format_token_value(&self.file)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "inline_template".white(), format_token_value(&self.inline_template)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "body".white(), format_token_value(&self.body)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "plain".white(), format_token_value(&self.plain)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "html".white(), format_token_value(&self.html)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "renderable".white(), format_token_value(&self.renderable)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "collection".white(), format_token_value(&self.collection)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "object".white(), format_token_value(&self.object)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "as_name".white(), format_token_value(&self.as_name)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "spacer_template".white(), format_token_value(&self.spacer_template)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "formats".white(), format_token_value(&self.formats)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "variants".white(), format_token_value(&self.variants)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "handlers".white(), format_token_value(&self.handlers)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content_type".white(), format_token_value(&self.content_type)));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "locals".white(), format_array_value(&self.locals, &"    ".white().to_string())));

    output
  }
}

impl ERBNode for ERBRenderNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct RubyStrictLocalNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub name: Option<Token>,
  pub default_value: Option<Box<RubyLiteralNode>>,
  pub required: bool,
  pub double_splat: bool,
}

impl Node for RubyStrictLocalNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    if let Some(ref node) = self.default_value {
      children.push(node.as_ref() as &dyn Node);
    }
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    if let Some(ref node) = self.default_value {
      all_errors.extend(node.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "RubyStrictLocalNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "name".white(), format_token_value(&self.name)));
    output.push_str(&format!("{}{}: {}", "├── ".white(), "default_value".white(), format_node_value(&self.default_value, &"│   ".white().to_string(), true)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "required".white(), format_bool_value(self.required)));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "double_splat".white(), format_bool_value(self.double_splat)));

    output
  }
}

#[derive(Debug, Clone)]
pub struct ERBStrictLocalsNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  // no-op for analyzed_ruby
  pub prism_node: Option<String>,
  pub locals: Vec<AnyNode>,
}

impl Node for ERBStrictLocalsNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.locals.iter().map(|n| n as &dyn Node));
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.locals {
      all_errors.extend(child.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBStrictLocalsNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    if let Some(ref prism_string) = self.prism_node {
      let prefix = "│   ";
      output.push_str(&format!("{}{}: \n", "├── ".white(), "prism_node".white()));
      output.push_str(&format!("{}└── ", prefix));
      let prettified = prettify_prism_tree(prism_string);
      let indented = prettified.lines().enumerate().map(|(i, line)| {
        if i == 0 { line.to_string() } else { format!("{}    {}", prefix, line) }
      }).collect::<Vec<_>>().join("\n");
      output.push_str(&indented);
      output.push('\n');
    }
    output.push_str(&format!("{}{}: {}", "└── ".white(), "locals".white(), format_array_value(&self.locals, &"    ".white().to_string())));

    output
  }
}

impl ERBNode for ERBStrictLocalsNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct ERBYieldNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
}

impl Node for ERBYieldNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    Vec::new()
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    self.errors.iter().map(|e| e as &dyn ErrorNode).collect()
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBYieldNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "└── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));

    output
  }
}

impl ERBNode for ERBYieldNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

#[derive(Debug, Clone)]
pub struct ERBInNode {
  pub node_type: String,
  pub location: Location,
  pub errors: Vec<AnyError>,
  pub tag_opening: Option<Token>,
  pub content: Option<Token>,
  pub tag_closing: Option<Token>,
  pub then_keyword: Option<Location>,
  pub statements: Vec<AnyNode>,
}

impl Node for ERBInNode {
  fn node_type(&self) -> &str {
    &self.node_type
  }

  fn location(&self) -> &Location {
    &self.location
  }

  fn errors(&self) -> &[AnyError] {
    &self.errors
  }

  fn child_nodes(&self) -> Vec<&dyn Node> {
    let mut children: Vec<&dyn Node> = Vec::new();
    children.extend(self.statements.iter().map(|n| n as &dyn Node));
    children
  }

  fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
    let mut all_errors: Vec<&dyn ErrorNode> = Vec::new();
    all_errors.extend(self.errors.iter().map(|e| e as &dyn ErrorNode));

    for child in &self.statements {
      all_errors.extend(child.recursive_errors());
    }

    all_errors
  }

  fn tree_inspect(&self) -> String {
    let mut output = String::new();

    output.push_str(&format!("{} {} {}\n",
      "@".white(),
      "ERBInNode".yellow().bold(),
      format!("(location: {})", self.location).dimmed()
    ));
    output.push_str(&format_errors_field(&self.errors, &"│   ".white().to_string()));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_opening".white(), format_token_value(&self.tag_opening)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "content".white(), format_token_value(&self.content)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "tag_closing".white(), format_token_value(&self.tag_closing)));
    output.push_str(&format!("{}{}: {}\n", "├── ".white(), "then_keyword".white(), self.then_keyword.as_ref().map(|l| format!("(location: {})", l).dimmed().to_string()).unwrap_or_else(|| "∅".magenta().to_string())));
    output.push_str(&format!("{}{}: {}", "└── ".white(), "statements".white(), format_array_value(&self.statements, &"    ".white().to_string())));

    output
  }
}

impl ERBNode for ERBInNode {
  fn tag_opening(&self) -> &Option<Token> {
    &self.tag_opening
  }

  fn content(&self) -> &Option<Token> {
    &self.content
  }

  fn tag_closing(&self) -> &Option<Token> {
    &self.tag_closing
  }

  fn location(&self) -> &Location {
    &self.location
  }
}

