1 module dextool.plugin.mutate.backend.report.html.page_tree_map; 2 3 import std.json : JSONValue; 4 import arsd.dom : Document, Element, require, Table, RawSource; 5 6 import dextool.plugin.mutate.backend.report.html : FileIndex; 7 import dextool.plugin.mutate.backend.report.html.tmpl : tmplBasicPage, filesCss; 8 import dextool.plugin.mutate.backend.resource; 9 10 /** A JSON-like object that is more easily manipulated than JSONValue. 11 * 12 * Has operator overloads for "index", "index assign" and "in" allowing the 13 * children list to be private and cleaner construction of the object. 14 */ 15 auto makeTreeMapPage(FileIndex[] files) { 16 import std.format : format; 17 18 auto doc = tmplBasicPage.filesCss; 19 doc.mainBody.setAttribute("onload", "init()"); 20 21 auto s = doc.root.childElements("head")[0].addChild("script"); 22 s.addChild(new RawSource(doc, jsD3Mini)); 23 24 doc.mainBody.addChild("div").setAttribute("id", "container"); 25 26 auto s2 = doc.mainBody.addChild("script"); 27 s2.addChild(new RawSource(doc, format("const g_indata = %s", makeTreeMapJSON(files)))); 28 s2.addChild(new RawSource(doc, jsTreeMap)); 29 30 return doc.toPrettyString; 31 } 32 33 private: 34 35 auto makeTreeMapJSON(FileIndex[] files) { 36 import std.array : array; 37 import std.path : pathSplitter; 38 39 auto root = new JSONLike(); 40 root.name = "root"; 41 42 foreach (f; files) { 43 auto path = pathSplitter(f.display); 44 auto parent = root; 45 foreach (seg; path) { 46 if (seg !in parent) { 47 auto child = new JSONLike(); 48 child.name = seg; 49 parent[seg] = child; 50 } 51 parent = parent[seg]; 52 } 53 parent.locs = f.stat.total; 54 parent.score = f.stat.score; 55 } 56 return root.toJSONValue().toPrettyString(); 57 } 58 59 class JSONLike { 60 import std.array : empty; 61 import std.format : format; 62 import std.typecons : Nullable; 63 64 string name; 65 private JSONLike[string] children; 66 Nullable!long locs; 67 Nullable!double score; 68 69 JSONValue toJSONValue() { 70 JSONValue root = ["name" : name]; 71 72 if (!children.empty) { 73 JSONValue[] descendants = []; 74 foreach (c; children) { 75 descendants ~= c.toJSONValue(); 76 } 77 root.object["children"] = JSONValue(descendants); 78 } 79 if (!locs.isNull) 80 root.object["locs"] = JSONValue(locs.get); 81 82 if (!score.isNull) 83 root.object["score"] = JSONValue(format("%.2s", score.get)); 84 85 return root; 86 } 87 88 JSONLike opIndex(string i) pure nothrow { 89 return children[i]; 90 } 91 92 JSONLike opIndexAssign(JSONLike value, string i) pure nothrow { 93 children[i] = value; 94 return value; 95 } 96 97 bool opBinaryRight(string op)(string a) { 98 static if (op == "in") { 99 if (a in children) { 100 return true; 101 } 102 return false; 103 } else 104 static assert(0, "Operator " ~ op ~ " not implmeneted"); 105 } 106 }