1 /**
2  * Copyright: © 2014 Economic Modeling Specialists, Intl.
3  * Authors: Brian Schott
4  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt Boost License 1.0)
5  */
6 module ddoc.comments;
7 import ddoc.sections;
8 import ddoc.lexer;
9 
10 public import ddoc.types;
11 
12 Comment parseComment(string text, string[string] macros, bool removeUnknown = true)
13 out(retVal)
14 {
15 	assert(retVal.sections.length >= 2);
16 }
17 do
18 {
19 	import ddoc.highlight : highlight;
20 
21 	return Comment.parse(text, macros, removeUnknown, &highlight);
22 }
23 
24 unittest
25 {
26 	// Issue #21
27 	Comment test = parseComment("\nParams:\n    dg = \n", null);
28 	assert(test.sections.length == 3);
29 	assert(test.sections[2].name == "Params");
30 }
31 
32 unittest
33 {
34 	import std.conv : text;
35 
36 	auto macros = ["A" : "<a href=\"$0\">"];
37 	auto comment = `Best-comment-ever © 2014
38 
39 I thought the same. I was considering writing it, actually.
40 Imagine how having the $(A tool) would have influenced the "final by
41 default" discussion. Amongst others, of course.
42 
43 It essentially comes down to persistent compiler-as-a-library
44 issue. Tools like dscanner can help with some of more simple
45 transition cases but anything more complicated is likely to
46 require full semantic analysis.
47 Params:
48 	a = $(A param)
49 Returns:
50 	nothing of consequence
51 `;
52 
53 	Comment c = parseComment(comment, macros);
54 	import std.string : format;
55 
56 	assert(c.sections.length == 4, format("%d", c.sections.length));
57 	assert(c.sections[0].name is null);
58 	assert(c.sections[0].content == "Best-comment-ever © 2014", c.sections[0].content);
59 	assert(c.sections[1].name is null);
60 	assert(c.sections[2].name == "Params");
61 	assert(c.sections[2].mapping[0][0] == "a");
62 	assert(c.sections[2].mapping[0][1] == `<a href="param">`, c.sections[2].mapping[0][1]);
63 	assert(c.sections[3].name == "Returns");
64 }
65 
66 unittest
67 {
68 	auto macros = ["A" : "<a href=\"$0\">"];
69 	auto comment = `Best $(Unknown comment) ever`;
70 
71 	Comment c = parseComment(comment, macros, true);
72 
73 	assert(c.sections.length >= 1);
74 	assert(c.sections[0].name is null);
75 	assert(c.sections[0].content == "Best  ever", c.sections[0].content);
76 }
77 
78 unittest
79 {
80 	auto macros = ["A" : "<a href=\"$0\">"];
81 	auto comment = `Best $(Unknown comment) ever`;
82 
83 	Comment c = parseComment(comment, macros, false);
84 
85 	assert(c.sections.length >= 1);
86 	assert(c.sections[0].name is null);
87 	assert(c.sections[0].content == "Best $(Unknown comment) ever", c.sections[0].content);
88 }
89 
90 unittest
91 {
92 	auto comment = `---
93 auto subcube(T...)(T values);
94 ---
95 Creates a new cube in a similar way to whereCube, but allows the user to
96 define a new root for specific dimensions.`c;
97 	string[string] macros;
98 	const Comment c = parseComment(comment, macros);
99 }
100 
101 ///
102 unittest
103 {
104 	import std.conv : text;
105 
106 	auto s1 = `Stop the world
107 
108 This function tells the Master to stop the world, taking effect immediately.
109 
110 Params:
111 reason = Explanation to give to the $(B Master)
112 duration = Time for which the world $(UNUSED)would be stopped (as time itself stop, this is always $(F double.infinity))
113 
114 ---
115 void main() {
116   import std.datetime : msecs;
117   import master.universe.control;
118   stopTheWorld("Too fast", 42.msecs);
119   assert(0); // Will never be reached.
120 }
121 ---
122 
123 Returns:
124 Nothing, because nobody can restart it.
125 
126 Macros:
127 F= $0`;
128 
129 	immutable expected = `<pre class="d_code"><font color=blue>void</font> main() {
130   <font color=blue>import</font> std.datetime : msecs;
131   <font color=blue>import</font> master.universe.control;
132   stopTheWorld(<font color=red>"Too fast"</font>, 42.msecs);
133   <font color=blue>assert</font>(0); <font color=green>// Will never be reached.</font>
134 }</pre>`;
135 
136 	auto c = parseComment(s1, null);
137 
138 	assert(c.sections.length == 6, text(c.sections.length));
139 	assert(c.sections[0].name is null, c.sections[0].name);
140 	assert(c.sections[0].content == "Stop the world", c.sections[0].content);
141 
142 	assert(c.sections[1].name is null, c.sections[1].name);
143 	assert(
144 		c.sections[1].content == `This function tells the Master to stop the world, taking effect immediately.`,
145 		c.sections[1].content);
146 
147 	assert(c.sections[2].name == "Params", c.sections[2].name);
148 	//	writeln(c.sections[2].mapping);
149 	assert(c.sections[2].mapping[0][0] == "reason", c.sections[2].mapping[0][0]);
150 	assert(c.sections[2].mapping[0][1] == "Explanation to give to the <b>Master</b>",
151 		c.sections[2].mapping[0][1]);
152 	assert(c.sections[2].mapping[1][0] == "duration", c.sections[2].mapping[0][1]);
153 	assert(
154 		c.sections[2].mapping[1][1] == "Time for which the world would be stopped (as time itself stop, this is always double.infinity)",
155 		c.sections[2].mapping[1][1]);
156 
157 	assert(c.sections[3].name == "Examples", c.sections[3].name);
158 	assert(c.sections[3].content == expected, c.sections[3].content);
159 
160 	assert(c.sections[4].name == "Returns", c.sections[4].name);
161 	assert(c.sections[4].content == `Nothing, because nobody can restart it.`,
162 		c.sections[4].content);
163 
164 	assert(c.sections[5].name == "Macros", c.sections[5].name);
165 	assert(c.sections[5].mapping[0][0] == "F", c.sections[5].mapping[0][0]);
166 	assert(c.sections[5].mapping[0][1] == "$0", c.sections[5].mapping[0][1]);
167 }
168 
169 unittest
170 {
171 	import std.stdio : writeln, writefln;
172 
173 	auto comment = `Unrolled Linked List.
174 
175 Nodes are (by default) sized to fit within a 64-byte cache line. The number
176 of items stored per node can be read from the $(B nodeCapacity) field.
177 See_also: $(LINK http://en.wikipedia.org/wiki/Unrolled_linked_list)
178 Params:
179 	T = the element type
180 	supportGC = true to ensure that the GC scans the nodes of the unrolled
181 		list, false if you are sure that no references to GC-managed memory
182 		will be stored in this container.
183 	cacheLineSize = Nodes will be sized to fit within this number of bytes.`;
184 
185 	auto parsed = parseComment(comment, null);
186 	assert(parsed.sections[3].name == "Params");
187 	assert(parsed.sections[3].mapping.length == 3);
188 	assert(parsed.sections[3].mapping[1][0] == "supportGC");
189 	assert(parsed.sections[3].mapping[1][1][0] == 't', "<<" ~ parsed.sections[3].mapping[1][1] ~ ">>");
190 }