popup and diagnose ui

This commit is contained in:
2026-06-02 13:00:13 +02:00
parent a8b2960eb4
commit 2738bc8042
25 changed files with 587 additions and 461 deletions
+121 -82
View File
@@ -13,23 +13,10 @@
#include "include/cJSON.h"
#include "include/data.h"
#include "include/lsp_ui.h"
#include "include/split_screen.h"
#include "include/terminal.h"
#include "include/utils.h"
void createContextBuffer(const int x, const int y, const char* text)
{
E.context_buffers = bAlloc(sizeof(ContextBuffer));
ContextBuffer* buffer = E.context_buffers;
buffer->editor_x = x;
buffer->editor_y = y;
buffer->height = 1;
buffer->rows = bAlloc(sizeof(struct row));
if (!buffer->rows) return;
buffer->rows[0].chars = strdup(text);
buffer->rows[0].size = strlen(text);
buffer->width = strlen(text);
}
static void lsp_send(int fd, const char* json)
{
int body_len = strlen(json);
@@ -42,7 +29,7 @@ static void lsp_send(int fd, const char* json)
write(fd, json, body_len);
// Log to stderr for debugging
fprintf(stderr, "[LSP →] Content-Length: %d | %s\n", body_len, json);
appDebug("[LSP →] Content-Length: %d | %s\n", body_len, json);
fflush(stderr);
}
@@ -55,10 +42,8 @@ static int lsp_uri_to_buffer_id(const char* uri)
for (int i = 0; i < E.number_of_buffer; i++) {
if (E.buffers[i].filename == NULL) continue;
char abs[PATH_MAX];
realpath(E.buffers[i].filename, abs);
fprintf(stderr, "[URI MATCH] comparing '%s' vs '%s'\n", abs, path);
if (strcmp(abs, path) == 0)
appDebug("[URI MATCH] comparing '%s' vs '%s'\n", E.buffers[i].fullname, path);
if (strcmp(E.buffers[i].fullname, path) == 0)
return E.buffers[i].buffer_id;
}
return -1;
@@ -112,7 +97,7 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
cJSON* root = cJSON_Parse(json);
if (!root)
{
fprintf(stderr, "[LSP ←] Failed to parse JSON: %.120s\n", json);
appDebug("[LSP ←] Failed to parse JSON: %.120s\n", json);
return;
}
@@ -125,7 +110,7 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
if (error)
{
cJSON* msg = cJSON_GetObjectItem(error, "message");
fprintf(stderr, "[LSP ←] ERROR: %s\n",
appDebug("[LSP ←] ERROR: %s\n",
msg ? msg->valuestring : "(no message)");
cJSON_Delete(root);
return;
@@ -135,17 +120,16 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
if (method && !id)
{
const char* m = method->valuestring;
fprintf(stderr, "[LSP ←] NOTIF: %s\n", m);
appDebug("[LSP ←] NOTIF: %s\n", m);
if (strcmp(m, "textDocument/publishDiagnostics") == 0)
{
// Find which buffer this diagnostic belongs to
cJSON* params = cJSON_GetObjectItem(root, "params");
cJSON* uri = cJSON_GetObjectItem(params, "uri");
int buf_id = lsp_uri_to_buffer_id(
uri ? uri->valuestring : "");
int buf_id = splitScreenGetActivePane()->buffer_id;
fprintf(stderr, "[LSP ←] Diagnostics for buffer %d\n", buf_id);
appDebug("[LSP ←] Diagnostics for buffer %d\n", buf_id);
pthread_mutex_lock(&lsp->lock);
lspParseDiagnostics(json, &E.lsp_diagnostics, buf_id);
@@ -157,7 +141,7 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
{
cJSON* params = cJSON_GetObjectItem(root, "params");
cJSON* message = cJSON_GetObjectItem(params, "message");
fprintf(stderr, "[LSP ←] LOG: %s\n",
appDebug("[LSP ←] LOG: %s\n",
message ? message->valuestring : "");
}
E.lsp_client->completion_just_arrived = 1;
@@ -171,12 +155,12 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
if (id && result)
{
int response_id = id->valueint;
fprintf(stderr, "[LSP ←] RESPONSE id=%d\n", response_id);
appDebug("[LSP ←] RESPONSE id=%d\n", response_id);
// initialize response → send initialized + mark ready
if (lsp->state == LSP_INITIALIZING)
{
fprintf(stderr, "[LSP ←] Initialize OK, sending initialized\n");
appDebug("[LSP ←] Initialize OK, sending initialized\n");
lsp_send(lsp->write_fd,
"{\"jsonrpc\":\"2.0\",\"method\":\"initialized\",\"params\":{}}");
@@ -195,7 +179,7 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
if (items && cJSON_IsArray(items))
{
int count = cJSON_GetArraySize(items);
fprintf(stderr, "[LSP ←] Completion: %d items\n", count);
appDebug("[LSP ←] Completion: %d items\n", count);
// Print each item to stderr for debugging
cJSON* item;
@@ -205,14 +189,14 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
cJSON* label = cJSON_GetObjectItem(item, "label");
cJSON* detail = cJSON_GetObjectItem(item, "detail");
cJSON* kind = cJSON_GetObjectItem(item, "kind");
fprintf(stderr, " [%d] kind=%-2d %-40s %s\n",
appDebug(" [%d] kind=%-2d %-40s %s\n",
i++,
kind ? kind->valueint : 0,
label ? label->valuestring : "(no label)",
detail ? detail->valuestring : "");
if (i >= 10)
{
fprintf(stderr, " ... (%d more)\n", count - 10);
appDebug(" ... (%d more)\n", count - 10);
break;
}
}
@@ -224,7 +208,7 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
pthread_mutex_unlock(&lsp->lock);
E.lsp_client->completion_just_arrived = 1;
fprintf(stderr, "[POPUP] visible=%d count=%d origin=(%d,%d)\n",
appDebug("[POPUP] visible=%d count=%d origin=(%d,%d)\n",
E.lsp_completion.visible,
E.lsp_completion.count,
E.lsp_completion.origin_x,
@@ -243,7 +227,7 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
cJSON* start = cJSON_GetObjectItem(range, "start");
int line = cJSON_GetObjectItem(start, "line")->valueint;
int col = cJSON_GetObjectItem(start, "character")->valueint;
fprintf(stderr, "[LSP ←] Definition: %s:%d:%d\n",
appDebug("[LSP ←] Definition: %s:%d:%d\n",
uri_item->valuestring, line, col);
E.lsp_client->completion_just_arrived = 1;
@@ -252,7 +236,7 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
return;
}
fprintf(stderr, "[LSP ←] Unhandled response id=%d: %.80s\n",
appDebug("[LSP ←] Unhandled response id=%d: %.80s\n",
response_id, json);
}
@@ -274,63 +258,122 @@ static void* lsp_reader(void* arg)
// ─── lifecycle ───────────────────────────────────────────────────────────────
int lspStart(LspClient* lsp, const char* project_root)
int lspStart(LspClient *lsp, const char *project_root)
{
// ── Pipes ─────────────────────────────────────────────────────────────────
int to_clangd[2], from_clangd[2];
pipe(to_clangd);
pipe(from_clangd);
pipe(lsp->wake_pipe);
if (pipe(to_clangd) < 0 || pipe(from_clangd) < 0) {
fprintf(stderr, "[LSP] pipe() failed\n");
free(lsp);
return 0;
}
if (pipe(lsp->wake_pipe) < 0) {
fprintf(stderr, "[LSP] wake pipe() failed\n");
free(lsp);
return 0;
}
// ── Fork clangd ───────────────────────────────────────────────────────────
lsp->pid = fork();
if (lsp->pid == 0)
{
// Child: become clangd
dup2(to_clangd[0], STDIN_FILENO);
if (lsp->pid < 0) {
fprintf(stderr, "[LSP] fork() failed\n");
free(lsp);
return 0;
}
if (lsp->pid == 0) {
// Child — become clangd
dup2(to_clangd[0], STDIN_FILENO);
dup2(from_clangd[1], STDOUT_FILENO);
close(to_clangd[1]);
close(from_clangd[0]);
execlp("clangd", "clangd", "--log=error", "--completion-style=detailed", NULL);
_exit(1); // clangd not found
close(lsp->wake_pipe[0]);
close(lsp->wake_pipe[1]);
execlp("clangd", "clangd",
"--log=error",
"--completion-style=detailed",
NULL);
fprintf(stderr, "[LSP] execlp failed — is clangd installed?\n");
_exit(1);
}
// Parent — keep write end of to_clangd, read end of from_clangd
close(to_clangd[0]);
close(from_clangd[1]);
lsp->write_fd = to_clangd[1];
lsp->read_fd = from_clangd[0];
lsp->next_id = 1;
lsp->state = LSP_INITIALIZING;
pthread_mutex_init(&lsp->lock, NULL);
lsp->read_fd = from_clangd[0];
lsp->next_id = 1;
lsp->state = LSP_INITIALIZING;
// Send initialize
char buf[1024];
snprintf(buf, sizeof(buf),
"{\"jsonrpc\":\"2.0\",\"id\":%d,\"method\":\"initialize\","
"\"params\":{\"processId\":%d,\"rootUri\":\"file://%s\","
"\"capabilities\":{"
"\"textDocument\":{"
"\"completion\":{\"completionItem\":{\"snippetSupport\":false}},"
"\"hover\":{},"
"\"definition\":{},"
"\"publishDiagnostics\":{}"
"}"
"}}}",
lsp->next_id++, getpid(), project_root);
// ── Threading ─────────────────────────────────────────────────────────────
pthread_mutex_init(&lsp->lock, NULL);
pthread_cond_init (&lsp->ready_cond, NULL);
pthread_mutex_init(&lsp->lock, NULL);
pthread_cond_init(&lsp->ready_cond, NULL);
// Start reader thread BEFORE sending initialize
// so it can handle the response
pthread_create(&lsp->reader_thread, NULL, lsp_reader, lsp);
// ── Send initialize ───────────────────────────────────────────────────────
char abs_root[PATH_MAX];
if (realpath(project_root, abs_root) == NULL)
strncpy(abs_root, project_root, PATH_MAX - 1);
lsp_send(lsp->write_fd, buf);
cJSON *req = cJSON_CreateObject();
cJSON *params = cJSON_CreateObject();
cJSON *caps = cJSON_CreateObject();
cJSON *td_caps = cJSON_CreateObject();
cJSON *comp_caps = cJSON_CreateObject();
cJSON *comp_item = cJSON_CreateObject();
cJSON_AddStringToObject(req, "jsonrpc", "2.0");
cJSON_AddNumberToObject(req, "id", lsp->next_id++);
cJSON_AddStringToObject(req, "method", "initialize");
// rootUri
char root_uri[PATH_MAX + 8];
snprintf(root_uri, sizeof(root_uri), "file://%s", abs_root);
cJSON_AddNumberToObject(params, "processId", getpid());
cJSON_AddStringToObject(params, "rootUri", root_uri);
// Capabilities — tell clangd what we support
cJSON_AddBoolToObject (comp_item, "snippetSupport", 0);
cJSON_AddBoolToObject (comp_item, "commitCharactersSupport", 0);
cJSON_AddItemToObject (comp_caps, "completionItem", comp_item);
cJSON_AddItemToObject (td_caps, "completion", comp_caps);
cJSON_AddItemToObject (td_caps, "hover", cJSON_CreateObject());
cJSON_AddItemToObject (td_caps, "definition", cJSON_CreateObject());
cJSON_AddItemToObject (td_caps, "publishDiagnostics", cJSON_CreateObject());
cJSON_AddItemToObject (caps, "textDocument", td_caps);
cJSON_AddItemToObject (params, "capabilities", caps);
cJSON_AddItemToObject (req, "params", params);
char *msg = cJSON_PrintUnformatted(req);
lsp_send(lsp->write_fd, msg);
free(msg);
cJSON_Delete(req);
// ── Wait for LSP_READY ────────────────────────────────────────────────────
// Reader thread will handle the initialize response,
// send "initialized", and signal ready_cond
pthread_mutex_lock(&lsp->lock);
while (lsp->state != LSP_READY)
pthread_cond_wait(&lsp->ready_cond, &lsp->lock);
while (lsp->state != LSP_READY) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 5; // 5 second timeout — clangd should respond fast
int rc = pthread_cond_timedwait(&lsp->ready_cond, &lsp->lock, &ts);
if (rc == ETIMEDOUT) {
fprintf(stderr, "[LSP] timeout waiting for initialize response\n");
pthread_mutex_unlock(&lsp->lock);
// Don't kill clangd — it might still come up, just return what we have
return 1;
}
}
pthread_mutex_unlock(&lsp->lock);
return 0;
}
fprintf(stderr, "[LSP] ready — clangd initialized at %s\n", abs_root);
return 1;
}
// ─── document sync ───────────────────────────────────────────────────────────
// Build the full buffer text into a bAlloc'd string
@@ -355,10 +398,9 @@ void lspDidOpen(LspClient* lsp, struct buffer_t* buf)
{
if (lsp->state != LSP_READY || buf->b_lsp_open) return;
char abs[PATH_MAX];
realpath(buf->filename, abs);
char uri[PATH_MAX + 8];
snprintf(uri, sizeof(uri), "file://%s", abs);
snprintf(uri, sizeof(uri), "file://%s", buf->fullname);
const char* lang = "c";
if (strstr(buf->filename, ".cpp") || strstr(buf->filename, ".cc"))
@@ -395,10 +437,9 @@ void lspDidChange(LspClient* lsp, struct buffer_t* buf)
{
if (lsp->state != LSP_READY || !buf->b_lsp_open) return;
char abs[PATH_MAX];
realpath(buf->filename, abs);
char uri[PATH_MAX + 8];
snprintf(uri, sizeof(uri), "file://%s", abs);
snprintf(uri, sizeof(uri), "file://%s", buf->fullname);
char* raw = buffer_to_text(buf);
@@ -431,10 +472,9 @@ void lspDidChange(LspClient* lsp, struct buffer_t* buf)
void lspDidClose(LspClient* lsp, struct buffer_t* buf)
{
char abs[PATH_MAX];
realpath(buf->filename, abs);
char uri[PATH_MAX + 8];
snprintf(uri, sizeof(uri), "file://%s", abs);
snprintf(uri, sizeof(uri), "file://%s", buf->fullname);
cJSON* root = cJSON_CreateObject();
cJSON* params = cJSON_CreateObject();
@@ -458,16 +498,15 @@ void lspRequestCompletion(LspClient* lsp, struct buffer_t* buf,
int screen_x, int screen_y)
{
if (lsp->state != LSP_READY) return;
lsp->completion_cursor_x = screen_x; // ← add
lsp->completion_cursor_x = screen_x;
lsp->completion_cursor_y = screen_y;
appDebug("LSP REQUEST COMP");
char* msg;
char abs[PATH_MAX];
realpath(buf->filename, abs);
char uri[PATH_MAX + 8];
snprintf(uri, sizeof(uri), "file://%s", abs);
snprintf(uri, sizeof(uri), "file://%s", buf->fullname);
appDebug("FULLNAME : %s\n", buf->fullname);
cJSON* req = cJSON_CreateObject();
cJSON* params = cJSON_CreateObject();