1 module served.ddoc; 2 3 import served.protocol; 4 5 import std.string; 6 import ddoc; 7 8 string ddocToMarkdown(string ddoc) 9 { 10 auto lexer = Lexer(prepareDDoc(ddoc), true); 11 return expand(lexer, markdownMacros).replace("$", "$"); 12 } 13 14 MarkedString[] ddocToMarked(string ddoc) 15 { 16 MarkedString[] ret; 17 if (!ddoc.length) 18 return ret; 19 20 auto md = ddoc.ddocToMarkdown; 21 22 ret ~= MarkedString(""); 23 24 foreach (line; md.lineSplitter!(KeepTerminator.yes)) 25 { 26 if (line.strip == "```d") 27 ret ~= MarkedString("", "d"); 28 else if (line.strip == "```") 29 ret ~= MarkedString(""); 30 else 31 ret[$ - 1].value ~= line; 32 } 33 34 return ret; 35 } 36 37 string prepareDDoc(string str) 38 { 39 import ddoc.lexer; 40 41 auto lex = Lexer(str, true); 42 string output; 43 bool wasHeader = false; 44 bool hadWhitespace = false; 45 bool insertNewlineIntoPos = false; 46 bool wroteSomething = false; 47 size_t newlinePos = 0; 48 int numNewlines = 0; 49 foreach (tok; lex) 50 { 51 if (tok.type == Type.embedded || tok.type == Type.inlined) 52 { 53 if (tok.type == Type.embedded) 54 { 55 if (numNewlines == 0) 56 output ~= "\n\n"; 57 else if (numNewlines == 1) 58 output ~= "\n"; 59 } 60 output ~= tok.type == Type.embedded ? "$(D_CODE " : "$(DDOC_BACKQUOTED "; 61 output ~= tok.text; 62 output ~= ")"; 63 } 64 else if (tok.type == Type.newline) 65 { 66 numNewlines++; 67 if (insertNewlineIntoPos) 68 { 69 output = output[0 .. newlinePos] ~ "\n" ~ output[newlinePos .. $]; 70 insertNewlineIntoPos = false; 71 } 72 if (wasHeader) 73 output ~= "\n"; 74 output ~= tok.text; 75 newlinePos = output.length; 76 hadWhitespace = false; 77 wroteSomething = false; 78 } 79 else if (tok.type == Type.whitespace) 80 { 81 insertNewlineIntoPos = false; 82 hadWhitespace = true; 83 if (wroteSomething) 84 output ~= tok.text; 85 } 86 else 87 { 88 numNewlines = 0; 89 insertNewlineIntoPos = false; 90 if (!hadWhitespace && tok.text.length && tok.text[$ - 1] == ':') 91 insertNewlineIntoPos = true; 92 output ~= tok.text; 93 wroteSomething = true; 94 } 95 wasHeader = tok.text.length && tok.text[$ - 1] == ':'; 96 } 97 return output; 98 } 99 100 string[string] markdownMacros; 101 102 shared static this() 103 { 104 //dfmt off 105 markdownMacros = [ 106 `B` : `**$0**`, 107 `I` : `*$0*`, 108 `U` : `<u>$0</u>`, 109 `P` : ` 110 111 $0 112 113 `, 114 `BR` : "\n\n", 115 `DL` : `$0`, 116 `DT` : `**$0**`, 117 `DD` : ` 118 119 * $0`, 120 `TABLE` : `$0`, 121 `TR` : `$0|`, 122 `TH` : `| **$0** `, 123 `TD` : `| $0 `, 124 `OL` : `$0`, 125 `UL` : `$0`, 126 `LI` : `* $0`, 127 `LINK` : `[$0]$(LPAREN)$0$(RPAREN)`, 128 `LINK2` : `[$+]$(LPAREN)$1$(RPAREN)`, 129 `LPAREN` : `(`, 130 `RPAREN` : `)`, 131 `DOLLAR` : `$`, 132 `BACKTICK` : "`", 133 `DEPRECATED` : `$0`, 134 `RED` : `<font color=red>**$0**</font>`, 135 `BLUE` : `<font color=blue>$0</font>`, 136 `GREEN` : `<font color=green>$0</font>`, 137 `YELLOW` : `<font color=yellow>$0</font>`, 138 `BLACK` : `<font color=black>$0</font>`, 139 `WHITE` : `<font color=white>$0</font>`, 140 `D_CODE` : "$(BACKTICK)$(BACKTICK)$(BACKTICK)d 141 $0 142 $(BACKTICK)$(BACKTICK)$(BACKTICK)", 143 `D_INLINECODE` : "$(BACKTICK)$0$(BACKTICK)", 144 `D_COMMENT` : "$(BACKTICK)$0$(BACKTICK)", 145 `D_STRING` : "$(BACKTICK)$0$(BACKTICK)", 146 `D_KEYWORD` : "$(BACKTICK)$0$(BACKTICK)", 147 `D_PSYMBOL` : "$(BACKTICK)$0$(BACKTICK)", 148 `D_PARAM` : "$(BACKTICK)$0$(BACKTICK)", 149 `DDOC` : `# $(TITLE) 150 151 $(BODY)`, 152 `DDOC_BACKQUOTED` : `$(D_INLINECODE $0)`, 153 `DDOC_COMMENT` : ``, 154 `DDOC_DECL` : `$(DT $(BIG $0))`, 155 `DDOC_DECL_DD` : `$(DD $0)`, 156 `DDOC_DITTO` : `$(BR)$0`, 157 `DDOC_SECTIONS` : `$0`, 158 `DDOC_SUMMARY` : `$0$(BR)$(BR)`, 159 `DDOC_DESCRIPTION` : `$0$(BR)$(BR)`, 160 `DDOC_AUTHORS` : "$(B Authors:)$(BR)\n$0$(BR)$(BR)", 161 `DDOC_BUGS` : "$(RED BUGS:)$(BR)\n$0$(BR)$(BR)", 162 `DDOC_COPYRIGHT` : "$(B Copyright:)$(BR)\n$0$(BR)$(BR)", 163 `DDOC_DATE` : "$(B Date:)$(BR)\n$0$(BR)$(BR)", 164 `DDOC_DEPRECATED` : "$(RED Deprecated:)$(BR)\n$0$(BR)$(BR)", 165 `DDOC_EXAMPLES` : "$(B Examples:)$(BR)\n$0$(BR)$(BR)", 166 `DDOC_HISTORY` : "$(B History:)$(BR)\n$0$(BR)$(BR)", 167 `DDOC_LICENSE` : "$(B License:)$(BR)\n$0$(BR)$(BR)", 168 `DDOC_RETURNS` : "$(B Returns:)$(BR)\n$0$(BR)$(BR)", 169 `DDOC_SEE_ALSO` : "$(B See Also:)$(BR)\n$0$(BR)$(BR)", 170 `DDOC_STANDARDS` : "$(B Standards:)$(BR)\n$0$(BR)$(BR)", 171 `DDOC_THROWS` : "$(B Throws:)$(BR)\n$0$(BR)$(BR)", 172 `DDOC_VERSION` : "$(B Version:)$(BR)\n$0$(BR)$(BR)", 173 `DDOC_SECTION_H` : `$(B $0)$(BR)$(BR)`, 174 `DDOC_SECTION` : `$0$(BR)$(BR)`, 175 `DDOC_MEMBERS` : `$(DL $0)`, 176 `DDOC_MODULE_MEMBERS` : `$(DDOC_MEMBERS $0)`, 177 `DDOC_CLASS_MEMBERS` : `$(DDOC_MEMBERS $0)`, 178 `DDOC_STRUCT_MEMBERS` : `$(DDOC_MEMBERS $0)`, 179 `DDOC_ENUM_MEMBERS` : `$(DDOC_MEMBERS $0)`, 180 `DDOC_TEMPLATE_MEMBERS` : `$(DDOC_MEMBERS $0)`, 181 `DDOC_ENUM_BASETYPE` : `$0`, 182 `DDOC_PARAMS` : "$(B Params:)$(BR)\n$(TABLE $0)$(BR)", 183 `DDOC_PARAM_ROW` : `$(TR $0)`, 184 `DDOC_PARAM_ID` : `$(TD $0)`, 185 `DDOC_PARAM_DESC` : `$(TD $0)`, 186 `DDOC_BLANKLINE` : `$(BR)$(BR)`, 187 188 `DDOC_ANCHOR` : `<a name="$1"></a>`, 189 `DDOC_PSYMBOL` : `$(U $0)`, 190 `DDOC_PSUPER_SYMBOL` : `$(U $0)`, 191 `DDOC_KEYWORD` : `$(B $0)`, 192 `DDOC_PARAM` : `$(I $0)`]; 193 //dfmt on 194 } 195 196 unittest 197 { 198 void assertEqual(A, B)(A a, B b) 199 { 200 import std.conv : to; 201 202 assert(a == b, a.to!string ~ " is not equal to " ~ b.to!string); 203 } 204 205 auto md = ddocToMarkdown(`$(D something, else) is *a 206 ------------ 207 test 208 /** this is some test code */ 209 assert (whatever); 210 --------- 211 Params: 212 a = $(B param) 213 Returns: 214 nothing of consequence`); 215 assertEqual(md, "$(D something, else) is *a 216 217 ```d 218 test 219 /** this is some test code */ 220 assert (whatever); 221 ``` 222 223 Params: 224 225 a = **param** 226 227 Returns: 228 229 nothing of consequence"); 230 }