aboutsummaryrefslogtreecommitdiff
path: root/parse_wiki_text/src/heading.rs
blob: 9c4d647092f3892eda5241b61f936029f05b68da (plain) (blame)
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
// Copyright 2019 Fredrik Portström <https://portstrom.com>
// This is free software distributed under the terms specified in
// the file LICENSE at the top-level directory of this distribution.

pub fn parse_heading_end(state: &mut crate::State) {
    let mut end_position = state.scan_position;
    loop {
        match state.get_byte(end_position - 1) {
            Some(b'\t') | Some(b' ') => end_position -= 1,
            _ => break,
        }
    }
    let open_node = state.stack.pop().unwrap();
    if state.get_byte(end_position - 1) != Some(b'=') || end_position < open_node.start + 3 {
        state.warnings.push(crate::Warning {
            end: end_position,
            message: crate::WarningMessage::InvalidHeadingSyntaxRewinding,
            start: open_node.start,
        });
        state.rewind(open_node.nodes, open_node.start);
        return;
    }
    let start_level = match open_node.type_ {
        crate::OpenNodeType::Heading { level } => level,
        _ => unreachable!(),
    };
    let mut end_level: u8 = 1;
    while end_level < start_level
        && end_position - end_level as usize > open_node.start + end_level as usize + 2
        && state.get_byte(end_position - end_level as usize - 1) == Some(b'=')
    {
        end_level += 1;
    }
    let position = state.skip_whitespace_backwards(end_position - end_level as usize);
    if end_level < start_level {
        state.warnings.push(crate::Warning {
            end: end_position,
            message: crate::WarningMessage::UnexpectedHeadingLevelCorrecting,
            start: open_node.start,
        });
        let inner_start_position = open_node.start + end_level as usize;
        if match state.nodes.get_mut(0) {
            None => {
                state.flushed_position = inner_start_position;
                false
            }
            Some(crate::Node::Text { end, start, value }) => {
                *start = inner_start_position;
                *value = &state.wiki_text[inner_start_position..*end];
                false
            }
            Some(_) => true,
        } {
            let end = state.skip_whitespace_forwards(open_node.start + start_level as usize);
            state.nodes.insert(
                0,
                crate::Node::Text {
                    end,
                    start: inner_start_position,
                    value: &state.wiki_text[inner_start_position..end],
                },
            );
        }
    }
    state.flush(position);
    let nodes = std::mem::replace(&mut state.nodes, open_node.nodes);
    state.nodes.push(crate::Node::Heading {
        end: end_position,
        level: end_level,
        nodes,
        start: open_node.start,
    });
    state.scan_position += 1;
    state.skip_empty_lines();
}

pub fn parse_heading_start(state: &mut crate::State) {
    let mut level = 1;
    while state.get_byte(state.scan_position + level) == Some(b'=') && level < 6 {
        level += 1;
    }
    let position = state.skip_whitespace_forwards(state.scan_position + level);
    state.flushed_position = position;
    state.push_open_node(
        crate::OpenNodeType::Heading { level: level as u8 },
        position,
    );
}