comrak/
cm.rs

1use crate::ctype::{isalpha, isdigit, ispunct, isspace};
2use crate::nodes::{
3    AstNode, ListDelimType, ListType, NodeAlert, NodeCodeBlock, NodeHeading, NodeHtmlBlock,
4    NodeLink, NodeMath, NodeTable, NodeValue, NodeWikiLink,
5};
6use crate::nodes::{NodeList, TableAlignment};
7#[cfg(feature = "shortcodes")]
8use crate::parser::shortcodes::NodeShortCode;
9use crate::parser::{Options, WikiLinksMode};
10use crate::scanners;
11use crate::strings::trim_start_match;
12use crate::{nodes, Plugins};
13
14use std::cmp::max;
15use std::io::{self, Write};
16
17/// Formats an AST as CommonMark, modified by the given options.
18pub fn format_document<'a>(
19    root: &'a AstNode<'a>,
20    options: &Options,
21    output: &mut dyn Write,
22) -> io::Result<()> {
23    // Formatting an ill-formed AST might lead to invalid output. However, we don't want to pay for
24    // validation in normal workflow. As a middleground, we validate the AST in debug builds. See
25    // https://github.com/kivikakk/comrak/issues/371.
26    #[cfg(debug_assertions)]
27    root.validate().unwrap_or_else(|e| {
28        panic!("The document to format is ill-formed: {:?}", e);
29    });
30
31    format_document_with_plugins(root, options, output, &Plugins::default())
32}
33
34/// Formats an AST as CommonMark, modified by the given options. Accepts custom plugins.
35pub fn format_document_with_plugins<'a>(
36    root: &'a AstNode<'a>,
37    options: &Options,
38    output: &mut dyn Write,
39    _plugins: &Plugins,
40) -> io::Result<()> {
41    let mut f = CommonMarkFormatter::new(root, options);
42    f.format(root);
43    if !f.v.is_empty() && f.v[f.v.len() - 1] != b'\n' {
44        f.v.push(b'\n');
45    }
46    output.write_all(&f.v)?;
47    Ok(())
48}
49
50struct CommonMarkFormatter<'a, 'o, 'c> {
51    node: &'a AstNode<'a>,
52    options: &'o Options<'c>,
53    v: Vec<u8>,
54    prefix: Vec<u8>,
55    column: usize,
56    need_cr: u8,
57    last_breakable: usize,
58    begin_line: bool,
59    begin_content: bool,
60    no_linebreaks: bool,
61    in_tight_list_item: bool,
62    custom_escape: Option<fn(&'a AstNode<'a>, u8) -> bool>,
63    footnote_ix: u32,
64    ol_stack: Vec<usize>,
65}
66
67#[derive(PartialEq, Clone, Copy)]
68enum Escaping {
69    Literal,
70    Normal,
71    Url,
72    Title,
73}
74
75impl<'a, 'o, 'c> Write for CommonMarkFormatter<'a, 'o, 'c> {
76    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
77        self.output(buf, false, Escaping::Literal);
78        Ok(buf.len())
79    }
80
81    fn flush(&mut self) -> std::io::Result<()> {
82        Ok(())
83    }
84}
85
86impl<'a, 'o, 'c> CommonMarkFormatter<'a, 'o, 'c> {
87    fn new(node: &'a AstNode<'a>, options: &'o Options<'c>) -> Self {
88        CommonMarkFormatter {
89            node,
90            options,
91            v: vec![],
92            prefix: vec![],
93            column: 0,
94            need_cr: 0,
95            last_breakable: 0,
96            begin_line: true,
97            begin_content: true,
98            no_linebreaks: false,
99            in_tight_list_item: false,
100            custom_escape: None,
101            footnote_ix: 0,
102            ol_stack: vec![],
103        }
104    }
105
106    fn output(&mut self, buf: &[u8], wrap: bool, escaping: Escaping) {
107        let wrap = wrap && !self.no_linebreaks;
108
109        if self.in_tight_list_item && self.need_cr > 1 {
110            self.need_cr = 1;
111        }
112
113        let mut k = self.v.len() as i32 - 1;
114        while self.need_cr > 0 {
115            if k < 0 || self.v[k as usize] == b'\n' {
116                k -= 1;
117            } else {
118                self.v.push(b'\n');
119                if self.need_cr > 1 {
120                    self.v.extend(&self.prefix);
121                }
122            }
123            self.column = 0;
124            self.last_breakable = 0;
125            self.begin_line = true;
126            self.begin_content = true;
127            self.need_cr -= 1;
128        }
129
130        let mut i = 0;
131        while i < buf.len() {
132            if self.begin_line {
133                self.v.extend(&self.prefix);
134                self.column = self.prefix.len();
135            }
136
137            if self.custom_escape.is_some() && self.custom_escape.unwrap()(self.node, buf[i]) {
138                self.v.push(b'\\');
139            }
140
141            let nextc = buf.get(i + 1);
142            if buf[i] == b' ' && wrap {
143                if !self.begin_line {
144                    let last_nonspace = self.v.len();
145                    self.v.push(b' ');
146                    self.column += 1;
147                    self.begin_line = false;
148                    self.begin_content = false;
149                    while buf.get(i + 1) == Some(&(b' ')) {
150                        i += 1;
151                    }
152                    if !buf.get(i + 1).map_or(false, |&c| isdigit(c)) {
153                        self.last_breakable = last_nonspace;
154                    }
155                }
156            } else if escaping == Escaping::Literal {
157                if buf[i] == b'\n' {
158                    self.v.push(b'\n');
159                    self.column = 0;
160                    self.begin_line = true;
161                    self.begin_content = true;
162                    self.last_breakable = 0;
163                } else {
164                    self.v.push(buf[i]);
165                    self.column += 1;
166                    self.begin_line = false;
167                    self.begin_content = self.begin_content && isdigit(buf[i]);
168                }
169            } else {
170                self.outc(buf[i], escaping, nextc);
171                self.begin_line = false;
172                self.begin_content = self.begin_content && isdigit(buf[i]);
173            }
174
175            if self.options.render.width > 0
176                && self.column > self.options.render.width
177                && !self.begin_line
178                && self.last_breakable > 0
179            {
180                let remainder = self.v[self.last_breakable + 1..].to_vec();
181                self.v.truncate(self.last_breakable);
182                self.v.push(b'\n');
183                self.v.extend(&self.prefix);
184                self.v.extend(&remainder);
185                self.column = self.prefix.len() + remainder.len();
186                self.last_breakable = 0;
187                self.begin_line = false;
188                self.begin_content = false;
189            }
190
191            i += 1;
192        }
193    }
194
195    fn outc(&mut self, c: u8, escaping: Escaping, nextc: Option<&u8>) {
196        let follows_digit = !self.v.is_empty() && isdigit(self.v[self.v.len() - 1]);
197
198        let nextc = nextc.map_or(0, |&c| c);
199
200        let needs_escaping = c < 0x80
201            && escaping != Escaping::Literal
202            && ((escaping == Escaping::Normal
203                && (c < 0x20
204                    || c == b'*'
205                    || c == b'_'
206                    || c == b'['
207                    || c == b']'
208                    || c == b'#'
209                    || c == b'<'
210                    || c == b'>'
211                    || c == b'\\'
212                    || c == b'`'
213                    || c == b'!'
214                    || (c == b'&' && isalpha(nextc))
215                    || (c == b'!' && nextc == 0x5b)
216                    || (self.begin_content
217                        && (c == b'-' || c == b'+' || c == b'=')
218                        && !follows_digit)
219                    || (self.begin_content
220                        && (c == b'.' || c == b')')
221                        && follows_digit
222                        && (nextc == 0 || isspace(nextc)))))
223                || (escaping == Escaping::Url
224                    && (c == b'`'
225                        || c == b'<'
226                        || c == b'>'
227                        || isspace(c)
228                        || c == b'\\'
229                        || c == b')'
230                        || c == b'('))
231                || (escaping == Escaping::Title
232                    && (c == b'`' || c == b'<' || c == b'>' || c == b'"' || c == b'\\')));
233
234        if needs_escaping {
235            if escaping == Escaping::Url && isspace(c) {
236                write!(self.v, "%{:2X}", c).unwrap();
237                self.column += 3;
238            } else if ispunct(c) {
239                write!(self.v, "\\{}", c as char).unwrap();
240                self.column += 2;
241            } else {
242                let s = format!("&#{};", c);
243                self.write_all(s.as_bytes()).unwrap();
244                self.column += s.len();
245            }
246        } else {
247            self.v.push(c);
248            self.column += 1;
249        }
250    }
251
252    fn cr(&mut self) {
253        self.need_cr = max(self.need_cr, 1);
254    }
255
256    fn blankline(&mut self) {
257        self.need_cr = max(self.need_cr, 2);
258    }
259
260    fn format(&mut self, node: &'a AstNode<'a>) {
261        enum Phase {
262            Pre,
263            Post,
264        }
265        let mut stack = vec![(node, Phase::Pre)];
266
267        while let Some((node, phase)) = stack.pop() {
268            match phase {
269                Phase::Pre => {
270                    if self.format_node(node, true) {
271                        stack.push((node, Phase::Post));
272                        for ch in node.reverse_children() {
273                            stack.push((ch, Phase::Pre));
274                        }
275                    }
276                }
277                Phase::Post => {
278                    self.format_node(node, false);
279                }
280            }
281        }
282    }
283
284    fn get_in_tight_list_item(&self, node: &'a AstNode<'a>) -> bool {
285        let tmp = match nodes::containing_block(node) {
286            Some(tmp) => tmp,
287            None => return false,
288        };
289
290        match tmp.data.borrow().value {
291            NodeValue::Item(..) | NodeValue::TaskItem(..) => {
292                if let NodeValue::List(ref nl) = tmp.parent().unwrap().data.borrow().value {
293                    return nl.tight;
294                }
295                return false;
296            }
297            _ => {}
298        }
299
300        let parent = match tmp.parent() {
301            Some(parent) => parent,
302            None => return false,
303        };
304
305        match parent.data.borrow().value {
306            NodeValue::Item(..) | NodeValue::TaskItem(..) => {
307                if let NodeValue::List(ref nl) = parent.parent().unwrap().data.borrow().value {
308                    return nl.tight;
309                }
310            }
311            _ => {}
312        }
313
314        false
315    }
316
317    fn format_node(&mut self, node: &'a AstNode<'a>, entering: bool) -> bool {
318        self.node = node;
319        let allow_wrap = self.options.render.width > 0 && !self.options.render.hardbreaks;
320
321        let parent_node = node.parent();
322        if entering {
323            if parent_node.is_some()
324                && matches!(
325                    parent_node.unwrap().data.borrow().value,
326                    NodeValue::Item(..) | NodeValue::TaskItem(..)
327                )
328            {
329                self.in_tight_list_item = self.get_in_tight_list_item(node);
330            }
331        } else if matches!(node.data.borrow().value, NodeValue::List(..)) {
332            self.in_tight_list_item = parent_node.is_some()
333                && matches!(
334                    parent_node.unwrap().data.borrow().value,
335                    NodeValue::Item(..) | NodeValue::TaskItem(..)
336                )
337                && self.get_in_tight_list_item(node);
338        }
339        let next_is_block = node
340            .next_sibling()
341            .map_or(true, |next| next.data.borrow().value.block());
342
343        match node.data.borrow().value {
344            NodeValue::Document => (),
345            NodeValue::FrontMatter(ref fm) => self.format_front_matter(fm.as_bytes(), entering),
346            NodeValue::BlockQuote => self.format_block_quote(entering),
347            NodeValue::List(..) => self.format_list(node, entering),
348            NodeValue::Item(..) => self.format_item(node, entering),
349            NodeValue::DescriptionList => (),
350            NodeValue::DescriptionItem(..) => (),
351            NodeValue::DescriptionTerm => (),
352            NodeValue::DescriptionDetails => self.format_description_details(entering),
353            NodeValue::Heading(ref nch) => self.format_heading(nch, entering),
354            NodeValue::CodeBlock(ref ncb) => self.format_code_block(node, ncb, entering),
355            NodeValue::HtmlBlock(ref nhb) => self.format_html_block(nhb, entering),
356            NodeValue::ThematicBreak => self.format_thematic_break(entering),
357            NodeValue::Paragraph => self.format_paragraph(entering),
358            NodeValue::Text(ref literal) => {
359                self.format_text(literal.as_bytes(), allow_wrap, entering)
360            }
361            NodeValue::LineBreak => self.format_line_break(entering, next_is_block),
362            NodeValue::SoftBreak => self.format_soft_break(allow_wrap, entering),
363            NodeValue::Code(ref code) => {
364                self.format_code(code.literal.as_bytes(), allow_wrap, entering)
365            }
366            NodeValue::HtmlInline(ref literal) => {
367                self.format_html_inline(literal.as_bytes(), entering)
368            }
369            NodeValue::Raw(ref literal) => self.format_raw(literal.as_bytes(), entering),
370            NodeValue::Strong => {
371                if parent_node.is_none()
372                    || !matches!(parent_node.unwrap().data.borrow().value, NodeValue::Strong)
373                {
374                    self.format_strong();
375                }
376            }
377            NodeValue::Emph => self.format_emph(node),
378            NodeValue::TaskItem(symbol) => self.format_task_item(symbol, node, entering),
379            NodeValue::Strikethrough => self.format_strikethrough(),
380            NodeValue::Superscript => self.format_superscript(),
381            NodeValue::Link(ref nl) => return self.format_link(node, nl, entering),
382            NodeValue::Image(ref nl) => self.format_image(nl, allow_wrap, entering),
383            #[cfg(feature = "shortcodes")]
384            NodeValue::ShortCode(ref ne) => self.format_shortcode(ne, entering),
385            NodeValue::Table(..) => self.format_table(entering),
386            NodeValue::TableRow(..) => self.format_table_row(entering),
387            NodeValue::TableCell => self.format_table_cell(node, entering),
388            NodeValue::FootnoteDefinition(ref nfd) => {
389                self.format_footnote_definition(&nfd.name, entering)
390            }
391            NodeValue::FootnoteReference(ref nfr) => {
392                self.format_footnote_reference(nfr.name.as_bytes(), entering)
393            }
394            NodeValue::MultilineBlockQuote(..) => self.format_block_quote(entering),
395            NodeValue::Escaped => {
396                // noop - automatic escaping is already being done
397            }
398            NodeValue::Math(ref math) => self.format_math(math, allow_wrap, entering),
399            NodeValue::WikiLink(ref nl) => return self.format_wikilink(nl, entering),
400            NodeValue::Underline => self.format_underline(),
401            NodeValue::Subscript => self.format_subscript(),
402            NodeValue::SpoileredText => self.format_spoiler(),
403            NodeValue::EscapedTag(ref net) => self.format_escaped_tag(net),
404            NodeValue::Alert(ref alert) => self.format_alert(alert, entering),
405        };
406        true
407    }
408
409    fn format_front_matter(&mut self, front_matter: &[u8], entering: bool) {
410        if entering {
411            self.output(front_matter, false, Escaping::Literal);
412        }
413    }
414
415    fn format_block_quote(&mut self, entering: bool) {
416        if entering {
417            write!(self, "> ").unwrap();
418            self.begin_content = true;
419            write!(self.prefix, "> ").unwrap();
420        } else {
421            let new_len = self.prefix.len() - 2;
422            self.prefix.truncate(new_len);
423            self.blankline();
424        }
425    }
426
427    fn format_list(&mut self, node: &'a AstNode<'a>, entering: bool) {
428        let ol_start = match node.data.borrow().value {
429            NodeValue::List(NodeList {
430                list_type: ListType::Ordered,
431                start,
432                ..
433            }) => Some(start),
434            _ => None,
435        };
436
437        if entering {
438            if let Some(start) = ol_start {
439                self.ol_stack.push(start);
440            }
441        } else {
442            if ol_start.is_some() {
443                self.ol_stack.pop();
444            }
445
446            if match node.next_sibling() {
447                Some(next_sibling) => matches!(
448                    next_sibling.data.borrow().value,
449                    NodeValue::CodeBlock(..) | NodeValue::List(..)
450                ),
451                _ => false,
452            } {
453                self.cr();
454                write!(self, "<!-- end list -->").unwrap();
455                self.blankline();
456            }
457        }
458    }
459
460    fn format_item(&mut self, node: &'a AstNode<'a>, entering: bool) {
461        let parent = match node.parent().unwrap().data.borrow().value {
462            NodeValue::List(ref nl) => *nl,
463            _ => unreachable!(),
464        };
465
466        let mut listmarker = vec![];
467
468        let marker_width = if parent.list_type == ListType::Bullet {
469            2
470        } else {
471            let list_number = if let Some(last_stack) = self.ol_stack.last_mut() {
472                let list_number = *last_stack;
473                if entering {
474                    *last_stack += 1;
475                };
476                list_number
477            } else {
478                match node.data.borrow().value {
479                    NodeValue::Item(ref ni) => ni.start,
480                    NodeValue::TaskItem(_) => parent.start,
481                    _ => unreachable!(),
482                }
483            };
484            let list_delim = parent.delimiter;
485            write!(
486                listmarker,
487                "{}{} ",
488                list_number,
489                if list_delim == ListDelimType::Paren {
490                    ")"
491                } else {
492                    "."
493                }
494            )
495            .unwrap();
496            let mut current_len = listmarker.len();
497
498            while current_len < self.options.render.ol_width {
499                write!(listmarker, " ").unwrap();
500                current_len += 1;
501            }
502
503            listmarker.len()
504        };
505
506        if entering {
507            if parent.list_type == ListType::Bullet {
508                let bullet = char::from(self.options.render.list_style as u8);
509                write!(self, "{} ", bullet).unwrap();
510            } else {
511                self.write_all(&listmarker).unwrap();
512            }
513            self.begin_content = true;
514            for _ in 0..marker_width {
515                write!(self.prefix, " ").unwrap();
516            }
517        } else {
518            let new_len = if self.prefix.len() > marker_width {
519                self.prefix.len() - marker_width
520            } else {
521                0
522            };
523            self.prefix.truncate(new_len);
524            self.cr();
525        }
526    }
527
528    fn format_description_details(&mut self, entering: bool) {
529        if entering {
530            write!(self, ": ").unwrap()
531        }
532    }
533
534    fn format_heading(&mut self, nch: &NodeHeading, entering: bool) {
535        if entering {
536            for _ in 0..nch.level {
537                write!(self, "#").unwrap();
538            }
539            write!(self, " ").unwrap();
540            self.begin_content = true;
541            self.no_linebreaks = true;
542        } else {
543            self.no_linebreaks = false;
544            self.blankline();
545        }
546    }
547
548    fn format_code_block(&mut self, node: &'a AstNode<'a>, ncb: &NodeCodeBlock, entering: bool) {
549        if entering {
550            let first_in_list_item = node.previous_sibling().is_none()
551                && match node.parent() {
552                    Some(parent) => {
553                        matches!(
554                            parent.data.borrow().value,
555                            NodeValue::Item(..) | NodeValue::TaskItem(..)
556                        )
557                    }
558                    _ => false,
559                };
560
561            if !first_in_list_item {
562                self.blankline();
563            }
564
565            let info = ncb.info.as_bytes();
566            let literal = ncb.literal.as_bytes();
567
568            #[allow(clippy::len_zero)]
569            if !(info.len() > 0
570                || literal.len() <= 2
571                || isspace(literal[0])
572                || first_in_list_item
573                || self.options.render.prefer_fenced
574                || isspace(literal[literal.len() - 1]) && isspace(literal[literal.len() - 2]))
575            {
576                write!(self, "    ").unwrap();
577                write!(self.prefix, "    ").unwrap();
578                self.write_all(literal).unwrap();
579                let new_len = self.prefix.len() - 4;
580                self.prefix.truncate(new_len);
581            } else {
582                let fence_char = if info.contains(&b'`') { b'~' } else { b'`' };
583                let numticks = max(3, longest_char_sequence(literal, fence_char) + 1);
584                for _ in 0..numticks {
585                    write!(self, "{}", fence_char as char).unwrap();
586                }
587                if !info.is_empty() {
588                    write!(self, " ").unwrap();
589                    self.write_all(info).unwrap();
590                }
591                self.cr();
592                self.write_all(literal).unwrap();
593                self.cr();
594                for _ in 0..numticks {
595                    write!(self, "{}", fence_char as char).unwrap();
596                }
597            }
598            self.blankline();
599        }
600    }
601
602    fn format_html_block(&mut self, nhb: &NodeHtmlBlock, entering: bool) {
603        if entering {
604            self.blankline();
605            self.write_all(nhb.literal.as_bytes()).unwrap();
606            self.blankline();
607        }
608    }
609
610    fn format_thematic_break(&mut self, entering: bool) {
611        if entering {
612            self.blankline();
613            write!(self, "-----").unwrap();
614            self.blankline();
615        }
616    }
617
618    fn format_paragraph(&mut self, entering: bool) {
619        if !entering {
620            self.blankline();
621        }
622    }
623
624    fn format_text(&mut self, literal: &[u8], allow_wrap: bool, entering: bool) {
625        if entering {
626            self.output(literal, allow_wrap, Escaping::Normal);
627        }
628    }
629
630    fn format_line_break(&mut self, entering: bool, next_is_block: bool) {
631        if entering {
632            if !self.options.render.hardbreaks && !next_is_block {
633                // If the next element is a block, a backslash means a
634                // literal backslash instead of a line break. In this case
635                // we can just skip the line break since it's meaningless
636                // before a block.
637                write!(self, "\\").unwrap();
638            }
639            self.cr();
640        }
641    }
642
643    fn format_soft_break(&mut self, allow_wrap: bool, entering: bool) {
644        if entering {
645            if !self.no_linebreaks
646                && self.options.render.width == 0
647                && !self.options.render.hardbreaks
648            {
649                self.cr();
650            } else if self.options.render.hardbreaks {
651                self.output(&[b'\n'], allow_wrap, Escaping::Literal);
652            } else {
653                self.output(&[b' '], allow_wrap, Escaping::Literal);
654            }
655        }
656    }
657
658    fn format_code(&mut self, literal: &[u8], allow_wrap: bool, entering: bool) {
659        if entering {
660            let numticks = shortest_unused_sequence(literal, b'`');
661            for _ in 0..numticks {
662                write!(self, "`").unwrap();
663            }
664
665            let all_space = literal
666                .iter()
667                .all(|&c| c == b' ' || c == b'\r' || c == b'\n');
668            let has_edge_space = literal[0] == b' ' || literal[literal.len() - 1] == b' ';
669            let has_edge_backtick = literal[0] == b'`' || literal[literal.len() - 1] == b'`';
670
671            let pad = literal.is_empty() || has_edge_backtick || (!all_space && has_edge_space);
672            if pad {
673                write!(self, " ").unwrap();
674            }
675            self.output(literal, allow_wrap, Escaping::Literal);
676            if pad {
677                write!(self, " ").unwrap();
678            }
679            for _ in 0..numticks {
680                write!(self, "`").unwrap();
681            }
682        }
683    }
684
685    fn format_html_inline(&mut self, literal: &[u8], entering: bool) {
686        if entering {
687            self.write_all(literal).unwrap();
688        }
689    }
690
691    fn format_raw(&mut self, literal: &[u8], entering: bool) {
692        if entering {
693            self.write_all(literal).unwrap();
694        }
695    }
696
697    fn format_strong(&mut self) {
698        write!(self, "**").unwrap();
699    }
700
701    fn format_emph(&mut self, node: &'a AstNode<'a>) {
702        let emph_delim = if match node.parent() {
703            Some(parent) => matches!(parent.data.borrow().value, NodeValue::Emph),
704            _ => false,
705        } && node.next_sibling().is_none()
706            && node.previous_sibling().is_none()
707        {
708            b'_'
709        } else {
710            b'*'
711        };
712
713        self.write_all(&[emph_delim]).unwrap();
714    }
715
716    fn format_task_item(&mut self, symbol: Option<char>, node: &'a AstNode<'a>, entering: bool) {
717        self.format_item(node, entering);
718        if entering {
719            write!(self, "[{}] ", symbol.unwrap_or(' ')).unwrap();
720        }
721    }
722
723    fn format_strikethrough(&mut self) {
724        write!(self, "~~").unwrap();
725    }
726
727    fn format_superscript(&mut self) {
728        write!(self, "^").unwrap();
729    }
730
731    fn format_underline(&mut self) {
732        write!(self, "__").unwrap();
733    }
734
735    fn format_subscript(&mut self) {
736        write!(self, "~").unwrap();
737    }
738
739    fn format_spoiler(&mut self) {
740        write!(self, "||").unwrap();
741    }
742
743    fn format_escaped_tag(&mut self, net: &String) {
744        self.output(net.as_bytes(), false, Escaping::Literal);
745    }
746
747    fn format_link(&mut self, node: &'a AstNode<'a>, nl: &NodeLink, entering: bool) -> bool {
748        if is_autolink(node, nl) {
749            if entering {
750                write!(self, "<{}>", trim_start_match(&nl.url, "mailto:")).unwrap();
751                return false;
752            }
753        } else if entering {
754            write!(self, "[").unwrap();
755        } else {
756            write!(self, "](").unwrap();
757            self.output(nl.url.as_bytes(), false, Escaping::Url);
758            if !nl.title.is_empty() {
759                write!(self, " \"").unwrap();
760                self.output(nl.title.as_bytes(), false, Escaping::Title);
761                write!(self, "\"").unwrap();
762            }
763            write!(self, ")").unwrap();
764        }
765
766        true
767    }
768
769    fn format_wikilink(&mut self, nl: &NodeWikiLink, entering: bool) -> bool {
770        if entering {
771            write!(self, "[[").unwrap();
772            if self.options.extension.wikilinks() == Some(WikiLinksMode::UrlFirst) {
773                self.output(nl.url.as_bytes(), false, Escaping::Url);
774                write!(self, "|").unwrap();
775            }
776        } else {
777            if self.options.extension.wikilinks() == Some(WikiLinksMode::TitleFirst) {
778                write!(self, "|").unwrap();
779                self.output(nl.url.as_bytes(), false, Escaping::Url);
780            }
781            write!(self, "]]").unwrap();
782        }
783
784        true
785    }
786
787    fn format_image(&mut self, nl: &NodeLink, allow_wrap: bool, entering: bool) {
788        if entering {
789            write!(self, "![").unwrap();
790        } else {
791            write!(self, "](").unwrap();
792            self.output(nl.url.as_bytes(), false, Escaping::Url);
793            if !nl.title.is_empty() {
794                self.output(&[b' ', b'"'], allow_wrap, Escaping::Literal);
795                self.output(nl.title.as_bytes(), false, Escaping::Title);
796                write!(self, "\"").unwrap();
797            }
798            write!(self, ")").unwrap();
799        }
800    }
801
802    #[cfg(feature = "shortcodes")]
803    fn format_shortcode(&mut self, ne: &NodeShortCode, entering: bool) {
804        if entering {
805            write!(self, ":").unwrap();
806            self.output(ne.code.as_bytes(), false, Escaping::Literal);
807            write!(self, ":").unwrap();
808        }
809    }
810
811    fn format_table(&mut self, entering: bool) {
812        if entering {
813            self.custom_escape = Some(table_escape);
814        } else {
815            self.custom_escape = None;
816        }
817        self.blankline();
818    }
819
820    fn format_table_row(&mut self, entering: bool) {
821        if entering {
822            self.cr();
823            write!(self, "|").unwrap();
824        }
825    }
826
827    fn format_table_cell(&mut self, node: &'a AstNode<'a>, entering: bool) {
828        if entering {
829            write!(self, " ").unwrap();
830        } else {
831            write!(self, " |").unwrap();
832
833            let row = &node.parent().unwrap().data.borrow().value;
834            let in_header = match *row {
835                NodeValue::TableRow(header) => header,
836                _ => panic!(),
837            };
838
839            if in_header && node.next_sibling().is_none() {
840                let table = &node.parent().unwrap().parent().unwrap().data.borrow().value;
841                let alignments = match *table {
842                    NodeValue::Table(NodeTable { ref alignments, .. }) => alignments,
843                    _ => panic!(),
844                };
845
846                self.cr();
847                write!(self, "|").unwrap();
848                for a in alignments {
849                    write!(
850                        self,
851                        " {} |",
852                        match *a {
853                            TableAlignment::Left => ":--",
854                            TableAlignment::Center => ":-:",
855                            TableAlignment::Right => "--:",
856                            TableAlignment::None => "---",
857                        }
858                    )
859                    .unwrap();
860                }
861                self.cr();
862            }
863        }
864    }
865    fn format_footnote_definition(&mut self, name: &str, entering: bool) {
866        if entering {
867            self.footnote_ix += 1;
868            writeln!(self, "[^{}]:", name).unwrap();
869            write!(self.prefix, "    ").unwrap();
870        } else {
871            let new_len = self.prefix.len() - 4;
872            self.prefix.truncate(new_len);
873        }
874    }
875
876    fn format_footnote_reference(&mut self, r: &[u8], entering: bool) {
877        if entering {
878            self.write_all(b"[^").unwrap();
879            self.write_all(r).unwrap();
880            self.write_all(b"]").unwrap();
881        }
882    }
883
884    fn format_math(&mut self, math: &NodeMath, allow_wrap: bool, entering: bool) {
885        if entering {
886            let literal = math.literal.as_bytes();
887            let start_fence = if math.dollar_math {
888                if math.display_math {
889                    "$$"
890                } else {
891                    "$"
892                }
893            } else {
894                "$`"
895            };
896
897            let end_fence = if start_fence == "$`" {
898                "`$"
899            } else {
900                start_fence
901            };
902
903            self.output(start_fence.as_bytes(), false, Escaping::Literal);
904            self.output(literal, allow_wrap, Escaping::Literal);
905            self.output(end_fence.as_bytes(), false, Escaping::Literal);
906        }
907    }
908
909    fn format_alert(&mut self, alert: &NodeAlert, entering: bool) {
910        if entering {
911            write!(
912                self,
913                "> [!{}]",
914                alert.alert_type.default_title().to_uppercase()
915            )
916            .unwrap();
917            if alert.title.is_some() {
918                let title = alert.title.as_ref().unwrap();
919                write!(self, " {}", title).unwrap();
920            }
921            writeln!(self).unwrap();
922            write!(self, "> ").unwrap();
923            self.begin_content = true;
924            write!(self.prefix, "> ").unwrap();
925        } else {
926            let new_len = self.prefix.len() - 2;
927            self.prefix.truncate(new_len);
928            self.blankline();
929        }
930    }
931}
932
933fn longest_char_sequence(literal: &[u8], ch: u8) -> usize {
934    let mut longest = 0;
935    let mut current = 0;
936    for c in literal {
937        if *c == ch {
938            current += 1;
939        } else {
940            if current > longest {
941                longest = current;
942            }
943            current = 0;
944        }
945    }
946    if current > longest {
947        longest = current;
948    }
949    longest
950}
951
952fn shortest_unused_sequence(literal: &[u8], f: u8) -> usize {
953    let mut used = 1;
954    let mut current = 0;
955    for c in literal {
956        if *c == f {
957            current += 1;
958        } else {
959            if current > 0 {
960                used |= 1 << current;
961            }
962            current = 0;
963        }
964    }
965
966    if current > 0 {
967        used |= 1 << current;
968    }
969
970    let mut i = 0;
971    while used & 1 != 0 {
972        used >>= 1;
973        i += 1;
974    }
975    i
976}
977
978fn is_autolink<'a>(node: &'a AstNode<'a>, nl: &NodeLink) -> bool {
979    if nl.url.is_empty() || scanners::scheme(nl.url.as_bytes()).is_none() {
980        return false;
981    }
982
983    if !nl.title.is_empty() {
984        return false;
985    }
986
987    let link_text = match node.first_child() {
988        None => return false,
989        Some(child) => match child.data.borrow().value {
990            NodeValue::Text(ref t) => t.clone(),
991            _ => return false,
992        },
993    };
994
995    trim_start_match(&nl.url, "mailto:") == link_text
996}
997
998fn table_escape<'a>(node: &'a AstNode<'a>, c: u8) -> bool {
999    match node.data.borrow().value {
1000        NodeValue::Table(..) | NodeValue::TableRow(..) | NodeValue::TableCell => false,
1001        _ => c == b'|',
1002    }
1003}