From c0397f1f4c80e73a2e4ccd1946703fe6a5bb405e Mon Sep 17 00:00:00 2001 From: pommicket Date: Tue, 3 Jan 2023 18:01:04 -0500 Subject: go to type definition --- README.md | 10 +++++++++- buffer.c | 5 ++++- command.c | 6 ++++++ command.h | 1 + ide-definitions.c | 15 ++++++++++++++- lsp-parse.c | 19 ++++++++++++++++--- lsp-write.c | 8 +++++++- lsp.c | 8 ++++++++ lsp.h | 8 ++++++-- main.c | 8 ++++---- ted.cfg | 3 +++ ted.h | 2 ++ test/lsp/multidef.cpp | 7 +++++++ 13 files changed, 87 insertions(+), 13 deletions(-) create mode 100644 test/lsp/multidef.cpp diff --git a/README.md b/README.md index 83c037d..5c2982d 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,15 @@ I would recommend trying out an LSP server if you're unsure about which one to u ted has support for [LSPs](https://microsoft.github.io/language-server-protocol/)! -You can Ctrl+Click on an identifier to go to its definition. +You can Ctrl+Click on an identifier to go to its definition, or Ctrl+Shift+Click to go +to its declaration, or Ctrl+Alt+Click to go to its type's definition. + +(clangd seems to sometimes go to the declaration even +when the definition should be available, e.g. +when Ctrl+clicking on the function declarations +in `ted.h`. This is Not My Fault, +and VSCode's clangd extension suffers from the same problem.) + You can also press Ctrl+D to get a searchable list of all functions/types where you can select one to go to its definition. diff --git a/buffer.c b/buffer.c index 4645f75..0d372fc 100644 --- a/buffer.c +++ b/buffer.c @@ -2561,14 +2561,17 @@ bool buffer_handle_click(Ted *ted, TextBuffer *buffer, vec2 click, u8 times) { // select to position buffer_select_to_pos(buffer, buffer_pos); break; - case KEY_MODIFIER_CTRL: + case KEY_MODIFIER_CTRL: case KEY_MODIFIER_CTRL | KEY_MODIFIER_SHIFT: + case KEY_MODIFIER_CTRL | KEY_MODIFIER_ALT: if (!buffer->is_line_buffer) { // go to definition/declaration buffer_cursor_move_to_pos(buffer, buffer_pos); GotoType type = GOTO_DEFINITION; if (ted->key_modifier & KEY_MODIFIER_SHIFT) type = GOTO_DECLARATION; + else if (ted->key_modifier & KEY_MODIFIER_ALT) + type = GOTO_TYPE_DEFINITION; buffer_goto_word_at_cursor(buffer, type); } break; diff --git a/command.c b/command.c index bf26b3a..9ba6766 100644 --- a/command.c +++ b/command.c @@ -67,6 +67,7 @@ static CommandName const command_names[] = { {"goto-definition", CMD_GOTO_DEFINITION}, {"goto-definition-at-cursor", CMD_GOTO_DEFINITION_AT_CURSOR}, {"goto-declaration-at-cursor", CMD_GOTO_DECLARATION_AT_CURSOR}, + {"goto-type-definition-at-cursor", CMD_GOTO_TYPE_DEFINITION_AT_CURSOR}, {"find", CMD_FIND}, {"find-replace", CMD_FIND_REPLACE}, {"tab-close", CMD_TAB_CLOSE}, @@ -417,6 +418,11 @@ void command_execute(Ted *ted, Command c, i64 argument) { buffer_goto_word_at_cursor(buffer, GOTO_DECLARATION); } } break; + case CMD_GOTO_TYPE_DEFINITION_AT_CURSOR: { + if (buffer && buffer_is_named_file(buffer)) { + buffer_goto_word_at_cursor(buffer, GOTO_TYPE_DEFINITION); + } + } break; case CMD_FIND_USAGES: usages_find(ted); break; diff --git a/command.h b/command.h index 3f7d155..2713df8 100644 --- a/command.h +++ b/command.h @@ -76,6 +76,7 @@ ENUM_U16 { CMD_GOTO_DEFINITION, // "go to definition of..." menu CMD_GOTO_DEFINITION_AT_CURSOR, CMD_GOTO_DECLARATION_AT_CURSOR, + CMD_GOTO_TYPE_DEFINITION_AT_CURSOR, CMD_COPY, CMD_CUT, diff --git a/ide-definitions.c b/ide-definitions.c index 7873630..7175e08 100644 --- a/ide-definitions.c +++ b/ide-definitions.c @@ -64,11 +64,22 @@ void definition_goto(Ted *ted, LSP *lsp, const char *name, LSPDocumentPosition p case GOTO_DECLARATION: request_type = LSP_REQUEST_DECLARATION; break; + case GOTO_TYPE_DEFINITION: + request_type = LSP_REQUEST_TYPE_DEFINITION; + break; + case GOTO_IMPLEMENTATION: + request_type = LSP_REQUEST_IMPLEMENTATION; + break; } // send that request LSPRequest request = {.type = request_type}; request.data.definition.position = position; LSPRequestID id = lsp_send_request(lsp, &request); + if (id == 0 && request.type == LSP_REQUEST_IMPLEMENTATION) { + // if we can't go to the implementation, try going to the definition + request.type = LSP_REQUEST_DEFINITION; + id = lsp_send_request(lsp, &request); + } defs->last_request_id = id; defs->last_request_lsp = lsp->id; defs->last_request_time = ted->frame_time; @@ -142,7 +153,9 @@ void definitions_process_lsp_response(Ted *ted, LSP *lsp, const LSPResponse *res switch (response->request.type) { case LSP_REQUEST_DEFINITION: - case LSP_REQUEST_DECLARATION: { + case LSP_REQUEST_DECLARATION: + case LSP_REQUEST_TYPE_DEFINITION: + case LSP_REQUEST_IMPLEMENTATION: { // handle textDocument/definition or textDocument/declaration response const LSPResponseDefinition *response_def = &response->data.definition; diff --git a/lsp-parse.c b/lsp-parse.c index 9c22fe4..42f2291 100644 --- a/lsp-parse.c +++ b/lsp-parse.c @@ -151,23 +151,34 @@ static void parse_capabilities(LSP *lsp, const JSON *json, JSONObject capabiliti arr_add(lsp->signature_help_retrigger_chars, '>'); } - // check for hover support + // check for textDocument/hover support JSONValue hover_value = json_object_get(json, capabilities, "hoverProvider"); if (hover_value.type != JSON_UNDEFINED && hover_value.type != JSON_FALSE) { cap->hover_support = true; } - // check for definition support + // check for textDocument/definition support JSONValue definition_value = json_object_get(json, capabilities, "definitionProvider"); if (definition_value.type != JSON_UNDEFINED && definition_value.type != JSON_FALSE) { cap->definition_support = true; } - // check for declaration support + // check for textDocument/declaration support JSONValue declaration_value = json_object_get(json, capabilities, "declarationProvider"); if (declaration_value.type != JSON_UNDEFINED && declaration_value.type != JSON_FALSE) { cap->declaration_support = true; } + // check for textDocument/typeDefinition support + JSONValue type_definition_value = json_object_get(json, capabilities, "typeDefinitionProvider"); + if (type_definition_value.type != JSON_UNDEFINED && type_definition_value.type != JSON_FALSE) { + cap->type_definition_support = true; + } + + // check for textDocument/implementation support + JSONValue implementation_value = json_object_get(json, capabilities, "implementationProvider"); + if (implementation_value.type != JSON_UNDEFINED && implementation_value.type != JSON_FALSE) { + cap->implementation_support = true; + } // check for textDocument/documentHighlight support JSONValue highlight_value = json_object_get(json, capabilities, "documentHighlightProvider"); @@ -890,6 +901,8 @@ void process_message(LSP *lsp, JSON *json) { break; case LSP_REQUEST_DEFINITION: case LSP_REQUEST_DECLARATION: + case LSP_REQUEST_TYPE_DEFINITION: + case LSP_REQUEST_IMPLEMENTATION: add_to_messages = parse_definition(lsp, json, &response); break; case LSP_REQUEST_HIGHLIGHT: diff --git a/lsp-write.c b/lsp-write.c index 691bcba..48229c8 100644 --- a/lsp-write.c +++ b/lsp-write.c @@ -281,6 +281,10 @@ static const char *lsp_request_method(LSPRequest *request) { return "textDocument/definition"; case LSP_REQUEST_DECLARATION: return "textDocument/declaration"; + case LSP_REQUEST_TYPE_DEFINITION: + return "textDocument/typeDefinition"; + case LSP_REQUEST_IMPLEMENTATION: + return "textDocument/implementation"; case LSP_REQUEST_HIGHLIGHT: return "textDocument/documentHighlight"; case LSP_REQUEST_RENAME: @@ -549,7 +553,9 @@ void write_request(LSP *lsp, LSPRequest *request) { write_obj_end(o); } break; case LSP_REQUEST_DEFINITION: - case LSP_REQUEST_DECLARATION: { + case LSP_REQUEST_DECLARATION: + case LSP_REQUEST_TYPE_DEFINITION: + case LSP_REQUEST_IMPLEMENTATION: { const LSPRequestDefinition *def = &request->data.definition; write_key_obj_start(o, "params"); write_document_position(o, def->position); diff --git a/lsp.c b/lsp.c index bd01bbd..3dc0fee 100644 --- a/lsp.c +++ b/lsp.c @@ -49,6 +49,8 @@ void lsp_request_free(LSPRequest *r) { case LSP_REQUEST_HOVER: case LSP_REQUEST_DEFINITION: case LSP_REQUEST_DECLARATION: + case LSP_REQUEST_TYPE_DEFINITION: + case LSP_REQUEST_IMPLEMENTATION: case LSP_REQUEST_REFERENCES: case LSP_REQUEST_HIGHLIGHT: case LSP_REQUEST_DID_CLOSE: @@ -179,6 +181,10 @@ static bool lsp_supports_request(LSP *lsp, const LSPRequest *request) { return cap->definition_support; case LSP_REQUEST_DECLARATION: return cap->declaration_support; + case LSP_REQUEST_TYPE_DEFINITION: + return cap->type_definition_support; + case LSP_REQUEST_IMPLEMENTATION: + return cap->implementation_support; case LSP_REQUEST_WORKSPACE_SYMBOLS: return cap->workspace_symbols_support; case LSP_REQUEST_RENAME: @@ -220,6 +226,8 @@ static bool request_type_is_notification(LSPRequestType type) { case LSP_REQUEST_HOVER: case LSP_REQUEST_DEFINITION: case LSP_REQUEST_DECLARATION: + case LSP_REQUEST_TYPE_DEFINITION: + case LSP_REQUEST_IMPLEMENTATION: case LSP_REQUEST_REFERENCES: case LSP_REQUEST_RENAME: case LSP_REQUEST_WORKSPACE_SYMBOLS: diff --git a/lsp.h b/lsp.h index f5404b1..c146a9b 100644 --- a/lsp.h +++ b/lsp.h @@ -66,6 +66,8 @@ typedef enum { LSP_REQUEST_HOVER, // textDocument/hover LSP_REQUEST_DEFINITION, // textDocument/definition LSP_REQUEST_DECLARATION, // textDocument/declaration + LSP_REQUEST_TYPE_DEFINITION, // textDocument/typeDefinition + LSP_REQUEST_IMPLEMENTATION, // textDocument/implementation LSP_REQUEST_HIGHLIGHT, // textDocument/documentHighlight LSP_REQUEST_REFERENCES, // textDocument/references LSP_REQUEST_RENAME, // textDocument/rename @@ -201,7 +203,7 @@ typedef struct { LSPRequestCompletion completion; LSPRequestSignatureHelp signature_help; LSPRequestHover hover; - // LSP_REQUEST_DEFINITION, LSP_REQUEST_DECLARATION, or LSP_REQUEST_TYPE_DEFINITION + // LSP_REQUEST_DEFINITION, LSP_REQUEST_DECLARATION, LSP_REQUEST_TYPE_DEFINITION, or LSP_REQUEST_IMPLEMENTATION LSPRequestDefinition definition; LSPRequestHighlight highlight; LSPRequestReferences references; @@ -456,7 +458,7 @@ typedef struct { LSPResponseCompletion completion; LSPResponseSignatureHelp signature_help; LSPResponseHover hover; - // LSP_REQUEST_DEFINITION, LSP_REQUEST_DECLARATION, or LSP_REQUEST_TYPE_DEFINITION + // LSP_REQUEST_DEFINITION, LSP_REQUEST_DECLARATION, LSP_REQUEST_TYPE_DEFINITION, or LSP_REQUEST_IMPLEMENTATION LSPResponseDefinition definition; LSPResponseWorkspaceSymbols workspace_symbols; LSPResponseRename rename; @@ -484,6 +486,8 @@ typedef struct { bool hover_support; bool definition_support; bool declaration_support; + bool implementation_support; + bool type_definition_support; bool workspace_symbols_support; bool highlight_support; // support for multiple root folders diff --git a/main.c b/main.c index d4535a0..a49e6dd 100644 --- a/main.c +++ b/main.c @@ -1,8 +1,6 @@ /* @TODO: -- go to type definition -- why does asking for a definition in ted.h send us to the wrong place? - (vscode doesn't experience this problem) +- fix problem where LSP_REQUEST_DEFINITION tag isn't being found - ted.h documentation - handle multiple symbols with same name in go-to-definition menu - better non-error window/showMessage(Request) @@ -28,8 +26,10 @@ - make buffer->path NULL for untitled buffers & fix resulting mess - rust-analyzer bug reports: - bad json can give "Unexpected error: client exited without proper shutdown sequence" +- clangd bug report: + - textDocumemt/definition on ted.h declarations just gives you the declaration FUTURE FEATURES: -- add numlock as a key modifier? (but make sure "Ctrl+S" handles both "No NumLock+Ctrl+S" and "NumLock+Ctrl+S") +- make go-to-definition/hover/highlight modifier key configurable - return to previous location in buffer - font setting & support for multiple fonts to cover more characters - comment-start & comment-end settings diff --git a/ted.cfg b/ted.cfg index bca3276..4ab4aa7 100644 --- a/ted.cfg +++ b/ted.cfg @@ -265,7 +265,10 @@ Ctrl+t = :generate-tags Ctrl+d = :goto-definition # alternative to ctrl+click Ctrl+' = :goto-definition-at-cursor +# alternative to ctrl+shift+click Ctrl+Shift+' = :goto-declaration-at-cursor +# alternative to ctrl+alt+click +Ctrl+Alt+' = :goto-type-definition-at-cursor Ctrl+g = :goto-line Ctrl+\ = :split-horizontal diff --git a/ted.h b/ted.h index 19a975c..6fb6754 100644 --- a/ted.h +++ b/ted.h @@ -467,6 +467,8 @@ typedef struct { typedef enum { GOTO_DECLARATION, GOTO_DEFINITION, + GOTO_IMPLEMENTATION, + GOTO_TYPE_DEFINITION, } GotoType; typedef struct { diff --git a/test/lsp/multidef.cpp b/test/lsp/multidef.cpp new file mode 100644 index 0000000..eb5336d --- /dev/null +++ b/test/lsp/multidef.cpp @@ -0,0 +1,7 @@ + +void f(int x, int y) { +} + + +void f(int x) { +} -- cgit v1.2.3