1 /**
2 	Merges eponymous templates to a single definition with template arguments.
3 
4 	Copyright: © 2012-2016 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.processors.eptemplates;
9 
10 import ddox.api;
11 import ddox.entities;
12 
13 import std.algorithm;
14 
15 
16 void mergeEponymousTemplates(Package root)
17 {
18 	import std.array : array;
19 	import std.string : strip;
20 
21 	static bool canMerge(TemplateDeclaration templ, Declaration m)
22 	{
23 		// if we encounter any templated member, skip the
24 		// eponymous merge to avoid hiding the nested template
25 		// arguments/constraints
26 		if (cast(TemplateDeclaration)m || m.isTemplate) return false;
27 
28 		// if both, the parent template and the member are documented,
29 		// abort the merge, so that the member documentation is shown
30 		// individually
31 		if (templ.docGroup.text.strip.length && m.docGroup.text.strip.length)
32 			return false;
33 		return true;
34 	}
35 
36 	void processDecls(ref Declaration[] decls)
37 	{
38 		Declaration[] new_decls;
39 		foreach (d; decls) {
40 			if (auto templ = cast(TemplateDeclaration)d) {
41 				// process members recursively
42 				processDecls(templ.members);
43 
44 				// search for eponymous template members
45 				Declaration[] epmembers = templ.members.filter!(m => m.name == templ.name).array;
46 				if (!epmembers.length || !epmembers.all!(m => canMerge(templ, m))) {
47 					// keep the template if there are no eponymous members or not all are mergeable
48 					new_decls ~= templ;
49 					continue;
50 				}
51 
52 				foreach (m; epmembers) {
53 					m.templateArgs = templ.templateArgs;
54 					m.templateConstraint = templ.templateConstraint;
55 					m.isTemplate = true;
56 					m.protection = templ.protection;
57 					m.parent = templ.parent;
58 					if (templ.docGroup.text.strip.length) m.docGroup = templ.docGroup;
59 					m.inheritingDecl = templ.inheritingDecl;
60 				}
61 
62 				// if we found some, replace all references of the original template with the new modified members
63 				foreach (i, m; templ.docGroup.members) {
64 					if (m !is templ) continue;
65 					auto newm = templ.docGroup.members[0 .. i];
66 					foreach (epm; epmembers)
67 						if (epm.docGroup is templ.docGroup)
68 							newm ~= epm;
69 					newm ~= templ.docGroup.members[i+1 .. $];
70 					templ.docGroup.members = newm;
71 					break;
72 				}
73 				new_decls ~= epmembers;
74 			} else {
75 				if (auto comp = cast(CompositeTypeDeclaration)d)
76 					processDecls(comp.members);
77 
78 				new_decls ~= d;
79 			}
80 		}
81 		decls = new_decls;
82 	}
83 
84 	root.visit!Module((Module mod){
85 		processDecls(mod.members);
86 	});
87 }