From 49ab483be3e7af88a3932a43f222aa42cacd3515 Mon Sep 17 00:00:00 2001 From: pommicket Date: Fri, 4 Aug 2023 21:39:13 -0400 Subject: document links seem to be working --- buffer.c | 3 +- config.c | 1 + gl.c | 2 + ide-definitions.c | 16 +++++++- ide-document-link.c | 102 ++++++++++++++++++++++++++++++++++++++++++-------- main.c | 2 +- os-posix.c | 20 ++++++++++ os-win.c | 5 +++ os.h | 5 +++ ted.cfg | 3 ++ ted.h | 22 ++++++++++- test/lsp/multidef.cpp | 2 + 12 files changed, 162 insertions(+), 21 deletions(-) diff --git a/buffer.c b/buffer.c index 39bc1dd..379bda1 100644 --- a/buffer.c +++ b/buffer.c @@ -207,8 +207,7 @@ BufferPos buffer_pos_end_of_file(TextBuffer *buffer) { return (BufferPos){.line = buffer->nlines - 1, .index = buffer->lines[buffer->nlines-1].len}; } -// Get the font used for this buffer. -static Font *buffer_font(TextBuffer *buffer) { +Font *buffer_font(TextBuffer *buffer) { return buffer->ted->font; } diff --git a/config.c b/config.c index 3544273..4109fb7 100644 --- a/config.c +++ b/config.c @@ -91,6 +91,7 @@ static const SettingBool settings_bool[] = { {"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}, + {"document-links", &settings_zero.document_links, true}, {"lsp-enabled", &settings_zero.lsp_enabled, true}, {"lsp-log", &settings_zero.lsp_log, true}, {"hover-enabled", &settings_zero.hover_enabled, true}, diff --git a/gl.c b/gl.c index 4b41f42..c1ec812 100644 --- a/gl.c +++ b/gl.c @@ -230,6 +230,8 @@ void gl_geometry_init(void) { } void gl_geometry_rect(Rect r, u32 color_rgba) { + if (r.size.x <= 0 || r.size.y <= 0) + return; vec4 color = rgba_u32_to_vec4(color_rgba); vec2 p1 = r.pos; diff --git a/ide-definitions.c b/ide-definitions.c index 103d8c4..f5e37b6 100644 --- a/ide-definitions.c +++ b/ide-definitions.c @@ -176,7 +176,21 @@ void definitions_process_lsp_response(Ted *ted, LSP *lsp, const LSPResponse *res const LSPResponseDefinition *response_def = &response->data.definition; if (!arr_len(response_def->locations)) { - // no definition. do the error cursor. + // no definition. + const char* link = NULL; + + // check for document links + TextBuffer *buffer = ted->active_buffer; + if (buffer) { + BufferPos pos = buffer_pos_from_lsp(buffer, response->request.data.definition.position.pos); + link = document_link_at_buffer_pos(ted, pos); + if (link) { + open_with_default_application(link); + return; + } + } + + // do the error cursor. ted_flash_error_cursor(ted); return; } diff --git a/ide-document-link.c b/ide-document-link.c index 9d4803f..2328a6b 100644 --- a/ide-document-link.c +++ b/ide-document-link.c @@ -1,11 +1,61 @@ #include "ted.h" +void document_link_clear(Ted *ted) { + DocumentLinks *dl = &ted->document_links; + arr_foreach_ptr(dl->links, DocumentLink, l) { + free(l->target); + free(l->tooltip); + } + arr_clear(dl->links); + dl->requested_document = 0; +} + +static bool document_link_activation_key_down(Ted *ted) { + return ted_is_ctrl_down(ted); +} + + +static Rect document_link_get_rect(Ted *ted, DocumentLink *link) { + TextBuffer *buffer = ted->active_buffer; + DocumentLinks *dl = &ted->document_links; + if (buffer_lsp_document_id(buffer) != dl->requested_document) { + return (Rect){0}; + } + + vec2 a = buffer_pos_to_pixels(buffer, link->start); + vec2 b = buffer_pos_to_pixels(buffer, link->end); + if (a.y != b.y) { + // multi-line link. let's ignore it because it'd be tough to deal with. + return (Rect){0}; + } + + if (a.x > b.x) { + // swap positions + vec2 temp = a; + a = b; + b = temp; + } + + float y0 = a.y; + float char_height = text_font_char_height(buffer_font(buffer)); + return (Rect) { + .pos = {a.x, y0}, + .size = {b.x - a.x, char_height} + }; +} + void document_link_frame(Ted *ted) { - DocumentLink *document_link = &ted->document_link; + Settings *settings = ted_active_settings(ted); + if (!settings->document_links) { + document_link_clear(ted); + return; + } + DocumentLinks *dl = &ted->document_links; - bool key_down = ted_is_ctrl_down(ted); + bool key_down = document_link_activation_key_down(ted); if (!key_down) { - ted_cancel_lsp_request(ted, &document_link->last_request); + ted_cancel_lsp_request(ted, &dl->last_request); + document_link_clear(ted); return; } @@ -17,38 +67,60 @@ void document_link_frame(Ted *ted) { if (!lsp) return; - if (!document_link->last_request.id) { + if (!dl->last_request.id) { // send the request LSPRequest request = {.type = LSP_REQUEST_DOCUMENT_LINK}; LSPRequestDocumentLink *lnk = &request.data.document_link; lnk->document = buffer_lsp_document_id(buffer); - document_link->last_request = lsp_send_request(lsp, &request); + dl->last_request = lsp_send_request(lsp, &request); + dl->requested_document = lnk->document; + } + + arr_foreach_ptr(dl->links, DocumentLink, l) { + Rect r = document_link_get_rect(ted, l); + if (rect_contains_point(r, ted->mouse_pos)) { + ted->cursor = ted->cursor_hand; + } } } void document_link_process_lsp_response(Ted *ted, const LSPResponse *response) { - DocumentLink *document_link = &ted->document_link; + DocumentLinks *dl = &ted->document_links; if (response->request.type != LSP_REQUEST_DOCUMENT_LINK) return; - if (!document_link->last_request.id) + if (!dl->last_request.id) return; // request was cancelled - bool key_down = ted_is_ctrl_down(ted); + bool key_down = document_link_activation_key_down(ted); if (!key_down) return; TextBuffer *buffer = ted->active_buffer; if (!buffer) return; - if (buffer_lsp_document_id(buffer) != response->request.data.document_link.document) + if (buffer_lsp_document_id(buffer) != dl->requested_document) return; // request was for a different document const LSPResponseDocumentLink *response_data = &response->data.document_link; arr_foreach_ptr(response_data->links, const LSPDocumentLink, link) { - BufferPos start = buffer_pos_from_lsp(buffer, link->range.start); - BufferPos end = buffer_pos_from_lsp(buffer, link->range.end); - printf("%d:%d — %d:%d\t: %s\n", - start.line, start.index, end.line, end.index, - lsp_response_string(response, link->target)); - + DocumentLink *l = arr_addp(dl->links); + l->start = buffer_pos_from_lsp(buffer, link->range.start); + l->end = buffer_pos_from_lsp(buffer, link->range.end); + l->target = str_dup(lsp_response_string(response, link->target)); + const char *tooltip = lsp_response_string(response, link->tooltip); + l->tooltip = *tooltip ? str_dup(tooltip) : NULL; + } +} + +const char *document_link_at_buffer_pos(Ted *ted, BufferPos pos) { + DocumentLinks *dl = &ted->document_links; + TextBuffer *buffer = ted->active_buffer; + if (buffer_lsp_document_id(buffer) != dl->requested_document) { + return NULL; + } + + arr_foreach_ptr(dl->links, DocumentLink, l) { + if (buffer_pos_cmp(pos, l->start) >= 0 && buffer_pos_cmp(pos, l->end) < 0) + return l->target; } + return NULL; } diff --git a/main.c b/main.c index 23e6071..2b7a051 100644 --- a/main.c +++ b/main.c @@ -5,7 +5,6 @@ TODO: FUTURE FEATURES: - autodetect indentation (tabs vs spaces) - robust find (results shouldn't move around when you type things) -- document links using LSP textDocument/documentLink request - rename using LSP (textDocument/rename) - we have request writing & response parsing support for it, but that hasn't been tested yet - i'm putting this off for now since it seems hard to have undo support for it. @@ -1196,6 +1195,7 @@ int main(int argc, char **argv) { autocomplete_close(ted); highlights_close(ted); session_write(ted); + document_link_clear(ted); for (int i = 0; i < TED_LSP_MAX; ++i) { if (!ted->lsps[i]) break; diff --git a/os-posix.c b/os-posix.c index ca950b1..4485845 100644 --- a/os-posix.c +++ b/os-posix.c @@ -388,3 +388,23 @@ int process_check_status(Process **pproc, ProcessExitInfo *info) { return -1; } } + +bool open_with_default_application(const char *path) { + const char *cmd = NULL; +#if __linux__ + cmd = "xdg-open"; +#elif __APPLE__ + cmd = "open"; +#endif + if (!cmd) + return false; + switch (fork()) { + case 0: + execlp(cmd, cmd, path, NULL); + abort(); + case -1: + return false; + default: + return true; + } +} diff --git a/os-win.c b/os-win.c index 043d54f..7d335c7 100644 --- a/os-win.c +++ b/os-win.c @@ -411,3 +411,8 @@ int process_check_status(Process **pprocess, ProcessExitInfo *info) { return -1; } } + + +bool open_with_default_application(const char *path) { + todo +} diff --git a/os.h b/os.h index 48d67c3..3542ffe 100644 --- a/os.h +++ b/os.h @@ -155,5 +155,10 @@ int process_check_status(Process **process, ProcessExitInfo *info); /// `*process` will be set to NULL. void process_kill(Process **process); +/// runs xdg-open or equivalent on the given path, which can be a URL. +/// +/// returns `true` on success. +bool open_with_default_application(const char *path); + #endif // OS_H_ diff --git a/ted.cfg b/ted.cfg index ae7c370..81cdd43 100644 --- a/ted.cfg +++ b/ted.cfg @@ -56,6 +56,9 @@ trigger-characters = on identifier-trigger-characters = off # display "phantom completions"? (if there is only one completion, display it next to the cursor) phantom-completions = on +# "document links" LSP functionality. if enabled, ctrl+clicking on web links +# and such will open them. +document-links = on # 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 2a78e1f..c872697 100644 --- a/ted.h +++ b/ted.h @@ -272,6 +272,7 @@ typedef struct { bool hover_enabled; bool highlight_enabled; bool highlight_auto; + bool document_links; bool vsync; bool save_backup; bool crlf_windows; @@ -665,10 +666,19 @@ typedef struct { Signature signatures[SIGNATURE_HELP_MAX]; } SignatureHelp; +typedef struct { + char *target; + char *tooltip; + BufferPos start; + BufferPos end; +} DocumentLink; + /// "document link" information (LSP) typedef struct { + LSPDocumentID requested_document; LSPServerRequestID last_request; -} DocumentLink; + DocumentLink *links; +} DocumentLinks; /// "hover" information from LSP server typedef struct { @@ -834,7 +844,7 @@ typedef struct Ted { bool building; Autocomplete autocomplete; SignatureHelp signature_help; - DocumentLink document_link; + DocumentLinks document_links; Hover hover; Definitions definitions; Highlights highlights; @@ -969,6 +979,8 @@ bool buffer_pos_valid(TextBuffer *buffer, BufferPos p); Language buffer_language(TextBuffer *buffer); /// clip the rectangle so it's all inside the buffer. returns true if there's any rectangle left. bool buffer_clip_rect(TextBuffer *buffer, Rect *r); +/// Get the font used for this buffer. +Font *buffer_font(TextBuffer *buffer); /// get LSP server which deals with this buffer LSP *buffer_lsp(TextBuffer *buffer); /// Get the settings used for this buffer. @@ -1511,6 +1523,12 @@ void definitions_frame(Ted *ted); // === ide-document-link.c === void document_link_frame(Ted *ted); void document_link_process_lsp_response(Ted *ted, const LSPResponse *response); +/// get document link at this position in the active buffer. +/// +/// the returned pointer won't be freed immediately, but could be on the next frame, +/// so don't keep it around long. +const char *document_link_at_buffer_pos(Ted *ted, BufferPos pos); +void document_link_clear(Ted *ted); // === ide-highlights.c === void highlights_close(Ted *ted); diff --git a/test/lsp/multidef.cpp b/test/lsp/multidef.cpp index b9511d0..da612fc 100644 --- a/test/lsp/multidef.cpp +++ b/test/lsp/multidef.cpp @@ -1,3 +1,5 @@ +#include + class V { void f(int x, int y) { } -- cgit v1.2.3