From 815d652b570f53c989f62d0c7db847d7d6dfd940 Mon Sep 17 00:00:00 2001
From: pommicket <pommicket@gmail.com>
Date: Thu, 7 Sep 2023 14:38:49 -0400
Subject: textDocument/publishDiagnostics parsing

---
 lsp-parse.c    | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 lsp-write.c    |  8 ++++++++
 lsp.c          |  7 +++++++
 lsp.h          | 26 ++++++++++++++++++++++++++
 main.c         |  3 +++
 ted-internal.h |  2 ++
 ted.c          |  6 ++++++
 7 files changed, 104 insertions(+), 1 deletion(-)

diff --git a/lsp-parse.c b/lsp-parse.c
index ace4881..5337de5 100644
--- a/lsp-parse.c
+++ b/lsp-parse.c
@@ -708,6 +708,56 @@ static bool parse_window_message(LSP *lsp, const JSON *json, LSPRequest *request
 	return true;
 }
 
+static bool parse_diagnostic(LSP *lsp, LSPRequest *request, const JSON *json, JSONObject diagnostic_in, LSPDiagnostic *diagnostic_out) {
+	if (!parse_range(lsp, json, json_object_get(json, diagnostic_in, "range"),
+		&diagnostic_out->range))
+		return false;
+	diagnostic_out->message = lsp_request_add_json_string(
+		request, json,
+		json_object_get_string(json, diagnostic_in, "request")
+	);
+	JSONValue severity_val = json_object_get(json, diagnostic_in, "severity");
+	LSPDiagnosticSeverity severity = LSP_DIAGNOSTIC_SEVERITY_INFORMATION;
+	if (severity_val.type == JSON_NUMBER) {
+		int s = (int)json_force_number(severity_val);
+		if (s >= LSP_DIAGNOSTIC_SEVERITY_MIN && s <= LSP_DIAGNOSTIC_SEVERITY_MAX) {
+			severity = (LSPDiagnosticSeverity)s;
+		}
+	}
+	diagnostic_out->severity = severity;
+	JSONValue code_val = json_object_get(json, diagnostic_in, "code");
+	if (code_val.type == JSON_NUMBER) {
+		int code = (int)code_val.val.number;
+		char string[32] = {0};
+		strbuf_printf(string, "%d", code);
+		diagnostic_out->code = lsp_request_add_string(request, string);
+	} else if (code_val.type == JSON_STRING) {
+		diagnostic_out->code = lsp_request_add_json_string(request, json, code_val.val.string);
+	}
+	JSONObject code_description = json_object_get_object(json, diagnostic_in, "codeDescription");
+	diagnostic_out->code_description_uri = lsp_request_add_json_string(
+		request, json,
+		json_object_get_string(json, code_description, "href")
+	);
+	return true;
+}
+
+static bool parse_publish_diagnostics(LSP *lsp, const JSON *json, LSPRequest *request) {
+	LSPRequestPublishDiagnostics *pub = &request->data.publish_diagnostics;
+	JSONObject params = json_force_object(json_get(json, "params"));
+	JSONValue uri_val = json_object_get(json, params, "uri");
+	if (!parse_document_uri(lsp, json, uri_val, &pub->document))
+		return false;
+	JSONArray diagnostics = json_object_get_array(json, params, "diagnostics");
+	for (u32 i = 0; i < diagnostics.len; ++i) {
+		JSONObject diagnostic_in = json_array_get_object(json, diagnostics, i);
+		LSPDiagnostic *diagnostic_out = arr_addp(pub->diagnostics);
+		if (!parse_diagnostic(lsp, request, json, diagnostic_in, diagnostic_out))
+			return false;
+	}
+	return true;
+}
+
 // returns true if `request` was actually filled with a request.
 static bool parse_server2client_request(LSP *lsp, JSON *json, LSPRequest *request) {
 	JSONValue method_value = json_get(json, "method");
@@ -750,7 +800,8 @@ static bool parse_server2client_request(LSP *lsp, JSON *json, LSPRequest *reques
 	} else if (str_has_prefix(method, "$/") || str_has_prefix(method, "telemetry/")) {
 		// we can safely ignore this
 	} else if (streq(method, "textDocument/publishDiagnostics")) {
-		// currently ignored
+		request->type = LSP_REQUEST_PUBLISH_DIAGNOSTICS;
+		return parse_publish_diagnostics(lsp, json, request);
 	} else {
 		debug_println("Unrecognized request method: %s", method);
 	}
diff --git a/lsp-write.c b/lsp-write.c
index 99a4e82..fd80799 100644
--- a/lsp-write.c
+++ b/lsp-write.c
@@ -274,6 +274,8 @@ static const char *lsp_request_method(LSPRequest *request) {
 		return "textDocument/completion";
 	case LSP_REQUEST_SIGNATURE_HELP:
 		return "textDocument/signatureHelp";
+	case LSP_REQUEST_PUBLISH_DIAGNOSTICS:
+		return "textDocument/publishDiagnostics";
 	case LSP_REQUEST_HOVER:
 		return "textDocument/hover";
 	case LSP_REQUEST_REFERENCES:
@@ -392,6 +394,7 @@ void write_request(LSP *lsp, LSPRequest *request) {
 	case LSP_REQUEST_SHOW_MESSAGE:
 	case LSP_REQUEST_LOG_MESSAGE:
 	case LSP_REQUEST_WORKSPACE_FOLDERS:
+	case LSP_REQUEST_PUBLISH_DIAGNOSTICS:
 		assert(0);
 		break;
 	case LSP_REQUEST_SHUTDOWN:
@@ -454,6 +457,11 @@ void write_request(LSP *lsp, LSPRequest *request) {
 					write_key_obj_start(o, "documentLink");
 						write_key_bool(o, "tooltipSupport", true);
 					write_obj_end(o);
+					
+					// publish diagnostics capabilities
+					write_key_obj_start(o, "publishDiagnostics");
+						write_key_bool(o, "codeDescriptionSupport", true);
+					write_obj_end(o);
 				write_obj_end(o);
 				write_key_obj_start(o, "workspace");
 					write_key_bool(o, "workspaceFolders", true);
diff --git a/lsp.c b/lsp.c
index 5944c29..ec9d3d7 100644
--- a/lsp.c
+++ b/lsp.c
@@ -119,6 +119,11 @@ void lsp_request_free(LSPRequest *r) {
 	case LSP_REQUEST_DOCUMENT_LINK:
 	case LSP_REQUEST_CONFIGURATION:
 	case LSP_REQUEST_DID_OPEN:
+		break;
+	case LSP_REQUEST_PUBLISH_DIAGNOSTICS: {
+		LSPRequestPublishDiagnostics *pub = &r->data.publish_diagnostics;
+		arr_free(pub->diagnostics);
+		} break;
 	case LSP_REQUEST_SHOW_MESSAGE:
 	case LSP_REQUEST_LOG_MESSAGE:
 	case LSP_REQUEST_RENAME:
@@ -209,6 +214,7 @@ static bool lsp_supports_request(LSP *lsp, const LSPRequest *request) {
 	case LSP_REQUEST_SHOW_MESSAGE:
 	case LSP_REQUEST_LOG_MESSAGE:
 	case LSP_REQUEST_WORKSPACE_FOLDERS:
+	case LSP_REQUEST_PUBLISH_DIAGNOSTICS:
 		return false;
 	case LSP_REQUEST_INITIALIZE:
 	case LSP_REQUEST_INITIALIZED:
@@ -268,6 +274,7 @@ static bool request_type_is_notification(LSPRequestType type) {
 	case LSP_REQUEST_DID_CHANGE:
 	case LSP_REQUEST_DID_CHANGE_WORKSPACE_FOLDERS:
 	case LSP_REQUEST_CONFIGURATION:
+	case LSP_REQUEST_PUBLISH_DIAGNOSTICS:
 		return true;
 	case LSP_REQUEST_INITIALIZE:
 	case LSP_REQUEST_SHUTDOWN:
diff --git a/lsp.h b/lsp.h
index a03256d..1d9d8ee 100644
--- a/lsp.h
+++ b/lsp.h
@@ -94,6 +94,7 @@ typedef enum {
 	LSP_REQUEST_SHOW_MESSAGE, //< window/showMessage and window/showMessageRequest
 	LSP_REQUEST_LOG_MESSAGE, //< window/logMessage
 	LSP_REQUEST_WORKSPACE_FOLDERS, //< workspace/workspaceFolders - NOTE: this is handled directly in lsp-parse.c (because it only needs information from the LSP struct)
+	LSP_REQUEST_PUBLISH_DIAGNOSTICS, //< textDocument/publishDiagnostics
 } LSPRequestType;
 
 typedef enum {
@@ -148,6 +149,30 @@ typedef struct {
 	LSPString message;
 } LSPRequestMessage;
 
+typedef enum {
+#define LSP_DIAGNOSTIC_SEVERITY_MIN 1
+	LSP_DIAGNOSTIC_SEVERITY_ERROR = 1,
+	LSP_DIAGNOSTIC_SEVERITY_WARNING = 2,
+	LSP_DIAGNOSTIC_SEVERITY_INFORMATION = 3,
+	LSP_DIAGNOSTIC_SEVERITY_HINT = 4,
+#define LSP_DIAGNOSTIC_SEVERITY_MAX 4
+} LSPDiagnosticSeverity;
+
+typedef struct {
+	LSPRange range;
+	LSPDiagnosticSeverity severity;
+	LSPString code;
+	LSPString message;
+	/// URI to description of code
+	/// e.g. for Rust's E0621, this would be https://doc.rust-lang.org/error_codes/E0621.html
+	LSPString code_description_uri;
+} LSPDiagnostic;
+
+typedef struct {
+	LSPDocumentID document;
+	LSPDiagnostic *diagnostics; // dynamic array
+} LSPRequestPublishDiagnostics;
+
 
 /// these triggers are used for completion context and signature help context.
 #define LSP_TRIGGER_NONE 0 // not actually defined in LSP spec
@@ -247,6 +272,7 @@ typedef struct {
 		LSPRequestDidChangeWorkspaceFolders change_workspace_folders;
 		LSPRequestRename rename;
 		LSPRequestDocumentLink document_link;
+		LSPRequestPublishDiagnostics publish_diagnostics;
 	} data;
 } LSPRequest;
 
diff --git a/main.c b/main.c
index 1bb1163..12324ce 100644
--- a/main.c
+++ b/main.c
@@ -885,6 +885,9 @@ int main(int argc, char **argv) {
 						LSPRequestMessage *m = &r->data.message;
 						ted_log(ted, "%s\n", lsp_request_string(r, m->message));
 						} break;
+					case LSP_REQUEST_PUBLISH_DIAGNOSTICS: {
+						ted_process_publish_diagnostics(ted, r);
+					} break;
 					default: break;
 					}
 					} break;
diff --git a/ted-internal.h b/ted-internal.h
index 406ce55..49ca912 100644
--- a/ted-internal.h
+++ b/ted-internal.h
@@ -711,5 +711,7 @@ void ted_color_settings_for_message_type(MessageType type, ColorSetting *bg_colo
 void ted_load_fonts(Ted *ted);
 /// Free all of ted's fonts.
 void ted_free_fonts(Ted *ted);
+/// process textDocument/publishDiagnostics request
+void ted_process_publish_diagnostics(Ted *ted, LSPRequest *request);
 
 #endif // TED_INTERNAL_H_
diff --git a/ted.c b/ted.c
index 762b269..7ce2e87 100644
--- a/ted.c
+++ b/ted.c
@@ -875,3 +875,9 @@ bool ted_close_buffer_with_file(Ted *ted, const char *path) {
 	ted_close_buffer(ted, buffer);
 	return true;
 }
+
+void ted_process_publish_diagnostics(Ted *ted, LSPRequest *request) {
+	assert(request->type == LSP_REQUEST_PUBLISH_DIAGNOSTICS);
+	LSPRequestPublishDiagnostics *pub = &request->data.publish_diagnostics;
+	printf("%u diagnostics\n",arr_len(pub->diagnostics));
+}
-- 
cgit v1.2.3