From 6e6b407550db47d3f9c85875c0f69444cf640796 Mon Sep 17 00:00:00 2001 From: pommicket Date: Thu, 29 Dec 2022 13:38:46 -0500 Subject: hover highlight --- buffer.c | 42 ++++++++++++++++++++++------------ build.c | 4 ++-- colors.h | 2 ++ find.c | 2 +- hover.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++------------ lsp-parse.c | 2 +- main.c | 2 +- ted.cfg | 1 + ui.c | 2 +- 9 files changed, 98 insertions(+), 35 deletions(-) diff --git a/buffer.c b/buffer.c index 70483a8..4562219 100644 --- a/buffer.c +++ b/buffer.c @@ -233,12 +233,12 @@ char32_t buffer_char_after_cursor(TextBuffer *buffer) { return buffer_char_after_pos(buffer, buffer->cursor_pos); } -BufferPos buffer_start_of_file(TextBuffer *buffer) { +BufferPos buffer_pos_start_of_file(TextBuffer *buffer) { (void)buffer; return (BufferPos){.line = 0, .index = 0}; } -BufferPos buffer_end_of_file(TextBuffer *buffer) { +BufferPos buffer_pos_end_of_file(TextBuffer *buffer) { return (BufferPos){.line = buffer->nlines - 1, .index = buffer->lines[buffer->nlines-1].len}; } @@ -493,7 +493,7 @@ static BufferPos buffer_pos_advance(TextBuffer *buffer, BufferPos pos, size_t nc index = 0; ++line; } - return buffer_end_of_file(buffer); + return buffer_pos_end_of_file(buffer); } @@ -947,6 +947,7 @@ void buffer_scroll(TextBuffer *buffer, double dx, double dy) { // returns the position of the character at the given position in the buffer. v2 buffer_pos_to_pixels(TextBuffer *buffer, BufferPos pos) { + buffer_pos_validate(buffer, &pos); u32 line = pos.line, index = pos.index; // we need to convert the index to a column u32 col = buffer_index_to_column(buffer, line, index); @@ -1105,7 +1106,7 @@ i64 buffer_pos_move_horizontally(TextBuffer *buffer, BufferPos *p, i64 by) { } else if (by > 0) { i64 by_start = by; if (p->line >= buffer->nlines) - *p = buffer_end_of_file(buffer); // invalid position; move to end of buffer + *p = buffer_pos_end_of_file(buffer); // invalid position; move to end of buffer Line *line = &buffer->lines[p->line]; while (by > 0) { if (by <= line->len - p->index) { @@ -1386,11 +1387,11 @@ void buffer_cursor_move_to_end_of_line(TextBuffer *buffer) { } void buffer_cursor_move_to_start_of_file(TextBuffer *buffer) { - buffer_cursor_move_to_pos(buffer, buffer_start_of_file(buffer)); + buffer_cursor_move_to_pos(buffer, buffer_pos_start_of_file(buffer)); } void buffer_cursor_move_to_end_of_file(TextBuffer *buffer) { - buffer_cursor_move_to_pos(buffer, buffer_end_of_file(buffer)); + buffer_cursor_move_to_pos(buffer, buffer_pos_end_of_file(buffer)); } @@ -1454,9 +1455,22 @@ LSPDocumentPosition buffer_pos_to_lsp_document_position(TextBuffer *buffer, Buff } BufferPos buffer_pos_from_lsp(TextBuffer *buffer, LSPPosition lsp_pos) { - BufferPos pos = {.line = lsp_pos.line}; - abort(); // @TODO - return pos; + if (lsp_pos.line >= buffer->nlines) { + return buffer_pos_end_of_file(buffer); + } + const Line *line = &buffer->lines[lsp_pos.line]; + const char32_t *str = line->str; + u32 character = 0; + for (u32 i = 0; i < line->len; ++i) { + if (character >= lsp_pos.character) + return (BufferPos){.line = lsp_pos.line, .index = i}; + if (str[i] < 0x10000) + character += 1; + else + character += 2; + } + + return buffer_pos_end_of_line(buffer, lsp_pos.line); } LSPPosition buffer_cursor_pos_as_lsp_position(TextBuffer *buffer) { @@ -1680,11 +1694,11 @@ void buffer_select_to_end_of_line(TextBuffer *buffer) { } void buffer_select_to_start_of_file(TextBuffer *buffer) { - buffer_select_to_pos(buffer, buffer_start_of_file(buffer)); + buffer_select_to_pos(buffer, buffer_pos_start_of_file(buffer)); } void buffer_select_to_end_of_file(TextBuffer *buffer) { - buffer_select_to_pos(buffer, buffer_end_of_file(buffer)); + buffer_select_to_pos(buffer, buffer_pos_end_of_file(buffer)); } // select the word the cursor is inside of @@ -1710,8 +1724,8 @@ void buffer_select_line(TextBuffer *buffer) { } void buffer_select_all(TextBuffer *buffer) { - buffer_cursor_move_to_pos(buffer, buffer_start_of_file(buffer)); - buffer_select_to_pos(buffer, buffer_end_of_file(buffer)); + buffer_cursor_move_to_pos(buffer, buffer_pos_start_of_file(buffer)); + buffer_select_to_pos(buffer, buffer_pos_end_of_file(buffer)); } // stop selecting @@ -2372,7 +2386,7 @@ bool buffer_save(TextBuffer *buffer) { // if the last line isn't empty, add a newline to the end of the file char32_t c = '\n'; String32 s = {&c, 1}; - buffer_insert_text_at_pos(buffer, buffer_end_of_file(buffer), s); + buffer_insert_text_at_pos(buffer, buffer_pos_end_of_file(buffer), s); } } diff --git a/build.c b/build.c index 07d57eb..9c5ee7a 100644 --- a/build.c +++ b/build.c @@ -207,7 +207,7 @@ static void build_frame(Ted *ted, float x1, float y1, float x2, float y2) { } else { if (ret == 0) ret = 1; // got a code point - buffer_insert_char_at_pos(buffer, buffer_end_of_file(buffer), c); + buffer_insert_char_at_pos(buffer, buffer_pos_end_of_file(buffer), c); p += ret; } } @@ -216,7 +216,7 @@ static void build_frame(Ted *ted, float x1, float y1, float x2, float y2) { if (any_text_inserted) { // show bottom of output (only relevant if there are no build errors) - buffer->cursor_pos = buffer_end_of_file(buffer); + buffer->cursor_pos = buffer_pos_end_of_file(buffer); buffer_scroll_to_cursor(buffer); } diff --git a/colors.h b/colors.h index 46d03fd..8bfdc03 100644 --- a/colors.h +++ b/colors.h @@ -35,6 +35,7 @@ typedef enum { COLOR_HOVER_BG, COLOR_HOVER_BORDER, COLOR_HOVER_TEXT, + COLOR_HOVER_HL, COLOR_YES, COLOR_NO, @@ -102,6 +103,7 @@ static ColorName const color_names[] = { {COLOR_HOVER_BORDER, "hover-border"}, {COLOR_HOVER_BG, "hover-bg"}, {COLOR_HOVER_TEXT, "hover-text"}, + {COLOR_HOVER_HL, "hover-hl"}, {COLOR_YES, "yes"}, {COLOR_NO, "no"}, {COLOR_CANCEL, "cancel"}, diff --git a/find.c b/find.c index 5834c04..abdfc3e 100644 --- a/find.c +++ b/find.c @@ -136,7 +136,7 @@ void find_update(Ted *ted, bool force) { find_free_pattern(ted); if (find_compile_pattern(ted)) { - BufferPos pos = buffer_start_of_file(buffer); + BufferPos pos = buffer_pos_start_of_file(buffer); BufferPos best_scroll_candidate = {U32_MAX, U32_MAX}; BufferPos cursor_pos = buffer->cursor_pos; // find all matches diff --git a/hover.c b/hover.c index 708e009..e9f8e36 100644 --- a/hover.c +++ b/hover.c @@ -24,6 +24,7 @@ static bool get_hover_position(Ted *ted, LSPDocumentPosition *pos, TextBuffer ** } return false; } + void hover_send_request(Ted *ted) { Hover *hover = &ted->hover; LSPRequest request = {.type = LSP_REQUEST_HOVER}; @@ -42,12 +43,23 @@ void hover_process_lsp_response(Ted *ted, LSPResponse *response) { Hover *hover = &ted->hover; LSPResponseHover *hover_response = &response->data.hover; - free(hover->text); TextBuffer *buffer=0; - get_hover_position(ted, NULL, &buffer, NULL); + 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; - 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); @@ -106,23 +118,65 @@ void hover_frame(Ted *ted, double dt) { 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; + 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 + 400; + 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) + font->char_height; + float height = (float)(state.y_largest - y) + char_height; if (height > 300) { height = 300; } - state.x = x; + + 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; @@ -133,14 +187,6 @@ void hover_frame(Ted *ted, double dt) { rgba_u32_to_floats(colors[COLOR_HOVER_TEXT], state.color); text_utf8_with_state(font, &state, text); } - if (!buffer_pos_eq(hover->range_start, hover->range_end)) { - // draw the highlight - v2 range_start = buffer_pos_to_pixels(buffer, hover->range_start); - v2 range_end = buffer_pos_to_pixels(buffer, hover->range_end); - range_end.y += font->char_height; - Rect rect = rect_endpoints(range_start, range_end); - gl_geometry_rect(rect, colors[COLOR_HOVER_BG]);//@TODO: HOVER_HL color - } gl_geometry_draw(); text_render(font); diff --git a/lsp-parse.c b/lsp-parse.c index c89a8c4..4b05f29 100644 --- a/lsp-parse.c +++ b/lsp-parse.c @@ -63,7 +63,7 @@ static bool parse_position(LSP *lsp, const JSON *json, JSONValue pos_value, LSPP || !lsp_expect_number(lsp, character, "document column number")) return false; pos->line = (u32)line.val.number; - pos->character = (u32)line.val.number; + pos->character = (u32)character.val.number; return true; } diff --git a/main.c b/main.c index 2087c8e..9f0fef7 100644 --- a/main.c +++ b/main.c @@ -3,8 +3,8 @@ - more LSP stuff: - go to definition using LSP - find usages + - refactoring? - test full unicode position handling -- highlight hover range - hover-enabled, hover-time settings - 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/ted.cfg b/ted.cfg index f954a48..42a0d14 100644 --- a/ted.cfg +++ b/ted.cfg @@ -307,6 +307,7 @@ autocomplete-hl = #f6a3 hover-bg = #000a hover-border = #fffa hover-text = #fff +hover-hl = #ffc4 # these control the text color for various kinds of completions autocomplete-variable = #bfb autocomplete-function = #aaf diff --git a/ui.c b/ui.c index 583962e..94648f8 100644 --- a/ui.c +++ b/ui.c @@ -365,7 +365,7 @@ static char *file_selector_update(Ted *ted, FileSelector *fs) { *p = PATH_SEPARATOR; if (file_selector_cd(ted, fs, dir_name)) { - buffer_delete_chars_at_pos(line_buffer, buffer_start_of_file(line_buffer), last_path_sep + 1); // delete up to and including the last path separator + buffer_delete_chars_at_pos(line_buffer, buffer_pos_start_of_file(line_buffer), last_path_sep + 1); // delete up to and including the last path separator buffer_clear_undo_redo(line_buffer); } else { // delete up to first path separator in line buffer -- cgit v1.2.3