use std::{borrow::Cow, fmt, io};
use crate::{FormatterResult, tree_sitter::SyntaxNode};
fn escape(input: &str) -> Cow<'_, str> {
let mut buffer = String::new();
let mut start: usize = 0;
let length = input.len();
let append = |buffer: &mut String, from: &mut usize, to: usize, suffix: &str| {
if buffer.is_empty() {
buffer.reserve(length * 3);
}
*buffer += &input[*from..to];
*buffer += suffix;
*from = to + 1;
};
for (idx, current) in input.chars().enumerate() {
match current {
'\n' => append(&mut buffer, &mut start, idx, r#"\\n"#),
'\t' => append(&mut buffer, &mut start, idx, r#"\\t"#),
otherwise => {
let mut escaped = otherwise.escape_default().peekable();
if escaped.peek() == Some(&'\\') {
append(
&mut buffer,
&mut start,
idx,
&otherwise.escape_default().to_string(),
);
}
}
}
}
if buffer.is_empty() {
input.into()
} else {
buffer += &input[start..length];
buffer.into()
}
}
impl fmt::Display for SyntaxNode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let shape = if self.is_named { "ellipse" } else { "box" };
writeln!(
f,
" {} [label=\"{}\", shape={shape}];",
self.id,
escape(&self.kind)
)?;
for child in &self.children {
writeln!(f, " {} -- {};", self.id, child.id)?;
write!(f, "{child}")?;
}
Ok(())
}
}
pub fn write(output: &mut dyn io::Write, root: &SyntaxNode) -> FormatterResult<()> {
writeln!(output, "graph {{")?;
write!(output, "{root}")?;
writeln!(output, "}}")?;
Ok(())
}
#[cfg(test)]
mod test {
use super::escape;
use std::borrow::Cow;
#[test]
fn double_escape() {
assert_eq!(escape("foo"), "foo");
assert_eq!(escape("'"), r#"\'"#);
assert_eq!(escape("\n"), r#"\\n"#);
assert_eq!(escape("\t"), r#"\\t"#);
assert_eq!(
escape("Here's something\nlonger"),
r#"Here\'s something\\nlonger"#
);
}
#[test]
fn escape_borrowed() {
match escape("foo") {
Cow::Borrowed("foo") => (),
_ => panic!("Expected a borrowed, unmodified str"),
}
}
#[test]
fn escape_owned() {
match escape("'") {
Cow::Owned(s) => assert_eq!(s, r#"\'"#),
_ => panic!("Expected an owned, escaped string"),
}
}
}