Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/markdown/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ pub(super) fn flush_wrapped_spans(
push_wrapped_blockquote_lines(lines, spans, render_width, theme, marker_color);
} else if !item_stack.is_empty() {
let first_prefix = list_item_prefix(
blockquote_depth > 0,
blockquote_depth,
list_stack,
item_stack,
theme,
marker_color,
);
let continuation_prefix = list_item_prefix(
blockquote_depth > 0,
blockquote_depth,
list_stack,
item_stack,
theme,
Expand Down Expand Up @@ -177,7 +177,7 @@ pub(super) fn push_code_block_lines(
) -> BlockLayout {
let prefix = if !item_stack.is_empty() {
list_item_prefix(
ctx.blockquote_depth > 0,
ctx.blockquote_depth,
ctx.list_stack,
item_stack,
ctx.theme_colors,
Expand Down Expand Up @@ -310,7 +310,7 @@ pub(super) fn push_special_block_lines<F: Fn(&str) -> Vec<Span<'static>>>(
let show_line_numbers = ctx.show_line_numbers;
let center = ctx.center;
let prefix = if !item_stack.is_empty() {
list_item_prefix(blockquote_depth > 0, list_stack, item_stack, theme, None)
list_item_prefix(blockquote_depth, list_stack, item_stack, theme, None)
} else if blockquote_depth > 0 {
block_prefix(true, theme, None)
} else {
Expand Down
94 changes: 54 additions & 40 deletions src/markdown/lists.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,58 +25,71 @@ pub(super) struct ItemState {
pub(super) marker_emitted: bool,
pub(super) continuation_indent: usize,
pub(super) checkbox: Option<bool>,
pub(super) opened_at_bq_depth: usize,
}

pub(super) fn list_item_prefix(
in_bq: bool,
blockquote_depth: usize,
list_stack: &[ListKind],
item_stack: &mut [ItemState],
theme: &MarkdownTheme,
marker_color: Option<ratatui::style::Color>,
) -> Vec<Span<'static>> {
let mut prefix = block_prefix(in_bq, theme, marker_color);
let in_bq = blockquote_depth > 0;
let Some(item) = item_stack.last_mut() else {
return prefix;
return block_prefix(in_bq, theme, marker_color);
};

if item.marker_emitted {
prefix.push(Span::raw(" ".repeat(item.continuation_indent)));
return prefix;
let bq_is_outer = in_bq && blockquote_depth == item.opened_at_bq_depth;

let mut prefix = Vec::new();

if bq_is_outer {
prefix.extend(block_prefix(in_bq, theme, marker_color));
}

let depth = list_stack.len();
prefix.push(Span::raw(" ".repeat(depth.saturating_sub(1))));

let marker: Cow<'static, str> = match item.checkbox {
// Avoids U+2611 emoji rendering on Windows.
Some(true) if cfg!(target_os = "windows") => TASK_CHECKED_ALT.into(),
Some(true) => TASK_CHECKED.into(),
Some(false) => TASK_UNCHECKED.into(),
None => match list_stack.last().copied().unwrap_or(ListKind::Unordered) {
ListKind::Unordered => match depth {
1 => "• ".into(),
2 => "◦ ".into(),
_ => "▸ ".into(),
if item.marker_emitted {
prefix.push(Span::raw(" ".repeat(item.continuation_indent)));
} else {
let depth = list_stack.len();
prefix.push(Span::raw(" ".repeat(depth.saturating_sub(1))));

let marker: Cow<'static, str> = match item.checkbox {
// Avoids U+2611 emoji rendering on Windows.
Some(true) if cfg!(target_os = "windows") => TASK_CHECKED_ALT.into(),
Some(true) => TASK_CHECKED.into(),
Some(false) => TASK_UNCHECKED.into(),
None => match list_stack.last().copied().unwrap_or(ListKind::Unordered) {
ListKind::Unordered => match depth {
1 => "• ".into(),
2 => "◦ ".into(),
_ => "▸ ".into(),
},
ListKind::Ordered(n) => format!("{n}. ").into(),
},
ListKind::Ordered(n) => format!("{n}. ").into(),
},
};
item.continuation_indent = " ".repeat(depth.saturating_sub(1)).len() + display_width(&marker);
item.marker_emitted = true;

let marker_style = match item.checkbox {
Some(true) => Style::default().fg(theme.task_checked),
Some(false) => Style::default().fg(theme.task_unchecked),
None => match list_stack.last().copied().unwrap_or(ListKind::Unordered) {
ListKind::Unordered => match depth {
1 => Style::default().fg(theme.list_level_1),
2 => Style::default().fg(theme.list_level_2),
_ => Style::default().fg(theme.list_level_3),
};
item.continuation_indent = 2 * depth.saturating_sub(1) + display_width(&marker);
item.marker_emitted = true;

let marker_style = match item.checkbox {
Some(true) => Style::default().fg(theme.task_checked),
Some(false) => Style::default().fg(theme.task_unchecked),
None => match list_stack.last().copied().unwrap_or(ListKind::Unordered) {
ListKind::Unordered => match depth {
1 => Style::default().fg(theme.list_level_1),
2 => Style::default().fg(theme.list_level_2),
_ => Style::default().fg(theme.list_level_3),
},
ListKind::Ordered(_) => Style::default().fg(theme.ordered_list),
},
ListKind::Ordered(_) => Style::default().fg(theme.ordered_list),
},
};
prefix.push(Span::styled(marker, marker_style));
};
prefix.push(Span::styled(marker, marker_style));
}

if in_bq && !bq_is_outer {
prefix.extend(block_prefix(in_bq, theme, marker_color));
}

prefix
}

Expand All @@ -96,14 +109,14 @@ pub(super) fn flush_list_item_spans(
}

let first_prefix = list_item_prefix(
blockquote_depth > 0,
blockquote_depth,
list_stack,
item_stack,
theme,
marker_color,
);
let continuation_prefix = list_item_prefix(
blockquote_depth > 0,
blockquote_depth,
list_stack,
item_stack,
theme,
Expand Down Expand Up @@ -138,11 +151,12 @@ pub(super) fn end_list(lines: &mut Vec<Line<'static>>, list_stack: &mut Vec<List
}
}

pub(super) fn start_item(item_stack: &mut Vec<ItemState>) {
pub(super) fn start_item(item_stack: &mut Vec<ItemState>, blockquote_depth: usize) {
item_stack.push(ItemState {
marker_emitted: false,
continuation_indent: 0,
checkbox: None,
opened_at_bq_depth: blockquote_depth,
});
}

Expand Down
24 changes: 15 additions & 9 deletions src/markdown/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ use blocks::{
use fences::normalize_code_fences;
use links::build_link_spans;
use lists::{
end_item, end_list, flush_list_item_spans, start_item, start_list, ItemState, ListKind,
end_item, end_list, flush_list_item_spans, list_item_prefix, start_item, start_list, ItemState,
ListKind,
};
#[cfg(test)]
pub(crate) use lists::{TASK_CHECKED, TASK_CHECKED_ALT, TASK_UNCHECKED};
Expand Down Expand Up @@ -542,13 +543,18 @@ pub(crate) fn parse_markdown_with_width(
let color = alert_color(k, theme_colors);
blockquote_color = Some(color);
let (icon, label) = alert_icon_label(k);
lines.push(Line::from(vec![
Span::styled("▏ ", Style::default().fg(color)),
Span::styled(
format!("{icon} {label}"),
Style::default().fg(color).add_modifier(Modifier::BOLD),
),
]));
let mut header = list_item_prefix(
blockquote_depth,
&list_stack,
&mut item_stack,
theme_colors,
blockquote_color,
);
header.push(Span::styled(
format!("{icon} {label}"),
Style::default().fg(color).add_modifier(Modifier::BOLD),
));
lines.push(Line::from(header));
}
}
MdEvent::End(TagEnd::BlockQuote(_)) => {
Expand Down Expand Up @@ -584,7 +590,7 @@ pub(crate) fn parse_markdown_with_width(
last_block = LastBlock::Other;
}
MdEvent::Start(Tag::Item) => {
start_item(&mut item_stack);
start_item(&mut item_stack, blockquote_depth);
}
MdEvent::End(TagEnd::Item) => {
end_item(
Expand Down
Loading
Loading