From edb5384f0ec9d658beebc3e90072b46068094a50 Mon Sep 17 00:00:00 2001 From: arthur barraux Date: Fri, 22 May 2026 13:40:03 +0200 Subject: [PATCH] temp commit --- config/init.lisp | 1 - include/buffer.h | 2 + include/builtins.h | 1 - include/data.h | 26 +-- include/define.h | 3 +- include/editor_op.h | 5 +- include/file_io.h | 5 - include/input.h | 2 +- include/output.h | 2 - include/row_op.h | 16 +- include/terminal.h | 2 + main.c | 15 +- src/buffer.c | 205 ++++++++++------ src/builtins.c | 106 ++++----- src/editor_op.c | 134 ++++++++--- src/file_io.c | 59 ++--- src/init.c | 18 +- src/input.c | 83 ++++--- src/output.c | 487 +++++++++++++++++++++++---------------- src/row_op.c | 79 ++++--- src/split_screen.c | 96 ++++---- src/syntax_highlighter.c | 109 ++++++--- src/terminal.c | 13 ++ 23 files changed, 848 insertions(+), 621 deletions(-) diff --git a/config/init.lisp b/config/init.lisp index f266f73..8946093 100644 --- a/config/init.lisp +++ b/config/init.lisp @@ -80,7 +80,6 @@ (map-key "PAGE-DOWN" move-cursor-page-down "no-prefix") (map-key "f" editor-open-file "user") (map-key "TAB" editor-insert-tab "no-prefix") -(map-key "CTRL-k" editor-del-row "no-prefix") (map-key "CTRL-s" buffer-find "no-prefix") (map-key "CTRL-r" editor-move-to-end-of-word "no-prefix") (map-key "ARROW-RIGHT" editor-switch-next-buffer "user") diff --git a/include/buffer.h b/include/buffer.h index 02a4600..a7dc864 100644 --- a/include/buffer.h +++ b/include/buffer.h @@ -55,4 +55,6 @@ int bufferSave(void); */ int bufferSaveAll(void); +void bufferFind(struct buffer_t *buf); + #endif diff --git a/include/builtins.h b/include/builtins.h index 099ba1c..0f69aeb 100644 --- a/include/builtins.h +++ b/include/builtins.h @@ -17,7 +17,6 @@ Lisp l_editorInsertNewLine(Lisp args, LispError* e, LispContext ctx); Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx); Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx); Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx); -Lisp editorDelRow_L(Lisp args, LispError *e, LispContext ctx); Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx); // Editor diff --git a/include/data.h b/include/data.h index 2e8dfb2..4f547dd 100644 --- a/include/data.h +++ b/include/data.h @@ -33,15 +33,12 @@ typedef enum { */ typedef struct { int buffer_id; // Which buffer this pane displays - int start_row; // Starting row on screen - int start_col; // Starting column on screen int height; // Height of this pane int width; // Width of this pane + int origin_x, origin_y; int cursor_x; // Local cursor x in this pane int cursor_y; // Local cursor y in this pane - int rx, ry; - int row_offset; // Scroll offset for rows - int col_offset; // Scroll offset for columns + int x_offset, y_offset; int is_active; // Is this pane currently active } EditorPane; @@ -95,15 +92,12 @@ enum buffer_type { FILE_BUFF, TERMINAL_BUFF }; struct buffer_t { enum buffer_type type; int buffer_id; - int width, height; - int x, y; /**< Position in the buffer (cursor) */ - frow *row; + int x, y; /**< Position in the file */ + row_t *row; int numrows; char *filename; enum bufferStatus_e state; int dirty; /**< Has this buffer been modified since last save */ - int row_offset; /**< Scroll offset for rows in this buffer */ - int col_offset; /**< Scroll offset for columns in this buffer */ }; /** @@ -112,19 +106,16 @@ struct buffer_t { */ struct editorConfig { int cursor_x, cursor_y; /**< Cursor position */ - int rx; /**< Position in the render*/ - int row_offset; /**< Position scroll of lines */ - int col_offset; /**< Position scroll of colomns*/ int screenrows; /**< Terminal height*/ int screencols; /**< Terminal width*/ ScreenLayout layout; - int numrows; /**< Number of rows contained */ - row_t *rows; /**< Store all the rows printed */ + row_t *rows; /**< Store all the rows printed */ + int dirty; - int prefix_state; - char status_msg[80]; + + char *status_msg; time_t status_msg_time; struct termios orig_termios; /**< Terminal communication interface */ @@ -143,6 +134,7 @@ struct editorConfig { struct prefix_t *prefix; int number_of_prefix; + int prefix_state; struct buffer_t buffers[64]; int number_of_buffer; diff --git a/include/define.h b/include/define.h index f3f0b18..35cf467 100644 --- a/include/define.h +++ b/include/define.h @@ -8,8 +8,9 @@ #define HIDE_CURSOR "\x1b[?25l" #define SHOW_CURSOR "\x1b[?25h" #define ERASE_END_LINE "\x1b[K" -#define TAB "\x09" +#define TAB "\x9" #define SPACE "\x20" +#define APP_DEBUG enum editorKey_e { BACKSPACE = 127, diff --git a/include/editor_op.h b/include/editor_op.h index 277a1be..d174d27 100644 --- a/include/editor_op.h +++ b/include/editor_op.h @@ -2,10 +2,11 @@ #define EDITOR_OP_H_ #include "data.h" -void bufferInsertChar(int c); void bufferInsertNewLine(); - +void bufferInsertBytes(char *src, int n); void editorSetStatusMessage(const char *fmt, ...); +void bufferDelBytes(); + #endif // EDITOR_OP_H_ diff --git a/include/file_io.h b/include/file_io.h index 7095570..abd7a3e 100644 --- a/include/file_io.h +++ b/include/file_io.h @@ -8,15 +8,10 @@ #include #include -char *bufferRowsToString(struct buffer_t *buf,int *buffer_len); - - void editorCloseFile(void); void editorOpen(struct buffer_t *buffer); void editorSave(); -void bufferFind(struct buffer_t *buf); - #endif // FILE_IO_H_ diff --git a/include/input.h b/include/input.h index d20bc60..af90923 100644 --- a/include/input.h +++ b/include/input.h @@ -22,7 +22,7 @@ char *editorPrompt(char *prompt, char * PlaceHolder, char bPathMode); -char *key_to_string(int key); +const char *file_completion(const char *path); int editorMoveCursor(int key); diff --git a/include/output.h b/include/output.h index 5539a8c..09ac7da 100644 --- a/include/output.h +++ b/include/output.h @@ -9,8 +9,6 @@ * \brief Draws left rows of the editor. */ -void editorDrawRows(struct abuf *ab); - void editorRefreshScreen(); void editorScroll(); diff --git a/include/row_op.h b/include/row_op.h index 57bd30d..3742b66 100644 --- a/include/row_op.h +++ b/include/row_op.h @@ -10,17 +10,9 @@ void bufferInsertRow(struct buffer_t *buffer, int at, char *s, size_t len); int editorRowCxToByte(const row_t *row, int cursor_x); - -int editorRowCharCount(row_t *row); - -void bufferDelRow(struct buffer_t *buffer, int at); -void editorRowInsertBytes(row_t *row, int at, const char *src, int len); - -void editorRowDelByte(row_t *row, int at, int n); -void bufferRowInsertChar(struct buffer_t *buffer, frow *row, int at, int c); - -void bufferRowAppendString(struct buffer_t *buffer, frow *row, char *s, size_t len); - -void bufferRowDelchar(struct buffer_t *buffer, frow *row, int at); +void bufferFreeRow(row_t *row); +int editorRowCharCount(row_t *row, int x); +void bufferRowInsertBytes(struct buffer_t *buffer, row_t *row, int at, char *src, int n); +void bufferRowDelByte(struct buffer_t *buffer, row_t *row, int at, int n); #endif // ROW_OP_H_ diff --git a/include/terminal.h b/include/terminal.h index 416b7fa..6769379 100644 --- a/include/terminal.h +++ b/include/terminal.h @@ -33,4 +33,6 @@ int getWindowSize(int *rows, int *cols); char *key_to_string(int key); +void appDebug(const char *fmt, ...); + #endif diff --git a/main.c b/main.c index 9bd5683..6d25df7 100644 --- a/main.c +++ b/main.c @@ -5,6 +5,10 @@ * interactions. \version 0.1 \date 21 septembre 2024 */ +#define _DEFAULT_SOURCE +#define _BSD_SOURCE +#define _GNU_SOURCE + #include "include/buffer.h" #include "include/split_screen.h" #include @@ -12,10 +16,6 @@ #include #include -#define _DEFAULT_SOURCE -#define _BSD_SOURCE -#define _GNU_SOURCE - #include "include/data.h" #include "include/file_io.h" #include "include/init.h" @@ -28,7 +28,7 @@ struct editorConfig E; int main(int argc, char *argv[]) { char * splash_screen = (char *) calloc(256, sizeof(char)); - + enableRawMode(); initEditor(); if (argc >= 2) { @@ -37,11 +37,12 @@ int main(int argc, char *argv[]) { } else { strcat(splash_screen, getenv("HOME")); strcat(splash_screen, "/.beluga/assets/beluga.txt"); - fprintf(stderr, "%s\n", splash_screen); + + appDebug("splash : %s\n", splash_screen); EditorPane *active = splitScreenGetActivePane(); active->buffer_id = bufferCreate(splash_screen); } - //free(splash_screen); + free(splash_screen); editorSetStatusMessage("HELP: Ctrl-x Ctrl-s = save | Ctrl-x Ctrl-c = quit"); diff --git a/src/buffer.c b/src/buffer.c index 0392256..e21502c 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -12,6 +12,8 @@ #include #include +#include "include/input.h" + extern struct editorConfig E; /** @@ -19,10 +21,13 @@ extern struct editorConfig E; * @param filename The filename to search for * @return Buffer ID if found, -1 if not found */ -static int bufferFindByFilename(const char *filename) { - for (int i = 0; i < E.number_of_buffer; i++) { - if (E.buffers[i].filename != NULL && - strcmp(E.buffers[i].filename, filename) == 0) { +static int bufferFindByFilename(const char* filename) +{ + for (int i = 0; i < E.number_of_buffer; i++) + { + if (E.buffers[i].filename != NULL && + strcmp(E.buffers[i].filename, filename) == 0) + { return E.buffers[i].buffer_id; } } @@ -34,9 +39,12 @@ static int bufferFindByFilename(const char *filename) { * @param buffer_id The buffer ID to find * @return Pointer to buffer, NULL if not found */ -struct buffer_t *bufferFindById(int buffer_id) { - for (int i = 0; i < E.number_of_buffer; i++) { - if (E.buffers[i].buffer_id == buffer_id) { +struct buffer_t* bufferFindById(int buffer_id) +{ + for (int i = 0; i < E.number_of_buffer; i++) + { + if (E.buffers[i].buffer_id == buffer_id) + { return &E.buffers[i]; } } @@ -50,36 +58,37 @@ struct buffer_t *bufferFindById(int buffer_id) { * @param filename Path to the file * @return Buffer ID on success, -1 on failure */ -int bufferCreate(const char *filename) { +int bufferCreate(const char* filename) +{ // Check if file is already open - int existing_id = bufferFindByFilename(filename); - if (existing_id != -1) { + const int existing_id = bufferFindByFilename(filename); + if (existing_id != -1) + { return bufferSwitch(existing_id); } - + // Check if we have space for more buffers - if (E.number_of_buffer >= 64) { + if (E.number_of_buffer >= 64) + { editorSetStatusMessage("Error: maximum buffers reached (64)"); return -1; } - + // Initialize new buffer - struct buffer_t *new_buf = &E.buffers[E.number_of_buffer]; + struct buffer_t* new_buf = &E.buffers[E.number_of_buffer]; new_buf->buffer_id = E.number_of_buffer; new_buf->filename = strdup(filename); new_buf->type = FILE_BUFF; new_buf->state = READ_AND_WRITE; new_buf->x = 0; new_buf->y = 0; - new_buf->row_offset = 0; - new_buf->col_offset = 0; - new_buf->dirty = 0; // New file starts clean - + new_buf->dirty = 0; // New file starts clean + // Load file content using existing editorOpen editorOpen(new_buf); - + E.number_of_buffer++; - + editorSetStatusMessage("Opened: %s (buffer %d)", filename, new_buf->buffer_id); return new_buf->buffer_id; } @@ -90,17 +99,19 @@ int bufferCreate(const char *filename) { * @param buffer_id The buffer ID to switch to * @return 0 on success, -1 on failure */ -int bufferSwitch(int buffer_id) { - struct buffer_t *target = bufferFindById(buffer_id); - if (target == NULL) { - E.layout.panes[E.layout.active_pane].buffer_id = buffer_id; +int bufferSwitch(int buffer_id) +{ + struct buffer_t* target = bufferFindById(buffer_id); + if (target == NULL) + { + E.layout.panes[E.layout.active_pane].buffer_id = buffer_id; editorSetStatusMessage("Error: buffer not found"); return -1; } - - - editorSetStatusMessage("Switched to: %s (buffer %d)", - target->filename, buffer_id); + + + editorSetStatusMessage("Switched to: %s (buffer %d)", + target->filename, buffer_id); return 0; } @@ -112,51 +123,62 @@ int bufferSwitch(int buffer_id) { * @param buffer_id The buffer ID to close * @return 0 on success, -1 on failure */ -int bufferClose(int buffer_id) { - struct buffer_t *buf = bufferFindById(buffer_id); - if (buf == NULL) { +int bufferClose(int buffer_id) +{ + struct buffer_t* buf = bufferFindById(buffer_id); + if (buf == NULL) + { editorSetStatusMessage("Error: buffer not found"); return -1; } - EditorPane * active = splitScreenGetActivePane(); - + EditorPane* active = splitScreenGetActivePane(); + // If this is the current buffer, find next buffer to switch to - if (active->buffer_id == buffer_id) { + if (active->buffer_id == buffer_id) + { int next_id = -1; - + // Try to switch to next buffer - for (int i = 0; i < E.number_of_buffer; i++) { - if (E.buffers[i].buffer_id != buffer_id) { + for (int i = 0; i < E.number_of_buffer; i++) + { + if (E.buffers[i].buffer_id != buffer_id) + { next_id = E.buffers[i].buffer_id; break; } } - - if (next_id != -1) { + + if (next_id != -1) + { bufferSwitch(next_id); - } else { + } + else + { // No other buffers, clear editor editorCloseFile(); } } - + // Free buffer resources free(buf->filename); buf->filename = NULL; buf->buffer_id = -1; - + // Remove from buffer list by shifting - for (int i = 0; i < E.number_of_buffer; i++) { - if (E.buffers[i].buffer_id == buffer_id) { - for (int j = i; j < E.number_of_buffer - 1; j++) { + for (int i = 0; i < E.number_of_buffer; i++) + { + if (E.buffers[i].buffer_id == buffer_id) + { + for (int j = i; j < E.number_of_buffer - 1; j++) + { E.buffers[j] = E.buffers[j + 1]; } E.number_of_buffer--; break; } } - + editorSetStatusMessage("Closed buffer %d", buffer_id); return 0; } @@ -165,8 +187,9 @@ int bufferClose(int buffer_id) { * @brief Gets the current active buffer * @return Pointer to current buffer_t, NULL if none */ -struct buffer_t *bufferGetCurrent(void) { - EditorPane * active = splitScreenGetActivePane(); +struct buffer_t* bufferGetCurrent(void) +{ + EditorPane* active = splitScreenGetActivePane(); return bufferFindById(active->buffer_id); } @@ -175,24 +198,27 @@ struct buffer_t *bufferGetCurrent(void) { * @details Prints buffer information to status message * @return Number of open buffers */ -int bufferListAll(void) { - if (E.number_of_buffer == 0) { +int bufferListAll(void) +{ + if (E.number_of_buffer == 0) + { editorSetStatusMessage("No buffers open"); return 0; } - + char buf[256] = ""; int offset = 0; - EditorPane * active = splitScreenGetActivePane(); - - for (int i = 0; i < E.number_of_buffer; i++) { - struct buffer_t *b = &E.buffers[i]; + EditorPane* active = splitScreenGetActivePane(); + + for (int i = 0; i < E.number_of_buffer; i++) + { + struct buffer_t* b = &E.buffers[i]; char marker = (b->buffer_id == active->buffer_id) ? '*' : ' '; offset += snprintf(&buf[offset], sizeof(buf) - offset, - "%c%d:%s ", marker, b->buffer_id, - b->filename ? b->filename : "[No Name]"); + "%c%d:%s ", marker, b->buffer_id, + b->filename ? b->filename : "[No Name]"); } - + editorSetStatusMessage("Buffers: %s", buf); return E.number_of_buffer; } @@ -201,13 +227,15 @@ int bufferListAll(void) { * @brief Saves current buffer to disk * @return 0 on success, -1 on failure */ -int bufferSave(void) { - EditorPane * active = splitScreenGetActivePane(); - if (active->buffer_id == -1) { +int bufferSave(void) +{ + EditorPane* active = splitScreenGetActivePane(); + if (active->buffer_id == -1) + { editorSetStatusMessage("Error: no buffer active"); return -1; } - + editorSave(); return 0; } @@ -217,20 +245,59 @@ int bufferSave(void) { * @details Iterates through all buffers, saves each one * @return 0 on success, -1 on failure */ -int bufferSaveAll(void) { +int bufferSaveAll(void) +{ int saved = 0; int failed = 0; - - for (int i = 0; i < E.number_of_buffer; i++) { - if (bufferSwitch(E.buffers[i].buffer_id) == 0) { - if (E.dirty && bufferSave() == 0) { + + for (int i = 0; i < E.number_of_buffer; i++) + { + if (bufferSwitch(E.buffers[i].buffer_id) == 0) + { + if (E.dirty && bufferSave() == 0) + { saved++; } - } else { + } + else + { failed++; } } - + editorSetStatusMessage("Saved %d buffers (%d failed)", saved, failed); return (failed == 0) ? 0 : -1; } + +/** + * @brief Searches for a string in the document + * @details Prompts user for a search query, then searches forward from current + * cursor position. Updates cursor position to the first match found. + * @note Updates global editor state E (cursor position, row_offset) + * @note Search is case-sensitive and operates on rendered line content + * @note Searches begin from the line after current cursor position + * @see editorPrompt() + * @see editorRowRxToCx() + */ +void bufferFind(struct buffer_t* buf) +{ + appDebug("searching\n"); + char* query = editorPrompt("Search: %s (ESC to cancel)", "", 0); + EditorPane* active = splitScreenGetActivePane(); + + if (query == NULL) + return; + int i; + for (i = active->cursor_y + 1; i < buf->numrows; i++) + { + row_t* row = &buf->row[i]; + char* match = strstr(row->chars, query); + if (match) + { + active->cursor_y = i; + buf->y = buf->numrows; + break; + } + } + free(query); +} diff --git a/src/builtins.c b/src/builtins.c index f4222ee..fc7929d 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -2,17 +2,18 @@ * @file builtins.c * @brief Built-in Lisp functions for editor operations * @details Provides Lisp bindings for core editor functionality including - * cursor movement, file operations, keybinding management, and text manipulation + * cursor movement, file operations, keybinding management, and text + * manipulation */ #include "../include/builtins.h" +#include "../include/buffer.h" #include "../include/data.h" #include "../include/define.h" #include "../include/editor_op.h" #include "../include/file_io.h" #include "../include/input.h" #include "../include/row_op.h" -#include "../include/buffer.h" #include "../include/split_screen.h" #include @@ -57,7 +58,7 @@ Lisp mapKey(Lisp args, LispError *e, LispContext ctx) { const char *key_sequence = lisp_string(lisp_car(args)); args = lisp_cdr(args); // second argument - Lisp func = lisp_car(args); + const Lisp func = lisp_car(args); E.key_binds = (struct keyBind_t *)realloc( E.key_binds, ++E.number_of_keybinds * sizeof(struct keyBind_t)); @@ -74,13 +75,14 @@ Lisp mapKey(Lisp args, LispError *e, LispContext ctx) { struct prefix_t prefix = find_prefix(prefix_name); E.key_binds[E.number_of_keybinds - 1].prefix_id = prefix.prefix_id; - + return lisp_null(); } /** * @brief Lisp function to move cursor in a specified direction - * @details Moves the editor cursor up, down, left, or right based on direction string. + * @details Moves the editor cursor up, down, left, or right based on direction + * string. * @param args Lisp list with one argument: direction string * - "u": up, "d": down, "r": right, "l": left * @param e Error pointer for Lisp error handling @@ -106,7 +108,7 @@ Lisp moveCursor(Lisp args, LispError *e, LispContext ctx) { is_in = editorMoveCursor(ARROW_LEFT); break; } - fprintf(stderr, "move lisp %d\n", is_in); + appDebug("move lisp %d\n", is_in); return lisp_make_bool(is_in); } @@ -130,21 +132,20 @@ void free_structs(void) { for (i = 0; i < E.number_of_buffer; ++i) { free(E.buffers[i].filename); for (j = 0; j < E.buffers[i].numrows; ++j) { - free(E.buffers[i].row[j].render); free(E.buffers[i].row[j].chars); } free(E.buffers[i].row); } - + free(E.init_file_path); fclose(E.fd_init_file); - } /** * @brief Lisp function to quit the editor - * @details Closes editor with unsaved changes protection. Prompts user to confirm - * quit after multiple attempts if file is dirty. Cleans up resources and restores terminal. + * @details Closes editor with unsaved changes protection. Prompts user to + * confirm quit after multiple attempts if file is dirty. Cleans up resources + * and restores terminal. * @param args Lisp arguments (unused) * @param e Error pointer for Lisp error handling * @param ctx Lisp context @@ -182,10 +183,10 @@ Lisp editorQuit(Lisp args, LispError *e, LispContext ctx) { Lisp l_editorSplitScreenVertical(Lisp args, LispError *e, LispContext ctx) { if (E.number_of_buffer >= 2) { - splitScreenVertical(E.buffers[0].buffer_id, E.buffers[1].buffer_id); - } else { - editorSetStatusMessage("Need at least 2 buffers open"); - } + splitScreenVertical(E.buffers[0].buffer_id, E.buffers[1].buffer_id); + } else { + editorSetStatusMessage("Need at least 2 buffers open"); + } return lisp_null(); } @@ -222,9 +223,10 @@ Lisp l_editorInsertNewLine(Lisp args, LispError *e, LispContext ctx) { * @note Uses E.constantes.TAB_LENGTH for indentation width */ Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx) { - - for (int i = 0; ibuffer_id); + for (int i = 0; i < E.constantes.TAB_LENGTH; ++i) { + bufferInsertBytes(" ", 1); } return lisp_null(); @@ -240,7 +242,9 @@ Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx) { * @note Updates global editor state E */ Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx) { - E.layout.panes[E.layout.active_pane].cursor_x = 0; + EditorPane *active = splitScreenGetActivePane(); + struct buffer_t *buf = bufferFindById(active->buffer_id); + buf->x = 0; return lisp_null(); } @@ -256,9 +260,7 @@ Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx) { Lisp moveCursorEndLine(Lisp args, LispError *e, LispContext ctx) { EditorPane *active = splitScreenGetActivePane(); struct buffer_t *buf = bufferFindById(active->buffer_id); - if (active->cursor_y < buf->numrows) { - active->cursor_x = buf->row[active->cursor_y].size; - } + buf->x = buf->row[buf->y].size; return lisp_null(); } @@ -272,13 +274,14 @@ Lisp moveCursorEndLine(Lisp args, LispError *e, LispContext ctx) { * @see editorDelChar() */ Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx) { - bufferDelChar(); + bufferDelBytes(); return lisp_null(); } /** * @brief Lisp function to move cursor up by one screen - * @details Scrolls up one full screen height, moving cursor to top of visible area. + * @details Scrolls up one full screen height, moving cursor to top of visible + * area. * @param args Lisp arguments (unused) * @param e Error pointer for Lisp error handling * @param ctx Lisp context @@ -288,7 +291,7 @@ Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx) { */ Lisp editorMoveCursorPageUp(Lisp args, LispError *e, LispContext ctx) { EditorPane *active = splitScreenGetActivePane(); - active->cursor_y = active->row_offset; + active->cursor_y = active->y_offset; int times = E.screenrows; while (--times) { editorMoveCursor(ARROW_UP); @@ -298,7 +301,8 @@ Lisp editorMoveCursorPageUp(Lisp args, LispError *e, LispContext ctx) { /** * @brief Lisp function to move cursor down by one screen - * @details Scrolls down one full screen height, moving cursor to bottom of visible area. + * @details Scrolls down one full screen height, moving cursor to bottom of + * visible area. * @param args Lisp arguments (unused) * @param e Error pointer for Lisp error handling * @param ctx Lisp context @@ -309,7 +313,7 @@ Lisp editorMoveCursorPageUp(Lisp args, LispError *e, LispContext ctx) { Lisp editorMoveCursorPageDown(Lisp args, LispError *e, LispContext ctx) { EditorPane *active = splitScreenGetActivePane(); struct buffer_t *buffer = bufferFindById(active->buffer_id); - active->cursor_y = active->row_offset + E.screenrows - 1; + active->cursor_y = active->y_offset + E.screenrows - 1; if (active->cursor_y > buffer->numrows) { active->cursor_y = buffer->numrows; } @@ -334,20 +338,20 @@ Lisp editorMoveCursorPageDown(Lisp args, LispError *e, LispContext ctx) { */ Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) { char *filename = editorPrompt("Open : %s", getenv("PWD"), 1); - if (filename){ + if (filename) { // editorOpen(filename); EditorPane *active = splitScreenGetActivePane(); active->buffer_id = bufferCreate(filename); } free(filename); - return lisp_null(); } /** * @brief Lisp function to insert a character - * @details Extracts a character from Lisp string argument and inserts it at cursor. + * @details Extracts a character from Lisp string argument and inserts it at + * cursor. * @param args Lisp list with one string argument * @param e Error pointer for Lisp error handling * @param ctx Lisp context @@ -355,8 +359,8 @@ Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) { * @note Uses first character of the string argument */ Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx) { - char c = lisp_string(lisp_car(args))[0]; - bufferInsertChar(c); + char *src = lisp_string(lisp_car(args)); + bufferInsertBytes(src, strlen(src)); return lisp_null(); } @@ -373,14 +377,14 @@ Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx) { */ Lisp addPackage(Lisp args, LispError *e, LispContext ctx) { const char *package_name = lisp_string(lisp_car(args)); - fprintf(stderr, "%s\n", package_name); + appDebug("%s\n", package_name); char *package_dir = (char *)calloc(256, sizeof(char)); FILE *fd_package = NULL; strcat(package_dir, getenv("HOME")); strcat(package_dir, "/.beluga/packages/"); strcat(package_dir, package_name); strcat(package_dir, "/init.lisp"); - fprintf(stderr, "%s\n", package_dir); + appDebug("%s\n", package_dir); fd_package = fopen(package_dir, "r"); lisp_eval(lisp_read_file(fd_package, &E.ctx_error, E.ctx), &E.ctx_error, E.ctx); @@ -390,30 +394,13 @@ Lisp addPackage(Lisp args, LispError *e, LispContext ctx) { return lisp_null(); } -/** - * @brief Lisp function to delete the current row - * @details Removes the line at the current cursor position. - * @param args Lisp arguments (unused) - * @param e Error pointer for Lisp error handling - * @param ctx Lisp context - * @return lisp_null() - * @note Updates global editor state E - * @see editorDelRow() - */ -Lisp editorDelRow_L(Lisp args, LispError *e, LispContext ctx) { - EditorPane *active = splitScreenGetActivePane(); - struct buffer_t *buffer = bufferFindById(active->buffer_id); - bufferDelRow(buffer, active->cursor_y); - return lisp_null(); -} - Lisp editorSwitchNextBuffer(Lisp args, LispError *e, LispContext ctx) { - fprintf(stderr, "switch buffer\n"); + appDebug("switch buffer\n"); if (E.number_of_buffer > 0) { EditorPane *active = splitScreenGetActivePane(); int next_idx = (active->buffer_id + 1) % E.number_of_buffer; active->buffer_id = next_idx; - } + } return lisp_null(); } @@ -440,7 +427,7 @@ Lisp editorUnifiedPanes(Lisp args, LispError *e, LispContext ctx) { * @see editorFind() */ Lisp bufferFind_L(Lisp args, LispError *e, LispContext ctx) { - fprintf(stderr, "LispFind\n"); + appDebug("LispFind\n"); EditorPane *active = splitScreenGetActivePane(); struct buffer_t *buffer = bufferFindById(active->buffer_id); bufferFind(buffer); @@ -449,8 +436,8 @@ Lisp bufferFind_L(Lisp args, LispError *e, LispContext ctx) { /** * @brief Lisp function to read character at cursor - * @details Returns the character at the current cursor position as a Lisp character. - * Returns 'a' if at end of line. + * @details Returns the character at the current cursor position as a Lisp + * character. Returns 'a' if at end of line. * @param args Lisp arguments (unused) * @param e Error pointer for Lisp error handling * @param ctx Lisp context @@ -460,11 +447,10 @@ Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx) { Lisp returned_char; EditorPane *active = splitScreenGetActivePane(); struct buffer_t *buffer = bufferFindById(active->buffer_id); - if (buffer->row[active->cursor_y].render[active->cursor_x] == 0) { + if (buffer->row[buffer->y].chars[buffer->x] == 0) { returned_char = lisp_make_char('a'); } else { - - returned_char = lisp_make_char(buffer->row[active->cursor_y].render[active->cursor_x]); + returned_char = lisp_make_char(buffer->row[buffer->y].chars[buffer->x]); } return returned_char; } @@ -488,7 +474,7 @@ Lisp editorSetPrefix(Lisp args, LispError *e, LispContext ctx) { struct prefix_t prefix = find_prefix(prefix_name); E.prefix_state = prefix.prefix_id; editorSetStatusMessage("prefix %s", prefix.prefix_name); - fprintf(stderr, "%s set\n", prefix_name); + appDebug("%s set\n", prefix_name); return lisp_null(); } diff --git a/src/editor_op.c b/src/editor_op.c index fd9c0b6..158f767 100644 --- a/src/editor_op.c +++ b/src/editor_op.c @@ -1,8 +1,13 @@ #include "../include/editor_op.h" + +#include + #include "../include/row_op.h" -#include "include/buffer.h" -#include "include/data.h" -#include "include/split_screen.h" +#include "../include/buffer.h" +#include "../include/data.h" +#include "../include/split_screen.h" +#include "../include/terminal.h" +#include "../include/utf8.h" extern struct editorConfig E; @@ -17,46 +22,99 @@ extern struct editorConfig E; * @note Updates global editor state E (status_msg, status_msg_time) * @see editorDrawMessageBar() */ -void editorSetStatusMessage(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - vsnprintf(E.status_msg, sizeof(E.status_msg), fmt, ap); - va_end(ap); - E.status_msg_time = time(NULL); +void editorSetStatusMessage(const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(E.status_msg, E.screencols, fmt, ap); + va_end(ap); + E.status_msg_time = time(NULL); } -void bufferInsertChar(int c) { - EditorPane *active = splitScreenGetActivePane(); - struct buffer_t *buf = bufferFindById(active->buffer_id); - if (active->cursor_y == buf->numrows) { - bufferInsertRow(buf, buf->numrows, "", 0); +void bufferInsertBytes(char* src, int n) +{ + appDebug("bufferInsertBytes \r\n"); + EditorPane* active = splitScreenGetActivePane(); + struct buffer_t* buf = bufferFindById(active->buffer_id); + if (buf->y == buf->numrows) + { + bufferInsertRow(buf, buf->numrows, "", 0); } - bufferRowInsertChar(buf, &buf->row[active->cursor_y], active->cursor_x, c); - active->cursor_x++; + bufferRowInsertBytes(buf, &buf->row[buf->y], buf->x, src, n); + buf->x += n; } -void bufferInsertNewLine() { - /* - * Add new line and place the cursor at the beginning of it - */ - fprintf(stderr, "Inserting new line\n"); +void bufferDelBytes(void) +{ + EditorPane* active = splitScreenGetActivePane(); + struct buffer_t* buf = bufferFindById(active->buffer_id); + + /* Nothing to delete */ + if (buf->numrows == 0) return; + if (buf->x == 0 && buf->y == 0) return; + + /* Use row_offset, not col_offset, for row indexing */ + row_t* r = &buf->row[buf->y]; + + if (buf->x > 0) + { + int byte_end = editorRowCxToByte(r, buf->x); + int byte_start = editorRowCxToByte(r, buf->x - 1); + int char_width = byte_end - byte_start; /* byte width of the character */ + + bufferRowDelByte(buf, r, byte_start, char_width); + E.dirty = 1; + } + else + { + /* Merge current row into the previous one */ + row_t* prev = &buf->row[buf->y - 1]; // FIX: was buf->y (same as r) + int prev_char_count = editorRowCharCount(prev, prev->size); + + bufferRowInsertBytes(buf, prev, prev->size, r->chars, r->size); + free(r->chars); + r->chars = NULL; + + memmove(&buf->row[buf->y], + &buf->row[buf->y + 1], + sizeof(row_t) * (buf->numrows - buf->y - 1)); + buf->numrows--; + + active->cursor_x = prev_char_count; + buf->x = prev_char_count; + active->cursor_y--; + buf->y--; + E.dirty = 1; + } +} + +void bufferInsertNewLine(void) { + appDebug("Inserting new line\n"); EditorPane *active = splitScreenGetActivePane(); - struct buffer_t *buf = bufferFindById(active->buffer_id); - frow *row; - if (!active->cursor_x) { - bufferInsertRow(buf, active->cursor_y, "", 0); - } else { - row = &buf->row[active->cursor_y]; - bufferInsertRow(buf, active->cursor_y + 1, &row->chars[active->cursor_x], - row->size - active->cursor_x); - row = &buf->row[active->cursor_y]; - row->size = active->cursor_x; - row->chars[row->size] = '\0'; - bufferUpdatfrow(row); - } - ++active->cursor_y; - active->cursor_x = 0; - fprintf(stderr, "Insert new line done\n"); -} + struct buffer_t *buf = bufferFindById(active->buffer_id); + appDebug("buf x %d\n", buf->x); + + if (buf->y >= buf->numrows) { + /* Cursor is past the last row: just append a blank line */ + bufferInsertRow(buf, buf->numrows, "", 0); + } else { + row_t *row = &buf->row[buf->y]; + + /* Insert the tail (from cursor to end) as a new row below */ + bufferInsertRow(buf, buf->y + 1, &row->chars[buf->x], row->size - buf->x); + + /* Re-fetch: realloc inside bufferInsertRow may have moved the array */ + row = &buf->row[buf->y]; + + /* Truncate the current row at the cursor */ + row->size = buf->x; + row->chars[row->size] = '\0'; + /* Do NOT touch row->cap — the allocation is still valid */ + } + + buf->y++; + buf->x = 0; + appDebug("Insert new line done\n"); +} diff --git a/src/file_io.c b/src/file_io.c index 1f72b54..8874422 100644 --- a/src/file_io.c +++ b/src/file_io.c @@ -33,12 +33,10 @@ void editorCloseFile(void) { struct buffer_t *buf = bufferFindById(active->buffer_id); active->cursor_x = 0; active->cursor_y = 0; - active->rx = 0; - active->row_offset = 0; - active->col_offset = 0; + active->x_offset = 0; + active->y_offset = 0; for (int i = 0; i < buf->numrows; ++i) { free(buf->row[i].chars); - free(buf->row[i].render); } buf->numrows = 0; free(buf->row); @@ -69,14 +67,17 @@ void editorOpen(struct buffer_t* buffer) { die("fopen"); char *line = NULL; - size_t line_cap = 0; + size_t line_cap; ssize_t line_len; + rewind(fp); + while ((line_len = getline(&line, &line_cap, fp)) != -1) { while (line_len > 0 && (line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) { --line_len; } + appDebug("line %s\n", line); bufferInsertRow(buffer, buffer->numrows, line, line_len); free(line); line = NULL; @@ -100,7 +101,6 @@ void editorSave() { EditorPane *active = splitScreenGetActivePane(); struct buffer_t *buffer = bufferFindById(active->buffer_id); int len; - char *buf; int fd; if (buffer->filename == NULL) { buffer->filename = editorPrompt("Save as: %s (ESC to cancel)", "", 1); @@ -109,52 +109,21 @@ void editorSave() { return; } } - buf = bufferRowsToString(buffer, &len); fd = open(buffer->filename, O_RDWR | O_CREAT, 0644); if (fd != -1) { - if (ftruncate(fd, len) != -1) { - if (write(fd, buf, len) == len) { + for (int i = 0; i < buffer->numrows; ++i) + { + len = strlen(buffer->row[i].chars); + if (write(fd, buffer->row[i].chars, len) != len) { close(fd); - free(buf); E.dirty = 0; - editorSetStatusMessage("%d bytes written to disk", len); + editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno)); + return; } + write(fd, "\n", 1); } close(fd); } - free(buf); - editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno)); + editorSetStatusMessage("File saved"); } - -/** - * @brief Searches for a string in the document - * @details Prompts user for a search query, then searches forward from current - * cursor position. Updates cursor position to the first match found. - * @note Updates global editor state E (cursor position, row_offset) - * @note Search is case-sensitive and operates on rendered line content - * @note Searches begin from the line after current cursor position - * @see editorPrompt() - * @see editorRowRxToCx() - */ -void bufferFind(struct buffer_t *buf) { - fprintf(stderr, "searching\n"); - char *query = editorPrompt("Search: %s (ESC to cancel)", "", 0); - EditorPane *active = splitScreenGetActivePane(); - - if (query == NULL) - return; - int i; - for (i = active->cursor_y + 1; i < buf->numrows; i++) { - frow *row = &buf->row[i]; - char *match = strstr(row->render, query); - if (match) { - active->cursor_y = i; - active->cursor_x = bufferRowRxToCx(row, match - row->render); - buf->row_offset = buf->numrows; - break; - } - } - free(query); -} - diff --git a/src/init.c b/src/init.c index 9278198..c626f34 100644 --- a/src/init.c +++ b/src/init.c @@ -17,7 +17,7 @@ struct editorConfig; void registerBuiltin(char *key_sequence, LispCFunc f) { lisp_env_define(E.ctx.p->env, lisp_make_symbol(key_sequence, E.ctx), lisp_make_func(f), E.ctx); - + } void initBuiltins() { @@ -35,7 +35,6 @@ void initBuiltins() { registerBuiltin("editor-open-file", editorOpenFile); registerBuiltin("editor-insert-char", editorPrintC); registerBuiltin("add-package", addPackage); - registerBuiltin("editor-del-row", editorDelRow_L); registerBuiltin("buffer-find", bufferFind_L); registerBuiltin("editor-read-char", editorReadChar_L); registerBuiltin("add-prefix", editorPrefix); @@ -50,6 +49,7 @@ void initBuiltins() { void initConfig() { E.ctx = lisp_init(); + E.ctx.p->err_port = fopen("log.err", "w"); E.env = lisp_env(E.ctx); lisp_lib_load(E.ctx); // Init builtins lisp functions @@ -90,23 +90,31 @@ void initEditor() { if (getWindowSize(&E.screenrows, &E.screencols) == -1) { die("getWindowSize"); } + appDebug("%d %d\n", E.screenrows, E.screencols); E.screenrows -= 2; // Init graphics variables splitScreenInit(); EditorPane *active = splitScreenGetActivePane(); + E.cursor_x = 0; + E.cursor_y = 0; + active->cursor_x = 0; active->cursor_y = 0; - active->rx = 0; - active->row_offset = 0; - active->col_offset = 0; + active->origin_x = 0; + active->origin_y = 0; + active->width = E.screencols; + active->height = E.screenrows; + E.init_file_path = (char *)calloc(256, sizeof(char)); strcat(E.init_file_path, getenv("HOME")); strcat(E.init_file_path, "/.beluga/config/init.lisp"); + appDebug("%s\n", E.init_file_path); E.fd_init_file = fopen(E.init_file_path, "r"); // Status bar + E.status_msg = (char *)calloc(E.screencols, sizeof(char)); E.status_msg[0] = '\0'; E.status_msg_time = 0; diff --git a/src/input.c b/src/input.c index bc59a02..4ecd613 100644 --- a/src/input.c +++ b/src/input.c @@ -5,6 +5,7 @@ #include "include/data.h" #include "include/buffer.h" #include "include/data.h" +#include "include/row_op.h" #include "include/split_screen.h" #include #include @@ -16,6 +17,9 @@ #include #include +#include "include/terminal.h" +#include "include/utf8.h" + extern struct editorConfig E; /** @@ -40,7 +44,7 @@ extern struct editorConfig E; const char *file_completion(const char *path) { DIR *dir; struct dirent *entry; - char directory[128]; + char directory[256]; char predict[128]; const char *last_slash; int predict_len = 0; @@ -48,8 +52,8 @@ const char *file_completion(const char *path) { // path is a directory if (path[strlen(path) - 1] == '/') { - fprintf(stderr, "[FILE COMP] is dir\n"); - strncpy(directory, path, 128); + appDebug("[FILE COMP] is dir\n"); + strncpy(directory, path, 256); } // Find dir name @@ -61,9 +65,9 @@ const char *file_completion(const char *path) { strncpy(predict, last_slash + 1, predict_len); directory[dir_len] = '\0'; predict[predict_len] = '\0'; - fprintf(stderr, "%s %s\n", directory, predict); + appDebug("%s %s\n", directory, predict); } else { - fprintf(stderr, "[FILE COMP] dir not found\n"); + appDebug("[FILE COMP] dir not found\n"); return strdup(path); } @@ -73,7 +77,7 @@ const char *file_completion(const char *path) { while ((entry = readdir(dir)) != NULL) { if (strncmp(entry->d_name, predict, predict_len) == 0) { - static char full_path[512]; + static char full_path[1024]; snprintf(full_path, sizeof(full_path), "%s%s", directory, entry->d_name); struct stat st; @@ -90,7 +94,7 @@ const char *file_completion(const char *path) { closedir(dir); dir = NULL; free(entry); - fprintf(stderr, "[FILE COMP] no entries\n"); + appDebug("[FILE COMP] no entries\n"); return strdup(path); } @@ -109,7 +113,8 @@ const char *file_completion(const char *path) { * @note Uses editorReadKey() for input and editorRefreshScreen() for display */ char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) { - size_t buf_size = 128; + size_t buf_size = 256; + appDebug("[FILE COMP] %s %d\n", placeHolder, strlen(placeHolder)); char *buf = malloc(buf_size); size_t buf_len = 0; int c = 0; @@ -135,18 +140,18 @@ char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) { return buf; } } else if (bPathMode && c == '\t') { - char path[128]; + char path[256]; char *pwd; if (buf[0] != '/') { pwd = getenv("PWD"); - fprintf(stderr, "%s\n", pwd); + appDebug("%s\n", pwd); memcpy(path, pwd, strlen(pwd)); path[strlen(pwd)] = '/'; strncat(path, buf, buf_len); } else { strcpy(path, buf); } - memset(buf, 0, 128); + memset(buf, 0, 256); buf_len = 0; char * buf_complete = (char *) file_completion(path); strcpy(buf, buf_complete); @@ -154,7 +159,7 @@ char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) { buf_len = strlen(buf); buf[buf_len] = '\0'; - } else if (!iscntrl(c) && c < 128) { + } else if (!iscntrl(c) && c < 256) { if (buf_len == buf_size - 1) { buf_size *= 2; buf = realloc(buf, buf_size); @@ -177,40 +182,49 @@ char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) { int editorMoveCursor(int key) { EditorPane *active = splitScreenGetActivePane(); struct buffer_t *buf = bufferFindById(active->buffer_id); - frow *row = (active->cursor_y + active->row_offset >= buf->numrows) ? NULL : &buf->row[active->cursor_y + active->row_offset]; - int row_len; - int x = active->cursor_x + active->col_offset; - int y = active->cursor_y + active->row_offset; + row_t *row = &buf->row[buf->y]; switch (key) { case ARROW_RIGHT: - if (row && x < row->size) { - active->cursor_x++; - - } else if (row && y == row->size) { - active->cursor_y++; - active->cursor_x = 0; + if (row && buf->x < row->size) { + int len = utf8Seqlen(row->chars[buf->x]); + buf->x += len; + } else if (row && buf->y < buf->numrows) { + buf->y++; + buf->x = 0; } break; case ARROW_DOWN: - if (y < buf->numrows) { + if (buf->y < buf->numrows) { - active->cursor_y++; + buf->y++; } break; case ARROW_UP: - if (y != 0) { - --active->cursor_y; + if (buf->y != 0) { + --buf->y; } break; case ARROW_LEFT: - if (x != 0) { - --active->cursor_x; - } else if (y > 0) { - --active->cursor_y; - active->cursor_x = buf->row[y].size; + if (buf->x != 0) { + --buf->x; + } else if (buf->y > 0) { + --buf->y; + buf->x = buf->row[buf->y].size; } break; } +/* + fprintf(stderr, "acx: %d acy %d aox %d aoy %d bx %d by %d ah %d aw %d\n", + active->cursor_x, + active->cursor_y, + active->x_offset, + active->y_offset, + buf->x, + buf->y, + active->height, + active->width + ); + */ return 1; @@ -228,7 +242,7 @@ int editorMoveCursor(int key) { int executeKeyBind(char *key_sequence) { int i; int previous_state = 0; - fprintf(stderr, "pressed %s\n", key_sequence); + appDebug("pressed %s\n", key_sequence); for (i = 0; i < E.number_of_keybinds; ++i) { if (!strcmp(key_sequence, E.key_binds[i].key_sequence)) { if (E.prefix_state != E.key_binds[i].prefix_id) { @@ -256,10 +270,13 @@ int executeKeyBind(char *key_sequence) { */ void editorProcessKeypress() { int c = editorReadKey(); + char key_sequence[8]; if (executeKeyBind(key_to_string(c))) { return; } - bufferInsertChar(c); + int seq_len = utf8Encode(c, key_sequence); + appDebug("key seq : %s\n", key_sequence); + bufferInsertBytes(key_sequence, seq_len); E.quit_times_buffer = E.constantes.QUIT_TIMES; } diff --git a/src/output.c b/src/output.c index 343da98..d8eab6e 100644 --- a/src/output.c +++ b/src/output.c @@ -13,6 +13,7 @@ #include "../include/row_op.h" #include "../include/split_screen.h" #include "../include/syntax_highlighter.h" +#include "../include/terminal.h" #include #include #include @@ -23,127 +24,135 @@ extern struct editorConfig E; /** * @brief Renders a single pane with its buffer content */ -static void editorDrawPane(struct abuf *ab, EditorPane *pane) { - if (pane == NULL || pane->buffer_id < 0) - return; - - struct buffer_t *buf = bufferFindById(pane->buffer_id); - if (buf == NULL) - return; - - // Draw content for this pane - for (int y = 0; y < pane->height; y++) { - int file_row = y + pane->row_offset; - - // Position cursor at start of pane row +static void editorDrawPane(struct abuf* ab, EditorPane* pane) +{ + int file_row; char pos_buf[32]; - int pos_len = snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH", - pane->start_row + y + 1, pane->start_col + 1); - abAppend(ab, pos_buf, pos_len); + int pos_len; + int chars_printed; + int start_offset; + int byte_len_to_print; + int bytes_to_print; + char* highlighted; + char welcome[80]; + int welcome_len; + int padding; - // Apply background color (6 bytes for RGB format) - abAppend(ab, E.theme.BACKGROUND_COLOR, strlen(E.theme.BACKGROUND_COLOR)); + if (pane == NULL || pane->buffer_id < 0) + return; - int chars_printed = 0; + const struct buffer_t* buf = bufferFindById(pane->buffer_id); + if (buf == NULL) + return; - if (file_row >= buf->numrows) { - // Empty line - show tilde - if (buf->numrows == 0 && y == pane->height / 3) { - char welcome[80]; - int welcome_len = - snprintf(welcome, sizeof(welcome), "Buffer %d", pane->buffer_id); - if (welcome_len > pane->width) - welcome_len = pane->width; + // Draw all pane + for (int y = 0; y < pane->height; y++) + { + file_row = y + pane->y_offset; - int padding = (pane->width - welcome_len) / 2; - if (padding) { - abAppend(ab, "~", 1); - chars_printed++; - padding--; + // Set cursor at start of pane row + pos_len = snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH", + pane->origin_y + y + 1, pane->origin_x + 1); + abAppend(ab, pos_buf, pos_len); + + // Apply background color (6 bytes for RGB format) + abAppend(ab, E.theme.BACKGROUND_COLOR, strlen(E.theme.BACKGROUND_COLOR)); + + chars_printed = 0; + + // pane line is out of buffer + if (file_row >= buf->numrows) + { + // Empty line - show tilde + + abAppend(ab, "~", 1); + for (int i = 0; + i < pane->width - 1; + ++i) + { + abAppend(ab, " ", 1); + } } - while (padding-- && chars_printed < pane->width) { - abAppend(ab, " ", 1); - chars_printed++; + else + { + start_offset = pane->x_offset; + + if (buf->filename[strlen(buf->filename) - 1] == 'c') + { + // Render line with syntax highlighting, constrain to pane width + highlighted = highlight_line( + &buf->row[file_row].chars[pane->x_offset], &byte_len_to_print); + + // Print only up to pane width + abAppend(ab, highlighted, byte_len_to_print); + free(highlighted); + } + else + { + // Render basic line + bytes_to_print = + buf->row[file_row].size - pane->x_offset < pane->width + ? buf->row[file_row].size - pane->x_offset + : pane->width; + abAppend(ab, &buf->row[file_row].chars[pane->x_offset], bytes_to_print); + } + // Fill remaining space with background color to pane width + for (int i = 0; + i < pane->width - editorRowCharCount(&buf->row[file_row], pane->width) + pane->x_offset; + ++i) + { + abAppend(ab, " ", 1); + } } - abAppend(ab, welcome, welcome_len); - chars_printed += welcome_len; - } else { - abAppend(ab, "~", 1); - chars_printed++; - } - } else { - // Render line with syntax highlighting, constrain to pane width - int start_offset = pane->col_offset; - int visible_len = buf->row[file_row].rsize - start_offset; - if (visible_len < 0) - visible_len = 0; - if (visible_len > pane->width) - visible_len = pane->width; - - if (buf->filename[strlen(buf->filename) - 1] == 'c') { - int byte_len_to_print; - - char *highlighted = highlight_line( - &buf->row[file_row].render[start_offset], &byte_len_to_print); - - // Print only up to pane width - - abAppend(ab, highlighted, byte_len_to_print); - free(highlighted); - } else { - abAppend(ab, &buf->row[file_row].render[start_offset], buf->row[file_row].rsize); - } - - chars_printed = visible_len; } - - // Fill remaining space with background color to pane width - abAppend(ab, E.theme.BACKGROUND_COLOR, strlen(E.theme.BACKGROUND_COLOR)); - while (chars_printed < pane->width) { - abAppend(ab, " ", 1); - chars_printed++; - } - - // Restore background color at end of line - } } /** * @brief Renders all panes based on current split configuration */ -static void editorDrawAllPanes(struct abuf *ab) { - ScreenLayout *layout = splitScreenGetLayout(); +static void editorDrawAllPanes(struct abuf* ab) +{ + const ScreenLayout* layout = splitScreenGetLayout(); - if (layout->num_panes == 1) { - // Single pane fullscreen - editorDrawPane(ab, &layout->panes[0]); - } else if (layout->num_panes == 2) { - // Draw both panes - for (int i = 0; i < 2; i++) { - editorDrawPane(ab, &layout->panes[i]); - - // Draw pane border/divider if not the last pane - if (layout->mode == SPLIT_VERTICAL && i == 0) { - // Draw vertical divider - int divider_col = layout->panes[0].width; - for (int y = 0; y < layout->panes[0].height; y++) { - char pos_buf[32]; - snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH", y + 1, divider_col); - abAppend(ab, pos_buf, strlen(pos_buf)); - abAppend(ab, "\x1b[1m|\x1b[0m", 9); // Bold pipe divider - } - } else if (layout->mode == SPLIT_HORIZONTAL && i == 0) { - // Draw horizontal divider - int divider_row = layout->panes[0].height; - char pos_buf[32]; - snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH", divider_row + 1, 1); - abAppend(ab, pos_buf, strlen(pos_buf)); - for (int x = 0; x < E.screencols; x++) { - abAppend(ab, "\x1b[1m-\x1b[0m", 9); // Bold dash divider - } - } + if (layout->num_panes == 1) + { + // Single pane fullscreen + editorDrawPane(ab, &layout->panes[0]); + } + else if (layout->num_panes == 2) + { + // Draw both panes + for (int i = 0; i < 2; i++) + { + editorDrawPane(ab, &layout->panes[i]); + + // Draw pane border/divider if not the last pane + if (layout->mode == SPLIT_VERTICAL && i == 0) + { + // Draw vertical divider + int divider_col = layout->panes[0].width; + for (int y = 0; y < layout->panes[0].height; y++) + { + char pos_buf[32]; + snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH", y + 1, divider_col); + abAppend(ab, pos_buf, strlen(pos_buf)); + abAppend(ab, "\x1b[1m|\x1b[0m", 9); // Bold pipe divider + } + } + else if (layout->mode == SPLIT_HORIZONTAL && i == 0) + { + // Draw horizontal divider + int divider_row = layout->panes[0].height; + char pos_buf[32]; + snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH", divider_row + 1, 1); + abAppend(ab, pos_buf, strlen(pos_buf)); + for (int x = 0; x < E.screencols; x++) + { + abAppend(ab, "\x1b[1m-\x1b[0m", 9); // Bold dash divider + } + } + } } - } } /** @@ -153,35 +162,89 @@ static void editorDrawAllPanes(struct abuf *ab) { * @note Updates global editor state E * @see editorRowCxToRx() */ -void editorScroll() { - EditorPane *active = splitScreenGetActivePane(); - struct buffer_t *buf = bufferFindById(active->buffer_id); - active->rx = active->cursor_x; +void editorScroll() +{ + EditorPane* active = splitScreenGetActivePane(); + struct buffer_t* buf = bufferFindById(active->buffer_id); + int rel_x, rel_y; - fprintf(stderr, "%d %d / %d %d\n", active->cursor_x, active->col_offset, - active->cursor_y, active->row_offset); + // compute relative coordinates + rel_x = editorRowCharCount(&buf->row[buf->y], buf->x); + appDebug("counting %d\n", rel_x); + rel_y = buf->y; - if (active->cursor_x < 0) { - active->cursor_x = 0; - active->col_offset = active->col_offset == 0 ? 0 : --active->col_offset; - } + appDebug("%d %d / %d %d\n", active->cursor_x, active->x_offset, + active->cursor_y, active->y_offset); - if (active->cursor_y < 0) { - active->cursor_y = 0; - active->row_offset = active->row_offset == 0 ? 0 : --active->row_offset; - } + while (rel_x != active->cursor_x + active->x_offset || + rel_y != active->cursor_y + active->y_offset) + { + if (rel_x < active->cursor_x + active->x_offset) + { + // LEFT + if (active->cursor_x == 0 && active->x_offset) + { + active->x_offset--; + } else + { + active->cursor_x--; + } + } else + { + // RIGHT + if (rel_x > active->cursor_x + active->x_offset) + { + if (active->cursor_x == active->width - 1) + { + active->x_offset++; + } else + { + active->cursor_x++; + } + } + } - if (active->cursor_x == active->width) { + if (rel_y < active->cursor_y + active->y_offset) + { + if (active->cursor_y == 0 && active->y_offset) + { + active->y_offset--; + } else + { + active->cursor_y--; + } + } - active->cursor_x--; - active->col_offset++; - } + if (rel_y > active->cursor_y + active->y_offset) + { + if (active->cursor_y == active->height - 1) + { + active->y_offset++; + } else + { + active->cursor_y++; + } + } - if (active->cursor_y == active->height) { + } +} - active->cursor_y--; - active->row_offset++; - } +char * basename(char *path) +{ + int len = strlen(path); + int flag=0; + + + for(int i=len-1; i>0; i--) + { + if(path[i]=='/' ) + { + flag=1; + path = path+i+1; + break; + } + } + return path; } /** @@ -192,55 +255,63 @@ void editorScroll() { * @param ab Pointer to append buffer structure for accumulating output * @note Uses ANSI escape codes for color inversion */ -void editorDrawStatusBar(struct abuf *ab) { - int len, render_len; - char status[80], render_status[80]; - EditorPane *active = splitScreenGetActivePane(); - struct buffer_t *buf = bufferFindById(active->buffer_id); +void editorDrawStatusBar(struct abuf* ab) +{ + // TO MODIFY + int len, render_len; + char status[E.screencols], render_status[E.screencols * 4]; + EditorPane* active = splitScreenGetActivePane(); + struct buffer_t* buf = bufferFindById(active->buffer_id); - abAppend(ab, "\x1b[7m", 4); // inverting colors + abAppend(ab, "\x1b[7m", 4); // inverting colors - const char *mode_str = ""; - ScreenLayout *layout = splitScreenGetLayout(); - if (layout->mode == SPLIT_VERTICAL) - mode_str = " [V-SPLIT]"; - else if (layout->mode == SPLIT_HORIZONTAL) - mode_str = " [H-SPLIT]"; + const char* mode_str = ""; + ScreenLayout* layout = splitScreenGetLayout(); + if (layout->mode == SPLIT_VERTICAL) + mode_str = " [V-SPLIT]"; + else if (layout->mode == SPLIT_HORIZONTAL) + mode_str = " [H-SPLIT]"; - // Build buffer status showing all buffers with dirty indicators - char buf_status[200] = ""; - int offset = 0; - for (int i = 0; i < E.number_of_buffer; i++) { - struct buffer_t *b = &E.buffers[i]; - char marker = (b->buffer_id == active->buffer_id) ? '>' : ' '; - char dirty_marker = b->dirty ? '*' : ' '; - offset += snprintf(&buf_status[offset], sizeof(buf_status) - offset, - "%c%d:%s%c ", marker, b->buffer_id, - b->filename ? b->filename : "[No Name]", dirty_marker); - } - - len = snprintf(status, sizeof(status), "%s%s", buf_status, mode_str); - - render_len = snprintf(render_status, sizeof(render_status), "%d/%d", - active->cursor_y + 1, buf->numrows); - - if (len > E.screencols) { - len = E.screencols; - } - - abAppend(ab, status, len); - while (len < E.screencols) { - if (E.screencols - len == render_len) { - abAppend(ab, render_status, render_len); - break; - } else { - abAppend(ab, " ", 1); - ++len; + // Build buffer status showing all buffers with dirty indicators + char buf_status[1024] = ""; + int offset = 0; + for (int i = 0; i < E.number_of_buffer; i++) + { + struct buffer_t* b = &E.buffers[i]; + char marker = (b->buffer_id == active->buffer_id) ? '>' : ' '; + char dirty_marker = b->dirty ? '*' : ' '; + offset += snprintf(&buf_status[offset], sizeof(buf_status) - offset, + "%c%d:%s%c ", marker, b->buffer_id, + b->filename ? basename(b->filename) : "[No Name]", dirty_marker); } - } - abAppend(ab, "\x1b[m", 3); // normal text mode - abAppend(ab, "\r\n", 2); + len = snprintf(status, sizeof(status), "%s%s", buf_status, mode_str); + + render_len = snprintf(render_status, sizeof(render_status), "%d/%d", + active->cursor_y + 1, buf->numrows); + + if (len > E.screencols) + { + len = E.screencols; + } + + abAppend(ab, status, len); + while (len < E.screencols) + { + if (E.screencols - len == render_len + 1) + { + abAppend(ab, render_status, render_len); + break; + } + else + { + abAppend(ab, " ", 1); + ++len; + } + } + + abAppend(ab, "\x1b[m", 3); // normal text mode + abAppend(ab, "\r\n", 2); } /** @@ -250,15 +321,18 @@ void editorDrawStatusBar(struct abuf *ab) { * @param ab Pointer to append buffer structure for accumulating output * @note Messages are set by editorSetStatusMessage() */ -void editorDrawMessageBar(struct abuf *ab) { - int msg_len = strlen(E.status_msg); - abAppend(ab, ERASE_END_LINE, 3); - if (msg_len > E.screencols) { - msg_len = E.screencols; - } - if (msg_len && time(NULL) - E.status_msg_time < 5) { - abAppend(ab, E.status_msg, msg_len); - } +void editorDrawMessageBar(struct abuf* ab) +{ + int msg_len = strlen(E.status_msg); + abAppend(ab, ERASE_END_LINE, 3); + if (msg_len > E.screencols) + { + msg_len = E.screencols; + } + if (msg_len && time(NULL) - E.status_msg_time < 5) + { + abAppend(ab, E.status_msg, msg_len); + } } /** @@ -272,33 +346,36 @@ void editorDrawMessageBar(struct abuf *ab) { * @see editorDrawMessageBar() */ -void editorRefreshScreen() { - struct abuf ab = ABUF_INIT; - char buf[32]; +void editorRefreshScreen() +{ + struct abuf ab = ABUF_INIT; + char buf[32]; - abAppend(&ab, HIDE_CURSOR, 6); - abAppend(&ab, CURSOR_TOP_LEFT, 3); - abAppend(&ab, E.theme.BACKGROUND_COLOR, - strlen(E.theme.BACKGROUND_COLOR)); // RGB background is 12 bytes + abAppend(&ab, HIDE_CURSOR, 6); + abAppend(&ab, CURSOR_TOP_LEFT, 3); + abAppend(&ab, E.theme.BACKGROUND_COLOR, + strlen(E.theme.BACKGROUND_COLOR)); // RGB background is 12 bytes - // Draw all panes - editorScroll(); - editorDrawAllPanes(&ab); + // Draw all panes + editorScroll(); + editorDrawAllPanes(&ab); - // Draw status bar and message bar - editorDrawStatusBar(&ab); - editorDrawMessageBar(&ab); + // Draw status bar and message bar + editorDrawStatusBar(&ab); + editorDrawMessageBar(&ab); - // Position cursor in active pane - EditorPane *active = splitScreenGetActivePane(); - if (active != NULL) { - snprintf(buf, sizeof(buf), "\x1b[%d;%dH", - active->cursor_y + active->start_row + 1, - active->cursor_x + active->start_col + 1); - abAppend(&ab, buf, strlen(buf)); - } + // Position cursor in active pane + EditorPane* active = splitScreenGetActivePane(); + struct buffer_t* buffer = bufferGetCurrent(); + if (active != NULL) + { + snprintf(buf, sizeof(buf), "\x1b[%d;%dH", + active->cursor_y + active->origin_y + 1, + active->cursor_x + active->origin_x + 1); + abAppend(&ab, buf, strlen(buf)); + } - abAppend(&ab, SHOW_CURSOR, 6); - write(STDOUT_FILENO, ab.b, ab.len); - abFree(&ab); + abAppend(&ab, SHOW_CURSOR, 6); + write(STDOUT_FILENO, ab.b, ab.len); + abFree(&ab); } diff --git a/src/row_op.c b/src/row_op.c index 54cb0cb..b015510 100644 --- a/src/row_op.c +++ b/src/row_op.c @@ -6,44 +6,46 @@ #include #include +#include "include/terminal.h" #include "include/utf8.h" extern struct editorConfig E; - void bufferInsertRow(struct buffer_t *buffer, int at, char *s, size_t len) { + if (at < 0 || at > buffer->numrows) + return; - if (at < 0 || at > buffer->numrows) { - + row_t *tmp = realloc(buffer->row, sizeof(row_t) * (buffer->numrows + 1)); + if (!tmp) return; - } - row_t *tmp = (row_t *)realloc(buffer->row, sizeof(row_t) * (buffer->numrows + 1)); - if (!tmp) { - return; - } buffer->row = tmp; - memmove(&buffer->row[at + 1], &buffer->row[at], sizeof(row_t) * (buffer->numrows - at)); - buffer->row[at].size = len; - buffer->row[at].cap = len + 1; + /* Shift existing rows to make room at 'at' — no at++ */ + if (at < buffer->numrows) { + memmove(&buffer->row[at + 1], &buffer->row[at], + sizeof(row_t) * (buffer->numrows - at)); /* not -at+1 */ + } + + buffer->row[at].size = len; + buffer->row[at].cap = len + 1; buffer->row[at].chars = malloc(len + 1); + if (!buffer->row[at].chars) + return; memcpy(buffer->row[at].chars, s, len); - buffer->row[at].chars[len] = '\n'; + buffer->row[at].chars[len] = '\0'; /* always NUL-terminate */ - - ++buffer->numrows; - ++buffer->dirty; + buffer->numrows++; + buffer->dirty++; } -void bufferFreeRow(row_t *row) { - free(row->chars); -} +void bufferFreeRow(row_t *row) { free(row->chars); } int editorRowCxToByte(const row_t *row, int cursor_x) { int i = 0, col = 0; while (col < cursor_x && i < row->size) { int sl = utf8Seqlen((unsigned char)row->chars[i]); - if (sl < 1) sl = 1; + if (sl < 1) + sl = 1; col++; i += sl; } @@ -54,7 +56,8 @@ int editorRowCxToByte(const row_t *row, int cursor_x) { * \fn editorRowInsertChar(erow *row, int at, int c) * \param at Index of where we want to insert the char */ -void bufferRowInsertBytes(struct buffer_t *buffer, row_t *row, int at, char *src, int n) { +void bufferRowInsertBytes(struct buffer_t *buffer, row_t *row, int at, + char *src, int n) { if (buffer->state == READ_ONLY) return; if (row->size + n + 1 > row->cap) { @@ -65,35 +68,31 @@ void bufferRowInsertBytes(struct buffer_t *buffer, row_t *row, int at, char *src memcpy(row->chars + at, src, n); row->size += n; row->chars = realloc(row->chars, row->size + 2); - memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1); - ++row->size; ++buffer->dirty; } - /** * \fn bufferRowDelChar(struct bufferConfig *E, frow *frow, int at) * \brief Delete the a char at the chosen position on the given row * \param at Index of the char to delete * \param row Row on operation is made */ -void bufferRowDelByte(struct buffer_t *buffer, row_t *row, int at, int n) -{ - if (at < 0 || at >= row->size) { +void bufferRowDelByte(struct buffer_t *buffer, row_t *row, int at, int n) { + if (at < 0 || at >= row->size) return; - memmove(row->chars + at, row->chars + at + n, row->size - at - n); - row->size -= n; - row->chars[row->size] = '\0'; - } + memmove(row->chars + at, row->chars + at + n, row->size - at - n); + row->size -= n; + row->chars[row->size] = '\0'; + buffer->x -= n; ++buffer->dirty; } -int editorRowCharCount(row_t *row) - { - int n = 0, i = 0; - while (i < row->size) { - int sl = utf8Seqlen((unsigned char)row->chars[i]); - if (sl < 1) sl = 1; - n++; i += sl; - } - return n; +int editorRowCharCount(row_t *row, int x) { + int n = 0, i = 0; + while (i < x && i < row->size) { + int sl = utf8Seqlen((unsigned char)row->chars[i]); + if (sl < 1) + sl = 1; + n++; + i += sl; } - + return n; +} diff --git a/src/split_screen.c b/src/split_screen.c index b59d1c3..9fa11b0 100644 --- a/src/split_screen.c +++ b/src/split_screen.c @@ -18,19 +18,19 @@ void splitScreenInit(void) { E.layout.mode = SPLIT_NONE; E.layout.num_panes = 1; E.layout.active_pane = 0; - + E.layout.panes = malloc(sizeof(EditorPane) * 2); - + // Initialize single fullscreen pane E.layout.panes[0].buffer_id = -1; // No buffer for now - E.layout.panes[0].start_row = 0; - E.layout.panes[0].start_col = 0; + E.layout.panes[0].origin_y = 0; + E.layout.panes[0].origin_x = 0; E.layout.panes[0].height = E.screenrows - 2; // Leave room for status bar E.layout.panes[0].width = E.screencols; E.layout.panes[0].cursor_x = 0; E.layout.panes[0].cursor_y = 0; - E.layout.panes[0].row_offset = 0; - E.layout.panes[0].col_offset = 0; + E.layout.panes[0].y_offset = 0; + E.layout.panes[0].x_offset = 0; E.layout.panes[0].is_active = 1; } @@ -47,40 +47,40 @@ int splitScreenVertical(int buffer_id_left, int buffer_id_right) { editorSetStatusMessage("Error: invalid buffer IDs"); return -1; } - + // Reallocate panes array E.layout.panes = realloc(E.layout.panes, sizeof(EditorPane) * 2); E.layout.mode = SPLIT_VERTICAL; E.layout.num_panes = 2; E.layout.active_pane = 0; - + int half_width = E.screencols / 2; - int pane_height = E.screenrows - 2; // Leave room for status bar - + int pane_height = E.screenrows; // Leave room for status bar + // Left pane E.layout.panes[0].buffer_id = buffer_id_left; - E.layout.panes[0].start_row = 0; - E.layout.panes[0].start_col = 0; + E.layout.panes[0].origin_y = 0; + E.layout.panes[0].origin_x = 0; E.layout.panes[0].height = pane_height; E.layout.panes[0].width = half_width; E.layout.panes[0].cursor_x = 0; E.layout.panes[0].cursor_y = 0; - E.layout.panes[0].row_offset = 0; - E.layout.panes[0].col_offset = 0; + E.layout.panes[0].y_offset = 0; + E.layout.panes[0].x_offset = 0; E.layout.panes[0].is_active = 1; - + // Right pane E.layout.panes[1].buffer_id = buffer_id_right; - E.layout.panes[1].start_row = 0; - E.layout.panes[1].start_col = half_width; + E.layout.panes[1].origin_y = 0; + E.layout.panes[1].origin_x = half_width; E.layout.panes[1].height = pane_height; E.layout.panes[1].width = E.screencols - half_width; E.layout.panes[1].cursor_x = 0; E.layout.panes[1].cursor_y = 0; - E.layout.panes[1].row_offset = 0; - E.layout.panes[1].col_offset = 0; + E.layout.panes[1].y_offset = 0; + E.layout.panes[1].x_offset = 0; E.layout.panes[1].is_active = 0; - + editorSetStatusMessage("Vertical split: %d | %d", buffer_id_left, buffer_id_right); return 0; } @@ -98,39 +98,39 @@ int splitScreenHorizontal(int buffer_id_top, int buffer_id_bottom) { editorSetStatusMessage("Error: invalid buffer IDs"); return -1; } - + // Reallocate panes array E.layout.panes = realloc(E.layout.panes, sizeof(EditorPane) * 2); E.layout.mode = SPLIT_HORIZONTAL; E.layout.num_panes = 2; E.layout.active_pane = 0; - + int half_height = (E.screenrows - 2) / 2; // Account for status bar - + // Top pane E.layout.panes[0].buffer_id = buffer_id_top; - E.layout.panes[0].start_row = 0; - E.layout.panes[0].start_col = 0; + E.layout.panes[0].origin_y = 0; + E.layout.panes[0].origin_x = 0; E.layout.panes[0].height = half_height; E.layout.panes[0].width = E.screencols; E.layout.panes[0].cursor_x = 0; E.layout.panes[0].cursor_y = 0; - E.layout.panes[0].row_offset = 0; - E.layout.panes[0].col_offset = 0; + E.layout.panes[0].y_offset = 0; + E.layout.panes[0].x_offset = 0; E.layout.panes[0].is_active = 1; - + // Bottom pane E.layout.panes[1].buffer_id = buffer_id_bottom; - E.layout.panes[1].start_row = half_height; - E.layout.panes[1].start_col = 0; + E.layout.panes[1].origin_y = half_height; + E.layout.panes[1].origin_x = 0; E.layout.panes[1].height = E.screenrows - 2 - half_height; E.layout.panes[1].width = E.screencols; E.layout.panes[1].cursor_x = 0; E.layout.panes[1].cursor_y = 0; - E.layout.panes[1].row_offset = 0; - E.layout.panes[1].col_offset = 0; + E.layout.panes[1].x_offset = 0; + E.layout.panes[1].y_offset = 0; E.layout.panes[1].is_active = 0; - + editorSetStatusMessage("Horizontal split: %d / %d", buffer_id_top, buffer_id_bottom); return 0; } @@ -142,13 +142,13 @@ void splitScreenUnify(void) { E.layout.mode = SPLIT_NONE; E.layout.num_panes = 1; E.layout.active_pane = 0; - - E.layout.panes[0].start_row = 0; - E.layout.panes[0].start_col = 0; - E.layout.panes[0].height = E.screenrows - 2; + + E.layout.panes[0].origin_x = 0; + E.layout.panes[0].origin_y = 0; + E.layout.panes[0].height = E.screenrows; E.layout.panes[0].width = E.screencols; E.layout.panes[0].is_active = 1; - + editorSetStatusMessage("Unified view"); } @@ -161,17 +161,17 @@ int splitScreenSwitchPane(void) { editorSetStatusMessage("No split to switch"); return -1; } - + // Deactivate current pane E.layout.panes[E.layout.active_pane].is_active = 0; - + // Move to next pane E.layout.active_pane = (E.layout.active_pane + 1) % E.layout.num_panes; - + // Activate new pane E.layout.panes[E.layout.active_pane].is_active = 1; - - editorSetStatusMessage("Switched to pane %d (buffer %d)", + + editorSetStatusMessage("Switched to pane %d (buffer %d)", E.layout.active_pane, E.layout.panes[E.layout.active_pane].buffer_id); return 0; @@ -187,15 +187,15 @@ int splitScreenSetPaneBuffer(int buffer_id) { editorSetStatusMessage("Error: invalid buffer ID"); return -1; } - + EditorPane *active = &E.layout.panes[E.layout.active_pane]; active->buffer_id = buffer_id; active->cursor_x = 0; active->cursor_y = 0; - active->row_offset = 0; - active->col_offset = 0; - - editorSetStatusMessage("Pane %d now showing buffer %d", + active->origin_x = 0; + active->origin_y = 0; + + editorSetStatusMessage("Pane %d now showing buffer %d", E.layout.active_pane, buffer_id); return 0; } diff --git a/src/syntax_highlighter.c b/src/syntax_highlighter.c index 1662293..0c32f53 100644 --- a/src/syntax_highlighter.c +++ b/src/syntax_highlighter.c @@ -1,5 +1,6 @@ #include "../include/syntax_highlighter.h" #include "../include/data.h" +#include "../include/utf8.h" #include #include #include @@ -18,10 +19,52 @@ const char *c_types[] = {"int", "char", "float", "double", "void", "long", "short", "unsigned", "signed", "bool", NULL}; +// Returns the byte length of the UTF-8 character starting at s. +// Never returns 0 for a non-NUL byte, so callers won't infinite-loop. +static int utf8_char_len(const char *s) +{ + unsigned char c = (unsigned char)*s; + if (c == 0) return 0; + if (c < 0x80) return 1; + if ((c & 0xE0) == 0xC0) return 2; + if ((c & 0xF0) == 0xE0) return 3; + if ((c & 0xF8) == 0xF0) return 4; + return 1; // continuation byte or invalid — advance 1 to avoid infinite loop +} + +// Copy one full UTF-8 character from src+i into dst+pos, advance both indices. +static void copy_utf8_char(char *dst, int *dst_pos, const char *src, int *src_pos) +{ + int len = utf8_char_len(&src[*src_pos]); + for (int b = 0; b < len; b++) + dst[(*dst_pos)++] = src[(*src_pos)++]; +} + // Check if character is alphanumeric or underscore -int is_word_char(char c) { - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || c == '_' || c == '#'; +int is_word_char(const char *s) +{ + uint32_t cp = utf8Decode(&s); + + if ((cp >= 'a' && cp <= 'z') || (cp >= 'A' && cp <= 'Z') || + (cp >= '0' && cp <= '9') || cp == '_' || cp == '#') + return 1; + + if (cp == 0xFFFD) return 0; + + if (cp >= 0x00C0 && cp <= 0x017F) return 1; + if (cp >= 0x0370 && cp <= 0x03FF) return 1; + if (cp >= 0x0400 && cp <= 0x04FF) return 1; + if (cp >= 0x0600 && cp <= 0x06FF) return 1; + if (cp >= 0x05D0 && cp <= 0x05EA) return 1; + if (cp >= 0x0900 && cp <= 0x097F) return 1; + if (cp >= 0x4E00 && cp <= 0x9FFF) return 1; + if ((cp >= 0x3040 && cp <= 0x309F) || + (cp >= 0x30A0 && cp <= 0x30FF)) return 1; + if (cp >= 0xAC00 && cp <= 0xD7A3) return 1; + if ((cp >= 0x0660 && cp <= 0x0669) || + (cp >= 0x06F0 && cp <= 0x06F9)) return 1; + + return 0; } // Check if string is a keyword @@ -65,26 +108,29 @@ int comment_section = 0; // Highlight a line of C code and return the highlighted string // Returns a newly allocated string that must be freed by the caller char *highlight_line(const char *line, int *length) { - char *result = malloc(1024); // Allocate space for result + // Each byte can expand to at most (color_prefix + 4 bytes + color_reset). + // Allocate generously based on line length to avoid overflow. + int line_len = strlen(line); + int buf_size = line_len * 32 + 256; + char *result = malloc(buf_size); int result_pos = 0; int i = 0; while (line[i] != '\0' && line[i] != '\n') { - // Skip whitespace + // Skip whitespace — copy full UTF-8 char (whitespace is always ASCII, + // but using copy_utf8_char keeps the pattern consistent) if (line[i] == ' ' || line[i] == '\t') { - result[result_pos++] = line[i++]; + copy_utf8_char(result, &result_pos, line, &i); continue; } - + if (comment_section) { result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_COMMENT); while (line[i] != '\0' && line[i] != '\n') { - - if (line[i] == '*' && line[i + 1] == '/') { - comment_section = 0; - } - - result[result_pos++] = line[i++]; + if (line[i] == '*' && line[i + 1] == '/') { + comment_section = 0; + } + copy_utf8_char(result, &result_pos, line, &i); } result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET); continue; @@ -94,14 +140,14 @@ char *highlight_line(const char *line, int *length) { if (line[i] == '/' && line[i + 1] == '/') { result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_COMMENT); while (line[i] != '\0' && line[i] != '\n') { - result[result_pos++] = line[i++]; + copy_utf8_char(result, &result_pos, line, &i); } result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET); continue; } // Handle block comments - if ((line[i] == '/' && line[i + 1] == '*')) { + if (line[i] == '/' && line[i + 1] == '*') { comment_section = 1; result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_COMMENT); result[result_pos++] = line[i++]; @@ -110,9 +156,10 @@ char *highlight_line(const char *line, int *length) { if (line[i] == '*' && line[i + 1] == '/') { result[result_pos++] = line[i++]; result[result_pos++] = line[i++]; + comment_section = 0; break; } - result[result_pos++] = line[i++]; + copy_utf8_char(result, &result_pos, line, &i); } result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET); continue; @@ -125,9 +172,9 @@ char *highlight_line(const char *line, int *length) { while (line[i] != '\0' && line[i] != '"') { if (line[i] == '\\') { result[result_pos++] = line[i++]; - result[result_pos++] = line[i++]; + copy_utf8_char(result, &result_pos, line, &i); } else { - result[result_pos++] = line[i++]; + copy_utf8_char(result, &result_pos, line, &i); } } if (line[i] == '"') @@ -143,9 +190,9 @@ char *highlight_line(const char *line, int *length) { while (line[i] != '\0' && line[i] != '\'') { if (line[i] == '\\') { result[result_pos++] = line[i++]; - result[result_pos++] = line[i++]; + copy_utf8_char(result, &result_pos, line, &i); } else { - result[result_pos++] = line[i++]; + copy_utf8_char(result, &result_pos, line, &i); } } if (line[i] == '\'') @@ -157,22 +204,26 @@ char *highlight_line(const char *line, int *length) { // Handle numbers if (line[i] >= '0' && line[i] <= '9') { result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_NUMBER); - while (is_word_char(line[i]) || line[i] == '.') { - result[result_pos++] = line[i++]; + while (is_word_char(&line[i]) || line[i] == '.') { + copy_utf8_char(result, &result_pos, line, &i); } result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET); continue; } // Handle identifiers and keywords - if (is_word_char(line[i])) { + if (is_word_char(&line[i])) { int start = i; - while (is_word_char(line[i])) - i++; + // Advance by full UTF-8 character widths, not single bytes + while (line[i] != '\0' && is_word_char(&line[i])) + i += utf8_char_len(&line[i]); char word[256]; - strncpy(word, &line[start], i - start); - word[i - start] = '\0'; + int word_len = i - start; + if (word_len >= (int)sizeof(word)) + word_len = (int)sizeof(word) - 1; + strncpy(word, &line[start], word_len); + word[word_len] = '\0'; TokenType type = TOKEN_DEFAULT; if (is_keyword(word)) @@ -185,9 +236,9 @@ char *highlight_line(const char *line, int *length) { continue; } - // Handle operators and other characters + // Handle operators and other characters (including non-ASCII multi-byte) result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_DEFAULT); - result[result_pos++] = line[i++]; + copy_utf8_char(result, &result_pos, line, &i); result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET); } diff --git a/src/terminal.c b/src/terminal.c index 15899dc..1e6ccc6 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -5,6 +5,7 @@ #include "../include/data.h" #include "../include/define.h" +#include #include #include #include @@ -85,6 +86,7 @@ int editorReadKey() { char c; /* read first byte — may be start of UTF-8 or escape */ while (read(STDIN_FILENO, &c, 1) != 1); + appDebug("f : %X\r\n", c); if (c == '\x1b') { char seq[6]; @@ -179,3 +181,14 @@ int getWindowSize(int *rows, int *cols) { return 0; } } + +void appDebug(const char *fmt, ...) { + #ifdef APP_DEBUG + va_list ap; + char message[256]; + va_start(ap, fmt); + vsnprintf(message, 256, fmt, ap); + va_end(ap); + fprintf(stderr, "%s\n", message); + #endif +}