zhlint/hyper/markdown/
context.rs

1use std::{cmp::{max, min}, ops::Range};
2
3#[derive(Debug, Clone)]
4pub struct Pair {
5  pub start_range: Range<usize>,
6  pub start_content: String,
7  pub end_range: Range<usize>,
8  pub end_content: String,
9}
10
11#[derive(Debug, PartialEq, Clone)]
12pub enum InlineType {
13  Text,
14  MarkPair,
15  SingleMark,
16  SingleMarkCode,
17  SingleMarkConnect,
18}
19
20#[derive(Debug, Clone)]
21pub struct InlineMark {
22  pub pair: Pair,
23  pub meta: InlineType,
24}
25
26#[derive(Debug)]
27pub struct BlockMark {
28  pub pair: Pair,
29  pub inline_marks: Vec<InlineMark>,
30}
31
32#[derive(Debug)]
33pub struct Context<'a> {
34  pub str: &'a str,
35  pub blocks: Vec<BlockMark>,
36  pub errors: Vec<String>,
37  pub unresolved_block: Option<BlockMark>,
38  pub cur_index: usize,
39}
40
41#[derive(PartialEq)]
42enum RangeVsPair {
43  Ahead,
44  Inside,
45  Behind,
46  None,
47}
48
49fn update_pair_range(pair: &mut Pair, range: Range<usize>) -> RangeVsPair {
50  if range.start >= pair.start_range.start && range.end <= pair.end_range.end {
51    pair.start_range.end = min(range.start, pair.start_range.end);
52    pair.end_range.start = max(range.end, pair.end_range.start);
53    RangeVsPair::Inside
54  } else if range.start < pair.start_range.start {
55    RangeVsPair::Ahead
56  } else if range.end > pair.end_range.end {
57    RangeVsPair::Behind
58  } else {
59    RangeVsPair::None
60  }
61}
62
63fn determine_pair_content(str: &str, pair: &mut Pair) {
64  pair.start_content = str[pair.start_range.clone()].to_string();
65  pair.end_content = str[pair.end_range.clone()].to_string();
66}
67
68impl<'a> Context<'a> {
69  pub fn new(str: &str) -> Context {
70    Context {
71      str,
72      blocks: Vec::new(),
73      errors: Vec::new(),
74      unresolved_block: None,
75      cur_index: 0,
76    }
77  }
78  pub fn get_unresolved_inlines(&mut self) -> Vec<&mut InlineMark> {
79    self.unresolved_block.as_mut().map(|block| {
80      block.inline_marks.iter_mut().filter(|inline|
81        inline.pair.end_range.end >= self.cur_index
82      ).collect()
83    }).unwrap_or_default()
84  }
85  pub fn handle_block(&mut self, range: Range<usize>) {
86    // println!("handle block {:?} {:?}", range, &self.str[range.clone()]);
87    let current_block = BlockMark {
88      pair: Pair {
89        start_range: range.clone(), // init the right from the max length
90        start_content: String::new(), // determine when resolved
91        end_range: range.clone(), // init the left from the max length
92        end_content: String::new(), // determine when resolved
93      },
94      inline_marks: Vec::new(),
95    };
96    if self.unresolved_block.is_some() {
97      let mut unresolved_block = self.unresolved_block.take().unwrap();
98      determine_pair_content(self.str, &mut unresolved_block.pair);
99      self.blocks.push(unresolved_block);
100    }
101    self.unresolved_block = Some(current_block);
102    // println!("context {:?}", self);
103  }
104  pub fn update_unresolved_range(&mut self, range: Range<usize>) {
105    if let Some(last_block) = &mut self.unresolved_block {
106      update_pair_range(&mut last_block.pair, range.clone());
107      if self.cur_index > last_block.pair.start_range.start && self.cur_index < range.start {
108        // InlineType::SingleMarkConnect
109        let connect_range = Range { start: self.cur_index, end: range.start };
110        let connect_mark = InlineMark {
111          pair: Pair {
112            start_range: connect_range.clone(),
113            start_content: self.str[connect_range.clone()].to_string(),
114            end_range: Range { start: range.start, end: range.start },
115            end_content: String::new(),
116          },
117          meta: InlineType::SingleMarkConnect,
118        };
119        last_block.inline_marks.push(connect_mark);
120      }
121      let str = self.str;
122      let mut new_cur_index = range.end;
123      for inline in self.get_unresolved_inlines() {
124        if inline.meta == InlineType::MarkPair && update_pair_range(&mut inline.pair, range.clone()) == RangeVsPair::Behind {
125          determine_pair_content(str, &mut inline.pair);
126          new_cur_index = inline.pair.end_range.end;
127        }
128      }
129      self.cur_index = new_cur_index;
130    }
131  }
132  pub fn handle_inline(&mut self, range: Range<usize>, inline_type: InlineType) {
133    // println!("handle inline {:?} {:?} {:?} {:?}", &self.cur_index, range, inline_type, &self.str[range.clone()]);
134    // 0. => update temp start_content and end_content in the range
135    // 1. text: Text
136    // 2. mark pair: Start(Emphasis), Start(Strong), Start(Strikethrough), Start(Link)
137    //    - add to inline_marks with temp start_content and end_content
138    // 3. mark pair with code: Code
139    //    - add to inline_marks with full data
140    // 4. single mark: Start(Image), FootnoteReference, SoftBreak, HardBreak
141    //    - add to inline_marks with full data
142    self.update_unresolved_range(range.clone());
143    if let Some(last_block) = &mut self.unresolved_block {
144      match inline_type {
145        InlineType::Text => {
146          // skip
147        }
148        InlineType::MarkPair => {
149          let inline_mark = InlineMark {
150            pair: Pair {
151              start_range: range.clone(), // init the right from the max length
152              start_content: String::new(), // determine when resolved
153              end_range: range.clone(), // init the left from the max length
154              end_content: String::new(), // determine when resolved
155            },
156            meta: inline_type,
157          };
158          last_block.inline_marks.push(inline_mark);
159        }
160        InlineType::SingleMarkCode => {
161          let inline_mark = InlineMark {
162            pair: Pair {
163              start_range: range.clone(),
164              start_content: self.str[range.clone()].to_string(),
165              end_range: Range { start: range.end, end: range.end },
166              end_content: String::new(),
167            },
168            meta: inline_type,
169          };
170          self.unresolved_block.as_mut().unwrap().inline_marks.push(inline_mark);
171        }
172        InlineType::SingleMark => {
173          let inline_mark = InlineMark {
174            pair: Pair {
175              start_range: range.clone(),
176              start_content: self.str[range.clone()].to_string(),
177              end_range: Range { start: range.end, end: range.end },
178              end_content: String::new(),
179            },
180            meta: inline_type,
181          };
182          self.unresolved_block.as_mut().unwrap().inline_marks.push(inline_mark);
183        }
184        InlineType::SingleMarkConnect => {
185          // skip
186        }
187      }
188    }
189    // println!("context {:?}", self);
190  }
191  pub fn finalize(&mut self) {
192    if let Some(mut last_block) = self.unresolved_block.take() {
193      determine_pair_content(self.str, &mut last_block.pair);
194      self.blocks.push(last_block);
195    }
196  }
197}
198
199#[derive(Debug)]
200pub struct ParseResult {
201  pub blocks: Vec<BlockMark>,
202  pub errors: Vec<String>, // TODO: validation
203}