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::engine::Engine;
use crate::foundations::{
@ -5,7 +7,7 @@ use crate::foundations::{
};
use crate::introspection::Locatable;
use crate::model::bibliography::Works;
use crate::model::CslStyle;
use crate::model::{unresolved_reference, BibliographyElem, CslStyle};
use crate::text::{Lang, Region, TextElem};
/// 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
/// used to cite works from the bibliography. The label then corresponds to the
/// citation key.
#[elem(Synthesize)]
#[elem(Synthesize, Show)]
pub struct CiteElem {
/// The citation key that identifies the entry in the bibliography that
/// 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! {
CiteElem,
v: Content => v.unpack::<Self>().map_err(|_| "expected citation")?,

View File

@ -1,18 +1,20 @@
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::foundations::{
cast, elem, Content, Context, Func, IntoValue, Label, NativeElement, Packed, Show,
Smart, StyleChain, Synthesize,
cast, elem, Content, Context, Func, IntoValue, Label, NativeElement, Packed, Repr,
Show, Smart, StyleChain, Synthesize,
};
use crate::introspection::{Counter, Locatable};
use crate::introspection::{Counter, Locatable, QueryError};
use crate::math::EquationElem;
use crate::model::{
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.
///
@ -163,22 +165,25 @@ impl Synthesize for Packed<RefElem> {
impl Show for Packed<RefElem> {
#[typst_macros::time(name = "ref", span = self.span())]
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 result = engine.introspector.query_label(self.target);
if BibliographyElem::has(engine, target) {
if elem.is_ok() {
if BibliographyElem::has(engine, self.target) {
if result.is_ok() {
bail!(span, "label occurs in the document and its bibliography");
}
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>() {
return Ok(footnote.into_ref(target).pack().spanned(span));
return Ok(footnote.into_ref(self.target).pack().spanned(span));
}
let elem = elem.clone();
@ -305,3 +310,26 @@ pub trait Refable {
/// Returns the numbering of this element.
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
= Hello <a> <b>
// Warning: 12-19 content labelled multiple times
// Hint: 12-19 only the last label is used, the rest are ignored
#let f = [#block()<c>]
// Warning: 12-26 content labelled multiple times
// Hint: 12-26 only the last label is used, the rest are ignored
#let f = [#metadata(none)<c>]
#f<d>
// Warning: 6-13 content labelled multiple times
// Hint: 6-13 only the last label is used, the rest are ignored
#[#[#block()]<e>]<f>
// Warning: 6-20 content labelled multiple times
// Hint: 6-20 only the last label is used, the rest are ignored
#[#[#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
--- label-unattached-warn ---

View File

@ -49,6 +49,17 @@ A @netwok @arrgh @quark, B.
#show bibliography: none
#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 ---
// Test citation in other introspection.
#set page(width: 180pt)

View File

@ -10,9 +10,15 @@ See @setup.
As seen in @intro, we proceed.
--- 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
--- 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 ---
= First <foo>
= Second <foo>