diff --git a/assets/files/works.bib b/assets/files/works.bib index 72f06db1d..62be35c68 100644 --- a/assets/files/works.bib +++ b/assets/files/works.bib @@ -1,4 +1,4 @@ -@article{stupid, +@article{netwok, title={At-scale impact of the {Net Wok}: A culinarically holistic investigation of distributed dumplings}, author={Astley, Rick and Morris, Linda}, journal={Armenian Journal of Proceedings}, @@ -26,7 +26,7 @@ author={Leeson, Peter T.}, } -@misc{cannonfodder, +@misc{distress, title={An Insight into Bibliographical Distress}, author={Aldrin, Buzz} } diff --git a/docs/src/reference/details.yml b/docs/src/reference/details.yml index 789e53902..25799402d 100644 --- a/docs/src/reference/details.yml +++ b/docs/src/reference/details.yml @@ -95,10 +95,17 @@ visualize: | be in the future. meta: | - Document structuring and metadata setup. + Document structuring, introspection, and metadata configuration. - Here, you can find functions to structure your document and configure its - metadata. + Here, you can find functions to structure your document and interact with that + structure. This includes section headings and figures, bibliography + management, cross-referencing and more. + + Moreover, this category is home to Typst's introspection capabilities: With + the `counter` function, you can access and manipulate page, section, figure, + and equation counters or create custom ones. And the `query` function lets you + search for elements in the document to construct things like a list of + figures or headers which show the current chapter title. symbols: | These two modules give names to symbols and emoji to make them easy to insert @@ -114,6 +121,8 @@ sym: | [formulas]($category/math), these symbols can be used without the `#sym.` prefix. + The `d` in an integral's `dx` can be written as `[$dif x$]`. + emoji: | Named emoji. diff --git a/docs/src/reference/scripting.md b/docs/src/reference/scripting.md index 1aa399d1a..15e4be910 100644 --- a/docs/src/reference/scripting.md +++ b/docs/src/reference/scripting.md @@ -178,8 +178,7 @@ can be either: - a [dictionary]($type/dictionary) that has the specified key, - a [symbols]($type/symbol) that has the specified modifier, - a [module]($type/module) containing the specified definition, -- [content]($type/content) that exposes the specified field. Most elements - expose some or all of the non-settable arguments passed to them as fields. +- [content]($type/content) that has the specified field. ```example #let dict = (greet: "Hello") diff --git a/docs/src/reference/styling.md b/docs/src/reference/styling.md index 4997bf197..a3cc3c2eb 100644 --- a/docs/src/reference/styling.md +++ b/docs/src/reference/styling.md @@ -13,15 +13,17 @@ of elements. ## Set rules { #set-rules } With set rules, you can customize the appearance of elements. They are written as a [function call]($type/function) to the respective function preceded by the -`{set}` keyword (or `[#set]` in markup). Only settable parameters must be -provided as arguments. Refer to each function's documentation for a list of -settable parameters. In the example below, we use two set rules to change the -[font family]($func/text.family) and -[heading numbering]($func/heading.numbering) style. +`{set}` keyword (or `[#set]` in markup). Only optional parameters of that +function can be provided to the set rule. Refer to each function's documentation +to see which parameters are optional. In the example below, we use two set rules +to change the [font family]($func/text.family) and +[heading numbering]($func/heading.numbering). ```example -#set text(font: "New Computer Modern") #set heading(numbering: "I.") +#set text( + font: "New Computer Modern" +) = Introduction With set rules, you can style @@ -35,9 +37,10 @@ your document. Below, we use a content block to scope the list styling to one particular list. ```example -This list is affected: -#[#set list(marker: [--]) - - Dash] +This list is affected: #[ + #set list(marker: [--]) + - Dash +] This one is not: - Bullet @@ -87,8 +90,7 @@ fantasy encyclopedia. #set align(center) #set text(font: "Inria Serif") \~ #emph(it.body) - #(counter(heading) - .display(it.numbering)) \~ + #counter(heading).display() \~ ] = Dragon diff --git a/docs/src/reference/types.md b/docs/src/reference/types.md index a64f4eb39..3a768d27d 100644 --- a/docs/src/reference/types.md +++ b/docs/src/reference/types.md @@ -379,18 +379,16 @@ the resulting parts. - returns: array # Content -Representation of text, elements, and more. +Representation of document content. This type is at the heart of Typst. All markup you write and most -[functions]($type/function) you call produce content values. You -can create a content value by enclosing markup in square brackets. This is also -how you pass content to functions. Typst does not allow you to peek into -content, but you can affect its appearance in various ways using set and show -rules. See the chapter on [styling]($styling) for more details. +[functions]($type/function) you call produce content values. You can create a +content value by enclosing markup in square brackets. This is also how you pass +content to functions. ```example -#type([*Hello!*]) \ -#strong[Hello!] +Type of *Hello!* is +#type([*Hello!*]) ``` Content can be added with the `+` operator, @@ -398,21 +396,62 @@ Content can be added with the `+` operator, integers. Wherever content is expected, you can also pass a [string]($type/string) or `{none}`. -### Reactivity -Content is reactive to the styles that are active where it is inserted. -When you write a set or show rule, content that was _created_ before the show -rule is stilled affected by the show rule if it is _inserted_ after the show -rule. +## Representation +Content consists of elements with fields. When constructing an element with +its _element function,_ you provide these fields as arguments and when you have +a content value, you can access its fields with +[field access syntax]($scripting/#field-access). -```example -// Content is created here. -#let mytext = [= A heading] +Some fields are required: These must be provided when constructing an element +and as a consequence, they are always available through field access on content +of that type. Required fields are marked as such in the documentation. -// But still affected by the -// styles that are active here. -#show heading: set text(green) -#mytext -``` +Most fields are optional: Like required fields, they can be passed to the +element function to configure them for a single element. However, these can also +be configured with [set rules]($styling/#set-rules) to apply them to all +elements within a scope. Optional fields are only available with field access +syntax when they are were explicitly passed to the element function, not when +they result from a set rule. + +Each element has a default appearance. However, you can also completely +customize its appearance with a [show rule]($styling/#show-rules). The show rule +is passed the element. It can access the element's field and produce arbitrary +content from it. + +In the web app, you can hover over a content variable to see exactly which +elements the content is composed of and what fields they have. Alternatively, +you can inspect the output of the [`repr`]($func/repr) function. + +## Methods +### func() +The content's element function. This function can be used to create the element +contained in this content. It can be used in set and show rules for the element. +Can be compared with global functions to check whether you have a specific +kind of element. + +- returns: function + +### has() +Whether the content has the specified field. + +- field: string (positional, required) + The field to look for. +- returns: boolean + +### at() +Access the specified field on the content. + +- field: string (positional, required) + The field to access. +- returns: any + +### location() +The location of the content. This is only available on content returned by +[query]($func/query), for other content it will fail with an error. The +resulting location can be used with [counters]($func/counter), +[state]($func/state) and [queries]($func/query). + +- returns: location # Array A sequence of values. diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs index a8a806ad6..61a1f9c45 100644 --- a/library/src/layout/page.rs +++ b/library/src/layout/page.rs @@ -318,7 +318,7 @@ impl PageElem { Numbering::Func(_) => true, }; Counter::new(CounterKey::Page) - .display(numbering, both) + .display(Some(numbering), both) .aligned(self.number_align(styles)) }) }); diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs index cf6652039..4e91dd51c 100644 --- a/library/src/math/mod.rs +++ b/library/src/math/mod.rs @@ -158,7 +158,7 @@ pub struct EquationElem { } impl Synthesize for EquationElem { - fn synthesize(&mut self, _: &Vt, styles: StyleChain) { + fn synthesize(&mut self, styles: StyleChain) { self.push_block(self.block(styles)); self.push_numbering(self.numbering(styles)); } @@ -216,7 +216,7 @@ impl Layout for EquationElem { if let Some(numbering) = self.numbering(styles) { let pod = Regions::one(regions.base(), Axes::splat(false)); let counter = Counter::of(Self::func()) - .display(numbering, false) + .display(Some(numbering), false) .layout(vt, styles, pod)? .into_frame(); diff --git a/library/src/meta/bibliography.rs b/library/src/meta/bibliography.rs index 5f244a8f7..2223ce2af 100644 --- a/library/src/meta/bibliography.rs +++ b/library/src/meta/bibliography.rs @@ -16,6 +16,31 @@ use crate::text::TextElem; /// A bibliography / reference listing. /// +/// You can create a new bibliography by calling this function with a path +/// to a bibliography file in either one of two formats: +/// +/// - A Hayagriva `.yml` file. Hayagriva is a new bibliography file format +/// designed for use with Typst. Visit its +/// [documentation](https://github.com/typst/hayagriva/blob/main/docs/file-format.md) +/// for more details. +/// - A BibLaTeX `.bib` file. +/// +/// As soon as you add a bibliography somewhere in your document, you can start +/// citing things with reference syntax (`[@key]`) or explicit calls to the +/// [citation]($func/cite) function (`[#cite("key")]`). The bibliography will +/// only show entries for works that were referenced in the document. +/// +/// # Example +/// ```example +/// This was already noted by +/// pirates long ago. @arrgh +/// +/// Multiple sources say ... +/// #cite("arrgh", "netwok"). +/// +/// #bibliography("works.bib") +/// ``` +/// /// Display: Bibliography /// Category: meta #[element(Locatable, Synthesize, Show, LocalName)] @@ -90,7 +115,7 @@ impl BibliographyElem { } impl Synthesize for BibliographyElem { - fn synthesize(&mut self, _: &Vt, styles: StyleChain) { + fn synthesize(&mut self, styles: StyleChain) { self.push_style(self.style(styles)); } } @@ -192,21 +217,64 @@ impl BibliographyStyle { } } -/// A citation of another work. +/// Cite a work from the bibliography. +/// +/// Before you starting citing, you need to add a +/// [bibliography]($func/bibliography) somewhere in your document. +/// +/// # Example +/// ```example +/// This was already noted by +/// pirates long ago. @arrgh +/// +/// Multiple sources say ... +/// #cite("arrgh", "netwok"). +/// +/// #bibliography("works.bib") +/// ``` +/// +/// # Syntax +/// This function indirectly has dedicated syntax. [References]($func/ref) +/// can be used to cite works from the bibliography. The label then +/// corresponds to the citation key. /// /// Display: Citation /// Category: meta #[element(Locatable, Synthesize, Show)] pub struct CiteElem { - /// The citation key. + /// The citation keys that identify the elements that shall be cited in + /// the bibliography. + /// + /// Reference syntax supports only a single key. #[variadic] pub keys: Vec, /// 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 over and + /// over again. @distress[p.~7] + /// + /// #bibliography("works.bib") + /// ``` #[positional] pub supplement: Option, /// Whether the citation should include brackets. + /// + /// ```example + /// #set cite(brackets: false) + /// + /// @netwok follow these methods + /// in their work ... + /// + /// #bibliography( + /// "works.bib", + /// style: "author-date", + /// ) + /// ``` #[default(true)] pub brackets: bool, @@ -214,11 +282,19 @@ pub struct CiteElem { /// /// When set to `{auto}`, automatically picks the preferred citation style /// for the bibliography's style. + /// + /// ```example + /// #set cite(style: "alphanumerical") + /// Alphanumerical references. + /// @netwok + /// + /// #bibliography("works.bib") + /// ``` pub style: Smart, } impl Synthesize for CiteElem { - fn synthesize(&mut self, _: &Vt, styles: StyleChain) { + fn synthesize(&mut self, styles: StyleChain) { self.push_supplement(self.supplement(styles)); self.push_brackets(self.brackets(styles)); self.push_style(self.style(styles)); @@ -243,6 +319,11 @@ impl Show for CiteElem { } } +cast_from_value! { + CiteElem, + v: Content => v.to::().cloned().ok_or("expected citation")?, +} + /// A citation style. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)] pub enum CitationStyle { @@ -261,6 +342,12 @@ pub enum CitationStyle { Keys, } +impl CitationStyle { + fn is_short(self) -> bool { + matches!(self, Self::Numerical | Self::Alphanumerical | Self::Keys) + } +} + /// Fully formatted citations and references. #[derive(Default)] struct Works { @@ -280,7 +367,7 @@ impl Works { ])) .into_iter() .map(|elem| match elem.to::() { - Some(reference) => reference.to_citation(StyleChain::default()), + Some(reference) => reference.citation().unwrap(), _ => elem.to::().unwrap().clone(), }) .collect(); @@ -371,6 +458,10 @@ fn create( ) .display; + if style.is_short() { + display.value = display.value.replace(' ', "\u{a0}"); + } + if brackets && len == 1 { display = display.with_default_brackets(&*citation_style); } diff --git a/library/src/meta/counter.rs b/library/src/meta/counter.rs index b93b8d150..c407219f0 100644 --- a/library/src/meta/counter.rs +++ b/library/src/meta/counter.rs @@ -5,8 +5,9 @@ use ecow::{eco_vec, EcoVec}; use smallvec::{smallvec, SmallVec}; use typst::eval::Tracer; -use super::{Numbering, NumberingPattern}; +use super::{FigureElem, HeadingElem, Numbering, NumberingPattern}; use crate::layout::PageElem; +use crate::math::EquationElem; use crate::prelude::*; /// Count through pages, elements, and more. @@ -165,7 +166,7 @@ use crate::prelude::*; /// which one doesn't matter. After the heading follow two calls to `step()`, /// so the final value is `{(6,)}`. /// -/// ## Different kinds of state +/// ## Other kinds of state /// The `counter` function is closely related to [state]($func/state) function. /// Read its documentation for more details on state management in Typst and /// why it doesn't just use normal variables for counters. @@ -181,6 +182,9 @@ use crate::prelude::*; /// varies, e.g. for the heading argument, you can use an /// [argument sink]($type/arguments). /// +/// If this is omitted, displays the counter with the numbering style for the +/// counted element or with the pattern `{"1.1"}` if no such style exists. +/// /// - returns: content /// /// ### step() @@ -275,14 +279,10 @@ impl Counter { mut args: Args, span: Span, ) -> SourceResult { - let pattern = |s| NumberingPattern::from_str(s).unwrap().into(); let value = match method { - "display" => self - .display( - args.eat()?.unwrap_or_else(|| pattern("1.1")), - args.named("both")?.unwrap_or(false), - ) - .into(), + "display" => { + self.display(args.eat()?, args.named("both")?.unwrap_or(false)).into() + } "step" => self .update(CounterUpdate::Step( args.named("level")?.unwrap_or(NonZeroUsize::ONE), @@ -298,7 +298,7 @@ impl Counter { } /// Display the current value of the counter. - pub fn display(self, numbering: Numbering, both: bool) -> Content { + pub fn display(self, numbering: Option, both: bool) -> Content { DisplayElem::new(self, numbering, both).pack() } @@ -574,7 +574,7 @@ struct DisplayElem { /// The numbering to display the counter with. #[required] - numbering: Numbering, + numbering: Option, /// Whether to display both the current and final value. #[required] @@ -582,10 +582,28 @@ struct DisplayElem { } impl Show for DisplayElem { - fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult { + fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult { let location = self.0.location().unwrap(); let counter = self.counter(); - let numbering = self.numbering(); + let numbering = self + .numbering() + .or_else(|| { + let CounterKey::Selector(Selector::Elem(func, _)) = counter.0 else { + return None; + }; + + if func == HeadingElem::func() { + HeadingElem::numbering_in(styles) + } else if func == FigureElem::func() { + FigureElem::numbering_in(styles) + } else if func == EquationElem::func() { + EquationElem::numbering_in(styles) + } else { + None + } + }) + .unwrap_or_else(|| NumberingPattern::from_str("1.1").unwrap().into()); + let state = if self.both() { counter.both(vt, location)? } else { diff --git a/library/src/meta/figure.rs b/library/src/meta/figure.rs index 9251f3ef3..73a8ef5b1 100644 --- a/library/src/meta/figure.rs +++ b/library/src/meta/figure.rs @@ -10,7 +10,7 @@ use crate::text::TextElem; /// ## Example /// ```example /// = Pipeline -/// @fig-lab shows the central step of +/// @lab shows the central step of /// our molecular testing pipeline. /// /// #figure( @@ -18,7 +18,7 @@ use crate::text::TextElem; /// caption: [ /// The molecular testing pipeline. /// ], -/// ) +/// ) /// ``` /// /// Display: Figure @@ -43,7 +43,7 @@ pub struct FigureElem { } impl Synthesize for FigureElem { - fn synthesize(&mut self, _: &Vt, styles: StyleChain) { + fn synthesize(&mut self, styles: StyleChain) { self.push_numbering(self.numbering(styles)); } } @@ -57,7 +57,7 @@ impl Show for FigureElem { let name = self.local_name(TextElem::lang_in(styles)); caption = TextElem::packed(eco_format!("{name}\u{a0}")) + Counter::of(Self::func()) - .display(numbering, false) + .display(Some(numbering), false) .spanned(self.span()) + TextElem::packed(": ") + caption; diff --git a/library/src/meta/heading.rs b/library/src/meta/heading.rs index 1eaca187f..ca38d5465 100644 --- a/library/src/meta/heading.rs +++ b/library/src/meta/heading.rs @@ -80,7 +80,7 @@ pub struct HeadingElem { } impl Synthesize for HeadingElem { - fn synthesize(&mut self, _: &Vt, styles: StyleChain) { + fn synthesize(&mut self, styles: StyleChain) { self.push_level(self.level(styles)); self.push_numbering(self.numbering(styles)); self.push_outlined(self.outlined(styles)); @@ -92,7 +92,7 @@ impl Show for HeadingElem { let mut realized = self.body(); if let Some(numbering) = self.numbering(styles) { realized = Counter::of(Self::func()) - .display(numbering, false) + .display(Some(numbering), false) .spanned(self.span()) + HElem::new(Em::new(0.3).into()).with_weak(true).pack() + realized; diff --git a/library/src/meta/link.rs b/library/src/meta/link.rs index c82a74436..d8cb779a1 100644 --- a/library/src/meta/link.rs +++ b/library/src/meta/link.rs @@ -1,7 +1,7 @@ use crate::prelude::*; use crate::text::{Hyphenate, TextElem}; -/// Link to a URL or another location in the document. +/// Link to a URL or a location in the document. /// /// The link function makes its positional `body` argument clickable and links /// it to the destination specified by the `dest` argument. By default, links diff --git a/library/src/meta/reference.rs b/library/src/meta/reference.rs index 6e794d2dd..c103dd025 100644 --- a/library/src/meta/reference.rs +++ b/library/src/meta/reference.rs @@ -2,30 +2,37 @@ use super::{BibliographyElem, CiteElem, Counter, LocalName, Numbering}; use crate::prelude::*; use crate::text::TextElem; -/// A reference to a label. +/// A reference to a label or bibliography. /// /// The reference function produces a textual reference to a label. For example, /// a reference to a heading will yield an appropriate string such as "Section /// 1" for a reference to the first heading. The references are also links to /// the respective element. /// +/// Reference syntax can also be used to [cite]($func/cite) from a bibliography. +/// /// # Example /// ```example /// #set heading(numbering: "1.") +/// #set math.equation(numbering: "(1)") /// /// = Introduction -/// Recent developments in typesetting -/// software have rekindled hope in -/// previously frustrated researchers. +/// Recent developments in +/// typesetting software have +/// rekindled hope in previously +/// frustrated researchers. @distress /// As shown in @results, we ... /// /// = Results -/// We evaluate our method in a -/// series of tests. @perf discusses -/// the performance aspects of ... +/// We discuss our approach in +/// comparison with others. /// /// == Performance -/// As described in @intro, we ... +/// @slow demonstrates what slow +/// software looks like. +/// $ O(n) = 2^n $ +/// +/// #bibliography("works.bib") /// ``` /// /// ## Syntax @@ -33,9 +40,12 @@ use crate::text::TextElem; /// created by typing an `@` followed by the name of the label (e.g. /// `[= Introduction ]` can be referenced by typing `[@intro]`). /// +/// To customize the supplement, add content in square brackets after the +/// reference: `[@intro[Chapter]]`. +/// /// Display: Reference /// Category: meta -#[element(Locatable, Show)] +#[element(Synthesize, Locatable, Show)] pub struct RefElem { /// The target label that should be referenced. #[required] @@ -58,9 +68,22 @@ pub struct RefElem { /// /// = Introduction /// In @intro, we see how to turn - /// Sections into Chapters. + /// Sections into Chapters. And + /// in @intro[Part], it is done + /// manually. /// ``` pub supplement: Smart>, + + /// A synthesized citation. + #[synthesized] + pub citation: Option, +} + +impl Synthesize for RefElem { + fn synthesize(&mut self, styles: StyleChain) { + let citation = self.to_citation(styles); + self.push_citation(Some(citation)); + } } impl Show for RefElem { @@ -77,7 +100,7 @@ impl Show for RefElem { bail!(self.span(), "label occurs in the document and its bibliography"); } - return self.to_citation(styles).show(vt, styles); + return Ok(self.to_citation(styles).pack()); } let [elem] = matches.as_slice() else { @@ -126,11 +149,12 @@ impl RefElem { /// Turn the rference into a citation. pub fn to_citation(&self, styles: StyleChain) -> CiteElem { let mut elem = CiteElem::new(vec![self.target().0]); + elem.0.set_location(self.0.location().unwrap()); + elem.synthesize(styles); elem.push_supplement(match self.supplement(styles) { Smart::Custom(Some(Supplement::Content(content))) => Some(content), _ => None, }); - elem.0.set_location(self.0.location().unwrap()); elem } } diff --git a/library/src/text/raw.rs b/library/src/text/raw.rs index d24254ed1..c2630aef3 100644 --- a/library/src/text/raw.rs +++ b/library/src/text/raw.rs @@ -121,7 +121,7 @@ impl RawElem { } impl Synthesize for RawElem { - fn synthesize(&mut self, _: &Vt, styles: StyleChain) { + fn synthesize(&mut self, styles: StyleChain) { self.push_lang(self.lang(styles)); } } diff --git a/src/model/realize.rs b/src/model/realize.rs index 51d69fdc8..10e1b0e2e 100644 --- a/src/model/realize.rs +++ b/src/model/realize.rs @@ -42,7 +42,7 @@ pub fn realize( } if let Some(elem) = elem.with_mut::() { - elem.synthesize(vt, styles); + elem.synthesize(styles); } elem.mark_prepared(); @@ -165,7 +165,7 @@ pub trait Locatable {} /// rule. pub trait Synthesize { /// Prepare the element for show rule application. - fn synthesize(&mut self, vt: &Vt, styles: StyleChain); + fn synthesize(&mut self, styles: StyleChain); } /// The base recipe for an element. diff --git a/tests/typ/meta/bibliography.typ b/tests/typ/meta/bibliography.typ index ce4b8f31c..0d9c19a04 100644 --- a/tests/typ/meta/bibliography.typ +++ b/tests/typ/meta/bibliography.typ @@ -14,7 +14,7 @@ --- #set page(width: 200pt) = Details -See also #cite("arrgh", "cannonfodder", [p. 22]), @arrgh[p. 4], and @cannonfodder[p. 5]. +See also #cite("arrgh", "distress", [p. 22]), @arrgh[p. 4], and @distress[p. 5]. #bibliography("/works.bib") --- @@ -22,7 +22,8 @@ See also #cite("arrgh", "cannonfodder", [p. 22]), @arrgh[p. 4], and @cannonfodde #set page(width: 200pt) #bibliography("/works.bib", title: [Works to be cited], style: "author-date") #line(length: 100%) -As described by #cite("stupid", brackets: false), +#[#set cite(brackets: false) +As described by @netwok], the net-work is a creature of its own. This is close to piratery! @arrgh And quark! @quark diff --git a/tests/typ/meta/counter.typ b/tests/typ/meta/counter.typ index a38142168..32d16cae4 100644 --- a/tests/typ/meta/counter.typ +++ b/tests/typ/meta/counter.typ @@ -31,8 +31,7 @@ Second: #mine.display("I") #counter(heading).step() = Alpha -In #counter(heading).display(). - +In #counter(heading).display() == Beta #set heading(numbering: none)