1 module workspaced.com.snippets.dependencies; 2 3 import workspaced.api; 4 import workspaced.com.dub; 5 import workspaced.com.snippets; 6 7 import std.algorithm; 8 9 /// 10 alias SnippetList = const(PlainSnippet)[]; 11 12 /// A list of dependencies usable in an associative array 13 struct DependencySet 14 { 15 private string[] sorted; 16 17 void set(scope const(string)[] deps) 18 { 19 sorted.length = deps.length; 20 sorted[] = deps; 21 sorted.sort!"a<b"; 22 } 23 24 bool hasAll(scope string[] deps) const 25 { 26 deps.sort!"a<b"; 27 int a, b; 28 while (a < sorted.length && b < deps.length) 29 { 30 const as = sorted[a]; 31 const bs = deps[b]; 32 const c = cmp(as, bs); 33 34 if (c == 0) 35 { 36 a++; 37 b++; 38 } 39 else if (c < 0) 40 return false; 41 else 42 b++; 43 } 44 return a == sorted.length; 45 } 46 47 bool opEquals(const ref DependencySet other) const 48 { 49 return sorted == other.sorted; 50 } 51 52 size_t toHash() const @safe nothrow 53 { 54 size_t ret; 55 foreach (v; sorted) 56 ret ^= typeid(v).getHash((() @trusted => &v)()); 57 return ret; 58 } 59 } 60 61 /// Representation for a plain snippet with required dependencies. Maps to the 62 /// parameters of `DependencyBasedSnippetProvider.addSnippet`. 63 struct DependencySnippet 64 { 65 /// 66 const(string)[] requiredDependencies; 67 /// 68 PlainSnippet snippet; 69 } 70 71 /// ditto 72 struct DependencySnippets 73 { 74 /// 75 const(string)[] requiredDependencies; 76 /// 77 const(PlainSnippet)[] snippets; 78 } 79 80 class DependencyBasedSnippetProvider : SnippetProvider 81 { 82 SnippetList[DependencySet] snippets; 83 84 void addSnippet(const(string)[] requiredDependencies, const PlainSnippet snippet) 85 { 86 DependencySet set; 87 set.set(requiredDependencies); 88 89 if (auto v = set in snippets) 90 *v ~= snippet; 91 else 92 snippets[set] = [snippet]; 93 } 94 95 Future!(Snippet[]) provideSnippets(scope const WorkspaceD.Instance instance, 96 scope const(char)[] file, scope const(char)[] code, int position, const SnippetInfo info) 97 { 98 if (!instance.has!DubComponent) 99 return typeof(return).fromResult(null); 100 else 101 { 102 string id = typeid(this).name; 103 auto dub = instance.get!DubComponent; 104 return typeof(return).async(delegate() { 105 string[] deps; 106 foreach (dep; dub.dependencies) 107 { 108 deps ~= dep.name; 109 deps ~= dep.dependencies.keys; 110 } 111 Snippet[] ret; 112 foreach (k, v; snippets) 113 { 114 if (k.hasAll(deps)) 115 { 116 foreach (snip; v) 117 if (snip.levels.canFind(info.level)) 118 ret ~= snip.buildSnippet(id); 119 } 120 } 121 return ret; 122 }); 123 } 124 } 125 126 Future!Snippet resolveSnippet(scope const WorkspaceD.Instance instance, 127 scope const(char)[] file, scope const(char)[] code, int position, 128 const SnippetInfo info, Snippet snippet) 129 { 130 snippet.resolved = true; 131 return typeof(return).fromResult(snippet); 132 } 133 } 134 135 unittest 136 { 137 DependencySet set; 138 set.set(["vibe-d", "mir", "serve-d"]); 139 assert(set.hasAll(["vibe-d", "serve-d", "mir"])); 140 assert(set.hasAll(["vibe-d", "serve-d", "serve-d", "serve-d", "mir", "mir"])); 141 assert(set.hasAll(["vibe-d", "serve-d", "mir", "workspace-d"])); 142 assert(set.hasAll(["diet-ng", "vibe-d", "serve-d", "mir", "workspace-d"])); 143 assert(!set.hasAll(["diet-ng", "serve-d", "mir", "workspace-d"])); 144 assert(!set.hasAll(["diet-ng", "serve-d", "vibe-d", "workspace-d"])); 145 assert(!set.hasAll(["diet-ng", "mir", "mir", "vibe-d", "workspace-d"])); 146 assert(!set.hasAll(["diet-ng", "mir", "vibe-d", "workspace-d"])); 147 148 set.set(["vibe-d:http"]); 149 assert(set.hasAll([ 150 "botan", "botan", "botan-math", "botan-math", "diet-ng", "diet-ng", 151 "eventcore", "eventcore", "libasync", "libasync", "memutils", 152 "memutils", "memutils", "mir-linux-kernel", "mir-linux-kernel", 153 "openssl", "openssl", "openssl", "stdx-allocator", "stdx-allocator", 154 "stdx-allocator", "taggedalgebraic", "taggedalgebraic", "vibe-core", 155 "vibe-core", "vibe-d", "vibe-d:core", "vibe-d:core", "vibe-d:core", 156 "vibe-d:core", "vibe-d:core", "vibe-d:core", "vibe-d:crypto", 157 "vibe-d:crypto", "vibe-d:crypto", "vibe-d:data", "vibe-d:data", 158 "vibe-d:data", "vibe-d:http", "vibe-d:http", "vibe-d:http", "vibe-d:http", 159 "vibe-d:http", "vibe-d:inet", "vibe-d:inet", "vibe-d:inet", "vibe-d:inet", 160 "vibe-d:mail", "vibe-d:mail", "vibe-d:mongodb", "vibe-d:mongodb", 161 "vibe-d:redis", "vibe-d:redis", "vibe-d:stream", "vibe-d:stream", 162 "vibe-d:stream", "vibe-d:stream", "vibe-d:textfilter", "vibe-d:textfilter", 163 "vibe-d:textfilter", "vibe-d:textfilter", "vibe-d:tls", "vibe-d:tls", 164 "vibe-d:tls", "vibe-d:tls", "vibe-d:utils", "vibe-d:utils", "vibe-d:utils", 165 "vibe-d:utils", "vibe-d:utils", "vibe-d:utils", "vibe-d:web", "vibe-d:web" 166 ])); 167 168 set.set(null); 169 assert(set.hasAll([])); 170 assert(set.hasAll(["foo"])); 171 }