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