use crate::lang::{TokenClass, classify};
use tree_sitter::Node;
pub const ID: u16 = u16::MAX;
pub const STR: u16 = u16::MAX - 1;
pub const NUM: u16 = u16::MAX - 2;
pub struct Normalized {
pub codes: Vec<u16>,
pub sig_lines: u32,
}
pub fn normalize(node: Node) -> Normalized {
let mut codes: Vec<u16> = Vec::with_capacity(256);
let mut sig_lines = 0u32;
let mut last_line = u32::MAX;
visit_leaves(node, &mut |leaf| {
let class = classify(leaf.kind());
if class == TokenClass::Comment {
return;
}
let row = leaf.start_position().row as u32;
if row != last_line {
sig_lines += 1;
last_line = row;
}
let code = match class {
TokenClass::Ident => ID,
TokenClass::Str => STR,
TokenClass::Num => NUM,
TokenClass::Comment => unreachable!(),
TokenClass::Other => leaf.kind_id(),
};
let collapsible = code == STR || code == NUM;
if collapsible && codes.last() == Some(&code) {
return;
}
codes.push(code);
});
Normalized { codes, sig_lines }
}
pub fn significant_lines(node: Node) -> u32 {
let mut count = 0u32;
let mut last_line = u32::MAX;
visit_leaves(node, &mut |leaf| {
if classify(leaf.kind()) == TokenClass::Comment {
return;
}
let row = leaf.start_position().row as u32;
if row != last_line {
count += 1;
last_line = row;
}
});
count
}
fn visit_leaves(node: Node, f: &mut impl FnMut(Node)) {
let mut cursor = node.walk();
'outer: loop {
while cursor.goto_first_child() {}
f(cursor.node());
loop {
if cursor.node() == node {
break 'outer;
}
if cursor.goto_next_sibling() {
continue 'outer;
}
if !cursor.goto_parent() {
break 'outer;
}
}
}
}