diff options
Diffstat (limited to 'support/texlab/src/syntax/latex/analysis.rs')
-rw-r--r-- | support/texlab/src/syntax/latex/analysis.rs | 943 |
1 files changed, 33 insertions, 910 deletions
diff --git a/support/texlab/src/syntax/latex/analysis.rs b/support/texlab/src/syntax/latex/analysis.rs index af8b756f31..063d8e95f3 100644 --- a/support/texlab/src/syntax/latex/analysis.rs +++ b/support/texlab/src/syntax/latex/analysis.rs @@ -1,913 +1,36 @@ -use crate::{ - protocol::{Options, Position, Range, RangeExt, Uri}, - syntax::{generic_ast::AstNodeIndex, lang_data::*, latex::ast::*, text::SyntaxNode}, - tex::Resolver, +mod command; +mod distro_file; +mod environment; +mod explicit_link; +mod implicit_link; +mod label_name; +mod label_number; +mod theorem; +mod types; + +use crate::syntax::latex; + +pub use self::types::*; +use self::{ + command::analyze_command, + environment::analyze_begin, + explicit_link::{analyze_import, analyze_include}, + implicit_link::analyze_implicit_links, + label_name::analyze_label_name, + label_number::analyze_label_number, + theorem::analyze_theorem_definition, }; -use itertools::{iproduct, Itertools}; -use serde::{Deserialize, Serialize}; -use std::{borrow::Cow, ops::Deref, path::Path}; -#[derive(Debug, Clone)] -pub struct SymbolTableParams<'a> { - pub tree: Tree, - pub uri: &'a Uri, - pub resolver: &'a Resolver, - pub options: &'a Options, - pub current_dir: &'a Path, -} - -#[derive(Debug, Clone)] -pub struct SymbolTable { - pub(crate) tree: Tree, - pub commands: Vec<AstNodeIndex>, - pub environments: Vec<Environment>, - pub is_standalone: bool, - pub includes: Vec<Include>, - pub imports: Vec<Import>, - pub components: Vec<String>, - pub citations: Vec<Citation>, - pub command_definitions: Vec<CommandDefinition>, - pub glossary_entries: Vec<GlossaryEntry>, - pub equations: Vec<Equation>, - pub inlines: Vec<Inline>, - pub math_operators: Vec<MathOperator>, - pub theorem_definitions: Vec<TheoremDefinition>, - pub sections: Vec<Section>, - pub labels: Vec<Label>, - pub label_numberings: Vec<LabelNumbering>, - pub captions: Vec<Caption>, - pub items: Vec<Item>, -} - -impl SymbolTable { - pub fn analyze(params: SymbolTableParams) -> Self { - let SymbolTableParams { - tree, - uri, - resolver, - options, - current_dir, - } = params; - - let commands: Vec<_> = tree.commands().collect(); - let ctx = SymbolContext { - tree: &tree, - commands: &commands, - uri, - resolver, - options, - current_dir, - }; - - let mut environments = None; - let mut includes = None; - let mut imports = None; - let mut citations = None; - let mut command_definitions = None; - let mut glossary_entries = None; - let mut equations = None; - let mut inlines = None; - let mut math_operators = None; - let mut theorem_definitions = None; - let mut sections = None; - let mut labels = None; - let mut label_numberings = None; - let mut captions = None; - let mut items = None; - - rayon::scope(|s| { - s.spawn(|_| environments = Some(Environment::parse(ctx))); - s.spawn(|_| includes = Some(Include::parse(ctx))); - s.spawn(|_| imports = Some(Import::parse(ctx))); - s.spawn(|_| citations = Some(Citation::parse(ctx))); - s.spawn(|_| command_definitions = Some(CommandDefinition::parse(ctx))); - s.spawn(|_| glossary_entries = Some(GlossaryEntry::parse(ctx))); - s.spawn(|_| equations = Some(Equation::parse(ctx))); - s.spawn(|_| inlines = Some(Inline::parse(ctx))); - s.spawn(|_| math_operators = Some(MathOperator::parse(ctx))); - s.spawn(|_| theorem_definitions = Some(TheoremDefinition::parse(ctx))); - s.spawn(|_| sections = Some(Section::parse(ctx))); - s.spawn(|_| labels = Some(Label::parse(ctx))); - s.spawn(|_| label_numberings = Some(LabelNumbering::parse(ctx))); - s.spawn(|_| captions = Some(Caption::parse(ctx))); - s.spawn(|_| items = Some(Item::parse(ctx))); - }); - - let is_standalone = environments - .as_ref() - .unwrap() - .iter() - .any(|env| env.is_root(&tree)); - - let components = includes - .as_ref() - .unwrap() - .iter() - .flat_map(|include| include.components(&tree)) - .collect(); - - Self { - tree, - commands, - environments: environments.unwrap(), - is_standalone, - includes: includes.unwrap(), - imports: imports.unwrap(), - components, - citations: citations.unwrap(), - command_definitions: command_definitions.unwrap(), - glossary_entries: glossary_entries.unwrap(), - equations: equations.unwrap(), - inlines: inlines.unwrap(), - math_operators: math_operators.unwrap(), - theorem_definitions: theorem_definitions.unwrap(), - sections: sections.unwrap(), - labels: labels.unwrap(), - label_numberings: label_numberings.unwrap(), - captions: captions.unwrap(), - items: items.unwrap(), - } - } - - pub fn is_direct_child(&self, env: Environment, pos: Position) -> bool { - env.range(&self.tree).contains(pos) - && !self - .environments - .iter() - .filter(|e| e.left.parent != env.left.parent) - .filter(|e| env.range(&self.tree).contains(e.range(&self.tree).start)) - .any(|e| e.range(&self.tree).contains(pos)) - } - - pub fn is_enum_item(&self, enumeration: Environment, item: Item) -> bool { - let item_range = self.tree[item.parent].range(); - enumeration.range(&self.tree).contains(item_range.start) - && !self - .environments - .iter() - .filter(|env| env.left.parent != enumeration.left.parent) - .filter(|env| env.left.is_enum(&self.tree)) - .filter(|env| { - enumeration - .range(&self.tree) - .contains(env.range(&self.tree).start) - }) - .any(|env| env.range(&self.tree).contains(item_range.start)) - } - - pub fn find_label_by_range(&self, range: Range) -> Option<&Label> { - self.labels - .iter() - .filter(|label| label.kind == LatexLabelKind::Definition) - .filter(|label| label.names(&self).len() == 1) - .find(|label| range.contains(self[label.parent].range().start)) - } - - pub fn find_label_by_environment(&self, env: Environment) -> Option<&Label> { - self.labels - .iter() - .filter(|label| label.kind == LatexLabelKind::Definition) - .filter(|label| label.names(&self.tree).len() == 1) - .find(|label| self.is_direct_child(env, self.tree[label.parent].start())) - } -} - -impl Deref for SymbolTable { - type Target = Tree; - - fn deref(&self) -> &Self::Target { - &self.tree - } -} - -#[derive(Debug, Clone, Copy)] -pub struct SymbolContext<'a> { - tree: &'a Tree, - commands: &'a [AstNodeIndex], - uri: &'a Uri, - resolver: &'a Resolver, - options: &'a Options, - current_dir: &'a Path, -} - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct EnvironmentDelimiter { - pub parent: AstNodeIndex, -} - -impl EnvironmentDelimiter { - pub fn name(self, tree: &Tree) -> Option<&Token> { - tree.extract_word(self.parent, GroupKind::Group, 0) - } - - pub fn is_math(self, tree: &Tree) -> bool { - self.is_special(tree, LANGUAGE_DATA.math_environments.iter()) - } - - pub fn is_enum(self, tree: &Tree) -> bool { - self.is_special(tree, LANGUAGE_DATA.enum_environments.iter()) - } - - fn is_special<'a, I: Iterator<Item = &'a String>>(self, tree: &Tree, mut values: I) -> bool { - match self.name(tree) { - Some(name) => values.any(|env| env == name.text()), - None => false, - } - } -} - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct Environment { - pub left: EnvironmentDelimiter, - pub right: EnvironmentDelimiter, -} - -impl Environment { - pub fn is_root(self, tree: &Tree) -> bool { - self.left - .name(tree) - .iter() - .chain(self.right.name(tree).iter()) - .any(|name| name.text() == "document") - } - - pub fn range(self, tree: &Tree) -> Range { - let start = tree[self.left.parent].start(); - let end = tree[self.right.parent].end(); - Range::new(start, end) - } - - fn parse(ctx: SymbolContext) -> Vec<Self> { - let mut stack = Vec::new(); - let mut envs = Vec::new(); - for parent in ctx.commands { - if let Some((delim, delim_cmd)) = Self::parse_delimiter(ctx.tree, *parent) { - if delim_cmd.name.text() == "\\begin" { - stack.push(delim); - } else if let Some(left) = stack.pop() { - envs.push(Self { left, right: delim }); - } - } - } - envs - } - - fn parse_delimiter( - tree: &Tree, - parent: AstNodeIndex, - ) -> Option<(EnvironmentDelimiter, &Command)> { - let cmd = tree.as_command(parent)?; - if cmd.name.text() != "\\begin" && cmd.name.text() != "\\end" { - return None; - } - - let group = tree.extract_group(parent, GroupKind::Group, 0)?; - if tree.extract_word(parent, GroupKind::Group, 0).is_some() - || tree.children(group).next().is_none() - || tree.as_group(group)?.right.is_none() - { - Some((EnvironmentDelimiter { parent }, cmd)) - } else { - None - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Include { - pub parent: AstNodeIndex, - pub arg_index: usize, - pub kind: LatexIncludeKind, - pub all_targets: Vec<Vec<Uri>>, - pub include_extension: bool, -} - -impl Include { - pub fn paths<'a>(&self, tree: &'a Tree) -> Vec<&'a Token> { - tree.extract_comma_separated_words(self.parent, GroupKind::Group, self.arg_index) - .unwrap() - } - - pub fn components<'a>(&self, tree: &'a Tree) -> impl Iterator<Item = String> + 'a { - let kind = self.kind; - self.paths(tree) - .into_iter() - .filter_map(move |path| match kind { - LatexIncludeKind::Package => Some(format!("{}.sty", path.text())), - LatexIncludeKind::Class => Some(format!("{}.cls", path.text())), - LatexIncludeKind::Latex - | LatexIncludeKind::Bibliography - | LatexIncludeKind::Image - | LatexIncludeKind::Svg - | LatexIncludeKind::Pdf - | LatexIncludeKind::Everything => None, - }) - } - - fn parse(ctx: SymbolContext) -> Vec<Self> { - iproduct!(ctx.commands, LANGUAGE_DATA.include_commands.iter()) - .filter_map(|(parent, desc)| Self::parse_single(ctx, *parent, desc)) - .collect() - } - - fn parse_single( - ctx: SymbolContext, - parent: AstNodeIndex, - desc: &LatexIncludeCommand, - ) -> Option<Self> { - let cmd = ctx.tree.as_command(parent)?; - if cmd.name.text() != desc.name { - return None; - } - - let mut all_targets = Vec::new(); - let paths = ctx - .tree - .extract_comma_separated_words(parent, GroupKind::Group, desc.index)?; - for path in paths { - let mut targets = Vec::new(); - let base_url = base_url(ctx)?; - targets.push(base_url.join(path.text()).ok()?.into()); - - if let Some(extensions) = desc.kind.extensions() { - for extension in extensions { - let path = format!("{}.{}", path.text(), extension); - targets.push(base_url.join(&path).ok()?.into()); - } - } - - if let Some(target) = Self::resolve_distro_file(ctx, desc, path.text()) { - targets.push(target); - } - all_targets.push(targets); - } - - let include = Self { - parent, - arg_index: desc.index, - kind: desc.kind, - all_targets, - include_extension: desc.include_extension, - }; - Some(include) - } - - fn resolve_distro_file( - ctx: SymbolContext, - desc: &LatexIncludeCommand, - name: &str, - ) -> Option<Uri> { - let mut path = ctx.resolver.files_by_name.get(name); - if let Some(extensions) = desc.kind.extensions() { - for extension in extensions { - path = path.or_else(|| { - let full_name = format!("{}.{}", name, extension); - ctx.resolver.files_by_name.get(&full_name) - }); - } - } - path.and_then(|p| Uri::from_file_path(p).ok()) - } -} - -fn base_url(ctx: SymbolContext) -> Option<Uri> { - if let Some(root_directory) = ctx - .options - .latex - .as_ref() - .and_then(|opts| opts.root_directory.as_ref()) - { - let file_name = ctx.uri.path_segments()?.last()?; - let path = ctx.current_dir.join(root_directory).join(file_name); - Uri::from_file_path(path).ok() - } else { - Some(ctx.uri.clone()) - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Import { - pub parent: AstNodeIndex, - pub targets: Vec<Uri>, -} - -impl Import { - pub fn dir<'a>(&self, tree: &'a Tree) -> &'a Token { - tree.extract_word(self.parent, GroupKind::Group, 0).unwrap() - } - - pub fn file<'a>(&self, tree: &'a Tree) -> &'a Token { - tree.extract_word(self.parent, GroupKind::Group, 1).unwrap() - } - - fn parse(ctx: SymbolContext) -> Vec<Self> { - ctx.commands - .iter() - .filter_map(|parent| Self::parse_single(ctx, *parent)) - .collect() - } - - fn parse_single(ctx: SymbolContext, parent: AstNodeIndex) -> Option<Self> { - let cmd = ctx.tree.as_command(parent)?; - if cmd.name.text() != "\\import" && cmd.name.text() != "\\subimport" { - return None; - } - - let dir = ctx.tree.extract_word(parent, GroupKind::Group, 0)?; - let file = ctx.tree.extract_word(parent, GroupKind::Group, 1)?; - - let mut targets = Vec::new(); - let base_url = base_url(ctx)?.join(dir.text()).ok()?; - targets.push(base_url.join(file.text()).ok()?.into()); - targets.push(base_url.join(&format!("{}.tex", file.text())).ok()?.into()); - Some(Self { parent, targets }) - } -} - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct Citation { - parent: AstNodeIndex, - arg_index: usize, -} - -impl Citation { - pub fn keys(self, tree: &Tree) -> Vec<&Token> { - tree.extract_comma_separated_words(self.parent, GroupKind::Group, self.arg_index) - .unwrap() - } - - fn parse(ctx: SymbolContext) -> Vec<Self> { - iproduct!(ctx.commands, LANGUAGE_DATA.citation_commands.iter()) - .filter_map(|(parent, desc)| Self::parse_single(ctx, *parent, desc)) - .collect() - } - - fn parse_single( - ctx: SymbolContext, - parent: AstNodeIndex, - desc: &LatexCitationCommand, - ) -> Option<Self> { - let cmd = ctx.tree.as_command(parent)?; - if cmd.name.text() != desc.name { - return None; - } - - ctx.tree - .extract_comma_separated_words(parent, GroupKind::Group, desc.index)?; - - Some(Self { - parent, - arg_index: desc.index, - }) - } -} - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct CommandDefinition { - pub parent: AstNodeIndex, - pub definition: AstNodeIndex, - pub definition_index: usize, - pub implementation: AstNodeIndex, - pub implementation_index: usize, - pub arg_count_index: usize, -} - -impl CommandDefinition { - pub fn definition_name(self, tree: &Tree) -> &str { - tree.as_command(self.definition).unwrap().name.text() - } - - fn parse(ctx: SymbolContext) -> Vec<Self> { - let def = LANGUAGE_DATA.command_definition_commands.iter(); - iproduct!(ctx.commands, def) - .filter_map(|(parent, desc)| Self::parse_single(ctx, *parent, desc)) - .collect() - } - - fn parse_single( - ctx: SymbolContext, - parent: AstNodeIndex, - desc: &LatexCommandDefinitionCommand, - ) -> Option<Self> { - let cmd = ctx.tree.as_command(parent)?; - if cmd.name.text() != desc.name { - return None; - } - - let group_kind = GroupKind::Group; - let implementation = - ctx.tree - .extract_group(parent, group_kind, desc.implementation_index)?; - - let def_group = ctx - .tree - .extract_group(parent, group_kind, desc.definition_index)?; - - let mut def_children = ctx.tree.children(def_group); - let definition = def_children.next()?; - ctx.tree.as_command(definition)?; - Some(Self { - parent, - definition, - definition_index: desc.definition_index, - implementation, - implementation_index: desc.implementation_index, - arg_count_index: desc.arg_count_index, - }) - } -} - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct GlossaryEntry { - pub parent: AstNodeIndex, - pub label_index: usize, - pub kind: LatexGlossaryEntryKind, -} - -impl GlossaryEntry { - pub fn label(self, tree: &Tree) -> &Token { - tree.extract_word(self.parent, GroupKind::Group, self.label_index) - .unwrap() - } - - fn parse(ctx: SymbolContext) -> Vec<Self> { - let entry = LANGUAGE_DATA.glossary_entry_definition_commands.iter(); - iproduct!(ctx.commands, entry) - .filter_map(|(parent, desc)| Self::parse_single(ctx, *parent, desc)) - .collect() - } - - fn parse_single( - ctx: SymbolContext, - parent: AstNodeIndex, - desc: &LatexGlossaryEntryDefinitionCommand, - ) -> Option<Self> { - let cmd = ctx.tree.as_command(parent)?; - if cmd.name.text() != desc.name { - return None; - } - - ctx.tree - .extract_word(parent, GroupKind::Group, desc.label_index)?; - - Some(Self { - parent, - label_index: desc.label_index, - kind: desc.kind, - }) - } -} - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct Equation { - pub left: AstNodeIndex, - pub right: AstNodeIndex, -} - -impl Equation { - pub fn range(self, tree: &Tree) -> Range { - let start = tree[self.left].start(); - let end = tree[self.right].end(); - Range::new(start, end) - } - - fn parse(ctx: SymbolContext) -> Vec<Self> { - let mut equations = Vec::new(); - let mut left = None; - for node in ctx.commands { - let cmd = ctx.tree.as_command(*node).unwrap(); - let name = cmd.name.text(); - if name == "\\[" || name == "\\(" { - left = Some(node); - } else if name == "\\]" || name == "\\)" { - if let Some(begin) = left { - equations.push(Self { - left: *begin, - right: *node, - }); - left = None; - } - } - } - equations - } -} - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct Inline { - pub left: AstNodeIndex, - pub right: AstNodeIndex, -} - -impl Inline { - pub fn range(self, tree: &Tree) -> Range { - let start = tree[self.left].start(); - let end = tree[self.right].end(); - Range::new(start, end) - } - - fn parse(ctx: SymbolContext) -> Vec<Self> { - let mut inlines = Vec::new(); - let mut left = None; - for node in ctx - .tree - .nodes() - .into_iter() - .filter(|node| ctx.tree.as_math(*node).is_some()) - .sorted_by_key(|node| ctx.tree[*node].start()) - { - if let Some(l) = left { - inlines.push(Inline { - left: l, - right: node, - }); - left = None; - } else { - left = Some(node); - } - } - inlines - } -} - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct MathOperator { - pub parent: AstNodeIndex, - pub definition: AstNodeIndex, - pub definition_index: usize, - pub implementation: AstNodeIndex, - pub implementation_index: usize, -} - -impl MathOperator { - pub fn definition_name(self, tree: &Tree) -> &str { - tree.as_command(self.definition).unwrap().name.text() - } - - fn parse(ctx: SymbolContext) -> Vec<Self> { - iproduct!(ctx.commands, LANGUAGE_DATA.math_operator_commands.iter()) - .filter_map(|(parent, desc)| Self::parse_single(ctx, *parent, desc)) - .collect() - } - - fn parse_single( - ctx: SymbolContext, - parent: AstNodeIndex, - desc: &LatexMathOperatorCommand, - ) -> Option<Self> { - let cmd = ctx.tree.as_command(parent)?; - if cmd.name.text() != desc.name { - return None; - } - - let group_kind = GroupKind::Group; - let def_group = ctx - .tree - .extract_group(parent, group_kind, desc.definition_index)?; - let implementation = - ctx.tree - .extract_group(parent, group_kind, desc.implementation_index)?; - - let mut def_children = ctx.tree.children(def_group); - let definition = def_children.next()?; - Some(Self { - parent, - definition, - definition_index: desc.definition_index, - implementation, - implementation_index: desc.implementation_index, - }) - } -} - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct TheoremDefinition { - pub parent: AstNodeIndex, - pub arg_index: usize, -} - -impl TheoremDefinition { - pub fn name(self, tree: &Tree) -> &Token { - tree.extract_word(self.parent, GroupKind::Group, self.arg_index) - .unwrap() - } - - fn parse(ctx: SymbolContext) -> Vec<Self> { - let thm = LANGUAGE_DATA.theorem_definition_commands.iter(); - iproduct!(ctx.commands, thm) - .filter_map(|(parent, desc)| Self::parse_single(ctx, *parent, desc)) - .collect() - } - - fn parse_single( - ctx: SymbolContext, - parent: AstNodeIndex, - desc: &LatexTheoremDefinitionCommand, - ) -> Option<Self> { - let cmd = ctx.tree.as_command(parent)?; - if cmd.name.text() != desc.name { - return None; - } - - let group_kind = GroupKind::Group; - ctx.tree.extract_word(parent, group_kind, desc.index)?; - - Some(Self { - parent, - arg_index: desc.index, - }) - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Section { - pub parent: AstNodeIndex, - pub arg_index: usize, - pub level: i32, - pub prefix: Cow<'static, str>, -} - -impl Section { - pub fn print(&self, tree: &Tree) -> Option<String> { - tree.print_group_content(self.parent, GroupKind::Group, self.arg_index) - } - - fn parse(ctx: SymbolContext) -> Vec<Self> { - iproduct!(ctx.commands, LANGUAGE_DATA.section_commands.iter()) - .filter_map(|(parent, desc)| Self::parse_single(ctx, *parent, desc)) - .collect() - } - - fn parse_single( - ctx: SymbolContext, - parent: AstNodeIndex, - desc: &'static LatexSectionCommand, - ) -> Option<Self> { - let cmd = ctx.tree.as_command(parent)?; - if cmd.name.text() != desc.name { - return None; - } - - let group_kind = GroupKind::Group; - ctx.tree.extract_group(parent, group_kind, desc.index)?; - - Some(Self { - parent, - arg_index: desc.index, - level: desc.level, - prefix: Cow::from(&desc.prefix), - }) - } -} - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct Label { - pub parent: AstNodeIndex, - pub arg_index: usize, - pub kind: LatexLabelKind, -} - -impl Label { - pub fn names(self, tree: &Tree) -> Vec<&Token> { - tree.extract_comma_separated_words(self.parent, GroupKind::Group, self.arg_index) - .unwrap() - } - - fn parse(ctx: SymbolContext) -> Vec<Self> { - iproduct!(ctx.commands, LANGUAGE_DATA.label_commands.iter()) - .filter_map(|(parent, desc)| Self::parse_single(ctx, *parent, desc)) - .collect() - } - - fn parse_single( - ctx: SymbolContext, - parent: AstNodeIndex, - desc: &LatexLabelCommand, - ) -> Option<Self> { - let cmd = ctx.tree.as_command(parent)?; - if cmd.name.text() != desc.name { - return None; - } - - ctx.tree - .extract_comma_separated_words(parent, GroupKind::Group, desc.index)?; - - Some(Self { - parent, - arg_index: desc.index, - kind: desc.kind, - }) - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct LabelNumbering { - pub parent: AstNodeIndex, - pub number: String, -} - -impl LabelNumbering { - pub fn name<'a>(&self, tree: &'a Tree) -> &'a Token { - tree.extract_word(self.parent, GroupKind::Group, 0).unwrap() - } - - fn parse(ctx: SymbolContext) -> Vec<Self> { - ctx.commands - .iter() - .filter_map(|parent| Self::parse_single(ctx, *parent)) - .collect() - } - - fn parse_single(ctx: SymbolContext, parent: AstNodeIndex) -> Option<Self> { - let cmd = ctx.tree.as_command(parent)?; - if cmd.name.text() != "\\newlabel" { - return None; - } - - ctx.tree.extract_word(parent, GroupKind::Group, 0)?; - - let arg = ctx.tree.extract_group(parent, GroupKind::Group, 1)?; - let mut analyzer = FirstText::default(); - analyzer.visit(ctx.tree, arg); - Some(Self { - parent, - number: analyzer.text?, - }) - } -} - -#[derive(Debug, Default)] -struct FirstText { - text: Option<String>, -} - -impl Visitor for FirstText { - fn visit(&mut self, tree: &Tree, node: AstNodeIndex) { - if let Some(text) = tree.as_text(node) { - self.text = Some(text.words.iter().map(Token::text).join(" ")); - } - - if self.text.is_none() { - tree.walk(self, node); - } - } -} - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct Caption { - pub parent: AstNodeIndex, - pub arg_index: usize, -} - -impl Caption { - pub fn print(self, tree: &Tree) -> Option<String> { - tree.print_group_content(self.parent, GroupKind::Group, self.arg_index) - } - - fn parse(ctx: SymbolContext) -> Vec<Self> { - ctx.commands - .iter() - .flat_map(|parent| Self::parse_single(ctx, *parent)) - .collect() - } - - fn parse_single(ctx: SymbolContext, parent: AstNodeIndex) -> Option<Self> { - let cmd = ctx.tree.as_command(parent)?; - if cmd.name.text() != "\\caption" { - return None; - } - - ctx.tree.extract_group(parent, GroupKind::Group, 0)?; - Some(Self { - parent, - arg_index: 0, - }) - } -} - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct Item { - pub parent: AstNodeIndex, -} - -impl Item { - pub fn name(self, tree: &Tree) -> Option<String> { - tree.print_group_content(self.parent, GroupKind::Options, 0) - } - - fn parse(ctx: SymbolContext) -> Vec<Self> { - ctx.commands - .iter() - .filter_map(|parent| Self::parse_single(ctx, *parent)) - .collect() - } - - fn parse_single(ctx: SymbolContext, parent: AstNodeIndex) -> Option<Self> { - let cmd = ctx.tree.as_command(parent)?; - if cmd.name.text() != "\\item" { - return None; - } - - Some(Self { parent }) - } +pub fn analyze(context: &mut LatexAnalyzerContext, root: &latex::SyntaxNode) { + analyze_implicit_links(context); + for node in root.descendants() { + analyze_command(context, node) + .or_else(|| analyze_begin(context, node)) + .or_else(|| analyze_include(context, node)) + .or_else(|| analyze_import(context, node)) + .or_else(|| analyze_label_name(context, node)) + .or_else(|| analyze_label_number(context, node)) + .or_else(|| analyze_theorem_definition(context, node)); + } + context.extras.has_document_environment = context.extras.environment_names.contains("document"); } |