1 /++
2  * md2d
3  * modified from https://github.com/dlang-community/dmarkdown/blob/master/source/dmarkdown/markdown.d
4  +/
5 module md2d;
6 
7 import std.string;
8 import std.algorithm : canFind;
9 import md2d.parser : Block, MarkdownSettings;
10 
11 unittest {
12 	mixin(md2d(`
13 ### Status 枚举
14 状态
15 | 序号 | 字段名 | 名称 | 说明 |
16 | --- | --- | --- | --- |
17 | 2 | active | 正常 |  |`));
18 	static assert(Status.active == 2);
19 }
20 
21 private void addField(S)(ref S r, S name, S type = null,
22 	S val = null, S comment = null) {
23 	r ~= '\t';
24 	if (comment.length) {
25 		bool multiline = comment.canFind('\n');
26 		r ~= multiline ? "/++\n\t\t" : "/// ";
27 		r ~= comment;
28 		r ~= multiline ? "\n\t  +/\n\t" : "\n\t";
29 	}
30 	if (type.length) {
31 		r ~= type;
32 		r ~= ' ';
33 	}
34 	r ~= name.replace(' ', '_');
35 	if (val.length) {
36 		r ~= " = ";
37 		r ~= val;
38 	}
39 	r ~= type ? ";\n" : ",\n";
40 }
41 
42 void parseMD(string src, ref Block block, MarkdownSettings settings = MarkdownSettings()) {
43 	import md2d.parser;
44 
45 	auto allLines = src.splitLines;
46 	auto lines = parseLines(allLines, settings);
47 	parseBlocks(block, lines, null, settings);
48 }
49 
50 string md2d(string src, MarkdownSettings settings = MarkdownSettings()) {
51 	import md2d.parser : BT = BlockType;
52 
53 	Block root;
54 	parseMD(src, root, settings);
55 	string str, header;
56 	foreach (b; root.blocks) {
57 		if (b.type == BT.Header && b.headerLevel > 1) {
58 			string cls, t = b.text[0].strip;
59 			if (t[0] == '`')
60 				t = t.split('`')[1];
61 			else if (t.endsWith("struct")) {
62 				cls = "struct";
63 				t = t[0 .. $ - "struct".length];
64 			} else if (t.endsWith("结构体")) {
65 				cls = "struct";
66 				t = t[0 .. $ - "结构体".length];
67 			} else if (t.endsWith("union")) {
68 				cls = "union";
69 				t = t[0 .. $ - "union".length];
70 			} else if (t.endsWith("联合体")) {
71 				cls = "union";
72 				t = t[0 .. $ - "联合体".length];
73 			} else if (t.endsWith("class")) {
74 				cls = "class";
75 				t = t[0 .. $ - "class".length];
76 			} else if (t.endsWith("类")) {
77 				cls = "class";
78 				t = t[0 .. $ - "类".length];
79 			} else if (t.endsWith("enum")) {
80 				cls = "enum";
81 				t = t[0 .. $ - "enum".length];
82 			} else if (t.endsWith("表")) {
83 				cls = "enum";
84 				t = t[0 .. $ - "表".length];
85 			} else if (t.endsWith("枚举")) {
86 				cls = "enum";
87 				t = t[0 .. $ - "枚举".length];
88 			} else
89 				continue;
90 			t = t.stripRight;
91 			if (!t.canFind(' ')) {
92 				if (header)
93 					str ~= "}\n\n";
94 				if (cls) {
95 					char c = t[0];
96 					if (c >= 'A' && c <= 'Z')
97 						header = "/// " ~ t ~ "\n" ~ cls ~ " " ~ t ~ "\n{\n";
98 					else if (cls.length == 4)
99 						header = "/// " ~ t ~ "\n" ~ cls ~ "\n{\n";
100 				} else
101 					header = t ~ " {\n";
102 			}
103 		} else if (header) {
104 			if (b.type == BT.Table) {
105 				str ~= header;
106 				int nRow = -1,
107 				tRow = -1,
108 				vRow = -1,
109 				cRow1 = -1,
110 				cRow2 = -1;
111 				foreach (r; b.blocks)
112 					if (r.type == BT.TableRow) {
113 						string name, type, val, comment;
114 						for (int i; i < r.blocks.length; i++) {
115 							auto c = r.blocks[i];
116 							if (c.type == BT.TableHeader) {
117 								string t = c.text[0];
118 								if (t.startsWith("Field") || t.startsWith("字段"))
119 									nRow = i;
120 								else if (t == "Type" || t.endsWith("类型"))
121 									tRow = i;
122 								else if (t == "Value" || t.endsWith("值") || t == "Index" || t == "序号")
123 									vRow = i;
124 								else if (t == "Name" || t.endsWith("名称") || t.startsWith(
125 										"中文"))
126 									cRow1 = i;
127 								else if (t == "Description" || t.endsWith("说明"))
128 									cRow2 = i;
129 							} else if (c.type == BT.TableData) {
130 								if (i == nRow)
131 									name = c.text[0];
132 								else if (i == tRow)
133 									type = c.text[0];
134 								else if (i == vRow)
135 									val = c.text[0];
136 								else {
137 									if (!settings.noComment)
138 										if (i == cRow1)
139 											comment = c.text[0].strip;
140 								}
141 							}
142 						}
143 						if (name)
144 							str.addField(name, type, val, comment);
145 					}
146 			} else if (b.type == BT.Plain) {
147 				str ~= "}\n\n";
148 				header = null;
149 			}
150 		}
151 		if (b.type == BT.Code) {
152 			if (header) {
153 				str ~= header ~ '\t';
154 				header = " ";
155 			}
156 			str ~= b.text.join("\n\t") ~ '\n';
157 		}
158 	}
159 	if (header)
160 		str ~= '}';
161 	return str;
162 }