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 }