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
|
use std::mem::replace;
use crate::ast::{
Line,
parser::{ParserInternalStatus, ParserMatchResult},
};
#[derive(Default)]
struct HeadingTmp {
/// 当前输入的层级
lvl: u8,
/// 是否正在输入标题前缀
typing_heading_prefix: bool,
/// 是否正在输入标题内容
typing_heading_content: bool,
// 第一个 Sharp 符号的位置
first_sharp_col: Option<u16>,
}
pub(crate) fn proc(c: &char, inr: &mut ParserInternalStatus) -> ParserMatchResult {
// 如果本行在剔除开头所有字符后仍不为 # 开头,则说明本行无法用于标题解析
let lookback = inr.lookback.clone();
if !lookback.trim_start().starts_with('#') {
// 本行不是标题行,放弃整行的解析
return ParserMatchResult::Abort;
}
// 当前行
let row = inr.row;
let col = inr.col;
// 获得临时数据
let tmp = inr.get_tmp_or_init::<HeadingTmp>("headings_tmp");
// 如果是行首,则初始化 tmp 数据
if col == 0 {
tmp.lvl = 0;
tmp.typing_heading_prefix = false;
tmp.typing_heading_content = false;
tmp.first_sharp_col = None;
}
match c {
// 键入了 # 符号
'#' => {
// 如果正在输入标题前缀,则层级增加
if tmp.typing_heading_prefix {
tmp.lvl += 1;
// 如果层级大于 6 (6级标题)
if tmp.lvl > 6 {
// 语法异常,抛出
return ParserMatchResult::SyntaxError {
begin_col: tmp.first_sharp_col.unwrap_or(col),
begin_row: row,
end_col: col,
end_row: row,
msg: "Heading level cannot exceed 6".to_string(),
};
}
}
// 如果不在输入标题
else {
// 记录为正在输入前缀
tmp.typing_heading_prefix = true;
// 设置层级为 1
tmp.lvl = 1;
// 标记第一个 # 符号的位置
tmp.first_sharp_col = Some(col);
}
}
// 输入了空格
' ' => {
// 如果正在输入标题前缀
if tmp.typing_heading_prefix {
// 标记为没输入前缀,并切换为正在输入内容
tmp.typing_heading_prefix = false;
tmp.typing_heading_content = true;
}
}
// 输入了换行
'\n' => {
// 如果正在输入标题内容
if tmp.typing_heading_content {
// 拿出所有 records_tokens
let tokens = replace(&mut inr.records_tokens, Vec::new());
// 建立标题行
let line = Line { row, tokens };
// 追加行
inr.records_lines.push(line);
}
}
_ => return ParserMatchResult::Sad,
}
ParserMatchResult::Done
}
|