1use pulldown_cmark::{
22 Alignment, BlockQuoteKind, CodeBlockKind, Event, LinkType, Options, Parser, Tag, TagEnd,
23};
24use std::ops::Range;
25
26use crate::{
27 doc::{Align, Block, BlockKind, Doc, Form, Mark, MarkSpan, QuoteKind, Quoted, Text},
28 marks::Marks,
29 select::Cursor,
30};
31
32mod inline;
33mod lift;
34mod state;
35
36pub use inline::*;
37pub use lift::*;
38use state::*;
39
40pub(crate) fn split_size(spelled: &str) -> (&str, Option<u32>) {
44 let Some((name, tail)) = spelled.rsplit_once(char::is_whitespace) else {
45 return (spelled, None);
46 };
47 match tail.parse().ok().filter(|size| *size > 0) {
48 Some(size) => (name.trim_end(), Some(size)),
49 None => (spelled, None),
50 }
51}
52
53pub(crate) const OPTIONS: Options = Options::ENABLE_TABLES
55 .union(Options::ENABLE_STRIKETHROUGH)
56 .union(Options::ENABLE_TASKLISTS)
57 .union(Options::ENABLE_GFM);
58
59impl From<BlockQuoteKind> for QuoteKind {
60 fn from(kind: BlockQuoteKind) -> Self {
61 match kind {
62 BlockQuoteKind::Note => Self::Note,
63 BlockQuoteKind::Tip => Self::Tip,
64 BlockQuoteKind::Important => Self::Important,
65 BlockQuoteKind::Warning => Self::Warning,
66 BlockQuoteKind::Caution => Self::Caution,
67 }
68 }
69}
70
71pub fn parse(source: &str) -> Doc {
73 parse_plain(source)
74}
75
76pub struct ParsedDoc {
78 pub doc: Doc,
79 pub block_ranges: Vec<Range<usize>>,
89}
90
91pub fn parse_ranges(source: &str) -> ParsedDoc {
93 parse_ranges_with(source, &Marks::default())
94}
95
96pub fn parse_ranges_with(source: &str, marks: &Marks) -> ParsedDoc {
98 if marks.is_empty() {
99 let (doc, starts) = parse_spanned(source);
100 return ParsedDoc {
101 block_ranges: ranges(&starts, source.len()),
102 doc,
103 };
104 }
105 let (lifted, aligned) = lift(source, marks);
106 let (mut doc, starts) = parse_spanned(&lifted);
107 settle_all(&mut doc, marks);
108 let starts: Vec<usize> = starts.iter().map(|&at| unlift(&aligned, at)).collect();
109 ParsedDoc {
110 block_ranges: ranges(&starts, source.len()),
111 doc,
112 }
113}
114
115impl From<&str> for Doc {
116 fn from(source: &str) -> Self {
117 parse(source)
118 }
119}
120
121impl From<&str> for ParsedDoc {
122 fn from(source: &str) -> Self {
123 parse_ranges(source)
124 }
125}
126
127impl From<(&str, &Marks)> for Doc {
128 fn from((source, marks): (&str, &Marks)) -> Self {
129 parse_with(source, marks)
130 }
131}
132
133fn ranges(starts: &[usize], len: usize) -> Vec<Range<usize>> {
140 let mut out = Vec::with_capacity(starts.len());
141 let mut at = 0;
142 for &start in starts.iter().skip(1) {
143 let start = start.clamp(at, len);
144 out.push(at..start);
145 at = start;
146 }
147 if !starts.is_empty() {
148 out.push(at..len);
149 }
150 out
151}
152
153pub fn parse_with(source: &str, marks: &Marks) -> Doc {
161 if marks.is_empty() {
162 return parse_plain(source);
163 }
164 let mut doc = parse_plain(&lift(source, marks).0);
165 settle_all(&mut doc, marks);
166 doc
167}
168
169fn settle_all(doc: &mut Doc, marks: &Marks) {
171 for block in &mut doc.blocks {
172 for part in block.parts() {
173 if let Some(text) = block.text_at_mut(part) {
174 settle(text, marks);
175 }
176 }
177 }
178}
179
180fn parse_plain(source: &str) -> Doc {
181 parse_spanned(source).0
182}
183
184fn parse_spanned(source: &str) -> (Doc, Vec<usize>) {
189 let mut state = ParseState::default();
190 for (event, range) in Parser::new_ext(source, OPTIONS).into_offset_iter() {
191 state.event(event, range);
192 }
193 state.doc.renumber();
194 (state.doc, state.starts)
195}
196
197#[derive(Default)]
199struct TextBuilder {
200 text: String,
201 marks: Vec<MarkSpan>,
202 open: Vec<usize>,
204}
205
206impl TextBuilder {
207 fn open(&mut self, mark: Mark) {
211 let ix = self.marks.len();
212 let at = self.text.len();
213 self.marks.push(MarkSpan {
214 range: at..at,
215 mark,
216 });
217 self.open.push(ix);
218 }
219
220 fn is_empty(&self) -> bool {
223 self.text.is_empty() && self.marks.is_empty()
224 }
225
226 fn close(&mut self) {
227 if let Some(ix) = self.open.pop() {
228 self.marks[ix].range.end = self.text.len();
229 }
230 }
231
232 fn wrap(&mut self, mark: Mark, s: &str) {
234 let start = self.text.len();
235 self.text.push_str(s);
236 self.marks.push(MarkSpan {
237 range: start..self.text.len(),
238 mark,
239 });
240 }
241
242 fn take(&mut self) -> Text {
243 self.open.clear();
244 let mut text = normalize(
245 &std::mem::take(&mut self.text),
246 &std::mem::take(&mut self.marks),
247 );
248 settle_mentions(&mut text);
249 linkify(&mut text);
250 text
251 }
252}