summaryrefslogtreecommitdiff
path: root/src/lsp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lsp')
-rw-r--r--src/lsp/json_rpc.c283
-rw-r--r--src/lsp/lsp_analysis.c620
-rw-r--r--src/lsp/lsp_index.c329
-rw-r--r--src/lsp/lsp_index.h41
-rw-r--r--src/lsp/lsp_main.c90
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;
}