summaryrefslogtreecommitdiff
path: root/autocomplete.c
diff options
context:
space:
mode:
Diffstat (limited to 'autocomplete.c')
-rw-r--r--autocomplete.c90
1 files changed, 60 insertions, 30 deletions
diff --git a/autocomplete.c b/autocomplete.c
index 8f3df10..2955183 100644
--- a/autocomplete.c
+++ b/autocomplete.c
@@ -99,39 +99,65 @@ static bool autocomplete_using_lsp(Ted *ted) {
static void autocomplete_no_suggestions(Ted *ted) {
Autocomplete *ac = &ted->autocomplete;
- if (ac->trigger_char == 0)
+ if (ac->trigger == TRIGGER_INVOKED)
ted->cursor_error_time = time_get_seconds();
autocomplete_close(ted);
}
-static void autocomplete_find_completions(Ted *ted, char32_t trigger_char) {
+static void autocomplete_send_completion_request(Ted *ted, TextBuffer *buffer, BufferPos pos, uint32_t trigger) {
+ LSP *lsp = buffer_lsp(buffer);
+ Autocomplete *ac = &ted->autocomplete;
+
+ LSPRequest request = {
+ .type = LSP_REQUEST_COMPLETION
+ };
+
+ LSPCompletionTriggerKind lsp_trigger = LSP_TRIGGER_CHARACTER;
+ switch (trigger) {
+ case TRIGGER_INVOKED: lsp_trigger = LSP_TRIGGER_INVOKED; break;
+ case TRIGGER_INCOMPLETE: lsp_trigger = LSP_TRIGGER_INCOMPLETE; break;
+ }
+
+ request.data.completion = (LSPRequestCompletion) {
+ .position = {
+ .document = lsp_document_id(lsp, buffer->filename),
+ .pos = buffer_pos_to_lsp(buffer, pos)
+ },
+ .context = {
+ .trigger_kind = lsp_trigger,
+ .trigger_character = {0},
+ }
+ };
+ if (trigger < UNICODE_CODE_POINTS)
+ unicode_utf32_to_utf8(request.data.completion.context.trigger_character, trigger);
+ lsp_send_request(lsp, &request);
+ ac->waiting_for_lsp = true;
+ ac->lsp_request_time = ted->frame_time;
+ // *technically sepaking* this can mess things up if a complete
+ // list arrives only after the user has typed some stuff
+ // (in that case we'll send a TriggerKind = incomplete request even though it makes no sense).
+ // but i don't think any servers will have a problem with that.
+ ac->is_list_complete = false;
+}
+
+static void autocomplete_find_completions(Ted *ted, uint32_t trigger) {
Autocomplete *ac = &ted->autocomplete;
TextBuffer *buffer = ted->active_buffer;
BufferPos pos = buffer->cursor_pos;
if (buffer_pos_eq(pos, ac->last_pos))
return; // no need to update completions.
- ac->trigger_char = trigger_char;
+ ac->trigger = trigger;
ac->last_pos = pos;
LSP *lsp = buffer_lsp(buffer);
if (lsp) {
- LSPRequest request = {
- .type = LSP_REQUEST_COMPLETION
- };
- bool invoked = ac->trigger_char == 0 || is_word(ac->trigger_char);
- request.data.completion = (LSPRequestCompletion) {
- .position = {
- .document = lsp_document_id(lsp, buffer->filename),
- .pos = buffer_pos_to_lsp(buffer, pos)
- },
- .context = {
- .trigger_kind = invoked ? LSP_TRIGGER_INVOKED : LSP_TRIGGER_CHARACTER,
- .trigger_character = {0},
- }
- };
- unicode_utf32_to_utf8(request.data.completion.context.trigger_character, ac->trigger_char);
- lsp_send_request(lsp, &request);
- ac->waiting_for_lsp = true;
+ if (ac->is_list_complete && trigger == TRIGGER_INCOMPLETE) {
+ // the list of completions we got from the LSP server is complete,
+ // so we just need to call autocomplete_update_suggested,
+ // we don't need to send a new request.
+ } else {
+ autocomplete_send_completion_request(ted, buffer, pos, trigger);
+ }
} else {
// tag completion
autocomplete_clear_completions(ted);
@@ -150,6 +176,10 @@ static void autocomplete_find_completions(Ted *ted, char32_t trigger_char) {
arr_add(ac->suggested, (u32)i);
}
free(completions);
+
+ // if we got the full list of tags beginning with `word_at_cursor`,
+ // then we don't need to call `tags_beginning_with` again.
+ ac->is_list_complete = ncompletions == TAGS_MAX_COMPLETIONS;
}
autocomplete_update_suggested(ted);
@@ -157,7 +187,6 @@ static void autocomplete_find_completions(Ted *ted, char32_t trigger_char) {
static void autocomplete_process_lsp_response(Ted *ted, const LSPResponse *response) {
Autocomplete *ac = &ted->autocomplete;
- bool was_waiting = ac->waiting_for_lsp;
ac->waiting_for_lsp = false;
if (!ac->open) {
// user hit escape or down or something before completions arrived.
@@ -186,6 +215,7 @@ static void autocomplete_process_lsp_response(Ted *ted, const LSPResponse *respo
ted_completion->documentation = *documentation ? str_dup(documentation) : NULL;
}
+ ac->is_list_complete = completion->is_complete;
}
autocomplete_update_suggested(ted);
switch (arr_len(ac->suggested)) {
@@ -193,15 +223,16 @@ static void autocomplete_process_lsp_response(Ted *ted, const LSPResponse *respo
autocomplete_no_suggestions(ted);
return;
case 1:
- // if we just finished loading suggestions, and there's only one suggestion, use it
- if (was_waiting)
+ // if autocomplete was invoked by Ctrl+Space, and there's only one completion, select it.
+ if (ac->trigger == TRIGGER_INVOKED)
autocomplete_complete(ted, ac->completions[ac->suggested[0]]);
return;
}
}
-// open autocomplete, or just do the completion if there's only one suggestion
-static void autocomplete_open(Ted *ted, char32_t trigger_character) {
+// open autocomplete
+// trigger should either be a character (e.g. '.') or one of the TRIGGER_* constants.
+static void autocomplete_open(Ted *ted, uint32_t trigger) {
Autocomplete *ac = &ted->autocomplete;
if (ac->open) return;
if (!ted->active_buffer) return;
@@ -212,8 +243,7 @@ static void autocomplete_open(Ted *ted, char32_t trigger_character) {
ted->cursor_error_time = 0;
ac->last_pos = (BufferPos){0,0};
ac->cursor = 0;
- ac->open_time = ted->frame_time;
- autocomplete_find_completions(ted, trigger_character);
+ autocomplete_find_completions(ted, trigger);
switch (arr_len(ac->completions)) {
case 0:
@@ -261,13 +291,13 @@ static void autocomplete_frame(Ted *ted) {
u32 const *colors = settings->colors;
float const padding = settings->padding;
- autocomplete_find_completions(ted, 0);
-
+ autocomplete_find_completions(ted, TRIGGER_INCOMPLETE);
+
size_t ncompletions = arr_len(ac->suggested);
if (ac->waiting_for_lsp && ncompletions == 0) {
struct timespec now = ted->frame_time;
- if (timespec_sub(now, ac->open_time) < 0.2) {
+ if (timespec_sub(now, ac->lsp_request_time) < 0.2) {
// don't show "Loading..." unless we've actually been loading for a bit of time
return;
}