Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse page ranges following citeproc #155

Merged
merged 30 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3f83bd2
Change page range construction for single numbers
DerDrodt Mar 20, 2024
be39404
Switch to InclusiveRange
DerDrodt Mar 21, 2024
23befaa
Update citationberg
DerDrodt Apr 9, 2024
4e68404
Fix JSON taxonomy
DerDrodt Apr 9, 2024
26540cb
Add passing test
DerDrodt Apr 9, 2024
b179c5d
Change handling of page ranges
DerDrodt Apr 23, 2024
36e9917
Merge branch 'main' into page-range
DerDrodt Apr 23, 2024
993267c
Adapt to new citationberg version
DerDrodt May 10, 2024
c8635cf
Update citationberg rev
DerDrodt May 10, 2024
8849e3b
Merge branch 'main' into page-range
DerDrodt May 10, 2024
ac278c4
Fix cslJSON
DerDrodt May 10, 2024
99c8c1b
Change provisional error message for `NumericErr`
reknih Aug 5, 2024
025c97a
Merge branch 'main' into pr/155
reknih Aug 5, 2024
bd66f01
Have CI report errors
reknih Aug 5, 2024
f269f09
Merge branch 'page-range' of https://github.com/DerDrodt/hayagriva in…
DerDrodt Aug 19, 2024
3a8542a
Merge branch 'main' into page-range
DerDrodt Aug 19, 2024
bbf99b2
Fix page ranges
DerDrodt Aug 20, 2024
aca3971
Formatting
DerDrodt Aug 20, 2024
817530a
Clippy
DerDrodt Aug 20, 2024
dfe2250
Update citationberg
DerDrodt Aug 20, 2024
b12df03
Update citationberg
reknih Sep 2, 2024
361626e
Make clippy happy
reknih Sep 2, 2024
963bd7b
Merge branch 'main' into page-range
Drodt Sep 3, 2024
a35df0c
Fix todos
DerDrodt Sep 3, 2024
0ceac2e
Formatting
DerDrodt Sep 3, 2024
6b5c7fc
Merge branch 'main' into page-range
DerDrodt Sep 5, 2024
26e7079
Do not assume there is only one page variable
reknih Oct 1, 2024
5bcba62
Use `Iterator::cmp` instead of loops
reknih Oct 1, 2024
fce95a1
Merge branch 'main' into pr/155
reknih Oct 1, 2024
de4bb72
Clippy
reknih Oct 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ archive = ["ciborium"]
csl-json = ["citationberg/json"]

[dependencies]
citationberg = { git = "https://github.com/typst/citationberg.git", rev = "61ca6a7fcc48365f805e521cc8bc1f8f679ff372" }
citationberg = { git = "https://github.com/typst/citationberg.git", rev = "e3fd3f08e0e16983b7c3514b791b64c704dc2524" }
indexmap = { version = "2.0.2", features = ["serde"] }
numerals = "0.1.4"
paste = "1.0.14"
Expand Down
14 changes: 11 additions & 3 deletions src/csl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use std::num::{NonZeroI16, NonZeroUsize};
use std::{mem, vec};

