mirror of
https://github.com/typst/typst
synced 2025-06-13 23:56:25 +08:00
Turn unresolved references and citations into warnings
This commit is contained in:
parent
4c0f1173ab
commit
d4242ff8c1
@ -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")?,
|
||||
|
@ -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
BIN
tests/ref/cite-missing.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
BIN
tests/ref/label-multiple-ignored-warn.png
Normal file
BIN
tests/ref/label-multiple-ignored-warn.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 894 B |
BIN
tests/ref/ref-label-complex-missing.png
Normal file
BIN
tests/ref/ref-label-complex-missing.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
tests/ref/ref-label-missing.png
Normal file
BIN
tests/ref/ref-label-missing.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 423 B |
@ -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 ---
|
||||
|
@ -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)
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user