use rowan::ast::AstNode; use rustc_hash::FxHashMap; use syntax::latex::{self, HasCurly}; #[derive(Debug, Clone, Default)] pub struct Semantics { pub label_numbers: FxHashMap, pub section_numbers: FxHashMap, } impl Semantics { pub fn process_root(&mut self, root: &latex::SyntaxNode) { for node in root.descendants() { self.process_node(&node); } } fn process_node(&mut self, node: &latex::SyntaxNode) { if let Some(label_number) = latex::LabelNumber::cast(node.clone()) { self.process_label_number(&label_number); } if let Some(toc_line) = latex::TocContentsLine::cast(node.clone()) { self.process_toc_line(&toc_line); } } fn process_label_number(&mut self, label_number: &latex::LabelNumber) -> Option<()> { let name = label_number .name() .and_then(|group| group.key()) .map(|key| key.to_string())?; let group = label_number.text()?; let group = group .syntax() .children() .filter_map(latex::CurlyGroup::cast) .find_map(|group| { latex::Text::cast(group.syntax().first_child()?)?; Some(group) })?; let text = group.content_text()?.replace(['{', '}'], ""); self.label_numbers.insert(name, text); Some(()) } fn process_toc_line(&mut self, toc_line: &latex::TocContentsLine) -> Option<()> { let unit = toc_line.unit().and_then(|u| u.content_text())?.to_string(); if [ "part", "chapter", "section", "subsection", "subsubsection", "paragraph", "subparagraph", ] .contains(&unit.as_str()) { let line = toc_line.line()?; let name = line.syntax().children().find_map(|child| { latex::Text::cast(child.clone())?; Some(child) })?; let name = name.to_string(); let num_line = line .syntax() .children() .find_map(|child| latex::TocNumberLine::cast(child.clone()))?; let number = num_line.number()?; let number = number.content_text()?.replace(['{', '}'], ""); self.section_numbers.insert(name, number); } Some(()) } }