mirror of
https://github.com/typst/typst
synced 2025-08-14 23:18:32 +08:00
wip: bibliography entry
This commit is contained in:
parent
a543ee9445
commit
f4b02fa705
@ -16,6 +16,7 @@ use hayagriva::{
|
|||||||
};
|
};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
use typst_macros::{cast, scope};
|
||||||
use typst_syntax::{Span, Spanned};
|
use typst_syntax::{Span, Spanned};
|
||||||
use typst_utils::{Get, ManuallyHash, NonZeroExt, PicoStr};
|
use typst_utils::{Get, ManuallyHash, NonZeroExt, PicoStr};
|
||||||
|
|
||||||
@ -28,8 +29,8 @@ use crate::foundations::{
|
|||||||
};
|
};
|
||||||
use crate::introspection::{Introspector, Locatable, Location};
|
use crate::introspection::{Introspector, Locatable, Location};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
BlockBody, BlockElem, Em, GridCell, GridChild, GridElem, GridItem, HElem, PadElem,
|
BlockBody, BlockElem, Em, GridCell, GridChild, GridElem, GridItem, HElem, Length,
|
||||||
Sides, Sizing, TrackSizings,
|
PadElem, Rel, Sides, Sizing, Spacing, TrackSizings,
|
||||||
};
|
};
|
||||||
use crate::loading::{DataSource, Load};
|
use crate::loading::{DataSource, Load};
|
||||||
use crate::model::{
|
use crate::model::{
|
||||||
@ -85,7 +86,7 @@ use crate::World;
|
|||||||
///
|
///
|
||||||
/// #bibliography("works.bib")
|
/// #bibliography("works.bib")
|
||||||
/// ```
|
/// ```
|
||||||
#[elem(Locatable, Synthesize, Show, ShowSet, LocalName)]
|
#[elem(scope, Locatable, Synthesize, Show, ShowSet, LocalName)]
|
||||||
pub struct BibliographyElem {
|
pub struct BibliographyElem {
|
||||||
/// One or multiple paths to or raw bytes for Hayagriva `.yml` and/or
|
/// One or multiple paths to or raw bytes for Hayagriva `.yml` and/or
|
||||||
/// BibLaTeX `.bib` files.
|
/// BibLaTeX `.bib` files.
|
||||||
@ -152,6 +153,12 @@ pub struct BibliographyElem {
|
|||||||
pub region: Option<Region>,
|
pub region: Option<Region>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[scope]
|
||||||
|
impl BibliographyElem {
|
||||||
|
#[elem]
|
||||||
|
type BibliographyEntry;
|
||||||
|
}
|
||||||
|
|
||||||
impl BibliographyElem {
|
impl BibliographyElem {
|
||||||
/// Find the document's bibliography.
|
/// Find the document's bibliography.
|
||||||
pub fn find(introspector: Tracked<Introspector>) -> StrResult<Packed<Self>> {
|
pub fn find(introspector: Tracked<Introspector>) -> StrResult<Packed<Self>> {
|
||||||
@ -220,6 +227,8 @@ impl Show for Packed<BibliographyElem> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Does this get the keys in the same order as the loop below with references?
|
||||||
|
let keys = BibliographyElem::keys(engine.introspector);
|
||||||
let works = Works::generate(engine).at(span)?;
|
let works = Works::generate(engine).at(span)?;
|
||||||
let references = works
|
let references = works
|
||||||
.references
|
.references
|
||||||
@ -229,17 +238,24 @@ impl Show for Packed<BibliographyElem> {
|
|||||||
|
|
||||||
if references.iter().any(|(prefix, _)| prefix.is_some()) {
|
if references.iter().any(|(prefix, _)| prefix.is_some()) {
|
||||||
let row_gutter = ParElem::spacing_in(styles);
|
let row_gutter = ParElem::spacing_in(styles);
|
||||||
|
|
||||||
let mut cells = vec![];
|
let mut cells = vec![];
|
||||||
for (prefix, reference) in references {
|
|
||||||
cells.push(GridChild::Item(GridItem::Cell(
|
// FIXME: Not quite correct as `prefix` and `body` are now one grid cell instead of two (how to "split" content?)!
|
||||||
Packed::new(GridCell::new(prefix.clone().unwrap_or_default()))
|
for (idx, (prefix, reference)) in references.iter().enumerate() {
|
||||||
.spanned(span),
|
let indent =
|
||||||
)));
|
if works.hanging_indent { Some(INDENT.into()) } else { None };
|
||||||
cells.push(GridChild::Item(GridItem::Cell(
|
let entry = BibliographyEntry::new(keys[idx].0, reference.clone())
|
||||||
Packed::new(GridCell::new(reference.clone())).spanned(span),
|
.with_prefix(prefix.clone())
|
||||||
)));
|
.with_indent(indent);
|
||||||
|
|
||||||
|
cells.push(
|
||||||
|
entry
|
||||||
|
.pack()
|
||||||
|
.try_into()
|
||||||
|
.expect("todo: conversion content to GridChild"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
seq.push(
|
seq.push(
|
||||||
GridElem::new(cells)
|
GridElem::new(cells)
|
||||||
.with_columns(TrackSizings(smallvec![Sizing::Auto; 2]))
|
.with_columns(TrackSizings(smallvec![Sizing::Auto; 2]))
|
||||||
@ -249,20 +265,9 @@ impl Show for Packed<BibliographyElem> {
|
|||||||
.spanned(span),
|
.spanned(span),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
for (_, reference) in references {
|
for (idx, (_, reference)) in references.iter().enumerate() {
|
||||||
let realized = reference.clone();
|
let entry = BibliographyEntry::new(keys[idx].0, reference.clone());
|
||||||
let block = if works.hanging_indent {
|
seq.push(entry.pack().spanned(span));
|
||||||
let body = HElem::new((-INDENT).into()).pack() + realized;
|
|
||||||
let inset = Sides::default()
|
|
||||||
.with(TextElem::dir_in(styles).start(), Some(INDENT.into()));
|
|
||||||
BlockElem::new()
|
|
||||||
.with_body(Some(BlockBody::Content(body)))
|
|
||||||
.with_inset(inset)
|
|
||||||
} else {
|
|
||||||
BlockElem::new().with_body(Some(BlockBody::Content(realized)))
|
|
||||||
};
|
|
||||||
|
|
||||||
seq.push(block.pack().spanned(span));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,6 +355,67 @@ impl Debug for Bibliography {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a single entry in a bibliography.
|
||||||
|
///
|
||||||
|
/// Exposes the citation key, the citation prefix and the formatted entry.
|
||||||
|
#[elem(scope, name = "entry", title = "Bibliography Entry", Show)]
|
||||||
|
pub struct BibliographyEntry {
|
||||||
|
/// The citation key that identifies the entry in the bibliography.
|
||||||
|
#[required]
|
||||||
|
pub key: Label,
|
||||||
|
|
||||||
|
/// The fully formatted entry body.
|
||||||
|
#[required]
|
||||||
|
pub body: Content,
|
||||||
|
|
||||||
|
/// Optional prefix for citation styles which use them, e.g., IEEE.
|
||||||
|
pub prefix: Option<Content>,
|
||||||
|
|
||||||
|
/// Whether the citation style has a hanging indent.
|
||||||
|
indent: Option<Rel<Length>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[scope]
|
||||||
|
impl BibliographyEntry {}
|
||||||
|
|
||||||
|
impl Show for Packed<BibliographyEntry> {
|
||||||
|
#[typst_macros::time(name = "bibliography.entry", span = self.span())]
|
||||||
|
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
|
if self.prefix.is_some() {
|
||||||
|
let prefix = GridChild::Item(GridItem::Cell(
|
||||||
|
Packed::new(GridCell::new(self.prefix(styles).unwrap()))
|
||||||
|
.spanned(self.span()),
|
||||||
|
))
|
||||||
|
.into_value();
|
||||||
|
let body = GridChild::Item(GridItem::Cell(
|
||||||
|
Packed::new(GridCell::new(self.body.clone())).spanned(self.span()),
|
||||||
|
))
|
||||||
|
.into_value();
|
||||||
|
|
||||||
|
return Ok(Content::sequence([prefix.display(), body.display()]));
|
||||||
|
} else {
|
||||||
|
let block = if let Some(indent) = self.indent(styles) {
|
||||||
|
let body = HElem::new(Spacing::Rel(-indent)).pack() + self.body.clone();
|
||||||
|
let inset =
|
||||||
|
Sides::default().with(TextElem::dir_in(styles).start(), Some(indent));
|
||||||
|
|
||||||
|
BlockElem::new()
|
||||||
|
.with_body(Some(BlockBody::Content(body)))
|
||||||
|
.with_inset(inset)
|
||||||
|
} else {
|
||||||
|
BlockElem::new().with_body(Some(BlockBody::Content(self.body.clone())))
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(block.pack().spanned(self.span()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cast! {
|
||||||
|
BibliographyEntry,
|
||||||
|
v: Content => v.unpack::<Self>().map_err(|_| "expected bibliography entry")?
|
||||||
|
}
|
||||||
|
|
||||||
/// Decode on library from one data source.
|
/// Decode on library from one data source.
|
||||||
fn decode_library(source: &DataSource, data: &Bytes) -> StrResult<Library> {
|
fn decode_library(source: &DataSource, data: &Bytes) -> StrResult<Library> {
|
||||||
let src = data.as_str().map_err(FileError::from)?;
|
let src = data.as_str().map_err(FileError::from)?;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user