use citationberg::taxonomy::{
DateVariable, Locator, NameVariable, NumberVariable, OtherTerm, StandardVariable,
Term, Variable,
DateVariable, Locator, NameVariable, NumberVariable, OtherTerm, PageVariable,
StandardVariable, Term, Variable,
};
use citationberg::{
taxonomy as csl_taxonomy, Affixes, BaseLanguage, Citation, CitationFormat, Collapse,
Expand All @@ -30,7 +30,7 @@ use self::elem::last_text_mut_child;
pub use self::elem::{
BufWriteFormat, Elem, ElemChild, ElemChildren, ElemMeta, Formatted, Formatting,
};
use self::taxonomy::{EntryLike, NumberVariableResult};
use self::taxonomy::{EntryLike, NumberVariableResult, PageVariableResult};

#[cfg(feature = "archive")]
pub mod archive;
Expand Down Expand Up @@ -2567,6 +2567,14 @@ impl<'a, T: EntryLike> Context<'a, T> {
res
}

fn resolve_page_variable(
&self,
variable: PageVariable,
) -> Option<PageVariableResult> {
self.writing.prepare_variable_query(variable)?;
self.instance.resolve_page_variable(variable)
}

/// Resolve a name variable.
///
/// Honors suppressions.
Expand Down
179 changes: 114 additions & 65 deletions src/csl/rendering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@ use std::fmt::Write;
use std::str::FromStr;

use citationberg::taxonomy::{
Locator, NumberVariable, OtherTerm, StandardVariable, Term, Variable,
Locator, NumberOrPageVariable, NumberVariable, OtherTerm, PageVariable,
StandardVariable, Term, Variable,
};
use citationberg::{
ChooseBranch, CslMacro, DateDayForm, DateMonthForm, DatePartName, DateParts,
DateStrongAnyForm, GrammarGender, LabelPluralize, LayoutRenderingElement,
LongShortForm, NumberForm, TestPosition, TextCase, ToAffixes, ToFormatting,
LongShortForm, NumberForm, PageRangeFormat, TestPosition, TextCase, ToAffixes,
ToFormatting,
};
use citationberg::{TermForm, TextTarget};

use crate::csl::taxonomy::NumberVariableResult;
use crate::csl::taxonomy::{NumberVariableResult, PageVariableResult};
use crate::lang::{Case, SentenceCase, TitleCase};
use crate::types::{ChunkedString, Date, MaybeTyped, Numeric};
use crate::PageRanges;

use super::taxonomy::EntryLike;
use super::{Context, ElemMeta, IbidState, SpecialForm, UsageInfo};
Expand Down Expand Up @@ -81,21 +84,19 @@ impl RenderCsl for citationberg::Text {
}
_ => ctx.push_chunked(&val),
},
ResolvedTextTarget::NumberVariable(var, n) => match n {
ResolvedTextTarget::NumberVariable(_, n) => match n {
NumberVariableResult::Regular(MaybeTyped::Typed(num))
if num.will_transform() =>
{
render_typed_num(num.as_ref(), NumberForm::default(), var, None, ctx);
}
NumberVariableResult::Regular(n)
if matches!(var, NumberVariable::Page) =>
{
// TODO: Remove this hack
ctx.push_str(&n.to_str().replace('-', "–"))
render_typed_num(num.as_ref(), NumberForm::default(), None, ctx);
}
NumberVariableResult::Regular(n) => ctx.push_str(&n.to_str()),
NumberVariableResult::Transparent(n) => ctx.push_transparent(n),
},
ResolvedTextTarget::PageVariable(p) => match p {
MaybeTyped::Typed(r) => render_page_range(&r, ctx),
MaybeTyped::String(s) => ctx.push_str(&s.replace('-', "–")),
},
ResolvedTextTarget::Macro(mac) => {
for child in &mac.children {
child.render(ctx);
Expand Down Expand Up @@ -138,6 +139,9 @@ impl RenderCsl for citationberg::Text {
match target {
ResolvedTextTarget::StandardVariable(s, _) => var == Variable::Standard(s),
ResolvedTextTarget::NumberVariable(n, _) => var == Variable::Number(n),
ResolvedTextTarget::PageVariable(_) => {
var == Variable::Page(PageVariable::Page)
}
ResolvedTextTarget::Macro(mac) => {
mac.children.iter().any(|c| c.will_render(ctx, var))
}
Expand Down Expand Up @@ -170,7 +174,8 @@ impl RenderCsl for citationberg::Text {
(false, UsageInfo { has_vars: true, ..Default::default() })
}
ResolvedTextTarget::StandardVariable(_, _)
| ResolvedTextTarget::NumberVariable(_, _) => (
| ResolvedTextTarget::NumberVariable(_, _)
| ResolvedTextTarget::PageVariable(_) => (
true,
UsageInfo {
has_vars: true,
Expand Down Expand Up @@ -198,6 +203,7 @@ impl RenderCsl for citationberg::Text {
enum ResolvedTextTarget<'a, 'b> {
StandardVariable(StandardVariable, Cow<'a, ChunkedString>),
NumberVariable(NumberVariable, NumberVariableResult<'a>),
PageVariable(PageVariableResult),
Macro(&'a CslMacro),
Term(&'a str),
Value(&'b str),
Expand Down Expand Up @@ -243,6 +249,9 @@ impl<'a, 'b> ResolvedTextTarget<'a, 'b> {
TextTarget::Variable { var: Variable::Number(var), .. } => ctx
.resolve_number_variable(*var)
.map(|n| ResolvedTextTarget::NumberVariable(*var, n)),
TextTarget::Variable { var: Variable::Page(pv), .. } => {
ctx.resolve_page_variable(*pv).map(ResolvedTextTarget::PageVariable)
}
TextTarget::Variable { .. } => None,
TextTarget::Macro { name } => {
ctx.style.get_macro(name).map(ResolvedTextTarget::Macro)
Expand Down Expand Up @@ -278,7 +287,7 @@ impl RenderCsl for citationberg::Number {
Some(NumberVariableResult::Regular(MaybeTyped::Typed(num)))
if num.will_transform() =>
{
render_typed_num(num.as_ref(), self.form, self.variable, gender, ctx);
render_typed_num(num.as_ref(), self.form, gender, ctx);
}
Some(NumberVariableResult::Regular(MaybeTyped::Typed(num))) => {
write!(ctx, "{}", num).unwrap()
Expand Down Expand Up @@ -345,41 +354,31 @@ impl RenderCsl for citationberg::Number {
fn render_typed_num<T: EntryLike>(
num: &Numeric,
form: NumberForm,
variable: NumberVariable,
gender: Option<GrammarGender>,
ctx: &mut Context<T>,
) {
let normal_num = if form == NumberForm::Numeric && variable == NumberVariable::Page {
if let Some(range) = num.range() {
render_page_range(range, ctx);
false
} else {
true
}
} else {
true
};

if normal_num {
num.with_form(ctx, form, gender, ctx.ordinal_lookup()).unwrap();
}
num.with_form(ctx, form, gender, &ctx.ordinal_lookup()).unwrap();
}

fn render_page_range<T: EntryLike>(
range: std::ops::RangeInclusive<i32>,
ctx: &mut Context<T>,
) {
ctx.style
.csl
.settings
.page_range_format
.unwrap_or_default()
.format(
range,
ctx,
ctx.term(OtherTerm::PageRangeDelimiter.into(), TermForm::default(), false)
.or(Some("–")),
)
fn render_page_range<T: EntryLike>(range: &PageRanges, ctx: &mut Context<T>) {
let format = ctx.style.csl.settings.page_range_format.unwrap_or_default();
let delim = ctx
.term(OtherTerm::PageRangeDelimiter.into(), TermForm::default(), false)
.or(Some("–"));

range
.ranges
.iter()
.try_for_each(|r| match r {
crate::PageRangesPart::Ampersand => ctx.write_str(" & "),
crate::PageRangesPart::Comma => ctx.write_str(", "),
crate::PageRangesPart::EscapedRange(start, end) => PageRangeFormat::Expanded
.format(ctx, &start.to_string(), &end.to_string(), delim),
crate::PageRangesPart::SinglePage(page) => ctx.write_str(&page.to_string()),
crate::PageRangesPart::Range(start, end) => {
format.format(ctx, &start.to_string(), &end.to_string(), delim)
}
})
.unwrap();
}

Expand All @@ -393,7 +392,11 @@ fn label_pluralization(
LabelPluralize::Contextual => match variable {
NumberVariableResult::Regular(MaybeTyped::String(_)) => false,
NumberVariableResult::Regular(MaybeTyped::Typed(n)) => {
n.is_plural(label.variable.is_number_of_variable())
if let NumberOrPageVariable::Number(v) = label.variable {
n.is_plural(v.is_number_of_variable())
} else {
panic!("Incompatiable variable types")
}
Comment on lines +395 to +399
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to enforce this on a type system level but it's okay for now

}
NumberVariableResult::Transparent(_) => false,
},
Expand All @@ -406,19 +409,40 @@ impl RenderCsl for citationberg::Label {
return;
}

let Some(variable) = ctx.resolve_number_variable(self.variable) else {
return;
};
match self.variable {
NumberOrPageVariable::Number(n) => {
let Some(variable) = ctx.resolve_number_variable(n) else {
return;
};

let depth = ctx.push_elem(citationberg::Formatting::default());
let plural = label_pluralization(self, variable);
let depth = ctx.push_elem(citationberg::Formatting::default());
let plural = label_pluralization(self, variable);

let content = ctx
.term(Term::from(self.variable), self.label.form, plural)
.unwrap_or_default();
let content = ctx
.term(Term::from(self.variable), self.label.form, plural)
.unwrap_or_default();

render_label_with_var(&self.label, ctx, content);
ctx.commit_elem(depth, None, Some(ElemMeta::Label));
render_label_with_var(&self.label, ctx, content);
ctx.commit_elem(depth, None, Some(ElemMeta::Label));
}
NumberOrPageVariable::Page(pv) => {
let Some(p) = ctx.resolve_page_variable(pv) else {
return;
};

let depth = ctx.push_elem(citationberg::Formatting::default());
let plural = match p {
MaybeTyped::Typed(p) => p.is_plural(),
_ => false,
};

let content =
ctx.term(Term::from(pv), self.label.form, plural).unwrap_or_default();

render_label_with_var(&self.label, ctx, content);
ctx.commit_elem(depth, None, Some(ElemMeta::Label));
}
}
}

fn will_render<T: EntryLike>(&self, _ctx: &mut Context<T>, _var: Variable) -> bool {
Expand All @@ -427,23 +451,26 @@ impl RenderCsl for citationberg::Label {

fn will_have_info<T: EntryLike>(&self, ctx: &mut Context<T>) -> (bool, UsageInfo) {
match ctx.instance.kind {
Some(SpecialForm::VarOnly(Variable::Number(n))) if self.variable != n => {
Some(SpecialForm::VarOnly(Variable::Number(n)))
if self.variable != NumberOrPageVariable::Number(n) =>
{
return (false, UsageInfo::default());
}
Some(
SpecialForm::VarOnly(_)
| SpecialForm::OnlyFirstDate
| SpecialForm::OnlyYearSuffix,
) => {
if self.variable != NumberVariable::Locator {
if self.variable != NumberOrPageVariable::Number(NumberVariable::Locator)
{
return (true, UsageInfo::default());
}
}
_ => {}
}

// Never yield a label if the locator is set to custom.
if self.variable == NumberVariable::Locator
if self.variable == NumberOrPageVariable::Number(NumberVariable::Locator)
&& ctx
.instance
.cite_props
Expand All @@ -454,14 +481,33 @@ impl RenderCsl for citationberg::Label {
return (false, UsageInfo::default());
}

if let Some(num) = ctx.resolve_number_variable(self.variable) {
let plural = label_pluralization(self, num);
(
ctx.term(Term::from(self.variable), self.label.form, plural).is_some(),
UsageInfo::default(),
)
} else {
(false, UsageInfo::default())
match self.variable {
NumberOrPageVariable::Number(n) => {
if let Some(num) = ctx.resolve_number_variable(n) {
let plural = label_pluralization(self, num);
(
ctx.term(Term::from(self.variable), self.label.form, plural)
.is_some(),
UsageInfo::default(),
)
} else {
(false, UsageInfo::default())
}
}
NumberOrPageVariable::Page(pv) => {
if let Some(p) = ctx.resolve_page_variable(pv) {
let plural = match p {
MaybeTyped::Typed(p) => p.is_plural(),
_ => false,
};
(
ctx.term(Term::from(pv), self.label.form, plural).is_some(),
UsageInfo::default(),
)
} else {
(false, UsageInfo::default())
}
}
}
}
}
Expand Down Expand Up @@ -1091,6 +1137,9 @@ impl<'a, 'b, T: EntryLike> Iterator for BranchConditionIter<'a, 'b, T> {
Variable::Name(n) => {
!self.ctx.resolve_name_variable(n).is_empty()
}
Variable::Page(pv) => {
self.ctx.resolve_page_variable(pv).is_some()
}
})
} else {
None
Expand Down
13 changes: 13 additions & 0 deletions src/csl/sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,19 @@ impl<'a> StyleContext<'a> {
(None, None) => Ordering::Equal,
}
}
SortKey::Variable { variable: Variable::Page(pv), .. } => {
let a =
InstanceContext::sort_instance(a, a_idx).resolve_page_variable(*pv);
let b =
InstanceContext::sort_instance(b, b_idx).resolve_page_variable(*pv);

match (a, b) {
(Some(a), Some(b)) => a.csl_cmp(&b),
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(None, None) => Ordering::Equal,
}
}
SortKey::MacroName {
name,
names_min,
Expand Down
Loading