Refactor ListItem into Bullet in markdown widget

This commit is contained in:
Héctor Ramón Jiménez 2025-11-29 13:22:28 +01:00
parent 798e777772
commit 6c271541b0
No known key found for this signature in database
GPG key ID: 7CC46565708259A7

View file

@ -210,7 +210,7 @@ pub enum Item {
/// The first number of the list, if it is ordered. /// The first number of the list, if it is ordered.
start: Option<u64>, start: Option<u64>,
/// The items of the list. /// The items of the list.
items: Vec<ListItem>, bullets: Vec<Bullet>,
}, },
/// An image. /// An image.
Image { Image {
@ -354,14 +354,32 @@ impl Span {
/// The item of a list. /// The item of a list.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ListItem { pub enum Bullet {
checked: Option<bool>, /// A simple bullet point.
items: Vec<Item>, Point {
/// The contents of the bullet point.
items: Vec<Item>,
},
/// A task.
Task {
/// The contents of the task.
items: Vec<Item>,
/// Whether the task is done or not.
done: bool,
},
} }
impl ListItem { impl Bullet {
fn items(&self) -> &[Item] {
match self {
Bullet::Point { items } | Bullet::Task { items, .. } => items,
}
}
fn push(&mut self, item: Item) { fn push(&mut self, item: Item) {
self.items.push(item); let (Bullet::Point { items } | Bullet::Task { items, .. }) = self;
items.push(item);
} }
} }
@ -518,7 +536,7 @@ fn parse_with<'a>(
struct List { struct List {
start: Option<u64>, start: Option<u64>,
items: Vec<ListItem>, bullets: Vec<Bullet>,
} }
let broken_links = Rc::new(RefCell::new(HashSet::new())); let broken_links = Rc::new(RefCell::new(HashSet::new()));
@ -582,7 +600,7 @@ fn parse_with<'a>(
if let Some(scope) = stack.last_mut() { if let Some(scope) = stack.last_mut() {
match scope { match scope {
Scope::List(list) => { Scope::List(list) => {
list.items.last_mut().expect("item context").push(item); list.bullets.last_mut().expect("item context").push(item);
} }
Scope::Quote(items) => { Scope::Quote(items) => {
items.push(item); items.push(item);
@ -646,17 +664,14 @@ fn parse_with<'a>(
stack.push(Scope::List(List { stack.push(Scope::List(List {
start: first_item, start: first_item,
items: Vec::new(), bullets: Vec::new(),
})); }));
prev prev
} }
pulldown_cmark::Tag::Item => { pulldown_cmark::Tag::Item => {
if let Some(Scope::List(list)) = stack.last_mut() { if let Some(Scope::List(list)) = stack.last_mut() {
list.items.push(ListItem { list.bullets.push(Bullet::Point { items: Vec::new() });
checked: None,
items: Vec::new(),
});
} }
None None
@ -800,7 +815,7 @@ fn parse_with<'a>(
&mut stack, &mut stack,
Item::List { Item::List {
start: list.start, start: list.start,
items: list.items, bullets: list.bullets,
}, },
source, source,
) )
@ -989,11 +1004,15 @@ fn parse_with<'a>(
pulldown_cmark::Event::Rule => { pulldown_cmark::Event::Rule => {
produce(state.borrow_mut(), &mut stack, Item::Rule, source) produce(state.borrow_mut(), &mut stack, Item::Rule, source)
} }
pulldown_cmark::Event::TaskListMarker(checked) => { pulldown_cmark::Event::TaskListMarker(done) => {
if let Some(Scope::List(list)) = stack.last_mut() if let Some(Scope::List(list)) = stack.last_mut()
&& let Some(item) = list.items.last_mut() && let Some(item) = list.bullets.last_mut()
&& let Bullet::Point { items } = item
{ {
item.checked = Some(checked); *item = Bullet::Task {
items: std::mem::take(items),
done,
};
} }
None None
@ -1229,13 +1248,14 @@ where
code, code,
lines, lines,
} => viewer.code_block(settings, language.as_deref(), code, lines), } => viewer.code_block(settings, language.as_deref(), code, lines),
Item::List { start: None, items } => { Item::List {
viewer.unordered_list(settings, items) start: None,
} bullets,
} => viewer.unordered_list(settings, bullets),
Item::List { Item::List {
start: Some(start), start: Some(start),
items, bullets,
} => viewer.ordered_list(settings, *start, items), } => viewer.ordered_list(settings, *start, bullets),
Item::Quote(quote) => viewer.quote(settings, quote), Item::Quote(quote) => viewer.quote(settings, quote),
Item::Rule => viewer.rule(settings), Item::Rule => viewer.rule(settings),
Item::Table { columns, rows } => viewer.table(settings, columns, rows), Item::Table { columns, rows } => viewer.table(settings, columns, rows),
@ -1308,28 +1328,29 @@ where
pub fn unordered_list<'a, Message, Theme, Renderer>( pub fn unordered_list<'a, Message, Theme, Renderer>(
viewer: &impl Viewer<'a, Message, Theme, Renderer>, viewer: &impl Viewer<'a, Message, Theme, Renderer>,
settings: Settings, settings: Settings,
items: &'a [ListItem], bullets: &'a [Bullet],
) -> Element<'a, Message, Theme, Renderer> ) -> Element<'a, Message, Theme, Renderer>
where where
Message: 'a, Message: 'a,
Theme: Catalog + 'a, Theme: Catalog + 'a,
Renderer: core::text::Renderer<Font = Font> + 'a, Renderer: core::text::Renderer<Font = Font> + 'a,
{ {
column(items.iter().map(|list_item| { column(bullets.iter().map(|bullet| {
row![ row![
list_item match bullet {
.checked Bullet::Point { .. } => {
.map(|is_checked| Element::from(
checkbox(is_checked).text_size(settings.text_size)
))
.unwrap_or_else(|| Element::from(
text("•") text("•")
.width(settings.text_size) .width(settings.text_size)
.center() .center()
.size(settings.text_size) .size(settings.text_size)
)), .into()
}
Bullet::Task { done, .. } => {
Element::from(checkbox(*done).size(settings.text_size))
}
},
view_with( view_with(
&list_item.items, bullet.items(),
Settings { Settings {
spacing: settings.spacing * 0.6, spacing: settings.spacing * 0.6,
..settings ..settings
@ -1351,39 +1372,25 @@ pub fn ordered_list<'a, Message, Theme, Renderer>(
viewer: &impl Viewer<'a, Message, Theme, Renderer>, viewer: &impl Viewer<'a, Message, Theme, Renderer>,
settings: Settings, settings: Settings,
start: u64, start: u64,
items: &'a [ListItem], bullets: &'a [Bullet],
) -> Element<'a, Message, Theme, Renderer> ) -> Element<'a, Message, Theme, Renderer>
where where
Message: 'a, Message: 'a,
Theme: Catalog + 'a, Theme: Catalog + 'a,
Renderer: core::text::Renderer<Font = Font> + 'a, Renderer: core::text::Renderer<Font = Font> + 'a,
{ {
let digits = ((start + items.len() as u64).max(1) as f32).log10().ceil(); let digits = ((start + bullets.len() as u64).max(1) as f32)
.log10()
.ceil();
column(items.iter().enumerate().map(|(i, list_item)| { column(bullets.iter().enumerate().map(|(i, bullet)| {
row![ row![
list_item text!("{}.", i as u64 + start)
.checked .size(settings.text_size)
.map(|is_checked| { .align_x(alignment::Horizontal::Right)
Element::from( .width(settings.text_size * ((digits / 2.0).ceil() + 1.0),),
container(
checkbox(is_checked).text_size(settings.text_size),
)
.align_right(
settings.text_size * ((digits / 2.0).ceil() + 1.0),
),
)
})
.unwrap_or_else(|| Element::from(
text!("{}.", i as u64 + start)
.size(settings.text_size)
.align_x(alignment::Horizontal::Right)
.width(
settings.text_size * ((digits / 2.0).ceil() + 1.0)
),
)),
view_with( view_with(
&list_item.items, bullet.items(),
Settings { Settings {
spacing: settings.spacing * 0.6, spacing: settings.spacing * 0.6,
..settings ..settings
@ -1622,9 +1629,9 @@ where
fn unordered_list( fn unordered_list(
&self, &self,
settings: Settings, settings: Settings,
items: &'a [ListItem], bullets: &'a [Bullet],
) -> Element<'a, Message, Theme, Renderer> { ) -> Element<'a, Message, Theme, Renderer> {
unordered_list(self, settings, items) unordered_list(self, settings, bullets)
} }
/// Displays an ordered list. /// Displays an ordered list.
@ -1634,9 +1641,9 @@ where
&self, &self,
settings: Settings, settings: Settings,
start: u64, start: u64,
items: &'a [ListItem], bullets: &'a [Bullet],
) -> Element<'a, Message, Theme, Renderer> { ) -> Element<'a, Message, Theme, Renderer> {
ordered_list(self, settings, start, items) ordered_list(self, settings, start, bullets)
} }
/// Displays a quote. /// Displays a quote.