Initial commit

This commit is contained in:
Isaac Mills 2024-03-18 18:33:03 -04:00
commit f7b7310259
Signed by: fnmain
GPG key ID: B67D7410F33A0F61
4 changed files with 620 additions and 0 deletions

237
src/lib.rs Normal file
View file

@ -0,0 +1,237 @@
use std::borrow::Cow;
use cosmic_text::{Attrs, Buffer, Color, Family, FontSystem, Metrics, Shaping, Style, Weight};
use jotdown::{Container, Event, ListKind};
pub use jotdown;
use range_map::RangeMap;
pub struct JotdownBufferIter<'a, 'b, T: Iterator<Item = Event<'a>>> {
djot: T,
font_system: &'b mut FontSystem,
width: f32,
metrics: Metrics,
indent: Vec<Indent>,
}
struct JotdownIntoBuffer<'a, 'b, T: Iterator<Item = Event<'a>>> {
djot: &'b mut T,
attrs: Attrs<'a>,
metrics: Metrics,
indent: &'b mut Vec<Indent>,
image_url: Option<Cow<'a, str>>,
link_start: usize,
urls: Vec<(range_map::Range<usize>, &'a str)>,
location: usize,
added: bool,
}
#[derive(Default, Clone, Copy)]
pub struct Indent {
pub modifier: Option<ListKind>,
pub indent: f32,
}
pub const INDENT_AMOUNT: f32 = 18.0;
impl<'a, 'b, T: Iterator<Item = Event<'a>>> Iterator for JotdownIntoBuffer<'a, 'b, T> {
type Item = (&'a str, Attrs<'a>);
fn next(&mut self) -> Option<Self::Item> {
while let Some(event) = self.djot.next() {
match event {
Event::LeftSingleQuote => {
self.added = true;
self.location += "".len();
return Some(("", self.attrs.clone()));
}
Event::LeftDoubleQuote => {
self.added = true;
self.location += "".len();
return Some(("", self.attrs.clone()));
}
Event::RightSingleQuote => {
self.added = true;
self.location += "".len();
return Some(("", self.attrs.clone()));
}
Event::RightDoubleQuote => {
self.added = true;
self.location += "".len();
return Some(("", self.attrs.clone()));
}
Event::Ellipsis => {
self.added = true;
self.location += "".len();
return Some(("", self.attrs.clone()));
}
Event::EmDash => {
self.added = true;
self.location += "".len();
return Some(("", self.attrs.clone()));
}
Event::EnDash => {
self.added = true;
self.location += "".len();
return Some(("", self.attrs.clone()));
}
Event::Softbreak | Event::NonBreakingSpace => {
self.added = true;
self.location += " ".len();
return Some((" ", self.attrs.clone()));
}
Event::Str(Cow::Borrowed(s)) | Event::Symbol(Cow::Borrowed(s)) => {
self.added = true;
self.location += s.len();
return Some((s, self.attrs.clone()));
}
Event::Start(container, _) => match container {
Container::Heading { level, .. } => {
self.metrics = Metrics::new(
self.metrics.font_size * (4.0 - level as f32),
self.metrics.line_height * (4.0 - level as f32),
);
}
Container::Emphasis => self.attrs = self.attrs.style(Style::Italic),
Container::Strong => self.attrs = self.attrs.weight(Weight::BOLD),
Container::Verbatim => self.attrs = self.attrs.family(Family::Monospace),
Container::List { kind, .. } => self.indent.push(Indent {
indent: self.indent.last().copied().unwrap_or_default().indent
+ INDENT_AMOUNT,
modifier: Some(kind),
}),
Container::Image(url, _) => self.image_url = Some(url),
Container::Link(_, _) => {
self.link_start = self.location + 1;
self.attrs = self.attrs.color(Color::rgb(96, 198, 233));
}
_ => {}
},
Event::End(container) => match container {
Container::Emphasis => self.attrs = self.attrs.style(Style::Normal),
Container::Strong => self.attrs = self.attrs.weight(Weight::NORMAL),
Container::Verbatim => self.attrs = self.attrs.family(Family::SansSerif),
Container::List { .. } => {
self.indent.pop();
}
Container::Heading { .. } | Container::Paragraph | Container::Image(_, _) => {
if self.added {
return None;
}
}
Container::Link(Cow::Borrowed(url), _) => {
self.urls
.push((range_map::Range::new(self.link_start, self.location), url));
self.attrs = self.attrs.color(Color::rgb(255, 255, 255));
}
_ => {}
},
Event::Hardbreak | Event::ThematicBreak(_) => {}
Event::Blankline | Event::Escape | Event::FootnoteReference(_) => {}
Event::Str(Cow::Owned(_)) | Event::Symbol(Cow::Owned(_)) => panic!(),
}
}
return None;
}
}
pub struct JotdownItem<'a> {
pub indent: Indent,
pub buffer: Buffer,
pub image_url: Option<Cow<'a, str>>,
pub url_map: Option<RangeMap<usize, &'a str>>,
}
impl<'a, 'b, T: Iterator<Item = Event<'a>>> Iterator for JotdownBufferIter<'a, 'b, T> {
type Item = JotdownItem<'a>;
fn next(&mut self) -> Option<Self::Item> {
let mut buffer = Buffer::new(&mut self.font_system, self.metrics);
let mut jot = JotdownIntoBuffer {
djot: &mut self.djot,
attrs: Attrs::new().family(Family::SansSerif),
metrics: self.metrics,
indent: &mut self.indent,
image_url: None,
added: false,
link_start: 0,
location: 0,
urls: Vec::new(),
};
buffer.set_rich_text(
&mut self.font_system,
&mut jot,
Attrs::new().family(Family::SansSerif),
Shaping::Advanced,
);
buffer.set_wrap(&mut self.font_system, cosmic_text::Wrap::WordOrGlyph);
buffer.set_metrics(&mut self.font_system, jot.metrics);
let image_url = jot.image_url;
let urls = jot.urls;
let added = jot.added;
let indent = self.indent.last().copied().unwrap_or_default();
buffer.set_size(&mut self.font_system, self.width - indent.indent, f32::MAX);
if !added {
return None;
} else {
buffer.shape_until_scroll(&mut self.font_system, true);
return Some(JotdownItem {
indent,
url_map: if urls.is_empty() {
None
} else {
RangeMap::try_from_iter(
urls.into_iter(), // .scan(
// (
// 0,
// buffer
// .layout_runs()
// .flat_map(|run| run.glyphs.into_iter())
// .enumerate(),
// ),
// |(len, glyphs), range| {
// let mut start = None;
// while let Some(glyph) = glyphs.next() {
// *len += glyph.1.end - glyph.1.start;
// if start.is_none() && *len >= range.0.start {
// start = Some(glyph.0);
// } else if *len >= range.0.end {
// return Some((
// range_map::Range::new(start.unwrap(), glyph.0),
// range.1,
// ));
// }
// }
// log::info!("{:?}", start);
// return None;
// },
// ),
)
.ok()
},
buffer,
image_url,
});
}
}
}
pub fn jotdown_into_buffers<'a, 'b, T: Iterator<Item = Event<'a>>>(
djot: T,
font_system: &'b mut FontSystem,
metrics: Metrics,
width: f32,
) -> JotdownBufferIter<'a, 'b, T> {
JotdownBufferIter {
djot,
font_system,
width,
metrics,
indent: Vec::new(),
}
}