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 string[] declStyleClasses(Declaration decl)
140 {
141 	string[] ret;
142 	ret ~= decl.protection.to!string().toLower();
143 	if (decl.inheritingDecl) ret ~= "inherited";
144 	if (auto tdecl = cast(TypedDeclaration)decl) {
145 		assert(tdecl.type !is null, typeid(tdecl).name~" declaration without type!?");
146 		if (tdecl.type.attributes.canFind("@property")) ret ~= "property";
147 		if (tdecl.type.attributes.canFind("static")) ret ~= "static";
148 	}
149 	return ret;
150 }
151 
152 string formatType()(Type type, string delegate(Entity) link_to, bool include_code_tags = true)
153 {
154 	if( !type ) return "{null}";
155 	//logDebug("format type: %s", type);
156 	auto ret = appender!string();
157 	formatType(ret, type, link_to, include_code_tags);
158 	return ret.data();
159 }
160 
161 void formatType(R)(ref R dst, Type type, string delegate(Entity) link_to, bool include_code_tags = true)
162 {
163 	if (include_code_tags) dst.put("<code class=\"prettyprint lang-d\">");
164 	foreach( att; type.attributes){
165 		dst.put(att); 
166 		dst.put(' ');
167 	}
168 	if( type.kind != TypeKind.Function && type.kind != TypeKind.Delegate ){
169 		foreach( att; type.modifiers ){
170 			dst.put(att);
171 			dst.put('(');
172 		}
173 	}
174 	switch( type.kind ){
175 		default:
176 		case TypeKind.Primitive:
177 			if (type.typeDecl && !cast(TemplateParameterDeclaration)type.typeDecl) {
178 				auto mn = type.typeDecl.module_.qualifiedName;
179 				auto qn = type.typeDecl.nestedName;
180 				if( qn.startsWith(mn~".") ) qn = qn[mn.length+1 .. $];
181 				formattedWrite(dst, "<a href=\"%s\">%s</a>", link_to(type.typeDecl), qn);
182 			} else {
183 				dst.put(type.typeName);
184 			}
185 			if( type.templateArgs.length ){
186 				dst.put('!');
187 				dst.put(type.templateArgs);
188 			}
189 			break;
190 		case TypeKind.Function:
191 		case TypeKind.Delegate:
192 			formatType(dst, type.returnType, link_to, false);
193 			dst.put(' ');
194 			dst.put(type.kind == TypeKind.Function ? "function" : "delegate");
195 			dst.put('(');
196 			foreach( size_t i, pt; type.parameterTypes ){
197 				if( i > 0 ) dst.put(", ");
198 				formatType(dst, pt, link_to, false);
199 				if( type._parameterNames[i].length ){
200 					dst.put(' ');
201 					dst.put(type._parameterNames[i]);
202 				}
203 				if( type._parameterDefaultValues[i] ){
204 					dst.put(" = ");
205 					dst.put(type._parameterDefaultValues[i].valueString);
206 				}
207 			}
208 			dst.put(')');
209 			foreach( att; type.modifiers ){
210 				dst.put(' ');
211 				dst.put(att);
212 			}
213 			break;
214 		case TypeKind.Pointer:
215 			formatType(dst, type.elementType, link_to, false);
216 			dst.put('*');
217 			break;
218 		case TypeKind.Array:
219 			formatType(dst, type.elementType, link_to, false);
220 			dst.put("[]");
221 			break;
222 		case TypeKind.StaticArray:
223 			formatType(dst, type.elementType, link_to, false);
224 			formattedWrite(dst, "[%s]", type.arrayLength);
225 			break;
226 		case TypeKind.AssociativeArray:
227 			formatType(dst, type.elementType, link_to, false);
228 			dst.put('[');
229 			formatType(dst, type.keyType, link_to, false);
230 			dst.put(']');
231 			break;
232 	}
233 	if( type.kind != TypeKind.Function && type.kind != TypeKind.Delegate ){
234 		foreach( att; type.modifiers ) dst.put(')');
235 	}
236 	if (include_code_tags) dst.put("</code>");
237 }
238 
239 Type getPropertyType(Entity[] mems...)
240 {
241 	foreach (ov; mems) {
242 		auto ovf = cast(FunctionDeclaration)ov;
243 		if (!ovf) continue;
244 		auto rt = ovf.returnType;
245 		if (rt.typeName != "void") return rt;
246 		if (ovf.parameters.length == 0) continue;
247 		return ovf.parameters[0].type;
248 	}
249 	return null;
250 }
251 
252 bool anyPropertyGetter(Entity[] mems...)
253 {
254 	foreach (ov; mems) {
255 		auto ovf = cast(FunctionDeclaration)ov;
256 		if (!ovf) continue;
257 		if (ovf.returnType.typeName == "void") continue;
258 		if (ovf.parameters.length == 0) return true;
259 	}
260 	return false;
261 }
262 
263 bool anyPropertySetter(Entity[] mems...)
264 {
265 	foreach (ov; mems) {
266 		auto ovf = cast(FunctionDeclaration)ov;
267 		if (!ovf) continue;
268 		if (ovf.parameters.length == 1) return true;
269 	}
270 	return false;
271 }