#include "core/editor.h" #include #include #include static EditorColor make_color(int r, int g, int b, int a) { if (r < 0) r = 0; if (g < 0) g = 0; if (b < 0) b = 0; if (a < 0) a = 0; if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; if (a > 255) a = 255; EditorColor c; c.r = (uint8_t)r; c.g = (uint8_t)g; c.b = (uint8_t)b; c.a = (uint8_t)a; return c; } static Buffer *active_buffer(Editor *editor) { if (editor->current_buffer < 0 || editor->current_buffer >= editor->buffer_count) { return NULL; } return &editor->buffers[editor->current_buffer]; } static const Buffer *active_buffer_const(const Editor *editor) { if (editor->current_buffer < 0 || editor->current_buffer >= editor->buffer_count) { return NULL; } return &editor->buffers[editor->current_buffer]; } static void clamp_point(Editor *editor) { Buffer *buffer = active_buffer(editor); if (!buffer) { return; } size_t point = buffer_point(buffer); size_t len = buffer_length(buffer); if (point > len) { buffer_set_point(buffer, len); } } static bool valid_aux_index(const Editor *editor, int aux_index) { return aux_index >= 0 && aux_index < editor->aux_buffer_count; } static bool valid_buffer_index(const Editor *editor, int index) { return index >= 0 && index < editor->buffer_count; } bool editor_init(Editor *editor) { editor->buffer_count = 0; editor->current_buffer = 0; if (!buffer_init(&editor->buffers[0])) { return false; } editor->buffer_count = 1; editor->aux_buffer_count = 0; editor->desired_col = -1; editor->scroll_line = 0; snprintf(editor->font_query, sizeof(editor->font_query), "%s", "monospace:size=14"); editor->font_dirty = true; editor->bg_top = make_color(248, 248, 248, 255); editor->bg_bottom = make_color(238, 238, 238, 255); editor->text = make_color(56, 60, 67, 255); editor->cursor = make_color(176, 65, 11, 255); editor->cursor_shape = LEMACS_CURSOR_BAR; editor->cursor_thickness = 2; editor->view_count = 0; editor->should_quit = false; return true; } void editor_free(Editor *editor) { for (int i = 0; i < editor->buffer_count; ++i) { buffer_free(&editor->buffers[i]); } for (int i = 0; i < editor->aux_buffer_count; ++i) { buffer_free(&editor->aux_buffers[i]); } editor->buffer_count = 0; editor->current_buffer = 0; editor->aux_buffer_count = 0; editor->view_count = 0; } void editor_set_font_query(Editor *editor, const char *query) { snprintf(editor->font_query, sizeof(editor->font_query), "%s", (query && query[0] != '\0') ? query : "monospace:size=14"); editor->font_dirty = true; } const char *editor_font_query(const Editor *editor) { return editor->font_query; } bool editor_consume_font_dirty(Editor *editor) { bool dirty = editor->font_dirty; editor->font_dirty = false; return dirty; } bool editor_set_theme_color(Editor *editor, const char *name, int r, int g, int b, int a) { EditorColor c = make_color(r, g, b, a); if (strcmp(name, "bg_top") == 0) { editor->bg_top = c; return true; } if (strcmp(name, "bg_bottom") == 0) { editor->bg_bottom = c; return true; } if (strcmp(name, "text") == 0) { editor->text = c; return true; } if (strcmp(name, "cursor") == 0) { editor->cursor = c; return true; } if (strcmp(name, "accent") == 0) { editor->cursor = c; return true; } return false; } bool editor_set_cursor_shape(Editor *editor, int shape) { if (shape < LEMACS_CURSOR_BAR || shape > LEMACS_CURSOR_UNDERLINE) { return false; } editor->cursor_shape = shape; return true; } void editor_set_cursor_thickness(Editor *editor, int thickness) { if (thickness < 1) { thickness = 1; } if (thickness > 24) { thickness = 24; } editor->cursor_thickness = thickness; } int editor_cursor_shape(const Editor *editor) { return editor->cursor_shape; } int editor_cursor_thickness(const Editor *editor) { return editor->cursor_thickness; } int editor_aux_buffer_new(Editor *editor) { if (editor->aux_buffer_count >= LEMACS_MAX_AUX_BUFFERS) { return -1; } int index = editor->aux_buffer_count; if (!buffer_init(&editor->aux_buffers[index])) { return -1; } editor->aux_buffer_count++; return index; } bool editor_aux_buffer_set_text(Editor *editor, int aux_index, const char *text) { if (!valid_aux_index(editor, aux_index)) { return false; } Buffer *buffer = &editor->aux_buffers[aux_index]; buffer->gap_start = 0; buffer->gap_end = buffer->capacity; buffer->path[0] = '\0'; buffer->modified = false; if (!text || text[0] == '\0') { return true; } return buffer_insert_cstr(buffer, text); } Buffer *editor_aux_buffer_get(Editor *editor, int aux_index) { if (!valid_aux_index(editor, aux_index)) { return NULL; } return &editor->aux_buffers[aux_index]; } void editor_layout_reset(Editor *editor) { editor->view_count = 0; } bool editor_layout_add_view(Editor *editor, int source_type, int source_index, int x, int y, int width, int height, bool show_cursor, EditorColor bg, EditorColor fg) { if (editor->view_count >= LEMACS_MAX_VIEWS) { return false; } if (width <= 0 || height <= 0) { return false; } if (source_type == 1 && !valid_aux_index(editor, source_index)) { return false; } if (source_type != 0 && source_type != 1) { return false; } EditorView *view = &editor->views[editor->view_count++]; view->x = x; view->y = y; view->width = width; view->height = height; view->source_type = source_type; view->source_index = source_index; view->show_cursor = show_cursor; view->bg = bg; view->fg = fg; return true; } void editor_move_left(Editor *editor) { Buffer *buffer = active_buffer(editor); if (!buffer) { return; } size_t point = buffer_point(buffer); if (point > 0) { buffer_set_point(buffer, point - 1); } editor->desired_col = -1; } void editor_move_right(Editor *editor) { Buffer *buffer = active_buffer(editor); if (!buffer) { return; } size_t point = buffer_point(buffer); size_t len = buffer_length(buffer); if (point < len) { buffer_set_point(buffer, point + 1); } editor->desired_col = -1; } void editor_move_up(Editor *editor) { Buffer *buffer = active_buffer(editor); if (!buffer) { return; } size_t point = buffer_point(buffer); int line = 0; int col = 0; buffer_index_to_line_col(buffer, point, &line, &col); if (editor->desired_col < 0) { editor->desired_col = col; } if (line > 0) { size_t new_point = buffer_line_col_to_index(buffer, line - 1, editor->desired_col); buffer_set_point(buffer, new_point); } clamp_point(editor); } void editor_move_down(Editor *editor) { Buffer *buffer = active_buffer(editor); if (!buffer) { return; } size_t point = buffer_point(buffer); int line = 0; int col = 0; buffer_index_to_line_col(buffer, point, &line, &col); if (editor->desired_col < 0) { editor->desired_col = col; } size_t len = buffer_length(buffer); int last_line = 0; int last_col = 0; buffer_index_to_line_col(buffer, len, &last_line, &last_col); if (line < last_line) { size_t new_point = buffer_line_col_to_index(buffer, line + 1, editor->desired_col); buffer_set_point(buffer, new_point); } clamp_point(editor); } void editor_move_beginning_of_line(Editor *editor) { Buffer *buffer = active_buffer(editor); if (!buffer) { return; } size_t point = buffer_point(buffer); while (point > 0) { char ch = buffer_char_at(buffer, point - 1); if (ch == '\n') { break; } point--; } buffer_set_point(buffer, point); editor->desired_col = -1; } void editor_move_end_of_line(Editor *editor) { Buffer *buffer = active_buffer(editor); if (!buffer) { return; } size_t point = buffer_point(buffer); size_t len = buffer_length(buffer); while (point < len) { char ch = buffer_char_at(buffer, point); if (ch == '\n') { break; } point++; } buffer_set_point(buffer, point); editor->desired_col = -1; } void editor_set_point(Editor *editor, size_t point) { Buffer *buffer = active_buffer(editor); if (!buffer) { return; } buffer_set_point(buffer, point); editor->desired_col = -1; } char editor_char_at(const Editor *editor, size_t index) { const Buffer *buffer = active_buffer_const(editor); if (!buffer) { return '\0'; } return buffer_char_at(buffer, index); } bool editor_open_file(Editor *editor, const char *path) { Buffer *buffer = active_buffer(editor); if (!buffer) { return false; } if (!path || path[0] == '\0') { return false; } if (!buffer_load_file(buffer, path)) { if (errno != ENOENT) { return false; } buffer->gap_start = 0; buffer->gap_end = buffer->capacity; buffer->modified = false; strncpy(buffer->path, path, sizeof(buffer->path) - 1); buffer->path[sizeof(buffer->path) - 1] = '\0'; } buffer_set_point(buffer, 0); editor->desired_col = -1; editor->scroll_line = 0; return true; } bool editor_save_file(Editor *editor, const char *path) { Buffer *buffer = active_buffer(editor); if (!buffer) { return false; } return buffer_save_file(buffer, path); } Buffer *editor_current_buffer(Editor *editor) { return active_buffer(editor); } const Buffer *editor_current_buffer_const(const Editor *editor) { return active_buffer_const(editor); } int editor_buffer_count(const Editor *editor) { return editor->buffer_count; } int editor_current_buffer_index(const Editor *editor) { return editor->current_buffer; } bool editor_switch_buffer(Editor *editor, int index) { if (!valid_buffer_index(editor, index)) { return false; } editor->current_buffer = index; editor->desired_col = -1; editor->scroll_line = 0; return true; } int editor_new_buffer(Editor *editor) { if (editor->buffer_count >= LEMACS_MAX_BUFFERS) { return -1; } int index = editor->buffer_count; if (!buffer_init(&editor->buffers[index])) { return -1; } editor->buffer_count++; editor->current_buffer = index; editor->desired_col = -1; editor->scroll_line = 0; return index; } const char *editor_buffer_path_at(const Editor *editor, int index) { if (!valid_buffer_index(editor, index)) { return NULL; } const char *path = buffer_path(&editor->buffers[index]); if (!path || path[0] == '\0') { return NULL; } return path; } bool editor_buffer_modified_at(const Editor *editor, int index) { if (!valid_buffer_index(editor, index)) { return false; } return buffer_modified(&editor->buffers[index]); }