diff options
Diffstat (limited to 'ide-hover.c')
-rw-r--r-- | ide-hover.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/ide-hover.c b/ide-hover.c new file mode 100644 index 0000000..9742e58 --- /dev/null +++ b/ide-hover.c @@ -0,0 +1,195 @@ +// LSP hover information (textDocument/hover request) + +void hover_close(Ted *ted) { + Hover *hover = &ted->hover; + hover->open = false; + free(hover->text); + hover->text = NULL; +} + +static bool get_hover_position(Ted *ted, LSPDocumentPosition *pos, TextBuffer **pbuffer, LSP **lsp) { + // find the buffer where the mouse is + for (int i = 0; i < TED_MAX_BUFFERS; ++i) { + TextBuffer *buffer = &ted->buffers[i]; + if (!buffer->filename) continue; + LSP *l = buffer_lsp(buffer); + if (!l) continue; + BufferPos mouse_pos = {0}; + if (buffer_pixels_to_pos(buffer, ted->mouse_pos, &mouse_pos)) { + if (pos) *pos = buffer_pos_to_lsp_document_position(buffer, mouse_pos); + if (pbuffer) *pbuffer = buffer; + if (lsp) *lsp = l; + return true; + } + } + return false; +} + +void hover_send_request(Ted *ted) { + Hover *hover = &ted->hover; + LSPRequest request = {.type = LSP_REQUEST_HOVER}; + LSPRequestHover *h = &request.data.hover; + LSP *lsp = NULL; + if (get_hover_position(ted, &h->position, NULL, &lsp)) { + hover->requested_position = h->position; + hover->requested_lsp = lsp->id; + lsp_send_request(lsp, &request); + } +} + +void hover_process_lsp_response(Ted *ted, LSPResponse *response) { + if (!response) return; + if (response->request.type != LSP_REQUEST_HOVER) return; + + Hover *hover = &ted->hover; + LSPResponseHover *hover_response = &response->data.hover; + + TextBuffer *buffer=0; + LSPDocumentPosition pos={0}; + LSP *lsp=0; + get_hover_position(ted, &pos, &buffer, &lsp); + + if (hover->text // we already have hover text + && ( + lsp->id != hover->requested_lsp // this request is from a different LSP + || !lsp_document_position_eq(response->request.data.hover.position, pos) // this request is for a different position + )) { + // this is a stale request. ignore it + return; + } + + free(hover->text); hover->text = NULL; + + if (buffer) { + hover->range_start = buffer_pos_from_lsp(buffer, hover_response->range.start); + hover->range_end = buffer_pos_from_lsp(buffer, hover_response->range.end); + } + const char *contents = lsp_response_string(response, hover_response->contents); + if (*contents) { + hover->text = str_dup(contents); + char *p = hover->text + strlen(hover->text) - 1; + // remove trailing whitespace + // (rust-analyzer gives us trailing newlines for local variables) + for (; p > hover->text && isspace(*p); --p) + *p = '\0'; + } +} + +void hover_frame(Ted *ted, double dt) { + const Settings *settings = ted_active_settings(ted); + if (!settings->hover_enabled) + return; + Hover *hover = &ted->hover; + + bool shift_down = SDL_GetKeyboardState(NULL)[SDL_SCANCODE_LSHIFT] + || SDL_GetKeyboardState(NULL)[SDL_SCANCODE_RSHIFT]; + + if (!shift_down) { + hover_close(ted); + } + + (void)dt; + if (!hover->open) { + if (shift_down) { + hover_send_request(ted); + hover->open = true; + } + return; + } + + TextBuffer *buffer=0; + { + LSPDocumentPosition pos={0}; + LSP *lsp=0; + if (get_hover_position(ted, &pos, &buffer, &lsp)) { + if (lsp->id != hover->requested_lsp + || !lsp_document_position_eq(pos, hover->requested_position)) { + // refresh hover + hover_send_request(ted); + } + } else { + hover_close(ted); + return; + } + } + + + + const float padding = settings->padding; + const float border = settings->border_thickness; + const u32 *colors = settings->colors; + const char *text = hover->text; + Font *font = ted->font; + float x = ted->mouse_pos.x, y = ted->mouse_pos.y + font->char_height; + float char_height = font->char_height; + + BufferPos range_start = hover->range_start, range_end = hover->range_end; + if (!buffer_pos_eq(range_start, range_end)) { + // draw the highlight + if (range_start.line == range_end.line) { + v2 a = buffer_pos_to_pixels(buffer, range_start); + v2 b = buffer_pos_to_pixels(buffer, range_end); + b.y += char_height; + gl_geometry_rect(rect_endpoints(a, b), colors[COLOR_HOVER_HL]); + } else if (range_end.line - range_start.line < 1000) { // prevent gigantic highlights from slowing things down + // multiple lines. + v2 a = buffer_pos_to_pixels(buffer, range_start); + v2 b = buffer_pos_to_pixels(buffer, buffer_pos_end_of_line(buffer, range_start.line)); + b.y += char_height; + gl_geometry_rect(rect_endpoints(a, b), colors[COLOR_HOVER_HL]); + + for (u32 line = range_start.line + 1; line < range_end.line; ++line) { + // these lines are fully contained in the range. + BufferPos start = buffer_pos_start_of_line(buffer, line); + BufferPos end = buffer_pos_end_of_line(buffer, line); + a = buffer_pos_to_pixels(buffer, start); + b = buffer_pos_to_pixels(buffer, end); + b.y += char_height; + gl_geometry_rect(rect_endpoints(a, b), colors[COLOR_HOVER_HL]); + } + + // last line + a = buffer_pos_to_pixels(buffer, buffer_pos_start_of_line(buffer, range_end.line)); + b = buffer_pos_to_pixels(buffer, range_end); + b.y += char_height; + gl_geometry_rect(rect_endpoints(a, b), colors[COLOR_HOVER_HL]); + } + + } + if (hover->text) { + float max_width = 400; + TextRenderState state = text_render_state_default; + state.x = state.min_x = x; + state.y = state.min_y = y; + state.render = false; + state.wrap = true; + state.max_x = x + max_width; + state.max_y = ted->window_height; + text_utf8_with_state(font, &state, text); + float width = (float)(state.x_largest - x); + float height = (float)(state.y_largest - y) + char_height; + if (height > 300) { + height = 300; + } + + if (x + width > ted->window_width) + x -= width; // open left + if (y + height > ted->window_height) + y -= height + char_height * 2; // open up + state.x = state.min_x = x; + state.y = state.min_y = y; + state.max_x = x + max_width; + state.y = y; + state.render = true; + state.max_y = y + height; + + Rect rect = rect_xywh(x - padding, y - padding, width + 2*padding, height + 2*padding); + gl_geometry_rect(rect, colors[COLOR_HOVER_BG]); + gl_geometry_rect_border(rect, border, colors[COLOR_HOVER_BORDER]); + rgba_u32_to_floats(colors[COLOR_HOVER_TEXT], state.color); + text_utf8_with_state(font, &state, text); + } + + gl_geometry_draw(); + text_render(font); +} |