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