#include "edit.h" #include "arena_alloc.h" #include "gapbuffer.h" #include "mprintf.h" #include "self.h" #include "walloc.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct edit_line { struct list_node_link lines_link; struct gapbuffer gb; }; struct cursor { size_t col; size_t line; }; struct editor { struct list_node_link* lines; struct edit_line* current_line; struct cursor cursor; size_t col_offset; size_t row_offset; }; static struct editor editor; static bool prepare_lines_cb (void* ctx, const char* start, size_t len) { struct edit_line* line = malloc (sizeof (*line)); if (line == NULL) return false; memset (line, 0, sizeof (*line)); gapbuffer_init (&wmalloc, NULL, &line->gb, len); for (size_t chr = 0; chr < len; chr++) gapbuffer_insert (&wrealloc, NULL, &line->gb, start[chr]); list_append (editor.lines, &line->lines_link); if (editor.current_line == NULL) editor.current_line = line; return true; } static void prepare_lines (const char* text) { strtokenize (text, '\n', NULL, &prepare_lines_cb); } static void update_horz_scroll (size_t screen_cols) { if (editor.cursor.col < editor.col_offset) editor.col_offset = editor.cursor.col; if (editor.cursor.col >= editor.col_offset + screen_cols) editor.col_offset = editor.cursor.col - screen_cols + 1; } static void update_vert_scroll (size_t screen_rows) { if (editor.cursor.line < editor.row_offset) editor.row_offset = editor.cursor.line; if (editor.cursor.line >= editor.row_offset + (screen_rows - 1)) editor.row_offset = editor.cursor.line - (screen_rows - 1) + 1; } void edit_start (const char* file_path, const char* text) { prepare_lines (text); struct arena temp_arena; memset (&temp_arena, 0, sizeof (temp_arena)); size_t cols, rows; terminal_dimensions (&cols, &rows); mprintf (ANSIQ_SCR_CLR_ALL ANSIQ_CUR_INVISIBLE); bool edit_run = true; while (edit_run) { update_horz_scroll (cols); update_vert_scroll (rows); int w; const size_t backbuffer_max = 128 * 1024; size_t bb_remaining = backbuffer_max; char* backbuffer = arena_malloc (&temp_arena, backbuffer_max); memset (backbuffer, 0, backbuffer_max); char* bbptr = backbuffer; w = snprintf (bbptr, bb_remaining, ANSIQ_CUR_HOME); bbptr += w; bb_remaining -= w; size_t lines_drawn = 0; size_t current_idx = 0; struct list_node_link *line_link, *tmp_line_link; list_foreach (editor.lines, line_link, tmp_line_link) { if (current_idx < editor.row_offset) { current_idx++; continue; } if (lines_drawn >= rows - 1) break; struct edit_line* line = list_entry (line_link, struct edit_line, lines_link); w = snprintf (bbptr, bb_remaining, ANSIQ_SCR_CLR_LINE); bbptr += w; bb_remaining -= w; char* render = gapbuffer_get_string (&warena_malloc, &temp_arena, &line->gb); size_t len = strlen (render); if (len > editor.col_offset) { char* visible = render + editor.col_offset; size_t visible_len = len - editor.col_offset; if (visible_len > cols) visible_len = cols; w = snprintf (bbptr, bb_remaining, "%.*s", (int)visible_len, visible); bbptr += w; bb_remaining -= w; } w = snprintf (bbptr, bb_remaining, ANSIQ_SCR_CLR2END "\n"); bbptr += w; bb_remaining -= w; lines_drawn++; current_idx++; } w = snprintf (bbptr, bb_remaining, ANSIQ_DYN_CUR_SET ANSIQ_SCR_CLR_LINE, (int)rows, 1); bbptr += w; bb_remaining -= w; w = snprintf (bbptr, bb_remaining, "Editing %s; line %zu col %zu", file_path, editor.cursor.line, editor.cursor.col); bbptr += w; bb_remaining -= w; w = snprintf (bbptr, bb_remaining, ANSIQ_DYN_CUR_SET ANSIQ_CUR_VISIBLE, (int)(editor.cursor.line - editor.row_offset) + 1, (int)(editor.cursor.col - editor.col_offset) + 1); bbptr += w; bb_remaining -= w; mail_send (e_pgid, backbuffer, strlen (backbuffer)); uint8_t ch = 0; mail_receive (&ch, 1); gapbuffer_move (&editor.current_line->gb, editor.cursor.col); switch (ch) { case '\b': if (editor.cursor.col > 0) { editor.cursor.col--; gapbuffer_backspace (&editor.current_line->gb); } break; case KB_DELETE: if (editor.cursor.col < gapbuffer_length (&editor.current_line->gb)) editor.current_line->gb.gap_end++; break; case KB_LEFT: if (editor.cursor.col > 0) editor.cursor.col--; break; case KB_RIGHT: if (editor.cursor.col < gapbuffer_length (&editor.current_line->gb)) editor.cursor.col++; break; case KB_UP: { if (editor.cursor.line > 0) { editor.cursor.line--; editor.current_line = list_entry (editor.current_line->lines_link.prev, struct edit_line, lines_link); size_t len = gapbuffer_length (&editor.current_line->gb); if (editor.cursor.col > len) editor.cursor.col = len; } } break; case KB_DOWN: { if (editor.current_line->lines_link.next != NULL) { editor.cursor.line++; editor.current_line = list_entry (editor.current_line->lines_link.next, struct edit_line, lines_link); size_t len = gapbuffer_length (&editor.current_line->gb); if (editor.cursor.col > len) editor.cursor.col = len; } } break; case KB_HOME: editor.cursor.col = 0; break; case KB_END: editor.cursor.col = gapbuffer_length (&editor.current_line->gb); break; case KB_CTRL ('Q'): edit_run = false; break; default: if (isprint (ch)) { gapbuffer_insert (&wrealloc, NULL, &editor.current_line->gb, ch); editor.cursor.col++; } break; } arena_reset (&temp_arena); } mprintf (ANSIQ_SCR_CLR_ALL ANSIQ_CUR_VISIBLE); arena_destroy (&temp_arena); struct list_node_link *line_link, *tmp_line_link; list_foreach (editor.lines, line_link, tmp_line_link) { struct edit_line* line = list_entry (line_link, struct edit_line, lines_link); free (line->gb.buffer); free (line); } memset (&editor, 0, sizeof (editor)); }