diff --git a/src/interpreter/ast.rs b/src/interpreter/ast.rs index cb93d9a6..b5185906 100644 --- a/src/interpreter/ast.rs +++ b/src/interpreter/ast.rs @@ -7,7 +7,7 @@ use crate::interpreter::html::style::{FontStyle, FontWeight, Style, TextDecorati use crate::interpreter::html::{style, Attr, HeaderType, Picture, TagName}; use crate::interpreter::{Span, WindowInteractor}; use crate::opts::ResolvedTheme; -use crate::positioner::{Positioned, Section, Spacer, DEFAULT_MARGIN}; +use crate::positioner::{Positioned, Row, Section, Spacer, DEFAULT_MARGIN}; use crate::table::Table; use crate::text::{Text, TextBox}; use crate::utils::{Align, ImageCache}; @@ -15,7 +15,6 @@ use crate::Element; use comrak::Anchorizer; use glyphon::FamilyOwned; use parking_lot::Mutex; -use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::sync::Arc; use wgpu::TextureFormat; @@ -67,51 +66,19 @@ impl<'a> Input<'a> { } type Opts<'a> = &'a AstOpts; -trait OutputStream { - type Output; - fn push(&mut self, i: impl Into); +type Output = Vec; - fn map(&mut self, f: F) -> Map - where - Self: Sized, - { - Map(self, f, PhantomData) - } -} -impl OutputStream for Vec { - type Output = T; - fn push(&mut self, i: impl Into) { - self.push(i.into()); - } -} -struct Map<'a, T: OutputStream, F, O>(&'a mut T, F, PhantomData); -impl<'a, T, F, O> OutputStream for Map<'a, T, F, O> -where - T: OutputStream, - F: FnMut(O) -> T::Output, -{ - type Output = O; - fn push(&mut self, i: impl Into) { - self.0.push(self.1(i.into())) - } -} -struct Dummy(PhantomData); -impl Dummy { - const fn new() -> Self { - Self(PhantomData) - } -} -impl OutputStream for Dummy { - type Output = T; - fn push(&mut self, _i: impl Into) {} -} -trait Push { +trait Push { + fn push_element>(&mut self, element: I); fn push_spacer(&mut self); fn push_text_box(&mut self, global: &Static, element: &mut TextBox, state: State); } -impl> Push for T { +impl Push for Output { + fn push_element>(&mut self, element: I) { + self.push(element.into()); + } fn push_spacer(&mut self) { - self.push(Spacer::invisible()) + self.push_element(Spacer::invisible()) } fn push_text_box(&mut self, global: &Static, element: &mut TextBox, state: State) { let mut tb = std::mem::replace(element, TextBox::new(vec![], global.opts.hidpi_scale)); @@ -122,7 +89,7 @@ impl> Push for T { if content { tb.indent = state.global_indent; - self.push(tb); + self.push_element(tb); } } else { element.is_checkbox = tb.is_checkbox; @@ -251,14 +218,14 @@ trait Process { element: Self::Context<'_>, state: State, node: &HirNode, - output: &mut impl OutputStream, + output: &mut Output, ); fn process_content<'a>( _global: &Static, _element: Self::Context<'_>, _state: State, _input: impl IntoIterator, - _output: &mut impl OutputStream, + _output: &mut Output, ) { unimplemented!() } @@ -363,7 +330,7 @@ impl Process for FlowProcess { element: Self::Context<'_>, mut state: State, node: &HirNode, - output: &mut impl OutputStream, + output: &mut Output, ) { let attributes = &node.attributes; match node.tag { @@ -472,7 +439,7 @@ impl Process for FlowProcess { output.push_text_box(global, element, state); output.push_spacer(); } - TagName::HorizontalRuler => output.push(Spacer::visible()), + TagName::HorizontalRuler => output.push_element(Spacer::visible()), TagName::Picture => PictureProcess::process(global, (), state, node, output), TagName::Source => tracing::warn!("Source tag can only be inside an Picture."), TagName::Image => ImageProcess::process(global, None, state, node, output), @@ -577,7 +544,7 @@ impl Process for FlowProcess { element: Self::Context<'_>, state: State, content: impl IntoIterator, - output: &mut impl OutputStream, + output: &mut Output, ) { for node in content { match node { @@ -604,7 +571,7 @@ impl Process for DetailsProcess { _element: Self::Context<'_>, state: State, node: &HirNode, - output: &mut impl OutputStream, + output: &mut Output, ) { let mut section = Section::bare(global.opts.hidpi_scale); *section.hidden.get_mut() = true; @@ -626,7 +593,7 @@ impl Process for DetailsProcess { &mut tb, state.borrow(), &summary.content, - &mut Dummy::new(), + &mut vec![], ); *section.summary = Some(Positioned::new(tb)); @@ -638,14 +605,19 @@ impl Process for DetailsProcess { } } - let mut section_content = vec![]; - let s = &mut section_content.map(Positioned::new); + let mut section_content: Vec = vec![]; let mut tb = TextBox::new(vec![], global.opts.hidpi_scale); - FlowProcess::process_content(global, &mut tb, state.borrow(), content, s); - s.push_text_box(global, &mut tb, state); - section.elements = section_content; - output.push(section) + FlowProcess::process_content( + global, + &mut tb, + state.borrow(), + content, + &mut section_content, + ); + section_content.push_text_box(global, &mut tb, state); + section.elements = section_content.drain(..).map(Positioned::new).collect(); + output.push_element(section) } } @@ -657,7 +629,7 @@ impl Process for OrderedListProcess { element: Self::Context<'_>, mut state: State, node: &HirNode, - output: &mut impl OutputStream, + output: &mut Output, ) { let mut index = 1; for attr in &node.attributes { @@ -699,7 +671,7 @@ impl Process for UnorderedListProcess { element: Self::Context<'_>, mut state: State, node: &HirNode, - output: &mut impl OutputStream, + output: &mut Output, ) { output.push_text_box(global, element, state.borrow()); state.global_indent += DEFAULT_MARGIN / 2.; @@ -728,7 +700,7 @@ impl Process for ListItemProcess { (element, prefix): Self::Context<'_>, state: State, node: &HirNode, - output: &mut impl OutputStream, + output: &mut Output, ) { let anchor = node.attributes.iter().find_map(|attr| attr.to_anchor()); if let Some(anchor) = anchor { @@ -769,22 +741,25 @@ impl Process for ListItemProcess { struct ImageProcess; impl ImageProcess { fn push_image_from_picture( - opts: Opts, + global: &Static, state: State, - output: &mut impl OutputStream, + output: &mut Output, picture: Picture, ) { let align = picture.inner.align; - let src = picture.resolve_src(opts.color_scheme).to_owned(); + let src = picture.resolve_src(global.opts.color_scheme).to_owned(); let align = align.unwrap_or_default(); let is_url = src.starts_with("http://") || src.starts_with("https://"); - let mut image = match opts.image_cache.lock().unwrap().get(&src) { + let mut image = match global.opts.image_cache.lock().unwrap().get(&src) { Some(image_data) if is_url => { - Image::from_image_data(image_data.clone(), opts.hidpi_scale) - } - _ => { - Image::from_src(src, opts.hidpi_scale, opts.window.lock().image_callback()).unwrap() + Image::from_image_data(image_data.clone(), global.opts.hidpi_scale) } + _ => Image::from_src( + src, + global.opts.hidpi_scale, + global.opts.window.lock().image_callback(), + ) + .unwrap(), } .with_align(align); @@ -795,8 +770,16 @@ impl ImageProcess { image = image.with_size(size); } - output.push(image); - output.push_spacer() + if Align::Left == align { + if let Some(Element::Row(row)) = output.iter_mut().next_back() { + row.elements.push(Positioned::new(image)) + } else { + output.push_element(Row::with_image(image, global.opts.hidpi_scale)) + } + } else { + output.push_element(image); + output.push_spacer() + } } } impl Process for ImageProcess { @@ -806,7 +789,7 @@ impl Process for ImageProcess { mut element: Self::Context<'_>, mut state: State, node: &HirNode, - output: &mut impl OutputStream, + output: &mut Output, ) { if element.is_none() { element = Some(Picture::builder()); @@ -829,7 +812,7 @@ impl Process for ImageProcess { } match builder.try_finish() { - Ok(pic) => Self::push_image_from_picture(global.opts, state, output, pic), + Ok(pic) => Self::push_image_from_picture(global, state, output, pic), Err(err) => tracing::warn!("Invalid : {err}"), } } @@ -842,7 +825,7 @@ impl Process for SourceProcess { element: Self::Context<'_>, _state: State, node: &HirNode, - _output: &mut impl OutputStream, + _output: &mut Output, ) { let mut media = None; let mut src_set = None; @@ -873,7 +856,7 @@ impl Process for PictureProcess { _element: Self::Context<'_>, mut state: State, node: &HirNode, - output: &mut impl OutputStream, + output: &mut Output, ) { let mut builder = Picture::builder(); @@ -914,7 +897,7 @@ impl Process for TableProcess { _element: Self::Context<'_>, state: State, node: &HirNode, - output: &mut impl OutputStream, + output: &mut Output, ) { let mut table = Table::new(); Self::process_with( @@ -935,7 +918,7 @@ impl Process for TableProcess { |_| {}, ); output.push_spacer(); - output.push(table); + output.push_element(table); output.push_spacer(); } } @@ -948,7 +931,7 @@ impl Process for TableHeadProcess { element: Self::Context<'_>, state: State, node: &HirNode, - output: &mut impl OutputStream, + output: &mut Output, ) { Self::process_with( global, @@ -977,7 +960,7 @@ impl Process for TableRowProcess { element: Self::Context<'_>, state: State, node: &HirNode, - output: &mut impl OutputStream, + output: &mut Output, ) { Self::process_with( global, @@ -1014,7 +997,7 @@ impl Process for TableCellProcess { (table, is_header): Self::Context<'_>, mut state: State, node: &HirNode, - _output: &mut impl OutputStream, + _output: &mut Output, ) { let row = table .rows @@ -1032,7 +1015,7 @@ impl Process for TableCellProcess { &mut tb, state, &node.content, - &mut Dummy::new(), // TODO allow anything inside tables not only text. + &mut vec![], // TODO allow anything inside tables not only text. ); row.push(tb); diff --git a/src/interpreter/snapshots/inlyne__interpreter__tests__image_loading_fails_gracefully.snap b/src/interpreter/snapshots/inlyne__interpreter__tests__image_loading_fails_gracefully.snap index 9e8e20bf..8dc64ff4 100644 --- a/src/interpreter/snapshots/inlyne__interpreter__tests__image_loading_fails_gracefully.snap +++ b/src/interpreter/snapshots/inlyne__interpreter__tests__image_loading_fails_gracefully.snap @@ -1,30 +1,37 @@ --- source: src/interpreter/tests.rs -description: "![This actually returns JSON 😈](http://127.0.0.1:43275/snapshot.png)" +description: "![This actually returns JSON 😈](http://127.0.0.1:41497/snapshot.png)" expression: "interpret_md_with_opts(&text, opts)" --- [ - Image( - Image { - image_data: Mutex { - data: Some( - ImageData { - lz4_blob: { len: 7759, data: [4, 34, 77, ..] }, - scale: false, - dimensions: (63, 72), - }, - ), - poisoned: false, - .. - }, - is_aligned: Some(Left), - .. + Row( + Row { + elements: [ + Positioned { + inner: Image( + Image { + image_data: Mutex { + data: Some( + ImageData { + lz4_blob: { len: 7759, data: [4, 34, 77, ..] }, + scale: false, + dimensions: (63, 72), + }, + ), + poisoned: false, + .. + }, + is_aligned: Some(Left), + .. + }, + ), + bounds: None, + }, + ], + hidpi_scale: 1.0, }, ), Spacer( InvisibleSpacer(5), ), - Spacer( - InvisibleSpacer(5), - ), ]