render bibliography

This commit is contained in:
Maciej Jur 2024-04-28 00:26:19 +02:00
parent 115613071c
commit d87069c17d
Signed by: kamov
GPG key ID: 191CBFF5F72ECAFD
13 changed files with 293 additions and 124 deletions

View file

@ -15,17 +15,3 @@ I've come across 〜達 used for inanimate things several times with one example
奈落への滝が嗤い続け轟く
“目指したトコロは此処だったのか?”
今は先を急ぐ **水達**に乗せられたまま
## Bibliography
:::bibtex
@book{dojg-advanced,
title = {A dictionary of advanced Japanese grammar},
author = {Makino, Seiichi and Tsutsui, Michio},
isbn = {9784789012959},
lccn = {2017414531},
year = {2008},
publisher = {Japan Times}
}
:::

View file

@ -0,0 +1,8 @@
@book{dojg-advanced,
title = {A dictionary of advanced Japanese grammar},
author = {Makino, Seiichi and Tsutsui, Michio},
isbn = {9784789012959},
lccn = {2017414531},
year = {2008},
publisher = {Japan Times}
}

View file

@ -3,6 +3,7 @@ mod render;
mod sack;
use camino::Utf8PathBuf;
use hayagriva::Library;
use hypertext::Renderable;
pub use load::{gather, Source, SourceKind};
@ -19,6 +20,7 @@ pub trait Content {
content: T,
outline: Outline,
sack: &'s Sack,
bib: Option<Vec<String>>,
) -> impl Renderable + 'html
where
'f: 'html,
@ -28,5 +30,5 @@ pub trait Content {
fn as_link(&self, path: Utf8PathBuf) -> Option<Linkable>;
fn render(data: &str) -> (Outline, String);
fn render(data: &str, lib: Option<&Library>) -> (Outline, String, Option<Vec<String>>);
}

View file

@ -73,12 +73,10 @@ pub fn render(items: &[Item]) {
})
.collect();
let sack = Sack::new(&assets);
for item in items {
match item {
Item::Real(real) => render_real(real, &sack),
Item::Fake(fake) => render_fake(fake, &sack),
Item::Real(real) => render_real(real, &Sack::new(&assets, &real.out)),
Item::Fake(fake) => render_fake(fake, &Sack::new(&assets, &fake.0)),
}
}
}

View file

@ -1,8 +1,11 @@
use std::collections::HashMap;
use camino::Utf8PathBuf;
use hayagriva::Library;
use crate::html::{Link, LinkDate, Linkable};
use super::Asset;
use super::{Asset, AssetKind};
#[derive(Debug)]
@ -35,11 +38,12 @@ impl TreePage {
#[derive(Debug)]
pub struct Sack<'a> {
assets: &'a [&'a Asset],
path: &'a Utf8PathBuf,
}
impl<'a> Sack<'a> {
pub fn new(assets: &'a [&'a Asset]) -> Self {
Self { assets }
pub fn new(assets: &'a [&'a Asset], path: &'a Utf8PathBuf) -> Self {
Self { assets, path }
}
pub fn get_links(&self, path: &str) -> Vec<LinkDate> {
@ -69,4 +73,21 @@ impl<'a> Sack<'a> {
tree
}
pub fn get_library(&self) -> Option<&Library> {
let glob = format!("{}/*.bib", self.path.parent()?);
let glob = glob::Pattern::new(&glob).unwrap();
let opts = glob::MatchOptions {
case_sensitive: true,
require_literal_separator: true,
require_literal_leading_dot: false,
};
self.assets.iter()
.filter(|asset| glob.matches_path_with(asset.out.as_ref(), opts))
.find_map(|asset| match asset.kind {
AssetKind::Bib(ref lib) => Some(lib),
_ => None
})
}
}

View file

@ -6,6 +6,7 @@ mod list;
mod show;
mod special;
mod wiki;
mod misc;
pub use home::home;
pub use page::page;

View file

