Turn unresolved references and citations into warnings

This commit is contained in:
Laurenz 2024-09-17 11:23:35 +02:00
parent 4c0f1173ab
commit d4242ff8c1
9 changed files with 89 additions and 22 deletions

View File

@ -1,3 +1,5 @@
use ecow::eco_format;
use crate::diag::{error, At, HintedString, SourceResult}; use crate::diag::{error, At, HintedString, SourceResult};
use crate::engine::Engine; use crate::engine::Engine;
use crate::foundations::{ use crate::foundations::{
@ -5,7 +7,7 @@ use crate::foundations::{
}; };
use crate::introspection::Locatable; use crate::introspection::Locatable;
use crate::model::bibliography::Works; use crate::model::bibliography::Works;
use crate::model::CslStyle; use crate::model::{unresolved_reference, BibliographyElem, CslStyle};
use crate::text::{Lang, Region, TextElem}; use crate::text::{Lang, Region, TextElem};
/// Cite a work from the bibliography. /// Cite a work from the bibliography.
@ -40,7 +42,7 @@ use crate::text::{Lang, Region, TextElem};
/// This function indirectly has dedicated syntax. [References]($ref) can be /// This function indirectly has dedicated syntax. [References]($ref) can be
/// used to cite works from the bibliography. The label then corresponds to the /// used to cite works from the bibliography. The label then corresponds to the
/// citation key. /// citation key.
#[elem(Synthesize)] #[elem(Synthesize, Show)]
pub struct CiteElem { pub struct CiteElem {
/// The citation key that identifies the entry in the bibliography that /// The citation key that identifies the entry in the bibliography that
/// shall be cited, as a label. /// shall be cited, as a label.
@ -117,6 +119,26 @@ impl Synthesize for Packed<CiteElem> {
} }
} }
impl Show for Packed<CiteElem> {
#[typst_macros::time(name = "cite", span = self.span())]
fn show(&self, engine: &mut Engine, _: StyleChain) -> SourceResult<Content> {
if !BibliographyElem::has(engine, self.key) {
return Ok(unresolved_reference(
engine,
eco_format!(
"key `{}` does not exist in the bibliography",
self.key.as_str()
),
"cite",
self.key,
self.span(),
));
}
Ok(self.clone().pack())
}
}
cast! { cast! {
CiteElem, CiteElem,
v: Content => v.unpack::<Self>().map_err(|_| "expected citation")?, v: Content => v.unpack::<Self>().map_err(|_| "expected citation")?,

View File

@ -1,18 +1,20 @@
use comemo::Track; use comemo::Track;
use ecow::eco_format; use ecow::{eco_format, EcoString};
use crate::diag::{bail, At, Hint, SourceResult}; use crate::diag::{bail, At, Hint, SourceDiagnostic, SourceResult};
use crate::engine::Engine; use crate::engine::Engine;
use crate::foundations::{ use crate::foundations::{
cast, elem, Content, Context, Func, IntoValue, Label, NativeElement, Packed, Show, cast, elem, Content, Context, Func, IntoValue, Label, NativeElement, Packed, Repr,
Smart, StyleChain, Synthesize, Show, Smart, StyleChain, Synthesize,
}; };
use crate::introspection::{Counter, Locatable}; use crate::introspection::{Counter, Locatable, QueryError};
use crate::math::EquationElem; use crate::math::EquationElem;
use crate::model::{ use crate::model::{
BibliographyElem, CiteElem, Destination, Figurable, FootnoteElem, Numbering, BibliographyElem, CiteElem, Destination, Figurable, FootnoteElem, Numbering,
}; };
use crate::text::TextElem; use crate::syntax::{is_valid_label_literal, Span};
use crate::text::{RawContent, RawElem, TextElem};
use crate::visualize::Color;
/// A reference to a label or bibliography. /// A reference to a label or bibliography.
/// ///
@ -163,22 +165,25 @@ impl Synthesize for Packed<RefElem> {
impl Show for Packed<RefElem> { impl Show for Packed<RefElem> {
#[typst_macros::time(name = "ref", span = self.span())] #[typst_macros::time(name = "ref", span = self.span())]
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> { fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
let target = *self.target();
let elem = engine.introspector.query_label(target);
let span = self.span(); let span = self.span();
let result = engine.introspector.query_label(self.target);
if BibliographyElem::has(engine, target) { if BibliographyElem::has(engine, self.target) {
if elem.is_ok() { if result.is_ok() {
bail!(span, "label occurs in the document and its bibliography"); bail!(span, "label occurs in the document and its bibliography");
} }
return Ok(to_citation(self, engine, styles)?.pack().spanned(span)); return Ok(to_citation(self, engine, styles)?.pack().spanned(span));
} }
let elem = elem.at(span)?; if let Err(error @ QueryError::MissingLabel(_)) = result {
return Ok(unresolved_reference(engine, error, "ref", self.target, span));
}
let elem = result.at(span)?;
if let Some(footnote) = elem.to_packed::<FootnoteElem>() { if let Some(footnote) = elem.to_packed::<FootnoteElem>() {
return Ok(footnote.into_ref(target).pack().spanned(span)); return Ok(footnote.into_ref(self.target).pack().spanned(span));
} }
let elem = elem.clone(); let elem = elem.clone();
@ -305,3 +310,26 @@ pub trait Refable {
/// Returns the numbering of this element. /// Returns the numbering of this element.
fn numbering(&self) -> Option<&Numbering>; fn numbering(&self) -> Option<&Numbering>;
} }
/// Generates a warning for an unresolved reference and returns placeholder
/// content.
pub(crate) fn unresolved_reference(
engine: &mut Engine,
message: impl Into<EcoString>,
func: &str,
target: Label,
span: Span,
) -> Content {
engine.sink.warn(SourceDiagnostic::warning(span, message));
let text = if is_valid_label_literal(target.as_str()) {
eco_format!("@{}", target.as_str())
} else {
eco_format!("#{func}(label({}))", target.as_str().repr())
};
return RawElem::new(RawContent::Text(text))
.pack()
.spanned(span)
.styled(TextElem::set_fill(Color::RED.into()));
}

BIN
tests/ref/cite-missing.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

View File

@ -72,16 +72,16 @@ _Visible_
// Hint: 1-8 only the last label is used, the rest are ignored // Hint: 1-8 only the last label is used, the rest are ignored
= Hello <a> <b> = Hello <a> <b>
// Warning: 12-19 content labelled multiple times // Warning: 12-26 content labelled multiple times
// Hint: 12-19 only the last label is used, the rest are ignored // Hint: 12-26 only the last label is used, the rest are ignored
#let f = [#block()<c>] #let f = [#metadata(none)<c>]
#f<d> #f<d>
// Warning: 6-13 content labelled multiple times // Warning: 6-20 content labelled multiple times
// Hint: 6-13 only the last label is used, the rest are ignored // Hint: 6-20 only the last label is used, the rest are ignored
#[#[#block()]<e>]<f> #[#[#metadata(none)]<e>]<f>
// Error: 1-3 label `<a>` does not exist in the document // Warning: 1-3 label `<a>` does not exist in the document
@a @a
--- label-unattached-warn --- --- label-unattached-warn ---

View File

@ -49,6 +49,17 @@ A @netwok @arrgh @quark, B.
#show bibliography: none #show bibliography: none
#bibliography("/assets/bib/works.bib") #bibliography("/assets/bib/works.bib")
--- cite-missing ---
// Warning: 2-15 key `peter` does not exist in the bibliography
// Warning: 31-37 label `<extra>` does not exist in the document
#cite(<peter>) @netwok @arrgh @extra
// Warning: 2-20 key `>?&` does not exist in the bibliography
#cite(label(">?&"))
#show bibliography: none
#bibliography("/assets/bib/works.bib")
--- issue-785-cite-locate --- --- issue-785-cite-locate ---
// Test citation in other introspection. // Test citation in other introspection.
#set page(width: 180pt) #set page(width: 180pt)

View File

@ -10,9 +10,15 @@ See @setup.
As seen in @intro, we proceed. As seen in @intro, we proceed.
--- ref-label-missing --- --- ref-label-missing ---
// Error: 1-5 label `<foo>` does not exist in the document // Warning: 1-5 label `<foo>` does not exist in the document
@foo @foo
--- ref-label-complex-missing ---
#set page(width: auto)
// Warning: 2-28 label `label("is;/"bad%//#")` does not exist in the document
#ref(label("is;\"bad%//#"))
--- ref-label-duplicate --- --- ref-label-duplicate ---
= First <foo> = First <foo>
= Second <foo> = Second <foo>