use super::ast::*; use crate::range::RangeExt; use crate::syntax::language::*; use crate::syntax::text::{CharStream, SyntaxNode}; use itertools::Itertools; use lsp_types::Range; use std::sync::Arc; #[derive(Debug, PartialEq, Eq, Clone)] pub struct LatexSection { pub command: Arc, pub index: usize, pub level: i32, pub prefix: &'static str, } impl LatexSection { fn parse(commands: &[Arc]) -> Vec { let mut sections = Vec::new(); for command in commands { for LatexSectionCommand { name, index, level, prefix, } in &LANGUAGE_DATA.section_commands { if command.name.text() == name && command.args.len() > *index { sections.push(Self { command: Arc::clone(command), index: *index, level: *level, prefix: prefix.as_ref(), }) } } } sections } pub fn extract_text(&self, text: &str) -> Option { let content = &self.command.args[self.index]; let right = content.right.as_ref()?; let range = Range::new_simple( content.left.start().line, content.left.start().character + 1, right.end().line, right.end().character - 1, ); Some(CharStream::extract(&text, range)) } } impl SyntaxNode for LatexSection { fn range(&self) -> Range { self.command.range() } } #[derive(Debug, PartialEq, Eq, Clone)] pub struct LatexLabel { pub command: Arc, index: usize, pub kind: LatexLabelKind, } impl LatexLabel { pub fn names(&self) -> Vec<&LatexToken> { self.command.extract_comma_separated_words(self.index) } fn parse(commands: &[Arc]) -> Vec { let mut labels = Vec::new(); for command in commands { for LatexLabelCommand { name, index, kind } in &LANGUAGE_DATA.label_commands { if command.name.text() == name && command.has_comma_separated_words(*index) { labels.push(Self { command: Arc::clone(command), index: *index, kind: *kind, }); } } } labels } } impl SyntaxNode for LatexLabel { fn range(&self) -> Range { self.command.range } } #[derive(Debug, PartialEq, Eq, Clone)] pub struct LatexLabelNumbering { pub command: Arc, pub number: String, } impl LatexLabelNumbering { pub fn name(&self) -> &LatexToken { self.command.extract_word(0).unwrap() } fn parse(commands: &[Arc]) -> Vec { commands .iter() .map(Arc::clone) .filter_map(Self::parse_single) .collect() } fn parse_single(command: Arc) -> Option { #[derive(Debug, Default)] struct FirstText { text: Option>, } impl LatexVisitor for FirstText { fn visit_root(&mut self, root: Arc) { LatexWalker::walk_root(self, root); } fn visit_group(&mut self, group: Arc) { LatexWalker::walk_group(self, group); } fn visit_command(&mut self, command: Arc) { LatexWalker::walk_command(self, command); } fn visit_text(&mut self, text: Arc) { if self.text.is_none() { self.text = Some(text); } } fn visit_comma(&mut self, comma: Arc) { LatexWalker::walk_comma(self, comma); } fn visit_math(&mut self, math: Arc) { LatexWalker::walk_math(self, math); } } if command.name.text() != "\\newlabel" || !command.has_word(0) { return None; } let mut analyzer = FirstText::default(); analyzer.visit_group(Arc::clone(command.args.get(1)?)); let number = analyzer .text? .words .iter() .map(|word| word.text()) .join(" "); Some(Self { command: Arc::clone(&command), number, }) } } #[derive(Debug, PartialEq, Eq, Clone)] pub struct LatexCaption { pub command: Arc, pub index: usize, } impl LatexCaption { fn parse(commands: &[Arc]) -> Vec { let mut captions = Vec::new(); for command in commands { if command.name.text() == "\\caption" && !command.args.is_empty() { captions.push(Self { command: Arc::clone(&command), index: 0, }); } } captions } } impl SyntaxNode for LatexCaption { fn range(&self) -> Range { self.command.range() } } #[derive(Debug, PartialEq, Eq, Clone)] pub struct LatexItem { pub command: Arc, } impl LatexItem { fn parse(commands: &[Arc]) -> Vec { let mut items = Vec::new(); for command in commands { if command.name.text() == "\\item" { items.push(Self { command: Arc::clone(&command), }); } } items } pub fn name(&self) -> Option { if let Some(options) = self.command.options.get(0) { if options.children.len() == 1 { if let LatexContent::Text(text) = &options.children[0] { return Some(text.words.iter().map(|word| word.text()).join(" ")); } } } None } } impl SyntaxNode for LatexItem { fn range(&self) -> Range { self.command.range() } } #[derive(Debug, PartialEq, Eq, Clone)] pub struct LatexStructureInfo { pub sections: Vec, pub labels: Vec, pub label_numberings: Vec, pub captions: Vec, pub items: Vec, } impl LatexStructureInfo { pub fn parse(commands: &[Arc]) -> Self { Self { sections: LatexSection::parse(commands), labels: LatexLabel::parse(commands), label_numberings: LatexLabelNumbering::parse(commands), captions: LatexCaption::parse(commands), items: LatexItem::parse(commands), } } }