summaryrefslogtreecommitdiff
path: root/support/texlab/src/syntax/latex/analysis.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/syntax/latex/analysis.rs')
-rw-r--r--support/texlab/src/syntax/latex/analysis.rs943
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");
}