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 }