From 508a2f502df514072e6a1755566e718720ead45e Mon Sep 17 00:00:00 2001 From: Syed Daanish Date: Fri, 3 Oct 2025 12:40:47 +0100 Subject: [PATCH] Random Stuff --- README.md | 20 ++++++ src/helper_functions.c | 71 ++++++++++++++++++++- src/helper_functions.h | 3 + src/highlighters.c | 55 ++++++++++++++-- src/lsp_handler.c | 142 +++++++++++++++++------------------------ src/lsp_handler.h | 16 +++++ src/node.h | 11 ++++ tests/lsp_test.c | 15 ++--- tests/test.rb | 10 +-- tests/tls | Bin 135784 -> 135784 bytes 10 files changed, 236 insertions(+), 107 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..0ece8c0 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# Cubit + +> It is still under developmant. + +## Description + +It is a vimmish editor with hopefully everything an IDE can have for ruby/c/c++ & html/css/js workflows. + +## TODO + +- Fix lsp handling to support varying standards. +- Add support for other languages. +- Make it a bit more stable. + +## Features + +- It currently supports tree-sitter syntax highlighting for ruby and c/c++. +- It also supports their respective LSP's. + - Solargraph for ruby. + - clangd for c/c++. diff --git a/src/helper_functions.c b/src/helper_functions.c index 0ef9e71..2ef2b20 100644 --- a/src/helper_functions.c +++ b/src/helper_functions.c @@ -4,6 +4,7 @@ #include "editor.h" #include "node.h" #include "snake.h" +#include #include #include #include @@ -20,14 +21,80 @@ void die(const char *s) { usleep(1000000000); } +void measure_text(const char *text, int *out_lines, int *out_longest) { + if (!text) { + *out_lines = 0; + *out_longest = 0; + return; + } + char *copy = strdup(text); + if (!copy) { + *out_lines = 0; + *out_longest = 0; + return; + } + int lines = 0; + int longest = 0; + char *line = strtok(copy, "\n"); + while (line) { + int len = strlen(line); + if (len > longest) + longest = len; + lines++; + line = strtok(NULL, "\n"); + } + free(copy); + *out_lines = lines + 1; + *out_longest = longest; +} + +int is_word_char(char c) { return isalnum((unsigned char)c) || c == '_'; } + +void wrap_text(char *text, int max_width) { + if (!text || max_width <= 0) + return; + int line_length = 0; + char *read_pos = text; + char *write_pos = text; + char *last_space = NULL; + while (*read_pos) { + *write_pos = *read_pos; + if (!is_word_char(*read_pos)) { + if (*read_pos != '\n') + last_space = write_pos; + if (*read_pos == '\n') { + line_length = 0; + last_space = NULL; + } else { + line_length++; + } + } else { + line_length++; + } + if (line_length > max_width) { + if (last_space != NULL) { + *last_space = '\n'; + line_length = write_pos - last_space; + last_space = NULL; + } else { + memmove(write_pos + 1, write_pos, strlen(write_pos) + 1); + *write_pos = '\n'; + write_pos++; + line_length = 1; + } + } + read_pos++; + write_pos++; + } +} + size_t clamp_to_visible(BufferData *e, int row, int dir) { if (row < 0) return 0; if ((size_t)row >= e->line_count) return e->line_count - 1; - while (row >= 0 && (size_t)row < e->line_count && line_is_hidden(e, row)) { + while (row >= 0 && (size_t)row < e->line_count && line_is_hidden(e, row)) row += dir; - } if (row < 0) return 0; if ((size_t)row >= e->line_count) diff --git a/src/helper_functions.h b/src/helper_functions.h index 0599859..35c74d0 100644 --- a/src/helper_functions.h +++ b/src/helper_functions.h @@ -22,3 +22,6 @@ char *line_to_string(Line *line); char *lines_to_string(BufferData *e); uint32_t fnv1a_32(const char *data, size_t len); int file_type(const char *filename); +void measure_text(const char *text, int *out_lines, int *out_longest); +int is_word_char(char c); +void wrap_text(char *text, int max_width); diff --git a/src/highlighters.c b/src/highlighters.c index 354fbac..04a7e30 100644 --- a/src/highlighters.c +++ b/src/highlighters.c @@ -11,7 +11,6 @@ #include "ruby_ts_table.h" #include "snake.h" #include -#include #include #include #include @@ -145,10 +144,6 @@ void append_highlight_byte(Line *line, size_t start_col, size_t end_col, h->end_col = end; } -static int is_word_char(char c) { - return isalnum((unsigned char)c) || c == '_'; -} - void append_highlight(Line *lh, size_t start_col, size_t end_col, Color *fg, Color *bg, uint8_t flags) { lh->highlight_count++; @@ -253,6 +248,56 @@ void word_highlighter(Node *buffer) { append_highlight(virt, y, y + 1, (Color *)colors + DARK, NULL, 0b010); } } + // now handle lsp errors hl + for (size_t i = 0; i < e->line_count; i++) { + Line *l = e->lines[i]; + if (!l) + continue; + for (int j = 0; j < buffer->Buffer.error_count; j++) { + int start_line = buffer->Buffer.errors[j].start_line; + int end_line = buffer->Buffer.errors[j].end_line; + int start_col = buffer->Buffer.errors[j].start_col; + int end_col = buffer->Buffer.errors[j].end_col; + if (start_line <= i && end_line >= i) { + if (i == start_line && i == end_line) + append_highlight(l, start_col, end_col, NULL, NULL, 0b000); + else if (i == start_line) + append_highlight(l, start_col, l->cluster_count, NULL, NULL, 0b000); + else if (i == end_line) + append_highlight(l, 0, end_col, NULL, (Color *)colors + RED, 0b000); + else + append_highlight(l, 0, l->cluster_count, NULL, NULL, 0b000); + if (i == start_line) { + VirtLine *virt = e->virtual_lines[i]; + if (!virt) { + virt = calloc(1, sizeof(VirtLine)); + if (!virt) + die("calloc failed"); + virt->line_count = 1; + virt->lines = calloc(1, sizeof(Line *)); + if (!virt->lines) + die("calloc failed"); + virt->lines[0] = calloc(1, sizeof(Line)); + e->virtual_lines[i] = virt; + } + if (!virt->lines) { + virt->line_count = 1; + virt->lines = calloc(1, sizeof(Line *)); + if (!virt->lines) + die("calloc failed"); + virt->lines[0] = calloc(1, sizeof(Line)); + } + Line *virt_l = virt->lines[0]; + virt_l->cluster_count = l->cluster_count + 5; + virt_l->clusters = calloc(l->cluster_count + 5, sizeof(Cluster)); + for (size_t y = l->cluster_count; y < l->cluster_count + 5; y++) { + virt_l->clusters[y].utf8 = strdup("ยท"); + virt_l->clusters[y].width = 1; + } + } + } + } + } if (!e || e->line_count == 0) return; Line *line = e->lines[e->cursor_row]; diff --git a/src/lsp_handler.c b/src/lsp_handler.c index e11c460..cd23a13 100644 --- a/src/lsp_handler.c +++ b/src/lsp_handler.c @@ -3,7 +3,6 @@ #include "constants.h" #include "helper_functions.h" #include "node.h" -#include #include #include #include @@ -19,17 +18,6 @@ int lsp_pid = -1; int version = 1; int c_id = 1; pthread_mutex_t lsp_mutex = PTHREAD_MUTEX_INITIALIZER; -typedef enum { - HOVER, -} LspRequestType; -typedef struct LspQueue LspQueue; -typedef struct LspQueue { - LspRequestType type; - int id; - Node *buffer; - struct LspQueue *next; -} LspQueue; - LspQueue *lsp_head = NULL; void lsp_enqueue(LspRequestType type, int id, Node *buffer) { @@ -50,73 +38,6 @@ void lsp_enqueue(LspRequestType type, int id, Node *buffer) { pthread_mutex_unlock(&lsp_mutex); } -void measure_text(const char *text, int *out_lines, int *out_longest) { - if (!text) { - *out_lines = 0; - *out_longest = 0; - return; - } - char *copy = strdup(text); - if (!copy) { - *out_lines = 0; - *out_longest = 0; - return; - } - int lines = 0; - int longest = 0; - char *line = strtok(copy, "\n"); - while (line) { - int len = strlen(line); - if (len > longest) - longest = len; - lines++; - line = strtok(NULL, "\n"); - } - free(copy); - *out_lines = lines + 1; - *out_longest = longest; -} - -void wrap_text(char *text, int max_width) { - if (!text || max_width <= 0) - return; - int line_length = 0; - char *read_pos = text; - char *write_pos = text; - char *last_space = NULL; - while (*read_pos) { - *write_pos = *read_pos; - if (isspace(*read_pos)) { - if (*read_pos != '\n') - last_space = write_pos; - if (*read_pos == '\n') { - line_length = 0; - last_space = NULL; - } else { - line_length++; - } - } else { - line_length++; - } - if (line_length > max_width) { - if (last_space != NULL) { - *last_space = '\n'; - line_length = write_pos - last_space; - last_space = NULL; - } else { - memmove(write_pos + 1, write_pos, strlen(write_pos) + 1); - *write_pos = '\n'; - write_pos++; - line_length = 1; - } - } - read_pos++; - write_pos++; - } -} - -void send_notification(cJSON *msg); - void handle_hover_response(Node *buffer, cJSON *msg) { cJSON *result = cJSON_GetObjectItem(msg, "result"); cJSON *contents = cJSON_GetObjectItem(result, "contents"); @@ -170,6 +91,8 @@ void *lsp_thread(void *arg) { pthread_mutex_unlock(&lsp_mutex); if (matched->type == HOVER) handle_hover_response(matched->buffer, msg); + else if (matched->type == INITIALIZED) + handle_initialized(msg); free(matched); break; } @@ -180,13 +103,56 @@ void *lsp_thread(void *arg) { cJSON *method_json = cJSON_GetObjectItem(msg, "method"); if (method_json && cJSON_IsString(method_json)) { const char *method = method_json->valuestring; - // send_notification(msg); TODO + if (method) { + if (strcmp(method, "textDocument/publishDiagnostics") == 0) { + cJSON *params = cJSON_GetObjectItem(msg, "params"); + cJSON *diagnostics = cJSON_GetObjectItem(params, "diagnostics"); + cJSON *diag = NULL; + const char *filename = + cJSON_GetObjectItem(params, "uri")->valuestring; + if (root.Root.focused && root.Root.focused->type == BUFFER) { + free(root.Root.focused->Buffer.errors); + root.Root.focused->Buffer.errors = NULL; + root.Root.focused->Buffer.error_count = 0; + cJSON_ArrayForEach(diag, diagnostics) + handle_diagnostic(diag, root.Root.focused); + } + } + } } } cJSON_Delete(msg); } } +void handle_diagnostic(cJSON *diag, Node *buffer) { + cJSON *range = cJSON_GetObjectItem(diag, "range"); + cJSON *start = cJSON_GetObjectItem(range, "start"); + int start_line = cJSON_GetObjectItem(start, "line")->valueint; + int start_char = cJSON_GetObjectItem(start, "character")->valueint; + cJSON *end = cJSON_GetObjectItem(range, "end"); + int end_line = cJSON_GetObjectItem(end, "line")->valueint; + int end_char = cJSON_GetObjectItem(end, "character")->valueint; + int severity = cJSON_GetObjectItem(diag, "severity")->valueint; + char *text = cJSON_GetObjectItem(diag, "message")->valuestring; + buffer->Buffer.errors = + realloc(buffer->Buffer.errors, + (buffer->Buffer.error_count + 1) * sizeof(LSPErrors)); + buffer->Buffer.errors[buffer->Buffer.error_count].start_line = start_line; + buffer->Buffer.errors[buffer->Buffer.error_count].start_col = start_char; + buffer->Buffer.errors[buffer->Buffer.error_count].end_line = end_line; + buffer->Buffer.errors[buffer->Buffer.error_count].end_col = end_char; + buffer->Buffer.errors[buffer->Buffer.error_count].severity = severity; + buffer->Buffer.errors[buffer->Buffer.error_count].message = text; + buffer->Buffer.error_count++; +} + +void handle_initialized(cJSON *_msg) { + cJSON *initialized = make_initialized_request(); + lsp_send(initialized); + cJSON_Delete(initialized); +} + void send_notification(cJSON *msg) { char *dump = cJSON_Print(msg); wrap_text(dump, 200); @@ -196,8 +162,8 @@ void send_notification(cJSON *msg) { void lsp_setup(Node *buffer, uint8_t lang) { if (lang == RUBY) { - char *argv[] = {"ruby-lsp", NULL}; - start_lsp("ruby-lsp", argv); + char *argv[] = {"solargraph", "stdio", NULL}; + start_lsp("solargraph", argv); } else if (lang == C_LANG) { char *argv[] = {"clangd", NULL}; start_lsp("clangd", argv); @@ -206,6 +172,7 @@ void lsp_setup(Node *buffer, uint8_t lang) { } cJSON *init = make_folder_init_request(1, buffer->Buffer.folder); lsp_send(init); + lsp_enqueue(INITIALIZED, 1, buffer); cJSON_Delete(init); char *text = lines_to_string(buffer->Buffer.data); cJSON *req = make_open_file_request(buffer->Buffer.filename, "c", text, 1); @@ -239,7 +206,7 @@ void handle_sigchld(int sig) { pid_t pid; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { if (pid == lsp_pid) - die("clangd exited"); + die("lsp exited"); } } @@ -356,6 +323,15 @@ cJSON *make_folder_init_request(int id, const char *root_uri) { return init; } +cJSON *make_initialized_request() { + 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_open_file_request(const char *uri, const char *languageId, const char *text, int version) { cJSON *msg = cJSON_CreateObject(); diff --git a/src/lsp_handler.h b/src/lsp_handler.h index 17efa6d..b6dc239 100644 --- a/src/lsp_handler.h +++ b/src/lsp_handler.h @@ -8,6 +8,18 @@ #include #include +typedef enum { + INITIALIZED, + HOVER, +} LspRequestType; +typedef struct LspQueue LspQueue; +typedef struct LspQueue { + LspRequestType type; + int id; + Node *buffer; + struct LspQueue *next; +} LspQueue; + void start_lsp(const char *cmd, char *const argv[]); void lsp_send(cJSON *msg); cJSON *lsp_recv(); @@ -24,3 +36,7 @@ void lsp_setup(Node *buffer, uint8_t lang); void lsp_change(Node *buffer, size_t start_line, size_t start_col, size_t end_line, size_t end_col, const char *new_text); void lsp_hover(Node *buffer, size_t line, size_t character); +void send_notification(cJSON *msg); +cJSON *make_initialized_request(); +void handle_initialized(cJSON *_msg); +void handle_diagnostic(cJSON *diag, Node *buffer); diff --git a/src/node.h b/src/node.h index 96bcece..8c0235f 100644 --- a/src/node.h +++ b/src/node.h @@ -53,6 +53,15 @@ typedef struct ColHighlight { uint8_t flags; } ColHighlight; +typedef struct LSPErrors { + size_t start_line; + size_t start_col; + size_t end_line; + size_t end_col; + char *message; + uint8_t severity; +} LSPErrors; + typedef void (*HlFunc)(Node *e); typedef void (*LspUpdateFunc)(Node *e); typedef void (*LspChangeFunc)(Node *e, size_t start_line, size_t start_col, @@ -106,6 +115,8 @@ struct Node { BufferData *data; HlFunc hl_func_basic; HlFunc hl_func_syntax; + LSPErrors *errors; + uint8_t error_count; char *filename; char *folder; _Bool hidden; diff --git a/tests/lsp_test.c b/tests/lsp_test.c index f319a7f..0345214 100644 --- a/tests/lsp_test.c +++ b/tests/lsp_test.c @@ -159,7 +159,6 @@ cJSON *make_folder_init_request(int id, const char *root_uri) { cJSON *params = cJSON_CreateObject(); cJSON_AddStringToObject(params, "rootUri", root_uri); - // capabilities cJSON *capabilities = cJSON_CreateObject(); cJSON *textDocument = cJSON_CreateObject(); cJSON *diagnosticProvider = cJSON_CreateObject(); @@ -297,8 +296,8 @@ void print_wrapped(const char *text) { } int main() { - char *argv[] = {"clangd", NULL}; - start_lsp("clangd", argv); + char *argv[] = {"ruby-lsp", NULL}; + start_lsp("ruby-lsp", argv); cJSON *init = make_folder_init_request(1, "file:///home/syed/main/cubit/tests/"); lsp_send(init); @@ -306,10 +305,10 @@ int main() { cJSON *initialized = make_initialized_request(); lsp_send(initialized); cJSON_Delete(initialized); - char *text = read_file("/home/syed/main/cubit/tests/test.c"); + char *text = read_file("/home/syed/main/cubit/tests/test.rb"); printf("%s\n", text); cJSON *req = make_open_file_request( - "file:///home/syed/main/cubit/tests/test.c", "c", text, 1); + "file:///home/syed/main/cubit/tests/test.rb", "ruby", text, 1); lsp_send(req); cJSON_Delete(req); free(text); @@ -328,7 +327,7 @@ int main() { break; if (key == 'f') { cJSON *req = make_fold_request( - next_id++, "file:///home/syed/main/cubit/tests/test.c"); + next_id++, "file:///home/syed/main/cubit/tests/test.rb"); lsp_send(req); cJSON_Delete(req); } @@ -340,13 +339,13 @@ int main() { } if (key == 'h') { cJSON *req = make_hover_request( - next_id++, "file:///home/syed/main/cubit/tests/test.c"); + next_id++, "file:///home/syed/main/cubit/tests/test.rb"); 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"); + next_id++, "file:///home/syed/main/cubit/tests/test.rb"); lsp_send(req); cJSON_Delete(req); } diff --git a/tests/test.rb b/tests/test.rb index e261e59..543279a 100644 --- a/tests/test.rb +++ b/tests/test.rb @@ -16,6 +16,7 @@ bool_f = false nil_val = nil + # --- Strings and symbols s1 = "double-quoted string with escape\nand interpolation: #{int_lit}" s2 = 'single-quoted string with \\n literal' @@ -382,15 +383,6 @@ hex = 0xff oct = 0o755 bin = 0b101010 -# --- Case with guards -val = 10 -case val -when Integer if val.even? - case_guard = :even_int -else - case_guard = :other -end - # --- Ternary, safe nav, assignment forms tern = val > 5 ? :big : :small safe_len = nil&.to_s&.length diff --git a/tests/tls b/tests/tls index 25d78ee6c0953d216f68d2460e63d80c5b1bbf0b..fb0ca24c30c3ecc0ab5d0f1452f7d2005727a634 100755 GIT binary patch delta 1856 zcmZ8gZERCz6n@XWtG4TEHyCX+*>lp9Hj<>&f?}JjDpL=}et4X<_Sc=s0>7}no3l&@{ z-3zC9XQ{Vr;YDELn(EWhtTD#M`B_p^ReJVOo_dx)AXTZ=)je3U0H@CGNDTB)mn!No^!^SIN? z|8<&64(BQN`S;Fx*J{0dDo=dKEiUp?V~osGpYR=|F62h;TRc+YpSZldxQs^TDHeXq zRqryIPUVSqzNRb;4j!ZE<{65W9LmE`!&@o(c#>j}|3WduE1nNSm=99i%Fk15<5`Lw zeB<&kbo2L@d&@57tPd|Xw~t>XRe4Fz2BM4A7%y{sPq@i>o^be`PItP~4K`b4qq4S1 zSzE6Jl#!vHH=BKpex-q^mWlQr(lU6W8ahhrS%cE(Bb919NBiGu_639dfwaOrdax^A zH#ByD?^qK+TJmvBRl|k?k)~7&MrvWTNCog>EnKf?V%454F9)<3)MA5AgT_V;{P;~B ztijuL;FtXM=0wOd@rP)gK8`vhKfWD zSgsc`CLIXb+Q?WP6b{f*!C5`f+dfLN=HMsj;XBZ7Oi&{FHwiS*&4%;eT3f0Y>Uo9 zcJYRO)-*Xem9YONs9#_GWWqE&*3WKeub2GF2@-!bSj5yb(#_@#MsBM}oW^Yj;DyBW zD!swEg!$m;Ae;8<3r{8FKgs;J#^*AT&>CJa=u|@M_?^))B+pUp4+h&T57AbBG}w0e zBxy4SYnM~Rell3Z)Vnm=!!8+=jJ-z7KmSn-6#JLC8g1~`=#9aEd;DpeN!s`6rzH*4 T^h|m(2LCj{iS+5uU}pdS!mOwI delta 1857 zcmZ8gZA?>V6n@XWbQVh2Rl&9>>@vrge6)pvP%r}79JsiE*+g+dij=PbMPN?5#9PRc zCHrUd63_O-qz2pv;fG8zT3I4nri)9=VnPOvFoi4`%bc5uCe-<3=YC*98*-leoaemf zJons}wQ=LxxbbpCO#7#DLbkMczxdu+Gm%vpVV5H5`>#xkNiPUeKEJx{4M8g4LRt%4 z;H_ychpzyb@{4w8XjBu8}EkyYU#cTW~ z#T(qZy%uiqCW`a?;&zu~E?=M7wfXE4|8u+BE@{d21aXxY*~o6Trj~baKDx$(HW&BV zjMg&=$^-tL&27)o%A*P5W1ehx@qg*qdV=zlAF|Up-D&xIo5zX#GdeeHr*R2N3SY6i z?Yhfof@tNt9kpQN0~E7(jA9Olj9Mt*2Pk@Ym|_LLL$QWu@2G`3-c7NdPf={-af;2n zW@jz<`6oMF4(S!m)Oz1$b3ObvsgXP_TOQb~4su7P%W+S4cqTy&e$<*d)0zopb8(5Z zhyL7Bxzyk5f4`#Gvqvf-s%8T1r`3#pS#-dae5k^^d0)BRRUgZ6t^nrK-7L>}@U>Ep3SNzr;Kf4N zhO>pR3rcaZkosge;T=nHnW~p)P(C9&;Y2P*j)5_Gi*BzPA07r5N=Lv50`6{vdbofw zN?G&t3ZIT+7;J#m<9%Cf-Mkv9`Z4x53)lA zkUXElm~gV%e3)Lw2`OC$#>DWaviY{iSn&pr$mS18D23!@DigzB%BI2g0amu5UzW{# z1;%P%%Uh~`PL^!+VCrcPvi*>(B}ZlRGTqKrNY#cc%IW6UpJU7qCavo|+0^Z4?f7#a zybN#Rqdv%i2FwT$G-7oCvf((M2+&oq*k+6pO3Lm}M7QYUH7U)V?O_TJw* znnYuUBC@zjF7MklIvg^zb+Nzo{Y{=V&h`?R)%L WNf9-@G&4N_4@!W~jC~H*d;SNV3ZmTr