diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-16 12:43:51 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-16 12:43:51 +0000 |
| commit | e77725b55190b8ec6dcab46aa137c32652ea004b (patch) | |
| tree | a80e237f1f65873f908f5819488c1c32683aff74 /src/lsp | |
| parent | 2e1aa3d8853f3b49e93b1d50b1b6e60e8238d79c (diff) | |
Support for 'alias' + test. Improved formatting and '.gitignore'.
Diffstat (limited to 'src/lsp')
| -rw-r--r-- | src/lsp/json_rpc.c | 283 | ||||
| -rw-r--r-- | src/lsp/lsp_analysis.c | 620 | ||||
| -rw-r--r-- | src/lsp/lsp_index.c | 329 | ||||
| -rw-r--r-- | src/lsp/lsp_index.h | 41 | ||||
| -rw-r--r-- | src/lsp/lsp_main.c | 90 |
5 files changed, 749 insertions, 614 deletions
diff --git a/src/lsp/json_rpc.c b/src/lsp/json_rpc.c index c75af95..903da71 100644 --- a/src/lsp/json_rpc.c +++ b/src/lsp/json_rpc.c @@ -5,81 +5,107 @@ #include <string.h> // Basic JSON parsing helpers -char *get_json_string(const char *json, const char *key) { - char search[256]; - sprintf(search, "\"%s\":\"", key); - char *p = strstr(json, search); - if (!p) { - return NULL; - } - p += strlen(search); - char *end = strchr(p, '"'); - if (!end) { - return NULL; - } - int len = end - p; - char *res = malloc(len + 1); - strncpy(res, p, len); - res[len] = 0; - return res; +char *get_json_string(const char *json, const char *key) +{ + char search[256]; + sprintf(search, "\"%s\":\"", key); + char *p = strstr(json, search); + if (!p) + { + return NULL; + } + p += strlen(search); + char *end = strchr(p, '"'); + if (!end) + { + return NULL; + } + int len = end - p; + char *res = malloc(len + 1); + strncpy(res, p, len); + res[len] = 0; + return res; } // Extract nested "text" from params/contentChanges/0/text or // params/textDocument/text This is very hacky for MVP. proper JSON library // needed. -char *get_text_content(const char *json) { - char *p = strstr(json, "\"text\":\""); - if (!p) { - return NULL; - } - p += 8; - - size_t cap = strlen(p); - char *res = malloc(cap + 1); - char *dst = res; - - while (*p) { - if (*p == '\\') { - p++; - if (*p == 'n') { - *dst++ = '\n'; - } else if (*p == 'r') { - *dst++ = '\r'; - } else if (*p == 't') { - *dst++ = '\t'; - } else if (*p == '"') { - *dst++ = '"'; - } else if (*p == '\\') { - *dst++ = '\\'; - } else { - *dst++ = *p; // preserve others - } - p++; - } else if (*p == '"') { - break; // End of string. - } else { - *dst++ = *p++; +char *get_text_content(const char *json) +{ + char *p = strstr(json, "\"text\":\""); + if (!p) + { + return NULL; + } + p += 8; + + size_t cap = strlen(p); + char *res = malloc(cap + 1); + char *dst = res; + + while (*p) + { + if (*p == '\\') + { + p++; + if (*p == 'n') + { + *dst++ = '\n'; + } + else if (*p == 'r') + { + *dst++ = '\r'; + } + else if (*p == 't') + { + *dst++ = '\t'; + } + else if (*p == '"') + { + *dst++ = '"'; + } + else if (*p == '\\') + { + *dst++ = '\\'; + } + else + { + *dst++ = *p; // preserve others + } + p++; + } + else if (*p == '"') + { + break; // End of string. + } + else + { + *dst++ = *p++; + } } - } - *dst = 0; - return res; + *dst = 0; + return res; } // 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) { - *line = atoi(l + 7); - } - char *c = strstr(pos, "\"character\":"); - if (c) { - *col = atoi(c + 12); - } +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) + { + *line = atoi(l + 7); + } + char *c = strstr(pos, "\"character\":"); + if (c) + { + *col = atoi(c + 12); + } } void lsp_check_file(const char *uri, const char *src); @@ -87,71 +113,82 @@ 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 handle_request(const char *json_str) { - if (strstr(json_str, "\"method\":\"initialize\"")) { - const char *response = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{" - "\"capabilities\":{\"textDocumentSync\":1," - "\"definitionProvider\":true,\"hoverProvider\":true," - "\"completionProvider\":{" - "\"triggerCharacters\":[\".\"]}}}}"; - fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(response), - response); - fflush(stdout); - return; - } - - if (strstr(json_str, "\"method\":\"textDocument/didOpen\"") || - strstr(json_str, "\"method\":\"textDocument/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); - lsp_check_file(uri, text); +void handle_request(const char *json_str) +{ + if (strstr(json_str, "\"method\":\"initialize\"")) + { + const char *response = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{" + "\"capabilities\":{\"textDocumentSync\":1," + "\"definitionProvider\":true,\"hoverProvider\":true," + "\"completionProvider\":{" + "\"triggerCharacters\":[\".\"]}}}}"; + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(response), response); + fflush(stdout); + return; } - if (uri) { - free(uri); - } - if (text) { - free(text); + if (strstr(json_str, "\"method\":\"textDocument/didOpen\"") || + strstr(json_str, "\"method\":\"textDocument/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); + lsp_check_file(uri, text); + } + + if (uri) + { + free(uri); + } + if (text) + { + free(text); + } } - } - - if (strstr(json_str, "\"method\":\"textDocument/definition\"")) { - char *uri = get_json_string(json_str, "uri"); - int line = 0, col = 0; - get_json_position(json_str, &line, &col); - 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/definition\"")) + { + char *uri = get_json_string(json_str, "uri"); + int line = 0, col = 0; + get_json_position(json_str, &line, &col); + + 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\"")) { - char *uri = get_json_string(json_str, "uri"); - int line = 0, col = 0; - get_json_position(json_str, &line, &col); - - 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/hover\"")) + { + char *uri = get_json_string(json_str, "uri"); + int line = 0, col = 0; + get_json_position(json_str, &line, &col); + + 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\"")) { - char *uri = get_json_string(json_str, "uri"); - int line = 0, col = 0; - get_json_position(json_str, &line, &col); - if (uri) { - fprintf(stderr, "zls: Completion request at %d:%d\n", line, col); - lsp_completion(uri, line, col); - free(uri); + if (strstr(json_str, "\"method\":\"textDocument/completion\"")) + { + char *uri = get_json_string(json_str, "uri"); + int line = 0, col = 0; + get_json_position(json_str, &line, &col); + + if (uri) + { + fprintf(stderr, "zls: Completion request at %d:%d\n", line, col); + lsp_completion(uri, line, col); + free(uri); + } } - } } diff --git a/src/lsp/lsp_analysis.c b/src/lsp/lsp_analysis.c index 73ba2c5..d455894 100644 --- a/src/lsp/lsp_analysis.c +++ b/src/lsp/lsp_analysis.c @@ -9,335 +9,387 @@ static LSPIndex *g_index = NULL; -typedef struct Diagnostic { - int line; - int col; - char *message; - struct Diagnostic *next; +typedef struct Diagnostic +{ + int line; + int col; + char *message; + struct Diagnostic *next; } Diagnostic; -typedef struct { - Diagnostic *head; - Diagnostic *tail; +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_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; +void lsp_check_file(const char *uri, const char *json_src) +{ + // Prepare ParserContext (persistent). + if (g_ctx) + { - // 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); + 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); - if (g_index) { - lsp_index_free(g_index); - } - g_index = lsp_index_new(); - if (root) { - lsp_build_index(g_index, root); - } + Lexer l; + lexer_init(&l, json_src); - // Construct JSON Response (notification) + ASTNode *root = parse_program(g_ctx, &l); - char *notification = malloc(128 * 1024); - char *p = notification; - p += sprintf( - p, - "{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/" - "publishDiagnostics\",\"params\":{\"uri\":\"%s\",\"diagnostics\":[", - uri); + if (g_index) + { + lsp_index_free(g_index); + } + g_index = lsp_index_new(); + if (root) + { + lsp_build_index(g_index, root); + } - Diagnostic *d = diagnostics.head; - while (d) { + // Construct JSON Response (notification) + char *notification = malloc(128 * 1024); + char *p = notification; 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); + "{\"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, ","); + } - if (d->next) { - p += sprintf(p, ","); + d = d->next; } - d = d->next; - } - - p += sprintf(p, "]}}"); + p += sprintf(p, "]}}"); - // Send notification. - long len = strlen(notification); - fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", len, notification); - fflush(stdout); + // Send notification. + long len = strlen(notification); + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", len, notification); + fflush(stdout); - free(notification); + free(notification); - // Cleanup. - Diagnostic *cur = diagnostics.head; - while (cur) { - Diagnostic *next = cur->next; - free(cur->message); - free(cur); - cur = next; - } + // 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_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; - } +void lsp_hover(const char *uri, int line, int col) +{ + (void)uri; + if (!g_index) + { + return; } - } - 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); + LSPRange *r = lsp_find_at(g_index, line, col); + char *text = NULL; - 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); - } + 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++; +void lsp_completion(const char *uri, int line, int col) +{ + (void)uri; + if (!g_ctx) + { + return; } - 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; + // 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 (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++; + 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--; } - *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; - } + + 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; + } + } - 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; + 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\":["); + char *json = xmalloc(1024 * 1024); // 1MB buffer. + char *p = json; + p += sprintf(p, "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":["); - int first = 1; + int first = 1; - // Functions - FuncSig *f = g_ctx->func_registry; - while (f) { - if (!first) { - p += sprintf(p, ","); + // 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; } - 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, ","); + + // 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, "{\"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); + + p += sprintf(p, "]}"); + + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(json), json); + fflush(stdout); + free(json); } diff --git a/src/lsp/lsp_index.c b/src/lsp/lsp_index.c index fdd0cf8..d952b77 100644 --- a/src/lsp/lsp_index.c +++ b/src/lsp/lsp_index.c @@ -4,168 +4,201 @@ #include <stdlib.h> #include <string.h> -LSPIndex *lsp_index_new() { return calloc(1, sizeof(LSPIndex)); } - -void lsp_index_free(LSPIndex *idx) { - if (!idx) { - return; - } - LSPRange *c = idx->head; - while (c) { - LSPRange *n = c->next; - if (c->hover_text) { - free(c->hover_text); - } - free(c); - c = n; - } - free(idx); +LSPIndex *lsp_index_new() +{ + return calloc(1, sizeof(LSPIndex)); } -void lsp_index_add(LSPIndex *idx, LSPRange *r) { - if (!idx->head) { - idx->head = r; - idx->tail = r; - } else { - idx->tail->next = r; - idx->tail = r; - } +void lsp_index_free(LSPIndex *idx) +{ + if (!idx) + { + return; + } + LSPRange *c = idx->head; + while (c) + { + LSPRange *n = c->next; + if (c->hover_text) + { + free(c->hover_text); + } + free(c); + c = n; + } + free(idx); } -void lsp_index_add_def(LSPIndex *idx, Token t, const char *hover, - ASTNode *node) { - if (t.line <= 0) { - return; - } - LSPRange *r = calloc(1, sizeof(LSPRange)); - r->type = RANGE_DEFINITION; - r->start_line = t.line - 1; - r->start_col = t.col - 1; - r->end_line = t.line - 1; - r->end_col = t.col - 1 + t.len; - if (hover) { - r->hover_text = strdup(hover); - } - r->node = node; - - lsp_index_add(idx, r); +void lsp_index_add(LSPIndex *idx, LSPRange *r) +{ + if (!idx->head) + { + idx->head = r; + idx->tail = r; + } + else + { + idx->tail->next = r; + idx->tail = r; + } } -void lsp_index_add_ref(LSPIndex *idx, Token t, Token def_t, ASTNode *node) { - if (t.line <= 0 || def_t.line <= 0) { - return; - } - LSPRange *r = calloc(1, sizeof(LSPRange)); - r->type = RANGE_REFERENCE; - r->start_line = t.line - 1; - r->start_col = t.col - 1; - r->end_line = t.line - 1; - r->end_col = t.col - 1 + t.len; - - r->def_line = def_t.line - 1; - r->def_col = def_t.col - 1; - r->node = node; - - lsp_index_add(idx, r); -} +void lsp_index_add_def(LSPIndex *idx, Token t, const char *hover, ASTNode *node) +{ + if (t.line <= 0) + { + return; + } + LSPRange *r = calloc(1, sizeof(LSPRange)); + r->type = RANGE_DEFINITION; + r->start_line = t.line - 1; + r->start_col = t.col - 1; + r->end_line = t.line - 1; + r->end_col = t.col - 1 + t.len; + if (hover) + { + r->hover_text = strdup(hover); + } + r->node = node; -LSPRange *lsp_find_at(LSPIndex *idx, int line, int col) { - LSPRange *curr = idx->head; - LSPRange *best = NULL; + lsp_index_add(idx, r); +} - while (curr) { - if (line >= curr->start_line && line <= curr->end_line) { - if (line == curr->start_line && col < curr->start_col) { - curr = curr->next; - continue; - } +void lsp_index_add_ref(LSPIndex *idx, Token t, Token def_t, ASTNode *node) +{ + if (t.line <= 0 || def_t.line <= 0) + { + return; + } + LSPRange *r = calloc(1, sizeof(LSPRange)); + r->type = RANGE_REFERENCE; + r->start_line = t.line - 1; + r->start_col = t.col - 1; + r->end_line = t.line - 1; + r->end_col = t.col - 1 + t.len; + + r->def_line = def_t.line - 1; + r->def_col = def_t.col - 1; + r->node = node; + + lsp_index_add(idx, r); +} - if (line == curr->end_line && col > curr->end_col) { +LSPRange *lsp_find_at(LSPIndex *idx, int line, int col) +{ + LSPRange *curr = idx->head; + LSPRange *best = NULL; + + while (curr) + { + if (line >= curr->start_line && line <= curr->end_line) + { + if (line == curr->start_line && col < curr->start_col) + { + curr = curr->next; + continue; + } + + if (line == curr->end_line && col > curr->end_col) + { + curr = curr->next; + continue; + } + + best = curr; + } curr = curr->next; - continue; - } - - best = curr; } - curr = curr->next; - } - return best; + return best; } // Walker. -void lsp_walk_node(LSPIndex *idx, ASTNode *node) { - if (!node) { - return; - } - - // Definition logic. - if (node->type == NODE_FUNCTION) { - char hover[256]; - sprintf(hover, "fn %s(...) -> %s", node->func.name, - node->func.ret_type ? node->func.ret_type : "void"); - lsp_index_add_def(idx, node->token, hover, node); - - // Recurse body. - lsp_walk_node(idx, node->func.body); - } else if (node->type == NODE_VAR_DECL) { - char hover[256]; - sprintf(hover, "var %s", node->var_decl.name); - lsp_index_add_def(idx, node->token, hover, node); - - lsp_walk_node(idx, node->var_decl.init_expr); - } else if (node->type == NODE_CONST) { - char hover[256]; - sprintf(hover, "const %s", node->var_decl.name); - lsp_index_add_def(idx, node->token, hover, node); - - lsp_walk_node(idx, node->var_decl.init_expr); - } - - // Reference logic. - if (node->definition_token.line > 0 && - node->definition_token.line != node->token.line) { - // It has a definition! - lsp_index_add_ref(idx, node->token, node->definition_token, node); - } else if (node->definition_token.line > 0) { - lsp_index_add_ref(idx, node->token, node->definition_token, node); - } - - // General recursion. - - switch (node->type) { - case NODE_ROOT: - lsp_walk_node(idx, node->root.children); - break; - case NODE_BLOCK: - lsp_walk_node(idx, node->block.statements); - break; - case NODE_IF: - lsp_walk_node(idx, node->if_stmt.condition); - lsp_walk_node(idx, node->if_stmt.then_body); - lsp_walk_node(idx, node->if_stmt.else_body); - break; - case NODE_WHILE: - lsp_walk_node(idx, node->while_stmt.condition); - lsp_walk_node(idx, node->while_stmt.body); - break; - case NODE_RETURN: - lsp_walk_node(idx, node->ret.value); - break; - case NODE_EXPR_BINARY: - lsp_walk_node(idx, node->binary.left); - lsp_walk_node(idx, node->binary.right); - break; - case NODE_EXPR_CALL: - lsp_walk_node(idx, node->call.callee); - lsp_walk_node(idx, node->call.args); - break; - default: - break; - } - - // Walk next sibling. - lsp_walk_node(idx, node->next); +void lsp_walk_node(LSPIndex *idx, ASTNode *node) +{ + if (!node) + { + return; + } + + // Definition logic. + if (node->type == NODE_FUNCTION) + { + char hover[256]; + sprintf(hover, "fn %s(...) -> %s", node->func.name, + node->func.ret_type ? node->func.ret_type : "void"); + lsp_index_add_def(idx, node->token, hover, node); + + // Recurse body. + lsp_walk_node(idx, node->func.body); + } + else if (node->type == NODE_VAR_DECL) + { + char hover[256]; + sprintf(hover, "var %s", node->var_decl.name); + lsp_index_add_def(idx, node->token, hover, node); + + lsp_walk_node(idx, node->var_decl.init_expr); + } + else if (node->type == NODE_CONST) + { + char hover[256]; + sprintf(hover, "const %s", node->var_decl.name); + lsp_index_add_def(idx, node->token, hover, node); + + lsp_walk_node(idx, node->var_decl.init_expr); + } + + // Reference logic. + if (node->definition_token.line > 0 && node->definition_token.line != node->token.line) + { + // It has a definition! + lsp_index_add_ref(idx, node->token, node->definition_token, node); + } + else if (node->definition_token.line > 0) + { + lsp_index_add_ref(idx, node->token, node->definition_token, node); + } + + // General recursion. + + switch (node->type) + { + case NODE_ROOT: + lsp_walk_node(idx, node->root.children); + break; + case NODE_BLOCK: + lsp_walk_node(idx, node->block.statements); + break; + case NODE_IF: + lsp_walk_node(idx, node->if_stmt.condition); + lsp_walk_node(idx, node->if_stmt.then_body); + lsp_walk_node(idx, node->if_stmt.else_body); + break; + case NODE_WHILE: + lsp_walk_node(idx, node->while_stmt.condition); + lsp_walk_node(idx, node->while_stmt.body); + break; + case NODE_RETURN: + lsp_walk_node(idx, node->ret.value); + break; + case NODE_EXPR_BINARY: + lsp_walk_node(idx, node->binary.left); + lsp_walk_node(idx, node->binary.right); + break; + case NODE_EXPR_CALL: + lsp_walk_node(idx, node->call.callee); + lsp_walk_node(idx, node->call.args); + break; + default: + break; + } + + // Walk next sibling. + lsp_walk_node(idx, node->next); } -void lsp_build_index(LSPIndex *idx, ASTNode *root) { lsp_walk_node(idx, root); } +void lsp_build_index(LSPIndex *idx, ASTNode *root) +{ + lsp_walk_node(idx, root); +} diff --git a/src/lsp/lsp_index.h b/src/lsp/lsp_index.h index 8b8207b..f4aaf96 100644 --- a/src/lsp/lsp_index.h +++ b/src/lsp/lsp_index.h @@ -4,31 +4,36 @@ #include "parser.h" -typedef enum { RANGE_DEFINITION, RANGE_REFERENCE } RangeType; - -typedef struct LSPRange { - int start_line; - int start_col; - int end_line; - int end_col; // Approximation. - RangeType type; - int def_line; - int def_col; - char *hover_text; - ASTNode *node; - struct LSPRange *next; +typedef enum +{ + RANGE_DEFINITION, + RANGE_REFERENCE +} RangeType; + +typedef struct LSPRange +{ + int start_line; + int start_col; + int end_line; + int end_col; // Approximation. + RangeType type; + int def_line; + int def_col; + char *hover_text; + ASTNode *node; + struct LSPRange *next; } LSPRange; -typedef struct LSPIndex { - LSPRange *head; - LSPRange *tail; +typedef struct LSPIndex +{ + LSPRange *head; + LSPRange *tail; } LSPIndex; // API. LSPIndex *lsp_index_new(); void lsp_index_free(LSPIndex *idx); -void lsp_index_add_def(LSPIndex *idx, Token t, const char *hover, - ASTNode *node); +void lsp_index_add_def(LSPIndex *idx, Token t, const char *hover, ASTNode *node); void lsp_index_add_ref(LSPIndex *idx, Token t, Token def_t, ASTNode *node); LSPRange *lsp_find_at(LSPIndex *idx, int line, int col); diff --git a/src/lsp/lsp_main.c b/src/lsp/lsp_main.c index a9a9f06..fbe5312 100644 --- a/src/lsp/lsp_main.c +++ b/src/lsp/lsp_main.c @@ -6,47 +6,55 @@ #include <unistd.h> // Simple Main Loop for LSP. -int lsp_main(int argc, char **argv) { - (void)argc; - (void)argv; - fprintf(stderr, "zls: Zen Language Server starting...\n"); - - while (1) { - // Read headers - int content_len = 0; - char line[512]; - while (fgets(line, sizeof(line), stdin)) { - if (0 == strcmp(line, "\r\n")) { - break; // End of headers - } - if (0 == strncmp(line, "Content-Length: ", 16)) { - content_len = atoi(line + 16); - } +int lsp_main(int argc, char **argv) +{ + (void)argc; + (void)argv; + fprintf(stderr, "zls: Zen Language Server starting...\n"); + + while (1) + { + // Read headers + int content_len = 0; + char line[512]; + while (fgets(line, sizeof(line), stdin)) + { + if (0 == strcmp(line, "\r\n")) + { + break; // End of headers + } + if (0 == strncmp(line, "Content-Length: ", 16)) + { + content_len = atoi(line + 16); + } + } + + if (content_len <= 0) + { + // Maybe EOF or error? + if (feof(stdin)) + { + break; + } + continue; // Wait for more (yeah we gotta work on this). + } + + // Read body. + char *body = malloc(content_len + 1); + if (fread(body, 1, content_len, stdin) != (size_t)content_len) + { + fprintf(stderr, "zls: Error reading body\n"); + free(body); + break; + } + body[content_len] = 0; + + // Process JSON-RPC. + fprintf(stderr, "zls: Received: %s\n", body); + handle_request(body); + + free(body); } - if (content_len <= 0) { - // Maybe EOF or error? - if (feof(stdin)) { - break; - } - continue; // Wait for more (yeah we gotta work on this). - } - - // Read body. - char *body = malloc(content_len + 1); - if (fread(body, 1, content_len, stdin) != (size_t)content_len) { - fprintf(stderr, "zls: Error reading body\n"); - free(body); - break; - } - body[content_len] = 0; - - // Process JSON-RPC. - fprintf(stderr, "zls: Received: %s\n", body); - handle_request(body); - - free(body); - } - - return 0; + return 0; } |
