1 module served.linters.diagnosticmanager;
2 
3 import std.array : array;
4 import std.algorithm : map, sort;
5 
6 import served.utils.memory;
7 import served.types;
8 
9 import painlessjson;
10 
11 enum NumDiagnosticProviders = 3;
12 alias DiagnosticCollection = PublishDiagnosticsParams[];
13 DiagnosticCollection[NumDiagnosticProviders] diagnostics;
14 
15 DiagnosticCollection combinedDiagnostics;
16 DocumentUri[] publishedUris;
17 
18 void combineDiagnostics()
19 {
20 	combinedDiagnostics.length = 0;
21 	foreach (provider; diagnostics)
22 	{
23 		foreach (errors; provider)
24 		{
25 			size_t index = combinedDiagnostics.length;
26 			foreach (i, existing; combinedDiagnostics)
27 			{
28 				if (existing.uri == errors.uri)
29 				{
30 					index = i;
31 					break;
32 				}
33 			}
34 			if (index == combinedDiagnostics.length)
35 				combinedDiagnostics ~= PublishDiagnosticsParams(errors.uri);
36 			combinedDiagnostics[index].diagnostics ~= errors.diagnostics;
37 		}
38 	}
39 }
40 
41 /// Returns a reference to existing diagnostics for a given url in a given slot or creates a new array for them and returns the reference for it.
42 /// Params:
43 ///   slot = the diagnostic provider slot to edit
44 ///   uri = the document uri to attach the diagnostics array for
45 ref auto createDiagnosticsFor(int slot)(string uri)
46 {
47 	static assert(slot < NumDiagnosticProviders);
48 	foreach (ref existing; diagnostics[slot])
49 		if (existing.uri == uri)
50 			return existing.diagnostics;
51 
52 	return pushRef(diagnostics[slot], PublishDiagnosticsParams(uri, null)).diagnostics;
53 }
54 
55 private ref T pushRef(T)(ref T[] arr, T value)
56 {
57 	auto len = arr.length++;
58 	return arr[len] = value;
59 }
60 
61 void updateDiagnostics(string uriHint = "")
62 {
63 	combineDiagnostics();
64 	foreach (diagnostics; combinedDiagnostics)
65 	{
66 		if (!uriHint.length || diagnostics.uri == uriHint)
67 		{
68 			// TODO: related information
69 			RequestMessage request;
70 			request.method = "textDocument/publishDiagnostics";
71 			request.params = diagnostics.toJSON;
72 			rpc.send(request);
73 		}
74 	}
75 
76 	// clear old diagnostics
77 	auto diags = combinedDiagnostics.map!"a.uri".array;
78 	auto sorted = diags.sort!"a<b";
79 	foreach (submitted; publishedUris)
80 	{
81 		if (!sorted.contains(submitted))
82 		{
83 			RequestMessage request;
84 			request.method = "textDocument/publishDiagnostics";
85 			request.params = PublishDiagnosticsParams(submitted, null).toJSON;
86 			rpc.send(request);
87 		}
88 	}
89 	destroyUnset(publishedUris);
90 	publishedUris = diags;
91 }