diff --git a/crates/base-db/src/semantics/tex.rs b/crates/base-db/src/semantics/tex.rs index acd94b605..7d150fc4b 100644 --- a/crates/base-db/src/semantics/tex.rs +++ b/crates/base-db/src/semantics/tex.rs @@ -38,6 +38,7 @@ pub struct Semantics { pub can_be_root: bool, pub can_be_compiled: bool, pub diagnostic_suppressions: Vec, + pub bibitems: FxHashSet, } impl Semantics { @@ -49,7 +50,7 @@ impl Semantics { } latex::SyntaxElement::Token(token) => match token.kind() { latex::COMMAND_NAME => { - self.commands.push(Span::command(&token)); + self.commands.push(Span::command(&token)); } latex::COMMENT if token.text().contains("texlab: ignore") => { self.diagnostic_suppressions.push(token.text_range()); @@ -85,6 +86,8 @@ impl Semantics { self.process_theorem_definition(theorem_def); } else if let Some(graphics_path) = latex::GraphicsPath::cast(node.clone()) { self.process_graphics_path(graphics_path); + } else if let Some(bibitem) = latex::BibItem::cast(node.clone()) { + self.process_bibitem(bibitem); } } @@ -334,6 +337,16 @@ impl Semantics { self.graphics_paths.insert(path.to_string()); } } + + fn process_bibitem(&mut self, bibitem: latex::BibItem) { + if let Some(name) = bibitem.name() { + if let Some(key) = name.key() { + self.bibitems.insert( + Span::from(&key) + ); + } + } + } } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] diff --git a/crates/diagnostics/src/citations.rs b/crates/diagnostics/src/citations.rs index 43d5f495d..2678d7e45 100644 --- a/crates/diagnostics/src/citations.rs +++ b/crates/diagnostics/src/citations.rs @@ -18,13 +18,17 @@ pub fn detect_undefined_citations<'a>( ) -> Option<()> { let data = document.data.as_tex()?; + let bibitems: FxHashSet<&str> = data.semantics.bibitems.iter() + .map(|bib| bib.text.as_str()) + .collect(); + let entries: FxHashSet<&str> = Entry::find_all(project) .map(|(_, entry)| entry.name_text()) .collect(); for citation in &data.semantics.citations { let name = citation.name_text(); - if name != "*" && !entries.contains(name) { + if name != "*" && !entries.contains(name) && !bibitems.contains(name) { let diagnostic = Diagnostic::Tex(citation.name.range, TexError::UndefinedCitation); results .entry(document.uri.clone()) @@ -41,24 +45,38 @@ pub fn detect_unused_entries<'a>( document: &'a Document, results: &mut FxHashMap>, ) -> Option<()> { - let data = document.data.as_bib()?; - - // If this is a huge bibliography, then don't bother checking for unused entries. - if data.semantics.entries.len() > MAX_UNUSED_ENTRIES { - return None; - } let citations: FxHashSet<&str> = Citation::find_all(project) .map(|(_, citation)| citation.name_text()) .collect(); - for entry in &data.semantics.entries { - if !citations.contains(entry.name.text.as_str()) { - let diagnostic = Diagnostic::Bib(entry.name.range, BibError::UnusedEntry); - results - .entry(document.uri.clone()) - .or_default() - .push(diagnostic); + if let Some(data) = document.data.as_bib() + { + // If this is a huge bibliography, then don't bother checking for unused entries. + if data.semantics.entries.len() > MAX_UNUSED_ENTRIES { + return None; + } + + for entry in &data.semantics.entries { + if !citations.contains(entry.name.text.as_str()) { + let diagnostic = Diagnostic::Bib(entry.name.range, BibError::UnusedEntry); + results + .entry(document.uri.clone()) + .or_default() + .push(diagnostic); + } + } + }; + + if let Some(tex_data) = document.data.as_tex(){ + for bibitem in &tex_data.semantics.bibitems { + if !citations.contains(bibitem.text.as_str()) { + let diagnostic = Diagnostic::Bib(bibitem.range, BibError::UnusedEntry); + results + .entry(document.uri.clone()) + .or_default() + .push(diagnostic); + } } } diff --git a/crates/parser/src/latex.rs b/crates/parser/src/latex.rs index 669ba82f9..8393f19d4 100644 --- a/crates/parser/src/latex.rs +++ b/crates/parser/src/latex.rs @@ -158,6 +158,7 @@ impl<'a> Parser<'a> { CommandName::EndBlockComment => self.generic_command(), CommandName::VerbatimBlock => self.verbatim_block(), CommandName::GraphicsPath => self.graphics_path(), + CommandName::BibItem => self.bibitem(), }, } } @@ -1235,6 +1236,18 @@ impl<'a> Parser<'a> { } } } + + fn bibitem(&mut self) { + self.builder.start_node(BIBITEM.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(Token::LCurly) { + self.curly_group_word(); + } + + self.builder.finish_node(); + } } pub fn parse_latex(text: &str, config: &SyntaxConfig) -> GreenNode { diff --git a/crates/parser/src/latex/lexer/commands.rs b/crates/parser/src/latex/lexer/commands.rs index 4d1a7ca32..31cbf1330 100644 --- a/crates/parser/src/latex/lexer/commands.rs +++ b/crates/parser/src/latex/lexer/commands.rs @@ -96,6 +96,7 @@ pub fn classify(name: &str, config: &SyntaxConfig) -> CommandName { "iffalse" => CommandName::BeginBlockComment, "fi" => CommandName::EndBlockComment, "verb" => CommandName::VerbatimBlock, + "bibitem" => CommandName::BibItem, _ if config.citation_commands.contains(name) => CommandName::Citation, _ if config.label_definition_commands.contains(name) => CommandName::LabelDefinition, diff --git a/crates/parser/src/latex/lexer/types.rs b/crates/parser/src/latex/lexer/types.rs index 85e542792..3936742ae 100644 --- a/crates/parser/src/latex/lexer/types.rs +++ b/crates/parser/src/latex/lexer/types.rs @@ -95,6 +95,7 @@ pub enum CommandName { BeginBlockComment, EndBlockComment, VerbatimBlock, + BibItem, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] diff --git a/crates/syntax/src/latex/cst.rs b/crates/syntax/src/latex/cst.rs index 806be2aa8..536648de6 100644 --- a/crates/syntax/src/latex/cst.rs +++ b/crates/syntax/src/latex/cst.rs @@ -750,3 +750,15 @@ impl GraphicsPath { self.syntax().descendants().filter_map(CurlyGroupWord::cast) } } + +cst_node!(BibItem, BIBITEM); + +impl BibItem { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWord::cast) + } +} diff --git a/crates/syntax/src/latex/kind.rs b/crates/syntax/src/latex/kind.rs index 362f2ea05..9fc2bac87 100644 --- a/crates/syntax/src/latex/kind.rs +++ b/crates/syntax/src/latex/kind.rs @@ -82,6 +82,7 @@ pub enum SyntaxKind { ENVIRONMENT_DEFINITION, GRAPHICS_PATH, BLOCK_COMMENT, + BIBITEM, ROOT, }