From ccc3a30b1dbc7db92b54e764968dbdb756552336 Mon Sep 17 00:00:00 2001 From: Albert Cervin Date: Thu, 10 Oct 2024 23:26:04 +0200 Subject: [PATCH] More lsp protocol/transport work --- Makefile | 4 +- src/dged/json.c | 15 ++++++-- src/dged/json.h | 11 +++++- src/dged/jsonrpc.c | 25 +++++++++++++ src/dged/jsonrpc.h | 23 +++++++----- src/dged/lsp.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++ src/dged/lsp.h | 43 +++++++-------------- 7 files changed, 169 insertions(+), 45 deletions(-) create mode 100644 src/dged/jsonrpc.c diff --git a/Makefile b/Makefile index 5630d10..3a41005 100644 --- a/Makefile +++ b/Makefile @@ -79,8 +79,8 @@ ASAN ?= false .endif .if $(LSP_ENABLE) == true - HEADERS += src/dged/lsp.h src/main/lsp.h src/dged/json.h - SOURCES += src/dged/lsp.c src/dged/json.c + HEADERS += src/dged/lsp.h src/main/lsp.h src/dged/json.h src/dged/jsonrpc.h + SOURCES += src/dged/lsp.c src/dged/json.c src/dged/jsonrpc.c MAIN_SOURCES += src/main/lsp.c TEST_SOURCES += test/json.c CFLAGS += -DLSP_ENABLED diff --git a/src/dged/json.c b/src/dged/json.c index a8c3fed..3dafa46 100644 --- a/src/dged/json.c +++ b/src/dged/json.c @@ -322,9 +322,7 @@ uint64_t json_len(struct json_object *obj) { return HASHMAP_SIZE(&obj->members); } -uint64_t json_empty(struct json_object *obj) { - return json_len(obj) == 0; -} +bool json_empty(struct json_object *obj) { return json_len(obj) == 0; } bool json_contains(struct json_object *obj, struct s8 key) { // TODO: get rid of alloc @@ -346,3 +344,14 @@ struct json_value *json_get(struct json_object *obj, struct s8 key) { return result; } + +void json_set(struct json_object *obj, struct s8 key_, struct json_value val) { + // TODO: get rid of alloc + char *k = s8tocstr(key_); + uint32_t hash = 0; + HASHMAP_INSERT(&obj->members, struct json_object_member, k, val, hash); + + (void)hash; + (void)key; + free(k); +} diff --git a/src/dged/json.h b/src/dged/json.h index ced0f73..d686538 100644 --- a/src/dged/json.h +++ b/src/dged/json.h @@ -61,7 +61,7 @@ void json_destroy(struct json_value *value); * * @returns True if @ref obj is empty, false otherwise. */ -void json_empty(struct json_object *obj); +bool json_empty(struct json_object *obj); /** * Return the number of members in a JSON object. @@ -93,6 +93,15 @@ bool json_contains(struct json_object *obj, struct s8 key); */ struct json_value *json_get(struct json_object *obj, struct s8 key); +/** + * Set a value in a JSON object. + * + * @param [in] obj The JSON object to set in. + * @param [in] key The key of the value to set. + * @param [in] value The JSON value to set. + */ +void json_set(struct json_object *obj, struct s8 key, struct json_value val); + /** * Get the length of a JSON array. * diff --git a/src/dged/jsonrpc.c b/src/dged/jsonrpc.c new file mode 100644 index 0000000..864b839 --- /dev/null +++ b/src/dged/jsonrpc.c @@ -0,0 +1,25 @@ +#include "jsonrpc.h" + +#include + +struct jsonrpc_request jsonrpc_request_create(struct json_value id, + struct s8 method, + struct json_object *params) { + return (struct jsonrpc_request){ + .id = id, + .method = method, + .params = params, + }; +} + +struct jsonrpc_response jsonrpc_parse_response(const uint8_t *buf, + uint64_t size) { + (void)buf; + (void)size; + return (struct jsonrpc_response){}; +} + +struct s8 jsonrpc_request_to_string(const struct jsonrpc_request *request) { + (void)request; + return (struct s8){.l = 0, .s = NULL}; +} diff --git a/src/dged/jsonrpc.h b/src/dged/jsonrpc.h index ad2b0cc..835e602 100644 --- a/src/dged/jsonrpc.h +++ b/src/dged/jsonrpc.h @@ -1,6 +1,8 @@ #ifndef _JSONRPC_H #define _JSONRPC_H +#include + #include "json.h" #include "s8.h" @@ -10,6 +12,12 @@ struct jsonrpc_request { struct json_object *params; }; +struct jsonrpc_error { + int code; + struct s8 message; + struct json_value data; +}; + struct jsonrpc_response { struct json_value id; bool ok; @@ -19,14 +27,11 @@ struct jsonrpc_response { } value; }; -struct jsonrpc_error { - int code; - struct s8 message; - struct json_value data; -}; - -struct jsonrpc_request jsonrpc_request_create(struct s8 method, struct json_object *params); -struct jsonrpc_response jsonrpc_parse_response(const uint8_t *buf, uint64_t size); -struct s8 jsonrpc_request_to_string(const struct jsonprc_request *request); +struct jsonrpc_request jsonrpc_request_create(struct json_value id, + struct s8 method, + struct json_object *params); +struct jsonrpc_response jsonrpc_parse_response(const uint8_t *buf, + uint64_t size); +struct s8 jsonrpc_request_to_string(const struct jsonrpc_request *request); #endif diff --git a/src/dged/lsp.c b/src/dged/lsp.c index e73a41f..139452d 100644 --- a/src/dged/lsp.c +++ b/src/dged/lsp.c @@ -6,9 +6,25 @@ #include #include "buffer.h" +#include "jsonrpc.h" #include "process.h" #include "reactor.h" +struct pending_write { + char headers[256]; + uint64_t headers_len; + uint64_t request_id; + uint64_t written; + struct s8 payload; +}; + +struct pending_read { + uint64_t request_id; + struct s8 payload; +}; + +typedef VEC(struct pending_write) write_vec; + struct lsp { const char *name; char *const *command; @@ -19,6 +35,11 @@ struct lsp { uint32_t stdin_event; uint32_t stdout_event; uint32_t stderr_event; + + request_id current_id; + + write_vec writes; + VEC(struct pending_read) reads; }; struct lsp *lsp_create(char *const command[], struct reactor *reactor, @@ -65,6 +86,7 @@ struct lsp *lsp_create(char *const command[], struct reactor *reactor, lsp->stdin_event = -1; lsp->stdout_event = -1; lsp->stderr_event = -1; + lsp->current_id = 0; return lsp; } @@ -109,6 +131,49 @@ uint32_t lsp_update(struct lsp *lsp, struct lsp_response **responses, } } + // write pending requests + if (reactor_poll_event(lsp->reactor, lsp->stdin_event)) { + VEC_FOR_EACH(&lsp->writes, struct pending_write * w) { + size_t written = 0; + uint64_t to_write = 0; + if (w->written < w->headers_len) { + to_write = w->headers_len - w->written; + written = write(lsp->process->stdin, w->headers + w->written, to_write); + } else { + to_write = w->payload.l + w->headers_len - w->written; + written = write(lsp->process->stdin, w->payload.s, to_write); + + if (to_write == written) { + VEC_APPEND(&lsp->reads, struct pending_read * r); + r->request_id = w->request_id; + } + } + + w->written += written; + + // if this happens, we ran out of buffer space + if (written < to_write) { + goto cleanup_writes; + } + } + } + +cleanup_writes: + /* lsp->writes = filter(&lsp->writes, x: x.written < x.payload.l + + * x.headers_len) */ + write_vec writes = lsp->writes; + VEC_INIT(&lsp->writes, VEC_SIZE(&writes)); + + VEC_FOR_EACH(&writes, struct pending_write * w) { + if (w->written < w->payload.l + w->headers_len) { + // copying 256 bytes, goodbye vaccuum tubes... + VEC_PUSH(&lsp->writes, *w); + } + } + VEC_DESTROY(&writes); + + // TODO: process incoming responses + return 0; } @@ -125,6 +190,12 @@ int lsp_start_server(struct lsp *lsp) { memcpy(lsp->process, &p, sizeof(struct process)); lsp->stderr_event = reactor_register_interest( lsp->reactor, lsp->process->stderr, ReadInterest); + lsp->stdin_event = reactor_register_interest( + lsp->reactor, lsp->process->stdin, WriteInterest); + + if (lsp->stdin_event == (uint32_t)-1) { + return -2; + } return 0; } @@ -161,3 +232,25 @@ uint64_t lsp_server_pid(const struct lsp *lsp) { } const char *lsp_server_name(const struct lsp *lsp) { return lsp->name; } + +request_id lsp_request(struct lsp *lsp, struct lsp_request request) { + struct json_value js_id = { + .type = Json_Number, + .value.number = (double)lsp->current_id, + .parent = NULL, + }; + + struct jsonrpc_request req = + jsonrpc_request_create(js_id, request.method, request.params); + struct s8 payload = jsonrpc_request_to_string(&req); + + VEC_APPEND(&lsp->writes, struct pending_write * w); + w->headers_len = + snprintf(w->headers, 256, "Content-Length: %d\r\n\r\n", payload.l); + w->request_id = lsp->current_id; + w->payload = payload; + + ++lsp->current_id; + + return w->request_id; +} diff --git a/src/dged/lsp.h b/src/dged/lsp.h index 3fd6285..99bf619 100644 --- a/src/dged/lsp.h +++ b/src/dged/lsp.h @@ -1,6 +1,7 @@ #ifndef _LSP_H #define _LSP_H +#include "json.h" #include "location.h" #include "s8.h" @@ -8,17 +9,25 @@ struct buffer; struct lsp; struct reactor; -typedef uint32_t request_id; +typedef uint64_t request_id; + +struct lsp_response_error {}; struct lsp_response { request_id id; + bool ok; union payload_data { - void *result; - struct s8 error; + struct json_value result; + struct lsp_response_error error; } payload; }; +struct lsp_request { + struct s8 method; + struct json_object *params; +}; + struct lsp_notification { int something; }; @@ -27,30 +36,6 @@ struct lsp_client { void (*log_message)(int type, struct s8 msg); }; -struct hover { - struct s8 contents; - - bool has_range; - struct region *range; -}; - -struct text_doc_item { - struct s8 uri; - struct s8 language_id; - uint32_t version; - struct s8 text; -}; - -struct text_doc_position { - struct s8 uri; - struct location pos; -}; - -struct initialize_params { - struct s8 client_name; - struct s8 client_version; -}; - // lifecycle functions struct lsp *lsp_create(char *const command[], struct reactor *reactor, struct buffer *stderr_buffer, @@ -68,8 +53,6 @@ uint64_t lsp_server_pid(const struct lsp *lsp); const char *lsp_server_name(const struct lsp *lsp); // protocol functions -void lsp_initialize(struct lsp *lsp, struct initialize_params); -void lsp_did_open_document(struct lsp *lsp, struct text_doc_item document); -request_id lsp_hover(struct lsp *lsp, struct text_doc_position); +request_id lsp_request(struct lsp *lsp, struct lsp_request request); #endif