Skip to content

Commit e94daf5

Browse files
committed
Add Yaml Header Parse
Add yaml header parse, use it to write blog, like hexo 😄
1 parent d6493c7 commit e94daf5

File tree

4 files changed

+177
-2
lines changed

4 files changed

+177
-2
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using Microsoft.Toolkit.Parsers.Markdown.Helpers;
5+
6+
namespace Microsoft.Toolkit.Parsers.Markdown.Blocks
7+
{
8+
/// <summary>
9+
/// Yaml Header. use for blog.
10+
/// e.g.
11+
/// ---
12+
/// title: something
13+
/// tag: something
14+
/// ---
15+
/// </summary>
16+
public class YamlHeaderBlock : MarkdownBlock
17+
{
18+
/// <summary>
19+
/// Initializes a new instance of the <see cref="YamlHeaderBlock"/> class.
20+
/// </summary>
21+
public YamlHeaderBlock()
22+
: base(MarkdownBlockType.YamlHeader)
23+
{
24+
}
25+
26+
/// <summary>
27+
/// Gets or sets yaml header properties
28+
/// </summary>
29+
public Dictionary<string, string> Children { get; set; }
30+
31+
/// <summary>
32+
/// Parse yaml header
33+
/// </summary>
34+
/// <param name="markdown"> The markdown text. </param>
35+
/// <param name="start"> The location of the first hash character. </param>
36+
/// <param name="end"> The location of the end of the line. </param>
37+
/// <param name="realEndIndex"> The location of the actual end of the aprse. </param>
38+
/// <returns>Parsed <see cref="YamlHeaderBlock"/> class</returns>
39+
internal static YamlHeaderBlock Parse(string markdown, int start, int end, out int realEndIndex)
40+
{
41+
// As yaml header, must be start a line with "---"
42+
// and end with a line "---"
43+
realEndIndex = start;
44+
int lineStart = start;
45+
if (lineStart != 0 || markdown.Substring(start, 3) != "---")
46+
{
47+
return null;
48+
}
49+
50+
int startUnderlineIndex = Common.FindNextSingleNewLine(markdown, lineStart, end, out int startOfNextLine);
51+
if (startUnderlineIndex - lineStart != 3)
52+
{
53+
return null;
54+
}
55+
56+
bool lockedFinalUnderline = false;
57+
58+
// if current line not contain the ": ", check it is end of parse, if not, exit
59+
// if next line is the end, exit
60+
int pos = startOfNextLine;
61+
List<string> elements = new List<string>();
62+
while (pos < end)
63+
{
64+
int nextUnderLineIndex = Common.FindNextSingleNewLine(markdown, pos, end, out startOfNextLine);
65+
bool haveSeparator = markdown.Substring(pos, nextUnderLineIndex - pos).Contains(": ");
66+
if (haveSeparator)
67+
{
68+
elements.Add(markdown.Substring(pos, nextUnderLineIndex - pos));
69+
}
70+
else if (markdown.Substring(pos, 3) == "---")
71+
{
72+
lockedFinalUnderline = true;
73+
realEndIndex = pos + 3;
74+
break;
75+
}
76+
else if (startOfNextLine == pos + 1)
77+
{
78+
pos = startOfNextLine;
79+
continue;
80+
}
81+
else
82+
{
83+
return null;
84+
}
85+
86+
pos = startOfNextLine;
87+
}
88+
89+
// if not have the end, return
90+
if (!lockedFinalUnderline)
91+
{
92+
return null;
93+
}
94+
95+
// parse yaml header properties
96+
if (elements.Count < 1)
97+
{
98+
return null;
99+
}
100+
101+
var result = new YamlHeaderBlock();
102+
foreach (var item in elements)
103+
{
104+
string[] splits = item.Split(new string[] { ": " }, StringSplitOptions.None);
105+
if (splits.Length < 2)
106+
{
107+
continue;
108+
}
109+
else
110+
{
111+
if (result.Children == null)
112+
{
113+
result.Children = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
114+
}
115+
116+
if (!result.Children.ContainsKey(splits[0].Trim()))
117+
{
118+
result.Children.Add(splits[0].Trim(), splits[1].Trim());
119+
}
120+
else
121+
{
122+
result.Children[splits[0].Trim()] = splits[1].Trim();
123+
}
124+
}
125+
}
126+
127+
if (result.Children.Count == 0)
128+
{
129+
return null;
130+
}
131+
132+
return result;
133+
}
134+
135+
/// <summary>
136+
/// Converts the object into it's textual representation.
137+
/// </summary>
138+
/// <returns> The textual representation of this object. </returns>
139+
public override string ToString()
140+
{
141+
if (Children == null)
142+
{
143+
return base.ToString();
144+
}
145+
else
146+
{
147+
return string.Join(string.Empty, Children);
148+
}
149+
}
150+
}
151+
}

Microsoft.Toolkit.Parsers/Markdown/Enums/MarkdownBlockType.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ public enum MarkdownBlockType
5757
/// <summary>
5858
/// A link block
5959
/// </summary>
60-
LinkReference
60+
LinkReference,
61+
62+
/// <summary>
63+
/// A Yaml header block
64+
/// </summary>
65+
YamlHeader
6166
}
6267
}

Microsoft.Toolkit.Parsers/Markdown/MarkdownDocument.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,21 @@ internal static List<MarkdownBlock> Parse(string markdown, int start, int end, i
224224
// Or a quote if the line starts with a greater than character (optionally preceded by whitespace).
225225
// Or a horizontal rule if the line contains nothing but 3 '*', '-' or '_' characters (with optional whitespace).
226226
MarkdownBlock newBlockElement = null;
227-
if (nonSpaceChar == '#' && nonSpacePos == startOfLine)
227+
if (nonSpaceChar == '-' && nonSpacePos == startOfLine)
228+
{
229+
// Yaml Header
230+
newBlockElement = YamlHeaderBlock.Parse(markdown, startOfLine, markdown.Length, out startOfLine);
231+
if (newBlockElement != null)
232+
{
233+
realStartOfLine = startOfLine;
234+
endOfLine = startOfLine + 3;
235+
startOfNextLine = Common.FindNextSingleNewLine(markdown, startOfLine, end, out startOfNextLine);
236+
}
237+
238+
paragraphText.Clear();
239+
}
240+
241+
if (newBlockElement == null && nonSpaceChar == '#' && nonSpacePos == startOfLine)
228242
{
229243
// Hash-prefixed header.
230244
newBlockElement = HeaderBlock.ParseHashPrefixedHeader(markdown, startOfLine, endOfLine);

Microsoft.Toolkit.Parsers/Markdown/Render/MarkdownRendererBase.Blocks.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ public partial class MarkdownRendererBase
1616
/// </summary>
1717
protected abstract void RenderParagraph(ParagraphBlock element, IRenderContext context);
1818

19+
/// <summary>
20+
/// Renders a yaml header element.
21+
/// </summary>
22+
protected abstract void RenderYamlHeader(YamlHeaderBlock element, IRenderContext context);
23+
1924
/// <summary>
2025
/// Renders a header element.
2126
/// </summary>

0 commit comments

Comments
 (0)