From 91fc9b10e1700b482b13af1d25d95d80085a9b79 Mon Sep 17 00:00:00 2001 From: pommicket Date: Fri, 30 Dec 2022 14:21:35 -0500 Subject: go to definition --- ide-definitions.c | 113 ++++++++++++++++++++++++++++++++++++++++++------------ lsp.c | 2 +- main.c | 3 ++ menu.c | 7 +--- tags.c | 12 +++--- ted.c | 1 + ted.h | 14 +++++-- 7 files changed, 110 insertions(+), 42 deletions(-) diff --git a/ide-definitions.c b/ide-definitions.c index a9ff945..4dbe9d2 100644 --- a/ide-definitions.c +++ b/ide-definitions.c @@ -74,14 +74,48 @@ void definitions_frame(Ted *ted) { } static void definitions_clear_entries(Definitions *defs) { - arr_foreach_ptr(defs->selector_all_entries, SelectorEntry, entry) { - free((char*)entry->name); + arr_foreach_ptr(defs->selector_all_definitions, SymbolInfo, def) { + free(def->name); } - arr_clear(defs->selector_all_entries); + arr_clear(defs->selector_all_definitions); arr_clear(defs->selector.entries); defs->selector.n_entries = 0; } +static int definition_entry_qsort_cmp(const void *av, const void *bv) { + const SymbolInfo *a = av, *b = bv; + // first, sort by length + size_t a_len = strlen(a->name), b_len = strlen(b->name); + if (a_len < b_len) return -1; + if (a_len > b_len) return 1; + // then sort alphabetically + return strcmp(a->name, b->name); +} + +// put the entries matching the search term into the selector. +static void definitions_selector_filter_entries(Ted *ted) { + Definitions *defs = &ted->definitions; + Selector *sel = &defs->selector; + + // create selector entries based on search term + char *search_term = str32_to_utf8_cstr(buffer_get_line(&ted->line_buffer, 0)); + + arr_clear(sel->entries); + + arr_foreach_ptr(defs->selector_all_definitions, SymbolInfo, info) { + if (!search_term || stristr(info->name, search_term)) { + SelectorEntry *entry = arr_addp(sel->entries); + entry->name = info->name; + entry->color = info->color; + } + } + free(search_term); + + arr_qsort(sel->entries, definition_entry_qsort_cmp); + + sel->n_entries = arr_len(sel->entries); +} + void definitions_process_lsp_response(Ted *ted, LSP *lsp, const LSPResponse *response) { Definitions *defs = &ted->definitions; @@ -117,16 +151,20 @@ void definitions_process_lsp_response(Ted *ted, LSP *lsp, const LSPResponse *res const u32 *colors = settings->colors; definitions_clear_entries(defs); - arr_set_len(defs->selector_all_entries, arr_len(symbols)); + arr_set_len(defs->selector_all_definitions, arr_len(symbols)); for (size_t i = 0; i < arr_len(symbols); ++i) { const LSPSymbolInformation *symbol = &symbols[i]; - SelectorEntry *entry = &defs->selector_all_entries[i]; + SymbolInfo *def = &defs->selector_all_definitions[i]; - entry->name = str_dup(lsp_response_string(response, symbol->name)); + def->name = str_dup(lsp_response_string(response, symbol->name)); SymbolKind kind = symbol_kind_to_ted(symbol->kind); - entry->color = colors[color_for_symbol_kind(kind)]; + def->color = colors[color_for_symbol_kind(kind)]; + def->from_lsp = true; + def->position = lsp_location_start_position(symbol->location); } + definitions_selector_filter_entries(ted); + } break; default: debug_println("?? bad request type in %s", __func__); @@ -134,18 +172,33 @@ void definitions_process_lsp_response(Ted *ted, LSP *lsp, const LSPResponse *res } } +void definitions_send_request_if_needed(Ted *ted) { + LSP *lsp = buffer_lsp(ted->prev_active_buffer); + if (!lsp) + return; + Definitions *defs = &ted->definitions; + char *query = buffer_contents_utf8_alloc(&ted->line_buffer); + if (defs->last_request_query && strcmp(defs->last_request_query, query) == 0) { + free(query); + return; // no need to update symbols + } + LSPRequest request = {.type = LSP_REQUEST_WORKSPACE_SYMBOLS}; + LSPRequestWorkspaceSymbols *syms = &request.data.workspace_symbols; + syms->query = str_dup(query); + defs->last_request_id = lsp_send_request(lsp, &request); + defs->last_request_time = ted->frame_time; + free(defs->last_request_query); + defs->last_request_query = query; +} + void definitions_selector_open(Ted *ted) { Definitions *defs = &ted->definitions; definitions_clear_entries(defs); LSP *lsp = buffer_lsp(ted->prev_active_buffer); if (lsp) { - LSPRequest request = {.type = LSP_REQUEST_WORKSPACE_SYMBOLS}; - LSPRequestWorkspaceSymbols *syms = &request.data.workspace_symbols; - syms->query = str_dup(""); - defs->last_request_id = lsp_send_request(lsp, &request); - defs->last_request_time = ted->frame_time; + definitions_send_request_if_needed(ted); } else { - defs->selector_all_entries = tags_get_entries(ted); + defs->selector_all_definitions = tags_get_symbols(ted); } ted_switch_to_buffer(ted, &ted->line_buffer); buffer_select_all(ted->active_buffer); @@ -158,27 +211,37 @@ void definitions_selector_close(Ted *ted) { definitions_clear_entries(defs); // @TODO : cancel defs->last_request_id = 0; + free(defs->last_request_query); + defs->last_request_query = NULL; } -char *definitions_selector_update(Ted *ted) { +void definitions_selector_update(Ted *ted) { Definitions *defs = &ted->definitions; Selector *sel = &defs->selector; sel->enable_cursor = true; - // create selector entries based on search term - char *search_term = str32_to_utf8_cstr(buffer_get_line(&ted->line_buffer, 0)); - - arr_clear(sel->entries); + definitions_selector_filter_entries(ted); - arr_foreach_ptr(defs->selector_all_entries, SelectorEntry, entry) { - if (!search_term || stristr(entry->name, search_term)) { - arr_add(sel->entries, *entry); + // send new request if search term has changed. + // this is needed because e.g. clangd gives an incomplete list + definitions_send_request_if_needed(ted); + + char *chosen = selector_update(ted, sel); + if (chosen) { + arr_foreach_ptr(defs->selector_all_definitions, SymbolInfo, info) { + if (strcmp(info->name, chosen) == 0) { + if (info->from_lsp) { + menu_close(ted); + ted_go_to_lsp_document_position(ted, NULL, info->position); + } else { + menu_close(ted); + tag_goto(ted, chosen); + } + } } + + free(chosen); } - - sel->n_entries = arr_len(sel->entries); - - return selector_update(ted, sel); } void definitions_selector_render(Ted *ted, Rect bounds) { diff --git a/lsp.c b/lsp.c index 4b95179..c8de1a7 100644 --- a/lsp.c +++ b/lsp.c @@ -1,5 +1,5 @@ // print server-to-client communication -#define LSP_SHOW_S2C 1 +#define LSP_SHOW_S2C 0 // print client-to-server communication #define LSP_SHOW_C2S 0 diff --git a/main.c b/main.c index 8048b3e..f010f91 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,7 @@ /* @TODO: +- show location in definitions menu +- handle multiple symbols with same name - cancelling requests e.g. in ide-definitions.c - sort symbols by score (clangd extension?) - more LSP stuff: @@ -7,6 +9,7 @@ - find usages - refactoring? - ted_active_lsp should return something even when buffer isn't open +- some way of opening + closing all C files in directory for clangd workspace/symbols to work - test full unicode position handling - check if there are any other non-optional/nice-to-have-support-for server-to-client requests - better non-error window/showMessage(Request) diff --git a/menu.c b/menu.c index 139ae07..4c62768 100644 --- a/menu.c +++ b/menu.c @@ -238,12 +238,7 @@ static void menu_update(Ted *ted) { } break; case MENU_GOTO_DEFINITION: { - char *chosen_tag = definitions_selector_update(ted); - if (chosen_tag) { - menu_close(ted); - tag_goto(ted, chosen_tag); - free(chosen_tag); - } + definitions_selector_update(ted); } break; case MENU_GOTO_LINE: { char *contents = str32_to_utf8_cstr(buffer_get_line(line_buffer, 0)); diff --git a/tags.c b/tags.c index ebd5497..1d62f00 100644 --- a/tags.c +++ b/tags.c @@ -347,26 +347,26 @@ top:; return success; } -SelectorEntry *tags_get_entries(Ted *ted) { +SymbolInfo *tags_get_symbols(Ted *ted) { // read tags file and extract tag names char const *filename = tags_filename(ted, true); if (!filename) return NULL; FILE *file = fopen(filename, "rb"); if (!file) return NULL; - SelectorEntry *entries = NULL; + SymbolInfo *infos = NULL; u32 color = ted_color(ted, COLOR_TEXT); if (file) { char line[1024]; while (fgets(line, sizeof line, file)) { if (line[0] != '!') { // tag metadata is formatted as tag names beginning with ! size_t len = strcspn(line, "\t"); - SelectorEntry *entry = arr_addp(entries); - entry->name = strn_dup(line, len); - entry->color = color; + SymbolInfo *info = arr_addp(infos); + info->name = strn_dup(line, len); + info->color = color; } } fclose(file); } - return entries; + return infos; } diff --git a/ted.c b/ted.c index 0bf0668..e98348d 100644 --- a/ted.c +++ b/ted.c @@ -535,6 +535,7 @@ void ted_go_to_position(Ted *ted, const char *path, u32 line, u32 index, bool is } void ted_go_to_lsp_document_position(Ted *ted, LSP *lsp, LSPDocumentPosition position) { + if (!lsp) lsp = ted_active_lsp(ted); const char *path = lsp_document_path(lsp, position.document); u32 line = position.pos.line; u32 character = position.pos.character; diff --git a/ted.h b/ted.h index 393aee6..22a8362 100644 --- a/ted.h +++ b/ted.h @@ -413,6 +413,13 @@ typedef struct { BufferPos range_end; } Hover; +typedef struct { + char *name; + u32 color; + bool from_lsp; + LSPDocumentPosition position; // only set if from_lsp = true +} SymbolInfo; + typedef struct { // ID of the last request which was sent out. // used to process responses in chronological order (= ID order). @@ -421,8 +428,9 @@ typedef struct { LSPRequestID last_request_id; struct timespec last_request_time; + char *last_request_query; // last query string which we sent a request for Selector selector; // for "go to definition of..." menu - SelectorEntry *selector_all_entries; // an array of all definitions + SymbolInfo *selector_all_definitions; // an array of all definitions } Definitions; @@ -583,8 +591,6 @@ void signature_help_retrigger(Ted *ted); // alone isn't sufficient) void definition_goto(Ted *ted, LSP *lsp, const char *name, LSPDocumentPosition pos); void definitions_selector_open(Ted *ted); -// returns tag selected (should be free'd), or NULL if none was. -// if this is an LSP-based menu, this function will always return NULL. -char *definitions_selector_update(Ted *ted); +void definitions_selector_update(Ted *ted); void definitions_selector_render(Ted *ted, Rect bounds); void definitions_selector_close(Ted *ted); -- cgit v1.2.3