typst_library/model/cite.rs
1use typst_syntax::Spanned;
2
3use crate::diag::SourceResult;
4use crate::engine::Engine;
5use crate::foundations::{
6 Cast, Content, Derived, Label, Packed, Smart, StyleChain, Synthesize, cast, elem,
7};
8use crate::introspection::Locatable;
9use crate::model::bibliography::Works;
10use crate::model::{CslSource, CslStyle};
11use crate::text::{Lang, Region, TextElem};
12
13/// Cite a work from the bibliography.
14///
15/// Before you starting citing, you need to add a @bibliography[bibliography]
16/// somewhere in your document.
17///
18/// = Example <example>
19/// ```example
20/// This was already noted by
21/// pirates long ago. @arrgh
22///
23/// Multiple sources say ...
24/// @arrgh @netwok.
25///
26/// You can also call `cite`
27/// explicitly. #cite(<arrgh>)
28///
29/// #bibliography("works.bib")
30/// ```
31///
32/// If your source name contains certain characters such as slashes, which are
33/// not recognized by the `<>` syntax, you can explicitly call `label` instead.
34///
35/// ```typ
36/// Computer Modern is an example of a modernist serif typeface.
37/// #cite(label("DBLP:books/lib/Knuth86a")).
38/// >>> #bibliography("works.bib")
39/// ```
40///
41/// = Syntax <syntax>
42/// This function indirectly has dedicated syntax. @ref[References] can be used
43/// to cite works from the bibliography. The label then corresponds to the
44/// citation key.
45#[elem(Locatable, Synthesize)]
46pub struct CiteElem {
47 /// The citation key that identifies the entry in the bibliography that
48 /// shall be cited, as a label.
49 ///
50 /// ```example
51 /// // All the same
52 /// @netwok \
53 /// #cite(<netwok>) \
54 /// #cite(label("netwok"))
55 /// >>> #set text(0pt)
56 /// >>> #bibliography("works.bib", style: "apa")
57 /// ```
58 #[required]
59 pub key: Label,
60
61 /// A supplement for the citation such as page or chapter number.
62 ///
63 /// In reference syntax, the supplement can be added in square brackets:
64 ///
65 /// ```example
66 /// This has been proven. @distress[p.~7]
67 ///
68 /// #bibliography("works.bib")
69 /// ```
70 pub supplement: Option<Content>,
71
72 /// The kind of citation to produce. Different forms are useful in different
73 /// scenarios: A normal citation is useful as a source at the end of a
74 /// sentence, while a "prose" citation is more suitable for inclusion in the
75 /// flow of text.
76 ///
77 /// If set to `{none}`, the cited work is included in the bibliography, but
78 /// nothing will be displayed.
79 ///
80 /// ```example
81 /// #cite(<netwok>, form: "prose")
82 /// show the outsized effects of
83 /// pirate life on the human psyche.
84 /// >>> #set text(0pt)
85 /// >>> #bibliography("works.bib", style: "apa")
86 /// ```
87 #[default(Some(CitationForm::Normal))]
88 pub form: Option<CitationForm>,
89
90 /// The citation style.
91 ///
92 /// This can be:
93 /// - `{auto}` to automatically use the
94 /// @bibliography.style[bibliography's style] for citations.
95 /// - A string with the name of one of the built-in styles (see below). Some
96 /// of the styles listed below appear twice, once with their full name and
97 /// once with a short alias.
98 /// - A path string or @path to a
99 /// #link("https://citationstyles.org/")[CSL file].
100 /// - Raw bytes from which a CSL style should be decoded.
101 #[parse(match args.named::<Spanned<Smart<CslSource>>>("style")? {
102 Some(Spanned { v: Smart::Custom(source), span }) => Some(Smart::Custom(
103 CslStyle::load(engine, Spanned::new(source, span))?
104 )),
105 Some(Spanned { v: Smart::Auto, .. }) => Some(Smart::Auto),
106 None => None,
107 })]
108 pub style: Smart<Derived<CslSource, CslStyle>>,
109
110 /// The text language setting where the citation is.
111 #[internal]
112 #[synthesized]
113 pub lang: Lang,
114
115 /// The text region setting where the citation is.
116 #[internal]
117 #[synthesized]
118 pub region: Option<Region>,
119}
120
121impl Synthesize for Packed<CiteElem> {
122 fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> {
123 let elem = self.as_mut();
124 elem.lang = Some(styles.get(TextElem::lang));
125 elem.region = Some(styles.get(TextElem::region));
126 Ok(())
127 }
128}
129
130cast! {
131 CiteElem,
132 v: Content => v.unpack::<Self>().map_err(|_| "expected citation")?,
133}
134
135/// The form of the citation.
136#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Cast)]
137pub enum CitationForm {
138 /// Display in the standard way for the active style.
139 #[default]
140 Normal,
141 /// Produces a citation that is suitable for inclusion in a sentence.
142 Prose,
143 /// Mimics a bibliography entry, with full information about the cited work.
144 Full,
145 /// Shows only the cited work's author(s).
146 Author,
147 /// Shows only the cited work's year.
148 Year,
149}
150
151/// A group of consecutive citations.
152///
153/// This element is automatically created from adjacent citations during
154/// realization. Citations are grouped without regard to which bibliography they
155/// end up being assigned to. They are only split into subgroups during
156/// bibliography assignment within the call tree of [`Works::generate`].
157///
158/// Each subgroup created there may consist of one or multiple citations that
159/// are processed as a union by hayagriva. If hayagriva were to support a single
160/// citation group for multiple bibliographies, the subgroup concept could be
161/// removed, but it's unclear whether that can be reasonably supported.
162///
163/// Another alternative would have been to already segment by assigned
164/// bibliography when grouping consecutive citations into [`CiteGroup`]s during
165/// realization. This would be quite clean, but unfortunately it would incur one
166/// additional document iteration, which is too high a price to pay for the
167/// conceptual cleanliness.
168///
169/// The citation group element is purposefully kept internal to retain
170/// flexibility in how it is collected.
171#[elem(Locatable)]
172pub struct CiteGroup {
173 /// Holds citations and potentially spaces in between them. The spaces are
174 /// retained so that they can be correctly rendered between subgroups
175 /// assigned to different bibliographies.
176 #[required]
177 pub children: Vec<Content>,
178}
179
180impl Packed<CiteGroup> {
181 pub fn realize(&self, engine: &mut Engine) -> SourceResult<Content> {
182 let loc = self.location().unwrap();
183 let span = self.span();
184 Works::generate(engine, span)?.citation(loc, span)
185 }
186}