Initial commit
This commit is contained in:
commit
f7b7310259
4 changed files with 620 additions and 0 deletions
237
src/lib.rs
Normal file
237
src/lib.rs
Normal 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(),
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue