diff options
-rw-r--r-- | arr.c | 4 | ||||
-rw-r--r-- | autocomplete.c | 87 | ||||
-rw-r--r-- | command.c | 4 | ||||
-rw-r--r-- | lsp-write-request.c | 2 | ||||
-rw-r--r-- | lsp.c | 28 | ||||
-rw-r--r-- | lsp.h | 6 | ||||
-rw-r--r-- | main.c | 34 | ||||
-rw-r--r-- | ted.c | 6 | ||||
-rw-r--r-- | ted.h | 2 |
9 files changed, 156 insertions, 17 deletions
@@ -37,8 +37,8 @@ static inline ArrHeader *arr_hdr_(void *arr) { return (ArrHeader *)((char *)arr - offsetof(ArrHeader, data)); } -static inline u32 arr_len(void *arr) { - return arr ? arr_hdr_(arr)->len : 0; +static inline u32 arr_len(const void *arr) { + return arr ? arr_hdr_((void*)arr)->len : 0; } static inline u32 arr_cap(void *arr) { diff --git a/autocomplete.c b/autocomplete.c index ed6687a..cf8eb8b 100644 --- a/autocomplete.c +++ b/autocomplete.c @@ -1,10 +1,11 @@ #define TAGS_MAX_COMPLETIONS 200 // max # of tag completions to scroll through -#define AUTOCOMPLETE_NCOMPLETIONS 10 // max # of completions to show at once +#define AUTOCOMPLETE_NCOMPLETIONS_VISIBLE 10 // max # of completions to show at once static void autocomplete_clear_completions(Ted *ted) { arr_foreach_ptr(ted->autocompletions, Autocompletion, completion) { free(completion->label); free(completion->text); + free(completion->filter); } arr_clear(ted->autocompletions); } @@ -32,6 +33,42 @@ static void autocomplete_select_cursor_completion(Ted *ted) { } +static void autocomplete_next(Ted *ted) { + const Autocompletion *const completions = ted->autocompletions; + i64 ncompletions = arr_len(completions); + if (ncompletions == 0) return; + i64 cursor = mod_i64(ted->autocomplete_cursor + 1, (i64)ncompletions); + for (; cursor < ncompletions; ++cursor) { + if (completions[cursor].visible) + break; + } + if (cursor == ncompletions) { + for (cursor = 0; cursor < ncompletions; ++cursor) { + if (completions[cursor].visible) + break; + } + } + ted->autocomplete_cursor = (i32)cursor; +} + +static void autocomplete_prev(Ted *ted) { + const Autocompletion *const completions = ted->autocompletions; + i64 ncompletions = arr_len(completions); + if (ncompletions == 0) return; + i64 cursor = mod_i64(ted->autocomplete_cursor - 1, (i64)ncompletions); + for (; cursor >= 0; --cursor) { + if (completions[cursor].visible) + break; + } + if (cursor < 0) { + for (cursor = ncompletions - 1; cursor >= 0; --cursor) { + if (completions[cursor].visible) + break; + } + } + ted->autocomplete_cursor = (i32)cursor; +} + void autocomplete_close(Ted *ted) { if (ted->autocomplete) { ted->autocomplete = false; @@ -39,6 +76,16 @@ void autocomplete_close(Ted *ted) { } } +static void autocomplete_update_visibility(Ted *ted) { + char *word = str32_to_utf8_cstr( + buffer_word_at_cursor(ted->active_buffer) + ); + arr_foreach_ptr(ted->autocompletions, Autocompletion, completion) { + completion->visible = str_has_prefix(completion->filter, word); + } + free(word); +} + static void autocomplete_find_completions(Ted *ted) { TextBuffer *buffer = ted->active_buffer; BufferPos pos = buffer->cursor_pos; @@ -72,11 +119,32 @@ static void autocomplete_find_completions(Ted *ted) { for (size_t i = 0; i < ncompletions; ++i) { ted->autocompletions[i].label = completions[i]; ted->autocompletions[i].text = str_dup(completions[i]); + ted->autocompletions[i].filter = str_dup(completions[i]); } free(completions); } } +static void autocomplete_process_lsp_response(Ted *ted, const LSPResponse *response) { + // @TODO: check if same buffer is open and if cursor has moved + const LSPRequest *request = &response->request; + if (request->type == LSP_REQUEST_COMPLETION) { + const LSPResponseCompletion *completion = &response->data.completion; + size_t ncompletions = arr_len(completion->items); + arr_set_len(ted->autocompletions, ncompletions); + for (size_t i = 0; i < ncompletions; ++i) { + const LSPCompletionItem *lsp_completion = &completion->items[i]; + Autocompletion *ted_completion = &ted->autocompletions[i]; + // @TODO: deal with fancier textEdits + ted_completion->label = str_dup(lsp_response_string(response, lsp_completion->label)); + ted_completion->filter = str_dup(lsp_response_string(response, lsp_completion->filter_text)); + ted_completion->text = str_dup(lsp_response_string(response, lsp_completion->text_edit.new_text)); + } + if (ncompletions) + ted->autocomplete = true; // open menu + } +} + // open autocomplete, or just do the completion if there's only one suggestion static void autocomplete_open(Ted *ted) { if (!ted->active_buffer) return; @@ -114,10 +182,18 @@ static void autocomplete_frame(Ted *ted) { float const padding = settings->padding; autocomplete_find_completions(ted); + autocomplete_update_visibility(ted); - size_t ncompletions = arr_len(ted->autocompletions); - if (ncompletions > AUTOCOMPLETE_NCOMPLETIONS) - ncompletions = AUTOCOMPLETE_NCOMPLETIONS; + char *completions[AUTOCOMPLETE_NCOMPLETIONS_VISIBLE] = {0}; + size_t ncompletions = 0; + arr_foreach_ptr(ted->autocompletions, Autocompletion, completion) { + if (completion->visible) { + completions[ncompletions++] = completion->label; + if (ncompletions == AUTOCOMPLETE_NCOMPLETIONS_VISIBLE) + break; + } + } + float menu_width = 400, menu_height = (float)ncompletions * char_height + 2 * padding; if (ncompletions == 0) { @@ -163,6 +239,7 @@ static void autocomplete_frame(Ted *ted) { for (uint i = 0; i < ted->nmouse_clicks[SDL_BUTTON_LEFT]; ++i) { v2 click = ted->mouse_clicks[SDL_BUTTON_LEFT][i]; if (rect_contains_point(ted->autocomplete_rect, click)) { + // @TODO : fix me u16 entry = (u16)((click.y - start_y) / char_height); if (entry < ncompletions) { // entry was clicked on! use this completion. @@ -177,7 +254,7 @@ static void autocomplete_frame(Ted *ted) { rgba_u32_to_floats(colors[COLOR_TEXT], state.color); for (size_t i = 0; i < ncompletions; ++i) { state.x = x + padding; state.y = y; - text_utf8_with_state(font, &state, ted->autocompletions[i].label); + text_utf8_with_state(font, &state, completions[i]); y += char_height; } @@ -264,13 +264,13 @@ void command_execute(Ted *ted, Command c, i64 argument) { break; case CMD_AUTOCOMPLETE: if (ted->autocomplete) - ++ted->autocomplete_cursor; + autocomplete_next(ted); else autocomplete_open(ted); break; case CMD_AUTOCOMPLETE_BACK: if (ted->autocomplete) - --ted->autocomplete_cursor; + autocomplete_prev(ted); break; case CMD_UNDO: diff --git a/lsp-write-request.c b/lsp-write-request.c index 49db0e4..de314aa 100644 --- a/lsp-write-request.c +++ b/lsp-write-request.c @@ -297,7 +297,7 @@ static void write_request(LSP *lsp, LSPRequest *request) { memcpy(header + strlen(header), "\r\n\r\n", 4); char *content = header; - #if 1 + #if 0 printf("\x1b[1m%s\x1b[0m\n",content); #endif @@ -61,13 +61,14 @@ static void lsp_request_free(LSPRequest *r) { static void lsp_response_free(LSPResponse *r) { arr_free(r->string_data); - switch (r->type) { + switch (r->request.type) { case LSP_REQUEST_COMPLETION: arr_free(r->data.completion.items); break; default: break; } + lsp_request_free(&r->request); } void lsp_message_free(LSPMessage *message) { @@ -231,7 +232,6 @@ static bool parse_range(LSP *lsp, const JSON *json, JSONValue range_value, LSPRa static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response) { // deal with textDocument/completion response. // result: CompletionItem[] | CompletionList | null - response->type = LSP_REQUEST_COMPLETION; LSPResponseCompletion *completion = &response->data.completion; JSONValue result = json_get(json, "result"); @@ -274,6 +274,7 @@ static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response) // defaults item->sort_text = item->label; + item->filter_text = item->label; item->text_edit = (LSPTextEdit) { .type = LSP_TEXT_EDIT_PLAIN, .at_cursor = true, @@ -288,6 +289,13 @@ static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response) json, sort_text_value.val.string); } + JSONValue filter_text_value = json_object_get(json, item_object, "filterText"); + if (filter_text_value.type == JSON_STRING) { + // LSP allows using a different string for filtering. + item->filter_text = lsp_response_add_json_string(response, + json, filter_text_value.val.string); + } + JSONValue text_type_value = json_object_get(json, item_object, "insertTextFormat"); if (text_type_value.type == JSON_NUMBER) { double type = text_type_value.val.number; @@ -328,7 +336,6 @@ static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response) } } - } qsort_with_context(completion->items, items.len, sizeof *completion->items, @@ -396,6 +403,7 @@ static void process_message(LSP *lsp, JSON *json) { } else { LSPResponse response = {0}; bool success = false; + response.request = response_to; switch (response_to.type) { case LSP_REQUEST_COMPLETION: success = parse_completion(lsp, json, &response); @@ -410,6 +418,7 @@ static void process_message(LSP *lsp, JSON *json) { message->type = LSP_RESPONSE; message->u.response = response; SDL_UnlockMutex(lsp->messages_mutex); + response_to.type = 0; // don't free } else { lsp_response_free(&response); } @@ -461,12 +470,17 @@ static void lsp_receive(LSP *lsp, size_t max_size) { // kind of a hack. this is needed because arr_set_len zeroes the data. arr_hdr_(lsp->received_data)->len = (u32)received_so_far; lsp->received_data[received_so_far] = '\0';// null terminate - #if 1 + #if 0 printf("\x1b[3m%s\x1b[0m\n",lsp->received_data); #endif u64 response_offset=0, response_size=0; while (has_response(lsp->received_data, received_so_far, &response_offset, &response_size)) { + if (response_offset + response_size > arr_len(lsp->received_data)) { + // we haven't received this whole response yet. + break; + } + char *copy = strn_dup(lsp->received_data + response_offset, response_size); JSON json = {0}; if (json_parse(&json, copy)) { @@ -478,6 +492,10 @@ static void lsp_receive(LSP *lsp, size_t max_size) { json_free(&json); } size_t leftover_data_len = arr_len(lsp->received_data) - (response_offset + response_size); + + //printf("arr_cap = %u response_offset = %u, response_size = %zu, leftover len = %u\n", + // arr_hdr_(lsp->received_data)->cap, + // response_offset, response_size, leftover_data_len); memmove(lsp->received_data, lsp->received_data + response_offset + response_size, leftover_data_len); arr_set_len(lsp->received_data, leftover_data_len); @@ -551,7 +569,7 @@ static int lsp_communication_thread(void *data) { // I WILL KILL YOU IF IT TAKES ANY LONGER time_sleep_ms(1); - #if 1 + #if 0 char buf[1024]={0}; long long n = process_read(&lsp->process, buf, sizeof buf); if (n>0) { @@ -127,7 +127,11 @@ typedef struct { } LSPTextEdit; typedef struct { + // display text for this completion LSPString label; + // text used to filter completions + LSPString filter_text; + // the edit to be applied when this completion is selected. LSPTextEdit text_edit; // note: the items are sorted here in this file, // so you probably don't need to access this. @@ -141,7 +145,7 @@ typedef struct { typedef LSPRequestType LSPResponseType; typedef struct { - LSPResponseType type; + LSPRequest request; // the request which this is a response to // LSP responses tend to have a lot of strings. // to avoid doing a ton of allocations+frees, // they're all stored here. @@ -327,7 +327,7 @@ int main(int argc, char **argv) { while (lsp_next_message(&lsp, &message)) { if (message.type == LSP_RESPONSE) { const LSPResponse *response = &message.u.response; - switch (response->type) { + switch (response->request.type) { case LSP_REQUEST_COMPLETION: { const LSPResponseCompletion *completion = &response->data.completion; arr_foreach_ptr(completion->items, LSPCompletionItem, item) { @@ -880,6 +880,38 @@ int main(int argc, char **argv) { menu_update(ted); } + { + LSP *lsp = ted_get_active_lsp(ted); + if (lsp) { + LSPMessage message = {0}; + while (lsp_next_message(lsp, &message)) { + switch (message.type) { + case LSP_REQUEST: { + LSPRequest *r = &message.u.request; + switch (r->type) { + case LSP_REQUEST_SHOW_MESSAGE: { + LSPRequestMessage *m = &r->data.message; + // @TODO: multiple messages + ted_seterr(ted, "%s", m->message); + } break; + case LSP_REQUEST_LOG_MESSAGE: { + LSPRequestMessage *m = &r->data.message; + // @TODO: actual logging + printf("%s\n", m->message); + } break; + default: break; + } + } break; + case LSP_RESPONSE: { + LSPResponse *r = &message.u.response; + autocomplete_process_lsp_response(ted, r); + } break; + } + lsp_message_free(&message); + } + } + } + ted_update_window_dimensions(ted); float window_width = ted->window_width, window_height = ted->window_height; @@ -71,6 +71,12 @@ LSP *ted_get_lsp(Ted *ted, Language lang) { return ted->test_lsp; } +LSP *ted_get_active_lsp(Ted *ted) { + if (!ted->active_buffer) + return NULL; + return buffer_lsp(ted->active_buffer); +} + u32 ted_color(Ted *ted, ColorSetting color) { return ted_active_settings(ted)->colors[color]; } @@ -353,7 +353,9 @@ typedef struct { } BuildError; typedef struct { + bool visible; // updated every frame depending on word at cursor + filter char *label; + char *filter; char *text; } Autocompletion; |