partial.rs (3010B)
1 //! Partial state tracking for incomplete markdown elements. 2 3 use crate::element::Span; 4 5 /// Tracks an in-progress markdown element that might be completed 6 /// when more tokens arrive. 7 #[derive(Debug, Clone)] 8 pub struct Partial { 9 /// What kind of element we're building 10 pub kind: PartialKind, 11 12 /// Byte offset into the buffer where this element starts 13 pub start_pos: usize, 14 15 /// Start of content region (after markers like "# ") 16 pub content_start: usize, 17 18 /// End of content accumulated so far 19 pub content_end: usize, 20 } 21 22 impl Partial { 23 pub fn new(kind: PartialKind, start_pos: usize) -> Self { 24 Self { 25 kind, 26 start_pos, 27 content_start: start_pos, 28 content_end: start_pos, 29 } 30 } 31 32 /// Get the content span 33 pub fn content_span(&self) -> Span { 34 Span::new(self.content_start, self.content_end) 35 } 36 37 /// Get the content as a string slice from the buffer 38 pub fn content<'a>(&self, buffer: &'a str) -> &'a str { 39 &buffer[self.content_start..self.content_end] 40 } 41 42 /// Check if content is empty 43 pub fn content_is_empty(&self) -> bool { 44 self.content_start == self.content_end 45 } 46 } 47 48 /// The kind of partial element being tracked. 49 #[derive(Debug, Clone, PartialEq)] 50 pub enum PartialKind { 51 /// Fenced code block waiting for closing ``` 52 /// Stores the fence info (backticks count, language) 53 CodeFence { 54 fence_char: char, // ` or ~ 55 fence_len: usize, // typically 3 56 language: Option<Span>, 57 }, 58 59 /// Inline code waiting for closing backtick(s) 60 InlineCode { backtick_count: usize }, 61 62 /// Bold text waiting for closing ** or __ 63 Bold { 64 marker: char, // * or _ 65 }, 66 67 /// Italic text waiting for closing * or _ 68 Italic { marker: char }, 69 70 /// Bold+italic waiting for closing *** or ___ 71 BoldItalic { marker: char }, 72 73 /// Strikethrough waiting for closing ~~ 74 Strikethrough, 75 76 /// Link: seen [, waiting for ](url) 77 /// States: text, post-bracket, url 78 Link { state: LinkState, text: Span }, 79 80 /// Image: seen  81 Image { state: LinkState, alt: Span }, 82 83 /// Heading started with # at line start, collecting content 84 Heading { level: u8 }, 85 86 /// List item started, collecting content 87 ListItem { 88 ordered: bool, 89 number: Option<u32>, 90 indent: usize, 91 }, 92 93 /// Blockquote started with >, collecting content 94 BlockQuote { depth: usize }, 95 96 /// Table being accumulated row by row 97 Table { 98 headers: Vec<Span>, 99 rows: Vec<Vec<Span>>, 100 seen_separator: bool, 101 }, 102 103 /// Paragraph being accumulated (waiting for double newline) 104 Paragraph, 105 } 106 107 /// State machine for link/image parsing. 108 #[derive(Debug, Clone, PartialEq)] 109 pub enum LinkState { 110 /// Collecting text between [ and ] 111 Text, 112 /// Seen ], expecting ( 113 PostBracket, 114 /// Collecting URL between ( and ) 115 Url(Span), 116 }