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 }