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 enum NumDiagnosticProviders = 3;
10 alias DiagnosticCollection = PublishDiagnosticsParams[];
11 DiagnosticCollection[NumDiagnosticProviders] diagnostics;
12 
13 DiagnosticCollection combinedDiagnostics;
14 DocumentUri[] publishedUris;
15 
16 void combineDiagnostics()
17 {
18 	combinedDiagnostics.length = 0;
19 	foreach (provider; diagnostics)
20 	{
21 		foreach (errors; provider)
22 		{
23 			size_t index = combinedDiagnostics.length;
24 			foreach (i, existing; combinedDiagnostics)
25 			{
26 				if (existing.uri == errors.uri)
27 				{
28 					index = i;
29 					break;
30 				}
31 			}
32 			if (index == combinedDiagnostics.length)
33 				combinedDiagnostics ~= PublishDiagnosticsParams(errors.uri);
34 			combinedDiagnostics[index].diagnostics ~= errors.diagnostics;
35 		}
36 	}
37 }
38 
39 /// 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.
40 /// Params:
41 ///   slot = the diagnostic provider slot to edit
42 ///   uri = the document uri to attach the diagnostics array for
43 ref auto createDiagnosticsFor(int slot)(string uri)
44 {
45 	static assert(slot < NumDiagnosticProviders);
46 	foreach (ref existing; diagnostics[slot])
47 		if (existing.uri == uri)
48 			return existing.diagnostics;
49 
50 	return pushRef(diagnostics[slot], PublishDiagnosticsParams(uri, null)).diagnostics;
51 }
52 
53 private ref T pushRef(T)(ref T[] arr, T value)
54 {
55 	auto len = arr.length++;
56 	return arr[len] = value;
57 }
58 
59 void updateDiagnostics(string uriHint = "")
60 {
61 	combineDiagnostics();
62 	foreach (diagnostics; combinedDiagnostics)
63 	{
64 		if (!uriHint.length || diagnostics.uri == uriHint)
65 		{
66 			// TODO: related information
67 			RequestMessageRaw request;
68 			request.method = "textDocument/publishDiagnostics";
69 			request.paramsJson = diagnostics.serializeJson;
70 			rpc.send(request);
71 		}
72 	}
73 
74 	// clear old diagnostics
75 	auto diags = combinedDiagnostics.map!"a.uri".array;
76 	auto sorted = diags.sort!"a<b";
77 	foreach (submitted; publishedUris)
78 	{
79 		if (!sorted.contains(submitted))
80 		{
81 			RequestMessageRaw request;
82 			request.method = "textDocument/publishDiagnostics";
83 			request.paramsJson = PublishDiagnosticsParams(submitted, null).serializeJson;
84 			rpc.send(request);
85 		}
86 	}
87 	destroyUnset(publishedUris);
88 	publishedUris = diags;
89 }