summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--autocomplete.c131
-rw-r--r--buffer.c4
-rw-r--r--command.c8
-rw-r--r--main.c3
-rw-r--r--ted.h18
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;