diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-11 15:11:00 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-11 15:11:00 +0000 |
| commit | 55247a3f12a9eee7ba3fd7ca6d8fcea7a82c20f3 (patch) | |
| tree | a2a71e2eb8ca0b2c483518c1902d89d18709c9ab /src/lsp/lsp_analysis.c | |
| parent | 2e7abed7cfe84a2c0df371cde35f8f68cfdca16c (diff) | |
Added src/ folder. Now I will add the rest.
Diffstat (limited to 'src/lsp/lsp_analysis.c')
| -rw-r--r-- | src/lsp/lsp_analysis.c | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/src/lsp/lsp_analysis.c b/src/lsp/lsp_analysis.c new file mode 100644 index 0000000..7a22906 --- /dev/null +++ b/src/lsp/lsp_analysis.c @@ -0,0 +1,390 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "parser.h" +#include "parser.h" +#include "json_rpc.h" +#include "lsp_index.h" + +static LSPIndex *g_index = NULL; + +typedef struct Diagnostic +{ + int line; + int col; + char *message; + struct Diagnostic *next; +} Diagnostic; + +typedef struct +{ + Diagnostic *head; + Diagnostic *tail; +} DiagnosticList; + +static ParserContext *g_ctx = NULL; +static char *g_last_src = NULL; + +// Callback for parser errors. +void lsp_on_error(void *data, Token t, const char *msg) +{ + DiagnosticList *list = (DiagnosticList *)data; + Diagnostic *d = xmalloc(sizeof(Diagnostic)); + d->line = t.line > 0 ? t.line - 1 : 0; + d->col = t.col > 0 ? t.col - 1 : 0; + d->message = xstrdup(msg); + d->next = NULL; + + if (!list->head) + { + list->head = d; + list->tail = d; + } + else + { + list->tail->next = d; + list->tail = d; + } +} + +void lsp_check_file(const char *uri, const char *json_src) +{ + // Prepare ParserContext (persistent). + if (g_ctx) + { + + free(g_ctx); + } + g_ctx = calloc(1, sizeof(ParserContext)); + g_ctx->is_fault_tolerant = 1; + + DiagnosticList diagnostics = {0}; + g_ctx->error_callback_data = &diagnostics; + g_ctx->on_error = lsp_on_error; + + // Prepare Lexer. + // Cache source. + if (g_last_src) + { + free(g_last_src); + } + g_last_src = strdup(json_src); + + Lexer l; + lexer_init(&l, json_src); + + ASTNode *root = parse_program(g_ctx, &l); + + if (g_index) + { + lsp_index_free(g_index); + } + g_index = lsp_index_new(); + if (root) + { + lsp_build_index(g_index, root); + } + + // Construct JSON Response (notification) + + char *notification = malloc(128 * 1024); + char *p = notification; + p += sprintf(p, + "{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/" + "publishDiagnostics\",\"params\":{\"uri\":\"%s\",\"diagnostics\":[", + uri); + + Diagnostic *d = diagnostics.head; + while (d) + { + + p += sprintf(p, + "{\"range\":{\"start\":{\"line\":%d,\"character\":%d},\"end\":{\"line\":%d," + "\"character\":%d}},\"severity\":1,\"message\":\"%s\"}", + d->line, d->col, d->line, d->col + 1, d->message); + + if (d->next) + { + p += sprintf(p, ","); + } + + d = d->next; + } + + p += sprintf(p, "]}}"); + + // Send notification. + long len = strlen(notification); + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", len, notification); + fflush(stdout); + + free(notification); + + // Cleanup. + Diagnostic *cur = diagnostics.head; + while (cur) + { + Diagnostic *next = cur->next; + free(cur->message); + free(cur); + cur = next; + } +} + +void lsp_goto_definition(const char *uri, int line, int col) +{ + if (!g_index) + { + return; + } + + LSPRange *r = lsp_find_at(g_index, line, col); + if (r && r->type == RANGE_REFERENCE) + { + // Found reference, return definition + char resp[1024]; + sprintf(resp, + "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"uri\":\"%s\",\"range\":{\"start\":{" + "\"line\":%d,\"character\":%d},\"end\":{\"line\":%d,\"character\":%d}}}}", + uri, r->def_line, r->def_col, r->def_line, r->def_col); + + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(resp), resp); + fflush(stdout); + } + else if (r && r->type == RANGE_DEFINITION) + { + // Already at definition? Return itself. + char resp[1024]; + sprintf(resp, + "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"uri\":\"%s\",\"range\":{\"start\":{" + "\"line\":%d,\"character\":%d},\"end\":{\"line\":%d,\"character\":%d}}}}", + uri, r->start_line, r->start_col, r->end_line, r->end_col); + + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(resp), resp); + fflush(stdout); + } + else + { + // Null result + const char *null_resp = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":null}"; + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(null_resp), null_resp); + fflush(stdout); + } +} + +void lsp_hover(const char *uri, int line, int col) +{ + (void)uri; + if (!g_index) + { + return; + } + + LSPRange *r = lsp_find_at(g_index, line, col); + char *text = NULL; + + if (r) + { + if (r->type == RANGE_DEFINITION) + { + text = r->hover_text; + } + else if (r->type == RANGE_REFERENCE) + { + LSPRange *def = lsp_find_at(g_index, r->def_line, r->def_col); + if (def && def->type == RANGE_DEFINITION) + { + text = def->hover_text; + } + } + } + + if (text) + { + char *json = malloc(16384); + // content: { kind: markdown, value: text } + sprintf(json, + "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"contents\":{\"kind\":\"markdown\"," + "\"value\":\"```c\\n%s\\n```\"}}}", + text); + + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(json), json); + fflush(stdout); + free(json); + } + else + { + const char *null_resp = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":null}"; + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(null_resp), null_resp); + fflush(stdout); + } +} + +void lsp_completion(const char *uri, int line, int col) +{ + (void)uri; + if (!g_ctx) + { + return; + } + + // Context-aware completion (Dot access) + if (g_last_src) + { + // Simple line access + int cur_line = 0; + + char *ptr = g_last_src; + // Fast forward to line + while (*ptr && cur_line < line) + { + if (*ptr == '\n') + { + cur_line++; + } + ptr++; + } + + if (col > 0 && ptr[col - 1] == '.') + { + // Found dot! + // Scan backwards for identifier: [whitespace] [ident] . + int i = col - 2; + while (i >= 0 && (ptr[i] == ' ' || ptr[i] == '\t')) + { + i--; + } + + if (i >= 0) + { + int end_ident = i; + while (i >= 0 && (isalnum(ptr[i]) || ptr[i] == '_')) + { + i--; + } + int start_ident = i + 1; + + if (start_ident <= end_ident) + { + int len = end_ident - start_ident + 1; + char var_name[256]; + strncpy(var_name, ptr + start_ident, len); + var_name[len] = 0; + + char *type_name = NULL; + Symbol *sym = find_symbol_in_all(g_ctx, var_name); + + if (sym) + { + if (sym->type_info) + { + type_name = type_to_string(sym->type_info); + } + else if (sym->type_name) + { + type_name = sym->type_name; + } + } + + if (type_name) + { + char clean_name[256]; + char *src = type_name; + if (0 == strncmp(src, "struct ", 7)) + { + src += 7; + } + char *dst = clean_name; + while (*src && *src != '*') + { + *dst++ = *src++; + } + *dst = 0; + + // Lookup struct. + StructDef *sd = g_ctx->struct_defs; + while (sd) + { + if (0 == strcmp(sd->name, clean_name)) + { + char *json_fields = malloc(1024 * 1024); + char *pj = json_fields; + pj += sprintf(pj, "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":["); + + int ffirst = 1; + if (sd->node && sd->node->strct.fields) + { + ASTNode *field = sd->node->strct.fields; + while (field) + { + if (!ffirst) + { + pj += sprintf(pj, ","); + } + pj += sprintf( + pj, + "{\"label\":\"%s\",\"kind\":5,\"detail\":\"field %s\"}", + field->field.name, field->field.type); // Kind 5 = Field + ffirst = 0; + field = field->next; + } + } + + pj += sprintf(pj, "]}"); + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", + strlen(json_fields), json_fields); + fflush(stdout); + free(json_fields); + free(json); + return; // Done, yippee. + } + sd = sd->next; + } + } + } + } + } + } + + char *json = xmalloc(1024 * 1024); // 1MB buffer. + char *p = json; + p += sprintf(p, "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":["); + + int first = 1; + + // Functions + FuncSig *f = g_ctx->func_registry; + while (f) + { + if (!first) + { + p += sprintf(p, ","); + } + p += sprintf(p, "{\"label\":\"%s\",\"kind\":3,\"detail\":\"fn %s(...)\"}", f->name, + f->name); // Kind 3 = Function + first = 0; + f = f->next; + } + + // Structs + StructDef *s = g_ctx->struct_defs; + while (s) + { + if (!first) + { + p += sprintf(p, ","); + } + p += sprintf(p, "{\"label\":\"%s\",\"kind\":22,\"detail\":\"struct %s\"}", s->name, + s->name); // Kind 22 = Struct + first = 0; + s = s->next; + } + + p += sprintf(p, "]}"); + + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(json), json); + fflush(stdout); + free(json); +} |
