1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*!
Contains the transition function for parsing attributions inside block quotes.

Copyright © 2020 Santtu Söderholm
*/

use super::*;
use crate::parser::types_and_aliases::IndentedBlockResult;

/// A function that generates attribution nodes inside a block quote.
/// An attribution ends block quotes, so encountering one makes the parser focus on the parent of the current node.
pub fn attribution(
    src_lines: &Vec<String>,
    base_indent: usize,
    section_level: &mut usize,
    line_cursor: &mut LineCursor,
    mut doctree: DocTree,
    captures: &regex::Captures,
    pattern_name: &Pattern,
) -> TransitionResult {

    let match_len = captures.get(0).unwrap().as_str().chars().count() + base_indent;
    let attribution_line_indent = captures.get(1).unwrap().as_str().chars().count() + base_indent;

    match Parser::parent_indent_matches(doctree.shared_data(), attribution_line_indent) {
        IndentationMatch::JustRight => {
            // Attempt to create attribution node inside current block quote and focus on the parent node

            let current_line = if let Some(line) = src_lines.get(line_cursor.relative_offset()) {
                line
            } else {
                panic!("Found an attribution marker on line {} but the line doesn't exist? Computer says no...", line_cursor.sum_total())
            };

            let line_after_marker = Parser::line_suffix(current_line, match_len - base_indent);

            let empty_after_marker = line_after_marker.as_str().trim().is_empty();

            let first_indent = if empty_after_marker {
                None
            } else {
                Some(match_len)
            };

            let next_indent = if let Some((indent, offset)) =
                Parser::indent_on_subsequent_lines(src_lines, line_cursor.relative_offset())
            {
                if offset == 0 && indent >= attribution_line_indent {
                    Some(indent)
                } else {
                    Some(match_len)
                }
            } else {
                Some(match_len)
            };

            let (attribution_string, offset) = if let IndentedBlockResult::Ok {lines, minimum_indent, offset, blank_finish } =
                Parser::read_indented_block(
                    src_lines,
                    line_cursor.relative_offset(),
                    true,
                    true,
                    next_indent,
                    first_indent,
                    true,
                ) {
                    (lines.join(" ").trim().to_string(), offset)
            } else {
                panic!(
                    "Could not read comment block on line {}...",
                    line_cursor.sum_total()
                )
            };

            doctree = match doctree.push_data(
        TreeNodeType::Attribution {
                    raw_text: attribution_string,
                }
            ) {
                Ok(tree) => tree,
                Err(tree) => {
                    return TransitionResult::Failure {
                        message: format!(
                            "Node insertion error on line {}. Computer says no...",
                            line_cursor.sum_total()
                        ),
                        doctree: tree,
                    }
                }
            };
            doctree = doctree.focus_on_parent();

            TransitionResult::Success {
                doctree: doctree,
                push_or_pop: PushOrPop::Pop,
                line_advance: LineAdvance::Some(offset),
            }
        }
        IndentationMatch::TooMuch => {
            // Create another block quote
            doctree = match doctree.push_data_and_focus(
                TreeNodeType::BlockQuote {
                    body_indent: attribution_line_indent,
                }
            ) {
                Ok(tree) => tree,
                Err(tree) => {
                    return TransitionResult::Failure {
                        message: format!(
                            "Node insertion error on line {}. Computer says no...",
                            line_cursor.sum_total()
                        ),
                        doctree: tree,
                    }
                }
            };

            return TransitionResult::Success {
                doctree: doctree,
                push_or_pop: PushOrPop::Push(vec![State::BlockQuote]),
                line_advance: LineAdvance::None,
            };
        }
        _ => {
            doctree = doctree.focus_on_parent();
            TransitionResult::Success {
                doctree: doctree,
                push_or_pop: PushOrPop::Pop,
                line_advance: LineAdvance::None,
            }
        }
    }
}