1use crate::parser::Parser;
2use crate::support::list_marker_at;
3use crate::{Inline, Item, ListItem, ListItemBlock};
4use mos_core::SourceSpan;
5
6enum RawListItemBlock {
7 Paragraph {
8 start: usize,
9 end: usize,
10 },
11 List {
12 ordered: bool,
13 items: Vec<ListItem>,
14 span: SourceSpan,
15 },
16}
17
18impl Parser<'_> {
19 pub(crate) fn parse_list(&mut self) {
22 let Some((indent, ordered, _)) = list_marker_at(self.src.as_bytes(), self.pos) else {
23 return;
24 };
25 let item = self.parse_list_at(indent, ordered);
26 self.items.push(item);
27 }
28
29 fn parse_list_at(&mut self, base_indent: usize, base_ordered: bool) -> Item {
30 let list_start = self.pos;
31 let mut items: Vec<ListItem> = Vec::new();
32 let mut list_end = list_start;
33
34 while self.pos < self.src.len() {
35 if self.at_blank_line() {
36 break;
37 }
38 if self.at_line_comment() {
42 self.skip_line();
43 continue;
44 }
45 if self.at_block_comment() {
46 self.consume_block_comment();
47 if self.at_blank_line() {
48 self.skip_line();
49 }
50 continue;
51 }
52 let Some((indent, ordered, _)) = list_marker_at(self.src.as_bytes(), self.pos) else {
53 break;
54 };
55 if indent != base_indent || ordered != base_ordered {
56 break;
57 }
58
59 let item = self.parse_list_item(base_indent);
60 list_end = list_end.max(item.span.end());
61 items.push(item);
62 }
63
64 Item::List {
65 ordered: base_ordered,
66 items,
67 span: self.span(list_start, list_end),
68 }
69 }
70
71 fn parse_list_item(&mut self, base_indent: usize) -> ListItem {
72 let Some((_, _, content_start)) = list_marker_at(self.src.as_bytes(), self.pos) else {
73 let span = self.span(self.pos, self.pos);
74 return ListItem {
75 inlines: Vec::new(),
76 blocks: Vec::new(),
77 span,
78 };
79 };
80
81 let (line_start, content_end, line_end) = self.current_line_bounds();
82 let text_column = content_start.saturating_sub(line_start);
83 let item_start = line_start;
84 let mut item_end = content_end;
85 let mut raw_blocks = vec![RawListItemBlock::Paragraph {
86 start: content_start,
87 end: content_end,
88 }];
89 self.pos = line_end;
90
91 while self.pos < self.src.len() {
92 if self.at_blank_line() {
93 break;
94 }
95
96 if let Some((indent, ordered, _)) = list_marker_at(self.src.as_bytes(), self.pos) {
97 if indent > base_indent {
98 let nested = self.parse_list_at(indent, ordered);
99 if let Item::List {
100 ordered,
101 items,
102 span,
103 } = nested
104 {
105 item_end = item_end.max(span.end());
106 raw_blocks.push(RawListItemBlock::List {
107 ordered,
108 items,
109 span,
110 });
111 }
112 continue;
113 }
114 break;
115 }
116
117 let (line_start, content_end, line_end) = self.current_line_bounds();
118 let leading_spaces = self.leading_spaces_on_line(line_start, content_end);
119 if leading_spaces < text_column {
120 break;
121 }
122
123 let continuation_start = line_start + text_column;
124 match raw_blocks.last_mut() {
125 Some(RawListItemBlock::Paragraph { end, .. }) => {
126 *end = content_end;
127 }
128 Some(RawListItemBlock::List { .. }) | None => {
129 raw_blocks.push(RawListItemBlock::Paragraph {
130 start: continuation_start,
131 end: content_end,
132 });
133 }
134 }
135 item_end = item_end.max(content_end);
136 self.pos = line_end;
137 }
138
139 self.finish_list_item(raw_blocks, item_start, item_end)
140 }
141
142 fn finish_list_item(
143 &mut self,
144 raw_blocks: Vec<RawListItemBlock>,
145 item_start: usize,
146 item_end: usize,
147 ) -> ListItem {
148 let mut inlines: Vec<Inline> = Vec::new();
149 let mut blocks: Vec<ListItemBlock> = Vec::new();
150 let mut saw_first_paragraph = false;
151
152 for raw in raw_blocks {
153 match raw {
154 RawListItemBlock::Paragraph { start, end } => {
155 let paragraph = self.parse_list_paragraph(start, end);
156 if !saw_first_paragraph {
157 inlines.clone_from(¶graph);
158 saw_first_paragraph = true;
159 }
160 blocks.push(ListItemBlock::Paragraph {
161 inlines: paragraph,
162 span: self.span(start, end),
163 });
164 }
165 RawListItemBlock::List {
166 ordered,
167 items,
168 span,
169 } => {
170 blocks.push(ListItemBlock::List {
171 ordered,
172 items,
173 span,
174 });
175 }
176 }
177 }
178
179 ListItem {
180 inlines,
181 blocks,
182 span: self.span(item_start, item_end),
183 }
184 }
185
186 fn parse_list_paragraph(&mut self, start: usize, end: usize) -> Vec<Inline> {
187 let slice = &self.src[start..end];
188 let mut inlines = self.parse_inlines(slice, start);
189 for inline in &mut inlines {
190 if inline.text.contains("\r\n") {
191 inline.text = inline.text.replace("\r\n", "\n");
192 }
193 }
194 inlines
195 }
196
197 fn leading_spaces_on_line(&self, line_start: usize, content_end: usize) -> usize {
198 let bytes = self.src.as_bytes();
199 let mut i = line_start;
200 while i < content_end && bytes[i] == b' ' {
201 i += 1;
202 }
203 i - line_start
204 }
205}