diff --git a/Cargo.lock b/Cargo.lock index 1eceaa613..c31425a1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1500,6 +1500,21 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "roff" version = "0.2.1" @@ -1544,6 +1559,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + [[package]] name = "rustversion" version = "1.0.12" @@ -1602,6 +1629,16 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "semver" version = "1.0.17" @@ -1678,6 +1715,12 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -1989,6 +2032,7 @@ dependencies = [ "typst-library", "unicode_names2", "unscanny", + "ureq", "yaml-front-matter", ] @@ -2164,6 +2208,30 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9df2af067a7953e9c3831320f35c1cc0600c30d44d9f7a12b01db1cd88d6b47" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "ureq" +version = "2.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d" +dependencies = [ + "base64", + "flate2", + "log", + "once_cell", + "rustls", + "serde", + "serde_json", + "url", + "webpki", + "webpki-roots", +] + [[package]] name = "url" version = "2.3.1" @@ -2283,6 +2351,35 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + [[package]] name = "weezl" version = "0.1.7" diff --git a/docs/Cargo.toml b/docs/Cargo.toml index a2e64f99b..a5885a8a5 100644 --- a/docs/Cargo.toml +++ b/docs/Cargo.toml @@ -22,3 +22,4 @@ heck = "0.4" yaml-front-matter = "0.1" unicode_names2 = "0.6.0" once_cell = "1" +ureq = { version = "2.6", features = ["json"] } diff --git a/docs/src/contribs.rs b/docs/src/contribs.rs new file mode 100644 index 000000000..df1d49445 --- /dev/null +++ b/docs/src/contribs.rs @@ -0,0 +1,97 @@ +use std::cmp::Reverse; +use std::collections::HashMap; +use std::fmt::Write; + +use serde::Deserialize; + +use super::Html; + +/// Build HTML detailing the contributors between two tags. +pub fn contributors(from: &str, to: &str) -> Option { + let staff = ["laurmaedje", "reknih"]; + + let url = format!("https://api.github.com/repos/typst/typst/compare/{from}...{to}"); + let response: Response = ureq::get(&url) + .set("X-GitHub-Api-Version", "2022-11-28") + .call() + .ok()? + .into_json() + .ok()?; + + // Determine number of contributions per person. + let mut contributors = HashMap::::new(); + for commit in response.commits { + contributors + .entry(commit.author.login.clone()) + .or_insert_with(|| Contributor { + login: commit.author.login, + avatar: commit.author.avatar_url, + contributions: 0, + }) + .contributions += 1; + } + + // Keep only non-staff people. + let mut contributors: Vec<_> = contributors + .into_values() + .filter(|c| !staff.contains(&c.login.as_str())) + .collect(); + + // Sort by highest number of commits. + contributors.sort_by_key(|c| Reverse(c.contributions)); + if contributors.is_empty() { + return None; + } + + let mut html = "Thanks to everyone who contributed to this release!".to_string(); + html += ""; + + Some(Html::new(html)) +} + +#[derive(Debug)] +struct Contributor { + login: String, + avatar: String, + contributions: usize, +} + +#[derive(Debug, Deserialize)] +struct Response { + commits: Vec, +} + +#[derive(Debug, Deserialize)] +struct Commit { + author: Author, +} + +#[derive(Debug, Deserialize)] +struct Author { + login: String, + avatar_url: String, +} diff --git a/docs/src/general/changelog.md b/docs/src/general/changelog.md index 22f4147ba..1b28bc094 100644 --- a/docs/src/general/changelog.md +++ b/docs/src/general/changelog.md @@ -45,6 +45,8 @@ description: | - [Ratios]($type/ratio) can now be multiplied with more types and be converted to [floats]($type/float) with the [`float`]($func/float) function + + ## April 04, 2023 (v0.1.0) - **Breaking changes:** - When using the CLI, you now have to use subcommands: @@ -113,7 +115,7 @@ description: | - Fixed line number in error message for CSV parsing - Fixed invalid autocompletion after certain markup elements -Thanks to everybody who contributed to this release! + ## March 28, 2023 - **Breaking changes:** @@ -153,7 +155,7 @@ Thanks to everybody who contributed to this release! - Links in bibliographies are now affected by link styling - Fixed hovering over comments in web app -Thanks to everybody who contributed to this release! + ## March 21, 2023 - Reference and bibliography management diff --git a/docs/src/html.rs b/docs/src/html.rs index b189a09c5..d3566b078 100644 --- a/docs/src/html.rs +++ b/docs/src/html.rs @@ -1,3 +1,5 @@ +use std::ops::Range; + use comemo::Prehashed; use md::escape::escape_html; use pulldown_cmark as md; @@ -108,16 +110,21 @@ impl<'a> Handler<'a> { // Rewrite HTML images. md::Event::Html(html) if html.starts_with(" { - let needle = "src=\""; - let offset = html.find(needle).unwrap() + needle.len(); - let len = html[offset..].find('"').unwrap(); - let range = offset..offset + len; + let range = html_attr_range(html, "src").unwrap(); let path = &html[range.clone()]; let mut buf = html.to_string(); buf.replace_range(range, &self.handle_image(path)); *html = buf.into(); } + // Rewrite contributor sectinos. + md::Event::Html(html) if html.starts_with(" { + let from = html_attr(html, "from").unwrap(); + let to = html_attr(html, "to").unwrap(); + let Some(output) = contributors(from, to) else { return false }; + *html = output.raw.into(); + } + // Rewrite links. md::Event::Start(md::Tag::Link(ty, dest, _)) => { assert!( @@ -327,6 +334,19 @@ fn code_block(resolver: &dyn Resolver, lang: &str, text: &str) -> Html { resolver.example(highlighted, &frames) } +/// Extract an attribute value from an HTML element. +fn html_attr<'a>(html: &'a str, attr: &str) -> Option<&'a str> { + html.get(html_attr_range(html, attr)?) +} + +/// Extract the range of the attribute value of an HTML element. +fn html_attr_range(html: &str, attr: &str) -> Option> { + let needle = format!("{attr}=\""); + let offset = html.find(&needle)? + needle.len(); + let len = html[offset..].find('"')?; + Some(offset..offset + len) +} + /// World for example compilations. struct DocWorld(Source); diff --git a/docs/src/lib.rs b/docs/src/lib.rs index 4a20a008b..7333090d1 100644 --- a/docs/src/lib.rs +++ b/docs/src/lib.rs @@ -1,7 +1,9 @@ //! Documentation provider for Typst. +mod contribs; mod html; +pub use contribs::contributors; pub use html::Html; use std::fmt::{self, Debug, Formatter};