diff --git a/src/syntax/lang_data.json b/src/syntax/lang_data.json index 7b5f9bfd3..09ced3053 100644 --- a/src/syntax/lang_data.json +++ b/src/syntax/lang_data.json @@ -580,19 +580,19 @@ { "name": "\\newcommand", "definitionIndex": 0, - "argumentCountIndex": 0, + "argCountIndex": 0, "implementationIndex": 1 }, { "name": "\\renewcommand", "definitionIndex": 0, - "argumentCountIndex": 0, + "argCountIndex": 0, "implementationIndex": 1 }, { "name": "\\DeclareRobustCommand", "definitionIndex": 0, - "argumentCountIndex": 0, + "argCountIndex": 0, "implementationIndex": 1 } ], diff --git a/src/syntax/lang_data.rs b/src/syntax/lang_data.rs index fe7cf4e4c..f91687b93 100644 --- a/src/syntax/lang_data.rs +++ b/src/syntax/lang_data.rs @@ -97,7 +97,7 @@ pub struct LatexIncludeCommand { pub struct LatexCommandDefinitionCommand { pub name: String, pub definition_index: usize, - pub argument_count_index: usize, + pub arg_count_index: usize, pub implementation_index: usize, } diff --git a/src/syntax/latex/analysis.rs b/src/syntax/latex/analysis.rs index 3bac5372c..4c898a24a 100644 --- a/src/syntax/latex/analysis.rs +++ b/src/syntax/latex/analysis.rs @@ -1,6 +1,6 @@ use crate::{ protocol::{Options, Uri}, - syntax::{lang_data::*, latex::ast::*}, + syntax::{lang_data::*, latex::ast::*, text::SyntaxNode}, tex::Resolver, }; use itertools::Itertools; @@ -23,8 +23,16 @@ pub struct SymbolTable { pub tree: Tree, pub commands: Vec, pub environments: Vec, + pub is_standalone: bool, pub includes: Vec, pub components: Vec, + pub citations: Vec, + pub command_definitions: Vec, + pub glossary_entries: Vec, + pub equations: Vec, + pub inlines: Vec, + pub math_operators: Vec, + pub theorem_definitions: Vec, } impl SymbolTable { @@ -48,18 +56,37 @@ impl SymbolTable { }; let environments = Environment::parse(ctx); + let is_standalone = environments.iter().any(|env| env.is_root(&tree)); + let includes = Include::parse(ctx); let components = includes .iter() .flat_map(|include| include.components(&tree)) .collect(); + let citations = Citation::parse(ctx); + let command_definitions = CommandDefinition::parse(ctx); + let glossary_entries = GlossaryEntry::parse(ctx); + + let equations = Equation::parse(ctx); + let inlines = Inline::parse(ctx); + let math_operators = MathOperator::parse(ctx); + let theorem_definitions = TheoremDefinition::parse(ctx); + Self { tree, commands, environments, + is_standalone, includes, components, + citations, + command_definitions, + glossary_entries, + equations, + inlines, + math_operators, + theorem_definitions, } } } @@ -80,7 +107,7 @@ pub struct EnvironmentDelimiter { } impl EnvironmentDelimiter { - pub fn name<'a>(self, tree: &'a Tree) -> Option<&'a Token> { + pub fn name(self, tree: &Tree) -> Option<&Token> { tree.extract_word(self.node, GroupKind::Group, 0) } @@ -92,7 +119,7 @@ impl EnvironmentDelimiter { self.is_special(tree, LANGUAGE_DATA.enum_environments.iter()) } - fn is_special<'a, I: Iterator>(&self, tree: &Tree, mut values: I) -> bool { + fn is_special<'a, I: Iterator>(self, tree: &Tree, mut values: I) -> bool { match self.name(tree) { Some(name) => values.any(|env| env == name.text()), None => false, @@ -271,3 +298,339 @@ impl Include { path.and_then(|p| Uri::from_file_path(p).ok()) } } + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub struct Citation { + node: NodeIndex, + arg_index: usize, +} + +impl Citation { + pub fn keys(self, tree: &Tree) -> Vec<&Token> { + tree.extract_comma_separated_words(self.node, GroupKind::Group, self.arg_index) + .unwrap() + } + + fn parse(ctx: SymbolContext) -> Vec { + let mut citations = Vec::new(); + for cmd in ctx.commands { + for LatexCitationCommand { name, index } in &LANGUAGE_DATA.citation_commands { + if let Some(citation) = Self::parse_single(ctx, *cmd, name, *index) { + citations.push(citation); + } + } + } + citations + } + + fn parse_single( + ctx: SymbolContext, + node: NodeIndex, + name: &str, + arg_index: usize, + ) -> Option { + let cmd = ctx.tree.as_command(node)?; + if cmd.name.text() != name { + return None; + } + + ctx.tree + .extract_comma_separated_words(node, GroupKind::Group, arg_index)?; + + Some(Self { node, arg_index }) + } +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub struct CommandDefinition { + pub parent: NodeIndex, + pub definition: NodeIndex, + pub definition_index: usize, + pub implementation: NodeIndex, + 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 { + let mut defs = Vec::new(); + for parent in ctx.commands { + for LatexCommandDefinitionCommand { + name, + definition_index, + implementation_index, + arg_count_index, + } in &LANGUAGE_DATA.command_definition_commands + { + if let Some(def) = Self::parse_single( + ctx, + *parent, + name, + *definition_index, + *implementation_index, + *arg_count_index, + ) { + defs.push(def); + } + } + } + defs + } + + fn parse_single( + ctx: SymbolContext, + parent: NodeIndex, + name: &str, + definition_index: usize, + implementation_index: usize, + arg_count_index: usize, + ) -> Option { + let cmd = ctx.tree.as_command(parent)?; + if cmd.name.text() != name { + return None; + } + + let group_kind = GroupKind::Group; + let imp = ctx + .tree + .extract_group(parent, group_kind, implementation_index)?; + + let def_group = ctx + .tree + .extract_group(parent, group_kind, definition_index)?; + + let mut def_children = ctx.tree.children(def_group); + let def = def_children.next()?; + ctx.tree.as_command(def)?; + Some(Self { + parent, + definition: def, + definition_index, + implementation: imp, + implementation_index, + arg_count_index, + }) + } +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub struct GlossaryEntry { + pub node: NodeIndex, + pub label_index: usize, + pub kind: LatexGlossaryEntryKind, +} + +impl GlossaryEntry { + pub fn label(self, tree: &Tree) -> &Token { + tree.extract_word(self.node, GroupKind::Group, self.label_index) + .unwrap() + } + + fn parse(ctx: SymbolContext) -> Vec { + let mut entries = Vec::new(); + for cmd in ctx.commands { + for LatexGlossaryEntryDefinitionCommand { + name, + label_index, + kind, + } in &LANGUAGE_DATA.glossary_entry_definition_commands + { + if let Some(entry) = Self::parse_single(ctx, *cmd, name, *label_index, *kind) { + entries.push(entry); + } + } + } + entries + } + + fn parse_single( + ctx: SymbolContext, + node: NodeIndex, + name: &str, + label_index: usize, + kind: LatexGlossaryEntryKind, + ) -> Option { + let cmd = ctx.tree.as_command(node)?; + if cmd.name.text() != name { + return None; + } + + ctx.tree.extract_word(node, GroupKind::Group, label_index)?; + Some(Self { + node, + label_index, + kind, + }) + } +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub struct Equation { + pub left: NodeIndex, + pub right: NodeIndex, +} + +impl Equation { + fn parse(ctx: SymbolContext) -> Vec { + 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: NodeIndex, + pub right: NodeIndex, +} + +impl Inline { + fn parse(ctx: SymbolContext) -> Vec { + let mut inlines = Vec::new(); + let mut left = None; + for node in ctx + .tree + .graph + .node_indices() + .filter(|node| ctx.tree.as_math(*node).is_some()) + .sorted_by_key(|node| ctx.tree.graph.node_weight(*node).unwrap().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 { + parent: NodeIndex, + definition: NodeIndex, + definition_index: usize, + implementation: NodeIndex, + 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 { + let mut operators = Vec::new(); + for parent in ctx.commands { + for LatexMathOperatorCommand { + name, + definition_index, + implementation_index, + } in &LANGUAGE_DATA.math_operator_commands + { + if let Some(operator) = + Self::parse_single(ctx, *parent, name, *definition_index, *implementation_index) + { + operators.push(operator); + } + } + } + operators + } + + fn parse_single( + ctx: SymbolContext, + parent: NodeIndex, + name: &str, + definition_index: usize, + implementation_index: usize, + ) -> Option { + let cmd = ctx.tree.as_command(parent)?; + if cmd.name.text() != name { + return None; + } + + let group_kind = GroupKind::Group; + let def_group = ctx + .tree + .extract_group(parent, group_kind, definition_index)?; + let implementation = ctx + .tree + .extract_group(parent, group_kind, implementation_index)?; + + let mut def_children = ctx.tree.children(def_group); + let definition = def_children.next()?; + Some(Self { + parent, + definition, + definition_index, + implementation, + implementation_index, + }) + } +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub struct TheoremDefinition { + node: NodeIndex, + arg_index: usize, +} + +impl TheoremDefinition { + pub fn name(self, tree: &Tree) -> &Token { + tree.extract_word(self.node, GroupKind::Group, self.arg_index) + .unwrap() + } + + fn parse(ctx: SymbolContext) -> Vec { + let mut defs = Vec::new(); + for cmd in ctx.commands { + for LatexTheoremDefinitionCommand { name, index } in + &LANGUAGE_DATA.theorem_definition_commands + { + if let Some(def) = Self::parse_single(ctx, *cmd, name, *index) { + defs.push(def); + } + } + } + defs + } + + fn parse_single( + ctx: SymbolContext, + node: NodeIndex, + name: &str, + arg_index: usize, + ) -> Option { + let cmd = ctx.tree.as_command(node)?; + if cmd.name.text() != name { + return None; + } + + ctx.tree.extract_word(node, GroupKind::Group, arg_index)?; + Some(Self { node, arg_index }) + } +} diff --git a/src/syntax/latex/ast.rs b/src/syntax/latex/ast.rs index 4e130a5bd..366f340cc 100644 --- a/src/syntax/latex/ast.rs +++ b/src/syntax/latex/ast.rs @@ -216,6 +216,14 @@ impl Tree { } } + pub fn as_math(&self, node: NodeIndex) -> Option<&Math> { + if let Node::Math(math) = self.graph.node_weight(node)? { + Some(math) + } else { + None + } + } + pub fn extract_group( &self, parent: NodeIndex,