summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arr.c4
-rw-r--r--autocomplete.c87
-rw-r--r--command.c4
-rw-r--r--lsp-write-request.c2
-rw-r--r--lsp.c28
-rw-r--r--lsp.h6
-rw-r--r--main.c34
-rw-r--r--ted.c6
-rw-r--r--ted.h2
9 files changed, 156 insertions, 17 deletions
diff --git a/arr.c b/arr.c
index 2ca394c..402420f 100644
--- a/arr.c
+++ b/arr.c
@@ -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;
}
diff --git a/command.c b/command.c
index b6f3195..81a712c 100644
--- a/command.c
+++ b/command.c
@@ -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
diff --git a/lsp.c b/lsp.c
index e9654cb..d7f0574 100644
--- a/lsp.c
+++ b/lsp.c
@@ -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) {
diff --git a/lsp.h b/lsp.h
index d8604b4..6245623 100644
--- a/lsp.h
+++ b/lsp.h
@@ -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.
diff --git a/main.c b/main.c
index 06b52c2..6176bf5 100644
--- a/main.c
+++ b/main.c
@@ -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;
diff --git a/ted.c b/ted.c
index 2ce39fe..229935a 100644
--- a/ted.c
+++ b/ted.c
@@ -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];
}
diff --git a/ted.h b/ted.h
index df50f8b..77e3c51 100644
--- a/ted.h
+++ b/ted.h
@@ -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;