diff --git a/Cargo.lock b/Cargo.lock index 2fd99e2..0c1ec43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,6 +51,7 @@ name = "cosmic-jotdown" version = "0.1.0" dependencies = [ "cosmic-text", + "databake", "emath", "image", "jotdown", diff --git a/Cargo.toml b/Cargo.toml index f092cc1..8c6a740 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,8 @@ rangemap = "1.5.1" nominals = "0.3.0" emath = "0.27.2" image = { version = "0.24.9", default-features = false } +databake = { version = "0.1.7", features = ["derive"], optional = true } [features] serde = ["dep:serde", "rangemap/serde1"] +databake = ["dep:databake"] diff --git a/src/lib.rs b/src/lib.rs index 47ae90c..8638c51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,15 +3,17 @@ use std::borrow::Cow; use cosmic_text::{self, Align}; use cosmic_text::{Attrs, Buffer, Color, Family, FontSystem, Metrics, Shaping, Style, Weight}; +#[cfg(feature = "databake")] +use databake::Bake; use emath::{Pos2, Rect, Vec2}; use jotdown::{Container, Event, ListKind, OrderedListNumbering, OrderedListStyle}; use nominals::{LetterLower, LetterUpper, Nominal, RomanLower, RomanUpper}; -#[cfg(feature = "serde")] +#[cfg(any(feature = "serde", feature = "databake"))] pub mod serde_suck; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -#[cfg(feature = "serde")] +#[cfg(any(feature = "serde", feature = "databake"))] pub use serde_suck::*; use rangemap::RangeMap; @@ -42,6 +44,20 @@ pub struct Indent { pub indent: f32, } +#[cfg(feature = "databake")] +impl databake::Bake for Indent { + fn bake(&self, ctx: &databake::CrateEnv) -> databake::TokenStream { + let indent = self.indent; + let modifier = self.modifier.bake(ctx); + databake::quote! { + cosmic_jotdown::Indent { + indent: #indent + modifier: #modifier + } + } + } +} + pub const INDENT_AMOUNT: f32 = 16.0; impl<'a, 'b, T: Iterator>> Iterator for JotdownIntoBuffer<'a, 'b, T> { @@ -166,17 +182,30 @@ pub struct RichText<'a>( #[cfg_attr(feature = "serde", serde(with = "AttrsSerde"))] pub Attrs<'a>, ); +#[cfg(feature = "databake")] +impl<'a> Bake for RichText<'a> { + fn bake(&self, ctx: &databake::CrateEnv) -> databake::TokenStream { + let field0 = self.0.bake(ctx); + let field1: AttrsSerde = self.1.into(); + let field1 = field1.bake(ctx); + databake::quote! { + cosmic_jotdown::RichText(#field0, #field1) + } + } +} + #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "databake", derive(databake::Bake))] +#[cfg_attr(feature = "databake", databake(path = cosmic_jotdown::JotdownItem))] pub struct JotdownItem<'a> { pub indent: Indent, #[cfg_attr(feature = "serde", serde(borrow))] - pub buffer: Vec>, - #[cfg_attr(feature = "serde", serde(with = "MetricsSerde"))] - pub metrics: Metrics, + pub buffer: Cow<'a, [RichText<'a>]>, + pub metrics: (f32, f32), pub image_url: Option>, pub margin: f32, - pub url_map: Option>>, + pub url_map: Option)]>>, } #[derive(Clone)] @@ -394,10 +423,16 @@ impl<'a> JotdownItem<'a> { relative_bounds: measure_buffer(&buffer, Vec2::new(width, f32::MAX)), buffer, metrics: Metrics::new( - self.metrics.font_size * metrics.font_size, - self.metrics.line_height * metrics.line_height, + self.metrics.0 * metrics.font_size, + self.metrics.1 * metrics.line_height, ), - url_map: self.url_map, + url_map: { + self.url_map.map(|self_url_map| { + let mut url_map = RangeMap::new(); + url_map.extend(self_url_map.iter().map(|m| (m.0..m.1, m.2.clone()))); + url_map + }) + }, image_urls: None, } } @@ -408,8 +443,8 @@ impl<'a> JotdownItem<'a> { modifier: None, indent: 0.0, }, - buffer: text, - metrics: Metrics::new(1.0, 1.1), + buffer: Cow::Owned(text), + metrics: (1.0, 1.1), url_map: None, margin: 0.0, image_url: None, @@ -428,8 +463,8 @@ impl<'a> JotdownItem<'a> { let mut buffer = Buffer::new( font_system, Metrics::new( - metrics.font_size * self.metrics.font_size, - metrics.line_height * self.metrics.line_height, + metrics.font_size * self.metrics.0, + metrics.line_height * self.metrics.1, ), ); buffer.set_rich_text( @@ -486,16 +521,16 @@ impl<'a, T: Iterator>> Iterator for JotdownBufferIter<'a, T> { url_map: if urls.is_empty() { None } else { - let mut map = RangeMap::new(); + let mut map = Vec::new(); map.extend( urls.into_iter() - .map(|(range, url)| (range.start..range.end + 1, url)), + .map(|(range, url)| (range.start, range.end + 1, url)), ); - Some(map) + Some(Cow::Owned(map)) }, - buffer, + buffer: Cow::Owned(buffer), image_url, - metrics, + metrics: (metrics.font_size, metrics.line_height), margin: match top_level_containers { Some(Container::Heading { level, .. }) => match level { 1 => 0.34, diff --git a/src/serde_suck.rs b/src/serde_suck.rs index fa0c772..7503334 100644 --- a/src/serde_suck.rs +++ b/src/serde_suck.rs @@ -1,17 +1,22 @@ -use cosmic_text::{Align, Attrs, CacheKeyFlags, Color, Family, Metrics, Stretch, Style, Weight}; +use cosmic_text::{Align, Attrs, CacheKeyFlags, Color, Family, Stretch, Style, Weight}; +#[cfg(feature = "databake")] +use databake::Bake; use jotdown::{ListKind, OrderedListNumbering, OrderedListStyle}; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(remote = "Option")] +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "serde", serde(remote = "Option"))] pub enum ListKindOption { - Some(#[serde(with = "ListKindSerde")] ListKind), + Some(#[cfg_attr(feature = "serde", serde(with = "ListKindSerde"))] ListKind), None, } -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(remote = "jotdown::ListKind")] +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "serde", serde(remote = "jotdown::ListKind"))] pub enum ListKindSerde { Unordered, Ordered { @@ -22,8 +27,9 @@ pub enum ListKindSerde { Task, } -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(remote = "jotdown::OrderedListNumbering")] +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "serde", serde(remote = "jotdown::OrderedListNumbering"))] pub enum OrderedListNumberingSerde { Decimal, AlphaLower, @@ -32,23 +38,30 @@ pub enum OrderedListNumberingSerde { RomanUpper, } -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(remote = "jotdown::OrderedListStyle")] +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "serde", serde(remote = "jotdown::OrderedListStyle"))] pub enum OrderedListStyleSerde { Period, Paren, ParenParen, } -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(remote = "Metrics")] +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "serde", serde(remote = "cosmic_text::Metrics"))] +#[cfg_attr(feature = "databake", derive(Bake))] +#[cfg_attr(feature = "databake", databake(path = cosmic_text::Metrics))] pub struct MetricsSerde { pub font_size: f32, pub line_height: f32, } -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(remote = "Family")] +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "serde", serde(remote = "Family"))] +#[cfg_attr(feature = "databake", derive(Bake))] +#[cfg_attr(feature = "databake", databake(path = cosmic_text::Family))] pub enum FamilySerde<'a> { Name(&'a str), Serif, @@ -58,8 +71,24 @@ pub enum FamilySerde<'a> { Monospace, } -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(remote = "Stretch")] +impl<'a> From> for FamilySerde<'a> { + fn from(value: Family<'a>) -> Self { + match value { + Family::Name(n) => FamilySerde::Name(n), + Family::Serif => FamilySerde::Serif, + Family::SansSerif => FamilySerde::SansSerif, + Family::Cursive => FamilySerde::Cursive, + Family::Fantasy => FamilySerde::Fantasy, + Family::Monospace => FamilySerde::Monospace, + } + } +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "serde", serde(remote = "Stretch"))] +#[cfg_attr(feature = "databake", derive(Bake))] +#[cfg_attr(feature = "databake", databake(path = cosmic_text::Stretch))] pub enum StretchSerde { UltraCondensed, ExtraCondensed, @@ -72,29 +101,66 @@ pub enum StretchSerde { UltraExpanded, } -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(remote = "Style")] +impl From for StretchSerde { + fn from(value: Stretch) -> Self { + match value { + Stretch::UltraCondensed => StretchSerde::UltraCondensed, + Stretch::ExtraCondensed => StretchSerde::ExtraCondensed, + Stretch::Condensed => StretchSerde::Condensed, + Stretch::SemiCondensed => StretchSerde::SemiCondensed, + Stretch::Normal => StretchSerde::Normal, + Stretch::SemiExpanded => StretchSerde::SemiExpanded, + Stretch::Expanded => StretchSerde::Expanded, + Stretch::ExtraExpanded => StretchSerde::ExtraExpanded, + Stretch::UltraExpanded => StretchSerde::UltraExpanded, + } + } +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "serde", serde(remote = "Style"))] +#[cfg_attr(feature = "databake", derive(Bake))] +#[cfg_attr(feature = "databake", databake(path = cosmic_text::Style))] pub enum StyleSerde { Normal, Italic, Oblique, } -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(remote = "Weight")] +impl From