CSL support

This commit is contained in:
Laurenz 2023-10-31 01:33:06 +01:00
parent 27ab2bb9a2
commit ab5dd2a1cb
25 changed files with 1300 additions and 654 deletions

251
Cargo.lock generated
View File

@ -19,6 +19,17 @@ dependencies = [
"cpufeatures",
]
[[package]]
name = "ahash"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "ahash"
version = "0.8.6"
@ -151,11 +162,10 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "biblatex"
version = "0.8.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc9fd60378277e44cd400ec5f35e768ce0d5a63d8d18ac7b1a9231196251dae5"
checksum = "2e41df82f0d1c4919d946bb0c7c3d179b6071246243d308a1bdee6cfecee3bc7"
dependencies = [
"chrono",
"numerals",
"paste",
"strum",
@ -229,6 +239,28 @@ version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
name = "bytecheck"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627"
dependencies = [
"bytecheck_derive",
"ptr_meta",
"simdutf8",
]
[[package]]
name = "bytecheck_derive"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "bytemuck"
version = "1.14.0"
@ -345,6 +377,16 @@ dependencies = [
"inout",
]
[[package]]
name = "citationberg"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c857faf24e89710f105b623c174508070a9e11e056a749f251ca4c56f59ad88"
dependencies = [
"quick-xml 0.28.2",
"serde",
]
[[package]]
name = "clap"
version = "4.4.7"
@ -476,9 +518,9 @@ dependencies = [
[[package]]
name = "cpufeatures"
version = "0.2.10"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fbc60abd742b35f2492f808e1abbb83d45f72db402e14c55057edc9c7b1e9e4"
checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
dependencies = [
"libc",
]
@ -884,6 +926,9 @@ name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash 0.7.7",
]
[[package]]
name = "hashbrown"
@ -893,23 +938,24 @@ checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
[[package]]
name = "hayagriva"
version = "0.3.2"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "065e90e53aa502be868a307f58ca6b46e31143641e809047c689de75619d8cea"
checksum = "c5af3d464a6b5ae882f15fe1da4e696fd96b77fee78ded933e0ad81d1d87cbc5"
dependencies = [
"biblatex",
"chrono",
"isolang",
"lazy_static",
"linked-hash-map",
"ciborium",
"citationberg",
"indexmap 2.0.2",
"numerals",
"paste",
"regex",
"strum",
"rkyv",
"serde",
"serde_yaml 0.9.27",
"thiserror",
"unic-langid",
"unicode-segmentation",
"unscanny",
"url",
"yaml-rust",
]
[[package]]
@ -1034,9 +1080,9 @@ dependencies = [
[[package]]
name = "icu_properties_data"
version = "1.3.2"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c8bb3b67a8347e94d580434369e5c7ee89999b9309d04b7cfc88dfaa0f31b59"
checksum = "98507b488098f45eb95ef495612a2012e4d8ad6095dda86cb2f1728aa2204a60"
[[package]]
name = "icu_provider"
@ -1208,7 +1254,7 @@ version = "0.11.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c50453ec3a6555fad17b1cd1a80d16af5bc7cb35094f64e429fd46549018c6a3"
dependencies = [
"ahash",
"ahash 0.8.6",
"clap",
"crossbeam-channel",
"crossbeam-utils",
@ -1293,15 +1339,6 @@ dependencies = [
"once_cell",
]
[[package]]
name = "isolang"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f80f221db1bc708b71128757b9396727c04de86968081e18e89b0575e03be071"
dependencies = [
"phf",
]
[[package]]
name = "itoa"
version = "1.0.9"
@ -1729,24 +1766,6 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
name = "phf"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_shared"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
dependencies = [
"siphasher",
]
[[package]]
name = "pico-args"
version = "0.5.0"
@ -1842,6 +1861,26 @@ dependencies = [
"cc",
]
[[package]]
name = "ptr_meta"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1"
dependencies = [
"ptr_meta_derive",
]
[[package]]
name = "ptr_meta_derive"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "pulldown-cmark"
version = "0.9.3"
@ -1863,6 +1902,16 @@ dependencies = [
"memchr",
]
[[package]]
name = "quick-xml"
version = "0.28.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1"
dependencies = [
"memchr",
"serde",
]
[[package]]
name = "quick-xml"
version = "0.30.0"
@ -2011,6 +2060,15 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rend"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2571463863a6bd50c32f94402933f03457a3fbaf697a707c5be741e459f08fd"
dependencies = [
"bytecheck",
]
[[package]]
name = "resvg"
version = "0.36.0"
@ -2030,9 +2088,9 @@ dependencies = [
[[package]]
name = "rgb"
version = "0.8.36"
version = "0.8.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59"
checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8"
dependencies = [
"bytemuck",
]
@ -2051,6 +2109,34 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "rkyv"
version = "0.7.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58"
dependencies = [
"bitvec",
"bytecheck",
"hashbrown 0.12.3",
"ptr_meta",
"rend",
"rkyv_derive",
"seahash",
"tinyvec",
"uuid",
]
[[package]]
name = "rkyv_derive"
version = "0.7.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "roff"
version = "0.2.1"
@ -2083,9 +2169,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.20"
version = "0.38.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0"
checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
dependencies = [
"bitflags 2.4.1",
"errno",
@ -2184,6 +2270,12 @@ dependencies = [
"untrusted",
]
[[package]]
name = "seahash"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]]
name = "self-replace"
version = "1.3.5"
@ -2202,18 +2294,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
version = "1.0.189"
version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537"
checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.189"
version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5"
checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
dependencies = [
"proc-macro2",
"quote",
@ -2222,9 +2314,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.107"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa",
"ryu",
@ -2254,9 +2346,9 @@ dependencies = [
[[package]]
name = "serde_yaml"
version = "0.9.25"
version = "0.9.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574"
checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c"
dependencies = [
"indexmap 2.0.2",
"itoa",
@ -2302,6 +2394,12 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "simdutf8"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
[[package]]
name = "simplecss"
version = "0.2.1"
@ -2508,13 +2606,13 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.8.0"
version = "3.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
dependencies = [
"cfg-if",
"fastrand 2.0.1",
"redox_syscall 0.3.5",
"redox_syscall 0.4.1",
"rustix",
"windows-sys",
]
@ -2641,9 +2739,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
version = "0.8.4"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ef75d881185fd2df4a040793927c153d863651108a93c7e17a9e591baa95cc6"
checksum = "8ff9e3abce27ee2c9a37f9ad37238c1bdd4e789c84ba37df76aa4d528f5072cc"
dependencies = [
"serde",
"serde_spanned",
@ -2662,9 +2760,9 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.20.4"
version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380f9e8120405471f7c9ad1860a713ef5ece6a670c7eae39225e477340f32fc4"
checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81"
dependencies = [
"indexmap 2.0.2",
"serde",
@ -2845,7 +2943,7 @@ dependencies = [
"semver",
"serde",
"serde_json",
"serde_yaml 0.9.25",
"serde_yaml 0.9.27",
"siphasher",
"tar",
"tempfile",
@ -2871,7 +2969,7 @@ dependencies = [
"once_cell",
"pulldown-cmark",
"serde",
"serde_yaml 0.9.25",
"serde_yaml 0.9.27",
"syntect",
"typed-arena",
"typst",
@ -2911,6 +3009,7 @@ dependencies = [
"icu_provider_adapters",
"icu_provider_blob",
"icu_segmenter",
"indexmap 2.0.2",
"kurbo",
"lipsum",
"log",
@ -2918,7 +3017,7 @@ dependencies = [
"roxmltree",
"rustybuzz",
"serde_json",
"serde_yaml 0.9.25",
"serde_yaml 0.9.27",
"smallvec",
"syntect",
"time",
@ -2992,6 +3091,7 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff"
dependencies = [
"serde",
"tinystr",
]
@ -3130,6 +3230,7 @@ dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
]
[[package]]
@ -3205,6 +3306,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "uuid"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
[[package]]
name = "valuable"
version = "0.1.0"
@ -3447,9 +3554,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "winnow"
version = "0.5.17"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c"
checksum = "176b6138793677221d420fd2f0aeeced263f197688b36484660da767bca2fa32"
dependencies = [
"memchr",
]
@ -3550,18 +3657,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.7.15"
version = "0.7.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81ba595b9f2772fbee2312de30eeb80ec773b4cb2f1e8098db024afadda6c06f"
checksum = "dd66a62464e3ffd4e37bd09950c2b9dd6c4f8767380fabba0d523f9a775bc85a"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.15"
version = "0.7.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "772666c41fb6dceaf520b564b962d738a8e1a83b41bd48945f50837aed78bb1d"
checksum = "255c4596d41e6916ced49cfafea18727b24d67878fa180ddfd69b9df34fd1726"
dependencies = [
"proc-macro2",
"quote",

View File

@ -23,13 +23,14 @@ chinese-number = { version = "0.7.2", default-features = false, features = ["num
comemo = "0.3"
csv = "1"
ecow = { version = "0.2", features = ["serde"] }
hayagriva = "0.3.2"
hayagriva = "0.4"
hypher = "0.1.4"
icu_properties = { version = "1.3", features = ["serde"] }
icu_provider = { version = "1.3", features = ["sync"] }
icu_provider_adapters = "1.3"
icu_provider_blob = "1.3"
icu_segmenter = { version = "1.3", features = ["serde"] }
indexmap = "2"
kurbo = "0.9"
lipsum = "0.9"
log = "0.4"

View File

@ -51,11 +51,10 @@ use std::mem;
use typed_arena::Arena;
use typst::diag::SourceResult;
use typst::eval::Tracer;
use typst::model::DelayedErrors;
use typst::model::{applicable, realize, StyleVecBuilder};
use typst::model::{applicable, realize, DelayedErrors, StyleVecBuilder};
use crate::math::{EquationElem, LayoutMath};
use crate::meta::DocumentElem;
use crate::meta::{CiteElem, CiteGroup, DocumentElem};
use crate::prelude::*;
use crate::shared::BehavedBuilder;
use crate::text::{LinebreakElem, SmartquoteElem, SpaceElem, TextElem};
@ -302,6 +301,8 @@ struct Builder<'a, 'v, 't> {
par: ParBuilder<'a>,
/// The current list building state.
list: ListBuilder<'a>,
/// The current citation grouping state.
cites: CiteGroupBuilder<'a>,
}
/// Temporary storage arenas for building.
@ -322,6 +323,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
flow: FlowBuilder::default(),
par: ParBuilder::default(),
list: ListBuilder::default(),
cites: CiteGroupBuilder::default(),
}
}
@ -351,6 +353,12 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
return Ok(());
}
if self.cites.accept(content, styles) {
return Ok(());
}
self.interrupt_cites()?;
if self.list.accept(content, styles) {
return Ok(());
}
@ -438,7 +446,21 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
Ok(())
}
fn interrupt_cites(&mut self) -> SourceResult<()> {
if !self.cites.items.is_empty() {
let staged = mem::take(&mut self.cites.staged);
let (group, styles) = mem::take(&mut self.cites).finish();
let stored = self.scratch.content.alloc(group);
self.accept(stored, styles)?;
for (content, styles) in staged {
self.accept(content, styles)?;
}
}
Ok(())
}
fn interrupt_list(&mut self) -> SourceResult<()> {
self.interrupt_cites()?;
if !self.list.items.is_empty() {
let staged = mem::take(&mut self.list.staged);
let (list, styles) = mem::take(&mut self.list).finish();
@ -713,3 +735,37 @@ impl Default for ListBuilder<'_> {
}
}
}
/// Accepts citations.
#[derive(Default)]
struct CiteGroupBuilder<'a> {
/// The citations.
items: StyleVecBuilder<'a, CiteElem>,
/// Trailing content for which it is unclear whether it is part of the list.
staged: Vec<(&'a Content, StyleChain<'a>)>,
}
impl<'a> CiteGroupBuilder<'a> {
fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool {
if !self.items.is_empty()
&& (content.is::<SpaceElem>() || content.is::<MetaElem>())
{
self.staged.push((content, styles));
return true;
}
if let Some(citation) = content.to::<CiteElem>() {
self.items.push(citation.clone(), styles);
return true;
}
false
}
fn finish(self) -> (Content, StyleChain<'a>) {
let (items, styles) = self.items.finish();
let items = items.into_items();
let span = items.first().map(|cite| cite.span()).unwrap_or(Span::detached());
(CiteGroup::new(items).pack().spanned(span), styles)
}
}

View File

@ -51,6 +51,13 @@ pub struct HElem {
pub weak: bool,
}
impl HElem {
/// Zero-width horizontal weak spacing that eats surrounding spaces.
pub fn hole() -> Self {
Self::new(Abs::zero().into()).with_weak(true)
}
}
impl Behave for HElem {
fn behaviour(&self) -> Behaviour {
if self.amount().is_fractional() {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,156 @@
use super::bibliography::Works;
use super::CslStyle;
use crate::prelude::*;
use crate::text::TextElem;
/// Cite a work from the bibliography.
///
/// Before you starting citing, you need to add a [bibliography]($bibliography)
/// somewhere in your document.
///
/// # Example
/// ```example
/// This was already noted by
/// pirates long ago. @arrgh
///
/// Multiple sources say ...
/// @arrgh @netwok.
///
/// You can also call `cite`
/// explicitly. #cite(<arrgh>)
///
/// #bibliography("works.bib")
/// ```
///
/// # Syntax
/// 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)]
pub struct CiteElem {
/// The citation key that identifies the entry in the bibliography that
/// shall be cited, as a label.
///
/// ```example
/// // All the same
/// @netwok \
/// #cite(<netwok>) \
/// #cite(label("netwok"))
/// >>> #set text(0pt)
/// >>> #bibliography("works.bib", style: "apa")
/// ```
#[required]
pub key: Label,
/// A supplement for the citation such as page or chapter number.
///
/// In reference syntax, the supplement can be added in square brackets:
///
/// ```example
/// This has been proven. @distress[p.~7]
///
/// #bibliography("works.bib")
/// ```
pub supplement: Option<Content>,
/// The kind of citation to produce. Different forms are useful in different
/// scenarios: A normal citation is useful as a source at the end of a
/// sentence, while a "prose" citation is more suitable for inclusion in the
/// flow of text.
///
/// If set to `{none}`, the cited work is included in the bibliography, but
/// nothing will be displayed.
///
/// ```example
/// #cite(<netwok>, form: "prose")
/// show the outsized effects of
/// pirate life on the human psyche.
/// >>> #set text(0pt)
/// >>> #bibliography("works.bib", style: "apa")
/// ```
#[default(Some(CitationForm::Normal))]
pub form: Option<CitationForm>,
/// The citation style.
///
/// Should be either `{auto}`, one of the built-in styles (see below) or a
/// path to a [CSL file](https://citationstyles.org/). Some of the styles
/// listed below appear twice, once with their full name and once with a
/// short alias.
///
/// When set to `{auto}`, automatically use the
/// [bibliography's style]($bibliography.style) for the citations.
#[parse(CslStyle::parse_smart(vm, args)?)]
pub style: Smart<CslStyle>,
/// The text language setting where the citation is.
#[internal]
#[synthesized]
pub lang: Lang,
/// The text region setting where the citation is.
#[internal]
#[synthesized]
pub region: Option<Region>,
}
impl Synthesize for CiteElem {
fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
self.push_supplement(self.supplement(styles));
self.push_form(self.form(styles));
self.push_style(self.style(styles));
self.push_lang(TextElem::lang_in(styles));
self.push_region(TextElem::region_in(styles));
Ok(())
}
}
cast! {
CiteElem,
v: Content => v.to::<Self>().cloned().ok_or("expected citation")?,
}
/// The form of the citation.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Cast)]
pub enum CitationForm {
/// Display in the standard way for the active style.
#[default]
Normal,
/// Produces a citation that is suitable for inclusion in a sentence.
Prose,
/// Mimics a bibliography entry, with full information about the cited work.
Full,
/// Shows only the cited work's author(s).
Author,
/// Shows only the cited work's year.
Year,
}
/// A group of citations.
///
/// This is automatically created from adjacent citations during show rule
/// application.
#[elem(Locatable, Show)]
pub struct CiteGroup {
/// The citations.
#[required]
pub children: Vec<CiteElem>,
}
impl Show for CiteGroup {
#[tracing::instrument(name = "CiteGroup::show", skip(self, vt))]
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
Ok(vt.delayed(|vt| {
let location = self.0.location().unwrap();
let span = self.span();
Works::generate(vt.world, vt.introspector)
.at(span)?
.citations
.get(&location)
.cloned()
.unwrap_or_else(|| {
bail!(span, "failed to format citation (this is a bug)")
})
}))
}
}

View File

@ -8,24 +8,6 @@ use crate::prelude::*;
use crate::text::{SuperElem, TextElem, TextSize};
use crate::visualize::LineElem;
/// The body of a footnote can be either some content or a label referencing
/// another footnote.
#[derive(Debug)]
pub enum FootnoteBody {
Content(Content),
Reference(Label),
}
cast! {
FootnoteBody,
self => match self {
Self::Content(v) => v.into_value(),
Self::Reference(v) => v.into_value(),
},
v: Content => Self::Content(v),
v: Label => Self::Reference(v),
}
/// A footnote.
///
/// Includes additional remarks and references on the same page with footnotes.
@ -147,9 +129,9 @@ impl Show for FootnoteElem {
let counter = Counter::of(Self::elem());
let num = counter.at(vt, loc)?.display(vt, &numbering)?;
let sup = SuperElem::new(num).pack();
let hole = HElem::new(Abs::zero().into()).with_weak(true).pack();
let loc = loc.variant(1);
Ok(hole + sup.linked(Destination::Location(loc)))
// Add zero-width weak spacing to make the footnote "sticky".
Ok(HElem::hole().pack() + sup.linked(Destination::Location(loc)))
}))
}
}
@ -160,6 +142,24 @@ impl Count for FootnoteElem {
}
}
/// The body of a footnote can be either some content or a label referencing
/// another footnote.
#[derive(Debug)]
pub enum FootnoteBody {
Content(Content),
Reference(Label),
}
cast! {
FootnoteBody,
self => match self {
Self::Content(v) => v.into_value(),
Self::Reference(v) => v.into_value(),
},
v: Content => Self::Content(v),
v: Label => Self::Reference(v),
}
/// An entry in a footnote list.
///
/// This function is not intended to be called directly. Instead, it is used

View File

@ -1,6 +1,7 @@
//! Interaction between document parts.
mod bibliography;
mod cite;
mod context;
mod counter;
mod document;
@ -18,6 +19,7 @@ mod reference;
mod state;
pub use self::bibliography::*;
pub use self::cite::*;
pub use self::context::*;
pub use self::counter::*;
pub use self::document::*;

View File

@ -85,6 +85,9 @@ use crate::text::TextElem;
#[elem(title = "Reference", Synthesize, Locatable, Show)]
pub struct RefElem {
/// The target label that should be referenced.
///
/// Can be a label that is defined in the document or an entry from the
/// [`bibliography`]($bibliography).
#[required]
pub target: Label,
@ -222,7 +225,7 @@ impl Show for RefElem {
impl RefElem {
/// Turn the reference into a citation.
pub fn to_citation(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<CiteElem> {
let mut elem = CiteElem::new(vec![self.target().0]);
let mut elem = CiteElem::new(self.target());
elem.0.set_location(self.0.location().unwrap());
elem.synthesize(vt, styles)?;
elem.push_supplement(match self.supplement(styles) {

View File

@ -1,6 +1,6 @@
use super::{SmartquoteElem, SpaceElem, TextElem};
use crate::layout::{BlockElem, HElem, PadElem, Spacing, VElem};
use crate::meta::{BibliographyElem, BibliographyStyle, CiteElem};
use crate::meta::{CitationForm, CiteElem};
use crate::prelude::*;
/// Displays a quote alongside it's author.
@ -125,22 +125,16 @@ cast! {
}
impl Show for QuoteElem {
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
let mut realized = self.body();
let block = self.block(styles);
if self.quotes(styles) == Smart::Custom(true) || !block {
// use h(0pt, weak: true) to make the quotes "sticky"
// Add zero-width weak spacing to make the quotes "sticky".
let hole = HElem::hole().pack();
let quote = SmartquoteElem::new().with_double(true).pack();
let weak_h = HElem::new(Spacing::Rel(Rel::zero())).with_weak(true).pack();
realized = Content::sequence([
quote.clone(),
weak_h.clone(),
realized,
weak_h,
quote,
]);
realized =
Content::sequence([quote.clone(), hole.clone(), realized, hole, quote]);
}
if block {
@ -154,41 +148,23 @@ impl Show for QuoteElem {
seq.push(content);
}
Attribution::Label(label) => {
let citation = vt.delayed(|vt| {
let citation = CiteElem::new(vec![label.0]);
let bib =
BibliographyElem::find(vt.introspector).at(self.span())?;
// TODO: these should use the citation-format attribute, once CSL
// is implemented and retrieve the authors for non-author formats
// themselves, see:
// - https://github.com/typst/typst/pull/2252#issuecomment-1741146989
// - https://github.com/typst/typst/pull/2252#issuecomment-1744634132
Ok(match bib.style(styles) {
// author-date and author
BibliographyStyle::Apa
| BibliographyStyle::Mla
| BibliographyStyle::ChicagoAuthorDate => {
citation.with_brackets(false).pack()
}
// notes, label and numeric
BibliographyStyle::ChicagoNotes
| BibliographyStyle::Ieee => citation.pack(),
})
});
seq.push(citation);
seq.push(
CiteElem::new(label)
.with_form(Some(CitationForm::Prose))
.pack(),
);
}
}
// use v(0.9em, weak: true) bring the attribution closer to the quote
// Use v(0.9em, weak: true) bring the attribution closer to the
// quote.
let weak_v = VElem::weak(Spacing::Rel(Em::new(0.9).into())).pack();
realized += weak_v + Content::sequence(seq).aligned(Align::END);
}
realized = PadElem::new(realized).pack();
} else if let Some(Attribution::Label(label)) = self.attribution(styles) {
realized += SpaceElem::new().pack() + CiteElem::new(vec![label.0]).pack();
realized += SpaceElem::new().pack() + CiteElem::new(label).pack();
}
Ok(realized)

View File

@ -564,6 +564,11 @@ impl<T> StyleVec<T> {
self.items.iter()
}
/// Extract the contained items.
pub fn into_items(self) -> Vec<T> {
self.items
}
/// Iterate over the contained style lists. Note that zipping this with
/// `items()` does not yield the same result as calling `iter()` because
/// this method only returns lists once that are shared by consecutive

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -0,0 +1,5 @@
// Test the full bibliography.
---
#set page(paper: "a6")
#bibliography("/files/works.bib", full: true)

View File

@ -2,8 +2,9 @@
---
#set page(width: 200pt)
= Details
See also #cite("arrgh", "distress", supplement: [p. 22]), @arrgh[p. 4], and @distress[p. 5].
See also @arrgh #cite(<distress>, supplement: [p.~22]), @arrgh[p.~4], and @distress[p.~5].
#bibliography("/files/works.bib")
---
@ -16,23 +17,18 @@ See also #cite("arrgh", "distress", supplement: [p. 22]), @arrgh[p. 4], and @dis
)
#line(length: 100%)
#[#set cite(brackets: false)
As described by @netwok],
As described by #cite(<netwok>, form: "prose"),
the net-work is a creature of its own.
This is close to piratery! @arrgh
And quark! @quark
---
// Error: 15-55 duplicate bibliography keys: arrgh, distress, glacier-melt, issue201, mcintosh_anxiety, netwok, psychology25, quark, restful, sharing, tolkien54
#bibliography(("/files/works.bib", "/files/works.bib"))
---
#set page(width: 200pt)
#set heading(numbering: "1.")
#show bibliography: set heading(numbering: "1.")
= Multiple Bibs
Now we have multiple bibliographies containing #cite("glacier-melt", "keshav2007read")
Now we have multiple bibliographies containing @glacier-melt @keshav2007read
#bibliography(("/files/works.bib", "/files/works_too.bib"))
---
@ -43,3 +39,6 @@ Now we have multiple bibliographies containing #cite("glacier-melt", "keshav2007
@arrgh
#bibliography("/files/works.bib")
---
// Error: 15-55 duplicate bibliography keys: netwok, issue201, arrgh, quark, distress, glacier-melt, tolkien54, sharing, restful, mcintosh_anxiety, psychology25
#bibliography(("/files/works.bib", "/files/works.bib"))

View File

@ -0,0 +1,10 @@
// Test citation forms.
---
#set page(width: 200pt)
Nothing: #cite(<arrgh>, form: none)
#cite(<netwok>, form: "prose") say stuff.
#bibliography("/files/works.bib", style: "apa")

View File

@ -54,7 +54,6 @@
=== Lower heading
---
// Error: 2-23 cannot outline cite
#outline(target: cite)
#cite("arrgh", "distress", supplement: [p. 22])
#bibliography("/files/works.bib")
// Error: 2-27 cannot outline metadata
#outline(target: metadata)
#metadata("hello")

View File

@ -18,6 +18,7 @@ And I quote: #quote(attribution: [René Descartes])[cogito, ergo sum].
---
// Spacing with other blocks
#set quote(block: true)
#set text(8pt)
#lorem(10)
#quote(lorem(10))
@ -25,27 +26,35 @@ And I quote: #quote(attribution: [René Descartes])[cogito, ergo sum].
---
// Inline citation
#bibliography("/files/works.bib")
#set text(8pt)
#quote(attribution: <tolkien54>)[In a hole in the ground there lived a hobbit.]
#set text(0pt)
#bibliography("/files/works.bib")
---
// Citation-format: label or numeric
#set text(8pt)
#set quote(block: true)
#bibliography("/files/works.bib", style: "ieee")
#quote(attribution: <tolkien54>)[In a hole in the ground there lived a hobbit.]
#set text(0pt)
#bibliography("/files/works.bib", style: "ieee")
---
// Citation-format: note
#set text(8pt)
#set quote(block: true)
#bibliography("/files/works.bib", style: "chicago-notes")
#quote(attribution: <tolkien54>)[In a hole in the ground there lived a hobbit.]
#set text(0pt)
#bibliography("/files/works.bib", style: "chicago-notes")
---
// Citation-format: author-date or author
#set text(8pt)
#set quote(block: true)
#bibliography("/files/works.bib", style: "apa")
#quote(attribution: <tolkien54>)[In a hole in the ground there lived a hobbit.]
#set text(0pt)
#bibliography("/files/works.bib", style: "apa")