From 5cad1bee9b72610d9d97b5f97e7f1a245a2d2ba5 Mon Sep 17 00:00:00 2001 From: pommicket Date: Thu, 7 Sep 2023 15:18:45 -0400 Subject: more diagnostics --- buffer.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++------ colors.c | 30 ++++++++++ find.c | 4 +- ide-autocomplete.c | 4 +- ide-document-link.c | 2 +- ide-hover.c | 2 +- lsp-parse.c | 2 +- main.c | 8 +-- node.c | 8 ++- ted-internal.h | 3 +- ted.c | 31 +++++++--- ted.h | 23 +++++-- ui.c | 4 +- 13 files changed, 241 insertions(+), 48 deletions(-) diff --git a/buffer.c b/buffer.c index 1d8effd..8d48c6d 100644 --- a/buffer.c +++ b/buffer.c @@ -28,6 +28,14 @@ struct BufferEdit { double time; // time at start of edit (i.e. the time just before the edit), in seconds since epoch }; +typedef struct { + MessageType severity; + BufferPos pos; + char *message; + // may be NULL + char *url; +} Diagnostic; + struct TextBuffer { /// NULL if this buffer is untitled or doesn't correspond to a file (e.g. line buffers) char *path; @@ -98,6 +106,8 @@ struct TextBuffer { /// see \ref frame_earliest_line_modified. u32 frame_latest_line_modified; + Diagnostic *diagnostics; + /// lines Line *lines; /// last error @@ -974,6 +984,19 @@ static void buffer_line_free(Line *line) { free(line->str); } +static void diagnostic_free(Diagnostic *diagnostic) { + free(diagnostic->message); + free(diagnostic->url); + memset(diagnostic, 0, sizeof *diagnostic); +} + +static void buffer_diagnostics_clear(TextBuffer *buffer) { + arr_foreach_ptr(buffer->diagnostics, Diagnostic, d) { + diagnostic_free(d); + } + arr_clear(buffer->diagnostics); +} + static void buffer_free_inner(TextBuffer *buffer) { Ted *ted = buffer->ted; if (!ted->quit) { // don't send didClose on quit (calling buffer_lsp would actually create a LSP if this is called after destroying all the LSPs which isnt good) @@ -995,7 +1018,7 @@ static void buffer_free_inner(TextBuffer *buffer) { buffer_edit_free(edit); arr_foreach_ptr(buffer->redo_history, BufferEdit, edit) buffer_edit_free(edit); - + buffer_diagnostics_clear(buffer); arr_free(buffer->undo_history); arr_free(buffer->redo_history); memset(buffer, 0, sizeof *buffer); @@ -1386,6 +1409,24 @@ i64 buffer_pos_move_down(TextBuffer *buffer, BufferPos *pos, i64 by) { return +buffer_pos_move_vertically(buffer, pos, +by); } +bool buffer_pos_move_according_to_edit(BufferPos *pos, const EditInfo *edit) { + if (buffer_pos_cmp(*pos, edit->pos) <= 0) + return true; + if (edit->chars_inserted) { + if (edit->pos.line == pos->line) { + pos->index += edit->end.index - edit->pos.index; + } + pos->line += edit->end.line - edit->pos.line; + } else { + if (buffer_pos_cmp(*pos, edit->end) < 0) + return false; + if (pos->line == edit->end.line) + pos->index += edit->pos.index - edit->end.index; + pos->line -= edit->end.line - edit->pos.line; + } + return true; +} + static bool buffer_line_is_blank(Line *line) { for (u32 i = 0; i < line->len; ++i) if (!is32_space(line->str[i])) @@ -1947,11 +1988,14 @@ BufferPos buffer_insert_text_at_pos(TextBuffer *buffer, BufferPos pos, String32 const EditInfo info = { .pos = pos, + .end = b, .chars_deleted = 0, - .newlines_deleted = 0, .chars_inserted = insertion_len, - .newlines_inserted = n_added_lines, }; + // move diagnostics around as needed + arr_foreach_ptr(buffer->diagnostics, Diagnostic, d) { + buffer_pos_move_according_to_edit(&d->pos, &info); + } signature_help_retrigger(buffer->ted); arr_foreach_ptr(buffer->ted->edit_notifys, EditNotifyInfo, n) { @@ -2424,8 +2468,8 @@ void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, i64 nchars_) u32 line_idx = pos.line; u32 index = pos.index; Line *line = &buffer->lines[line_idx], *lines_end = &buffer->lines[buffer->nlines]; - u32 newlines_deleted = 0; - + const BufferPos end_pos = buffer_pos_advance(buffer, pos, nchars); + if (nchars + index > line->len) { // delete rest of line nchars -= line->len - index + 1; // +1 for the newline that got deleted @@ -2440,7 +2484,6 @@ void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, i64 nchars_) // delete everything to the end of the file for (u32 idx = line_idx + 1; idx < buffer->nlines; ++idx) { buffer_line_free(&buffer->lines[idx]); - ++newlines_deleted; } buffer_shorten(buffer, line_idx + 1); } else { @@ -2453,7 +2496,7 @@ void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, i64 nchars_) // remove all lines between line + 1 and last_line (inclusive). buffer_delete_lines(buffer, line_idx + 1, (u32)(last_line - line)); - newlines_deleted = (u32)(last_line - line); + const u32 newlines_deleted = (u32)(last_line - line); buffer_shorten(buffer, buffer->nlines - newlines_deleted); } } else { @@ -2474,11 +2517,14 @@ void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, i64 nchars_) const EditInfo info = { .pos = pos, + .end = end_pos, .chars_inserted = 0, - .newlines_inserted = 0, .chars_deleted = deletion_len, - .newlines_deleted = newlines_deleted, }; + // move diagnostics around as needed + arr_foreach_ptr(buffer->diagnostics, Diagnostic, d) { + buffer_pos_move_according_to_edit(&d->pos, &info); + } arr_foreach_ptr(buffer->ted->edit_notifys, EditNotifyInfo, n) { n->fn(n->context, buffer, &info); } @@ -3268,9 +3314,10 @@ void buffer_render(TextBuffer *buffer, Rect r) { float render_start_y = y1 - (float)(buffer->scroll_y - start_line) * char_height; // where the 1st line is rendered - + const Diagnostic *hover_diagnostic = NULL; // line numbering if (!buffer->is_line_buffer && settings->line_numbers) { + const Diagnostic *diagnostic = arr_len(buffer->diagnostics) ? buffer->diagnostics : NULL; float max_digit_width = 0; for (char32_t digit = '0'; digit <= '9'; ++digit) { max_digit_width = maxf(max_digit_width, text_font_char_width(font, digit)); @@ -3285,23 +3332,56 @@ void buffer_render(TextBuffer *buffer, Rect r) { text_state.max_y = y2; float y = render_start_y; - u32 cursor_line = buffer->cursor_pos.line; + const u32 cursor_line = buffer->cursor_pos.line; + const float diagnostic_x2 = x1 + line_number_width + 2; for (u32 line = start_line; line < nlines; ++line) { char str[32] = {0}; strbuf_printf(str, "%" PRIu32, line + 1); // convert line number to string - float x = x1 + line_number_width - text_get_size_vec2(font, str).x; // right justify + const float x = x1 + line_number_width - text_get_size_vec2(font, str).x; // right justify + u32 line_number_color = settings_color(settings, line == cursor_line ? COLOR_CURSOR_LINE_NUMBER : COLOR_LINE_NUMBERS); + if (diagnostic) { + while (diagnostic->pos.line < line) { + ++diagnostic; + if (diagnostic == buffer->diagnostics + arr_len(buffer->diagnostics)) { + diagnostic = NULL; + break; + } + } + } + if (diagnostic && diagnostic->pos.line == line) { + // show diagnostic + ColorSetting color_setting=0; + ted_color_settings_for_message_type(diagnostic->severity, NULL, &color_setting); + u32 color = settings_color(settings, color_setting) | 0xff; + u32 alt_line_number_color = line == cursor_line ? 0xffffffff : 0x000000ff; + if (color_contrast_ratio_u32(color, line_number_color) + < color_contrast_ratio_u32(color, alt_line_number_color)) { + // change color so that line number is still visible + line_number_color = alt_line_number_color; + } + const Rect rect = rect4(x1, y, diagnostic_x2, y + char_height); + gl_geometry_rect( + rect, + color & 0xffffff7f + ); + if (ted_mouse_in_rect(ted, rect)) { + hover_diagnostic = diagnostic; + if (diagnostic->url && ted_clicked_in_rect(ted, rect)) + open_with_default_application(diagnostic->url); + ted->cursor = ted->cursor_hand; + } + } // set color - settings_color_floats(settings, line == cursor_line ? COLOR_CURSOR_LINE_NUMBER : COLOR_LINE_NUMBERS, - text_state.color); + rgba_u32_to_floats(line_number_color, text_state.color); text_state.x = x; text_state.y = y; text_state_break_kerning(&text_state); text_utf8_with_state(font, &text_state, str); y += char_height; + if (y > y2) break; } - x1 += line_number_width; - x1 += 2; // a little bit of padding + x1 = diagnostic_x2; // line separating line numbers from text gl_geometry_rect(rect_xywh(x1, y1, border_thickness, y2 - y1), settings_color(settings, COLOR_LINE_NUMBERS_SEPARATOR)); x1 += border_thickness; @@ -3321,7 +3401,7 @@ void buffer_render(TextBuffer *buffer, Rect r) { } // change cursor to ibeam when it's hovering over the buffer - if ((!menu_is_any_open(ted) || buffer == ted->line_buffer) && rect_contains_point(rect4(x1, y1, x2, y2), ted->mouse_pos)) { + if ((!menu_is_any_open(ted) || buffer == ted->line_buffer) && ted_mouse_in_rect(ted, rect4(x1, y1, x2, y2))) { ted->cursor = ted->cursor_ibeam; } @@ -3331,7 +3411,7 @@ void buffer_render(TextBuffer *buffer, Rect r) { buffer->center_cursor_next_frame = false; } - if (rect_contains_point(rect4(x1, y1, x2, y2), ted->mouse_pos) && !menu_is_any_open(ted)) { + if (ted_mouse_in_rect(ted, rect4(x1, y1, x2, y2)) && !menu_is_any_open(ted)) { // scroll with mouse wheel double scroll_speed = 2.5; buffer_scroll(buffer, ted->scroll_total_x * scroll_speed, ted->scroll_total_y * scroll_speed); @@ -3521,6 +3601,8 @@ void buffer_render(TextBuffer *buffer, Rect r) { } gl_geometry_draw(); } + + //TODO(hover_diagnostic); } @@ -3745,3 +3827,53 @@ void buffer_highlight_lsp_range(TextBuffer *buffer, LSPRange range, ColorSetting gl_geometry_rect(r2, settings_color(settings, COLOR_HOVER_HL)); } } + +static MessageType diagnostic_severity(const LSPDiagnostic *diagnostic) { + switch (diagnostic->severity) { + case LSP_DIAGNOSTIC_SEVERITY_ERROR: + return MESSAGE_ERROR; + case LSP_DIAGNOSTIC_SEVERITY_WARNING: + return MESSAGE_WARNING; + case LSP_DIAGNOSTIC_SEVERITY_INFORMATION: + case LSP_DIAGNOSTIC_SEVERITY_HINT: + return MESSAGE_INFO; + } + assert(0); + return MESSAGE_INFO; +} + +static int diagnostic_cmp(const void *av, const void *bv) { + const Diagnostic *a = av, *b = bv; + // first sort by line + if (a->pos.line < b->pos.line) return -1; + if (a->pos.line > b->pos.line) return 1; + + // then put higher severity diagnostics first + if (a->severity < b->severity) return 1; + if (a->severity > b->severity) return -1; + + return 0; +} + +void buffer_publish_diagnostics(TextBuffer *buffer, const LSPRequest *request, LSPDiagnostic *diagnostics) { + buffer_diagnostics_clear(buffer); + arr_foreach_ptr(diagnostics, const LSPDiagnostic, diagnostic) { + Diagnostic *d = arr_addp(buffer->diagnostics); + d->pos = buffer_pos_from_lsp(buffer, diagnostic->range.start); + d->severity = diagnostic_severity(diagnostic); + char message[280]; + const char *code = lsp_request_string(request, diagnostic->code); + if (*code) { + str_printf(message, sizeof message - 4, "[%s] %s", code, + lsp_request_string(request, diagnostic->message)); + } else { + str_cpy(message, sizeof message - 4, + lsp_request_string(request, diagnostic->message)); + } + strcpy(&message[sizeof message - 4], "..."); + d->message = str_dup(message); + const char *url = lsp_request_string(request, diagnostic->code_description_uri); + d->url = *url ? str_dup(url) : NULL; + } + arr_qsort(buffer->diagnostics, diagnostic_cmp); +} diff --git a/colors.c b/colors.c index 3e704e9..d9ef7aa 100644 --- a/colors.c +++ b/colors.c @@ -169,3 +169,33 @@ u32 color_apply_opacity(u32 color, float opacity) { opacity = clampf(opacity, 0.0f, 1.0f); return (color & 0xffffff00) | (u32)((color & 0xff) * opacity); } + + +static float color_relative_luminance(const float rgb[3]) { + // see https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef + float c[3]; + for (int i = 0; i < 3; ++i) { + const float x = rgb[i]; + c[i] = x <= 0.03928f ? x * (1.0f / 12.92f) : powf((x + 0.055f) * (1.0f / 1.055f), 2.4f); + } + return 0.2126f * c[0] + 0.7152f * c[1] + 0.0722f * c[2]; +} + +float color_contrast_ratio(const float rgb1[3], const float rgb2[3]) { + // see https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef + float l1 = color_relative_luminance(rgb1); + float l2 = color_relative_luminance(rgb2); + if (l1 < l2) { + float temp = l1; + l1 = l2; + l2 = temp; + } + return (l1 + 0.05f) / (l2 + 0.05f); +} + +float color_contrast_ratio_u32(u32 color1, u32 color2) { + float rgb1[4], rgb2[4]; + rgba_u32_to_floats(color1, rgb1); + rgba_u32_to_floats(color2, rgb2); + return color_contrast_ratio(rgb1, rgb2); +} diff --git a/find.c b/find.c index f5fd9ce..b5fee08 100644 --- a/find.c +++ b/find.c @@ -538,7 +538,7 @@ static void find_edit_notify(void *context, TextBuffer *buffer, const EditInfo * const u32 line = info->pos.line; if (info->chars_inserted) { - const u32 newlines_inserted = info->newlines_inserted; + const u32 newlines_inserted = info->end.line - info->pos.line; if (newlines_inserted) { // update line numbers for find results after insertion. @@ -553,7 +553,7 @@ static void find_edit_notify(void *context, TextBuffer *buffer, const EditInfo * find_research_lines(ted, line, line + newlines_inserted); } else if (info->chars_deleted) { - const u32 newlines_deleted = info->newlines_deleted; + const u32 newlines_deleted = info->end.line - info->pos.line; if (newlines_deleted) { // update line numbers for find results after deletion. diff --git a/ide-autocomplete.c b/ide-autocomplete.c index 6f67506..295dcda 100644 --- a/ide-autocomplete.c +++ b/ide-autocomplete.c @@ -616,7 +616,7 @@ void autocomplete_frame(Ted *ted) { ac->rect = menu_rect; } - i32 mouse_entry = scroll + (i32)((ted->mouse_pos.y - start_y) / char_height); + i32 mouse_entry = scroll + (i32)((ted_mouse_pos(ted).y - start_y) / char_height); Autocompletion *document = NULL; if (ncompletions) { @@ -629,7 +629,7 @@ void autocomplete_frame(Ted *ted) { } } if (mouse_entry >= 0 && mouse_entry < (i32)ncompletions - && rect_contains_point(ac->rect, ted->mouse_pos)) { + && ted_mouse_in_rect(ted, ac->rect)) { // highlight moused over entry Rect r = rect_xywh(x, start_y + (float)(mouse_entry - scroll) * char_height, menu_width, char_height); gl_geometry_rect(r, colors[COLOR_AUTOCOMPLETE_HL]); diff --git a/ide-document-link.c b/ide-document-link.c index 2d43807..71307b5 100644 --- a/ide-document-link.c +++ b/ide-document-link.c @@ -101,7 +101,7 @@ void document_link_frame(Ted *ted) { arr_foreach_ptr(dl->links, DocumentLink, l) { Rect r = document_link_get_rect(ted, l); - if (rect_contains_point(r, ted->mouse_pos)) { + if (ted_mouse_in_rect(ted, r)) { ted->cursor = ted->cursor_hand; } } diff --git a/ide-hover.c b/ide-hover.c index ae870c3..59498f7 100644 --- a/ide-hover.c +++ b/ide-hover.c @@ -159,7 +159,7 @@ void hover_frame(Ted *ted, double dt) { const char *text = hover->text; Font *font = ted->font; float char_height = text_font_char_height(font); - float x = ted->mouse_pos.x, y = ted->mouse_pos.y + char_height; + float x = ted_mouse_pos(ted).x, y = ted_mouse_pos(ted).y + char_height; buffer_highlight_lsp_range(buffer, hover->range, COLOR_HOVER_HL); diff --git a/lsp-parse.c b/lsp-parse.c index 5337de5..df87a13 100644 --- a/lsp-parse.c +++ b/lsp-parse.c @@ -714,7 +714,7 @@ static bool parse_diagnostic(LSP *lsp, LSPRequest *request, const JSON *json, JS return false; diagnostic_out->message = lsp_request_add_json_string( request, json, - json_object_get_string(json, diagnostic_in, "request") + json_object_get_string(json, diagnostic_in, "message") ); JSONValue severity_val = json_object_get(json, diagnostic_in, "severity"); LSPDiagnosticSeverity severity = LSP_DIAGNOSTIC_SEVERITY_INFORMATION; diff --git a/main.c b/main.c index 12324ce..dbfb6a1 100644 --- a/main.c +++ b/main.c @@ -641,7 +641,7 @@ int main(int argc, char **argv) { } else if (key_modifier == 0) { // scroll with mouse wheel Sint32 dx = event.wheel.x, dy = -event.wheel.y; - if (autocomplete_box_contains_point(ted, ted->mouse_pos)) { + if (autocomplete_box_contains_point(ted, ted_mouse_pos(ted))) { autocomplete_scroll(ted, dy); } else { ted->scroll_total_x += dx; @@ -886,7 +886,7 @@ int main(int argc, char **argv) { ted_log(ted, "%s\n", lsp_request_string(r, m->message)); } break; case LSP_REQUEST_PUBLISH_DIAGNOSTICS: { - ted_process_publish_diagnostics(ted, r); + ted_process_publish_diagnostics(ted, lsp, r); } break; default: break; } @@ -984,7 +984,7 @@ int main(int argc, char **argv) { if (ted->resizing_build_output) { if (ted->mouse_state & SDL_BUTTON_LMASK) { // resize it - ted->build_output_height = clampf((y2 - ted->mouse_pos.y) / ted->window_height, 0.05f, 0.8f); + ted->build_output_height = clampf((y2 - ted_mouse_pos(ted).y) / ted->window_height, 0.05f, 0.8f); } else { // stop resizing build output ted->resizing_build_output = false; @@ -996,7 +996,7 @@ int main(int argc, char **argv) { // start resizing build output ted->resizing_build_output = true; } - if (rect_contains_point(gap, ted->mouse_pos)) { + if (ted_mouse_in_rect(ted, gap)) { ted->cursor = ted->cursor_resize_v; } } diff --git a/node.c b/node.c index ee99883..aaa730f 100644 --- a/node.c +++ b/node.c @@ -383,7 +383,8 @@ void node_frame(Ted *ted, Node *node, Rect r) { if (node == ted->dragging_tab_node && i == ted->dragging_tab_idx) { // make tab follow mouse - tab_rect.pos = vec2_add(tab_rect.pos, vec2_sub(ted->mouse_pos, ted->dragging_tab_origin)); + tab_rect.pos = vec2_add(tab_rect.pos, + vec2_sub(ted_mouse_pos(ted), ted->dragging_tab_origin)); } // tab border @@ -447,7 +448,8 @@ void node_frame(Ted *ted, Node *node, Rect r) { ted->resizing_split = NULL; } else { // resize the split - float mouse_coord = node->split_vertical ? ted->mouse_pos.y : ted->mouse_pos.x; + const vec2 mouse_pos = ted_mouse_pos(ted); + float mouse_coord = node->split_vertical ? mouse_pos.y : mouse_pos.x; float rect_coord1 = (node->split_vertical ? rect_y1 : rect_x1)(r); float rect_coord2 = (node->split_vertical ? rect_y2 : rect_x2)(r); // make sure the split doesn't make one of the sides too small @@ -469,7 +471,7 @@ void node_frame(Ted *ted, Node *node, Rect r) { r2.size.x = r.size.x - split_pos - padding; r_between = rect_xywh(r.pos.x + split_pos - padding, r.pos.y, 2 * padding, r.size.y); } - if (rect_contains_point(r_between, ted->mouse_pos)) { + if (ted_mouse_in_rect(ted, r_between)) { ted->cursor = resize_cursor; } if (ted_clicked_in_rect(ted, r_between)) diff --git a/ted-internal.h b/ted-internal.h index 49ca912..e5b9874 100644 --- a/ted-internal.h +++ b/ted-internal.h @@ -464,6 +464,7 @@ bool buffer_handle_click(Ted *ted, TextBuffer *buffer, vec2 click, u8 times); void buffer_center_cursor_next_frame(TextBuffer *buffer); /// perform a series of checks to make sure the buffer doesn't have any invalid values void buffer_check_valid(TextBuffer *buffer); +void buffer_publish_diagnostics(TextBuffer *buffer, const LSPRequest *request, LSPDiagnostic *diagnostics); // === build.c === void build_frame(Ted *ted, float x1, float y1, float x2, float y2); @@ -712,6 +713,6 @@ void ted_load_fonts(Ted *ted); /// Free all of ted's fonts. void ted_free_fonts(Ted *ted); /// process textDocument/publishDiagnostics request -void ted_process_publish_diagnostics(Ted *ted, LSPRequest *request); +void ted_process_publish_diagnostics(Ted *ted, LSP *lsp, LSPRequest *request); #endif // TED_INTERNAL_H_ diff --git a/ted.c b/ted.c index 7ce2e87..ec04e0b 100644 --- a/ted.c +++ b/ted.c @@ -745,7 +745,7 @@ bool ted_get_mouse_buffer_pos(Ted *ted, TextBuffer **pbuffer, BufferPos *ppos) { arr_foreach_ptr(ted->buffers, TextBufferPtr, pbuf) { TextBuffer *buffer = *pbuf; BufferPos pos = {0}; - if (buffer_pixels_to_pos(buffer, ted->mouse_pos, &pos)) { + if (buffer_pixels_to_pos(buffer, ted_mouse_pos(ted), &pos)) { if (ppos) *ppos = pos; if (pbuffer) *pbuffer = buffer; return true; @@ -826,16 +826,16 @@ MessageType ted_message_type_from_lsp(LSPWindowMessageType type) { void ted_color_settings_for_message_type(MessageType type, ColorSetting *bg_color, ColorSetting *border_color) { switch (type) { case MESSAGE_ERROR: - *bg_color = COLOR_ERROR_BG; - *border_color = COLOR_ERROR_BORDER; + if (bg_color) *bg_color = COLOR_ERROR_BG; + if (border_color) *border_color = COLOR_ERROR_BORDER; break; case MESSAGE_WARNING: - *bg_color = COLOR_WARNING_BG; - *border_color = COLOR_WARNING_BORDER; + if (bg_color) *bg_color = COLOR_WARNING_BG; + if (border_color) *border_color = COLOR_WARNING_BORDER; break; case MESSAGE_INFO: - *bg_color = COLOR_INFO_BG; - *border_color = COLOR_INFO_BORDER; + if (bg_color) *bg_color = COLOR_INFO_BG; + if (border_color) *border_color = COLOR_INFO_BORDER; break; } } @@ -876,8 +876,21 @@ bool ted_close_buffer_with_file(Ted *ted, const char *path) { return true; } -void ted_process_publish_diagnostics(Ted *ted, LSPRequest *request) { +void ted_process_publish_diagnostics(Ted *ted, LSP *lsp, LSPRequest *request) { assert(request->type == LSP_REQUEST_PUBLISH_DIAGNOSTICS); LSPRequestPublishDiagnostics *pub = &request->data.publish_diagnostics; - printf("%u diagnostics\n",arr_len(pub->diagnostics)); + const char *path = lsp_document_path(lsp, pub->document); + TextBuffer *buffer = ted_get_buffer_with_file(ted, path); + if (buffer) { + buffer_publish_diagnostics(buffer, request, pub->diagnostics); + } +} + + +vec2 ted_mouse_pos(Ted *ted) { + return ted->mouse_pos; +} + +bool ted_mouse_in_rect(Ted *ted, Rect r) { + return rect_contains_point(r, ted->mouse_pos); } diff --git a/ted.h b/ted.h index d35fd74..8de7fc4 100644 --- a/ted.h +++ b/ted.h @@ -314,6 +314,11 @@ typedef struct { typedef struct { /// position where the edit took place BufferPos pos; + /// "end" position + /// + /// for insertions, this is the position of one past the last character inserted, + /// for deletions, this is the position of the end of the deletion prior to applying the edit + BufferPos end; /// number of characters (unicode codepoints, including newlines) deleted /// /// if this is non-zero, \ref chars_inserted will be zero. @@ -322,10 +327,6 @@ typedef struct { /// /// if this is non-zero, \ref chars_deleted will be zero. u32 chars_inserted; - /// number of newlines deleted - u32 newlines_deleted; - /// number of newlines inserted - u32 newlines_inserted; } EditInfo; /// this type of callback is called right after an edit is made to the buffer. @@ -423,6 +424,10 @@ char32_t buffer_pos_move_to_matching_bracket(TextBuffer *buffer, BufferPos *pos) /// /// \returns `true` if cursor was to the right of a bracket. bool buffer_cursor_move_to_matching_bracket(TextBuffer *buffer); +/// move `*pos` so that it stays in the same place after `edit` is applied. +/// +/// if `pos` was deleted by `edit`, returns `false`. +bool buffer_pos_move_according_to_edit(BufferPos *pos, const EditInfo *edit); /// ensures that `p` refers to a valid position, moving it if needed. void buffer_pos_validate(TextBuffer *buffer, BufferPos *p); /// is this a valid buffer position? @@ -796,6 +801,12 @@ Status color_from_str(const char *str, u32 *color); u32 color_blend(u32 bg, u32 fg); /// multiply color's alpha value by `opacity`. u32 color_apply_opacity(u32 color, float opacity); +/// get WCAG contrast ratio between colors +float color_contrast_ratio(const float rgb1[3], const float rgb2[3]); +/// get WCAG contrast ratio between colors. +/// +/// the "alpha" components (i.e. lowest 8 bits) of `color1`, `color2` are ignored +float color_contrast_ratio_u32(u32 color1, u32 color2); // === command.c === /// parse command @@ -1138,6 +1149,10 @@ void ted_close_buffer(Ted *ted, TextBuffer *buffer); bool ted_close_buffer_with_file(Ted *ted, const char *path); /// save all buffers bool ted_save_all(Ted *ted); +/// get mouse position +vec2 ted_mouse_pos(Ted *ted); +/// test whether mouse is in rect +bool ted_mouse_in_rect(Ted *ted, Rect r); /// reload all buffers from their files void ted_reload_all(Ted *ted); /// Change ted's font size. diff --git a/ui.c b/ui.c index f359241..3dd7875 100644 --- a/ui.c +++ b/ui.c @@ -324,7 +324,7 @@ void selector_render(Ted *ted, Selector *s) { float x = r_unclipped.pos.x, y = r_unclipped.pos.y; text_state.x = x; text_state.y = y; - if (rect_contains_point(r_clipped, ted->mouse_pos) || (s->enable_cursor && s->cursor == i)) { + if (ted_mouse_in_rect(ted, r_clipped) || (s->enable_cursor && s->cursor == i)) { // highlight it gl_geometry_rect(r_clipped, settings_color(settings, COLOR_MENU_HL)); } @@ -661,7 +661,7 @@ vec2 button_get_size(Ted *ted, const char *text) { } void button_render(Ted *ted, Rect button, const char *text, u32 color) { - if (rect_contains_point(button, ted->mouse_pos)) { + if (ted_mouse_in_rect(ted, button)) { // highlight button when hovering over it u32 new_color = (color & 0xffffff00) | ((color & 0xff) / 3); gl_geometry_rect(button, new_color); -- cgit v1.2.3