//! Inline markdown events, turned into styled spans. //! //! One run of inline text can sit inside several nested styles at once, //! e.g. bold inside italic, so this walks the events keeping a stack of //! active styles and patches each new one on top with [`Style::patch`]. //! A hard break starts a new [`Line`] inside the same [`Text`]; the //! caller wraps the whole thing with `crate::wrap::wrap_spans`, which //! wraps each line on its own. //! //! [`text`] renders the content of a block that is cleanly delimited by //! its own end tag, a paragraph or a heading, and consumes that end tag //! along with the content. [`run`] renders a tight list item's bare //! inline text instead, which has no wrapping tag of its own: it stops //! at whichever block-level event follows it, without consuming that //! event, so the caller can dispatch it as the item's next child. use pulldown_cmark::{Event, LinkType, Tag, TagEnd}; use ratatui::style::Style; use ratatui::text::{Line, Span, Text}; use super::{Events, style}; /// Turns the inline events of a block delimited by `end` into styled /// text, consuming the block's content and its `end` tag. The caller /// gets a cursor sitting on whatever follows the block. /// /// Every span starts from `base`, with each active style patched on top /// of it. A plain paragraph's `base` is [`Style::default`]; a heading's /// is its own [`style::heading`], and a block quote's is /// [`style::quote`], so that emphasis, a code span, or a link inside one /// of those still carries it. pub(super) fn text(events: &mut Events, end: TagEnd, base: Style) -> Text<'static> { let text = styled(events, base, |event| *event != Event::End(end)); // `styled` stops on the end tag without taking it, and the only other // way out is the end of the stream, so this reads the end tag or // nothing at all. events.next(); text } /// Turns a tight list item's bare inline events into styled text. Its /// content has no wrapping block tag, so this stops as soon as the next /// event is not one of the inline events it knows how to render, leaving /// that event for the caller to read. pub(super) fn run(events: &mut Events, base: Style) -> Text<'static> { styled(events, base, is_inline_event) } /// True for an event `run` renders itself: plain and coded text, a soft /// or hard break, raw HTML, the start of emphasis, strong, strikethrough, /// a link, or an image, and the end of one of those spans. Anything else, /// a block-level start or an unrelated end tag, is the boundary `run` /// stops at. fn is_inline_event(event: &Event<'static>) -> bool { matches!( event, Event::Text(_) | Event::Code(_) | Event::Html(_) | Event::InlineHtml(_) | Event::SoftBreak | Event::HardBreak | Event::Start( Tag::Emphasis | Tag::Strong | Tag::Strikethrough | Tag::Link { .. } | Tag::Image { .. } ) | Event::End(TagEnd::Emphasis | TagEnd::Strong | TagEnd::Strikethrough) ) } /// Walks inline events, starting from `base`, for as long as `keep_going` /// says the next event belongs to this run. fn styled( events: &mut Events, base: Style, keep_going: impl Fn(&Event<'static>) -> bool, ) -> Text<'static> { let mut lines = Vec::new(); let mut spans: Vec> = Vec::new(); let mut active = base; let mut stack: Vec