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(`&#36;(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 }