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