summaryrefslogtreecommitdiff
path: root/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'buffer.c')
-rw-r--r--buffer.c168
1 files changed, 150 insertions, 18 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);
+}