1 /**
2 	Internal functions for use inside the HTML templates.
3 
4 	Copyright: © 2012 RejectedSoftware e.K.
5 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
6 	Authors: Sönke Ludwig
7 */
8 module ddox.api;
9 
10 public import ddox.ddox;
11 public import ddox.ddoc;
12 
13 import std.algorithm;
14 import std.array;
15 import std.conv;
16 import std.format;
17 import std.string;
18 import vibe.core.log;
19 import vibe.data.json;
20 
21 
22 
23 class DocGroupContext : DdocContext {
24 	private {
25 		DocGroup m_group;
26 		string delegate(Entity ent) m_linkTo;
27 	}
28 
29 	this(DocGroup grp, string delegate(Entity ent) link_to)
30 	{
31 		m_group = grp;
32 		m_linkTo = link_to;
33 	}
34 
35 	@property string docText() { return m_group.text; }
36 	@property string[] overrideMacroDefinitions() { return null; }
37 	@property string[] defaultMacroDefinitions() { return null; }
38 	string lookupScopeSymbolLink(string name)
39 	{
40 		if (name == "this") return null;
41 
42 		bool is_global = false;
43 		if (name.startsWith(".")) {
44 			is_global = true;
45 			name = name[1 .. $];
46 		}
47 		
48 		foreach( def; m_group.members ){
49 			Entity n, nmod;
50 			if (is_global) {
51 				n = def.module_.lookup(name);
52 				nmod = def.module_.lookup!Module(name);
53 			} else {
54 				// if this is a function, first search the parameters
55 				// TODO: maybe do the same for function/delegate variables/type aliases
56 				if( auto fn = cast(FunctionDeclaration)def ){
57 					foreach( p; fn.parameters )
58 						if( p.name == name )
59 							return m_linkTo(p);
60 				}
61 
62 				// then look up the name in the outer scope
63 				n = def.lookup(name);
64 				nmod = def.lookup!Module(name);
65 			}
66 
67 			// packages are not linked
68 			if (cast(Package)n) {
69 				if (nmod) n = nmod;
70 				else continue;
71 			}
72 
73 			// module names must be fully qualified
74 			if( auto mod = cast(Module)n )
75 				if( mod.qualifiedName != name )
76 					continue;
77 
78 			// don't return links to the declaration itself, but
79 			// the sepcial string # that will still print the identifier
80 			// as code
81 			if( n is def ) return "#";
82 			
83 			if( n ) return m_linkTo(n);
84 		}
85 		return null;
86 	}
87 }
88 
89 
90 string getFunctionName(Json proto)
91 {
92 	auto n = proto.name.get!string;
93 	if( auto ptn = "templateName" in proto ){
94 		auto tn = ptn.get!string;
95 		if( tn.startsWith(n~"(") )
96 			return tn;
97 		return tn ~ "." ~ n;
98 	}
99 	return n;
100 }
101 
102 DocGroup[] docGroups(Declaration[] items)
103 {
104 	DocGroup[] ret;
105 	foreach( itm; items ){
106 		bool found = false;
107 		foreach( g; ret )
108 			if( g is itm.docGroup ){
109 				found = true;
110 				break;
111 			}
112 		if( !found ) ret ~= itm.docGroup;
113 	}
114 	return ret;
115 }
116 
117 
118 bool hasChild(T)(Module mod){ return hasChild!T(mod.members); }
119 bool hasChild(T)(CompositeTypeDeclaration decl){ return hasChild!T(decl.members); }
120 bool hasChild(T)(TemplateDeclaration mod){ return hasChild!T(mod.members); }
121 bool hasChild(T)(Declaration[] decls){ foreach( m; decls ) if( cast(T)m ) return true; return false; }
122 
123 T[] getChildren(T)(Module mod){ return getChildren!T(mod.members); }
124 T[] getChildren(T)(CompositeTypeDeclaration decl){ return getChildren!T(decl.members); }
125 T[] getChildren(T)(TemplateDeclaration decl){ return getChildren!T(decl.members); }
126 T[] getChildren(T)(Declaration[] decls)
127 {
128 	T[] ret;
129 	foreach( ch; decls )
130 		if( auto ct = cast(T)ch )
131 			ret ~= ct;
132 	return ret;
133 }
134 
135 T[] getDocGroups(T)(Module mod){ return getDocGroups!T(mod.members); }
136 T[] getDocGroups(T)(CompositeTypeDeclaration decl){ return getDocGroups!T(decl.members); }
137 T[] getDocGroups(T)(TemplateDeclaration decl){ return getDocGroups!T(decl.members); }
138 T[] getDocGroups(T)(Declaration[] decls)
139 {
140 	T[] ret;
141 	DocGroup dg;
142 	string name;
143 	foreach( d; decls ){
144 		auto dt = cast(T)d;
145 		if( !dt ) continue;
146 		if( dt.docGroup !is dg || dt.name != name ){
147 			ret ~= dt;
148 			dg = d.docGroup;
149 			name = d.name;
150 		}
151 	}
152 	return ret;
153 }
154 
155 ///
156 string getAttributeString(string[] attributes, AttributeStringKind kind)
157 {
158 	enum backAttributes = ["const", "immutable", "shared", "nothrow", "@safe", "@trusted", "@system", "pure", "@property", "@nogc"];
159 	auto ret = appender!string();
160 	foreach (a; attributes) {
161 		bool back = backAttributes.canFind(a);
162 		if (kind == AttributeStringKind.normal || back == (kind == AttributeStringKind.functionSuffix)) {
163 			if (kind == AttributeStringKind.functionSuffix) ret.put(' ');
164 			ret.put(a);
165 			if (kind != AttributeStringKind.functionSuffix) ret.put(' ');
166 		}
167 	}
168 	return ret.data;
169 }
170 /// ditto
171 string getAttributeString(Declaration decl, AttributeStringKind kind)
172 {
173 	return getAttributeString(decl.attributes, kind);
174 }
175 
176 enum AttributeStringKind { normal, functionPrefix, functionSuffix }
177 
178 string[] declStyleClasses(Declaration decl)
179 {
180 	string[] ret;
181 	ret ~= decl.protection.to!string().toLower();
182 	if (decl.inheritingDecl) ret ~= "inherited";
183 	if (auto tdecl = cast(TypedDeclaration)decl) {
184 		assert(tdecl.type !is null, typeid(tdecl).name~" declaration without type!?");
185 		if (tdecl.type.attributes.canFind("@property")) ret ~= "property";
186 		if (tdecl.type.attributes.canFind("static")) ret ~= "static";
187 	}
188 	return ret;
189 }
190 
191 string formatType()(Type type, string delegate(Entity) link_to, bool include_code_tags = true)
192 {
193 	if( !type ) return "{null}";
194 	//logDebug("format type: %s", type);
195 	auto ret = appender!string();
196 	formatType(ret, type, link_to, include_code_tags);
197 	return ret.data();
198 }
199 
200 void formatType(R)(ref R dst, Type type, string delegate(Entity) link_to, bool include_code_tags = true)
201 {
202 	import ddox.highlight;
203 
204 	if (include_code_tags) dst.put("<code class=\"prettyprint lang-d\">");
205 	foreach( att; type.attributes){
206 		dst.highlightDCode(att);
207 		dst.put(' ');
208 	}
209 	if( type.kind != TypeKind.Function && type.kind != TypeKind.Delegate ){
210 		foreach( att; type.modifiers ){
211 			dst.highlightDCode(att);
212 			dst.highlightDCode("(");
213 		}
214 	}
215 	switch (type.kind) {
216 		default:
217 		case TypeKind.Primitive:
218 			if (type.typeDecl && !cast(TemplateParameterDeclaration)type.typeDecl) {
219 				auto mn = type.typeDecl.module_.qualifiedName;
220 				auto qn = type.typeDecl.nestedName;
221 				if( qn.startsWith(mn~".") ) qn = qn[mn.length+1 .. $];
222 				formattedWrite(dst, "<a href=\"%s\">%s</a>", link_to(type.typeDecl), highlightDCode(qn).replace(".", ".<wbr/>")); // TODO: avoid allocating replace
223 			} else {
224 				dst.highlightDCode(type.typeName);
225 			}
226 			if( type.templateArgs.length ){
227 				dst.put('!');
228 				dst.put(type.templateArgs);
229 			}
230 			break;
231 		case TypeKind.Function:
232 		case TypeKind.Delegate:
233 			formatType(dst, type.returnType, link_to, false);
234 			dst.put(' ');
235 			dst.highlightDCode(type.kind == TypeKind.Function ? "function" : "delegate");
236 			dst.highlightDCode("(");
237 			foreach( size_t i, pt; type.parameterTypes ){
238 				if( i > 0 ) dst.highlightDCode(", ");
239 				formatType(dst, pt, link_to, false);
240 				if( type._parameterNames[i].length ){
241 					dst.put(' ');
242 					dst.put(type._parameterNames[i]);
243 				}
244 				if( type._parameterDefaultValues[i] ){
245 					dst.highlightDCode(" = ");
246 					dst.put(type._parameterDefaultValues[i].valueString);
247 				}
248 			}
249 			dst.highlightDCode(")");
250 			foreach( att; type.modifiers ){
251 				dst.put(' ');
252 				dst.put(att);
253 			}
254 			break;
255 		case TypeKind.Pointer:
256 			formatType(dst, type.elementType, link_to, false);
257 			dst.highlightDCode("*");
258 			break;
259 		case TypeKind.Array:
260 			formatType(dst, type.elementType, link_to, false);
261 			dst.highlightDCode("[]");
262 			break;
263 		case TypeKind.StaticArray:
264 			formatType(dst, type.elementType, link_to, false);
265 			dst.highlightDCode("[");
266 			dst.highlightDCode(type.arrayLength.to!string);
267 			dst.highlightDCode("]");
268 			break;
269 		case TypeKind.AssociativeArray:
270 			formatType(dst, type.elementType, link_to, false);
271 			dst.highlightDCode("[");
272 			formatType(dst, type.keyType, link_to, false);
273 			dst.highlightDCode("]");
274 			break;
275 	}
276 	if( type.kind != TypeKind.Function && type.kind != TypeKind.Delegate ){
277 		foreach( att; type.modifiers ) dst.highlightDCode(")");
278 	}
279 	if (include_code_tags) dst.put("</code>");
280 }
281 
282 Type getPropertyType(Entity[] mems...)
283 {
284 	foreach (ov; mems) {
285 		auto ovf = cast(FunctionDeclaration)ov;
286 		if (!ovf) continue;
287 		auto rt = ovf.returnType;
288 		if (rt.typeName != "void") return rt;
289 		if (ovf.parameters.length == 0) continue;
290 		return ovf.parameters[0].type;
291 	}
292 	return null;
293 }
294 
295 bool anyPropertyGetter(Entity[] mems...)
296 {
297 	foreach (ov; mems) {
298 		auto ovf = cast(FunctionDeclaration)ov;
299 		if (!ovf) continue;
300 		if (ovf.returnType.typeName == "void") continue;
301 		if (ovf.parameters.length == 0) return true;
302 	}
303 	return false;
304 }
305 
306 bool anyPropertySetter(Entity[] mems...)
307 {
308 	foreach (ov; mems) {
309 		auto ovf = cast(FunctionDeclaration)ov;
310 		if (!ovf) continue;
311 		if (ovf.parameters.length == 1) return true;
312 	}
313 	return false;
314 }