@ -19,7 +19,7 @@ const INTRO: &str = r#"
fn intro() -> impl Renderable {
let (_, html) = parse(INTRO);
let (_, html, _) = parse(INTRO, None);
maud!(
section .p-card.intro-jp lang="ja-JP" {
(Raw(html))

95
src/html/misc.rs Normal file
View file

@ -0,0 +1,95 @@
use hypertext::{html_elements, maud_move, GlobalAttributes, Renderable};
use crate::gen::{Sack, TreePage};
use crate::text::md::Outline;
/// Render the outline for a document
pub fn show_outline(outline: Outline) -> impl Renderable {
maud_move!(
section .link-tree {
h2 .link-tree__heading {
a .link-tree__heading-text href="#top" { "Content" }
}
nav #table-of-contents .link-tree__nav {
ul .link-tree__nav-list {
@for (title, id) in outline.0 {
li .link-tree__nav-list-item {
a .link-tree__nav-list-text.link href=(format!("#{}", id)) {
(title)
}
}
}
}
}
}
)
}
/// Render the bibliography for a document
pub fn show_bibliography(bib: Vec<String>) -> impl Renderable {
maud_move!(
section .markdown {
h2 {
"Bibliography"
}
ol .bibliography {
@for item in bib {
li {
(item)
}
}
}
}
)
}
/// Render the page tree
pub fn show_page_tree(sack: &Sack, glob: &str) -> impl Renderable {
let tree = sack.get_tree(glob);
maud_move!(
h2 .link-tree__heading {
// {pages.chain(x => x.prefix)
// .map(pathify)
// .mapOrDefault(href =>
// <a class="link-tree__heading-text" href={href}>{heading}</a>,
// <span class="link-tree__heading-text">{heading}</span>
// )}
}
nav .link-tree__nav {
(show_page_tree_level(&tree))
}
)
}
fn show_page_tree_level(tree: &TreePage) -> impl Renderable + '_ {
let subs = {
let mut subs: Vec<_> = tree.subs.iter().collect();
subs.sort_by(|a, b| a.0.cmp(b.0));
subs
};
maud_move!(
ul .link-tree__nav-list {
@for (key, next) in subs {
li .link-tree__nav-list-item {
span .link-tree__nav-list-text {
@if let Some(ref link) = next.link {
a .link-tree__nav-list-text.link href=(link.path.as_str()) {
(&link.name)
}
} @else {
span .link-tree__nav-list-text {
(key)
}
}
}
@if !next.subs.is_empty() {
(show_page_tree_level(next))
}
}
}
}
)
}

View file

@ -1,35 +1,16 @@
use hypertext::{html_elements, maud_move, GlobalAttributes, Renderable};
use crate::md::Post;
use crate::html::misc::{show_bibliography, show_outline};
use crate::html::page;
use crate::md::Post;
use crate::text::md::Outline;
pub fn tree(outline: Outline) -> impl Renderable {
maud_move!(
section .link-tree {
h2 .link-tree__heading {
a .link-tree__heading-text href="#top" { "Content" }
}
nav #table-of-contents .link-tree__nav {
ul .link-tree__nav-list {
@for (title, id) in outline.0 {
li .link-tree__nav-list-item {
a .link-tree__nav-list-text.link href=(format!("#{id}")) {
(title)
}
}
}
}
}
}
)
}
pub fn post<'fm, 'md, 'post, T>(
fm: &'fm Post,
content: T,
outline: Outline,
bib: Option<Vec<String>>,
) -> impl Renderable + 'post
where
'fm: 'post,
@ -47,7 +28,7 @@ pub fn post<'fm, 'md, 'post, T>(
label .wiki-aside__slider for="wiki-aside-shown" {
img .wiki-icon src="/static/svg/double-arrow.svg" width="24" height="24";
}
(tree(outline))
(show_outline(outline))
}
article .wiki-article /*class:list={classlist)*/ {
@ -57,6 +38,10 @@ pub fn post<'fm, 'md, 'post, T>(
section .wiki-article__markdown.markdown {
(content)
}
@if let Some(bib) = bib {
(show_bibliography(bib))
}
}
}
);

View file

@ -1,62 +1,10 @@
use hypertext::{html_elements, maud_move, GlobalAttributes, Renderable};
use crate::{
gen::{Sack, TreePage},
html::page,
md::Wiki,
text::md::Outline
};
fn tree(sack: &Sack) -> impl Renderable {
let tree = sack.get_tree("wiki/**/*.html");
maud_move!(
h2 .link-tree__heading {
// {pages.chain(x => x.prefix)
// .map(pathify)
// .mapOrDefault(href =>
// <a class="link-tree__heading-text" href={href}>{heading}</a>,
// <span class="link-tree__heading-text">{heading}</span>
// )}
}
nav .link-tree__nav {
(list(&tree))
}
)
}
fn list(tree: &TreePage) -> impl Renderable + '_ {
let subs = {
let mut subs: Vec<_> = tree.subs.iter().collect();
subs.sort_by(|a, b| a.0.cmp(b.0));
subs
};
maud_move!(
ul .link-tree__nav-list {
@for (key, next) in subs {
li .link-tree__nav-list-item {
span .link-tree__nav-list-text {
@if let Some(ref link) = next.link {
a .link-tree__nav-list-text.link href=(link.path.as_str()) {
(&link.name)
}
} @else {
span .link-tree__nav-list-text {
(key)
}
}
}
@if !next.subs.is_empty() {
(list(next))
}
}
}
}
)
}
use crate::gen::Sack;
use crate::html::misc::show_page_tree;
use crate::html::{misc::show_bibliography, page};
use crate::md::Wiki;
use crate::text::md::Outline;
pub fn wiki<'data, 'html, 'sack, T>(
@ -64,6 +12,7 @@ pub fn wiki<'data, 'html, 'sack, T>(
content: T,
_: Outline,
sack: &'sack Sack,
bib: Option<Vec<String>>,
) -> impl Renderable + 'html
where
'sack: 'html,
@ -84,7 +33,7 @@ pub fn wiki<'data, 'html, 'sack, T>(
// Navigation tree
section .link-tree {
div {
(tree(sack))
(show_page_tree(sack, "wiki/**/*.html"))
}
}
}
@ -96,6 +45,10 @@ pub fn wiki<'data, 'html, 'sack, T>(
section .wiki-article__markdown.markdown {
(content)
}
@if let Some(bib) = bib {
(show_bibliography(bib))
}
}
}
);

