mirror of
https://github.com/typst/typst
synced 2025-06-14 16:16:24 +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::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")?,
|
||||||
|
@ -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
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
|
// 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 ---
|
||||||
|
@ -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)
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user