summaryrefslogtreecommitdiff
path: root/src/lsp/json_rpc.c
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-20 12:51:23 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-20 12:51:23 +0000
commitb106fe19b55e9fe3348b3a5c9992c21dac27b02c (patch)
tree32663c9a791b7f45ce9bc61c5a87351d5dc2c782 /src/lsp/json_rpc.c
parente5d8c4219cfe5629a3ce4dbff01406a1817a788f (diff)
Working a bit on the LSP + fixed some bugs
Diffstat (limited to 'src/lsp/json_rpc.c')
-rw-r--r--src/lsp/json_rpc.c234
1 files changed, 168 insertions, 66 deletions
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <ctype.h>
-// 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);
+ }
+ }
}