From 7568add9317f2bad5f633e5c587c76b87175899f Mon Sep 17 00:00:00 2001 From: pommicket Date: Tue, 20 Dec 2022 21:54:52 -0500 Subject: autocomplete very broken but kinda working --- autocomplete.c | 131 +++++++++++++++++++++++++-------------------------------- buffer.c | 4 +- command.c | 8 ++-- main.c | 3 +- ted.h | 18 +++++--- 5 files changed, 77 insertions(+), 87 deletions(-) diff --git a/autocomplete.c b/autocomplete.c index cf8eb8b..16d99a2 100644 --- a/autocomplete.c +++ b/autocomplete.c @@ -2,31 +2,34 @@ #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) { + Autocomplete *ac = &ted->autocomplete; + arr_foreach_ptr(ac->completions, Autocompletion, completion) { free(completion->label); free(completion->text); free(completion->filter); } - arr_clear(ted->autocompletions); + arr_clear(ac->completions); + arr_clear(ac->suggested); } // do the actual completion -static void autocomplete_complete(Ted *ted, Autocompletion *completion) { +static void autocomplete_complete(Ted *ted, Autocompletion completion) { TextBuffer *buffer = ted->active_buffer; buffer_start_edit_chain(buffer); // don't merge with other edits if (is_word(buffer_char_before_cursor(buffer))) buffer_backspace_words_at_cursor(buffer, 1); // delete whatever text was already typed - buffer_insert_utf8_at_cursor(buffer, completion->text); + buffer_insert_utf8_at_cursor(buffer, completion.text); buffer_end_edit_chain(buffer); autocomplete_close(ted); } static void autocomplete_select_cursor_completion(Ted *ted) { - if (ted->autocomplete) { - size_t ncompletions = arr_len(ted->autocompletions); - if (ncompletions) { - i64 cursor = mod_i64(ted->autocomplete_cursor, (i64)ncompletions); - autocomplete_complete(ted, &ted->autocompletions[cursor]); + Autocomplete *ac = &ted->autocomplete; + if (ac->open) { + size_t nsuggestions = arr_len(ac->suggested); + if (nsuggestions) { + i64 cursor = mod_i64(ac->cursor, (i64)nsuggestions); + autocomplete_complete(ted, ac->completions[ac->suggested[cursor]]); autocomplete_close(ted); } } @@ -34,64 +37,41 @@ 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; + ++ted->autocomplete.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; + --ted->autocomplete.cursor; } void autocomplete_close(Ted *ted) { - if (ted->autocomplete) { - ted->autocomplete = false; + if (ted->autocomplete.open) { + ted->autocomplete.open = false; autocomplete_clear_completions(ted); } } -static void autocomplete_update_visibility(Ted *ted) { +void autocomplete_update_suggested(Ted *ted) { + Autocomplete *ac = &ted->autocomplete; + arr_clear(ac->suggested); 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); + for (u32 i = 0; i < arr_len(ac->completions); ++i) { + Autocompletion *completion = &ac->completions[i]; + if (str_has_prefix(completion->filter, word)) + arr_add(ac->suggested, i); // suggest this one } free(word); } static void autocomplete_find_completions(Ted *ted) { + Autocomplete *ac = &ted->autocomplete; TextBuffer *buffer = ted->active_buffer; BufferPos pos = buffer->cursor_pos; - if (buffer_pos_eq(pos, ted->autocomplete_pos)) + if (buffer_pos_eq(pos, ac->last_pos)) return; // no need to update completions. - ted->autocomplete_pos = pos; + ac->last_pos = pos; LSP *lsp = buffer_lsp(buffer); if (lsp) { @@ -111,69 +91,77 @@ static void autocomplete_find_completions(Ted *ted) { char *word_at_cursor = str32_to_utf8_cstr(buffer_word_at_cursor(buffer)); char **completions = calloc(TAGS_MAX_COMPLETIONS, sizeof *completions); - size_t ncompletions = tags_beginning_with(ted, word_at_cursor, completions, TAGS_MAX_COMPLETIONS); + u32 ncompletions = (u32)tags_beginning_with(ted, word_at_cursor, completions, TAGS_MAX_COMPLETIONS); free(word_at_cursor); - arr_set_len(ted->autocompletions, ncompletions); + arr_set_len(ac->completions, ncompletions); 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]); + ac->completions[i].label = completions[i]; + ac->completions[i].text = str_dup(completions[i]); + ac->completions[i].filter = str_dup(completions[i]); + arr_add(ac->suggested, (u32)i); } free(completions); } + + autocomplete_update_suggested(ted); } static void autocomplete_process_lsp_response(Ted *ted, const LSPResponse *response) { + Autocomplete *ac = &ted->autocomplete; // @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); + arr_set_len(ac->completions, ncompletions); for (size_t i = 0; i < ncompletions; ++i) { const LSPCompletionItem *lsp_completion = &completion->items[i]; - Autocompletion *ted_completion = &ted->autocompletions[i]; + Autocompletion *ted_completion = &ac->completions[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 + ac->open = true; // open menu } + autocomplete_update_suggested(ted); } // open autocomplete, or just do the completion if there's only one suggestion static void autocomplete_open(Ted *ted) { + Autocomplete *ac = &ted->autocomplete; + if (!ted->active_buffer) return; TextBuffer *buffer = ted->active_buffer; if (!buffer->filename) return; if (buffer->view_only) return; ted->cursor_error_time = 0; - ted->autocomplete_pos = (BufferPos){0,0}; - ted->autocomplete_cursor = 0; - ted->autocompletions = NULL; + ac->last_pos = (BufferPos){0,0}; + ac->cursor = 0; autocomplete_find_completions(ted); - switch (arr_len(ted->autocompletions)) { + + switch (arr_len(ac->completions)) { case 0: ted->cursor_error_time = time_get_seconds(); autocomplete_close(ted); break; case 1: - autocomplete_complete(ted, &ted->autocompletions[0]); + autocomplete_complete(ted, ac->completions[0]); // (^ this calls autocomplete_close) break; default: // open autocomplete menu - ted->autocomplete = true; + ac->open = true; break; } } static void autocomplete_frame(Ted *ted) { + Autocomplete *ac = &ted->autocomplete; TextBuffer *buffer = ted->active_buffer; Font *font = ted->font; float char_height = text_font_char_height(font); @@ -182,16 +170,14 @@ static void autocomplete_frame(Ted *ted) { float const padding = settings->padding; autocomplete_find_completions(ted); - autocomplete_update_visibility(ted); 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; - } + arr_foreach_ptr(ac->suggested, u32, suggestion) { + Autocompletion *completion = &ac->completions[*suggestion]; + completions[ncompletions++] = completion->label; + if (ncompletions == AUTOCOMPLETE_NCOMPLETIONS_VISIBLE) + break; } float menu_width = 400, menu_height = (float)ncompletions * char_height + 2 * padding; @@ -202,7 +188,7 @@ static void autocomplete_frame(Ted *ted) { return; } - ted->autocomplete_cursor = (i32)mod_i64(ted->autocomplete_cursor, (i64)ncompletions); + ac->cursor = (i32)mod_i64(ac->cursor, (i64)ncompletions); v2 cursor_pos = buffer_pos_to_pixels(buffer, buffer->cursor_pos); bool open_up = cursor_pos.y > 0.5f * (buffer->y1 + buffer->y2); // should the completion menu open upwards? @@ -217,7 +203,7 @@ static void autocomplete_frame(Ted *ted) { Rect menu_rect = rect(V2(x, start_y), V2(menu_width, menu_height)); gl_geometry_rect(menu_rect, colors[COLOR_MENU_BG]); //gl_geometry_rect_border(menu_rect, 1, colors[COLOR_BORDER]); - ted->autocomplete_rect = menu_rect; + ac->rect = menu_rect; } // vertical padding @@ -232,18 +218,17 @@ static void autocomplete_frame(Ted *ted) { ted->cursor = ted->cursor_hand; } { // highlight cursor entry - Rect r = rect(V2(x, start_y + (float)ted->autocomplete_cursor * char_height), V2(menu_width, char_height)); + Rect r = rect(V2(x, start_y + (float)ac->cursor * char_height), V2(menu_width, char_height)); gl_geometry_rect(r, colors[COLOR_MENU_HL]); } 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 + if (rect_contains_point(ac->rect, click)) { u16 entry = (u16)((click.y - start_y) / char_height); if (entry < ncompletions) { // entry was clicked on! use this completion. - autocomplete_complete(ted, &ted->autocompletions[entry]); + autocomplete_complete(ted, ac->completions[ac->suggested[entry]]); } } } diff --git a/buffer.c b/buffer.c index b947b4b..008de6b 100644 --- a/buffer.c +++ b/buffer.c @@ -2343,8 +2343,8 @@ u32 buffer_last_rendered_line(TextBuffer *buffer) { // returns true if the buffer "used" this event bool buffer_handle_click(Ted *ted, TextBuffer *buffer, v2 click, u8 times) { BufferPos buffer_pos; - if (ted->autocomplete) { - if (rect_contains_point(ted->autocomplete_rect, click)) + if (ted->autocomplete.open) { + if (rect_contains_point(ted->autocomplete.rect, click)) return false; // don't look at clicks in the autocomplete menu else autocomplete_close(ted); // close autocomplete menu if user clicks outside of it diff --git a/command.c b/command.c index 81a712c..5ed30b7 100644 --- a/command.c +++ b/command.c @@ -127,7 +127,7 @@ void command_execute(Ted *ted, Command c, i64 argument) { buffer = &ted->line_buffer; ted_switch_to_buffer(ted, buffer); buffer_select_all(buffer); - } else if (ted->autocomplete) { + } else if (ted->autocomplete.open) { autocomplete_select_cursor_completion(ted); } else if (buffer) { if (buffer->selection) @@ -263,13 +263,13 @@ void command_execute(Ted *ted, Command c, i64 argument) { } break; case CMD_AUTOCOMPLETE: - if (ted->autocomplete) + if (ted->autocomplete.open) autocomplete_next(ted); else autocomplete_open(ted); break; case CMD_AUTOCOMPLETE_BACK: - if (ted->autocomplete) + if (ted->autocomplete.open) autocomplete_prev(ted); break; @@ -376,7 +376,7 @@ void command_execute(Ted *ted, Command c, i64 argument) { if (*ted->error_shown) { // dismiss error box *ted->error_shown = '\0'; - } else if (ted->autocomplete) { + } else if (ted->autocomplete.open) { autocomplete_close(ted); } else if (ted->menu) { menu_escape(ted); diff --git a/main.c b/main.c index 6176bf5..aed6f53 100644 --- a/main.c +++ b/main.c @@ -9,6 +9,7 @@ - rust-analyzer should wait until cargo metadata/check is done before sending initialize response FUTURE FEATURES: - robust find (results shouldn't move around when you type things) +- multiple files with command line arguments - configurable max buffer size + max view-only buffer size - :set-build-command, don't let ../Cargo.toml override ./Makefile - add numlock as a key modifier @@ -1009,7 +1010,7 @@ int main(int argc, char **argv) { if (ted->nodes_used[0]) { float y1 = padding; node_frame(ted, node, rect4(x1, y1, x2, y)); - if (ted->autocomplete) { + if (ted->autocomplete.open) { autocomplete_frame(ted); } } else { diff --git a/ted.h b/ted.h index 77e3c51..2bc7d43 100644 --- a/ted.h +++ b/ted.h @@ -353,12 +353,21 @@ typedef struct { } BuildError; typedef struct { - bool visible; // updated every frame depending on word at cursor + filter char *label; char *filter; char *text; } Autocompletion; +typedef struct { + bool open; // is the autocomplete window open? + + Autocompletion *completions; // dynamic array of all completions + u32 *suggested; // dynamic array of completions to be suggested (indices into completions) + BufferPos last_pos; // position of cursor last time completions were generated. if this changes, we need to recompute completions. + i32 cursor; // which completion is currently selected (index into suggested) + Rect rect; // rectangle where the autocomplete menu is (needed to avoid interpreting autocomplete clicks as other clicks) +} Autocomplete; + typedef struct Ted { struct LSP *test_lsp; // @TODO: something better @@ -410,12 +419,7 @@ typedef struct Ted { Command warn_unsaved; // if non-zero, the user is trying to execute this command, but there are unsaved changes bool build_shown; // are we showing the build output? bool building; // is the build process running? - bool autocomplete; // is the autocomplete window open? - - Autocompletion *autocompletions; // dynamic array of suggestions - BufferPos autocomplete_pos; // position of cursor last time completions were generated. if this changes, we need to recompute completions. - i32 autocomplete_cursor; // which completion is currently selected - Rect autocomplete_rect; // rectangle where the autocomplete menu is (needed to avoid interpreting autocomplete clicks as other clicks) + Autocomplete autocomplete; FILE *log; -- cgit v1.2.3