summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--command.c4
-rw-r--r--config.c1
-rw-r--r--ide-autocomplete.c144
-rw-r--r--main.c13
-rw-r--r--ted.cfg2
-rw-r--r--ted.h12
6 files changed, 128 insertions, 48 deletions
diff --git a/command.c b/command.c
index bafb4f6..5f92719 100644
--- a/command.c
+++ b/command.c
@@ -281,8 +281,8 @@ 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.open) {
- autocomplete_select_cursor_completion(ted);
+ } else if (ted->autocomplete.open || ted->autocomplete.phantom) {
+ autocomplete_select_completion(ted);
} else if (buffer) {
if (buffer->selection)
buffer_indent_selection(buffer);
diff --git a/config.c b/config.c
index 5d819ad..3fc3183 100644
--- a/config.c
+++ b/config.c
@@ -82,6 +82,7 @@ static SettingBool const settings_bool[] = {
{"indent-with-spaces", &settings_zero.indent_with_spaces, true},
{"trigger-characters", &settings_zero.trigger_characters, true},
{"identifier-trigger-characters", &settings_zero.identifier_trigger_characters, true},
+ {"phantom-completions", &settings_zero.phantom_completions, true},
{"signature-help-enabled", &settings_zero.signature_help_enabled, true},
{"lsp-enabled", &settings_zero.lsp_enabled, true},
{"hover-enabled", &settings_zero.hover_enabled, true},
diff --git a/ide-autocomplete.c b/ide-autocomplete.c
index 49fcba3..033841f 100644
--- a/ide-autocomplete.c
+++ b/ide-autocomplete.c
@@ -5,8 +5,7 @@
#define TAGS_MAX_COMPLETIONS 200 // max # of tag completions to scroll through
#define AUTOCOMPLETE_NCOMPLETIONS_VISIBLE 10 // max # of completions to show at once
-static void autocomplete_clear_completions(Ted *ted) {
- Autocomplete *ac = &ted->autocomplete;
+static void autocomplete_clear_completions(Autocomplete *ac) {
arr_foreach_ptr(ac->completions, Autocompletion, completion) {
free(completion->label);
free(completion->text);
@@ -18,6 +17,11 @@ static void autocomplete_clear_completions(Ted *ted) {
arr_clear(ac->suggested);
}
+static void autocomplete_clear_phantom(Autocomplete *ac) {
+ free(ac->phantom);
+ ac->phantom = NULL;
+}
+
// do the actual completion
static void autocomplete_complete(Ted *ted, Autocompletion completion) {
TextBuffer *buffer = ted->active_buffer;
@@ -29,15 +33,19 @@ static void autocomplete_complete(Ted *ted, Autocompletion completion) {
autocomplete_close(ted);
}
-void autocomplete_select_cursor_completion(Ted *ted) {
+void autocomplete_select_completion(Ted *ted) {
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);
}
+ } else if (ac->phantom) {
+ Autocompletion fake_completion = {
+ .text = ac->phantom
+ };
+ autocomplete_complete(ted, fake_completion);
}
}
@@ -78,12 +86,11 @@ void autocomplete_prev(Ted *ted) {
void autocomplete_close(Ted *ted) {
Autocomplete *ac = &ted->autocomplete;
- if (ac->open) {
- ac->open = false;
- autocomplete_clear_completions(ted);
- ted_cancel_lsp_request(ted, ac->last_request_lsp, ac->last_request_id);
- ac->last_request_id = 0;
- }
+ ac->open = false;
+ autocomplete_clear_phantom(ac);
+ autocomplete_clear_completions(ac);
+ ted_cancel_lsp_request(ted, ac->last_request_lsp, ac->last_request_id);
+ ac->last_request_id = 0;
}
static void autocomplete_update_suggested(Ted *ted) {
@@ -111,7 +118,7 @@ static void autocomplete_no_suggestions(Ted *ted) {
autocomplete_close(ted);
}
-static void autocomplete_send_completion_request(Ted *ted, TextBuffer *buffer, BufferPos pos, uint32_t trigger) {
+static void autocomplete_send_completion_request(Ted *ted, TextBuffer *buffer, BufferPos pos, uint32_t trigger, bool phantom) {
if (!buffer->path)
return; // no can do
@@ -146,6 +153,7 @@ static void autocomplete_send_completion_request(Ted *ted, TextBuffer *buffer, B
if (ac->last_request_id) {
ac->last_request_lsp = lsp->id;
ac->last_request_time = ted->frame_time;
+ ac->last_request_phantom = phantom;
// *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).
@@ -154,9 +162,11 @@ static void autocomplete_send_completion_request(Ted *ted, TextBuffer *buffer, B
}
}
-static void autocomplete_find_completions(Ted *ted, uint32_t trigger) {
+static void autocomplete_find_completions(Ted *ted, uint32_t trigger, bool phantom) {
Autocomplete *ac = &ted->autocomplete;
TextBuffer *buffer = ted->active_buffer;
+ if (!buffer)
+ return;
BufferPos pos = buffer->cursor_pos;
if (buffer_pos_eq(pos, ac->last_pos))
return; // no need to update completions.
@@ -170,30 +180,40 @@ static void autocomplete_find_completions(Ted *ted, uint32_t trigger) {
// 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);
+ autocomplete_send_completion_request(ted, buffer, pos, trigger, phantom);
}
} else {
// tag completion
- autocomplete_clear_completions(ted);
+ autocomplete_clear_completions(ac);
char *word_at_cursor = str32_to_utf8_cstr(buffer_word_at_cursor(buffer));
- char **completions = calloc(TAGS_MAX_COMPLETIONS, sizeof *completions);
- u32 ncompletions = (u32)tags_beginning_with(ted, word_at_cursor, completions, TAGS_MAX_COMPLETIONS);
- free(word_at_cursor);
-
- arr_set_len(ac->completions, ncompletions);
-
- for (size_t i = 0; i < ncompletions; ++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);
+ if (phantom) {
+ char *completion = NULL;
+ if (tags_beginning_with(ted, word_at_cursor, &completion, 1) == 1) {
+ // show phantom
+ ac->phantom = completion;
+ } else {
+ free(completion);
+ }
+ } else {
+ char **completions = calloc(TAGS_MAX_COMPLETIONS, sizeof *completions);
+ u32 ncompletions = (u32)tags_beginning_with(ted, word_at_cursor, completions, TAGS_MAX_COMPLETIONS);
+
+ arr_set_len(ac->completions, ncompletions);
+
+ for (size_t i = 0; i < ncompletions; ++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);
+
+ // 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;
}
- 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;
+ free(word_at_cursor);
}
autocomplete_update_suggested(ted);
@@ -253,13 +273,23 @@ void autocomplete_process_lsp_response(Ted *ted, const LSPResponse *response) {
if (request->id != ac->last_request_id)
return; // old request
ac->last_request_id = 0;
- if (!ac->open) {
+ if (!ac->open && !ac->last_request_phantom) {
// user hit escape or down or something before completions arrived.
return;
}
+ if (ac->open && ac->last_request_phantom) {
+ // i'm not sure if this is possible, but just in case,
+ return;
+ }
const LSPResponseCompletion *completion = &response->data.completion;
size_t ncompletions = arr_len(completion->items);
+ printf("got %zu\n",ncompletions);
+ if (ac->last_request_phantom && ncompletions != 1) {
+ // only show phantom completion if there's exactly 1 completion.
+ autocomplete_clear_phantom(ac);
+ return;
+ }
arr_set_len(ac->completions, ncompletions);
for (size_t i = 0; i < ncompletions; ++i) {
const LSPCompletionItem *lsp_completion = &completion->items[i];
@@ -278,6 +308,13 @@ void autocomplete_process_lsp_response(Ted *ted, const LSPResponse *response) {
ted_completion->documentation = *documentation ? str_dup(documentation) : NULL;
}
+ if (ac->last_request_phantom) {
+ assert(ncompletions == 1);
+ ac->phantom = str_dup(ac->completions[0].text);
+ autocomplete_clear_completions(ac);
+ return;
+ }
+
ac->is_list_complete = completion->is_complete;
autocomplete_update_suggested(ted);
@@ -300,11 +337,12 @@ void autocomplete_open(Ted *ted, uint32_t trigger) {
TextBuffer *buffer = ted->active_buffer;
if (!buffer->path) return;
if (buffer->view_only) return;
+ autocomplete_clear_phantom(ac);
ted->cursor_error_time = 0;
- ac->last_pos = (BufferPos){0,0};
+ ac->last_pos = (BufferPos){U32_MAX,0};
ac->cursor = 0;
- autocomplete_find_completions(ted, trigger);
+ autocomplete_find_completions(ted, trigger, false);
switch (arr_len(ac->completions)) {
case 0:
@@ -325,6 +363,17 @@ void autocomplete_open(Ted *ted, uint32_t trigger) {
}
}
+static void autocomplete_find_phantom(Ted *ted) {
+ Autocomplete *ac = &ted->autocomplete;
+ if (ac->open) return;
+ if (!ted->active_buffer) return;
+ TextBuffer *buffer = ted->active_buffer;
+ if (!buffer->path) return;
+ if (buffer->view_only) return;
+
+ autocomplete_find_completions(ted, TRIGGER_INVOKED, true);
+}
+
static char symbol_kind_icon(SymbolKind k) {
switch (k) {
case SYMBOL_FUNCTION:
@@ -346,17 +395,38 @@ static char symbol_kind_icon(SymbolKind k) {
}
void autocomplete_frame(Ted *ted) {
- Autocomplete *ac = &ted->autocomplete;
- if (!ac->open) return;
-
TextBuffer *buffer = ted->active_buffer;
+ if (!buffer) return;
Font *font = ted->font;
float char_height = text_font_char_height(font);
const Settings *settings = buffer_settings(buffer);
const u32 *colors = settings->colors;
const float padding = settings->padding;
+ if (settings->phantom_completions) {
+ autocomplete_find_phantom(ted);
+ }
+
+ Autocomplete *ac = &ted->autocomplete;
+ if (!ac->open && ac->phantom) {
+ // display phantom completion
+ char *word_at_cursor = buffer_word_at_cursor_utf8(buffer);
+ if (*word_at_cursor && str_has_prefix(ac->phantom, word_at_cursor)) {
+ const char *completion = ac->phantom + strlen(word_at_cursor);
+ vec2 pos = buffer_pos_to_pixels(buffer, buffer->cursor_pos);
+ text_utf8(font, completion, pos.x, pos.y,
+ colors[COLOR_TEXT] & 0xffffff7f);
+ text_render(font);
+ } else {
+ // this phantom is no longer relevant
+ autocomplete_clear_phantom(ac);
+ }
+ free(word_at_cursor);
+ return;
+ }
+ if (!ac->open)
+ return;
- autocomplete_find_completions(ted, TRIGGER_INCOMPLETE);
+ autocomplete_find_completions(ted, TRIGGER_INCOMPLETE, false);
size_t ncompletions = arr_len(ac->suggested);
bool waiting_for_lsp = ac->last_request_id != 0;
diff --git a/main.c b/main.c
index f9f2014..18276d2 100644
--- a/main.c
+++ b/main.c
@@ -1,7 +1,5 @@
/*
@TODO:
-- LSP configuration in ted.cfg
- - disable publishDiagnostics for rust-analyzer (& others)?
- phantom completions
- debug-lsp option (which logs LSP messages)
- check LSP process status (TEST: what happens if LSP server is not installed)
@@ -747,11 +745,12 @@ int main(int argc, char **argv) {
// NOTE: we are not checking for signature help trigger
// characters because currently we ask for signature
// help any time a character is inserted.
-
- if (settings->identifier_trigger_characters
- && is32_word(last_char)
- && !is32_digit(last_char))
- autocomplete_open(ted, last_char);
+ if (is32_word(last_char)
+ && !is32_digit(last_char)) {
+ if (settings->identifier_trigger_characters) {
+ autocomplete_open(ted, last_char);
+ }
+ }
}
}
diff --git a/ted.cfg b/ted.cfg
index 521aaab..7648d12 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -33,6 +33,8 @@ restore-session = on
trigger-characters = on
# should all identifier characters (e.g. a-z) be treated as trigger characters?
identifier-trigger-characters = off
+# display "phantom completions"? (if there is only one completion, display it next to the cursor)
+phantom-completions = yes
# enable LSP support (for autocompletion, etc.)
# you can also set `lsp = ""` but this is a quick way to disable LSP servers for all langauges
lsp-enabled = yes
diff --git a/ted.h b/ted.h
index c42fd69..a3104e8 100644
--- a/ted.h
+++ b/ted.h
@@ -123,6 +123,7 @@ typedef struct {
bool restore_session;
bool regenerate_tags_if_not_found;
bool indent_with_spaces;
+ bool phantom_completions;
bool trigger_characters;
bool identifier_trigger_characters;
bool signature_help_enabled;
@@ -386,6 +387,11 @@ typedef struct {
i32 cursor; // which completion is currently selected (index into suggested)
i32 scroll;
+ // was the last request for phantom completion?
+ bool last_request_phantom;
+ // current phantom completion to be displayed
+ char *phantom;
+
Rect rect; // rectangle where the autocomplete menu is (needed to avoid interpreting autocomplete clicks as other clicks)
} Autocomplete;
@@ -1120,8 +1126,10 @@ GLuint gl_load_texture_from_image(const char *path);
// open autocomplete
// trigger should either be a character (e.g. '.') or one of the TRIGGER_* constants.
void autocomplete_open(Ted *ted, uint32_t trigger);
-void autocomplete_process_lsp_response(Ted *ted, const LSPResponse *response);
-void autocomplete_select_cursor_completion(Ted *ted);
+void autocomplete_process_lsp_response(Ted *ted, const LSPResponse *response);
+// select the completion the cursor is on,
+// or select the phantom completion if there is one.
+void autocomplete_select_completion(Ted *ted);
// scroll completion list
void autocomplete_scroll(Ted *ted, i32 by);
// move cursor to next completion