src/output/dump.rs (130 lines of code) (raw):
use std::io::Write;
use termcolor::{Color, ColorChoice, StandardStream, StandardStreamLock};
use crate::node::Node;
use crate::tools::{color, intense_color};
use crate::traits::*;
/// Dumps the `AST` of a code.
///
/// Returns a [`Result`] value, when an error occurs.
///
/// # Examples
///
/// ```
/// use std::path::PathBuf;
///
/// use rust_code_analysis::{dump_node, CppParser, ParserTrait};
///
/// let source_code = "int a = 42;";
///
/// // The path to a dummy file used to contain the source code
/// let path = PathBuf::from("foo.c");
/// let source_as_vec = source_code.as_bytes().to_vec();
///
/// // The parser of the code, in this case a CPP parser
/// let parser = CppParser::new(source_as_vec.clone(), &path, None);
///
/// // The root of the AST
/// let root = parser.get_root();
///
/// // Dump the AST from the first line of code in a file to the last one
/// dump_node(&source_as_vec, &root, -1, None, None).unwrap();
/// ```
///
/// [`Result`]: #variant.Result
pub fn dump_node(
code: &[u8],
node: &Node,
depth: i32,
line_start: Option<usize>,
line_end: Option<usize>,
) -> std::io::Result<()> {
let stdout = StandardStream::stdout(ColorChoice::Always);
let mut stdout = stdout.lock();
let ret = dump_tree_helper(
code,
node,
"",
true,
&mut stdout,
depth,
&line_start,
&line_end,
);
color(&mut stdout, Color::White)?;
ret
}
#[allow(clippy::too_many_arguments)]
fn dump_tree_helper(
code: &[u8],
node: &Node,
prefix: &str,
last: bool,
stdout: &mut StandardStreamLock,
depth: i32,
line_start: &Option<usize>,
line_end: &Option<usize>,
) -> std::io::Result<()> {
if depth == 0 {
return Ok(());
}
let (pref_child, pref) = if node.parent().is_none() {
("", "")
} else if last {
(" ", "╰─ ")
} else {
("│ ", "├─ ")
};
let node_row = node.start_row() + 1;
let mut display = true;
if let Some(line_start) = line_start {
display = node_row >= *line_start
}
if let Some(line_end) = line_end {
display = display && node_row <= *line_end
}
if display {
color(stdout, Color::Blue)?;
write!(stdout, "{prefix}{pref}")?;
intense_color(stdout, Color::Yellow)?;
write!(stdout, "{{{}:{}}} ", node.kind(), node.kind_id())?;
color(stdout, Color::White)?;
write!(stdout, "from ")?;
color(stdout, Color::Green)?;
let (pos_row, pos_column) = node.start_position();
write!(stdout, "({}, {}) ", pos_row + 1, pos_column + 1)?;
color(stdout, Color::White)?;
write!(stdout, "to ")?;
color(stdout, Color::Green)?;
let (pos_row, pos_column) = node.end_position();
write!(stdout, "({}, {}) ", pos_row + 1, pos_column + 1)?;
if node.start_row() == node.end_row() {
color(stdout, Color::White)?;
write!(stdout, ": ")?;
intense_color(stdout, Color::Red)?;
let code = &code[node.start_byte()..node.end_byte()];
if let Ok(code) = String::from_utf8(code.to_vec()) {
write!(stdout, "{code} ")?;
} else {
stdout.write_all(code).unwrap();
}
}
writeln!(stdout)?;
}
let count = node.child_count();
if count != 0 {
let prefix = format!("{prefix}{pref_child}");
let mut i = count;
let mut cursor = node.cursor();
cursor.goto_first_child();
loop {
i -= 1;
dump_tree_helper(
code,
&cursor.node(),
&prefix,
i == 0,
stdout,
depth - 1,
line_start,
line_end,
)?;
if !cursor.goto_next_sibling() {
break;
}
}
}
Ok(())
}
/// Configuration options for dumping the `AST` of a code.
#[derive(Debug)]
pub struct DumpCfg {
/// The first line of code to dump
///
/// If `None`, the code is dumped from the first line of code
/// in a file
pub line_start: Option<usize>,
/// The last line of code to dump
///
/// If `None`, the code is dumped until the last line of code
/// in a file
pub line_end: Option<usize>,
}
pub struct Dump {
_guard: (),
}
impl Callback for Dump {
type Res = std::io::Result<()>;
type Cfg = DumpCfg;
fn call<T: ParserTrait>(cfg: Self::Cfg, parser: &T) -> Self::Res {
dump_node(
parser.get_code(),
&parser.get_root(),
-1,
cfg.line_start,
cfg.line_end,
)
}
}