#include "../libs/cjson/cJSON.h" #include #include #include #include #include #include #include #include #include int lsp_stdin_fd = -1; int lsp_stdout_fd = -1; int cols; void start_lsp(const char *cmd, char *const argv[]) { int in_pipe[2]; int out_pipe[2]; if (pipe(in_pipe) == -1 || pipe(out_pipe) == -1) { perror("pipe"); exit(1); } pid_t pid = fork(); if (pid == -1) { perror("fork"); exit(1); } if (pid == 0) { dup2(in_pipe[0], STDIN_FILENO); dup2(out_pipe[1], STDOUT_FILENO); int devnull = open("/dev/null", O_WRONLY); if (devnull != -1) { dup2(devnull, STDERR_FILENO); close(devnull); } close(in_pipe[1]); close(out_pipe[0]); execvp(cmd, argv); perror("execvp"); _exit(1); } close(in_pipe[0]); close(out_pipe[1]); lsp_stdin_fd = in_pipe[1]; lsp_stdout_fd = out_pipe[0]; } void lsp_send(cJSON *msg) { char *dump = cJSON_PrintUnformatted(msg); int len = strlen(dump); dprintf(lsp_stdin_fd, "Content-Length: %d\r\n\r\n%s", len, dump); free(dump); } cJSON *lsp_recv() { char header[256]; int content_length = 0; char c; int pos = 0; int newlines = 0; while (read(lsp_stdout_fd, &c, 1) == 1) { header[pos++] = c; if (pos >= sizeof(header) - 1) break; if (c == '\n') { header[pos] = '\0'; if (strncmp(header, "Content-Length:", 15) == 0) { content_length = atoi(header + 15); } if (strcmp(header, "\r\n") == 0) break; pos = 0; } } if (content_length <= 0) return NULL; char *buf = malloc(content_length + 1); if (!buf) return NULL; int got = 0; while (got < content_length) { int n = read(lsp_stdout_fd, buf + got, content_length - got); if (n <= 0) break; got += n; } buf[got] = '\0'; cJSON *msg = cJSON_Parse(buf); free(buf); return msg; } cJSON *make_fold_request(int id, const char *uri) { cJSON *root = cJSON_CreateObject(); cJSON_AddStringToObject(root, "jsonrpc", "2.0"); cJSON_AddNumberToObject(root, "id", id); cJSON_AddStringToObject(root, "method", "textDocument/foldingRange"); cJSON *params = cJSON_CreateObject(); cJSON *doc = cJSON_CreateObject(); cJSON_AddStringToObject(doc, "uri", uri); cJSON_AddItemToObject(params, "textDocument", doc); cJSON_AddItemToObject(root, "params", params); return root; } cJSON *make_hover_request(int id, const char *uri) { cJSON *root = cJSON_CreateObject(); cJSON_AddStringToObject(root, "jsonrpc", "2.0"); cJSON_AddNumberToObject(root, "id", id); cJSON_AddStringToObject(root, "method", "textDocument/completion"); cJSON *params = cJSON_CreateObject(); cJSON *doc = cJSON_CreateObject(); cJSON_AddStringToObject(doc, "uri", uri); cJSON_AddItemToObject(params, "textDocument", doc); cJSON *pos = cJSON_CreateObject(); cJSON_AddNumberToObject(pos, "line", 15); cJSON_AddNumberToObject(pos, "character", 6); cJSON_AddItemToObject(params, "position", pos); cJSON_AddItemToObject(root, "params", params); return root; } cJSON *make_document_symbol_request(int id, const char *uri) { cJSON *root = cJSON_CreateObject(); cJSON_AddStringToObject(root, "jsonrpc", "2.0"); cJSON_AddNumberToObject(root, "id", id); cJSON_AddStringToObject(root, "method", "textDocument/documentSymbol"); cJSON *params = cJSON_CreateObject(); cJSON *textDocument = cJSON_CreateObject(); cJSON_AddStringToObject(textDocument, "uri", uri); cJSON_AddItemToObject(params, "textDocument", textDocument); cJSON_AddItemToObject(root, "params", params); return root; } cJSON *make_features_request(int id, const char *root_uri) { cJSON *root = cJSON_CreateObject(); cJSON_AddStringToObject(root, "jsonrpc", "2.0"); cJSON_AddNumberToObject(root, "id", id); cJSON_AddStringToObject(root, "method", "initialize"); cJSON *params = cJSON_CreateObject(); cJSON_AddStringToObject(params, "processId", "0"); cJSON_AddStringToObject(params, "rootUri", root_uri); cJSON *capabilities = cJSON_CreateObject(); cJSON_AddItemToObject(params, "capabilities", capabilities); cJSON_AddItemToObject(root, "params", params); return root; } cJSON *make_folder_init_request(int id, const char *root_uri) { cJSON *init = cJSON_CreateObject(); cJSON_AddStringToObject(init, "jsonrpc", "2.0"); cJSON_AddNumberToObject(init, "id", id); cJSON_AddStringToObject(init, "method", "initialize"); cJSON *params = cJSON_CreateObject(); cJSON_AddStringToObject(params, "rootUri", root_uri); // capabilities cJSON *capabilities = cJSON_CreateObject(); cJSON *textDocument = cJSON_CreateObject(); cJSON *diagnosticProvider = cJSON_CreateObject(); // Enable diagnostics cJSON_AddBoolToObject(diagnosticProvider, "interFileDependencies", false); cJSON_AddBoolToObject(diagnosticProvider, "workspaceDiagnostics", false); cJSON_AddItemToObject(textDocument, "diagnosticProvider", diagnosticProvider); cJSON_AddItemToObject(capabilities, "textDocument", textDocument); cJSON_AddItemToObject(params, "capabilities", capabilities); cJSON_AddItemToObject(init, "params", params); return init; } cJSON *make_initialized_request(void) { cJSON *msg = cJSON_CreateObject(); cJSON_AddStringToObject(msg, "jsonrpc", "2.0"); cJSON_AddStringToObject(msg, "method", "initialized"); cJSON *params = cJSON_CreateObject(); cJSON_AddItemToObject(msg, "params", params); return msg; } cJSON *make_change_file_request(int id, const char *uri) { cJSON *root = cJSON_CreateObject(); cJSON_AddStringToObject(root, "jsonrpc", "2.0"); cJSON_AddStringToObject(root, "method", "textDocument/didChange"); cJSON *params = cJSON_CreateObject(); cJSON *doc = cJSON_CreateObject(); cJSON_AddStringToObject(doc, "uri", uri); cJSON_AddNumberToObject(doc, "version", 2); cJSON_AddItemToObject(params, "textDocument", doc); cJSON *changes = cJSON_CreateArray(); cJSON *change = cJSON_CreateObject(); cJSON *range = cJSON_CreateObject(); cJSON *start = cJSON_CreateObject(); cJSON_AddNumberToObject(start, "line", 0); cJSON_AddNumberToObject(start, "character", 0); cJSON *end = cJSON_CreateObject(); cJSON_AddNumberToObject(end, "line", 0); cJSON_AddNumberToObject(end, "character", 0); cJSON_AddItemToObject(range, "start", start); cJSON_AddItemToObject(range, "end", end); cJSON_AddItemToObject(change, "range", range); cJSON_AddStringToObject(change, "text", ""); cJSON_AddItemToArray(changes, change); cJSON_AddItemToObject(params, "contentChanges", changes); cJSON_AddItemToObject(root, "params", params); return root; } cJSON *make_open_file_request(const char *uri, const char *languageId, const char *text, int version) { cJSON *msg = cJSON_CreateObject(); cJSON_AddStringToObject(msg, "jsonrpc", "2.0"); cJSON_AddStringToObject(msg, "method", "textDocument/didOpen"); cJSON *params = cJSON_CreateObject(); cJSON *doc = cJSON_CreateObject(); cJSON_AddStringToObject(doc, "uri", uri); cJSON_AddStringToObject(doc, "languageId", languageId); cJSON_AddNumberToObject(doc, "version", version); cJSON_AddStringToObject(doc, "text", text); cJSON_AddItemToObject(params, "textDocument", doc); cJSON_AddItemToObject(msg, "params", params); return msg; } int get_key(void) { struct termios oldt, newt; int ch; tcgetattr(STDIN_FILENO, &oldt); newt = oldt; newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); ch = getchar(); tcsetattr(STDIN_FILENO, TCSANOW, &oldt); struct winsize ws; ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); cols = ws.ws_col; return ch; } char *read_file(const char *path) { FILE *f = fopen(path, "rb"); if (!f) return NULL; // seek to end to get size if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return NULL; } long size = ftell(f); if (size < 0) { fclose(f); return NULL; } rewind(f); char *buf = malloc(size + 1); if (!buf) { fclose(f); return NULL; } size_t read_size = fread(buf, 1, size, f); buf[read_size] = '\0'; // null terminate fclose(f); return buf; } void print_wrapped(const char *text) { struct winsize ws; ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); int cols = ws.ws_col > 0 ? ws.ws_col : 80; // fallback to 80 if unknown int col = 0; for (size_t i = 0; text[i]; i++) { putchar(text[i]); col++; if (text[i] == '\n') { col = 0; // reset column on explicit newline } else if (col >= cols) { putchar('\n'); col = 0; } } } int main() { char *argv[] = {"clangd", NULL}; start_lsp("clangd", argv); cJSON *init = make_folder_init_request(1, "file:///home/syed/main/cubit/tests/"); lsp_send(init); cJSON_Delete(init); cJSON *initialized = make_initialized_request(); lsp_send(initialized); cJSON_Delete(initialized); char *text = read_file("/home/syed/main/cubit/tests/test.c"); printf("%s\n", text); cJSON *req = make_open_file_request( "file:///home/syed/main/cubit/tests/test.c", "c", text, 1); lsp_send(req); cJSON_Delete(req); free(text); // cJSON *doc_sym_req = make_document_symbol_request( // 2, "file:///home/syed/main/cubit/tests/test.c"); // lsp_send(doc_sym_req); // cJSON_Delete(doc_sym_req); usleep(1000000); int next_id = 3; while (1) { printf("Press 'f' to request fold ranges, 'q' to quit: "); fflush(stdout); int key = get_key(); printf("\n"); if (key == 'q') break; if (key == 'f') { cJSON *req = make_fold_request( next_id++, "file:///home/syed/main/cubit/tests/test.c"); lsp_send(req); cJSON_Delete(req); } if (key == 'i') { cJSON *req = make_features_request(next_id++, "file:///home/syed/main/cubit/tests/"); lsp_send(req); cJSON_Delete(req); } if (key == 'h') { cJSON *req = make_hover_request( next_id++, "file:///home/syed/main/cubit/tests/test.c"); lsp_send(req); cJSON_Delete(req); } if (key == 'c') { cJSON *req = make_change_file_request( next_id++, "file:///home/syed/main/cubit/tests/test.c"); lsp_send(req); cJSON_Delete(req); } cJSON *resp; if ((resp = lsp_recv()) == NULL) continue; char *dump = cJSON_PrintUnformatted(resp); cJSON *id = cJSON_GetObjectItem(resp, "id"); // if (id != NULL && id->valueint == 40) { // cJSON *result = cJSON_GetObjectItem(resp, "result"); // cJSON *contents = cJSON_GetObjectItem(result, "contents"); // cJSON *value = cJSON_GetObjectItem(contents, "value"); // if (value && value->type == cJSON_String) // printf("%s\n", value->valuestring); // } print_wrapped(dump); free(dump); cJSON_Delete(resp); } return 0; }