View file

@ -5,6 +5,7 @@ use std::fs;
use camino::{Utf8Path, Utf8PathBuf};
use chrono::Datelike;
use gen::{Asset, Sack, Content};
use hayagriva::Library;
use html::{Link, LinkDate, Linkable};
use hypertext::{Raw, Renderable};
use once_cell::sync::Lazy;
@ -54,13 +55,14 @@ impl Content for md::Post {
content: T,
outline: Outline,
_: &'s Sack,
bib: Option<Vec<String>>,
) -> impl Renderable + 'html
where
'f: 'html,
'm: 'html,
's: 'html,
T: Renderable + 'm {
html::post(self, content, outline)
html::post(self, content, outline, bib)
}
fn as_link(&self, path: Utf8PathBuf) -> Option<Linkable> {
@ -74,8 +76,8 @@ impl Content for md::Post {
}))
}
fn render(data: &str) -> (Outline, String) {
text::md::parse(data)
fn render(data: &str, lib: Option<&Library>) -> (Outline, String, Option<Vec<String>>) {
text::md::parse(data, lib)
}
}
@ -85,6 +87,7 @@ impl Content for md::Slide {
content: T,
_: Outline,
_: &'s Sack,
bib: Option<Vec<String>>,
) -> impl Renderable + 'html
where
'f: 'html,
@ -105,16 +108,16 @@ impl Content for md::Slide {
}))
}
fn render(data: &str) -> (Outline, String) {
fn render(data: &str, _: Option<&Library>) -> (Outline, String, Option<Vec<String>>) {
let html = data
.split("\n-----\n")
.map(|chunk| chunk.split("\n---\n").map(text::md::parse).map(|e| e.1).collect::<Vec<_>>())
.map(|chunk| chunk.split("\n---\n").map(|s| text::md::parse(s, None)).map(|e| e.1).collect::<Vec<_>>())
.map(|stack| match stack.len() > 1 {
true => format!("<section>{}</section>", stack.into_iter().map(|slide| format!("<section>{slide}</section>")).collect::<String>()),
false => format!("<section>{}</section>", stack[0])
})
.collect::<String>();
(Outline(vec![]), html)
(Outline(vec![]), html, None)
}
}
@ -124,13 +127,14 @@ impl Content for md::Wiki {
content: T,
outline: Outline,
sack: &'s Sack,
bib: Option<Vec<String>>,
) -> impl Renderable + 'html
where
'f: 'html,
'm: 'html,
's: 'html,
T: Renderable + 'm {
html::wiki(self, content, outline, sack)
html::wiki(self, content, outline, sack, bib)
}
fn as_link(&self, path: Utf8PathBuf) -> Option<Linkable> {
@ -141,8 +145,8 @@ impl Content for md::Wiki {
}))
}
fn render(data: &str) -> (Outline, String) {
text::md::parse(data)
fn render(data: &str, lib: Option<&Library>) -> (Outline, String, Option<Vec<String>>) {
text::md::parse(data, lib)
}
}
@ -185,8 +189,9 @@ fn transform<T>(meta: gen::Source) -> Asset
let link = T::as_link(&fm, Utf8Path::new("/").join(dir));
let call = move |sack: &Sack| {
let (outline, html) = T::render(&md);
T::transform(&fm, Raw(html), outline, sack).render().into()
let lib = sack.get_library();
let (outline, html, bib) = T::render(&md, lib);
T::transform(&fm, Raw(html), outline, sack, bib).render().into()
};
gen::Asset {
@ -249,7 +254,7 @@ fn main() {
gen::Asset {
kind: gen::AssetKind::Html(Box::new(|_| {
let data = std::fs::read_to_string("content/index.md").unwrap();
let (_, html) = text::md::parse(&data);
let (_, html, bib) = text::md::parse(&data, None);
html::home(Raw(html)).render().to_owned().into()
})),
out: "index.html".into(),

View file

@ -1,5 +1,6 @@
use std::collections::HashMap;
use hayagriva::{archive::ArchivedStyle, citationberg::{IndependentStyle, Locale, Style}, BibliographyDriver, BibliographyRequest, CitationItem, CitationRequest, Library};
use hypertext::Renderable;
use once_cell::sync::Lazy;
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd, TextMergeStream};
@ -32,10 +33,19 @@ static KATEX_B: Lazy<katex::Opts> = Lazy::new(||
.unwrap()
);
static LOCALE: Lazy<Vec<Locale>> = Lazy::new(|| hayagriva::archive::locales());
static STYLE: Lazy<IndependentStyle> = Lazy::new(||
match ArchivedStyle::InstituteOfPhysicsNumeric.get() {
Style::Independent(style) => style,
Style::Dependent(_) => unreachable!(),
}
);
pub struct Outline(pub Vec<(String, String)>);
pub fn parse(text: &str) -> (Outline, String) {
pub fn parse(text: &str, lib: Option<&Library>) -> (Outline, String, Option<Vec<String>>) {
let (outline, stream) = {
let stream = Parser::new_ext(text, *OPTS);
let mut stream: Vec<_> = TextMergeStream::new(stream).collect();
@ -50,12 +60,113 @@ pub fn parse(text: &str) -> (Outline, String) {
let stream = make_code(stream)
.into_iter()
.flat_map(make_ruby);
.flat_map(make_ruby)
.flat_map(make_cite)
.collect::<Vec<_>>();
let (stream, bib) = match lib {
Some(lib) => make_bib(stream, lib),
None => (stream, None),
};
let mut html = String::new();
pulldown_cmark::html::push_html(&mut html, stream.into_iter());
(outline, html)
(outline, html, bib)
}
fn make_bib<'a, 'b>(stream: Vec<Event<'a>>, lib: &'b Library) -> (Vec<Event<'a>>, Option<Vec<String>>) {
let mut driver = BibliographyDriver::new();
for event in stream.iter() {
match event {
Event::InlineMath(ref text) => match lib.get(text) {
Some(entry) => driver.citation(CitationRequest::from_items(vec![CitationItem::with_entry(entry)], &STYLE, &LOCALE)),
None => (),
},
_ => (),
}
}
let res = driver.finish(BibliographyRequest { style: &STYLE, locale: None, locale_files: &LOCALE });
let mut n = 0;
let stream = stream.into_iter()
.map(|event| match event {
Event::InlineMath(_) => {
let rf = match res.citations.get(n) {
Some(rf) => rf,
None => return event,
};
let rf = rf.citation.to_string().replace("\u{1b}[0m", "");
let rf = format!("<cite>{}</cite>", rf);
n += 1;
Event::InlineHtml(rf.into())
},
_ => event
})
.collect();
let bib = match res.bibliography {
Some(ref bib) => {
let test = bib.items.iter()
.map(|x| x.content.to_string()
.replace("\u{1b}[0m", "")
.replace("\u{01b}[3mA", "")
)
.collect::<Vec<_>>();
Some(test)
},
None => None,
};
(stream, bib)
}
static RE_CITE: Lazy<Regex> = Lazy::new(|| Regex::new(r":cite\[([^\]]+)\]").unwrap());
#[derive(Debug)]
enum Annotated_<'a> {
Text(&'a str),
Cite(&'a str),
}
fn annotate_(input: &str) -> Vec<Annotated_> {
let mut parts: Vec<Annotated_> = Vec::new();
let mut last_index = 0;
for cap in RE_CITE.captures_iter(input) {
let cite = cap.get(1).unwrap().as_str();
let index = cap.get(0).unwrap().start();
if index > last_index {
parts.push(Annotated_::Text(&input[last_index..index]));
}
parts.push(Annotated_::Cite(cite));
last_index = cap.get(0).unwrap().end();
}
if last_index < input.len() {
parts.push(Annotated_::Text(&input[last_index..]));
}
parts
}
fn make_cite(event: Event) -> Vec<Event> {
match event {
Event::Text(ref text) => {
annotate_(text)
.into_iter()
.map(|e| match e {
Annotated_::Text(text) => Event::Text(text.to_owned().into()),
Annotated_::Cite(cite) => Event::InlineMath(cite.to_owned().into()),
})
.collect()
},
_ => vec![event],
}
}
fn set_heading_ids(events: &mut [Event]) -> Outline {

View file

@ -1,5 +1,9 @@
.csl-bib-body {
display: flex;
flex-direction: column;
gap: 0.5em;
.bibliography {
li {
padding-left: 0.7em;
&::marker {
content: '[' counter(list-item) ']';
}
}
}