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 }