Skip to content

Commit

Permalink
Proper list wrapping (#46)
Browse files Browse the repository at this point in the history
* Proper list wrapping (comrak backend)

* Proper list wrapping (pulldown_cmark backend)

* Cleanup

* Add prettier spacing between list and items

* Changelog and doc
  • Loading branch information
lampsitter authored Apr 1, 2024
1 parent 73ef2f3 commit 07ec781
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 10 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@
### Changed

- Alerts are case-insensitive
- More spacing between list indicator and elements ([#46](https://github.com/lampsitter/egui_commonmark/pull/46)

### Fixed

- Lists align text when wrapping instead of wrapping at the beginning of the next
line ([#46](https://github.com/lampsitter/egui_commonmark/pull/46)

## 0.14.0 - 2024-03-26

Expand Down
6 changes: 6 additions & 0 deletions examples/markdown/lists.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,10 @@
1. Lorem ipsum dolor sit amet, consectetur __adipiscing elit, sed__ do
eiusmod tempor incididunt _ut_ labore ~~et~~ dolore magna aliqua. Ut enim
ad minim veniam, quis nostrud exercitation
2. Lorem ipsum dolor sit amet, consectetur __adipiscing elit, sed__ do
eiusmod tempor incididunt _ut_ labore ~~et~~ dolore magna aliqua. Ut enim
ad minim veniam, quis nostrud exercitation
- Lorem ipsum dolor sit amet, consectetur __adipiscing elit, sed__ do
eiusmod tempor incididunt _ut_ labore ~~et~~ dolore magna aliqua. Ut enim
ad minim veniam, quis nostrud exercitation

4 changes: 3 additions & 1 deletion src/elements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub(crate) fn number_point(ui: &mut Ui, number: &str) {
ui.painter().text(
rect.right_center(),
egui::Align2::RIGHT_CENTER,
format!("{number}. "),
format!("{number}."),
TextStyle::Body.resolve(ui.style()),
ui.visuals().strong_text_color(),
);
Expand Down Expand Up @@ -251,6 +251,8 @@ impl List {
} else {
unreachable!();
}

ui.add_space(4.0);
}

pub fn end_level(&mut self, ui: &mut Ui) {
Expand Down
4 changes: 3 additions & 1 deletion src/parsers/comrak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,9 @@ impl CommonMarkViewerInternal {
// we deliberately do not set it to false after this
}

self.render(ui, cache, options, max_width, c);
ui.horizontal_wrapped(|ui| {
self.render(ui, cache, options, max_width, c);
});

// To end the inlines
if self.should_insert_newline {
Expand Down
78 changes: 70 additions & 8 deletions src/parsers/pulldown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ pub struct ScrollableCache {
split_points: Vec<(usize, Pos2, Pos2)>,
}

pub type EventIteratorItem<'e> = (usize, (pulldown_cmark::Event<'e>, Range<usize>));

/// Parse events until a desired end tag is reached or no more events are found.
/// This is needed for multiple events that must be rendered inside a single widget
fn delayed_events<'e>(
events: &mut impl Iterator<Item = (usize, (pulldown_cmark::Event<'e>, Range<usize>))>,
events: &mut impl Iterator<Item = EventIteratorItem<'e>>,
end_at: pulldown_cmark::TagEnd,
) -> Vec<(pulldown_cmark::Event<'e>, Range<usize>)> {
let mut curr_event = events.next();
Expand All @@ -40,6 +42,30 @@ fn delayed_events<'e>(
}
}

fn delayed_events_list_item<'e>(
events: &mut impl Iterator<Item = EventIteratorItem<'e>>,
) -> Vec<(pulldown_cmark::Event<'e>, Range<usize>)> {
let mut curr_event = events.next();
let mut total_events = Vec::new();
loop {
if let Some(event) = curr_event.take() {
total_events.push(event.1.clone());
if let (_, (pulldown_cmark::Event::End(pulldown_cmark::TagEnd::Item), _range)) = event {
return total_events;
}

if let (_, (pulldown_cmark::Event::Start(pulldown_cmark::Tag::List(_)), _range)) = event
{
return total_events;
}
} else {
return total_events;
}

curr_event = events.next();
}
}

type Column<'e> = Vec<(pulldown_cmark::Event<'e>, Range<usize>)>;
type Row<'e> = Vec<Column<'e>>;

Expand Down Expand Up @@ -74,9 +100,7 @@ fn parse_row<'e>(
row
}

fn parse_table<'e>(
events: &mut impl Iterator<Item = (usize, (pulldown_cmark::Event<'e>, Range<usize>))>,
) -> Table<'e> {
fn parse_table<'e>(events: &mut impl Iterator<Item = EventIteratorItem<'e>>) -> Table<'e> {
let mut all_events = delayed_events(events, pulldown_cmark::TagEnd::Table)
.into_iter()
.peekable();
Expand Down Expand Up @@ -178,6 +202,7 @@ pub struct CommonMarkViewerInternal {
image: Option<crate::Image>,
should_insert_newline: bool,
fenced_code_block: Option<crate::FencedCodeBlock>,
is_list_item: bool,
is_table: bool,
is_blockquote: bool,
checkbox_events: Vec<CheckboxClickEvent>,
Expand All @@ -198,6 +223,7 @@ impl CommonMarkViewerInternal {
link: None,
image: None,
should_insert_newline: true,
is_list_item: false,
fenced_code_block: None,
is_table: false,
is_blockquote: false,
Expand Down Expand Up @@ -341,26 +367,61 @@ impl CommonMarkViewerInternal {
scroll_cache.split_points.clear();
}
}

#[allow(clippy::too_many_arguments)]
fn process_event<'e>(
&mut self,
ui: &mut Ui,
events: &mut impl Iterator<Item = (usize, (pulldown_cmark::Event<'e>, Range<usize>))>,
events: &mut impl Iterator<Item = EventIteratorItem<'e>>,
event: pulldown_cmark::Event,
src_span: Range<usize>,
cache: &mut CommonMarkCache,
options: &CommonMarkOptions,
max_width: f32,
) {
self.event(ui, event, src_span, cache, options, max_width);

self.item_list_wrapping(events, max_width, cache, options, ui);
self.fenced_code_block(events, max_width, cache, options, ui);
self.table(events, cache, options, ui, max_width);
self.blockquote(events, max_width, cache, options, ui);
}

fn item_list_wrapping<'e>(
&mut self,
events: &mut impl Iterator<Item = EventIteratorItem<'e>>,
max_width: f32,
cache: &mut CommonMarkCache,
options: &CommonMarkOptions,
ui: &mut Ui,
) {
if self.is_list_item {
self.is_list_item = false;

let item_events = delayed_events_list_item(events);
let mut events_iter = item_events.into_iter().enumerate();

// Required to ensure that the content of the list item is aligned with
// the * or - when wrapping
ui.horizontal_wrapped(|ui| {
while let Some((_, (e, src_span))) = events_iter.next() {
self.process_event(
ui,
&mut events_iter,
e,
src_span,
cache,
options,
max_width,
);
}
});
}
}

fn blockquote<'e>(
&mut self,
events: &mut impl Iterator<Item = (usize, (pulldown_cmark::Event<'e>, Range<usize>))>,
events: &mut impl Iterator<Item = EventIteratorItem<'e>>,
max_width: f32,
cache: &mut CommonMarkCache,
options: &CommonMarkOptions,
Expand Down Expand Up @@ -393,7 +454,7 @@ impl CommonMarkViewerInternal {

fn fenced_code_block<'e>(
&mut self,
events: &mut impl Iterator<Item = (usize, (pulldown_cmark::Event<'e>, Range<usize>))>,
events: &mut impl Iterator<Item = EventIteratorItem<'e>>,
max_width: f32,
cache: &mut CommonMarkCache,
options: &CommonMarkOptions,
Expand All @@ -410,7 +471,7 @@ impl CommonMarkViewerInternal {

fn table<'e>(
&mut self,
events: &mut impl Iterator<Item = (usize, (pulldown_cmark::Event<'e>, Range<usize>))>,
events: &mut impl Iterator<Item = EventIteratorItem<'e>>,
cache: &mut CommonMarkCache,
options: &CommonMarkOptions,
ui: &mut Ui,
Expand Down Expand Up @@ -566,6 +627,7 @@ impl CommonMarkViewerInternal {
}
}
pulldown_cmark::Tag::Item => {
self.is_list_item = true;
self.should_insert_newline = false;
self.list.start_item(ui, options);
}
Expand Down

0 comments on commit 07ec781

Please sign in to comment.