org_rust_parser/element/
plain_list.rs

1use crate::node_pool::NodeID;
2use crate::parse::parse_element;
3use crate::types::{Cursor, Expr, ParseOpts, Parseable, Parser, Result};
4
5use crate::element::Item;
6use crate::utils::variant_eq;
7
8use super::{BulletKind, CounterKind};
9
10#[derive(Debug, Clone)]
11pub struct PlainList {
12    pub children: Vec<NodeID>,
13    pub kind: ListKind,
14}
15
16#[derive(Debug, Clone, Copy)]
17pub enum ListKind {
18    Unordered,
19    Ordered(CounterKind),
20    Descriptive,
21}
22
23impl<'a> Parseable<'a> for PlainList {
24    fn parse(
25        parser: &mut Parser<'a>,
26        mut cursor: Cursor<'a>,
27        parent: Option<NodeID>,
28        mut parse_opts: ParseOpts,
29    ) -> Result<NodeID> {
30        // parse opts will provide us the appropriate indentation level
31
32        // prevents nested lists from adding unecessary levels of indentation
33        let start = cursor.index;
34
35        if !parse_opts.from_list {
36            parse_opts.indentation_level += 1;
37            parse_opts.from_list = true;
38        }
39
40        let original_item_id = Item::parse(parser, cursor, parent, parse_opts)?;
41        let reserve_id = parser.pool.reserve_id();
42
43        let item_node = &mut parser.pool[original_item_id];
44        let kind = if let Expr::Item(item) = &item_node.obj {
45            find_kind(item)
46        } else {
47            unreachable!()
48        };
49        item_node.parent = Some(reserve_id);
50
51        let mut children: Vec<NodeID> = Vec::new();
52        children.push(original_item_id);
53
54        cursor.index = item_node.end;
55
56        while let Ok(element_id) = parse_element(parser, cursor, Some(reserve_id), parse_opts) {
57            let got_obj = &parser.pool[element_id];
58            match &got_obj.obj {
59                Expr::Item(item) => {
60                    let item_kind = find_kind(item);
61                    // makes it so that a list that encounters a different bullet list
62                    // starts a new list
63                    if !variant_eq(&item_kind, &kind) {
64                        // HACK: modifying the cache directly is janky
65                        // but we need to do this so that we dont run into a cached item
66                        parser.cache.remove(&got_obj.start);
67                        break;
68                    } else {
69                        children.push(element_id);
70                        cursor.index = got_obj.end;
71                    }
72                }
73                _ => {
74                    break;
75                }
76            }
77        }
78        Ok(parser.alloc_with_id(
79            Self { children, kind },
80            start,
81            cursor.index,
82            parent,
83            reserve_id,
84        ))
85    }
86}
87
88fn find_kind(item: &Item) -> ListKind {
89    if let Some(tag) = item.tag {
90        ListKind::Descriptive
91    } else if let BulletKind::Ordered(counter_kind) = item.bullet {
92        ListKind::Ordered(counter_kind)
93    } else {
94        ListKind::Unordered
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use crate::{parse_org, Expr};
101
102    #[test]
103    fn basic_list() {
104        let input = r"
105- one
106";
107
108        let pool = parse_org(input);
109        pool.print_tree();
110    }
111
112    #[test]
113    fn list_two_items() {
114        let input = r"
115- one
116- two
117";
118
119        let pool = parse_org(input);
120        pool.print_tree();
121    }
122
123    #[test]
124    fn list_continued_item() {
125        let input = r"
126- one
127 abcdef
128- two
129";
130
131        let pool = parse_org(input);
132        pool.print_tree();
133    }
134
135    #[test]
136    fn list_space_ending() {
137        let input = r"
138- one
139 abcdef
140- two
141- three
142- four
143
144
145- five
146";
147
148        let pool = parse_org(input);
149        pool.print_tree();
150    }
151
152    #[test]
153    fn list_inconsistent_types() {
154        let input = r"
155- one
156 abcdef
1571. two
1582. three
1593. four
160- five
161- six
162";
163
164        let pool = parse_org(input);
165        pool.print_tree();
166    }
167
168    #[test]
169    fn list_elements_breaking_flow() {
170        let input = r"
171- one
172 abcdef
173- four
174this aint a list baby
175#+begin_src python
176not a list too
177#+end_src
178
179
180- five
181";
182
183        let pool = parse_org(input);
184        pool.print_tree();
185    }
186
187    #[test]
188    fn list_contained_elements() {
189        let input = r"
190- one
191      abcd
192      eif
193  #+begin_example
194  we are eating so good?
195  #+end_example
196
197- two
198";
199
200        let pool = parse_org(input);
201        pool.print_tree();
202    }
203
204    #[test]
205    fn nested_lists_basic() {
206        let input = r"
207- one
208 - two
209- three
210";
211
212        let pool = parse_org(input);
213        pool.print_tree();
214    }
215
216    #[test]
217    fn list_empty() {
218        let input = r"
219-
220-
221-
222-
223";
224
225        let pool = parse_org(input);
226        pool.print_tree();
227    }
228
229    #[test]
230    fn list_numbered_empty() {
231        let input = r"
2321.
2332.
2343.
2354.
236";
237
238        let pool = parse_org(input);
239        pool.print_tree();
240    }
241
242    #[test]
243    fn nested_list_2() {
244        let input = r"
245- one
246  - two
247    - three
248   - four
249- five
250";
251
252        let pool = parse_org(input);
253        pool.print_tree();
254    }
255
256    #[test]
257    fn nested_list_3() {
258        let input = r"
259- one
260  - two
261    - three
262  - four
263- five
264";
265
266        let pool = parse_org(input);
267        pool.print_tree();
268    }
269
270    #[test]
271    fn nested_list_4() {
272        let input = r"
2731. item 1
2742. [X] item 2
275   - some tag :: item 2.1
276";
277
278        let pool = parse_org(input);
279        pool.print_tree();
280    }
281
282    #[test]
283    fn blank_list() {
284        let input = r"1. item 1
285   abcdef
286
287   next one two three four five
288
289   more thangs more thangs more thangs
290   more thangs
291
2922. [X] item 2
293   - aome tag :: item 2.1
294";
295
296        let pool = parse_org(input);
297        pool.print_tree();
298    }
299
300    #[test]
301    fn combined_list() {
302        let input = r"
303- zero
304- one
305
306a*
307";
308        let pool = parse_org(input);
309        pool.print_tree();
310    }
311
312    #[test]
313    fn indent_list_prop() {
314        let input = r"
315- one
316- two
317  - qq
318
319
320 heyy
321";
322        let pool = parse_org(input);
323        pool.print_tree();
324    }
325
326    #[test]
327    fn list_no_item_with_sub_element() {
328        let input = r"-   [X]
329      |a|a|a|a|
330
331-
332
333";
334
335        let pool = parse_org(input);
336        pool.print_tree();
337    }
338
339    #[test]
340    fn mixed_list() {
341        let input = r#"1. one
342- two
343"#;
344
345        let pool = parse_org(input);
346        assert_eq!(
347            pool.pool
348                .iter()
349                .filter(|x| matches!(x.obj, Expr::PlainList(_)))
350                .count(),
351            2
352        );
353    }
354}