From 96bc44c1f4d9efc5deac6840bfe908459e452319 Mon Sep 17 00:00:00 2001 From: Andrei Tretyakov Date: Wed, 8 Apr 2026 23:35:58 +0300 Subject: [PATCH] Refactored Editor Core: Arena + Piece Table --- src/arena.c | 123 ++++++++++++++++ src/arena.h | 39 +++++ src/editor_context.c | 67 +++++++++ src/editor_context.h | 64 ++++++++ src/main.c | 48 ++---- src/piece_table.c | 340 +++++++++++++++++++++++++++++++++++++++++++ src/piece_table.h | 79 ++++++++++ src/terminal.h | 93 ++---------- 8 files changed, 738 insertions(+), 115 deletions(-) create mode 100644 src/arena.c create mode 100644 src/arena.h create mode 100644 src/editor_context.c create mode 100644 src/editor_context.h create mode 100644 src/piece_table.c create mode 100644 src/piece_table.h diff --git a/src/arena.c b/src/arena.c new file mode 100644 index 0000000..86b0fda --- /dev/null +++ b/src/arena.c @@ -0,0 +1,123 @@ +#include "arena.h" +#include +#include + +arena_t *arena_create(size_t block_size) +{ + if (block_size == 0) + block_size = ARENA_DEFAULT_SIZE; + + arena_t *arena = malloc(sizeof(arena_t)); + if (!arena) + return NULL; + + arena->head = NULL; + arena->current = NULL; + arena->block_size = block_size; + return arena; +} + +static arena_block_t *arena_new_block(arena_t *arena, size_t min_size) +{ + size_t capacity = arena->block_size; + if (min_size > capacity) + capacity = min_size; + + arena_block_t *block = malloc(sizeof(arena_block_t) + capacity); + if (!block) + return NULL; + + block->next = NULL; + block->used = 0; + block->capacity = capacity; + return block; +} + +void *arena_alloc(arena_t *arena, size_t size) +{ + if (!arena || size == 0) + return NULL; + + /* Align size */ + size = (size + ARENA_ALIGNMENT - 1) & ~(ARENA_ALIGNMENT - 1); + + arena_block_t *block = arena->current; + if (!block || block->used + size > block->capacity) + { + block = arena_new_block(arena, size); + if (!block) + return NULL; + + block->next = arena->current; + arena->current = block; + if (!arena->head) + arena->head = block; + } + + void *ptr = block->data + block->used; + block->used += size; + return ptr; +} + +void *arena_alloc_zeroed(arena_t *arena, size_t size) +{ + void *ptr = arena_alloc(arena, size); + if (ptr) + memset(ptr, 0, size); + return ptr; +} + +char *arena_strdup(arena_t *arena, const char *str) +{ + if (!str) + return NULL; + size_t len = strlen(str) + 1; + char *copy = arena_alloc(arena, len); + if (copy) + memcpy(copy, str, len); + return copy; +} + +void arena_reset(arena_t *arena) +{ + if (!arena) + return; + + arena_block_t *block = arena->head; + while (block) + { + block->used = 0; + block = block->next; + } + arena->current = arena->head; +} + +void arena_destroy(arena_t *arena) +{ + if (!arena) + return; + + arena_block_t *block = arena->head; + while (block) + { + arena_block_t *next = block->next; + free(block); + block = next; + } + free(arena); +} + +size_t arena_used(arena_t *arena) +{ + if (!arena) + return 0; + + size_t total = 0; + arena_block_t *block = arena->head; + while (block) + { + total += block->used; + block = block->next; + } + return total; +} \ No newline at end of file diff --git a/src/arena.h b/src/arena.h new file mode 100644 index 0000000..26000de --- /dev/null +++ b/src/arena.h @@ -0,0 +1,39 @@ +#ifndef ARENA_H +#define ARENA_H + +#include +#include + +#define ARENA_DEFAULT_SIZE (1024 * 1024) /* 1MB */ +#define ARENA_ALIGNMENT 16 + +typedef struct arena_block +{ + struct arena_block *next; + size_t used; + size_t capacity; + char data[]; +} arena_block_t; + +typedef struct +{ + arena_block_t *head; + arena_block_t *current; + size_t block_size; +} arena_t; + +/* Arena lifecycle */ +arena_t *arena_create(size_t block_size); +void arena_destroy(arena_t *arena); +void arena_reset(arena_t *arena); /* Reset without freeing */ + +/* Memory allocation */ +void *arena_alloc(arena_t *arena, size_t size); +void *arena_alloc_zeroed(arena_t *arena, size_t size); +char *arena_strdup(arena_t *arena, const char *str); + +/* Utilities */ +size_t arena_used(arena_t *arena); +void arena_print_stats(arena_t *arena); + +#endif /* ARENA_H */ \ No newline at end of file diff --git a/src/editor_context.c b/src/editor_context.c new file mode 100644 index 0000000..2d21d34 --- /dev/null +++ b/src/editor_context.c @@ -0,0 +1,67 @@ +#include "editor_context.h" +#include "terminal.h" +#include +#include + +editor_context_t *editor_context_create(void) +{ + editor_context_t *ctx = malloc(sizeof(editor_context_t)); + if (!ctx) + return NULL; + + memset(ctx, 0, sizeof(editor_context_t)); + + ctx->persistent_arena = arena_create(ARENA_DEFAULT_SIZE); + if (!ctx->persistent_arena) + goto error; + + ctx->scratch_arena = arena_create(ARENA_DEFAULT_SIZE); + if (!ctx->scratch_arena) + goto error; + + ctx->piece_table = piece_table_create(ctx->persistent_arena); + if (!ctx->piece_table) + goto error; + + piece_table_init_empty(ctx->piece_table); + + ctx->config.tab_stop = EDITOR_TAB_STOP; + ctx->config.status_msg_timeout = STATUS_MSG_TIMEOUT; + ctx->raw_mode_enabled = 0; + ctx->dirty = 0; + ctx->undo_stack = NULL; + ctx->redo_stack = NULL; + + return ctx; + +error: + if (ctx->persistent_arena) + arena_destroy(ctx->persistent_arena); + if (ctx->scratch_arena) + arena_destroy(ctx->scratch_arena); + free(ctx); + return NULL; +} + +void editor_context_destroy(editor_context_t *ctx) +{ + if (!ctx) + return; + + if (ctx->raw_mode_enabled) + { + terminal_disable_raw_mode(ctx); + } + + if (ctx->persistent_arena) + arena_destroy(ctx->persistent_arena); + if (ctx->scratch_arena) + arena_destroy(ctx->scratch_arena); + free(ctx->filename); + free(ctx); +} + +void editor_context_reset_scratch(editor_context_t *ctx) +{ + arena_reset(ctx->scratch_arena); +} \ No newline at end of file diff --git a/src/editor_context.h b/src/editor_context.h new file mode 100644 index 0000000..9cae7ca --- /dev/null +++ b/src/editor_context.h @@ -0,0 +1,64 @@ +#ifndef EDITOR_CONTEXT_H +#define EDITOR_CONTEXT_H + +#include "arena.h" +#include "piece_table.h" +#include "syntax.h" +#include "config.h" +#include +#include + +typedef struct editor_config +{ + int tab_stop; + int status_msg_timeout; +} editor_config_t; + +typedef struct editor_context +{ + /* Memory management */ + arena_t *persistent_arena; /* Long-lived allocations */ + arena_t *scratch_arena; /* Temporary operations */ + + /* Text storage */ + piece_table_t *piece_table; + + /* Editor state */ + int cx, cy; /* Cursor position */ + int rx; /* Render cursor position */ + int rowoff; /* Row scroll offset */ + int coloff; /* Column scroll offset */ + int screenrows; + int screencols; + + char *filename; + int dirty; /* Unsaved modifications */ + + char statusmsg[STATUS_MSG_SIZE]; + time_t statusmsg_time; + + /* Terminal */ + struct termios orig_termios; + int raw_mode_enabled; + + /* Syntax */ + editor_syntax_t *syntax; + + /* Configuration */ + editor_config_t config; + + /* Undo stack */ + edit_log_t *undo_stack; + edit_log_t *redo_stack; +} editor_context_t; + +/* Context lifecycle */ +editor_context_t *editor_context_create(void); +void editor_context_destroy(editor_context_t *ctx); +void editor_context_reset_scratch(editor_context_t *ctx); + +/* Error handling with context */ +editor_error_t editor_context_set_error(editor_context_t *ctx, editor_error_t err); +const char *editor_context_error_string(editor_context_t *ctx, editor_error_t err); + +#endif /* EDITOR_CONTEXT_H */ \ No newline at end of file diff --git a/src/main.c b/src/main.c index f2af713..4a08c12 100644 --- a/src/main.c +++ b/src/main.c @@ -17,56 +17,34 @@ int main(int argc, char *argv[]) { - editor_error_t err; - - /* Initialize terminal */ - err = terminal_enable_raw_mode(); - struct sigaction sa; - sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); - sa.sa_handler = terminal_handle_winch; - - if (sigaction(SIGWINCH, &sa, NULL) == -1) - { - editor_set_status_message("Warning: Cannot handle window resize"); - } - - if (IS_ERR(err)) - { - fprintf(stderr, "Fatal: %s\n", editor_error_string(err)); + editor_context_t *ctx = editor_context_create(); + if (!ctx) return 1; - } - /* Initialize editor */ - err = editor_init(); + editor_error_t err = terminal_enable_raw_mode(ctx); if (IS_ERR(err)) { - editor_set_status_error(err); - editor_safe_recovery(); + fprintf(stderr, "Error: %s\n", editor_error_string(err)); + editor_context_destroy(ctx); + return 1; } - /* Open file if provided */ if (argc >= 2) { - err = file_open(argv[1]); + err = piece_table_load(ctx->piece_table, argv[1]); if (IS_ERR(err)) { - editor_set_status_error(err); - output_refresh_screen(); - sleep(2); + editor_set_status_message(ctx, "Error loading file"); } } - /* Show help message */ - editor_set_status_message( - "HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find"); - - /* Main event loop */ + /* Main loop */ while (1) { - output_refresh_screen(); - input_process_keypress(); + output_refresh_screen(ctx); + input_process_keypress(ctx); } + editor_context_destroy(ctx); return 0; -} +} \ No newline at end of file diff --git a/src/piece_table.c b/src/piece_table.c new file mode 100644 index 0000000..03751f1 --- /dev/null +++ b/src/piece_table.c @@ -0,0 +1,340 @@ +#include "piece_table.h" +#include +#include + +#define INITIAL_PIECE_CAPACITY 16 +#define ADDED_BUFFER_INITIAL (64 * 1024) /* 64KB */ + +static void ensure_piece_capacity(piece_table_t *pt) +{ + if (pt->piece_count < pt->piece_capacity) + return; + + size_t new_cap = pt->piece_capacity * 2; + piece_t *new_pieces = arena_alloc(pt->arena, sizeof(piece_t) * new_cap); + if (!new_pieces) + return; + + memcpy(new_pieces, pt->pieces, sizeof(piece_t) * pt->piece_count); + pt->pieces = new_pieces; + pt->piece_capacity = new_cap; +} + +static void ensure_added_capacity(piece_table_t *pt, size_t needed) +{ + if (pt->added_size + needed <= pt->added_capacity) + return; + + size_t new_cap = pt->added_capacity * 2; + if (new_cap < pt->added_size + needed) + new_cap = pt->added_size + needed + 4096; + + char *new_added = arena_alloc(pt->arena, new_cap); + if (!new_added) + return; + + memcpy(new_added, pt->added, pt->added_size); + pt->added = new_added; + pt->added_capacity = new_cap; +} + +piece_table_t *piece_table_create(arena_t *arena) +{ + piece_table_t *pt = arena_alloc_zeroed(arena, sizeof(piece_table_t)); + if (!pt) + return NULL; + + pt->arena = arena; + pt->piece_capacity = INITIAL_PIECE_CAPACITY; + pt->pieces = arena_alloc_zeroed(arena, sizeof(piece_t) * pt->piece_capacity); + + pt->added_capacity = ADDED_BUFFER_INITIAL; + pt->added = arena_alloc_zeroed(arena, pt->added_capacity); + + return pt; +} + +void piece_table_init_empty(piece_table_t *pt) +{ + pt->original = NULL; + pt->original_size = 0; + pt->added_size = 0; + pt->piece_count = 1; + pt->pieces[0] = (piece_t){.source = PIECE_ORIGINAL, .start = 0, .length = 0}; + pt->total_chars = 0; +} + +editor_error_t piece_table_load(piece_table_t *pt, const char *filename) +{ + FILE *fp = fopen(filename, "rb"); + if (!fp) + return ERR(errno, "fopen failed"); + + fseek(fp, 0, SEEK_END); + long size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + pt->original = arena_alloc(pt->arena, size + 1); + if (!pt->original) + { + fclose(fp); + return ERR(ENOMEM, "Out of memory"); + } + + size_t read = fread(pt->original, 1, size, fp); + fclose(fp); + + if (read != (size_t)size) + return ERR(EIO, "Read error"); + + pt->original[size] = '\0'; + pt->original_size = size; + pt->total_chars = size; + + pt->piece_count = 1; + pt->pieces[0] = (piece_t){.source = PIECE_ORIGINAL, .start = 0, .length = size}; + + return EOK; +} + +static size_t piece_get_char(piece_table_t *pt, piece_t *piece, size_t offset) +{ + char *source = (piece->source == PIECE_ORIGINAL) ? pt->original : pt->added; + return (unsigned char)source[piece->start + offset]; +} + +/* Find piece containing a global position */ +static size_t find_piece_at_offset(piece_table_t *pt, size_t offset, size_t *piece_offset) +{ + size_t current = 0; + for (size_t i = 0; i < pt->piece_count; i++) + { + if (offset < current + pt->pieces[i].length) + { + *piece_offset = offset - current; + return i; + } + current += pt->pieces[i].length; + } + *piece_offset = 0; + return pt->piece_count - 1; +} + +editor_error_t piece_table_insert(piece_table_t *pt, size_t pos, const char *text, size_t len) +{ + if (len == 0) + return EOK; + if (pos > pt->total_chars) + pos = pt->total_chars; + + ensure_added_capacity(pt, len); + memcpy(pt->added + pt->added_size, text, len); + + size_t piece_idx, piece_offset; + piece_idx = find_piece_at_offset(pt, pos, &piece_offset); + + piece_t *old_piece = &pt->pieces[piece_idx]; + + ensure_piece_capacity(pt); + + /* Shift pieces to make room for up to 3 new pieces */ + memmove(&pt->pieces[piece_idx + 2], &pt->pieces[piece_idx + 1], + sizeof(piece_t) * (pt->piece_count - piece_idx - 1)); + + /* Create new pieces */ + if (piece_offset > 0) + { + pt->pieces[piece_idx] = (piece_t){ + .source = old_piece->source, + .start = old_piece->start, + .length = piece_offset}; + piece_idx++; + } + + pt->pieces[piece_idx] = (piece_t){ + .source = PIECE_ADDED, + .start = pt->added_size, + .length = len}; + piece_idx++; + pt->added_size += len; + + if (piece_offset < old_piece->length) + { + pt->pieces[piece_idx] = (piece_t){ + .source = old_piece->source, + .start = old_piece->start + piece_offset, + .length = old_piece->length - piece_offset}; + piece_idx++; + } + + pt->piece_count = piece_idx; + pt->total_chars += len; + + return EOK; +} + +editor_error_t piece_table_delete(piece_table_t *pt, size_t pos, size_t len) +{ + if (len == 0 || pos >= pt->total_chars) + return EOK; + if (pos + len > pt->total_chars) + len = pt->total_chars - pos; + + size_t start_idx, start_offset, end_idx, end_offset; + start_idx = find_piece_at_offset(pt, pos, &start_offset); + end_idx = find_piece_at_offset(pt, pos + len, &end_offset); + + piece_t *start_piece = &pt->pieces[start_idx]; + piece_t *end_piece = &pt->pieces[end_idx]; + + /* Handle deletion within single piece */ + if (start_idx == end_idx) + { + if (start_offset == 0 && end_offset == start_piece->length) + { + /* Delete entire piece */ + memmove(&pt->pieces[start_idx], &pt->pieces[start_idx + 1], + sizeof(piece_t) * (pt->piece_count - start_idx - 1)); + pt->piece_count--; + } + else + { + /* Delete middle of piece - need to split */ + if (start_offset > 0) + { + start_piece->length = start_offset; + } + if (end_offset < start_piece->length) + { + ensure_piece_capacity(pt); + memmove(&pt->pieces[start_idx + 1], &pt->pieces[start_idx], + sizeof(piece_t) * (pt->piece_count - start_idx)); + pt->pieces[start_idx + 1] = (piece_t){ + .source = start_piece->source, + .start = start_piece->start + end_offset, + .length = start_piece->length - end_offset}; + pt->piece_count++; + } + } + } + else + { + /* Delete across multiple pieces */ + size_t new_count = start_idx + 1; + + /* Trim start piece */ + if (start_offset > 0) + { + start_piece->length = start_offset; + new_count = start_idx + 1; + } + else + { + new_count = start_idx; + } + + /* Trim end piece */ + if (end_offset < end_piece->length) + { + pt->pieces[new_count] = (piece_t){ + .source = end_piece->source, + .start = end_piece->start + end_offset, + .length = end_piece->length - end_offset}; + new_count++; + } + + /* Copy remaining pieces */ + memmove(&pt->pieces[new_count], &pt->pieces[end_idx + 1], + sizeof(piece_t) * (pt->piece_count - end_idx - 1)); + pt->piece_count = new_count + (pt->piece_count - end_idx - 1); + } + + pt->total_chars -= len; + return EOK; +} + +static char *extract_from_pieces(piece_table_t *pt, size_t pos, size_t len, arena_t *out_arena) +{ + char *result = arena_alloc(out_arena, len + 1); + if (!result) + return NULL; + + size_t result_pos = 0; + size_t current = 0; + + for (size_t i = 0; i < pt->piece_count && result_pos < len; i++) + { + piece_t *piece = &pt->pieces[i]; + size_t piece_end = current + piece->length; + + if (piece_end > pos && current < pos + len) + { + size_t copy_start = (pos > current) ? (pos - current) : 0; + size_t copy_end = (pos + len < piece_end) ? (pos + len - current) : piece->length; + size_t copy_len = copy_end - copy_start; + + char *source = (piece->source == PIECE_ORIGINAL) ? pt->original : pt->added; + memcpy(result + result_pos, source + piece->start + copy_start, copy_len); + result_pos += copy_len; + } + current += piece->length; + } + + result[result_pos] = '\0'; + return result; +} + +char *piece_table_extract(piece_table_t *pt, size_t pos, size_t len, arena_t *out_arena) +{ + if (pos > pt->total_chars) + return NULL; + if (pos + len > pt->total_chars) + len = pt->total_chars - pos; + return extract_from_pieces(pt, pos, len, out_arena); +} + +/* Line iteration */ +void piece_table_iterate_lines(piece_table_t *pt, line_iterator_t *iter) +{ + iter->pt = pt; + iter->line_start = 0; + iter->line_num = 0; + iter->line_buffer = NULL; +} + +int piece_table_next_line(line_iterator_t *iter) +{ + if (iter->line_start >= iter->pt->total_chars) + return 0; + + /* Find line end */ + size_t line_end = iter->line_start; + while (line_end < iter->pt->total_chars) + { + char c = *piece_table_extract(iter->pt, line_end, 1, iter->pt->arena); + if (c == '\n') + break; + line_end++; + } + + iter->line_end = line_end; + iter->line_buffer = piece_table_extract(iter->pt, iter->line_start, + line_end - iter->line_start, + iter->pt->arena); + + iter->line_start = line_end + 1; + iter->line_num++; + return 1; +} + +size_t piece_table_get_line_count(piece_table_t *pt) +{ + size_t count = 0; + for (size_t i = 0; i < pt->total_chars; i++) + { + char c = *piece_table_extract(pt, i, 1, pt->arena); + if (c == '\n') + count++; + } + return count + (pt->total_chars > 0 ? 1 : 0); +} \ No newline at end of file diff --git a/src/piece_table.h b/src/piece_table.h new file mode 100644 index 0000000..27e8ee7 --- /dev/null +++ b/src/piece_table.h @@ -0,0 +1,79 @@ +#ifndef PIECE_TABLE_H +#define PIECE_TABLE_H + +#include "arena.h" +#include "error.h" +#include +#include + +/* Piece flags */ +#define PIECE_ORIGINAL (1 << 0) /* From original file */ +#define PIECE_ADDED (1 << 1) /* From user edits */ + +typedef struct piece +{ + uint32_t source; /* PIECE_ORIGINAL or PIECE_ADDED */ + uint32_t start; /* Start offset in source buffer */ + uint32_t length; /* Piece length */ + uint32_t flags; /* Future use */ +} piece_t; + +typedef struct +{ + arena_t *arena; + char *original; /* Original file content */ + char *added; /* Added content buffer */ + size_t original_size; + size_t added_size; + size_t added_capacity; + + piece_t *pieces; /* Array of pieces */ + size_t piece_count; + size_t piece_capacity; + + size_t total_chars; /* Total text length */ +} piece_table_t; + +/* Lifecycle */ +piece_table_t *piece_table_create(arena_t *arena); +void piece_table_init_empty(piece_table_t *pt); +editor_error_t piece_table_load(piece_table_t *pt, const char *filename); +void piece_table_destroy(piece_table_t *pt); + +/* Text operations */ +editor_error_t piece_table_insert(piece_table_t *pt, size_t pos, const char *text, size_t len); +editor_error_t piece_table_delete(piece_table_t *pt, size_t pos, size_t len); +char *piece_table_extract(piece_table_t *pt, size_t pos, size_t len, arena_t *out_arena); +size_t piece_table_length(piece_table_t *pt); + +/* Line-based access (for rendering) */ +typedef struct +{ + piece_table_t *pt; + size_t line_start; /* Start offset of line */ + size_t line_end; /* End offset of line (exclusive) */ + size_t line_num; + char *line_buffer; /* Null-terminated line (from arena) */ +} line_iterator_t; + +void piece_table_iterate_lines(piece_table_t *pt, line_iterator_t *iter); +int piece_table_next_line(line_iterator_t *iter); + +/* Utility functions */ +size_t piece_table_get_line_count(piece_table_t *pt); +size_t piece_table_get_line_offset(piece_table_t *pt, size_t line_num); +size_t piece_table_get_line_length(piece_table_t *pt, size_t line_num); +char *piece_table_get_line(piece_table_t *pt, size_t line_num, arena_t *out_arena); + +/* Undo/redo support */ +typedef struct edit_log +{ + struct edit_log *next; + size_t pos; + size_t old_len; + size_t new_len; + char *old_data; /* Stored in arena */ + char *new_data; +} edit_log_t; + +#endif /* PIECE_TABLE_H */ \ No newline at end of file diff --git a/src/terminal.h b/src/terminal.h index 61bce13..678cbba 100644 --- a/src/terminal.h +++ b/src/terminal.h @@ -2,88 +2,21 @@ #define TERMINAL_H #include "error.h" +#include "editor_context.h" #include -/* ANSI color constants */ -#define ANSI_COLOR_BLACK "\x1b[30m" -#define ANSI_COLOR_RED "\x1b[31m" /* Set foreground color to red */ -#define ANSI_COLOR_GREEN "\x1b[32m" -#define ANSI_COLOR_YELLOW "\x1b[33m" -#define ANSI_COLOR_BLUE "\x1b[34m" -#define ANSI_COLOR_MAGENTA "\x1b[35m" -#define ANSI_COLOR_CYAN "\x1b[36m" -#define ANSI_COLOR_WHITE "\x1b[37m" -#define ANSI_COLOR_RESET "\x1b[39m" - -/* ANSI escape code constants */ -#define ANSI_CLEAR_SCREEN "\x1b[2J" /* Clear entire screen */ -#define ANSI_CURSOR_HOME "\x1b[H" /* Move cursor to (1,1) */ -#define ANSI_HIDE_CURSOR "\x1b[?25l" /* Hide cursor (for redraw) */ -#define ANSI_SHOW_CURSOR "\x1b[?25h" /* Show cursor */ -#define ANSI_SAVE_CURSOR "\x1b[s" /* Save cursor position */ -#define ANSI_RESTORE_CURSOR "\x1b[u" /* Restore cursor position */ -#define ANSI_CLEAR_LINE "\x1b[K" /* Clear line from cursor right */ -#define ANSI_RESET_FORMAT "\x1b[m" /* Reset all text attributes */ -#define ANSI_REVERSE_VIDEO "\x1b[7m" /* Swap foreground/background */ -#define ANSI_NORMAL_VIDEO "\x1b[27m" /* Disable reverse video */ -#define ANSI_SET_GRAPHICS "\x1b[%dm" /* Set SGR color/attribute code */ - -/* ANSI cursor position query */ -#define ANSI_QUERY_CURSOR_POS "\x1b[6n" /* Ask terminal for cursor position */ -#define ANSI_MOVE_RIGHT_999 "\x1b[999C" /* Move far right (force edge) */ -#define ANSI_MOVE_DOWN_999 "\x1b[999B" /* Move far down (force edge) */ -#define ANSI_FALLBACK_MOVE ANSI_MOVE_RIGHT_999 ANSI_MOVE_DOWN_999 - -/* ANSI cursor movement constants */ -#define ANSI_CURSOR_UP "\x1b[1A" -#define ANSI_CURSOR_DOWN "\x1b[1B" -#define ANSI_CURSOR_RIGHT "\x1b[1C" -#define ANSI_CURSOR_LEFT "\x1b[1D" -#define ANSI_CURSOR_POSITION "\x1b[%d;%dH" /* Move cursor to row;col */ - -/*** Terminal configuration ***/ -#define TERM_INDEX_BASE 1 /* Terminal coordinates are 1-based */ -#define TERM_VMIN 0 /* read() returns immediately */ -#define TERM_VTIME 1 /* read() timeout (0.1s units) */ - -/*** Key codes ***/ -#define KEY_ESC '\x1b' -#define KEY_ENTER '\r' -#define KEY_BACKSPACE 127 - -/*** Terminal initialization and cleanup ***/ -editor_error_t terminal_enable_raw_mode(void); -void terminal_disable_raw_mode(void); -void terminal_die(const char *s); - -/*** Terminal I/O ***/ -int terminal_read_key(void); - -/*** Terminal size detection ***/ -editor_error_t terminal_get_window_size(int *rows, int *cols); -editor_error_t terminal_get_cursor_position(int *rows, int *cols); - -/*** ANSI output helpers ***/ -void terminal_clear_screen(void); -void terminal_cursor_home(void); -void terminal_hide_cursor(void); -void terminal_show_cursor(void); -void terminal_clear_line(void); -void terminal_set_cursor_position(int row, int col); -void terminal_save_cursor(void); -void terminal_restore_cursor(void); - -/*** Terminal colors and attributes ***/ -void terminal_set_color(int color); -void terminal_reset_color(void); -void terminal_reverse_video(void); -void terminal_normal_video(void); - -/*** Signal handling ***/ +/* All terminal functions now take explicit context */ +editor_error_t terminal_enable_raw_mode(editor_context_t *ctx); +void terminal_disable_raw_mode(editor_context_t *ctx); +int terminal_read_key(editor_context_t *ctx); +editor_error_t terminal_get_window_size(editor_context_t *ctx, int *rows, int *cols); void terminal_handle_winch(int sig); -int terminal_check_resize(void); +int terminal_check_resize(editor_context_t *ctx); -/*** Terminal state access ***/ -struct termios *terminal_get_original_settings(void); +/* ANSI output helpers take context for terminal state */ +void terminal_clear_screen(editor_context_t *ctx); +void terminal_cursor_home(editor_context_t *ctx); +void terminal_hide_cursor(editor_context_t *ctx); +void terminal_show_cursor(editor_context_t *ctx); -#endif /* TERMINAL_H */ +#endif /* TERMINAL_H */ \ No newline at end of file