From b106fe19b55e9fe3348b3a5c9992c21dac27b02c Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Tue, 20 Jan 2026 12:51:23 +0000 Subject: Working a bit on the LSP + fixed some bugs --- src/lsp/json_rpc.c | 234 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 168 insertions(+), 66 deletions(-) (limited to 'src/lsp/json_rpc.c') diff --git a/src/lsp/json_rpc.c b/src/lsp/json_rpc.c index 903da71..c8c9a3b 100644 --- a/src/lsp/json_rpc.c +++ b/src/lsp/json_rpc.c @@ -3,108 +3,148 @@ #include #include #include +#include -// Basic JSON parsing helpers -char *get_json_string(const char *json, const char *key) +// Helper to skip whitespace +const char *skip_ws(const char *p) { - char search[256]; - sprintf(search, "\"%s\":\"", key); - char *p = strstr(json, search); - if (!p) + while (*p && isspace(*p)) { - return NULL; + p++; } - p += strlen(search); - char *end = strchr(p, '"'); - if (!end) + return p; +} + +// Robust JSON string extractor +// Finds "key" ... : ... "value" +char *get_json_string(const char *json, const char *key) +{ + char key_pattern[256]; + sprintf(key_pattern, "\"%s\"", key); + + char *p = strstr(json, key_pattern); + while (p) { - return NULL; + const char *cursor = p + strlen(key_pattern); + cursor = skip_ws(cursor); + if (*cursor == ':') + { + cursor++; // skip : + cursor = skip_ws(cursor); + if (*cursor == '"') + { + // Found start of value + cursor++; + const char *start = cursor; + // Find end " (handling escapes?) + // MVP: just find next " that is not escaped + while (*cursor) + { + if (*cursor == '"' && *(cursor - 1) != '\\') + { + break; + } + cursor++; + } + + int len = cursor - start; + char *res = malloc(len + 1); + strncpy(res, start, len); + res[len] = 0; + return res; + } + } + // False positive? Find next + p = strstr(p + 1, key_pattern); } - int len = end - p; - char *res = malloc(len + 1); - strncpy(res, p, len); - res[len] = 0; - return res; + return NULL; } -// Extract nested "text" from params/contentChanges/0/text or -// params/textDocument/text This is very hacky for MVP. proper JSON library -// needed. - +// Extract nested "text" from params... +// Since "text" might be huge and contain anything, we need to be careful. char *get_text_content(const char *json) { - char *p = strstr(json, "\"text\":\""); - if (!p) + // Search for "text" key + char *res = get_json_string(json, "text"); + if (!res) { return NULL; } - p += 8; - - size_t cap = strlen(p); - char *res = malloc(cap + 1); - char *dst = res; - while (*p) + size_t len = strlen(res); + char *unescaped = malloc(len + 1); + char *dst = unescaped; + char *src = res; + while (*src) { - if (*p == '\\') + if (*src == '\\') { - p++; - if (*p == 'n') + src++; + if (*src == 'n') { *dst++ = '\n'; } - else if (*p == 'r') + else if (*src == 'r') { *dst++ = '\r'; } - else if (*p == 't') + else if (*src == 't') { *dst++ = '\t'; } - else if (*p == '"') + else if (*src == '"') { *dst++ = '"'; } - else if (*p == '\\') + else if (*src == '\\') { *dst++ = '\\'; } else { - *dst++ = *p; // preserve others + *dst++ = *src; } - p++; - } - else if (*p == '"') - { - break; // End of string. } else { - *dst++ = *p++; + *dst++ = *src; } + src++; } *dst = 0; - return res; + free(res); + return unescaped; } -// Helper to get line/char void get_json_position(const char *json, int *line, int *col) { - char *pos = strstr(json, "\"position\":"); - if (!pos) - { - return; - } - char *l = strstr(pos, "\"line\":"); - if (l) + // Search for "line" and "character" + // Note: they are integers + + char *p = strstr(json, "\"line\""); + if (p) { - *line = atoi(l + 7); + p += 6; // skip "line" + p = (char *)skip_ws(p); + if (*p == ':') + { + p++; + p = (char *)skip_ws(p); + *line = atoi(p); + } } - char *c = strstr(pos, "\"character\":"); - if (c) + + p = strstr(json, "\"character\""); + if (p) { - *col = atoi(c + 12); + p += 11; + p = (char *)skip_ws(p); + if (*p == ':') + { + p++; + p = (char *)skip_ws(p); + *col = atoi(p); + } } } @@ -112,11 +152,42 @@ void lsp_check_file(const char *uri, const char *src); void lsp_goto_definition(const char *uri, int line, int col); void lsp_hover(const char *uri, int line, int col); void lsp_completion(const char *uri, int line, int col); +void lsp_document_symbol(const char *uri); +void lsp_references(const char *uri, int line, int col); +void lsp_signature_help(const char *uri, int line, int col); + +#include "lsp_project.h" void handle_request(const char *json_str) { - if (strstr(json_str, "\"method\":\"initialize\"")) + // Looser method detection + if (strstr(json_str, "initialize")) { + // Extract rootPath or rootUri + char *root = get_json_string(json_str, "rootPath"); + if (!root) + { + root = get_json_string(json_str, "rootUri"); + } + + // Clean up URI if needed + if (root && strncmp(root, "file://", 7) == 0) + { + char *clean = strdup(root + 7); + free(root); + root = clean; + } + + if (root) + { + lsp_project_init(root); + free(root); + } + else + { + lsp_project_init("."); + } + const char *response = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{" "\"capabilities\":{\"textDocumentSync\":1," "\"definitionProvider\":true,\"hoverProvider\":true," @@ -127,16 +198,14 @@ void handle_request(const char *json_str) return; } - if (strstr(json_str, "\"method\":\"textDocument/didOpen\"") || - strstr(json_str, "\"method\":\"textDocument/didChange\"")) + if (strstr(json_str, "didOpen") || strstr(json_str, "didChange")) { - char *uri = get_json_string(json_str, "uri"); char *text = get_text_content(json_str); if (uri && text) { - fprintf(stderr, "zls: Checking %s\n", uri); + // fprintf(stderr, "zls: Checking %s\n", uri); lsp_check_file(uri, text); } @@ -150,7 +219,7 @@ void handle_request(const char *json_str) } } - if (strstr(json_str, "\"method\":\"textDocument/definition\"")) + if (strstr(json_str, "textDocument/definition")) { char *uri = get_json_string(json_str, "uri"); int line = 0, col = 0; @@ -158,13 +227,12 @@ void handle_request(const char *json_str) if (uri) { - fprintf(stderr, "zls: Definition request at %d:%d\n", line, col); lsp_goto_definition(uri, line, col); free(uri); } } - if (strstr(json_str, "\"method\":\"textDocument/hover\"")) + if (strstr(json_str, "textDocument/hover")) { char *uri = get_json_string(json_str, "uri"); int line = 0, col = 0; @@ -172,13 +240,12 @@ void handle_request(const char *json_str) if (uri) { - fprintf(stderr, "zls: Hover request at %d:%d\n", line, col); lsp_hover(uri, line, col); free(uri); } } - if (strstr(json_str, "\"method\":\"textDocument/completion\"")) + if (strstr(json_str, "textDocument/completion")) { char *uri = get_json_string(json_str, "uri"); int line = 0, col = 0; @@ -186,9 +253,44 @@ void handle_request(const char *json_str) if (uri) { - fprintf(stderr, "zls: Completion request at %d:%d\n", line, col); lsp_completion(uri, line, col); free(uri); } } + + if (strstr(json_str, "textDocument/documentSymbol")) + { + char *uri = get_json_string(json_str, "uri"); + if (uri) + { + lsp_document_symbol(uri); + free(uri); + } + } + + if (strstr(json_str, "textDocument/references")) + { + char *uri = get_json_string(json_str, "uri"); + int line = 0, col = 0; + get_json_position(json_str, &line, &col); + + if (uri) + { + lsp_references(uri, line, col); + free(uri); + } + } + + if (strstr(json_str, "textDocument/signatureHelp")) + { + char *uri = get_json_string(json_str, "uri"); + int line = 0, col = 0; + get_json_position(json_str, &line, &col); + + if (uri) + { + lsp_signature_help(uri, line, col); + free(uri); + } + } } -- cgit v1.2.3